1

Good day,

I would like to write a macro (using Latex3/expl3) that posts pictures and can take several key-value parameters for generated labels/captions etc. I already found code that can do this, but passing generated labels does NOT work.

It started in this thread: \ref with macro as parameter

This is code of macro that takes general key-value parameters for placing a plot(works if I use \addpic with hardcoded label):

%usage example
% \addpic{
  % width=0.3,
  % image=example-image,
  % caption={This is an example image, and a comma in the caption},
  % label=one,
% }

% \addpic{
  % placement=bp,
  % width=0.2,
  % options={angle=90},
  % image=example-image-a,
  % caption=Rotated image,
  % shortcaption=In the text the image is rotated!,
  %}


\ExplSyntaxOn

% the user level command
\NewDocumentCommand{\addpic}{m}
 {
  \group_begin: % localize the changes to the variables
  \simonson_pic:n { #1 }
  \group_end:
 }

% the key-value interface
\keys_define:nn { simonson/pic }
 {
  placement .tl_set:N = \l_simonson_pic_placement_tl,
  placement .initial:n = htp,
  width .tl_set:N = \l_simonson_pic_width_tl,
  width .initial:n = 1,
  options .tl_set:N = \l_simonson_pic_options_tl,
  image .tl_set:N = \l_simonson_pic_image_tl,
  caption .tl_set:N = \l_simonson_pic_caption_tl,
  shortcaption .tl_set:N = \l_simonson_pic_shortcaption_tl,
  label .tl_set:N = \l_simonson_pic_label_tl,
 }

% the main command
\cs_new_protected:Nn \simonson_pic:n
 {
  % set the keys from the argument
  \keys_set:nn { simonson/pic } { #1 }
  % start the figure environment
  \__simonson_start_figure:V \l_simonson_pic_placement_tl
  \centering
  % include the image
  \__simonson_pic_image:VVV
    \l_simonson_pic_width_tl % the text width fraction
    \l_simonson_pic_options_tl % other options
    \l_simonson_pic_image_tl % the image name
  % the caption
  \tl_if_empty:NTF \l_simonson_pic_shortcaption_tl
   {
    \caption{\l_simonson_pic_caption_tl}
   }
   {
    \caption[\l_simonson_pic_shortcaption_tl]{\l_simonson_pic_caption_tl}
   }
   % the label
     In \ addpics \ macro:\ label=\l_simonson_pic_label_tl %for test(my code)
   \tl_if_empty:NF \l_simonson_pic_label_tl
    {
     \label{\l_simonson_pic_label_tl}
    }
   % end the figure environment

   %### %for parser to ignore
   \end{figure}
   %###
}

% syntactic sugar: we want some token lists to be expanded before usage
\cs_new_protected:Nn \__simonson_start_figure:n
 {
  \begin{figure}[#1]
 }
\cs_generate_variant:Nn \__simonson_start_figure:n { V }

\cs_new_protected:Nn \__simonson_pic_image:nnn
 {
  \includegraphics[width=#1\textwidth,#2]{#3}
 }
\cs_generate_variant:Nn \__simonson_pic_image:nnn { VVV }

\ExplSyntaxOff

Key requirement is that the code generating labels is located in one place (as macro) b/c I need to refer to generated labels and same code will be used when referencing using several key-valued parameters.

Here is what egreg suggested in first thread (I didn't indicate the final goal though there):

    \documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn
\keys_define:nn { strider/label }
 {
  Re        .tl_set:N = \l_strider_label_Re_tl, 
  DOF       .tl_set:N = \l_strider_label_DOF_tl,
  statsName .tl_set:N = \l_strider_label_statsName_tl,
  Re        .value_required:n = true,
  DOF       .value_required:n = true,
  statsName .value_required:n = true,
 }

\NewDocumentCommand{\keyref}{m}
 {
  \strider_label:Nn \ref { #1 }
 }
\NewDocumentCommand{\keylabel}{m}
 {
  \strider_label:Nn \label { #1 }
 }

\cs_new_protected:Nn \strider_label:Nn
 {
  \keys_set:nn { strider/label } { #2 }
  #1 % will be \label or \ref
   {
    fig \c_colon_str 
    \tl_item:Nn \l_strider_label_statsName_tl { 1 }
    \tl_item:Nn \l_strider_label_statsName_tl { 2 }
    \tl_item:Nn \l_strider_label_statsName_tl { 3 }
    Re \l_strider_label_Re_tl DOF \l_strider_label_DOF_tl
   }
 }
\ExplSyntaxOff

\begin{document}

\section{X}
\keylabel{statsName=Strouhal,DOF=4M, Re=50} % just to experiment

This gives a number: \keyref{statsName=Strouhal,DOF=4M, Re=50} (should be 1)

\end{document}

This does work, though don't see how to use \keylabel for passing label as parameter to \addpic. \addpic macro will rearly be used on document level (rather used in other macros that will generate label and caption) hence I would like to keep it as general as possible. Document level macro: sample call:

\myplotFF{file={\plotsPathReFifty/StrouhalNumber},Re=50,DOF=4M, statsName=Drag}

Macro:

\keys_define:nn{myPlot}{ 
Re .tl_set:N = \l_myPlot_Re_tl, 
DOF .tl_set:N = \l_myPlot_DOF_tl,
statsName .tl_set:N = \l_myPlot_statsName_tl,
file .tl_set:N = \l_myPlot_file_tl,
height .tl_set:N = \l_myPlot_height_tl,
label .tl_set:N = \l_myPlot_label_tl,
path .tl_set:N = \l_myPlot_path_tl

}

\NewDocumentCommand{\myplotFF}{m}{
        \keys_set:nn{myPlot}{#1}
            \addpic{

                width=0.6,
                %height=0.6,
                image=\l_myPlot_file_tl,
                caption=\getCaption{statsName=\l_myPlot_statsName_tl,
                DOF=\l_myPlot_DOF_tl, 
                Re=\l_myPlot_Re_tl},
                     label=\keylabel{statsName=\l_myPlot_statsName_tl,DOF=\l_myPlot_DOF_tl,Re=\l_myPlot_Re_tl},
            }
}

line with 'label=...' doesn't work of course, but if I pass a hardcoded label there it all works. Thats what the first thread all about. Thank you!

  • Sorry, I can't understand what the final goal is. – egreg May 20 '16 at 17:23
  • You probably ask about motivation? If yes, then the one goal is to be able to change plot captions in one place (i.e. only change onemacro that generate captions). Also using few macros allows to keep all paths in one place and therefore change it easily if necessary. Another is that placing a plot with single line like this: \myplotFF{file=\plotsPathReFifty/StrouhalNumber},Re=50,DOF=4M, statsName=Drag} improves readability for me. – strider May 20 '16 at 19:56
  • No, I'm asking for the proposed syntax and the intended output. – egreg May 20 '16 at 19:57
  • Can you give example? still don't understand – strider May 20 '16 at 19:58
  • You should give an example of what the macros should do. – egreg May 20 '16 at 20:16
2

If I've understood what you want correctly, then you would like the following input

\section{X}
\myplotFF{file={example-image-a},Re=50,DOF=4M, statsName=Drag}

to produce the following output

target output (possible)

The code as you currently have it is a mess. You seem to have multiple modules for one package/class/ and multiple sub-modules for handling the same options/data. This is not a good strategy. The whole point of the expl3 syntax is consistency and a hodgepodge of muddled sets of key-value pairs makes for wildly inconsistent code.

Note that this is not a criticism of the original answers. It is, rather, a problem created by attempting to throw quite different instantiations of related functions at each other in the hope that things will stick together to form a harmonious whole. This is a bit like letting a bunch of two-year olds lose in your living room with three or four sets of nicely coordinated paints which clash horribly in combination and hoping the toddlers' sense of aesthetics will combine them into a pleasing result. The fault does not lie in any of the coordinated colour schemes, but in the attempt to combine them using toddler methodology. The result ain't going to be pretty.

I've standardised on strider as the main module with strider/pic as the sole sub-module. I'm not sure the sub-module is helpful or necessary, but I don't know what the bigger picture is, so perhaps there is a reason for it.

% arara: pdflatex
\pdfminorversion=7
\documentclass{article}
\usepackage{xparse,graphicx}
\ExplSyntaxOn
% the key-value interface
\keys_define:nn {strider/pic} {
  Re .tl_set:N = \l_strider_label_Re_tl,
  DOF .tl_set:N = \l_strider_label_DOF_tl,
  statsName .tl_set:N = \l_strider_label_statsName_tl,
  file .tl_set:N = \l_strider_file_tl,
  height .tl_set:N = \l_strider_height_tl,
  Re        .value_required:n = true,
  DOF       .value_required:n = true,
  statsName .value_required:n = true,
  caption .tl_set:N = \l_strider_pic_caption_tl,
  placement .tl_set:N = \l_strider_pic_placement_tl,
  placement .initial:n = htp,
  width .tl_set:N = \l_strider_pic_width_tl,
  width .initial:n = 0.6,
  options .tl_set:N = \l_strider_pic_options_tl,
  shortcaption .tl_set:N = \l_strider_pic_shortcaption_tl,
}
\cs_new_protected:Nn \strider_label:N
{
  #1 % will be \label or \ref
  {
    fig \c_colon_str
    \tl_item:Nn \l_strider_label_statsName_tl { 1 }
    \tl_item:Nn \l_strider_label_statsName_tl { 2 }
    \tl_item:Nn \l_strider_label_statsName_tl { 3 }
    Re \l_strider_label_Re_tl DOF \l_strider_label_DOF_tl
  }
}
% the main command
\cs_new_protected:Nn \strider_pic:
{
  % start the figure environment
  \__strider_start_figure:V \l_strider_pic_placement_tl
  \centering
  % include the image
  \__strider_pic_image:VVV
  \l_strider_pic_width_tl % the text width fraction
  \l_strider_pic_options_tl % other options
  \l_strider_file_tl % the image name
  % the caption
  \tl_if_empty:NT \l_strider_pic_caption_tl
  {
    \tl_set:Nn \l_strider_pic_caption_tl
    {
      statsName:~\l_strider_label_statsName_tl,~DOF:~\l_strider_label_DOF_tl,~Re:~\l_strider_label_Re_tl
    }
  }
  \tl_if_empty:NTF \l_strider_pic_shortcaption_tl
  {
    \caption{\l_strider_pic_caption_tl}
  }
  {
    \caption[\l_strider_pic_shortcaption_tl]{\l_strider_pic_caption_tl}
  }
  \strider_label:N \label
  % end the figure environment

  %### %for parser to ignore
\end{figure}
%###
}
% syntactic sugar: we want some token lists to be expanded before usage
\cs_new_protected:Nn \__strider_start_figure:n
{
  \begin{figure}[#1]
}
\cs_generate_variant:Nn \__strider_start_figure:n { V }

\cs_new_protected:Nn \__strider_pic_image:nnn
{
  \includegraphics[width=#1\textwidth,#2]{#3}
}
\cs_generate_variant:Nn \__strider_pic_image:nnn { VVV }

\NewDocumentCommand{\myplotFF}{m}{
  \group_begin:
    \keys_set_known:nn { strider / pic } {#1}
    \strider_pic:
  \group_end:
}
\NewDocumentCommand{\keyref}{m}
{
  \keys_set_known:nn { strider / pic } { #1 }
  \strider_label:N \ref
}
\ExplSyntaxOff

\begin{document}
\section{X}
\myplotFF{file={example-image-a},Re=50,DOF=4M, statsName=Drag}

See figure \keyref{Re=50,DOF=4M, statsName=Drag}

\myplotFF{file={example-image-b},Re=48,DOF=4M, statsName=Drag,caption={Override caption in this case} }

See figure \keyref{Re=50,DOF=4M, statsName=Drag} and \keyref{Re=48,DOF=4M, statsName=Drag}
\end{document}

EDIT

The above code has been edited to demonstrate that the label is being automatically generated already! To show this, I've added back a very slightly modified version of \keyref so that the label can easily be illustrated in the output.

EDIT EDIT

Further edit to streamline a bit more and provide optional override for caption.

example with references and override

  • Yes, your example is what I need to achieve. Thank you for code suggestions! I am new to expl3 and didn't even use latex for last 10 years or so, feel free to suggest anything. The main issue now what to do with labeling. The last 3 parameters in myplotFF determine image uniquely, therefore I'm trying to use them for generating unique label. Another approach is to pass a hardcoded label each time. – strider May 21 '16 at 10:53
  • BTW, addpic macro code I took from internet, can barely understand it. Therefore I put it separately in first post. – strider May 21 '16 at 10:59
  • "Note that really, there is too much substantive stuff in the code for the user-level macro, so there is plenty of room here for further stream-lining and standardisation" Not sure I understand this. Should I provide more information? If so, which one? – strider May 21 '16 at 11:08
  • @strider My code automatically generates the label from those 3 pieces of information already, so I'm not really sure what further you are trying to do. I've edited the example a bit to make this clearer, but I've left the label-auto-generation alone as I don't understand what is missing. – cfr May 21 '16 at 12:23
  • @strider It is mostly that I think one of the expl3 people will tell me off for setting the caption in the document-level command rather than in the underlying macros. Since I don't understand expl3 well at all yet, I'm a bit cautious about using it in answers. Especially when egreg's code is being used in the question ;). – cfr May 21 '16 at 12:26

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.