2

How can I put something that is generated at the end of an environment into the beginning of the environment? I suspect it's either very simple or rather tricky, but I have no idea how to do it.

Example rendering

MWE:

\documentclass{article}

\usepackage{etoolbox}

\makeatletter

%% floruit handlers:

\newcommand\fl@years{}

\newcommand\fl@handler[1]{
    \ifnumcomp{#1}{<}{\fl@from}{\renewcommand\fl@from{#1}}{}
    \ifnumcomp{#1}{>}{\fl@to}{\renewcommand\fl@to{#1}}{}
}

\newcommand\fl@output{
    \newcommand\fl@from{10000}
    \newcommand\fl@to{0}
    \forlistloop{\fl@handler}{\fl@years}
    \textit{\ifnumcomp{\fl@from}{=}{\fl@to}{fl.~\fl@to}{fl.~\fl@from--\fl@to}}
}

\newbool{fl_called}

\newcommand\floruit[1]{#1\booltrue{fl_called}\listadd\fl@years{#1}}

%% person environment:

\newcounter{person}

\newenvironment{person}[1]{
    \refstepcounter{person}
    \renewcommand\fl@years{}
    \textbf{\theperson:~#1}\par
}{
    \ifbool{fl_called}{\par\fl@output}{}\par
}

\makeatother

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{document}

\begin{person}{Old MacDonald}
Old MacDonald had a farm in \floruit{1923}, but appears to have sold it by \floruit{1940}.
\end{person}

\begin{person}{Gary Cooper}
Gary Cooper starred in High Noon (\floruit{1952})...
\end{person}

\begin{person}{John Doe}
Blah blah (\floruit{1912}), more blah (\floruit{1930}) \& finally blah (\floruit{1931}).
\end{person}

\begin{person}{Jane Doe}
No floruit here...
\end{person}

The floruit should be with the name like so:

\textbf{3: John Doe} \textit{fl. 1912--1931}\par
Blah blah (1912), more blah (1930) \& finally blah (1931).\par

\end{document}
1
  • You could use a label strategy, by writing labels to the .aux file which are evaluated during the 2nd run of the code (which is necessary in most cases)
    – user31729
    Commented Jan 5, 2017 at 6:10

2 Answers 2

2

Here's a first starter (to be improved later on, with more time)

The idea is to write the data for \fl@from and \fl@to as 'label' information to the .aux file (which is read again at the next compilation run) with zref - facilities and extract the stored information again with \zref@extract.

In speech of zref from and to are properties.

\documentclass{article}

\usepackage{etoolbox}
\usepackage{zref}


\makeatletter

%% floruit handlers:

\newcommand\fl@from{10000}
\newcommand\fl@to{0}
\newcommand\fl@years{}

\newcommand\fl@handler[1]{
    \ifnumcomp{#1}{<}{\fl@from}{\renewcommand\fl@from{#1}}{}
    \ifnumcomp{#1}{>}{\fl@to}{\renewcommand\fl@to{#1}}{}
}


\newcommand{\fl@output}[1]{%
    \renewcommand\fl@from{10000}
    \renewcommand\fl@to{0}
    \forlistloop{\fl@handler}{\fl@years}
    \zref@ifrefundefined{#1}{}{%
      \ifnum\zref@extract{#1}{from}=\fl@from
      \else
      \ifnum\zref@extract{#1}{from}=\zref@extract{#1}{to}
      \textit{\zref@extract{#1}{to}}%
      \else
      \textit{fl.~\zref@extract{#1}{from}--\zref@extract{#1}{to}}%
      \fi
      \fi
    }
}


\newbool{fl_called}

\newcommand\floruit[1]{#1\booltrue{fl_called}\listadd\fl@years{#1}}
\zref@newprop{from}[-1]{\fl@from}
\zref@newprop{to}[-1]{\fl@to}

\newcommand{\florlabel}[1]{%
\zref@labelbyprops{#1}{from,to}%
}



%% person environment:

\newcounter{person}

\newenvironment{person}[1]{
  \refstepcounter{person}%
  \edef\@oldcurrentlabel{\@currentlabel}
  \renewcommand\fl@years{}
  \textbf{\theperson:~#1} \fl@output{flordata:\number\value{person}}\par
}{%
  \ifbool{fl_called}{\par\fl@output{flordata:\number\value{person}}}{}\par
  %Restore the label data in case we have some \refstepcounter after \refstepcounter{person}
  \edef\@currentlabel{\@oldcurrentlabel}
  \florlabel{flordata:\number\value{person}}%
}

\makeatother

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{document}

\begin{person}{Old MacDonald}
Old MacDonald had a farm in \floruit{1923}, but appears to have sold it by \floruit{1940}.
\end{person}

\begin{person}{Gary Cooper}
Gary Cooper starred in High Noon (\floruit{1952})...
\end{person}

\begin{person}{John Doe}
Blah blah (\floruit{1912}), more blah (\floruit{1930}) \& finally blah (\floruit{1931}).
\end{person}

\begin{person}{Jane Doe}
No floruit here...
\end{person}

The floruit should be with the name like so:

\textbf{3: John Doe} \textit{fl. 1912--1931}\par
Blah blah (1912), more blah (1930) \& finally blah (1931).\par

\end{document}

I will try to improve the list-handling. The etoolbox boolean values are tricky, in my point of view.

enter image description here

**Some update with expl3 features for a 'cleaner' code

\documentclass{article}
\usepackage{xparse}
\usepackage{zref}

\makeatletter

\ExplSyntaxOn

\cs_generate_variant:Nn \prop_put:Nnn {Nxx}
\cs_generate_variant:Nn \int_set:Nn {Nx}


% Storage containers
\seq_new:N \l_meide_flordata_seq
\prop_new:N \l_meide_flordata_prop

% Clear the list
\NewDocumentCommand{\ClearFlordata}{}{%
  \seq_clear:N \l_meide_flordata_seq
}

\NewDocumentCommand{\floruit}{m}{%
  \seq_put_right:Nn \l_meide_flordata_seq {#1}
  \DisplayInlineFlorData{#1}%
}

\DeclareExpandableDocumentCommand{\ExtractListProp}{m}{%
  \prop_item:Nn \l_meide_flordata_prop {#1}%
}


\int_new:N \l_meide_from_int
\int_new:N \l_meide_to_int
\NewDocumentCommand{\ProcessFlorData}{}{%
  % Checking first whether there's something in the list at all
  \seq_if_empty:NTF \l_meide_flordata_seq {%
    \prop_put:Nnx \l_meide_flordata_prop {from} { 0 } 
    \prop_put:Nnx \l_meide_flordata_prop {to} { 0 } 
  }{%
    \prop_put:Nnx\l_meide_flordata_prop {from} { \seq_item:Nn \l_meide_flordata_seq {1} } 
    \prop_put:Nnx\l_meide_flordata_prop {to} { \seq_item:Nn \l_meide_flordata_seq {2} } 
    \int_compare:nNnTF {\seq_count:N \l_meide_flordata_seq } > {1} {%
      \prop_put:Nnx \l_meide_flordata_prop {to} {\seq_item:Nn \l_meide_flordata_seq {2}}
    }{
      \prop_put:Nnx \l_meide_flordata_prop {to} {\seq_item:Nn \l_meide_flordata_seq {1}}
    }
  }
  % Determine the min and max of the list and store it to the from or to properties
  \seq_map_inline:Nn \l_meide_flordata_seq {
    \int_set:Nx \l_tmpa_int {\prop_item:Nn \l_meide_flordata_prop {from} }
    \int_set:Nx \l_tmpb_int {\int_min:nn {##1} {\l_tmpa_int }}
    \prop_put:NnV \l_meide_flordata_prop { from } { \l_tmpb_int }
    \int_set:Nx \l_tmpa_int {\prop_item:Nn \l_meide_flordata_prop {to} }
    \int_set:Nx \l_tmpb_int {\int_max:nn {##1} {\l_tmpa_int }}
    \prop_put:NnV \l_meide_flordata_prop { to } { \l_tmpb_int }
  }
  \edef\@currentlabel{\@oldcurrentlabel}
  % Do not write a label if there's nothing to write, i.e. the sequence is empty!
  \seq_if_empty:NF \l_meide_flordata_seq {
    \florlabel{flordata:\number\value{person}}%
  }
}

\ExplSyntaxOff

% Defines the format for the numbers
\NewDocumentCommand{\FormatFlorData}{om}{%
  \IfValueTF{#1}{%
    \textit{fl.~#1 -- #2}%
  }{%
    \textit{#2}%
  }%
}


% A simple wrapper for the display of the inline data given with \floruit
\NewDocumentCommand{\DisplayInlineFlorData}{+m}{%
  (#1)%
}

% Display the numbers

\NewDocumentCommand{\DisplayFlorData}{m}{%
  % Check first if the reference is defined
  \zref@ifrefundefined{#1}{}{%
    \ifnum\zref@extract{#1}{from}=\zref@extract{#1}{to}
    \FormatFlorData{\zref@extract{#1}{from}}%
    \else
    \FormatFlorData[\zref@extract{#1}{from}]{\zref@extract{#1}{to}}%%
    \fi
  }
}

% zref - related code

% Define zref - properties and fill them with the `\prop` - key - value of the same name
% This could be done in a loop using expl3 - loops etc.

\zref@newprop{from}[-1]{\ExtractListProp{from}}
\zref@newprop{to}[-1]{\ExtractListProp{to}}

\newcommand{\florlabel}[1]{%
  \zref@labelbyprops{#1}{from,to}%
}

% Basic Environment and counter definition


\newcounter{person}

\NewDocumentEnvironment{person}{+m}{%
  \ClearFlordata%
  \refstepcounter{person}
  % Store the last label for future purposes
  \edef\@oldcurrentlabel{\@currentlabel}
  \textbf{\theperson:~#1} \DisplayFlorData{flordata:\number\value{person}}\par
}{%
  % Now evaluate the data given in the environment and write the label accordingly. 
  \ProcessFlorData%
}

\makeatother

\begin{document}
\begin{person}{Old MacDonald}
Old MacDonald had a farm in \floruit{1923}, but appears to have sold it by \floruit{1940}.
\end{person}
\begin{person}{Gary Cooper}
Gary Cooper starred in High Noon \floruit{1952}...
\end{person}

\begin{person}{John Doe}
Blah blah \floruit{1912}, more blah \floruit{1930} \& finally blah \floruit{1931}.
\end{person}

\begin{person}{Mr. Gumby}
Was active in \floruit{1900}, \floruit{1855}, \floruit{2017}, \floruit{1066}.
\end{person}


\begin{person}{Jane Doe}
No floruit here...
\end{person}

\end{document}
6
  • I will provide another handling of storing the data to a list later on...
    – user31729
    Commented Jan 5, 2017 at 6:53
  • I'm playing around with this solution and now have this, which works (but I'm unsure how "correct" it is): gist – I'd be interested in your comments.
    – meide
    Commented Jan 5, 2017 at 21:12
  • @meide: Commented there
    – user31729
    Commented Jan 5, 2017 at 21:53
  • @meide: I've updated my code using expl3 features -- the code is cleaner now, in my point of view, however, I did not use your changes from the gist
    – user31729
    Commented Jan 6, 2017 at 12:22
  • Thanks again! I'm not at all familiar with expl3, but it looks interesting. I'll keep this in mind if I run into issues. I'm wary of touching my doc now while it works :)
    – meide
    Commented Jan 6, 2017 at 13:12
1

You can process the body of the environment person twice (thanks to environ). First time processing extracts the years, while the second time is to set the contents after setting the \fl@output.

The first processing happens when we store \BODY inside a temporary box. That means nothing gets set, but everything is "run through". To that end, we have to make the list additions and "from"/"to" storage global.

enter image description here

\documentclass{article}

\usepackage{etoolbox,environ}

\makeatletter

%% floruit handlers:

\newcommand\fl@years{}

\newcommand\fl@handler[1]{%
  \ifnumcomp{#1}{<}{\fl@from}{\gdef\fl@from{#1}}{}%
  \ifnumcomp{#1}{>}{\fl@to}{\gdef\fl@to{#1}}{}%
}

\newcommand\fl@output{%
  \gdef\fl@from{10000}%
  \gdef\fl@to{0}%
  \forlistloop{\fl@handler}{\fl@years}%
  \textit{\ifnumcomp{\fl@from}{=}{\fl@to}{fl.~\fl@to}{fl.~\fl@from--\fl@to}}%
}

\newbool{fl_called}

\newcommand\floruit[1]{#1\global\booltrue{fl_called}\listgadd\fl@years{#1}}

%% person environment:

\newcounter{person}

\NewEnviron{person}[1]{%
  \renewcommand\fl@years{}%
  \boolfalse{fl_called}%
  \sbox\@tempboxa{\BODY}% Process body once to capture \floruit's
  \refstepcounter{person}%
  \textbf{\theperson:~#1}\ifbool{fl_called}{~\fl@output}{}\par
  \BODY
  \par
}

\makeatother

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{document}

\begin{person}{Old MacDonald}
Old MacDonald had a farm in \floruit{1923}, but appears to have sold it by \floruit{1940}.
\end{person}

\begin{person}{Gary Cooper}
Gary Cooper starred in High Noon (\floruit{1952})...
\end{person}

\begin{person}{John Doe}
Blah blah (\floruit{1912}), more blah (\floruit{1930}) \& finally blah (\floruit{1931}).
\end{person}

\begin{person}{Jane Doe}
No floruit here...
\end{person}

The floruit should be with the name like so:

\textbf{3: John Doe} \textit{fl. 1912--1931}\par
Blah blah (1912), more blah (1930) \& finally blah (1931).\par

\end{document}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .