2

I am trying to learn how to do typesetting (delaying typesetting until later in the document), and am doing an example with recreating the figure. It is used like this:

\begin{myfigure}
  \mycaption{Foo}
\end{myfigure}

I would simply like to take that \mycaption{Foo} and place it into the \caption at the proper place in the document.

The full MWE is here:

\documentclass[a4paper]{article}

\usepackage[english]{babel}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{expl3}
\usepackage{xparse}
\usepackage{tikz}
\usepackage{xifthen}

\ExplSyntaxOn

\begin{document}

\NewDocumentEnvironment{myfigure}{mm} {
  \tl_new:N \l_myfigure_mycaption_tl

  \NewDocumentCommand{\mycaption}{m} {
    \tl_set:Nn \l_myfigure_mycaption_tl {#1}
  }

  \begin{figure}
    \begin{tikzpicture}
}{
    \end{tikzpicture}
    % a latex3 way of doing if-statement would be helpful to know.
    \ifthenelse{\isempty{\l_myfigure_mycaption_tl}}
      {}
      {\caption{\l_myfigure_mycaption_tl}}
  \end{figure}
}

\begin{myfigure}
  \mycaption{Foo}
\end{myfigure}

\end{document}

In that, I try creating the API \mycaption like this:

\tl_new:N \l_myfigure_mycaption_tl

\NewDocumentCommand{\mycaption}{m} {
  \tl_set:Nn \l_myfigure_mycaption_tl {#1}
}

I am trying to make this tl variable a local variable to the environment myfigure. I am trying to make it so it's not created globally outside of the environment, yet I want to use it in the "after block" of the environment.

Then I check if that variable is blank, and if it is not blank, I typeset the caption.

The goal is to be able to:

  1. Define an API variable (e.g. \mycaption) to use for setting a value in an environment, and
  2. Plugging that value into the appropriate place later in the document flow.
  3. Not sure if nesting the \NewDocumentCommand is correct (as per the comments under this answer: How to define nested environments and commands in LaTeX3)

This is essentially a getter/setter approach, so I'm wondering if this is the correct approach or there is a better alternative. If there is a better alternative that would be good to know, otherwise just knowing how to accomplish it using the approach I have outlined is helpful as well.

The caption shows up blank in my MWE.

10
  • By using the \l_.... syntax the 'variables' aren't local. You have to use the \....set:... macros and \group_begin: and \group_end: to keep their values local, i.e. in a group. \tl_new:N in a macro that is called repeatedly is bad, however, since it will complain at the 2nd usage – user31729 Mar 25 '18 at 20:44
  • 2
    if you find yourself nesting \NewDocumentCommand calls then most likely something is wrong, that should almost always just be the declaration of the top level document syntax. – David Carlisle Mar 25 '18 at 20:51
  • @DavidCarlisle I was wondering about that in the comments here tex.stackexchange.com/a/423113/26407. I was able to get it working with nested \NewDocumentCommand, but don't understand (a) why that works and (b) why that's wrong. Maybe related to tex.stackexchange.com/questions/422985/… – Lance Pollard Mar 25 '18 at 20:52
  • 1
    \tl_if_empty:NF \l_myfigure_my_caption {foo} is what you want for the \ifthenelse ... test – user31729 Mar 25 '18 at 20:53
  • 1
    There's another problem here: Using \tl_set:Nn inside of the doubled nested figure\tikzpicture won't work to make the content of the \l_myfigure...tl variable alive after \end{tikzpicture}, since \mycaption is effectively called inside the tikzpicture environment. It is local, but then lost after tikzpicture – user31729 Mar 25 '18 at 21:05
2

I suggest to use an optional third argument that holds the caption title and testing with \IfValueT{#3}{...}.

No need for \tl... code, in my point of view...

Also possible: Apply a key-value interface here for providing more options.

\documentclass[a4paper]{article}

\usepackage[english]{babel}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{expl3}
\usepackage{xparse}
\usepackage{tikz}
\usepackage{xifthen}





\NewDocumentEnvironment{myfigure}{mmo} {%

  #1%
  \begin{figure}
    \begin{tikzpicture}
    }{%
    \end{tikzpicture}
    % a latex3 way of doing if-statement would be helpful to know.
    \IfValueT{#3}{%
      \caption{#3}%
    }%
  \end{figure}
}


\begin{document}


\begin{myfigure}{Stuff}{}[foo]
\end{myfigure}


\begin{myfigure}{Otherstuff}{}
\end{myfigure}


\end{document}

Alternative solution, by storing the arguments locally

\documentclass[a4paper]{article}

\usepackage[english]{babel}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{expl3}
\usepackage{xparse}
\usepackage{tikz}
\usepackage{xifthen}

\ExplSyntaxOn

\tl_new:N \l_lance_mycaption_tl

\NewDocumentCommand{\mycaption}{+m}{%
  \tl_gset:Nn \l_lance_mycaption_tl {#1}
}

\NewDocumentEnvironment{myfigure}{mm} {%
  \tl_set_eq:NN \l_tmpa_tl \l_lance_mycaption_tl
  \begin{figure}
    \begin{tikzpicture}
    }{%
    \end{tikzpicture}
    \tl_if_empty:NF \l_lance_mycaption_tl {\caption{\tl_use:N \l_lance_mycaption_tl}}
  \end{figure}
  \tl_gset_eq:NN \l_lance_mycaption_tl  \l_tmpa_tl
}

\ExplSyntaxOff


\begin{document}


\begin{myfigure}{Stuff}{}
  \mycaption{Foo}
\end{myfigure}


\ExplSyntaxOn
Value\space of\space variable: \tl_use:N \l_lance_mycaption_tl
\ExplSyntaxOff

\begin{myfigure}{Otherstuff}{}
  \mycaption{Foobar}
\end{myfigure}


\end{document}
2
  • 1
    @LancePollard the variable approach is what I showed (although for the particular case of caption using an argument as in this answer would be better) – David Carlisle Mar 25 '18 at 21:24
  • @LancePollard: See the update at the end of the post please – user31729 Mar 25 '18 at 21:32
1

Something like this perhaps

\documentclass[a4paper]{article}

\usepackage[english]{babel}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}

\usepackage{xparse}
\usepackage{tikz}


\ExplSyntaxOn

\NewDocumentCommand{\mycaption}{m} {
    \tl_gset:Nn \g_myfigure_mycaption_tl {#1}
}

\NewDocumentEnvironment{myfigure}{}{
    \tl_set:Nn \l_myfigure_mycaption_tl {}
  \begin{figure}
    \begin{tikzpicture}
}{
    \end{tikzpicture}
    \tl_if_empty:NTF\g_myfigure_mycaption_tl
      {}
      {\caption{\g_myfigure_mycaption_tl}}
  \end{figure}
}

\ExplSyntaxOff

\begin{document}

\begin{myfigure}
  \mycaption{Foo}
\end{myfigure}

\end{document}
10
  • I would like for the \mycaption to only be usable/defined within the \myfigure scope. – Lance Pollard Mar 25 '18 at 21:09
  • @LancePollard Add a boolean to the start of the environment and test for that (cf. how things like \mathtt work). – Joseph Wright Mar 25 '18 at 21:11
  • @LancePollard that is almost certainly a mistake:-) You could define it like the standard \caption which explicitly tests that the current environment is a float and generates an error otherwise, but that just makes someone write a very popular capt-of package that is used in multiple answers here that simply subverts that test and lets you use captions anywhere. – David Carlisle Mar 25 '18 at 21:15
  • @LancePollard or as in Christian's answer don't have a \mycaption at all and make it an optional argument of the environment – David Carlisle Mar 25 '18 at 21:16
  • 1
    @LancePollard there are ways, but generally it is bad style to do so and it is better to make the commands globally defined whenever possible. – David Carlisle Mar 25 '18 at 21:19
1

You end up with \mycaption in the middle of a tikzpicture environment.

I'm not sure what the gain is in doing so and hiding tikzpicture which you can't pass options to.

Anyway, the simplest approach is to specify the caption at the beginning. Other options can be defined and set.

\documentclass[a4paper]{article}

\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[english]{babel}
\usepackage{expl3}
\usepackage{xparse}
\usepackage{tikz}

\ExplSyntaxOn

\keys_define:nn { pollard/figure }
 {
  caption      .tl_set:N = \l_myfigure_caption_tl,
  shortcaption .tl_set:N = \l_myfigure_caption_lof_tl,
  label        .tl_set:N = \l_myfigure_label_tl,
  align .choice:,
  align/left   .code:n = \raggedright,
  align/right  .code:n = \raggedleft,
  align/center .code:n = \centering,
 }

\NewDocumentEnvironment{myfigure}{O{}}
 {
  \begin{figure}[htp]
  \keys_set:nn { pollard/figure } { align=center, #1 }
  \begin{tikzpicture}
 }
 {
  \end{tikzpicture}
  \tl_if_empty:NF \l_myfigure_caption_tl
   {
    \tl_if_empty:NTF \l_myfigure_caption_lof_tl
     {
      \caption{\l_myfigure_caption_tl}
     }
     {
      \caption[\l_myfigure_caption_lof_tl]{\l_myfigure_caption_tl}
     }
    \tl_if_empty:NF \l_myfigure_label_tl { \label{\l_myfigure_label_tl} }
   }
  \end{figure}
}
\ExplSyntaxOff

\begin{document}

\listoffigures

\section{Examples}

\begin{myfigure}[
  caption=Foo,
]
\draw (0,0) -- (1,1);
\end{myfigure}

\begin{myfigure}[
  caption=Foo but with baz,
  shortcaption=Baz,
  label=foobaz,
  align=left,
]
\draw (0,0) -- (1,1);
\end{myfigure}

\end{document}

However, I can't see the advantage over

\begin{figure}[htp]
\centering

\begin{tikzpicture}
\draw (0,0) -- (1,1);
\end{tikzpicture}

\caption[Baz]{Foo but with baz}\label{foobaz}

\end{figure}

enter image description here

0

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.