summaryrefslogtreecommitdiff
path: root/macros/luatex/generic
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2023-10-21 03:01:29 +0000
committerNorbert Preining <norbert@preining.info>2023-10-21 03:01:29 +0000
commit6deeaf1bda76263051b929f5d5401c98d85e74ef (patch)
tree385460fec9e3be1f3e98950bde1c729aa3a8aa5b /macros/luatex/generic
parentc9ab6fbcc0f960c5e669419f2565ff121a7f33fd (diff)
CTAN sync 202310210301
Diffstat (limited to 'macros/luatex/generic')
-rw-r--r--macros/luatex/generic/minim-math/README6
-rw-r--r--macros/luatex/generic/minim-math/minim-math.doc4
-rw-r--r--macros/luatex/generic/minim-math/minim-math.lua12
-rw-r--r--macros/luatex/generic/minim-math/minim-math.pdfbin111554 -> 112093 bytes
-rw-r--r--macros/luatex/generic/minim-math/minim-math.tex63
-rw-r--r--macros/luatex/generic/minim-mp/README15
-rw-r--r--macros/luatex/generic/minim-mp/minim-lamp.ini11
-rw-r--r--macros/luatex/generic/minim-mp/minim-lamp.mp10
-rw-r--r--macros/luatex/generic/minim-mp/minim-mp.doc80
-rw-r--r--macros/luatex/generic/minim-mp/minim-mp.ini2
-rw-r--r--macros/luatex/generic/minim-mp/minim-mp.lua163
-rw-r--r--macros/luatex/generic/minim-mp/minim-mp.mp227
-rw-r--r--macros/luatex/generic/minim-mp/minim-mp.pdfbin98659 -> 109855 bytes
-rw-r--r--macros/luatex/generic/minim-mp/minim-mp.sty59
-rw-r--r--macros/luatex/generic/minim-mp/minim-mp.tex16
-rw-r--r--macros/luatex/generic/minim-mp/minim.mp136
-rw-r--r--macros/luatex/generic/minim-pdf/README11
-rw-r--r--macros/luatex/generic/minim-pdf/minim-pdf.doc79
-rw-r--r--macros/luatex/generic/minim-pdf/minim-pdf.lua48
-rw-r--r--macros/luatex/generic/minim-pdf/minim-pdf.pdfbin111274 -> 116670 bytes
-rw-r--r--macros/luatex/generic/minim-pdf/minim-pdf.tex141
-rw-r--r--macros/luatex/generic/minim/README6
-rw-r--r--macros/luatex/generic/minim/minim-alloc.lua15
-rw-r--r--macros/luatex/generic/minim/minim-alloc.tex16
-rw-r--r--macros/luatex/generic/minim/minim-doc.sty2
-rw-r--r--macros/luatex/generic/minim/minim.doc4
-rw-r--r--macros/luatex/generic/minim/minim.ini2
-rw-r--r--macros/luatex/generic/minim/minim.pdfbin321641 -> 338726 bytes
28 files changed, 845 insertions, 283 deletions
diff --git a/macros/luatex/generic/minim-math/README b/macros/luatex/generic/minim-math/README
index e6f44401e2..bb7786d6cb 100644
--- a/macros/luatex/generic/minim-math/README
+++ b/macros/luatex/generic/minim-math/README
@@ -1,4 +1,4 @@
-Version: 2023/1.2
+Version: 2023/1.3
SUMMARY
@@ -18,6 +18,10 @@ In order to obtain the typeset manual of this package, simply say:
HISTORY
+2023/1.3 (20/10/2023)
+
+ No notable changes.
+
2023/1.2 (1/1/2023)
Minor additions:
diff --git a/macros/luatex/generic/minim-math/minim-math.doc b/macros/luatex/generic/minim-math/minim-math.doc
index d458876b41..2a44dc72f4 100644
--- a/macros/luatex/generic/minim-math/minim-math.doc
+++ b/macros/luatex/generic/minim-math/minim-math.doc
@@ -8,8 +8,8 @@
\else \startmetadata
author {Esger Renkema}
title {minim-math}
- date {2023-03-03}
- version {2023/1.2}
+ date {2023-10-20}
+ version {2023/1.3}
keywords {LuaTeX; Plain TeX; Unicode mathematics}
stopmetadata
\maketitle \fi
diff --git a/macros/luatex/generic/minim-math/minim-math.lua b/macros/luatex/generic/minim-math/minim-math.lua
index a4540a92b2..2598094fdd 100644
--- a/macros/luatex/generic/minim-math/minim-math.lua
+++ b/macros/luatex/generic/minim-math/minim-math.lua
@@ -431,10 +431,6 @@ local is_delimiter =
, close = true
}
-local function tex_accent(class, num, char)
- return '\\math:'..accents[class]..'{'..num..'}'..char
-end
-
local function add_mathchar(code, char, class, cs, alphabet)
if char:sub(0,1) == ' ' then
-- accents can be given above a space (compare ' ́' with '́')
@@ -448,13 +444,15 @@ local function add_mathchar(code, char, class, cs, alphabet)
if is_delimiter[class] then tex.setdelcode(code, default_fam, code, 0, 0) end
elseif class == 'radical' then
if cs then
- token.set_macro(cs, '\\math:radical{'..code..'}'..char)
- tex.print('\\mathlet\\'..char..'\\'..cs)
+ tex.print(string.format('\\protected\\def\\%s{\\math:radical{%s}%s}', cs, code, char))
+ tex.print(string.format('\\mathlet\\%s\\%s', char, cs))
end
else
-- TODO: in the future, allow accent characters by re-ordering them
tex.setmathcode(code, 0, default_fam, 0) -- provisional
- if cs then token.set_macro(cs, tex_accent(class, code, char)) end
+ if cs then
+ tex.print(string.format('\\protected\\def\\%s{\\math:%s{%s}%s}', cs, accents[class], code, char))
+ end
end
add_to_alphabet(code, class)
if alphabet then
diff --git a/macros/luatex/generic/minim-math/minim-math.pdf b/macros/luatex/generic/minim-math/minim-math.pdf
index 6a80c7a07e..30e2129fcf 100644
--- a/macros/luatex/generic/minim-math/minim-math.pdf
+++ b/macros/luatex/generic/minim-math/minim-math.pdf
Binary files differ
diff --git a/macros/luatex/generic/minim-math/minim-math.tex b/macros/luatex/generic/minim-math/minim-math.tex
index d24a6693dc..1a8f1157c1 100644
--- a/macros/luatex/generic/minim-math/minim-math.tex
+++ b/macros/luatex/generic/minim-math/minim-math.tex
@@ -9,12 +9,23 @@
% \mathdef c {expansion}
\protected\def\mathdef#1#2{%
- \def\mathdef:tmp{#2}%
- \mathlet{#1}\mathdef:tmp}
-% \mathlet c \csname
-\protected\def\mathlet#1#2{%
+ \protected\def\mathdef:tmp{#2}%
\mathcode`#1="8000\relax
- \letcharcode`#1=#2}
+ \letcharcode`#1=\mathdef:tmp}
+% \mathlet c \csname
+\let\mathlet = \mathdef
+
+% sensible defaults
+\matheqdirmode = 1 % short skips also in rtl
+\mathdisplayskipmode = 1
+%
+% not set here, but maybe relevant:
+%
+% \mathsurroundskip = ...
+% \mathitalicsmode = 1
+% \mathscriptboxmode = 2
+% \matheqnogapstep = 1000 % default value
+% \predisplaygapfactor = 2000 % default value
% 1 Global style transformations
@@ -86,20 +97,20 @@
\newcount \accentfam \accentfam = 0
\newcount \extensiblefam \extensiblefam = 0
-\def\math:accent{\ifmmode% spec num char
+\protected\def\math:accent{\ifmmode% spec num char
\expandafter\math:mathaccent\else
\expandafter\math:textaccent\fi}
\def\math:mathaccent#1#2#3{\Umathaccent #1"0 \accentfam #2 }
\def\math:textaccent#1#2#3#4{#4#3}% might be a normal combining accent
-\def\math:stack#1#2#3{% spec num char
+\protected\def\math:stack#1#2#3{% spec num char
\withoptions[]{\math:stack:{#1}{#2}{#3}}}
\def\math:stack:#1#2#3[#4]#5{%
{\mathop{\math:accent{#1}{#2}{#3}{#5}}%
\ifx\math:stack#4\math:stack\else \limits
\ifx\math:stack#1\math:stack^\else_\fi{#4}\fi}}
-\def\math:radical{\ifmmode% num char
+\protected\def\math:radical{\ifmmode% num char
\expandafter\math:mathradical\else
\expandafter\math:textradical\fi}
\def\math:mathradical#1#2{\Uradical\radicalfam #1 }
@@ -255,6 +266,7 @@
\normalroots
+\let\of=\relax
\mathlet √ \sqrt
\mathdef ∛ {\root3\of}
\mathdef ∜ {\root4\of}
@@ -529,23 +541,34 @@
% 1 Displayed equations
-\newif\ifdisplay:
-\newif\ifdisplay:nr
-\newif\ifleftdisplays
-\newtoks\setdisplaynr
-\newtoks\display:specialnr
-\display:specialnr{\the\setdisplaynr}
-
% \display
\protected\def\display{\display:true}
% \displaynr {nr}
\protected\def\displaynr{\display:true\display:nrtrue
\nextifcat\bgroup\display:specialnr\relax}
+% \leftdisplaystrue
+\newif\ifleftdisplays
+% \setdisplaynr {...}
+\newtoks\setdisplaynr
+% \equationnumber
+\newcount\equationnumber \equationnumber = 0
+\setdisplaynr{\global\advance\equationnumber1(\the\equationnumber)}
+
+\newif\ifdisplay:
+\newif\ifdisplay:nr
+\newtoks\display:specialnr
+\display:specialnr{\the\setdisplaynr}
+
% \[ ... \]
-\protected\def\[{\ifdisplay:\expandafter\startdisplay\else\Ustartmath\fi}
-\protected\def\]{\global\expandafter\display:false
+\protected\def\[#1\]{%
+ \ifdisplay:\expandafter\startdisplay\else\Ustartmath\fi
+ \tagging:tagformula{#1}#1\relax
+ \global\expandafter\display:false
\ifdisplay:\expandafter\stopdisplay\else\Ustopmath\fi}
+\protected\def\]{\errmessage{Unmatched \string\]}}
+\ifdefined \tagging:tagformula \else
+ \let\tagging:tagformula=\ignore \fi
\protected\def\startdisplay{%
% if the display starts a paragraph, omit baselineskip
@@ -553,6 +576,8 @@
\ifvmode \nointerlineskip
\noindent \hbox to .5\hsize{\hss}\fi
\Ustartdisplaymath
+ % compatibility with minim-pdf
+ \csname tagging:indisplaytrue\endcsname
% for left-aligned equations, force manual placement
\ifleftdisplays\muskip0mu\fi}
\protected\def\stopdisplay{%
@@ -565,10 +590,6 @@
\Ustopdisplaymath
\ignorespaces}
-% default equation numbering
-\newcount\equationnumber \equationnumber = 0
-\setdisplaynr{\global\advance\eqnr1(\the\equationnumber)}
-
%  standard mappings
diff --git a/macros/luatex/generic/minim-mp/README b/macros/luatex/generic/minim-mp/README
index b28f8c4267..d712a7f11c 100644
--- a/macros/luatex/generic/minim-mp/README
+++ b/macros/luatex/generic/minim-mp/README
@@ -1,4 +1,4 @@
-Version: 2023/1.2
+Version: 2023/1.3
SUMMARY
@@ -19,11 +19,24 @@ In order to obtain the typeset manual of this package, simply say:
HISTORY
+2023/1.3 (20/10/2023)
+
+ * Various runscript improvements:
+ - Supplement runscript environment with runscript-specific functions.
+ - Provide an escape hatch for passing raw strings to lua.
+ - Add helper macros and functions for safely passing values between
+ metapost and lua.
+ * File i/o from metapost code is now included in the --recorder output.
+ * Improve font handling in minim-lamp.ini, and provide \begin{document}.
+ * Add a minim-mp.sty package file for LaTeX users.
+
+
2023/1.2 (3/3/2023)
* Add a few more metapost macros.
* Various small bug fixes.
+
2022/1.1 (3/3/2022)
New features:
diff --git a/macros/luatex/generic/minim-mp/minim-lamp.ini b/macros/luatex/generic/minim-mp/minim-lamp.ini
index 7900b3936a..d6ba6a6637 100644
--- a/macros/luatex/generic/minim-mp/minim-lamp.ini
+++ b/macros/luatex/generic/minim-mp/minim-lamp.ini
@@ -4,13 +4,20 @@
\input lualatex.ini
+\catcode`\@=11
+% see minim-lamp.mp
+\def\providebegindocument{%
+ \ifx\@onlypreamble\@notprerr
+ \else\expandafter\document\fi}
+\catcode`\@=12
+
\toksapp\everyjob{%
\documentclass{article}
- \input minim-mp
+ \usepackage{polyglossia, minim-mp}
\message{This is the minim-latex metapost processor.}\relax
\directlua {require('minim-mp').on_line = true}%
\newmetapostinstance[jobname="\jobname", mathmode="scaled"]\MP
- \runmetapost\MP{input "\jobname";}%
+ \runmetapost\MP{input "minim-lamp.mp"; input "\jobname";}%
\directlua {require('minim-mp').shipout(\the\MP)}%
\closemetapostinstance\MP
\csname @fileswfalse\endcsname\end{document}}
diff --git a/macros/luatex/generic/minim-mp/minim-lamp.mp b/macros/luatex/generic/minim-mp/minim-lamp.mp
new file mode 100644
index 0000000000..f3bc934d65
--- /dev/null
+++ b/macros/luatex/generic/minim-mp/minim-lamp.mp
@@ -0,0 +1,10 @@
+
+% This is a companion file to minim-lamp.ini, part of the minim-mp package.
+
+% the first figure ends the preamble
+let realbeginfig = beginfig;
+def beginfig =
+ scantokens "verbatimtex \providebegindocument etex;";
+ let beginfig = realbeginfig; realbeginfig
+enddef;
+
diff --git a/macros/luatex/generic/minim-mp/minim-mp.doc b/macros/luatex/generic/minim-mp/minim-mp.doc
index af84cd1581..2f7541ba75 100644
--- a/macros/luatex/generic/minim-mp/minim-mp.doc
+++ b/macros/luatex/generic/minim-mp/minim-mp.doc
@@ -8,8 +8,8 @@
\else \startmetadata
author {Esger Renkema}
title {minim-mp}
- date {2023-03-03}
- version {2023/1.2}
+ date {2023-10-20}
+ version {2023/1.3}
keywords {LuaTeX; Plain TeX; MetaPost; mplib}
stopmetadata
\maketitle \fi
@@ -32,14 +32,34 @@ explained there (for simple cases, you will not need them).
The logging of the metapost run will be included in the regular log file. If an
error occurs, the logging will also be shown on the terminal.
+\section As a stand-alone Metapost compiler % 1
+
This package can also be used as a stand-alone metapost compiler. Saying
\stopformulatagging$$\hbox{⟦luatex --fmt=minim-mp your_file.mp⟧}$$\startformulatagging
will create a pdf file of all images in ⟦your_file.mp⟧, in order, with page
sizes adjusted to image dimensions. You might need generate the format first,
this is done with
\stopformulatagging$$\hbox{⟦luatex --ini minim-mp.ini⟧}$$\startformulatagging
-Use minim-lamp instead of minim-mp for an experimental latex-based version
-of the minim-mp format.
+Use minim-lamp instead of minim-mp for a latex-based version of the minim-mp
+format.
+With minim-lamp, for specifying the contents of the preamble, you can use
+⟦verbatimtex ... etex;⟧ statements at the top of your file.
+Concluding the preamble with ⟦\begin{document}⟧ is optional, as both ⟦\begin⟧
+and ⟦\end{document}⟧ will be inserted automatically if omitted.
+
+\section LaTeX compatibility % 1
+
+An experimental latex package is included in ⟦minim-mp.sty⟧. It accepts no
+options, as it really is a rather thin wrapper around the plain tex package.
+It does, however, provide a proper ⟦*metapost⟧ latex environment as an
+alternative to ⟦\directmetapost⟧.
+
+The ⟦metapost⟧ environment has no persistent backing instance.
+You can create a similar environment ⟦envname⟧ that does with
+⟦*\newmetapostenvironment [options] {envname}⟧.
+If your demands are even more complex, you should fall back to the plain tex
+commands described below.
+
\section Metapost instances % 1
@@ -81,6 +101,7 @@ tex can parse as a number.
– Puts the image named ⟦name⟧ in box ⟦box-nr⟧.
Say ⟦*\remainingmpimages \id⟧ for the number of images not yet retrieved.
+
Finally, as a shorthand, ⟦*\runmetapostimage \id { code }⟧ will add
⟦beginfig ... endfig⟧ to your ⟦code⟧ and write the resulting image immediately
to the current list.
@@ -111,6 +132,7 @@ a (numerical) font~id or the (cs)name of a font (without backslash).
One possible use of the ⟦infont⟧ operator is setting text along curves:
+\nobreak
⟦beginfig(1)
save t, w, r, a; picture t;
t = "Running TeX from within MetaPost" infont "tenrm";
@@ -138,7 +160,11 @@ beginfig(1)
endfor
endfig;}\starttagging \stopelement{Figure}
-For the greatest amount of control, however, you will need the ⟦*glyph g of f⟧
+By default, the ⟦maketext⟧ operator is used for typesetting labels. You can,
+however, order de ⟦label⟧ macro to use ⟦infont⟧ instead by setting
+⟦*maketextlabels⟧ to ⟦false⟧.
+
+For the greatest amount of control, you can use the ⟦*glyph g of f⟧
operator, which returns the contours that make up a glyph. It is a bit more
versatile than its traditional metapost counterpart: ⟦g⟧ may also be the name
of the glyph instead of its index, while ⟦f⟧ can be a font~id or font name (as
@@ -191,6 +217,17 @@ metapost instance carries around its own lua environment so that assignments
you make are local to the instance. (You can of course order the global
environment to be used by giving ⟦env = _G⟧ as option to
⟦\newmetapostinstance⟧.)
+Any environment you specify will be supplemented with the contents of the
+⟦*M.mp_functions⟧ table. It currently contains two functions: ⟦quote(s)⟧, which
+escapes all double quotes in the string ⟦s⟧ before surrounding it with the same
+(so that it may be read as a metapost string literal); and ⟦sp_to_pt(nr)⟧,
+which prints dimensions in points (preventing overflows).
+
+When using ⟦runscript⟧ in this way, you must ensure its argument is a correct
+lua program. As an escape hatch, raw strings can be passed to lua with
+⟦runscript ("[[function_name]]" & raw_string)⟧.
+This will return the result of the function ⟦function_name⟧ applied to
+⟦raw_string⟧ as a lua string.
If your lua snippet returns nothing, the ⟦runscript⟧ call will be invisible to
metapost. If on the other hand it does return a value, that value will have to
@@ -199,6 +236,9 @@ be translated to metapost. Numbers and strings will be returned as they are
a metapost string). You can return a point or colour by returning an array of
two to four elements. For other return values, ⟦tostring()⟧ will be called.
+
+\section Passing values to lua
+
Do keep in mind that metapost and lua represent numbers in different ways and
that rounding errors may occur. For instance, metapost’s ⟦decimal epsilon⟧
returns ⟦0.00002⟧, which metapost understands as ⟦1/65536⟧, but lua as
@@ -210,10 +250,38 @@ than tex, so that ⟦epsilon⟧ when taken as a dimension is not quite equal to
⟦1sp⟧. Use the metapost macro ⟦*scaledpoints⟧ for passing to lua a metapost
dimension as an integral number of scaled points.
+Strings can be passed to lua with the ⟦*lua_string⟧ macro, which escapes the
+necessary characters and then surrounds its argument with quotes.
+A generic macro for passing values to lua, finally, is ⟦*quote_for_lua⟧, which
+automatically converts strings, numbers, points and colours to (metapost)
+strings that lua can understand.
+
+
+\section Querying tex and lua variables
+
+Stitching together lua snippets by hand is not very convenient. Therefore,
+minim-mp provides three helper macros that should cover most lua interaction.
+For running a single lua function, ⟦*luafunction⟧ ⟦<suffix> (<arguments>)⟧
+returns the result of the function ⟦str <suffix>⟧ applied to any number of
+arguments, which are quoted automatically.
+Variables can be queried with ⟦*luavariable⟧ ⟦<suffix>⟧
+and set with ⟦*setluavariable⟧ ⟦<suffix> = <value>;⟧.
+
+The details of metapost tokenisation make these macros rather powerful: you can
+not only say e.g. ⟦luavariable tex.jobname⟧ to get the current jobname, but
+even define a ⟦texvariable⟧ macro with
+\stopformulatagging$$\hbox{⟦vardef texvariable @# = luavariable tex @# enddef;⟧}$$\startformulatagging
+and have ⟦texvariable jobname⟧ work as expected.
+
+For accessing count, dimen, attribute or toks registers, the macros are
+⟦*tex.count⟧ ⟦[number]⟧ or ⟦*tex.count⟧⟦.name⟧ [etc. etc.] for getting and
+⟦*set tex.count⟧ ⟦[number] = value⟧ or ⟦*tex.count⟧⟦.name = value⟧ etc. for
+setting values.
+
\section Tiling patterns % 1
-The ⟦*withpattern(<name>)⟧ added to a ⟦fill⟧ statement will fill the path with
+The condition ⟦*withpattern(<name>)⟧ added to a ⟦fill⟧ statement will fill the path with
a pattern instead of a solid colour. If the patterns contains no colour
information of itself, it will have the colour given by ⟦withcolor⟧.
Stroking operations (the ⟦draw⟧ part) will not be affected.
diff --git a/macros/luatex/generic/minim-mp/minim-mp.ini b/macros/luatex/generic/minim-mp/minim-mp.ini
index e3312a0cb9..11387e00d1 100644
--- a/macros/luatex/generic/minim-mp/minim-mp.ini
+++ b/macros/luatex/generic/minim-mp/minim-mp.ini
@@ -1,7 +1,7 @@
\catcode`\{=1 \catcode`\}=2
\def\fmtname{minim-mp}
-\def\fmtversion{2021/1.0}
+\def\fmtversion{2023/1.3}
\input minim.ini
diff --git a/macros/luatex/generic/minim-mp/minim-mp.lua b/macros/luatex/generic/minim-mp/minim-mp.lua
index a247c49499..f1cfd49388 100644
--- a/macros/luatex/generic/minim-mp/minim-mp.lua
+++ b/macros/luatex/generic/minim-mp/minim-mp.lua
@@ -8,6 +8,16 @@ local M = {}
-- 1 AUXILIARY FUNCTIONS
+local GLUE_NODE = node.id 'glue'
+local GLYPH_NODE = node.id 'glyph'
+local KERN_NODE = node.id 'kern'
+local WHATSIT_NODE = node.id 'whatsit'
+
+local function copy_table(from, to)
+ for k,v in pairs(from) do to[k] = v end
+ return to
+end
+
-- 2 state metatable
-- q Q store/restore graphics state
@@ -338,7 +348,7 @@ function A.linestate (append, object)
local res = { }
if ml and ml ~= append.miterlimit then
append.miterlimit = ml
- table.insert(res, pdfnum('M', ml)) -- TODO is pdfnum here correct?
+ table.insert(res, pdfnum('M', ml))
end
local lj = object.linejoin
if lj and lj ~= append.linejoin then
@@ -352,7 +362,7 @@ function A.linestate (append, object)
end
local dl = object.dash
if dl then
- local d = string.format('[%s] %i d', table.concat(dl.dashes or {},' '), dl.offset)
+ local d = string.format('[%s] %s', table.concat(dl.dashes or {},' '), pdfnum('d', dl.offset))
if d ~= append.dashed then
append.dashed = d
table.insert(res, d)
@@ -691,13 +701,13 @@ local function definepattern(head, user, bbox)
for n in node.traverse(head) do
-- try if we can construct the content stream ourselves; otherwise,
-- stuff the pattern template into an xform.
- if n.id == 8 then
+ if n.id == WHATSIT_NODE then
-- try if we can simply concatenate pdf statements
- if n.subtype == 16 then -- literal
+ if n.subtype == node.subtype 'literal' then
table.insert(literals, n.data)
- elseif n.subtype == 30 then -- save
+ elseif n.subtype == node.subtype 'save' then
table.insert(literals, 'q')
- elseif n.subtype == 31 then -- restore
+ elseif n.subtype == node.subtype 'restore' then
table.insert(literals, 'Q')
else
resources, pat.stream = make_pattern_xform(head, bb)
@@ -807,8 +817,6 @@ M.instances = instances
-- 2 small instance helper functions
-local default_catcodes = alloc.registernumber('minim:mp:catcodes')
-
-- parameters: wd, ht+dp, dp
local function make_transform(w, h, d)
return string.format('identity xscaled %fpt yscaled %fpt shifted (0,-%fpt)',
@@ -866,11 +874,27 @@ local function print_log (nr, res, why)
i.status = res.status
end
-local function finder (name, mode, ftype)
- if mode == 'w' then
+local function new_file_finder()
+ -- HACK: The file finder is called twice for every file input by metapost.
+ -- Thus, in order for the file recorder to work properly, we only record
+ -- repeated files and hope for the best.
+ local last_found = false
+ local function record(name, record_fn)
+ if last_found and name == last_found then
+ record_fn(name)
+ last_found = false
+ else
+ last_found = name
+ end
+ end
+ return function(name, mode, ftype)
+ if mode == 'w' then
+ record(name, kpse.record_output_file)
+ else
+ name = kpse.find_file(name, ftype)
+ record(name, kpse.record_input_file)
+ end
return name
- else
- return kpse.find_file(name,ftype)
end
end
@@ -933,6 +957,12 @@ end
-- 2 running lua scripts
+-- There are two ways of running lua code with the runscript primitive. In the
+-- normal case, the argument to runscript is executed as lua code. You can
+-- bypass this mechanism by prepending the runscript argument with the name of
+-- a lua function between double square brackets. Then, the remainder of the
+-- string will be passed as an argument to that function.Q
+--
-- Code run with runscript may return a value; we will try and convert it to
-- a string that metapost can understand.
--
@@ -940,16 +970,17 @@ end
-- converted to a transformation. (The margin will be ignored for now, this may
-- change in the future.)
-local function default_env()
- local env = { }
- for k,v in pairs(_G) do
- env[k] = v
- end
- return env
+local function mkluastring(s)
+ return "'"..(s:gsub('\\', '\\\\'):gsub("'", "\\'")).."'"
end
local function runscript(code)
+ -- (possibly) pass directly to function
+ local fn, arg = code:match('^%[%[(.+)%]%](.*)')
+ if fn then code = 'return '..fn .. mkluastring(arg) end
+ -- execute the script
local f, msg = load(code, current_instance.jobname, 't', current_instance.env)
+ -- interpret results
if f then
local result = f()
if result == nil then
@@ -980,6 +1011,36 @@ local function runscript(code)
end
end
+-- The environment the scripts will be run in can be specified at instance
+-- creation; its default value is a copy (at that time) of the global
+-- environment. The runscript environment will then be extended with all
+-- elements of the M.mp_functions table.
+
+local E = { }
+M.mp_functions = E
+
+local function prepare_env(e) -- in M.open()
+ local env = e or copy_table(_G, { })
+ return copy_table(E, env)
+end
+
+function E.quote(val)
+ if type(val) ~= 'string' then return val end
+ local escaped = string.format('%s', val):gsub('"', '"&ditto&"')
+ return string.format('("%s")', escaped)
+end
+
+function E.sp_to_pt(val)
+ return string.format('%.16fpt', val/65536)
+end
+
+function E.quote_for_lua(s)
+ return E.quote(mkluastring(s))
+end
+
+E.rgb_to_gray = rgb_to_gray
+E.enable_debugging = M.enable_debugging
+
-- 2 typesetting with tex
-- The result of the maketext function is fed back into metapost; on that side,
@@ -1001,7 +1062,7 @@ local function maketext(text, mode)
if mode == 0 then -- btex or maketext
local nr = alloc.new_box()
table.insert(current_instance.boxes, nr)
- local assignment = string.format('\\global\\setbox%s=\\hbox{%s}', nr, text)
+ local assignment = string.format('\\global\\setbox%s=\\hbox{\\the\\everymaketext %s}', nr, text)
tex.runtoks(function() tex.print(current_instance.catcodes, assignment:explode('\n')) end)
local box = tex.box[nr]
return 'image ( fill unitsquare withprescript "TEXBOX:' ..nr..'";'..
@@ -1020,25 +1081,25 @@ local typeset_box = alloc.new_box('infont box')
local function process_typeset(text, fnt, sep, fn)
tex.runtoks(function()
- tex.sprint(default_catcodes, string.format(
+ tex.sprint(current_instance.catcodes, string.format(
'\\setbox%d=\\hbox{\\setfontid%d\\relax', typeset_box, getfontid(fnt)))
- tex.sprint(-2, text); tex.sprint(default_catcodes, '}')
+ tex.sprint(-2, text); tex.sprint(current_instance.catcodes, '}')
end)
local res, x = { }, 0
for n in node.traverse(tex.box[typeset_box].list) do
- if n.id == 29 then -- glyph
+ if n.id == GLYPH_NODE then
fn(res, n, x)
x = x + n.width
- elseif n.id == 12 then -- glue
+ elseif n.id == GLUE_NODE then
x = x + n.width
- elseif n.id == 13 then -- kern
+ elseif n.id == KERN_NODE then
x = x + n.kern
end
end
return table.concat(res, sep)
end
-function M.infont(text, fnt)
+function E.minim_infont(text, fnt)
return process_typeset(text, fnt, '', function(res, n, x)
table.insert(res, string.format(
'draw image ( fill unitsquare shifted (%fpt,0) withprescript "CHAR:%d %d %d %d";'
@@ -1059,6 +1120,10 @@ function M.make_glyph(glyphname, fnt)
end
local fontid = getfontid(fnt)
local thefont = font.getfont(fontid)
+ if not thefont then
+ alloc.err('Font not found: %s (id %s)', fnt, fontid)
+ return { }, 10
+ end
local fontname = thefont.psname
local gid = luaotfload.aux.gid_of_name(fontid, glyphname)
if not gid then
@@ -1091,24 +1156,22 @@ function M.make_glyph(glyphname, fnt)
return paths, thefont.size
end
-function M.get_named_glyph(name, fnt)
- local res, contours, size = {}, M.make_glyph(name, fnt)
- for _, c in ipairs(contours) do
- table.insert(res, string.format(
- '%s scaled %f', c, size/65536000))
- end
- return table.concat(res, ', ')
-end
-
local function get_glyphname(c_id)
return luaotfload.aux.name_of_slot(c_id)
end
-function M.get_glyph(c_id, fnt)
- return M.get_named_glyph(get_glyphname(c_id), fnt)
+function E.get_glyph(c_id, fnt)
+ local name = (type(c_id) == 'string') and c_id or get_glyphname(c_id)
+ local contours, size = M.make_glyph(name, fnt)
+ local res = { }
+ for _, c in ipairs(contours) do
+ table.insert(res, string.format(
+ '%s scaled %f', c, size/65536/1000))
+ end
+ return table.concat(res, ', ')
end
-function M.get_contours(text, fnt)
+function E.get_contours(text, fnt)
return process_typeset(text, fnt, ', ', function(res, n, x)
local contours, size = M.make_glyph(get_glyphname(n.char), n.font)
for _, c in ipairs(contours) do
@@ -1119,6 +1182,9 @@ function M.get_contours(text, fnt)
end)
end
+M.get_contours = E.get_contours
+
+
-- 2 opening, running and and closing instances
local function apply_default_instance_opts(t)
@@ -1126,7 +1192,7 @@ local function apply_default_instance_opts(t)
{ ini_version = true
, error_line = (texconfig.error_line or 79) - 5
, extensions = 1
- , find_file = finder
+ , find_file = new_file_finder()
--, script_error = ...
, job_name = t.jobname
, math_mode = t.math or 'scaled'
@@ -1168,7 +1234,8 @@ function M.run (nr, code)
save_image_list(self, picts)
end
-M.init_code = { 'let dump=endinput;', 'input INIT;', 'input minim.mp;' }
+M.init_code = { 'let dump=endinput;', 'input INIT;', 'input minim-mp.mp;', 'input minim.mp;' }
+local maketext_catcodes = alloc.registernumber('minim:mp:catcodes:maketext')
function M.open (t)
local nr = #instances + 1
@@ -1184,10 +1251,11 @@ function M.open (t)
, jobname = t.jobname
, results = { first = nil, last = nil, by_name = {}, count = 0 }
, status = 0
- , catcodes = t.catcodes or default_catcodes
+ , catcodes = t.catcodes or maketext_catcodes
, boxes = { } -- allocated by maketext
- , env = t.env or default_env()
+ , env = prepare_env(t.env)
}
+ current_instance = instances[nr]
print_log(nr, instance:execute(init), 'opening ')
return nr
end
@@ -1274,14 +1342,23 @@ end
local scan_int = token.scan_int
local scan_string = token.scan_string
+local code_catcodes = alloc.registernumber('minim:mp:catcodes:mpcode')
+local function scan_codestring()
+ local cct = tex.catcodetable
+ tex.catcodetable = code_catcodes
+ local res = token.scan_string()
+ tex.catcodetable = cct
+ return res
+end
+
alloc.luadef('closemetapostinstance', function() M.close(scan_int()) end)
alloc.luadef('runmetapost', function()
- M.run(scan_int(), scan_string())
+ M.run(scan_int(), scan_codestring())
end, 'protected')
alloc.luadef('runmetapostimage', function()
local i = scan_int()
- local code = 'beginfig(0)'..scan_string()..';endfig;'
+ local code = 'beginfig(0)'..scan_codestring()..';endfig;'
M.run(i, code)
node.write(M.get_image(i))
end, 'protected')
diff --git a/macros/luatex/generic/minim-mp/minim-mp.mp b/macros/luatex/generic/minim-mp/minim-mp.mp
new file mode 100644
index 0000000000..654449da03
--- /dev/null
+++ b/macros/luatex/generic/minim-mp/minim-mp.mp
@@ -0,0 +1,227 @@
+
+delimiters ();
+
+% This file is specific to the minim metapost processor (minim-mp) and cannot
+% be used with luaotfload or other metapost implementations. See minim.mp for
+% general-purpose extensions.
+
+message "Setting up the minim MetaPost extensions";
+
+def save_picture text t = save t; picture t; enddef;
+def save_boolean text t = save t; boolean t; enddef;
+def save_string text t = save t; string t; enddef;
+
+% 1 lua scripts, functions and variables
+
+def luafunction = gobble inner_luafunction enddef;
+vardef inner_luafunction @# (text args) = endgroup
+ % we remove the grouping so that the function may return any fragment of
+ % lua code, in particular lists to-be-parsed as text parameters
+ runscript ("return " & (str @#) & build_lua_arguments(args))
+ gobble begingroup enddef;
+
+vardef build_lua_arguments (text args) =
+ save_boolean first; first = true;
+ "(" for a = args:
+ & if first: hide(first:=false) "" else: "," fi
+ & (quote_for_lua a)
+ endfor & ")"
+enddef;
+
+vardef luavariable @# =
+ runscript ("return quote("&(str @#)&")") enddef;
+vardef setluavariable @# expr val=
+ runscript ((str @#)&" = "&quote_for_lua(val)) enddef;
+
+% quoting expressions for lua
+
+vardef lua_string(expr s) =
+ runscript ("[[quote_for_lua]]"&s) enddef;
+
+vardef hexadecimal expr n =
+ % TODO: support other number systems
+ save d, a, m; a = ASCII("a"); m := abs n;
+ save_string res; res =
+ if n < 0: "-0x" else: "0x" fi
+ for i = 1 upto 7: &
+ hide(d := floor m div 256; m := 16(m - 256d);)
+ if d < 10: decimal d else: char(a+d-10) fi
+ if i = 3: & "." fi
+ endfor; res enddef;
+
+vardef scaledpoints expr n =
+ % use the fact that 800bp = 803pt
+ save a, m; m := abs n;
+ a1 = m div (1025/1024); % 1025/1024 = 800epsilon * 82
+ m := m mod (1025/1024);
+ a2 = m div 800epsilon; % max 82
+ m := (m mod 800epsilon) div epsilon;
+ a3 = floor(m*803/800 + 1/2);
+ save_string res; res =
+ if n < 0: "-(" else: "(" fi
+ & decimal(a1) & "*803*82 + "
+ & decimal(a2) & "*803 + "
+ & decimal(a3) & ")";
+ res enddef;
+
+vardef quote_for_lua expr a =
+ save_string res; res =
+ if string a:
+ lua_string(a)
+ elseif numeric a:
+ hexadecimal(a)
+ elseif pair a:
+ "{"&hexadecimal(xpart a) &
+ ","&hexadecimal(ypart a) & "}"
+ elseif boolean a:
+ if a: "true" else: "false" fi
+ elseif color a:
+ "{"&hexadecimal(redpart a) &
+ ","&hexadecimal(greenpart a) &
+ ","&hexadecimal(bluepart a) & "}"
+ elseif cmykcolor a:
+ "{"&hexadecimal(cyanpart a) &
+ ","&hexadecimal(magentapart a) &
+ ","&hexadecimal(yellowpart a) &
+ ","&hexadecimal(blackpart a) & "}"
+ elseif pen a:
+ hide(errmessage("I cannot pass a pen value to lua");)
+ "nil"
+ elseif transform a:
+ hide(errmessage("I cannot pass a transform value to lua");)
+ "nil"
+ elseif picture a:
+ hide(errmessage("I cannot pass a picture value to lua");)
+ "nil"
+ else: % probably intentionally vacuous, which is fine
+ "nil"
+ fi; res enddef;
+
+% tex registers
+
+string _SUFFIX_HACK_[];
+vardef index_or_suffix (suffix s) =
+ save_string res; res =
+ if string _SUFFIX_HACK_ s: % this is a number
+ "["&decimal(s)&"]"
+ else: % this is a suffix
+ "."&(str s)
+ fi ; res enddef;
+
+vardef tex.count @# =
+ runscript ("return tex.count" & index_or_suffix(@#)) enddef;
+vardef tex.attribute @# =
+ runscript ("return tex.attribute" & index_or_suffix(@#)) enddef;
+vardef tex.dimen @# =
+ runscript ("return sp_to_pt(tex.dimen" & index_or_suffix(@#) & ")") enddef;
+vardef tex.toks @# =
+ runscript ("return quote(tex.toks" & index_or_suffix(@#) & ")") enddef;
+
+vardef set tex.count @# expr val =
+ runscript ("tex.count" & index_or_suffix(@#) & " = " & decimal(val)) enddef;
+vardef set tex.attribute @# expr val =
+ runscript ("tex.attribute" & index_or_suffix(@#) & " = " & decimal(val)) enddef;
+vardef set tex.dimen @# expr val =
+ runscript ("tex.dimen" & index_or_suffix(@#) & " = " & scaledpoints(val)) enddef;
+vardef set tex.toks @# expr val =
+ runscript ("tex.toks" & index_or_suffix(@#) & " = " & lua_string(val)) enddef;
+
+% 1 typesetting
+
+let textext = maketext;
+let TEX = maketext;
+
+% baseline pair or numeric -> fill statement
+vardef baseline expr o =
+ fill if numeric o : (0,o) else: o fi
+ -- cycle withprescript "BASELINE:"; enddef;
+
+% boxresource number -> fill statement + setbounds
+vardef boxresource expr nr = image(
+ fill unitsquare withprescript "BOXRESOURCE:" & decimal nr ;
+ setbounds currentpicture to unitsquare transformed runscript
+ ("return { 'box_size', tex.getboxresourcedimensions(" & decimal nr & ") }");
+ ) enddef;
+
+% actual typesetting
+
+primarydef t infont f = image( luafunction minim_infont (t, f); ) enddef;
+
+def glyph expr c of f = luafunction get_glyph (c, f) enddef;
+
+def contours expr t of f = luafunction get_contours (t, f) enddef;
+
+% labels can optionally be typeset with maketext
+% and the default font must be set explicitly
+string defaultfont;
+boolean maketextlabels;
+maketextlabels := true;
+vardef thelabel@#(expr s,z) = % Position s near z
+ save_picture p;
+ p = if picture s : s
+ elseif maketextlabels : maketext s
+ elseif known defaultfont : s infont defaultfont
+ else : s infont luafunction font.current()
+ fi;
+ p shifted (z + labeloffset*laboff@# -
+ (labxf@#*lrcorner p + labyf@#*ulcorner p
+ + (1-labxf@#-labyf@#)*llcorner p
+ ) ) enddef;
+
+% 1 even-odd rule and multidraw
+
+def nofill expr c = fill c withprescript "OTYPE:nofill" enddef;
+def eofill expr c = fill c withprescript "OTYPE:eofill" enddef;
+def eofilldraw expr c = filldraw c withprescript "OTYPE:eofilldraw" enddef;
+
+def multidraw (text paths) text opts = draw image(
+ for p = paths: ; nofill p opts endfor
+ withprescript "OTYPE:outline";) enddef;
+def multifill (text paths) text opts = draw image(
+ for p = paths: ; nofill p opts endfor
+ withprescript "OTYPE:fill";) enddef;
+def multifilldraw (text paths) text opts = draw image(
+ for p = paths: ; nofill p opts endfor
+ withprescript "OTYPE:filldraw";) enddef;
+def multieofill (text paths) text opts = draw image(
+ for p = paths: ; nofill p opts endfor
+ withprescript "OTYPE:eofill";) enddef;
+def multieofilldraw (text paths) text opts = draw image(
+ for p = paths: ; nofill p opts endfor
+ withprescript "OTYPE:eofilldraw";) enddef;
+
+% 1 patterns
+
+def withpattern(suffix s) = withprescript
+ ("fillpattern: " & decimal(_patterns_.s)) enddef;
+def beginpattern(suffix s) =
+ begingroup clearxy; clearit;
+ interim defaultcolormodel:=1;
+ charcode:=incr _patterns_._last_;
+ _patterns_.s:=charcode; drawoptions();
+ pickup pencircle scaled 0.4pt;
+ save painttype; painttype:=2;
+ save _withcolor; let _withcolor = withcolor; save withcolor;
+ def withcolor = hide(painttype:=1) _withcolor enddef;
+ save matrix; transform matrix; enddef;
+def endpattern (expr xstep, ystep) =
+ if unknown matrix : matrix:=identity; fi
+ special "definepattern:" for e = charcode, tilingtype, painttype,
+ xstep, ystep, xxpart matrix, xypart matrix, yxpart matrix, yypart matrix:
+ & " " & decimal(e) endfor;
+ shipit; endgroup enddef;
+newinternal tilingtype; tilingtype:=1;
+_patterns_._last_ := 0;
+
+
+% 1 miscellaneous
+
+vardef debug_pdf = luafunction enable_debugging () enddef;
+
+def texmessage text msg = luafunction texio.write_nl (msg) enddef;
+
+vardef rgb_to_gray expr c =
+ luafunction rgb_to_gray (redpart c, greenpart c, bluepart c)
+enddef;
+
+% 
diff --git a/macros/luatex/generic/minim-mp/minim-mp.pdf b/macros/luatex/generic/minim-mp/minim-mp.pdf
index 15ada22b36..aa64541ffe 100644
--- a/macros/luatex/generic/minim-mp/minim-mp.pdf
+++ b/macros/luatex/generic/minim-mp/minim-mp.pdf
Binary files differ
diff --git a/macros/luatex/generic/minim-mp/minim-mp.sty b/macros/luatex/generic/minim-mp/minim-mp.sty
new file mode 100644
index 0000000000..ae0312b560
--- /dev/null
+++ b/macros/luatex/generic/minim-mp/minim-mp.sty
@@ -0,0 +1,59 @@
+
+\ProvidesPackage{minim-mp}[2023/03/03 Run MetaPost code from LaTeX]
+
+\input minim-mp
+
+\makeatletter
+
+\RequirePackage{environ}
+
+% work around latex’s \protect mechanism
+\let\mnm@protect=\protect
+\everymaketext{\let\protect=\mnm@protect}
+
+% the defaultfont is the current \normalfont
+\def\mnm@setnormalfont{{\normalfont
+ \xdef\mnm@normalfont{\expandafter\csstring\the\font}}}
+
+% a one-off metapost environment
+\newenvironment{metapost}[1][]{%
+ \begingroup \catcode`\#=12
+ \let\mpcolor = \minimpcolor
+ \csname:metapost:\endcsname[#1]}
+ {\csname end:metapost:\endcsname \endgroup}
+\NewEnviron{:metapost:}[1][]{%
+ \mnm@setnormalfont
+ \let\protect=\noexpand
+ \directmetapost[#1]{%
+ defaultfont:="\mnm@normalfont"; \BODY; }%
+ \let\protect=\mnm@protect}
+
+% separate metapost instances
+\newcommand\newmetapostenvironment[2][]{%
+ \newmetapostinstance[#1]\mnm@tmp
+ \expandafter\let\csname #2@instance\endcsname\mnm@tmp
+ \newenvironment{#2}{%
+ \begingroup \catcode`\#=12
+ \let\mpcolor = \minimpcolor
+ \csname:#2:\endcsname}
+ {\csname end:#2:\endcsname \endgroup}%
+ \NewEnviron{:#2:}{%
+ \mnm@setnormalfont
+ \let\protect=\noexpand
+ \runmetapostimage
+ \csname #2@instance\endcsname
+ {defaultfont:="\mnm@normalfont";\BODY;}%
+ \let\protect=\mnm@protect}}
+
+% \mpcolor support
+\def\minimpcolor#1#{\dominimpcolor{#1}}
+\def\dominimpcolor#1#2{%
+ \directlua{ tex.scantoks('mpcolorspectoks', 0, '#1{#2}')
+ tex.runtoks 'mpcolorruntoks' }
+ (\the\mpcolorvaltoks)}
+\newtoks\mpcolorspectoks \newtoks\mpcolorruntoks \newtoks\mpcolorvaltoks
+\mpcolorruntoks{\expandafter\extractcolorspec\the\mpcolorspectoks\mptmpcolor
+ \expandafter\mpcolorvaltoks\expandafter\@gobble\mptmpcolor}
+
+\makeatother
+
diff --git a/macros/luatex/generic/minim-mp/minim-mp.tex b/macros/luatex/generic/minim-mp/minim-mp.tex
index ad84a4f19d..6d95334677 100644
--- a/macros/luatex/generic/minim-mp/minim-mp.tex
+++ b/macros/luatex/generic/minim-mp/minim-mp.tex
@@ -9,9 +9,14 @@
\input minim-pdfresources
% a default catcode table
-\newcatcodetable \minim:mp:catcodes
+\newcatcodetable \minim:mp:catcodes:maketext
{\catcode`\:=12 \catcode`\@=12
- \savecatcodetable\csname minim:mp:catcodes\endcsname}
+ \savecatcodetable\csname minim:mp:catcodes:maketext\endcsname}
+% and an empty catcode table for reading metapost code
+\newcatcodetable \minim:mp:catcodes:mpcode
+{\catcodetable\minim:initcatcodes
+ \catcode`\{=1 \catcode`\}=2 \catcode9=10
+ \savecatcodetable\csname minim:mp:catcodes:mpcode\endcsname}
\directlua { require('minim-mp') }
@@ -30,9 +35,10 @@
% \boxnamedmpimage \instance box-nr {name}
% \directmetapost [ options ] { code }
-\protected\def\directmetapost{\withoptions[]\directmetapost:}
+\protected\def\directmetapost{%
+ \begingroup\catcodetable\minim:mp:catcodes:mpcode
+ \withoptions[]\directmetapost:}
\long\def\directmetapost:[#1]#2{%
- \begingroup
\newmetapostinstance[#1]\:mpinst:
\runmetapost\:mpinst:{#2}%
\loop \ifnum\remainingmpimages\:mpinst:>0\relax
@@ -55,6 +61,8 @@
\newcount\GtoG \GtoG = 7152
\newcount\BtoG \BtoG = 0722
+\newtoks \everymaketext
+
\catcode`\: = \minimmploaded
diff --git a/macros/luatex/generic/minim-mp/minim.mp b/macros/luatex/generic/minim-mp/minim.mp
index 0bbaa2a27c..2bcea78343 100644
--- a/macros/luatex/generic/minim-mp/minim.mp
+++ b/macros/luatex/generic/minim-mp/minim.mp
@@ -1,7 +1,13 @@
-delimiters ();
+if known minim.mp:
+ message "skipping: already read";
+ expandafter endinput fi
+minim.mp := 1.3;
-message "Loading the minim extension macros";
+% This file only contains code that is not specific to the minim metapost
+% processor. See minim-mp.mp for the wireup of minim-mp’s extensions.
+
+message "Loading general minim extension macros";
% redefine some constants to work with the new numerical engines
numeric mm, cm, pt, pc, dd, cc;
@@ -9,112 +15,8 @@ pc = 12 pt; cc = 12 dd; cm = 10 mm;
803 pt = 800; 127 mm = 360; 1157 dd = 1238 pt;
eps := 1/2048; infinity := 64*64-epsilon;
-% interaction with tex
-vardef baseline expr o =
- fill if numeric o : (0,o) else: o fi
- -- cycle withprescript "BASELINE:"; enddef;
-vardef boxresource expr nr = image(
- fill unitsquare withprescript "BOXRESOURCE:" & decimal nr ;
- setbounds currentpicture to unitsquare transformed runscript
- ("return { 'box_size', tex.getboxresourcedimensions(" & decimal nr & ") }");
- ) enddef;
-
-primarydef t infont f = image( runscript(
- "return require('minim-mp').infont("&ditto&t&ditto&", "
- &if numeric f: decimal(f) else: ditto&f&ditto fi&")" ) ) enddef;
-
-% even-odd rule
-def nofill expr c = fill c withprescript "OTYPE:nofill" enddef;
-def eofill expr c = fill c withprescript "OTYPE:eofill" enddef;
-def eofilldraw expr c = filldraw c withprescript "OTYPE:eofilldraw" enddef;
-
-def multidraw (text paths) text opts = draw image(
- for p = paths: ; nofill p opts endfor
- withprescript "OTYPE:outline";) enddef;
-def multifill (text paths) text opts = draw image(
- for p = paths: ; nofill p opts endfor
- withprescript "OTYPE:fill";) enddef;
-def multifilldraw (text paths) text opts = draw image(
- for p = paths: ; nofill p opts endfor
- withprescript "OTYPE:filldraw";) enddef;
-def multieofill (text paths) text opts = draw image(
- for p = paths: ; nofill p opts endfor
- withprescript "OTYPE:eofill";) enddef;
-def multieofilldraw (text paths) text opts = draw image(
- for p = paths: ; nofill p opts endfor
- withprescript "OTYPE:eofilldraw";) enddef;
-
-vardef clipto text t =
- clip currentpicture to bbox currentpicture
- for c = t: -- c -- cycle endfor -- cycle enddef;
-vardef clipout text t =
- clip currentpicture to
- for c = t: c -- cycle -- endfor cycle enddef;
-
-vardef glyph expr c of f = image(for p =
- runscript("return require('minim-mp').get_"
- &if string c: "named_glyph("&ditto&c&ditto
- else: "glyph("& decimal c fi &", "
- &if numeric f: decimal(f) else: ditto&f&ditto fi
- & ")" ) : ; nofill p endfor
- withprescript "OTYPE:fill"; ) enddef;
-
-def contours expr t of f =
- runscript("return require('minim-mp').get_contours("&ditto&t&ditto&", "
- &if numeric f: decimal(f) else: ditto&f&ditto fi& ")" ) enddef;
-
-% interaction with lua
-vardef hexadecimal expr n =
- % TODO: support other number systems
- save d, a, m; a = ASCII("a"); m := abs n;
- if n < 0: "-0x" else: "0x" fi
- for i = 1 upto 7: &
- hide(d := floor m div 256; m := 16(m - 256d);)
- if d < 10: decimal d else: char(a+d-10) fi
- if i = 3: & "." fi
- endfor enddef;
-vardef scaledpoints expr n =
- % use the fact that 800bp = 803pt
- save a, m; m := abs n;
- a1 = m div (1025/1024); % 1025/1024 = 800epsilon * 82
- m := m mod (1025/1024);
- a2 = m div 800epsilon; % max 82
- m := (m mod 800epsilon) div epsilon;
- a3 = floor(m*803/800 + 1/2);
- if n < 0: "-(" else: "(" fi
- & decimal(a1) & "*803*82 + "
- & decimal(a2) & "*803 + "
- & decimal(a3) & ")" enddef;
-
-% reporting
-def debug_pdf = runscript
- ("require('minim-mp').enable_debugging()") enddef;
-def texmessage expr msg = runscript
- ("texio.write_nl("&ditto&msg&ditto&")") enddef;
-
-% patterns
-def withpattern(suffix s) = withprescript
- ("fillpattern: " & decimal(_patterns_.s)) enddef;
-def beginpattern(suffix s) =
- begingroup clearxy; clearit;
- interim defaultcolormodel:=1;
- charcode:=incr _patterns_._last_;
- _patterns_.s:=charcode; drawoptions();
- pickup pencircle scaled 0.4pt;
- save painttype; painttype:=2;
- save _withcolor; let _withcolor = withcolor; save withcolor;
- def withcolor = hide(painttype:=1) _withcolor enddef;
- save matrix; transform matrix; enddef;
-def endpattern (expr xstep, ystep) =
- if unknown matrix : matrix:=identity; fi
- special "definepattern:" for e = charcode, tilingtype, painttype,
- xstep, ystep, xxpart matrix, xypart matrix, yxpart matrix, yypart matrix:
- & " " & decimal(e) endfor;
- shipit; endgroup enddef;
-newinternal tilingtype; tilingtype:=1;
-_patterns_._last_ := 0;
-
-message "Loading additional definitions";
+% constants
+pi := 355/113;
% shorthands
primarydef p xshifted x = p shifted (x,0) enddef;
@@ -133,6 +35,14 @@ unitcircle := fullcircle shifted urcorner fullcircle ;
vardef clockwise primary c =
if turningnumber c > 0: reverse fi c enddef;
+% clipping
+vardef clipto text t =
+ clip currentpicture to bbox currentpicture
+ for c = t: -- c -- cycle endfor -- cycle enddef;
+vardef clipout text t =
+ clip currentpicture to
+ for c = t: c -- cycle -- endfor cycle enddef;
+
% cmyk colours
cmykcolor cyan, magenta, yellow, key;
cyan = (1,0,0,0);
@@ -140,15 +50,21 @@ magenta = (0,1,0,0);
yellow = (0,0,1,0);
key = (0,0,0,1);
-% constants
-pi := 355/113;
+% also in minim-mp.mp
+if unknown rgb_to_gray red:
+ vardef rgb_to_gray expr c =
+ 0.2126*redpart c + 0.7152*greenpart c + 0.0722*bluepart c
+ enddef;
+fi
% shorthands
+def save_pen text t = save t; pen t; enddef;
def save_pair text t = save t; pair t; enddef;
def save_path text t = save t; path t; enddef;
def save_color text t = save t; color t; enddef;
def save_cmykcolor text t = save t; cmykcolor t; enddef;
def save_transform text t = save t; transform t; enddef;
+def save_picture text t = save t; picture t; enddef;
def save_boolean text t = save t; boolean t; enddef;
def save_string text t = save t; string t; enddef;
diff --git a/macros/luatex/generic/minim-pdf/README b/macros/luatex/generic/minim-pdf/README
index cff0857823..eb9a1bd114 100644
--- a/macros/luatex/generic/minim-pdf/README
+++ b/macros/luatex/generic/minim-pdf/README
@@ -1,4 +1,4 @@
-Version: 2023/1.2
+Version: 2023/1.3
SUMMARY
@@ -27,6 +27,14 @@ In order to obtain the typeset manual of this package, simply say:
HISTORY
+2023/1.3 (20/10/2023)
+
+ New features:
+
+ * Add \markcolumnspan and \markrowspan
+ * Add \markcolumnhead and \markrowhead
+
+
2023/1.2 (3/3/2023)
Breaking changes:
@@ -38,6 +46,7 @@ HISTORY
* Support the use of an IDTree in the document structure.
* Add \tagattribute, \settagid and \showstructure.
+
2022/1.1 (3/3/2022)
New features:
diff --git a/macros/luatex/generic/minim-pdf/minim-pdf.doc b/macros/luatex/generic/minim-pdf/minim-pdf.doc
index 3030378bcb..e04deb31ab 100644
--- a/macros/luatex/generic/minim-pdf/minim-pdf.doc
+++ b/macros/luatex/generic/minim-pdf/minim-pdf.doc
@@ -14,8 +14,8 @@ all other features of the package are described here.
\else \startmetadata
author {Esger Renkema}
title {minim-pdf}
- date {2023-03-03}
- version {2023/1.2}
+ date {2023-10-20}
+ version {2023/1.3}
keywords {LuaTeX; Plain TeX; PDF/A; Tagged PDF; accessibility; a11y;
hypertext; bookmarks; document outline; associated files}
stopmetadata
@@ -392,12 +392,38 @@ This package ensures the existence of the ⟦nohyph⟧, ⟦nolang⟧, ⟦uncoded
⟦undetermined⟧ dummy languages, all without hyphenation.
-\section Helper macros % 1
+\section Mathematics % 1
-For marking up an entry in a table of contents, you can use the macro
-⟦*\marktocentry {dest} {lbl} {title} {filler} {pageno}⟧, which should insert all
-tags in the correct way. (The ⟦dest⟧ is a link destination and can be empty;
-the ⟦lbl⟧ is a section number and can also be empty.)
+You can auto-tag equations as formulas by specifing ⟦*\autotagformulas⟧.
+After this command, auto-tagging can be switched off and on with
+⟦*\stopformulatagging⟧ and ⟦*\startformulatagging⟧.
+Auto-tagging formulas is dangerous, because sometimes equations are used for
+lay-out and should not be marked as such.
+It is also somewhat fragile, as it requires equations to end with dollar
+signs (and not with ⟦\Ustopmath⟧ or ⟦\Ustopdisplaymath⟧).
+
+The tex source of an equation can be associated with the ⟦Formula⟧ structure
+element in various ways, which can be configured with ⟦*\includeformulasources
+{options}⟧, where the ⟦options⟧ must be a comma-separated list of
+⟦alttext⟧, ⟦actualtext⟧ or ⟦attachment⟧.
+The ⟦alttext⟧ and ⟦actualtext⟧ option will set the ⟦/Alt⟧ or ⟦/ActualText⟧
+attributes to the unexpanded source code of the equation, surrounded by the
+appropriate number of dollar signs.
+The ⟦attachment⟧ option attaches the source of the formula as an embedded file
+with its ⟦/AFRelation⟧ set to ⟦Source⟧;
+this will only work if ⟦\pdfaconformancelevel⟧ equals three.
+The name of this file can be changed by redefining ⟦*\formulafilename⟧ inside
+the equation.
+The default value is ⟦{actualtext,attachment}⟧.
+
+Note that the contents of the equation will be expanded fully (as in ⟦\xdef⟧)
+before their inclusion as the equation source. This may place restrictions on
+the macros you want to use (those in minim-math should be safe).
+Any occurrence of ⟦\alttext⟧ or ⟦\actualtext⟧ overrides the
+automatically-assigned value and will be stripped from the equation source.
+
+
+\section Tables % 1
For marking up tables, a whole array of helper macros is available. First,
⟦*\marktable⟧ should be given \emph{before} the ⟦\halign⟧.
@@ -416,24 +442,29 @@ If you use a table for typesetting a list, you can use ⟦*\marktableaslist⟧
instead, which marks the first column as list label and the second column as
list item. Of course, this only works with two-column tables.
-Finally, you can auto-tag equations as formulas by specifing
-⟦*\autotagformulas⟧. This is especially dangerous, because sometimes equations
-are used for lay-out and should not be marked as such.
-After the latter command, auto-tagging can be switched off and on with
-⟦*\stopformulatagging⟧ and ⟦*\startformulatagging⟧.
+Cells spanning multiple cells or rows can be marked with
+⟦*\markcolumnspan {width}⟧ and ⟦*\markrowspan {height}⟧; these statements may not
+occur before ⟦\marktablecell⟧. Note that while ⟦\markcolumnspan⟧ properly
+increases the (internal) column number, ⟦\markcolumnrow⟧ does nothing of the
+sort (and indeed, no general logic can be given in the latter case). Always proceed
+with caution when using cells spanning multiple rows, and inspect the resulting
+structure carefully.
+
+Marking a table header (either manually or with ⟦\automarktable⟧) will not
+connect normal cells with their headers; you will have to connect these
+manually by including ⟦*\markcolumnhead⟧ or ⟦*\markrowhead⟧ in the appropriate
+header cells. This must be done \emph{after} ⟦\markcolumnspan⟧ if the latter
+appplies. If properly setup like this, other cells of the table (including
+header cells) will be assigned to matching row or column headers automatically.
+
+
+\section Other helper macros % 1
+
+For marking up an entry in a table of contents, you can use the macro
+⟦*\marktocentry {dest} {lbl} {title} {filler} {pageno}⟧, which should insert all
+tags in the correct way. (The ⟦dest⟧ is a link destination and can be empty;
+the ⟦lbl⟧ is a section number and can also be empty.)
-The source of the equation can be associated with the ⟦Formula⟧ structure
-element in various ways, which can be configured with ⟦*\includeformulasources
-{option}⟧, where the ⟦option⟧ must be one of ⟦none⟧, ⟦actualtext⟧, ⟦attachment⟧
-or ⟦both⟧.
-The ⟦actualtext⟧ option will use the unexpanded source code of the equation,
-surrounded by the appropriate number of dollar signs.
-The ⟦attachment⟧ option will only work if ⟦\pdfaconformancelevel⟧ equals three:
-then the source of the formula will be attached in an embedded file with the
-⟦/AFRelation⟧ set to ⟦Source⟧.
-The name of this file can be changed by redefining ⟦*\formulafilename⟧ inside
-the equation.
-The default value is ⟦both⟧.
% 
diff --git a/macros/luatex/generic/minim-pdf/minim-pdf.lua b/macros/luatex/generic/minim-pdf/minim-pdf.lua
index 72070366d3..74d56eeaac 100644
--- a/macros/luatex/generic/minim-pdf/minim-pdf.lua
+++ b/macros/luatex/generic/minim-pdf/minim-pdf.lua
@@ -7,6 +7,13 @@ alloc.remember('minim-pdf')
-- 1 helper functions
+local GLUE_NODE = node.id 'glue'
+local GLYPH_NODE = node.id 'glyph'
+local RULE_NODE = node.id 'rule'
+local WHATSIT_NODE = node.id 'whatsit'
+local USER_DEFINED = node.subtype 'user_defined'
+local START_LINK = node.subtype 'pdf_start_link'
+
local function insert_formatted(t, ...)
table.insert(t, string.format(...))
end
@@ -23,6 +30,14 @@ local pdf_name = alloc.pdf_name
local pdf_string = alloc.pdf_string
local options_scanner = alloc.options_scanner
+-- is this table empty?
+local function is_empty(t)
+ for _, _ in pairs(t or {}) do
+ return false
+ end
+ return true
+end
+
-- has this table just one element?
local function singleton(t)
local one = false
@@ -55,9 +70,9 @@ local function full_traverse(head)
end, { { } }, { next = head }
end
-local function pdf_err(n, msg)
+local function pdf_err(n, msg, ...)
local m = node.new('whatsit', 'pdf_literal')
- m.mode, m.token = 2, '%% Warning: '..msg
+ m.mode, m.token = 2, '%% Warning: '..string.format(msg, ...)
node.insert_after(n, n, m)
end
@@ -367,7 +382,7 @@ local function write_structure()
end
if #se.class > 1 then table.insert(res, ']') end
end
- if se.attributes then
+ if not is_empty(se.attributes) then
table.insert(res, '/A')
make_attributes(res, se.attributes)
end
@@ -574,6 +589,13 @@ alloc.luadef('tagging:attribute', function()
token.scan_string(), token.scan_string(), token.scan_string())
end)
+alloc.luadef('tagging:ignore:attribute', function()
+ -- if \tagging:enabled is false
+ token.scan_string()
+ token.scan_string()
+ token.scan_string()
+end)
+
alloc.luadef('newattributeclass', function()
local s = options_scanner():subtable('attr')
attribute_classes[token.scan_string()] = s:scan().attr
@@ -669,18 +691,18 @@ function M.mark_content_items(box)
end
-- traversing all nodes
for n, b in full_traverse(box) do
- local marker = n.id == 8 and n.subtype == 8
+ local marker = n.id == WHATSIT_NODE and n.subtype == USER_DEFINED
and n.user_id == marker_whatsit and n.value
local stat = n[current_status]
-- first we start with marking artifacts
if marker and marker.what == 'art_start' then
- insert_tags(b);
+ insert_tags(b)
start_content(n, b)
local a = string.format('/Artifact << /Type/%s >> BDC', marker.it)
open_artifacts[stat], open, art = a, pdf_literal(a), stat
elseif marker and marker.what == 'art_stop' then
end_node = n
- insert_tags(b);
+ insert_tags(b)
if art then open_artifacts[art], art = nil, nil end
elseif stat and stat >10 and stat ~=art then -- broken artifact
insert_tags(b);
@@ -688,14 +710,14 @@ function M.mark_content_items(box)
local a = open_artifacts[stat]
if not a then
alloc.err('Encountered tail of unknown artifact (see pdf)')
- pdf_err(n, 'unknown artifact (this should not be possible)')
+ pdf_err(n, 'unknown artifact nr. %d (this should not be possible)', stat)
a = '/Artifact << /Type/Unknown >> BDC'
end
open, art = pdf_literal(a), stat
elseif stat then -- inside artifact or tagging disabled
end_node = n
-- then attach links to Link elements
- elseif n.id == 8 and n.subtype == 19 then -- pdf_start_link
+ elseif n.id == WHATSIT_NODE and n.subtype == START_LINK then
local _se, _order = n[current_struct], n[current_order]
local link = structure[_se]
if link.struct == 'Link' then
@@ -714,10 +736,10 @@ function M.mark_content_items(box)
elseif marker and marker.what == 'mci_stop' then
end_node = end_node and n; insert_tags(b)
-- finally, see if we need to intervene between content nodes
- elseif n.id == 2 or n.id == 29 -- rule, glyph or content marker
+ elseif n.id == RULE_NODE or n.id == GLYPH_NODE
or marker and marker.what == 'content' then
local _se, _order = n[current_struct], n[current_order]
- if n.id == 2 and (n.width == 0 or n.height == 0 and n.depth == 0) then
+ if n.id == RULE_NODE and (n.width == 0 or n.height == 0 and n.depth == 0) then
-- ignore invisible rules
elseif not _se or not _order then
-- unmarkable content should not be possible (but is)
@@ -731,7 +753,7 @@ function M.mark_content_items(box)
else -- nothing changed: continue current mci
end_node = n
end
- elseif n.id == 12 and n.subtype > 2 and n.subtype < 8 then
+ elseif n.id == GLUE_NODE and n.subtype > 2 and n.subtype < 8 then
-- parskip or displayskip always closes mci
insert_tags(b)
end
@@ -963,8 +985,8 @@ function M.mark_spaces(head, gc)
for g in node.traverse_id(12, head) do -- glue
if g.prev and g.subtype == 13 or g.subtype == 14 then -- (x)spaceskip
local p = g.prev
- p[space_attr] = p.id == 29 and p.font
- or g.next and g.next.id == 29 and g.next.font
+ p[space_attr] = p.id == GLYPH_NODE and p.font
+ or g.next and g.next.id == GLYPH_NODE and g.next.font
or font.current()
end
end
diff --git a/macros/luatex/generic/minim-pdf/minim-pdf.pdf b/macros/luatex/generic/minim-pdf/minim-pdf.pdf
index 39500a7985..a6e22ee194 100644
--- a/macros/luatex/generic/minim-pdf/minim-pdf.pdf
+++ b/macros/luatex/generic/minim-pdf/minim-pdf.pdf
Binary files differ
diff --git a/macros/luatex/generic/minim-pdf/minim-pdf.tex b/macros/luatex/generic/minim-pdf/minim-pdf.tex
index a8fe5aeea6..d8b06255ba 100644
--- a/macros/luatex/generic/minim-pdf/minim-pdf.tex
+++ b/macros/luatex/generic/minim-pdf/minim-pdf.tex
@@ -112,19 +112,24 @@
\protected\def\addstructuretype{\withoptions[]\addstructure:type}
\def\addstructure:type[#1]{\struct:addalias{return {#1}}}
-% \setalttext {...} and \setactualtext {...}
+% \setalttext {...}
\protected\def\setalttext{\iftagging:enabled
- \expandafter\tagging:alt\fi}
+ \expandafter\tagging:alt\else
+ \expandafter\tagging:ignore\fi}
+% \setactualtext {...}
\protected\def\setactualtext{\iftagging:enabled
- \expandafter\tagging:actual\fi}
+ \expandafter\tagging:actual\else
+ \expandafter\tagging:ignore\fi}
% \tagattribute Owner Key /verbatim_value
\protected\def\tagattribute{\iftagging:enabled
- \expandafter\tagging:attribute\fi}
+ \expandafter\tagging:attribute\else
+ \expandafter\tagging:ignore:attribute\fi}
% \settagid {name}
\protected\def\settagid{\iftagging:enabled
- \expandafter\tagging:tagid\fi}
+ \expandafter\tagging:tagid\else
+ \expandafter\tagging:ignore\fi}
% \showstructure
\newtoks\tagging:showtoks
@@ -146,7 +151,8 @@
% 1 tagging tables
-% \marktable \halign {\marktablerow\marktablecell#&\marktablecell#\cr
+% \marktable \halign
+% {\marktablerow\marktablecell#&\marktablecell#\cr
% \marktableheader
% Header & cells \cr
% \marktablebody
@@ -155,7 +161,7 @@
% ... & ... \cr ... }
%
% lots of variables and constants
-\newcount\tagging:tablec
+\newcount\tagging:tablec % global; only used for cel ids
\newcount\tagging:tablerow
\newcount\tagging:tablecol
\newcount\tagging:tablecolspan \tagging:tablecolspan 1
@@ -166,11 +172,11 @@
% initialising variables
\def\marktable{\startelement{Table}%
\global\advance\tagging:tablec1
- \global\tagging:tablerow0
- \global\tagging:tablecol0
- \global\def\tagging:tablecolheaders{}%
- \global\def\tagging:tablerowheaders{}%
- \global\let\tagging:cell\tagging:TD}
+ \tagging:tablerow0
+ \tagging:tablecol0
+ \def\tagging:tablecolheaders{}%
+ \def\tagging:tablerowheaders{}%
+ \let\tagging:cell\tagging:TD}
% the header
\def\marktableheader{%
\noalign{\global\let\tagging:cell\tagging:TH
@@ -201,21 +207,78 @@
\continueelementfrom\tagging:row
\startelement{\tagging:cell}%
\tagging:assignheaders}
-% table headers
+
+% spans
+\def\markcolumnspan#1{%
+ \tagattribute Table ColSpan #1\relax
+ \tagging:tablecolspan#1\relax
+ % adjust the column number
+ \global\advance\tagging:tablecol\numexpr#1-1\relax}
+\def\markrowspan#1{%
+ % note that the row number is not adjusted: \marktablerow must be present
+ % somewhere on every row to increment it.
+ \tagattribute Table RowSpan #1\relax
+ \tagging:tablerowspan#1\relax}
+
+% headers
+\def\markcolumnhead{%
+ \tagging:setcelid
+ \tagattribute Table Scope /Column\relax
+ \tagging:i=1 \loop
+ \xdef\tagging:tablecolheaders{\tagging:tablecolheaders
+ {\the\numexpr\tagging:tablecol-\tagging:tablecolspan+\tagging:i}{\tagging:thecelid}}%
+ \advance\tagging:i1 \unless\ifnum\tagging:i>\tagging:tablecolspan\repeat}
+\def\markrowhead{%
+ \tagging:setcelid
+ \tagattribute Table Scope /Row\relax
+ \tagging:i=0 \loop
+ \xdef\tagging:tablerowheaders{\tagging:tablerowheaders
+ {\the\numexpr\tagging:tablerow+\tagging:i}{\tagging:thecelid}}%
+ \advance\tagging:i1 \ifnum\tagging:i<\tagging:tablerowspan\repeat}
+\def\tagging:setcelid{%
+ % assign an identifier to the cell structure element
+ \edef\tagging:thecelid{t\the\tagging:tablec r\the\tagging:tablerow c\the\tagging:tablecol}%
+ \expandafter\settagid\expandafter{\tagging:thecelid}}
+
+% assign cells to their headers
\def\tagging:assignheaders{%
- % TODO
- }
+ \def\tagging:tmp{[}%
+ \expandafter\tagging:assignheaders:do\expandafter\tagging:tablecol
+ \tagging:tablecolheaders{}{}%
+ \expandafter\tagging:assignheaders:do\expandafter\tagging:tablerow
+ \tagging:tablerowheaders{}{}%
+ \ifx\tagging:tmp\tagging:noheaders\else
+ \tagattribute Table Headers {\tagging:tmp\space]}\fi}
+\def\tagging:assignheaders:do#1#2#3{%
+ \ifx\tagging:noheaders#3\tagging:noheaders
+ \expandafter\tagging:assignheaders:done
+ \else
+ \ifnum#1=#2\relax
+ % no cell is its own header
+ \edef\tagging:cmp:a{\tagging:thecelid}%
+ \edef\tagging:cmp:b{#3}%
+ \unless\ifx\tagging:cmp:a\tagging:cmp:b
+ \edef\tagging:tmp{\tagging:tmp\space\pdfstring{#3}}%
+ \fi
+ \fi
+ \fi\tagging:assignheaders:do{#1}}
+\def\tagging:noheaders{[}%]
+\def\tagging:assignheaders:done#1#2{}
+\def\tagging:thecelid{}
% \automarktable \halign ... { ... }
\def\automarktable#1#{\marktable
\def\tagging:table{\tagging:mktrow{#1}}%
\afterassignment\tagging:table
- \let\tagging:tmp= }
+ \let\tagging:tmp= }% eats a bgroup
\def\tagging:mktrow#1#2&#3\cr{\iftrue
- \tagging:mktcell{#1\bgroup\marktablerow\marktablecell#2}\fi
+ \tagging:mktcell{#1\bgroup % \halign ...
+ \marktablerow\marktablecell#2}\fi
#3&\tagging:mktrow&\cr}
\def\tagging:mktcell#1#2\fi#3&{\fi
+ % the next ifx is true on the last (artificial) template cell
\ifx\tagging:mktrow#3\tagging:mkthdr{#1}\fi
+ % the next ifx detects a double && (repeated template)
\ifx\tagging:mktrow#3\tagging:mktrow
\tagging:mktcell{#1&}\else
\tagging:mktcell{#1&\marktablecell#3}\fi}
@@ -228,7 +291,7 @@
\def\marktableaslist#1#{\startelement{L}%
\def\tagging:table{\tagging:mktlist{#1}}%
\afterassignment\tagging:table
- \let\tagging:tmp= }
+ \let\tagging:tmp= }% eats a bgroup
\def\tagging:mktlist#1#2&{#1\bgroup
\startelement{LI}\savecurrentelementto\tagging:li
\startelement{Lbl}#2&\continueelementfrom\tagging:li
@@ -254,6 +317,7 @@
\newif \iftagging:indisplay
\newif \iftagging:toplevelmath \tagging:toplevelmathtrue
\newif \iftagging:formulaenabled
+\newif \iftagging:tagthisformula \tagging:tagthisformulatrue
\let\startformulatagging = \tagging:formulaenabledtrue
\let\stopformulatagging = \tagging:formulaenabledfalse
\newcount \tagging:formulanr
@@ -269,36 +333,51 @@
\def\tagging:startformula\fi\fi\fi#1${\tagging:makeformula{#1}{$}}
\def\tagging:startdisplay\fi\fi\fi#1$${\tagging:makeformula{#1}{$$}}
\def\tagging:makeformula#1#2{\fi\fi\fi
- \global\advance\tagging:formulanr1\relax
- \startelement attr Layout Placement \iftagging:indisplay/Block\else/Inline
- % some html converters ignore Placement attributes
- attr CSS-2.00 display (inline)\fi{Formula}%
- \formulasource:set{#1}%
+ \tagging:tagformula{#1}%
\scantextokens{#1}#2}
+\def\tagging:tagformula#1{%
+ \iftagging:formulaenabled
+ \global\advance\tagging:formulanr1\relax
+ \startelement attr Layout Placement \iftagging:indisplay/Block\else/Inline
+ % some html converters ignore Placement attributes
+ attr CSS-2.00 display (inline)\fi{Formula}%
+ \formulasource:set{#1}\fi}
\def\tagging:formulasource#1{%
$\iftagging:indisplay$\fi
\unexpanded{#1}%
$\iftagging:indisplay$\fi}
-% \includeformulasources {none|actualtext|attachment|both}
-\def\includeformulasources#1{%
- \expandafter\let
- \expandafter\formulasource:set
- \csname formulasource:#1\endcsname}
-\def\formulasource:none#1{}
+% \includeformulasources {alttext,actualtext,attachment}
+\def\formulasource:set#1{%
+ \begingroup
+ % strip \setalttext, \setactualtext
+ \def\setalttext#1{}\def\setactualtext#1{}%
+ \xdef\formulasource:thesource{#1}%
+ \endgroup
+ \expandafter\splitcommalist
+ \expandafter\formulasource:do
+ \expandafter{\the\includeformulasources}}
+\def\formulasource:do#1{%
+ \expandafter\expandafter \csname formulasource:#1\endcsname
+ \expandafter{\formulasource:thesource}}
\def\formulasource:actualtext#1{%
\setactualtext{\tagging:formulasource{#1}}}
+\def\formulasource:alttext#1{%
+ \setalttext{\tagging:formulasource{#1}}}
\def\formulasource:attachment#1{%
\ifnum3=\pdfaconformancelevel
\embedfile mimetype text/x-tex
relation Source desc {Equation source}
name {\formulafilename.tex}
string {\tagging:formulasource{#1}}\fi}
+\newtoks\includeformulasources
+\includeformulasources{actualtext,attachment}
+
+% for compatibility with earlier versions (DO NOT USE)
+\def\formulasource:none#1{}
\def\formulasource:both#1{%
\formulasource:actualtext{#1}%
\formulasource:attachment{#1}}
-\includeformulasources{both}
-
% 1 hyperlinks
diff --git a/macros/luatex/generic/minim/README b/macros/luatex/generic/minim/README
index 2114fa4fb4..36fe201f7c 100644
--- a/macros/luatex/generic/minim/README
+++ b/macros/luatex/generic/minim/README
@@ -1,4 +1,4 @@
-Version: 2023/1.2
+Version: 2023/1.3
SUMMARY
@@ -31,6 +31,10 @@ the packages mentioned above.
HISTORY
+2023/1.3 (20/10/2023)
+
+ No changes.
+
2023/1.2 (3/3/2023)
* Added \Ucharcat, as in XeTeX.
diff --git a/macros/luatex/generic/minim/minim-alloc.lua b/macros/luatex/generic/minim/minim-alloc.lua
index 3775aafa43..ce53e5d440 100644
--- a/macros/luatex/generic/minim/minim-alloc.lua
+++ b/macros/luatex/generic/minim/minim-alloc.lua
@@ -262,13 +262,14 @@ local function make_alloc_old(fname, globcount)
end
-- existing allocation counters
-make_alloc_old ('count', 260 )
-make_alloc_old ('dimen', 261 )
-make_alloc_old ('skip', 262 )
-make_alloc_old ('muskip', 263 )
-make_alloc_old ('box', 264 )
-make_alloc_old ('toks', 265 )
-make_alloc_old ('marks', 266 )
+local globoffset = tex.count['alloc:globoffset']
+make_alloc_old ('count', globoffset + 0 )
+make_alloc_old ('dimen', globoffset + 1 )
+make_alloc_old ('skip', globoffset + 2 )
+make_alloc_old ('muskip', globoffset + 3 )
+make_alloc_old ('box', globoffset + 4 )
+make_alloc_old ('toks', globoffset + 5 )
+make_alloc_old ('marks', globoffset + 6 )
function M.luadef (csname, fn, ...)
local nr = M.new_function(csname)
diff --git a/macros/luatex/generic/minim/minim-alloc.tex b/macros/luatex/generic/minim/minim-alloc.tex
index 4110301413..1b3ca06795 100644
--- a/macros/luatex/generic/minim/minim-alloc.tex
+++ b/macros/luatex/generic/minim/minim-alloc.tex
@@ -85,7 +85,6 @@
\def\ProvidesFile#1{\begincsname minim:intercept:#1\endcsname
\minim:providesfile{#1}}
-
% repair ltluatex, which, without apparent reason, resets all allocation
% counters, even if they already exist
\expandafter\def\csname minim:intercept:ltluatex.tex\endcsname
@@ -109,8 +108,15 @@
% 1 allocation
-% use global allocation (see etex.src)
-\ifdefined \globcount
+% used on the lua side by make_alloc_old
+\newcount \alloc:globoffset
+\alloc:globoffset = 260
+
+\ifdefined \documentclass
+ % latex has no \globcount etc.
+ \alloc:globoffset = 10
+\else \ifdefined \globcount
+ % use global allocation (see etex.src)
\let\newcount = \globcount
\let\newdimen = \globdimen
\let\newskip = \globskip
@@ -118,7 +124,7 @@
\let\newbox = \globbox
\let\newtoks = \globtoks
\let\newmarks = \globmarks
-\fi
+\fi \fi
% new allocation macros
@@ -153,6 +159,8 @@
% those ourselves:
\ifnum\csname e@alloc@ccodetable@count\endcsname = 0
\csname e@alloc@ccodetable@count\endcsname = 4 \fi
+% an empty table
+\newcatcodetable \minim:initcatcodes
% set initial chunk name value
\def\chardef:chunk#1#2{\chardef#1#2\directlua{lua.name[\the#2]='\csstring#1'}}
diff --git a/macros/luatex/generic/minim/minim-doc.sty b/macros/luatex/generic/minim/minim-doc.sty
index 5da6494c5e..97b173beac 100644
--- a/macros/luatex/generic/minim/minim-doc.sty
+++ b/macros/luatex/generic/minim/minim-doc.sty
@@ -59,7 +59,7 @@
\scriptfont 0 = \tenmaths
\scriptscriptfont 0 = \tenmathss
-\expandglyphsinfont \tenrm 40 25 5
+\expandglyphsinfont \tenrm 25 25 5
\protrudechars=2 \adjustspacing=2
\tenrm
diff --git a/macros/luatex/generic/minim/minim.doc b/macros/luatex/generic/minim/minim.doc
index 7061917398..bf56d89747 100644
--- a/macros/luatex/generic/minim/minim.doc
+++ b/macros/luatex/generic/minim/minim.doc
@@ -4,8 +4,8 @@
\startmetadata
author {Esger Renkema}
title {minim}
- date {2023-03-03}
- version {2023/1.2}
+ date {2023-10-20}
+ version {2023/1.3}
keywords {LuaTeX; Plain TeX; MetaPost; PDF/A; Tagged PDF; accessibility; a11y;
Unicode mathematics; XMP; metadata; hypertext; bookmarks}
stopmetadata
diff --git a/macros/luatex/generic/minim/minim.ini b/macros/luatex/generic/minim/minim.ini
index 26aed3a2c5..e8017cc8f5 100644
--- a/macros/luatex/generic/minim/minim.ini
+++ b/macros/luatex/generic/minim/minim.ini
@@ -7,7 +7,7 @@
\input minim-plain
\unless\ifdefined\fmtversion
- \def\fmtversion{2021/1.0}\fi
+ \def\fmtversion{2023/1.3}\fi
\suppressoutererror = 1
\input minim-etex
diff --git a/macros/luatex/generic/minim/minim.pdf b/macros/luatex/generic/minim/minim.pdf
index 3685ecf7fd..b6e05b6cc8 100644
--- a/macros/luatex/generic/minim/minim.pdf
+++ b/macros/luatex/generic/minim/minim.pdf
Binary files differ