5

I have a .csv file called sbriefdata with some data in it, for example

A;B;10
C;D;20
E;F;30
G;H;40

and I want a separate .pdf file for each of the rows in the .csv file. I know how to do a mail merge by reading the data and I know how to create separate .pdf files from this question (I use the solution provided by Ulrike Fischer). However, I am unable to combine these two features to get what I want. I tried

\documentclass{article}

\newif\ifmore \moretrue
\def\chopline#1;#2;#3\\{
\def\name{#1}
\def\vorname{#2}
\def\punkte{#3}
}

\begin{document}

\newread\data
\openin\data=sbriefdata.csv
\loop
\read\data to \line
\ifeof\data
\global\morefalse
\else
\expandafter\chopline\line\\

\ifx\conditionmacro\undefined
\immediate\write18{%
pdflatex --jobname="\jobname-\name"
"\gdef\string\conditionmacro{\name}\string\input\space\jobname"
}%
\expandafter\stop
\fi

\vorname \name \punkte \newline

\fi
\ifmore\repeat
\closein\data

\end{document}

but this only produces a single PDF named jobname-A with all the data instead of four different PDFs. It seems I don't fully understand the commands to build multiple PDFs (I thought since it is in a loop it should produce multiple PDFs). So I would like to know

1) How I can change my code to get multiple PDFs and/or

2) What exactly do the commands between \ifx and \fi do? E.g, why is the line \ifx\conditionmacro\undefined important and what does "\gdef\string\conditionmacro{\name}\string\input\space\jobname" do?

  • 1
    Marcels solution works however since it uses the first column for filename and one "name....pdf" would overwite a similar "name....pdf" I urge you to add as first column a fixed width value e.g. Index/Order, 001 002 003 this can help with sequencing your data in a spreadsheet but also allows the filename to be a fixed number of characters 001.pdf 002.pdf – KJO Mar 6 at 12:41
5
+100

After the first invocation, \stop is called and the execution is stopped. Therefore no additional documents are generated. But how can we fix the code?

We still need the documentclass and a way to split the line into its fields:

\documentclass{article}

\def\chopline#1;#2;#3 \\{
\def\name{#1}
\def\vorname{#2}
\def\punkte{#3}
}

Now there are two cases: Either we are directly called by the user. Then we never defined \conditionmacro and we want to invoke a new LaTeX instance for every line. Otherwise we were invoked in this manner and \conditionmacro is already set to samoe line. To differentiate these cases, we test if \conditionmacro is undefined:

\ifx\conditionmacro\undefined

In the first case, we now read the file: First open it, then read every line in a loop until the End Of File is reached:

  \newread\data
  \openin\data=sbriefdata.csv
  \loop
    \read\data to \line
  \unless\ifeof\data

Now we are in the loop body and have to handle one line. First figure out the name for the filename:

    \expandafter\chopline\line\\

We want to pass this line to a new pdflatex instance, so e.g. if we read A;B;10 the macro \conditionmacro should be defined as A;B;10 in the inner file. This can be archived by adding \gef\conditionmacro{A;B;10} in front of the regular file content. So:

    \immediate\write18{%
    pdflatex --jobname="\jobname-\name"
    "\gdef\string\conditionmacro{\line}\string\input{\jobname}"
    }%

The whole "\gdef\string\conditionmacro{\line}\string\input{\jobname}" will be expanded in the outer TeX instance, resulting in our case in \gdef\conditionmacro{A;B;10}\input{sbrief} if our original file is called sbrief.tex. Here the \string was just used to stop \conditionmacro and \input from being expanded. How this does exactly what we want: \conditionmacro is defined and then the original file content is read.

Now our loop is finished, we can close the file end end the outer TeX run.

  \repeat
  \closein\data
  \expandafter\stop
\fi

In our first case, we stopped at the \stop above, so if we reach this point we are in the second case: \conditionmacro is set and we have to create one of the actual documents. First split \conditionmacro into it's parts:

\expandafter\chopline\conditionmacro\\

then "just" create the document:

\begin{document}
Hallo \vorname\space\name,

Sie haben \punkte\space Punkte erzielt.
\end{document}

The full code:

\documentclass{article}

\def\chopline#1;#2;#3 \\{
\def\name{#1}
\def\vorname{#2}
\def\punkte{#3}
}

\ifx\conditionmacro\undefined
  \newread\data
  \openin\data=sbriefdata.csv
  \loop
    \read\data to \line
  \unless\ifeof\data
    \expandafter\chopline\line\\
    \immediate\write18{%
    pdflatex --jobname="\jobname-\name"
    "\gdef\string\conditionmacro{\line}\string\input{\jobname}"
    }%
  \repeat
  \closein\data
  \expandafter\stop
\fi

\expandafter\chopline\conditionmacro\\

\begin{document}

Hallo \vorname\space\name,

Sie haben \punkte\space Punkte erzielt.

\end{document}

If you additionally want to collect all the subdocuments in a collected PDF, you can include the PDF files after compiling into the outer document:

\documentclass{article}

\def\chopline#1;#2;#3 \\{
\def\name{#1}
\def\vorname{#2}
\def\punkte{#3}
}

\ifx\conditionmacro\undefined
  \usepackage{pdfpages}
  \begin{document}
  \newread\data
  \openin\data=sbriefdata.csv
  \loop
    \read\data to \line
  \unless\ifeof\data
    \expandafter\chopline\line\\
    \immediate\write18{%
    pdflatex --jobname="\jobname-\name"
    "\gdef\string\conditionmacro{\line}\string\input{\jobname}"
    }%
    \includepdf[pages=-]{\jobname-\name.pdf}
  \repeat
  \closein\data
  \csname fi\endcsname
  \end{document}
\fi

\expandafter\chopline\conditionmacro\\

\begin{document}

Hallo \vorname\space\name,

du hast \punkte\space Punkte erzielt.

\end{document}
  • Thanks for the answer and the detailed explanation, that was exactly what I was looking for. Would it be possible to adapt the solution so that all individual PDFs are created and there will be also one aggregate PDF created that contains all the individual PDFs beginning on a new page? – Martin Mar 7 at 16:05
  • I mean all the individual documents that get generated from your solution – Martin Mar 7 at 19:53
  • 1
    @Martin See updated answer – Marcel Krüger Mar 7 at 19:59
  • Thanks a lot. Can you tell me what the line \csname fi\endcsname does? I have also never seen two \begin{document} commands in one LaTeX-file. Does this "simply" build two pdfs when specifying a different name for one of the documents? And are two pdflatex runs neccesary since in the first run the files for the \includepdf command do not exist or is are two runs not neccesary? – Martin Mar 8 at 18:08

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.