summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Master/texmf-dist/doc/luatex/minim-mp/EUPL-1.2-EN.txt287
-rw-r--r--Master/texmf-dist/doc/luatex/minim-mp/README28
-rw-r--r--Master/texmf-dist/doc/luatex/minim-mp/minim-mp.doc329
-rw-r--r--Master/texmf-dist/doc/luatex/minim-mp/minim-mp.pdfbin0 -> 82287 bytes
-rw-r--r--Master/texmf-dist/metapost/minim-mp/minim.mp73
-rw-r--r--Master/texmf-dist/tex/luatex/minim-mp/minim-mp.ini17
-rw-r--r--Master/texmf-dist/tex/luatex/minim-mp/minim-mp.lua1282
-rw-r--r--Master/texmf-dist/tex/luatex/minim-mp/minim-mp.tex61
-rwxr-xr-xMaster/tlpkg/libexec/ctan2tds2
-rw-r--r--Master/tlpkg/tlpsrc/collection-luatex.tlpsrc2
-rw-r--r--Master/tlpkg/tlpsrc/minim-mp.tlpsrc0
11 files changed, 2080 insertions, 1 deletions
diff --git a/Master/texmf-dist/doc/luatex/minim-mp/EUPL-1.2-EN.txt b/Master/texmf-dist/doc/luatex/minim-mp/EUPL-1.2-EN.txt
new file mode 100644
index 00000000000..4153cd37753
--- /dev/null
+++ b/Master/texmf-dist/doc/luatex/minim-mp/EUPL-1.2-EN.txt
@@ -0,0 +1,287 @@
+ EUROPEAN UNION PUBLIC LICENCE v. 1.2
+ EUPL © the European Union 2007, 2016
+
+This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined
+below) which is provided under the terms of this Licence. Any use of the Work,
+other than as authorised under this Licence is prohibited (to the extent such
+use is covered by a right of the copyright holder of the Work).
+
+The Work is provided under the terms of this Licence when the Licensor (as
+defined below) has placed the following notice immediately following the
+copyright notice for the Work:
+
+ Licensed under the EUPL
+
+or has expressed by any other means his willingness to license under the EUPL.
+
+1. Definitions
+
+In this Licence, the following terms have the following meaning:
+
+- ‘The Licence’: this Licence.
+
+- ‘The Original Work’: the work or software distributed or communicated by the
+ Licensor under this Licence, available as Source Code and also as Executable
+ Code as the case may be.
+
+- ‘Derivative Works’: the works or software that could be created by the
+ Licensee, based upon the Original Work or modifications thereof. This Licence
+ does not define the extent of modification or dependence on the Original Work
+ required in order to classify a work as a Derivative Work; this extent is
+ determined by copyright law applicable in the country mentioned in Article 15.
+
+- ‘The Work’: the Original Work or its Derivative Works.
+
+- ‘The Source Code’: the human-readable form of the Work which is the most
+ convenient for people to study and modify.
+
+- ‘The Executable Code’: any code which has generally been compiled and which is
+ meant to be interpreted by a computer as a program.
+
+- ‘The Licensor’: the natural or legal person that distributes or communicates
+ the Work under the Licence.
+
+- ‘Contributor(s)’: any natural or legal person who modifies the Work under the
+ Licence, or otherwise contributes to the creation of a Derivative Work.
+
+- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of
+ the Work under the terms of the Licence.
+
+- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,
+ renting, distributing, communicating, transmitting, or otherwise making
+ available, online or offline, copies of the Work or providing access to its
+ essential functionalities at the disposal of any other natural or legal
+ person.
+
+2. Scope of the rights granted by the Licence
+
+The Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
+sublicensable licence to do the following, for the duration of copyright vested
+in the Original Work:
+
+- use the Work in any circumstance and for all usage,
+- reproduce the Work,
+- modify the Work, and make Derivative Works based upon the Work,
+- communicate to the public, including the right to make available or display
+ the Work or copies thereof to the public and perform publicly, as the case may
+ be, the Work,
+- distribute the Work or copies thereof,
+- lend and rent the Work or copies thereof,
+- sublicense rights in the Work or copies thereof.
+
+Those rights can be exercised on any media, supports and formats, whether now
+known or later invented, as far as the applicable law permits so.
+
+In the countries where moral rights apply, the Licensor waives his right to
+exercise his moral right to the extent allowed by law in order to make effective
+the licence of the economic rights here above listed.
+
+The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to
+any patents held by the Licensor, to the extent necessary to make use of the
+rights granted on the Work under this Licence.
+
+3. Communication of the Source Code
+
+The Licensor may provide the Work either in its Source Code form, or as
+Executable Code. If the Work is provided as Executable Code, the Licensor
+provides in addition a machine-readable copy of the Source Code of the Work
+along with each copy of the Work that the Licensor distributes or indicates, in
+a notice following the copyright notice attached to the Work, a repository where
+the Source Code is easily and freely accessible for as long as the Licensor
+continues to distribute or communicate the Work.
+
+4. Limitations on copyright
+
+Nothing in this Licence is intended to deprive the Licensee of the benefits from
+any exception or limitation to the exclusive rights of the rights owners in the
+Work, of the exhaustion of those rights or of other applicable limitations
+thereto.
+
+5. Obligations of the Licensee
+
+The grant of the rights mentioned above is subject to some restrictions and
+obligations imposed on the Licensee. Those obligations are the following:
+
+Attribution right: The Licensee shall keep intact all copyright, patent or
+trademarks notices and all notices that refer to the Licence and to the
+disclaimer of warranties. The Licensee must include a copy of such notices and a
+copy of the Licence with every copy of the Work he/she distributes or
+communicates. The Licensee must cause any Derivative Work to carry prominent
+notices stating that the Work has been modified and the date of modification.
+
+Copyleft clause: If the Licensee distributes or communicates copies of the
+Original Works or Derivative Works, this Distribution or Communication will be
+done under the terms of this Licence or of a later version of this Licence
+unless the Original Work is expressly distributed only under this version of the
+Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee
+(becoming Licensor) cannot offer or impose any additional terms or conditions on
+the Work or Derivative Work that alter or restrict the terms of the Licence.
+
+Compatibility clause: If the Licensee Distributes or Communicates Derivative
+Works or copies thereof based upon both the Work and another work licensed under
+a Compatible Licence, this Distribution or Communication can be done under the
+terms of this Compatible Licence. For the sake of this clause, ‘Compatible
+Licence’ refers to the licences listed in the appendix attached to this Licence.
+Should the Licensee's obligations under the Compatible Licence conflict with
+his/her obligations under this Licence, the obligations of the Compatible
+Licence shall prevail.
+
+Provision of Source Code: When distributing or communicating copies of the Work,
+the Licensee will provide a machine-readable copy of the Source Code or indicate
+a repository where this Source will be easily and freely available for as long
+as the Licensee continues to distribute or communicate the Work.
+
+Legal Protection: This Licence does not grant permission to use the trade names,
+trademarks, service marks, or names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the copyright notice.
+
+6. Chain of Authorship
+
+The original Licensor warrants that the copyright in the Original Work granted
+hereunder is owned by him/her or licensed to him/her and that he/she has the
+power and authority to grant the Licence.
+
+Each Contributor warrants that the copyright in the modifications he/she brings
+to the Work are owned by him/her or licensed to him/her and that he/she has the
+power and authority to grant the Licence.
+
+Each time You accept the Licence, the original Licensor and subsequent
+Contributors grant You a licence to their contributions to the Work, under the
+terms of this Licence.
+
+7. Disclaimer of Warranty
+
+The Work is a work in progress, which is continuously improved by numerous
+Contributors. It is not a finished work and may therefore contain defects or
+‘bugs’ inherent to this type of development.
+
+For the above reason, the Work is provided under the Licence on an ‘as is’ basis
+and without warranties of any kind concerning the Work, including without
+limitation merchantability, fitness for a particular purpose, absence of defects
+or errors, accuracy, non-infringement of intellectual property rights other than
+copyright as stated in Article 6 of this Licence.
+
+This disclaimer of warranty is an essential part of the Licence and a condition
+for the grant of any rights to the Work.
+
+8. Disclaimer of Liability
+
+Except in the cases of wilful misconduct or damages directly caused to natural
+persons, the Licensor will in no event be liable for any direct or indirect,
+material or moral, damages of any kind, arising out of the Licence or of the use
+of the Work, including without limitation, damages for loss of goodwill, work
+stoppage, computer failure or malfunction, loss of data or any commercial
+damage, even if the Licensor has been advised of the possibility of such damage.
+However, the Licensor will be liable under statutory product liability laws as
+far such laws apply to the Work.
+
+9. Additional agreements
+
+While distributing the Work, You may choose to conclude an additional agreement,
+defining obligations or services consistent with this Licence. However, if
+accepting obligations, You may act only on your own behalf and on your sole
+responsibility, not on behalf of the original Licensor or any other Contributor,
+and only if You agree to indemnify, defend, and hold each Contributor harmless
+for any liability incurred by, or claims asserted against such Contributor by
+the fact You have accepted any warranty or additional liability.
+
+10. Acceptance of the Licence
+
+The provisions of this Licence can be accepted by clicking on an icon ‘I agree’
+placed under the bottom of a window displaying the text of this Licence or by
+affirming consent in any other similar way, in accordance with the rules of
+applicable law. Clicking on that icon indicates your clear and irrevocable
+acceptance of this Licence and all of its terms and conditions.
+
+Similarly, you irrevocably accept this Licence and all of its terms and
+conditions by exercising any rights granted to You by Article 2 of this Licence,
+such as the use of the Work, the creation by You of a Derivative Work or the
+Distribution or Communication by You of the Work or copies thereof.
+
+11. Information to the public
+
+In case of any Distribution or Communication of the Work by means of electronic
+communication by You (for example, by offering to download the Work from a
+remote location) the distribution channel or media (for example, a website) must
+at least provide to the public the information requested by the applicable law
+regarding the Licensor, the Licence and the way it may be accessible, concluded,
+stored and reproduced by the Licensee.
+
+12. Termination of the Licence
+
+The Licence and the rights granted hereunder will terminate automatically upon
+any breach by the Licensee of the terms of the Licence.
+
+Such a termination will not terminate the licences of any person who has
+received the Work from the Licensee under the Licence, provided such persons
+remain in full compliance with the Licence.
+
+13. Miscellaneous
+
+Without prejudice of Article 9 above, the Licence represents the complete
+agreement between the Parties as to the Work.
+
+If any provision of the Licence is invalid or unenforceable under applicable
+law, this will not affect the validity or enforceability of the Licence as a
+whole. Such provision will be construed or reformed so as necessary to make it
+valid and enforceable.
+
+The European Commission may publish other linguistic versions or new versions of
+this Licence or updated versions of the Appendix, so far this is required and
+reasonable, without reducing the scope of the rights granted by the Licence. New
+versions of the Licence will be published with a unique version number.
+
+All linguistic versions of this Licence, approved by the European Commission,
+have identical value. Parties can take advantage of the linguistic version of
+their choice.
+
+14. Jurisdiction
+
+Without prejudice to specific agreement between parties,
+
+- any litigation resulting from the interpretation of this License, arising
+ between the European Union institutions, bodies, offices or agencies, as a
+ Licensor, and any Licensee, will be subject to the jurisdiction of the Court
+ of Justice of the European Union, as laid down in article 272 of the Treaty on
+ the Functioning of the European Union,
+
+- any litigation arising between other parties and resulting from the
+ interpretation of this License, will be subject to the exclusive jurisdiction
+ of the competent court where the Licensor resides or conducts its primary
+ business.
+
+15. Applicable Law
+
+Without prejudice to specific agreement between parties,
+
+- this Licence shall be governed by the law of the European Union Member State
+ where the Licensor has his seat, resides or has his registered office,
+
+- this licence shall be governed by Belgian law if the Licensor has no seat,
+ residence or registered office inside a European Union Member State.
+
+Appendix
+
+‘Compatible Licences’ according to Article 5 EUPL are:
+
+- GNU General Public License (GPL) v. 2, v. 3
+- GNU Affero General Public License (AGPL) v. 3
+- Open Software License (OSL) v. 2.1, v. 3.0
+- Eclipse Public License (EPL) v. 1.0
+- CeCILL v. 2.0, v. 2.1
+- Mozilla Public Licence (MPL) v. 2
+- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3
+- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for
+ works other than software
+- European Union Public Licence (EUPL) v. 1.1, v. 1.2
+- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong
+ Reciprocity (LiLiQ-R+).
+
+The European Commission may update this Appendix to later versions of the above
+licences without producing a new version of the EUPL, as long as they provide
+the rights granted in Article 2 of this Licence and protect the covered Source
+Code from exclusive appropriation.
+
+All other changes or additions to this Appendix require the production of a new
+EUPL version.
diff --git a/Master/texmf-dist/doc/luatex/minim-mp/README b/Master/texmf-dist/doc/luatex/minim-mp/README
new file mode 100644
index 00000000000..dee13d1bfe3
--- /dev/null
+++ b/Master/texmf-dist/doc/luatex/minim-mp/README
@@ -0,0 +1,28 @@
+Version: 2021/1.0
+
+SUMMARY
+
+This package offers low-level mplib integration for plain LuaTeX and is
+designed with the purpose of being easy to extend.
+
+The use of multiple simultaneous MetaPost instances is supported, as well as
+running TeX or lua code from within MetaPost. With the included minim-mp
+format file, you can even use LuaTeX as a stand-alone MetaPost compiler.
+
+
+DOCUMENTATION
+
+In order to obtain the typeset manual of this package, simply say:
+
+ luatex minim-mp.doc
+
+
+COPYING
+
+(c) 2021 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:
+
+https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+
diff --git a/Master/texmf-dist/doc/luatex/minim-mp/minim-mp.doc b/Master/texmf-dist/doc/luatex/minim-mp/minim-mp.doc
new file mode 100644
index 00000000000..788d26b6a0e
--- /dev/null
+++ b/Master/texmf-dist/doc/luatex/minim-mp/minim-mp.doc
@@ -0,0 +1,329 @@
+
+\input minim-doc.sty
+
+\manual{minim-mp}
+
+\ifchapter \chapter Metapost
+
+\else \startmetadata
+ author {Esger Renkema}
+ title {minim-mp}
+ date {2021-06-01}
+ version {2021/1.0}
+ keywords {LuaTeX; Plain TeX; MetaPost; mplib}
+ stopmetadata
+\maketitle \fi
+
+This package offers low-level mplib integration for plain luatex.
+The use of multiple simultaneous metapost instances is supported, as well as
+running tex or lua code from within metapost.
+In order to use it, simply say ⟦\input minim-mp.tex⟧.
+
+After this, ⟦*\directmetapost [ options ] { mp code }⟧ will result in a series
+of images corresponding to the ⟦beginfig ... endfig⟧ statements in your
+⟦mp code⟧. Every image will be in a box of its own.
+
+Every call to ⟦\directmetapost⟧ opens and closes a separate metapost instance.
+If you want your second call to remember the first, you will have to define
+a persistent metapost instance. This will also give you more control over image
+extraction. See below under „Metapost instances”. The ⟦options⟧ will also be
+explained there (for simple cases, you will not need them).
+
+The logging of the metapost run will be included in the regular log file. If an
+error occurs, the logging will also be shown on the terminal.
+
+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.
+
+\section Metapost instances % 1
+
+For more complicated uses, you can define your own instances by saying
+⟦*\newmetapostinstance [ options ] \id⟧.
+An instance can be closed with ⟦*\closemetapostinstance \id⟧.
+These are the options you can use:
+
+\smallskip\smallskip\automarktable
+\halign{#\quad\hfil&#\quad\hfil&#\hfil\cr
+Option&Default&Description\cr
+⟦jobname⟧&⟦':metapost:'⟧&Used in error messages.\cr
+⟦format⟧&⟦'plain.mp'⟧&Format to initialise the instance with.\cr
+⟦math⟧&⟦'scaled'⟧&One of ⟦scaled⟧, ⟦decimal⟧ or ⟦double⟧.\cr
+⟦seed⟧&⟦nil⟧&Random seed for this instance.\cr
+⟦catcodes⟧&⟦0⟧&Catcode table for ⟦btex ... etex⟧.\cr
+⟦env⟧&copy of ⟦_G⟧&Lua environment; see below.\cr
+}\smallskip
+
+Now that you have your own instance, you can run chunks of metapost code in it
+with ⟦*\runmetapost \id { code }⟧. Any images that your code may have contained
+will have to be extracted explicitly. This is possible in a number of ways,
+although each image can be retrieved only once.
+
+⟦*\getnextmpimage \id⟧
+– Writes the first unretrieved image to the current node list.
+There, the image will be contained in a single box node.
+
+⟦*\getnamedmpimage \id {name}⟧
+– Retrieves an image by name
+regardless of its position,
+and writes it to the current node list.
+
+⟦*\boxnextmpimage \id box-nr⟧
+– Puts the next unretrieved image in box ⟦box-nr⟧. The number may be anything
+tex can parse as a number.
+
+⟦*\boxnamedmpimage \id box-nr {name}⟧
+– Puts the image named ⟦name⟧ in box ⟦box-nr⟧.
+
+Say ⟦*\remainingmpimages \id⟧ for the number of images not yet retrieved.
+Finally, as a shorthand, ⟦*\runmetapostimage \id { code }⟧ will add
+⟦beginfig ... endfig⟧ to your ⟦code⟧ and write the resulting image immediately
+to the current list.
+
+
+\section Running tex from within metapost % 1
+
+You can include tex snippets with either ⟦*maketext "tex text"⟧ or
+⟦*btex ... etex⟧ statements. The tex code will be executed in the current
+environment without an extra grouping level. The result of either statement at
+the place where it is invoked is an image object of the proper dimensions that
+can be moved, scaled, rotated and mirrored. You can even specify a colour. Its
+contents, however, will only be added afterwards and are invisible to metapost.
+
+Arbitrary tex statements may be included in ⟦*verbatimtex ... etex⟧, which may
+occur anywhere. These ⟦btex⟧ and ⟦verbatimtex⟧ statements are executed in the
+order they are given.
+
+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.
+
+One possible use of the ⟦infont⟧ operator is setting text along curves:
+
+⟦beginfig(1)
+ save t, w, r, a; picture t;
+ t = "Running TeX from within MetaPost" infont "tenrm";
+ w = xpart lrcorner t = 3.141593 r;
+ for c within t :
+ x := xpart (llcorner c + lrcorner c)/2;
+ a := 90 - 180 x/w;
+ draw c rotatedaround((x,0), a)
+ shifted (-r*sind(a)-x, r*cosd(a));
+ endfor
+endfig;⟧
+\vskip-2\parskip plus 2\parskip
+\quitvmode\hfil
+\startelement alt {The text ‘Running TeX from within MetaPost’ set along a half-circle.} {Figure}%
+\stoptagging\directmetapost{%
+beginfig(1)
+ save t, w, r, a; picture t;
+ t = "Running TeX from within MetaPost" infont "tenrm";
+ w = xpart lrcorner t = 3.141593 r;
+ for c within t :
+ x := xpart (llcorner c + lrcorner c)/2;
+ a := 90 - 180 x/w;
+ draw c rotatedaround((x,0), a)
+ shifted (-r*sind(a)-x, r*cosd(a));
+ endfor
+endfig;}\starttagging \stopelement{Figure}
+
+\section Running lua from within metapost % 1
+
+You can call out to lua with ⟦*runscript "lua code"⟧. For this purpose, each
+metapost instance carries around its own lua environment so that assignments
+you make are local to the instance. (You can of course order the global
+environment to be used by giving ⟦env = _G⟧ as option to
+⟦\newmetapostinstance⟧.)
+
+If your lua snippet returns nothing, the ⟦runscript⟧ call will be invisible to
+metapost. If on the other hand it does return a value, that value will have to
+be translated to metapost. Numbers and strings will be returned as they are
+(so make sure the string is surrounded by quotes if you want to return
+a metapost string). You can return a point or colour by returning an array of
+two to four elements. For other return values, ⟦tostring()⟧ will be called.
+
+Do keep in mind that metapost and lua represent numbers in different ways and
+that rounding errors may occur. For instance, metapost’s ⟦decimal epsilon⟧
+returns ⟦0.00002⟧, which metapost understands as ⟦1/65536⟧, but lua as
+⟦1/50000⟧. Use the metapost macro ⟦*hexadecimal⟧ instead of ⟦decimal⟧ for
+passing unambiguous numbers to lua.
+
+Additionally, you should be aware that metapost uses slightly bigger points
+than tex, so that ⟦epsilon⟧ when taken as a dimension is not quite equal to
+⟦1sp⟧. Use the metapost macro ⟦*scaledpoints⟧ for passing to lua a metapost
+dimension as an integral number of scaled points.
+
+
+\section Tiling patterns % 1
+
+The ⟦*withpattern(<name>)⟧ added to a ⟦fill⟧ statement will fill the path with
+a pattern instead of a solid colour. If the patterns contains no colour
+information of itself, it will have the colour given by ⟦withcolor⟧.
+Stroking operations (the ⟦draw⟧ part) will not be affected.
+Patterns will always look the same, irrespective of any transformations you
+apply to the picture.
+
+To define a pattern, sketch it between
+⟦*beginpattern(<name>) ... endpattern(xstep, ystep);⟧
+where ⟦<name>⟧ is a suffix and ⟦(xstep, ystep)⟧ are the horizontal and vertical
+distances between applications of the pattern.
+Inside the definition, you can draw the pattern using whatever coordinates you
+like; assign a value to the ⟦*matrix⟧ transformation to specify how the pattern
+should be projected onto the page. This ⟦matrix⟧ will also be applied to
+⟦xstep⟧ and ⟦ystep⟧.
+
+You can also change the internal variable ⟦*tilingtype⟧ and the normal variable
+⟦*painttype⟧, although the latter will be set to~1 automatically if you use any
+colour inside the pattern definition. Consult the pdf specification for more
+information on these parameters.
+
+You can use text inside patterns, as in this example:
+\hfill \vbox to 0pt{
+\startelement alt{A circle filled with red As in a rectangular pattern.}{Figure}
+\directmetapost{
+% define the pattern
+picture letter; letter = maketext("a");
+beginpattern(a)
+ draw letter rotated 45;
+ matrix = identity rotated 45;
+endpattern(12pt,12pt);
+% use the pattern
+beginfig(1)
+ fill fullcircle scaled 3cm withpattern(a) withcolor 3/4red;
+ draw fullcircle scaled 3cm withpen pencircle scaled 1;
+endfig;}\stopelement{Figure}\vss}\hskip10pt\strut
+
+⟦% define the pattern
+picture letter; letter = maketext("a");
+beginpattern(a)
+ draw letter rotated 45;
+ matrix = identity rotated 45;
+endpattern(12pt,12pt);
+% use the pattern
+beginfig(1)
+ fill fullcircle scaled 3cm withpattern(a) withcolor 3/4red;
+ draw fullcircle scaled 3cm withpen pencircle scaled 1;
+endfig;⟧
+
+A small pattern library is available in the ⟦*minim-hatching.mp⟧ file; see the
+accompanying documentation sheet for an overview of patterns.
+Tiling patterns cannot be used together with tikz/pgf; see below under
+‘Resource management’.
+
+
+\section Other metapost extensions % 1
+
+There is currently no support for the ⟦*glyph of⟧ operator.
+
+You can set the baseline of an image with ⟦*baseline(p)⟧. There, ⟦p⟧ must
+either be a point through which the baseline should pass, or a number (where an
+x~coordinate of zero will be added). Transformations will be taken into
+account, hence the specification of two coordinates. The last given baseline
+will be used.
+
+Previously-defined box resources can be included with ⟦*boxresource(nr)⟧. The
+result will be an image object with the proper dimensions. This image can be
+transformed in any way you like, but you cannot inspect the contents of the
+resource within metapost.
+
+You can write to tex’s log directly with ⟦*texmessage "hello"⟧.
+
+You can write direct pdf statements with ⟦*special "pdf: statements"⟧ and you
+can add comments to the pdf file with ⟦*special "pdfcomment: comments"⟧.
+Say ⟦*special "latelua: lua code"⟧ to insert a ⟦late_lua⟧ whatsit.
+All three specials can also be given as pre- or postscripts to another object.
+In that case, they will be added before or after the object they are attached to.
+
+
+\section Lua interface % 1
+
+In what follows, you should assume ⟦M⟧ to be the result of
+\stopformulatagging$$\hbox{⟦M = require('minim-mp')⟧}$$\startformulatagging
+as this package does not claim a table in the global environment for itself.
+
+You can open a new instance with ⟦*nr = M.open {options}⟧.
+This returns an index in the ⟦*M.instances⟧ table.
+Run code with ⟦*M.run (nr, code)⟧ and close the instance with ⟦*M.close (nr)⟧.
+Images can be retrieved only with ⟦*box_node = M.get_image(nr, [name])⟧; omit
+the ⟦name⟧ to select the first image.
+Say ⟦*nr_remaining = M.left(nr)⟧ for the number of remaining images.
+
+Each metapost instance is a table containing the following entries:
+
+\smallskip\smallskip
+\marktableaslist \halign{\qquad#\quad\hfil&#\hfil\cr
+⟦jobname⟧&The jobname.\cr
+⟦instance⟧&The primitive metapost instance.\cr
+⟦results⟧&A linked list of unretrieved images.\cr
+⟦status⟧&The last exit status (will never decrease).\cr
+⟦catcodes⟧&Number of the catcode table used with ⟦btex ... etex⟧.\cr
+⟦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
+⟦*M.enable_debugging()⟧ to lua. This will write out a summary of metapost
+object information to the pdf file, just above the pdf instructions that object
+was translated into. For this purpose, the pdf will be generated uncompressed.
+Additionally, a small summary of every generated image will be written to log
+and terminal.
+
+
+\section Extending metapost % 1
+
+You can extend this package by adding new metapost specials. Specials should
+have the form ⟦"identifier: instructions"⟧ and can be added as pre- or
+postscript to metapost objects. A single object can carry multiple specials and a
+⟦*special "..."⟧ statement is equivalent to an empty object with a single
+prefix.
+
+Handling of specials is specified in three lua tables: ⟦*M.specials⟧,
+⟦*M.prescripts⟧ and ⟦*M.postscripts⟧.
+The ⟦identifier⟧ above should equal the key of an entry in the relevant table,
+while the value of an entry in one of these tables should be a function with
+three parameters: the internal image processor state, the ⟦instructions⟧ from
+above and the metapost object itself.
+
+If the ⟦identifier⟧ of a prescript is present in the first table, the
+corresponding function will replace normal object processing.
+Only one prescript may match with this table.
+Functions in the the other two tables will run before or after normal
+processing.
+
+Specials can store information in the ⟦user⟧ table of the picture that is being
+processed; this information is still available inside the ⟦*finish_mpfigure⟧
+callback that is executed just before the processed image is surrounded by
+properly-dimensioned boxes.
+
+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.
+
+\endmanual %
+
diff --git a/Master/texmf-dist/doc/luatex/minim-mp/minim-mp.pdf b/Master/texmf-dist/doc/luatex/minim-mp/minim-mp.pdf
new file mode 100644
index 00000000000..b511de32976
--- /dev/null
+++ b/Master/texmf-dist/doc/luatex/minim-mp/minim-mp.pdf
Binary files differ
diff --git a/Master/texmf-dist/metapost/minim-mp/minim.mp b/Master/texmf-dist/metapost/minim-mp/minim.mp
new file mode 100644
index 00000000000..c78dde2a927
--- /dev/null
+++ b/Master/texmf-dist/metapost/minim-mp/minim.mp
@@ -0,0 +1,73 @@
+
+delimiters ();
+
+message "Loading the minim extension macros";
+
+% interaction with tex
+def baseline expr o =
+ fill if numeric o: (0,o) else o fi
+ -- cycle withprescript "BASELINE:"; enddef;
+def boxresource expr nr = image(
+ fill unitsquare withprescript "BOXRESOURCE:" & decimal nr ;
+ setbounds currentpicture to unitsquare transformed runscript
+ ("return { 'box_size', tex.getboxresourcedimensions(" & decimal nr & ") }");
+ ) enddef;
+
+primarydef t infont f = image( runscript(
+ "return require('minim-mp').infont("&ditto&t&ditto&", "
+ &if numeric f: decimal(f) else: ditto&f&ditto fi&")" ) ) enddef;
+
+% interaction with lua
+vardef hexadecimal expr n =
+ % TODO: support other number systems
+ save d, a, m; a = ASCII("a"); m := abs n;
+ if n < 0: "-0x" else: "0x" fi
+ for i = 1 upto 7: &
+ hide(d := floor m div 256; m := 16(m - 256d);)
+ if d < 10: decimal d else: char(a+d-10) fi
+ if i = 3: & "." fi
+ endfor enddef;
+vardef scaledpoints expr n =
+ % use the fact that 800bp = 803pt
+ save a, m; m := abs n;
+ a1 = m div (1025/1024); % 1025/1024 = 800epsilon * 82
+ m := m mod (1025/1024);
+ a2 = m div 800epsilon; % max 82
+ m := (m mod 800epsilon) div epsilon;
+ a3 = floor(m*803/800 + 1/2);
+ if n < 0: "-(" else: "(" fi
+ & decimal(a1) & "*803*82 + "
+ & decimal(a2) & "*803 + "
+ & decimal(a3) & ")" enddef;
+
+% reporting
+def debug_pdf = runscript
+ ("require('minim-mp').enable_debugging()") enddef;
+def texmessage expr msg = runscript
+ ("texio.write_nl("&ditto&msg&ditto&")") enddef;
+
+% patterns
+def withpattern(suffix s) = withprescript
+ ("fillpattern: " & decimal(_patterns_.s)) enddef;
+def beginpattern(suffix s) =
+ begingroup clearxy; clearit;
+ interim defaultcolormodel:=1;
+ charcode:=incr _patterns_._last_;
+ _patterns_.s:=charcode; drawoptions();
+ pickup pencircle scaled 0.4pt;
+ save painttype; painttype:=2;
+ save _withcolor; let _withcolor = withcolor; save withcolor;
+ def withcolor = hide(painttype:=1) _withcolor enddef;
+ save matrix; transform matrix; enddef;
+def endpattern (expr xstep, ystep) =
+ if unknown matrix : matrix:=identity; fi
+ special "definepattern:" for e = charcode, tilingtype, painttype,
+ xstep, ystep, xxpart matrix, xypart matrix, yxpart matrix, yypart matrix:
+ & " " & decimal(e) endfor;
+ shipit; endgroup enddef;
+newinternal tilingtype; tilingtype:=1;
+_patterns_._last_ := 0;
+
+% so that we can load plain.mp after this
+let dump = endinput ;
+
diff --git a/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.ini b/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.ini
new file mode 100644
index 00000000000..a257f6a18ab
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.ini
@@ -0,0 +1,17 @@
+
+\catcode`\{=1 \catcode`\}=2
+\def\fmtname{minim-mp}
+\def\fmtversion{2021/1.0}
+
+\input minim.ini
+
+\toksapp\everyjob{%
+ \message{This is the minim metapost processor.}\relax
+ \newmetapostinstance[jobname="\jobname", mathmode="scaled"]\MP
+ \runmetapost\MP{input "\jobname";}%
+ \directlua {require('minim-mp').shipout(\the\MP)}%
+ \closemetapostinstance\MP
+ \bye}
+
+\dump
+
diff --git a/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.lua b/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.lua
new file mode 100644
index 00000000000..0f1851eba0d
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.lua
@@ -0,0 +1,1282 @@
+
+local alloc = require ('minim-alloc')
+local cb = require('minim-callbacks')
+alloc.remember ('minim-mp')
+
+local M = {}
+
+-- 1 AUXILIARY FUNCTIONS
+
+-- 2 pdf resource management
+
+-- We add /ColorSpace and /Pattern entries to all page and xform resources.
+-- Each key refers to a central (global) object mapping names to objects. This
+-- central dictionary will be written to the pdf inside the finish_pdffile
+-- callback.
+--
+-- In the future, /Shading and /ExtGState may also be added.
+--
+-- Central resource dictionaries and all used resources will be written to the
+-- pdf inside the finish_pdf callback.
+--
+-- Since a resource dictionary can only contain one entry of each kind, this
+-- package cannot coëxist with other packages doing their own resource
+-- management. I am aware of only one other package doing that, however, which
+-- is pgf. Luckily, pgf users are unlikely to be interested in metapost. Due to
+-- this incompatibility, however, resource management must be enabled
+-- explicitly.
+
+-- keys are pdf names (starting with a slash)
+local patterns = { }; M.patterns = patterns
+local colourspaces = { }; M.colourspaces = colourspaces
+
+local global_resources, pattern_dict_objnum, colourspace_dict_objnum
+function M.enable_resource_management()
+ if global_resources then return end
+ -- central dictionary objects
+ pattern_dict_objnum = pdf.reserveobj()
+ colourspace_dict_objnum = pdf.reserveobj()
+ global_resources = string.format('/Pattern %d 0 R /ColorSpace %d 0 R',
+ pattern_dict_objnum, colourspace_dict_objnum)
+ -- add to page and xform resources
+ pdf.setpageresources((pdf.getpageresources() or '')..global_resources)
+ pdf.setxformresources((pdf.getxformresources() or '')..global_resources)
+end
+
+-- Saved patterns should have the following fields:
+-- objnum the reserved object number for the pattern (optional)
+-- used true if the pattern is in use (should be set automatically)
+-- attr the pattern attributes
+-- stream the pattern drawing statements
+-- painttype the paint type of the pattern (1 or 2)
+-- Saved colour spaces should have the following fields:
+-- objnum the reserved object number for the colour space (optional)
+-- used true if the pattern is in use (should be set automatically)
+-- content the colour space contents (a pdf array)
+
+function M.write_resources()
+ if not global_resources then return end
+ -- patterns
+ local used_patterns = { '<<' }
+ for name, pat in pairs(patterns) do
+ if pat.used then
+ local objnum = pat.objnum or pdf.reserveobj()
+ pdf.immediateobj(objnum, 'stream', pat.stream, pat.attr)
+ table.insert(used_patterns, string.format('%s %d 0 R', name, objnum))
+ end
+ end
+ table.insert(used_patterns, '>>')
+ pdf.immediateobj(pattern_dict_objnum, table.concat(used_patterns, ' '))
+ -- colour spaces
+ local used_spaces = { '<<' }
+ for name, space in pairs(colourspaces) do
+ if space.used then
+ local objnum = space.objnum or pdf.reserveobj()
+ pdf.immediateobj(objnum, space.content)
+ table.insert(used_spaces, string.format('%s %d 0 R', name, objnum))
+ end
+ end
+ table.insert(used_spaces, '>>')
+ pdf.immediateobj(colourspace_dict_objnum, table.concat(used_spaces, ' '))
+end
+
+-- Write out resource objects at the end of the run
+cb.register('finish_pdffile', M.write_resources)
+
+-- 2 state metatable
+
+-- q Q store/restore graphics state
+
+-- In what follows, ‘append’ is a structure that contains a (tex) node list
+-- that will become the picture that is being constructed and an internal
+-- graphics state.
+--
+-- We can call append:somefunction(...) to append a node or append:somevariable
+-- to query the graphics state. These go via its metatable:
+
+local A = { } -- appending functions
+local append_meta =
+{
+ -- Either return an appending function or an entry in the graphics state.
+ __index = function(table, key)
+ return A[key] or table.state[#table.state][key]
+ end,
+ __newindex = function(table, key, value)
+ table.state[#table.state][key] = value
+ end
+}
+
+local function init_append()
+ return setmetatable({
+ head = false, tail = false, -- node list
+ baseline = false, -- internal state
+ node_count = 0, -- node count
+ state = { }, -- graphics state variables
+ user = { }, -- user data for extensions
+ }, append_meta)
+end
+
+function A.node(append, nd)
+ append.node_count = append.node_count + 1
+ append.head, append.tail = node.insert_after(append.head or nil, append.tail or nil, nd)
+end
+
+function A.save(append)
+ local st = append.state[#append.state] or { }
+ append.state[#append.state+1] =
+ {
+ miterlimit = st.miterlimit,
+ linejoin = st.linejoin,
+ linecap = st.linecap,
+ dashed = st.dashed,
+ stroke = st.stroke,
+ fill = st.fill
+ }
+ append:node(node.new(8, 30)) -- q
+end
+
+function A.restore(append)
+ append.state[#append.state] = nil
+ append:node(node.new(8, 31)) -- Q
+end
+
+-- The following callback is executed just before the final step of surrounding
+-- the image in properly-dimensioned boxes. It receives the processed image
+-- object as argument. That object has the following fields:
+-- head the head of the node list
+-- name the image name
+-- user the image user table
+-- wd, ht, dp tex dimensions
+-- x0, y0 position of (0,0) w.r.t. tex dimension
+-- bbox original metapost bounding box
+-- discard whether not to save this image
+cb.new_callback('finish_mpfigure', 'simple')
+
+-- 2 debugging
+
+local debugging = false
+
+function M.enable_debugging()
+ debugging = true
+ pdf.setcompresslevel(0)
+end
+
+local function print_prop(append, obj, prop)
+ if obj[prop] then
+ local val = string.gsub(tostring(obj[prop]), '\r', '<CR>')
+ append:literal('%% %s: %s', prop, val)
+ end
+end
+
+local function print_multi(append, obj, prop)
+ if obj[prop] then
+ local x = ''
+ for i = 1,#obj[prop] do
+ x = x .. tostring(obj[prop][i])..' '
+ end
+ append:literal('%% %s: %s', prop, x)
+ end
+end
+
+local function print_knots(append, obj, ptype)
+ if obj[ptype] then
+ append:literal('%% %s: see below', ptype)
+ for i = 1, #obj[ptype] do
+ local a,b = obj[ptype][i].left_type or 'joined', obj[ptype][i].right_type or 'joined'
+ append:literal('%% -+- knot: %s-%s', a, b)
+ append:literal('%% | x_coord: %s', tostring(obj[ptype][i].x_coord))
+ append:literal('%% | y_coord: %s', tostring(obj[ptype][i].y_coord))
+ append:literal('%% | right_x: %s', tostring(obj[ptype][i].right_x))
+ append:literal('%% | right_y: %s', tostring(obj[ptype][i].right_y))
+ append:literal('%% | left_x: %s', tostring(obj[ptype][i].left_x))
+ append:literal('%% | left_y: %s', tostring(obj[ptype][i].left_y))
+ end
+ end
+end
+
+function A.printobj(append, obj)
+ if not debugging then return end
+ local nc = append.node_count
+ append:literal('%%Object Type: %s', obj.type)
+ print_prop(append, obj, 'text')
+ print_prop(append, obj, 'font')
+ print_prop(append, obj, 'dsize')
+ print_prop(append, obj, 'width')
+ print_prop(append, obj, 'height')
+ print_prop(append, obj, 'depth')
+ print_multi(append, obj, 'transform')
+ print_multi(append, obj, 'color')
+ print_knots(append, obj, 'path')
+ print_knots(append, obj, 'htap')
+ if obj.pen then
+ local x = obj.pen.type or 'not elliptical'
+ append:literal('%% pen: see below, form: %s', x)
+ local x = mplib.pen_info(obj)
+ append:literal('%% | width: %s', tostring(x.width))
+ append:literal('%% | sx: %s', tostring(x.sx))
+ append:literal('%% | sy: %s', tostring(x.sy))
+ append:literal('%% | rx: %s', tostring(x.rx))
+ append:literal('%% | ry: %s', tostring(x.ry))
+ append:literal('%% | tx: %s', tostring(x.tx))
+ append:literal('%% | ty: %s', tostring(x.ty))
+ print_knots(append, obj, 'pen')
+ end
+ if obj.dash then
+ local d = ''
+ for i = 1,#obj.dash.dashes do
+ d = d ..tonumber(obj.dash.dashes[i]).. ' '
+ end
+ append:literal('%% dashes: %s', d)
+ append:literal('%%dash offset: %s', tostring(obj.dash.offset))
+ end
+ print_prop(append, obj, 'prescript')
+ print_prop(append, obj, 'postscript')
+ print_prop(append, obj, 'miterlimit')
+ print_prop(append, obj, 'linejoin')
+ print_prop(append, obj, 'linecap')
+ append.node_count = nc
+end
+
+-- 2 pdf literal helpers
+
+local function format_numbers(...)
+ return (string.format(...)
+ :gsub('[.]0+ ', ' ')
+ :gsub('([.][1-9]+)0+ ', '%1 '))
+end
+
+-- Only to be used for coordinates: ‘cm’ parameters should not be rounded.
+local function pdfnum(operator, ...)
+ local fmt = string.format('%%.%sf %s', pdf.getdecimaldigits(), operator)
+ return format_numbers(fmt, ...)
+end
+
+local function point_fmt(operator, ...)
+ local dd = pdf.getdecimaldigits()
+ local fmt = string.format('%%.%sf %%.%sf %s', dd, dd, operator)
+ return format_numbers(fmt, ...)
+end
+
+local function bbox_fmt(...)
+ local dd = pdf.getdecimaldigits()
+ local fmt = string.format('/BBox [ %%.%sf %%.%sf %%.%sf %%.%sf ]', dd, dd, dd, dd)
+ return format_numbers(fmt, ...)
+end
+
+local function curve_fmt(...)
+ local dd = pdf.getdecimaldigits()
+ local fmt = string.format('%%.%sf %%.%sf %%.%sf %%.%sf %%.%sf %%.%sf c', dd, dd, dd, dd, dd, dd)
+ return format_numbers(fmt, ...)
+end
+
+function A.literal(append, fmt, ...)
+ local lit = node.new(8,16)
+ lit.data = fmt:format(...)
+ append:node(lit)
+end
+
+-- 2 colour conversion
+
+local function rgb_to_gray (r,g,b)
+ return ( tex.count['RtoG'] * r / 10000
+ + tex.count['GtoG'] * g / 10000
+ + tex.count['BtoG'] * b / 10000 )
+end
+
+local function cmyk_to_rgb (c,m,y,k)
+ return (1-k)*(1-c), (1-k)*(1-m), (1-k)*(1-y)
+end
+
+local function rgb_to_cmyk (r, g, b)
+ local k = 1 - math.max(r, g, b)
+ if k == 1 then
+ return 0, 0, 0, k
+ else
+ return (1-r-k)/(1-k), (1-g-k)/(1-k), (1-b-k)/(1-k), k
+ end
+end
+
+local function convert_colour(cr)
+ -- always write rgb or cmyk black as grayscale
+ if cr[1] == 0 and cr[2] == 0 and cr[3] == 0
+ and (cr[4] == 1 or not cr[4]) then
+ return { 0 }
+ end
+ -- see if we must convert to another colour space
+ local n = #cr
+ local convert = tex.count['convertmpcolours']
+ if convert == 1 then
+ if n == 3 then
+ return { rgb_to_gray(table.unpack(cr)) }
+ elseif n == 4 then
+ return { rgb_to_gray(cmyk_to_rgb(table.unpack(cr))) }
+ end
+ elseif convert == 3 and n == 4 then
+ return { cmyk_to_rgb(table.unpack(cr)) }
+ elseif convert == 4 and n == 3 then
+ return { rgb_to_cmyk(table.unpack(cr)) }
+ end
+ -- otherwise return the colour unchanged
+ return cr
+end
+
+-- 2 colour literals
+
+-- MetaPost supports colour models with 1, 3 or 4 parameters. The corresponding
+-- pdf operators are
+
+-- RG rg RGB colour model for stroke/filling
+-- K k CMYK colour model for stroke/filling
+-- G g Grayscale colour model for stroke/filling
+
+-- ... CS cs select colourspace for stroke/filling
+-- ... SCN scn select colour in colourspace for stroke/filling
+
+-- In order to prevent duplicate colour settings, ‘append’ contains the state
+-- 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 ]' }
+
+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 function get_colour_params(cr)
+ return format_numbers(colour_template[#cr], table.unpack(cr))
+end
+
+local function get_stroke_colour(cr)
+ return get_colour_params(cr)..colour_stroke_operators[#cr]
+end
+
+local function get_fill_colour(cr, pattern)
+ local params = get_colour_params(cr)
+ if pattern then
+ local ptype, pname = table.unpack(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)
+ elseif ptype == 1 then -- uncoloured pattern
+ return string.format('/Pattern cs %s scn', pname)
+ else -- should be unreachable
+ alloc.err('Unknown pattern paint type %s', ptype)
+ end
+ end
+ return params..colour_fill_operators[#cr]
+end
+
+-- TODO We should allow the user access to the internals here, so that support
+-- for other colour spaces may be added. Then, pattern support could also be
+-- moved into a plugin.
+function A.colour(append, cr, otype)
+ if cr and #cr > 0 then
+ cr = convert_colour(cr)
+ -- stroke colour
+ if otype ~= 'fill' then
+ local stroke = get_stroke_colour(cr)
+ if stroke ~= append.stroke then
+ append.stroke = stroke
+ append:literal(stroke)
+ end
+ end
+ -- fill colour (possibly a pattern)
+ if otype ~= 'outline' then
+ local fill = get_fill_colour(cr, append.pattern)
+ append.pattern = nil -- patterns only apply to one object
+ if fill ~= append.fill then
+ append.fill = fill
+ append:literal(fill)
+ end
+ end
+ end
+end
+
+-- 2 stroke setting literals
+
+-- M miterlimit
+-- j linejoin
+-- J linecap
+-- d dash
+
+-- As with colour, we record the last-used settings in ‘append’. This function
+-- is only called for fill/outline objects.
+
+function A.linestate (append, object)
+ local ml = object.miterlimit
+ local res = { }
+ if ml and ml ~= append.miterlimit then
+ append.miterlimit = ml
+ table.insert(res, pdfnum('M', ml)) -- TODO is pdfnum here correct?
+ end
+ local lj = object.linejoin
+ if lj and lj ~= append.linejoin then
+ append.linejoin = lj
+ table.insert(res, string.format('%i j', lj))
+ end
+ local lc = object.linecap
+ if lc and lc ~= append.linecap then
+ append.linecap = lc
+ table.insert(res, string.format('%i J', lc))
+ end
+ local dl = object.dash
+ if dl then
+ local d = string.format('[%s] %i d', table.concat(dl.dashes or {},' '), dl.offset)
+ if d ~= append.dashed then
+ append.dashed = d
+ table.insert(res, d)
+ end
+ elseif append.dashed then
+ append.dashed = false
+ table.insert(res, string.format('[] 0 d'))
+ end
+ append:literal(table.concat(res, ' '))
+end
+
+-- 2 path literals
+
+-- MetaPost only gives us curved lines; if some of the segments are straight,
+-- we have to find out ourselves:
+
+local bend_tolerance = 131/65536 -- context uses this value, presumably with good reasons
+
+local function curved (a,b)
+ local d = b.left_x - a.right_x
+ if math.abs(a.right_x - a.x_coord - d) <= bend_tolerance
+ and math.abs(b.x_coord - b.left_x - d) <= bend_tolerance then
+ d = b.left_y - a.right_y
+ if math.abs(a.right_y - a.y_coord - d) <= bend_tolerance
+ and math.abs(b.y_coord - b.left_y - d) <= bend_tolerance then
+ return false
+ end
+ end
+ return true
+end
+
+-- Write the path as list of points.
+
+-- m move to this point without drawing anything
+-- c make a curved line (two control points)
+-- l make a straight line
+
+function A.curve(append, path, open, concat)
+ -- The ‘concat’ function transforms the given coordinate pairs
+ -- according to the current transformation.
+ concat = concat or function(px, py) return px, py end
+ local res = { }
+ local append_line = function(to, op)
+ table.insert(res, point_fmt(op, concat(to.x_coord, to.y_coord)))
+ end
+ local append_curve = function(from, to)
+ local a, b = concat(from.right_x, from.right_y)
+ local c, d = concat(to.left_x, to.left_y)
+ table.insert(res, curve_fmt(a,b,c,d,concat(to.x_coord, to.y_coord)))
+ end
+ local append_knot = function(from, to)
+ if not from then
+ append_line(to, 'm')
+ elseif curved(from, to) then
+ append_curve(from, to)
+ else
+ append_line(to, 'l')
+ end
+ end
+ local knot, prev_knot
+ for i=1, #path do
+ knot = path[i]
+ append_knot(prev_knot, knot)
+ prev_knot = knot
+ end
+ if not open then
+ append_knot(knot, path[1])
+ elseif #path == 1 then
+ append_line(path[1], 'l') -- special case: draw a point
+ end
+ append:literal(table.concat(res, ' '))
+end
+
+-- And actually draw the path.
+
+-- S draw the path
+-- h close the path
+-- s close and draw the path (equivalent to h S)
+-- f fill the path (implies h)
+-- b close, fill and draw the path (equivalent to h B)
+-- n do nothing (used for clipping paths)
+
+local function get_path_operator (otype, open)
+ if otype == 'fill' then
+ return 'f'
+ elseif otype == 'outline' then
+ return (open and 'S') or 's'
+ elseif otype == 'both' then
+ return 'b'
+ end
+end
+
+--
+
+-- 1 PROCESSING OBJECTS
+
+-- Our processors are located in two tables: one that contains a processor for
+-- each object and one that contains processors for certain ‘special’ statements. Every
+-- table contains functions that take the ‘append’ structure and the current
+-- object as arguments. The tables are indexed by object/special name.
+
+-- The treatment of pre- and postscripts is like this:
+-- * Pre- and postscripts should be of the form ‘type: instruction’.
+-- * If the type of a prescript is present in the ‘specials’ table, the
+-- corresponding function will be used in place of the default handling.
+-- * Execution of other prescripts and of all postscripts will be given by the
+-- entries in the ‘prescripts’ and ‘postscripts’ table corresponding to the
+-- type.
+
+local process = {}
+local specials = {}
+M.specials = specials
+
+local prescripts = {}
+local postscripts = {}
+M.prescripts = prescripts
+M.postscripts = postscripts
+
+-- 2 choosing a parser
+
+local function split_specials(specials)
+ if not specials then return function() return end end
+ local l = specials:explode('\r')
+ local i, n = 0, #l
+ return function()
+ if i < n then i = i + 1 else return end
+ local sp, instr = l[i]:match('^([a-zA-Z_]+): *(.*)')
+ return sp, instr or l[i]
+ end
+end
+
+local function parse_object (append, object)
+ append:printobj(object)
+ local processor = nil
+ for sp, instr in split_specials(object.prescript) do
+ if not sp then
+ alloc.err('Unrecognised prescript: %s', instr)
+ append:literal('%% unsupported special excluded (see log file)')
+ elseif specials[sp] then
+ processor = function() specials[sp](append, instr, object) end
+ elseif prescripts[sp] then
+ prescripts[sp](append, instr, object)
+ else
+ alloc.err('Unrecognised prescript ‘%s’ with instruction: %s', sp, instr)
+ append:literal('%% unsupported special excluded (see log file)')
+ end
+ end
+ if processor then
+ processor()
+ else
+ process[object.type](append, object)
+ end
+ for sp, instr in split_specials(object.postscript) do
+ if not sp then
+ alloc.err('Unrecognised postscript: %s', instr)
+ append:literal('%% unsupported special excluded (see log file)')
+ elseif postscripts[sp] then
+ postscripts[sp](append, instr, object)
+ else
+ alloc.err('Unrecognised postscript ‘%s’ with instruction: %s', sp, instr)
+ append:literal('%% unsupported special excluded (see log file)')
+ end
+ end
+end
+
+-- 2 start/stop bounds
+
+-- These can be ignored, because they are taken into account by MetaPost when
+-- it reports the bounding box of the picture.
+
+process.start_bounds = function(append, object) end
+process.stop_bounds = function(append, object) end
+
+-- 2 clipping
+
+-- W clipping operator
+-- n path no-op
+
+process.start_clip = function(append, object)
+ append:save()
+ append:curve(object.path, false)
+ append:literal('W n')
+end
+
+process.stop_clip = function(append, object)
+ append:restore()
+end
+
+-- 2 fill and outline
+
+function A.set_pen(append, object, otype, open)
+ 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
+ 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
+ end
+end
+
+process.fill_or_outline = function(append, object, otype)
+ 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
+ if t then
+ local d = t.sx * t.sy - t.rx - t.ry
+ local concat = function(px, py)
+ return (t.sy*px - t.ry*py)/d, (t.sx*py - t.rx*px)/d
+ end
+ appendpath = function(path)
+ append:save()
+ append:literal('%f %f %f %f %f %f cm', t.sx, t.rx, t.ry, t.sy, t.tx, t.ty)
+ append:curve(path, open, concat)
+ append:literal(operator)
+ append:restore()
+ end
+ else
+ appendpath = function(path)
+ append:curve(path, open)
+ append:literal(operator)
+ end
+ end
+ if object.path then appendpath(object.path) end
+ if object.htap then appendpath(object.htap) end -- reversed trajectory
+end
+
+process.fill = function(append, object)
+ process.fill_or_outline(append, object, 'fill')
+end
+
+process.outline = function(append, object)
+ process.fill_or_outline(append, object, 'outline')
+end
+
+-- 2 specials
+
+-- pure specials are already taken care of in parse_object (they only have
+-- a ‘prescript’ field).
+process.special = function(append, object) end
+
+prescripts.pdf = function(append, special, object)
+ append:literal(special)
+end
+postscripts.pdf = prescripts.pdf
+
+prescripts.pdfcomment = function(append, str, object)
+ append:literal('%% %s', str)
+end
+postscripts.pdfcomment = prescripts.pdfcomment
+
+prescripts.latelua = function(append, str, object)
+ local n = node.new(8,7) -- late_lua
+ n.data = str
+ append:node(n)
+end
+postscripts.latelua = prescripts.latelua
+
+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).
+ append.baseline = object.path[1].y_coord
+end
+
+-- 2 patterns
+
+prescripts.fillpattern = function(append, str, object)
+ M.enable_resource_management()
+ local name = '/MnmP'..tonumber(str)
+ local pat = patterns[name]
+ if not pat then
+ alloc.err('Unknown pattern %s', name)
+ else
+ append.pattern = { pat.painttype, name }
+ pat.used = true
+ end
+end
+
+specials.definepattern = function(append, str, object)
+ local nr, tiling, paint, xs, ys, xx, xy, yx, yy = table.unpack(str:explode())
+ append.user.pattern_info = { nr = nr, xstep = xs, ystep = ys,
+ tilingtype = tiling, painttype = paint,
+ matrix = { xx = xx, xy = xy, yx = yx, yy = yy, x = 0, y = 0 } }
+end
+
+local function definepattern(head, user, bb)
+ local bb = bbox_fmt(table.unpack(bb))
+ local pat, literals, resources = { }, { }
+ -- pattern content
+ for n in node.traverse(head) do
+ -- try if we can construct the content stream ourselves; otherwise,
+ -- stuff the pattern template into an xform.
+ if n.id == 8 then
+ -- try if we can simply concatenate pdf statements
+ if n.subtype == 16 then -- literal
+ table.insert(literals, n.data)
+ elseif n.subtype == 30 then -- save
+ table.insert(literals, 'q')
+ elseif n.subtype == 31 then -- restore
+ table.insert(literals, 'Q')
+ else
+ goto fail
+ end
+ 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
+ -- 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',
+ 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
+end
+
+cb.register('finish_mpfigure', function(img)
+ if img.user.pattern_info then
+ definepattern(img.head, img.user, img.bbox)
+ img.discard = true
+ end
+end)
+
+-- 2 text
+
+process.text = function(append, object)
+ alloc.err('Text figure primitives are not supported (this message should not occur)')
+end
+
+local function get_transform(rect)
+ -- reconstruct the applied transformation by examining a unit square, i.e.
+ -- ‘rect’ was originally the path (0,0) -- (0,1) -- (1,1) -- (1,0)
+ local ll, lr, ul = rect[1], rect[2], rect[4]
+ -- determine translation from (0,0)
+ local tx, ty = ll.x_coord, ll.y_coord
+ -- determine rotation and scaling from the diagonals
+ local sx, rx = lr.x_coord - tx, lr.y_coord - ty
+ local ry, sy = ul.x_coord - tx, ul.y_coord - ty
+ return sx, rx, ry, sy, tx, ty
+end
+
+local function make_surrounding(nd_id, head)
+ local nd = node.new(nd_id)
+ nd.dir = 'TLT'
+ nd.head = 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)
+ vertical.shift = tex.sp('-'..ty..'bp')
+ horizontal.shift = tex.sp(''..tx..'bp')
+ return outer
+end
+
+local function apply_transform(rect, box)
+ local sx, rx, ry, sy, tx, ty = get_transform(rect)
+ local transform = node.new(8,29)
+ transform.next, box.prev = box, transform
+ transform.data = string.format('%f %f %f %f', sx, rx, ry, sy)
+ return apply_translation(transform, tx, ty)
+end
+
+function A.box(append, object, box)
+ box.width = 0 ; box.height = 0 ; box.depth = 0;
+ local outer = apply_transform(object.path, box)
+ append:colour(object.color, 'fill')
+ append:save()
+ append:node(outer)
+ append:restore()
+end
+
+specials.TEXBOX = function(append, box, object)
+ local box = tex.getbox(tonumber(box))
+ append:box(object, node.copy_list(box))
+end
+
+specials.CHAR = function(append, data, object)
+ local char, font, xo, yo = table.unpack(data:explode(' '))
+ local n = node.new(29)
+ n.char, n.font, n.xoffset, n.yoffset =
+ tonumber(char), tonumber(font), tonumber(xo), tonumber(yo)
+ append:box(object, node.hpack(n))
+end
+
+specials.BOXRESOURCE = function(append, resource, object)
+ local rule = tex.useboxresource(tonumber(resource))
+ append:box(object, rule)
+end
+
+--
+
+-- 1 METAPOST INSTANCES
+
+local instances = { }
+M.instances = instances
+
+-- 2 small instance helper functions
+
+local default_catcodes = alloc.new_catcodetable('minim:mp:catcodes')
+
+-- parameters: wd, ht+dp, dp
+local function make_transform(w, h, d)
+ return string.format('identity xscaled %fpt yscaled %fpt shifted (0,-%fpt)',
+ w/65536, (h+d)/65536, d/65536)
+end
+
+local function print_log (nr, res)
+ local i = instances[nr]
+ -- only write to term if exit status increases
+ local log, alog
+ if res.status > i.status then
+ local nrlines, maxlines = 0, 16
+ alog = alloc.amsg
+ log = function(...)
+ if nrlines == maxlines then
+ 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
+ log, alog = alloc.log, alloc.alog
+ end
+ -- split log into lines; discard empty lines at the end
+ local report = res.log and res.log:explode('\n') or { }
+ while report[#report] == '' do
+ 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)
+ end
+ -- generate error or warning if needed
+ if res.status > i.status then
+ if res.status == 3 then
+ alloc.err('Metapost instance now defunct')
+ 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')
+ end
+ end
+ -- save the exit status for later comparison
+ i.status = res.status
+end
+
+local function finder (name, mode, ftype)
+ if mode == 'w' then
+ return name
+ else
+ return kpse.find_file(name,ftype)
+ end
+end
+
+-- 2 processing results
+
+local function process_results(nr, res)
+ print_log(nr, res)
+ local pictures = {}
+ if res.fig then
+ alloc.alog (' (%d figures)', #res.fig)
+ for i=1, #res.fig do
+ local fig = res.fig[i]
+ local name = fig:filename()
+ local append = init_append()
+ -- parse and append all objects
+ append:save()
+ local objects = fig:objects()
+ for j=1, #objects do
+ parse_object (append, objects[j])
+ end
+ append:restore()
+ -- bounding box and baseline information
+ local bb = fig:boundingbox()
+ local llx, lly, urx, ury = table.unpack(bb)
+ local bas = append.baseline or lly
+ local pic =
+ { head = append.head
+ , user = append.user
+ , name = name
+ , bbox = bb }
+ if llx > urx and lly > ury then
+ pic.discard = true -- picture is empty
+ pic.wd, pic.ht, pic.dp, pic.x0, pic.y0 = 0,0,0,0,0
+ else
+ pic.wd = tex.sp(string.format('%s bp', urx - llx))
+ pic.ht = tex.sp(string.format('%s bp', ury - bas))
+ pic.dp = tex.sp(string.format('%s bp', bas - lly))
+ pic.x0 = tex.sp(string.format('%s bp', -llx ))
+ pic.y0 = tex.sp(string.format('%s bp', -bas ))
+ end
+ cb.call_callback('finish_mpfigure', pic)
+ if not pic.discard then
+ pic.head = apply_translation(append.head, -llx, -bas)
+ end
+ if debugging then
+ alloc.msg('┌ image %s, with %s objects, %s nodes',
+ name, #objects, append.node_count)
+ alloc.msg('│ dimensions (%fpt+%fpt)x%fpt',
+ pic.ht/65536, pic.dp/65536, pic.wd/65536)
+ alloc.msg('└ origin (%fpt,%fpt)',
+ pic.x0/65536, pic.y0/65536)
+ if pic.discard then alloc.amsg(' [discarded]') end
+ end
+ table.insert(pictures, pic)
+ end
+ else
+ alloc.alog (' (no figures)')
+ end
+ return pictures
+end
+
+-- 2 running lua scripts
+
+-- Code run with runscript may return a value; we will try and convert it to
+-- a string that metapost can understand.
+--
+-- Tables of the form { 'box_size', width, height, depth, margin } are
+-- converted to a transformation. (The margin will be ignored for now, this may
+-- change in the future.)
+
+local function default_env()
+ local env = { }
+ for k,v in pairs(_G) do
+ env[k] = v
+ end
+ 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
+ local result = f()
+ if result == nil then
+ return
+ else
+ local t = type(result)
+ if t == 'number' then
+ return string.format('%.16f', result)
+ elseif t == 'string' then
+ return result
+ elseif t == 'table' and t[1] == 'box_size' then
+ -- TODO: take the margin into account if present (t[5])?
+ return make_transform(t[2], t[3], t[4])
+ elseif t == 'table' and #t < 5 then
+ local fmt = #t == 1 and '%.16f'
+ or #t == 2 and '(%.16f, %.16f)'
+ or #t == 3 and '(%.16f, %.16f, %.16f)'
+ or #t == 4 and '(%.16f, %.16f, %.16f, %.16f)'
+ return fmt:format(table.unpack(t))
+ else
+ return tostring(result)
+ end
+ end
+ else
+ local mp_msg = string.gsub(msg, '"', '"&ditto&"')
+ -- TODO: provide errhelp once the echo is gone
+ return string.format('hide(errmessage "Lua error: %s")', mp_msg)
+ end
+end
+
+-- 2 typesetting with tex
+
+-- The result of the maketext function is fed back into metapost; on that side,
+-- the representation of a <picture primary> will be expected for btex...etex
+-- and an empty string for verbatimtex...etex. maketext is indistinguishable
+-- from btex...etex.
+--
+-- We return here an image() containing a single path; it is that path that
+-- will be replaced with a stored node list later on.
+--
+-- The path itself is a unit rectangle: from the rectangle we will be able to
+-- reconstruct how the tex box will have been transformed. We indicate with
+-- a prescript that the rectangle should be replaced.
+--
+-- The size of the picture is communicated to MetaPost by a ‘setbounds’
+-- instruction.
+
+local function maketext(text, mode)
+ if mode == 0 then -- btex or maketext
+ local nr = alloc.new_box()
+ table.insert(current_instance.boxes, nr)
+ local assignment = string.format('\\global\\setbox%s=\\hbox{%s}', nr, text)
+ tex.runtoks(function() tex.print(current_instance.catcodes, assignment:explode('\n')) end)
+ local box = tex.box[nr]
+ return 'image ( fill unitsquare withprescript "TEXBOX:' ..nr..'";'..
+ 'setbounds currentpicture to unitsquare transformed '..
+ make_transform(box.width, box.height, box.depth) .. ';)'
+ elseif mode == 1 then -- verbatimtex
+ tex.runtoks(function() tex.print(current_instance.catcodes, text:explode('\n')) end)
+ end
+end
+
+local infont_box = alloc.new_box('infont box')
+function M.infont(text, fnt)
+ local fontid = tonumber(fnt) or font.id(fnt)
+ tex.runtoks(function()
+ tex.sprint(default_catcodes, string.format(
+ '\\setbox%d=\\hbox{\\setfontid%d\\relax', infont_box, fontid))
+ tex.sprint(-2, text); tex.sprint(default_catcodes, '}')
+ end)
+ local res, x = { }, 0
+ for n in node.traverse(tex.box[infont_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))
+ x = x + n.width
+ elseif n.id == 12 then -- glue
+ x = x + n.width
+ elseif n.id == 13 then -- kern
+ x = x + n.kern
+ end
+ end
+ return table.concat(res, '')
+end
+
+-- 2 opening, running and and closing instances
+
+local function apply_default_instance_opts(t)
+ return
+ { ini_version = true
+ , error_line = (texconfig.error_line or 79) - 5
+ , extensions = 1
+ , find_file = finder
+ --, script_error = ...
+ , job_name = t.jobname
+ , math_mode = t.math or 'scaled'
+ , random_seed = t.seed or nil
+ -- , file_line_error_style
+ , make_text = maketext
+ , run_script = runscript
+ }
+end
+
+local function save_image_list(self, res)
+ for _, img in ipairs(res) do
+ if img.discard then
+ node.flush_list(img.head)
+ else
+ self.results.count = self.results.count + 1
+ img.prev, img.next = self.results.last, nil
+ self.results.by_name[img.name] = img
+ if nil == self.results.first then
+ self.results.first = img
+ else
+ self.results.last.next = img
+ end
+ self.results.last = img
+ end
+ end
+end
+
+function M.run (nr, code)
+ local self = instances[nr]
+ if self.status == 3 then
+ alloc.msg('metapost instance %s (%s) is defunct and will not run', self.jobname, nr)
+ 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)
+end
+
+M.init_files = { '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 opts = apply_default_instance_opts(t)
+ local instance = mplib.new(opts)
+ instance:execute(init)
+ local continue
+ -- adding the instance
+ instances[nr] =
+ { nr = nr
+ , instance = instance
+ , jobname = t.jobname
+ , results = { first = nil, last = nil, by_name = {}, count = 0 }
+ , status = 0
+ , catcodes = t.catcodes or default_catcodes
+ , boxes = { } -- allocated by maketext
+ , env = t.env or default_env()
+ }
+ 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)
+ end
+ for _, nr in ipairs(i.boxes) do
+ -- remove allocated boxes
+ tex.box[nr] = nil
+ end
+ instances[nr] = false
+end
+
+-- 2 retrieving and using results
+
+local function retrieve(nr, name)
+ local results = instances[nr].results
+ local image
+ if name then
+ image = results.by_name[name]
+ results.by_name [name] = nil
+ else
+ image = results.first
+ end
+ results.count = results.count - 1
+ if image.prev then
+ if image.next then
+ image.prev.next = image.next
+ image.next.prev = image.prev
+ else
+ image.prev.next = nil
+ results.last = image.prev
+ end
+ elseif image.next then
+ results.first = image.next
+ image.next.prev = nil
+ else
+ results.first = nil
+ results.last = nil
+ end
+ return image
+end
+
+function M.left (nr)
+ return instances[nr].results.count
+end
+
+function M.get_image(nr, name)
+ local image = retrieve(nr, name)
+ if image then
+ local box = node.hpack(image.head)
+ box.width = image.wd
+ box.height = image.ht
+ box.depth = image.dp
+ return box
+ end
+end
+
+function M.shipout(nr)
+ local ox, oy = pdf.getorigin
+ local ho, vo = tex.hoffset, tex.voffset
+ pdf.setorigin()
+ tex.hoffset = 0
+ while M.left(nr) > 0 do
+ local image = retrieve(nr)
+ tex.pageheight = (image.ht + image.dp)
+ tex.pagewidth = image.wd
+ tex.voffset = image.ht
+ tex.box[255] = node.vpack(node.hpack(image.head))
+ tex.shipout(255)
+ tex.count[0] = tex.count[0] + 1
+ end
+ pdf.setorigin(ox, oy)
+ tex.hoffset, tex.voffset = ho, vo
+end
+
+-- 2 tex interface
+
+local scan_int = token.scan_int
+local scan_string = token.scan_string
+
+alloc.luadef('closemetapostinstance', function() M.close(scan_int()) end)
+
+alloc.luadef('runmetapost', function()
+ M.run(scan_int(), scan_string())
+end, 'protected')
+alloc.luadef('runmetapostimage', function()
+ local i = scan_int()
+ M.run(i, 'beginfig(0); '..scan_string()..' endfig;')
+ node.write(M.get_image(i))
+end, 'protected')
+
+alloc.luadef('getnextmpimage', function()
+ node.write(M.get_image(scan_int()))
+end)
+alloc.luadef('getnamedmpimage', function()
+ node.write(M.get_image(scan_int(), scan_string()))
+end)
+
+alloc.luadef('boxnextmpimage', function()
+ local i = scan_int()
+ local b = scan_int()
+ tex.box[b] = M.get_image(i)
+end)
+
+alloc.luadef('boxnamedmpimage', function()
+ local i = scan_int()
+ local b = scan_int()
+ local n = scan_string()
+ tex.box[b] = M.get_image(i, n)
+end)
+
+alloc.luadef('remainingmpimages', function()
+ tex.sprint(M.left(scan_int()))
+end)
+
+--
+
+--
+
+return M
+
diff --git a/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.tex b/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.tex
new file mode 100644
index 00000000000..8e39c7f227a
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/minim-mp/minim-mp.tex
@@ -0,0 +1,61 @@
+
+\ifdefined\minimmploaded
+ \message{(skipped)}
+ \expandafter\endinput\fi
+\chardef\minimmploaded = \catcode`\:
+\catcode`\: = 11
+
+\input minim-alloc
+
+% a default catcode table
+\newcatcodetable \minim:mp:catcodes
+{\catcode`\:=12 \catcode`\@=12
+ \savecatcodetable\csname minim:mp:catcodes\endcsname}
+
+\directlua { require('minim-mp') }
+
+% The following are provided from the lua side.
+%
+% Note that only two of these are \protected – you are responsible for the
+% proper time and place of expansion of the others.
+%
+% \closemetapostinstance \instance
+% \runmetapost \instance { code } % \protected
+% \runmetapostimage \instance { code } % \protected
+% \getnextmpimage \instance
+% \getnamedmpimage \instance {name}
+% \remainingmpimages \instance
+% \boxnextmpimage \instance box-nr
+% \boxnamedmpimage \instance box-nr {name}
+
+% \directmetapost [ options ] { code }
+\protected\def\directmetapost{\withoptions[]\directmetapost:}
+\def\directmetapost:[#1]#2{%
+ \begingroup
+ \newmetapostinstance[#1]\:mpinst:
+ \runmetapost\:mpinst:{#2}%
+ \loop \ifnum\remainingmpimages\:mpinst:>0\relax
+ \getnextmpimage\:mpinst: \repeat
+ \closemetapostinstance\:mpinst:
+ \endgroup}
+
+% \newmetapostinstance [ options ] \instance
+\protected\def\newmetapostinstance{\withoptions[]\newmetapostinstance:}
+\def\newmetapostinstance:[#1]#2{%
+ \chardef#2=\directlua{
+ tex.sprint(require('minim-mp').open{#1})
+ }\relax}
+
+% colour conversion: 1 grayscale 2 cmyk 3 rgb 4 rgb as spot
+\newcount \convertmpcolours
+\convertmpcolours = 0
+
+\newcount\RtoG \RtoG = 2126
+\newcount\GtoG \GtoG = 7152
+\newcount\BtoG \BtoG = 0722
+
+\catcode`\: = \minimmploaded
+
+
+
+
diff --git a/Master/tlpkg/libexec/ctan2tds b/Master/tlpkg/libexec/ctan2tds
index b352073d978..fa0c09da300 100755
--- a/Master/tlpkg/libexec/ctan2tds
+++ b/Master/tlpkg/libexec/ctan2tds
@@ -2082,7 +2082,7 @@ $standardtex
'mil3', 'NULL',
'minim', '\.(lua|tex|sty|ini)$',
'minim-math', '\.(lua|tex)$',
- 'minim-mp', '\.(lua|tex)$',
+ 'minim-mp', '\.(lua|tex|ini)$',
'minim-pdf', '\.(lua|tex)$',
'minitoc', '\.ml[do]|' . $standardtex,
'misc', '\.sty|\.ltx|\.cls',
diff --git a/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc b/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
index 9e080bf7afb..1b3481560a7 100644
--- a/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
+++ b/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
@@ -53,6 +53,8 @@ depend luavlna
depend luaxml
depend minim
depend minim-math
+depend minim-mp
+depend minim-pdf
depend newpax
depend nodetree
depend odsfile
diff --git a/Master/tlpkg/tlpsrc/minim-mp.tlpsrc b/Master/tlpkg/tlpsrc/minim-mp.tlpsrc
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/Master/tlpkg/tlpsrc/minim-mp.tlpsrc