6

This question has been significantly EDITED, as I learned more of the problem.


If I was good, I would be able to create macros with multiple optional arguments, but not only can't I, but in this case, I'm trying to adapt an existing structure. I wrote the boxhandler package to be able to tune figure/table captions layouts (in particular, to a strange style my organization has). That aside, the syntax for a figure is

\bxfigure[htbp]{caption}{figure content}

Someone has requested the ability to use \caption's optional argument, which this present structure does not allow. So I thought I could provide a syntax that works with the above syntax or with the following

\bxfigure[htbp]{[LOF caption]full caption}{figure content}

The code I thought would have done that is

\documentclass{article}
\usepackage{boxhandler}
\usepackage{etoolbox}
\makeatletter
\renewcommand\bx@caption[1]{%
\expandafter\optcaption\expandafter[#1]\relax%
}

\def\optcaption[#1]#2\relax{%
\ifstrempty{#2}{\caption{#1}}{\caption[\@gobble#1]{\striptrailingbracket#2}}}
%\ifstrempty{#2}{Full caption is \fbox{#1}}
%  {LOF caption is \fbox{\@gobble#1} Full caption is \fbox{\striptrailingbracket#2}}}

\def\striptrailingbracket#1]{#1}
\makeatother
\begin{document}
\listoffigures

%\def\x{my caption}
%\expandafter\optcaption\expandafter[\x]\relax

\def\x{[LOF caption]my caption}
%\expandafter\optcaption\expandafter[\x]\relax

\bxfigure[h]{my caption}{\fbox{TEXT}}
\bxfigure[h]{[LOF caption]my caption}{\fbox{TEXT}}
\bxfigure[h]{\x}{\fbox{TEXT}}
%\optcaption[[LOF caption]my caption]\relax
\end{document}

enter image description here

but as you see, the \optcaption parser does not extract the bracketed content from the argument, and always thinks the #1 argument of \def\optcaption[#1]#2\relax{} is without a right bracket.

To make the problem simpler, I studied the behavior of \optcaption specifically, apart from including it in \bxfigure. And here,

\documentclass{article}
\usepackage{boxhandler}
\usepackage{etoolbox}
\makeatletter
\renewcommand\bx@caption[1]{%
\expandafter\optcaption\expandafter[#1]\relax%
}

\def\optcaption[#1]#2\relax{%
%\ifstrempty{#2}{\caption{#1}}{\caption[\@gobble#1]{\striptrailingbracket#2}}}
\ifstrempty{#2}{Full caption is \fbox{#1}}
  {LOF caption is \fbox{\@gobble#1} Full caption is \fbox{\striptrailingbracket#2}}}

\def\striptrailingbracket#1]{#1}
\begin{document}

All of these work...\par
\bx@caption{[LOF caption]my caption}\par
\def\x{my caption}
\bx@caption{\x}\par
\def\x{[LOF caption]my caption}\par
\bx@caption{\x}\par
~\\The following does not work (and boxhandler passes the caption via a 
csname; this is the root of the problem)\par
\bx@caption{\csname x\endcsname}\par
~\\This fixes it, but it requires me to change the code that calls bx@caption\par
\expandafter\bx@caption\expandafter{\csname x\endcsname}
\end{document}

enter image description here

we see that it is able to detect the ] and extract the optional argument, even when that argument is passed by way of a macro \x, but not when it is passed as \csname x\endcsname.


So I suppose I can go back and change the boxhandler calls to \bx@caption to pre-expand the \csname as I did in my last example (see my answer below).

But if anyone can rewrite \bx@caption to be able to properly digest a \csname argument (without patching \ReciteFigure), I will accept that answer.

  • If it doesn't need to be plain tex, I would recommend using the xparse package and a syntax which is more LaTeX "standard" style with two optional parameters: \bxfigure[htbp][LOF caption]{full caption}{figure content}. Of course this would need to be able to handle usage such as \bxfigure[][LOF caption]{full caption}{figure content}. – Peter Grill Feb 12 '14 at 16:31
  • @PeterGrill I may consider that possibility in the final evaluation, but am still interested in finding out why #1 is characteristically different than \x. And it certainly doesn't need to be plain tex. – Steven B. Segletes Feb 12 '14 at 16:35
  • #1 will contain the first argument supplied. \x may be passed as-is, a single token. – Werner Feb 12 '14 at 16:42
5

LaTeX optional arguments are a delimited argument defined via \def\foo[#1]{...} (unless you define them via latex3 declarations) so [] are not matched and the argument ends at the first ]

\optcaption[[LOF caption]my caption]\relax

#1 is [LOF caption

To use [] in an optional argument you need

 \optcaption[{[LOF caption]my caption}]\relax

The LaTeX3 declarations define the command to use a slower, more careful way of picking up the optional argument that does match [].

It is not clear what

\caption[\@gobble#1]

is intended to do but it almost certainly does the wrong thing.

If #1 is [LOF caption]full caption then the above is

\caption[\@gobble[LOF caption]full caption]

so the optional argument of \caption is \@gobble[LOF caption and \@gobble will gobble the [ however the following mandatory argument of \caption is then just f. and ull caption] are left as trailing tokens that do whatever they do.


In your edited question you need to expand twice, or simpler expand until there is an unexpandable token before looking for [ so

\renewcommand\bx@caption[1]{%
\expandafter\optcaption\expandafter[\romannumeral-`x#1]\relax%
}
  • I think the problem is actually more complex than I present it above, and that the guts of boxhandler may come into play, in which case I should probably delete the question. In particular, \bxfigure[h]{{[LOF caption]my caption}}{\fbox{TEXT}} still does not work, while \bx@caption{\x} does work. That leads me to believe the issue is in the guts of boxhandler, in the way that the caption argument is stored and later recalled. – Steven B. Segletes Feb 12 '14 at 16:49
  • @StevenB.Segletes \x is a single token which you pass from internal macro to another as a token, but #1 is not a token it is a placeholder for the tokens passed in and unless you brace it as {#1} when passing to an internal macro that macro will get the first token (only) of the original argument, not the whole of #1. – David Carlisle Feb 12 '14 at 16:58
  • @StevenB.Segletes see update – David Carlisle Feb 12 '14 at 17:15
  • \@gobble#1 eats the leading [ from an argument of the form [LOF caption. See my update. Upon further digging, I recall that boxhandler provides the caption in a \csname x\endcsname manner, which I now believe is the root of the problem. – Steven B. Segletes Feb 12 '14 at 17:36
  • @StevenB.Segletes it does, but it makes the main caption just be a single letter. I added some code following your edit to the question – David Carlisle Feb 12 '14 at 17:45
2

Well, once I determined the problem was with digesting a \csname that was passed to \bx@caption, I determined that one solution was to patch boxhandler to pre-expand the argument that was being passed. Using the definitions in my first example, and patching the calls to \bx@caption took care of it.

If anyone else can solve this problem without resorting to xpatch, I will accept that answer.


David's edit of using the \romannumeral trick,

\renewcommand\bx@caption[1]{%
\expandafter\optcaption\expandafter[\romannumeral-`x#1]\relax%
}

instead of what I have in this answer, alleviates the need for xpatch, so he gets the points! Thank you, David.


\documentclass{article}
\usepackage{xpatch}% http://ctan.org/pkg/etoolbox
\usepackage{boxhandler}
\usepackage{etoolbox}
\makeatletter
\xpatchcmd{\ReciteFigure}% <cmd>
  {\bx@caption}% <search>
  {\expandafter\bx@caption\expandafter}% <replace>
  {}{}% <success><failure>

\xpatchcmd{\ReciteTable}% <cmd>
  {\bx@caption}% <search>
  {\expandafter\bx@caption\expandafter}% <replace>
  {}{}% <success><failure>

\renewcommand\bx@caption[1]{%
\expandafter\optcaption\expandafter[#1]\relax%
}

\def\optcaption[#1]#2\relax{%
\ifstrempty{#2}{\caption{#1}}{\caption[\@gobble#1]{\striptrailingbracket#2}}}

\def\striptrailingbracket#1]{#1}
\makeatother
\begin{document}
\listoffigures
\bxfigure[ht]{my caption}{\fbox{TEXT}}
\bxfigure[ht]{[LOF caption]my caption}{\fbox{TEXT}}
\end{document}

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.