Files
businesscard-qrcode/businesscard-qrcode.cls

365 lines
13 KiB
OpenEdge ABL
Raw Normal View History

2018-08-15 23:21:17 +02:00
% Author: Marc Wäckerlin
% License: LGPL
2018-08-12 00:59:57 +02:00
\NeedsTeXFormat{LaTeX2e}
2018-08-15 23:21:17 +02:00
\ProvidesClass{businesscard-qrcode}[2018/08/15 version 1.2 ready for ctan]
2018-08-12 00:59:57 +02:00
2018-08-13 15:24:59 +02:00
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% option evaluation
2018-08-12 00:59:57 +02:00
\RequirePackage{kvoptions}
\SetupKeyvalOptions{
family=BCQ,
prefix=BCQ@
}
\DeclareStringOption[89mm]{paperwidth}
\DeclareStringOption[59mm]{paperheight}
\DeclareStringOption[85mm]{contentwidth}
\DeclareStringOption[55mm]{contentheight}
\DeclareStringOption[8pt]{fontsize}
\DeclareStringOption[2mm]{padding}
\DeclareStringOption[2]{cutdist}
\DeclareStringOption[1]{cutlen}
\DeclareStringOption[0.50]{textwidth}
2018-08-12 00:59:57 +02:00
\DeclareStringOption[0.40]{qrwidth}
2025-11-08 17:09:38 +01:00
\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
2018-08-12 00:59:57 +02:00
\DeclareStringOption[de]{lang}
2025-11-08 17:09:38 +01:00
\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}
2018-08-12 00:59:57 +02:00
\DeclareBoolOption[true]{hint}
\DeclareComplementaryOption{nohint}{hint}
\DeclareBoolOption[true]{icon}
\DeclareComplementaryOption{noicon}{icon}
\DeclareBoolOption[true]{rightalign}
\DeclareComplementaryOption{leftalign}{rightalign}
\DeclareBoolOption[true]{iconleft}
\DeclareComplementaryOption{iconright}{iconleft}
\DeclareBoolOption[true]{fill}
\DeclareComplementaryOption{nofill}{fill}
\DeclareBoolOption[true]{qrfirst}
\DeclareComplementaryOption{textfirst}{qrfirst}
\DeclareBoolOption[true]{https}
\DeclareComplementaryOption{www}{https}
2018-08-12 00:59:57 +02:00
\DeclareDefaultOption{\@unknownoptionerror}
\ProcessKeyvalOptions*
2018-08-13 15:24:59 +02:00
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% definitions from the options
2018-08-12 00:59:57 +02:00
\def\content{paperwidth=\BCQ@contentwidth,paperheight=\BCQ@contentheight}
\def\papersize{width=\BCQ@paperwidth,height=\BCQ@paperheight}
\def\padding{\BCQ@padding} % padding around the content
\def\border{\BCQ@cutdist} % distance between start of cutmark and content in mm
\def\cutlen{\BCQ@cutlen} % length of ct marks in mm
\def\textpercents{\BCQ@textwidth} % size of text part 0..1
\def\imagepercents{\BCQ@qrwidth} % size of qrcode image part 0..1
\def\lang{\BCQ@lang}
\def\protdisplay{\ifBCQ@https https://\else www.\fi}
\def\protprefix{\ifBCQ@https https://\fi}
\ifBCQ@address\def\printaddress{}\fi
2018-08-12 00:59:57 +02:00
2018-08-13 15:24:59 +02:00
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% global dependencies and settings
\LoadClass[\BCQ@fontsize]{extarticle}
\RequirePackage{marvosym}
\RequirePackage{fontawesome}
\RequirePackage[final]{qrcode}
\RequirePackage{etoolbox}
\RequirePackage{DejaVuSans}
\RequirePackage[T1]{fontenc}
\RequirePackage{wrapfig}
2025-11-08 17:09:38 +01:00
\RequirePackage{graphicx} % needed for optional photo
\RequirePackage{tikz} % needed for logo overlay in QR code
2018-08-13 15:24:59 +02:00
\RequirePackage[\content,top=\padding,left=\padding,right=\padding,bottom=\padding]{geometry}
%\RequirePackage{pbox}
\RequirePackage{varwidth}
\RequirePackage{calc}
\pagestyle{empty}
\setlength{\parindent}{0pt}
\renewcommand*\familydefault{\sfdefault}
\setlength{\fboxsep}{0pt}
2018-08-14 16:38:43 +02:00
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% data registration
\newcommand\registerData[1]{
\expandafter\newcommand\csname #1\endcsname[1]{
\expandafter\def\csname X#1\endcsname{##1}
}
}
\registerData{type}
\registerData{givennames}
\registerData{familynames}
\registerData{honoricprefix}
\registerData{honoricsuffix}
\registerData{additionalnames}
\registerData{pobox}
\registerData{extaddr}
\registerData{street}
\registerData{city}
\registerData{region}
\registerData{zip}
\registerData{country}
\registerData{phone}
\registerData{email}
\registerData{jabber}
\registerData{matrixorg}
\registerData{cloud}
\registerData{homepage}
\registerData{wordpress}
\registerData{drupal}
\registerData{joomla}
\registerData{wikipedia}
\registerData{link}
\registerData{world}
\registerData{git}
\registerData{gitea}
\registerData{github}
\registerData{facebook}
\registerData{twitter}
\registerData{youtube}
\registerData{google}
\registerData{pgpurl}
\registerData{pgpfingerprint}
2025-11-08 17:09:38 +01:00
\registerData{photo} % path to photo file for display next to name
2018-08-14 16:38:43 +02:00
2018-08-13 15:24:59 +02:00
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% auxiliary commands
\newcommand\enforceright{\leftskip0pt plus 1fill\rightskip0pt}
2018-08-12 00:59:57 +02:00
\newcommand\exec[1]{\csname #1\endcsname}
\newcommand\insa[3][]{\ifcsdef{#2}{
\ifBCQ@iconleft
\ifBCQ@icon\parbox{1em}{\centering\exec{#3}}\ \fi\ifBCQ@hint{\tiny#1}\fi\ifBCQ@fill\hfill\fi\exec{#2}
\else
\ifBCQ@hint{\tiny#1\ }\fi\exec{#2}\ifBCQ@fill\hfill\fi\ifBCQ@icon\ \parbox{1em}{\centering\exec{#3}}\fi
\fi
}{}}
2018-08-12 00:59:57 +02:00
\newcommand\ifexists[2]{\ifcsdef{#1}{#2}{}}
\newcommand\ifboth[3]{\ifcsdef{#1}{\ifcsdef{#2}{#3}{}}{}}
\newcommand\ifany[3]{\ifcsdef{#1}{#3}{\ifcsdef{#2}{#3}{}}}
\newcommand\cond[1]{\ifcsdef{#1}{\exec{#1}}{}}
2018-08-14 16:38:43 +02:00
\newcommand\heightscale{\dimexpr\textheight-\ifcsempty{name}{0em}{2em}-\ifcsdef{Xpgpfingerprint}{2em}{0em}-\ifcsdef{Xadditionalnames}{2em}{0em}\relax}
2018-08-13 15:24:59 +02:00
2018-08-12 00:59:57 +02:00
2018-08-13 15:24:59 +02:00
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2018-08-14 16:38:43 +02:00
% 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}}
2018-08-12 00:59:57 +02:00
2025-11-08 17:09:38 +01:00
% internal box for measuring name height when photo is present
\newsavebox{\BCQ@namebox}
2018-08-13 15:24:59 +02:00
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% vcard - the content of the vcard
2025-11-08 17:09:38 +01:00
% 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}
2018-08-12 00:59:57 +02:00
2018-08-13 15:24:59 +02:00
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% address - the address as shown in the textbox
2018-08-12 00:59:57 +02:00
\newcommand\address{
2018-08-14 16:38:43 +02:00
\cond{Xpobox}
2018-08-14 16:38:43 +02:00
\cond{Xextaddr}
2018-08-14 16:38:43 +02:00
\cond{Xstreet}
2018-08-14 16:38:43 +02:00
\cond{Xzip} \cond{Xcity}
2018-08-14 16:38:43 +02:00
\cond{Xregion} \cond{Xcountry}
}
2018-08-13 15:24:59 +02:00
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% inserttext - assemble the textbox
\newcommand\inserttext{
%\frame
{
\begin{minipage}[c][\heightscale][c]{\textpercents\textwidth}
\ifBCQ@rightalign\begin{flushright}\fi
\ifexists{printaddress}{
\ifBCQ@iconleft
\ifBCQ@icon\parbox[c]{1em}{\faMapMarker}\ \ifBCQ@fill\hfill\fi\fi
%\frame{
%\pbox[t]{\dimexpr\textwidth-2em\relax}{
\begin{varwidth}{\dimexpr\textwidth-2em\relax}
\ifBCQ@rightalign\enforceright\fi\address
\end{varwidth}
%}
%}
\else
%\pbox[t]{\dimexpr\textwidth-2em\relax}{
\begin{varwidth}{\dimexpr\textwidth-2em\relax}
\ifBCQ@rightalign\enforceright\fi\address
\end{varwidth}
%}
\ifBCQ@icon\ifBCQ@fill\hfill\fi\ \parbox[c]{1em}{\faMapMarker}\fi
\fi
\vspace{1em}
}
2018-08-14 16:38:43 +02:00
\insa[tel:]{Xphone}{faMobile}\insa[mailto:]{Xemail}{Email}\insa[xmpp]{Xjabber}{faCommentsO}\insa[matrix.org]{Xmatrixorg}{faCommentsO}\insa[nextcloud-id]{Xcloud}{faCloud}\insa[\protdisplay]{Xhomepage}{faHome}\insa[\protdisplay]{Xwordpress}{faWordpress}\insa[\protdisplay]{Xdrupal}{faDrupal}\insa[\protdisplay]{Xjoomla}{faJoomla}\insa[{\protprefix}\lang.wikipedia.org/wiki/]{Xwikipedia}{faWikipediaW}\insa[\protdisplay]{Xlink}{faLink}\insa[\protdisplay]{Xworld}{faGlobe}\insa[\protdisplay]{Xgit}{faGit}\insa[\protdisplay]{Xgitea}{faGithubAlt}\insa[{\protprefix}github.com/]{Xgithub}{faGithub}\insa[{\protprefix}facebook.com/]{Xfacebook}{faFacebook}\insa[{\protprefix}twitter.com/]{Xtwitter}{faTwitter}\insa[{\protprefix}youtube.com/user/]{Xyoutube}{faYoutube}\insa[{\protprefix}plus.google.com/+]{Xgoogle}{faGooglePlus}
2018-08-12 00:59:57 +02:00
2018-08-13 15:24:59 +02:00
\ifBCQ@rightalign\end{flushright}\fi
\end{minipage}
}
}
2018-08-12 00:59:57 +02:00
2018-08-13 15:24:59 +02:00
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% insertqrcode - insert the qr-code
\newcommand\insertqrcode{
2018-08-13 15:24:59 +02:00
%\frame
{
\begin{minipage}[c][\heightscale][c]{\imagepercents\textwidth}
2025-11-08 17:09:38 +01:00
\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
2018-08-13 15:24:59 +02:00
\end{minipage}
}
}
2018-08-13 15:24:59 +02:00
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% insertname - insert the name on the top
\newcommand\insertname{
2025-11-08 17:09:38 +01:00
% 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}}%
}
2018-08-13 15:24:59 +02:00
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% drawcard - assemble all blocks into the visiting card
\newcommand\drawcard{
\ifBCQ@rightalign\begin{flushright}\fi
\insertname
\noindent\makebox[\linewidth]{\rule{\paperwidth}{0.4pt}} % horizontal line
\vfill
\ifBCQ@qrfirst
\insertqrcode
2018-08-13 15:24:59 +02:00
\hfill
\inserttext
2018-08-13 15:24:59 +02:00
\else
\inserttext
2018-08-13 15:24:59 +02:00
\hfill
\insertqrcode
\fi
2018-08-14 16:38:43 +02:00
\ifexists{Xpgpfingerprint}{
2018-08-13 15:24:59 +02:00
\vfill
\ifBCQ@iconleft
2018-08-14 16:38:43 +02:00
\faLock\ifBCQ@hint{\tiny\ pgp}\fi\hfill{\small\Xpgpfingerprint}
2018-08-13 15:24:59 +02:00
\else
2018-08-14 16:38:43 +02:00
{\small\Xpgpfingerprint}\hfill\ifBCQ@hint{\tiny{pgp\ }}\fi\faLock
2018-08-13 15:24:59 +02:00
\fi
}
\ifBCQ@rightalign\end{flushright}\fi
}
2018-08-13 15:24:59 +02:00
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% cut / crop marks
2018-08-12 00:59:57 +02:00
\RequirePackage[\papersize,noinfo,center,pdftex]{crop}
\newcommand\tl{
\begin{picture}(0,0)
\thinlines\unitlength1mm
\put(-\border,0){\line(1,0){\cutlen}}
\put(0,\border){\line(0,-1){\cutlen}}
2018-08-12 00:59:57 +02:00
\end{picture}
}
\newcommand\tr{
\begin{picture}(0,0)
\thinlines\unitlength1mm
\put(\border,0){\line(-1,0){\cutlen}}
\put(0,\border){\line(0,-1){\cutlen}}
\end{picture}
}
\newcommand\bl{
\begin{picture}(0,0)
\thinlines\unitlength1mm
\put(-\border,0){\line(1,0){\cutlen}}
\put(0,-\border){\line(0,1){\cutlen}}
\end{picture}
}
\newcommand\br{
\begin{picture}(0,0)
\thinlines\unitlength1mm
\put(\border,0){\line(-1,0){\cutlen}}
\put(0,-\border){\line(0,1){\cutlen}}
\end{picture}
}
\cropdef[]\tl\tr\bl\br{cut}
\crop[cut]