Busqué durante mucho tiempo una solución a mi problema, ¡pero dicho problema no parecía prestarse bien a "google-eez"!
Este es mi problema: tengo una presentación de Beamer que presento a varios clientes. Como me gusta personalizar dicha presentación para adaptarla a la organización de cada cliente, tengo varios "fragmentos" de código, aproximadamente 10 líneas cada uno, en mi archivo principal Beamer .tex que incorpora logotipos, establece colores, muestra nombres de organizaciones, todo lo que sirve "personalizar" la presentación a un cliente determinado. La presentación de cada cliente por separado, por supuesto, requiere que se inserte otro fragmento de código de 10 líneas en dicho documento .tex principal. Como se puede imaginar, después de un tiempo, estos "fragmentos de personalización" sirven para confundir dicho documento .tex principal.
Lo que me gustaría hacer es crear archivos de "datos" separados, uno para cada cliente, que contengan los detalles que son únicos para cada cliente (o, si es posible, almacenartododichos datos del cliente, detodoclientes, en unsolteroarchivo). Luego, me gustaría poder, usando una sola línea en el documento .tex principal, extraer los datos de personalización de los archivos de datos adecuados.
El OP agregó el texto en cursiva el 16 de febrero de 2016: Tenga en cuenta que mi deseo es poder generar, a pedido, una presentación personalizada solo unos días antes de entregarla.
Algo como:
\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}
ArchivoA.txt, por supuesto, es uno de los archivos de "datos" (o, quizás, elúnicodicho archivo, si es posible) que mencioné anteriormente.
Algunas de mis investigaciones parecían indicar que el catchfile
paquete era la respuesta a mis problemas, pero no podía entender la documentación de dicho paquete.
¿Cómo puedo hacer esto? Y, ¿es posible combinar todos mis archivos de "datos" en un solo archivo (lo que realmente me gustaría hacer), o es mejor mantenerlos separados, uno para cada cliente?
Respuesta1
Mencioné esto en un comentario originalmente y me pidieron que lo publicara como respuesta.
Otra forma muy sencilla de hacer esto sin otros paquetes es simplemente tener las \newcommand
declaraciones en su propio .tex
archivo y usar un \input
para cargarlas. Es decir tener:
% 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}
en clientA.tex
y de manera similar para otros clientes. Luego se puede cargar con un 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}
Como han demostrado otros, esto se puede ampliar con un script para producir diapositivas en masa, aunque Digger parece requerir que solo se cree un conjunto de diapositivas a la vez.
Respuesta2
Usaría un script de shell para hacer esto, definiría el nombre del archivo \input
para cada cliente en el script y luego lo pasaría al pdflatex
comando (o cualquier motor que esté usando).
He aquí un ejemplo. Para cada cliente, cree un .tex
archivo que contenga las definiciones relevantes para ese cliente. Aquí hay tres archivos de muestra:
clienteA.tex
\newcommand\clientname{Client A}
\newcommand\clienttheme{EastLansing}
clienteB.tex
\newcommand\clientname{Client B}
\newcommand\clienttheme{Madrid}
clienteC.tex
\newcommand\clientname{Client C}
\newcommand\clienttheme{Bergen}
La primera línea de su client-presentation.tex
archivo (antes \documentclass
) contendrá la línea:
\InputIfFileExists{\clientfile}{}{\typeout{\detokenize{\clientfile}\ not found!}}
Archivo de presentación
\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}
Luego, en la línea de comando, lo definimos \clientfile
y lo pasamos a pdflatex
. Así es como se vería un solo archivo:
pdflatex "\def\clientfile{clientA}\input{client-presentation}"
Esto creará client-presentation.pdf
con comandos como se define en clientA.tex
.
Script de shell de un solo uso
Ahora podemos crear un script de shell simple que toma un nombre base de archivo de cliente y un nombre base de archivo de presentación y luego produce un documento para ese cliente. Dado que es posible que el documento necesite más de una compilación para resolver referencias, etc., yo solía latexmk
hacer la compilación. Esto garantizará en la mayoría de los casos que el documento se compila correctamente.
#!/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
Script de shell para automatización
Ahora podemos automatizar la producción de varios archivos de clientes a la vez, si es necesario.
#!/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
Este script de shell toma el nombre base del archivo de presentación como argumento y genera archivos PDF separados para cada clientX.tex
archivo en el directorio actual.
Salida de ejemplo:
Respuesta3
Podría considerar un enfoque de interfaz clave-valor. Esto es menos frágil, ya que las claves se pueden especificar en cualquier orden en lugar de tener que estar en líneas específicas.
Además, los paquetes clave-valor permiten especificar valores predeterminados si faltan ciertos datos para clientes específicos o para proporcionar compatibilidad con versiones anteriores si agrega más claves en el futuro. Con algunos paquetes clave-valor, puede marcar ciertas claves comorequeridoo todo tipo de otras cosas. Básicamente, agrega mucha flexibilidad.
A continuación se muestra un ejemplo básico del enfoque. VerUna gran lista de cada paquete keyvalpara obtener una descripción de los paquetes y el artículo sobre el remolcadorImplementación de entradas de valores clave: una introducción porJosé WrightyChristian Feuersängerpara una introducción a las ideas y posibilidades de las interfaces clave-valor.
\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}
Respuesta4
A continuación se muestra un ejemplo sencillo de cómo podría utilizar textmerg
. Suponiendo que sepa qué variables desea en cada caso, puede crear un archivo individual para cada empresa y luego "fusionarlo" con su documento. Entonces, dado este archivo (llamado, digamos, textmerg-example.dat
)
FooCo Inc.
rose
whale
serif
4cm
example-image
Luego configura su archivo beamer de esta manera:
\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}
Obviamente, querrás pensar con bastante cuidado qué cosas podrías querer cambiar cada vez.
EditarSi desea producir muchos archivos al mismo tiempo utilizando un conjunto diferente de variables, puede modificar el ejemplo anterior. Tomemos dos .dat
archivos: 1-textmerg.dat
y 2-textmerg.dat
:
FooCo Inc.
rose
whale
serif
4cm
example-image
y
SOME COMPANY
orchid
seahorse
default
7cm
example-image
Luego modificamos el archivo beamer anterior de esta manera:
% 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}
Ahora, para producir todos los diferentes archivos PDF de una sola vez, podríamos usar algo como:
for num in {1..2} ; do pdflatex -jobname=$num-beamer "\def\tmdatanum{$num}\input{main.tex}" ; done
Esto selecciona (inclusive) los archivos que comienzan en el rango "entre" 1 y 2, les asigna un nombre correspondiente 1-
o 2-beamer
para la salida, define el comando \tmdatanum
que se usa en nuestro comando sin bucle \Merge
y luego ingresa el archivo principal del proyecto. , main.tex
. El resultado deberían ser dos archivos separados con las configuraciones nombradas en los dos .dat
archivos diferentes.
Alternativamente, mergit
se podría construir un script de shell (llamado, por ejemplo, ) siguiendo estas líneas:
#!/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
Por lo tanto, mergit main 2
crearía sus dos archivos PDF como en la frase anterior.