summaryrefslogtreecommitdiff
path: root/macros/luatex/generic
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2022-03-04 03:01:04 +0000
committerNorbert Preining <norbert@preining.info>2022-03-04 03:01:04 +0000
commit575ab7d0e7534edd510bbc89a2ef4b04582af591 (patch)
tree9fc4cf55b7ca8b168bdfd747be21de4c9ef2b30e /macros/luatex/generic
parent6b9ed20cb01cb52c7aaf2a48202f5d8aa588ff79 (diff)
CTAN sync 202203040301
Diffstat (limited to 'macros/luatex/generic')
-rw-r--r--macros/luatex/generic/minim-math/README17
-rw-r--r--macros/luatex/generic/minim-math/minim-math-table.lua15
-rw-r--r--macros/luatex/generic/minim-math/minim-math.doc16
-rw-r--r--macros/luatex/generic/minim-math/minim-math.lua6
-rw-r--r--macros/luatex/generic/minim-math/minim-math.pdfbin106915 -> 110408 bytes
-rw-r--r--macros/luatex/generic/minim-math/minim-math.tex4
-rw-r--r--macros/luatex/generic/minim-mp/README32
-rw-r--r--macros/luatex/generic/minim-mp/minim-lamp.ini20
-rw-r--r--macros/luatex/generic/minim-mp/minim-mp.doc112
-rw-r--r--macros/luatex/generic/minim-mp/minim-mp.ini1
-rw-r--r--macros/luatex/generic/minim-mp/minim-mp.lua390
-rw-r--r--macros/luatex/generic/minim-mp/minim-mp.pdfbin82287 -> 94557 bytes
-rw-r--r--macros/luatex/generic/minim-mp/minim-mp.tex3
-rw-r--r--macros/luatex/generic/minim-mp/minim.mp81
-rw-r--r--macros/luatex/generic/minim-pdf/README27
-rw-r--r--macros/luatex/generic/minim-pdf/minim-languagecodes.lua8
-rw-r--r--macros/luatex/generic/minim-pdf/minim-pdf.doc86
-rw-r--r--macros/luatex/generic/minim-pdf/minim-pdf.lua294
-rw-r--r--macros/luatex/generic/minim-pdf/minim-pdf.pdfbin108362 -> 110458 bytes
-rw-r--r--macros/luatex/generic/minim-pdf/minim-pdf.tex62
-rw-r--r--macros/luatex/generic/minim-xmp/README17
-rw-r--r--macros/luatex/generic/minim-xmp/minim-xmp.doc68
-rw-r--r--macros/luatex/generic/minim-xmp/minim-xmp.lua7
-rw-r--r--macros/luatex/generic/minim-xmp/minim-xmp.pdfbin61507 -> 72506 bytes
-rw-r--r--macros/luatex/generic/minim-xmp/minim-xmp.tex4
-rw-r--r--macros/luatex/generic/minim/README28
-rw-r--r--macros/luatex/generic/minim/minim-alloc.doc105
-rw-r--r--macros/luatex/generic/minim/minim-alloc.lua183
-rw-r--r--macros/luatex/generic/minim/minim-alloc.tex107
-rw-r--r--macros/luatex/generic/minim/minim-callbacks.lua175
-rw-r--r--macros/luatex/generic/minim/minim-doc.sty64
-rw-r--r--macros/luatex/generic/minim/minim-hooks.lua18
-rw-r--r--macros/luatex/generic/minim/minim-hooks.tex8
-rw-r--r--macros/luatex/generic/minim/minim-lmodern.tex18
-rw-r--r--macros/luatex/generic/minim/minim-pdfresources.lua154
-rw-r--r--macros/luatex/generic/minim/minim-pdfresources.tex51
-rw-r--r--macros/luatex/generic/minim/minim-plain.tex14
-rw-r--r--macros/luatex/generic/minim/minim.doc11
-rw-r--r--macros/luatex/generic/minim/minim.pdfbin285949 -> 317085 bytes
-rw-r--r--macros/luatex/generic/penlight/README.md4
-rw-r--r--macros/luatex/generic/penlight/penlight.pdfbin38237 -> 38311 bytes
-rw-r--r--macros/luatex/generic/penlight/penlight.sty22
-rw-r--r--macros/luatex/generic/penlight/penlight.tex4
-rw-r--r--macros/luatex/generic/penlight/penlightextras.lua75
44 files changed, 1561 insertions, 750 deletions
diff --git a/macros/luatex/generic/minim-math/README b/macros/luatex/generic/minim-math/README
index b463a8bad3..2463570ef8 100644
--- a/macros/luatex/generic/minim-math/README
+++ b/macros/luatex/generic/minim-math/README
@@ -1,4 +1,4 @@
-Version: 2021/1.0
+Version: 2022/1.1
SUMMARY
@@ -16,9 +16,22 @@ In order to obtain the typeset manual of this package, simply say:
luatex minim-math.doc
+HISTORY
+
+2022/1.1 (1/1/2022)
+
+ Minor changes:
+
+ * Made \setminus refer to the proper symbol.
+
+2021/1.0 (1/6/2021)
+
+ This was the original release.
+
+
COPYING
-(c) 2021 Esger Renkema
+(c) 2022 Esger Renkema
These files may be distributed under the terms of the European Union Public
Licence (EUPL) version 1.2 or later. A copy can be obtained at:
diff --git a/macros/luatex/generic/minim-math/minim-math-table.lua b/macros/luatex/generic/minim-math/minim-math-table.lua
index 3a12d1da99..5e6a70036c 100644
--- a/macros/luatex/generic/minim-math/minim-math-table.lua
+++ b/macros/luatex/generic/minim-math/minim-math-table.lua
@@ -106,13 +106,22 @@ return
, { code = 0x00308, char = ' ̈', class = 'accent', cs = 'ddot' } -- combining diaeresis
, { code = 0x00309, char = ' ̉', class = 'accent', cs = 'ovhook' } -- combining hook above
, { code = 0x0030A, char = ' ̊', class = 'accent', cs = 'ocirc' } -- combining ring above
+ , { code = 0x0030B, char = ' ̋', class = 'accent', cs = 'dacute' } -- combining double acute accent
, { code = 0x0030C, char = ' ̌', class = 'accent', cs = 'check' } -- combining caron
, { code = 0x00310, char = ' ̐', class = 'accent', cs = 'candra' } -- combining candrabindu
, { code = 0x00312, char = ' ̒', class = 'accent', cs = 'oturnedcomma' } -- combining turned comma above
, { code = 0x00315, char = ' ̕', class = 'accent', cs = 'ocommatopright' } -- combining comma above right
, { code = 0x0031A, char = ' ̚', class = 'accent', cs = 'droang' } -- combining left angle above
+ , { code = 0x00323, char = ' ̣', class = 'botaccent', cs = 'dotbelow' } -- combining dot below
+ , { code = 0x00324, char = ' ̤', class = 'botaccent', cs = 'ddotbelow' } -- combining diaeresis below
+ , { code = 0x00325, char = ' ̥', class = 'botaccent', cs = 'ocircbelow' } -- combining ring below
+ , { code = 0x00326, char = ' ̦', class = 'botaccent', cs = 'commabelow' } -- combining comma below
+ , { code = 0x00327, char = ' ̧', class = 'botaccent', cs = 'cedilla' } -- combining cedilla
+ , { code = 0x00328, char = ' ̨', class = 'botaccent', cs = 'ogonek' } -- combining ogonek
+ , { code = 0x00329, char = ' ̩', class = 'botaccent', cs = 'vlinebelow' } -- combining vertical line below
, { code = 0x00330, char = ' ̰', class = 'botaccent', cs = 'wideutilde' } -- combining tilde below
, { code = 0x00331, char = ' ̱', class = 'botaccent', cs = 'underbar' } -- combining macron below
+ , { code = 0x00336, char = ' ̶', class = 'overlay', cs = 'strike' } -- combining long stroke overlay
, { code = 0x00338, char = ' ̸', class = 'overlay', cs = 'not' } -- combining long solidus overlay
, { code = 0x00391, char = 'Α', class = 'var', cs = 'Alpha', alphabet = 'Greek' } -- greek capital letter alpha
, { code = 0x00392, char = 'Β', class = 'var', cs = 'Beta', alphabet = 'Greek' } -- greek capital letter beta
@@ -236,7 +245,7 @@ return
, { code = 0x02110, char = 'ℐ', class = 'ord', cs = 'mscrI', alphabet = 'script' } -- script capital i
, { code = 0x02111, char = 'ℑ', class = 'ord', cs = 'Im' } -- black-letter capital i
, { code = 0x02112, char = 'ℒ', class = 'ord', cs = 'mscrL', alphabet = 'script' } -- script capital l
- , { code = 0x02113, char = 'ℓ', class = 'ord', cs = 'ell', alphabet = 'script' } -- script small l
+ , { code = 0x02113, char = 'ℓ', class = 'ord', cs = 'ell' } -- script small l
, { code = 0x02115, char = 'ℕ', class = 'ord', cs = 'BbbN', alphabet = 'blackboard' } -- double-struck capital n
, { code = 0x02118, char = '℘', class = 'ord', cs = 'wp', } -- weierstrass elliptic function
, { code = 0x02119, char = 'ℙ', class = 'ord', cs = 'BbbP', alphabet = 'blackboard' } -- double-struck capital p
@@ -402,7 +411,7 @@ return
, { code = 0x02213, char = '∓', class = 'bin', cs = 'mp' } -- minus-or-plus sign
, { code = 0x02214, char = '∔', class = 'bin', cs = 'dotplus' } -- dot plus
, { code = 0x02215, char = '∕', class = 'bin', cs = 'divslash' } -- division slash
- , { code = 0x02216, char = '∖', class = 'bin', cs = 'smallsetminus' } -- set minus
+ , { code = 0x02216, char = '∖', class = 'bin', cs = 'setminus' } -- set minus
, { code = 0x02217, char = '∗', class = 'bin', cs = 'ast' } -- asterisk operator
, { code = 0x02218, char = '∘', class = 'bin', cs = 'vysmwhtcircle' } -- ring operator
, { code = 0x02219, char = '∙', class = 'bin', cs = 'vysmblkcircle' } -- bullet operator
@@ -1171,7 +1180,7 @@ return
, { code = 0x029F2, char = '⧲', class = 'ord', cs = 'errbarcircle' } -- error-barred white circle
, { code = 0x029F3, char = '⧳', class = 'ord', cs = 'errbarblackcircle' } -- error-barred black circle
, { code = 0x029F4, char = '⧴', class = 'rel', cs = 'ruledelayed' } -- rule-delayed
- , { code = 0x029F5, char = '⧵', class = 'bin', cs = 'setminus' } -- reverse solidus operator
+ , { code = 0x029F5, char = '⧵', class = 'bin', cs = 'rsolop' } -- reverse solidus operator
, { code = 0x029F6, char = '⧶', class = 'bin', cs = 'dsol' } -- solidus with overbar
, { code = 0x029F7, char = '⧷', class = 'bin', cs = 'rsolbar' } -- reverse solidus with horizontal stroke
, { code = 0x029F8, char = '⧸', class = 'op', cs = 'xsol' } -- big solidus
diff --git a/macros/luatex/generic/minim-math/minim-math.doc b/macros/luatex/generic/minim-math/minim-math.doc
index a176c9b1d0..79ff1dee71 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 {2021-06-01}
- version {2021/1.0}
+ date {2022-03-03}
+ version {2022/1.1}
keywords {LuaTeX; Plain TeX; Unicode mathematics}
stopmetadata
\maketitle \fi
@@ -49,7 +49,7 @@ Shorthand\quad\strut & Value of ⟦style⟧\quad\strut & Result \cr
Styles without shorthand are ⟦sans⟧/⟦sf⟧, ⟦sfit⟧, ⟦sfbf⟧, ⟦sfbfit⟧, ⟦tt⟧/⟦mono⟧
and finally the special value ⟦*clear⟧ for using the default style.
You can use the shorthands directly in sub- and superscripts: ⟦v^\scr F⟧ will
-result in $v^\scr F$.
+result in $v^\scr F$\kern-4pt.
While math families are not used anymore for switching between styles (see
below), you still can use ⟦\fam⟧ with the values 0, 1, 2, 4, 5, 6 or~7 for
@@ -135,11 +135,11 @@ this has been made so with ⟦*\mathdef ħ {\hbar}⟧ (you could also have said
The minimum you need do to set up a mathematical font is this:
⟦*\font\tenmath
- {Latin Modern Math:mode=base;script=math;ssty=0} at 10pt
+ {Latin Modern Math:mode=base;script=math;ssty=0;} at 10pt
\font\tenmaths
- {Latin Modern Math:mode=base;script=math;ssty=1} at 7pt
+ {Latin Modern Math:mode=base;script=math;ssty=1;} at 7pt
\font\tenmathss
- {Latin Modern Math:mode=base;script=math;ssty=2} at 5pt
+ {Latin Modern Math:mode=base;script=math;ssty=2;} at 5pt
\textfont 0 = \tenmath
\scriptfont 0 = \tenmaths
\scriptscriptfont 0 = \tenmathss⟧
@@ -159,6 +159,10 @@ family
⟦*\accentfam⟧, ⟦*\radicalfam⟧ and ⟦*\extensiblefam⟧ that control the family of
all accents, radicals and extensibles.
+Do note that various spacing constants are set according to the \emph{last}
+math family that is assigned to. Therefore, you should assign your main math
+font to a family after all others.
+
\section Shorthands and additions
diff --git a/macros/luatex/generic/minim-math/minim-math.lua b/macros/luatex/generic/minim-math/minim-math.lua
index 194e938819..6373809e43 100644
--- a/macros/luatex/generic/minim-math/minim-math.lua
+++ b/macros/luatex/generic/minim-math/minim-math.lua
@@ -2,7 +2,7 @@
local M = {}
local alloc = require ('minim-alloc')
-local callbacks = require ('minim-callbacks')
+local cb = require ('minim-callbacks')
alloc.remember('minim-math')
@@ -312,7 +312,7 @@ local transformcodes = alloc.saved_table ('math:transformcodes')
-- set a character as transformable
local function set_transform (num, char)
- c = alloc.new_count ('math transform '..char)
+ local c = alloc.new_count ('math transform '..char)
transformcodes[num] = c
tex.setcount ('global', c, num)
end
@@ -403,7 +403,7 @@ local function inspect_noads (h,d,n)
return true
end
-callbacks.register ('mlist_to_mlist', inspect_noads)
+cb.register ('mlist_to_mlist', inspect_noads)
-- 1 Reading the math character table
diff --git a/macros/luatex/generic/minim-math/minim-math.pdf b/macros/luatex/generic/minim-math/minim-math.pdf
index b5269815dc..33e9aec9f6 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 e05ce17f7c..06fb632a5c 100644
--- a/macros/luatex/generic/minim-math/minim-math.tex
+++ b/macros/luatex/generic/minim-math/minim-math.tex
@@ -156,6 +156,10 @@
\mathlet · \cdot
\Umathchardef\cdotp "6"0"B7
+% move a macro over
+\protected\def\*{\discretionary{\thinspace
+ \the\textfont\Umathcharfam`\×\Uchar`\×}{}{}}
+
% some aliases
\let\neq = \ne
\let\le = \leq
diff --git a/macros/luatex/generic/minim-mp/README b/macros/luatex/generic/minim-mp/README
index dee13d1bfe..2e04ee1050 100644
--- a/macros/luatex/generic/minim-mp/README
+++ b/macros/luatex/generic/minim-mp/README
@@ -1,4 +1,4 @@
-Version: 2021/1.0
+Version: 2022/1.1
SUMMARY
@@ -17,9 +17,37 @@ In order to obtain the typeset manual of this package, simply say:
luatex minim-mp.doc
+HISTORY
+
+2022/1.1 (3/3/2022)
+
+ New features:
+
+ * Made instance initialisation more flexible; this replaces M.init_files
+ with M.init_code (see manual).
+ * Add support for the glyph of operator.
+ * Add support for the even-odd rule of filling and noncontinuous paths.
+ * Add experimental latex support (the minim-lamp format).
+ * Make minim-mp compatible with tikz/pgf.
+ * Expand the plain macro set slightly.
+
+ Minor changes:
+
+ * Improved display of metapost logs.
+ * Redefine units to fit other numerical engines better.
+ * Fixed a bug in pattern generation that prevented simplifying patterns
+ into raw pdf code.
+ * No longer write out superfluous line widths.
+
+
+2021/1.0 (1/6/2021)
+
+ This was the original release.
+
+
COPYING
-(c) 2021 Esger Renkema
+(c) 2022 Esger Renkema, Michal Vlasák
These files may be distributed under the terms of the European Union Public
Licence (EUPL) version 1.2 or later. A copy can be obtained at:
diff --git a/macros/luatex/generic/minim-mp/minim-lamp.ini b/macros/luatex/generic/minim-mp/minim-lamp.ini
new file mode 100644
index 0000000000..7900b3936a
--- /dev/null
+++ b/macros/luatex/generic/minim-mp/minim-lamp.ini
@@ -0,0 +1,20 @@
+
+\let\DUMP=\dump
+\let\dump=\endinput
+
+\input lualatex.ini
+
+\toksapp\everyjob{%
+ \documentclass{article}
+ \input 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";}%
+ \directlua {require('minim-mp').shipout(\the\MP)}%
+ \closemetapostinstance\MP
+ \csname @fileswfalse\endcsname\end{document}}
+
+\let\dump=\DUMP
+\dump
+
diff --git a/macros/luatex/generic/minim-mp/minim-mp.doc b/macros/luatex/generic/minim-mp/minim-mp.doc
index 788d26b6a0..b7fb47f416 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 {2021-06-01}
- version {2021/1.0}
+ date {2022-03-03}
+ version {2022/1.1}
keywords {LuaTeX; Plain TeX; MetaPost; mplib}
stopmetadata
\maketitle \fi
@@ -35,7 +35,11 @@ error occurs, the logging will also be shown on the terminal.
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.
+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.
\section Metapost instances % 1
@@ -95,10 +99,15 @@ Arbitrary tex statements may be included in ⟦*verbatimtex ... etex⟧, which m
occur anywhere. These ⟦btex⟧ and ⟦verbatimtex⟧ statements are executed in the
order they are given.
+Previously-defined box resources can be included with ⟦*boxresource(nr)⟧. The
+result will be an image object with the proper dimensions. This image can be
+transformed in any way you like, but you cannot inspect the contents of the
+resource within metapost.
+
You can also use metapost’s ⟦*infont⟧ operator, which restricts the text
to-be-typeset to a single font, but returns an ⟦picture⟧ containing a ⟦picture⟧
for each character. The right-hand argument of ⟦infont⟧ should either be
-a (numerical) font id or the (cs)name of a font.
+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:
@@ -129,6 +138,52 @@ beginfig(1)
endfor
endfig;}\starttagging \stopelement{Figure}
+For the greatest amount of control, however, you will need 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
+with ⟦infont⟧).
+
+A variant of ⟦glyph of⟧ is the ⟦*contours t of f⟧ macro: it first typesets the
+string ⟦t⟧ in the same way as ⟦infont⟧ does (so that kerning and font shaping
+are applied), but returns a (comma-separated) list of contours, such as may be
+used in a ⟦for⟧ loop. Due to rounding errors, the glyphs will not match exactly.
+
+Be aware that the contours returned by these operators may be disjoint:
+a letter ⟦o⟧, for example, will consists of two. This means you cannot recreate
+the characters by just filling each contour: this would turn the ⟦o⟧ into
+a filled-in circle. Instead, you must use ⟦multifill⟧ on the entire output of
+⟦glyph of⟧ or ⟦contours of⟧ (see the next section).
+
+
+\section Partial paths and the even-odd rule % 1
+
+You can fill or draw two or more disjoint paths in one go by using ⟦*nofill⟧ as
+drawing operator for all paths but the last. This may make it easier to cut
+something out of a shape, since you do not have to worry about stitching the
+paths together.
+
+While metapost fills paths according to the winding number, the pdf format also
+supports filling according to the even-odd rule. This has been made possible
+with the ⟦*eofill⟧ and ⟦*eofilldraw⟧ drawing statements, which can of course
+also be used as the final statement after a series of nofills.
+
+The macros ⟦*multi(draw|fill|filldraw|eofill|eofilldraw)⟧ take a list of paths
+between parentheses and can be followed by the usual drawing options.
+For example,
+⟦multidraw (contours "example" of "tenbf") withpen currentpen scaled 1/10;⟧
+will give the word%
+\markelement{Span}{\setactualtext{example }
+\directmetapost{beginfig(1) multidraw (contours "example" of "tenbf")
+withpen currentpen scaled 1/10; baseline 0; endfig;}}
+in a thin outline.
+
+Finally, two handy clipping macros have been added:
+⟦*clipout⟧ and ⟦*clipto⟧, which both receive a list of paths as a ‘text’
+parameter and either clip their ensemble out of the picture, or the picture to
+the ensemble.
+
+
\section Running lua from within metapost % 1
You can call out to lua with ⟦*runscript "lua code"⟧. For this purpose, each
@@ -194,7 +249,7 @@ beginfig(1)
fill fullcircle scaled 3cm withpattern(a) withcolor 3/4red;
draw fullcircle scaled 3cm withpen pencircle scaled 1;
endfig;}\stopelement{Figure}\vss}\hskip10pt\strut
-
+\par\nobreak
⟦% define the pattern
picture letter; letter = maketext("a");
beginpattern(a)
@@ -209,25 +264,16 @@ endfig;⟧
A small pattern library is available in the ⟦*minim-hatching.mp⟧ file; see the
accompanying documentation sheet for an overview of patterns.
-Tiling patterns cannot be used together with tikz/pgf; see below under
-‘Resource management’.
\section Other metapost extensions % 1
-There is currently no support for the ⟦*glyph of⟧ operator.
-
You can set the baseline of an image with ⟦*baseline(p)⟧. There, ⟦p⟧ must
either be a point through which the baseline should pass, or a number (where an
x~coordinate of zero will be added). Transformations will be taken into
account, hence the specification of two coordinates. The last given baseline
will be used.
-Previously-defined box resources can be included with ⟦*boxresource(nr)⟧. The
-result will be an image object with the proper dimensions. This image can be
-transformed in any way you like, but you cannot inspect the contents of the
-resource within metapost.
-
You can write to tex’s log directly with ⟦*texmessage "hello"⟧.
You can write direct pdf statements with ⟦*special "pdf: statements"⟧ and you
@@ -235,6 +281,15 @@ can add comments to the pdf file with ⟦*special "pdfcomment: comments"⟧.
Say ⟦*special "latelua: lua code"⟧ to insert a ⟦late_lua⟧ whatsit.
All three specials can also be given as pre- or postscripts to another object.
In that case, they will be added before or after the object they are attached to.
+Do note that all ⟦special⟧ statements will appear at the beginning of the
+image; use pre- and postscripts to drawing statements if the order matters.
+
+Minim-mp also provides a few elementary macros and constants that are
+conspicuously absent from plain.mp; I hope their addition is uncontroversial.
+The constants are ⟦*pi⟧ (355/113), ⟦*fullsquare⟧, ⟦*unitcircle⟧ and the
+cmyk-colours ⟦*cyan⟧, ⟦*magenta⟧, ⟦*yellow⟧ and ⟦*key⟧. The macros are
+⟦*clockwise⟧, ⟦*xshifted⟧, ⟦*yshifted⟧, ⟦*hflip⟧ and ⟦*vflip⟧, where the flips
+are defined in such a way that ⟦p & hflip p⟧ gives the expected result.
\section Lua interface % 1
@@ -262,29 +317,6 @@ Each metapost instance is a table containing the following entries:
⟦env⟧&The lua environment for ⟦runscript⟧.\cr}
-\section PDF resource management % 1
-
-This package can add ⟦/Pattern⟧ and ⟦/ColorSpace⟧ entries to all page and xform
-resource dictionaries.
-Both refer to a single, global dictionary shared by all pages.
-Support for other keys may be added in the future.
-
-At the moment, this implementation only serves tiling pattern support;
-the mechanism will be enabled automatically at the first use of a tiling
-pattern (merely defining a pattern will not enable it) and is of little use for
-anything else.
-The relevant tables, should you want to expand on it yourself, are
-⟦M.colourspaces⟧ and ⟦M.patterns⟧; see the source file for additional
-instructions.
-
-Since pdf resource management must be done exactly once, this package may clash
-with other graphics packages doing the same.
-In particular, minim’s resource management will cause double (and thus invalid)
-entries in pages’ attribute dictionaries when used together with \red{tikz} or
-\red{pgf}.
-They can be used together, however, if you do not use minim’s tiling patterns.
-
-
\section Debugging % 1
You can enable (global) debugging by saying ⟦*debug_pdf⟧ to metapost or
@@ -321,9 +353,9 @@ processed; this information is still available inside the ⟦*finish_mpfigure⟧
callback that is executed just before the processed image is surrounded by
properly-dimensioned boxes.
-The ⟦*M.init_files⟧ table contains the list of metapost files that new instances
-are initialised with.
-The actual format will be loaded after the files in this table.
+The ⟦*M.init_code⟧ table contains the code used for initialing new instances.
+In it, the string ⟦INIT⟧ will be replaced with the value of the ⟦format⟧ option
+(normally ⟦plain.mp⟧).
\endmanual %
diff --git a/macros/luatex/generic/minim-mp/minim-mp.ini b/macros/luatex/generic/minim-mp/minim-mp.ini
index a257f6a18a..e3312a0cb9 100644
--- a/macros/luatex/generic/minim-mp/minim-mp.ini
+++ b/macros/luatex/generic/minim-mp/minim-mp.ini
@@ -7,6 +7,7 @@
\toksapp\everyjob{%
\message{This is the minim metapost processor.}\relax
+ \directlua {require('minim-mp').on_line = true}%
\newmetapostinstance[jobname="\jobname", mathmode="scaled"]\MP
\runmetapost\MP{input "\jobname";}%
\directlua {require('minim-mp').shipout(\the\MP)}%
diff --git a/macros/luatex/generic/minim-mp/minim-mp.lua b/macros/luatex/generic/minim-mp/minim-mp.lua
index 0f1851eba0..366313e60b 100644
--- a/macros/luatex/generic/minim-mp/minim-mp.lua
+++ b/macros/luatex/generic/minim-mp/minim-mp.lua
@@ -1,88 +1,13 @@
local alloc = require ('minim-alloc')
local cb = require('minim-callbacks')
+local pdfres = require('minim-pdfresources')
alloc.remember ('minim-mp')
local M = {}
-- 1 AUXILIARY FUNCTIONS
--- 2 pdf resource management
-
--- We add /ColorSpace and /Pattern entries to all page and xform resources.
--- Each key refers to a central (global) object mapping names to objects. This
--- central dictionary will be written to the pdf inside the finish_pdffile
--- callback.
---
--- In the future, /Shading and /ExtGState may also be added.
---
--- Central resource dictionaries and all used resources will be written to the
--- pdf inside the finish_pdf callback.
---
--- Since a resource dictionary can only contain one entry of each kind, this
--- package cannot coëxist with other packages doing their own resource
--- management. I am aware of only one other package doing that, however, which
--- is pgf. Luckily, pgf users are unlikely to be interested in metapost. Due to
--- this incompatibility, however, resource management must be enabled
--- explicitly.
-
--- keys are pdf names (starting with a slash)
-local patterns = { }; M.patterns = patterns
-local colourspaces = { }; M.colourspaces = colourspaces
-
-local global_resources, pattern_dict_objnum, colourspace_dict_objnum
-function M.enable_resource_management()
- if global_resources then return end
- -- central dictionary objects
- pattern_dict_objnum = pdf.reserveobj()
- colourspace_dict_objnum = pdf.reserveobj()
- global_resources = string.format('/Pattern %d 0 R /ColorSpace %d 0 R',
- pattern_dict_objnum, colourspace_dict_objnum)
- -- add to page and xform resources
- pdf.setpageresources((pdf.getpageresources() or '')..global_resources)
- pdf.setxformresources((pdf.getxformresources() or '')..global_resources)
-end
-
--- Saved patterns should have the following fields:
--- objnum the reserved object number for the pattern (optional)
--- used true if the pattern is in use (should be set automatically)
--- attr the pattern attributes
--- stream the pattern drawing statements
--- painttype the paint type of the pattern (1 or 2)
--- Saved colour spaces should have the following fields:
--- objnum the reserved object number for the colour space (optional)
--- used true if the pattern is in use (should be set automatically)
--- content the colour space contents (a pdf array)
-
-function M.write_resources()
- if not global_resources then return end
- -- patterns
- local used_patterns = { '<<' }
- for name, pat in pairs(patterns) do
- if pat.used then
- local objnum = pat.objnum or pdf.reserveobj()
- pdf.immediateobj(objnum, 'stream', pat.stream, pat.attr)
- table.insert(used_patterns, string.format('%s %d 0 R', name, objnum))
- end
- end
- table.insert(used_patterns, '>>')
- pdf.immediateobj(pattern_dict_objnum, table.concat(used_patterns, ' '))
- -- colour spaces
- local used_spaces = { '<<' }
- for name, space in pairs(colourspaces) do
- if space.used then
- local objnum = space.objnum or pdf.reserveobj()
- pdf.immediateobj(objnum, space.content)
- table.insert(used_spaces, string.format('%s %d 0 R', name, objnum))
- end
- end
- table.insert(used_spaces, '>>')
- pdf.immediateobj(colourspace_dict_objnum, table.concat(used_spaces, ' '))
-end
-
--- Write out resource objects at the end of the run
-cb.register('finish_pdffile', M.write_resources)
-
-- 2 state metatable
-- q Q store/restore graphics state
@@ -125,6 +50,7 @@ function A.save(append)
local st = append.state[#append.state] or { }
append.state[#append.state+1] =
{
+ linewidth = st.linewidth,
miterlimit = st.miterlimit,
linejoin = st.linejoin,
linecap = st.linecap,
@@ -159,6 +85,7 @@ local debugging = false
function M.enable_debugging()
debugging = true
pdf.setcompresslevel(0)
+ pdf.setobjcompresslevel(0)
end
local function print_prop(append, obj, prop)
@@ -270,7 +197,7 @@ local function curve_fmt(...)
end
function A.literal(append, fmt, ...)
- local lit = node.new(8,16)
+ local lit = node.new(8,16) -- pdf_literal
lit.data = fmt:format(...)
append:node(lit)
end
@@ -336,14 +263,14 @@ end
-- variables ‘stroke’ and ‘fill’ that record the last-used colour settings.
-- preload device colour pattern colour spaces
-colourspaces['/PsG'] = { content = '[ /Pattern /DeviceGray ]' }
-colourspaces['/PsRG'] = { content = '[ /Pattern /DeviceRGB ]' }
-colourspaces['/PsK'] = { content = '[ /Pattern /DeviceCMYK ]' }
+pdfres.add_resource('ColorSpace', 'PsG', { value = '[ /Pattern /DeviceGray ]' })
+pdfres.add_resource('ColorSpace', 'PsRG', { value = '[ /Pattern /DeviceRGB ]' })
+pdfres.add_resource('ColorSpace', 'PsK', { value = '[ /Pattern /DeviceCMYK ]' })
local colour_template = { '%.3f ', '%.3f %.3f ', '%.3f %.3f %.3f ', '%.3f %.3f %.3f %.3f ' }
local colour_stroke_operators = { 'G', nil, 'RG', 'K' }
local colour_fill_operators = { 'g', nil, 'rg', 'k' }
-local colour_pattern_spaces = { '/PsG', nil, '/PsRG', '/PsK' }
+local colour_pattern_spaces = { 'PsG', nil, 'PsRG', 'PsK' }
local function get_colour_params(cr)
return format_numbers(colour_template[#cr], table.unpack(cr))
@@ -353,16 +280,16 @@ local function get_stroke_colour(cr)
return get_colour_params(cr)..colour_stroke_operators[#cr]
end
-local function get_fill_colour(cr, pattern)
+local function get_fill_colour(append, cr)
local params = get_colour_params(cr)
- if pattern then
- local ptype, pname = table.unpack(pattern)
+ if append.pattern then
+ local ptype, pname = table.unpack(append.pattern)
if ptype == 2 then -- coloured pattern
local space = colour_pattern_spaces[#cr]
- colourspaces[space].used = true
- return string.format('%s cs %s%s scn', space, params, pname)
+ append:node(pdfres.use_resource_node('ColorSpace', space))
+ return string.format('/%s cs %s%s scn', space, params, alloc.pdf_name(pname))
elseif ptype == 1 then -- uncoloured pattern
- return string.format('/Pattern cs %s scn', pname)
+ return string.format('/Pattern cs %s scn', alloc.pdf_name(pname))
else -- should be unreachable
alloc.err('Unknown pattern paint type %s', ptype)
end
@@ -386,7 +313,7 @@ function A.colour(append, cr, otype)
end
-- fill colour (possibly a pattern)
if otype ~= 'outline' then
- local fill = get_fill_colour(cr, append.pattern)
+ local fill = get_fill_colour(append, cr)
append.pattern = nil -- patterns only apply to one object
if fill ~= append.fill then
append.fill = fill
@@ -432,7 +359,7 @@ function A.linestate (append, object)
end
elseif append.dashed then
append.dashed = false
- table.insert(res, string.format('[] 0 d'))
+ table.insert(res, '[] 0 d')
end
append:literal(table.concat(res, ' '))
end
@@ -505,16 +432,27 @@ end
-- h close the path
-- s close and draw the path (equivalent to h S)
-- f fill the path (implies h)
+-- f* fill the path, use even/odd rule
-- b close, fill and draw the path (equivalent to h B)
+-- b* close, fill and draw, use even/odd rule
-- n do nothing (used for clipping paths)
-local function get_path_operator (otype, open)
+local function get_path_operator(otype, open)
if otype == 'fill' then
return 'f'
elseif otype == 'outline' then
return (open and 'S') or 's'
elseif otype == 'both' then
return 'b'
+ elseif otype == 'nofill' then
+ return ''
+ elseif otype == 'eofill' then
+ return 'f*'
+ elseif otype == 'eofilldraw' then
+ return 'b*'
+ else
+ alloc.err('Unknown path type ‘%s’', otype)
+ return 'f'
end
end
@@ -557,7 +495,8 @@ local function split_specials(specials)
end
end
-local function parse_object (append, object)
+local function parse_object(append, object)
+ append.object_info = { }
append:printobj(object)
local processor = nil
for sp, instr in split_specials(object.prescript) do
@@ -616,29 +555,28 @@ end
-- 2 fill and outline
-function A.set_pen(append, object, otype, open)
+process.fill_or_outline = function(append, object, otype)
+ append:linestate(object)
+ local t, appendpath
if object.pen and object.pen.type == 'elliptical' then
-- metapost includes nonelliptical pens in the outline
- local t = mplib.pen_info(object)
- append:literal(pdfnum('w', t.width))
- if otype == 'fill' then otype = 'both' end
+ t = mplib.pen_info(object)
+ if t.width ~= append.linewidth then
+ append:literal(pdfnum('w', t.width))
+ append.linewidth = t.width
+ end
+ if otype == 'fill' then otype = 'both' end -- for in append:colour
local transformed = not ( t.sx == 1 and t.rx == 0
and t.ry == 0 and t.sy == 1
and t.tx == 0 and t.ty == 0 )
- return transformed and t, get_path_operator(otype, open), otype
- else
- return false, get_path_operator(otype, open), otype
+ t = transformed and t
end
-end
-
-process.fill_or_outline = function(append, object, otype)
+ append:colour(object.color, otype) -- otype is 'fill' 'outline' or 'both'
+ otype = append.object_info.otype or otype -- 'eofill' etc.
local open = object.path
and object.path[1].left_type
and object.path[#object.path].right_type
- local t, operator, otype = append:set_pen(object, otype, open)
- append:colour(object.color, otype)
- append:linestate(object)
- local appendpath
+ local operator = get_path_operator(otype, open)
if t then
local d = t.sx * t.sy - t.rx - t.ry
local concat = function(px, py)
@@ -671,6 +609,9 @@ end
-- 2 specials
+-- this will be pointed to the right table at the start of each run
+local current_instance = false
+
-- pure specials are already taken care of in parse_object (they only have
-- a ‘prescript’ field).
process.special = function(append, object) end
@@ -692,6 +633,10 @@ prescripts.latelua = function(append, str, object)
end
postscripts.latelua = prescripts.latelua
+prescripts.OTYPE = function(append, str, object)
+ append.object_info.otype = append.object_info.otype or str
+end
+
specials.BASELINE = function(append, str, object)
-- object is a ‘fill’ statement with only a single point in its path (and
-- will thus not have to be transformed).
@@ -700,15 +645,26 @@ end
-- 2 patterns
+-- Saved patterns have the following fields:
+-- attr the pattern attributes
+-- stream the pattern drawing statements
+-- painttype the paint type of the pattern (1 or 2)
+
+local function write_pattern_object(pat)
+ local objnum = pat.objnum or pdf.reserveobj()
+ pdf.obj(objnum, 'stream', pat.stream, pat.attr)
+ pdf.refobj(objnum)
+ return string.format('%d 0 R', objnum)
+end
+
prescripts.fillpattern = function(append, str, object)
- M.enable_resource_management()
- local name = '/MnmP'..tonumber(str)
- local pat = patterns[name]
+ local name = 'MnmP'..tonumber(str)
+ local pat = pdfres.get_resource('Pattern', name)
if not pat then
alloc.err('Unknown pattern %s', name)
else
append.pattern = { pat.painttype, name }
- pat.used = true
+ append:node(pdfres.use_resource_node('Pattern', name))
end
end
@@ -719,9 +675,18 @@ specials.definepattern = function(append, str, object)
matrix = { xx = xx, xy = xy, yx = yx, yy = yy, x = 0, y = 0 } }
end
+local function make_pattern_xform(head, bb)
+ -- regrettably, pdf.refobj does not work with xforms, so we must
+ -- write it to the pdf immediately, whether the pattern will be
+ -- used or not.
+ local xform = tex.saveboxresource(node.hpack(node.copy_list(head)),
+ '/Subtype/Form '..bb, nil, true, 4)
+ return string.format(' /Resources << /XObject << /PTempl %d 0 R >> >>', xform), '/PTempl Do'
+end
+
local function definepattern(head, user, bb)
local bb = bbox_fmt(table.unpack(bb))
- local pat, literals, resources = { }, { }
+ local pat, literals, resources = { write = write_pattern_object }, { }
-- pattern content
for n in node.traverse(head) do
-- try if we can construct the content stream ourselves; otherwise,
@@ -735,31 +700,27 @@ local function definepattern(head, user, bb)
elseif n.subtype == 31 then -- restore
table.insert(literals, 'Q')
else
- goto fail
+ resources, pat.stream = make_pattern_xform(head, bb)
+ goto continue
end
+ else
+ resources, pat.stream = make_pattern_xform(head, bb)
+ goto continue
end
pat.stream = table.concat(literals, '\n')
end
- ::fail:: do
- -- regrettably, pdf.refobj does not work with xforms, so we must
- -- write it to the pdf immediately, whether the pattern will be
- -- used or not.
- local xform = tex.saveboxresource(node.hpack(node.copy_list(head)),
- '/Subtype/Form '..bb, resources, true, 4)
- resources = string.format(' /Resources << /XObject << /PTempl %d 0 R >> >>', xform)
- pat.stream = '/PTempl Do'
- end
+ ::continue::
-- construct the pattern object
local i = user.pattern_info
local m = i.matrix
pat.painttype = tonumber(i.painttype)
pat.attr = table.concat({
- string.format(' /PatternType 1 /PaintType %d /TilingType %s /XStep %f /YStep %f\n',
+ string.format('/PatternType 1 /PaintType %d /TilingType %s /XStep %f /YStep %f\n',
i.painttype, i.tilingtype, i.xstep, i.ystep),
string.format('%s\n/Matrix [ %s %s %s %s %s %s ]', bb, m.xx, m.xy, m.yx, m.yy, m.x, m.y),
resources }, '')
-- remember the pattern
- patterns['/MnmP'..i.nr] = pat
+ pdfres.add_resource('Pattern', 'MnmP'..i.nr, pat)
end
cb.register('finish_mpfigure', function(img)
@@ -787,17 +748,16 @@ local function get_transform(rect)
return sx, rx, ry, sy, tx, ty
end
-local function make_surrounding(nd_id, head)
+local function make_surrounding_box(nd_id, head)
local nd = node.new(nd_id)
- nd.dir = 'TLT'
- nd.head = head
+ nd.dir, nd.head = 'TLT', head
return nd
end
-local function apply_translation(box, tx, ty)
- local horizontal = make_surrounding(0, box)
- local vertical = make_surrounding(1, horizontal)
- local outer = make_surrounding(0, vertical)
+local function wrap_picture(head, tx, ty)
+ local horizontal = make_surrounding_box(0, head)
+ local vertical = make_surrounding_box(1, horizontal)
+ local outer = make_surrounding_box(0, vertical)
vertical.shift = tex.sp('-'..ty..'bp')
horizontal.shift = tex.sp(''..tx..'bp')
return outer
@@ -805,10 +765,10 @@ end
local function apply_transform(rect, box)
local sx, rx, ry, sy, tx, ty = get_transform(rect)
- local transform = node.new(8,29)
+ local transform = node.new(8,29) -- pdf_setmatrix
transform.next, box.prev = box, transform
transform.data = string.format('%f %f %f %f', sx, rx, ry, sy)
- return apply_translation(transform, tx, ty)
+ return wrap_picture(transform, tx, ty)
end
function A.box(append, object, box)
@@ -827,7 +787,7 @@ end
specials.CHAR = function(append, data, object)
local char, font, xo, yo = table.unpack(data:explode(' '))
- local n = node.new(29)
+ local n = node.new(29) -- glyph
n.char, n.font, n.xoffset, n.yoffset =
tonumber(char), tonumber(font), tonumber(xo), tonumber(yo)
append:box(object, node.hpack(n))
@@ -847,7 +807,7 @@ M.instances = instances
-- 2 small instance helper functions
-local default_catcodes = alloc.new_catcodetable('minim:mp:catcodes')
+local default_catcodes = alloc.registernumber('minim:mp:catcodes')
-- parameters: wd, ht+dp, dp
local function make_transform(w, h, d)
@@ -855,21 +815,27 @@ local function make_transform(w, h, d)
w/65536, (h+d)/65536, d/65536)
end
-local function print_log (nr, res)
+local status_names = { [0] = 'good', 'warning', 'error', 'fatal' }
+local function print_status(st)
+ return string.format('status %d (%s)', st, status_names[st])
+end
+
+M.on_line = false
+local function print_log (nr, res, why)
local i = instances[nr]
- -- only write to term if exit status increases
+ -- only write to term if on_line or if exit status increases
local log, alog
- if res.status > i.status then
+ if M.on_line or res.status > i.status then
local nrlines, maxlines = 0, 16
alog = alloc.amsg
log = function(...)
- if nrlines == maxlines then
+ if M.on_line or nrlines < maxlines then
+ nrlines = nrlines + 1
+ alloc.msg(...)
+ else
alloc.log(...)
alloc.term('╧ [... see log file for full report ...]')
log, alog = alloc.log, alloc.alog
- else
- nrlines = nrlines + 1
- alloc.msg(...)
end
end
else
@@ -881,15 +847,11 @@ local function print_log (nr, res)
report[#report] = nil
end
-- write out the log
- if #report > 0 then
- log('┌ exit status %d', res.status)
- for _,line in ipairs(report) do
- log('│ '..line)
- end
- log('└')
- else
- log('[ no logs for this run; exit status %d ]', res.status)
+ log('┌ %smetapost instance %s (%d)', why or '', i.jobname, i.nr)
+ for _,line in ipairs(report) do
+ log('│ '..line)
end
+ log('└ %s', print_status(res.status))
-- generate error or warning if needed
if res.status > i.status then
if res.status == 3 then
@@ -897,7 +859,7 @@ local function print_log (nr, res)
elseif res.status == 2 then
alloc.err('Error in metapost code. Further errors will be ignored')
elseif res.status == 1 then
- alloc.msg('Metapost: code caused warning')
+ alloc.msg('Metapost code caused warning')
end
end
-- save the exit status for later comparison
@@ -915,7 +877,6 @@ end
-- 2 processing results
local function process_results(nr, res)
- print_log(nr, res)
local pictures = {}
if res.fig then
alloc.alog (' (%d figures)', #res.fig)
@@ -951,7 +912,7 @@ local function process_results(nr, res)
end
cb.call_callback('finish_mpfigure', pic)
if not pic.discard then
- pic.head = apply_translation(append.head, -llx, -bas)
+ pic.head = wrap_picture(append.head, -llx, -bas)
end
if debugging then
alloc.msg('┌ image %s, with %s objects, %s nodes',
@@ -987,8 +948,6 @@ local function default_env()
return env
end
-local current_instance = false
-
local function runscript(code)
local f, msg = load(code, current_instance.jobname, 't', current_instance.env)
if f then
@@ -1053,22 +1012,22 @@ local function maketext(text, mode)
end
end
-local infont_box = alloc.new_box('infont box')
-function M.infont(text, fnt)
- local fontid = tonumber(fnt) or font.id(fnt)
+local function getfontid(fnt)
+ return tonumber(fnt) or font.id(fnt)
+end
+
+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(
- '\\setbox%d=\\hbox{\\setfontid%d\\relax', infont_box, fontid))
+ '\\setbox%d=\\hbox{\\setfontid%d\\relax', typeset_box, getfontid(fnt)))
tex.sprint(-2, text); tex.sprint(default_catcodes, '}')
end)
local res, x = { }, 0
- for n in node.traverse(tex.box[infont_box].list) do
+ for n in node.traverse(tex.box[typeset_box].list) do
if n.id == 29 then -- glyph
- table.insert(res, string.format(
- 'draw image ( fill unitsquare shifted (%fpt,0) withprescript "CHAR:%d %d %d %d";'
- ..'setbounds currentpicture to unitsquare transformed %s shifted (%fpt,0););',
- x/65536, n.char, n.font, n.xoffset, n.yoffset,
- make_transform(n.width, n.height, n.depth), x/65536))
+ fn(res, n, x)
x = x + n.width
elseif n.id == 12 then -- glue
x = x + n.width
@@ -1076,7 +1035,88 @@ function M.infont(text, fnt)
x = x + n.kern
end
end
- return table.concat(res, '')
+ return table.concat(res, sep)
+end
+
+function M.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";'
+ ..'setbounds currentpicture to unitsquare transformed %s shifted (%fpt,0););',
+ x/65536, n.char, n.font, n.xoffset, n.yoffset,
+ make_transform(n.width, n.height, n.depth), x/65536))
+ end)
+end
+
+
+-- 2 glyph contours
+
+function M.make_glyph(glyphname, fnt)
+ -- gather information
+ if not luaotfload then
+ alloc.err('Luaotfload required for glyph of operator')
+ return { }, 10
+ end
+ local fontid = getfontid(fnt)
+ local thefont = font.getfont(fontid)
+ local fontname = thefont.psname
+ local gid = luaotfload.aux.gid_of_name(fontid, glyphname)
+ if not gid then
+ alloc.err('Font %s has no glyph named %s', thefont.psname, glyphname)
+ return { }, 10
+ end
+ local segments = fonts.hashes.shapes[fontid].glyphs[gid].segments
+ if #segments == 0 then return { }, 10 end
+ local q = 1000 / (thefont.units_per_em or 1000)
+ -- retrieve the contours
+ local path, paths = { '(' }, { }
+ for _, s in ipairs(segments) do
+ local op = s[#s]
+ if op == 'm' then
+ if #path > 1 then
+ table.insert(path, '--cycle)')
+ table.insert(paths, table.concat(path, ''))
+ path = { '(' }
+ end
+ table.insert(path, string.format('(%f, %f)', q*s[1], q*s[2]))
+ elseif op == 'l' then
+ table.insert(path, string.format('--(%f, %f)', q*s[1], q*s[2]))
+ elseif op == 'c' then
+ table.insert(path, string.format('..controls (%f, %f) and (%f, %f) .. (%f, %f)',
+ q*s[1], q*s[2], q*s[3], q*s[4], q*s[5], q*s[6]))
+ end
+ end
+ table.insert(path, '--cycle)')
+ table.insert(paths, table.concat(path, ''))
+ 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)
+end
+
+function M.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
+ table.insert(res, string.format(
+ '%s scaled %f shifted (%fpt, %fpt)', c,
+ size/65536/1024, (x + n.xoffset)/65536, n.yoffset/65536))
+ end
+ end)
end
-- 2 opening, running and and closing instances
@@ -1122,27 +1162,21 @@ function M.run (nr, code)
return
end
current_instance = self
- alloc.log ('metapost: executing chunk in %s (%d)', self.jobname, nr)
- local res = process_results(nr, self.instance:execute(code))
- save_image_list(self, res)
+ local res = self.instance:execute(code)
+ print_log(nr, res)
+ local picts = process_results(nr, res)
+ save_image_list(self, picts)
end
-M.init_files = { 'minim.mp' }
+M.init_code = { 'let dump=endinput;', 'input INIT;', 'input minim.mp;' }
function M.open (t)
local nr = #instances + 1
t.jobname = t.jobname or ':metapost:'
- alloc.log ('metapost: creating instance %s (%d)', t.jobname, nr)
-- creating instance options
- local init = ""
- for _, f in ipairs(M.init_files) do
- init = string.format('%s input %s;', init, f)
- end
- init = string.format('%s input %s;', init, t.format or 'plain.mp')
+ local init = string.gsub(table.concat(M.init_code, ''), 'INIT', t.format or 'plain.mp')
local opts = apply_default_instance_opts(t)
local instance = mplib.new(opts)
- instance:execute(init)
- local continue
-- adding the instance
instances[nr] =
{ nr = nr
@@ -1154,15 +1188,15 @@ function M.open (t)
, boxes = { } -- allocated by maketext
, env = t.env or default_env()
}
+ print_log(nr, instance:execute(init), 'opening ')
return nr
end
function M.close (nr)
local i = instances[nr]
- alloc.log ('metapost: closing instance %s (%d) ', i.jobname, nr)
if i.instance then
local res = i.instance:finish()
- print_log(nr, res)
+ print_log(nr, res, 'closing ')
end
for _, nr in ipairs(i.boxes) do
-- remove allocated boxes
diff --git a/macros/luatex/generic/minim-mp/minim-mp.pdf b/macros/luatex/generic/minim-mp/minim-mp.pdf
index b511de3297..1011878d1e 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.tex b/macros/luatex/generic/minim-mp/minim-mp.tex
index 8e39c7f227..ad84a4f19d 100644
--- a/macros/luatex/generic/minim-mp/minim-mp.tex
+++ b/macros/luatex/generic/minim-mp/minim-mp.tex
@@ -6,6 +6,7 @@
\catcode`\: = 11
\input minim-alloc
+\input minim-pdfresources
% a default catcode table
\newcatcodetable \minim:mp:catcodes
@@ -30,7 +31,7 @@
% \directmetapost [ options ] { code }
\protected\def\directmetapost{\withoptions[]\directmetapost:}
-\def\directmetapost:[#1]#2{%
+\long\def\directmetapost:[#1]#2{%
\begingroup
\newmetapostinstance[#1]\:mpinst:
\runmetapost\:mpinst:{#2}%
diff --git a/macros/luatex/generic/minim-mp/minim.mp b/macros/luatex/generic/minim-mp/minim.mp
index c78dde2a92..f3f03eb3d0 100644
--- a/macros/luatex/generic/minim-mp/minim.mp
+++ b/macros/luatex/generic/minim-mp/minim.mp
@@ -1,13 +1,19 @@
delimiters ();
+% redefine some constants to work with the new numerical engines
+numeric mm, cm, pt, pc, dd, cc;
+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;
+
message "Loading the minim extension macros";
% interaction with tex
-def baseline expr o =
- fill if numeric o: (0,o) else o fi
+vardef baseline expr o =
+ fill if numeric o : (0,o) else: o fi
-- cycle withprescript "BASELINE:"; enddef;
-def boxresource expr nr = image(
+vardef boxresource expr nr = image(
fill unitsquare withprescript "BOXRESOURCE:" & decimal nr ;
setbounds currentpicture to unitsquare transformed runscript
("return { 'box_size', tex.getboxresourcedimensions(" & decimal nr & ") }");
@@ -17,6 +23,46 @@ 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
@@ -68,6 +114,31 @@ def endpattern (expr xstep, ystep) =
newinternal tilingtype; tilingtype:=1;
_patterns_._last_ := 0;
-% so that we can load plain.mp after this
-let dump = endinput ;
+% shorthands
+primarydef p xshifted x = p shifted (x,0) enddef;
+primarydef p yshifted y = p shifted (0,y) enddef;
+
+% reverse paths to allow „p & vflip p”
+def hflip primary p = if path p: reverse fi p xscaled -1 enddef;
+def vflip primary p = if path p: reverse fi p yscaled -1 enddef;
+
+% missing definitions
+path fullsquare, unitcircle ;
+fullsquare := unitsquare shifted - center unitsquare ;
+unitcircle := fullcircle shifted urcorner fullcircle ;
+
+% complement counterclockwise
+vardef clockwise primary c =
+ if turningnumber c > 0: reverse fi c enddef;
+
+% cmyk colours
+cmykcolor cyan, magenta, yellow, key;
+cyan = (1,0,0,0);
+magenta = (0,1,0,0);
+yellow = (0,0,1,0);
+key = (0,0,0,1);
+
+% constants
+pi := 355/113;
+
diff --git a/macros/luatex/generic/minim-pdf/README b/macros/luatex/generic/minim-pdf/README
index 1d1f02a913..d8f2b19c6e 100644
--- a/macros/luatex/generic/minim-pdf/README
+++ b/macros/luatex/generic/minim-pdf/README
@@ -1,4 +1,4 @@
-Version: 2021/1.0
+Version: 2022/1.1
SUMMARY
@@ -25,9 +25,32 @@ In order to obtain the typeset manual of this package, simply say:
luatex minim-pdf.doc
+HISTORY
+
+2022/1.1 (3/3/2022)
+
+ New features:
+
+ * Added support for structure element attributes.
+ * Added support for structure element attribute classes.
+ * Improved tagging of equations; added configuration.
+ * Made tagged artifacts more robust: they can now occur across page
+ breaks.
+ * The lua interface should now be more-or-less stable.
+
+ Minor changes:
+
+ * PDF strings will be encoded in a more clever (and readable) way.
+ * Structure element tags will have special characters encoded properly.
+
+2021/1.0 (1/6/2021)
+
+ This was the original release.
+
+
COPYING
-(c) 2021 Esger Renkema
+(c) 2022 Esger Renkema
These files may be distributed under the terms of the European Union Public
Licence (EUPL) version 1.2 or later. A copy can be obtained at:
diff --git a/macros/luatex/generic/minim-pdf/minim-languagecodes.lua b/macros/luatex/generic/minim-pdf/minim-languagecodes.lua
index df3e95a65e..6a8b6f3b0b 100644
--- a/macros/luatex/generic/minim-pdf/minim-languagecodes.lua
+++ b/macros/luatex/generic/minim-pdf/minim-languagecodes.lua
@@ -78,6 +78,7 @@ return {
latvian = 'lv',
lithuanian = 'lt',
liturgicallatin = 'la-x-liturgic',
+ macedonian = 'mk',
malayalam = 'ml',
marathi = 'mr',
medievalgreek = 'gkm',
@@ -94,8 +95,10 @@ return {
norwegian = 'nb',
nynorsk = 'nn',
occitan = 'oc',
+ oldgerman = 'de',
oriya = 'or',
pali = 'pi',
+ panjabi = 'pa',
patois = 'fr',
persian = 'fa',
piedmontese = 'pms',
@@ -111,6 +114,7 @@ return {
sahidic = 'cop-x-sahidic',
samaritan = 'smp',
sanskrit = 'sa',
+ schoolfinnish = 'fi',
serbian = 'sr-latn',
serbianc = 'sr-cyrl',
slovak = 'sk',
@@ -129,11 +133,11 @@ return {
turkish = 'tr',
turkmen = 'tk',
ugaritic = 'uga',
- uncoded = 'mis',
- undetermined = 'und',
ukenglish = 'en-UK',
UKenglish = 'en-UK',
ukrainian = 'uk',
+ uncoded = 'mis',
+ undetermined = 'und',
uppersorbian = 'hsb',
usenglish = 'en-US',
USenglish = 'en-US',
diff --git a/macros/luatex/generic/minim-pdf/minim-pdf.doc b/macros/luatex/generic/minim-pdf/minim-pdf.doc
index 2c06dd25b0..71e8cea89c 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 {2021-06-01}
- version {2021/1.0}
+ date {2022-03-03}
+ version {2022/1.1}
keywords {LuaTeX; Plain TeX; PDF/A; Tagged PDF; accessibility; a11y;
hypertext; bookmarks; document outline; associated files}
stopmetadata
@@ -47,8 +47,9 @@ generated name.
\section Bookmarks % 1
-Bookmarks can be added with ⟦*\outline [open] [dest {name}] {title}⟧.
-Add ⟦open⟧ to have the bookmark appear initially open and
+Bookmarks can be added with ⟦*\outline [open|closed] [dest {name}] {title}⟧.
+Add ⟦open⟧ or ⟦closed⟧ to have the bookmark appear initially open or
+closed (default), and
say ⟦dest {name}⟧ for having it refer to a specific named destination
(otherwise, a new one will be created where the ⟦\outline⟧ command appears).
@@ -129,11 +130,10 @@ With the minim-xmp package, a useful setting is
\section Lua module % 1
The interface of the lua module (available via
-⟦local M = require('minim-pdf')⟧) is not stable yet, and may change.
-One function of interest, however, is
-⟦M.pdf_string(...)⟧, wich converts a lua string to a pdf string. The
-surrounding ⟦<>⟧ or ⟦()⟧ characters are included in the return value.
-
+⟦local M = require('minim-pdf')⟧) should be stable by now.
+Though it contains lua equivalents for most tex commands described here, using
+them directly is not very ergonomical and not recommended.
+Please consult the source if you do want to use them anyway.
% 
@@ -160,7 +160,7 @@ be seen by tex’s stomach in the \emph{logical} order.
Furthermore, while the macros in this package are sophisticated enough that
tagging can be done without any manual intervantion, it is quite possible and
-rather easy to generate the wrong document structore, or even cause syntax
+rather easy to generate the wrong document structure, or even cause syntax
errors in the resulting pdf code.
You should always check the result in an external application.
@@ -169,21 +169,17 @@ This is the full list of limitations, pitfalls and shortcomings:
\item1. Document content must be seen by tex in its logical order (although you
can mark out-of-order content explicitly if you know what you are doing; see
below).
-\item2. Artifacts cannot be split across pages. A pagebreak inmidst an artifact
-will cause incorrect pdf without error or warning.
-\item3. The contents of ⟦\localleftbox⟧ and ⟦\localrightbox⟧ must be marked
+\item2. The contents of ⟦\localleftbox⟧ and ⟦\localrightbox⟧ must be marked
manually, probably as artifact.
-\item4. You must mark page header, page footer and footnote rule yourself; no
+\item3. You must mark page header, page footer and footnote rule yourself; no
default is set.
-\item5. There currently is no way of marking xforms or other pdf objects as
+\item4. There currently is no way of marking xforms or other pdf objects as
content items of themselves.
-\item6. The content of xforms (i.e. pdf objects created by ⟦\useboxresource⟧)
+\item5. The content of xforms (i.e. pdf objects created by ⟦\useboxresource⟧)
should not contain tagging commands.
-\item7. Likewise, you should be careful with box reuse: it might work, but you
+\item6. Likewise, you should be careful with box reuse: it might work, but you
should check.
-\item8. The use of structure element attributes is currently not supported
-except in a limited number of cases.
-\item9. This package currently only supports pdf~1.7 tagging and is not yet
+\item7. This package currently only supports pdf~1.7 tagging and is not yet
ready for use with pdf~2.0.
\stoplist
@@ -234,7 +230,7 @@ structure element instead.
The begining and ending of a content item can be forced with
⟦*\startcontentitem⟧ and ⟦*\stopcontentitem⟧, while ⟦*\ensurecontentitem⟧ will
only open a new content item if you are currently outside any.
-If you need some part to be a single content item, that can use
+If you need some part to be a single content item, you can use
⟦*\startsinglecontentitem ... \stopsinglecontentitem⟧.
This will disable all SE and MCI tagging inside.
@@ -249,8 +245,6 @@ Artifacts can be marked in two ways: with ⟦*\markartifact {type} {...}⟧ or w
The ⟦type⟧ is written to the pdf attribute dictionary directly, so that if you
need a subtype, you can write e.g.
⟦\startartifact {Pagination /Subtype/Header} etc⟧.
-Do make sure your artifact does not contain a page break, as this will result
-in invalid output.
Inside artifacts, other structure content markers will be ignored. Furthermore,
this package makes sure artifacts are never part of marked content items,
@@ -281,11 +275,11 @@ perfect nor complete. You should always check the results in an external
application.
Particular care should be taken when ‘skipping’ structure levels: the sequence
chapter – subsection – section will result in the section beneath the subsection.
-If you are in doubt about an element being closed already, you can use
+If you are in doubt whether an element has been closed already, you can use
⟦*\ensurestopelement {Tag}⟧ instead of ⟦\stopelement⟧
to prevent an error being raised.
-All these helpful features can also be disabled by setting
+All these helpful features can be disabled by setting
⟦*\strictstructuretagging⟧ to a positive value. Then, every structure element
will have to be closed by an explicit closing tag, as in xml.
In this case, ⟦\stopelement⟧ and ⟦\ensurestopelement⟧ will be equivalent.
@@ -293,6 +287,8 @@ In this case, ⟦\stopelement⟧ and ⟦\ensurestopelement⟧ will be equivalent
By default, ⟦P⟧ structure elements are inserted automatically at the start of
every paragraph. The tag can be changed with ⟦*\nextpartag {Tag}⟧; leaving the
argument empty will prevent marking the next paragraph.
+Keep in mind that the reassignment is local: if a paragraph marked with
+⟦\nextpartag⟧ starts inside a group, it will not reset.
Auto-marking paragraphs can be (locally) disabled or enabled by saying
⟦*\markparagraphsfalse⟧ or ⟦*\markparagraphstrue⟧.
@@ -326,20 +322,28 @@ in more complicated situations you should therefore use
⟦*\savecurrentelementto\name ... \continueelementfrom\name⟧ which restores the
current SE from a named identifier ⟦\name⟧.
-
\section Structure element options % 1
-The ⟦\startcontentitem⟧ command allows a few options that are not mentioned
-above: its full syntax is ⟦*\startcontentitem <options> {Tag}⟧.
+The ⟦\startelement⟧ command allows a few options that are not mentioned
+above: its full syntax is ⟦*\startelement <options> {Tag}⟧.
The three most useful options are ⟦alt⟧ for setting an alt-text (the ⟦/Alt⟧
entry in the structure element dicionary), ⟦actual⟧ for a text replacement
(⟦/ActualText⟧) and ⟦lang⟧ for the language (⟦/Lang⟧; see the next section).
The alternative and actual texts can also be given after the fact with
-⟦*\setalttext{...}⟧ and ⟦*\setactualtext{...}⟧. These apply to the current
+⟦*\setalttext{...}⟧ and ⟦*\setactualtext{...}⟧; these apply to the current
structure element.
-Setting structure element attributes is not supported at this moment, except
-the placement attributes ⟦block⟧ and ⟦inline⟧, which can be given as options.
+Structure element attributes can be given with
+⟦attr <owner> <key> <value>⟧, e.g. ⟦attr Layout Placement /Inline⟧.
+Note that for the ⟦owner⟧ and ⟦key⟧ the initial slash must be omitted; the
+⟦value⟧ on the other hand will be written to the pdf verbatim.
+Any number of attributes can be added.
+
+Finally, structure element classes can be given with the
+⟦class <classname>⟧ keyword, which can be repeated.
+Classes can be defined with
+⟦*\newattributeclass classname <attributes>⟧ where ⟦<attributes>⟧ can be any
+number of ⟦attr⟧ statements as above.
\section Languages % 1
@@ -358,7 +362,7 @@ separate codes to dialects.
There is a small set of default language code associations, which can be found
in the file ⟦minim-languagecodes.lua⟧.
It covers most languages defined by the hyph-utf8 package, as well as (due to
-their ubiquitous use) many ancient languages.
+their ubiquitous use) some ancient languages.
An actual language change introduced by ⟦\uselanguage⟧ will not otherwise be
acted upon by this package. Therefore, you will probably want to add
@@ -381,7 +385,7 @@ This package ensures the existence of the ⟦nohyph⟧, ⟦nolang⟧, ⟦uncoded
⟦undetermined⟧ dummy languages, all without hyphenation.
-\section Helper macros
+\section 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
@@ -410,14 +414,18 @@ 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⟧.
-Both alt and actualtext of the ⟦Formula⟧ structure element will be set to the
-(unexpanded) source code of the equation, surrounded by the appropriate number
-of dollar signs.
-Furthermore, if ⟦\pdfaconformancelevel⟧ equals three, the source of the formula
-will be attached in an embedded file with the ⟦/AFRelation⟧ set to ⟦Source⟧.
+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 ffe845061c..96725523d2 100644
--- a/macros/luatex/generic/minim-pdf/minim-pdf.lua
+++ b/macros/luatex/generic/minim-pdf/minim-pdf.lua
@@ -2,6 +2,7 @@
local M = { }
local alloc = require('minim-alloc')
local cb = require('minim-callbacks')
+alloc.remember('minim-hooks')
alloc.remember('minim-pdf')
-- 1 helper functions
@@ -18,9 +19,27 @@ local function add_to_names(...)
pdf.setnames((pdf.getnames() or '') .. string.format(...) .. ' ')
end
+local pdf_name = alloc.pdf_name
+local pdf_string = alloc.pdf_string
+local options_scanner = alloc.options_scanner
+
+-- has this table just one element?
+local function singleton(t)
+ local one = false
+ for _, _ in pairs(t) do
+ if one then
+ return false
+ else
+ one = true
+ end
+ end
+ return one
+end
+
-- in-depth node list traversal
-- returns current and parent node
--- only dives into hbox and vbox nodes
+-- only dives into hbox, vbox and ins nodes
+-- returns node and enclosing box
local function full_traverse(head)
return function(stack, last)
local next = last.next
@@ -36,103 +55,8 @@ local function full_traverse(head)
end, { { } }, { next = head }
end
--- re-encode to utf-16 and surround by <>
-local function pdf_hex_string(text)
- local str = { [1] = '<feff' }
- for i in text:utfvalues() do
- if i < 0xFFFF then
- insert_formatted(str, '%04x', i)
- else
- i = i - 0x10000
- m = math.floor(i/0x400) + 0xD800
- n = ( i % 0x400 ) + 0xDC00
- insert_formatted(str, '%04x%04x' ,m, n)
- end
- end
- table.insert(str, '>')
- return table.concat(str,'')
-end
-
--- try and produce a () string first, then fall back to <>
-function M.pdf_string(text)
- local str = { '(' }
- for i in text:utfvalues() do
- if i > 0x7E then
- return pdf_hex_string(text)
- elseif i < 0x20 then
- insert_formatted(str, '\\%03o', i)
- elseif i == 0x28 or i == 0x29 or i == 0x5c then
- insert_formatted(str, '\\%c', i)
- else
- insert_formatted(str, '%c', i)
- end
- end
- table.insert(str, ')')
- return table.concat(str,'')
- end
-
--- make available as \pdfstring{...}
-alloc.luadef('pdfstring', function() M.pdf_string(token.scan_string()) end)
-
--- try and produce a date of the format (D:YYYYMMDD)
-function M.pdf_date(text)
- local y, m, d = string.match(text, '^([12][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)$')
- if not y then d, m, y = string.match(text, '^([0-9][0-9]?)-([0-9][0-9]?)-([12][0-9][0-9][0-9])$') end
- if y then
- return string.format('(D:%4d%02d%02d)', y, m, d)
- else
- return string.format('(D:%s)', text)
- end
-end
-
-function M.options_scanner()
- return { scanners = { },
- add = function(this, name, scanner)
- this.scanners[name] = scanner
- return this
- end,
- keyword = function(this, name)
- return this:add(name, function() return true end)
- end,
- int = function(this, name)
- return this:add(name, token.scan_int)
- end,
- glue = function(this, name)
- return this:add(name, token.scan_glue)
- end,
- dimen = function(this, name)
- return this:add(name, token.scan_dimen)
- end,
- string = function(this, name)
- return this:add(name, token.scan_string)
- end,
- argument = function(this, name)
- return this:add(name, token.scan_argument)
- end,
- word = function(this, name)
- return this:add(name, token.scan_word)
- end,
- list = function(this, name)
- return this:add(name, token.scan_list)
- end,
- scan = function(this, defaults)
- local res = defaults or { }
- repeat
- local matched = false
- for name, scanner in pairs(this.scanners) do
- if token.scan_keyword(name) then
- matched, this.scanners[name] = true, nil
- res[name] = scanner()
- end
- end
- until not matched
- return res
- end
- }
-end
-
local function pdf_err(n, msg)
- local m = node.new(8,16)
+ local m = node.new(8,16) -- pdf_literal
m.mode, m.token = 2, '%% Warning: '..msg
node.insert_after(n, n, m)
end
@@ -158,10 +82,10 @@ local structure = { {
local parent_tree = { } -- list of list of structure elements
-- Our four helper attributes
-local current_struct = alloc.new_attribute('tagging:current:se')
-local current_order = alloc.new_attribute('tagging:element:order')
-local current_status = alloc.new_attribute('tagging:current:status')
-local current_lang = alloc.new_attribute('tagging:current:language')
+local current_struct = alloc.registernumber('tagging:current:se')
+local current_order = alloc.registernumber('tagging:element:order')
+local current_status = alloc.registernumber('tagging:current:status')
+local current_lang = alloc.registernumber('tagging:current:language')
local function current_structure_element()
return structure[tex.attribute[current_struct]]
@@ -242,7 +166,7 @@ local structure_types = alloc.saved_table('structure types', {
Note = { type = 'inline' },
Reference = { type = 'inline' },
BibEntry = { type = 'inline' },
- Code = { type = 'inline' },
+ Code = { type = 'inline', attributes = { ['CSS-2.00'] = { ['white-space'] = '(pre)' } } },
Link = { type = 'inline' }, -- contains link objects
Annot = { type = 'inline' }, -- contains other annotations
-- ruby/warichu
@@ -318,7 +242,6 @@ alloc.luadef('struct:addalias', function()
M.add_structure_alias(stype, alias, settings)
end, 'protected')
-
-- 1 writing the document structure
local function stable_sort_on_order_field(unsorted)
@@ -373,7 +296,7 @@ local function make_rolemap()
local aliases = { }
for k, v in pairs(structure_types) do
if v.aliasof and v.inuse and v.aliasof ~= k then
- insert_formatted(aliases, '/%s/%s', k, v.aliasof)
+ insert_formatted(aliases, '%s%s', pdf_name(k), pdf_name(v.aliasof))
end
end
if #aliases > 0 then
@@ -382,6 +305,33 @@ local function make_rolemap()
return ''
end
+local function make_attributes(res, t)
+ -- is there just one attribute?
+ local list = not singleton(t)
+ if list then table.insert(res, '[') end
+ for ao, ac in pairs(t) do
+ insert_formatted(res, '<< /O %s', pdf_name(ao))
+ for k,v in pairs(ac) do
+ insert_formatted(res, '%s %s', pdf_name(k), v)
+ end
+ table.insert(res, '>>')
+ end
+ if list then table.insert(res, ']') end
+end
+
+local attribute_classes = alloc.saved_table('attribute classes')
+local function make_classmap()
+ local classes = { }
+ for c, as in pairs(attribute_classes) do
+ insert_formatted(classes, '\n%s', pdf_name(c))
+ make_attributes(classes, as)
+ end
+ if #classes > 0 then
+ return '\n/ClassMap << ' .. table.concat(classes, ' ') .. ' >>'
+ end
+ return ''
+end
+
local function write_structure()
if #structure == 1 then return end
-- reserve object numbers, prepare for writing
@@ -395,22 +345,29 @@ local function write_structure()
add_to_catalog('/StructTreeRoot %s 0 R', root_obj)
-- write the structure tree root
pdf.immediateobj(root_obj, string.format(
- '<< /Type/StructTreeRoot /K %d 0 R /ParentTree %d 0 R%s >>',
- structure[1].objnum, parent_tree_obj, make_rolemap()))
+ '<< /Type/StructTreeRoot /K %d 0 R /ParentTree %d 0 R%s%s >>',
+ structure[1].objnum, parent_tree_obj, make_rolemap(), make_classmap()))
-- write structure elements
for _, se in ipairs(structure) do
if not se.hidden then
local res = { '<<' }
- insert_formatted(res, '/Type/StructElem /S/%s /P %d 0 R', se.struct, se.parent.objnum)
+ insert_formatted(res, '/Type/StructElem /S%s /P %d 0 R', pdf_name(se.struct), se.parent.objnum)
if se.lang and se.lang ~= se.parent.lang then insert_formatted(res, '/Lang (%s)', se.lang) end
- if se.alt then insert_formatted(res, '/Alt %s', M.pdf_string(se.alt)) end
- if se.actual then insert_formatted(res, '/ActualText %s', M.pdf_string(se.actual)) end
+ if se.alt then insert_formatted(res, '/Alt %s', pdf_string(se.alt)) end
+ if se.actual then insert_formatted(res, '/ActualText %s', pdf_string(se.actual)) end
if #se.children > 0 then insert_formatted(res, '\n/K %s', format_K_array(se)) end
if se.mainpage then insert_formatted(res, '/Pg %d 0 R', se.mainpage) end
+ if se.class then
+ table.insert(res, '/C')
+ if #se.class > 1 then table.insert(res, '[') end
+ for _, c in ipairs(se.class) do
+ insert_formatted(res, '%s', pdf_name(c))
+ end
+ if #se.class > 1 then table.insert(res, ']') end
+ end
if se.attributes then
- table.insert(res, '/A <<')
- for k,v in pairs(se.attributes) do insert_formatted(res, '/%s %s', k, v) end
- table.insert(res, '>>')
+ table.insert(res, '/A')
+ make_attributes(res, se.attributes)
end
if se.files then
table.insert(res, '/AF [')
@@ -487,11 +444,10 @@ end, 'protected')
-- \uselanguage patch; provide default document language
-- and associate names with numbers
-alloc.luadef('minim:apply:language:code', function()
- local name = token.scan_string()
+cb.register('uselanguage', function(name)
local nr = language_codes[name] or alloc.err('No language code known for ‘%s’', name) and 1
if not structure[1].lang then M.set_document_language(language_codes[nr]) end
- tex.sprint(nr)
+ tex.attribute['tagging:current:language'] = nr
end)
local function write_language()
@@ -536,24 +492,20 @@ end, 'protected')
function M.open_structure_node(n)
local info = structure_types[n.struct]
if not info then
- alloc.err('Unknown structure type %s replaced by NonStruct', n.struct)
+ alloc.err('Unknown structure type %s replaced with NonStruct', n.struct)
n.struct, info = 'NonStruct', structure_types.NonStruct
end
info.inuse = true
if info.attributes then
- n.attributes = n.attributes or { }
- for k,v in pairs(info.attributes) do
- n.attributes[k] = n.attributes[k] or v
+ n.attributes = n.attr or { }
+ for k1,v1 in pairs(info.attributes) do
+ n.attributes[k1] = n.attributes[k1] or { }
+ for k2, v2 in pairs(v1) do
+ n.attributes[k1][k2] = n.attributes[k1][k2] or v2
+ end
end
- end
- if n.block then
- n.type = 'block'
- n.attributes = n.attributes or { }
- n.attributes.Placement = '/Block'
- elseif n.inline then
- n.type = 'inline'
- n.attributes = n.attributes or { }
- n.attributes.Placement = '/Inline'
+ else
+ n.attributes = n.attr
end
n.index = #structure + 1
n.children = { }
@@ -573,13 +525,13 @@ function M.open_structure_node(n)
end
alloc.luadef('tagging:startelement', function()
- local s = M.options_scanner()
+ local s = options_scanner()
:string('type') -- 'section', 'group', 'block' etc.
:argument('alt')
:argument('actual')
:string('lang')
- :keyword('block')
- :keyword('inline')
+ :subtable('attr')
+ :string('class', true)
:scan()
s.struct = token.scan_string()
M.open_structure_node(s)
@@ -593,6 +545,11 @@ alloc.luadef('tagging:actual', function()
current_structure_element().actual = token.scan_string()
end)
+alloc.luadef('newattributeclass', function()
+ local s = options_scanner():subtable('attr')
+ attribute_classes[token.scan_string()] = s:scan().attr
+end, 'protected')
+
-- 1 marking content items
@@ -613,7 +570,7 @@ local function clear_page_tagging_parameters()
_open_mci_node_ = function (se_num, order)
pageobj = pageobj or pdf.getpageref(status.total_pages + 1)
local se = structure[se_num]
- pdf.print(string.format('/%s <</MCID %d>> BDC ', se.struct, #mcid_list))
+ pdf.print(string.format('%s <</MCID %d>> BDC ', pdf_name(se.struct), #mcid_list))
table.insert(se.children, { mcid = #mcid_list, order = order, pageobj = pageobj })
table.insert(mcid_list, se)
-- unhide hidden parents (done here to preserve the correct order)
@@ -631,17 +588,13 @@ local function new_open_mci_node(se, order)
return n
end
-local function new_open_art_node(atype)
+local function pdf_literal(token)
local n = node.new(8,16) -- pdf_literal
- n.mode, n.token = 1, string.format('/Artifact << /Type/%s >> BDC', atype)
+ n.mode, n.token = 1, token
return n
end
-local function new_emc_node()
- local n = node.new(8,16) -- pdf_literal
- n.mode, n.token = 1, 'EMC'
- return n
-end
+local open_artifacts = { }
cb.register('finish_pdfpage', function(shippingout)
if shippingout then
@@ -655,17 +608,17 @@ cb.register('finish_pdfpage', function(shippingout)
end
end)
--- 1 content item boundaries and linking
+-- 1 content item boundaries and linking (in pre-shipout)
function M.mark_content_items(box)
- local se, order, open
+ local se, order, art, open
local start_node, end_node, parent_node
local pageobj = pdf.getpageref(status.total_pages + 1)
- -- inserting mci markers
+ -- helper functions for inserting mci markers
local insert_tags = function(end_parent)
if not open then return end
parent_node.list = node.insert_before(parent_node.list, start_node, open)
- node.insert_after(end_parent.list, end_node, new_emc_node())
+ node.insert_after(end_parent.list, end_node, pdf_literal('EMC'))
start_node, end_node, parent_node, open = nil, nil, nil, nil
end
local start_content = function(n, b)
@@ -676,18 +629,31 @@ function M.mark_content_items(box)
for n, b in full_traverse(box) do
local marker = n.id == 8 and n.subtype == 8
and n.user_id == marker_whatsit and n.value
- if marker and marker.what == 'art_start' then
+ local stat = n[current_status]
-- first we start with marking artifacts
+ if marker and marker.what == 'art_start' then
insert_tags(b);
start_content(n, b)
- open = new_open_art_node(marker.it)
+ 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);
- elseif n[current_status] then
- -- inside artifact, do nothing
- elseif n.id == 8 and n.subtype == 19 then -- pdf_start_link
+ open_artifacts[art], art = nil, nil
+ elseif stat and stat >10 and stat ~=art then -- broken artifact
+ insert_tags(b);
+ start_content(n, b)
+ 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)')
+ 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
local _se, _order = n[current_struct], n[current_order]
local link = structure[_se]
if link.struct == 'Link' then
@@ -698,16 +664,16 @@ function M.mark_content_items(box)
alloc.err('Link found outside Link structure element (see pdf)')
pdf_err(n, 'unmarked link')
end
+ -- handle explicit start and stop nodes
elseif marker and marker.what == 'mci_start' then
- -- explicit start and stop nodes
insert_tags(b);
start_content(n, b)
open = new_open_mci_node(se, order)
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
or marker and marker.what == 'content' then
- -- now see if we need to intervene between content nodes
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
-- ignore invisible rules
@@ -720,8 +686,7 @@ function M.mark_content_items(box)
insert_tags(b);
start_content(n, b)
open = new_open_mci_node(se, order)
- else
- -- nothing changed: continue current mci
+ else -- nothing changed: continue current mci
end_node = n
end
elseif n.id == 12 and n.subtype > 2 and n.subtype < 8 then
@@ -730,10 +695,6 @@ function M.mark_content_items(box)
end
end
-- close the last mci
- if start_node and start_node.user_id and start_node.value
- and start_node.value.what == 'art_start' then
- alloc.err('Page %d ends with a broken artifact', status.total_pages+1)
- end
insert_tags(box)
end
@@ -786,7 +747,7 @@ local function write_bookmarks()
-- write bookmark objects
for i=2, #bookmarks do
local bm, res = bookmarks[i], { }
- insert_formatted(res, '<< /Title %s\n/Parent %s 0 R /Dest (%s)', M.pdf_string(bm.title),
+ insert_formatted(res, '<< /Title %s\n/Parent %s 0 R /Dest (%s)', pdf_string(bm.title),
bm.parent and bm.parent.outline_obj or bookmarks[1].outline_obj, bm.dest)
if bm.next then insert_formatted(res, '/Next %s 0 R', bm.next.outline_obj) end
if bm.prev then insert_formatted(res, '/Prev %s 0 R', bm.prev.outline_obj) end
@@ -827,9 +788,8 @@ function M.add_bookmark(bm)
end
alloc.luadef('outline', function()
- local s = M.options_scanner()
- :keyword('open')
- :keyword('closed') -- default; ignored
+ local s = options_scanner()
+ :keyword('open', 'closed')
:string('dest')
:scan()
s.title = token.scan_string()
@@ -866,8 +826,8 @@ function M.embed_file(t)
end
-- embed the file
local attr = { '/Type/EmbeddedFile' }
- if t.mimetype then insert_formatted(attr, '/Subtype/%s', string.gsub(t.mimetype, '/', '#2F')) end
- if t.moddate then insert_formatted(attr, '/Params << /ModDate %s >>', M.pdf_date(t.moddate)) end
+ if t.mimetype then insert_formatted(attr, '/Subtype%s', pdf_name(t.mimetype)) end
+ if t.moddate then insert_formatted(attr, '/Params << /ModDate %s >>', alloc.pdf_date(t.moddate)) end
local ef = pdf.obj {
immediate = true,
compresslevel = t.uncompressed and 0 or nil,
@@ -878,7 +838,7 @@ function M.embed_file(t)
-- write the filespec
local res = { '<< /Type/Filespec' }
insert_formatted(res, '/F %s /UF %s /EF << /F %d 0 R /UF %d 0 R >>', t.name, t.name, ef, ef)
- if t.desc then insert_formatted(res, '/Desc %s', M.pdf_string(t.desc)) end
+ if t.desc then insert_formatted(res, '/Desc %s', pdf_string(t.desc)) end
if t.relation then insert_formatted(res, '/AFRelationship /%s', t.relation) end
table.insert(res, '>>')
local fs = pdf.immediateobj(table.concat(res, ' '))
@@ -887,7 +847,7 @@ function M.embed_file(t)
end
alloc.luadef('embedfile', function()
- local t = M.options_scanner()
+ local t = options_scanner()
:string('desc')
:string('file')
:string('string')
@@ -901,7 +861,7 @@ alloc.luadef('embedfile', function()
if not t.name then
t.name = t.file or aloc.err('No name specified for embedded file stream')
end
- t.name = M.pdf_string(t.name or '(unnamed)')
+ t.name = pdf_string(t.name or '(unnamed)')
local fs, ef = M.embed_file(t, t.global)
-- where to attach?
if t.global then
@@ -1036,7 +996,7 @@ local function write_pagelabels()
end
local res = { '/PageLabels << /Nums [' }
for _, l in ipairs(pagelabels) do
- local s, p = l.s and '/S/'..l.s, l.p and ' /P '..M.pdf_string(l.p)
+ local s, p = l.s and '/S/'..l.s, l.p and ' /P '..pdf_string(l.p)
table.insert(res, string.format('%d << /St %d %s%s >>', l.start, l.st, s or '', p or ''))
end
table.insert(res, ']')
@@ -1083,7 +1043,7 @@ function M.add_default_rgb_output_intent(t)
N = 3, path = 'sRGB.icc' }
end
-function M.add_default_cmyk_output_intents(t)
+function M.add_default_cmyk_output_intent(t)
t = t or {}
M.add_output_intent {
subtype = t.subtype or 'GTS_PDFA1',
diff --git a/macros/luatex/generic/minim-pdf/minim-pdf.pdf b/macros/luatex/generic/minim-pdf/minim-pdf.pdf
index b460bdf4ed..414e577186 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 f8075ae6e4..630c374d41 100644
--- a/macros/luatex/generic/minim-pdf/minim-pdf.tex
+++ b/macros/luatex/generic/minim-pdf/minim-pdf.tex
@@ -5,6 +5,9 @@
\chardef\minimpdfloaded = \catcode`\:
\catcode`\: = 11
+\input minim-alloc
+\input minim-hooks
+
% Abbreviations used throughout this document:
% se structure element
% mci marked content item
@@ -19,9 +22,6 @@
% 1 the attributes
-\input minim-alloc
-\input minim-hooks
-
% By three attributes do we determine the document structure.
%
% One for marking the current structure element:
@@ -37,7 +37,8 @@
%
% One for marking the status:
% - assignments are generally local
-% - if set, disables tagging and content marking
+% - if set, disables tagging and content marking in pre_shipout
+% - values >10 indicate artifacts
\newattribute \tagging:current:status \tagging:current:status = \unset
%
% A fourth attribute keeps track of the current language
@@ -53,7 +54,7 @@
% disables marking structure elements
\newif\iftagging:enabled \tagging:enabledtrue
\protected\def\starttagging{\tagging:enabledtrue
- \ifnum1=\tagging:current:status
+ \ifnum0<\tagging:current:status
\tagging:current:status\unset\fi}
\protected\def\stoptagging{\tagging:enabledfalse
\ifnum\unset=\tagging:current:status
@@ -62,9 +63,12 @@
% \markartifact {Layout} {...}
% \startartifact {Pagination /Subtype/Header} ... \stopartifact
\long\def\markartifact#1#2{\startartifact{#1}#2\stopartifact}
-\protected\def\startartifact{\begingroup\stoptagging
+\protected\def\startartifact{\begingroup\tagging:enabledfalse
+ \tagging:current:status=\tagging:current:artifact
+ \global\advance\tagging:current:artifact1\relax
\tagging:mci:incr\tagging:art:markstart}
\protected\def\stopartifact{\tagging:art:markstop\tagging:mci:incr\endgroup}
+\newcount\tagging:current:artifact \tagging:current:artifact = 11
% \startcontentitem ... \stopcontentitem
\protected\def\startcontentitem{\iftagging:enabled\tagging:mci:incr\tagging:mci:markstart\fi}
@@ -223,20 +227,36 @@
\def\tagging:startdisplay\fi\fi\fi#1$${\tagging:makeformula{#1}{$$}}
\def\tagging:makeformula#1#2{\fi\fi\fi
\global\advance\tagging:formulanr1\relax
- \startelement\iftagging:indisplay block\else inline\fi{Formula}%
- \setalttext{\tagging:formulasource{#1}}%
- \setactualtext{\tagging:formulasource{#1}}%
- \ifnum3=\pdfaconformancelevel
- \embedfile mimetype text/x-tex
- relation Source desc {Equation source}
- name {\formulafilename.tex}
- string {\tagging:formulasource{#1}}\fi
+ \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}%
\scantextokens{#1}#2}
\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{}
+\def\formulasource:actualtext#1{%
+ \setactualtext{\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}
+\def\formulasource:both#1{%
+ \formulasource:actualtext{#1}%
+ \formulasource:attachment{#1}}
+\includeformulasources{both}
+
+
% 1 hyperlinks
% provided by the lua module:
@@ -265,20 +285,6 @@
% provided by the lua module:
% \setdocumentlanguage {name or code}
% \setlanguagecode {name} {code}
-% \minim:apply:language:code{name} → index
-
-% patch in language codes to \uselanguage
-\ifcsname uselanguage@hook\endcsname
- \expandafter\let
- \expandafter\minim:uselanguagehook
- \lastnamedcs \fi
-\expandafter\edef\csname uselanguage@hook\endcsname#1{%
- % tagging support
- \noexpand\tagging:current:language
- \noexpand\minim:apply:language:code{#1}%
- % previous definitions
- \ifdefined\minim:uselanguagehook
- \noexpand\minim:uselanguagehook{#1}\fi}
% \newnamedlanguage {name} {lhm} {rhm}
\def\newnamedlanguage#1#2#3{%
diff --git a/macros/luatex/generic/minim-xmp/README b/macros/luatex/generic/minim-xmp/README
index 0383085ee2..46d9d3cc6e 100644
--- a/macros/luatex/generic/minim-xmp/README
+++ b/macros/luatex/generic/minim-xmp/README
@@ -1,4 +1,4 @@
-Version: 2021/1.0
+Version: 2022/1.1
SUMMARY
@@ -19,9 +19,22 @@ In order to obtain the typeset manual of this package, simply say:
luatex minim-xmp.doc
+HISTORY
+
+2022/1.1 (3/3/2022)
+
+ New features:
+
+ * Writing metadata can now be disabled globally.
+
+2021/1.0 (1/6/2021)
+
+ This was the original release.
+
+
COPYING
-(c) 2021 Esger Renkema
+(c) 2022 Esger Renkema
These files may be distributed under the terms of the European Union Public
Licence (EUPL) version 1.2 or later. A copy can be obtained at:
diff --git a/macros/luatex/generic/minim-xmp/minim-xmp.doc b/macros/luatex/generic/minim-xmp/minim-xmp.doc
index 01d2084f6c..57fe527d2c 100644
--- a/macros/luatex/generic/minim-xmp/minim-xmp.doc
+++ b/macros/luatex/generic/minim-xmp/minim-xmp.doc
@@ -8,8 +8,8 @@
\else \startmetadata
author {Esger Renkema}
title {minim-xmp}
- date {2021-06-01}
- version {2021/1.0}
+ date {2022-03-03}
+ version {2022/1.1}
keywords {LuaTeX; Plain TeX; XMP; metadata; PDF/A;}
stopmetadata
\maketitle \fi
@@ -156,6 +156,68 @@ after which the generated XMP code will be
</colours:Favourite>
</rdf:Description>⟧
+If use of the pdf/a format is detected (i.e. a ⟦pdfaid⟧ entry is present in the
+metadata), the following pdf/a extension schema will also be generated:
+
+\font\seventt {Latin Modern Mono:script=latn;+smcp;} at 7pt
+\begingroup\rightskip=-6cm\baselineskip=9pt
+⟦∥let∥tentt∥seventt∥seventt<rdf:Description rdf:about=""
+ xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/"
+ xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#"
+ xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#"
+ xmlns:pdfaType="http://www.aiim.org/pdfa/ns/type#"
+ xmlns:pdfaField="http://www.aiim.org/pdfa/ns/field#" >
+ <pdfaExtension:schemas>
+ <rdf:Bag>
+ <rdf:li rdf:parseType="Resource">
+ <pdfaSchema:schema>Example namespace</pdfaSchema:schema>
+ <pdfaSchema:namespaceURI>http://example.com/minim/colours/</pdfaSchema:namespaceURI>
+ <pdfaSchema:prefix>colours</pdfaSchema:prefix>
+ <pdfaSchema:property>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <pdfaProperty:name>Favourite</pdfaProperty:name>
+ <pdfaProperty:valueType>Colour</pdfaProperty:valueType>
+ <pdfaProperty:category>external</pdfaProperty:category>
+ <pdfaProperty:description>The author’s favourite colour</pdfaProperty:description>
+ </rdf:li>
+ </rdf:Seq>
+ </pdfaSchema:property>
+ <pdfaSchema:valueType>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <pdfaType:type>Colour</pdfaType:type>
+ <pdfaType:namespaceURI>http://example.com/minim/colours/</pdfaType:namespaceURI>
+ <pdfaType:prefix>c</pdfaType:prefix>
+ <pdfaType:description>RGB Colour</pdfaType:description>
+ <pdfaType:field>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <pdfaField:name>B</pdfaField:name>
+ <pdfaField:valueType>Integer</pdfaField:valueType>
+ <pdfaField:description>Blue component</pdfaField:description>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <pdfaField:name>G</pdfaField:name>
+ <pdfaField:valueType>Integer</pdfaField:valueType>
+ <pdfaField:description>Green component</pdfaField:description>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <pdfaField:name>R</pdfaField:name>
+ <pdfaField:valueType>Integer</pdfaField:valueType>
+ <pdfaField:description>Red component</pdfaField:description>
+ </rdf:li>
+ </rdf:Seq>
+ </pdfaType:field>
+ </rdf:li>
+ </rdf:Seq>
+ </pdfaSchema:valueType>
+ </rdf:li>
+ </rdf:Bag>
+ </pdfaExtension:schemas>
+</rdf:Description>⟧
+\par\endgroup
+
You probably will not need defining your own value types,
so in most cases you should omit the fifth argument to ⟦add_namespace⟧.
If you do define a new value type, you can specify its prefix if it differs
@@ -182,6 +244,8 @@ All metadata will be written to the PDF file uncompressed.
The ⟦*\metadatamodification⟧ setting controls whether XMP packets will be
marked as read-only (value~0; default) or writeable (value~1).
Writeable XMP packets will be padded with about 2\thinspace kB of whitespace.
+You can prohibit writing metadata altogether
+by specifying ⟦*\writedocumentmetadata = 0⟧.
If the document-level metadata contains values in the ⟦pdfaid⟧ namespace,
metadata extension schemas will be appended to the document-level metadata
diff --git a/macros/luatex/generic/minim-xmp/minim-xmp.lua b/macros/luatex/generic/minim-xmp/minim-xmp.lua
index ba01344ccd..95275aef35 100644
--- a/macros/luatex/generic/minim-xmp/minim-xmp.lua
+++ b/macros/luatex/generic/minim-xmp/minim-xmp.lua
@@ -2,6 +2,7 @@
local M = { }
local alloc = require('minim-alloc')
alloc.remember('minim-xmp')
+local cb = require('minim-callbacks')
local function sorted_pairs(t)
local keys, i = { }, 0
@@ -506,11 +507,13 @@ M.aliases = {
version = { 'xmpMM:VersionID' },
}
-callback.register('finish_pdffile', function()
+cb.register('finish_pdffile', function()
+ if tex.count['writedocumentmetadata'] == 0 then return end
if #XMP > 1 then
alloc.err('Not all metadata has been written out.')
XMP = { [1] = XMP[1] }
- elseif #XMP > 0 then
+ end
+ if #XMP > 0 then
local metadata_obj = M.write_metadata()
local catalog = pdf.getcatalog() or ''
pdf.setcatalog(catalog..string.format('/Metadata %s 0 R', metadata_obj))
diff --git a/macros/luatex/generic/minim-xmp/minim-xmp.pdf b/macros/luatex/generic/minim-xmp/minim-xmp.pdf
index fdf048a857..11c86a9d67 100644
--- a/macros/luatex/generic/minim-xmp/minim-xmp.pdf
+++ b/macros/luatex/generic/minim-xmp/minim-xmp.pdf
Binary files differ
diff --git a/macros/luatex/generic/minim-xmp/minim-xmp.tex b/macros/luatex/generic/minim-xmp/minim-xmp.tex
index 2ec4adc895..2548ad857b 100644
--- a/macros/luatex/generic/minim-xmp/minim-xmp.tex
+++ b/macros/luatex/generic/minim-xmp/minim-xmp.tex
@@ -19,6 +19,10 @@
\newcount \metadataseparator
\metadataseparator = `\;
+% \writedocumentmetadata
+\newcount \writedocumentmetadata
+\writedocumentmetadata = 1
+
% Defined from the lua side:
%
% \getmetadata ns:key
diff --git a/macros/luatex/generic/minim/README b/macros/luatex/generic/minim/README
index 4a16b4e801..31c34b28bf 100644
--- a/macros/luatex/generic/minim/README
+++ b/macros/luatex/generic/minim/README
@@ -1,4 +1,4 @@
-Version: 2021/1.0
+Version: 2022/1.1
SUMMARY
@@ -29,9 +29,33 @@ For the sake of completeness, this manual also includes the documentation of
the packages mentioned above.
+HISTORY
+
+2022/1.1 (3/3/2022)
+
+ Major changes:
+
+ * Updated the allocation mechanism, distinguishing the new_<register>()
+ allocators from the new registernumber() function.
+ * Included a new (pgf-compatible) pdf resource manager.
+
+ Minor changes:
+
+ * Updated the manual to reflect the latest versions of the other minims.
+ * Included a few helper functions moved in from minim-pdf.
+ * Fixed a bug where the primitive callback.register() could not remove
+ list callbacks (the module’s M.register() still cannot).
+ * Added an M.deregister() function for callbacks.
+ * Added a uselanguage callback.
+
+2021/1.0 (1/6/2021)
+
+ This was the original release.
+
+
COPYING
-(c) 2021 Esger Renkema
+(c) 2022 Esger Renkema, Michal Vlasák
These files may be distributed under the terms of the European Union Public
Licence (EUPL) version 1.2 or later. A copy can be obtained at:
diff --git a/macros/luatex/generic/minim/minim-alloc.doc b/macros/luatex/generic/minim/minim-alloc.doc
index fa08988bf4..edf268c81d 100644
--- a/macros/luatex/generic/minim/minim-alloc.doc
+++ b/macros/luatex/generic/minim/minim-alloc.doc
@@ -1,6 +1,8 @@
\input minim-doc.sty
+\chapter Programming macros
+
This chapter describes the programming helper modules on which all the above
modules depend. It mainly concerns register allocation, callback management and
format file inclusion.
@@ -78,15 +80,8 @@ name, the number of that register will be returned. You will need this when you
want to allow your lua code to be included in a format file; it has nothing to
do with the tex-side ⟦\countdef⟧ and the like.
-For the new allocation macros listed above and (as a special case) for
-⟦\newbox⟧,
-after saying ⟦\newwhatsit\name⟧, the call ⟦M.new_whatsit('name')⟧ will return
-the number of ⟦\name⟧. For the other (older) allocation macros, this is not the
-case. After all, because of the ⟦\countdef⟧ etc. included in ⟦\newcount⟧ etc.
-you can already use ⟦tex.count['name']⟧ etc. for retrieving tex-side
-allocations from lua. The exceptions to this are ⟦\newbox⟧, which is why it is
-included with the new macros, and ⟦\newattribute⟧, for which you can use both
-methods.
+If you want to access from lua a register defined in tex, the function
+⟦*M.registernumber('name')⟧ will give you the index of register ⟦\name⟧.
Besides ⟦\newluachunkname\name⟧, you can also use
⟦*\setluachunkname \name {actual name}⟧
@@ -102,21 +97,24 @@ take no parameters.
As noted at the beginning of this chapter, the callback functions are only
available after you say ⟦local C = require('minim-callbacks')⟧.
-This module will override the primitive callback functions with its own
-⟦*C.register⟧, ⟦*C.find⟧ and ⟦*C.list⟧; the original primitive functions can be
-found in the ⟦*C.primitives⟧ table.
-
The simple function of this module is allowing multiple callbacks to co-exist.
Different callbacks call for different implementations, and some callbacks can
-only contain a single function.
-Any callbacks that are already assigned before loading this module will be
-preserved; this includes the ltluatex callback mechanism if it has already been
-installed.
+only contain a single function. Its interface matches the primitive interface,
+with ⟦*C.register(callback, fn)⟧, ⟦*C.find(callback)⟧ and ⟦*C.list()⟧ taking
+the same arguments. In addition to these, ⟦*C.deregister(fn)⟧ will allow you to
+remove a callback. This is necessary when you want to remove a callback from
+a list or from the bottom of a stack. The ⟦fn⟧ variable should point to the
+same object.
-You can create your own callbacks with ⟦*C.new_callback(name, type)⟧. The ⟦type⟧
-should be one of the types mentioned below or ⟦'single'⟧ for callbacks that
-allow only one function. If the ⟦name⟧ is that of a primitive callback, new
-registrations will target your new callback.
+Any callbacks that are already assigned before loading this module will be
+preserved and the primitive callback interface is still available, though
+callbacks registered through the latter will actually use the new functions.
+Ltluatex may be loaded either before or after this module.
+
+You can create your own callbacks with ⟦*C.new_callback(name, type)⟧. The
+⟦type⟧ should be one of the types mentioned below or ⟦'single'⟧ for callbacks
+that allow only one function. If the ⟦name⟧ is that of a primitive callback,
+new registrations will target your new callback.
You can call the new callback with ⟦*C.call_callback(name, ...)⟧, adding any
number of parameters.
@@ -131,7 +129,6 @@ Callbacks of this type are
⟦vpack_filter⟧,
⟦pre_output_filter⟧ and
⟦mlist_to_mlist⟧.
-There is no way of unregistering callbacks of this type.
Similarly, for the ⟦*data⟧ callbacks
⟦process_input_buffer⟧,
@@ -153,6 +150,7 @@ will be called. These are
Register ⟦nil⟧ at the callback to pop a function off the stack.
Finally, for the ⟦*simple⟧ callbacks
+⟦uselanguage⟧,
⟦contribute_filter⟧,
⟦pre_dump⟧,
⟦wrapup_run⟧,
@@ -164,21 +162,50 @@ Finally, for the ⟦*simple⟧ callbacks
⟦process_rule⟧.
all registered functions are called in order with the same arguments.
-The new ⟦*mlist_to_mlist⟧ callback is called before ⟦mlist_to_hlist⟧ and should
-not convert noads to nodes.
+Two callbacks are new:
+the new ⟦*mlist_to_mlist⟧ callback is called before ⟦mlist_to_hlist⟧ and should
+not convert noads to nodes, while the ⟦*uselanguage⟧ callback is called from
+⟦\uselanguage⟧.
If you create a new callback with a number for a name, that callback will
replace the ⟦*process_rule⟧ callback when its number matches the index property
of the rule.
+\section Programming helpers
+
+Optional keyword arguments to tex macros can be defined with help of
+⟦*M.options_scanner()⟧. An example from the definition of minim-pdf’s
+⟦\outline⟧:
+
+⟦ local s = M.options_scanner()
+ :keyword('open', 'closed')
+ :string('dest')
+ :scan()
+ s.title = token.scan_string()
+ M.add_bookmark(s)⟧
+
+Here, the ⟦keyword⟧ function adds two opposite keywords: if one is present, its
+value will be set to ⟦true⟧ and the other’s to ⟦false⟧ (the second keyword is
+optional). The ⟦string⟧ function stores the result of ⟦token.scan_string⟧ under
+its key. Of the same form you have ⟦int⟧, ⟦glue⟧, ⟦dimen⟧, ⟦argument⟧, ⟦word⟧
+and ⟦list⟧. All these take an optional second argument: if ⟦true⟧ then the
+keyword can be repeated and its values will be stored as a list.
+
+The ⟦scan⟧ function, finally, scans all keywords, which may appear in any
+order. You can give it a table with default values. In the example given above,
+the argument ⟦s⟧ to ⟦M.add_bookmark⟧ will consist of a table with at most the
+following entries: ⟦open⟧, ⟦closed⟧, ⟦dest⟧ and ⟦title⟧, though entries whose
+keywords do not occur will not be present.
+
+This function is particularly useful when used together with
+⟦*M.luadef('csname', function, ...)⟧, which defines primitive-like tex macros
+from lua. There, ⟦function⟧ can be any function (it will be assigned a lua
+function register) and at the place of the dots you may append ⟦'protected'⟧
+and/or ⟦'global'⟧.
-\section Miscellaneous functions
-This section describes functions and macros that are internal to this package,
-but might be of general usefulness.
+\section Miscellaneous functions
-For instance, you might find the function ⟦*M.table_to_text(table)⟧ useful when
-debugging lua code.
The small functions
⟦*M.msg(...)⟧, ⟦*M.log(...)⟧ and ⟦*M.err(...)⟧
include a call to ⟦M.string.format⟧;
@@ -186,14 +213,24 @@ additionally,
⟦*M.amsg(...)⟧ and ⟦*M.alog(...)⟧
do not start a new line.
-Very useful is ⟦*M.luadef('csname', function, ...)⟧ for defining primitive-like
-tex macros from lua: ⟦function⟧ can be any function (it will be assigned a lua
-function register) and at the place of the dots you may append ⟦'protected'⟧
-and/or ⟦'global'⟧.
-
Both ⟦*M.unset⟧ and ⟦*\unset⟧ contain the value ⟦-0x7FFFFFFF⟧ that can be used
for clearing attributes.
+When writing data to pdf literals, ⟦*M.pdf_string(...)⟧,
+⟦*M.pdf_name(...)⟧ and ⟦*M.pdf_date(...)⟧ may be useful: they produce strings
+that can be written to the pdf directly. Surrounding ⟦<>⟧ or ⟦()⟧ characters or
+a leading ⟦/⟧ will be included in the return value. The ⟦M.pdf_date⟧ function
+expects a value of the form ⟦yyyy[-mm[-dd]]⟧ and returns a date of the form
+⟦(D:yyyymmdd)⟧.
+The function ⟦M.pdf_string⟧ is also available to tex through the macro
+⟦*\pdfstring⟧.
+
+Finally the function ⟦*M.table_to_text(table)⟧ may be useful when debugging lua
+code: it dumps a table as a (lua-readable) string. Cyclic references will spin
+in into an eternal loop, however.
+
+\section Miscellaneous helper macros
+
On the tex side, ⟦*\voidbox⟧, ⟦*\ignore⟧, ⟦*\spacechar⟧, ⟦*\unbrace⟧,
⟦*\firstoftwo⟧ and ⟦*\secondoftwo⟧ should be self-explanatory and
uncontroversial additions. For looking ahead, you can use
diff --git a/macros/luatex/generic/minim/minim-alloc.lua b/macros/luatex/generic/minim/minim-alloc.lua
index c99ca2c73d..8f6a69ed0d 100644
--- a/macros/luatex/generic/minim/minim-alloc.lua
+++ b/macros/luatex/generic/minim/minim-alloc.lua
@@ -27,6 +27,131 @@ function M.err (...)
tex.error(string.format(...))
end
+-- 1 pdf helpers
+
+local function insert_formatted(t, ...)
+ table.insert(t, string.format(...))
+end
+
+local function hexify(c)
+ return string.format('#%02x', c:byte())
+end
+function M.pdf_name(n)
+ return '/'..n
+ :gsub('[][(){}<>#%%/\\]', hexify)
+ :gsub('%G', hexify)
+end
+
+-- re-encode to utf-16
+local function pdf_hex_string(text)
+ local str = { [1] = '<feff' }
+ for i in text:utfvalues() do
+ if i <= 0xffff then
+ insert_formatted(str, '%04x', i)
+ else
+ i = i - 0x10000
+ m = math.floor(i/0x400) + 0xd800
+ n = ( i % 0x400 ) + 0xdc00
+ insert_formatted(str, '%04x%04x', m, n)
+ end
+ end
+ table.insert(str, '>')
+ return table.concat(str,'')
+end
+local function octify(c)
+ return string.format('\\%03o', c:byte())
+end
+function M.pdf_string(text)
+ -- do note that \ddd is decimal in lua, octal in pdf
+ if text:match('[^%g\009\010\13 ]') then
+ return pdf_hex_string(text)
+ else
+ return string.format('(%s)', text
+ :gsub('[()\\]','\\%0')
+ :gsub('%c', octify))
+ end
+end
+
+-- try and produce a date of the format (D:YYYYMMDD)
+function M.pdf_date(text)
+ local y, m, d = string.match(text, '^([12][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)$')
+ if not y then d, m, y = string.match(text, '^([0-9][0-9]?)-([0-9][0-9]?)-([12][0-9][0-9][0-9])$') end
+ if y then
+ return string.format('(D:%4d%02d%02d)', y, m, d)
+ else
+ return string.format('(D:%s)', text)
+ end
+end
+
+-- 1 scanning helpers
+
+local function make_scanner(fn)
+ return function(this, name, multiple)
+ this.scanners[name] = multiple and function()
+ this.res[name] = this.res[name] or { }
+ table.insert(this.res[name], fn())
+ end or function()
+ this.scanners[name] = nil
+ this.res[name] = fn()
+ end
+ return this
+ end
+end
+
+function M.options_scanner()
+ return { scanners = { }, res = { },
+ int = make_scanner(token.scan_int),
+ glue = make_scanner(token.scan_glue),
+ dimen = make_scanner(token.scan_dimen),
+ string = make_scanner(token.scan_string),
+ argument = make_scanner(token.scan_argument),
+ word = make_scanner(token.scan_word),
+ list = make_scanner(token.scan_list),
+ keyword = function(this, name, opposite)
+ this.scanners[name] = function()
+ this.res[name] = true
+ if opposite then this.res[opposite] = false end
+ end
+ if opposite then
+ this.scanners[opposite] = function()
+ this.res[name] = false
+ this.res[opposite] = true
+ end
+ end
+ return this
+ end,
+ table = function(this, name)
+ this.scanners[name] = function()
+ this.res[name] = this.res[name] or { }
+ this.res[name][token.scan_string()] = token.scan_string()
+ end
+ return this
+ end,
+ subtable = function(this, name)
+ this.scanners[name] = function()
+ this.res[name] = this.res[name] or { }
+ local sub = token.scan_string();
+ this.res[name][sub] = this.res[name][sub] or { }
+ this.res[name][sub][token.scan_string()] = token.scan_string()
+ end
+ return this
+ end,
+ scan = function(this, defaults)
+ this.res = defaults or this.res
+ repeat
+ local matched = false
+ for name, scanner in pairs(this.scanners) do
+ if token.scan_keyword(name) then
+ matched = true
+ scanner()
+ end
+ end
+ until not matched
+ return this.res
+ end
+ }
+end
+
-- 1 saving modules and tables
local tables = package.loaded['minim-saved-tables']
@@ -70,7 +195,7 @@ function M.table_to_text (tbl)
return '{ ' .. table.concat (r,', ') .. ' }'
end
-require('minim-callbacks')
+local cb = require('minim-callbacks')
M.remember('minim-callbacks')
M.remember('minim-alloc')
@@ -79,11 +204,23 @@ M.remember('minim-alloc')
-- like \unset
M.unset = -0x7FFFFFFF
-local allocations = M.saved_table ('minim:allocations')
+-- works for both \chardef and the likes of \countdef
+function M.registernumber(csname)
+ if token.is_defined(csname) then
+ return token.create(csname).index
+ else
+ return -- would return 0 otherwise
+ end
+end
+
+-- we need remember lua-made allocations in the format file, since otherwise,
+-- a different register will be reserved when the lua code is seen again in the
+-- actual run.
+local allocations = M.saved_table('minim lua-side allocations')
-local function make_alloc_new (fname, globcount)
+local function make_alloc_new(fname, globcount)
allocations[fname] = allocations[fname] or { }
- M['new_'..fname] = function (id)
+ M['new_'..fname] = function(id)
local nr
if id and allocations[fname][id] then
nr = allocations[fname][id]
@@ -108,7 +245,7 @@ make_alloc_new ('userrule' , 'e@alloc@rule@count' )
-- We need different allocation functions for the older registers, because
-- etex’s global allocation macros are off-by-one w.r.t. all other.
--
-local function make_alloc_old (fname, globcount, loccount)
+local function make_alloc_old(fname, globcount)
allocations[fname] = allocations[fname] or { }
M['new_'..fname] = function (id)
local nr
@@ -122,21 +259,16 @@ local function make_alloc_old (fname, globcount, loccount)
end
return nr
end
- M['local_'..fname] = function ()
- local nr = tex.count[loccount] - 1
- tex.setcount(loccount, nr)
- return nr
- end
end
-- existing allocation counters
-make_alloc_old ('count', 260, 270 )
-make_alloc_old ('dimen', 261, 271 )
-make_alloc_old ('skip', 262, 272 )
-make_alloc_old ('muskip', 263, 273 )
-make_alloc_old ('box', 264, 274 )
-make_alloc_old ('toks', 265, 275 )
-make_alloc_old ('marks', 266, 276 )
+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 )
function M.luadef (csname, fn, ...)
local nr = M.new_function(csname)
@@ -144,8 +276,19 @@ function M.luadef (csname, fn, ...)
token.set_lua(csname, nr, ...)
end
-M.luadef ('minim:rememberalloc', function()
- allocations[token.scan_string()][token.scan_string()] = tex.count['allocationnumber']
+-- the current file
+M.luadef('minim:currentfile', function()
+ tex.sprint((status.filename:gsub('^.*/', '')))
+end)
+
+-- make pdf_string() available as \pdfstring{...}
+M.luadef('pdfstring', function() M.pdf_string(token.scan_string()) end)
+
+-- uselanguage hook callback
+cb.new_callback('uselanguage', 'simple')
+M.luadef('minim:uselanguagecallback', function()
+ local langname = token.scan_string()
+ cb.call_callback('uselanguage', langname)
end)
-- 1 dumping information to the format file
@@ -188,7 +331,7 @@ local function dump_saved_tables()
lua.bytecode[saved_tables_bytecode] = load(saved_tables)
end
-callback.register ('pre_dump', dump_saved_tables)
+cb.register ('pre_dump', dump_saved_tables)
--
diff --git a/macros/luatex/generic/minim/minim-alloc.tex b/macros/luatex/generic/minim/minim-alloc.tex
index 66f08c9045..8752c2e336 100644
--- a/macros/luatex/generic/minim/minim-alloc.tex
+++ b/macros/luatex/generic/minim/minim-alloc.tex
@@ -62,58 +62,48 @@
% 1 ltluatex compatibility
+% \ProvidesFile identifies most problematic files
+\ifdefined \ProvidesFile
+ \let\minim:providesfile=\ProvidesFile\else
+ \def\minim:providesfile#1#2[#3]{%
+ \wlog{Loading file #1 version #3.}}\fi
+\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
-\def\minim:ltltx:file{ltluatex.tex}
-\let\ProvidesFile:saved=\ProvidesFile
-\def\ProvidesFile#1#2[#3]{%
- \ifdefined\ProvidesFile:saved \ProvidesFile:saved{#1}#2[#3]\fi
- \def\minim:ltltx:arg{#1}%
- \ifx\minim:ltltx:file\minim:ltltx:arg
- \let\ProvidesFile=\ProvidesFile:saved
- \let\endinput:saved=\endinput
- \protected\edef\endinput{%
- \minim:ltltx:fix{e@alloc@attribute@count}%
- \minim:ltltx:fix{e@alloc@whatsit@count}%
- \minim:ltltx:fix{e@alloc@bytecode@count}%
- \minim:ltltx:fix{e@alloc@luafunction@count}%
- \minim:ltltx:fix{e@alloc@luachunk@count}%
- \minim:ltltx:fix{e@alloc@ccodetable@count}%
- % also patch in remembering allocations to lua
- \alloc:patch \noexpand\attribute
- \alloc:patch \noexpand\whatsit
- \alloc:patch \noexpand\luabytecode
- \alloc:patch \noexpand\luachunkname
- \alloc:patch \noexpand\catcodetable
- \alloc:patch \noexpand\userrule
- % restore register allocation functions
- \directlua{ local C = require('minim-callbacks')
- callback.register, callback.find, callback.list
- = C.register, C.find, C.list }%
- \let\noexpand\endinput=\noexpand\endinput:saved
- \noexpand\endinput}\fi}
+\expandafter\def\csname minim:intercept:ltluatex.tex\endcsname
+ {\wlog{minim: applying ltluatex patches.}%
+ \edef\endinput{\let\noexpand\endinput=\noexpand\minim:endinput
+ \minim:ltltx:fix{e@alloc@attribute@count}%
+ \minim:ltltx:fix{e@alloc@whatsit@count}%
+ \minim:ltltx:fix{e@alloc@bytecode@count}%
+ \minim:ltltx:fix{e@alloc@luafunction@count}%
+ \minim:ltltx:fix{e@alloc@luachunk@count}%
+ \minim:ltltx:fix{e@alloc@ccodetable@count}%
+ % restore register allocation functions
+ \noexpand\directlua{ callback.register =
+ require('minim-callbacks').primitiveregister }%
+ \noexpand\endinput}}
+\let\minim:endinput = \endinput
\def\minim:ltltx:fix#1{%
\ifnum0<\the\csname#1\endcsname
\wlog{Restoring \csname#1\endcsname to previous value \the\csname#1\endcsname}%
\expandafter\noexpand\csname#1\endcsname =\the\csname#1\endcsname\relax\fi}
-\protected\def\alloc:patch#1{%
- \expandafter\expandafter\expandafter\let
- \expandafter\expandafter\csname\csstring#1:ltluatex:new\endcsname
- \csname new\csstring#1\endcsname
- \expandafter\edef\csname new\csstring#1\endcsname##1{%
- \noexpand\minim:rememberalloc{\csstring#1}{\noexpand\csstring##1}%
- \expandafter\noexpand\csname\csstring#1:ltluatex:new\endcsname{##1}}}
% 1 allocation
% use global allocation (see etex.src)
-\let\newcount = \globcount
-\let\newdimen = \globdimen
-\let\newskip = \globskip
-\let\newmuskip = \globmuskip
-\let\newbox = \globbox
-\let\newtoks = \globtoks
-\let\newmarks = \globmarks
+\ifdefined \globcount
+ \let\newcount = \globcount
+ \let\newdimen = \globdimen
+ \let\newskip = \globskip
+ \let\newmuskip = \globmuskip
+ \let\newbox = \globbox
+ \let\newtoks = \globtoks
+ \let\newmarks = \globmarks
+\fi
% new allocation macros
@@ -121,8 +111,7 @@
\unless\ifcsname#3\endcsname
\expandafter\newcount \csname#3\endcsname
\csname#3\endcsname 0\fi
- \ifcsname new\csstring#1\endcsname
- \alloc:patch#1\else
+ \unless\ifcsname new\csstring#1\endcsname
\expandafter\edef\csname new\csstring#1\endcsname{%
\noexpand\minim:alloc\noexpand#1\noexpand#2%
\expandafter\noexpand\csname#3\endcsname}\fi}
@@ -131,9 +120,7 @@
\global\advance#31%
\allocationnumber#3%
\global#2#4\allocationnumber
- \wlog{\string#4=\string#1\the\allocationnumber}%
- % minim:rememberalloc will be defined from lua side
- \minim:rememberalloc{\csstring#1}{\csstring#4}}
+ \wlog{\string#4=\string#1\the\allocationnumber}}
% all names and counters below are identical to those from ltluatex
% note: we cannot use \newluafunction, or ltluatex will not load
@@ -147,7 +134,10 @@
% initialise new catcode tables
\def\catcode:chardef#1#2{\chardef#1#2\initcatcodetable#2}
-\csname e@alloc@ccodetable@count\endcsname = 4 % because ltluatex allocates 4...
+% ltluatex initialises the catcode tables 1–4, so we make sure not to claim
+% those ourselves:
+\ifnum\csname e@alloc@ccodetable@count\endcsname = 0
+ \csname e@alloc@ccodetable@count\endcsname = 4 \fi
% set initial chunk name value
\def\chardef:chunk#1#2{\chardef#1#2\directlua{lua.name[\the#2]='\csstring#1'}}
@@ -156,17 +146,22 @@
\protected\def\setluachunkname#1#2{\newluachunkname#1%
\directlua{lua.name[\the#1]='\luaescapestring{#2}'}}
-% patch in remembering box allocations
-\def\minim:boxchardef#1{%
- \minim:rememberalloc{box}{\csstring#1}%
- \mathchardef#1}
-\edef\newbox{%
- \expandafter\noexpand\csname et@xglob\endcsname
- 4\box\noexpand\minim:boxchardef}
-
% undefine our helper functions
\let\alloc:makenew=\undefined
+% patch in our callback to \uselanguage
+% \minim:uselanguagecallback is defined from Lua
+\ifcsname uselanguage@hook\endcsname
+ \expandafter\let
+ \expandafter\minim:uselanguagehook
+ \lastnamedcs \fi
+\expandafter\edef\csname uselanguage@hook\endcsname#1{%
+ % our uselanguage callback
+ \noexpand\minim:uselanguagecallback{#1}%
+ % previous definitions
+ \ifdefined\minim:uselanguagehook
+ \noexpand\minim:uselanguagehook{#1}\fi}
+
% 1 format file compatibility
% all other work is done at the lua end
diff --git a/macros/luatex/generic/minim/minim-callbacks.lua b/macros/luatex/generic/minim/minim-callbacks.lua
index 3ac075dc01..af3db59e24 100644
--- a/macros/luatex/generic/minim/minim-callbacks.lua
+++ b/macros/luatex/generic/minim/minim-callbacks.lua
@@ -1,41 +1,51 @@
local M = {}
-local function log(msg, ...)
+local function log(...) end
+local function alog(...) end
+local function do_log(msg, ...)
texio.write_nl('log', string.format(msg, ...))
end
-- 1 capturing the callback mechanism
--- if ltluatex is loaded, we must get callback.register back
-if luatexbase ~= nil then
- local luatex_base = luatexbase
- luatexbase.uninstall ()
- luatexbase = luatex_base
-end
-
local primitives = { }
M.primitives = primitives
primitives.register = callback.register
primitives.find = callback.find
primitives.list = callback.list
-local own_callbacks = {}
-local callback_lists = {}
+-- use the ltluatex functions if needed
+local primitive_register = primitives.register
+if luatexbase then
+ primitive_register = function(cb, f)
+ if f == nil then
+ luatexbase.remove_from_callback(cb, 'minim callback')
+ elseif f == false then
+ luatexbase.disable_callback(cb)
+ else
+ luatexbase.add_to_callback(cb, f, 'minim callback')
+ end
+ end
+end
+
+
+local own_callbacks = {}
+local callback_lists = {}
local callback_stacks = {}
-- 1 finding callbacks
-function M.find (name)
- local f = own_callbacks[name]
+function M.find(cb)
+ local f = own_callbacks[cb]
if f == nil then
- return primitives.find(name)
+ return primitives.find(cb)
else
return f
end
end
-function M.list (name)
+function M.list()
local t = {}
for n,f in pairs(callback_lists) do
if f then
@@ -44,14 +54,16 @@ function M.list (name)
t[n] = false
end
end
- for n,f in pairs(own_callbacks) do
+ -- no stack callbacks, since the active callback is in one of the two below
+ for n,f in pairs(primitives.list()) do
if f then
t[n] = t[n] or true
else
t[n] = t[n] or false
end
end
- for n,f in pairs(primitives.list()) do
+ -- this might obscure primitive callbacks (this is intended)
+ for n,f in pairs(own_callbacks) do
if f then
t[n] = t[n] or true
else
@@ -63,42 +75,73 @@ end
-- 1 registering callbacks
-local function register_simple (cb,f)
+local function register_simple(cb,f)
-- prefer user-defined callbacks over built-in
- local x = own_callbacks[cb]
- if x == nil then
- return primitives.register (cb, f)
+ local own = own_callbacks[cb]
+ log ('callback %s: %s (%s)', f == nil and 'removed' or f and 'set' or 'disabled',
+ cb, own == nil and 'primitive' or 'user-defined')
+ if own == nil then -- may be set to ‘false’
+ return primitive_register(cb, f)
else
- -- default to false because nil would delete the callback itself
- own_callbacks[cb] = f or false
+ own_callbacks[cb] = f or false -- ‘nil’ would delete the callback
return -1
end
end
-- will be redefined later
-local function announce_callback(cb, f) end
-function M.register (cb, f)
- announce_callback(cb, f)
+function M.register(cb, f)
local list = callback_lists[cb]
+ if list then
+ if f == nil then
+ return tex.error('Use ‘deregister’ for removing list callbacks')
+ else
+ list[#list+1] = f
+ log('callback set: %s (#%d on list)', cb, #list)
+ return -2
+ end
+ end
local stack = callback_stacks[cb]
if stack then
if f == nil then -- pop
local p = stack[#stack]
stack[#stack] = nil
- return register_simple (cb,p)
+ return register_simple(cb,p)
else -- push
- stack[#stack+1] = M.find (cb)
- return register_simple (cb,f)
+ stack[#stack+1] = M.find(cb)
+ return register_simple(cb,f)
end
- elseif list ~= nil then
- list[#list+1] = f
- return -2
- else
- return register_simple (cb,f)
end
+ return register_simple(cb, f)
end
+function M.deregister(cb, f)
+ local list = callback_lists[cb]
+ if list then
+ for i,g in ipairs(list) do
+ if f == g then
+ log('callback removed: %s (#%d on list)', cb, i)
+ table.remove(list, i)
+ return true, -2
+ end
+ end
+ return false
+ end
+ local stack = callback_stacks[cb]
+ if stack then
+ for i,g in ipairs(stack) do
+ if f == g then
+ log('callback removed: %s (#%d on stack)', cb, i)
+ table.remove(stack, i)
+ return true, -3
+ end
+ end
+ -- no return: fall through to next
+ end
+ if f == M.find(cb) then
+ return true, register_simple(cb, nil)
+ end
+end
-- 1 lists of callback functions
@@ -169,22 +212,40 @@ function M.call_callback (name, ...)
end
end
+-- 1 replace the primitive registering
+
+-- TODO: preserve return values
+local primitively_registered = { }
+function M.primitiveregister(cb, f)
+ local rv = false
+ if f == nil then
+ f = primitively_registered[cb]
+ if f == nil then
+ rv = M.register(cb)
+ else
+ _, rv = M.deregister(cb, f)
+ end
+ else
+ rv = M.register(cb, f)
+ end
+ alog(' through primitive interface')
+ primitively_registered[cb] = f
+ return rv
+end
+
+
-- 1 initialisation
-- save all registered callbacks
-local saved = {}
-for n,s in pairs(primitives.list()) do
- if s then
- log('save callback: %s', n)
- saved[n] = callback.find(n)
+if not luatexbase then
+ for n,s in pairs(primitives.list()) do
+ if s then
+ do_log('save callback: %s', n)
+ primitively_registered[n] = primitives.find(n)
+ end
end
end
--- replace the primitive registering
-callback.register = M.register
-callback.find = M.find
-callback.list = M.list
-
-- string processing callbacks
register_list ('process_input_buffer', call_list_data)
register_list ('process_output_buffer', call_list_data)
@@ -202,7 +263,7 @@ register_list ('pre_output_filter', call_list_node)
M.new_callback ('mlist_to_mlist', 'node')
M.new_callback ('mlist_to_hlist', 'stack')
M.register ('mlist_to_hlist', node.mlist_to_hlist )
-primitives.register ('mlist_to_hlist', function (head, ...)
+primitive_register ('mlist_to_hlist', function (head, ...)
local newhead = M.call_callback ('mlist_to_mlist', head, ...)
if newhead ~= true then
head = newhead or head
@@ -232,7 +293,7 @@ stack_callback ('build_page_insert')
-- process_rule
M.new_callback ('process_rule', 'simple')
-primitives.register ('process_rule', function (rule, ...)
+primitive_register ('process_rule', function (rule, ...)
local p = own_callbacks[rule.index]
if p then
p (rule, ...)
@@ -242,23 +303,23 @@ primitives.register ('process_rule', function (rule, ...)
end)
-- restore all registered callbacks
-for n,f in pairs(saved) do
- log('restore callback: %s', n)
- M.register (n,f)
+for n,f in pairs(primitively_registered) do
+ do_log('restore callback: %s', n)
+ M.primitiveregister (n,f)
end
saved = nil
+--
-local function announce_callback(cb, f)
- if f then
- log('callback added: %s', cb)
- else
- log('callback removed: %s', cb)
- end
-end
-
+-- replace primitive callbacks
+callback.find = M.find
+callback.list = M.list
+callback.register = M.primitiveregister
---
+log = do_log
+local function alog(msg, ...)
+ texio.write('log', string.format(msg, ...))
+end
return M
diff --git a/macros/luatex/generic/minim/minim-doc.sty b/macros/luatex/generic/minim/minim-doc.sty
index 1d3c2d4cea..93e770153b 100644
--- a/macros/luatex/generic/minim/minim-doc.sty
+++ b/macros/luatex/generic/minim/minim-doc.sty
@@ -6,6 +6,7 @@
\expandafter\endinput\fi
\input minim
+%\decompressedpdf
\pdfalevel 3a
\overfullrule = 0pt
@@ -15,7 +16,6 @@
\pdfvariable horigin 0pt
\pdfvariable vorigin 0pt
\frenchspacing
-\raggedbottom
\uselanguage{ukenglish}
\hyphenation {
@@ -38,27 +38,27 @@
% 1 fonts
\input luaotfload.sty
-\font\tenrm {Latin Modern Roman:script=latn;protrusion=default} at 10pt
-\font\tenbf {Latin Modern Roman/B:script=latn} at 10pt
-\font\tenit {Latin Modern Roman/I:script=latn} at 10pt
-\font\tentt {Latin Modern Mono:script=latn;+smcp} at 10pt
-\font\title {Latin Modern Roman/B:script=latn} at 12pt
-\font\Title {Latin Modern Roman/B:script=latn} at 16pt
-
-\font\tenmath {Latin Modern Math:mode=base;script=math;ssty=0} at 10pt
-\font\tenmaths {Latin Modern Math:mode=base;script=math;ssty=1} at 7pt
-\font\tenmathss {Latin Modern Math:mode=base;script=math;ssty=2} at 5pt
-\textfont 0 = \tenmath
-\scriptfont 0 = \tenmaths
-\scriptscriptfont 0 = \tenmathss
-
-\font\altmath {Tex Gyre Pagella Math:mode=base;script=math;ssty=0} at 10pt
-\font\altmaths {Tex Gyre Pagella Math:mode=base;script=math;ssty=1} at 7pt
-\font\altmathss {Tex Gyre Pagella Math:mode=base;script=math;ssty=2} at 5pt
+\font\tenrm {Latin Modern Roman:script=latn;protrusion=default;} at 10pt
+\font\tenbf {Latin Modern Roman/B:script=latn;} at 10pt
+\font\tenit {Latin Modern Roman/I:script=latn;} at 10pt
+\font\tentt {Latin Modern Mono:script=latn;+smcp;} at 10pt
+\font\title {Latin Modern Roman/B:script=latn;} at 12pt
+\font\Title {Latin Modern Roman/B:script=latn;} at 16pt
+
+\font\altmath {Tex Gyre Pagella Math:mode=base;script=math;ssty=0;} at 10pt
+\font\altmaths {Tex Gyre Pagella Math:mode=base;script=math;ssty=1;} at 7pt
+\font\altmathss {Tex Gyre Pagella Math:mode=base;script=math;ssty=2;} at 5pt
\textfont 1 = \altmath
\scriptfont 1 = \altmaths
\scriptscriptfont 1 = \altmathss
+\font\tenmath {Latin Modern Math:mode=base;script=math;ssty=0;} at 10pt
+\font\tenmaths {Latin Modern Math:mode=base;script=math;ssty=1;} at 7pt
+\font\tenmathss {Latin Modern Math:mode=base;script=math;ssty=2;} at 5pt
+\textfont 0 = \tenmath
+\scriptfont 0 = \tenmaths
+\scriptscriptfont 0 = \tenmathss
+
\expandglyphsinfont \tenrm 40 25 5
\protrudechars=2 \adjustspacing=2
\tenrm
@@ -67,21 +67,31 @@
% 1 code typesetting
-\def\red#1{\quitvmode\pdfextension
- literal{0.75 0 0 rg}{#1}\pdfextension literal{0 g}}
+\chardef\thecolourstack = \pdffeedback colorstackinit page {0 g 0 G}
+\def\pushcolour#1{\pdfextension colorstack \thecolourstack push {#1}}
+\def\popcolour{\pdfextension colorstack \thecolourstack pop\relax}
+\def\red#1{\pushcolour{0.75 0 0 rg}{#1}\popcolour}
\def\emph#1{{\tenit #1\/}}
+\newattributeclass Code
+ attr Layout Placement /Block
+ attr CSS-2.00 display (block)
+
\def\breakablespace{\penalty0~}
\catcode`\⟦=\active
\def⟦{\quitvmode\begingroup\catcode`\∥=0\relax
\markparagraphsfalse
\def\do##1{\catcode`##1=12}\dospecials
- \catcode`\ =\active
- \letcharcode`\ =\breakablespace\obeylines
- \parskip0pt\tentt
+ \catcode`\%=\active \letcharcode`\%=\verbatimcomment
+ \catcode`\ =\active \letcharcode`\ =\breakablespace
+ \def\par{\ifhmode\tentt\endgraf\else\vskip\baselineskip\fi}%
+ \everypar{\stopelement{Code}%
+ \startelement class Code {Code}}%
+ \parskip0pt\tentt \obeylines
\nextif*{\verbatim\ignore}{\expandafter\ignore\verbatim}}
\def\verbatim#1⟧{\red{\markelement{Code}{#1}}\endgroup}
+\def\verbatimcomment{\tenit\Uchar`\%}
% 1 the minim symbol
@@ -109,7 +119,6 @@ endfig;}
\setbox\notehead=\hbox{\markartifact{Layout}{\box\notehead}}
\closemetapostinstance \mnmMP
-%
% 1 document structure
% page artifacts
@@ -134,9 +143,9 @@ endfig;}
\ensurestopelement{Section}%
\startelement{Chapter}%
\outline open {#1}%
- \nextpartag{H}\red{\Title#1\hfill\copy\notehead}%
\addtotoc{\chapter{#1}{\lastdestinationname}}%
- \bigskip}
+ \nextpartag{H}\quitvmode
+ \red{\Title#1\hfill\copy\notehead}\bigskip\nobreak}
% \section Title \par
\addstructuretype Sect Section
@@ -144,8 +153,8 @@ endfig;}
\bigskip\penalty-50\relax
\startelement{Section}%
\outline closed {#1}%
- \nextpartag{H}\quitvmode
\addtotoc{\section{#1}{\lastdestinationname}}%
+ \nextpartag{H}\quitvmode
\red{\title#1}%
\par\nobreak}
@@ -200,7 +209,6 @@ obtained at
{\hyperlink url {https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12}%
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\endlink}$$\startformulatagging}
-
% for identifying which file we are typesetting
\edef\thejobname{\expandafter\scantextokens\expandafter{\jobname}}
\newif \ifchapter
diff --git a/macros/luatex/generic/minim/minim-hooks.lua b/macros/luatex/generic/minim/minim-hooks.lua
index c399547c9e..f11ce27875 100644
--- a/macros/luatex/generic/minim/minim-hooks.lua
+++ b/macros/luatex/generic/minim/minim-hooks.lua
@@ -1,13 +1,11 @@
--- Adds a callback just before a box is shipped out, opposite to
--- 'finish_pdfpage'.
-
-local alloc = require('minim-alloc')
local cb = require ('minim-callbacks')
-alloc.remember('minim-hooks')
local M = { }
+-- Add a callback just before a box is shipped out, opposite to
+-- 'finish_pdfpage'.
+
cb.new_callback('pre_shipout', 'simple')
M.primitive_shipout = tex.shipout
@@ -16,15 +14,5 @@ function tex.shipout(nr)
M.primitive_shipout(nr)
end
-local shipout_box = alloc.new_box('minim:shipout:box')
-
-alloc.luadef('minim:shipout', function()
- tex.box[shipout_box] = token.scan_list()
- cb.call_callback('pre_shipout', shipout_box)
- -- we must let tex do the rest, because some other package may have
- -- redefined \shipout before us.
- tex.sprint(token.create('minim:shipout:do'))
-end, 'protected')
-
return M
diff --git a/macros/luatex/generic/minim/minim-hooks.tex b/macros/luatex/generic/minim/minim-hooks.tex
index b3b8092654..deef66fc9a 100644
--- a/macros/luatex/generic/minim/minim-hooks.tex
+++ b/macros/luatex/generic/minim/minim-hooks.tex
@@ -21,13 +21,17 @@
\chardef\minimhooksloaded = \catcode`\:
\catcode`\: = 11
-\input minim-alloc
+\directlua{ require('minim-hooks') }
% 1 the pre_shipout callback
\newbox\minim:shipout:box \let\minim:shipout:orig = \shipout
\def\minim:shipout:do{\minim:shipout:orig\box\minim:shipout:box}
-\directlua{require('minim-hooks')} \let\shipout\minim:shipout
+\protected\def\minim:shipout:new{\directlua{
+ tex.box[\the\minim:shipout:box] = token.scan_list()
+ require('minim-callbacks').call_callback('pre_shipout', \the\minim:shipout:box)
+ token.put_next(token.create('minim:shipout:do'))}}
+\let\shipout = \minim:shipout:new
% 1 invisibly adding to \everypar
diff --git a/macros/luatex/generic/minim/minim-lmodern.tex b/macros/luatex/generic/minim/minim-lmodern.tex
index 61bc0054e9..e420dd2a1c 100644
--- a/macros/luatex/generic/minim/minim-lmodern.tex
+++ b/macros/luatex/generic/minim/minim-lmodern.tex
@@ -11,15 +11,15 @@
\def\preloadfont#1#2#3{\def#1{\initfontloader\font#1{#2}at#3#1}}
\def\initfontloader{\input luaotfload.sty \let\initfontloader=\relax}
-\preloadfont \tenrm {Latin Modern Roman:script=latn;+liga;+kern;+dlig}{10pt}
-\preloadfont \tenit {Latin Modern Roman/I:script=latn;+liga;+kern;+dlig}{10pt}
-\preloadfont \tenbf {Latin Modern Roman/B:script=latn;+liga;+kern;+dlig}{10pt}
-\preloadfont \tensl {Latin Modern Roman Slanted:script=latn;+liga;+kern;+dlig}{10pt}
-\preloadfont \tentt {Latin Modern Mono:script=latn;+liga;+kern;+dlig}{10pt}
-
-\preloadfont\tenmath {Latin Modern Math:mode=base;script=math;ssty=0}{10pt}
-\preloadfont\tenmaths {Latin Modern Math:mode=base;script=math;ssty=1}{7pt}
-\preloadfont\tenmathss {Latin Modern Math:mode=base;script=math;ssty=2}{5pt}
+\preloadfont \tenrm {Latin Modern Roman:script=latn;+liga;+kern;+dlig;}{10pt}
+\preloadfont \tenit {Latin Modern Roman/I:script=latn;+liga;+kern;+dlig;}{10pt}
+\preloadfont \tenbf {Latin Modern Roman/B:script=latn;+liga;+kern;+dlig;}{10pt}
+\preloadfont \tensl {Latin Modern Roman Slanted:script=latn;+liga;+kern;+dlig;}{10pt}
+\preloadfont \tentt {Latin Modern Mono:script=latn;+liga;+kern;+dlig;}{10pt}
+
+\preloadfont\tenmath {Latin Modern Math:mode=base;script=math;ssty=0;}{10pt}
+\preloadfont\tenmaths {Latin Modern Math:mode=base;script=math;ssty=1;}{7pt}
+\preloadfont\tenmathss {Latin Modern Math:mode=base;script=math;ssty=2;}{5pt}
\toksapp\everyjob{\tenmath\tenmaths\tenmathss\tenrm
\textfont0=\tenmath\scriptfont0=\tenmaths\scriptscriptfont0=\tenmathss}
diff --git a/macros/luatex/generic/minim/minim-pdfresources.lua b/macros/luatex/generic/minim/minim-pdfresources.lua
new file mode 100644
index 0000000000..bc09aeaf40
--- /dev/null
+++ b/macros/luatex/generic/minim/minim-pdfresources.lua
@@ -0,0 +1,154 @@
+
+local M = { }
+
+local alloc = require 'minim-alloc'
+local cb = require 'minim-callbacks'
+
+-- the standard table design
+local function init_resources()
+ return {
+ ExtGState = { },
+ ColorSpace = { },
+ Pattern = { },
+ Shading = { },
+ }
+end
+
+-- the module will use optex’s functions if present
+M.self_destruct = pdf.add_page_resource or false
+
+-- resource_data is a table that can contain any data. only the following keys
+-- are used by this module:
+--
+-- _entry_ internal value; do not use
+-- value value in the resource dictionary
+-- write() called for writing the resource to the pdf; returns value
+--
+-- of the last two, only one needs be present; if the value is missing it will
+-- be set at first use to the result of write().
+--
+local resource_data = init_resources() -- name ↦ { [write = ... | value = ...], ... }
+--
+function M.add_resource(kind, name, resource)
+ resource_data[kind][name] = resource
+end
+function M.get_resource(kind, name)
+ return resource_data[kind][name]
+end
+function M.use_resource(kind, name)
+ local res = resource_data[kind][name]
+ if not res then
+ return alloc.err('Unknown %s resource: %s', kind, name)
+ end
+ res.value = res.value or res:write()
+ if not res._entry_ then -- first-use
+ res._entry_ = string.format('%s %s', alloc.pdf_name(name), res.value)
+ if M.self_destruct then
+ M.self_destruct(kind, name, res.value)
+ end
+ end
+ return res._entry_
+end
+
+-- global resources are mainly for pgf compatibility: contains adds entries to
+-- the resource dictionaries that will be added for every page.
+--
+local global_resources = init_resources() -- name ↦ '/Key <value>'
+--
+function M.add_global_resource(kind, name, value)
+ local r = global_resources[kind]
+ if M.self_destruct then
+ M.self_destruct(kind, name, value)
+ elseif name == nil then -- used for pgf compatibility
+ if value ~= '' then
+ r[''] = r[''] and string.format('%s %s', global_resources[kind][''], value) or value
+ end
+ else
+ r[name] = string.format('%s %s', alloc.pdf_name(name), value)
+ end
+end
+
+-- we can now provide optex’s interface
+if not M.self_destruct then
+ pdf.add_page_resource = M.add_global_resource
+end
+
+-- for nonglobal resources, every use must be markes with a late_lua node. from
+-- those, the M.use_resouce() function will be called automatically.
+--
+local page_resources = init_resources() -- name ↦ '/Key <value>'
+--
+function _with_pdf_resource_(kind, name) -- global, for use in latelua
+ page_resources[kind][name] = M.use_resource(kind, name)
+end
+function M.use_resource_node(kind, name)
+ local n = node.new('whatsit', 'late_lua')
+ n.data = string.format('_with_pdf_resource_("%s", "%s")', kind, name)
+ return n
+end
+
+-- construction and caching of resource dictionaries.
+--
+local previous_dicts = init_resources() -- pdf dict ↦ objnum
+--
+local function make_resource_entry(kind)
+ local arr = { }
+ for _, entry in pairs(page_resources[kind]) do table.insert(arr, entry) end
+ for _, entry in pairs(global_resources[kind]) do table.insert(arr, entry) end
+ if #arr == 0 then return false end
+ table.sort(arr)
+ arr[0] = '<<'; table.insert(arr, '>>')
+ local dict = table.concat(arr, ' ', 0)
+ local objnum = previous_dicts[kind][dict]
+ if not objnum then
+ objnum = pdf.obj(dict)
+ pdf.refobj(objnum)
+ previous_dicts[kind][dict] = objnum
+ end
+ return string.format('/%s %d 0 R', kind, objnum)
+end
+
+local removal_regex = {
+ ExtGState = ' */ExtGState *%d+ +0 *R',
+ ColorSpace = ' */ColorSpace *%d+ +0 *R',
+ Pattern = ' */Pattern *%d+ +0 *R',
+ Shading = ' */Shading *%d+ +0 *R' }
+
+local function update_page_resources(shippingout)
+ if M.self_destruct then return end
+ local resources = shippingout and pdf.getpageresources() or pdf.getxformresources() or ''
+ for kind, used in pairs(page_resources) do
+ resources = string.gsub(resources, removal_regex[kind], '')
+ local entry = make_resource_entry(kind)
+ if entry then
+ resources = resources == '' and entry or string.format('%s %s', resources, entry)
+ end
+ end
+ -- update page resources
+ if shippingout then
+ pdf.setpageresources(resources)
+ else
+ pdf.setxformresources(resources)
+ end
+ page_resources = init_resources()
+end
+
+cb.register('finish_pdfpage', update_page_resources)
+
+-- for pgf compatibility, reroute the pgf resource registration functions to
+-- our global resource pool.
+--
+alloc.luadef('minim:pgf:fix:luaside', function()
+ alloc.luadef('pgf@sys@addpdfresource@extgs@plain', function()
+ M.add_global_resource('ExtGState', nil, token.scan_string())
+ end)
+ alloc.luadef('pgf@sys@addpdfresource@patterns@plain', function()
+ M.add_global_resource('Pattern', nil, token.scan_string())
+ end)
+ alloc.luadef('pgf@sys@addpdfresource@colorspaces@plain', function()
+ M.add_global_resource('ColorSpace', nil, token.scan_string())
+ end)
+end)
+
+return M
+
diff --git a/macros/luatex/generic/minim/minim-pdfresources.tex b/macros/luatex/generic/minim/minim-pdfresources.tex
new file mode 100644
index 0000000000..6b8dc29577
--- /dev/null
+++ b/macros/luatex/generic/minim/minim-pdfresources.tex
@@ -0,0 +1,51 @@
+
+\ifdefined \minimpdfresourcesloaded
+ \message{(skipped)}
+ \expandafter\endinput\fi
+\chardef\minimpdfresourcesloaded=\catcode`\:
+\catcode`\:=11
+
+% at the moment, this file only deals with pgf compatibility. if you do not use
+% pgf, you only need the lua module.
+
+\directlua{ require 'minim-pdfresources' }
+
+% 1 pgf compatibility
+
+% this ballet inserts our fix directly at the end of pgfsys-luatex.def
+\expandafter\def\csname minim:intercept:pgfsys-luatex.def\endcsname
+ {\wlog{minim: applying pgf patches...}\newtoks\minim:pgf:fix:toks
+ \minim:pgf:fix:toks\csname pgfutil@everybye\endcsname
+ \let\endinput=\minim:pgf:fix:endinput}
+\def\minim:pgf:fix:endinput{%
+ \begincsname minim:pgf:fix:\minim:currentfile\endcsname
+ \minim:endinput}
+\expandafter\def\csname minim:pgf:fix:pgfsys-luatex.def\endcsname{%
+ \csname pgfutil@everybye\endcsname\minim:pgf:fix:toks \minim:pgf:fix:common
+ \let\endinput=\minim:endinput \wlog{minim: done applying pgf patches}}
+
+\def\minim:pgf:fix:common{\directlua{%
+ local pdfres = require 'minim-pdfresources'
+ pdfres.add_global_resource('ExtGState', nil,
+ '\luaescapestring{\csname pgf@sys@pgf@resource@list@extgs\endcsname}')
+ pdfres.add_global_resource('Pattern', nil,
+ '\luaescapestring{\csname pgf@sys@pgf@resource@list@patterns\endcsname}')
+ pdfres.add_global_resource('ColorSpace', nil,
+ '\luaescapestring{\csname pgf@sys@pgf@resource@list@colorspaces\endcsname}')}
+ \expandafter\let\csname pgf@sys@pdf@possible@resources\endcsname = \empty
+ \minim:pgf:fix:luaside \pdfvariable pageresources{}}
+
+% if pgf has already been loaded, copy the already-defined resources
+\ifcsname pgf@sys@pdf@extgs@objnum\endcsname \minim:pgf:fix:common
+ % three empty registers will be written at the end. having three unused
+ % empty objects in the pdf is not ideal, but will not lead to problems.
+ \expandafter\let\csname pgf@sys@pgf@resource@list@extgs\endcsname = \empty
+ \expandafter\let\csname pgf@sys@pgf@resource@list@patterns\endcsname = \empty
+ \expandafter\let\csname pgf@sys@pgf@resource@list@colorspaces\endcsname = \empty
+\fi
+
+% 
+
+\catcode`\:=\minimpdfresourcesloaded
+\endinput
+
diff --git a/macros/luatex/generic/minim/minim-plain.tex b/macros/luatex/generic/minim/minim-plain.tex
index 85fd802b8b..1fc0178ed8 100644
--- a/macros/luatex/generic/minim/minim-plain.tex
+++ b/macros/luatex/generic/minim/minim-plain.tex
@@ -490,8 +490,6 @@
\def\leavevmode{\unhbox\voidb@x} % begins a paragraph, if necessary
\def\_{\leavevmode \kern.06em \vbox{\hrule width.3em}}
-\def\AA{\leavevmode\setbox0\hbox{!}\dimen@\ht0\advance\dimen@-1ex%
- \rlap{\raise.67\dimen@\hbox{\char'27}}A}
\def\mathhexbox#1#2#3{\leavevmode
\hbox{$\m@th \mathchar"#1#2#3$}}
@@ -512,18 +510,6 @@
\def\dots{\relax\ifmmode\ldots\else$\m@th\ldots\,$\fi}
\def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX}
-\def\`#1{{\accent18 #1}}
-\def\'#1{{\accent19 #1}}
-\def\v#1{{\accent20 #1}} \let\^^_=\v
-\def\u#1{{\accent21 #1}} \let\^^S=\u
-\def\=#1{{\accent22 #1}}
-\def\^#1{{\accent94 #1}} \let\^^D=\^
-\def\.#1{{\accent95 #1}}
-\def\H#1{{\accent"7D #1}}
-\def\~#1{{\accent"7E #1}}
-\def\"#1{{\accent"7F #1}}
-\def\t#1{{\edef\next{\the\font}\the\textfont1\accent"7F\next#1}}
-
\def\hrulefill{\leaders\hrule\hfill}
\def\dotfill{\cleaders\hbox{$\m@th \mkern1.5mu.\mkern1.5mu$}\hfill}
\def\rightarrowfill{$\m@th\smash-\mkern-7mu%
diff --git a/macros/luatex/generic/minim/minim.doc b/macros/luatex/generic/minim/minim.doc
index d16250a0fe..a0ad8f2d4a 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 {2021-06-01}
- version {2021/1.0}
+ date {2022-03-03}
+ version {2022/1.1}
keywords {LuaTeX; Plain TeX; MetaPost; PDF/A; Tagged PDF; accessibility; a11y;
Unicode mathematics; XMP; metadata; hypertext; bookmarks}
stopmetadata
@@ -62,12 +62,7 @@ Please get in touch if you find this not the case.
Particular care has been taken to be compatible with \red{ltluatex}. All
overlapping functions should produce the same results and ltluatex can be
loaded either before or after minim.
-
-One point of incompatibility exists between \red{tikz/pgf} and the pattern
-functionality of minim-mp, due to conflicting implementations of pdf resource
-management.
-If you do not use filling patterns, however, the two packages can be used
-together.
+Earlier incompatibilities with \red{pgf} have been resolved.
\licencesection
diff --git a/macros/luatex/generic/minim/minim.pdf b/macros/luatex/generic/minim/minim.pdf
index 715525bcf9..1596eb65d4 100644
--- a/macros/luatex/generic/minim/minim.pdf
+++ b/macros/luatex/generic/minim/minim.pdf
Binary files differ
diff --git a/macros/luatex/generic/penlight/README.md b/macros/luatex/generic/penlight/README.md
index 818fb7d4ea..ca66c41b74 100644
--- a/macros/luatex/generic/penlight/README.md
+++ b/macros/luatex/generic/penlight/README.md
@@ -1,11 +1,11 @@
# penlight -- Lua libraries for use in LuaLaTeX
-This LuaLaTeX package provides a thin-wrapper to use the [penlight](https://github.com/lunarmodules/Penlight) Lua libraries with LuaLaTeX.
+This LuaLaTeX package provides a wrapper to use the [penlight](https://github.com/lunarmodules/Penlight) Lua libraries with LuaLaTeX, with some extra functionality added.
# License
-Copyright (C) 2021 Kale Ewasiuk
+Copyright (C) 2021-2022 Kale Ewasiuk
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/macros/luatex/generic/penlight/penlight.pdf b/macros/luatex/generic/penlight/penlight.pdf
index a8e166a1ae..33f3457df2 100644
--- a/macros/luatex/generic/penlight/penlight.pdf
+++ b/macros/luatex/generic/penlight/penlight.pdf
Binary files differ
diff --git a/macros/luatex/generic/penlight/penlight.sty b/macros/luatex/generic/penlight/penlight.sty
index b9f5a9490a..936cb6dce7 100644
--- a/macros/luatex/generic/penlight/penlight.sty
+++ b/macros/luatex/generic/penlight/penlight.sty
@@ -1,6 +1,6 @@
% Kale Ewasiuk (kalekje@gmail.com)
-% 2022-02-27
-% Copyright (C) 2021 Kale Ewasiuk
+% 2022-03-03
+% Copyright (C) 2021-2022 Kale Ewasiuk
%
% Permission is hereby granted, free of charge, to any person obtaining a copy
% of this software and associated documentation files (the "Software"), to deal
@@ -22,7 +22,7 @@
% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
% OR OTHER DEALINGS IN THE SOFTWARE.
-\ProvidesPackage{penlight}[2022-02-27]
+\ProvidesPackage{penlight}[2022-03-03]
\RequirePackage{luacode}
@@ -32,17 +32,23 @@
}}
\DeclareOption{penlight}{\luadirect{
penlight = require('penlight')
- __PENLIGHT__ = 'penlight'
+ __PENLIGHT__ = 'penlight'
}}
\DeclareOption{stringx}{\luadirect{_G[__PENLIGHT__].stringx.import()}}
\DeclareOption{format}{\luadirect{_G[__PENLIGHT__].stringx.format_operator()}}
\DeclareOption{func}{\luadirect{_G[__PENLIGHT__].utils.import(__PENLIGHT__..'.func')}}
-\DeclareOption{extras}{\luadirect{require('penlightextras')}}
-\DeclareOption{extrasnoglobals}{
+\DeclareOption{extras}{\luadirect{require'penlightextras'}}
+\DeclareOption{extrasnoglobals}{\luadirect{
__PL_NO_GLOBALS__ = true
- \luadirect{require('penlightextras')
- }}
+ require'penlightextras'
+}}
\ProcessOptions*\relax
+
+\luadirect{
+if __PENLIGHT__ == nil then
+ tex.print('\\PackageError{penlight}{penlight or pl option must be passed to penlight as the first option}{}')
+end
+}
diff --git a/macros/luatex/generic/penlight/penlight.tex b/macros/luatex/generic/penlight/penlight.tex
index 176dc4e557..2096c10b68 100644
--- a/macros/luatex/generic/penlight/penlight.tex
+++ b/macros/luatex/generic/penlight/penlight.tex
@@ -1,6 +1,6 @@
% Kale Ewasiuk (kalekje@gmail.com)
-% 2022-02-27
-% Copyright (C) 2021 Kale Ewasiuk
+% 2022-03-03
+% Copyright (C) 2021-2022 Kale Ewasiuk
%
% Permission is hereby granted, free of charge, to any person obtaining a copy
% of this software and associated documentation files (the "Software"), to deal
diff --git a/macros/luatex/generic/penlight/penlightextras.lua b/macros/luatex/generic/penlight/penlightextras.lua
index 0cdec03763..192c86c923 100644
--- a/macros/luatex/generic/penlight/penlightextras.lua
+++ b/macros/luatex/generic/penlight/penlightextras.lua
@@ -1,8 +1,31 @@
+--% Kale Ewasiuk (kalekje@gmail.com)
+--% 2022-03-03
+--% Copyright (C) 2021-2022 Kale Ewasiuk
+--%
+--% Permission is hereby granted, free of charge, to any person obtaining a copy
+--% of this software and associated documentation files (the "Software"), to deal
+--% in the Software without restriction, including without limitation the rights
+--% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+--% copies of the Software, and to permit persons to whom the Software is
+--% furnished to do so, subject to the following conditions:
+--%
+--% The above copyright notice and this permission notice shall be included in
+--% all copies or substantial portions of the Software.
+--%
+--% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+--% ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+--% TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+--% PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+--% SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+--% ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+--% ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+--% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+--% OR OTHER DEALINGS IN THE SOFTWARE.
__SKIP_TEX__ = __SKIP_TEX__ or false --if declared true before here, it will use regular print functions
-- (for troubleshooting with texlua instead of actual use in lua latex)
-
__PL_NO_GLOBALS__ = __PL_NO_GLOBALS__ or false
+__PL_EXTRAS__ = 1
-- requires penlight
local pl = _G['penlight'] or _G['pl'] -- penlight for this namespace is pl
@@ -10,8 +33,8 @@ local pl = _G['penlight'] or _G['pl'] -- penlight for this namespace is pl
-- some bonus string operations, % text operator, and functional programming
pl.stringx.import()
-pl.text.format_operator()
-pl.utils.import('pl.func') -- allow placeholder expressions _1 +1 etc.
+pl.stringx.format_operator()
+pl.utils.import'pl.func' -- allow placeholder expressions _1 +1 etc.
pl.COMP = require'pl.comprehension'.new() -- for comprehensions
@@ -561,15 +584,48 @@ end
+-- https://tex.stackexchange.com/questions/38150/in-lualatex-how-do-i-pass-the-content-of-an-environment-to-lua-verbatim
+pl.tex.recordedbuf = ""
+function pl.tex.readbuf(buf)
+ i,j = string.find(buf, '\\end{%w+}')
+ if i==nil then -- if not ending an environment
+ pl.tex.recordedbuf = pl.tex.recordedbuf .. buf .. "\n"
+ return ""
+ else
+ return nil
+ end
+end
+
+function pl.tex.startrecording()
+ pl.tex.recordedbuf = ""
+ luatexbase.add_to_callback('process_input_buffer', pl.tex.readbuf, 'readbuf')
+end
+
+function pl.tex.stoprecording()
+ luatexbase.remove_from_callback('process_input_buffer', 'readbuf')
+ pl.tex.recordedbuf = pl.tex.recordedbuf:gsub("\\end{%w+}\n","")
+end
+
+
+
+
if not __PL_NO_GLOBALS__ then
+ __PL_EXTRAS__ = 2
-- iterators
kpairs = pl.utils.kpairs
npairs = pl.utils.npairs
--enum = utils.enum
for k,v in pairs(pl.tablex) do -- extend the table table to contain tablex functions
- _G['table'][k] = v
+ if k == 'sort' then
+ table.sortk = v
+ elseif k == 'move' then
+ table.xmove = v
+ else
+ _G['table'][k] = v
+ end
end
+ table.join = table.concat -- alias
hasval = pl.hasval
COMP = pl.COMP
@@ -589,6 +645,12 @@ if not __PL_NO_GLOBALS__ then
_G[k] = v
end
+end
+
+
+
+
+
--_xTrue = pl.tex._xTrue
--_xFalse = pl.tex._xFalse
--_xNoValue = pl.tex._xNoValue
@@ -619,11 +681,6 @@ if not __PL_NO_GLOBALS__ then
--add_bkt_cnt = pl.tex.add_bkt_cnt
--close_bkt_cnt = pl.tex.close_bkt_cnt
-end
-
-
-
-
-- graveyard