Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

In the subcaption package, the default way to create subfigures is when the subfigure environment:

\documentclass{article}
\usepackage{caption}
\usepackage{subcaption}
\begin{document}
\begin{figure}
 \centering
 \begin{subfigure}{0.4\textwidth}
  \centering
  Subfigure1 is located here
  \caption{Subfigure1 caption}
  \label{subfig:1}
 \end{subfigure}
 \begin{subfigure}{0.4\textwidth}
  \centering
  Subfigure2 is located here
  \caption{Subfigure2 caption}
  \label{subfig:2}
 \end{subfigure}
 \caption{Figure caption}
 \label{fig}
\end{figure}
\end{document}

I like this syntax, especially in that the figure comes first, then the caption, then the label for the caption. However, I don't like having to specify the \textwidth within the environment. I would prefer that this parameter be optional with the default value being the width of the figure.

One solution to fix this problem is to use a savebox, but then source for the figure is (slightly) divorced from its primary location of use.

The subcaption package provides a solution for this problem via the \subcaptionbox command:

\documentclass{article}
\usepackage{caption}
\usepackage{subcaption}
\begin{document}
\begin{figure}
 \centering
 \subcaptionbox{Subfigure1 caption\label{subfig:1}}{
  \centering
  Subfigure1 is located here}
 \subcaptionbox{Subfigure2 caption\label{subfig:2}}{
  \centering
  Subfigure2 is located here}
 \caption{Figure caption}
 \label{fig}
\end{figure}
\end{document}

Now specifying \textwidth is optional and defaults to the width of the figure, but the source is much less readable. I dislike this solution for several reasons:

  1. It is easier to write, read, and understand large amounts of code placed within \begin{}...\end{} then within \someCommand{...}. (It is so easy to use the wrong number of curly braces.)
  2. The figure, caption, and label out written out of order from the way they are typeset (by default), which is the order in which I think about them.
  3. The location to place the caption is not explicit. (It is just a required value to the \subcaptionbox placed between some {}'s, so a fellow coauthor that doesn't know this syntax won't know how to add a caption if it is currently blank.)

Question:

Is there a way to get the best of both worlds? How can I get behavior equivalent to using the subfigure environment but with the specification of \textwidth being optional (and defaulting to the width of the figure)?

(This question is about the subcaption package because it seems that this is the best package for subfigures. I am open to suggestions that another package is better and fulfills my requirements.)

share|improve this question

1 Answer 1

This doesn't support all the features of \subcaptionbox: the optional argument <inner-pos> can't be expressed.

The optional argument to the xsubcaption environment is the explicit width of the subfigure.

\documentclass{article}
\usepackage{caption}
\usepackage{subcaption}
\usepackage{environ}

\makeatletter
\environfinalcode{}
\NewEnviron{xsubcaption}[1][-1sp]
 {\let\xsc@theoptcaption\@empty
  \let\xsc@thecaption\@empty
  \let\xsc@thelabel\@empty
  \expandafter\xsc@checkcaption\BODY\caption\xsc@checkcaption
  \expandafter\xsc@checklabel\BODY\label\xsc@checklabel
  \begingroup\edef\x{\endgroup
    \noexpand\subcaptionbox
      \ifx\xsc@theoptcaption\@empty\else
        [\unexpanded\expandafter{\xsc@theoptcaption}]%
      \fi
      {\unexpanded\expandafter{\xsc@thecaption}%
       \ifx\xsc@thelabel\@empty\else
         \noexpand\label{\unexpanded\expandafter{\xsc@thelabel}}%
       \fi} 
      \ifdim#1=-1sp % no optional argument
      \else
        [#1]%
      \fi
      {\unexpanded{\renewcommand\caption[2][]{}\renewcommand{\label}[1]{}}%
       \unexpanded\expandafter{\BODY}}}\x
}

\long\def\xsc@checkcaption#1\caption#2\xsc@checkcaption{%
  \if\relax\detokenize{#2}\relax
    \expandafter\@gobble
  \else
    \expandafter\@firstofone
  \fi
  {\expandafter\xsc@getcaption\BODY\xsc@getcaption}%
}
\long\def\xsc@getcaption#1\caption#2#3\xsc@getcaption{%
  \if[\detokenize{#2}%
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  {\expandafter\xsc@getcaptionopt\BODY\xsc@getcaptionopt}%
  {\def\xsc@thecaption{#2}}%
}
\long\def\xsc@getcaptionopt#1\caption[#2]#3#4\xsc@getcaptionopt{%
  \def\xsc@theoptcaption{#2}%
  \def\xsc@thecaption{#3}%
}
\long\def\xsc@checklabel#1\label#2\xsc@checklabel{%
  \if\relax\detokenize{#2}\relax
    \expandafter\@gobble
  \else
    \expandafter\@firstofone
  \fi
  {\expandafter\xsc@getlabel\BODY\xsc@getlabel}%
}
\long\def\xsc@getlabel#1\label#2#3\xsc@getlabel{%
  \def\xsc@thelabel{#2}%
}
\makeatother

\begin{document}
\begin{figure}
\centering
\begin{xsubcaption}
\centering
Subfigure1 is located here
\caption{Subfigure1 caption}\label{subfig:1}
\end{xsubcaption}
\begin{xsubcaption}
\centering
Subfigure2 is located here
\end{xsubcaption}
\begin{xsubcaption}[3cm]
\centering
Subfigure3 is located here
\caption[A]{BBB}
\end{xsubcaption}
\caption{Figure caption}
\label{fig}
\end{figure}

\ref{subfig:1} \ref{fig}
\end{document}

enter image description here

I use the environ package to collect the environment's body in the macro \BODY. Then I check whether \caption appears in the body; if so I check whether it has an optional argument. In caption (and the "list caption", if present) is stored in a macro and then the whole \subcaptionbox instruction is built from the various parts (when the body is typeset, \caption is redefined to do nothing).

(Added) The same checks are performed to catch a \label command outside the argument of \caption (both ways are supported, of course).

Finally the \subcaptionbox macro with all arguments determined with the code above is executed. It appears to be complicated, but it's not really difficult: it's a standard way to catch the presence of some token (\caption and \label) in a list of tokens, in this case the contents of the environment, which is available as the expansion of \BODY.

share|improve this answer
    
I want the subfigure label to appear after the subfigure caption but also don't want you to "introduce more checks". The code you have in the preamble is already completely unreadable. –  Tyson Williams Nov 9 '12 at 3:52
    
@TysonWilliams "I want" seems quite unpolite. The code is complex looking, because one has to look for a possibly missing \caption. It's no big deal to cope also with \label outside the argument of \caption. –  egreg Nov 9 '12 at 9:37
    
What should I say instead of "want"? –  Tyson Williams Nov 9 '12 at 16:25
    
"I would like", for example. :) However, the present version allows for \label to be outside the argument of \caption. –  egreg Nov 9 '12 at 16:36
    
Re: your addition. This code may be understandable to you, but it is completely opaque to me. Now, I certainly admit that I don't know as much LaTeX as I would like (though TEX.SE has taught me an incredible amount). However, the solution "I would like" needs to be understandable to my coauthors as well. If you put this functionality into a package, wrote documentation for it, and got it into TeX Live, then this answer would be acceptable. –  Tyson Williams Nov 10 '12 at 0:21

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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