summaryrefslogtreecommitdiff
path: root/macros/luatex/latex
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2024-01-13 03:01:12 +0000
committerNorbert Preining <norbert@preining.info>2024-01-13 03:01:12 +0000
commit65fecaf48c4ed6505cb45447a2d7cf1b9d933e3a (patch)
treec56b603d6058668578165093bb5ff98e735e0fe8 /macros/luatex/latex
parent1748d1e662c9618cc16f80d5e67669e9e9d6d4be (diff)
CTAN sync 202401130301
Diffstat (limited to 'macros/luatex/latex')
-rw-r--r--macros/luatex/latex/lua-placeholders/README.md11
-rw-r--r--macros/luatex/latex/lua-placeholders/doc/lua-placeholders-deps.bib13
-rw-r--r--macros/luatex/latex/lua-placeholders/doc/lua-placeholders-example/example-specification.yaml33
-rw-r--r--macros/luatex/latex/lua-placeholders/doc/lua-placeholders-example/example.json19
-rw-r--r--macros/luatex/latex/lua-placeholders/doc/lua-placeholders-example/example.pdfbin0 -> 67505 bytes
-rw-r--r--macros/luatex/latex/lua-placeholders/doc/lua-placeholders-example/example.tex225
-rw-r--r--macros/luatex/latex/lua-placeholders/doc/lua-placeholders-example/example.yaml14
-rw-r--r--macros/luatex/latex/lua-placeholders/doc/lua-placeholders-manual.pdfbin0 -> 256425 bytes
-rw-r--r--macros/luatex/latex/lua-placeholders/doc/lua-placeholders-manual.tex216
-rw-r--r--macros/luatex/latex/lua-placeholders/scripts/lua-placeholders-common.lua35
-rw-r--r--macros/luatex/latex/lua-placeholders/scripts/lua-placeholders-namespace.lua94
-rw-r--r--macros/luatex/latex/lua-placeholders/scripts/lua-placeholders-parser.lua88
-rw-r--r--macros/luatex/latex/lua-placeholders/scripts/lua-placeholders-types.lua273
-rw-r--r--macros/luatex/latex/lua-placeholders/scripts/lua-placeholders.lua217
-rw-r--r--macros/luatex/latex/lua-placeholders/tex/lua-placeholders.sty55
-rw-r--r--macros/luatex/latex/lua-typo/README.md28
-rw-r--r--macros/luatex/latex/lua-typo/doc/lua-typo-demo.pdfbin16859 -> 16859 bytes
-rw-r--r--macros/luatex/latex/lua-typo/doc/lua-typo-fr.pdfbin107474 -> 107762 bytes
-rw-r--r--macros/luatex/latex/lua-typo/doc/lua-typo.pdfbin217771 -> 215391 bytes
-rw-r--r--macros/luatex/latex/lua-typo/source/lua-typo.dtx121
20 files changed, 1389 insertions, 53 deletions
diff --git a/macros/luatex/latex/lua-placeholders/README.md b/macros/luatex/latex/lua-placeholders/README.md
new file mode 100644
index 0000000000..9d2c9ef966
--- /dev/null
+++ b/macros/luatex/latex/lua-placeholders/README.md
@@ -0,0 +1,11 @@
+# Lua(TeX) Placeholders
+
+A LaTeX package for specifying and inserting document placeholders with JSON or YAML formats.
+
+## Installation
+In order to install this package the right way, one should create the release tarball first with `make package`.
+Afterward be sure to unpack the contents of the release tarball anywhere where TeX will find it, i.e. `~/texmf`.
+
+## License
+This project is licensed under the LPPL version 1.3c and maintained by Erik Nijenhuis.
+See [LICENSE.txt](LICENSE.txt) for more information.
diff --git a/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-deps.bib b/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-deps.bib
new file mode 100644
index 0000000000..2e80c1b269
--- /dev/null
+++ b/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-deps.bib
@@ -0,0 +1,13 @@
+
+@misc{lyaml,
+ title = {lyaml},
+ author = {Andrew Danforth},
+ note = {Accessed: 6 January, 2024},
+ howpublished = {\url{https://github.com/gvvaughan/lyaml} and \url{https://luarocks.org/modules/gvvaughan/lyaml}},
+}
+
+@misc{libYAML,
+ title = {libYAML},
+ note = {Accessed: 6 January, 2024},
+ howpublished = {\url{https://pyyaml.org/wiki/LibYAML} and \url{https://packages.msys2.org/package/mingw-w64-x86_64-libyaml}},
+}
diff --git a/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-example/example-specification.yaml b/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-example/example-specification.yaml
new file mode 100644
index 0000000000..2d00cecaf3
--- /dev/null
+++ b/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-example/example-specification.yaml
@@ -0,0 +1,33 @@
+bool example:
+ type: bool
+ default: false
+string example:
+ type: string
+ placeholder: A string
+number example:
+ type: number
+ default: -1
+list example:
+ type: list
+ item type: string
+ default:
+ - A string
+ - A second string
+object example:
+ type: object
+ fields:
+ name:
+ type: string
+ placeholder: Your name
+ email:
+ type: string
+ placeholder: Your email
+table example:
+ type: table
+ columns:
+ description:
+ type: string
+ placeholder: The description
+ price:
+ type: number
+ placeholder: The price
diff --git a/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-example/example.json b/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-example/example.json
new file mode 100644
index 0000000000..e65389b1aa
--- /dev/null
+++ b/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-example/example.json
@@ -0,0 +1,19 @@
+{
+ "bool example": true,
+ "string example": "PeelInc.",
+ "number example": 1,
+ "list example": ["Tomatoes", "Potatoes"],
+ "object example": {
+ "name": "John Doe",
+ "email": "j.doe@example.com"
+ },
+ "table example": [
+ {
+ "description": "Peeling tomatoes",
+ "price": 50.00
+ }, {
+ "description": "Peeling potatoes",
+ "price": 25.00
+ }
+ ]
+}
diff --git a/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-example/example.pdf b/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-example/example.pdf
new file mode 100644
index 0000000000..fcdf27afab
--- /dev/null
+++ b/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-example/example.pdf
Binary files differ
diff --git a/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-example/example.tex b/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-example/example.tex
new file mode 100644
index 0000000000..805dd2469c
--- /dev/null
+++ b/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-example/example.tex
@@ -0,0 +1,225 @@
+%% example.tex
+%% Copyright 2024 E. Nijenhuis
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3c
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3c or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status ‘maintained’.
+%
+% The Current Maintainer of this work is E. Nijenhuis.
+%
+% This work consists of the files lua-placeholders.sty lua-placeholders-manual.pdf
+% lua-placeholders.lua lua-placeholders-common.lua lua-placeholders-namespace.lua lua-placeholders-parser.lua
+% and lua-placeholders-types.lua
+
+\documentclass{article}
+\usepackage{gitinfo-lua}
+\usepackage{lua-placeholders}
+\usepackage{listings}
+\usepackage{amsmath}
+\usepackage{calc}
+
+\loadrecipe[\jobname]{example-specification.yaml}
+
+\setlength{\parindent}{0pt}
+
+\begin{document}
+ \title{Lua \paramplaceholder{placeholders} Example\thanks{This example corresponds to \texttt{lua-placeholders} version \gitversion{} written on \gitdate.}}
+ \author{\dogitauthors[\\]}
+ \maketitle
+
+ \section*{Basics}
+ Wrong parameter:\\
+
+ \lstinline[style=TeX,morekeywords={param}]|\param{non existing}|
+ $\implies$
+ \param{non existing}\\
+
+ Conditional Parameter:\\
+
+ \lstinline[style=TeX,morekeywords={hasparam}]|\hasparam{list example}{is set}{is not set}|
+ $\implies$
+ \hasparam{list example}{is set}{is not set}
+
+ \section*{Before values loaded}
+
+ Boolean example:\\
+
+ \lstinline[style=TeX,morekeywords={param}]|\param{bool example}|
+ $\implies$
+ \param{bool example}\\
+
+ \lstinline[style=TeX,morekeywords={ifparam}]|\ifparam{bool example}{TRUE}{FALSE}|
+ $\implies$
+ \ifparam{bool example}{TRUE}{FALSE}\\
+
+ String example:\\
+
+ \lstinline[style=TeX,morekeywords={param}]|\param{string example}|
+ $\implies$
+ ``\param{string example}''\\
+
+ Number example:\\
+
+ \lstinline[style=TeX,morekeywords={param}]|\param{number example}|
+ $\implies$
+ \param{number example}\\
+
+ List example:\\
+
+ \lstinline[style=TeX,morekeywords={param}]|\param{list example}|
+ $\implies$
+ \param{list example}\\
+
+ \begin{lstlisting}[language={[LaTeX]TeX},morekeywords={formatitem,forlistitem}]
+\begin{enumerate}
+ \newcommand\formatitem[1]{\item #1}
+ \forlistitem{list example}{formatitem}
+\end{enumerate}
+ \end{lstlisting}
+ $\implies$
+ \begin{enumerate}
+ \newcommand\formatitem[1]{\item #1}
+ \forlistitem{list example}{formatitem}
+ \end{enumerate}
+
+ Object example:\\
+
+ \lstinline[style=TeX,morekeywords={paramfield}]|\paramfield{object example}{name}|\\
+ \lstinline[style=TeX,morekeywords={paramfield}]|\paramfield{object example}{email}|\\
+ $\implies$
+ \paramfield{object example}{name}
+ \paramfield{object example}{email}\\
+
+ \begin{lstlisting}[style=TeX,morekeywords={name,email}]
+\newcommand\name{...}
+\begin{paramobject}{object example}
+ \name \email
+\end{paramobject}
+% And here it works again
+\name
+ \end{lstlisting}
+ $\implies$
+ \newcommand\name{...}%
+ \parbox{\linewidth}{
+ \begin{paramobject}{object example}
+ \name \email
+ \end{paramobject}
+ \name
+ }\\
+
+ Table example:\\
+
+ \begin{lstlisting}[style=TeX,morekeywords={formatrow,fortablerow,description,price}]
+\newcommand\formatrow{\description & \price \\}%
+\begin{tabular}{l | l}
+ \textbf{Description} & \textbf{Price} \\ \hline
+ \fortablerow{table example}{formatrow}
+\end{tabular}
+ \end{lstlisting}
+ $\implies$
+ \newcommand\formatrow{\description & \price \\}%
+ \begin{tabular}{l | l}
+ \textbf{Description} & \textbf{Price} \\ \hline
+ \fortablerow{table example}{formatrow}
+ \end{tabular}
+
+
+ \section*{After values loaded}
+ \loadpayload[\jobname]{example.yaml}
+
+ Boolean example:\\
+
+ \lstinline[style=TeX,morekeywords={param}]|\param{bool example}|
+ $\implies$
+ \param{bool example}\\
+
+ \lstinline[style=TeX,morekeywords={ifparam}]|\ifparam{bool example}{TRUE}{FALSE}|
+ $\implies$
+ \ifparam{bool example}{TRUE}{FALSE}\\
+
+ String example:\\
+
+ \lstinline[style=TeX,morekeywords={param}]|\param{string example}|
+ $\implies$
+ ``\param{string example}''\\
+
+ Number example:\\
+
+ \lstinline[style=TeX,morekeywords={param}]|\param{number example}|
+ $\implies$
+ \param{number example}\\
+
+ List example:\\
+
+ \lstinline[style=TeX,morekeywords={param}]|\param{list example}|
+ $\implies$
+ \param{list example}\\
+
+ \begin{lstlisting}[language={[LaTeX]TeX},morekeywords={formatitem,forlistitem}]
+\begin{enumerate}
+ \newcommand\formatitem[1]{\item #1}
+ \forlistitem{list example}{formatitem}
+\end{enumerate}
+ \end{lstlisting}
+ $\implies$
+ \begin{enumerate}
+ \newcommand\formatitem[1]{\item #1}
+ \forlistitem{list example}{formatitem}
+ \end{enumerate}
+
+ Object example:\\
+
+ \lstinline[style=TeX,morekeywords={paramfield}]|\paramfield{object example}{name}|\\
+ \lstinline[style=TeX,morekeywords={paramfield}]|\paramfield{object example}{email}|\\
+ $\implies$
+ \paramfield{object example}{name}
+ \paramfield{object example}{email}\\
+
+ \begin{lstlisting}[style=TeX,morekeywords={name,email}]
+\newcommand\name{...}
+\begin{paramobject}{object example}
+ \name \email
+\end{paramobject}
+% And here it works again
+\name
+ \end{lstlisting}
+ $\implies$
+ \parbox{\linewidth}{
+ \begin{paramobject}{object example}
+ \name \email
+ \end{paramobject}
+ \name
+ }\\
+
+ Table example:\\
+
+ \begin{lstlisting}[style=TeX,morekeywords={formatrow,fortablerow,description,price}]
+\newcommand\formatrow{\description & \price \\}%
+\begin{tabular}{l | l}
+ \textbf{Description} & \textbf{Price} \\ \hline
+ \fortablerow{table example}{formatrow}
+\end{tabular}
+ \end{lstlisting}
+ $\implies$
+ \begin{tabular}{l | l}
+ \textbf{Description} & \textbf{Price} \\ \hline
+ \fortablerow{table example}{formatrow}
+ \end{tabular}
+
+ \section*{Specification File}
+ \lstinputlisting[language=YAML,numbers=left,xleftmargin=15pt,caption={example-specification.yaml},columns=fullflexible]{example-specification.yaml}
+
+ \clearpage
+
+ \section*{Payload File}
+ \lstinputlisting[language=YAML,numbers=left,xleftmargin={15pt},caption={example.yaml},columns=fullflexible]{example.yaml}
+
+ \section*{Fallback Payload}
+ \lstinputlisting[language=JSON,numbers=left,xleftmargin={15pt},caption={example.json},columns=fullflexible]{example.json}
+\end{document}
diff --git a/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-example/example.yaml b/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-example/example.yaml
new file mode 100644
index 0000000000..1806fad8da
--- /dev/null
+++ b/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-example/example.yaml
@@ -0,0 +1,14 @@
+bool example: true
+string example: PeelInc.
+number example: 1
+list example:
+ - Tomatoes
+ - Potatoes
+object example:
+ name: John Doe
+ email: j.doe@example.com
+table example:
+ - description: Peeling tomatoes
+ price: 50.00
+ - description: Peeling potatoes
+ price: 25.00
diff --git a/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-manual.pdf b/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-manual.pdf
new file mode 100644
index 0000000000..499978dbf0
--- /dev/null
+++ b/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-manual.pdf
Binary files differ
diff --git a/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-manual.tex b/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-manual.tex
new file mode 100644
index 0000000000..776ad3af5d
--- /dev/null
+++ b/macros/luatex/latex/lua-placeholders/doc/lua-placeholders-manual.tex
@@ -0,0 +1,216 @@
+%% lua-placeholders-manual.tex
+%% Copyright 2024 E. Nijenhuis
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3c
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3c or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status ‘maintained’.
+%
+% The Current Maintainer of this work is E. Nijenhuis.
+%
+% This work consists of the files lua-placeholders.sty lua-placeholders-manual.pdf
+% lua-placeholders.lua lua-placeholders-common.lua lua-placeholders-namespace.lua lua-placeholders-parser.lua
+% and lua-placeholders-types.lua
+
+\documentclass{ltxdoc}
+\usepackage[english]{babel}
+\usepackage[titlepage,authors]{gitinfo-lua}
+\usepackage{listings}
+\usepackage{lua-placeholders}
+\usepackage{multicol}
+\usepackage{calc}
+\usepackage{fmtcount}
+\usepackage[nodayofweek]{datetime}
+\usepackage{hyperref,embedfile}
+\usepackage{textcomp}
+\usepackage{attachfile2}
+\usepackage{enumitem}
+\usepackage{biblatex}
+
+\DefineBibliographyStrings{english}{%
+ bibliography = {References},
+}
+
+%! suppress = MissingBibliographystyle
+\bibliography{lua-placeholders-deps}
+
+\embedfile[desc={Parameter Specification Example},filespec=example.pdf]{lua-placeholders-example/example.pdf}
+\embedfile[desc={Parameter Specification Example (YAML)},filespec=example-specification.yaml]{lua-placeholders-example/example-specification.yaml}
+\embedfile[desc={Values Specification Example},filespec=example.yaml]{lua-placeholders-example/example.yaml}
+\embedfile[desc={Values Fallback Specification Example},filespec=example.json]{lua-placeholders-example/example.json}
+
+\def\projecturl{https://github.com/Xerdi/lua-placeholders}
+
+\newcommand\showexample[5][15pt]{%
+\begin{minipage}[t]{.5\linewidth - .5 \columnsep}%
+\lstinputlisting[firstnumber={#2},linerange={#3},style=YAML,frame=single,numbers=left,xleftmargin=0pt,numbersep=10pt,columns=fullflexible]{lua-placeholders-example/example-specification.yaml}
+\end{minipage}\hspace*{\columnsep}%
+\begin{minipage}[t]{.5\linewidth - .5 \columnsep}%
+\lstinputlisting[firstnumber={#4},linerange={#5},style=YAML,frame=single,numbers=left,xleftmargin={#1},numbersep=10pt,columns=fullflexible]{lua-placeholders-example/example.yaml}
+\end{minipage}\\%
+}
+
+\begin{document}
+ \title{Lua \paramplaceholder{placeholders}\thanks{This document corresponds to \texttt{lua-placeholders} version \gitversion, written on \gitdate}}
+ \maketitle
+ \begin{abstract}
+ A package for creating `example' documents, which show parameters as placeholders and `actual copy' documents, which show parameters with the real data, written in Lua\TeX{}.
+ \end{abstract}
+
+ \tableofcontents
+
+ \clearpage
+
+ \section{Introduction}
+ This package is meant for setting parameters in a Lua\LaTeX{} document in a more programmatically way with YAML\@.
+ Parameters can be specified by adding a `recipe' file.
+ These recipe files describe the parameter's type, placeholders and/or default values.
+ From thereon, the placeholders can be displayed in the document and an `\textit{example}' document can be created.
+ An `\textit{actual copy}' document can be created by loading additional payload files, which all must correspond to a recipe file.
+
+ \subsection{Pros}
+ \begin{enumerate}[align=left]
+ \item Create an `\textit{example}' or `\textit{actual copy}' document with the same \LaTeX{} source and YAML recipe.
+ \item Integration within systems is as easy as compiling a normal \LaTeX{} document, especially thanks to the fallback support to JSON, which is quite renown in programming languages.
+ \item Supports multiple data types and formatting macros which work in most \TeX{} environments, like \texttt{enumerate} or \texttt{tabular}.
+ \end{enumerate}
+
+ \subsection{Cons}
+ \begin{enumerate}[align=left]
+ \item The package only works with Lua\LaTeX{}.
+ \item In order for the files to be loaded, commandline option `\texttt{--shell-escape}' is required.
+ \item For YAML support, there needs to be a Lua setup with certain dependencies, which can be quite hard to setup on some systems.
+ \end{enumerate}
+
+ \subsection{Prerequisites}
+ If you're using JSON as \meta{recipe} and \meta{payload} format, the following requirements are no longer needed, since Lua\TeX{} already supports JSON formats out of the box.
+
+ For YAML support, however, this package requires the \texttt{lyaml}\cite{lyaml} Lua module for parsing the YAML files.
+ This also includes the \texttt{libYAML}\cite{libYAML} platform dependent library and optionally LuaRocks for installing \texttt{lyaml}.
+ Another requirement is Lua, which version meets the Lua version used by Lua\TeX{}.
+ If no \texttt{LUA\_PATH} is set, and you use LuaRocks, this package tries to call the LuaRocks executable to find the \texttt{LUA\_PATH}.
+ If \texttt{lyaml} can't be loaded, this package will fall back on accepting JSON files only.
+
+ \clearpage
+ \section{Usage}
+ This section describes the basic commands of \texttt{lua-placeholders}.
+ For more detail about type specific commands or the behavior of types with commands described here, see section~\ref{sec:spec}.\\
+
+ \subsection{Configuration}
+ \DescribeMacro{\strictparams} In order to give an error when values are missing, the \cmd{\strictparams}\footnote{The \cmd{\strictparams} command is still under development.} command can be used.
+ Make sure to do it before loading any \meta{recipe} and \meta{payload} files.
+ \DescribeMacro{\loadrecipe}
+ In order to load a recipe the macro \cmd{\loadrecipe}\oarg{namespace}\marg{filename} can be used.
+ Where the \meta{filename} is a YAML file with its corresponding extension.
+ The optional \meta{namespace} is only a placeholder in order to prevent any conflicts between duplicate \meta{key}s.
+ If left out, the \meta{namespace} defaults to the base name of the filename.
+ \DescribeMacro{\loadpayload} The same behaviour counts for \cmd{\loadpayload}\oarg{namespace}\marg{filename}.
+ The order of loading \meta{recipe} and \meta{payload} files doesn't matter.
+ If the \meta{payload} file got loaded first, it will be yielded until the corresponding \meta{recipe} file is loaded.
+
+ All other macros of this package also take the optional \meta{namespace}, which by default is equal to \cmd{\jobname}.
+ \DescribeMacro{\setnamespace} This default \meta{namespace} can be changed with \cmd{\setnamespace}\marg{new default namespace}.\\
+
+ \subsection{Displaying Parameters}
+ For displaying variables, the commands \cmd{\param} and \cmd{\PARAM} share the same interface.
+ \DescribeMacro{\param} The most trivial, displaying the variable as-is, is \cmd{\param}\oarg{namespace}\marg{key}.
+ \DescribeMacro{\PARAM} The \cmd{\PARAM} however, shows the value as upper case.
+
+ In some cases, it's required to output the text without any \TeX{} related functionality.
+ Another case is that some environments don't take macros with optional arguments well.
+ \DescribeMacro{\rawparam} For these cases there is \cmd{\rawparam}\marg{namespace}\marg{key}, which takes the namespace as mandatory argument, instead of optional, and doesn't output fancy \TeX{} placeholders.\\
+
+ \DescribeMacro{\hasparam} To check whether a parameter is set, the \cmd{\hasparam}\oarg{namespace}\marg{key}\marg{true case}\marg{false case} command is used.
+ However, a more robust way is using \LaTeX{} hooks.
+ For recipes being loaded, the hook \texttt{namespace/}\meta{name} is triggered once.
+ For payloads being loaded, the hook \texttt{namespace/}\meta{name}\texttt{loaded} is triggered once.
+ For more information on \LaTeX{} hooks, read the \texttt{lthooks} manual.
+
+ \clearpage
+
+ \section{Parameter Specification}\label{sec:spec}
+ Every parameter specified has a \meta{type} set.
+ Optionally there is a choice between setting a \meta{default} or a \meta{placeholder} for the parameter.
+ \begin{description}
+ \item[bool] Next to the textual representation of \textit{true} and \textit{false}, it provides a \LaTeX{} command using the \texttt{ifthen} package.
+ Therefore, only the \meta{default} setting makes sense.\\[5pt]
+ \hspace*{5pt}\parbox{\linewidth-5pt}{%
+ \hfill\texttt{Recipe}\hfill\hspace*{\columnsep}%
+ \hfill\texttt{Payload}\hfill\hspace*{\columnsep}}\\%
+ \showexample{1}{1-3}{1}{1-1}
+ \DescribeMacro{\param}
+ With a boolean type the \cmd{\param}\oarg{namespace}\marg{name} returns either \textit{true} or \textit{false}.
+ \DescribeMacro{\ifparam}
+ Additionally, it provides the \cmd{\ifparam}\oarg{namespace}\marg{name}\marg{true code}\marg{false code} command for top level boolean types.
+ The macro is just a wrapper for the boolean package \texttt{ifthen}, which supports spaces in names.
+ \item[string] representing a piece of text.
+ All \TeX{} related symbols in the text, like \textbackslash, \% and \#, are escaped.\\
+ \showexample{4}{4-6}{2}{2-2}
+ \DescribeMacro{\param} A string type can easily be placed in \LaTeX{} using the \cmd{\param} command.
+ \item[number] representing a number, like the number type of Lua.
+ In most cases it's necessary to use \meta{default} instead of \meta{placeholder}, especially when the number is used in calculations, since a placeholder will cause errors in \LaTeX{} calculations.\\
+ \showexample{7}{7-9}{3}{3-3}
+ A number type can be used with \cmd{\param}, just like the string type.
+ \DescribeMacro{\numparam} However, the \cmd{\numparam}\oarg{namespace}\marg{name} command uses \cmd{\numprint} to properly format the number according to the selected language.
+ Read the documentation of package \texttt{numprint} for more information.
+ \item[list] representing a list of values.
+ The value type is specified by \meta{value type}.
+ A \meta{default} setting can be set.
+ Due to its structure, a \meta{placeholder} would be somewhat incompatible with the corresponding macros.
+ However, a placeholder can be simulated by setting the placeholders as children of the \meta{default} list, as demonstrated in the example.\\
+ \showexample{10}{10-15}{4}{4-6}
+ \DescribeMacro{\param}
+ Command \cmd{\param} concatenates every item with command \cmd{\paramlistconjunction}.
+ \DescribeMacro{\paramlistconjunction}
+ By default, the conjunction is set to `\texttt{,\textasciitilde}'.
+
+ \DescribeMacro{\forlistitem}
+ There's also the \cmd{\forlistitem}\oarg{namespace}\marg{name}\marg{csname} command, which takes an additional \meta{csname} and will execute it for every item in the list.
+ This command doesn't handle advanced features like altering the conjunction.
+ Though, some utility commands will be set, which are only available in the \meta{csname}s implementation, in order to achieve the same goal.
+ \item[object] representing a list of key value pairs.
+ This parameter type requires a \meta{fields} specification to be set.
+ Any field must be of type \texttt{bool}, \texttt{number} or \texttt{string}.\\
+ \showexample{16}{16-24}{7}{7-9}
+ There is no support for the \cmd{\param} command.
+ \DescribeMacro{\paramfield}
+ In order to show to contents there is the \cmd{\paramfield}\oarg{namespace}\marg{name}\marg{field} command.
+ However, unlike the common command \cmd{\param}, the command \cmd{\hasparam} does work with object types.
+
+ \DescribeEnv{paramobject} There's also the \texttt{paramobject} environment, which takes an optional \meta{namespace} and takes the \meta{name} of the object as arguments and then defines for every field name a corresponding command.
+ Every command is appended with the \cmd{\xspace} command to prevent gobbling a space.
+ In other words, the author doesn't have to end the command with accolades `\{\}' to get the expected output.
+
+ \item[table] representing a table.
+ This parameter type requires a \meta{columns} specification to be set.
+ The \meta{columns} describes each column by name with its own type specification.
+ Like the object field, only the types \texttt{bool}, \texttt{number} and \texttt{string} are supported column types.\\
+ \showexample[20pt]{25}{25}{10}{10}
+ \DescribeMacro{\fortablerow}
+ Like the object, the table has no support for \cmd{\param}, but comes with a table specific command \cmd{\fortablerow}\oarg{namespace}\marg{name}\marg{csname}.
+ The control sequence name \meta{csname} is a user-defined command with no arguments, containing any of the column names in a command form.
+ For example, the name \texttt{example} would be accessible as \cmd{\example} in the user-defined command body.
+
+ Like the object field, a table cell doesn't require accolades, though, this is due to the Lua implementation behind it.
+ Technically every command in the user-defined command body is replaced with the variable in Lua, instead of redefining the command itself for every row, preventing issues with macro expansion between table rows and also column separators in \TeX{}.
+
+ \end{description}
+
+ \clearpage
+
+ \printbibliography[heading=bibnumbered]
+
+ \clearpage
+ \section{Example}
+
+ The source file \texttt{example.tex} is a perfect demonstration of all macros in action.
+ It shows perfectly what happens when there's a \meta{payload} file loaded and when not.
+ The result of this example \attachfile[icon=Paperclip,description={ELPI Example v\gitversion}]{lua-placeholders-example/example.pdf} is attached in the digital version of this document.
+
+ \lstinputlisting[language={[LaTeX]TeX},frame=single,caption={\ttfamily example.tex},captionpos=t,numbers=left,keywordsprefix={\\},firstnumber=20,firstline=20,columns=fullflexible]{lua-placeholders-example/example.tex}
+\end{document}
diff --git a/macros/luatex/latex/lua-placeholders/scripts/lua-placeholders-common.lua b/macros/luatex/latex/lua-placeholders/scripts/lua-placeholders-common.lua
new file mode 100644
index 0000000000..e68a5484e8
--- /dev/null
+++ b/macros/luatex/latex/lua-placeholders/scripts/lua-placeholders-common.lua
@@ -0,0 +1,35 @@
+-- lua-placeholders-common.lua
+-- Copyright 2024 E. Nijenhuis
+--
+-- This work may be distributed and/or modified under the
+-- conditions of the LaTeX Project Public License, either version 1.3c
+-- of this license or (at your option) any later version.
+-- The latest version of this license is in
+-- http://www.latex-project.org/lppl.txt
+-- and version 1.3c or later is part of all distributions of LaTeX
+-- version 2005/12/01 or later.
+--
+-- This work has the LPPL maintenance status ‘maintained’.
+--
+-- The Current Maintainer of this work is E. Nijenhuis.
+--
+-- This work consists of the files lua-placeholders.sty
+-- lua-placeholders-manual.pdf lua-placeholders.lua
+-- lua-placeholders-common.lua lua-placeholders-namespace.lua
+-- lua-placeholders-parser.lua and lua-placeholders-types.lua
+
+function table.copy(t)
+ local u = { }
+ for k, v in pairs(t) do
+ u[k] = v
+ end
+ return setmetatable(u, getmetatable(t))
+end
+
+lua_placeholders_toks = {
+ new_bool = token.create('provideboolean'),
+ set_bool = token.create('setboolean'),
+ list_conj = token.create('paramlistconjunction'),
+ placeholder_format = token.create('paramplaceholder'),
+ unknown_format = token.create('paramnotfound')
+}
diff --git a/macros/luatex/latex/lua-placeholders/scripts/lua-placeholders-namespace.lua b/macros/luatex/latex/lua-placeholders/scripts/lua-placeholders-namespace.lua
new file mode 100644
index 0000000000..fb84447a25
--- /dev/null
+++ b/macros/luatex/latex/lua-placeholders/scripts/lua-placeholders-namespace.lua
@@ -0,0 +1,94 @@
+-- lua-placeholders-namespace.lua
+-- Copyright 2024 E. Nijenhuis
+--
+-- This work may be distributed and/or modified under the
+-- conditions of the LaTeX Project Public License, either version 1.3c
+-- of this license or (at your option) any later version.
+-- The latest version of this license is in
+-- http://www.latex-project.org/lppl.txt
+-- and version 1.3c or later is part of all distributions of LaTeX
+-- version 2005/12/01 or later.
+--
+-- This work has the LPPL maintenance status ‘maintained’.
+--
+-- The Current Maintainer of this work is E. Nijenhuis.
+--
+-- This work consists of the files lua-placeholders.sty
+-- lua-placeholders-manual.pdf lua-placeholders.lua
+-- lua-placeholders-common.lua lua-placeholders-namespace.lua
+-- lua-placeholders-parser.lua and lua-placeholders-types.lua
+
+require('lua-placeholders-types')
+
+local namespace = {
+ strict = false,
+ recipe_file = nil,
+ recipe_loaded = false,
+ payload_file = nil,
+ payload_loaded = false
+}
+
+function namespace.parse_filename(path)
+ local abs_path = kpse.find_file(path)
+ local _, _, name = abs_path:find('/?%w*/*(%w+)%.%w+')
+ return name, abs_path
+end
+
+function namespace:new(_o)
+ local o = {
+ recipe_file = _o.recipe_file,
+ payload_file = _o.payload_file,
+ strict = _o.strict,
+ values = {}
+ }
+ setmetatable(o, self)
+ self.__index = self
+ return o
+end
+
+function namespace:load_recipe(params)
+ for key, opts in pairs(params) do
+ local param = base_param.define(key, opts)
+ if param then
+ self.values[key] = param
+ end
+ end
+ self.recipe_loaded = true
+end
+
+function namespace:load_payload(values)
+ if self.recipe_loaded then
+ if values then
+ for key, value in pairs(values) do
+ if self.values[key] then
+ local param = self.values[key]
+ param:load(key, value)
+ else
+ texio.write_nl('Warning: passed an unknown key ' .. key)
+ end
+ texio.write_nl('Key' .. key)
+ end
+ else
+ texio.write_nl('Warning: Payload file was empty')
+ end
+ self.payload_loaded = true
+ end
+end
+
+function namespace:param(key)
+ if not self.recipe_loaded then
+ tex.error('Error: Recipe was not loaded yet...')
+ return nil
+ end
+ if not self.payload_loaded then
+ if self.strict then
+ tex.error('Error: Payload was not loaded yet...')
+ return nil
+ else
+ texio.write_nl('Warning: Payload was not loaded yet...')
+ end
+ end
+ return self.values[key]
+end
+
+return namespace
diff --git a/macros/luatex/latex/lua-placeholders/scripts/lua-placeholders-parser.lua b/macros/luatex/latex/lua-placeholders/scripts/lua-placeholders-parser.lua
new file mode 100644
index 0000000000..1fa43227a4
--- /dev/null
+++ b/macros/luatex/latex/lua-placeholders/scripts/lua-placeholders-parser.lua
@@ -0,0 +1,88 @@
+-- lua-placeholders-parser.lua
+-- Copyright 2024 E. Nijenhuis
+--
+-- This work may be distributed and/or modified under the
+-- conditions of the LaTeX Project Public License, either version 1.3c
+-- of this license or (at your option) any later version.
+-- The latest version of this license is in
+-- http://www.latex-project.org/lppl.txt
+-- and version 1.3c or later is part of all distributions of LaTeX
+-- version 2005/12/01 or later.
+--
+-- This work has the LPPL maintenance status ‘maintained’.
+--
+-- The Current Maintainer of this work is E. Nijenhuis.
+--
+-- This work consists of the files lua-placeholders.sty
+-- lua-placeholders-manual.pdf lua-placeholders.lua
+-- lua-placeholders-common.lua lua-placeholders-namespace.lua
+-- lua-placeholders-parser.lua and lua-placeholders-types.lua
+
+local LUA_VERSION = string.sub(_VERSION, 5, -1)
+
+yaml_supported = false
+
+-- Check if LUA_PATH is set
+local current_path = os.getenv('LUA_PATH')
+if current_path then
+ texio.write_nl('Info: LUA path setup up correctly. Great job!')
+else
+ -- Set the LUA_PATH and LUA_CPATH using 'luarocks -lua-version <LuaLaTeX version> path'
+ texio.write_nl('Warning: No LUA_PATH set. Looking for LuaRocks installation...')
+ local handle = io.popen('luarocks --lua-version ' .. LUA_VERSION .. ' path')
+ if handle then
+ local buffer = handle:read('*a')
+ if handle:close() then
+ texio.write_nl('Info: luarocks command executed successfully')
+ local lua_path, lua_search_count = string.gsub(buffer, ".*LUA_PATH='([^']*)'.*", "%1")
+ local lua_cpath, clua_search_count = string.gsub(buffer, ".*LUA_CPATH='([^']*)'.*", "%1")
+ if lua_search_count > 0 then
+ texio.write_nl('Info: Setting LUA_PATH from LuaRocks', lua_path)
+ package.path = lua_path
+ end
+ if clua_search_count > 0 then
+ texio.write_nl('Info: Setting LUA_CPATH from LuaRocks', lua_cpath)
+ package.cpath = lua_cpath
+ end
+ else
+ texio.write_nl('Error: couldn\'t find LuaRocks installation')
+ texio.write_nl("Info: LUA PATH:\n\t" .. string.gsub(package.path, ';', '\n\t') .. '\n\n')
+ end
+ else
+ tex.error('Error: could not open a shell. Is shell-escape turned on?')
+ end
+end
+texio.write_nl('\n')
+
+-- For falling back to JSON
+require('lualibs')
+
+-- Require YAML configuration files
+-- Make sure to have the apt package lua-yaml installed
+local status, yaml = pcall(require, 'lyaml')
+if status then
+ yaml_supported = true
+else
+ texio.write_nl('Warning: No YAML support.')
+ texio.write_nl(yaml)
+ texio.write_nl('Info: Falling back to JSON.')
+end
+
+return function(filename)
+ local _, _, ext = string.match(filename, "(.-)([^\\]-([^\\%.]+))$")
+ local file = io.open(filename, "rb")
+ if not file then
+ error('File ' .. filename .. ' doesn\'t exist...')
+ end
+ local raw = file:read "*a"
+ file:close()
+ if ext == 'json' then
+ return utilities.json.tolua(raw)
+ else
+ if yaml_supported then
+ return yaml.load(raw)
+ else
+ tex.error('Error: no YAML support!')
+ end
+ end
+end
diff --git a/macros/luatex/latex/lua-placeholders/scripts/lua-placeholders-types.lua b/macros/luatex/latex/lua-placeholders/scripts/lua-placeholders-types.lua
new file mode 100644
index 0000000000..faa637c8f7
--- /dev/null
+++ b/macros/luatex/latex/lua-placeholders/scripts/lua-placeholders-types.lua
@@ -0,0 +1,273 @@
+-- lua-placeholders-types.lua
+-- Copyright 2024 E. Nijenhuis
+--
+-- This work may be distributed and/or modified under the
+-- conditions of the LaTeX Project Public License, either version 1.3c
+-- of this license or (at your option) any later version.
+-- The latest version of this license is in
+-- http://www.latex-project.org/lppl.txt
+-- and version 1.3c or later is part of all distributions of LaTeX
+-- version 2005/12/01 or later.
+--
+-- This work has the LPPL maintenance status ‘maintained’.
+--
+-- The Current Maintainer of this work is E. Nijenhuis.
+--
+-- This work consists of the files lua-placeholders.sty
+-- lua-placeholders-manual.pdf lua-placeholders.lua
+-- lua-placeholders-common.lua lua-placeholders-namespace.lua
+-- lua-placeholders-parser.lua and lua-placeholders-types.lua
+
+require('lua-placeholders-common')
+
+function table.copy(t)
+ local u = { }
+ for k, v in pairs(t) do
+ u[k] = v
+ end
+ return setmetatable(u, getmetatable(t))
+end
+
+base_param = {}
+function base_param:new(o)
+ o = o or {}
+ setmetatable(o, self)
+ self.__index = self
+ return o
+end
+
+function base_param:is_set()
+ return self and ((self.values or self.fields or self.value) ~= nil)
+end
+
+function base_param:val()
+ return self.value or self.values or self.default
+end
+
+function base_param:to_upper()
+ local val = self:val()
+ if type(val) == 'string' then
+ return val:upper()
+ elseif type(self.placeholder) == 'string' then
+ return '[' .. self.placeholder:upper() .. ']'
+ end
+end
+
+function base_param:print_val()
+ local value = self:val()
+ if value ~= nil then
+ tex.write(value)
+ else
+ tex.sprint(lua_placeholders_toks.placeholder_format, '{', self.placeholder or self.key, '}')
+ end
+end
+
+bool_param = base_param:new{
+ type = 'bool'
+}
+
+function bool_param:new(key, _o)
+ local o = {
+ key = key,
+ default = _o.default
+ }
+ setmetatable(o, self)
+ self.__index = self
+ tex.sprint(lua_placeholders_toks.new_bool, '{', o.key, '}')
+ return o
+end
+
+function bool_param:val()
+ local value
+ if self.value ~= nil then
+ value = tostring(self.value)
+ elseif self.default ~= nil then
+ value = tostring(self.default)
+ else
+ value = 'false'
+ end
+ return value
+end
+
+function bool_param:set_bool(key)
+ tex.sprint(lua_placeholders_toks.set_bool, '{', key, '}{', self:val(), '}')
+end
+
+str_param = base_param:new{
+ type = 'string'
+}
+
+function str_param:new(key, _o)
+ local o = {
+ key = key,
+ placeholder = _o.placeholder
+ }
+ setmetatable(o, self)
+ self.__index = self
+ return o
+end
+
+number_param = base_param:new{
+ type = 'number'
+}
+
+function number_param:new(key, _o)
+ local o = {
+ key = key,
+ placeholder = _o.placeholder,
+ default = _o.default
+ }
+ setmetatable(o, self)
+ self.__index = self
+ return o
+end
+
+function number_param:val()
+ if self.value or self.default then
+ return tex.number(self.value or self.default)
+ end
+end
+
+function number_param:print_num()
+ local val = self:val()
+ if val then
+ tex.print('\\numprint{' .. val .. '}')
+ else
+ tex.sprint(lua_placeholders_toks.placeholder_format, '{', self.placeholder or self.key, '}')
+ end
+end
+
+list_param = base_param:new{
+ type = 'list'
+}
+
+function list_param:new(key, _o)
+ local o = {
+ key = key,
+ item_type = base_param.define('list_item', { type = (_o["item type"] or 'string') }),
+ default = {}
+ }
+ if _o.default then
+ for _, default_val in ipairs(_o.default) do
+ local v = table.copy(o.item_type)
+ v:load('list_item', default_val)
+ table.insert(o.default, v)
+ end
+ end
+ setmetatable(o, self)
+ self.__index = self
+ return o
+end
+
+function list_param:val()
+ return self.values or self.default or {}
+end
+
+function list_param:print_val()
+ local list = self:val()
+ if #list > 0 then
+ if not self.values then
+ tex.sprint(lua_placeholders_toks.placeholder_format, '{')
+ end
+ tex.sprint(list[1]:val())
+ for i = 2, #list do
+ tex.sprint(lua_placeholders_toks.list_conj, list[i]:val())
+ end
+ if not self.values then
+ tex.sprint('}')
+ end
+ end
+end
+
+object_param = base_param:new{
+ type = 'object'
+}
+
+function object_param:new(key, _o)
+ local o = {
+ key = key,
+ fields = {},
+ default = _o.default
+ }
+ for _key, field in pairs(_o.fields) do
+ o.fields[_key] = base_param.define(_key, field)
+ end
+ setmetatable(o, self)
+ self.__index = self
+ return o
+end
+
+table_param = base_param:new{
+ type = 'table'
+}
+
+function table_param:new(key, _o)
+ local o = {
+ key = key,
+ columns = {}
+ }
+ for col_key, col in pairs(_o.columns) do
+ o.columns[col_key] = base_param.define(col_key, col)
+ end
+ setmetatable(o, self)
+ self.__index = self
+ return o
+end
+
+function base_param.define(key, o)
+ if o.type then
+ if o.type == 'bool' then
+ return bool_param:new(key, o)
+ elseif o.type == 'string' then
+ return str_param:new(key, o)
+ elseif o.type == 'number' then
+ return number_param:new(key, o)
+ elseif o.type == 'list' then
+ return list_param:new(key, o)
+ elseif o.type == 'object' then
+ return object_param:new(key, o)
+ elseif o.type == 'table' then
+ return table_param:new(key, o)
+ else
+ texio.write_nl('Warning: no such parameter type ' .. o.type)
+ end
+ else
+ error('ERROR: parameter must have a "type" field')
+ end
+end
+
+function base_param:load(key, value)
+ if self.type == 'list' then
+ self.values = {}
+ for _, val in ipairs(value) do
+ local param = table.copy(self.item_type)
+ param:load('list-item', val)
+ table.insert(self.values, param)
+ end
+ elseif self.type == 'table' then
+ self.values = {}
+ for _, row_vals in ipairs(value) do
+ local row = {}
+ for col_key, col in pairs(self.columns) do
+ local cell = table.copy(col)
+ cell:load(col.key, row_vals[col_key])
+ row[col_key] = cell
+ end
+ table.insert(self.values, row)
+ end
+ elseif self.type == 'object' then
+ for field_key, field in pairs(self.fields) do
+ local field_val = value[field_key]
+ if field_val then
+ field:load(field_key, field_val)
+ else
+ texio.write_nl('Warning: Passed unknown field to object', field_key)
+ end
+ end
+ else
+ self.value = value
+ end
+ if self.type == 'bool' then
+ self:set_bool(key)
+ end
+end
diff --git a/macros/luatex/latex/lua-placeholders/scripts/lua-placeholders.lua b/macros/luatex/latex/lua-placeholders/scripts/lua-placeholders.lua
new file mode 100644
index 0000000000..255b4d1f26
--- /dev/null
+++ b/macros/luatex/latex/lua-placeholders/scripts/lua-placeholders.lua
@@ -0,0 +1,217 @@
+-- lua-placeholders.lua
+-- Copyright 2024 E. Nijenhuis
+--
+-- This work may be distributed and/or modified under the
+-- conditions of the LaTeX Project Public License, either version 1.3c
+-- of this license or (at your option) any later version.
+-- The latest version of this license is in
+-- http://www.latex-project.org/lppl.txt
+-- and version 1.3c or later is part of all distributions of LaTeX
+-- version 2005/12/01 or later.
+--
+-- This work has the LPPL maintenance status ‘maintained’.
+--
+-- The Current Maintainer of this work is E. Nijenhuis.
+--
+-- This work consists of the files lua-placeholders.sty
+-- lua-placeholders-manual.pdf lua-placeholders.lua
+-- lua-placeholders-common.lua lua-placeholders-namespace.lua
+-- lua-placeholders-parser.lua and lua-placeholders-types.lua
+
+if not modules then
+ modules = {}
+end
+
+modules.lua_placeholders = {
+ version = "0.1.0",
+ date = "2024/01/12",
+ comment = 'Extended LaTeX Parameter Interface — for specifying and inserting document parameters',
+ author = 'Erik Nijenhuis',
+ license = 'free'
+}
+
+local api = {
+ namespaces = {},
+ parameters = {},
+ strict = false,
+ toks = {
+ is_set_true = token.create('has@param@true'),
+ is_set_false = token.create('has@param@false'),
+ }
+}
+local lua_placeholders = {}
+local lua_placeholders_mt = {
+ __index = api,
+ __newindex = function()
+ tex.error('Cannot override or set actions for this module...')
+ end
+}
+
+setmetatable(lua_placeholders, lua_placeholders_mt)
+
+local lua_placeholders_namespace = require('lua-placeholders-namespace')
+local load_resource = require('lua-placeholders-parser')
+
+local function get_param(key, namespace)
+ namespace = namespace or tex.jobname
+ local _namespace = api.namespaces[namespace]
+ return _namespace and _namespace:param(key)
+end
+
+function api.set_strict()
+ api.strict = true
+end
+
+function api.recipe(path, namespace_name)
+ if namespace_name == '' then
+ namespace_name = nil
+ end
+ local filename, abs_path = lua_placeholders_namespace.parse_filename(path)
+ local raw_recipe = load_resource(abs_path)
+ local name = namespace_name or raw_recipe.namespace or filename
+ local namespace = api.namespaces[name] or lua_placeholders_namespace:new { recipe_file = abs_path, strict = api.strict }
+ if not api.namespaces[name] then
+ api.namespaces[name] = namespace
+ end
+ if raw_recipe.namespace then
+ namespace:load_recipe(raw_recipe.parameters)
+ else
+ namespace:load_recipe(raw_recipe)
+ end
+ tex.print('\\UseOneTimeHook{namespace/' .. name .. '}')
+ if namespace.payload_file and not namespace.payload_loaded then
+ local raw_payload = load_resource(namespace.payload_file)
+ if raw_payload.namespace then
+ namespace:load_payload(raw_payload.parameters)
+ else
+ namespace:load_payload(raw_payload)
+ end
+ tex.print('\\UseOneTimeHook{namespace/' .. name .. '/loaded}')
+ end
+end
+
+function api.payload(path, namespace_name)
+ if namespace_name == '' then
+ namespace_name = nil
+ end
+ local filename, abs_path = lua_placeholders_namespace.parse_filename(path)
+ local raw_payload = load_resource(abs_path)
+ local name = namespace_name or raw_payload.namespace or filename
+ local namespace = api.namespaces[name] or lua_placeholders_namespace:new { payload_file = abs_path, strict = api.strict }
+ if not api.namespaces[name] then
+ api.namespaces[name] = namespace
+ end
+ if namespace.recipe_loaded then
+ if raw_payload.namespace then
+ namespace:load_payload(raw_payload.parameters)
+ else
+ namespace:load_payload(raw_payload)
+ end
+ tex.print('\\UseOneTimeHook{namespace/' .. name .. '/loaded}')
+ end
+end
+
+function api.param_object(key, namespace)
+ return get_param(key, namespace)
+end
+
+function api.param(key, namespace)
+ local param = get_param(key, namespace)
+ if param then
+ param:print_val()
+ elseif api.strict then
+ tex.error('Error: Parameter not set "' .. key .. '" in namespace "' .. namespace .. '".')
+ else
+ tex.sprint(lua_placeholders_toks.unknown_format, '{', key, '}')
+ end
+end
+
+function api.handle_param_is_set(key, namespace)
+ local param = get_param(key, namespace)
+ if param and param:is_set() then
+ tex.sprint(token.create('has@param@true'))
+ else
+ tex.sprint(token.create('has@param@false'))
+ end
+end
+
+function api.field(object_key, field, namespace)
+ local param = get_param(object_key, namespace)
+ if param then
+ local object = param.fields or param.default or {}
+ local f = object[field]
+ if f then
+ f:print_val()
+ else
+ tex.sprint(lua_placeholders_toks.unknown_format, '{', field, '}')
+ end
+ else
+ tex.error('No such object', object_key)
+ end
+end
+
+function api.with_object(object_key, namespace)
+ local object = get_param(object_key, namespace)
+ for key, param in pairs(object.fields) do
+ local val = param:val()
+ if val then
+ token.set_macro(key, param:val() .. '\\xspace')
+ else
+ token.set_macro(key, '\\paramplaceholder{' .. (param.placeholder or key) .. '}\\xspace')
+ end
+ end
+end
+
+function api.for_item(list_key, namespace, csname)
+ local param = get_param(list_key, namespace)
+ local list = param:val()
+ if #list > 0 then
+ if token.is_defined(csname) then
+ local tok = token.create(csname)
+ for _, item in ipairs(list) do
+ if param.values then
+ tex.sprint(tok, '{', item:val(), '}')
+ else
+ tex.sprint(tok, '{', lua_placeholders_toks.placeholder_format, '{', item:val(), '}}')
+ end
+ end
+ else
+ tex.error('No such command ', csname or 'nil')
+ end
+ end
+end
+
+function api.with_rows(key, namespace, csname)
+ local param = get_param(key, namespace)
+ if token.is_defined(csname) then
+ local row_content = token.get_macro(csname)
+ if param then
+ if param.values or api.strict then
+ if #param.values > 0 then
+ for _, row in ipairs(param.values) do
+ local format = row_content
+ for col_key, cell in pairs(row) do
+ format = format:gsub('\\' .. col_key, cell:val())
+ end
+ tex.print(format)
+ end
+ end
+ elseif param.columns then
+ texio.write_nl("Warning: no values set for " .. param.key)
+ local format = row_content
+ for col_key, col in pairs(param.columns) do
+ format = format:gsub('\\' .. col_key, '{\\paramplaceholder{' .. (col.placeholder or col_key) .. '}}')
+ end
+ tex.print(format)
+ else
+ tex.error('No values either columns available')
+ end
+ else
+ tex.error('Error: no such parameter')
+ end
+ else
+ tex.error('Error: no such command: ', csname or 'nil')
+ end
+end
+
+return lua_placeholders
diff --git a/macros/luatex/latex/lua-placeholders/tex/lua-placeholders.sty b/macros/luatex/latex/lua-placeholders/tex/lua-placeholders.sty
new file mode 100644
index 0000000000..9a2c247962
--- /dev/null
+++ b/macros/luatex/latex/lua-placeholders/tex/lua-placeholders.sty
@@ -0,0 +1,55 @@
+%% lua-placeholders.sty
+%% Copyright 2024 E. Nijenhuis
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3c
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3c or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status ‘maintained’.
+%
+% The Current Maintainer of this work is E. Nijenhuis.
+%
+% This work consists of the files lua-placeholders.sty
+% lua-placeholders-manual.pdf lua-placeholders.lua
+% lua-placeholders-common.lua lua-placeholders-namespace.lua
+% lua-placeholders-parser.lua and lua-placeholders-types.lua
+
+\NeedsTeXFormat{LaTeX2e}
+\ProvidesPackage{lua-placeholders}[2024/01/12 Extended LaTeX Paramter Interface Package]
+
+\RequirePackage{ifthen}
+\RequirePackage{luapackageloader}
+\RequirePackage{textcomp}
+\RequirePackage{xspace}
+
+\newcommand\curnamespace{\jobname}
+\newcommand\paramplaceholder[1]{\texttt{\textbf{[}#1\textbf{]}}}
+\newcommand\paramnotfound[1]{\paramplaceholder{{\textnormal{\textlangle unknown\textrangle}} #1}}
+\newcommand\paramlistconjunction{,~}
+
+\directlua{lua_placeholders = require('lua-placeholders')}
+
+\newcommand\setnamespace[1]{\renewcommand\curnamespace{#1}}
+\newcommand\strictparams{\directlua{lua_placeholders.set_strict()}}
+\newcommand%! suppress = NonMatchingIf
+\ifparam[4][\curnamespace]{%
+ \directlua{local p = lua_placeholders.param_object('#2','#1') if p then p:set_bool('#2') end}%
+ \ifthenelse{\boolean{#2}}{#3}{#4}}
+\newcommand\param[2][\curnamespace]{\directlua{lua_placeholders.param('#2', '#1')}}
+\newcommand\PARAM[2][\curnamespace]{\directlua{local p = lua_placeholders.param_object('#2', '#1') if p then tex.print(p:to_upper()) end}}
+\def\rawparam#1#2{\directlua{local p = lua_placeholders.param_object('#2', '#1') if p then tex.print(p:val() or p.default or p.placeholder) end}}
+\newcommand\numparam[2][\curnamespace]{\directlua{local n = lua_placeholders.param_object('#2', '#1') if n then n:print_num() end}}
+\newcommand\hasparam[4][\curnamespace]{%
+ \def\has@param@true{#3}%
+ \def\has@param@false{#4}%
+ \directlua{lua_placeholders.handle_param_is_set('#2', '#1')}}
+\newcommand\paramfield[3][\curnamespace]{\directlua{lua_placeholders.field('#2','#3', '#1')}}
+\newenvironment{paramobject}[2][\curnamespace]{\directlua{lua_placeholders.with_object('#2', '#1')}}{}
+\newcommand\forlistitem[3][\curnamespace]{\directlua{lua_placeholders.for_item('#2', '#1', '#3')}}
+\newcommand\fortablerow[3][\curnamespace]{\directlua{lua_placeholders.with_rows('#2', '#1', '#3')}}
+\newcommand\loadrecipe[2][]{\directlua{lua_placeholders.recipe('#2', '#1')}}
+\newcommand\loadpayload[2][]{\directlua{lua_placeholders.payload('#2', '#1')}}
diff --git a/macros/luatex/latex/lua-typo/README.md b/macros/luatex/latex/lua-typo/README.md
index 8f974434fa..801a2603af 100644
--- a/macros/luatex/latex/lua-typo/README.md
+++ b/macros/luatex/latex/lua-typo/README.md
@@ -80,31 +80,35 @@ Changes
- debugging stuff added.
* v.0.61:
- - bug fixes and documentation enhanced.
+ - bug fixes and documentation enhanced;
- colours `mygrey` and `myred` renamed as `LTgrey` and `LTred`.
* v.0.65:
- new option `ShortFinalWord` to detect short end-of-sentence word
- on top of next page.
+ on top of next page;
- code cleaning.
* v.0.70:
- - Options handled via `ltkeys` instead of `kvoptions`.
+ - options handled via `ltkeys` instead of `kvoptions`;
- Code cleaning, bug fixes.
* v.0.80:
- - Config file `lua-typo.cfg` changed (new colours added, all colour
- numbers are shifted by 1).
- - Colours no longer override each other, a special colour has been
- added for lines affected by multiple flaws (f.i. widow+overfull).
- - Bug fix (file `.typo`): in footnotes, line numbers for homeoarchy
+ - config file `lua-typo.cfg` changed (new colours added, all colour
+ numbers are shifted by 1);
+ - colours no longer override each other, a special colour has been
+ added for lines affected by multiple flaws (f.i. widow+overfull);
+ - bug fix (file `.typo`): in footnotes, line numbers for homeoarchy
were not reset.
* v.0.85:
- - A warning is issued if some pages fail to be checked properly.
- - Margin notes checking added (over/underfull lines, position).
- - Code cleaning, bug fixes.
+ - a warning is issued if some pages fail to be checked properly;
+ - margin notes checking added (over/underfull lines, position);
+ - code cleaning, bug fixes.
+
+* v.0.86:
+ - corrected typo in the `signature` function;
+ - compatibility with package `stfloats` added.
--
-Copyright 2020--2023 Daniel Flipo
+Copyright 2020--2024 Daniel Flipo
E-mail: daniel (dot) flipo (at) free (dot) fr
diff --git a/macros/luatex/latex/lua-typo/doc/lua-typo-demo.pdf b/macros/luatex/latex/lua-typo/doc/lua-typo-demo.pdf
index e82b355c62..08c3430123 100644
--- a/macros/luatex/latex/lua-typo/doc/lua-typo-demo.pdf
+++ b/macros/luatex/latex/lua-typo/doc/lua-typo-demo.pdf
Binary files differ
diff --git a/macros/luatex/latex/lua-typo/doc/lua-typo-fr.pdf b/macros/luatex/latex/lua-typo/doc/lua-typo-fr.pdf
index b117378ac8..92a1ad5d4e 100644
--- a/macros/luatex/latex/lua-typo/doc/lua-typo-fr.pdf
+++ b/macros/luatex/latex/lua-typo/doc/lua-typo-fr.pdf
Binary files differ
diff --git a/macros/luatex/latex/lua-typo/doc/lua-typo.pdf b/macros/luatex/latex/lua-typo/doc/lua-typo.pdf
index be36b78a7e..234889f673 100644
--- a/macros/luatex/latex/lua-typo/doc/lua-typo.pdf
+++ b/macros/luatex/latex/lua-typo/doc/lua-typo.pdf
Binary files differ
diff --git a/macros/luatex/latex/lua-typo/source/lua-typo.dtx b/macros/luatex/latex/lua-typo/source/lua-typo.dtx
index 7809a01377..0fdb2395f8 100644
--- a/macros/luatex/latex/lua-typo/source/lua-typo.dtx
+++ b/macros/luatex/latex/lua-typo/source/lua-typo.dtx
@@ -12,7 +12,7 @@
%
% Lua-Typo package for LaTeX version 2e
%
-% Copyright © 2020-2023 by Daniel Flipo
+% Copyright © 2020-2024 by Daniel Flipo
%
% Please report errors to: daniel (dot) flipo (at) free (dot) fr
%
@@ -166,11 +166,15 @@
% ne va pas provoquer d’autres désordres bien plus graves.\\
% b) Inversement, il n’est pas exclu que \pkg{lua-typo} comporte
% des bogues l’empêchant de détecter des lignes potentiellement
-% imparfaites.
+% imparfaites. Depuis la version~0.85, le fichier \file{.typo}
+% fournit, s’il y a lieu, une liste de pages « suspectes », celles où
+% aucune ligne de texte n’a été trouvée. L’avertissement peut être
+% anodin (page contenant uniquement des illustrations par exemple) ou
+% révéler un défaut de détection.
%
% \pkg{lua-typo} est largement configurable et devrait pouvoir
-% s’adapter aux exigences variables des auteurs ou correcteurs : voir
-% ci-dessous la liste des options et le fichier de configuration
+% s’adapter aux exigences variables des auteurs ou correcteurs :
+% voir ci-dessous la liste des options et le fichier de configuration
% \file{lua-typo.cfg}.
%
% \pkg{lua-typo} ne corrigeant aucun des « défauts » relevés, quels
@@ -489,7 +493,10 @@
% Moreover, correcting a potential issue somewhere may result in
% other much more serious flaws somewhere else …\\
% b) Conversely, possible bugs in \pkg{lua-typo} might hide issues
-% that should normally be highlighted.
+% that should normally be highlighted. Starting with version~0.85,
+% the \file{<jobname>.typo} file lists, if any, the pages on which
+% no text line could be found. The warning may be irrelevant (page
+% only composed of figures) or point out a possible bug.
%
% \pkg{lua-typo} is highly configurable in order to meet the
% variable expectations of authors and correctors: see the options’
@@ -579,12 +586,12 @@
% please note that \opt{All} has to be the first one, as options are
% taken into account as they are read \emph{i.e.} from left to right.
%
+% \newpage
% The list of all available options is printed to the \file{.log}
% file when option \opt{ShowOptions} is passed to \pkg{lua-typo},
% this option provides an easy way to get their names without having
% to look into the documentation.
%
-% \newpage
% With option \opt{None}, \pkg{lua-typo} \emph{does absolutely
% nothing}, all checks are disabled as the main function is not added
% to any LuaTeX callback. It not quite equivalent to commenting out
@@ -745,7 +752,7 @@
%<+scan>\ProvidesPackage{scan-page}
%<+dtx>\ProvidesFile{lua-typo.dtx}
%<*dtx|sty|scan>
- [2023-09-13 v.0.85 Daniel Flipo]
+ [2024-01-12 v.0.86 Daniel Flipo]
%</dtx|sty|scan>
%<*sty>
% \fi
@@ -1426,6 +1433,8 @@ end
% \changes{v0.65}{2023/03/02}{All ligatures are now split using the
% node’s `components’ field rather than a table.}
%
+% \changes{v0.86}{2024/01/10}{Typo corrected in the signature function.}
+%
% The first function adds a (non-nil) node to a signature of type
% string, nil nodes are ignored.
% It returns the augmented string and its length
@@ -1469,7 +1478,7 @@ local signature = function (node, string, swap)
local c2 = ""
if pre and pre.char then
if pre.components then
- for nn in traverse_id(GLYPH, post.components) do
+ for nn in traverse_id(GLYPH, pre.components) do
c1 = c1 .. utf8.char(nn.char)
end
else
@@ -2174,11 +2183,14 @@ end
% \changes{v0.50}{2021/05/02}{New function `get\_pagebody’ required for
% callback `pre\_shipout\_filter’.}
%
+% \changes{v0.86}{2024/01/10}{Package `stfloats’ adds 1sp to the
+% external \cs{vbox}. Be less picky regarding height test.}
+%
% \begin{macrocode}
local get_pagebody = function (head)
local textht = tex.getdimen("textheight")
local fn = head.list
- local body = nil
+ local body
repeat
fn = fn.next
until fn.id == VLIST and fn.height > 0
@@ -2189,9 +2201,17 @@ local get_pagebody = function (head)
%<dbg> texio.write(' ht=' .. ht .. ' dp=' .. dp)
first = fn.list
for n in traverse_id(VLIST,first) do
- if n.subtype == 0 and n.height == textht then
+% \end{macrocode}
+% Package `stfloats’ seems to add 1sp to the external \cs{vbox}
+% for each float found on the page.
+% Add $\pm8$sp tolerance when comparing |n.height| to
+% |\textheight|.
+% \begin{macrocode}
+ if n.subtype == 0 and n.height >= textht-1 and
+ n.height <= textht+8 then
%<dbg> local ht = string.format("%.1fpt", n.height/65536)
%<dbg> texio.write_nl('BODY found: ht=' .. ht)
+%<dbg> texio.write_nl(', ' .. n.height .. 'sp')
%<dbg> texio.write_nl(' ')
body = n
break
@@ -2199,13 +2219,17 @@ local get_pagebody = function (head)
%<dbg> texio.write_nl('Skip short VLIST:')
%<dbg> local ht = string.format("%.1fpt", n.height/65536)
%<dbg> local dp = string.format("%.1fpt", n.depth/65536)
-%<dbg> texio.write(' ht=' .. ht .. ' dp=' .. dp)
- first = n.list
- for n in traverse_id(VLIST,first) do
- if n.subtype == 0 and n.height == textht then
-%<dbg> local ht = string.format("%.1fpt", n.height/65536)
-%<dbg> texio.write_nl(' BODY: ht=' .. ht)
- body = n
+%<dbg> texio.write('ht=' .. ht .. ', ' .. n.height .. 'sp')
+%<dbg> texio.write('; dp=' .. dp)
+ local ff = n.list
+ for nn in traverse_id(VLIST,ff) do
+%<dbg> ht = string.format("%.1fpt", nn.height/65536)
+%<dbg> texio.write_nl(' VLIST: ht=' .. ht)
+%<dbg> texio.write(', ' .. nn.height .. 'sp')
+ if nn.subtype == 0 and nn.height >= textht-1 and
+ nn.height <= textht+8 then
+%<dbg> texio.write(' got BODY!')
+ body = nn
break
end
end
@@ -2697,7 +2721,7 @@ check_vtop = function (top, colno, vpos)
% \begin{macrocode}
page_bottom, body_bottom = check_EOP (nextnode)
% \end{macrocode}
-% Let’s check for an ``Overfull box’’. For a displayed equation
+% Let’s check for an `Overfull box’. For a displayed equation
% it is straightforward. A set of aligned equations all have the
% same (maximal) width; in order to avoid highlighting the whole
% set, we have to look for glues at the end of embedded
@@ -2855,22 +2879,25 @@ end
%
% \begin{macrocode}
luatypo.check_page = function (head)
- local textwd = tex.getdimen("textwidth")
- local textht = tex.getdimen("textheight")
- local checked, boxed, n2, n3, col, colno
- local body = get_pagebody(head)
local pageno = tex.getcount("c@page")
+ local body = get_pagebody(head)
+ local textwd, textht, checked, boxed
+ local top, first, next
+ local n2, n3, col, colno
local vpos = 0
local footnote = false
- local top = body
- local first = body.list
- local next
local count = 0
-%<dbg> texio.write_nl('Body=' .. tostring(node.type(top.id)))
-%<dbg> texio.write('-' .. tostring(top.subtype))
-%<dbg> texio.write('; First=' .. tostring(node.type(first.id)))
-%<dbg> texio.write('-' .. tostring(first.subtype))
-%<dbg> texio.write_nl(' ')
+ if body then
+ top = body
+ first = body.list
+ textwd = tex.getdimen("textwidth")
+ textht = tex.getdimen("textheight")
+%<dbg> texio.write_nl('Body=' .. tostring(node.type(top.id)))
+%<dbg> texio.write('-' .. tostring(top.subtype))
+%<dbg> texio.write('; First=' .. tostring(node.type(first.id)))
+%<dbg> texio.write('-' .. tostring(first.subtype))
+%<dbg> texio.write_nl(' ')
+ end
if ((first and first.id == HLIST and first.subtype == BOX) or
(first and first.id == VLIST and first.subtype == 0)) and
(first.width == textwd and first.height > 0 and not boxed) then
@@ -2880,12 +2907,13 @@ luatypo.check_page = function (head)
% \begin{macrocode}
%<dbg> local boxwd = string.format("%.1fpt", first.width/65536)
%<dbg> texio.write_nl('One step down: boxwd=' .. boxwd)
+%<dbg> texio.write_nl('Glue order=' .. tostring(first.glue_order))
%<dbg> texio.write_nl(' ')
top = body.list
% \end{macrocode}
% A float on top of a page is a VLIST-0 included in a VLIST-0 (body),
% it should not trigger this step down. Workaround: the body will be
-% read again.
+% scanned again.
% \begin{macrocode}
if first.id == VLIST then
boxed = body
@@ -3131,8 +3159,9 @@ return luatypo.check_page
%% Language specific settings (example for French):
%% short words (two letters max) to be avoided at end of lines.
-%%\luatypoOneChar{french}{"À Ô Y"}
-%%\luatypoTwoChars{french}{"Je Tu Il On Au De"}
+%%\luatypoOneChar{french}{"A À Ô Y"}
+%%\luatypoTwoChars{french}{"Ah Au Ça Çà Ce De Il Je La Là Le Ma Me Ne Ni
+%% Oh On Or Ou Où Sa Se Si Ta Tu Va Vu"}
% \end{macrocode}
%
% \iffalse
@@ -3258,7 +3287,7 @@ local is_glyph = node.is_glyph
local get_pagebody = function (head)
local textht = tex.getdimen("textheight")
local fn = head.list
- local body = nil
+ local body
repeat
fn = fn.next
until fn.id == VLIST and fn.height > 0
@@ -3267,9 +3296,11 @@ local get_pagebody = function (head)
local dp = string.format("%.1fpt", fn.depth/65536)
texio.write_nl("SKIP vlist: ht=" .. ht .. " dp=" .. dp)
for n in traverse_id(VLIST,first) do
- if n.subtype == 0 and n.height == textht then
+ if n.subtype == 0 and n.height >= textht-1 and
+ n.height <= textht+8 then
local ht = string.format("%.1fpt", n.height/65536)
- texio.write_nl(' BODY: ' .. ht)
+ texio.write_nl(' BODY found: ' .. ht)
+ texio.write(', ' .. n.height .. 'sp')
texio.write_nl(' ')
body = n
break
@@ -3277,13 +3308,15 @@ local get_pagebody = function (head)
local ht = string.format("%.1fpt", n.height/65536)
local dp = string.format("%.1fpt", n.depth/65536)
texio.write_nl(' SKIP vlist: ht=' .. ht .. ' dp=' .. dp)
- first = n.list
- for n in traverse_id(VLIST,first) do
- if n.subtype == 0 and n.height == textht then
- ht = string.format("%.1fpt", n.height/65536)
+ local ff = n.list
+ for nn in traverse_id(VLIST,ff) do
+ if nn.subtype == 0 and nn.height >= textht-1 and
+ nn.height <= textht+8 then
+ ht = string.format("%.1fpt", nn.height/65536)
texio.write_nl(' BODY: ' .. ht)
+ texio.write(', ' .. nn.height .. 'sp')
texio.write_nl(' ')
- body = n
+ body = nn
break
end
end
@@ -3360,6 +3393,12 @@ local scanvlist = function (head)
texio.write(tostring(node.type(body.id)))
texio.write('-')
texio.write(tostring(body.subtype))
+ else
+ texio.write_nl('Function get_pagebody returned NIL!')
+ texio.write_nl('=> Scanning whole page: node type ')
+ texio.write(tostring(node.type(head.id)))
+ texio.write('-')
+ texio.write(tostring(head.subtype))
end
while head do
texio.write_nl(tostring(node.type(head.id)))