How to define a macro with multiple optional parameters?
After I had a macro that worked, I tried to improve it by making some parameters optional.
Unfortunately the macro no longer works. Instead I'm getting errors I do not understand, for example:
LaTeX Warning: Label `####5' multiply defined.
LaTeX Warning: Label `####5' multiply defined.
! LaTeX Error: fLab undefined.
! Illegal parameter number in definition of fLab.
! Illegal parameter number in definition of reserved@a.
! LaTeX Error: fCap undefined.
...and so on.
The last code I tried looked like this:
%% Graphics figure with caption and label
% [1:placement,] 2:relative width, 3:file name[, 4:caption[, 5:label]]
newcommand{figCapLab}[5][htbp]{%
ifthenelse{equal{#5}{}}%
{renewcommand{fLab}{}}%
{renewcommand{fLab}{label{##5}}}%
ifthenelse{equal{#4}{}}%
{renewcommand{fCap}{fLab}}%
{renewcommand{fCap}{caption{fLab{small{}##4}}}}%
begin{figure}[#1]%
centering%
begin{minipage}[t]{#2textwidth}%
includegraphics[width=textwidth]{#3}% is width of surrounding minipage
fCap%
end{minipage}%
end{figure}
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
newcommand{figCap}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{#4}{}}%
{figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
newcommand{figLab}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{}{#4}}%
{figCapLab[#1]{#2}{#3}{}{#4}}%
}
Who can explain what went wrong?
People who like complete examples should add this prolog:
documentclass[a4paper,twoside]{report}
usepackage{german}
usepackage[latin1]{inputenc}
usepackage{a4}
usepackage{amsmath}
usepackage{url}
usepackage{graphicx}
usepackage{ifthen}
...and this epilog:
begin{document}
See ref{foo}.
figCapLab{0.9}{whatever.pdf}{Caption}{foo}
end{document}
macros parameters
|
show 4 more comments
After I had a macro that worked, I tried to improve it by making some parameters optional.
Unfortunately the macro no longer works. Instead I'm getting errors I do not understand, for example:
LaTeX Warning: Label `####5' multiply defined.
LaTeX Warning: Label `####5' multiply defined.
! LaTeX Error: fLab undefined.
! Illegal parameter number in definition of fLab.
! Illegal parameter number in definition of reserved@a.
! LaTeX Error: fCap undefined.
...and so on.
The last code I tried looked like this:
%% Graphics figure with caption and label
% [1:placement,] 2:relative width, 3:file name[, 4:caption[, 5:label]]
newcommand{figCapLab}[5][htbp]{%
ifthenelse{equal{#5}{}}%
{renewcommand{fLab}{}}%
{renewcommand{fLab}{label{##5}}}%
ifthenelse{equal{#4}{}}%
{renewcommand{fCap}{fLab}}%
{renewcommand{fCap}{caption{fLab{small{}##4}}}}%
begin{figure}[#1]%
centering%
begin{minipage}[t]{#2textwidth}%
includegraphics[width=textwidth]{#3}% is width of surrounding minipage
fCap%
end{minipage}%
end{figure}
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
newcommand{figCap}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{#4}{}}%
{figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
newcommand{figLab}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{}{#4}}%
{figCapLab[#1]{#2}{#3}{}{#4}}%
}
Who can explain what went wrong?
People who like complete examples should add this prolog:
documentclass[a4paper,twoside]{report}
usepackage{german}
usepackage[latin1]{inputenc}
usepackage{a4}
usepackage{amsmath}
usepackage{url}
usepackage{graphicx}
usepackage{ifthen}
...and this epilog:
begin{document}
See ref{foo}.
figCapLab{0.9}{whatever.pdf}{Caption}{foo}
end{document}
macros parameters
4
Instead of identifying mistakes in your code without much context, can you provide information on what you want to achieve ultimately? Perhaps there are better ways of achieving it.
– Werner
Mar 4 at 2:39
please extend your code fragment to complete but small document!
– Zarko
Mar 4 at 2:40
@Werner: There are always different ways to reach a goal, but if you change the way too frequently, you'll never make it. So I'd prefer a fix for my "solution" over a completely new attempt (like xparse).
– U. Windl
Mar 4 at 2:57
2
it is much easier for people to debug and help you if you provide the code as a complete document that can be run, here you just provide fragments, don't say what error you get and expect people to construct an example and guess that they get the same error that you meant to ask about.
– David Carlisle
Mar 4 at 7:53
1
you have only defined one optional argument, arguments 4 and 5 are mandatory but you are testing if they are{}
– David Carlisle
Mar 4 at 7:55
|
show 4 more comments
After I had a macro that worked, I tried to improve it by making some parameters optional.
Unfortunately the macro no longer works. Instead I'm getting errors I do not understand, for example:
LaTeX Warning: Label `####5' multiply defined.
LaTeX Warning: Label `####5' multiply defined.
! LaTeX Error: fLab undefined.
! Illegal parameter number in definition of fLab.
! Illegal parameter number in definition of reserved@a.
! LaTeX Error: fCap undefined.
...and so on.
The last code I tried looked like this:
%% Graphics figure with caption and label
% [1:placement,] 2:relative width, 3:file name[, 4:caption[, 5:label]]
newcommand{figCapLab}[5][htbp]{%
ifthenelse{equal{#5}{}}%
{renewcommand{fLab}{}}%
{renewcommand{fLab}{label{##5}}}%
ifthenelse{equal{#4}{}}%
{renewcommand{fCap}{fLab}}%
{renewcommand{fCap}{caption{fLab{small{}##4}}}}%
begin{figure}[#1]%
centering%
begin{minipage}[t]{#2textwidth}%
includegraphics[width=textwidth]{#3}% is width of surrounding minipage
fCap%
end{minipage}%
end{figure}
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
newcommand{figCap}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{#4}{}}%
{figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
newcommand{figLab}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{}{#4}}%
{figCapLab[#1]{#2}{#3}{}{#4}}%
}
Who can explain what went wrong?
People who like complete examples should add this prolog:
documentclass[a4paper,twoside]{report}
usepackage{german}
usepackage[latin1]{inputenc}
usepackage{a4}
usepackage{amsmath}
usepackage{url}
usepackage{graphicx}
usepackage{ifthen}
...and this epilog:
begin{document}
See ref{foo}.
figCapLab{0.9}{whatever.pdf}{Caption}{foo}
end{document}
macros parameters
After I had a macro that worked, I tried to improve it by making some parameters optional.
Unfortunately the macro no longer works. Instead I'm getting errors I do not understand, for example:
LaTeX Warning: Label `####5' multiply defined.
LaTeX Warning: Label `####5' multiply defined.
! LaTeX Error: fLab undefined.
! Illegal parameter number in definition of fLab.
! Illegal parameter number in definition of reserved@a.
! LaTeX Error: fCap undefined.
...and so on.
The last code I tried looked like this:
%% Graphics figure with caption and label
% [1:placement,] 2:relative width, 3:file name[, 4:caption[, 5:label]]
newcommand{figCapLab}[5][htbp]{%
ifthenelse{equal{#5}{}}%
{renewcommand{fLab}{}}%
{renewcommand{fLab}{label{##5}}}%
ifthenelse{equal{#4}{}}%
{renewcommand{fCap}{fLab}}%
{renewcommand{fCap}{caption{fLab{small{}##4}}}}%
begin{figure}[#1]%
centering%
begin{minipage}[t]{#2textwidth}%
includegraphics[width=textwidth]{#3}% is width of surrounding minipage
fCap%
end{minipage}%
end{figure}
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
newcommand{figCap}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{#4}{}}%
{figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
newcommand{figLab}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{}{#4}}%
{figCapLab[#1]{#2}{#3}{}{#4}}%
}
Who can explain what went wrong?
People who like complete examples should add this prolog:
documentclass[a4paper,twoside]{report}
usepackage{german}
usepackage[latin1]{inputenc}
usepackage{a4}
usepackage{amsmath}
usepackage{url}
usepackage{graphicx}
usepackage{ifthen}
...and this epilog:
begin{document}
See ref{foo}.
figCapLab{0.9}{whatever.pdf}{Caption}{foo}
end{document}
macros parameters
macros parameters
edited Mar 4 at 2:55
U. Windl
asked Mar 4 at 2:32
U. WindlU. Windl
1256
1256
4
Instead of identifying mistakes in your code without much context, can you provide information on what you want to achieve ultimately? Perhaps there are better ways of achieving it.
– Werner
Mar 4 at 2:39
please extend your code fragment to complete but small document!
– Zarko
Mar 4 at 2:40
@Werner: There are always different ways to reach a goal, but if you change the way too frequently, you'll never make it. So I'd prefer a fix for my "solution" over a completely new attempt (like xparse).
– U. Windl
Mar 4 at 2:57
2
it is much easier for people to debug and help you if you provide the code as a complete document that can be run, here you just provide fragments, don't say what error you get and expect people to construct an example and guess that they get the same error that you meant to ask about.
– David Carlisle
Mar 4 at 7:53
1
you have only defined one optional argument, arguments 4 and 5 are mandatory but you are testing if they are{}
– David Carlisle
Mar 4 at 7:55
|
show 4 more comments
4
Instead of identifying mistakes in your code without much context, can you provide information on what you want to achieve ultimately? Perhaps there are better ways of achieving it.
– Werner
Mar 4 at 2:39
please extend your code fragment to complete but small document!
– Zarko
Mar 4 at 2:40
@Werner: There are always different ways to reach a goal, but if you change the way too frequently, you'll never make it. So I'd prefer a fix for my "solution" over a completely new attempt (like xparse).
– U. Windl
Mar 4 at 2:57
2
it is much easier for people to debug and help you if you provide the code as a complete document that can be run, here you just provide fragments, don't say what error you get and expect people to construct an example and guess that they get the same error that you meant to ask about.
– David Carlisle
Mar 4 at 7:53
1
you have only defined one optional argument, arguments 4 and 5 are mandatory but you are testing if they are{}
– David Carlisle
Mar 4 at 7:55
4
4
Instead of identifying mistakes in your code without much context, can you provide information on what you want to achieve ultimately? Perhaps there are better ways of achieving it.
– Werner
Mar 4 at 2:39
Instead of identifying mistakes in your code without much context, can you provide information on what you want to achieve ultimately? Perhaps there are better ways of achieving it.
– Werner
Mar 4 at 2:39
please extend your code fragment to complete but small document!
– Zarko
Mar 4 at 2:40
please extend your code fragment to complete but small document!
– Zarko
Mar 4 at 2:40
@Werner: There are always different ways to reach a goal, but if you change the way too frequently, you'll never make it. So I'd prefer a fix for my "solution" over a completely new attempt (like xparse).
– U. Windl
Mar 4 at 2:57
@Werner: There are always different ways to reach a goal, but if you change the way too frequently, you'll never make it. So I'd prefer a fix for my "solution" over a completely new attempt (like xparse).
– U. Windl
Mar 4 at 2:57
2
2
it is much easier for people to debug and help you if you provide the code as a complete document that can be run, here you just provide fragments, don't say what error you get and expect people to construct an example and guess that they get the same error that you meant to ask about.
– David Carlisle
Mar 4 at 7:53
it is much easier for people to debug and help you if you provide the code as a complete document that can be run, here you just provide fragments, don't say what error you get and expect people to construct an example and guess that they get the same error that you meant to ask about.
– David Carlisle
Mar 4 at 7:53
1
1
you have only defined one optional argument, arguments 4 and 5 are mandatory but you are testing if they are
{}
– David Carlisle
Mar 4 at 7:55
you have only defined one optional argument, arguments 4 and 5 are mandatory but you are testing if they are
{}
– David Carlisle
Mar 4 at 7:55
|
show 4 more comments
3 Answers
3
active
oldest
votes
When I obey your vague instructions for creating a compilable example myself which exhibits the erroneous behavior
documentclass[a4paper,twoside]{report}
usepackage{german}
usepackage[latin1]{inputenc}
usepackage{a4}
usepackage{amsmath}
usepackage{url}
usepackage{graphicx}
usepackage{ifthen}
%% Graphics figure with caption and label
% [1:placement,] 2:relative width, 3:file name[, 4:caption[, 5:label]]
newcommand{figCapLab}[5][htbp]{%
ifthenelse{equal{#5}{}}%
{renewcommand{fLab}{}}%
{renewcommand{fLab}{label{##5}}}%
ifthenelse{equal{#4}{}}%
{renewcommand{fCap}{fLab}}%
{renewcommand{fCap}{caption{fLab{small{}##4}}}}%
begin{figure}[#1]%
centering%
begin{minipage}[t]{#2textwidth}%
includegraphics[width=textwidth]{#3}% is width of surrounding minipage
fCap%
end{minipage}%
end{figure}
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
newcommand{figCap}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{#4}{}}%
{figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
newcommand{figLab}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{}{#4}}%
{figCapLab[#1]{#2}{#3}{}{#4}}%
}
begin{document}
See ref{foo}.
figCapLab{0.9}{whatever.pdf}{Caption}{foo}
end{document}
, I don't get any of the errors you describe but I get:
LaTeX Warning: Reference `foo' on page 1 undefined on input line 43.
! LaTeX Error: fLab undefined.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! Illegal parameter number in definition of fLab.
<to be read again>
5
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! LaTeX Error: fCap undefined.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! Illegal parameter number in definition of fCap.
<to be read again>
4
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
LaTeX Warning: File `whatever.pdf' not found on input line 44.
! Package pdftex.def Error: File `whatever.pdf' not found: using draft setting.
See the pdftex.def package documentation for explanation.
Type H <return> for immediate help.
...
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! Illegal parameter number in definition of reserved@a.
<to be read again>
4
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! Illegal parameter number in definition of reserved@a.
<to be read again>
5
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! You can't use `macro parameter character #' in restricted horizontal mode.
<argument> ...e : ignorespaces fLab {small {}##
4}
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
[1{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./test.aux)
LaTeX Warning: There were undefined references.
When instead I do:
documentclass[a4paper,twoside]{report}
usepackage{german}
usepackage[latin1]{inputenc}
usepackage{a4}
usepackage{amsmath}
usepackage{url}
usepackage{graphicx}
usepackage{ifthen}
% As later renwecommand is done on these macros,
% they should be defined: !!!!!!!!!!!!
newcommand{fLab}{}
newcommand{fCap}{}
%% Graphics figure with caption and label
% #1: optional: placement
% #2: non-optional: relative width
% #3: non-optional: file name
% #4: non-optional: in case not empty: caption
% #5: non-optional: in case not empty: label
newcommand{figCapLab}[5][htbp]{%
ifthenelse{equal{#5}{}}%
{renewcommand{fLab}{}}%
{renewcommand{fLab}{label{#5}}}% !!!!Don't double the hash!!!!
ifthenelse{equal{#4}{}}%
{renewcommand{fCap}{fLab}}%
{renewcommand{fCap}{caption{fLab{small{}#4}}}}% !!!!Don't double the hash!!!!
begin{figure}[#1]%
centering
begin{minipage}[t]{#2textwidth}%
includegraphics[width=textwidth]{#3}% is width of surrounding minipage
fCap
end{minipage}%
end{figure}%%%%%%%
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
newcommand{figCap}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{#4}{}}%
{figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
newcommand{figLab}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{}{#4}}%
{figCapLab[#1]{#2}{#3}{}{#4}}%
}
begin{document}
See ref{foo}.
figCapLab{0.9}{example-grid-100x100pt.pdf}{Caption}{foo}
end{document}
, then I don't get any errors or warnings with the second compilation.
By the way 1:
I don't have whatever.pdf in the texmf-trees of my system.
If you wish to provide examples that process images, you can use the ready-to-use images that modern LaTeX systems offer to you:
E.g., the documentation of Martin Scharrer's MWE package lists all the images available due to that package. With current TeX-platforms and current releases of the MWE-package, the images are usable without the need of loading the package because they are integrated into the texmf-tree and into the filename-database.
By the way 2:
Under normal circumstances you may indent lines of TeX source code for improving readability. ;-) This is because when (La)TeX begins to read a line of input, the state of the reading-apparatus is in state N (new line) while in state N characters of category code 10(space) will not be tokenized as space tokens but will not yield any tokens at all.
By the way 3:
It is good practice to provide examples which people can compile as they are for exactly reproducing the erroneous behavior. Being able to reproduce erroneous behavior is important for being able to debug the code which produces that erroneous behavior.
Dr. Nicola Talbot's Creating a LaTeX Minimal Example provides some guidelines.
There also is How to make a “minimum example” from texfaq.org which also contains some links to advices for asking questions.
In What is a minimal working example? Christian Faulhammer does not use the term "Minimal Example" but he uses the term "Minimal Working Example" even for examples which work only in the sense that they are sufficient for exhibiting an erroneous behavior.
You may also be interested in the answers to the question I've just been asked to write a minimal example, what is that?
I named the filewhatever.pdf
to emphasize the fact that the actual file name is unimportant for the problem, and you can use any PDF file you like (assuming everyone who is going to answer this problem either has some PDF file or is able to create one). As my text installation (openSUSE) consists of more than 1000 individual packages, I must admit that I lost overview of what is needed for what. On lack of spaces: I'm still unsure which spaces will be ignored and which aren't , so I used no indent.
– U. Windl
Mar 5 at 1:05
Would it be possible to provide a context diff of the changes required to my definitions? I feel you thought "you made it hard for me to understand, so I'll make it hard for you to understand". And as written for another answer: Maybe explain what I did wrong.
– U. Windl
Mar 5 at 1:22
On##4
: I thought if an "inner"newcommand
accesses arguments of an outernewcommand
, the#
has to be doubled regardless whether the innernewcommand
has (formal) arguments.
– U. Windl
Mar 5 at 2:11
@U.Windl The innernewcommand
doesn't "access" the argument of the outernewcommand
. At the time of carrying out the "outer command", which was defined at an earlier point in time by what you call "the outernewcommand
", TeX uses the argument of the "outer command" for defining the "inner command" by carrying out the "innernewcommand
". Within the definition of the outernewcommand
the arguments of the innernewcommand
are to be denoted by means of doubled hashes because when carrying out the outer command that defines the inner command, two hashes are collapsed into one.
– Ulrich Diez
Mar 5 at 9:54
add a comment |
The OP asked me for a little explanation to this answer so I will add some text here. The standard LaTeX mechanism for optional arguments allows for one optional argument that comes prior to mandatory arguments. Thus, to create a syntax that provides an optional argument, 2 mandatory arguments, and then 2 optional arguments, requires 3 macros to be strung together successively, since there are 3 optional arguments requested.
The first macro absorbs an optional plus 2 mandatory arguments, and then it must invoke a second macro, and here is the key, as its final action! The reason the 2nd macro invocation must be the final action of the first macro is that the 2nd macro must absorb an optional argument. If there are any tokens in the first macro that follow the invocation of the 2nd macro in the chain, then those other tokens in the 1st macro will be absorbed as the argument, instead of what is intended.
Likewise, the 2nd macro must call on the 3rd macro as its final action.
Certain quirky highlights:
if the successive macro is to be called inside of an
if
block, one mustexpandafter
prior to the successive macro, so that the successive macro doesn't try to absorb theelse
orfi
.In this particular case, the final optional argument, the label, is only callable if a caption has been specified. Thus, the 2nd macro in the chain will only call on the 3rd macro if a caption has been specified. Otherwise, it will truncate the sequence.
The MWE:
documentclass{article}
usepackage{graphicx}
newcommandaddtofigtoks[1]{expandafterfigtoksexpandafter
{thefigtoks#1}}
newtoksfigtoks
newcommandfigCapLab[3][htbp]{%
figtoks{begin{figure}[#1]}
addtofigtoks{centering}
addtofigtoks{includegraphics[width=#2textwidth]{#3}}
optcap
}
newcommandoptcap[1][relax]{%
ifxrelax#1relax
addtofigtoks{end{figure}}
thefigtoks
else
addtofigtoks{caption{#1}}%
expandafterlabelopt
fi
}
newcommandlabelopt[1][relax]{%
ifxrelax#1relaxelseaddtofigtoks{label{#1}}fi
addtofigtoks{end{figure}}
thefigtoks
}
begin{document}
figCapLab{.2}{example-image-a}
figCapLab{.2}{example-image-b}[My caption]
figCapLab{.2}{example-image-c}[My caption][fg:label1]
figCapLab[p]{.2}{example-image}[Other caption][fg:label2]
In figures ref{fg:label1} and ref{fg:label2}...
end{document}
Ack-shu-ally, the more I think of it, tokens are not even needed:
documentclass{article}
usepackage{graphicx}
newcommandfigCapLab[3][htbp]{%
begin{figure}[#1]
centering
includegraphics[width=#2textwidth]{#3}
optcap
}
newcommandoptcap[1][relax]{%
ifxrelax#1relax
end{figure}
else
caption{#1}%
expandafterlabelopt
fi
}
newcommandlabelopt[1][relax]{%
ifxrelax#1relaxelselabel{#1}fi
end{figure}
}
begin{document}
figCapLab{.2}{example-image-a}
figCapLab{.2}{example-image-b}[My caption]
figCapLab{.2}{example-image-c}[My caption][fg:label1]
figCapLab[p]{.2}{example-image}[Other caption][fg:label2]
In figures ref{fg:label1} and ref{fg:label2}...
end{document}
As I tried to point out: Unlike most people asking questions, I don't just want a solution to copy without understanding, but instead I want to learn what I did wrong to use that knowledge for the future. Sorry, I'm pre-Google generation ;-)
– U. Windl
Mar 5 at 1:17
1
@U.Windl I will add some context to my answer.
– Steven B. Segletes
Mar 5 at 1:22
@U.Windl Please see my added commentary.
– Steven B. Segletes
Mar 5 at 1:34
Imagine (the truth!) that the user asking the question only knows LaTeX, and not TeX, so the user wonders whatoptcap
,relax
andexpandafter
really do. I guessifx
,else
, andfi
are TeX's low-level conditionals. When describing the processing in an abstract way (as you do), some concrete examples (e.g. actual arguments) would be helpful for the TeX Dummies like me.
– U. Windl
Mar 5 at 1:51
@U.Windloptcap
is just the 2nd macro I created in the string of 3 macros.relax
is a macro that does nothing---however, it can be tested for, which is how I use it here. The sequenceexpandaftermacroothermacro
causesothermacro
to be expanded once, beforemacro
is executed. So,expandafterlabeloptfi
causes thefi
to be executed (closing out the conditional), so thatlabelopt
will absorb the next token in the input stream, rather than absorbing thefi
. Indeed,ifx...else...fi
is one type of TeX conditional that compares the next 2 tokens WITHOUT expansion
– Steven B. Segletes
Mar 5 at 1:59
|
show 3 more comments
Here is how you can achieve your goal using xparse
:
documentclass{article}
usepackage{graphicx,xparse}
% figCapLab
% [<float spec>] #1
% {<width factor>} #2
% {<image>} #3
% [<caption>] #4
% [<label>] #5
NewDocumentCommand{figCapLab}{ O{htbp} m m o o }{%
begin{figure}[#1]
centering
includegraphics[width=#2linewidth]{#3}% Set image at width
IfValueT{#4}
{caption{#4}IfValueT{#5}{label{#5}}}% Set possible caption and label
end{figure}
}
begin{document}
figCapLab{.2}{example-image-a}
figCapLab{.2}{example-image-b}[My caption]
figCapLab{.2}{example-image-c}[My caption][fg:label1]
figCapLab[p]{.2}{example-image}[Other caption][fg:label2]
In figures ref{fg:label1} and ref{fg:label2}ldots
end{document}
Optional arguments with a default is specified using O{<default>}
while optional arguments without a default uses o
. Conditioning on whether or not a value is supplied is done using IfValueTF{<parameter>}{<true>}{<false>}
. There are also singular conditionals IfValueT
and IfValueF
, the former of which was used above.
The above code assumes that an empty caption (blank fourth argument) would not need a label
(fifth) argument. If that's needed, move the IfValueT{#5}{label{#5}}
out of the <true>
branch inside IfValueT{#4}
:
NewDocumentCommand{figCapLab}{ O{htbp} m m o o }{%
begin{figure}[#1]
centering
includegraphics[width=#2textwidth]{#3}% Set image at width
IfValueT{#4}{caption{#4}}% Possible caption
IfValueT{#5}{label{#5}}% Possible label
end{figure}
}
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "85"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f477627%2fhow-to-define-a-macro-with-multiple-optional-parameters%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
When I obey your vague instructions for creating a compilable example myself which exhibits the erroneous behavior
documentclass[a4paper,twoside]{report}
usepackage{german}
usepackage[latin1]{inputenc}
usepackage{a4}
usepackage{amsmath}
usepackage{url}
usepackage{graphicx}
usepackage{ifthen}
%% Graphics figure with caption and label
% [1:placement,] 2:relative width, 3:file name[, 4:caption[, 5:label]]
newcommand{figCapLab}[5][htbp]{%
ifthenelse{equal{#5}{}}%
{renewcommand{fLab}{}}%
{renewcommand{fLab}{label{##5}}}%
ifthenelse{equal{#4}{}}%
{renewcommand{fCap}{fLab}}%
{renewcommand{fCap}{caption{fLab{small{}##4}}}}%
begin{figure}[#1]%
centering%
begin{minipage}[t]{#2textwidth}%
includegraphics[width=textwidth]{#3}% is width of surrounding minipage
fCap%
end{minipage}%
end{figure}
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
newcommand{figCap}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{#4}{}}%
{figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
newcommand{figLab}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{}{#4}}%
{figCapLab[#1]{#2}{#3}{}{#4}}%
}
begin{document}
See ref{foo}.
figCapLab{0.9}{whatever.pdf}{Caption}{foo}
end{document}
, I don't get any of the errors you describe but I get:
LaTeX Warning: Reference `foo' on page 1 undefined on input line 43.
! LaTeX Error: fLab undefined.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! Illegal parameter number in definition of fLab.
<to be read again>
5
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! LaTeX Error: fCap undefined.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! Illegal parameter number in definition of fCap.
<to be read again>
4
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
LaTeX Warning: File `whatever.pdf' not found on input line 44.
! Package pdftex.def Error: File `whatever.pdf' not found: using draft setting.
See the pdftex.def package documentation for explanation.
Type H <return> for immediate help.
...
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! Illegal parameter number in definition of reserved@a.
<to be read again>
4
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! Illegal parameter number in definition of reserved@a.
<to be read again>
5
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! You can't use `macro parameter character #' in restricted horizontal mode.
<argument> ...e : ignorespaces fLab {small {}##
4}
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
[1{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./test.aux)
LaTeX Warning: There were undefined references.
When instead I do:
documentclass[a4paper,twoside]{report}
usepackage{german}
usepackage[latin1]{inputenc}
usepackage{a4}
usepackage{amsmath}
usepackage{url}
usepackage{graphicx}
usepackage{ifthen}
% As later renwecommand is done on these macros,
% they should be defined: !!!!!!!!!!!!
newcommand{fLab}{}
newcommand{fCap}{}
%% Graphics figure with caption and label
% #1: optional: placement
% #2: non-optional: relative width
% #3: non-optional: file name
% #4: non-optional: in case not empty: caption
% #5: non-optional: in case not empty: label
newcommand{figCapLab}[5][htbp]{%
ifthenelse{equal{#5}{}}%
{renewcommand{fLab}{}}%
{renewcommand{fLab}{label{#5}}}% !!!!Don't double the hash!!!!
ifthenelse{equal{#4}{}}%
{renewcommand{fCap}{fLab}}%
{renewcommand{fCap}{caption{fLab{small{}#4}}}}% !!!!Don't double the hash!!!!
begin{figure}[#1]%
centering
begin{minipage}[t]{#2textwidth}%
includegraphics[width=textwidth]{#3}% is width of surrounding minipage
fCap
end{minipage}%
end{figure}%%%%%%%
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
newcommand{figCap}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{#4}{}}%
{figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
newcommand{figLab}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{}{#4}}%
{figCapLab[#1]{#2}{#3}{}{#4}}%
}
begin{document}
See ref{foo}.
figCapLab{0.9}{example-grid-100x100pt.pdf}{Caption}{foo}
end{document}
, then I don't get any errors or warnings with the second compilation.
By the way 1:
I don't have whatever.pdf in the texmf-trees of my system.
If you wish to provide examples that process images, you can use the ready-to-use images that modern LaTeX systems offer to you:
E.g., the documentation of Martin Scharrer's MWE package lists all the images available due to that package. With current TeX-platforms and current releases of the MWE-package, the images are usable without the need of loading the package because they are integrated into the texmf-tree and into the filename-database.
By the way 2:
Under normal circumstances you may indent lines of TeX source code for improving readability. ;-) This is because when (La)TeX begins to read a line of input, the state of the reading-apparatus is in state N (new line) while in state N characters of category code 10(space) will not be tokenized as space tokens but will not yield any tokens at all.
By the way 3:
It is good practice to provide examples which people can compile as they are for exactly reproducing the erroneous behavior. Being able to reproduce erroneous behavior is important for being able to debug the code which produces that erroneous behavior.
Dr. Nicola Talbot's Creating a LaTeX Minimal Example provides some guidelines.
There also is How to make a “minimum example” from texfaq.org which also contains some links to advices for asking questions.
In What is a minimal working example? Christian Faulhammer does not use the term "Minimal Example" but he uses the term "Minimal Working Example" even for examples which work only in the sense that they are sufficient for exhibiting an erroneous behavior.
You may also be interested in the answers to the question I've just been asked to write a minimal example, what is that?
I named the filewhatever.pdf
to emphasize the fact that the actual file name is unimportant for the problem, and you can use any PDF file you like (assuming everyone who is going to answer this problem either has some PDF file or is able to create one). As my text installation (openSUSE) consists of more than 1000 individual packages, I must admit that I lost overview of what is needed for what. On lack of spaces: I'm still unsure which spaces will be ignored and which aren't , so I used no indent.
– U. Windl
Mar 5 at 1:05
Would it be possible to provide a context diff of the changes required to my definitions? I feel you thought "you made it hard for me to understand, so I'll make it hard for you to understand". And as written for another answer: Maybe explain what I did wrong.
– U. Windl
Mar 5 at 1:22
On##4
: I thought if an "inner"newcommand
accesses arguments of an outernewcommand
, the#
has to be doubled regardless whether the innernewcommand
has (formal) arguments.
– U. Windl
Mar 5 at 2:11
@U.Windl The innernewcommand
doesn't "access" the argument of the outernewcommand
. At the time of carrying out the "outer command", which was defined at an earlier point in time by what you call "the outernewcommand
", TeX uses the argument of the "outer command" for defining the "inner command" by carrying out the "innernewcommand
". Within the definition of the outernewcommand
the arguments of the innernewcommand
are to be denoted by means of doubled hashes because when carrying out the outer command that defines the inner command, two hashes are collapsed into one.
– Ulrich Diez
Mar 5 at 9:54
add a comment |
When I obey your vague instructions for creating a compilable example myself which exhibits the erroneous behavior
documentclass[a4paper,twoside]{report}
usepackage{german}
usepackage[latin1]{inputenc}
usepackage{a4}
usepackage{amsmath}
usepackage{url}
usepackage{graphicx}
usepackage{ifthen}
%% Graphics figure with caption and label
% [1:placement,] 2:relative width, 3:file name[, 4:caption[, 5:label]]
newcommand{figCapLab}[5][htbp]{%
ifthenelse{equal{#5}{}}%
{renewcommand{fLab}{}}%
{renewcommand{fLab}{label{##5}}}%
ifthenelse{equal{#4}{}}%
{renewcommand{fCap}{fLab}}%
{renewcommand{fCap}{caption{fLab{small{}##4}}}}%
begin{figure}[#1]%
centering%
begin{minipage}[t]{#2textwidth}%
includegraphics[width=textwidth]{#3}% is width of surrounding minipage
fCap%
end{minipage}%
end{figure}
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
newcommand{figCap}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{#4}{}}%
{figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
newcommand{figLab}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{}{#4}}%
{figCapLab[#1]{#2}{#3}{}{#4}}%
}
begin{document}
See ref{foo}.
figCapLab{0.9}{whatever.pdf}{Caption}{foo}
end{document}
, I don't get any of the errors you describe but I get:
LaTeX Warning: Reference `foo' on page 1 undefined on input line 43.
! LaTeX Error: fLab undefined.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! Illegal parameter number in definition of fLab.
<to be read again>
5
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! LaTeX Error: fCap undefined.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! Illegal parameter number in definition of fCap.
<to be read again>
4
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
LaTeX Warning: File `whatever.pdf' not found on input line 44.
! Package pdftex.def Error: File `whatever.pdf' not found: using draft setting.
See the pdftex.def package documentation for explanation.
Type H <return> for immediate help.
...
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! Illegal parameter number in definition of reserved@a.
<to be read again>
4
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! Illegal parameter number in definition of reserved@a.
<to be read again>
5
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! You can't use `macro parameter character #' in restricted horizontal mode.
<argument> ...e : ignorespaces fLab {small {}##
4}
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
[1{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./test.aux)
LaTeX Warning: There were undefined references.
When instead I do:
documentclass[a4paper,twoside]{report}
usepackage{german}
usepackage[latin1]{inputenc}
usepackage{a4}
usepackage{amsmath}
usepackage{url}
usepackage{graphicx}
usepackage{ifthen}
% As later renwecommand is done on these macros,
% they should be defined: !!!!!!!!!!!!
newcommand{fLab}{}
newcommand{fCap}{}
%% Graphics figure with caption and label
% #1: optional: placement
% #2: non-optional: relative width
% #3: non-optional: file name
% #4: non-optional: in case not empty: caption
% #5: non-optional: in case not empty: label
newcommand{figCapLab}[5][htbp]{%
ifthenelse{equal{#5}{}}%
{renewcommand{fLab}{}}%
{renewcommand{fLab}{label{#5}}}% !!!!Don't double the hash!!!!
ifthenelse{equal{#4}{}}%
{renewcommand{fCap}{fLab}}%
{renewcommand{fCap}{caption{fLab{small{}#4}}}}% !!!!Don't double the hash!!!!
begin{figure}[#1]%
centering
begin{minipage}[t]{#2textwidth}%
includegraphics[width=textwidth]{#3}% is width of surrounding minipage
fCap
end{minipage}%
end{figure}%%%%%%%
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
newcommand{figCap}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{#4}{}}%
{figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
newcommand{figLab}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{}{#4}}%
{figCapLab[#1]{#2}{#3}{}{#4}}%
}
begin{document}
See ref{foo}.
figCapLab{0.9}{example-grid-100x100pt.pdf}{Caption}{foo}
end{document}
, then I don't get any errors or warnings with the second compilation.
By the way 1:
I don't have whatever.pdf in the texmf-trees of my system.
If you wish to provide examples that process images, you can use the ready-to-use images that modern LaTeX systems offer to you:
E.g., the documentation of Martin Scharrer's MWE package lists all the images available due to that package. With current TeX-platforms and current releases of the MWE-package, the images are usable without the need of loading the package because they are integrated into the texmf-tree and into the filename-database.
By the way 2:
Under normal circumstances you may indent lines of TeX source code for improving readability. ;-) This is because when (La)TeX begins to read a line of input, the state of the reading-apparatus is in state N (new line) while in state N characters of category code 10(space) will not be tokenized as space tokens but will not yield any tokens at all.
By the way 3:
It is good practice to provide examples which people can compile as they are for exactly reproducing the erroneous behavior. Being able to reproduce erroneous behavior is important for being able to debug the code which produces that erroneous behavior.
Dr. Nicola Talbot's Creating a LaTeX Minimal Example provides some guidelines.
There also is How to make a “minimum example” from texfaq.org which also contains some links to advices for asking questions.
In What is a minimal working example? Christian Faulhammer does not use the term "Minimal Example" but he uses the term "Minimal Working Example" even for examples which work only in the sense that they are sufficient for exhibiting an erroneous behavior.
You may also be interested in the answers to the question I've just been asked to write a minimal example, what is that?
I named the filewhatever.pdf
to emphasize the fact that the actual file name is unimportant for the problem, and you can use any PDF file you like (assuming everyone who is going to answer this problem either has some PDF file or is able to create one). As my text installation (openSUSE) consists of more than 1000 individual packages, I must admit that I lost overview of what is needed for what. On lack of spaces: I'm still unsure which spaces will be ignored and which aren't , so I used no indent.
– U. Windl
Mar 5 at 1:05
Would it be possible to provide a context diff of the changes required to my definitions? I feel you thought "you made it hard for me to understand, so I'll make it hard for you to understand". And as written for another answer: Maybe explain what I did wrong.
– U. Windl
Mar 5 at 1:22
On##4
: I thought if an "inner"newcommand
accesses arguments of an outernewcommand
, the#
has to be doubled regardless whether the innernewcommand
has (formal) arguments.
– U. Windl
Mar 5 at 2:11
@U.Windl The innernewcommand
doesn't "access" the argument of the outernewcommand
. At the time of carrying out the "outer command", which was defined at an earlier point in time by what you call "the outernewcommand
", TeX uses the argument of the "outer command" for defining the "inner command" by carrying out the "innernewcommand
". Within the definition of the outernewcommand
the arguments of the innernewcommand
are to be denoted by means of doubled hashes because when carrying out the outer command that defines the inner command, two hashes are collapsed into one.
– Ulrich Diez
Mar 5 at 9:54
add a comment |
When I obey your vague instructions for creating a compilable example myself which exhibits the erroneous behavior
documentclass[a4paper,twoside]{report}
usepackage{german}
usepackage[latin1]{inputenc}
usepackage{a4}
usepackage{amsmath}
usepackage{url}
usepackage{graphicx}
usepackage{ifthen}
%% Graphics figure with caption and label
% [1:placement,] 2:relative width, 3:file name[, 4:caption[, 5:label]]
newcommand{figCapLab}[5][htbp]{%
ifthenelse{equal{#5}{}}%
{renewcommand{fLab}{}}%
{renewcommand{fLab}{label{##5}}}%
ifthenelse{equal{#4}{}}%
{renewcommand{fCap}{fLab}}%
{renewcommand{fCap}{caption{fLab{small{}##4}}}}%
begin{figure}[#1]%
centering%
begin{minipage}[t]{#2textwidth}%
includegraphics[width=textwidth]{#3}% is width of surrounding minipage
fCap%
end{minipage}%
end{figure}
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
newcommand{figCap}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{#4}{}}%
{figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
newcommand{figLab}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{}{#4}}%
{figCapLab[#1]{#2}{#3}{}{#4}}%
}
begin{document}
See ref{foo}.
figCapLab{0.9}{whatever.pdf}{Caption}{foo}
end{document}
, I don't get any of the errors you describe but I get:
LaTeX Warning: Reference `foo' on page 1 undefined on input line 43.
! LaTeX Error: fLab undefined.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! Illegal parameter number in definition of fLab.
<to be read again>
5
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! LaTeX Error: fCap undefined.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! Illegal parameter number in definition of fCap.
<to be read again>
4
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
LaTeX Warning: File `whatever.pdf' not found on input line 44.
! Package pdftex.def Error: File `whatever.pdf' not found: using draft setting.
See the pdftex.def package documentation for explanation.
Type H <return> for immediate help.
...
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! Illegal parameter number in definition of reserved@a.
<to be read again>
4
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! Illegal parameter number in definition of reserved@a.
<to be read again>
5
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! You can't use `macro parameter character #' in restricted horizontal mode.
<argument> ...e : ignorespaces fLab {small {}##
4}
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
[1{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./test.aux)
LaTeX Warning: There were undefined references.
When instead I do:
documentclass[a4paper,twoside]{report}
usepackage{german}
usepackage[latin1]{inputenc}
usepackage{a4}
usepackage{amsmath}
usepackage{url}
usepackage{graphicx}
usepackage{ifthen}
% As later renwecommand is done on these macros,
% they should be defined: !!!!!!!!!!!!
newcommand{fLab}{}
newcommand{fCap}{}
%% Graphics figure with caption and label
% #1: optional: placement
% #2: non-optional: relative width
% #3: non-optional: file name
% #4: non-optional: in case not empty: caption
% #5: non-optional: in case not empty: label
newcommand{figCapLab}[5][htbp]{%
ifthenelse{equal{#5}{}}%
{renewcommand{fLab}{}}%
{renewcommand{fLab}{label{#5}}}% !!!!Don't double the hash!!!!
ifthenelse{equal{#4}{}}%
{renewcommand{fCap}{fLab}}%
{renewcommand{fCap}{caption{fLab{small{}#4}}}}% !!!!Don't double the hash!!!!
begin{figure}[#1]%
centering
begin{minipage}[t]{#2textwidth}%
includegraphics[width=textwidth]{#3}% is width of surrounding minipage
fCap
end{minipage}%
end{figure}%%%%%%%
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
newcommand{figCap}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{#4}{}}%
{figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
newcommand{figLab}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{}{#4}}%
{figCapLab[#1]{#2}{#3}{}{#4}}%
}
begin{document}
See ref{foo}.
figCapLab{0.9}{example-grid-100x100pt.pdf}{Caption}{foo}
end{document}
, then I don't get any errors or warnings with the second compilation.
By the way 1:
I don't have whatever.pdf in the texmf-trees of my system.
If you wish to provide examples that process images, you can use the ready-to-use images that modern LaTeX systems offer to you:
E.g., the documentation of Martin Scharrer's MWE package lists all the images available due to that package. With current TeX-platforms and current releases of the MWE-package, the images are usable without the need of loading the package because they are integrated into the texmf-tree and into the filename-database.
By the way 2:
Under normal circumstances you may indent lines of TeX source code for improving readability. ;-) This is because when (La)TeX begins to read a line of input, the state of the reading-apparatus is in state N (new line) while in state N characters of category code 10(space) will not be tokenized as space tokens but will not yield any tokens at all.
By the way 3:
It is good practice to provide examples which people can compile as they are for exactly reproducing the erroneous behavior. Being able to reproduce erroneous behavior is important for being able to debug the code which produces that erroneous behavior.
Dr. Nicola Talbot's Creating a LaTeX Minimal Example provides some guidelines.
There also is How to make a “minimum example” from texfaq.org which also contains some links to advices for asking questions.
In What is a minimal working example? Christian Faulhammer does not use the term "Minimal Example" but he uses the term "Minimal Working Example" even for examples which work only in the sense that they are sufficient for exhibiting an erroneous behavior.
You may also be interested in the answers to the question I've just been asked to write a minimal example, what is that?
When I obey your vague instructions for creating a compilable example myself which exhibits the erroneous behavior
documentclass[a4paper,twoside]{report}
usepackage{german}
usepackage[latin1]{inputenc}
usepackage{a4}
usepackage{amsmath}
usepackage{url}
usepackage{graphicx}
usepackage{ifthen}
%% Graphics figure with caption and label
% [1:placement,] 2:relative width, 3:file name[, 4:caption[, 5:label]]
newcommand{figCapLab}[5][htbp]{%
ifthenelse{equal{#5}{}}%
{renewcommand{fLab}{}}%
{renewcommand{fLab}{label{##5}}}%
ifthenelse{equal{#4}{}}%
{renewcommand{fCap}{fLab}}%
{renewcommand{fCap}{caption{fLab{small{}##4}}}}%
begin{figure}[#1]%
centering%
begin{minipage}[t]{#2textwidth}%
includegraphics[width=textwidth]{#3}% is width of surrounding minipage
fCap%
end{minipage}%
end{figure}
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
newcommand{figCap}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{#4}{}}%
{figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
newcommand{figLab}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{}{#4}}%
{figCapLab[#1]{#2}{#3}{}{#4}}%
}
begin{document}
See ref{foo}.
figCapLab{0.9}{whatever.pdf}{Caption}{foo}
end{document}
, I don't get any of the errors you describe but I get:
LaTeX Warning: Reference `foo' on page 1 undefined on input line 43.
! LaTeX Error: fLab undefined.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! Illegal parameter number in definition of fLab.
<to be read again>
5
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! LaTeX Error: fCap undefined.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! Illegal parameter number in definition of fCap.
<to be read again>
4
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
LaTeX Warning: File `whatever.pdf' not found on input line 44.
! Package pdftex.def Error: File `whatever.pdf' not found: using draft setting.
See the pdftex.def package documentation for explanation.
Type H <return> for immediate help.
...
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! Illegal parameter number in definition of reserved@a.
<to be read again>
4
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! Illegal parameter number in definition of reserved@a.
<to be read again>
5
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
! You can't use `macro parameter character #' in restricted horizontal mode.
<argument> ...e : ignorespaces fLab {small {}##
4}
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}
?
[1{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./test.aux)
LaTeX Warning: There were undefined references.
When instead I do:
documentclass[a4paper,twoside]{report}
usepackage{german}
usepackage[latin1]{inputenc}
usepackage{a4}
usepackage{amsmath}
usepackage{url}
usepackage{graphicx}
usepackage{ifthen}
% As later renwecommand is done on these macros,
% they should be defined: !!!!!!!!!!!!
newcommand{fLab}{}
newcommand{fCap}{}
%% Graphics figure with caption and label
% #1: optional: placement
% #2: non-optional: relative width
% #3: non-optional: file name
% #4: non-optional: in case not empty: caption
% #5: non-optional: in case not empty: label
newcommand{figCapLab}[5][htbp]{%
ifthenelse{equal{#5}{}}%
{renewcommand{fLab}{}}%
{renewcommand{fLab}{label{#5}}}% !!!!Don't double the hash!!!!
ifthenelse{equal{#4}{}}%
{renewcommand{fCap}{fLab}}%
{renewcommand{fCap}{caption{fLab{small{}#4}}}}% !!!!Don't double the hash!!!!
begin{figure}[#1]%
centering
begin{minipage}[t]{#2textwidth}%
includegraphics[width=textwidth]{#3}% is width of surrounding minipage
fCap
end{minipage}%
end{figure}%%%%%%%
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
newcommand{figCap}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{#4}{}}%
{figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
newcommand{figLab}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{}{#4}}%
{figCapLab[#1]{#2}{#3}{}{#4}}%
}
begin{document}
See ref{foo}.
figCapLab{0.9}{example-grid-100x100pt.pdf}{Caption}{foo}
end{document}
, then I don't get any errors or warnings with the second compilation.
By the way 1:
I don't have whatever.pdf in the texmf-trees of my system.
If you wish to provide examples that process images, you can use the ready-to-use images that modern LaTeX systems offer to you:
E.g., the documentation of Martin Scharrer's MWE package lists all the images available due to that package. With current TeX-platforms and current releases of the MWE-package, the images are usable without the need of loading the package because they are integrated into the texmf-tree and into the filename-database.
By the way 2:
Under normal circumstances you may indent lines of TeX source code for improving readability. ;-) This is because when (La)TeX begins to read a line of input, the state of the reading-apparatus is in state N (new line) while in state N characters of category code 10(space) will not be tokenized as space tokens but will not yield any tokens at all.
By the way 3:
It is good practice to provide examples which people can compile as they are for exactly reproducing the erroneous behavior. Being able to reproduce erroneous behavior is important for being able to debug the code which produces that erroneous behavior.
Dr. Nicola Talbot's Creating a LaTeX Minimal Example provides some guidelines.
There also is How to make a “minimum example” from texfaq.org which also contains some links to advices for asking questions.
In What is a minimal working example? Christian Faulhammer does not use the term "Minimal Example" but he uses the term "Minimal Working Example" even for examples which work only in the sense that they are sufficient for exhibiting an erroneous behavior.
You may also be interested in the answers to the question I've just been asked to write a minimal example, what is that?
edited Mar 5 at 9:55
answered Mar 4 at 9:02
Ulrich DiezUlrich Diez
5,305619
5,305619
I named the filewhatever.pdf
to emphasize the fact that the actual file name is unimportant for the problem, and you can use any PDF file you like (assuming everyone who is going to answer this problem either has some PDF file or is able to create one). As my text installation (openSUSE) consists of more than 1000 individual packages, I must admit that I lost overview of what is needed for what. On lack of spaces: I'm still unsure which spaces will be ignored and which aren't , so I used no indent.
– U. Windl
Mar 5 at 1:05
Would it be possible to provide a context diff of the changes required to my definitions? I feel you thought "you made it hard for me to understand, so I'll make it hard for you to understand". And as written for another answer: Maybe explain what I did wrong.
– U. Windl
Mar 5 at 1:22
On##4
: I thought if an "inner"newcommand
accesses arguments of an outernewcommand
, the#
has to be doubled regardless whether the innernewcommand
has (formal) arguments.
– U. Windl
Mar 5 at 2:11
@U.Windl The innernewcommand
doesn't "access" the argument of the outernewcommand
. At the time of carrying out the "outer command", which was defined at an earlier point in time by what you call "the outernewcommand
", TeX uses the argument of the "outer command" for defining the "inner command" by carrying out the "innernewcommand
". Within the definition of the outernewcommand
the arguments of the innernewcommand
are to be denoted by means of doubled hashes because when carrying out the outer command that defines the inner command, two hashes are collapsed into one.
– Ulrich Diez
Mar 5 at 9:54
add a comment |
I named the filewhatever.pdf
to emphasize the fact that the actual file name is unimportant for the problem, and you can use any PDF file you like (assuming everyone who is going to answer this problem either has some PDF file or is able to create one). As my text installation (openSUSE) consists of more than 1000 individual packages, I must admit that I lost overview of what is needed for what. On lack of spaces: I'm still unsure which spaces will be ignored and which aren't , so I used no indent.
– U. Windl
Mar 5 at 1:05
Would it be possible to provide a context diff of the changes required to my definitions? I feel you thought "you made it hard for me to understand, so I'll make it hard for you to understand". And as written for another answer: Maybe explain what I did wrong.
– U. Windl
Mar 5 at 1:22
On##4
: I thought if an "inner"newcommand
accesses arguments of an outernewcommand
, the#
has to be doubled regardless whether the innernewcommand
has (formal) arguments.
– U. Windl
Mar 5 at 2:11
@U.Windl The innernewcommand
doesn't "access" the argument of the outernewcommand
. At the time of carrying out the "outer command", which was defined at an earlier point in time by what you call "the outernewcommand
", TeX uses the argument of the "outer command" for defining the "inner command" by carrying out the "innernewcommand
". Within the definition of the outernewcommand
the arguments of the innernewcommand
are to be denoted by means of doubled hashes because when carrying out the outer command that defines the inner command, two hashes are collapsed into one.
– Ulrich Diez
Mar 5 at 9:54
I named the file
whatever.pdf
to emphasize the fact that the actual file name is unimportant for the problem, and you can use any PDF file you like (assuming everyone who is going to answer this problem either has some PDF file or is able to create one). As my text installation (openSUSE) consists of more than 1000 individual packages, I must admit that I lost overview of what is needed for what. On lack of spaces: I'm still unsure which spaces will be ignored and which aren't , so I used no indent.– U. Windl
Mar 5 at 1:05
I named the file
whatever.pdf
to emphasize the fact that the actual file name is unimportant for the problem, and you can use any PDF file you like (assuming everyone who is going to answer this problem either has some PDF file or is able to create one). As my text installation (openSUSE) consists of more than 1000 individual packages, I must admit that I lost overview of what is needed for what. On lack of spaces: I'm still unsure which spaces will be ignored and which aren't , so I used no indent.– U. Windl
Mar 5 at 1:05
Would it be possible to provide a context diff of the changes required to my definitions? I feel you thought "you made it hard for me to understand, so I'll make it hard for you to understand". And as written for another answer: Maybe explain what I did wrong.
– U. Windl
Mar 5 at 1:22
Would it be possible to provide a context diff of the changes required to my definitions? I feel you thought "you made it hard for me to understand, so I'll make it hard for you to understand". And as written for another answer: Maybe explain what I did wrong.
– U. Windl
Mar 5 at 1:22
On
##4
: I thought if an "inner" newcommand
accesses arguments of an outer newcommand
, the #
has to be doubled regardless whether the inner newcommand
has (formal) arguments.– U. Windl
Mar 5 at 2:11
On
##4
: I thought if an "inner" newcommand
accesses arguments of an outer newcommand
, the #
has to be doubled regardless whether the inner newcommand
has (formal) arguments.– U. Windl
Mar 5 at 2:11
@U.Windl The inner
newcommand
doesn't "access" the argument of the outer newcommand
. At the time of carrying out the "outer command", which was defined at an earlier point in time by what you call "the outer newcommand
", TeX uses the argument of the "outer command" for defining the "inner command" by carrying out the "inner newcommand
". Within the definition of the outer newcommand
the arguments of the inner newcommand
are to be denoted by means of doubled hashes because when carrying out the outer command that defines the inner command, two hashes are collapsed into one.– Ulrich Diez
Mar 5 at 9:54
@U.Windl The inner
newcommand
doesn't "access" the argument of the outer newcommand
. At the time of carrying out the "outer command", which was defined at an earlier point in time by what you call "the outer newcommand
", TeX uses the argument of the "outer command" for defining the "inner command" by carrying out the "inner newcommand
". Within the definition of the outer newcommand
the arguments of the inner newcommand
are to be denoted by means of doubled hashes because when carrying out the outer command that defines the inner command, two hashes are collapsed into one.– Ulrich Diez
Mar 5 at 9:54
add a comment |
The OP asked me for a little explanation to this answer so I will add some text here. The standard LaTeX mechanism for optional arguments allows for one optional argument that comes prior to mandatory arguments. Thus, to create a syntax that provides an optional argument, 2 mandatory arguments, and then 2 optional arguments, requires 3 macros to be strung together successively, since there are 3 optional arguments requested.
The first macro absorbs an optional plus 2 mandatory arguments, and then it must invoke a second macro, and here is the key, as its final action! The reason the 2nd macro invocation must be the final action of the first macro is that the 2nd macro must absorb an optional argument. If there are any tokens in the first macro that follow the invocation of the 2nd macro in the chain, then those other tokens in the 1st macro will be absorbed as the argument, instead of what is intended.
Likewise, the 2nd macro must call on the 3rd macro as its final action.
Certain quirky highlights:
if the successive macro is to be called inside of an
if
block, one mustexpandafter
prior to the successive macro, so that the successive macro doesn't try to absorb theelse
orfi
.In this particular case, the final optional argument, the label, is only callable if a caption has been specified. Thus, the 2nd macro in the chain will only call on the 3rd macro if a caption has been specified. Otherwise, it will truncate the sequence.
The MWE:
documentclass{article}
usepackage{graphicx}
newcommandaddtofigtoks[1]{expandafterfigtoksexpandafter
{thefigtoks#1}}
newtoksfigtoks
newcommandfigCapLab[3][htbp]{%
figtoks{begin{figure}[#1]}
addtofigtoks{centering}
addtofigtoks{includegraphics[width=#2textwidth]{#3}}
optcap
}
newcommandoptcap[1][relax]{%
ifxrelax#1relax
addtofigtoks{end{figure}}
thefigtoks
else
addtofigtoks{caption{#1}}%
expandafterlabelopt
fi
}
newcommandlabelopt[1][relax]{%
ifxrelax#1relaxelseaddtofigtoks{label{#1}}fi
addtofigtoks{end{figure}}
thefigtoks
}
begin{document}
figCapLab{.2}{example-image-a}
figCapLab{.2}{example-image-b}[My caption]
figCapLab{.2}{example-image-c}[My caption][fg:label1]
figCapLab[p]{.2}{example-image}[Other caption][fg:label2]
In figures ref{fg:label1} and ref{fg:label2}...
end{document}
Ack-shu-ally, the more I think of it, tokens are not even needed:
documentclass{article}
usepackage{graphicx}
newcommandfigCapLab[3][htbp]{%
begin{figure}[#1]
centering
includegraphics[width=#2textwidth]{#3}
optcap
}
newcommandoptcap[1][relax]{%
ifxrelax#1relax
end{figure}
else
caption{#1}%
expandafterlabelopt
fi
}
newcommandlabelopt[1][relax]{%
ifxrelax#1relaxelselabel{#1}fi
end{figure}
}
begin{document}
figCapLab{.2}{example-image-a}
figCapLab{.2}{example-image-b}[My caption]
figCapLab{.2}{example-image-c}[My caption][fg:label1]
figCapLab[p]{.2}{example-image}[Other caption][fg:label2]
In figures ref{fg:label1} and ref{fg:label2}...
end{document}
As I tried to point out: Unlike most people asking questions, I don't just want a solution to copy without understanding, but instead I want to learn what I did wrong to use that knowledge for the future. Sorry, I'm pre-Google generation ;-)
– U. Windl
Mar 5 at 1:17
1
@U.Windl I will add some context to my answer.
– Steven B. Segletes
Mar 5 at 1:22
@U.Windl Please see my added commentary.
– Steven B. Segletes
Mar 5 at 1:34
Imagine (the truth!) that the user asking the question only knows LaTeX, and not TeX, so the user wonders whatoptcap
,relax
andexpandafter
really do. I guessifx
,else
, andfi
are TeX's low-level conditionals. When describing the processing in an abstract way (as you do), some concrete examples (e.g. actual arguments) would be helpful for the TeX Dummies like me.
– U. Windl
Mar 5 at 1:51
@U.Windloptcap
is just the 2nd macro I created in the string of 3 macros.relax
is a macro that does nothing---however, it can be tested for, which is how I use it here. The sequenceexpandaftermacroothermacro
causesothermacro
to be expanded once, beforemacro
is executed. So,expandafterlabeloptfi
causes thefi
to be executed (closing out the conditional), so thatlabelopt
will absorb the next token in the input stream, rather than absorbing thefi
. Indeed,ifx...else...fi
is one type of TeX conditional that compares the next 2 tokens WITHOUT expansion
– Steven B. Segletes
Mar 5 at 1:59
|
show 3 more comments
The OP asked me for a little explanation to this answer so I will add some text here. The standard LaTeX mechanism for optional arguments allows for one optional argument that comes prior to mandatory arguments. Thus, to create a syntax that provides an optional argument, 2 mandatory arguments, and then 2 optional arguments, requires 3 macros to be strung together successively, since there are 3 optional arguments requested.
The first macro absorbs an optional plus 2 mandatory arguments, and then it must invoke a second macro, and here is the key, as its final action! The reason the 2nd macro invocation must be the final action of the first macro is that the 2nd macro must absorb an optional argument. If there are any tokens in the first macro that follow the invocation of the 2nd macro in the chain, then those other tokens in the 1st macro will be absorbed as the argument, instead of what is intended.
Likewise, the 2nd macro must call on the 3rd macro as its final action.
Certain quirky highlights:
if the successive macro is to be called inside of an
if
block, one mustexpandafter
prior to the successive macro, so that the successive macro doesn't try to absorb theelse
orfi
.In this particular case, the final optional argument, the label, is only callable if a caption has been specified. Thus, the 2nd macro in the chain will only call on the 3rd macro if a caption has been specified. Otherwise, it will truncate the sequence.
The MWE:
documentclass{article}
usepackage{graphicx}
newcommandaddtofigtoks[1]{expandafterfigtoksexpandafter
{thefigtoks#1}}
newtoksfigtoks
newcommandfigCapLab[3][htbp]{%
figtoks{begin{figure}[#1]}
addtofigtoks{centering}
addtofigtoks{includegraphics[width=#2textwidth]{#3}}
optcap
}
newcommandoptcap[1][relax]{%
ifxrelax#1relax
addtofigtoks{end{figure}}
thefigtoks
else
addtofigtoks{caption{#1}}%
expandafterlabelopt
fi
}
newcommandlabelopt[1][relax]{%
ifxrelax#1relaxelseaddtofigtoks{label{#1}}fi
addtofigtoks{end{figure}}
thefigtoks
}
begin{document}
figCapLab{.2}{example-image-a}
figCapLab{.2}{example-image-b}[My caption]
figCapLab{.2}{example-image-c}[My caption][fg:label1]
figCapLab[p]{.2}{example-image}[Other caption][fg:label2]
In figures ref{fg:label1} and ref{fg:label2}...
end{document}
Ack-shu-ally, the more I think of it, tokens are not even needed:
documentclass{article}
usepackage{graphicx}
newcommandfigCapLab[3][htbp]{%
begin{figure}[#1]
centering
includegraphics[width=#2textwidth]{#3}
optcap
}
newcommandoptcap[1][relax]{%
ifxrelax#1relax
end{figure}
else
caption{#1}%
expandafterlabelopt
fi
}
newcommandlabelopt[1][relax]{%
ifxrelax#1relaxelselabel{#1}fi
end{figure}
}
begin{document}
figCapLab{.2}{example-image-a}
figCapLab{.2}{example-image-b}[My caption]
figCapLab{.2}{example-image-c}[My caption][fg:label1]
figCapLab[p]{.2}{example-image}[Other caption][fg:label2]
In figures ref{fg:label1} and ref{fg:label2}...
end{document}
As I tried to point out: Unlike most people asking questions, I don't just want a solution to copy without understanding, but instead I want to learn what I did wrong to use that knowledge for the future. Sorry, I'm pre-Google generation ;-)
– U. Windl
Mar 5 at 1:17
1
@U.Windl I will add some context to my answer.
– Steven B. Segletes
Mar 5 at 1:22
@U.Windl Please see my added commentary.
– Steven B. Segletes
Mar 5 at 1:34
Imagine (the truth!) that the user asking the question only knows LaTeX, and not TeX, so the user wonders whatoptcap
,relax
andexpandafter
really do. I guessifx
,else
, andfi
are TeX's low-level conditionals. When describing the processing in an abstract way (as you do), some concrete examples (e.g. actual arguments) would be helpful for the TeX Dummies like me.
– U. Windl
Mar 5 at 1:51
@U.Windloptcap
is just the 2nd macro I created in the string of 3 macros.relax
is a macro that does nothing---however, it can be tested for, which is how I use it here. The sequenceexpandaftermacroothermacro
causesothermacro
to be expanded once, beforemacro
is executed. So,expandafterlabeloptfi
causes thefi
to be executed (closing out the conditional), so thatlabelopt
will absorb the next token in the input stream, rather than absorbing thefi
. Indeed,ifx...else...fi
is one type of TeX conditional that compares the next 2 tokens WITHOUT expansion
– Steven B. Segletes
Mar 5 at 1:59
|
show 3 more comments
The OP asked me for a little explanation to this answer so I will add some text here. The standard LaTeX mechanism for optional arguments allows for one optional argument that comes prior to mandatory arguments. Thus, to create a syntax that provides an optional argument, 2 mandatory arguments, and then 2 optional arguments, requires 3 macros to be strung together successively, since there are 3 optional arguments requested.
The first macro absorbs an optional plus 2 mandatory arguments, and then it must invoke a second macro, and here is the key, as its final action! The reason the 2nd macro invocation must be the final action of the first macro is that the 2nd macro must absorb an optional argument. If there are any tokens in the first macro that follow the invocation of the 2nd macro in the chain, then those other tokens in the 1st macro will be absorbed as the argument, instead of what is intended.
Likewise, the 2nd macro must call on the 3rd macro as its final action.
Certain quirky highlights:
if the successive macro is to be called inside of an
if
block, one mustexpandafter
prior to the successive macro, so that the successive macro doesn't try to absorb theelse
orfi
.In this particular case, the final optional argument, the label, is only callable if a caption has been specified. Thus, the 2nd macro in the chain will only call on the 3rd macro if a caption has been specified. Otherwise, it will truncate the sequence.
The MWE:
documentclass{article}
usepackage{graphicx}
newcommandaddtofigtoks[1]{expandafterfigtoksexpandafter
{thefigtoks#1}}
newtoksfigtoks
newcommandfigCapLab[3][htbp]{%
figtoks{begin{figure}[#1]}
addtofigtoks{centering}
addtofigtoks{includegraphics[width=#2textwidth]{#3}}
optcap
}
newcommandoptcap[1][relax]{%
ifxrelax#1relax
addtofigtoks{end{figure}}
thefigtoks
else
addtofigtoks{caption{#1}}%
expandafterlabelopt
fi
}
newcommandlabelopt[1][relax]{%
ifxrelax#1relaxelseaddtofigtoks{label{#1}}fi
addtofigtoks{end{figure}}
thefigtoks
}
begin{document}
figCapLab{.2}{example-image-a}
figCapLab{.2}{example-image-b}[My caption]
figCapLab{.2}{example-image-c}[My caption][fg:label1]
figCapLab[p]{.2}{example-image}[Other caption][fg:label2]
In figures ref{fg:label1} and ref{fg:label2}...
end{document}
Ack-shu-ally, the more I think of it, tokens are not even needed:
documentclass{article}
usepackage{graphicx}
newcommandfigCapLab[3][htbp]{%
begin{figure}[#1]
centering
includegraphics[width=#2textwidth]{#3}
optcap
}
newcommandoptcap[1][relax]{%
ifxrelax#1relax
end{figure}
else
caption{#1}%
expandafterlabelopt
fi
}
newcommandlabelopt[1][relax]{%
ifxrelax#1relaxelselabel{#1}fi
end{figure}
}
begin{document}
figCapLab{.2}{example-image-a}
figCapLab{.2}{example-image-b}[My caption]
figCapLab{.2}{example-image-c}[My caption][fg:label1]
figCapLab[p]{.2}{example-image}[Other caption][fg:label2]
In figures ref{fg:label1} and ref{fg:label2}...
end{document}
The OP asked me for a little explanation to this answer so I will add some text here. The standard LaTeX mechanism for optional arguments allows for one optional argument that comes prior to mandatory arguments. Thus, to create a syntax that provides an optional argument, 2 mandatory arguments, and then 2 optional arguments, requires 3 macros to be strung together successively, since there are 3 optional arguments requested.
The first macro absorbs an optional plus 2 mandatory arguments, and then it must invoke a second macro, and here is the key, as its final action! The reason the 2nd macro invocation must be the final action of the first macro is that the 2nd macro must absorb an optional argument. If there are any tokens in the first macro that follow the invocation of the 2nd macro in the chain, then those other tokens in the 1st macro will be absorbed as the argument, instead of what is intended.
Likewise, the 2nd macro must call on the 3rd macro as its final action.
Certain quirky highlights:
if the successive macro is to be called inside of an
if
block, one mustexpandafter
prior to the successive macro, so that the successive macro doesn't try to absorb theelse
orfi
.In this particular case, the final optional argument, the label, is only callable if a caption has been specified. Thus, the 2nd macro in the chain will only call on the 3rd macro if a caption has been specified. Otherwise, it will truncate the sequence.
The MWE:
documentclass{article}
usepackage{graphicx}
newcommandaddtofigtoks[1]{expandafterfigtoksexpandafter
{thefigtoks#1}}
newtoksfigtoks
newcommandfigCapLab[3][htbp]{%
figtoks{begin{figure}[#1]}
addtofigtoks{centering}
addtofigtoks{includegraphics[width=#2textwidth]{#3}}
optcap
}
newcommandoptcap[1][relax]{%
ifxrelax#1relax
addtofigtoks{end{figure}}
thefigtoks
else
addtofigtoks{caption{#1}}%
expandafterlabelopt
fi
}
newcommandlabelopt[1][relax]{%
ifxrelax#1relaxelseaddtofigtoks{label{#1}}fi
addtofigtoks{end{figure}}
thefigtoks
}
begin{document}
figCapLab{.2}{example-image-a}
figCapLab{.2}{example-image-b}[My caption]
figCapLab{.2}{example-image-c}[My caption][fg:label1]
figCapLab[p]{.2}{example-image}[Other caption][fg:label2]
In figures ref{fg:label1} and ref{fg:label2}...
end{document}
Ack-shu-ally, the more I think of it, tokens are not even needed:
documentclass{article}
usepackage{graphicx}
newcommandfigCapLab[3][htbp]{%
begin{figure}[#1]
centering
includegraphics[width=#2textwidth]{#3}
optcap
}
newcommandoptcap[1][relax]{%
ifxrelax#1relax
end{figure}
else
caption{#1}%
expandafterlabelopt
fi
}
newcommandlabelopt[1][relax]{%
ifxrelax#1relaxelselabel{#1}fi
end{figure}
}
begin{document}
figCapLab{.2}{example-image-a}
figCapLab{.2}{example-image-b}[My caption]
figCapLab{.2}{example-image-c}[My caption][fg:label1]
figCapLab[p]{.2}{example-image}[Other caption][fg:label2]
In figures ref{fg:label1} and ref{fg:label2}...
end{document}
edited Mar 5 at 1:33
answered Mar 4 at 3:33
Steven B. SegletesSteven B. Segletes
158k9204411
158k9204411
As I tried to point out: Unlike most people asking questions, I don't just want a solution to copy without understanding, but instead I want to learn what I did wrong to use that knowledge for the future. Sorry, I'm pre-Google generation ;-)
– U. Windl
Mar 5 at 1:17
1
@U.Windl I will add some context to my answer.
– Steven B. Segletes
Mar 5 at 1:22
@U.Windl Please see my added commentary.
– Steven B. Segletes
Mar 5 at 1:34
Imagine (the truth!) that the user asking the question only knows LaTeX, and not TeX, so the user wonders whatoptcap
,relax
andexpandafter
really do. I guessifx
,else
, andfi
are TeX's low-level conditionals. When describing the processing in an abstract way (as you do), some concrete examples (e.g. actual arguments) would be helpful for the TeX Dummies like me.
– U. Windl
Mar 5 at 1:51
@U.Windloptcap
is just the 2nd macro I created in the string of 3 macros.relax
is a macro that does nothing---however, it can be tested for, which is how I use it here. The sequenceexpandaftermacroothermacro
causesothermacro
to be expanded once, beforemacro
is executed. So,expandafterlabeloptfi
causes thefi
to be executed (closing out the conditional), so thatlabelopt
will absorb the next token in the input stream, rather than absorbing thefi
. Indeed,ifx...else...fi
is one type of TeX conditional that compares the next 2 tokens WITHOUT expansion
– Steven B. Segletes
Mar 5 at 1:59
|
show 3 more comments
As I tried to point out: Unlike most people asking questions, I don't just want a solution to copy without understanding, but instead I want to learn what I did wrong to use that knowledge for the future. Sorry, I'm pre-Google generation ;-)
– U. Windl
Mar 5 at 1:17
1
@U.Windl I will add some context to my answer.
– Steven B. Segletes
Mar 5 at 1:22
@U.Windl Please see my added commentary.
– Steven B. Segletes
Mar 5 at 1:34
Imagine (the truth!) that the user asking the question only knows LaTeX, and not TeX, so the user wonders whatoptcap
,relax
andexpandafter
really do. I guessifx
,else
, andfi
are TeX's low-level conditionals. When describing the processing in an abstract way (as you do), some concrete examples (e.g. actual arguments) would be helpful for the TeX Dummies like me.
– U. Windl
Mar 5 at 1:51
@U.Windloptcap
is just the 2nd macro I created in the string of 3 macros.relax
is a macro that does nothing---however, it can be tested for, which is how I use it here. The sequenceexpandaftermacroothermacro
causesothermacro
to be expanded once, beforemacro
is executed. So,expandafterlabeloptfi
causes thefi
to be executed (closing out the conditional), so thatlabelopt
will absorb the next token in the input stream, rather than absorbing thefi
. Indeed,ifx...else...fi
is one type of TeX conditional that compares the next 2 tokens WITHOUT expansion
– Steven B. Segletes
Mar 5 at 1:59
As I tried to point out: Unlike most people asking questions, I don't just want a solution to copy without understanding, but instead I want to learn what I did wrong to use that knowledge for the future. Sorry, I'm pre-Google generation ;-)
– U. Windl
Mar 5 at 1:17
As I tried to point out: Unlike most people asking questions, I don't just want a solution to copy without understanding, but instead I want to learn what I did wrong to use that knowledge for the future. Sorry, I'm pre-Google generation ;-)
– U. Windl
Mar 5 at 1:17
1
1
@U.Windl I will add some context to my answer.
– Steven B. Segletes
Mar 5 at 1:22
@U.Windl I will add some context to my answer.
– Steven B. Segletes
Mar 5 at 1:22
@U.Windl Please see my added commentary.
– Steven B. Segletes
Mar 5 at 1:34
@U.Windl Please see my added commentary.
– Steven B. Segletes
Mar 5 at 1:34
Imagine (the truth!) that the user asking the question only knows LaTeX, and not TeX, so the user wonders what
optcap
, relax
and expandafter
really do. I guess ifx
, else
, and fi
are TeX's low-level conditionals. When describing the processing in an abstract way (as you do), some concrete examples (e.g. actual arguments) would be helpful for the TeX Dummies like me.– U. Windl
Mar 5 at 1:51
Imagine (the truth!) that the user asking the question only knows LaTeX, and not TeX, so the user wonders what
optcap
, relax
and expandafter
really do. I guess ifx
, else
, and fi
are TeX's low-level conditionals. When describing the processing in an abstract way (as you do), some concrete examples (e.g. actual arguments) would be helpful for the TeX Dummies like me.– U. Windl
Mar 5 at 1:51
@U.Windl
optcap
is just the 2nd macro I created in the string of 3 macros. relax
is a macro that does nothing---however, it can be tested for, which is how I use it here. The sequence expandaftermacroothermacro
causes othermacro
to be expanded once, before macro
is executed. So, expandafterlabeloptfi
causes the fi
to be executed (closing out the conditional), so that labelopt
will absorb the next token in the input stream, rather than absorbing the fi
. Indeed, ifx...else...fi
is one type of TeX conditional that compares the next 2 tokens WITHOUT expansion– Steven B. Segletes
Mar 5 at 1:59
@U.Windl
optcap
is just the 2nd macro I created in the string of 3 macros. relax
is a macro that does nothing---however, it can be tested for, which is how I use it here. The sequence expandaftermacroothermacro
causes othermacro
to be expanded once, before macro
is executed. So, expandafterlabeloptfi
causes the fi
to be executed (closing out the conditional), so that labelopt
will absorb the next token in the input stream, rather than absorbing the fi
. Indeed, ifx...else...fi
is one type of TeX conditional that compares the next 2 tokens WITHOUT expansion– Steven B. Segletes
Mar 5 at 1:59
|
show 3 more comments
Here is how you can achieve your goal using xparse
:
documentclass{article}
usepackage{graphicx,xparse}
% figCapLab
% [<float spec>] #1
% {<width factor>} #2
% {<image>} #3
% [<caption>] #4
% [<label>] #5
NewDocumentCommand{figCapLab}{ O{htbp} m m o o }{%
begin{figure}[#1]
centering
includegraphics[width=#2linewidth]{#3}% Set image at width
IfValueT{#4}
{caption{#4}IfValueT{#5}{label{#5}}}% Set possible caption and label
end{figure}
}
begin{document}
figCapLab{.2}{example-image-a}
figCapLab{.2}{example-image-b}[My caption]
figCapLab{.2}{example-image-c}[My caption][fg:label1]
figCapLab[p]{.2}{example-image}[Other caption][fg:label2]
In figures ref{fg:label1} and ref{fg:label2}ldots
end{document}
Optional arguments with a default is specified using O{<default>}
while optional arguments without a default uses o
. Conditioning on whether or not a value is supplied is done using IfValueTF{<parameter>}{<true>}{<false>}
. There are also singular conditionals IfValueT
and IfValueF
, the former of which was used above.
The above code assumes that an empty caption (blank fourth argument) would not need a label
(fifth) argument. If that's needed, move the IfValueT{#5}{label{#5}}
out of the <true>
branch inside IfValueT{#4}
:
NewDocumentCommand{figCapLab}{ O{htbp} m m o o }{%
begin{figure}[#1]
centering
includegraphics[width=#2textwidth]{#3}% Set image at width
IfValueT{#4}{caption{#4}}% Possible caption
IfValueT{#5}{label{#5}}% Possible label
end{figure}
}
add a comment |
Here is how you can achieve your goal using xparse
:
documentclass{article}
usepackage{graphicx,xparse}
% figCapLab
% [<float spec>] #1
% {<width factor>} #2
% {<image>} #3
% [<caption>] #4
% [<label>] #5
NewDocumentCommand{figCapLab}{ O{htbp} m m o o }{%
begin{figure}[#1]
centering
includegraphics[width=#2linewidth]{#3}% Set image at width
IfValueT{#4}
{caption{#4}IfValueT{#5}{label{#5}}}% Set possible caption and label
end{figure}
}
begin{document}
figCapLab{.2}{example-image-a}
figCapLab{.2}{example-image-b}[My caption]
figCapLab{.2}{example-image-c}[My caption][fg:label1]
figCapLab[p]{.2}{example-image}[Other caption][fg:label2]
In figures ref{fg:label1} and ref{fg:label2}ldots
end{document}
Optional arguments with a default is specified using O{<default>}
while optional arguments without a default uses o
. Conditioning on whether or not a value is supplied is done using IfValueTF{<parameter>}{<true>}{<false>}
. There are also singular conditionals IfValueT
and IfValueF
, the former of which was used above.
The above code assumes that an empty caption (blank fourth argument) would not need a label
(fifth) argument. If that's needed, move the IfValueT{#5}{label{#5}}
out of the <true>
branch inside IfValueT{#4}
:
NewDocumentCommand{figCapLab}{ O{htbp} m m o o }{%
begin{figure}[#1]
centering
includegraphics[width=#2textwidth]{#3}% Set image at width
IfValueT{#4}{caption{#4}}% Possible caption
IfValueT{#5}{label{#5}}% Possible label
end{figure}
}
add a comment |
Here is how you can achieve your goal using xparse
:
documentclass{article}
usepackage{graphicx,xparse}
% figCapLab
% [<float spec>] #1
% {<width factor>} #2
% {<image>} #3
% [<caption>] #4
% [<label>] #5
NewDocumentCommand{figCapLab}{ O{htbp} m m o o }{%
begin{figure}[#1]
centering
includegraphics[width=#2linewidth]{#3}% Set image at width
IfValueT{#4}
{caption{#4}IfValueT{#5}{label{#5}}}% Set possible caption and label
end{figure}
}
begin{document}
figCapLab{.2}{example-image-a}
figCapLab{.2}{example-image-b}[My caption]
figCapLab{.2}{example-image-c}[My caption][fg:label1]
figCapLab[p]{.2}{example-image}[Other caption][fg:label2]
In figures ref{fg:label1} and ref{fg:label2}ldots
end{document}
Optional arguments with a default is specified using O{<default>}
while optional arguments without a default uses o
. Conditioning on whether or not a value is supplied is done using IfValueTF{<parameter>}{<true>}{<false>}
. There are also singular conditionals IfValueT
and IfValueF
, the former of which was used above.
The above code assumes that an empty caption (blank fourth argument) would not need a label
(fifth) argument. If that's needed, move the IfValueT{#5}{label{#5}}
out of the <true>
branch inside IfValueT{#4}
:
NewDocumentCommand{figCapLab}{ O{htbp} m m o o }{%
begin{figure}[#1]
centering
includegraphics[width=#2textwidth]{#3}% Set image at width
IfValueT{#4}{caption{#4}}% Possible caption
IfValueT{#5}{label{#5}}% Possible label
end{figure}
}
Here is how you can achieve your goal using xparse
:
documentclass{article}
usepackage{graphicx,xparse}
% figCapLab
% [<float spec>] #1
% {<width factor>} #2
% {<image>} #3
% [<caption>] #4
% [<label>] #5
NewDocumentCommand{figCapLab}{ O{htbp} m m o o }{%
begin{figure}[#1]
centering
includegraphics[width=#2linewidth]{#3}% Set image at width
IfValueT{#4}
{caption{#4}IfValueT{#5}{label{#5}}}% Set possible caption and label
end{figure}
}
begin{document}
figCapLab{.2}{example-image-a}
figCapLab{.2}{example-image-b}[My caption]
figCapLab{.2}{example-image-c}[My caption][fg:label1]
figCapLab[p]{.2}{example-image}[Other caption][fg:label2]
In figures ref{fg:label1} and ref{fg:label2}ldots
end{document}
Optional arguments with a default is specified using O{<default>}
while optional arguments without a default uses o
. Conditioning on whether or not a value is supplied is done using IfValueTF{<parameter>}{<true>}{<false>}
. There are also singular conditionals IfValueT
and IfValueF
, the former of which was used above.
The above code assumes that an empty caption (blank fourth argument) would not need a label
(fifth) argument. If that's needed, move the IfValueT{#5}{label{#5}}
out of the <true>
branch inside IfValueT{#4}
:
NewDocumentCommand{figCapLab}{ O{htbp} m m o o }{%
begin{figure}[#1]
centering
includegraphics[width=#2textwidth]{#3}% Set image at width
IfValueT{#4}{caption{#4}}% Possible caption
IfValueT{#5}{label{#5}}% Possible label
end{figure}
}
answered Mar 4 at 5:52
WernerWerner
447k699891695
447k699891695
add a comment |
add a comment |
Thanks for contributing an answer to TeX - LaTeX Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f477627%2fhow-to-define-a-macro-with-multiple-optional-parameters%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
4
Instead of identifying mistakes in your code without much context, can you provide information on what you want to achieve ultimately? Perhaps there are better ways of achieving it.
– Werner
Mar 4 at 2:39
please extend your code fragment to complete but small document!
– Zarko
Mar 4 at 2:40
@Werner: There are always different ways to reach a goal, but if you change the way too frequently, you'll never make it. So I'd prefer a fix for my "solution" over a completely new attempt (like xparse).
– U. Windl
Mar 4 at 2:57
2
it is much easier for people to debug and help you if you provide the code as a complete document that can be run, here you just provide fragments, don't say what error you get and expect people to construct an example and guess that they get the same error that you meant to ask about.
– David Carlisle
Mar 4 at 7:53
1
you have only defined one optional argument, arguments 4 and 5 are mandatory but you are testing if they are
{}
– David Carlisle
Mar 4 at 7:55