2

For my template I want to create possibility to define a number of authors and automatically create a respective number of variables/commands which save the name of any author and make it able to print them later. The code has to be in the preamble.

For creating different variables/commands of the kind \varname{...} which can be printed with \printvarname, I, rather randomly, found the following code here on tex.SE which works fine:

\makealetter
\newcommand{\NewVariable}[1]{%
    \expandafter\newcommand\csname #1\endcsname[1]{\@namedef{@#1}{##1}}
    \@namedef{@#1}{}
    \expandafter\newcommand\csname print#1\endcsname{%
        \ifthenelse{\equal{\csname @#1\endcsname}{}}{}{\csname @#1\endcsname}}
}
\makeatother

Now I want to create a loop which automatically creates a predefined number of these commands of the syntax \authorone, \authortwo etc. For the definition of the numbers of authors I use a simple command: \newcommand{\setauthors}[1]{\def\numauthors{#1}}. One problem seems to be that a commandname can't take integers, but i need integers to define the number of authors. So i use the fmtcount package to convert them into words.

I tried different approaches from this website (foreach, @for, xintFor), but couldn't get one to work properly.

Here is a MWE with some approaches. I commented my test loops and added a simple example how it should be used:

\documentclass[%
]{article}
\usepackage[T1]{fontenc}

\usepackage{ifthen} \makeatletter % command to set multiple persistent variables with content \newcommand{\NewVariable}[1]{% \expandafter\newcommand\csname #1\endcsname[1]{@namedef{@#1}{##1}} @namedef{@#1}{} \expandafter\newcommand\csname print#1\endcsname{% \ifthenelse{\equal{\csname @#1\endcsname}{}}{}{\csname @#1\endcsname}} } \makeatother

\usepackage{pgffor} \usepackage{fmtcount} % to convert integers in words: e.g. 1 into one \usepackage{xinttools}

\newcommand{\setauthors}[1]{\def\numauthors{#1}}

\setauthors{3} %set number of authors

%\foreach \x in {1,...,\numauthors}{% % \NewVariable{author\numberstringnum{\x}} % }

%\xintFor #1 in {\xintSeq{1}{\numauthors}} \do {\NewVariable{author\numberstringnum{#1}}} % also tried the starred version \xintFor*

%\makeatletter % I know the definition of a range from 1 to \numauthors is not correct, its just a placeholder %@for\authorcount:1,...,\numauthors\do{\NewVariable{author\numberstringnum{\authorcount}}} %\makeatother

\NewVariable{authorone} % example for defined variable/command and how it should work

\authorone{Jimmy Buckets}

\begin{document}

\printauthorone

\end{document}

There seem to be much more variants of loops from other packages (also expl3 versions, which I understand even less), but I would prefer a solution with a minimum on required extra packages. Nevertheless, in the end it's important that it works. The future user should only add a number to \setauthors and fill out the generated author variables. All other stuff should be automated. It may also be an option to asign the authornumber to an array first...

I guess it's an expansion problem of the nested commands (NewVariable, numberstring..., loop). Unfortunately, the expansion sequence/order still remains a sealed book for me, because it differs so much from most programming languages I'm (a little bit) used to.

EDIT

To use @Alans great answer including the orcid. The expl3 command \cs_new_protected:Nn \l_lukeflo_get_orcid:n for creating a valid Orcid works better using a pre-defined command for getting the Orcid link including the symbol:

\usepackage{orcidlink}
\newcommand{\orcidid}[1]{\orcidlink{#1}\space\href{https://orcid.org/#1}{#1}}
...
\cs_new_protected:Nn \l_lukeflo_get_orcid:n {
    \tl_set:Nx \l_tmpa_tl {\seq_item:Nn \l_lukeflo_orcid_seq {#1}}
    \orcidid{\l_tmpa_tl}\par
}

Otherwise, href will add an unnecessary .pdf extension to the link. See my comments below Alans answer.

I am already grateful for your help

lukeflo
  • 1,555
  • 2
    Why do you think it's helpful to have these separate commands as opposed to having, e.g. a simple comma separated list of authors that gets processed? What use would they be put to? And even if you need access to specific authors, an array-like data structure like the expl3 sequence or property lists would likely be a simpler implementation. – Alan Munn Jun 07 '23 at 20:51
  • So it might be helpful to clarify in your question what your end (user) goal is, and perhaps we can suggest a simpler implementation than the one you're currently struggling with. – Alan Munn Jun 07 '23 at 21:36
  • You use the package fmtcount whose macros don't work in the preamble due to multi-language support where language is to be detected at the begin of the document-environment. Besides this \foreach does every iteration in a local scope, so macro-definitions won't survive outside \foreach. – Ulrich Diez Jun 07 '23 at 21:54
  • Hi. The need to define different authors is that they have to be recalled later in an automatically built section in which every author appears seperatley with affiliation, mail adress etc. Thus, a comma seperated list or a list with \and is not enough. OK, the language aspsct makes sense. I'm open for every other way ;) \foreach l, then, seems to be out... – lukeflo Jun 07 '23 at 22:11
  • @lukeflo That doesn't require separate commands. You can store reuse the list. And that's not a user level problem. – Alan Munn Jun 07 '23 at 22:34
  • In the besteht case, the template should be used to produce journal articles. But, becauae most authors have no experience with LaTeX and the editors in our company are mostly student assistants I want to keep complicated looking macros away from them. Therefore, I thought it a better idea to define some simple looking commands for them and put the code in the .cls file. – lukeflo Jun 08 '23 at 05:58

2 Answers2

4

Your stated goal in the question and in comments is to reduce the complexity of the markup that the users of your package or class need to use. To my mind then, creating multiple different command names is very counterintuitive from a user's point of view. So here's an approach that achieves the same goal, but with maximally simple user syntax: commands to enter authors, affiliations, and emails as simple comma delimited lists, some formatting commands implemented with a key-value setup command (which may or may not need to be user facing) and a command to print the elements. I've used expl3 methods which are built into the LaTeX kernel, so there are no extra packages involved. This answer is intended to show the kind of thing that can be quite easily done with these methods.

In this example, the only user-facing commands that are required are the \authors, \affiliations, and \emails commands. The rest may or may not need to be user facing depending on your actual use case. Since authors often have multiple affiliations, they can be entered as a list surrounded by braces. I've also added an \orcids command and shown code to produce the ORCID logo and link.

\documentclass{article}
\usepackage{orcidlink}
\ExplSyntaxOn
% First define three sequences for storing the data
\seq_new:N \l_lukeflo_authors_seq
\seq_new:N \l_lukeflo_email_seq
\seq_new:N \l_lukeflo_affil_seq
\seq_new:N \l_lukeflo_orcid_seq 
% Now set up some keys for modifying the output if needed
\keys_define:nn{mypackage}{
    author.tl_set:N = \l_lukeflo_authformat_tl,
    affil.tl_set:N = \l_lukeflo_affilformat_tl,
    email.tl_set:N = \l_lukeflo_emailformat_tl,
}
% User command to set the formatting keys.
\NewDocumentCommand{\authorSetup}{m}{
    \keys_set:nn {mypackage} {#1}}

% Set up the defaults \authorSetup{author=\itshape,affil={},email=\ttfamily}

% User command to enter authors (comma separated) \NewDocumentCommand{\authors}{m}{ \seq_set_from_clist:Nn \l_lukeflo_authors_seq {#1} } % User command to enter affiliations (comma separated; multiple affiliations with {}) \NewDocumentCommand{\affiliations}{m}{ \seq_set_from_clist:Nn \l_lukeflo_affil_seq {#1} } % User command to enter emails \NewDocumentCommand{\emails}{m}{ \seq_set_from_clist:Nn \l_lukeflo_email_seq {#1} } \NewDocumentCommand{\orcids}{m}{ \seq_set_from_clist:Nn \l_lukeflo_orcid_seq {#1} } % internal command to map print affiliations \cs_new_protected:Nn \lukeflo_get_affils:n { \clist_set:Nx \l_tmpa_clist {\seq_item:Nn \l_lukeflo_affil_seq {#1}} \clist_map_inline:Nn \l_tmpa_clist {\l_lukeflo_affilformat_tl {##1}\par} } \cs_new_protected:Nn \lukeflo_get_orcid:n { \tl_set:Nx \l_tmpa_tl {\seq_item:Nn \l_lukeflo_orcid_seq {#1}} \orcidlink{\l_tmpa_tl}\space\l_lukeflo_emailformat_tl \href{https\c_colon_str//orcid.org/\l_tmpa_tl/}{\l_tmpa_tl}\par }

% internal command to print an author/affiliations/email block \cs_new_protected:Nn \lukeflo_map_author_info:nn { {\parindent=0pt {\l_lukeflo_authformat_tl\seq_item:Nn \l_lukeflo_authors_seq {#1}\par} {\lukeflo_get_affils:n {#1}} {\l_lukeflo_emailformat_tl\seq_item:Nn \l_lukeflo_email_seq {#1}\par} {\lukeflo_get_orcid:n {#1}} \vspace{\baselineskip} } } % User command to print all the author blocks \NewDocumentCommand \printauthors {} { \seq_map_indexed_function:NN \l_lukeflo_authors_seq \lukeflo_map_author_info:nn } \ExplSyntaxOff \authors{Margaret Atwood, Zadie Smith, Madeleine Thien} \affiliations{University of Toronto,{New York University,Columbia University},Brooklyn College} \emails{ma@utoronto.ca, zs@nyu.edu,mt@brooklyn.cuny.edu} \orcids{0000-0000-0000-0000,0000-0000-0000-0000,0000-0000-0000-0000}

\begin{document} \printauthors \authorSetup{author=\bfseries} \printauthors \end{document}

output of code

Alan Munn
  • 218,180
  • Hi Alan, that works really great! expl3 syntax looks a lot more familiar for somebody who is mainly using Bash. I'll definitely take a deeper look into it, since it seems to avoid mainly the hard to understand parts of \expandafter etc. One thing: when I use your example, all lines are indented unless I use a command like \setlength\parindent{0pt}. Can I simply inject a similar standard-Latex-command into the author/affiliations/email block to turn off parindent for the author-section only, even if the rest of the document uses parindent for new paragraphs? – lukeflo Jun 09 '23 at 08:20
  • I edited your command (by copying your workflow ;) ) to add a line for the ORCID of the authors and everything works fine. To format the ORCID with the fitting symbol and as a hyperlink I used the orcidlink package and created a new command: \newcommand{\orcidid}[1]{\orcidlink{#1}\space\href{https://orcid.org/#1}{#1}}. It works fine if I call it somewhere in my document, but is it possible to include it in your authors command. E.g. in the {\l_dai_orcidformat_tl\seq_item:Nn \l_dai_orcid_seq {#1}\par} which i added? Maybe this should be a new question, if so, tell me – lukeflo Jun 09 '23 at 13:57
  • 1
    @lukeflo I've updated the answer to show how to put in the orcid link properly. I've also wrapped the author block in {...} with a 0pt \parindent. But further modifications should really be asked as a new question. :) You can never really escape expansion issues, but expl3 definitely makes them a lot simpler to deal with. – Alan Munn Jun 09 '23 at 15:52
  • Hi Alan, thank you very uich. I'm just getting started to understand expl3, but surely am inspired by all possibilities. For further questions I will open a new topic. – lukeflo Jun 09 '23 at 19:50
  • Just to check, if I understood it correctly: With \tl_set:Nx \l_tmpa_tl {\seq_item:Nn \l_lukeflo_orcid_seq {#1} you define a temporal variable (as token list) \l_tempa_tl which takes the Argument of the orcid-input command and is then used inside the following \orcidlink and \href command to create the formatted Orcid-entry. – lukeflo Jun 10 '23 at 11:58
  • 1
    @lukeflo No, that's not how the syntax of expl3 works. The macro \tl_set:Nx takes two arguments, on unbraced (the N argument), which in this case is the token list that will be set, and a second braced argument the x argument which will be expanded (hence the x). So the x causes what's inside the {...} to be expanded, and in that case it returns the element of the sequence \l_lukeflo_orcid_seq that corresponds whatever #1 at the time. This element is then put into the \l_tmpa_tl macro for further use. – Alan Munn Jun 10 '23 at 16:10
  • OK. Thanks for the explanation. I still look at it too offen with other script/programming languages in mind. Now I got it; at least in this case ;) – lukeflo Jun 10 '23 at 18:24
  • Hi Alan, just tested it with your new code. So far, everything compiles fine, but the link attached to the Orcid number itself using \href has an .pdf file ending set to the end of the URL. Therefore, the link doesn't work when I click on it. From where in the code this fileending appears? I have no clue. See the picture added to my initial question. Thanks – lukeflo Jun 11 '23 at 18:12
  • From the href link colors on your screenshot, I guess it's the same with your MWE. But it seems its a common behaviour of the package: tex.SE. If i try it outside the expl3 defined commands, href links an Orcid page properly. So maybe it's from the expansion process during the expl3 commands running. No need for you to post a solution, just wanted to add this experience ;) – lukeflo Jun 11 '23 at 18:22
  • It works if I use my self-defined Orcid link insider your get_orcid command. See my edit above. I guess it's because, in so doing, the command is processed (expanded?) before the expl3 command kicks in. But I'm not sure :D Anyway, many thanks again for your solution. expl3 is now a real püossibility for me, since the sytax is much more straightforward. I'm going to work through a detailed tutorial soon – lukeflo Jun 11 '23 at 18:59
  • 1
    @lukeflo The problem is with the catcodes inside expl3. I've updated the answer now, thanks to Why does hyperref exhibit this behaviour on generated URLs?. – Alan Munn Jun 11 '23 at 19:58
  • @lukeflo I've also renamed the functions defined with cs_new... which shouldn't have the l_ prefix according to proper Expl3 naming conventions. – Alan Munn Jun 11 '23 at 20:17
  • Of course, colon is special marker of expl3 Syntax. Should've come to my mind earlier. Thank you again. But now there seems to be a fixed Orcid in your codes href command and not the token list. – lukeflo Jun 11 '23 at 20:45
  • @lukeflo Oops, left over from my testing. Fixed. – Alan Munn Jun 11 '23 at 21:15
  • 1
    Hi, i just added a little if-condition to your \dai_get_orcid:n command to surpress the Orcid line if it's undefined: \tl_if_empty:NTF \l_tmpa_tl {} {\orcidlink{\l_tmpa_tl}\space\l_dai_emailformat_tl \href{https\c_colon_str//orcid.org/\l_tmpa_tl/}{\l_tmpa_tl}\par}. I hope the syntax is correct, works fine anyway :) – lukeflo Jun 13 '23 at 21:37
1

Problem 1:

You use the package fmtcount whose macros don't work in the preamble due to multi-language support where language is to be detected at the begin of the document-environment.

Problem 2:

pgffor's \foreach does every iteration in a local scope, so macro-definitions don't survive outside \foreach.

Problem 3:

You need to handle the circumstance that \NewVariable doesn't expand its argument when passing it to the replacement-text of a \newcommand where it is not carried out immediately but at the time when the command defined in terms of \newcommand is carried out.

Problem 4:

The snippet

 \@namedef{@#1}{}
 \expandafter\newcommand\csname print#1\endcsname{%
   \ifthenelse{\equal{\csname @#1\endcsname}{}}{}{\csname @#1\endcsname}}

seems somewhat pointless:
The control-sequence coming from \csname @#1\endcsname is initialized empty.
The \print...-command via \ifthenelse checks whether that control-sequence delivers emptiness and if so via branching delivers emptiness, which could as well come from expanding that control-sequence, without checking. ;-)



I understand that you prefer commands to strings denoting names of variables: Commands are not affected by things like \uppercase/lowercase while strings denoting names of variables might be. (E.g. \uppercase might turn a variablename-string "author 1" into "AUTHOR 1", which might be a problem.)


If you don't mind defining variables inside the document-environment, you can try s.th. like this:

\documentclass[%
]{article}

\usepackage{xinttools} \usepackage{fmtcount} \FCloadlang{english}

% command to set multiple persistent variables with content \makeatletter @ifdefinable\NewVariable{% \DeclareRobustCommand{\NewVariable}[1]{% \expandafter@NewVariable \csname #1\expandafter\endcsname \csname @#1\expandafter\endcsname \csname print#1\endcsname {#1}% }% }% \newcommand@NewVariable[4]{% \newcommand#1[1]{\renewcommand{#2}{##1}}% \newcommand#2{}% % In the \else-branch you could have an error-message about undefined variable #4. \newcommand*#3{\ifdefined#2\else\expandafter@gobble\fi#2}% }% \makeatother

\begin{document}

\newcommand{\setauthors}[1]{\def\numauthors{#1}}% \setauthors{3}% set number of authors \xintFor* #1 in {\xintSeq{1}{\numauthors}} \do {% \storenumberstringnum{scratchy}{#1}% \NewVariable{author\FMCuse{scratchy}}% }%

\authorone{Me} \authortwo{myself} \authorthree{I}

\printauthorone, {\printauthortwo} and \printauthorthree.

\end{document}

enter image description here

But I don't like this because this way names of control sequences depend on the language in use while introducing variables so that you need to ensure proper language setting whenever introducing another variable.


If you don't mind having strings denoting variable-names and loading the package textcase and using that package's command \NoCaseChange for preventing \MakeUppercase/\MakeLowercase changing letter-casing of names of variables in those edge situations where this might be needed, then you can do s.th. like this:

\documentclass{article}

\makeatletter %%=============================================================================== %% Obtain control sequence token from name of control sequence token: %%=============================================================================== %% \CsNameToCsToken<stuff not in braces>{NameOfCs} %% -> <stuff not in braces>\NameOfCs %% (<stuff not in braces> may be empty.) @ifdefinable\CsNameToCsToken{% \long\def\CsNameToCsToken#1#{\romannumeral\InnerCsNameToCsToken{#1}}% }% \newcommand\InnerCsNameToCsToken[2]{% \expandafter\Exchange\expandafter{\csname#2\endcsname}{@stopromannumeral#1}% }% @ifdefinable@stopromannumeral{\chardef@stopromannumeral=`^^00}% \newcommand\Exchange[2]{#2#1}% \makeatother

\CsNameToCsToken\newcommand{Author 1}{Me} \CsNameToCsToken\newcommand{Author 2}{MYSELF} \CsNameToCsToken\newcommand*{Author 3}{I}

\begin{document} \CsNameToCsToken{Author 1}, \MakeLowercase{\CsNameToCsToken{Author 2}} and \CsNameToCsToken{Author 3}.

\CsNameToCsToken\renewcommand{Author 1}{Myself} \CsNameToCsToken\renewcommand{Author 2}{I} \CsNameToCsToken\renewcommand*{Author 3}{me}

\CsNameToCsToken{Author 1}, \CsNameToCsToken{Author 2} and \CsNameToCsToken{Author 3}.

\end{document}

enter image description here


If you need to maintain a database with records of data for each author, consider using the package datatool.

Ulrich Diez
  • 28,770
  • Thanks Ulrich. I wanted to avoid using commands inside the documenta, because it could confuse possible authors. Both oft your solutions seem to work very good, so i mark the question as solved. But, as you said, it might be better to take a look at the datatool package. As long as it hell me to keep code away from the document itself, it could be very helpful. There always seems to be a LaTeX package I havn't heard of, mostly enlighting but sometimes also frustrating :D – lukeflo Jun 08 '23 at 06:03