4 Commits

Author SHA1 Message Date
Marc Wäckerlin
9c4d85528a added image in qr-code or next to name 2025-11-08 17:09:38 +01:00
Marc Wäckerlin
a43e21e3b2 initial release, ready for ctan, typo 2018-08-15 23:34:15 +02:00
Marc Wäckerlin
deeec7c8b2 more cleanups for ctan 2018-08-15 23:21:17 +02:00
Marc Wäckerlin
73d0ec06b0 textwidth in example updated 2018-08-15 00:05:01 +02:00
14 changed files with 159 additions and 54 deletions

View File

@@ -38,6 +38,7 @@ Copy `businesscard-qrcode/businesscard-qrcode.cls` to your LaTeX class path. Sim
```bash
mkdir ~/texmf/tex/latex/businesscard-qrcode
cp businesscard-qrcode/businesscard-qrcode.cls ~/texmf/tex/latex/businesscard-qrcode/
ln -s ~/texmf/tex/latex ~/texmf/tex/xelatex
```
@@ -46,6 +47,8 @@ Compilation
**Important:** You must use **`xelatex`** for compilation, because `xelatex` properly supports UTF-8 (e.g. needed for german umlauts or chinese characters). The package `inputenc` messes up with package `qrcode`.
Note for iOS: vCard content inside the QR now uses CRLF line endings (per spec) to improve import robustness on iOS without changing the vCard version (still 4.0 by default). Also the QR error correction level can be configured.
Document Structure
------------------
@@ -91,6 +94,10 @@ Layout options are set as options to the `\documentclass`, e.g.:
- `fill` or `nofill`: fill empty space between icon and text, default: `fill`
- `qrfirst` or `textfirst`: switch position of QR-Code and text block, default: `qrfirst`
- `https` or `www`: should links in the hints be prefixed with `https://` or `www.`, default: `https`
- `qreclevel=`: QR error correction level `L`, `M`, `Q`, or `H`, default: `Q`. Increase to `H` if you use `photoinqr=true` or expect minor print noise.
- `photoinqr` or `nophotoinqr`: place photo in the center of the QR code (`photoinqr`) instead of next to the name, default: `nophotoinqr`. **Requires `qreclevel=H` for reliable scanning**, as the photo occludes part of the QR code (~15% by default).
- `qrlogoscale=`: relative size of the photo overlay in QR code (when `photoinqr=true`), as fraction of QR code width, default: `0.15` (15%). Higher values may reduce scannability.
- `qrlogoborder=`: white padding around the photo overlay (when `photoinqr=true`), as fraction of QR code width, default: `0.02` (2%). Increases contrast and readability for scanners.
Data Definitions
@@ -194,15 +201,43 @@ Save it as file [texstudio_d30266.tex] and compile it to get [texstudio_d30266.p
See [examples] for more examples.
Photo (optional)
================
You can add an optional photo that appears next to the name or in the center of the QR code. The photo is dynamically scaled to match the name line height (when next to name) or to 15% of the QR code size (when in QR).
```latex
\photo{path/to/photo.jpg}
```
### Photo Placement Options:
**Default (photo next to name):**
```latex
\documentclass{businesscard-qrcode}
\photo{photo.jpg}
```
**Photo in QR code center:**
```latex
\documentclass[photoinqr,qreclevel=H]{businesscard-qrcode}
\photo{photo.jpg}
```
**Important:** When using `photoinqr=true`:
- Always use `qreclevel=H` (highest error correction) for reliable scanning
- Test QR code scannability with multiple apps before printing
- Start with `qrlogoscale=0.15` and `qrlogoborder=0.02`; if scanning fails, try `qrlogoscale=0.12` or `0.10`
- The photo is placed on a white rounded rectangle background to preserve contrast
Need More
=========
If you are missing a feature or a configuration option, just open a [ticket] and I will care about it.
If you are missing a feature or a configuration option, consult the [project] page. Just open a [ticket] and the [author] will care about it. Or extend it, it's [lgpl].
[ticket]: https://mrw.sh/templates/latex/issues "open issues and tickets for my LaTeX-templates project"
[examples]: examples "more examples"
[vcard]: https://tools.ietf.org/html/rfc6350 "RFC 6350, vCard Format Specification Version 4.0"
[app]: https://f-droid.org/de/packages/com.google.zxing.client.android/ "Barcode Scanner"
@@ -226,3 +261,8 @@ If you are missing a feature or a configuration option, just open a [ticket] and
[wikipedia]: https://wikipedia.org "the online encyclopedia"
[pgp]: https://en.wikipedia.org/wiki/Pretty_Good_Privacy "pretty good privacy — encryption standard"
[nextcloud federation id]: https://nextcloud.com/federation "share your cloud across others"
[I]: https://marc.wäckerlin.ch "Marc Wäckerlin"
[ticket]: https://mrw.sh/templates/businesscard-qrcode/issues "open issues and tickets for my LaTeX-templates project"
[author]: https://marc.wäckerlin.ch "Marc Wäckerlin"
[project]: https://mrw.sh/templates/businesscard-qrcode "the main project page"
[lgpl]: https://www.gnu.org/licenses/lgpl-3.0 "Library GNU Public License"

View File

@@ -1,5 +1,7 @@
% Author: Marc Wäckerlin
% License: LGPL
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{businesscard-qrcode}[2018/08/14 version 1.0 ready for ctan]
\ProvidesClass{businesscard-qrcode}[2018/08/15 version 1.2 ready for ctan]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% option evaluation
@@ -18,7 +20,12 @@
\DeclareStringOption[1]{cutlen}
\DeclareStringOption[0.50]{textwidth}
\DeclareStringOption[0.40]{qrwidth}
\DeclareStringOption[Q]{qreclevel} % QR code error correction level (L,M,Q,H) default Q
\DeclareStringOption[0.15]{qrlogoscale} % relative size of logo in QR code (0-1), default 0.15 = 15%
\DeclareStringOption[0.02]{qrlogoborder} % white padding around the photo overlay in QR code
\DeclareStringOption[de]{lang}
\DeclareBoolOption[false]{ioscrlf} % use CRLF line endings in vCard (iOS compatibility); default off to avoid pdfTeX issues
\DeclareBoolOption[false]{photoinqr} % if true: photo goes in QR center; if false (default): photo next to name
\DeclareBoolOption[true]{address}
\DeclareComplementaryOption{noaddress}{address}
\DeclareBoolOption[true]{hint}
@@ -64,6 +71,8 @@
\RequirePackage{DejaVuSans}
\RequirePackage[T1]{fontenc}
\RequirePackage{wrapfig}
\RequirePackage{graphicx} % needed for optional photo
\RequirePackage{tikz} % needed for logo overlay in QR code
\RequirePackage[\content,top=\padding,left=\padding,right=\padding,bottom=\padding]{geometry}
%\RequirePackage{pbox}
\RequirePackage{varwidth}
@@ -115,6 +124,7 @@
\registerData{google}
\registerData{pgpurl}
\registerData{pgpfingerprint}
\registerData{photo} % path to photo file for display next to name
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -140,36 +150,46 @@
% name - assemble full name from the parts, such as Xgivennames and Xfamilynames
\newcommand\name{\ifexists{Xhonoricprefix}{\Xhonoricprefix\ }\ifexists{Xgivennames}{\Xgivennames\ }\ifexists{Xfamilynames}{\Xfamilynames}\ifexists{Xhonoricsuffix}{\ \Xhonoricsuffix}}
% internal box for measuring name height when photo is present
\newsavebox{\BCQ@namebox}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% vcard - the content of the vcard
\newcommand\vcard{BEGIN:VCARD^^J
VERSION:4.0^^J
N:\cond{Xfamilynames};\cond{Xgivennames};\cond{Xadditionalnames};\cond{Xhonoricprefix};\cond{Xhonoricsuffix}^^J
FN:\name\ifexists{Xadditionalnames}{\ifcsempty{name}{}{\ }\Xadditionalnames}^^J
\ifexists{printaddress}{ADR\ifexists{Xtype}{;TYPE=\Xtype}:\cond{Xpobox};\cond{Xextaddr};\cond{Xstreet};\cond{Xcity};\cond{Xregion};\cond{Xzip};\cond{Xcountry}^^J}
\ifexists{Xphone}{TEL;VALUE=uri;TYPE=\ifexists{Xtype}{\Xtype,}voice,text:tel:\Xphone^^J}
\ifexists{Xemail}{EMAIL\ifexists{Xtype}{;TYPE=\Xtype}:\Xemail^^J}
\ifexists{Xjabber}{IMPP;TYPE=XMPP:\Xjabber^^J}
\ifexists{Xmatrixorg}{IMPP;TYPE=MATRIX:\Xmatrixorg^^J}
\ifexists{Xcloud}{URL:https://nextcloud.com/federation/\#\Xcloud^^J}
\ifexists{Xhomepage}{URL:https://\Xhomepage^^J}
\ifexists{Xwordpress}{URL:https://\Xwordpress^^J}
\ifexists{Xdrupal}{URL:https://\Xdrupal^^J}
\ifexists{Xjoomla}{URL:https://\Xjoomla^^J}
\ifexists{Xwikipedia}{URL:https://\lang.wikipedia.org/wiki/\Xwikipedia^^J}
\ifexists{Xlink}{URL:https://\Xlink^^J}
\ifexists{Xworld}{URL:https://\Xworld^^J}
\ifexists{Xgit}{URL:https://\Xgit^^J}
\ifexists{Xgitea}{URL:https://\Xgitea^^J}
\ifexists{Xgithub}{URL:https://github.com/\Xgithub^^J}
\ifexists{Xfacebook}{URL:https://facebook.com/\Xfacebook^^J}
\ifexists{Xtwitter}{URL:https://twitter.com/\Xtwitter^^J}
\ifexists{Xyoutube}{URL:https://youtube.com/user/\Xyoutube^^J}
\ifexists{Xgoogle}{URL:https://plus.google.com/+\Xgoogle^^J}
\ifexists{Xpgpurl}{KEY;MEDIATYPE=application/pgp-keys:\Xpgpurl^^J}
\ifexists{Xpgpfingerprint}{KEY:data:application/x-pgp-fingerprint,\Xpgpfingerprint^^J}
END:VCARD^^J}
% newline selection: default LF (robust for qrcode). Optional CRLF if ioscrlf option set.
\ifBCQ@ioscrlf
\def\BCQ@nl{\char13\char10}% CRLF
\else
\def\BCQ@nl{^^J}% LF only
\fi
% vcard assembly (version 4.0 retained). CRLF improves iOS import robustness.
\newcommand\vcard{BEGIN:VCARD\BCQ@nl
VERSION:4.0\BCQ@nl
N:\cond{Xfamilynames};\cond{Xgivennames};\cond{Xadditionalnames};\cond{Xhonoricprefix};\cond{Xhonoricsuffix}\BCQ@nl
FN:\name\ifexists{Xadditionalnames}{\ifcsempty{name}{} { }\Xadditionalnames}\BCQ@nl
\ifexists{printaddress}{ADR\ifexists{Xtype}{;TYPE=\Xtype}:\cond{Xpobox};\cond{Xextaddr};\cond{Xstreet};\cond{Xcity};\cond{Xregion};\cond{Xzip};\cond{Xcountry}\BCQ@nl}
\ifexists{Xphone}{TEL;VALUE=uri;TYPE=\ifexists{Xtype}{\Xtype,}voice,text:tel:\Xphone\BCQ@nl}
\ifexists{Xemail}{EMAIL\ifexists{Xtype}{;TYPE=\Xtype}:\Xemail\BCQ@nl}
\ifexists{Xjabber}{IMPP;TYPE=XMPP:\Xjabber\BCQ@nl}
\ifexists{Xmatrixorg}{IMPP;TYPE=MATRIX:\Xmatrixorg\BCQ@nl}
\ifexists{Xcloud}{URL:https://nextcloud.com/federation/\#\Xcloud\BCQ@nl}
\ifexists{Xhomepage}{URL:https://\Xhomepage\BCQ@nl}
\ifexists{Xwordpress}{URL:https://\Xwordpress\BCQ@nl}
\ifexists{Xdrupal}{URL:https://\Xdrupal\BCQ@nl}
\ifexists{Xjoomla}{URL:https://\Xjoomla\BCQ@nl}
\ifexists{Xwikipedia}{URL:https://\lang.wikipedia.org/wiki/\Xwikipedia\BCQ@nl}
\ifexists{Xlink}{URL:https://\Xlink\BCQ@nl}
\ifexists{Xworld}{URL:https://\Xworld\BCQ@nl}
\ifexists{Xgit}{URL:https://\Xgit\BCQ@nl}
\ifexists{Xgitea}{URL:https://\Xgitea\BCQ@nl}
\ifexists{Xgithub}{URL:https://github.com/\Xgithub\BCQ@nl}
\ifexists{Xfacebook}{URL:https://facebook.com/\Xfacebook\BCQ@nl}
\ifexists{Xtwitter}{URL:https://twitter.com/\Xtwitter\BCQ@nl}
\ifexists{Xyoutube}{URL:https://youtube.com/user/\Xyoutube\BCQ@nl}
\ifexists{Xgoogle}{URL:https://plus.google.com/+\Xgoogle\BCQ@nl}
\ifexists{Xpgpurl}{KEY;MEDIATYPE=application/pgp-keys:\Xpgpurl\BCQ@nl}
\ifexists{Xpgpfingerprint}{KEY:data:application/x-pgp-fingerprint,\Xpgpfingerprint\BCQ@nl}
END:VCARD\BCQ@nl}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -231,7 +251,24 @@ END:VCARD^^J}
%\frame
{
\begin{minipage}[c][\heightscale][c]{\imagepercents\textwidth}
\qrcode[level=Q,version=0,height=\textwidth]{\vcard}
\ifBCQ@photoinqr
% QR code with photo overlay in center
\ifcsdef{Xphoto}{%
\begin{tikzpicture}
\node[inner sep=0pt] (qr) {\qrcode[level=\BCQ@qreclevel,version=0,height=\textwidth]{\vcard}};
% White padding behind the photo to improve readability
\node[fill=white, rounded corners=0.5pt, inner sep=\BCQ@qrlogoborder\textwidth] at (qr.center) {%
\includegraphics[width=\BCQ@qrlogoscale\textwidth]{\Xphoto}%
};
\end{tikzpicture}%
}{%
% No photo defined, just show QR code
\qrcode[level=\BCQ@qreclevel,version=0,height=\textwidth]{\vcard}%
}
\else
% Standard QR code without overlay
\qrcode[level=\BCQ@qreclevel,version=0,height=\textwidth]{\vcard}
\fi
\end{minipage}
}
}
@@ -240,17 +277,28 @@ END:VCARD^^J}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% insertname - insert the name on the top
\newcommand\insertname{
%\frame
%\begin{minipage}{\textwidth}
%\pbox[t]{0.9\textwidth}
{\bfseries
\cond{name}
\cond{Xadditionalnames}
}
%\end{minipage}
% typeset name into box for measurement
\sbox{\BCQ@namebox}{\bfseries\cond{name}\ifexists{Xadditionalnames}{\ifcsempty{name}{} { }\Xadditionalnames}}%
\ifcsdef{Xphoto}{% photo defined
\ifBCQ@photoinqr
% Photo in QR code: show name only, same structure as no-photo case
{\bfseries\usebox{\BCQ@namebox}}%
\else
% Photo next to name: two-column layout (photo | name)
\begin{minipage}{\textwidth}
\begin{minipage}[c]{0.30\textwidth}% photo column (increased from 0.22)
\includegraphics[height=\dimexpr\ht\BCQ@namebox+\dp\BCQ@namebox\relax]{\Xphoto}% scaled to name height
\end{minipage}
\hfill
\begin{minipage}[c]{0.67\textwidth}% name column (adjusted from 0.75)
\ifBCQ@rightalign\raggedleft\fi% apply alignment
{\bfseries\usebox{\BCQ@namebox}}%
\end{minipage}
\end{minipage}
\fi
}{% no photo: just name
{\bfseries\usebox{\BCQ@namebox}}%
}
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View File

@@ -1,5 +1,5 @@
#AC_PREREQ([2.69])
AC_INIT([businesscard-qrcode], [1.1], [marc@waeckerlin.org])
AC_INIT([businesscard-qrcode], [1.2], [marc@waeckerlin.org])
AM_INIT_AUTOMAKE([1.9 tar-pax])
AC_ARG_WITH([latexdir],
@@ -15,7 +15,7 @@ AC_CHECK_PROG([xelatex])
EXAMPLES="example john-doe-hongkong \
peter-muster-example-company-zuerich special-papersize \
texstudio_d30266"
texstudio_d30266 photo-example photo-in-qr-example"
AC_SUBST(EXAMPLES)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +1,8 @@
\documentclass[paperheight=5.4cm,paperwidth=10.4cm,
contentheight=5cm,contentwidth=10cm,
cutdist=2]
cutdist=2,
textwidth=0.6,
qrwidth=.35]
{businesscard-qrcode}
\givennames{Gretchen\ Frieda}
@@ -13,4 +15,4 @@
\begin{document}
\drawcard
\end{document}
\end{document}

Binary file not shown.

View File

@@ -3,10 +3,10 @@ AUTOMAKE_OPTIONS = foreign
SUBDIRS = examples screenshots
dist_latex_DATA = @PACKAGE_NAME@.cls
dist_doc_DATA = README.md @PACKAGE_NAME@.pdf
dist_doc_DATA = README.md #@PACKAGE_NAME@.pdf
@PACKAGE_NAME@.pdf: README.md
pandoc --toc --latex-engine=xelatex -o $@ $<
#@PACKAGE_NAME@.pdf: README.md
# pandoc --toc --latex-engine=xelatex -o $@ $<
CLEANFILES = @PACKAGE_NAME@.pdf
#CLEANFILES = @PACKAGE_NAME@.pdf
MAINTAINERCLEANFILES = makefile.in aclocal.m4 configure install-sh missing

19
release.sh Executable file
View File

@@ -0,0 +1,19 @@
#!/bin/bash -ex
./autogen.sh
./configure --prefix=/ --docdir=/ --with-latexdir=/
make all
file=$(sed -n 's/AC_INIT(\[\(.*\)\], \[\(.*\)\], .*/\1/p' configure.ac)
version=$(sed -n 's/AC_INIT(\[\(.*\)\], \[\(.*\)\], .*/\2/p' configure.ac)
DESTDIR=$(pwd)/${file} make install
tar czf ${file}-${version}-ctan.tar.gz ${file}
DESTDIR=$(pwd)/${file} make uninstall maintainer-clean
for d in $(sed -n 's/SUBDIRS *= *//p' makefile.am); do
rmdir ${file}/${d}
done
rmdir ${file}
set +x
echo "====================================================================="
echo "File is ready for CTAN upload:"
echo "${file}-${version}-ctan.tar.gz"
echo "====================================================================="

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 77 KiB

View File

@@ -1,7 +1,3 @@
EXAMPLES = example john-doe-hongkong \
peter-muster-example-company-zuerich special-papersize \
texstudio_d30266
screenshotdir=${docdir}/screenshots
dist_screenshot_DATA=$(EXAMPLES:%=%.jpg)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 77 KiB