4

Unfortunately, I cannot write a better title for the question.

I am trying to parametrize my workflow, where I will allow myself to override page margins from the command line with the following:

xelatex "\newcommand{\mygeometry}{margin=2cm}\input{myfile.tex}"

Where in myfile.tex I have the defaults and actual application using \newgeometry command:

\providecommand{\mygeometry}{margin=3cm}

% ...

\newgeometry{\mygeometry}

I get the following error when trying to process this:

! Package xkeyval Error: `margin=2cm' undefined in families `Gm'.

As I understand, geometry uses xkeyval to parse arguments in its command set, and the kind of constructs like \newgeometry{\myargs} do not work as intended, because the value of \myargs is somehow not parsed by xkeyval, instead being understood verbatim, with = sign not having any effect, i.e. being interpreted as part of the argument(s).

How can I fix this? I would like to pass custom geometry to my .tex from command line, this has to do with me needing to produce different versions of the layout for different intentions.

Everything works if I simply use \newgeometry{margin=2cm}, i.e. substitute the value myself in the file.

  • 1
    Would \expandafter\newgeometry\expandafter{\mygeometry} fix it? – Steven B. Segletes Apr 14 '14 at 13:21
  • \typein variables in conjunction with a Makefile can help also. –  Apr 14 '14 at 13:28
  • @Steven, that did the trick! I was reading on '\expandafter' but it was just too hard to understand exactly how it works in such a short time. If you could remake your comment into a full blown answer, explaining why and how what you wrote works, I'd be very grateful :-) – Armen Michaeli Apr 14 '14 at 13:38

1 Answers1

3

Without trying it out, what I had suggested in my comment was to change the line in your myfile.tex from

\newgeometry{\mygeometry}

into

\expandafter\newgeometry\expandafter{\mygeometry}

If a macro (apparently \newgeometry is of this type) wants actual text as its argument, it may not be able to accept a mere pointer to such text, in this case, the macro \mygeometry, which is replaced with the desired text, but only upon expansion.

So, in cases like this, one would like to be able to "pre-expand" the \def so that a macro like \newgeometry thinks it is getting actual text.

The macro \expandafter momentarily skips over the first item that follows and expands the second item that follows. It then executes the first item which had followed. Now if the item you want expanded isn't the second item, but is, for example, the third item, then you have to string together \expandafters every other token, until you get there.

So, in this case, the syntax \expandafter\newgeometry\expandafter{\mygeometry} says to skip over \newgeometry momentarily and expand the next thing, which is another \expandafter. That one says to skip over the { momentarily and expand \mygeometry. At that point, \newgeometry is executed with a open-brace and an expanded version of \mygeometry. It is as if the replacement text for \mygeometry had actually been typed as the argument to \newgeometry.

While not needed for this problem, these sort of techniques can be used in all sorts of clever ways to expand, for example, the third token first, then the second token, and finally the first token if, the second token relies on an expanded form of the third token, and the first token relies on an expanded form of the second.

An example of such usage is given in my answer at How to put arrows on boundary circles in the tqft package, where I define a term such as

\def\LeftSideArrow{\expandafter\expandafter\expandafter\LeftSideArrowHelper\expandafter}

which will do precisely this.