diff options
-rw-r--r-- | Master/texmf-dist/doc/luatex/minim-mp/EUPL-1.2-EN.txt | 287 | ||||
-rw-r--r-- | Master/texmf-dist/doc/luatex/minim-mp/README | 28 | ||||
-rw-r--r-- | Master/texmf-dist/doc/luatex/minim-mp/minim-mp.doc | 329 | ||||
-rw-r--r-- | Master/texmf-dist/doc/luatex/minim-mp/minim-mp.pdf | bin | 0 -> 82287 bytes | |||
-rw-r--r-- | Master/texmf-dist/metapost/minim-mp/minim.mp | 73 | ||||
-rw-r--r-- | Master/texmf-dist/tex/luatex/minim-mp/minim-mp.ini | 17 | ||||
-rw-r--r-- | Master/texmf-dist/tex/luatex/minim-mp/minim-mp.lua | 1282 | ||||
-rw-r--r-- | Master/texmf-dist/tex/luatex/minim-mp/minim-mp.tex | 61 | ||||
-rwxr-xr-x | Master/tlpkg/libexec/ctan2tds | 2 | ||||
-rw-r--r-- | Master/tlpkg/tlpsrc/collection-luatex.tlpsrc | 2 | ||||
-rw-r--r-- | Master/tlpkg/tlpsrc/minim-mp.tlpsrc | 0 |
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⟧© 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 Binary files differnew file mode 100644 index 00000000000..b511de32976 --- /dev/null +++ b/Master/texmf-dist/doc/luatex/minim-mp/minim-mp.pdf 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 |