Introduction to LuaTeX
20 Jun 2020LuaTeX is a TeX engine which integrates Lua as its scripting language. The combination of an editing language and programming language allows us to generate beautiful structural documents.
LuaTeX is usually included in TeXLive and MikTeX distributions.
Running Lua functions in TeX
LuaTeX introduces a new primitive \directlua
, which allows one to run TeX and Lua code together. For example, if I define a TeX command called \texcmd
, I can pass its value directly to a Lua variable with \directlua
. The following code will leave some text
in the output document.
\newcommand{\texcmd}{some text}
\directlua{
local var = "\texcmd"
tex.print(var)
}
This does create some trouble when we need to output special characters from Lua code. For example, as it is suggested by this question on StackExchange, if we want to print a table from Lua, the following code is not going to work. That is because LuaTex will first treat content in \directlua
as if it is TeX and then send the expanded code to Lua interpreter. As a result, macros like \begin
will result in errors.
% this code will not work
\directlua{
tex.print("\\begin{tabular}{lll}")
tex.print("1 & a & Test A \\\\")
tex.print("2 & b & Test B \\\\")
tex.print("\\end{tabular}")
}
A naive way to deal with this problem is to escape the backslash. But it is still troublesome.
% this code will work
\directlua{
tex.print("\string\\begin{tabular}{lll}")
tex.print("1 & a & Test A \string\\\string\\")
tex.print("2 & b & Test B \string\\\string\\")
tex.print("\string\\end{tabular}")
}
Fortunately, with the help of luacode
package, the need of these tedious escaping can be eliminated. The luacode*
environment executes code segments as if it is pure Lua. Of course, TeX macros cannot be used in this environment anymore.
% this code will work
\begin{luacode*}
tex.print("\\begin{tabular}{lll}")
tex.print("1 & a & Test A \\\\")
tex.print("2 & b & Test B \\\\")
tex.print("\\end{tabular}")
\end{luacode*}
Apart from luacode*
, the luacode
package also provides several different alternatives to \directlua
, whose specifications are shown as below.
If the Lua function does not have any arguments, there is a way of calling it with much less parsing overhead. For example, we can put the table printing function into slot 1 of LuaTeX’s function table and call it with \luafunction1
.
\directlua{
% declare function
function print_table()
tex.print("\string\\begin{tabular}{lll}")
tex.print("1 & a & Test A \string\\\string\\")
tex.print("2 & b & Test B \string\\\string\\")
tex.print("\string\\end{tabular}")
end
% get function table
local t = lua.get_functions_table()
% assign function in slot 1
t[1] = print_table()
}
% call function in slot 1
\luafunction1
Example: sqrt table
This is an example that I included in the pythontex
article. The way pythontex
works is to redirect stdout
of Python to a file and use TeX to read the output afterwards. It is easy to see that this approach is much more indirect and slower than LuaTeX. With LuaTeX, I can do the following.
\documentclass{article}
\usepackage[a4paper]{geometry}
\usepackage{newtxtext, newtxmath}
\usepackage{amsmath}
\usepackage{luacode}
\usepackage{tasks}
\usepackage{datetime2}
\usepackage{expl3}
\begin{document}
% using luacode* because there are percentage signs
% declare the sqrt function in Lua
\begin{luacode*}
function compute_sqrt(v)
local s = math.sqrt(tonumber(v))
local s_f = math.floor(s)
local o
if (math.abs(s_f * s_f - v) < 1.0e-5) then
o = string.format("%.1f", s)
else
o = string.format("%.6f", s)
end
tex.print(o)
end
\end{luacode*}
% declare a wrapper in TeX
\newcommand{\luasqrt}[1]{\directlua{compute_sqrt(#1)}}
\ExplSyntaxOn
\tl_clear:N \l_tmpa_tl
% construct tasks list in a token variable
\tl_put_right:Nn \l_tmpa_tl {\begin{tasks}[style=itemize](4)}
\int_set:Nn \l_tmpa_int {0}
\int_do_while:nNnn {\l_tmpa_int} < {31} {
% use a loop to construct values
\tl_put_right:Nx \l_tmpa_tl {\exp_not:N \task $\exp_not:N\sqrt{\int_use:N \l_tmpa_int}=\luasqrt{\int_use:N \l_tmpa_int}$ \space}
\int_incr:N \l_tmpa_int
}
\tl_put_right:Nn \l_tmpa_tl {\end{tasks}}
% show the constructed token
\str_set:NV \l_tmpa_str \l_tmpa_tl
\par \str_use:N \l_tmpa_str
% show the constructed table
\tl_use:N \l_tmpa_tl
\ExplSyntaxOff
\luasqrt{5}
\par\DTMNow
\end{document}
I declared a function compute_sqrt
in Lua and created a LaTeX wrapper \luasqrt
for it. Then, I can use \luasqrt
to put the square root of any number into TeX input stream. To construct a table output, I use the tasks
package and constructed the entire table with loops in LaTeX3. The constructed token list is:
\begin {tasks}[style=itemize](4)\task $\sqrt {0}=0.0$ \task $\sqrt {1}=1.0$ \task $\sqrt {2}=1.414214$
\task $\sqrt {3}=1.732051$ \task $\sqrt {4}=2.0$ \task $\sqrt {5}=2.236068$ \task $\sqrt {6}=2.449490$
\task $\sqrt {7}=2.645751$ \task $\sqrt {8}=2.828427$ \task $\sqrt {9}=3.0$ \task $\sqrt {10}=3.162278$
\task $\sqrt {11}=3.316625$ \task $\sqrt {12}=3.464102$ \task $\sqrt {13}=3.605551$ \task $\sqrt
{14}=3.741657$ \task $\sqrt {15}=3.872983$ \task $\sqrt {16}=4.0$ \task $\sqrt {17}=4.123106$ \task
$\sqrt {18}=4.242641$ \task $\sqrt {19}=4.358899$ \task $\sqrt {20}=4.472136$ \task $\sqrt {21}=4.582576$
\task $\sqrt {22}=4.690416$ \task $\sqrt {23}=4.795832$ \task $\sqrt {24}=4.898979$ \task $\sqrt
{25}=5.0$ \task $\sqrt {26}=5.099020$ \task $\sqrt {27}=5.196152$ \task $\sqrt {28}=5.291503$ \task
$\sqrt {29}=5.385165$ \task $\sqrt {30}=5.477226$ \end {tasks}
And the table looks like this: