7

Looked a long time for a solution to my problem, but said problem did not seem to lend itself well to "google-eez"!

Here's my problem: I have a Beamer presentation that I give to many different customers. Since I like to customize said presentation to suit each customer's organization, I have several "chunks" of code, about 10 lines each, in my parent Beamer .tex file that pull in logos, set colors, display organization names - all things that serve to "personalize" the presentation to a given customer. Each separate customer, of course, requires that another 10 line chunk of code be inserted into said parent .tex document. As you might imagine, after a while these "personalization chunks" serve to muddle up said parent .tex document.

What I'd like to do is to create separate "data" files, one for each customer, that contain the particulars that are unique to each customer (or, if possible, to store all said customer data, from all customers, into a single file). Then, I'd like to be able to, using a single line in the parent .tex document, extract the customization data from the proper data file(s).

Italicized text was added by the OP on February 16, 2016: Please keep in mind that my desire is to be able to generate, on demand, a customized presentation just a few days before I am to deliver same.

Something like:

\documentclass{beamer}
\usepackage{xcolor}

\newcommand{\nameoforg}{1st line from FileA.data}% Name of organization
\newcommand{\mainRGB}{2nd line from FileA.data}% Title color code
\newcommand{\backRGB}{3rd line from FileA.data}% Background color code
\newcommand{\clientlogowidth}{4th line from FileA.data}% Logo width
\newcommand{\clientlogo}{5th line from FileA.data}% Logo file name

\definecolor{mainslidecolor}{RGB}{\mainRGB}
\definecolor{backslidecolor}{RGB}{\backRGB}


\begin{document}

\title[\nameoforg]{Presentation Title}
\titlegraphic{\includegraphics[width=\clientlogowidth]{\clientlogo}}

\begin{frame}
this is boring
\end{frame}

\end{document}

FileA.txt, of course, is one of the "data" files (or, perhaps, the sole such file, if that is possible) that I addressed above.

Some of my research seemed to indicate that the catchfile package was the answer to my woes, but I could not understand said package's documentation.

How can I do this? And, is it possible to combine all of my "data" files into one file (which I'd really like to do), or is it best to keep them separate, one to a customer?

  • 2
    How about having the five \newcommand in a separate .tex file and using \input{filename}? – JP-Ellis Feb 15 '16 at 22:44
  • @JP-Ellis That is a good input, and I'll use that method if there is no way to do specifically what I asked about. Thanks! – Digger Feb 15 '16 at 23:12
  • Would a .csv file be appropriate for this (e.g., datatool or csvsimple)? (Easiest to use if the .csv doesn't have to include any formatting itself.) Or the textmerg package? – jon Feb 15 '16 at 23:29
  • You might want to have a look at the datatool package. – GuM Feb 15 '16 at 23:32
  • Reading a line at a time is possible but seems fragile - see tex.stackexchange.com/questions/111335/… and tex.stackexchange.com/questions/2375/file-input-and-output. A python/awk/perl/sed script (called from your LaTeX master document) to generale \renewcommands you then \input would be easy. Keeping all your data in one file would be easy too. – Ethan Bolker Feb 15 '16 at 23:44
4

I mentioned this in a comment originally, and have been asked to post this as an answer.

Another very simple way to do this without other packages is to just have the \newcommand declarations in their own .tex file and using an \input to load them. That is, have:

% Name of organization
\newcommand{\nameoforg}{Organization A}
% Title color code
\newcommand{\mainRGB}{255,0,0}
% Background color code
\newcommand{\backRGB}{255,255,255}
% Logo width
\newcommand{\clientlogowidth}{6cm}
% Logo file name
\newcommand{\clientlogo}{example-image-a}

in clientA.tex and similarly for other clients. This can then be loaded with a simple \input{clientA.tex}:

\documentclass{beamer}
\usepackage{mwe}

\input{clientA.tex}

\definecolor{mainslidecolor}{RGB}{\mainRGB}
\definecolor{backslidecolor}{RGB}{\backRGB}

\title[\nameoforg]{Presentation Title}
\titlegraphic{\includegraphics[width=\clientlogowidth]{\clientlogo}}

\begin{document}
\begin{frame}
  \maketitle
\end{frame}

\begin{frame}
  Hello, World!
\end{frame}
\end{document}

As others have shown shown, this can be extended with a script to mass produce slides, though Digger seems to only require one set of slides to be created at a time.

  • This is a pretty "low tech" solution, especially compared to some of the other excellent answers here, but it is the one I went with. This is because it is easy and still fits my particular needs quite nicely! – Digger Feb 18 '16 at 4:44
  • I suspect that the reason JKP-Ellis's "\definecolor" commands did not work is because the model selected for said command is "RGB", which means the color is to be defined using an "x,y,z" set (the range of each of the three parameters being 0-255) - that, and I did not originally specify the required package in the OP. For more info, see LaTeX/Colors. For clarification, I have added the "xcolor" package to the OP. – Digger Feb 18 '16 at 4:57
  • @Digger Thanks for the heads up. I amended the answer so that it all works now :) – JP-Ellis Feb 19 '16 at 0:32
4

You might consider a key-value interface approach. This is less fragile, since the keys can be specified in any order rather than having to be on specific lines.

Additionally, key-value packages allow defaults to be specified, if certain data are missing for specific clients or to provide backwards-compatibility if you add more keys in the future. With some key-value packages, you can mark certain keys as required or all kinds of other things. Basically, it adds a whole lot of flexibility.

Here's a basic example of the approach. See A big list of every keyval package for a description of the packages and the TUGboat article Implementing key–value input: An introduction by Joseph Wright and Christian Feuersänger for an introduction to the ideas and possibilities of key-value interfaces.

\RequirePackage{filecontents}
\begin{filecontents*}{clientA.data}
\diggersetup{
  orgname = First Organization,
  mainRGB = {1,0,0},
  backRGB = {0,0,1},
  clientlogowidth = 1in,
  clientlogo = example-image-a,
}
\end{filecontents*}
\begin{filecontents*}{clientB.data}
\diggersetup{
  orgname = Second Organization,
  mainRGB = {1,1,0},
  backRGB = {0,1,0},
  clientlogowidth = 1.5in,
  clientlogo = example-image-b,
}
\end{filecontents*}

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

\documentclass{beamer}
\usetheme{Boadilla}

\usepackage{lmodern} % no font substitution warnings
\usepackage{graphicx}
\usepackage{keyval} % or your favorite key-value package of the month

\makeatletter
\newlength\clientlogowidth
\define@key{digger}{orgname}{\def\nameoforg{#1}}
\define@key{digger}{mainRGB}{\definecolor{mainslidecolor}{RGB}{#1}}
\define@key{digger}{backRGB}{\definecolor{backslidecolor}{RGB}{#1}}
\define@key{digger}{clientlogo}{\def\clientlogo{#1}}
\define@key{digger}{clientlogowidth}{\setlength\clientlogowidth{#1}}
\setkeys{digger}{ % default key settings
  orgname = No Name!, 
  mainRGB = {1,1,1}, 
  backRGB = {1,0,1}, 
  clientlogo = example-image, 
  clientlogowidth = 1in,
}
\newcommand{\diggersetup}[1]{\setkeys{digger}{#1}}
\makeatother

%-------------------------------------
\input{clientA.data}
%\input{clientB.data}
%-------------------------------------

\begin{document}

\title[\nameoforg]{Presentation Title}
\titlegraphic{\includegraphics[width=\clientlogowidth]{\clientlogo}}

\begin{frame}
\titlepage
\end{frame}

\begin{frame}
this is boring
\end{frame}
\end{document}

enter image description here

  • Very nice! Although I went with a different approach, this solution would probably be my next choice. – Digger Feb 17 '16 at 19:00
4

I would use a shell script to do this, and define the filename to \input for each client in the script, and then pass it to the pdflatex command (or whatever engine you are using.)

Here's an example. For each client, create a .tex file containing the relevant definitions for that client. Here are three sample files:

clientA.tex

\newcommand\clientname{Client A}
\newcommand\clienttheme{EastLansing}

clientB.tex

\newcommand\clientname{Client B}
\newcommand\clienttheme{Madrid}

clientC.tex

\newcommand\clientname{Client C}
\newcommand\clienttheme{Bergen}

The first line of your client-presentation.tex file (before \documentclass) will then contain the line:

\InputIfFileExists{\clientfile}{}{\typeout{\detokenize{\clientfile}\ not found!}}

Presentation file

\InputIfFileExists{\clientfile}{}{\typeout{\detokenize{\clientfile}\ not found!}}
\documentclass{beamer}
\usetheme{\clienttheme}
\title{My Presentation}
\author{Prepared for:\\\clientname}
\date{}
\begin{document}
\begin{frame}[plain]
\maketitle
\end{frame}
\begin{frame}
\begin{block}{This is a block}
\begin{itemize}
\item An item
\end{itemize}
\end{block}
\end{frame}
\end{document}

Then on the commandline, we define \clientfile and pass it to pdflatex. Here's what a single file would look like:

pdflatex "\def\clientfile{clientA}\input{client-presentation}"

This will create client-presentation.pdf with commands as defined in clientA.tex.

Single use shell script

We can now create a simple shell script which takes a client file basename and a presentation file basename and then produces document for that client. Since it is possible that the document may need more than one compilation to resolve references etc., I've used latexmk to do the compilation. This will ensure in most cases that the document is compiled properly.

#!/bin/sh
#Usage: first argument = client file base name, second = latex document base
latexmk -pdf -silent    \
        -jobname="$1_$2" \
        -pdflatex="pdflatex --file-line-error --shell-escape --synctex=1 %O '\def\clientfile{$1}\input{%S}'" $2.tex

Shell script for automation

We now can automate producing a bunch of client files at once, if needed.

#!/bin/sh
#Usage: pass the basename of the presentation file as an argument
for f in client*.tex; do
basefile=$(basename "$f")
if [ "${basefile%.*}" != "$1" ]
then
latexmk -pdf     \
        -jobname="${basefile%.*}_$1" \
        -pdflatex="pdflatex --file-line-error --shell-escape --synctex=1 %O '\def\clientfile{${basefile%.*}}\input{%S}'" $1.tex
fi
done

This shell script takes the basename of the presentation file as an argument, and generates separate PDFs for every clientX.tex file in the current directory.

Example output:

enter image description here enter image description here enter image description here

  • Very clever! However, my needs dictate that I just need one such presentation file to be present at any given time. I clarified this need in the OP after you posted this excellent answer. – Digger Feb 16 '16 at 22:56
  • @Digger There's no reason this solution won't do what you want. Just change the script so that you pass the client name as an argument and instead of looping through the files, you can just generate a single client file as needed. – Alan Munn Feb 16 '16 at 23:26
  • @Digger I'll update my answer to show that, along with a few tweaks to the automation version too. – Alan Munn Feb 17 '16 at 0:10
  • Alan, although I chose another answer, I'm sure your scheme will come in useful for many folks who view this page! – Digger Feb 17 '16 at 18:57
  • @Digger I think that Andrew's answer putting all the code in a package is a good solution to your problem. I focused more on the automation part, which was how I understood the problem initially, and this answer may well be useful to others. – Alan Munn Feb 17 '16 at 19:15
3

I think that the "proper" LaTeX way to do this is to create a "package" that contains all of the client specific information. Then, in your beamer file, you can specify which client you want by "calling" the package with a line like:

\usepackage[ClientB]{clients}

Here is a MWE. I have not used beamer as this is not really essential to the question:

\documentclass{article}
\usepackage[ClientB]{clients}
\begin{document}\parindent=0pt
  \nameoforg\\
  \mainRGB\\
  \backRGB\\
  \clientlogowidth\\
  \clientlogo
\end{document}

This produces:

enter image description here

Finally, here is an example "package" file, which is the most interesting part:

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{clients}[2016/02/16 Client data for use in beamer slides]

\newif\ifNoClientSet\NoClientSettrue% no client set initially

\DeclareOption{ClientA}{
  \newcommand{\nameoforg}{Client A organisation}% Name of organization
  \newcommand{\mainRGB}{Yellow}% Title color code
  \newcommand{\backRGB}{LightBlue}% Background color code
  \newcommand{\clientlogowidth}{20mm}% Logo width
  \newcommand{\clientlogo}{ClientALogo}% Logo file name
  \NoClientSetfalse
}
\DeclareOption{ClientB}{
  \newcommand{\nameoforg}{Client B organisation}% Name of organization
  \newcommand{\mainRGB}{Pink}% Title color code
  \newcommand{\backRGB}{Black}% Background color code
  \newcommand{\clientlogowidth}{30mm}% Logo width
  \newcommand{\clientlogo}{ClientBLogo}% Logo file name
  \NoClientSetfalse
}

\ProcessOptions

% give an error if no client has been set
\ifNoClientSet
   \PackageError{clients}{No client set!}
\fi

\endinput

So each "client" is given as an option to the class and, in the package file, \DeclareOption is used to describe each client. At the end of the package file \ProcessOptions is called to set the appropriate parameters.

I have added a test, via \ifNoClientSet, so that an error is thrown when no client is specified. Instead of doing this you might want to set some default values for each of the client parameters and use \renewcommand in the options.

Of course, you can also define other commands that are common to all clients in the "package" file.

  • 1
    Perhaps the most "elegant" solution here - I'm sure this will come in handy for many people who face this problem! – Digger Feb 17 '16 at 18:58
  • @Digger Doesn't seem to be that popular with the punters:) IN any case, you should award a green tick to whichever of these solutions you think best answers your question. – Andrew Feb 17 '16 at 19:38
2

Here's a simple example of how you could use textmerg. Assuming you know what variables you want in each case, you could make an individual file for each company, then "merge" it with your document. So, given this file (called, say, textmerg-example.dat)

FooCo Inc.
rose
whale
serif
4cm
example-image

You then set up your beamer file like so:

\documentclass{beamer}%
\usepackage{mwe}% to get "example-image" named in .dat file
\usepackage{textmerg}
% the "Fields" are read and assigned in the order of the .dat file
\Fields{
\nameoforg
\myinnertheme
\myoutertheme
\myfonttheme
\clientlogowidth
\clientlogo
}
% Load the specific options
\Merge{textmerg-example.dat}{}% <-- note the second argument is empty because we are looping anything.

\usecolortheme{\myinnertheme}
\usecolortheme{\myoutertheme}
\usefonttheme{\myfonttheme}
\title[\nameoforg]{Presentation Title}
\titlegraphic{\includegraphics[width=\clientlogowidth]{\clientlogo}}


\begin{document}
\maketitle

\begin{frame}
\frametitle{Some Frame Title}

\nameoforg: This is boring\ldots
\end{frame}

\end{document}

Obviously, you're going to want to think fairly carefully about what things you might want to change each time.

Edit If you want to produce many files at the same time using a different set of variables, then you can modify the above example. Let's take two .dat files: 1-textmerg.dat and 2-textmerg.dat:

FooCo Inc.
rose
whale
serif
4cm
example-image

and

SOME COMPANY
orchid
seahorse
default
7cm
example-image

Then we modify the above beamer file in this way:

% main.tex
\documentclass{beamer}%
\usepackage{mwe}% to get "example-image" named in .dat file
\usepackage{textmerg}
% the "Fields" are read and assigned in the order of the .dat file
\Fields{
\nameoforg
\myinnertheme
\myoutertheme
\myfonttheme
\clientlogowidth
\clientlogo
}
% Hack: we don't want to loop the file, so we leave the second argument empty
\providecommand{\tmdatanum}{1}% set a default input file
\Merge{\tmdatanum-textmerg.dat}{}
\usecolortheme{\myinnertheme}
\usecolortheme{\myoutertheme}
\usefonttheme{\myfonttheme}
\title[\nameoforg]{Presentation Title}
\titlegraphic{\includegraphics[width=\clientlogowidth]{\clientlogo}}


\begin{document}

\maketitle

\begin{frame}
\frametitle{Some Frame Title}

\nameoforg: This is boring\ldots

I have used \textbf{\myinnertheme\ \& \myoutertheme} as the inner and outer themes, with this as the client logo: \texttt{\clientlogo}.

\end{frame}

\end{document}

Now, in order to produce all the different PDFs in one shot, we could use something like:

for num in {1..2} ; do pdflatex -jobname=$num-beamer "\def\tmdatanum{$num}\input{main.tex}" ; done

This picks out (inclusively) the files that start in the range "between" 1 and 2, assigns them a corresponding name 1- or 2-beamer for the output, defines the command \tmdatanum which is used in our non-looped \Merge command, and then inputs the main project file, main.tex. The result should be two separate files with the settings named in the two different .dat files.

Alternatively, a shell script (called, say, mergit) could be constructed along these lines:

#!/bin/bash
#
# Usage: mergit [FILE] [Highest input file num]
#
MASTERFILE=$1
ENDNUMBER=$2
#
for ((num=1;num<=ENDNUMBER;num++))
do
  pdflatex -jobname=$num-beamer "\def\tmdatanum{$num}\input{$MASTERFILE.tex}"
done

Thus, mergit main 2 would create your two PDFs as in the one-liner above.

  • Two comments: first you're missing braces around the beamer code. (The \Merge macro takes 2 arguments: the file and the code to repeat). But the main problem with this approach (and the datatool approach, which would be similar) is it creates a single document with n beamer presentations inside it. But I think for this use case, you would really want n PDF files, one for each customer. – Alan Munn Feb 16 '16 at 2:34
  • @Alan Munn Yes, you are correct. I would want n PDF presentation files, one for each customer. – Digger Feb 16 '16 at 2:59
  • @AlanMunn -- Actually, it's a dirty hack: I was cheating and suggesting that each PDF load its own .dat file. (I.e., the second argument of \Merge was supposed to be empty, not simply omitted.) If you use the loop, the \Merge has to be in the document body, whereas if you are just initializing the commands in \Fields once, it doesn't seem to cause a problem. As for the n PDF files, I think that's better scripted on the command line. – jon Feb 16 '16 at 3:20
  • @jon Very interesting and instructive! Sure to be of use for many searchers. – Digger Feb 17 '16 at 19:02

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.