texlive[69352] Master/texmf-dist: minim (8jan24)

commits+karl at tug.org commits+karl at tug.org
Mon Jan 8 22:47:22 CET 2024


Revision: 69352
          https://tug.org/svn/texlive?view=revision&revision=69352
Author:   karl
Date:     2024-01-08 22:47:22 +0100 (Mon, 08 Jan 2024)
Log Message:
-----------
minim (8jan24)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/luatex/minim/README
    trunk/Master/texmf-dist/doc/luatex/minim/minim-alloc.doc
    trunk/Master/texmf-dist/doc/luatex/minim/minim.doc
    trunk/Master/texmf-dist/doc/luatex/minim/minim.pdf
    trunk/Master/texmf-dist/doc/luatex/minim-math/README
    trunk/Master/texmf-dist/doc/luatex/minim-math/minim-math.doc
    trunk/Master/texmf-dist/doc/luatex/minim-math/minim-math.pdf
    trunk/Master/texmf-dist/doc/luatex/minim-mp/README
    trunk/Master/texmf-dist/doc/luatex/minim-mp/minim-mp.doc
    trunk/Master/texmf-dist/doc/luatex/minim-mp/minim-mp.pdf
    trunk/Master/texmf-dist/doc/luatex/minim-pdf/README
    trunk/Master/texmf-dist/doc/luatex/minim-pdf/minim-pdf.doc
    trunk/Master/texmf-dist/doc/luatex/minim-pdf/minim-pdf.pdf
    trunk/Master/texmf-dist/doc/luatex/minim-xmp/README
    trunk/Master/texmf-dist/doc/luatex/minim-xmp/minim-xmp.doc
    trunk/Master/texmf-dist/doc/luatex/minim-xmp/minim-xmp.pdf
    trunk/Master/texmf-dist/metapost/minim-mp/minim-mp.mp
    trunk/Master/texmf-dist/metapost/minim-mp/minim.mp
    trunk/Master/texmf-dist/tex/luatex/minim/minim-doc.sty
    trunk/Master/texmf-dist/tex/luatex/minim/minim-pdfresources.lua
    trunk/Master/texmf-dist/tex/luatex/minim/minim-pdfresources.tex
    trunk/Master/texmf-dist/tex/luatex/minim-math/minim-math.tex
    trunk/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.lua
    trunk/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.sty
    trunk/Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.lua
    trunk/Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.tex
    trunk/Master/texmf-dist/tex/luatex/minim-xmp/minim-xmp.lua

Modified: trunk/Master/texmf-dist/doc/luatex/minim/README
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/minim/README	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/doc/luatex/minim/README	2024-01-08 21:47:22 UTC (rev 69352)
@@ -1,4 +1,4 @@
-Version: 2023/1.3
+Version: 2024/1.4
 
 SUMMARY
 
@@ -31,6 +31,11 @@
 
 HISTORY
 
+2024/1.4 (5/1/2024)
+
+    * Documented the pdf resource management module
+    * Updated the manual to pdf/ua
+
 2023/1.3 (20/10/2023)
 
    No changes.

Modified: trunk/Master/texmf-dist/doc/luatex/minim/minim-alloc.doc
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/minim/minim-alloc.doc	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/doc/luatex/minim/minim-alloc.doc	2024-01-08 21:47:22 UTC (rev 69352)
@@ -25,7 +25,7 @@
 \section Format files
 
 A major motivation for writing this module (and not, instead, depending on 
-⟦ltluatex.tex⟧) is the ability to write lua-heavy code that can be safely 
+ltluatex) is the ability to write lua-heavy code that can be safely 
 included in format files.
 For this purpose, the register allocation functions described below allow 
 ensuring that the allocation is made only once.
@@ -171,6 +171,46 @@
 replace the ⟦*process_rule⟧ callback when its number matches the index property 
 of the rule.
 
+\section PDF resources
+
+This package can perform sophisticated pdf resource management, assigning to 
+every page a resource object containing only the resources referenced on that 
+page. pdf resources are ⟦ExtGstate⟧, ⟦ColorSpace⟧, ⟦Pattern⟧ and ⟦Shading⟧ 
+objects that have to be referenced by name (⟦/name⟧) instead of with the usual 
+object references (⟦n 0 R⟧).\footnote
+    {*}{This is of course also the case for ⟦Font⟧ resources, but those are 
+    already managed by luatex’s pdf backend.}
+Currently, the only other package managing pdf resources for plain tex is 
+tikz/pgf, and the latter does so by collecting all resources in a single 
+(global) resource object. That approach is not wrong per se, but may cause 
+other tools processing the resulting pdf to retain unneeded resources.
+Both implementations can safely be used together, but since pgf does not keep 
+track of actual resource use, any resources defined through pgf will be added 
+to the resource dictionary of every subsequent page.
+
+The resource management is implemented in minim-pdfresources.lua and 
+minim-pdfresources.tex, of which the tex file currently only includes pgf 
+compatibility code (and may thus be omitted in the absence of pgf). In the 
+following, ⟦R = require 'minim-pdfresources'⟧ is understood.
+
+You must register resources before you use them. This can be done with
+⟦*R.add_resource(kind, name, resource)⟧, where ⟦kind⟧ is one of the resource 
+types, ⟦name⟧ is the name you want to use for it (without a preceding slash) 
+and ⟦resource⟧ is a table that may contain any data you want to associate with 
+the resource. In the ⟦resource⟧ table, either the key ⟦value⟧ must be present 
+(containing the (raw) contents of the resource; will be written to the pdf 
+as-is) or the key ⟦write⟧ (which must be a function that will be called once, 
+to generate the ⟦value⟧). Registered resources can be retrieved again with
+⟦*R.get_resource(kind, name)⟧.
+
+You can refer to registered resources with the ⟦name⟧ you used to register 
+them. Whenever you do so, however, you must mark the reference with a special 
+⟦late_lua⟧ node that will tell minim to include the resource in the resource 
+list for the page it appears on. These nodes can be created from lua with
+⟦*R.use_resource_node(kind, name)⟧ or directly inserted by tex with
+⟦*\withpdfresource {kind} {name}⟧ (the braces are optional).
+
+
 \section Programming helpers
 
 Optional keyword arguments to tex macros can be defined with help of 

Modified: trunk/Master/texmf-dist/doc/luatex/minim/minim.doc
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/minim/minim.doc	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/doc/luatex/minim/minim.doc	2024-01-08 21:47:22 UTC (rev 69352)
@@ -4,8 +4,8 @@
 \startmetadata
     author   {Esger Renkema}
     title    {minim}
-    date     {2023-10-20}
-    version  {2023/1.3}
+    date     {2024-01-05}
+    version  {2024/1.4}
     keywords {LuaTeX; Plain TeX; MetaPost; PDF/A; Tagged PDF; accessibility; a11y;
               Unicode mathematics; XMP; metadata; hypertext; bookmarks}
 stopmetadata
@@ -20,7 +20,9 @@
 Most features included in the format are provided by separate packages that can 
 be used on their own; see the packages
 
-\smallskip\marktableaslist \halign {\strut
+\smallskip\marktableaslist
+\tagattribute List ListNumbering /Description
+\halign {\strut
     \vrule height10ptdepth2ptwidth0pt\qquad
     \red{#}\quad\hfil&#\hfil\cr
 minim-mp&for mplib (MetaPost) support\cr

Modified: trunk/Master/texmf-dist/doc/luatex/minim/minim.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/luatex/minim-math/README
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/minim-math/README	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/doc/luatex/minim-math/README	2024-01-08 21:47:22 UTC (rev 69352)
@@ -1,4 +1,4 @@
-Version: 2023/1.3
+Version: 2024/1.4
 
 SUMMARY
 
@@ -18,6 +18,10 @@
 
 HISTORY
 
+2024/1.4 (5/1/2024)
+
+    * Add \floor and \ceil macros
+
 2023/1.3 (20/10/2023)
 
   No notable changes.

Modified: trunk/Master/texmf-dist/doc/luatex/minim-math/minim-math.doc
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/minim-math/minim-math.doc	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/doc/luatex/minim-math/minim-math.doc	2024-01-08 21:47:22 UTC (rev 69352)
@@ -8,8 +8,8 @@
 \else \startmetadata
     author   {Esger Renkema}
     title    {minim-math}
-    date     {2023-10-20}
-    version  {2023/1.3}
+    date     {2024-01-05}
+    version  {2024/1.4}
     keywords {LuaTeX; Plain TeX; Unicode mathematics}
     stopmetadata
 \maketitle \fi
@@ -64,6 +64,7 @@
 The default properties of characters can be set with one of the following three 
 commands:
 \startlist
+\tagattribute List ListNumbering /Unordered
 \item\ignore. ⟦*\mathmap   {character list} {style}⟧
 \item\ignore. ⟦*\mathclass {character list} {class}⟧
 \item\ignore. ⟦*\mathfam   {character list}   nr⟧
@@ -72,6 +73,7 @@
 Finally, the ⟦character list⟧ should be a comma-separated list with elements of 
 one of the following forms:
 \startlist
+\tagattribute List ListNumbering /Decimal
 {\def\⟦#1{⟦[\uppercase{#1}#1]\kern-1pt}
 \item1. a list of characters, like ⟦abc⟧ or ⟦\partial⟧ or $ℝ$;
 \item2. a character range, like ⟦`A-`Z⟧, ⟦65-90⟧ or ⟦"41-"5A⟧;
@@ -188,6 +190,7 @@
 ⟦\bra x, \ket y⟧             & $\bra x, \ket y$             \cr
 ⟦\braket x y⟧                & $\braket x y$                \cr
 ⟦\norm x, \Norm x⟧           & $\norm x, \Norm x$           \cr
+⟦\floor x, \ceil x⟧          & $\floor x, \ceil x$          \cr
 ⟦x \stackrel ?= y⟧           & $x \stackrel ?= y$           \cr
 ⟦x \stackbin a+ y⟧           & $x \stackbin a+ y$           \cr
 ⟦f\inv⟧                      & $f\inv$ \quad (cf. $f^{-1}$) \cr
@@ -233,6 +236,7 @@
 remember.
 
 \startlist
+\tagattribute List ListNumbering /Decimal
 \item1. ⟦*\eqalign⟧ gives a vertically centered box and can occur many times in 
 an equation, while ⟦*\eqalignno⟧ and ⟦*\leqalignno⟧ span whole lines (put the 
 equation numbers in the third column). All assume the relation (or operator) 

Modified: trunk/Master/texmf-dist/doc/luatex/minim-math/minim-math.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/luatex/minim-mp/README
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/minim-mp/README	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/doc/luatex/minim-mp/README	2024-01-08 21:47:22 UTC (rev 69352)
@@ -1,4 +1,4 @@
-Version: 2023/1.3
+Version: 2024/1.4
 
 SUMMARY
 
@@ -19,6 +19,12 @@
 
 HISTORY
 
+2024/1.4 (5/1/2024)
+
+    * Support the extended graphics state (extgstate)
+    * Allow box resource (xform) creation directly from metapost
+    * Support transparency and transparency groups
+
 2023/1.3 (20/10/2023)
 
     * Various runscript improvements:
@@ -30,13 +36,11 @@
     * Improve font handling in minim-lamp.ini, and provide \begin{document}.
     * Add a minim-mp.sty package file for LaTeX users.
 
-
 2023/1.2 (3/3/2023)
 
     * Add a few more metapost macros.
     * Various small bug fixes.
 
-
 2022/1.1 (3/3/2022)
 
   New features:
@@ -57,7 +61,6 @@
       into raw pdf code.
     * No longer write out superfluous line widths.
 
-
 2021/1.0 (1/6/2021)
 
   This was the original release.

Modified: trunk/Master/texmf-dist/doc/luatex/minim-mp/minim-mp.doc
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/minim-mp/minim-mp.doc	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/doc/luatex/minim-mp/minim-mp.doc	2024-01-08 21:47:22 UTC (rev 69352)
@@ -8,8 +8,8 @@
 \else \startmetadata
     author   {Esger Renkema}
     title    {minim-mp}
-    date     {2023-10-20}
-    version  {2023/1.3}
+    date     {2024-01-05}
+    version  {2024/1.4}
     keywords {LuaTeX; Plain TeX; MetaPost; mplib}
     stopmetadata
 \maketitle \fi
@@ -147,7 +147,7 @@
 \vskip-2\parskip plus 2\parskip
 \quitvmode\hfil
 \startelement alt {The text ‘Running TeX from within MetaPost’ set along a half-circle.} {Figure}%
-\stoptagging\directmetapost{%
+\startsinglecontentitem\directmetapost{%
 beginfig(1)
     save t, w, r, a; picture t;
     t = "Running TeX from within MetaPost" infont "tenrm";
@@ -158,7 +158,7 @@
         draw c rotatedaround((x,0), a)
                shifted (-r*sind(a)-x, r*cosd(a));
     endfor
-endfig;}\starttagging \stopelement{Figure}
+endfig;}\stopsinglecontentitem \stopelement{Figure}
 
 By default, the ⟦maketext⟧ operator is used for typesetting labels. You can, 
 however, order de ⟦label⟧ macro to use ⟦infont⟧ instead by setting 
@@ -199,9 +199,9 @@
 For example,
 ⟦multidraw (contours "example" of "tenbf") withpen currentpen scaled 1/10;⟧
 will give the word%
-\markelement{Span}{\setactualtext{example }
+\markelement{Span}{\setactualtext{example }\startsinglecontentitem\space
 \directmetapost{beginfig(1) multidraw (contours "example" of "tenbf")
-withpen currentpen scaled 1/10; baseline 0; endfig;}}
+withpen currentpen scaled 1/10; baseline 0; endfig;}\stopsinglecontentitem}
 in a thin outline.
 
 Finally, two handy clipping macros have been added:
@@ -233,10 +233,10 @@
 metapost. If on the other hand it does return a value, that value will have to 
 be translated to metapost. Numbers and strings will be returned as they are
 (so make sure the string is surrounded by quotes if you want to return 
-a metapost string). You can return a point or colour by returning an array of 
-two to four elements. For other return values, ⟦tostring()⟧ will be called.
+a metapost string). You can return a point, colour or transform by returning an 
+array of two to six elements (excluding five). For other return values, 
+⟦tostring()⟧ will be called.
 
-
 \section Passing values to lua
 
 Do keep in mind that metapost and lua represent numbers in different ways and 
@@ -275,7 +275,7 @@
 
 For accessing count, dimen, attribute or toks registers, the macros are
 ⟦*tex.count⟧ ⟦[number]⟧ or ⟦*tex.count⟧⟦.name⟧ [etc. etc.] for getting and
-⟦*set tex.count⟧ ⟦[number] = value⟧ or ⟦*tex.count⟧⟦.name = value⟧ etc. for 
+⟦*set tex.count⟧ ⟦[number] = value⟧ or ⟦*set tex.count⟧⟦.name = value⟧ etc. for 
 setting values.
 
 
@@ -305,7 +305,7 @@
 You can use text inside patterns, as in this example:
 \hfill \vbox to 0pt{
 \startelement alt{A circle filled with red As in a rectangular pattern.}{Figure}
-\directmetapost{
+\startsinglecontentitem \directmetapost{
 % define the pattern
 picture letter; letter = maketext("a");
 beginpattern(a)
@@ -316,7 +316,8 @@
 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
+endfig;}\stopsinglecontentitem
+\stopelement{Figure}\vss}\hskip10pt\strut
 \par\nobreak
 ⟦% define the pattern
 picture letter; letter = maketext("a");
@@ -333,7 +334,42 @@
 A small pattern library is available in the ⟦*minim-hatching.mp⟧ file; see the 
 accompanying documentation sheet for an overview of patterns.
 
+\section Advanced PDF graphics %
1
 
+You can use ⟦*savegstate⟧ and ⟦*restoregstate⟧ for inserting the ⟦q⟧ and ⟦Q⟧ 
+operators; these must always be paired, or horrible errors will occur. You may 
+need them if you use ⟦*setgstate(<params>)⟧ for modifying the extended 
+graphical state (ExtGState). The ⟦params⟧ must be a comma-separated 
+⟦Key=value⟧ list, with all ⟦value⟧s being suffixes. The latter restriction may 
+require the use of additional variables, but as this is a very low-level 
+command, it is best to wrap it in a more specialised macro anyway.
+The ⟦*withgstate (<params>)⟧ can be added to a drawing statement and includes 
+saving/restoring the graphical state.
+
+Note that while you could try and use ⟦setgstate⟧ for modifying variables like 
+the line cap or dash pattern, the result of doing so will be unpredictable, 
+since such changes will be invisible to metapost. Its intended use is 
+restricted to graphics parameters outside the scope of metapost.
+
+For applying transparency, ⟦*setalpha(a)⟧ updates the ⟦CA⟧ and ⟦ca⟧ parameters 
+as a stand-alone command and ⟦*withalpha(a)⟧ can be used in a drawing statement 
+where it will save/restore the graphical state around it. For applying 
+transparency to an ensemble of drawing statements, ⟦*transparent (a) <picture>⟧ 
+will create and insert the proper transparency group. The transparency group 
+attributes can be set with the string internal ⟦transparency_group_attrs⟧, 
+while for all three macros the blend mode can be set with the string internal 
+⟦blend_mode⟧ (it will be added whenever set).
+
+A transparency group is a special kind of XForm, and these can be created from 
+withing metapost: ⟦<id> =⟧ ⟦*saveboxresource (<attributes>) <picture>⟧ returns 
+a number identifying the resource and can be fed attributes in the same way as 
+⟦setgstate⟧. XForms defined through metapost are available to other metapost 
+instances, but not to tex, though the macro painting them, ⟦*boxresource <id>⟧, 
+also accepts identifiers of tex-defined box resources. There remains a subtle 
+difference, however: metapost-defined box resources are placed at their 
+original origin.
+
+
 \section Other metapost extensions %
1
 
 You can set the baseline of an image with ⟦*baseline(p)⟧. There, ⟦p⟧ must 
@@ -342,7 +378,9 @@
 account, hence the specification of two coordinates. The last given baseline 
 will be used.
 
-You can write to tex’s log directly with ⟦*texmessage "hello"⟧.
+You can write to tex’s log directly with ⟦*texmessage "hello";⟧. You can feed 
+it a comma-separated list of strings and numbers, which will be passed through 
+⟦string.format()⟧ first.
 
 You can write direct pdf statements with ⟦*special "pdf: statements"⟧ and you 
 can add comments to the pdf file with ⟦*special "pdfcomment: comments"⟧.
@@ -381,7 +419,9 @@
 Each metapost instance is a table containing the following entries:
 
 \smallskip\smallskip
-\marktableaslist \halign{\qquad#\quad\hfil&#\hfil\cr
+\marktableaslist
+\tagattribute List ListNumbering /Description
+\halign{\qquad#\quad\hfil&#\hfil\cr
 ⟦jobname⟧&The jobname.\cr
 ⟦instance⟧&The primitive metapost instance.\cr
 ⟦results⟧&A linked list of unretrieved images.\cr
@@ -424,7 +464,9 @@
 Specials can store information in the ⟦user⟧ table of the picture that is being 
 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.
+properly-dimensioned boxes. If a ⟦user.save_fn⟧ function is defined, it will 
+replace the normal saving of the image to the image list and the image node 
+list will be flushed.
 
 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 

Modified: trunk/Master/texmf-dist/doc/luatex/minim-mp/minim-mp.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/luatex/minim-pdf/README
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/minim-pdf/README	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/doc/luatex/minim-pdf/README	2024-01-08 21:47:22 UTC (rev 69352)
@@ -1,4 +1,4 @@
-Version: 2023/1.3
+Version: 2024/1.4
 
 SUMMARY
 
@@ -27,6 +27,20 @@
 
 HISTORY
 
+2024/1.4 (5/1/2024)
+
+  Breaking changes:
+
+    * Table cells and headers now connect automatically in \automarktable
+
+  New features:
+
+    * Add support for pdf/ua
+    * Re-write \hyperlink, adding many options
+    * Add ref and title options to structure elements
+    * New helper macros for footnotes
+    * Add \withpdfresource
+
 2023/1.3 (20/10/2023)
 
   New features:

Modified: trunk/Master/texmf-dist/doc/luatex/minim-pdf/minim-pdf.doc
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/minim-pdf/minim-pdf.doc	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/doc/luatex/minim-pdf/minim-pdf.doc	2024-01-08 21:47:22 UTC (rev 69352)
@@ -3,7 +3,7 @@
 
 \manual{minim-pdf}
 
-\ifchapter \chapter Hypertext
+\ifchapter \chapter Advanced PDF features
 
 This chapter and the next document the support of the modern pdf features 
 provided by the minim-pdf package.
@@ -14,8 +14,8 @@
 \else \startmetadata
     author   {Esger Renkema}
     title    {minim-pdf}
-    date     {2023-10-20}
-    version  {2023/1.3}
+    date     {2024-01-05}
+    version  {2024/1.4}
     keywords {LuaTeX; Plain TeX; PDF/A; Tagged PDF; accessibility; a11y;
               hypertext; bookmarks; document outline; associated files}
     stopmetadata
@@ -33,10 +33,16 @@
 
 \section Hyperlinks %
1
 
-For most simple cases, you can use
-⟦*\hyperlink [name {...} | url {...}] ... \endlink⟧ for linking to a named 
-destination in your own document or to an external hyperlink respectively.
-There is no support for nonsimple cases.
+Hyperlinks can be made with
+⟦*\hyperlink [alt {...}] [attr {...}] <action> ... \endlink⟧, where the 
+⟦<action>⟧ must be one of
+⟦name {...} | url {...} | name {...} | next | prev | first | last⟧
+With the ⟦name⟧ action, a named destination must be used (see below), while the 
+⟦user⟧ action will be passed directly to the back-end (as with the pdftex 
+primitive).
+The ⟦*\hyperlinkstyle⟧ token list can be used so set common (pdf) link 
+attributes; it defaults to ⟦/Border [0 0 0]⟧. The contents of the optional 
+⟦attr⟧ parameter will be appended to these.
 
 A named destination can be created with ⟦*\nameddestination {...}⟧ (also in 
 horizontal mode, unlike the backend primitive) and if you cannot think of 
@@ -47,9 +53,10 @@
 
 \section Bookmarks %
1
 
-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
+Bookmarks (also known as outlines) 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).
 
@@ -101,6 +108,48 @@
 You can disable them by setting ⟦*\writehyphensandspaces⟧ to a nonpositive value.
 
 
+\section PDF/UA %
1
+
+You can claim pdf/ua conformance with ⟦*\pdfualevel 1⟧.
+By itself, this will do very little:
+
+\startlist
+\tagattribute List ListNumbering /Decimal
+\item 1. The ⟦pdfuaid:part⟧ metadata key will be set.
+\item 2. A conforming ⟦ViewerPreferences⟧ dictionary will be added to the document catalog.
+\item 3. The ⟦/Suspects⟧ key of the ⟦MarkInfo⟧ dictionary will be set to ⟦false⟧.
+\item 4. ⟦/Tab /S⟧ will be added to the page attributes.
+\stoplist
+
+Also making your document pdf/a-compliant, however, will relieve you of a few 
+additional worries:
+
+\startlist
+\tagattribute List ListNumbering /Decimal
+\tagattribute List ContinuedList true
+\item 5. Fonts will be included properly.
+\item 6. The (natural) language of every element will be known.
+\item 7. Headings will be strongly-structured.
+\item 8. Table headers will have their ⟦Scope⟧ set properly.
+\item 9. A document outline will be generated automatically.
+\stoplist
+
+This leaves the following for you to provide before your document can be 
+pdf/ua-compliant:
+
+\startlist
+\tagattribute List ListNumbering /Decimal
+\tagattribute List ContinuedList true
+\item 10. Figure and Formula structure elements must have alt texts.
+\item 11. Hyperlinks must have alternate descriptions.
+\item 12. Lists must have the ⟦ListNumbering⟧ attribute set.
+\item 13. Tables must have headers that are tagged as such.
+\item 14. Page headers and footers must be marked as header or footer artifacts.
+\item 15. Document section structure elements should have their ⟦title⟧ set.
+\item 16. All embedded files must have a description.
+\stoplist
+
+
 \section Embedded files %
1
 
 You can attach (associate) files with ⟦*\embedfile <options>⟧.
@@ -110,7 +159,9 @@
 Arguments consisting of a single word can be given without braces and
 exactly one of the options ⟦file⟧ or ⟦string⟧ must be present.
 
-\smallskip\marktableaslist \halign{#\quad\hfil&#\hfil\cr
+\smallskip\marktableaslist
+\tagattribute List ListNumbering /Description
+\halign{#\quad\hfil&#\hfil\cr
 ⟦file ⟧\hfill⟦{...}⟧ & The file to embed.\cr
 ⟦string ⟧\hfill⟦{...}⟧ & The string to embed.\cr
 ⟦global⟧ & Attach to the document catalog.\cr
@@ -166,6 +217,7 @@
 
 This is the full list of limitations, pitfalls and shortcomings:
 \startlist
+\tagattribute List ListNumbering /Decimal
 \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).
@@ -342,10 +394,17 @@
 ⟦value⟧ on the other hand will be written to the pdf verbatim.
 Any number of attributes can be added.
 
-An identifier can be set with the ⟦id⟧ option, or after the fact with
+An identifier can be set with the ⟦id {...}⟧ option, or after the fact with
 ⟦*\settagid {...}⟧. This identifier will be added to the ⟦IDTree⟧ and is 
 entirely optional; you will probably already know when you need it.
+The ⟦ref {...}⟧ option lets a structure element refer to another (the ⟦/Ref⟧ 
+option in the structure element dictionary). Its argument should be the ⟦id⟧ of 
+the other structure element.
 
+The title of the structure element (corresponding to the ⟦/T⟧ entry in the 
+structure element dictionary) can be set with the ⟦title {...}⟧ option. The 
+pdf/ua standard requires this key for all document sections.
+
 Finally, structure element classes can be given with the
 ⟦class <classname>⟧ keyword, which can be repeated.
 Classes can be defined with
@@ -450,12 +509,13 @@
 with caution when using cells spanning multiple rows, and inspect the resulting 
 structure carefully.
 
-Marking a table header (either manually or with ⟦\automarktable⟧) will not 
-connect normal cells with their headers; you will have to connect these 
-manually by including ⟦*\markcolumnhead⟧ or ⟦*\markrowhead⟧ in the appropriate 
-header cells. This must be done \emph{after} ⟦\markcolumnspan⟧ if the latter 
-appplies. If properly setup like this, other cells of the table (including 
-header cells) will be assigned to matching row or column headers automatically.
+Marking up a table header (except if done through ⟦\automarktable⟧)
+will not connect normal table cells with their headers; you will have to 
+connect these manually by including ⟦*\markcolumnhead⟧ or ⟦*\markrowhead⟧ in 
+the appropriate header cells. This must be done \emph{after} ⟦\markcolumnspan⟧ 
+if the latter appplies. If properly setup like this, other cells of the table 
+(including header cells) will be assigned to matching row or column headers 
+automatically.
 
 
 \section Other helper macros %
1
@@ -465,6 +525,9 @@
 tags in the correct way. (The ⟦dest⟧ is a link destination and can be empty; 
 the ⟦lbl⟧ is a section number and can also be empty.)
 
+For tagging (foot)notes, ⟦*\marknoteref{*}⟧ and ⟦*\marknotelbl{*}⟧, when placed 
+around the footnote markers, will insert the proper ⟦Ref⟧, ⟦Note⟧ and ⟦Lbl⟧ 
+tags.
 
 % 
 

Modified: trunk/Master/texmf-dist/doc/luatex/minim-pdf/minim-pdf.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/luatex/minim-xmp/README
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/minim-xmp/README	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/doc/luatex/minim-xmp/README	2024-01-08 21:47:22 UTC (rev 69352)
@@ -1,4 +1,4 @@
-Version: 2023/1.2
+Version: 2024/1.3
 
 SUMMARY
 
@@ -21,6 +21,12 @@
 
 HISTORY
 
+2024/1.3 (5/1/2024)
+
+  New features:
+
+    * Add the pdfuaid namespace.
+
 2023/1.2 (3/3/2022)
 
   No new features.

Modified: trunk/Master/texmf-dist/doc/luatex/minim-xmp/minim-xmp.doc
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/minim-xmp/minim-xmp.doc	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/doc/luatex/minim-xmp/minim-xmp.doc	2024-01-08 21:47:22 UTC (rev 69352)
@@ -8,8 +8,8 @@
 \else \startmetadata
     author   {Esger Renkema}
     title    {minim-xmp}
-    date     {2023-03-03}
-    version  {2023/1.2}
+    date     {2024-01-05}
+    version  {2024/1.3}
     keywords {LuaTeX; Plain TeX; XMP; metadata; PDF/A;}
     stopmetadata
 \maketitle \fi
@@ -85,7 +85,8 @@
 \section Supported metadata keys
 
 Initially, the ⟦\setmetadata⟧ and ⟦\getmetadata⟧ recognise all pdf/a compatible 
-fields in the ⟦pdf⟧, ⟦pdfaid⟧, ⟦dc⟧, ⟦xmp⟧, ⟦xmpMM⟧ and ⟦xmpRights⟧ namespaces.
+fields in the ⟦pdf⟧, ⟦pdfaid⟧, ⟦pdfuaid⟧, ⟦dc⟧, ⟦xmp⟧, ⟦xmpMM⟧ and ⟦xmpRights⟧ 
+namespaces.
 Keys should be prefixed with their namespace, e.g. ⟦dc:creator⟧ or 
 ⟦xmp:CreatorTool⟧.
 Note that the ⟦dc⟧ namespace has lower-case fields; field names are 

Modified: trunk/Master/texmf-dist/doc/luatex/minim-xmp/minim-xmp.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/metapost/minim-mp/minim-mp.mp
===================================================================
--- trunk/Master/texmf-dist/metapost/minim-mp/minim-mp.mp	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/metapost/minim-mp/minim-mp.mp	2024-01-08 21:47:22 UTC (rev 69352)
@@ -2,7 +2,7 @@
 delimiters ();
 
 % This file is specific to the minim metapost processor (minim-mp) and cannot 
-% be used with luaotfload or other metapost implementations. See minim.mp for 
+% be used with luamplib or other metapost implementations. See minim.mp for 
 % general-purpose extensions.
 
 message "Setting up the minim MetaPost extensions";
@@ -10,6 +10,7 @@
 def save_picture   text t = save t; picture   t; enddef;
 def save_boolean   text t = save t; boolean   t; enddef;
 def save_string    text t = save t; string    t; enddef;
+def save_path      text t = save t; path      t; enddef;
 
 % 
1 lua scripts, functions and variables
 
@@ -16,7 +17,7 @@
 def luafunction = gobble inner_luafunction enddef;
 vardef inner_luafunction @# (text args) = endgroup
     % we remove the grouping so that the function may return any fragment of 
-    % lua code, in particular lists to-be-parsed as text parameters
+    % metapost code, in particular lists to-be-parsed as text parameters
     runscript ("return " & (str @#) & build_lua_arguments(args))
     gobble begingroup enddef;
 
@@ -38,7 +39,7 @@
 vardef lua_string(expr s) =
     runscript ("[[quote_for_lua]]"&s) enddef;
 
-vardef hexadecimal expr n =
+vardef hexadecimal tertiary n =
     % TODO: support other number systems
     save d, a, m; a = ASCII("a"); m := abs n;
     save_string res; res =
@@ -49,7 +50,7 @@
         if i = 3: & "." fi
     endfor; res enddef;
 
-vardef scaledpoints expr n =
+vardef scaledpoints tertiary n =
     % use the fact that 800bp = 803pt
     save a, m; m := abs n;
     a1 = m div (1025/1024); % 1025/1024 = 800epsilon * 82
@@ -64,7 +65,7 @@
         & decimal(a3) & ")";
     res enddef;
 
-vardef quote_for_lua expr a =
+vardef quote_for_lua tertiary a =
     save_string res; res =
     if string a:
         lua_string(a)
@@ -71,25 +72,29 @@
     elseif numeric a:
         hexadecimal(a)
     elseif pair a:
-        "{"&hexadecimal(xpart a) &
-        ","&hexadecimal(ypart a) & "}"
+        "{"& (hexadecimal xpart a) &
+        ","& (hexadecimal ypart a) & "}"
     elseif boolean a:
         if a: "true" else: "false" fi
     elseif color a:
-        "{"&hexadecimal(redpart a) &
-        ","&hexadecimal(greenpart a) &
-        ","&hexadecimal(bluepart a) & "}"
+        "{"& (hexadecimal redpart a) &
+        ","& (hexadecimal greenpart a) &
+        ","& (hexadecimal bluepart a) & "}"
     elseif cmykcolor a:
-        "{"&hexadecimal(cyanpart a) &
-        ","&hexadecimal(magentapart a) &
-        ","&hexadecimal(yellowpart a) &
-        ","&hexadecimal(blackpart a) & "}"
+        "{"& (hexadecimal cyanpart a) &
+        ","& (hexadecimal magentapart a) &
+        ","& (hexadecimal yellowpart a) &
+        ","& (hexadecimal blackpart a) & "}"
     elseif pen a:
         hide(errmessage("I cannot pass a pen value to lua");)
         "nil"
     elseif transform a:
-        hide(errmessage("I cannot pass a transform value to lua");)
-        "nil"
+        "{"& (hexadecimal xxpart a) &
+        ","& (hexadecimal xypart a) &
+        ","& (hexadecimal yxpart a) &
+        ","& (hexadecimal yypart a) &
+        ","& (hexadecimal xpart a) &
+        ","& (hexadecimal ypart a) & "}"
     elseif picture a:
         hide(errmessage("I cannot pass a picture value to lua");)
         "nil"
@@ -97,14 +102,33 @@
         "nil"
     fi; res enddef;
 
+% constructing lua tables
+
+def make_lua_dict (text t) = begingroup
+    % Note that though the argument may contain = signs and commas, the rest of 
+    % the arguments must consist of suffixes.
+    save_boolean first; first := true;
+    "{ " forsuffixes kv =
+        hide(let = _EQ_ undefined)
+        t hide(let = _EQ_ _EQ_) : &
+        if first: hide(first := false;) else: ", "& fi
+        begingroup make_lua_keyval kv endgroup
+    endfor & " }" endgroup
+enddef;
+let _EQ_ = =;
+
+vardef make_lua_keyval @# expr e =
+    (str @#) & " = " & quote_for_lua e
+enddef;
+
 % tex registers
 
 string _SUFFIX_HACK_[];
 vardef index_or_suffix (suffix s) =
     save_string res; res =
-    if string _SUFFIX_HACK_ s: % this is a number
+    if string _SUFFIX_HACK_ s: % s is a number
         "["&decimal(s)&"]"
-    else: % this is a suffix
+    else: % s is a suffix
         "."&(str s)
     fi ; res enddef;
 
@@ -128,20 +152,33 @@
 
 % 
1 typesetting
 
+boolean debug_tex_bboxes;
+debug_tex_bboxes := false;
+
 let textext = maketext;
 let TEX = maketext;
 
+def _set_maketext_result_ (expr nr)(text tr) = image (
+    fill unitsquare withprescript "TEXBOX:"&decimal nr;
+    save_path bb; bb := unitsquare transformed tr;
+    if debug_tex_bboxes: draw bb dashed evenly; fi
+    setbounds currentpicture to bb;) enddef;
+
 % baseline pair or numeric -> fill statement
 vardef baseline expr o =
     fill if numeric o : (0,o) else: o fi
         -- cycle withprescript "BASELINE:"; enddef;
 
-% boxresource number -> fill statement + setbounds
-vardef boxresource expr nr = image(
-    fill unitsquare withprescript "BOXRESOURCE:" & decimal nr ;
-    setbounds currentpicture to unitsquare transformed runscript
-        ("return { 'box_size', tex.getboxresourcedimensions(" & decimal nr & ") }");
-    ) enddef;
+vardef find_baseline expr p =
+    save_pair bl, ibl;
+    for c within p:
+        if clipped c or bounded c:
+            ibl := find_baseline c;
+            if known ibl: bl := ibl; fi
+        elseif "BASELINE:" = substring (0,9) of prescriptpart c:
+            bl := point 0 of pathpart c;
+        fi
+    endfor bl enddef;
 
 % actual typesetting
 
@@ -168,6 +205,129 @@
        + (1-labxf@#-labyf@#)*llcorner p
   )  ) enddef;
 
+% 
1 box resources (xforms)
+
+% boxresource number -> fill statement + setbounds
+vardef boxresource primary nr = image(
+    fill unitsquare withprescript "BOXRESOURCE:" & decimal nr ;
+    save btf; transform btf;
+    btf = luafunction get_boxresource_dimensions (nr);
+    save_path bb; bb = unitsquare transformed btf;
+    if debug_tex_bboxes: draw bb dashed evenly; fi
+    setbounds currentpicture to bb;
+    currentpicture := currentpicture
+        shifted luafunction get_boxresource_center (nr);
+    ) enddef;
+
+% id = saveboxresource (attributes) image ( ... );
+vardef saveboxresource (text attrs) expr p =
+    save id; id = luafunction reserve_xform_id();
+    % save the dimensions
+    save t, o; transform t; pair o;
+    (0,0) transformed t = llcorner p;
+    (1,0) transformed t = lrcorner p;
+    (0,1) transformed t = ulcorner p;
+    save bl; bl = -ypart find_baseline p;
+    o = (-xpart llcorner p, if known bl: bl else: 0 fi);
+    luafunction set_boxresource_dimensions (id, t shifted o, -o);
+    % shipout the xform
+    shipout image(draw p;
+        special "XFORMATTRS:" & make_lua_dict (attrs);
+        special "XFORMINDEX:" & decimal id;
+    ); id enddef;
+
+% 
1 extended graphics state
+
+vardef modify_scripts text t =
+    % Note: this is a no-op when the picture is empty
+    save_picture curr;
+    save_string pre, post;
+    % extract script parts
+    curr := image(drawdot origin t;);
+    pre = prescriptpart curr;
+    post = postscriptpart curr;
+    % add the prescript
+    if pre <> "":
+        curr := currentpicture; clearit;
+        draw curr withprescript pre;
+    fi
+    % add the postscript
+    if post <> "":
+        % here we find the reason for this routine: metapost *should* apply the 
+        % postscript to the last element of a picture, but chooses the first 
+        % instead.
+        curr := currentpicture; clearit;
+        setbounds curr to llcorner curr -- lrcorner curr --
+            urcorner curr -- ulcorner curr -- cycle;
+        % these bounds force an extra nesting level of the next function
+        draw with_postscript_to_last(curr, post);
+    fi
+enddef;
+
+vardef with_postscript_to_last (expr p, script) =
+    image( save i; i := 0;
+    for c within p:
+        if incr i < length(p): draw c;
+        elseif clipped c:
+            save_picture q; q:= with_postscript_to_last(c, script);
+            clip q to pathpart c;
+            addto currentpicture also q;
+        elseif bounded c:
+            save_picture q; q:= with_postscript_to_last(c, script);
+            setbounds q to pathpart c;
+            addto currentpicture also q;
+        else:
+            draw c withpostscript script;
+        fi
+    endfor) enddef;
+
+def append_postscript expr s =
+    if length(currentpicture) = 0: special s;
+    else: modify_scripts withpostscript s;
+    fi enddef;
+
+def savegstate =
+    append_postscript "gstate:save"; enddef;
+def restoregstate =
+    append_postscript "gstate:restore"; enddef;
+def setgstate =
+    append_postscript "extgstate:" & make_lua_dict enddef;
+
+def withgstate (text t) =
+    withprescript "extgstate:" & make_lua_dict (t)
+    withprescript "gstate:save"
+    withpostscript "gstate:restore" enddef;
+
+% 
1 transparency
+
+newinternal string blend_mode;
+newinternal string transparency_group_attrs;
+
+% setalpha (α)
+vardef setalpha expr alpha =
+    save a; a = alpha;
+    save_string bm; bm = "/" & blend_mode;
+    setgstate(CA=a, ca=a if bm <> "/":, BM=bm fi) enddef;
+
+% <drawing_cmd> withalpha (α)
+def withalpha = gobble _withalpha_ enddef;
+string _blend_mode_;
+vardef _withalpha_ (expr alpha) =
+    _with_alpha_ := alpha;
+    _blend_mode_ := "/" & blend_mode;
+    endgroup
+    withgstate(CA=_with_alpha_,ca=_with_alpha_ if _blend_mode_ <> "/":, BM=_blend_mode_ fi)
+    gobble begingroup enddef;
+
+% transparent (α) image (...)
+vardef transparent (expr alpha) expr p =
+    save_string attrs; attrs = "<< /S/Transparency "&transparency_group_attrs&">>";
+    save g; g = saveboxresource (Group=attrs) p;
+    savegstate; setalpha (alpha);
+    draw boxresource g;
+    restoregstate;
+enddef;
+
 % 
1 even-odd rule and multidraw
 
 def nofill expr c = fill c withprescript "OTYPE:nofill" enddef;
@@ -205,6 +365,9 @@
     def withcolor = hide(painttype:=1) _withcolor enddef;
     save matrix; transform matrix; enddef;
 def endpattern (expr xstep, ystep) =
+    modify_scripts
+        withprescript "pdf:/Artifact BMC"
+        withpostscript "pdf:EMC";
     if unknown matrix : matrix:=identity; fi
     special "definepattern:" for e = charcode, tilingtype, painttype,
         xstep, ystep, xxpart matrix, xypart matrix, yxpart matrix, yypart matrix:
@@ -213,12 +376,11 @@
 newinternal tilingtype; tilingtype:=1;
 _patterns_._last_ := 0;
 
-
 % 
1 miscellaneous
 
-vardef debug_pdf = luafunction enable_debugging () enddef;
+def debug_pdf = luafunction enable_debugging(); enddef;
 
-def texmessage text msg = luafunction texio.write_nl (msg) enddef;
+vardef texmessage text msg = luafunction texmessage (msg) enddef;
 
 vardef rgb_to_gray expr c =
     luafunction rgb_to_gray (redpart c, greenpart c, bluepart c)

Modified: trunk/Master/texmf-dist/metapost/minim-mp/minim.mp
===================================================================
--- trunk/Master/texmf-dist/metapost/minim-mp/minim.mp	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/metapost/minim-mp/minim.mp	2024-01-08 21:47:22 UTC (rev 69352)
@@ -70,8 +70,8 @@
 
 % missing trigonometric functions
 vardef tand    primary x = sind(x)/cosd(x) enddef;
-vardef arcsind primary x = angle((1+-+x, x)) enddef;
-vardef arccosd primary x = angle((x, 1+-+x)) enddef;
+vardef arcsind primary x = angle(1+-+x, x) enddef;
+vardef arccosd primary x = angle(x, 1+-+x) enddef;
 vardef arctand primary x = angle(1, x) enddef;
 
 % segments of the circle (counterclockwise)

Modified: trunk/Master/texmf-dist/tex/luatex/minim/minim-doc.sty
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/minim/minim-doc.sty	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/tex/luatex/minim/minim-doc.sty	2024-01-08 21:47:22 UTC (rev 69352)
@@ -9,6 +9,7 @@
 %\decompressedpdf
 
 \pdfalevel 3a
+\pdfualevel 1
 \overfullrule = 0pt
 
 % 
1 page layout
@@ -124,9 +125,23 @@
 % page artifacts
 \edef\tmp{\markartifact{Pagination /Subtype/Footer}{\the\footline}}
 \footline\expandafter{\tmp}
-\def\footnoterule{\markartifact{Layout}{\kern-3\p@
-  \hrule width 2truein \kern 2.6\p@}} % the \hrule is .4pt high
 
+% footnotes
+\edef\footnoterule{\markartifact{Layout}\footnoterule}
+\catcode`\@=11
+\def\footnote#1{\let\@sf\empty % parameter #2 (the text) is read later
+  \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\/\fi
+  \marknoteref{#1}\@sf\vfootnote{#1}}
+\def\vfootnote#1{%
+    \insert\footins\bgroup
+    \interlinepenalty\interfootnotelinepenalty
+    \splittopskip\ht\strutbox % top baseline for broken footnotes
+    \splitmaxdepth\dp\strutbox \floatingpenalty\@MM
+    \leftskip\z at skip \rightskip\z at skip \spaceskip\z at skip \xspaceskip\z at skip
+    \nextpartag{}\marknotelbl{#1}\enspace\startelement{P}%
+    \footstrut\futurelet\next\fo at t}
+\catcode`\@=12
+
 % \startlist \item x. ... \stoplist
 \def\listskip{\vskip 3pt plus 2pt\vskip-\parskip}
 \def\startlist{\smallskip\startelement{L}}
@@ -141,22 +156,26 @@
 \protected\def\chapter#1 \par{%
     \vfil\break
     \ensurestopelement{Section}%
-    \startelement{Chapter}%
+    \global\advance\chapterno1 \global\sectionno0
+    \startelement title{Chapter \the\chapterno}{Chapter}%
     \outline open {#1}%
     \addtotoc{\chapter{#1}{\lastdestinationname}}%
     \nextpartag{H}\quitvmode
     \red{\Title#1\hfill\copy\notehead}\bigskip\nobreak}
+\newcount \chapterno
 
 % \section Title \par
 \addstructuretype Sect Section
 \protected\def\section#1 \par{%
     \bigskip\penalty-50\relax
-    \startelement{Section}%
+    \global\advance\sectionno1
+    \startelement title{Section \the\chapterno.\the\sectionno}{Section}%
     \outline closed {#1}%
     \addtotoc{\section{#1}{\lastdestinationname}}%
     \nextpartag{H}\quitvmode
     \red{\title#1}%
     \par\nobreak}
+\newcount \sectionno
 
 % table of contents
 \newtoks\toc \newif\iftoc \toctrue
@@ -186,7 +205,9 @@
     \tocfalse \chapter \getmetadata title
         \hfill \tenrm version \getmetadata version
     \par\endgroup
-    \marktableaslist \halign {\strut
+    \marktableaslist
+        \tagattribute List ListNumbering /None
+        \halign {\strut
         \qquad##\quad&##\hfil\cr
     author&\getmetadata author\cr
     contact&{\def\tmp{@}\def\TMP{.}%
@@ -207,8 +228,9 @@
 been included as an attachment to this file; copies in other languages can be 
 obtained at
 \stopformulatagging$$\hbox
-{\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}
+{\hyperlink alt{Link to the website of the EUPL.}
+    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}}

Modified: trunk/Master/texmf-dist/tex/luatex/minim/minim-pdfresources.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/minim/minim-pdfresources.lua	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/tex/luatex/minim/minim-pdfresources.lua	2024-01-08 21:47:22 UTC (rev 69352)
@@ -50,7 +50,7 @@
     return res._entry_
 end
 
--- global resources are mainly for pgf compatibility: contains adds entries to 
+-- global resources are mainly for pgf compatibility: it contains entries to 
 -- the resource dictionaries that will be added for every page.
 --
 local global_resources = init_resources() -- name ↦ '/Key <value>'
@@ -87,6 +87,10 @@
     return n
 end
 
+alloc.luadef('withpdfresource', function()
+    node.write(M.use_resource_node(token.scan_string(), token.scan_string()))
+end, 'protected')
+
 -- construction and caching of resource dictionaries.
 --
 local previous_dicts = init_resources() -- pdf dict ↦ objnum

Modified: trunk/Master/texmf-dist/tex/luatex/minim/minim-pdfresources.tex
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/minim/minim-pdfresources.tex	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/tex/luatex/minim/minim-pdfresources.tex	2024-01-08 21:47:22 UTC (rev 69352)
@@ -13,6 +13,7 @@
 % 
1 pgf compatibility
 
 % this ballet inserts our fix directly at the end of pgfsys-luatex.def
+% (see the \ProvidesFile redefinition in minim-alloc.tex)
 \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 at everybye\endcsname

Modified: trunk/Master/texmf-dist/tex/luatex/minim-math/minim-math.tex
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/minim-math/minim-math.tex	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/tex/luatex/minim-math/minim-math.tex	2024-01-08 21:47:22 UTC (rev 69352)
@@ -468,6 +468,8 @@
 \def\braket#1#2{\left⟨#1\middle\vert#2\right⟩}
 \def\norm#1{\left\vert#1\right\vert} \let \abs=\norm
 \def\Norm#1{\left\Vert#1\right\Vert}
+\def\ceil#1{\left⌈#1\right⌉}
+\def\floor#1{\left⌊#1\right⌋}
 
 % a slightly smaller unary minus
 \def\unaryminus{\mathord{\mathpalette\unaryminus:make{}}}

Modified: trunk/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.lua	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.lua	2024-01-08 21:47:22 UTC (rev 69352)
@@ -4,7 +4,9 @@
 local pdfres = require('minim-pdfresources')
 alloc.remember ('minim-mp')
 
-local M = {}
+local M = { } -- module contents
+local E = { } -- runscript environment
+local A = { } -- postprocessing functions
 
 --
1 AUXILIARY FUNCTIONS
 
@@ -29,7 +31,6 @@
 -- We can call append:somefunction(...) to append a node or append:somevariable 
 -- to query the graphics state. These go via its metatable:
 
-local A = { } -- appending functions
 local append_meta =
 {
     -- Either return an appending function or an entry in the graphics state.
@@ -48,6 +49,7 @@
         node_count = 0,             -- node count
         state = { },                -- graphics state variables
         user = { },                 -- user data for extensions
+        object_info = { },          -- data for current object
         }, append_meta)
 end
 
@@ -78,7 +80,8 @@
 
 -- The following callback is executed just before the final step of surrounding 
 -- the image in properly-dimensioned boxes. It receives the processed image 
--- object as argument. That object has the following fields:
+-- object as argument. That object may have the following fields (which can be 
+-- changed):
 --     head         the head of the node list
 --     name         the image name
 --     user         the image user table
@@ -97,6 +100,7 @@
     pdf.setcompresslevel(0)
     pdf.setobjcompresslevel(0)
 end
+E.enable_debugging = M.enable_debugging
 
 local function print_prop(append, obj, prop)
     if obj[prop] then
@@ -179,7 +183,8 @@
 local function format_numbers(...)
     return (string.format(...)
         :gsub('[.]0+ ', ' ')
-        :gsub('([.][1-9]+)0+ ', '%1 '))
+        :gsub('([.][1-9]+)0+ ', '%1 ')
+        :gsub(' +$', ''))
 end
 
 -- Only to be used for coordinates: ‘cm’ parameters should not be rounded.
@@ -188,6 +193,12 @@
     return format_numbers(fmt, ...)
 end
 
+local function make_pdf_dashpattern(dl)
+    return string.format('[%s] %s',
+        table.concat(dl.dashes or {},' '),
+        pdfnum('d', dl.offset))
+end
+
 local function point_fmt(operator, ...)
     local dd = pdf.getdecimaldigits()
     local fmt = string.format('%%.%sf %%.%sf %s', dd, dd, operator)
@@ -212,6 +223,43 @@
     append:node(lit)
 end
 
+--
2 parsing helpers
+
+local function parse_lua_table(name, str)
+    local f, msg = load('return '..str, name, 't')
+    if msg then return alloc.err(msg) end
+    t = f()
+    if not t or type(t) ~= 'table' then
+        return alloc.err('%s attributes must be given as lua table', name)
+    end
+    -- format all numbers with pdfnum
+    for k, v in pairs(t) do
+        if type(v) == 'number' then
+            t[k] = pdfnum('', v)
+        end
+    end
+    return t
+end
+
+--
2 boxing helpers
+
+local function make_surrounding_box(nd_id, head)
+    local nd = node.new(nd_id)
+    nd.dir, nd.head = 'TLT', head
+    return nd
+end
+
+local function wrap_picture(head, x0, y0)
+    -- Note: this does not set the image dimensions
+    local horizontal = make_surrounding_box('hlist', head)
+    local vertical   = make_surrounding_box('vlist', horizontal)
+    local outer      = make_surrounding_box('hlist', vertical)
+    vertical.shift   = -y0
+    horizontal.shift = x0
+    return outer
+end
+M.wrap_picture = wrap_picture
+
 --
2 colour conversion
 
 local function rgb_to_gray (r,g,b)
@@ -219,6 +267,7 @@
            + tex.count['GtoG'] * g / 10000
            + tex.count['BtoG'] * b / 10000 )
 end
+E.rgb_to_gray = rgb_to_gray
 
 local function cmyk_to_rgb (c,m,y,k)
     return (1-k)*(1-c), (1-k)*(1-m), (1-k)*(1-y)
@@ -283,7 +332,7 @@
 local colour_pattern_spaces = { 'PsG', nil, 'PsRG', 'PsK' }
 
 local function get_colour_params(cr)
-    return format_numbers(colour_template[#cr], table.unpack(cr))
+    return format_numbers(colour_template[#cr], table.unpack(cr)) .. ' '
 end
 
 local function get_stroke_colour(cr)
@@ -362,7 +411,7 @@
     end
     local dl = object.dash
     if dl then
-        local d = string.format('[%s] %s', table.concat(dl.dashes or {},' '), pdfnum('d', dl.offset))
+        local d = make_pdf_dashpattern(dl)
         if d ~= append.dashed then
             append.dashed = d
             table.insert(res, d)
@@ -653,6 +702,105 @@
     append.baseline = object.path[1].y_coord
 end
 
+-- 
2 box resources (xforms)
+
+-- The first problem that we run into is that we want to refer to xforms (from 
+-- the metapost side) whose numerical id* we do know know yet (because the 
+-- xform will only be made at the end of the run, during the lua-side 
+-- processing of mplib output).
+--
+-- Our solution is using negative ids for metapost-defined xforms, which will 
+-- be converted to actual boxresource ids by means of a conversion table.
+--
+-- The second problem is the need to position metapost-defined xforms by their 
+-- original (metapost) origin. This necessitates storing extra information 
+-- lua-side.
+--
+-- * Not to be confused with the object number which we, due to pdftex’s 
+-- special handling of xforms, will never come to know.
+
+local xform_nr_shim = { }
+
+local function resolve_xform_id(nr)
+    nr = tonumber(nr)
+    if nr < 0 then
+        return xform_nr_shim[-nr]
+    else
+        return nr
+    end
+end
+
+local function reserve_xform_id()
+    xform_nr_shim[1+#xform_nr_shim] = false
+    return -#xform_nr_shim
+end
+
+M.resolve_xform_id = resolve_xform_id
+E.resolve_xform_id = resolve_xform_id
+E.reserve_xform_id = reserve_xform_id
+
+-- When using xforms in metapost, the positive numbers refer to tex-defined and 
+-- the negative to metapost-defined box resources.
+
+specials.BOXRESOURCE = function(append, id, object)
+    local realid = resolve_xform_id(id)
+    local rule = tex.useboxresource(realid)
+    append:box(object, node.hpack(rule))
+end
+
+local xform_sizes = { }
+local xform_center = { }
+
+local function set_boxresource_dimensions (id, transf, center)
+    xform_sizes[id] = transf
+    xform_center[id] = center
+end
+
+local function get_boxresource_dimensions (id)
+    if xform_sizes[id] then
+        return xform_sizes[id]
+    else
+        return { 'box_size', tex.getboxresourcedimensions(id) }
+    end
+end
+
+local function get_boxresource_center (id)
+    if xform_center[id] then
+        return xform_center[id]
+    else
+        return "(0,0)"
+    end
+end
+
+E.set_boxresource_dimensions = set_boxresource_dimensions 
+E.get_boxresource_dimensions = get_boxresource_dimensions 
+E.get_boxresource_center = get_boxresource_center 
+
+local function save_as_boxresource(pic)
+    -- attributes
+    local attrs = { '/Subtype/Form', bbox_fmt(0, -pic.dp, pic.wd, pic.ht) }
+    for k, v in pairs(pic.user.xform_attrs or { }) do
+        table.insert(attrs, string.format('%s %s', alloc.pdf_name(k), v))
+    end
+    -- box to-be-saved
+    local box = wrap_picture(node.copy_list(pic.head), pic.x0, pic.y0)
+    box.width, box.height, box.depth = pic.wd, pic.ht, pic.dp
+    -- save the box
+    local xform = tex.saveboxresource(box,
+        table.concat(attrs, ' '), nil, false, 4) -- 4: no /Type, /BBox or /Matrix
+    xform_nr_shim[-pic.user.xform_id] = xform
+end
+
+specials.XFORMINDEX = function(append, id, object)
+    append.user.xform_id = tonumber(id)
+    append.user.save_fn = save_as_boxresource
+end
+
+specials.XFORMATTRS = function(append, attrs, object)
+    local t = parse_lua_table('XForm attributes', attrs)
+    append.user.xform_attrs = t or { }
+end
+
 --
2 patterns
 
 -- Saved patterns have the following fields:
@@ -678,27 +826,20 @@
     end
 end
 
-specials.definepattern = function(append, str, object)
-    local nr, tiling, paint, xs, ys, xx, xy, yx, yy = table.unpack(str:explode())
-    append.user.pattern_info = { nr = nr, xstep = xs, ystep = ys,
-        tilingtype = tiling, painttype = paint,
-        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)
+    local xform = tex.saveboxresource((node.hpack(node.copy_list(head))),
+        '/Subtype/Form '..bb, nil, true, 4) -- 4: no /Type, /BBox or /Matrix
     return string.format(' /Resources << /XObject << /PTempl %d 0 R >> >>', xform), '/PTempl Do'
 end
 
-local function definepattern(head, user, bbox)
-    local bb = bbox_fmt(table.unpack(bbox))
+local function definepattern(pic)
+    local bb = bbox_fmt(table.unpack(pic.bbox))
     local pat, literals, resources = { write = write_pattern_object }, { }
     -- pattern content
-    for n in node.traverse(head) do
+    for n in node.traverse(pic.head) do
         -- try if we can construct the content stream ourselves; otherwise, 
         -- stuff the pattern template into an xform.
         if n.id == WHATSIT_NODE then
@@ -710,11 +851,11 @@
             elseif n.subtype == node.subtype 'restore' then
                 table.insert(literals, 'Q')
             else
-                resources, pat.stream = make_pattern_xform(head, bb)
+                resources, pat.stream = make_pattern_xform(pic.head, bb)
                 goto continue
             end
         else
-            resources, pat.stream = make_pattern_xform(head, bb)
+            resources, pat.stream = make_pattern_xform(pic.head, bb)
             goto continue
         end
         pat.stream = table.concat(literals, '\n')
@@ -721,7 +862,7 @@
     end
     ::continue::
     -- construct the pattern object
-    local i = user.pattern_info
+    local i = pic.user.pattern_info
     local m = i.matrix
     pat.painttype = tonumber(i.painttype)
     pat.attr = table.concat({
@@ -733,13 +874,89 @@
     pdfres.add_resource('Pattern', 'MnmP'..i.nr, pat)
 end
 
-cb.register('finish_mpfigure', function(img)
-    if img.user.pattern_info then
-        definepattern(img.head, img.user, img.bbox)
-        img.discard = true
+specials.definepattern = function(append, str, object)
+    local nr, tiling, paint, xs, ys, xx, xy, yx, yy = table.unpack(str:explode())
+    append.user.pattern_info = { nr = nr, xstep = xs, ystep = ys,
+        tilingtype = tiling, painttype = paint,
+        matrix = { xx = xx, xy = xy, yx = yx, yy = yy, x = 0, y = 0 } }
+    append.user.save_fn = definepattern
+end
+
+-- 
2 extgstates
+
+-- Specials for the graphics state:
+--
+--    gstate:save           q
+--    gstate:restore        Q
+--
+--    extgstate:label       /label gs
+--    extgstate:{...}       /newlabel gs
+--
+-- If a lua table is passed to the extgstate, minim will try and find an 
+-- earlier-defined matching ExtGState object. If it cannot be found, a new one 
+-- will be created and used. The values of the table will be written to the pdf 
+-- as-they-are (this means booleans and numbers are alright), but the keys will 
+-- be converted to pdf name objects.
+
+prescripts.gstate = function(append, str, object)
+    if str == 'save' or str == 'store' then
+        append:save()
+    elseif str == 'restore' then
+        append:restore()
+    else
+        alloc.err('Unknown gstate operator: »%s«', str)
     end
-end)
+end
+postscripts.gstate = prescripts.gstate
 
+local gs_next, gs_index = 1, { }
+
+function M.get_gstate_name(t)
+    -- format the pdf dict
+    local d = { }
+    for k,v in pairs(t) do
+        if type(v) == 'table' and t.dashes then
+            v = '[ '..make_pdf_dashpattern(v)..' ]'
+        end
+        table.insert(d, string.format('%s %s', alloc.pdf_name(k), v))
+    end
+    table.sort(d)
+    d[0] = '<<'; table.insert(d, '>>')
+    t.value = table.concat(d, ' ', 0)
+    -- check the index
+    if not gs_index[t.value] then
+        local name = string.format('MnmGs%d', gs_next)
+        gs_index[t.value] = name
+        gs_next = gs_next + 1
+        pdfres.add_resource('ExtGState', name, t)
+    end
+    -- return the resource name
+    return gs_index[t.value]
+end
+
+prescripts.extgstate = function(append, str, object)
+    -- determine resource name
+    local name, t = str
+    if string.find(str, '^[A-Za-z0-9_]+$') then
+        t = pdfres.get_resource('ExtGState', name)
+        if not t then return alloc.err('Unknown named ExtGState: %s', name) end
+    else
+        t = parse_lua_table('ExtGState', str)
+        if not t then return end
+        name = M.get_gstate_name(t)
+    end
+--  -- update state parameters (disabled for now; may be enabled if it has practical use)
+--  if t.LW then append.linewidth = t.LW end
+--  if t.ML then append.miterlimit = t.LW end
+--  if t.LJ then append.linejoin = t.LW end
+--  if t.LC then append.linecap = t.LW end
+--  if t.D and type(t.D) == 'table' then append.dashed = make_pdf_dashpattern(t.D) end
+    -- apply the state change
+    append:literal('%s gs', alloc.pdf_name(name))
+    append:node(pdfres.use_resource_node('ExtGState', name))
+end
+postscripts.extgstate = prescripts.extgstate
+
 -- 
2 text
 
 process.text = function(append, object)
@@ -758,26 +975,13 @@
     return sx, rx, ry, sy, tx, ty
 end
 
-local function make_surrounding_box(nd_id, head)
-    local nd = node.new(nd_id)
-    nd.dir, nd.head = 'TLT', head
-    return nd
-end
-
-local function wrap_picture(head, tx, ty)
-    local horizontal = make_surrounding_box('hlist', head)
-    local vertical   = make_surrounding_box('vlist', horizontal)
-    local outer      = make_surrounding_box('hlist', vertical)
-    vertical.shift   = tex.sp('-'..ty..'bp')
-    horizontal.shift = tex.sp(''..tx..'bp')
-    return outer
-end
-
 local function apply_transform(rect, box)
     local sx, rx, ry, sy, tx, ty = get_transform(rect)
     local transform = node.new('whatsit', 'pdf_setmatrix')
     transform.next, box.prev = box, transform
     transform.data = string.format('%f %f %f %f', sx, rx, ry, sy)
+    tx = tex.sp(''..tx..'bp')
+    ty = tex.sp(''..ty..'bp')
     return wrap_picture(transform, tx, ty)
 end
 
@@ -803,11 +1007,6 @@
     append:box(object, node.hpack(n))
 end
 
-specials.BOXRESOURCE = function(append, resource, object)
-    local rule = tex.useboxresource(tonumber(resource))
-    append:box(object, rule)
-end
-
 --
 
 --
1 METAPOST INSTANCES
@@ -935,8 +1134,12 @@
                 pic.y0 = tex.sp(string.format('%s bp',  -bas    ))
             end
             cb.call_callback('finish_mpfigure', pic)
+            if pic.user.save_fn then
+                pic.discard = true
+                pic.user.save_fn(pic)
+            end
             if not pic.discard then
-                pic.head = wrap_picture(append.head, -llx, -bas)
+                pic.head = wrap_picture(pic.head, pic.x0, pic.y0)
             end
             if debugging then
                 alloc.msg('┌ image %s, with %s objects, %s nodes',
@@ -967,11 +1170,11 @@
 -- a string that metapost can understand.
 --
 -- Tables of the form { 'box_size', width, height, depth, margin } are  
--- converted to a transformation. (The margin will be ignored for now, this may 
+-- converted to a transform. (The margin will be ignored for now, this may 
 -- change in the future.)
 
 local function mkluastring(s)
-    return "'"..(s:gsub('\\', '\\\\'):gsub("'", "\\'")).."'"
+    return "'"..(s:gsub('\\', '\\\\'):gsub("'", "\\'"):gsub('\n', '\\n')).."'"
 end
 
 local function runscript(code)
@@ -991,15 +1194,22 @@
                 return string.format('%.16f', result)
             elseif t == 'string' then
                 return result
-            elseif t == 'table' and t[1] == 'box_size' then
-                -- TODO: take the margin into account if present (t[5])?
-                return make_transform(t[2], t[3], t[4])
-            elseif t == 'table' and #t < 5 then
-                local fmt = #t == 1 and '%.16f'
-                         or #t == 2 and '(%.16f, %.16f)'
-                         or #t == 3 and '(%.16f, %.16f, %.16f)'
-                         or #t == 4 and '(%.16f, %.16f, %.16f, %.16f)'
-                return fmt:format(table.unpack(t))
+            elseif t == 'table' and result[1] == 'box_size' then
+                return make_transform(result[2], result[3], result[4])
+            elseif t == 'table' and #result < 5 then
+                local fmt = #result == 1 and '%.16f'
+                         or #result == 2 and '(%.16f, %.16f)'
+                         or #result == 3 and '(%.16f, %.16f, %.16f)'
+                         or #result == 4 and '(%.16f, %.16f, %.16f, %.16f)'
+                return fmt:format(table.unpack(result))
+            elseif t == 'table' and #result == 6 then
+                return table.concat({
+                        'begingroup save t; transform t;',
+                        'xxpart t = %.16f', 'xypart t = %.16f',
+                        'yxpart t = %.16f', 'yypart t = %.16f',
+                        'xpart t = %.16f',  'ypart t = %.16f',
+                        't endgroup' },
+                    ';'):format(table.unpack(result))
             else
                 return tostring(result)
             end
@@ -1016,8 +1226,8 @@
 -- environment. The runscript environment will then be extended with all 
 -- elements of the M.mp_functions table.
 
-local E = { }
 M.mp_functions = E
+E.texmessage = alloc.msg
 
 local function prepare_env(e) -- in M.open()
     local env = e or copy_table(_G, { })
@@ -1038,9 +1248,6 @@
     return E.quote(mkluastring(s))
 end
 
-E.rgb_to_gray = rgb_to_gray
-E.enable_debugging = M.enable_debugging
-
 --
2 typesetting with tex
 
 -- The result of the maketext function is fed back into metapost; on that side, 
@@ -1065,9 +1272,8 @@
         local assignment = string.format('\\global\\setbox%s=\\hbox{\\the\\everymaketext %s}', nr, text)
         tex.runtoks(function() tex.print(current_instance.catcodes, assignment:explode('\n')) end)
         local box = tex.box[nr]
-        return 'image ( fill unitsquare withprescript "TEXBOX:' ..nr..'";'..
-            'setbounds currentpicture to unitsquare transformed '..
-                make_transform(box.width, box.height, box.depth) .. ';)'
+        return string.format('_set_maketext_result_(%d, %s)', nr,
+            make_transform(box.width, box.height, box.depth))
     elseif mode == 1 then -- verbatimtex
         tex.runtoks(function() tex.print(current_instance.catcodes, text:explode('\n')) end)
     end
@@ -1311,7 +1517,7 @@
 function M.get_image(nr, name)
     local image = retrieve(nr, name)
     if image then
-        local box = node.hpack(image.head)
+        local box = image.head
         box.width  = image.wd
         box.height = image.ht
         box.depth  = image.dp
@@ -1329,7 +1535,7 @@
         tex.pageheight = (image.ht + image.dp)
         tex.pagewidth  = image.wd
         tex.voffset    = image.ht
-        tex.box[255]   = node.vpack(node.hpack(image.head))
+        tex.box[255]   = image.head
         tex.shipout(255)
         tex.count[0] = tex.count[0] + 1
     end

Modified: trunk/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.sty
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.sty	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.sty	2024-01-08 21:47:22 UTC (rev 69352)
@@ -3,10 +3,6 @@
 
 \input minim-mp
 
-\makeatletter
-
-\RequirePackage{environ}
-
 % work around latex’s \protect mechanism
 \let\mnm at protect=\protect
 \everymaketext{\let\protect=\mnm at protect}
@@ -21,12 +17,12 @@
     \let\mpcolor = \minimpcolor
     \csname:metapost:\endcsname[#1]}
     {\csname end:metapost:\endcsname \endgroup}
-\NewEnviron{:metapost:}[1][]{%
+\NewDocumentEnvironment{:metapost:}{O{}+b}{%
     \mnm at setnormalfont
     \let\protect=\noexpand
     \directmetapost[#1]{%
-        defaultfont:="\mnm at normalfont"; \BODY; }%
-    \let\protect=\mnm at protect}
+        defaultfont:="\mnm at normalfont"; #2; }%
+    \let\protect=\mnm at protect}{}
 
 % separate metapost instances
 \newcommand\newmetapostenvironment[2][]{%
@@ -37,13 +33,13 @@
          \let\mpcolor = \minimpcolor
          \csname:#2:\endcsname}
         {\csname end:#2:\endcsname \endgroup}%
-    \NewEnviron{:#2:}{%
+    \NewDocumentEnvironment{:#2:}{+b}{%
         \mnm at setnormalfont
         \let\protect=\noexpand
         \runmetapostimage
             \csname #2 at instance\endcsname
-            {defaultfont:="\mnm at normalfont";\BODY;}%
-        \let\protect=\mnm at protect}}
+            {defaultfont:="\mnm at normalfont";##1;}%
+        \let\protect=\mnm at protect}{}}
 
 % \mpcolor support
 \def\minimpcolor#1#{\dominimpcolor{#1}}
@@ -54,6 +50,3 @@
 \newtoks\mpcolorspectoks \newtoks\mpcolorruntoks \newtoks\mpcolorvaltoks
 \mpcolorruntoks{\expandafter\extractcolorspec\the\mpcolorspectoks\mptmpcolor
     \expandafter\mpcolorvaltoks\expandafter\@gobble\mptmpcolor}
-
-\makeatother
-

Modified: trunk/Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.lua	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.lua	2024-01-08 21:47:22 UTC (rev 69352)
@@ -7,9 +7,11 @@
 
 -- 
1 helper functions
 
+local HLIST_NODE = node.id 'hlist'
 local GLUE_NODE = node.id 'glue'
 local GLYPH_NODE = node.id 'glyph'
 local RULE_NODE = node.id 'rule'
+local DISC_NODE = node.id 'disc'
 local WHATSIT_NODE = node.id 'whatsit'
 local USER_DEFINED = node.subtype 'user_defined'
 local START_LINK = node.subtype 'pdf_start_link'
@@ -51,9 +53,8 @@
     return one
 end
 
--- in-depth node list traversal
--- returns current and parent node
--- only dives into hbox, vbox and ins nodes
+-- in-depth node list traversal; returns current and parent node
+-- only dives into hbox, vbox and ins nodes, and in disc no-break text
 -- returns node and enclosing box
 local function full_traverse(head)
     return function(stack, last)
@@ -178,7 +179,7 @@
     -- inline structure elements ILSE
     Span       = { type = 'inline' },
     Quote      = { type = 'inline' },
-    Note       = { type = 'inline' },
+    Note       = { type = 'extern' },
     Reference  = { type = 'inline' },
     BibEntry   = { type = 'inline' },
     Code       = { type = 'inline', attributes = { ['CSS-2.00'] = { ['white-space'] = '(pre)' } } },
@@ -198,9 +199,10 @@
     none    = { none=1, section=1, group=1, block=1, inline=1, contain=1 },
     section = { none=1, section=1, group=1, block=1 },
     group   = { none=1, group=1, block=1 },
-    block   = { none=1, inline=1 },
-    inline  = { none=1, inline=1 },
-    contain = { none=1, inline=1, block=1, group=1 },
+    extern  = { none=1, group=1, block=1 },
+    block   = { none=1, inline=1, extern=1 },
+    inline  = { none=1, inline=1, extern=1 },
+    contain = { none=1, inline=1, block=1, group=1, extern=1 },
 }
 
 local function check_structure_compatibility(parent, child, childtype)
@@ -354,10 +356,19 @@
     local id_tree, id_tree_obj = { }, pdf.reserveobj()
     structure[1].parent = { objnum = root_obj }
     for _, se in ipairs(structure) do
-        if not se.hidden then se.objnum = pdf.reserveobj() end
+        if not se.hidden then
+            se.objnum = pdf.reserveobj()
+            if se.id then
+                id_tree[se.id] = se.objnum
+            end
+        end
     end
     -- update the document catalog
-    add_to_catalog('/MarkInfo << /Marked true >>')
+    if tex.count['pdfuaconformancelevel'] > 0 then
+        add_to_catalog('/MarkInfo << /Marked true /Suspects false >>')
+    else
+        add_to_catalog('/MarkInfo << /Marked true >>')
+    end
     add_to_catalog('/StructTreeRoot %s 0 R', root_obj)
     -- write the structure tree root
     pdf.immediateobj(root_obj, string.format(
@@ -368,10 +379,11 @@
         if not se.hidden then
             local res = { '<<' }
             insert_formatted(res, '/Type/StructElem /S%s /P %d 0 R', pdf_name(se.struct), se.parent.objnum)
-            if se.id then id_tree[se.id] = se.objnum; insert_formatted(res, '/ID %s', pdf_string(se.id)) end
-            if se.lang and se.lang ~= se.parent.lang then insert_formatted(res, '/Lang (%s)', se.lang) end
+            if se.id then insert_formatted(res, '/ID %s', pdf_string(se.id)) end
+            if se.lang and se.lang ~= se.parent.lang then insert_formatted(res, '/Lang %s', pdf_string(se.lang)) 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.title then insert_formatted(res, '/T %s', pdf_string(se.title)) 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
@@ -382,6 +394,19 @@
                 end
                 if #se.class > 1 then table.insert(res, ']') end
             end
+            if se.ref then
+                table.insert(res, '/Ref')
+                table.insert(res, '[')
+                for _, c in ipairs(se.ref) do
+                    local onum = id_tree[c]
+                    if onum then
+                        insert_formatted(res, '%d 0 R', onum)
+                    else
+                        alloc.err('Invalid structure element ID: %s', c)
+                    end
+                end
+                table.insert(res, ']')
+            end
             if not is_empty(se.attributes) then
                 table.insert(res, '/A')
                 make_attributes(res, se.attributes)
@@ -481,7 +506,7 @@
 end)
 
 local function write_language()
-    add_to_catalog('/Lang (%s)', structure[1].lang or 'und')
+    add_to_catalog('/Lang %s', pdf_string(structure[1].lang or 'und'))
 end
 
 -- 
1 marking structure elements
@@ -559,9 +584,11 @@
 alloc.luadef('tagging:startelement', function()
     local s = options_scanner()
         :string('id')
+        :string('ref', true)
         :string('type') -- 'section', 'group', 'block' etc.
         :argument('alt')
         :argument('actual')
+        :argument('title')
         :string('lang')
         :subtable('attr')
         :string('class', true)
@@ -736,7 +763,7 @@
         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 == RULE_NODE or n.id == GLYPH_NODE
+        elseif n.id == RULE_NODE or n.id == GLYPH_NODE or n.id == DISC_NODE
         or marker and marker.what == 'content' then
             local _se, _order = n[current_struct], n[current_order]
             if n.id == RULE_NODE and (n.width == 0 or n.height == 0 and n.depth == 0) then
@@ -768,7 +795,7 @@
     end
 end)
 
--- 
1 destinations
+-- 
1 links and destinations
 
 local dest_count = alloc.new_count('link dest count')
 local function new_dest_name()
@@ -791,8 +818,42 @@
 alloc.luadef('newdestinationname', function() tex.sprint(new_dest_name()) end)
 alloc.luadef('lastdestinationname', function() tex.sprint(last_dest_name()) end)
 
--- 
1 bookmarks
+-- provide the arguments to \pdfextension startlink
+local link_types = { }
+alloc.luadef('hyper:makelink', function()
+    local s = options_scanner()
+        :argument('alt'):argument('contents') -- same thing
+        :argument('attr')
+        :scan()
+    local type = token.scan_word()
+    local userf = link_types[type]
+    if not userf then
+        alloc.err('Unknown hyperlink type: %s', type)
+        userf = link_types.first
+    end
+    local user = userf()
+    attr = { 'attr {\\csname minim:linkattr\\endcsname' }
+    if s.attr then table.insert(attr, s.attr) end
+    if s.alt or s.contents then insert_formatted(attr, '/Contents %s', pdf_string(s.alt or s.contents)) end
+    insert_formatted(attr, '} user {%s}', user)
+    tex.sprint(table.concat(attr, ' '))
+end)
 
+function link_types.dest()
+    return string.format('/Subtype/Link /A <</S/GoTo /D %s>>', pdf_string(token.scan_argument()))
+end
+function link_types.url()
+    return string.format('/Subtype/Link /A <</S/URI /URI %s>>', pdf_string(token.scan_argument()))
+end
+
+function link_types.user() return token.scan_argument() end
+function link_types.next() return '/Subtype/Link /A <</S/Named /N/NextPage>>' end
+function link_types.prev() return '/Subtype/Link /A <</S/Named /N/PrevPage>>' end
+function link_types.first() return '/Subtype/Link /A <</S/Named /N/FirstPage>>' end
+function link_types.last() return '/Subtype/Link /A <</S/Named /N/LastPage>>' end
+
+-- 
1 bookmarks (outlines)
+
 -- start with a dummy top-level bookmark
 local bookmarks = { { count = 0 } }
 structure[1].bookmark = bookmarks[1]
@@ -811,8 +872,8 @@
     -- 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)', pdf_string(bm.title),
-            bm.parent and bm.parent.outline_obj or bookmarks[1].outline_obj, bm.dest)
+        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, pdf_string(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
         if bm.struct and bm.struct.objnum then
@@ -945,13 +1006,13 @@
 
 function M.mark_discretionaries(head, gc)
     if not spaces_enabled() then return end
-    for disc in node.traverse_id(7, head) do
+    for disc in node.traverse_id(DISC_NODE, head) do
         if disc.subtype ~= 2 then -- ‘automatic’ (exclude explicit hyphens)
             local pre, post, replace = disc.pre, disc.post, disc.replace
             local se, order = disc[current_struct], disc[current_order]
             -- get the replacement text
             local actual = { }
-            for c in node.traverse_id(29, replace) do
+            for c in node.traverse_id(GLYPH_NODE, replace) do
                 table.insert(actual, c.char)
             end
             -- special case: a single hyphen
@@ -982,7 +1043,7 @@
 
 function M.mark_spaces(head, gc)
     if not spaces_enabled() then return end
-    for g in node.traverse_id(12, head) do -- glue
+    for g in node.traverse_id(GLUE_NODE, head) do
         if g.prev and g.subtype == 13 or g.subtype == 14 then -- (x)spaceskip
             local p = g.prev
             p[space_attr] = p.id == GLYPH_NODE and p.font
@@ -1011,7 +1072,7 @@
 
 function M.insert_spaces(head, gc)
     if not spaces_enabled() then return end
-    for line in node.traverse_id(0, head) do
+    for line in node.traverse_id(HLIST_NODE, head) do
         insert_spaces(line.head)
     end
     return true
@@ -1085,10 +1146,10 @@
             attr = string.format('/N %d', oi.N),
             immediate = true }
         insert_formatted(res, '<< /Type/OutputIntent /DestOutputProfile %d 0 R', p)
-        insert_formatted(res, '/S/%s /OutputConditionIdentifier (%s)', oi.subtype, oi.id)
-        if oi.info then insert_formatted(res, '/Info (%s)', oi.info) end
-        if oi.condition then insert_formatted(res, '/OutputCondition (%s)', oi.condition) end
-        if oi.registry then insert_formatted(res, '/RegistryName (%s)', oi.registry) end
+        insert_formatted(res, '/S/%s /OutputConditionIdentifier %s', oi.subtype, pdf_string(oi.id))
+        if oi.info then insert_formatted(res, '/Info %s', pdf_string(oi.info)) end
+        if oi.condition then insert_formatted(res, '/OutputCondition %s', pdf_string(oi.condition)) end
+        if oi.registry then insert_formatted(res, '/RegistryName %s', pdf_string(oi.registry)) end
         table.insert(res, '>>')
     end
     table.insert(res, ']')
@@ -1121,9 +1182,31 @@
 alloc.luadef('minim:default:rgb:profile', function() M.add_default_rgb_output_intent() end)
 alloc.luadef('minim:default:cmyk:profile', function() M.add_default_cmyk_output_intent() end)
 
+-- 
1 pdf/ua
+
+alloc.luadef('tagging:enablepdfua', function()
+    add_to_catalog('/ViewerPreferences << /DisplayDocTitle true >>')
+    local pageattr = pdf.getpageattributes()
+    if pageattr then
+        pdf.setpageattributes(pageattr .. ' /Tabs /S')
+    else
+        pdf.setpageattributes('/Tabs /S')
+    end
+end)
+
+local function perform_pdfua_checks()
+    local xmp = require 'minim-xmp'
+    if xmp.get_metadata('dc:title') == '' then
+        alloc.err('PDF/UA error: no dc:title metadata')
+    end
+end
+
 --
 
-cb.register ('finish_pdffile', function()
+cb.register('finish_pdffile', function()
+    if tex.count['pdfuaconformancelevel'] > 0 then
+        perform_pdfua_checks()
+    end
     if tagging_enabled() then
         write_language()
         write_structure()

Modified: trunk/Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.tex
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.tex	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.tex	2024-01-08 21:47:22 UTC (rev 69352)
@@ -167,8 +167,8 @@
 \newcount\tagging:tablecolspan \tagging:tablecolspan 1
 \newcount\tagging:tablerowspan \tagging:tablerowspan 1
 \newcount\tagging:i
-\def\tagging:TH{TH}
-\def\tagging:TD{TD}
+\def\tagging:TH{{TH}\markcolumnhead}
+\def\tagging:TD{{TD}}
 % initialising variables
 \def\marktable{\startelement{Table}%
     \global\advance\tagging:tablec1
@@ -205,7 +205,7 @@
 \def\marktablecell{%
     \global\advance\tagging:tablecol1
     \continueelementfrom\tagging:row
-    \startelement{\tagging:cell}%
+    \expandafter\startelement\tagging:cell%
     \tagging:assignheaders}
 
 % spans
@@ -301,7 +301,9 @@
 \def\marktocentry#1#2#3#4#5{%
     \ifx\marktocentry#1\marktocentry
         \def\marktocentry:link##1{##1}\else
-        \def\marktocentry:link##1{\hyperlink dest{#1}##1\endlink}\fi
+        \def\marktocentry:link##1{\hyperlink
+            alt{\csname minim:str:page\endcsname#5}
+            dest{#1}##1\endlink}\fi
     \markelement{TOCI}{\nextpartag{}\quitvmode
         \ifx\marktocentry#2\marktocentry\else
             \markelement{Lbl}{\marktocentry:link{#2}}\fi
@@ -308,7 +310,19 @@
         \markelement{Reference}{\marktocentry:link{#3%
             \ifx\marktocentry#4\marktocentry\else
                 \markartifact{Layout}{#4}\fi#5}}}}
+\def\minim:str:page{Page }
 
+% \marknoteref {*}
+\def\marknoteref#1{%
+    \startelement ref{\newdestinationname}{Reference}%
+    {\stopformulatagging#1}\stopelement{Reference}}
+% \marknotelbl {*}
+\def\marknotelbl#1{%
+    \startelement id{\lastdestinationname}{Note}%
+    \aftergroup\tagging:ensurestopNote
+    \markelement{Lbl}{{\stopformulatagging#1}}}
+\def\tagging:ensurestopNote{\ensurestopelement{Note}}
+
 % 
1 tagging formulas
 
 % \autotagformulas
@@ -351,7 +365,7 @@
 \def\formulasource:set#1{%
     \begingroup
         % strip \setalttext, \setactualtext
-        \def\setalttext#1{}\def\setactualtext#1{}%
+        \def\setalttext##1{}\def\setactualtext##1{}%
         \xdef\formulasource:thesource{#1}%
     \endgroup
     \expandafter\splitcommalist
@@ -370,6 +384,7 @@
             relation Source desc {Equation source}
             name {\formulafilename.tex}
             string {\tagging:formulasource{#1}}\fi}
+\def\formulasource:none#1{}
 \newtoks\includeformulasources
 \includeformulasources{actualtext,attachment}
 
@@ -390,18 +405,19 @@
 \def\linkdest:h#1{\vadjust pre{\linkdest:v{#1}}}
 \def\linkdest:v#1{\pdfextension dest name {#1} xyz\nobreak}
 
-% \hyperlink dest {name} ... \endlink
-% \hyperlink url {url} ... \endlink
+% \hyperlink <args> ... \endlink
+%   args: alt {...} attr {...} <type>
+%   type: dest {name} | url {url} | next | prev | first | last
 \protected\def\endlink{\pdfextension endlink\stopelement{Link}\relax}
 \protected\def\startlink{\startelement{Link}\pdfextension startlink}
-\def\hyperlink#1#{\quitvmode\hyperlink:rmspace#1 \hyperlink:rmspace}
-\def\hyperlink:rmspace#1 #2\hyperlink:rmspace{%
-    \startlink attr {\minim:linkattr}%
-    \csname hyperlink:#1\endcsname}
-\def\hyperlink:dest#1{user {/Subtype/Link /F 4 /A <</S/GoTo /D (#1)>>}}
-\def\hyperlink:url#1{user {/Subtype/Link /F 4 /A <</S/URI /URI (#1)>>}}
-\def\minim:linkattr{/Border [0 0 0]}
+\protected\def\hyperlink{\quitvmode\startlink\hyper:makelink}
 
+% \hyperlinkstyle { ... }
+\def\minim:linkattr{\the\hyperlinkstyle\the\minim:pdfalinkattr}
+\newtoks\hyperlinkstyle
+\newtoks\minim:pdfalinkattr
+\hyperlinkstyle{/Border [0 0 0]}
+
 % 
1 languages
 
 % provided by the lua module:
@@ -449,9 +465,12 @@
 
 \newcount \pdfaconformancelevel
 \pdfaconformancelevel = 0
+\newcount \pdfuaconformancelevel
+\pdfuaconformancelevel = 0
 
 % \pdfalevel 2b
 \def\pdfalevel#1#2{%
+    \minim:pdfalinkattr{ /F 4}% hyperlinks must be printed
     \global\pdfaconformancelevel=#1\relax
     \ifcsname minim:pdfa:#1#2\endcsname \lastnamedcs\else
         \errmessage{Unknown pdf/a standard pdf/a-#1}\fi}
@@ -474,6 +493,12 @@
 \expandafter\def\csname minim:pdfa:3b\endcsname{\minim:pdfasettings 7B3}
 \expandafter\def\csname minim:pdfa:3u\endcsname{\minim:pdfasettings 7U3}
 
+% \pdfualavel 1
+\def\pdfualevel{\input minim-xmp
+    \setmetadata pdfuaid:part {1}
+    \tagging:enablepdfua
+    \global\pdfuaconformancelevel= }
+
 % 
 
 \catcode`\: = \minimpdfloaded

Modified: trunk/Master/texmf-dist/tex/luatex/minim-xmp/minim-xmp.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/minim-xmp/minim-xmp.lua	2024-01-08 21:46:37 UTC (rev 69351)
+++ trunk/Master/texmf-dist/tex/luatex/minim-xmp/minim-xmp.lua	2024-01-08 21:47:22 UTC (rev 69352)
@@ -158,6 +158,12 @@
     part        = { 'Integer', 'Version identifier', true },
 }, nil, true)
 
+M.add_namespace('PDF/UA Identification', 'pdfuaid', 'http://www.aiim.org/pdfua/ns/id/', {
+    amd         = { 'Text', 'Amendment identifier', true },
+    corr        = { 'Text', 'Corrigendum identifier', true },
+    part        = { 'Integer', 'Version identifier', true },
+})
+
 M.add_namespace('Dublin Core', 'dc', 'http://purl.org/dc/elements/1.1/', {
     contributor = { 'Bag ProperName', 'Other contributors' },
     coverage    = { 'Text', 'Extent or scope' },



More information about the tex-live-commits mailing list.