texlive[62420] branches/branch2021.final/Master: minim* (4mar22)

commits+karl at tug.org commits+karl at tug.org
Fri Mar 4 23:06:50 CET 2022


Revision: 62420
          http://tug.org/svn/texlive?view=revision&revision=62420
Author:   karl
Date:     2022-03-04 23:06:49 +0100 (Fri, 04 Mar 2022)
Log Message:
-----------
minim* (4mar22) (branch)

Modified Paths:
--------------
    branches/branch2021.final/Master/texmf-dist/doc/luatex/minim/README
    branches/branch2021.final/Master/texmf-dist/doc/luatex/minim/minim-alloc.doc
    branches/branch2021.final/Master/texmf-dist/doc/luatex/minim/minim.doc
    branches/branch2021.final/Master/texmf-dist/doc/luatex/minim/minim.pdf
    branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-math/README
    branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-math/minim-math.doc
    branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-math/minim-math.pdf
    branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-mp/README
    branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-mp/minim-mp.doc
    branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-mp/minim-mp.pdf
    branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-pdf/README
    branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-pdf/minim-pdf.doc
    branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-pdf/minim-pdf.pdf
    branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-xmp/README
    branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-xmp/minim-xmp.doc
    branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-xmp/minim-xmp.pdf
    branches/branch2021.final/Master/texmf-dist/metapost/minim-mp/minim.mp
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-alloc.lua
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-alloc.tex
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-callbacks.lua
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-doc.sty
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-hooks.lua
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-hooks.tex
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-lmodern.tex
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-plain.tex
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-math/minim-math-table.lua
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-math/minim-math.lua
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-math/minim-math.tex
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.ini
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.lua
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.tex
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-pdf/minim-languagecodes.lua
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.lua
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.tex
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-xmp/minim-xmp.lua
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-xmp/minim-xmp.tex
    branches/branch2021.final/Master/tlpkg/bin/c2lx

Added Paths:
-----------
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-pdfresources.lua
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-pdfresources.tex
    branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-mp/minim-lamp.ini

Modified: branches/branch2021.final/Master/texmf-dist/doc/luatex/minim/README
===================================================================
--- branches/branch2021.final/Master/texmf-dist/doc/luatex/minim/README	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/doc/luatex/minim/README	2022-03-04 22:06:49 UTC (rev 62420)
@@ -1,4 +1,4 @@
-Version: 2021/1.0
+Version: 2022/1.1
 
 SUMMARY
 
@@ -29,9 +29,33 @@
 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:

Modified: branches/branch2021.final/Master/texmf-dist/doc/luatex/minim/minim-alloc.doc
===================================================================
--- branches/branch2021.final/Master/texmf-dist/doc/luatex/minim/minim-alloc.doc	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/doc/luatex/minim/minim-alloc.doc	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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 @@
 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 @@
 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.
+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.
+
 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.
+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 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 @@
 ⟦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 @@
 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 @@
 ⟦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.
-
-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 @@
 ⟦*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

Modified: branches/branch2021.final/Master/texmf-dist/doc/luatex/minim/minim.doc
===================================================================
--- branches/branch2021.final/Master/texmf-dist/doc/luatex/minim/minim.doc	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/doc/luatex/minim/minim.doc	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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,13 +62,8 @@
 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.
+Earlier incompatibilities with \red{pgf} have been resolved.
 
-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.
-
 \licencesection
 
 \input minim-mp.doc

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

Modified: branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-math/README
===================================================================
--- branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-math/README	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-math/README	2022-03-04 22:06:49 UTC (rev 62420)
@@ -1,4 +1,4 @@
-Version: 2021/1.0
+Version: 2022/1.1
 
 SUMMARY
 
@@ -16,9 +16,22 @@
     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:

Modified: branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-math/minim-math.doc
===================================================================
--- branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-math/minim-math.doc	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-math/minim-math.doc	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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 @@
 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 @@
 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,7 +159,11 @@
 ⟦*\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
 
 You can use ⟦*\text⟧ for adding nonmathematical text to your equations.

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

Modified: branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-mp/README
===================================================================
--- branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-mp/README	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-mp/README	2022-03-04 22:06:49 UTC (rev 62420)
@@ -1,4 +1,4 @@
-Version: 2021/1.0
+Version: 2022/1.1
 
 SUMMARY
 
@@ -17,9 +17,37 @@
     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:

Modified: branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-mp/minim-mp.doc
===================================================================
--- branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-mp/minim-mp.doc	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-mp/minim-mp.doc	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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 @@
 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 @@
 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 @@
     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 @@
     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,14 +264,10 @@
 
 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 
@@ -223,11 +274,6 @@
 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,8 +281,17 @@
 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
 
 In what follows, you should assume ⟦M⟧ to be the result of
@@ -262,29 +317,6 @@
 ⟦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 @@
 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 %
 

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

Modified: branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-pdf/README
===================================================================
--- branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-pdf/README	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-pdf/README	2022-03-04 22:06:49 UTC (rev 62420)
@@ -1,4 +1,4 @@
-Version: 2021/1.0
+Version: 2022/1.1
 
 SUMMARY
 
@@ -25,9 +25,32 @@
     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:

Modified: branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-pdf/minim-pdf.doc
===================================================================
--- branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-pdf/minim-pdf.doc	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-pdf/minim-pdf.doc	2022-03-04 22:06:49 UTC (rev 62420)
@@ -14,8 +14,8 @@
 \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 @@
 
 \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,12 +130,11 @@
 \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.
 
-
 % 
 
 \chapter Tagged PDF
@@ -160,7 +160,7 @@
 
 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 @@
 \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 @@
 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 @@
 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 @@
 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 @@
 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,22 +322,30 @@
 ⟦*\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
 
 If you do not specify a language code for a structure element, its language 
@@ -358,7 +362,7 @@
 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 @@
 ⟦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,15 +414,19 @@
 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⟧.
 
-
 % 
 
 \endmanual

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

Modified: branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-xmp/README
===================================================================
--- branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-xmp/README	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-xmp/README	2022-03-04 22:06:49 UTC (rev 62420)
@@ -1,4 +1,4 @@
-Version: 2021/1.0
+Version: 2022/1.1
 
 SUMMARY
 
@@ -19,9 +19,22 @@
     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:

Modified: branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-xmp/minim-xmp.doc
===================================================================
--- branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-xmp/minim-xmp.doc	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/doc/luatex/minim-xmp/minim-xmp.doc	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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 @@
     </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 @@
 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 

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

Modified: branches/branch2021.final/Master/texmf-dist/metapost/minim-mp/minim.mp
===================================================================
--- branches/branch2021.final/Master/texmf-dist/metapost/minim-mp/minim.mp	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/metapost/minim-mp/minim.mp	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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 @@
     "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 @@
 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;
+
+

Modified: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-alloc.lua
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-alloc.lua	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-alloc.lua	2022-03-04 22:06:49 UTC (rev 62420)
@@ -27,6 +27,131 @@
     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 @@
     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 @@
 -- 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
 
-local function make_alloc_new (fname, globcount)
+-- 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)
     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 @@
 -- 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 @@
         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,10 +276,21 @@
     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
 
 -- reserve a bytecode register
@@ -188,7 +331,7 @@
     lua.bytecode[saved_tables_bytecode] = load(saved_tables)
 end
 
-callback.register ('pre_dump', dump_saved_tables)
+cb.register ('pre_dump', dump_saved_tables)
 
 --
 

Modified: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-alloc.tex
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-alloc.tex	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-alloc.tex	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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 at alloc@attribute at count}%
-            \minim:ltltx:fix{e at alloc@whatsit at count}%
-            \minim:ltltx:fix{e at alloc@bytecode at count}%
-            \minim:ltltx:fix{e at alloc@luafunction at count}%
-            \minim:ltltx:fix{e at alloc@luachunk at count}%
-            \minim:ltltx:fix{e at alloc@ccodetable at 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 at alloc@attribute at count}%
+        \minim:ltltx:fix{e at alloc@whatsit at count}%
+        \minim:ltltx:fix{e at alloc@bytecode at count}%
+        \minim:ltltx:fix{e at alloc@luafunction at count}%
+        \minim:ltltx:fix{e at alloc@luachunk at count}%
+        \minim:ltltx:fix{e at alloc@ccodetable at 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 at alloc@ccodetable at 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 at alloc@ccodetable at count\endcsname = 0
+    \csname e at alloc@ccodetable at 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 at 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 at hook\endcsname
+    \expandafter\let
+        \expandafter\minim:uselanguagehook
+            \lastnamedcs \fi
+\expandafter\edef\csname uselanguage at 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

Modified: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-callbacks.lua
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-callbacks.lua	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-callbacks.lua	2022-03-04 22:06:49 UTC (rev 62420)
@@ -1,19 +1,14 @@
 
 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
@@ -20,22 +15,37 @@
 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,7 +54,8 @@
             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
@@ -51,7 +62,8 @@
             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 @@
 
 --
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 @@
     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 @@
 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 @@
 
 -- 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,24 +303,24 @@
 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
+-- 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
 
 

Modified: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-doc.sty
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-doc.sty	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-doc.sty	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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\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
+\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
 
-\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
-
 \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 @@
 \setbox\notehead=\hbox{\markartifact{Layout}{\box\notehead}}
 \closemetapostinstance \mnmMP
 
-%
 % 
1 document structure
 
 % page artifacts
@@ -134,9 +143,9 @@
     \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 @@
     \bigskip\penalty-50\relax
     \startelement{Section}%
     \outline closed {#1}%
+    \addtotoc{\section{#1}{\lastdestinationname}}%
     \nextpartag{H}\quitvmode
-    \addtotoc{\section{#1}{\lastdestinationname}}%
     \red{\title#1}%
     \par\nobreak}
 
@@ -200,7 +209,6 @@
 {\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

Modified: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-hooks.lua
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-hooks.lua	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-hooks.lua	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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 @@
     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
 

Modified: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-hooks.tex
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-hooks.tex	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-hooks.tex	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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
 

Modified: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-lmodern.tex
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-lmodern.tex	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-lmodern.tex	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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 \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\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}

Added: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-pdfresources.lua
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-pdfresources.lua	                        (rev 0)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-pdfresources.lua	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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 at sys@addpdfresource at extgs@plain', function()
+        M.add_global_resource('ExtGState', nil, token.scan_string())
+    end)
+    alloc.luadef('pgf at sys@addpdfresource at patterns@plain', function()
+        M.add_global_resource('Pattern', nil, token.scan_string())
+    end)
+    alloc.luadef('pgf at sys@addpdfresource at colorspaces@plain', function()
+        M.add_global_resource('ColorSpace', nil, token.scan_string())
+    end)
+end)
+
+return M
+


Property changes on: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-pdfresources.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-pdfresources.tex
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-pdfresources.tex	                        (rev 0)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-pdfresources.tex	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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 at 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 at 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 at sys@pgf at resource@list at extgs\endcsname}')
+    pdfres.add_global_resource('Pattern', nil,
+        '\luaescapestring{\csname pgf at sys@pgf at resource@list at patterns\endcsname}')
+    pdfres.add_global_resource('ColorSpace', nil,
+        '\luaescapestring{\csname pgf at sys@pgf at resource@list at colorspaces\endcsname}')}
+    \expandafter\let\csname pgf at sys@pdf at possible@resources\endcsname = \empty
+    \minim:pgf:fix:luaside \pdfvariable pageresources{}}
+
+% if pgf has already been loaded, copy the already-defined resources
+\ifcsname pgf at sys@pdf at 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 at sys@pgf at resource@list at extgs\endcsname = \empty
+    \expandafter\let\csname pgf at sys@pgf at resource@list at patterns\endcsname  = \empty
+    \expandafter\let\csname pgf at sys@pgf at resource@list at colorspaces\endcsname = \empty
+\fi
+
+% 
+
+\catcode`\:=\minimpdfresourcesloaded
+\endinput
+


Property changes on: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-pdfresources.tex
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-plain.tex
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-plain.tex	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim/minim-plain.tex	2022-03-04 22:06:49 UTC (rev 62420)
@@ -490,8 +490,6 @@
 
 \def\leavevmode{\unhbox\voidb at x} % begins a paragraph, if necessary
 \def\_{\leavevmode \kern.06em \vbox{\hrule width.3em}}
-\def\AA{\leavevmode\setbox0\hbox{!}\dimen@\ht0\advance\dimen at -1ex%
-  \rlap{\raise.67\dimen@\hbox{\char'27}}A}
 
 \def\mathhexbox#1#2#3{\leavevmode
   \hbox{$\m at th \mathchar"#1#2#3$}}
@@ -512,18 +510,6 @@
 \def\dots{\relax\ifmmode\ldots\else$\m at 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 at th \mkern1.5mu.\mkern1.5mu$}\hfill}
 \def\rightarrowfill{$\m at th\smash-\mkern-7mu%

Modified: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-math/minim-math-table.lua
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-math/minim-math-table.lua	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-math/minim-math-table.lua	2022-03-04 22:06:49 UTC (rev 62420)
@@ -106,13 +106,22 @@
   , { 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 @@
   , { 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 @@
   , { 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 @@
   , { 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

Modified: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-math/minim-math.lua
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-math/minim-math.lua	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-math/minim-math.lua	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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 @@
 
 -- 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 @@
     return true
 end
 
-callbacks.register ('mlist_to_mlist', inspect_noads)
+cb.register ('mlist_to_mlist', inspect_noads)
 
 --
1 Reading the math character table
 

Modified: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-math/minim-math.tex
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-math/minim-math.tex	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-math/minim-math.tex	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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

Added: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-mp/minim-lamp.ini
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-mp/minim-lamp.ini	                        (rev 0)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-mp/minim-lamp.ini	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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
+


Property changes on: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-mp/minim-lamp.ini
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.ini
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.ini	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.ini	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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)}%

Modified: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.lua
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.lua	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.lua	2022-03-04 22:06:49 UTC (rev 62420)
@@ -1,6 +1,7 @@
 
 local alloc = require ('minim-alloc')
 local cb = require('minim-callbacks')
+local pdfres = require('minim-pdfresources')
 alloc.remember ('minim-mp')
 
 local M = {}
@@ -7,82 +8,6 @@
 
 --
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 @@
     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 @@
 function M.enable_debugging()
     debugging = true
     pdf.setcompresslevel(0)
+    pdf.setobjcompresslevel(0)
 end
 
 local function print_prop(append, obj, prop)
@@ -270,7 +197,7 @@
 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 @@
 -- 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 @@
     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 @@
         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 @@
         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,10 +432,12 @@
 -- 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
@@ -515,6 +444,15 @@
         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 @@
     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 @@
 
 --
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 @@
 
 --
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 @@
 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 @@
 
 --
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 @@
         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 @@
             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 @@
     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 @@
 
 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 @@
 
 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 @@
 
 --
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 @@
         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 @@
         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 @@
         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 @@
 --
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 @@
             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 @@
     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 @@
     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,9 +1035,90 @@
             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
 
 local function apply_default_instance_opts(t)
@@ -1122,27 +1162,21 @@
         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 @@
         , 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

Modified: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.tex
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.tex	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.tex	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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}%

Modified: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-pdf/minim-languagecodes.lua
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-pdf/minim-languagecodes.lua	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-pdf/minim-languagecodes.lua	2022-03-04 22:06:49 UTC (rev 62420)
@@ -78,6 +78,7 @@
     latvian              = 'lv',
     lithuanian           = 'lt',
     liturgicallatin      = 'la-x-liturgic',
+    macedonian           = 'mk',
     malayalam            = 'ml',
     marathi              = 'mr',
     medievalgreek        = 'gkm',
@@ -94,8 +95,10 @@
     norwegian            = 'nb',
     nynorsk              = 'nn',
     occitan              = 'oc',
+    oldgerman            = 'de',
     oriya                = 'or',
     pali                 = 'pi',
+    panjabi              = 'pa',
     patois               = 'fr',
     persian              = 'fa',
     piedmontese          = 'pms',
@@ -111,6 +114,7 @@
     sahidic              = 'cop-x-sahidic',
     samaritan            = 'smp',
     sanskrit             = 'sa',
+    schoolfinnish        = 'fi',
     serbian              = 'sr-latn',
     serbianc             = 'sr-cyrl',
     slovak               = 'sk',
@@ -129,11 +133,11 @@
     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',

Modified: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.lua
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.lua	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.lua	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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 @@
     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 @@
     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 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 @@
     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 @@
     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 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 @@
     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 @@
     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 @@
 
 -- \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,25 +492,21 @@
 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
+    else
+        n.attributes = n.attr
     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'
-    end
     n.index = #structure + 1
     n.children = { }
     -- order and parent can be forced (needed for asynchronous elements)
@@ -573,13 +525,13 @@
 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,7 +545,12 @@
     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
 
 -- All content items should be explicitly opened and closed, per page, by the 
@@ -613,7 +570,7 @@
     _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 @@
     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 @@
     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 @@
     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
+        local stat = n[current_status]
+        -- first we start with marking artifacts
         if marker and marker.what == 'art_start' then
-        -- first we start with marking artifacts
             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
+            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
-        -- then attach links to Link elements
             local _se, _order = n[current_struct], n[current_order]
             local link = structure[_se]
             if link.struct == 'Link' then
@@ -698,16 +664,16 @@
                 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 @@
                 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 @@
         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 @@
     -- 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 @@
 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 @@
     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 @@
     -- 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 @@
 end
 
 alloc.luadef('embedfile', function()
-    local t = M.options_scanner()
+    local t = options_scanner()
         :string('desc')
         :string('file')
         :string('string')
@@ -901,7 +861,7 @@
     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 @@
     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 @@
         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',

Modified: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.tex
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.tex	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.tex	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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,14 +227,10 @@
 \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
@@ -237,6 +237,26 @@
     \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,21 +285,7 @@
 % 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 at hook\endcsname
-    \expandafter\let
-        \expandafter\minim:uselanguagehook
-            \lastnamedcs \fi
-\expandafter\edef\csname uselanguage at 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{%
     \expandafter\newlanguage\csname lang@#1\endcsname

Modified: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-xmp/minim-xmp.lua
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-xmp/minim-xmp.lua	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-xmp/minim-xmp.lua	2022-03-04 22:06:49 UTC (rev 62420)
@@ -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 @@
     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))

Modified: branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-xmp/minim-xmp.tex
===================================================================
--- branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-xmp/minim-xmp.tex	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/texmf-dist/tex/luatex/minim-xmp/minim-xmp.tex	2022-03-04 22:06:49 UTC (rev 62420)
@@ -19,6 +19,10 @@
 \newcount \metadataseparator
 \metadataseparator    = `\;
 
+% \writedocumentmetadata
+\newcount \writedocumentmetadata
+\writedocumentmetadata = 1
+
 % Defined from the lua side:
 %
 %  \getmetadata ns:key

Modified: branches/branch2021.final/Master/tlpkg/bin/c2lx
===================================================================
--- branches/branch2021.final/Master/tlpkg/bin/c2lx	2022-03-04 22:04:55 UTC (rev 62419)
+++ branches/branch2021.final/Master/tlpkg/bin/c2lx	2022-03-04 22:06:49 UTC (rev 62420)
@@ -29,8 +29,14 @@
   pkgs="l3kernel l3packages l3experimental l3build l3backend"
 elif test "x$1" = xjxu; then
   shift; label=jxu
-  pkgs="beaulivre colorist einfart lebhart mindflow minimalist"
-  pkgs="$pkgs projlib simplivre"
+  pkgs="beaulivre colorist einfart lebhart mindflow minimalist simplivre"
+  pkgs="$pkgs create-theorem crethe projlib"
+elif test "x$1" = xqyi; then
+  shift; label=qyi
+  pkgs="easybook spbmark"
+elif test "x$1" = xminim-all; then
+  shift; label=minim 
+  pkgs="minim-hatching minim-math minim-mp minim-pdf minim-xmp minim"
 else
   echo "$0: unknown latex or group type: $1 (one of: e dev 3 jxu)" >&2
   exit 1



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