# pgfkeys - is choice + value pass to graphicx

I'm trying to implement a macro which is a wrapper over the graphicx \includegraphics command (because I'd like to pass the parameters for cropping in a key-value fashion). While doing so, at first I introduced a key to set the width (option of includegraphics). Later on I needed the same macro where I set the scale and height option.

Now I have three macros doing basically the same which I'd like to merge into one. I saw there is the .is choice key which I tried using, but there I fail to additionally pass a value and later on passing the key-value pair to graphicx. But see my try:

\documentclass{article}

\usepackage{pgfkeys}
\usepackage{graphicx}

% \pgfkeys{
%   /testing/.cd,
%   scaling/.is choice,
%   scaling/width/.code=\def\testing{width=#1},
%   scaling/height/.code=\def\testing{height=#1},
%   scaling/scale/.code=\def\testing{scale=#1},
% }

\pgfkeys{
/graphicx/.cd,
left/.initial=0cm, left/.default=0cm,
% left/.store in=\owngraphicx@left,
right/.initial=0cm, right/.default=0cm,
top/.initial=0cm, top/.default=0cm,
bottom/.initial=0cm, bottom/.default=0cm,
page/.initial={1}, page/.default={1},
width/.initial=\linewidth, width/.default=\linewidth,
}
\newcommand{\slidesS}[2][]{%
\pgfkeys{/graphicx/.cd,left,right,top,bottom,page,#1}%
\fbox{%
\includegraphics[%
page=\pgfkeysvalueof{/graphicx/page},%
scale=\pgfkeysvalueof{/graphicx/width},%
clip,%
trim=\pgfkeysvalueof{/graphicx/left} \pgfkeysvalueof{/graphicx/bottom} \pgfkeysvalueof{/graphicx/right} \pgfkeysvalueof{/graphicx/top},%
]{#2}%
}%
}
\newcommand{\slidesW}[2][]{%
\pgfkeys{/graphicx/.cd,left,right,top,bottom,page,#1}%
\fbox{%
\includegraphics[%
page=\pgfkeysvalueof{/graphicx/page},%
width=\pgfkeysvalueof{/graphicx/width},%
clip,%
trim=\pgfkeysvalueof{/graphicx/left} \pgfkeysvalueof{/graphicx/bottom} \pgfkeysvalueof{/graphicx/right} \pgfkeysvalueof{/graphicx/top},%
]{#2}%
}%
}
\newcommand{\slidesH}[2][]{%
\pgfkeys{/graphicx/.cd,left,right,top,bottom,page,#1}%
\fbox{%
\includegraphics[%
page=\pgfkeysvalueof{/graphicx/page},%
height=\pgfkeysvalueof{/graphicx/width},%
clip,%
trim=\pgfkeysvalueof{/graphicx/left} \pgfkeysvalueof{/graphicx/bottom} \pgfkeysvalueof{/graphicx/right} \pgfkeysvalueof{/graphicx/top},%
]{#2}%
}%
}
\newcommand{\inputFile}{image.png}

\begin{document}

% \pgfkeys{
%   /testing/.cd,
%   scaling={height=10cm},
% }

\slidesS[left=1.8982075471698134cm, right=10cm, bottom=1cm, top=5.75cm, width=1]{\inputFile}
% \includegraphics[\testing]{\inputPdfFile}

\end{document}


the commented things are me trying to capture key+value of the is choice.

Any ideas on how to properly do this (using \testing in \includegraphics does not work, since width=10cm is perceived as an option, not as key=value I think)

• There's always the "hammer" solution of write code that generate code. Not really recommended though because... TeX code is kind of ugly most of the time... May 15, 2022 at 14:07

This answer is similar to the one using expkv-cs, but this time with pgfkeys. The idea remains the same: Only define the four keys left, bottom, right, top, and forward all unknown keys to \includegraphics. Just the forwarding part is a bit more complicated for pgfkeys. This answer doesn't incorporate the omission of clip and trim if they aren't necessary (if you want this I could build it in pgfkeys as well, just leave a comment).

\documentclass{article}

\usepackage{graphicx}
\usepackage{pgfkeys}

\makeatletter
\pgfqkeys{/graphicx}
{
.is family
,left/.initial=0pt, left/.default=0pt
,bottom/.initial=0pt, bottom/.default=0pt
,right/.initial=0pt, right/.default=0pt
,top/.initial=0pt, top/.default=0pt
,.unknown/.code=%
\edef\graphicwrap@unknown
{%
\unexpanded\expandafter{\graphicwrap@unknown},%
\pgfkeyscurrentname
\ifx\pgfkeysnovalue#1\else={\unexpanded{#1}}\fi
}%
}
\newcommand*\graphicwrap@unknown{}
\newcommand\graphicwrap[2][]
{%
\begingroup
\pgfqkeys{/graphicx}{#1}%
\expandafter\graphicwrap@\expandafter{\graphicwrap@unknown}{#2}%
\endgroup
}
\newcommand\graphicwrap@[2]
{%
\includegraphics
[%
clip
,trim=\pgfkeysvalueof{/graphicx/left}
\pgfkeysvalueof{/graphicx/bottom}
\pgfkeysvalueof{/graphicx/right}
\pgfkeysvalueof{/graphicx/top}
,#1%
]%
{#2}%
}
\makeatother

\begin{document}
\graphicwrap[width=5cm, left=2cm]{example-image-duck}
\graphicwrap[scale=0.5, left=2cm]{example-image-duck}

% of course old names still work as well
\graphicwrap[clip, trim=2cm 0 0 0]{example-image-duck}
\end{document}


A version that only uses clip and trim if one of the keys were used (without a second layer of keys, instead by letting the keys do more than just storing their value):

\documentclass{article}

\usepackage{graphicx}
\usepackage{pgfkeys}

\makeatletter
\pgfqkeys{/graphicx}
{
.is family
,left/.initial=0pt, left/.default=0pt
,bottom/.initial=0pt, bottom/.default=0pt
,right/.initial=0pt, right/.default=0pt
,top/.initial=0pt, top/.default=0pt
,left/.code=\pgfkeyssetvalue{/graphicx/left}{#1}\let\graphicwrap@do\graphicwrap@clip
,bottom/.code=\pgfkeyssetvalue{/graphicx/bottom}{#1}\let\graphicwrap@do\graphicwrap@clip
,right/.code=\pgfkeyssetvalue{/graphicx/right}{#1}\let\graphicwrap@do\graphicwrap@clip
,top/.code=\pgfkeyssetvalue{/graphicx/top}{#1}\let\graphicwrap@do\graphicwrap@clip
,.unknown/.code=%
\edef\graphicwrap@unknown
{%
\unexpanded\expandafter{\graphicwrap@unknown},%
\pgfkeyscurrentname
\ifx\pgfkeysnovalue#1\else={\unexpanded{#1}}\fi
}%
}
\newcommand\graphicwrap@clip[1]
{%
\includegraphics
[%
clip
,trim=\pgfkeysvalueof{/graphicx/left}
\pgfkeysvalueof{/graphicx/bottom}
\pgfkeysvalueof{/graphicx/right}
\pgfkeysvalueof{/graphicx/top}
,#1%
]%
}
\newcommand\graphicwrap@noclip[1]{\includegraphics[#1]}
\let\graphicwrap@do\graphicwrap@noclip
\newcommand*\graphicwrap@unknown{}
\newcommand\graphicwrap[2][]
{%
\begingroup
\pgfqkeys{/graphicx}{#1}%
\expandafter\graphicwrap@do\expandafter{\graphicwrap@unknown}{#2}%
\endgroup
}
\makeatother

\begin{document}
\graphicwrap[width=5cm, left=2cm]{example-image-duck}
\graphicwrap[scale=0.5, left=2cm]{example-image-duck}

% of course old names still work as well
\graphicwrap[clip, trim=2cm 0 0 0]{example-image-duck}
\end{document}

• Thanks for your help (even implementing two solutions). The expkv-cs solution looks pretty nice (I'm gonna definitely check out your package). But for now, I'd rather stick with pgfkeys (it's just what I already know). I don't think I'm gonna need the check for clip and trim (just passing 0cm to trim should suffice). But if it's not too much work I'd be interested how you do that ;) May 15, 2022 at 14:49
• @atticus you don't have to switch. expkv-cs can't do as much as pgfkeys can (the approach taken has serious limitations on versatility of keys, though most things can still be built in one way or the other). The approach for the detection is more or less the same as in the other answer, by adding an extra layer of keys. Another viable solution (for both) would be to detect whether all are either equivalent to 0 or equivalent to 0pt (but writing that code would be more complicated). May 15, 2022 at 14:55
• @atticus alternative version using clip only if necessary posted. Please note that there was a small bug in the .unknown handler's code, the check for a key without a value was wrong. May 15, 2022 at 15:11
• Alright. Thanks again :+1 May 15, 2022 at 15:20

The following uses expkv-cs, because setting up a small macro taking only a handful of keys is much easier there, imho (well, I'm the package author, so maybe not-so-humble, and pretty biased). Here it could be simpler by omitting the check whether clip and trim are necessary at all.

expkv-cs supports the special ... key, meaning all unknown keys are collected there. This allows to easily forward unknown keys to graphicx. Drawback of this approach: If any of the left, bottom, right, or top keys are used they are put first in the key=value list of \includegraphics, so the order of keys isn't identical (which shouldn't be much of a problem).

\documentclass{article}

\usepackage{graphicx}

\usepackage{expkv-cs} % <- sorry, not pgfkeys here
\makeatletter
% optional argument grabbing, expkv-cs uses mandatory arguments
\newcommand\graphicwrap[1][]{\graphicwrap@{#1}}
% internal keys and their initial values here
\ekvcSplitAndForward\graphicwrap@\graphicwrap@do
{
internal-left   = 0pt      % #1
,internal-bottom = 0pt      % #2
,internal-right  = 0pt      % #3
,internal-top    = 0pt      % #4
,internal-any    = \@gobble % #5: used as a flag that clip is needed
,...                        % #6: unknown keys
}
% additional keys (the front-facing ones) and what they do
\ekvcSecondaryKeys\graphicwrap@
{
meta left   = {internal-any=\@firstofone, internal-left   = {#1}}
,meta bottom = {internal-any=\@firstofone, internal-bottom = {#1}}
,meta right  = {internal-any=\@firstofone, internal-right  = {#1}}
,meta top    = {internal-any=\@firstofone, internal-top    = {#1}}
}
% output macro
\newcommand\graphicwrap@do[7]
{\expandafter\includegraphics\expandafter[#5{clip, trim=#1 #2 #3 #4}, #6]{#7}}
\makeatother

\begin{document}
\graphicwrap[width=5cm, left=2cm]{example-image-duck}
\graphicwrap[scale=0.5, left=2cm]{example-image-duck}

% of course old names still work as well
\graphicwrap[clip, trim=2cm 0 0 0]{example-image-duck}
\end{document}


For comparison, the following would be the variant without checking whether clip and trim are required:

\documentclass{article}

\usepackage{graphicx}

\usepackage{expkv-cs} % <- sorry, not pgfkeys here
\makeatletter
% optional argument grabbing, expkv-cs uses mandatory arguments
\newcommand\graphicwrap[1][]{\graphicwrap@{#1}}
% keys and their initial values here
\ekvcSplitAndForward\graphicwrap@\graphicwrap@do
{
left   = 0pt      % #1
,bottom = 0pt      % #2
,right  = 0pt      % #3
,top    = 0pt      % #4
,...               % #5: unknown keys
}
% output macro
\newcommand\graphicwrap@do[6]{\includegraphics[clip, trim=#1 #2 #3 #4, #5]{#6}}
\makeatother

\begin{document}
\graphicwrap[width=5cm, left=2cm]{example-image-duck}
\graphicwrap[scale=0.5, left=2cm]{example-image-duck}

% of course old names still work as well
\graphicwrap[clip, trim=2cm 0 0 0]{example-image-duck}
\end{document}