6

I'm trying to write a Tex command that automatically prints out attribution information for images I use in my document. I created 2 different commands. The first one is \nounattr (with 3 parameters) which takes the image's name, the author and the link of the image. The second command is \printnounattrs which has no arguments, but should simply print a list of all attributions added before using \nounattr. I was able to implement these commands and get a simple list, but I cannot add any styling to the list whatsoever.

This is what I have at the moment:

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{nounattr}[2018/01/03 The noun project attribution]

\RequirePackage{svg}
\RequirePackage{url}

\newcommand\printnounattrs{}
\newcommand\nounattr[3]{\edef\printnounattrs{\printnounattrs#1 pictogram werd ontworpen door #2 van The Noun Project, onder de Creative Commons license (CC BY 3.0). Zie #3. }}

An MWE is the following:

\documentclass{report}
\usepackage[left=2cm, right=2cm, top=2cm, bottom=2cm]{geometry}
\usepackage[parfill]{parskip}
\usepackage{nounattr}
\begin{document}
    \nounattr{Protein}{Ben Markoch}{https://thenounproject.com/search/?q=protein\&i=54837}
    \nounattr{Peptides}{Fredrik Edfors}{https://thenounproject.com/search/?q=peptide\&i=66903}

    \printnounattrs
\end{document}

Which yields: enter image description here

I would however like to add some styling into the edef in order for #1 pictogram to be printed italic and that the url is propely defined as a url using \url{#3}. I changed \nounattr to be:

\newcommand\nounattr[3]{\edef\printnounattrs{\printnounattrs\textit{#1 pictogram} werd ontworpen door #2 van The Noun Project, onder de Creative Commons license (CC BY 3.0). Zie \url{#3}. }}

But this gives me a undefined control sequence error. I don't understand what's happening and how I can possibly fix this?

  • 1
    Indeed some commands like \textit are not expandable, which means that they can't be used in an \edef context. There are many questions about this concept on this site, for example tex.stackexchange.com/q/35039/35864 and linked questions. – moewe Jan 4 at 9:17
  • 1
    You may want to look into 'list' implementations from etoolbox or expl3 instead. I think they can usually deal with these things as they don't go full \edef on the contents. – moewe Jan 4 at 9:21
  • @moewe Thanks for your help so far. I can't use \def as it causes TeX to go out of memory for some reason. I will take a look at the list implementations you mentioned :) – Pieter Verschaffelt Jan 4 at 9:22
  • Yes, I realised that after I had a closer look at what the code was doing. I have removed the \def suggestion from my comment. – moewe Jan 4 at 9:23
  • 1
    Here's one that should work: \expandafter\def\expandafter\printnounattrs\expandafter{\printnounattrs instead of \edef\printnounattrs{\printnounattrs – moewe Jan 4 at 9:25
9

There is a large group of TeX macros/commands that can't be used (as expected) in an \edef context. Those macros are called not expandable. This aspect of TeX programming is one that creeps up on one sooner or later and is well worth reading up on if one intends to go a bit further in (La)TeX coding. There are a great many questions about issues like this on this site (for example What are the differences between \def, \edef, \gdef and \xdef?, When to use \edef, \noexpand, and \expandafter?, ...), but I don't know about a good systematic introduction except for the typical TeX references such as the TeX book or TeX by Topic (see for example What is the best way to learn TeX?, Should I read Donald Knuth's The TeXbook?).

In your particular case \textit and \url are not expandable and will thus break when used in \edef.

If you want to retain the structure of your current approach you can replace the \edef with \expandafters and a \def

\documentclass{report}
\usepackage[left=2cm, right=2cm, top=2cm, bottom=2cm]{geometry}
\usepackage[parfill]{parskip}
\RequirePackage{url}

\newcommand\printnounattrs{}
\newcommand\nounattr[3]{%
  \expandafter\def\expandafter\printnounattrs\expandafter{%
    \printnounattrs
    \textit{#1 pictogram} werd ontworpen door #2 van The Noun Project,
    onder de Creative Commons license (CC BY 3.0).
    Zie \url{#3}. }%
}
\begin{document}
    \nounattr{Protein}{Ben Markoch}{https://thenounproject.com/search/?q=protein&i=54837}
    \nounattr{Peptides}{Fredrik Edfors}{https://thenounproject.com/search/?q=peptide&i=66903}
    \nounattr{Peptides}{Schmeptides}{https://thenounproject.com/search/?q=peptide&i=123&pq=v_w20w}

    \printnounattrs
\end{document}

While the \edef would try to expand everything in its argument as far as possible, the \expandafter\def\expandafter\printnounattrs\expandafter{\printnounattrs expands only the \printnounattrs in the definition exactly once, which means that we can concatenate our list as expected. (The simple \def\printnounattrs{\printnounattrs...} would lead to an infinite loop because that would to define the macro \printnounattrs to expand to \printnounattrs.)

Note that with this approach \url suffers from its usual issues that the URL can't contain %, #, ^^ or end with \ since it is used as an argument to a command.


If I were to write a macro for a similar task from scratch I would probably look into using the powerful list macros of etoolbox or LaTeX3/expl3.

Here is a naive implementation with etoolbox

\documentclass{report}
\usepackage{etoolbox}
\usepackage{url}

\newcommand\mylist{}
\newcommand\nounattr[3]{%
  \listadd{\mylist}{{#1}{#2}{#3}}}

\newcommand*{\printnounattr}[3]{%
  \textit{#1 pictogram} werd ontworpen door #2 van The Noun Project,
  onder de Creative Commons license (CC BY 3.0).
  Zie \url{#3}.
}

\makeatletter
\newcommand*{\printnounattr@i}[1]{%
  \printnounattr#1}
\newcommand*{\printnounattrs}{%
  \forlistloop{\printnounattr@i}{\mylist}}
\makeatother

\begin{document}
    \nounattr{Protein}{Ben Markoch}{https://thenounproject.com/search/?q=protein&i=54837}
    \nounattr{Peptides}{Fredrik Edfors}{https://thenounproject.com/search/?q=peptide&i=66903}
    \nounattr{Peptides}{Schmeptides}{https://thenounproject.com/search/?q=peptide&i=123&pq=v_w20w}

    \printnounattrs
\end{document}
7

\edef expands all commands to the fullest. However not all commands can be expanded, like e.g. \textit because it doesn't know what to do with the text when it's not in "printing mode". If you still however want to expand other tokens while leaving e.g. \textit untouched it you could use \noexpand (or \unexpanded if multple tokens) to prevent it from expanding (whilst all the others are):

\documentclass{article}
\def\a{alpha}
\edef\b{\noexpand\textit{\a}}
\show\b
\begin{document}
\b
\end{document}

Shows this in the log:

> \b=macro:
->\textit {alpha}.
l.4 \show\b

You can read more about \noexpand (and the related \unexpanded) at this answer

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.