1

I would like to create a template that provides some kind of interface for entering content. Some of the content should be repeated in multiple places in a final document, but I don't want to write duplicates in code. In fact, I want to let the template decide how to structure the content I give it. Let me try with an example. This is how I imagine my template:

\begin{document}
\section*{Products}
\begin[itemize}
  % for each product in the list of products
  %   \item product.name - product.description
\end{itemize}

\section*{Prices}
\begin{itemize}
  % for each product in list of products
  %  \item product.name: product.price
\end{itemize}
\end{document}

I would like the template to provide interface for defining list of products, and use it something like this in my main tex file:

\begin{products}
  \product{Product 1}{description for product 1}{\$10}
  \product{Product 2}{description for product 2}{\$20}
\end{products}

The final document should look like this:

\begin{document}
\section*{Products}
\begin[itemize}
  \item Product 1 - description for product 1
  \item Product 2 - description for product 2
\end{itemize}

\section*{Prices}
\begin{itemize}
  \item Product 1: \$10
  \item Product 2: \$20
\end{itemize}
\end{document}

I might want another template that structures the products differently. Ideally, I would just change the template file. In other words, the main file that defines list of products should not care how the products will be structured, it only provides data.

I might want to have template that would present data like this:

\section*{Products}
\begin{itemize}
  % for each product in the list of products
  %  \item product.name \(product.price\) - product.description
\end{itemize}

or

\begin{tabular}{lll}
  % for each product in the list of products
  %  product.name & product.price & product.description \\
\end{tabular}

or

\section*{Products}
\begin{itemize}
  % for each product in list of products
  %   \item.product.name - product.description
\end{itemize}

% some other sections here that have nothing to do with products

\section*{Prices}
...

and it should all work with the same data file. Imagine REST API how it provides data and different applications can display it in different ways. That's kind of thing that I am trying to achieve here - have a file in which I provide data that can be displayed in different ways which are defined by templates.

I've only used LaTeX for basic stuff so I don't know even if this is possible.

4
  • Hi and welcome. Just to make clear if I understand you correctly. You want to store data in some kind of variables/lists (should be absolutely no problem using datatool or expl3). But the output should be Latex code again, not an already typesetted document, like a PDF?
    – lukeflo
    Jul 6, 2023 at 20:39
  • 1
    Welcome to TeX.se. I'm not sure what you're trying to achieve exactly. Why transform your input, rather than have your products code produce the output you want?
    – Alan Munn
    Jul 6, 2023 at 22:05
  • @lukeflo It can be PDF. The end result that I wrote was only to show expected behaviour. I will take a look at datatool and expl3.
    – protwan
    Jul 7, 2023 at 10:13
  • @AlanMunn I am not aware of alternative, or any approaches to achieve this so I can't really answer. If there is a code that can produce desired output, I am all ears. I don't see how can products code produce output like that. I might have three different templates that work with the same data. One might put everything under one section, other might show it in table, and another might spread it over three sections, with some other sections in between that might contain data from another dataset unrelated to this one.
    – protwan
    Jul 7, 2023 at 10:19

2 Answers 2

3

This can be accomplished rather easily with LaTeX. Here I use pgfkeys because among other things this makes it easy to have spaces in the descriptions and it has a very convenient syntax for iterating over lists. For the convenience of those who want to play with this code I put everything in one file but in real-life applications you may want to separate the data file from the template file.

\documentclass{article}
\usepackage{pgffor}
% this creates a data file which you can separate this from the template file
\begin{filecontents*}{ProductData.tex}  
\pgfkeys{/products/.cd,
name/.initial={},
description/.initial={},
price/.initial={},
1/.style={name=iPad,description=Medium-sized tablet,price={\$1,000}},
2/.style={name=iPhone,description=Smartphone,price=\$8000},
3/.style={name=goat cheese,description=cheese made from goat milk,price=\$10},
4/.style={name=Emmentaler,description=Swiss cheese made from cow milk,price=\$8},
product description/.code={\item \pgfkeys{/products/.cd,#1}\pgfkeysvalueof{/products/name} --- \pgfkeysvalueof{/products/description}},
descriptions/.code={\section*{Products}
\begin{itemize}
    \pgfkeys{/products/product description/.list={#1}}
\end{itemize}},
product price/.code={\item \pgfkeys{/products/.cd,#1}\pgfkeysvalueof{/products/name}: \pgfkeysvalueof{/products/price}},
prices/.code={\section*{Prices}
\begin{itemize}
    \pgfkeys{/products/product price/.list={#1}}
\end{itemize}}}
\end{filecontents*}
\input{ProductData.tex}
\begin{document}
\pgfkeys{/products/.cd,
descriptions={1,2},
prices={1,...,4}}    
\end{document}

enter image description here

Once you have your data file, you only need a very short template file.

\documentclass{article}
\usepackage{pgffor}
\input{ProductData.tex}
\begin{document}
\pgfkeys{/products/.cd,
descriptions={1,2},
prices={1,...,4}}    
\end{document}

Also, you can add keys later easily.

4
  • 1
    Could you put the itemize part into the template and keep the data file pure data? Data should not care if it's going to be displayed as bullets, or in table, or in one section or spread out in two or three sections, that is template's job.
    – protwan
    Jul 7, 2023 at 10:10
  • 1
    @protwan Sure. Just remove \section*{Products} \begin{itemize} and \end{itemize} from descriptions/.code.
    – user299596
    Jul 7, 2023 at 16:32
  • 1
    How to then add it in the template part? \begin{itemize} what here? \end{itemize} How do I apply \item to every product?
    – protwan
    Jul 7, 2023 at 18:20
  • @protwan This is what the /.list key handler doe: it threads some code (or style) over a list.
    – user299596
    Jul 8, 2023 at 1:18
0

After I found that expl3 can solve the problem, this was relatively easy to accomplish.

The template file provides interface and structure of document, like so:

% template1.sty
\documentclass{article}

\usepackage{expl3}

\ExplSyntaxOn
\tl_new:N \l_product_name_tl
\tl_new:N \l_product_price_tl
\tl_new:N \l_product_description_tl

\newcommand{\product}[3]{
  \tl_put_right:Nn \l_product_name_tl {{#1}}
  \tl_put_right:Nn \l_product_price_tl {{#2}}
  \tl_put_right:Nn \l_product_description_tl {{#3}}
}

\newcommand{\productdesc}{
    \int_step_inline:nn {\tl_count:N \l_product_description_tl}{
      \subsection*{\tl_item:Nn \l_product_name_tl {##1}}
      \tl_item:Nn \l_product_description_tl {##1}
      \int_compare:nNnTF {##1} = {\tl_count:N \l_product_description_tl} {} {}
    }
}

\newcommand{\productprice}{
  \begin{itemize}
    \int_step_inline:nn {\tl_count:N \l_product_price_tl}{
      \item \tl_item:Nn \l_product_name_tl {##1}:~ \tl_item:Nn \l_product_price_tl {##1}
      \int_compare:nNnTF {##1} = {\tl_count:N \l_product_price_tl} {} {}
    }
  \end{itemize}
}
\ExplSyntaxOff

\newcommand{\unrelateddata}[1]{
  \newcommand{\getunrelateddata}{#1}
}

\newcommand{\makedoc}{
  \begin{document}

  \section*{Products}
  \productdesc

  \section*{Now Something Completely Different}
  This is completely unrelated to products.

  \getunrelateddata

  \section*{Prices}
  \productprice

  \end{document}
}

and the main (data) file might look like this:

\include{template1.sty}
\usepackage{lipsum}

\product{Product 1}{\$10.00}{
  \lipsum[1][1-4]
}
\product{Product 2}{\$20.00}{
  \lipsum[1][5-8]
}
\product{Product 3}{\$30.00}{
  \lipsum[1][9-12]
}
\product{Product 4}{\$40.00}{
  \lipsum[1][13-16]
}

\unrelateddata{
  \lipsum[2][1-10]
}

\makedoc

If I needed the same data presented in a different form, I could just make a new template that provides the same interface:

% template2.sty
\documentclass{article}

\usepackage{expl3}

\ExplSyntaxOn
\tl_new:N \l_product_name_tl
\tl_new:N \l_product_price_tl
\tl_new:N \l_product_description_tl

\newcommand{\product}[3]{
  \tl_put_right:Nn \l_product_name_tl {{#1}}
  \tl_put_right:Nn \l_product_price_tl {{#2}}
  \tl_put_right:Nn \l_product_description_tl {{#3}}
}

\newcommand{\productdesc}{
    \int_step_inline:nn {\tl_count:N \l_product_description_tl}{
      \textbf{\tl_item:Nn \l_product_name_tl {##1}}
      \begin{quote}
        \tl_item:Nn \l_product_description_tl {##1}
      \end{quote}
      \int_compare:nNnTF {##1} = {\tl_count:N \l_product_description_tl} {}{}
    }
}

\newcommand{\productdata}{
  \begin{tabular}{|l|l|p{8cm}|}
    \textbf{Product} & \textbf{Price} & \textbf{Description} \\
    \hline
    \int_step_inline:nn {\tl_count:N \l_product_name_tl}{
      \tl_item:Nn \l_product_name_tl {##1} & \tl_item:Nn \l_product_price_tl {##1} & \tl_item:Nn \l_product_description_tl {##1}
      \int_compare:nNnTF {##1} = {\tl_count:N \l_product_name_tl} {} {\\}
    }
  \end{tabular}
}
\ExplSyntaxOff

\newcommand{\unrelateddata}[1]{
  \newcommand{\getunrelateddata}{#1}
}

\newcommand{\makedoc}{
  \begin{document}

  \section*{Products}
  \productdesc

  \section*{Products in Table View}
  \productdata

  \section*{Now Something Completely Different}
  This is completely unrelated to products.

  \getunrelateddata

  \end{document}
}

and all I would have to do in the main file is change the included file.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .