7

How do I properly expand a macro \myviewport so that it is parsed as the viewport=0pt 10pt 100pt 200pt argument of \includegraphics[clip, \myviewport]{somepdf.pdf}?

\documentclass{article}
\usepackage{graphicx}
\begin{document}
\newcommand*{\aport}{45.29pt 86.28pt 235pt 187pt}
\includegraphics[clip, viewport={\aport}]{somepdf.pdf}
\end{document}

Error: ! Argument of \Gread@parse@vp has an extra }. My simple idea was based on \includegraphics optional argument not resolved if macro

What does work is giving individual macros for each viewport value:

\documentclass{article}
\usepackage{graphicx}
\usepackage{calc}
\begin{document}
\newlength{\Xoffset}
\setlength{\Xoffset}{35pt}
\newlength{\myl}
\setlength{\myl}{235pt - \Xoffset}
\newcommand*{\lux}{200pt}
\includegraphics[clip, viewport=10pt 50pt \lux{} \the\myl{}, page=3]{somepdf.pdf}
\end{document}

Note that \myl is a length and \lux is a "string".

In the long run I'd like to automate the cropping of some pdf files (which have Media Boxes not starting at 0 0):

\documentclass{article}
\usepackage{graphicx}
\usepackage{calc}

\newlength{\Xoffset}
\newlength{\Yoffset}
\newcommand*{\setpdfoffset}[2]{
    \setlength{\Xoffset}{#1}
    \setlength{\Yoffset}{#2}
}
\newcommand*{\aviewport}{} %set an empty default
\setpdfoffset{0pt}{0pt} %set a default

\newcommand*{\setviewport}[4]{
    \newlength{\mylux}\setlength{\mylux}{#1-\Xoffset}
    \newlength{\myluy}\setlength{\myluy}{#2-\Yoffset}
    \newlength{\myrox}\setlength{\myrox}{#3-\Xoffset}
    \newlength{\myroy}\setlength{\myroy}{#4-\Yoffset}
    \renewcommand*{\aviewport}{viewport={\the\mylux} {\the\myluy}  {\the\myrox}  {\the\myroy}}
}
\begin{document}

%in Windows command line:
% pdfinfo -box somepdf.pdf |awk "/MediaBox/ {print; print \"Xoffset=\"$2 \" Yoffset=\"$3\"\n\"; print \"\\newlength{\\Xoffset}\\setlength{\\Xoffset}{\"$2\"pt} \\newlength{\\Yoffset}\\setlength{\\Yoffset}{\"$3\"pt}\"}"

\setpdfoffset{35.29pt}{36.28pt}
\setviewport{45.29pt}{86.28pt}{235pt}{187pt}
\emph{\aviewport}
\fbox{\includegraphics[clip, \aviewport{}, page=2]{somepdf}
\end{document}

Error: keyval: viewport={10.0pt} {50.0pt} {199.71pt} {150.72pt} undefined. ...lip, \aviewport]{somepdf}}

XeLaTeX is my first choice, but pdfLaTeX doesn't like this code either.

Bonus: Get the X and Y Offset values for \setpdfoffset by using \input{|"pdfinfo -box somepdf.pdf |awk ..."}, but this is an escaping question and not an expanding.

1
  • 1
    Note that there are two subtly different issues here. Hiding the key=value part in a macro breaks in general with keyval methods as the parser for keys doesn't see the right name: that's general and not dependent on the key involved. Here, you have a second issue as viewport is looking for four values separated by spaces, and again hiding stuff in a macro confuses the internals.
    – Joseph Wright
    Jan 7, 2015 at 7:02

4 Answers 4

4

Here are a couple of ways:

enter image description here

\documentclass{article}
\usepackage{graphicx}
\newcommand{\myviewport}{viewport=0pt 10pt 200pt 50pt}
\begin{document}
\includegraphics[clip, viewport=0pt 10pt 200pt 50pt]{example-image}

\begingroup\edef\x{\endgroup\noexpand\includegraphics[clip, \myviewport]{example-image}}\x

{\let\includegraphics\relax\xdef\x{\includegraphics[clip, \myviewport]{example-image}}}\x
\end{document}

The first shows what the expected output should resemble.

The second defines an expanded version \x of \includegraphics[..]{...} where \includegraphics is left unexpanded. It's also local as \x is discarded after it is used. It is very similar to

\edef\x{\noexpand\includegraphics[clip, \myviewport]{example-image}}\x

without the localization of \x.

The third defines a similar \x which is now global (also expanded, hence \xdef). However, in order to avoid the expansion of \includegraphics it is set to \relax within a group. That prevents it from being expanded with the call to \xdef, and it also survives the group. In particular \show\x reveals

> \x=macro:
->\includegraphics [clip, viewport=0pt 10pt 200pt 50pt]{example-image}.

The viewport, trim and bb (bounding box) keys are special, as they require a specific value input. The input has to be in the expanded form #1 #2 #3 #4 and is recognized using TeX's argument text or \def. So, if the match is not exact, incorrect arguments may be assigned to incorrect elements, causing the problem you're experiencing.

Other macros may be used in a macro-form, to an extent. As a minimum, you have to use an explicit =:

\documentclass{article}

\usepackage{graphicx}

\begin{document}

\includegraphics[width=100pt]{example-image}

\newcommand{\mywidth}{width}%
\newcommand{\widthdimen}{100pt}%
\includegraphics[\mywidth=\widthdimen]{example-image}

%\newcommand{\mywidthkeyval}{width=100pt}%
%\includegraphics[\mywidthkeyval]{example-image}% Does not work

\includegraphics[clip, viewport=10pt 10pt 50pt 50pt]{example-image}

\newcommand{\myviewport}{viewport}%
\newcommand{\viewportvals}{10pt 10pt 50pt 50pt}%
\includegraphics[clip, \myviewport=10pt 10pt 50pt 50pt]{example-image}
%\includegraphics[clip, \myviewport=\viewportvals]{example-image}% Does not work

\end{document}
2
  • 1
    Perhaps you might explain a bit why expansion of both the key=value syntax and here the value itself are needed: as I've commented on the question, there are actually two related but distinct issues here as one is about keyval in general and one is tied to the format of the viewport value.
    – Joseph Wright
    Jan 7, 2015 at 7:04
  • All two versions of @Werner work. I'll stick with #1 for three reasons: a) it looks more like \includegraphics(compared to @steven-b-segletes); b) the macros for expanding it look a lot more readable and c) less prone to introduce stray spaces. I'm now using three lines to define my own includegraphics 1: newcommand*{\mygraphics}[2][]{% percent sign removes following whitespaces(e.g. generated from new lines), else they are in the document. 2: \begingroup\edef\x{\endgroup\noexpand\includegraphics[clip=true, #1]{#2}}\x 3:} usage \fbox{\mygraphics[\aviewport, page=1]{example-image}}
    – nks
    Jan 7, 2015 at 14:19
6

The four values for options viewport or trim are identified by the separating spaces. The parser, defined for these options by package graphicx expects explicit spaces and fails, if the spaces are hidden inside a macro.

The following example redefines option viewport (in the same way option trim can be redefined) to expand its argument before passing the expanded value to the original option code:

\documentclass{article}
\usepackage{graphicx}
\makeatletter
% Remember original option `viewport` as `viewport@org`
\let\KV@Gin@viewport@org\KV@Gin@viewport
% Redefine option `viewport` to expand the argument and
% pass it to option `viewport@org`.
\define@key{Gin}{viewport}{%
  \begingroup
  \edef\x{\endgroup
    \noexpand\setkeys{Gin}{viewport@org={#1}}%
  }\x
}
\makeatother

\begin{document}
  \newcommand*{\aport}{45.29pt 86.28pt 235pt 187pt}
  \includegraphics[clip, viewport=\aport]{tiger}
\end{document}

The equal sign and the comma are syntax characters in LaTeX's key value lists (package keyval and friends). The key value parser will fail, if these characters are hidden inside macros. But it is possible to define a new option for graphicx with an argument that is expanded and executed again as key value list:

\documentclass{article}
\usepackage{graphicx}
\makeatletter
\define@key{Gin}{expandoptions}{%
  \begingroup
  \edef\x{\endgroup
    \noexpand\setkeys{Gin}{#1}%
  }\x
}
\makeatother

\begin{document}
  \newcommand*{\viewaport}{viewport=45.29pt 86.28pt 235pt 187pt, clip}
  \includegraphics[expandoptions=\viewaport]{tiger}
\end{document}
3
  • Two questions: 1. this solution appears to work for me as long as the command (\aport in this case) does not take parameters, I have not succeeded in making it work with parameters... Is it possible to define something like an eviewport key capable of expanding a command with its parameters (e.g., eviewport=\icon{5}, where the image has a collection of icons)? 2. Would it be possible to to the same, with a more readable syntax, using latex3?
    – callegar
    Apr 23 at 16:09
  • To be more accurate: it fails when you have a command with optional parameters.
    – callegar
    Apr 23 at 16:34
  • Solved my own issue 1. I need \NewExpandableDocumentCommand.
    – callegar
    Apr 23 at 16:55
3

You can define a new key for \includegraphics, I'll call it xviewport.

\documentclass{article}
\usepackage{graphicx}
\usepackage{calc}

\makeatletter
\define@key{Gin}{xviewport}[]{%
  \begingroup\edef\x{%
    \endgroup\noexpand\setkeys{Gin}{viewport=\x@viewport}%
  }\x
}

\newlength{\Xoffset}
\newlength{\Yoffset}
\newcommand*{\setpdfoffset}[2]{%
  \setlength{\Xoffset}{#1}%
  \setlength{\Yoffset}{#2}%
}
\newcommand*{\setviewport}[4]{%
  \def\x@viewport{%
    {\the\dimexpr#1-\Xoffset} 
    {\the\dimexpr#2-\Yoffset}
    {\the\dimexpr#3-\Xoffset}
    {\the\dimexpr#4-\Yoffset}%
  }%
}
\makeatother

\begin{document}

\setpdfoffset{35.29pt}{36.28pt}
\setviewport{45.29pt}{86.28pt}{235pt}{187pt}

\fbox{\includegraphics[clip, xviewport]{example-image.pdf}}

\end{document}

enter image description here

2

This works, but may not be general enough for your need:

\documentclass{article}
\usepackage[demo]{graphicx}
\begin{document}
\newcommand*{\aport}{45.29pt 86.28pt 235pt 187pt}
\def\X{\includegraphics[clip, viewport=}
\expandafter\X\aport]{somepdf.pdf}
\end{document}
2
  • This works, but you need to be careful about whitespaces inside it: To see this, draw an \fbox{} around it: \fbox{\def\X{\includegraphics[clip, viewport=} \expandafter\X\aport]{example-image}} It adds a little white margin(approximately one character width, the space in viewport=} \expandafter) to the left side of the image. The other solutions provided by @werner don't add this. \fbox{\begingroup\edef\x{\endgroup\noexpand\includegraphics[clip, viewport = \aport] {example-image}}}\x}
    – nks
    Jan 7, 2015 at 11:41
  • @nkrs True, one must be always careful of stray spaces leading up to and following a macro use. But the actual macro use itself, as given by \fbox{\expandafter\X\aport]{somepdf.pdf}} introduces no stray space. Jan 7, 2015 at 11:46

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .