% \iffalse meta-comment -- by the way, this file contains UTF-8
%
% Copyright (C) 2008-2026 by Hans Hagen, Taco Hoekwater, Elie Roux,
% Manuel Pégourié-Gonnard, Philipp Gesang and Kim Dohyun.
% Currently maintained by Kim Dohyun.
% Support: <https://github.com/lualatex/luamplib>
%
% This work is under the GPL v2.0 license.
%
% This work consists of the main source file luamplib.dtx
% and the derived files
%    luamplib.sty, luamplib.lua and luamplib.pdf.
%
% Unpacking:
%    tex luamplib.dtx
%
% Documentation:
%    lualatex luamplib.dtx
%
%<*ignore>
\begingroup
  \def\x{LaTeX2e}%
\expandafter\endgroup
\ifcase 0\ifx\install y1\fi\expandafter
         \ifx\csname processbatchFile\endcsname\relax\else1\fi
         \ifx\fmtname\x\else 1\fi\relax
\else\csname fi\endcsname
%</ignore>
%<*install>
\input docstrip.tex
\Msg{************************************************************************}
\Msg{* Installation}
\Msg{* Package: luamplib - metapost package for LuaTeX.}
\Msg{************************************************************************}

\keepsilent
\askforoverwritefalse

\let\MetaPrefix\relax

\preamble

See source file '\inFileName' for licencing and contact information.

\endpreamble

\let\MetaPrefix\DoubleperCent

\generate{%
  \usedir{tex/luatex/luamplib}%
  \file{luamplib.sty}{\from{luamplib.dtx}{package}}%
}

\def\MetaPrefix{-- }

\def\luapostamble{%
  \MetaPrefix^^J%
  \MetaPrefix\space End of File `\outFileName'.%
}

\def\currentpostamble{\luapostamble}%

\generate{%
  \usedir{tex/luatex/luamplib}%
  \file{luamplib.lua}{\from{luamplib.dtx}{lua}}%
}

\obeyspaces
\Msg{************************************************************************}
\Msg{*}
\Msg{* To finish the installation you have to move the following}
\Msg{* files into a directory searched by TeX:}
\Msg{*}
\Msg{*     luamplib.sty luamplib.lua}
\Msg{*}
\Msg{* Happy TeXing!}
\Msg{*}
\Msg{************************************************************************}

\endbatchfile
%</install>
%<*ignore>
\fi
%</ignore>
%<*driver>
\NeedsTeXFormat{LaTeX2e}
\ProvidesFile{luamplib.drv}%
  [2026/07/10 v2.42.4 Interface for using the mplib library]%
\documentclass[11pt,a4paper]{ltxdoc}
\usepackage{geometry,graphicx,xspace}
\usepackage[x11names]{xcolor}
\AssignSocketPlug{build/column/outputbox}{space-floats-footnotes}
%
\def\primarycolor{DodgerBlue4}  %%-> rgb  16  78 139 | #104e8b
\def\secondarycolor{Goldenrod4} %%-> rgb 139 105 200 | #8b6914
%
\usepackage[
   hyperindex=false,
    bookmarks=true,
   colorlinks=true,
    linkcolor=\primarycolor,
     urlcolor=\secondarycolor,
    citecolor=\primarycolor,
     pdftitle={The luamplib package},
   pdfsubject={Interface for using the mplib library},
    pdfauthor={Hans Hagen, Taco Hoekwater, Elie Roux, Philipp Gesang \& Kim Dohyun},
  pdfkeywords={luatex, lualatex, mplib, metapost}
]{hyperref}
\usepackage{fontspec}
\setmainfont[
  Numbers     = OldStyle,
  Ligatures   = TeX,
  BoldFont    = {Linux Libertine O Bold},
  ItalicFont  = {Linux Libertine O Italic},
  SlantedFont = {Linux Libertine O Italic},
]{Linux Libertine O}
\setmonofont[Scale=MatchLowercase]{InconsolataN}
%setsansfont[Ligatures=TeX]{Linux Biolinum O}
\setsansfont[UprightFont=*Medium,BoldFont=*Heavy,Ligatures=TeX,Scale=MatchLowercase]{Iwona}
%setmathfont{XITS Math}

\newcommand\ConTeXt {Con\-\kern-.0667em\TeX t\xspace}

\newcommand*\email [1] {<\href{mailto:#1}{#1}>}
\newcommand \file       {\nolinkurl}

\newcommand*\metapost {\textsc{metapost}\xspace}
\newcommand*\mplib {\pkg{mplib}\xspace}

\protected\def\secref#1{\S\,\ref{sec:#1}}
\newcommand*\notabene{\par\textsc{n.b.}\quad}

\usepackage{luamplib}\mplibforcehmode
\newcount\vln
\def\startmpfig{\setbox0\vbox\bgroup\raggedleft
  \leftskip=0pt plus1fill\relax \parfillskip=0pt plus1fil\relax }
\def\stopmpfig#1{\egroup \vln=0\relax
  \AddToHook{para/begin}[mpfig/example]{\advance\vln 1\relax
    \ifnum #1=\vln \vadjust{\vbox to 0pt{\vskip-2ex \vss \unvbox0 \vss}}\fi }\relax
  \AddToHookNext{env/verbatim/after}{\RemoveFromHook{para/begin}[mpfig/example]}}

\def\bsx{\leavevmode\begingroup\begingroup\color{Goldenrod4}}
\def\esx{\endgroup\endgroup}

\let\descriptionlabelorig\descriptionlabel
\def\descriptionlabel#1{\descriptionlabelorig{\textsf{#1}}}

\flushbottom
\begin{document}
  \DocInput{luamplib.dtx}%
\end{document}
%</driver>
% \fi
%
% \CheckSum{0}
%
% \CharacterTable
%  {Upper-case    \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
%   Lower-case    \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
%   Digits        \0\1\2\3\4\5\6\7\8\9
%   Exclamation   \!     Double quote  \"     Hash (number) \#
%   Dollar        \$     Percent       \%     Ampersand     \&
%   Acute accent  \'     Left paren    \(     Right paren   \)
%   Asterisk      \*     Plus          \+     Comma         \,
%   Minus         \-     Point         \.     Solidus       \/
%   Colon         \:     Semicolon     \;     Less than     \<
%   Equals        \=     Greater than  \>     Question mark \?
%   Commercial at \@     Left bracket  \[     Backslash     \\
%   Right bracket \]     Circumflex    \^     Underscore    \_
%   Grave accent  \`     Left brace    \{     Vertical bar  \|
%   Right brace   \}     Tilde         \~}
%
% \title{The \pkg{luamplib} package}
% \author{Hans Hagen, Taco Hoekwater, Elie Roux, Philipp Gesang and Kim Dohyun\\
% Current Maintainer: Kim Dohyun\\
% Support: \url{https://github.com/lualatex/luamplib}}
% \date{2026/07/10 v2.42.4}
%
% \maketitle
%
% \begin{abstract}
% \centering
% Package to have \metapost code typeset directly in a document with \LuaTeX
% \end{abstract}
%
% \tableofcontents
%
% \section{Documentation}
%
% This package aims at providing a simple way to typeset directly \metapost
% code in a document with \LuaTeX. \LuaTeX\ is built with the Lua
% \mplib library, that runs \metapost code. This package is basically a
% wrapper for the Lua \mplib functions and some \TeX\
% functions to have the output of the \mplib functions in the PDF.
%
% Using this package is easy: in Plain, type your \metapost code between the
% macros \cs{mplibcode} and \cs{endmplibcode}, and in \LaTeX\ in the
% |mplibcode| environment.
%
% The resulting \metapost figures are put in a \TeX\ |hbox| with dimensions
% adjusted to the \metapost code.
%
% The code of \pkg{luamplib} is basically from the |luatex-mplib.lua| and |luatex-mplib.tex| files
% from \ConTeXt. They have been adapted to \LaTeX\ and Plain by Elie Roux and
% Philipp Gesang and new functionalities have been added by Kim Dohyun.
% The most notable changes are:
%
% \begin{itemize}
% \item Possibility to use |btex| |...| |etex| to typeset \TeX\ code.
%   \bsx|textext| \meta{string}\esx\ is a more versatile macro equivalent to \bsx|TEX| \meta{string}\esx\ from \pkg{TEX.mp}.
%   |TEX| is also allowed and is a synonym of |textext|.
%   The argument of
%   \mplib's primitive |maketext| will also be processed by the same routine.
%
% \item Possibility to use |verbatimtex| |...| |etex| to run a \TeX\ code.
%   \bsx|VerbatimTeX| \meta{string}\esx\ is a more versatile macro corresponding to |verbatimtex| command.
%   Of course the behavior cannot be the same as the stand-alone \pkg{mpost}, so that
%   you cannot include \cs{documentclass}, \cs{usepackage} etc.
%   When these \TeX\ commands are found in |verbatimtex| |...| |etex|, the entire code will be
%   ignored.
%
%   The treatment of |verbatimtex| command has changed a lot since v2.20:
%   see below \secref{mpliblegacybehavior}.
%
% \item In the past,
%   the package required PDF mode in order to have some output.
%   Starting with v2.7 it works in DVI mode as well, though
%   DVIPDFMx is the only DVI tool currently supported.
% \end{itemize}
%
% It seems to be convenient to divide the explanations of some more changes and cautions
% into three parts: \TeX, \textsc{MetaPost}, and Lua interfaces.
%
% \subsection{\TeX}
%
% \subsubsection{Forcing horizontal mode}\label{sec:mplibforcehmode}
%   When \bsx\cs{mplibforcehmode}\esx\ is declared, every \metapost figure box will be
%   typeset in horizontal mode, so that \cs{centering}, \cs{raggedleft} etc.\
%   will have effects. \cs{mplibnoforcehmode}, being default for backward compatibility,
%   reverts this setting.\footnote{Actually these commands redefine \cs{prependtomplibbox}. So you
%     can redefine this macro with anything suitable before a box.
%     But see \secref{taggedPDF} on Tagged PDF. }
%
% \subsubsection{Insert some code into every code chunk}
%   \label{sec:everymplib}
%   \bsx\cs{everymplib\{...\}}\esx\ and \bsx\cs{everyendmplib\{...\}}\esx\ redefine
%   the Lua table entry containing \metapost code
%   which will
%   be automatically inserted at the beginning and ending of each \metapost code chunk.
%\startmpfig
%\mpfig draw fullcircle scaled 1cm; \endmpfig
%\stopmpfig{3}
%\begin{verbatim}
%    \everymplib{ beginfig(0); }
%    \everyendmplib{ endfig; }
%    \begin{mplibcode}
%      % beginfig/endfig not needed
%      draw fullcircle scaled 1cm;
%    \end{mplibcode}
%\end{verbatim}
%
% \subsubsection{Choosing a \metapost format}
%   \label{sec:mplibsetformat}
%   There are (basically) two formats for \metapost: \emph{plain} and
%   \emph{metafun}. By default, the \emph{plain} format is used, but you can set
%   the format to be used by future figures at any time using
%   \bsx\cs{mplibsetformat}\allowbreak\marg{format name}\esx.
%
%   \notabene As \emph{metafun} is such a complicated format,
%   we cannot support all the special effects provided by \emph{metafun}.
%   At least, however, transparency (actually opacity), shading (gradient colors) and transparency group
%   are fully supported,
%   and outlinetext is supported by our own alternative |mpliboutlinetext|
%   (see below \secref{mpliboutlinetext}).
%   You can try other effects as well, though we did not fully tested their proper functioning.
%
% \begin{description}
% \item[transparency]
%   (|texdoc metafun| \S\,8.2) Transparency is so simple that you can apply it to an object,
%   with \emph{plain} format as well as \emph{metafun},
%   just by appending \bsx|withprescript| |"tr_transparency=|\allowbreak\meta{numeric}|"|\esx\ to the sentence.
%   ($0 \le \meta{numeric} \le 1$)
%
%   From v2.36, |withtransparency| is available with \emph{plain} format as well.
%   See below \secref{luamplibtransparency}.
%
% \item[shading]
%   (|texdoc metafun| \S\,8.3) One thing worth mentioning about shading is:
%   when a color expression is given in string type,
%   it is regarded by \pkg{luamplib} as
%   a color expression of \TeX\ side.
%   For instance, when |withshadecolors("orange",| |2/3red)| is given, the first color |"orange"| will be
%   interpreted as a \pkg{color}, \pkg{xcolor} or \pkg{l3color}'s expression.
%
%   From v2.36, shading is available with \emph{plain} format as well with extended functionality.
%   See below \secref{luamplibshading}.
%
% \item[transparency group]
%   \hypertarget{metafunformat}{}\relax
%   (|texdoc metafun| \S\,8.8)
%   As for transparency group, the current \emph{metafun} document is not correct.
%   The true syntax is:
%\begin{verbatim}
%    draw <picture>|<path> asgroup <string>
%\end{verbatim}
%   where \meta{string} should be |""| (empty), |"isolated"|, |"knockout"|, or |"isolated,knockout"|.
%   Beware that currently many of the PDF rendering applications,
%   except Adobe Acrobat and \TeX works,
%   cannot properly render the isolated or knockout effect.
%
%   Transparency group is available with \emph{plain} format as well with extended functionality.
%   See below \secref{transparencygroup}.
% \end{description}
%
% \subsubsection{Choosing a number system}
%   \label{sec:mplibnumbersystem}
%   Users can choose |numbersystem| option.
%   The default value is |scaled|, which can be changed
%   by declaring \bsx\cs{mplibnumbersystem\{double\}}\esx\ or
%   \bsx\cs{mplibnumbersystem\{decimal\}}\esx.
%
% \subsubsection{Showing \metapost log}
%   \label{sec:mplibshowlog}
%   When \bsx\cs{mplibshowlog\{enable\}}\esx\footnote{As for user's setting,
%   |enable|, |true| and |yes| are identical;
%   all others are identical to |disable|.} is declared, log messages returned by
%   the \metapost process will be printed to the |.log| file.
%   This is the \TeX{} side interface for |luamplib.showlog|.
%   Default value is |disable|.
%
% \subsubsection{verbatimtex Legacy behavior}
%   \label{sec:mpliblegacybehavior}
%   \paragraph{Legacy behavior}
%   By default, \cs{mpliblegacybehavior\{enable\}}
%   is already declared for backward compatibility,
%   in which case \TeX\ code in
%   |verbatimtex| |...| |etex| that comes just before |beginfig()|
%   will be inserted before the
%   following \metapost figure box.  In this way,
%   each figure box can be freely moved horizontally or vertically.
%   Also, a box number can be assigned to a figure box, allowing it to be
%   reused later.\footnote{But
%     the recommended way to reuse a figure is using \cs{mplibgroup} command.
%     See below \secref{mplibgroupendmplibgroup}.
%   }
%\begin{verbatim}
%    \mplibcode
%      verbatimtex \moveright 3cm etex; beginfig(0); ... endfig;
%      verbatimtex \leavevmode etex; beginfig(1); ... endfig;
%      verbatimtex \leavevmode\lower 1ex etex; beginfig(2); ... endfig;
%      verbatimtex \endgraf\moveright 1cm etex; beginfig(3); ... endfig;
%    \endmplibcode
%\end{verbatim}
%   \notabene \cs{endgraf} should be used instead of \cs{par} inside
%   mplibcode environment.
%
%   On the other hand, \TeX\ code in |verbatimtex| |...| |etex|
%   between |beginfig()| and |endfig| will be inserted
%   after flushing out the \metapost figure. An example:\footnote{\relax
%     But the recommended way to access \metapost variables
%     from \TeX\ (or Lua) side is to use Lua code via |luamplib.instances|.
%     For details see below \secref{luamplibinstances}.}
%\startmpfig
%\mpfig D:=sqrt(2)**9; draw fullcircle scaled D; VerbatimTeX("\gdef\Dia{"&decimal D&"}");\endmpfig
%\par diameter: \Dia bp.
%\stopmpfig{4}
%\begin{verbatim}
%    \mplibcode
%      D := sqrt(2)**9;
%      beginfig(0);
%        draw fullcircle scaled D;
%        VerbatimTeX("\gdef\Dia{" & decimal D & "}");
%      endfig;
%    \endmplibcode
%    diameter: \Dia bp.
%\end{verbatim}
%
%   \paragraph{New and recommended way}
%   By contrast,
%   when \bsx\cs{mpliblegacybehavior\{disable\}}\esx\ is declared, any
%   |verbatimtex| |...| |etex|, along with |btex| |...| |etex|, will be run
%   sequentially one by one.
%   So, some \TeX\ code in |verbatimtex| |...| |etex| will have effect on
%   |btex| |...| |etex| codes thereafter.
%\startmpfig
%\mpfig draw btex ABC etex; verbatimtex \bfseries etex; draw btex DEF etex shifted (1cm,0); draw btex GHI etex shifted (2cm,0); \endmpfig
%\stopmpfig{4}
%\begin{verbatim}
%    \begin{mplibcode}
%      beginfig(0);
%        draw btex ABC etex;
%        verbatimtex \bfseries etex;
%        draw btex DEF etex shifted (1cm,0);    % bold face
%        draw btex GHI etex shifted (2cm,0);    % bold face
%      endfig;
%    \end{mplibcode}
%\end{verbatim}
%
% \subsubsection{\TeX-text labels}
%   \label{sec:mplibtextextlabel}
%   \bsx\cs{mplibtextextlabel\{enable\}}\esx\ enables
%   the labels typeset via |textext| instead of |infont| operator.
%   So, |label("my text",| |origin)| thereafter is exactly the same as
%   |label(textext| |"my text",| |origin)|.
%   Default value is |disable|.
%
%   \notabene In the background,
%   \pkg{luamplib} redefines |infont| operator so that the right side
%   argument (the font part) is totally ignored. Therefore the left side arguemnt (the text part)
%   will be typeset with the current \TeX\ font.
%
%   From v2.35, however, the redefinition of |infont| operator has been revised:
%   when the character code of the text argument is less than $32$~(control characters),
%   or is equal to $35$~(|#|), $36$~(|$|), \iffalse |$|\fi
%   $37$~(|%|), $38$~(|&|), $92$~(|\|), $94$~(|^|),
%   $95$~(|_|), $123$~(|{|), $125$~(|}|), $126$~(|~|) or $127$~(DEL),
%   the original |infont| operator will be used instead of |textext| operator
%   so that the font part will be honored.
%   Despite the revision, please take care of |char| operator in the text argument,
%   as this might bring unpermitted characters into \TeX.
%
% \subsubsection{Inherit \metapost code}
%   \label{sec:mplibcodeinherit}
%   \bsx\cs{mplibcodeinherit\{enable\}}\esx\ enables the inheritance
%   of variables, constants, and macros defined by previous \metapost code chunks.
%   On the other hand, \cs{mplibcodeinherit}\allowbreak|{disable}| will make
%   each code chunk being treated as an independent instance, never
%   affected by previous code chunks.
%   Default value is |disable|.
%
% \subsubsection[\cs{mplibglobaltextext}]{\cs{mplibglobaltextext\{enable\textbar disable\}}}
%   \label{sec:mplibglobaltextext}
%   Default: |disable|.
%   Formerly, to inherit |btex| |...| |etex| boxes as well as other \metapost macros, variables and constants,
%   it was necessary to declare \cs{mplibglobaltextext}\allowbreak|{enable}| in advance.
%   But from v2.27, this is implicitly enabled when \cs{mplibcodeinherit}
%   is enabled.
%   The command still remains mostly for backward compatibility.
%\startmpfig
%\mpfig label(btex $\sqrt{2}$ etex, origin); draw fullcircle scaled 20; picture pic; pic := currentpicture; \endmpfig
%\par\mpfig currentpicture := pic scaled 2; \endmpfig
%\stopmpfig{6}
%\begin{verbatim}
%    \mplibcodeinherit{enable}
%    %\mplibglobaltextext{enable}
%    \everymplib{ beginfig(0);} \everyendmplib{ endfig;}
%    \mplibcode
%      label(btex $\sqrt{2}$ etex, origin);
%      draw fullcircle scaled 20;
%      picture pic; pic := currentpicture;
%    \endmplibcode
%    \mplibcode
%      currentpicture := pic scaled 2;
%    \endmplibcode
%\end{verbatim}
%
% \subsubsection{Separate \metapost instances}
%   \pkg{luamplib} v2.22 has added the support for several named \metapost instances
%   in \LaTeX\ environment |mplibcode| or
%   Plain \TeX\ commands \cs{mplibcode} |...| \cs{endmplibcode}.
%   The syntax for \LaTeX\ is:
%\begin{verbatim}
%    \begin{mplibcode}[instanceName]
%      % some mp code
%    \end{mplibcode}
%\end{verbatim}
%   The behavior is as follows.
%   \begin{itemize}
%   \item  All the variables and functions are shared
%     only among all the environments belonging to the same instance.
%   \item  \cs{mplibcodeinherit} only affects the environments
%     with no instance name set (since if a name is set,
%     the code is intended to be reused at some point).
%   \item |btex| |...| |etex| boxes are also shared and do not
%     require \cs{mplibglobaltextext}.
%   \item  When an instance names is set,
%     respective \cs{currentmpinstancename} is set as well.
%   \end{itemize}
%   In parellel with this functionality, we support
%   optional argument of instance name for \cs{everymplib} and
%   \cs{everyendmplib}, affecting only those |mplibcode| environments
%   of the same name.
%   Unnamed \cs{everymplib} affects not only those instances with no name,
%   but also those with name but with no corresponding \cs{everymplib}.
%   The syntax is:
%\begin{verbatim}
%    \everymplib[instanceName]{...}
%    \everyendmplib[instanceName]{...}
%\end{verbatim}
%
% \subsubsection{Verbatim input}
%   \label{sec:mplibverbatim}
%   Users can issue \bsx\cs{mplibverbatim\{enable\}}\esx, after which
%   the contents of mplibcode environment will be read verbatim. As a result,
%   except for \cs{mpdim} and \cs{mpcolor} (see \secref{mpdim} and \secref{mpcolor}),
%   all other \TeX\ commands outside of the
%   |btex| or |verbatimtex| |...| |etex| are not expanded and will be fed
%   literally to the \mplib library.
%   Default value is |disable|.
%
% \subsubsection{\TeX\ dimen}\label{sec:mpdim}
%   Besides other \TeX\ commands, \bsx\cs{mpdim\{...\}}\esx\ is specially allowed
%   in the mplibcode environment. This feature is inpired by \pkg{gmp} package authored by
%   Enrico Gregorio. Please refer to the manual of \pkg{gmp} package for details.
%\startmpfig
%\mpfig draw origin--(.4\mpdim{\linewidth},0) withpen pencircle scaled 4 dashed evenly scaled 4 withcolor \mpcolor{orange}; \endmpfig
%\stopmpfig{3}
%\begin{verbatim}
%    draw origin--(.4\mpdim{\linewidth},0)
%      withpen pencircle scaled 4 dashed evenly scaled 4
%      withcolor \mpcolor{orange} ;
%\end{verbatim}
%
% \subsubsection{\TeX\ color}\label{sec:mpcolor}
%   With the command \bsx\cs{mpcolor[...]\{...\}}\esx, color expressions of
%   \pkg{color}, \pkg{xcolor} and \pkg{l3color} module/packages can be used in the mplibcode
%   environment (after |withcolor| command, in principle).
%   See the example above at \secref{mpdim}.
%   The optional |[...]| denotes the option of \pkg{color} or \pkg{xcolor}'s \cs{color} command.
%   For spot colors, \pkg{l3color} module is well supported in PDF and DVI mode.
%   Package \pkg{colorspace} is also supported in PDF mode,
%   but could conflict with \pkg{luamplib}'s special features such as uncolored tiling pattern
%   when \cs{DocumentMetadata}, i.e.\ PDF management code, is not loaded.
%
%   \notabene Formerly,
%   only the first object would have been colored as intended
%   among multiple graphical objects in a \metapost image,
%   because \cs{mpcolor} always produced |withprescript| command internally.
%   Since v2.38.1, now that \cs{mpcolor} returns a \metapost color expression if possible,
%   users can issue the sentence as follows
%   without worrying about the location of the color command:
%\startmpfig
%\mpfig draw image (drawarrow (left--right) scaled 5) scaled 8 withcolor \mpcolor{red!50};\endmpfig
%\stopmpfig{2}
%\begin{verbatim}
%    draw image (drawarrow (left--right) scaled 5)
%      scaled 8
%      withcolor \mpcolor{red!50} ;
%\end{verbatim}
%   \notabene Be aware, however, that even after v2.38.1 \cs{mpcolor} still inserts
%   |withprescript| command when the color specified is a spot color.
%   Users therefore have to revise the code so that the color can have effect inside the image.
%   For instance:
%\begin{verbatim}
%    draw image (
%        drawarrow (left--right) scaled 5 withcolor \mpcolor{spotA}
%      ) scaled 8 ;
%\end{verbatim}
%
%
% \subsubsection[\cs{mpfig}, \cs{endmpfig}]{\cs{mpfig ...} \cs{endmpfig}}
%   Besides the |mplibcode| environment (for \LaTeX) and
%   \cs{mplibcode ...} \cs{endmplibcode} (for Plain),
%   we also provide unexpandable \TeX\ macros \cs{mpfig} |...| \cs{endmpfig} and its starred version
%   \cs{mpfig*} |...| \cs{endmpfig} to save typing toil.
%   The former is roughly the same as follows:
%\begin{verbatim}
%    \begin{mplibcode}[@mpfig]
%      beginfig(0)
%        token list declared by \everymplib[@mpfig]
%        ...
%        token list declared by \everyendmplib[@mpfig]
%      endfig;
%    \end{mplibcode}
%\end{verbatim}
%   and the starred version is roughly the same as follows:
%\begin{verbatim}
%    \begin{mplibcode}[@mpfig]
%      ...
%    \end{mplibcode}
%\end{verbatim}
%   In these macros \cs{mpliblegacybehavior\{disable\}}
%   is forcibly declared.
%   Again, as both share the same instance name, \metapost codes are inherited among them.
%   A simple example:
%\startmpfig
%\mpfig* input boxes \endmpfig \mpfig drawoptions(withpen pencircle scaled 1 withcolor 2/3red); circleit.a(btex Box 1 etex); drawboxed(a); \endmpfig
%\stopmpfig{3}
%\begin{verbatim}
%    \everymplib[@mpfig]{ drawoptions(withpen pencircle scaled 1 withcolor 2/3red); }
%    \mpfig* input boxes \endmpfig
%    \mpfig
%      circleit.a(btex Box 1 etex); drawboxed(a);
%    \endmpfig
%\end{verbatim}
%   Users can change the instance name (default value:\ |@mpfig|) by redefining
%   \cs{mpfiginstancename}, after which a new \mplib instance will start and
%   code inheritance too will begin anew.
%   \cs{let}\allowbreak\cs{mpfiginstancename}\allowbreak\cs{empty} will
%   prevent code inheritance if \cs{mplibcodeinherit} is not true.
%
% \subsubsection{About cache files}
%   \label{sec:mplibcachedir}
%   To support |btex| |...| |etex| in external |.mp| files, \pkg{luamplib}
%   inspects the content of each and every |.mp| file and makes caches
%   if nececcsary before returning their paths to the \mplib library.
%   This could waste the compilation time, as most |.mp| files
%   do not contain |btex| |...| |etex| commands.  So \pkg{luamplib} provides
%   macros as follows, so that users can give instructions about files
%   that do not require this functionality.
%   \begin{itemize}
%   \item \cs{mplibmakenocache}|{|\meta{filename}|[,|\meta{filename}|,...]}|
%   \item \cs{mplibcancelnocache}|{|\meta{filename}|[,|\meta{filename}|,...]}|
%   \end{itemize}
%   where \meta{filename} is a filename without |.mp| extension.
%   Note that |.mp| files under |$TEXMFMAIN/|\allowbreak|metapost/|\allowbreak|base| and
%   |$TEXMFMAIN/metapost/context/base| are already registered by default.
%   \notabene \bsx\cs{mplibmakenocache}|{*}|\esx\ will suppress making cache files.
%   Use it at your own risk.
%
%   By default, cache files will be stored in |$TEXMFVAR/luamplib_cache| or,
%   if it's not available (mostly not writable),
%   in the directory where output files are saved:
%   to be specific, |$TEXMF_OUTPUT_DIRECTORY/luamplib_cache|,
%   |./luamplib_cache|, |$TEXMFOUTPUT/luamplib_cache|, and |.|, in this order.
%   |$TEXMF_OUTPUT_DIRECTORY| is normally the value of |--output-directory|
%   command-line option.
%
%   Users can change this behavior by the command
%   \bsx\cs{mplibcachedir}\marg{directory path}\esx, where tilde (|~|) is interpreted
%   as the user's home directory (on a windows machine as well).
%   As backslashes (\cs{}) should be escaped by users, it would be easier to use
%   slashes (|/|) instead.
%
% \subsubsection{About figure box metric}
%   Notice that, after each figure is processed, the macro \cs{MPwidth} stores
%   the width value of the latest figure; \cs{MPheight}, the height value.
%   Incidentally, also note that \cs{MPllx}, \cs{MPlly}, \cs{MPurx}, and
%   \cs{MPury} store the bounding box information of the latest figure
%   without the unit |bp|.
%
% \subsubsection{luamplib.cfg}
%   At the end of package loading, \pkg{luamplib} searches
%   |luamplib.cfg| and, if found, reads the file in automatically.
%   Frequently used settings such as \cs{everymplib}, \cs{mplibforcehmode}
%   or \cs{mplibcodeinherit} are suitable for going into this file.
%
% \subsubsection{Tagged PDF}\label{sec:taggedPDF}
% When \pkg{tagpdf} package is loaded and activated, |mplibcode| environment accepts additional options for tagged PDF\@.
% The code related to this functionality is currently in experimental stage, not guaranteeing backward compatibility.
% Available optional keys are similar to those of the \LaTeX's |picture| environment
% (|texdoc| |latex-lab-graphic|).
% The default tagging mode is the |alt| key with |Figure| structure.
% \begin{description}
% \item[alt=\meta{text}] starts a |Figure| tag by default and
%   sets an alternate text of the figure from the \meta{text}.
%   BBox info will be added automatically to the PDF\@.
%   This key is needed for ordinary \metapost figures, for which,
%   if no |alt| text is given, a default text will be used with a warning issued.
%   You can change the alternate text within \metapost code as well:
%   \bsx|VerbatimTeX| |"\mplibalttext|\marg{text}|";|\esx
% \item[actualtext=\meta{text}] starts a |Span| tag implicitly and sets a
%   replacement text (a.k.a.\ actual text) from the \meta{text}.
%   If in vertical mode, horizontal mode will be forced by \cs{noindent} command.\footnote{\relax
%     It is not recommended to personally redefine \cs{prependtomplibbox}.
%     Apart from using \cs{mplibforcehmode} or \cs{mplibnoforcehmode},
%     the redefinition might be incompatible with |actualtext| key.
%     See \secref{mplibforcehmode} on these commands. }
%   BBox info will not be added.
%   This key is intended for figures which can be represented by a character or
%   a small sequence of characters.
%   You can change the actual text within \metapost code as well:
%   \bsx|VerbatimTeX| |"\mplibactualtext|\marg{text}|";|\esx
% \item[artifact] starts an |Artifact| MC (marked content).
%   BBox info will not be added.
%   This key is intended for decorative figures which have no semantic meaning.
% \item[text] starts an |Artifact| MC but enables tagging on \TeX-text boxes
%   (such as |btex| |...| |etex|, excluding pictures made by |infont| operator).
%   If in vertical mode, horizontal mode will be forced by \cs{noindent} command.\footnote{\relax
%     The key |text| also shares the limitation mentioned in the previous footnote.}
%   BBox info will not be added.
%   This key is intended for figures the meaning of which is the sequence of texts in the
%   \TeX-text boxes in the order they are drawn in the figure.
%
%   \notabene Within |text|-mode figures, reusing \TeX-text boxes is strongly discouraged.
%
%   Note that the text in a \TeX-text box which starts with |[taggingoff]| will not be tagged
%   at all, and
%   of course |[taggingoff]| and its trailing spaces will be gobbled by \pkg{luamplib}.
%   For example, the first and the third boxes in the following figure will not be tagged,
%   and still remain in the |Artifact| MC-chunks.
%\startmpfig
%\mpfig draw btex [taggingoff] $\sqrt 2$ etex; draw textext "$\sqrt 3$" shifted 12down; draw TEX "[taggingoff] $\sqrt 5$" shifted 24down; draw maketext "$\sqrt 7$" shifted 36down; draw mplibgraphictext "$\sqrt x$" shifted 48down; \endmpfig
%\stopmpfig{5}
%\begin{verbatim}
%    \begin{mplibcode}[text]
%      beginfig(1)
%        draw btex [taggingoff] $\sqrt 2$ etex ;
%        draw textext "$\sqrt 3$" shifted 12down ;
%        draw TEX "[taggingoff] $\sqrt 5$" shifted 24down ;
%        draw maketext "$\sqrt 7$" shifted 36down ;
%        draw mplibgraphictext "$\sqrt x$" shifted 48down ;
%      endfig;
%    \end{mplibcode}
%\end{verbatim}
% \item[off] Given this key, nothing will be tagged by \pkg{luamplib}.
% \item[tag=\meta{name}] You can choose a tag name, default value being |Figure|.\relax
%   \footnote{The option |tag=false|, however, is a synonym of the |off| key.}
%   For instance, you can set \bsx|tag=Formula,| |alt=|\meta{text}\esx\ to get
%   a |Formula| element with its alternate text.\footnote{Beware that this bypasses
%     \LaTeX's regular math formula tagging, for which the |text| key is needed.}
% \item[adjust-BBox=\meta{dimens}] You can correct the BBox attribute of the figure
%   by space-separated four dimensional values, which will be added to the
%   automatically calculated BBox values.
%   To draw the bounding box for checking with half-transparent red color, you can add
%   |debug=|\allowbreak|BBox| to the argument of \cs{DocumentMetadata} command.
% \item[tagging-setup=\meta{key-val list}]
%   This key accepts as its value the list of key-value options mentioned so far.
% \end{description}
% You can set these options anywhere in the document by declaring
% \bsx\cs{SetKeys}\allowbreak |[luamplib/tagging]|\allowbreak \marg{key-val list}\esx,
% which will affect \pkg{mplib} figures thereafter in the scope.
% And the options listed above are provided for \cs{mpfig} and \cs{usemplibgroup}
% (see \hyperlink{usemplibgroup}{below} \secref{transparencygroup}) commands as well.
%\begin{verbatim}
%    \begin{mplibcode}[myInstanceName, alt=drawing of a circle]
%      ...
%    \end{mplibcode}
%
%    \mpfig[alt=drawing of a square box]
%      ...
%    \endmpfig
%
%    \mppattern{...}           % see below
%      \mpfig[off]             % do not tag this figure
%        ...
%      \endmpfig
%    \endmppattern
%
%    \mplibgroup{...}          % see below
%      \mpfig[off]             % do not tag this figure
%        ...
%      \endmpfig
%    \endmplibgroup
%
%    \usemplibgroup[alt=drawing of a triangle]{...}
%\end{verbatim}
% As for the instance name of |mplibcode| environment,
% \bsx|instance=|\meta{name}\esx\ or \bsx|instancename=|\allowbreak\meta{name}\esx\ is also allowed
% in addition to the raw instance name as shown above.
%
%
% \subsection{\textsc{MetaPost}}
%
% \subsubsection{\TeX\ dimen and color}
%   \bsx|mplibdimen| \meta{string}\esx\ and \bsx|mplibcolor| \meta{string}\esx\
%   are \metapost interfaces for the \TeX\ commands
%   \cs{mpdim} and \cs{mpcolor} (see above \secref{mpdim} and \secref{mpcolor}). For example,
%   |mplibdimen| |"\linewidth"| is basically the same as \cs{mpdim\{\cs{linewidth}\}}, and
%   |mplibcolor| |"red!50"| is basically the same as \cs{mpcolor\{red!50\}}.
%   The difference is that these \metapost operators can also be used in external |.mp| files,
%   which cannot have \TeX\ commands outside of the |btex| or |verbatimtex| |...| |etex|.
%
% \subsubsection{More on \TeX\ color}
%   \label{sec:mplibtexcolor}
%   \bsx|mplibtexcolor| \meta{string}\esx\ is a \metapost operator that converts a \TeX\ color expression
%   to a \metapost color expression, that can be used anywhere color expression is expected
%   as well as after the |withcolor| command.\footnote{\relax
%     Since v2.38.1, the operation of |mplibtexcolor| is the same as that of |mplibcolor|
%     if the color specified is not a spot color.
%   }
%   For instance:
%\begin{verbatim}
%    color col;
%    col := mplibtexcolor "olive!50";
%\end{verbatim}
%   But the result may vary in its color model (gray/rgb/cmyk)
%   according to the given \TeX\ color.
%   Therefore the example shown above would raise a \metapost error:
%   |cmykcolor col;| should have been declared.
%   By contrast, \bsx|mplibrgbtexcolor| \meta{string}\esx\ always returns rgb-model expressions.
%
%   \notabene Spot colors are forced to cmyk or rgb model,
%   so these operators are not recommended for spot colors.
%
% \subsubsection{Fill and stroke colors}
%   \label{sec:withmplibcolors}
%   Unlike the |withcolor| command, users can specify one color for filling
%   and another color for stroking using the macro |withmplibcolors| at the end of a sentence.
%   The syntax is \bsx|withmplibcolors| |(|\meta{fill color expr}|,| \meta{stroke color expr}|)|\esx.
%   When the argument is in string type, it is regarded as the color expression of \TeX\ side.
%   A simple example (see also the example at \secref{mplibdrawglyph}):
%\startmpfig
%\mpfig filldraw fullcircle scaled 40 withpen pencircle scaled 2 withmplibcolors ("orange!60", 2/3red); \endmpfig
%\stopmpfig{2}
%\begin{verbatim}
%    filldraw fullcircle scaled 40
%      withpen pencircle scaled 2
%      withmplibcolors ("orange!60", 2/3red) ;
%\end{verbatim}
%   The PDF file size is much smaller than issueing two sentences with different colors,
%   though the apparent effect is the same.
%
% \subsubsection{Transparency}
%   \label{sec:luamplibtransparency}
%   \bsx|withtransparency(|\meta{number} \textbar\ \meta{string}|,| \meta{numeric}|)|\esx\
%   is provided for \emph{plain} format as well as \emph{metafun}.
%   The first argument accepts a number or a name among
%   alternative transparency methods (a.k.a.\ \emph{blend modes}; see |texdoc| |metafun| \S\,8.2 Figure~8.1).
%   The second argument accepts a numeric expression denoting opacity.
%\startmpfig
%\mpfig fill unitsquare scaled 40 withcolor 1/3[blue,white] withtransparency(1,0.5); fill fullcircle scaled 40 withcolor 1/3[red,white] withtransparency (1,0.5); \endmpfig
%\stopmpfig{6}
%\begin{verbatim}
%    \mpfig
%      fill unitsquare scaled 40
%        withcolor 1/3[blue,white]
%        withtransparency (1, 0.5)           % or ("normal", 0.5)
%        ;
%      fill fullcircle scaled 40
%        withcolor 1/3[red,white]
%        withtransparency (1, 0.5)
%        ;
%    \endmpfig
%\end{verbatim}
%
% \subsubsection{Fill and stroke transparency}
%   \label{sec:withmplibopacities}
% By analogy with the macro |withmplibcolors| (see above \secref{withmplibcolors}),
% the macro |withmplibopacities| is also provided.
% The syntax is \bsx|withmplibopacities|
% |(|\meta{number} \textbar\ \meta{string}|,| \meta{numeric}, \meta{numeric}|)|\esx.
% The first argument is the same as that of |withtransparency| command
% described above at \secref{luamplibtransparency};
% the latter two arguments are numeric expressions denoting
% \emph{fill opacity} and \emph{stroke opacity} respectively.
% It is more efficient than issueing two sentences with different opacities.
%\startmpfig
%\mpfig pickup pencircle scaled 2; filldraw unitsquare scaled 40 withcolor 1/3[blue,white] withmplibopacities (1, 1/2, 1); filldraw fullcircle scaled 40 withcolor 1/3[red,white] withmplibopacities (1, 1/2, 1); \endmpfig
%\stopmpfig{6}
%\begin{verbatim}
%    \mpfig
%      pickup pencircle scaled 2;
%      filldraw unitsquare scaled 40
%        withcolor 1/3[blue,white]
%        withmplibopacities (1, 1/2, 1)      % or ("normal", 1/2, 1)
%        ;
%      filldraw fullcircle scaled 40
%        withcolor 1/3[red,white]
%        withmplibopacities (1, 1/2, 1)
%        ;
%    \endmpfig
%\end{verbatim}
%
% \subsubsection{Text outline as a text image}
%     \label{sec:mplibgraphictext}
%   \bsx|mplibgraphictext| \meta{string}\esx\ is a \metapost operator,
%   the effect of which is similar to that of
%   \ConTeXt's |graphictext| or our own |mpliboutlinetext|
%   (see below \secref{mpliboutlinetext}).
%   However the syntax is somewhat different.
%\startmpfig
%\mpfig draw mplibgraphictext "$\sqrt{2+x}$" rotated 10 scaled 3 fakebold 2.5 fillcolor "red!50" drawcolor 2/3 red; \endmpfig
%\stopmpfig{3}
%\begin{verbatim}
%    draw mplibgraphictext "$\sqrt{2+x}$"
%      rotated 10 scaled 3
%      fakebold 2.5                           % fontspec option
%      fillcolor "red!50"                     % color expression
%      drawcolor 2/3 red                      % or strokecolor 2/3 red
%      ;
%\end{verbatim}
%   |fakebold|, |fillcolor| and |drawcolor| (or |strokecolor|) are optional;
%   default values are |2|, |"white"| and |"black"| respectively.\footnote{Users can
%     use the |withmplibcolors| macro instead of |fillcolor| and |drawcolor| options.
%     See \secref{withmplibcolors} on this macro.}
%   When the color expression is given in string type, it is regarded as
%   \pkg{color}, \pkg{xcolor} or \pkg{l3color}'s expression.
%   All from |mplibgraphictext| to the end of sentence will compose an
%   anonymous picture, which can be drawn or assigned to a variable.
%   Incidentally, |withfillcolor| and |withdrawcolor| are synonyms of
%   |fillcolor| and |drawcolor|, hopefully to be compatible with |graphictext|.
%
%   \notabene
%   In some cases,
%   especially when processing complicated \TeX\ code,
%   |mplibgraphictext| will produce better results than \ConTeXt\
%   or even than our own |mpliboutlinetext|, not to mention the much smaller PDF file size.
%   There are, however, some limitations such that you can't
%   apply shading (gradient colors) to the text with \emph{metafun}'s |withshademethod|.\relax
%   \footnote{But this limitation is now lifted by the introduction of |withshadingmethod|.
%     See below \secref{luamplibshading}.}
%   Again,
%   in DVI mode, \pkg{unicode-math} package is needed for math formulae,
%   as we cannot embolden type1 fonts in DVI mode.
%   But the most critical limitation is that, unlike |mpliboutlinetext|,
%   you cannot manipulate the shape of outline paths,
%   because the returned picture is basically a |btex| |...| |etex| picture.
%
%   \subsubsection{Glyph outline}
%   \metapost operator \bsx|mplibglyph|
%   \meta{number} \textbar\ \meta{string} |of| \meta{number} \textbar\ \meta{string}\esx\
%   returns a \metapost picture
%   containing outline paths of a glyph in OpenType, TrueType or Type1 (|.pfb|) fonts.
%   When a TFM font is specified, \metapost primitive |glyph| will be called.
%\begin{verbatim}
%    mplibglyph 50  of \fontid\font           % slot 50 of current font
%    mplibglyph "Q" of "TU/TeXGyrePagella(0)/m/n/10"      % font csname
%    mplibglyph "Q" of "texgyrepagella-regular.otf"       % raw filename
%    mplibglyph "R" of "utmr8a.pfb"                       % raw filename (type1 font)
%    mplibglyph "Q" of "Times.ttc(2)"                     % subfont number
%    mplibglyph "Q" of "SourceHanSansK-VF.otf[Regular]"   % instance name
%    mplibglyph "R" of "SourceHanSansK-VF.otf[wght=800]"  % axis names & values
%\end{verbatim}
%   Both arguments before and after `|of|' can be either a number or a string.
%   Number arguments are regarded as a glyph slot (GID) and a font id number, repectively.
%   String argument at the left side is regarded as a glyph name in the font or a unicode character.
%   String argument at the right side is regarded as a \TeX\ font csname (without backslash) or
%   the raw filename of a font. When it is a font filename, a number within parentheses
%   after the filename denotes a
%   subfont number (starting from zero) of a TTC font; a string within brackets denotes
%   an instance name, or names and values for |axis| feature, of a variable font.
%
%   \notabene Regrettably we have some bug in processing
%   not a few glyphs in |cmr10.pfb| and its family (or maybe other) Type1 fonts.\relax
%     \footnote{The bug seems to be fixed in |font-cff.lmt| contained in \ConTeXt mkxl, but
%     current \pkg{luaotfload} is based on |font-cff.lua| from \ConTeXt mkiv.
%     As you see, |mplibglyph| operator requires \pkg{luaotfload} package loaded,
%     which however is done automatically by \LaTeX\ format.}
%   If that happens, consider using |glyph| operator instead of |mplibglyph|.
%
%   \subsubsection{Drawing a glyph outline}
%     \label{sec:mplibdrawglyph}
%   As the structure of the picture returned by |mplibglyph| is quite similar to
%   the result of |glyph| primitive,
%   \metapost's |draw| command will fill the inner path of the picture with the background color.
%   In contrast, \bsx|mplibdrawglyph| \meta{picture}\esx\ command fills the paths
%   according to the nonzero winding number rule.
%   As a result, for instance, the area surrounded by inner path of ``O'' will remain transparent.
%
%   \notabene
%   To apply the nonzero winding number rule to a picture containing paths,
%   \pkg{luamplib} appends |withpostscript| |"collect"|
%   to the paths except the last one in the picture.
%   If you want the even-odd rule instead, you can
%   additionally declare |withpostscript| |"evenodd"| to the last path.
%
%   \notabene
%   By the way, when you want fill-and-stroke effect,
%   issueing |filldraw| command to the last path will not always produce what you want:
%   in such cases, you have to issue the command
%   |draw| \meta{the last path} |withpostscript| |"both"|
%   (or |"eoboth"| to apply even-odd rule).\footnote{\emph{metafun} provides macros
%     |nofill|, |eofill|, |fillup|, |eofillup| etc.\ (see \emph{metafun} manual \S\,2.11),
%     which \pkg{luamplib} with \emph{plain} format does not provide currently. }
%
%   As this could be somewhat annoying to users, \pkg{luamplib} v2.38.0 or later provides the following commands as well:
%   \bsx|mplibfillandstrokeglyph| \meta{picture}\esx, \bsx|mplibstrokeglyph| \meta{picture}\esx,
%   and \bsx|mplibfillglyph| \meta{picture}\esx, the last one being a synonym of |mplibdrawglyph| command.
%
%   An example:
%\startmpfig
%\mpfig mplibfillandstrokeglyph mplibglyph "R" of \fontid\font scaled 1/12 withpen pencircle scaled 1 withmplibcolors ("orange", 2/3red); \endmpfig
%\stopmpfig{3}
%\begin{verbatim}
%    mplibfillandstrokeglyph
%      mplibglyph "R" of \fontid\font scaled 1/12
%      withpen pencircle scaled 1
%      withmplibcolors ("orange", 2/3red) ;
%\end{verbatim}
%
%
% \subsubsection{Text outline as a path image}
%   \label{sec:mpliboutlinetext}
%   As said before at \secref{mplibsetformat}, \pkg{luamplib} provides
%   the \metapost operator \bsx|mpliboutlinetext| |(|\meta{string}|)|\esx\ which mimicks
%   \emph{metafun}'s |outlinetext|,
%   but with some enhancements including the support for right-to-left writing direction.
%   The syntax is the same as that of \emph{metafun}: see the \emph{metafun}
%   documentation \S\,8.7 (|texdoc| |metafun|).
%
%   A simple example:
%\startmpfig
%\mpfig draw mpliboutlinetext.b ("$\sqrt{2+\alpha}$") (withcolor \mpcolor{red!50}) (withpen pencircle scaled .25 withcolor 2/3red) scaled 3; \endmpfig
%\stopmpfig{2}
%\begin{verbatim}
%    draw mpliboutlinetext.b ("$\sqrt{2+\alpha}$")
%      (withcolor \mpcolor{red!50})
%      (withpen pencircle scaled .25 withcolor 2/3red)
%      scaled 3 ;
%\end{verbatim}
%   After the process, |mpliboutlinepic[]|
%   and |mpliboutlinenum| will be preserved as global variables;
%   |mpliboutlinepic[1]| |...| |mpliboutlinepic[mpliboutlinenum]|
%   will be an array of images, each of which containing outline paths of a glyph or a rule.
%
%   \notabene As Unicode grapheme cluster is not considered in the array, a unit that must be
%   a single cluster might be separated apart.
%
% \subsubsection{Unicode-aware length}
%   \bsx|mpliblength| \meta{string}\esx\ returns the number of unicode characters in the string.
%   This is a unicode-aware version equivalent to the \metapost primitive |length|, but
%   accepts only a string-type argument.
%   For instance, |mpliblength| |"abçdéf"| returns |6|, not |8|.
%
%   On the other hand,
%   \bsx|mplibuclength| \meta{string}\esx\ returns the number of unicode grapheme clusters in the string.
%   For instance, |mplibuclength| |"Äpfel"|,
%   where |Ä| is encoded using two codepoints (U+0041 and U+0308),
%   returns |5|, not |6| or |7|.
%   This operator requires \pkg{lua-uni-algos} package installed.
%
% \subsubsection{Unicode-aware substring}
%   \bsx|mplibsubstring| \meta{pair} |of| \meta{string}\esx\ is a unicode-aware version equivalent to the
%   \metapost's |substring ... of ...| primitive. The syntax is the same as the latter,
%   but the string is indexed by unicode characters.
%   For instance, |mplibsubstring| |(2,5)| |of| |"abçdéf"| returns |"çdé"|,
%   and |mplibsubstring| |(5,2)| |of| |"abçdéf"| returns |"édç"|.
%
%   On the other hand,
%   \bsx|mplibucsubstring| \meta{pair} |of| \meta{string}\esx\ returns the part of the string indexed
%   by unicode grapheme clusters.
%   For instance, |mplibucsubstring| |(0,1)| |of| |"Äpfel"|,
%   where |Ä| is encoded using two codepoints (U+0041 and U+0308),
%   returns |"Ä"|, not |"A"|.
%   This operator requires \pkg{lua-uni-algos} package installed.
%
% \subsubsection{Shading}
%   \label{sec:luamplibshading}
%   The syntax is exactly the same as \emph{metafun}'s new shading method (|texdoc metafun| \S\,8.3.3), except that
%   the `|shade|' contained in each and every macro name has changed to
%   `|shading|' in \pkg{luamplib}: for instance, while |withshademethod| is
%   a macro name which only works with \emph{metafun} format,
%   the equivalent provided by \pkg{luamplib}, |withshadingmethod|, works with \emph{plain} as well.
%   Other differences to the \emph{metafun}'s and some cautions are:
%   \begin{itemize}
%   \item \emph{Textual pictures} as well as paths can have shading effect.
%     The term \emph{textual picture} here means
%     a picture generated by |btex| |...| |etex|, |textext|, |TEX|, |maketext|,
%     |mplibgraphictext| (see below \secref{mplibgraphictext}), or |infont| operator,
%     though technically only the last one is a true textual picture.
%     Note that the picture, including transparency group,
%     in which the objects are filled \emph{without} color
%     can also be regarded as a textual picture.\footnote{\relax
%       See below \secref{mplibdrawglyph},
%       particularly the first \hyperlink{firstpatternexample}{example} of tiling pattern
%       at \secref{mppattern}.
%       See also \secref{transparencygroup} and \secref{mplibgroupendmplibgroup},
%       particularly the example in the \hyperlink{para:pictureshading}{note}
%       about picture shading and transparency group.}
%\startmpfig
%\mpfig draw btex \bfseries\TeX etex rotated 15 scaled 6 withshadingmethod "linear" withshadingvector (0,3) withshadingstep ( withshadingfraction 1/2 withshadingcolors (red,green)) withshadingstep ( withshadingfraction 1 withshadingcolors (green,blue)); \endmpfig
%\stopmpfig{6}
%\begin{verbatim}
%    draw btex \bfseries\TeX etex rotated 15 scaled 6
%      withshadingmethod "linear"
%      withshadingvector (0,3)
%      withshadingstep (
%        withshadingfraction 1/2
%        withshadingcolors (red,green)
%      )
%      withshadingstep (
%        withshadingfraction 1
%        withshadingcolors (green,blue)
%      ) ;
%\end{verbatim}
%   \item When shading a picture generated by `|infont|' operator or that has multiple components,
%     the effect of |withshadingvector| and that of |withshadingdirection| will be the same,
%     as \pkg{luamplib} considers only the bounding box of the picture.
%   \item A few more optional macros are available in addition to the \emph{metafun}'s:
%    |withshadingpoints|, |withshadingcenters|, |withshadingextend|, |withshadingmatrix|,
%    and |withshadingstroke|.
%   \item More shading methods are available: |"triangle"|, |"lattice"|, |"coons"|, and |"tensor"|.
%    See below \secref{trianglemesh}, \secref{latticemesh}, \secref{coonspatchmesh} and
%    \secref{tensorpatchmesh} for these methods.
%   \end{itemize}
% The syntax is \bsx\meta{path} \textbar\ \meta{textual picture} |withshadingmethod| \meta{string}\esx,
% where the latter shall be |"linear"|, |"circular"|, |"triangle"|, |"lattice"|, |"coons"|, or |"tensor"|.
% The balance of this subsection is to explain additional optional macros.
%
% Above all, there are two ways in specifying the shading coordinates, of which
% you can choose the more convenient one.
% First, the way that mimicks the \emph{metafun}'s:
% \begin{description}
%   \item[withshadingvector \meta{pair}]
%     Starting and ending points (as time value) on the path.
%   \item[withshadingdirection \meta{pair}]
%     Starting and ending points (as time value) on the bounding box,
%     default value being |(0,2)|.
%   \item[withshadingorigin \meta{pair}]
%     The center of both starting and ending circles,
%     default value being |center p|, where |p| is the operand of |withshadingmethod|.
%   \item[withshadingcenter \meta{pair}]
%     Value to specify the starting center.
%     For instance,
%     |(0,0)| means that the center of starting circle is |center p|;
%     |(1,1)| means |urcorner p|; |(-1,-1)| means |llcorner p|.
%   \item[withshadingradius \meta{pair}]
%     Radii of starting and ending circles. This is no-op in linear mode.
%     Default value: |(0,| |abs(center p -| |urcorner p))|
%   \item[withshadingfactor \meta{numeric}]
%     Multiplier of the radii, default value being |1.2|.
%     This is no-op in linear mode.
%   \item[withshadingtransform \meta{string}]
%     where \meta{string} shall be |"yes"| (respect transform) or |"no"| (ignore transform).
%     Default value: |"no"| for pictures made by |infont| operator or having multiple components;
%     |"yes"| for all other cases.
% \end{description}
% Secondly, the way provided by \pkg{luamplib} only:
% \begin{description}
%   \item[withshadingpoints (\meta{pair}, \meta{pair})]
%     In linear mode, values to specify directly the starting and ending points:
%     you can use it instead of |withshadingvector| or |withshadingdirection|.
%     In circular mode, the centers of starting and ending circles:
%     it could be easier than issueing two macros
%     |withshadingorigin| and |withshadingcenter|.
%     Note that, within the macro, both \bsx|withshadingfactor| |1|\esx\ and
%     \bsx|withshadingtransform| |"no"|\esx\ are already decalred.
%   \item[withshadingcenters (\meta{pair}, \meta{pair})]
%     Synonym of |withshadingpoints|.
%     Normally accompanied by |withshadingradius| which has the same meaning as described above.
% \end{description}
% Now, optional macros common to the both ways:
% \begin{description}
%   \item[withshadingstep (|...|)]
%     For combined shading of more than two colors.
%   \item[withshadingfraction \meta{numeric}]
%     Fractional number of each shading step,
%     and so only meaningful within |withshadingstep|.
%   \item[withshadingcolors (\meta{color expr}, \meta{color expr})]
%     Starting and ending colors,
%     default value being |(white,| |black)|.
%     String-type argument is regarded as the color expression of \TeX\ side.
%   \item[withshadingdomain \meta{pair}]
%     Limiting values of parametric variable
%     that varies on the axis of color gradient,
%     default value being |(0,1)|. Of course the values can be negative or greater than $1$.
%   \item[withshadingextend (\meta{boolean}, \meta{boolean})]
%     Values specifying whether to extend the shading
%     beyond the starting and ending points or circles, default value being |(true,| |true)|.
%     An example just to show the concept:
%\startmpfig
%\mpfig path p[]; p1 = fullcircle scaled 50; p2 = fullcircle scaled 50 shifted 40right; fill (subpath (2,6) of p1 -- subpath (-2,2) of p2 -- cycle) rotated 10 withshadingmethod "circular" withshadingcenters (center p1, center p2 rotated 10) withshadingradius (25,25) withshadingcolors (3/4[red,white], red) withshadingextend (false, false) ; \endmpfig
%\stopmpfig{8}
%\begin{verbatim}
%    \mpfig
%      path p[];
%      p1 = fullcircle scaled 50;
%      p2 = fullcircle scaled 50 shifted 40 right;
%      fill (subpath (2,6) of p1 -- subpath (-2,2) of p2 -- cycle) rotated 10
%        withshadingmethod  "circular"
%        withshadingcenters (center p1, center p2 rotated 10)
%        withshadingradius  (25, 25)
%        withshadingcolors  (3/4[red,white], red)
%        withshadingextend  (false, false) ;
%    \endmpfig
%\end{verbatim}
%   \item[withshadingmatrix \meta{string}]
%     \metapost code for transformation of shading, such as |"xscaled 1.2| |yscaled 0.8"|;
%     or six numerics separated by spaces, such as |"1.2 0 0 0.8 0 0"|.
%   \item[withshadingstroke \meta{string}]
%     where \meta{string} shall be |"yes"| or |"no"|.
%     Only meaningful when the shading object is a \meta{path};
%     if |"yes"|, we get the path stroked and \emph{then} shaded.
%     It is more efficient than issueing two sentences.
% \end{description}
%
% \subsubsection{Fading}
%   \metapost command |withfademethod| makes the color of an object gradiently transparent,
%   a.k.a.\ \emph{fading}.
%   The syntax is \bsx\meta{path} \textbar\ \meta{picture} |withfademethod| \meta{string}\esx,
%   the latter being either |"linear"| or |"circular"|.
%   Though it is similar to the |withshademethod| from \emph{metafun},
%   the differences are: (1)~the object of fading can be a picture as well as a path;
%   (2)~you cannot make gradient colors, but can only make gradient opacity.
%   Technically speaking, this command generates and applies a special kind of
%   masking transparency group described below at \secref{withmaskinggroup}.
%
%   Related macros to control optional values are:
%   \begin{description}
%   \item [withfadeopacity (\meta{numeric}, \meta{numeric})]
%     sets the starting opacity and the ending opacity, default value being |(1,0)|.
%     `|1|' denotes full color; `|0|' full transparency.
%   \item [withfadevector (\meta{pair}, \meta{pair})]
%     sets the starting and ending points. Default value in the linear mode is
%     |(llcorner p,| |lrcorner p)|, where |p| is the operand,
%     meaning that fading starts from the left edge and ends at the right edge.
%     Default value in the circular mode is |(center p,| |center p)|, which means
%     centers of both starting and ending circles are the center of the bounding box.
%   \item [withfadecenter] is a synonym of |withfadevector|.
%   \item [withfaderadius (\meta{numeric}, \meta{numeric})]
%     sets the radii of starting and ending circles. This is no-op in the linear mode.
%     Default value is |(0, abs(center p - urcorner p))|, meaning that fading starts from the
%     center and ends at the four corners of the bounding box.
%   \item [withfadestep (|...|)]
%     for combined fading of more than two opacities.
%   \item [withfadefraction \meta{numeric}]
%     Fractional number of each fading step. Only meaningful within |withfadestep|.
%   \item [withfadeextend (\meta{boolean}, \meta{boolean})]
%     specifies whether to extend the fading
%     beyond the starting and ending points or circles, default value being |(true,| |true)|.
%   \item [withfadematrix \meta{string}]
%     \metapost code for transformation of fading, such as |"xscaled 1.2| |yscaled 0.8"|;
%     or six numerics separated by spaces, such as |"1.2 0 0 0.8 0 0"|.
%   \item [withfadebbox (\meta{pair}, \meta{pair})]
%     sets the bounding box of the fading area, default value being |(llcorner p,| |urcorner p)|.
%     Though this option is not needed in most cases, there could be cases when users want to
%     explicitly control the bounding box.
%     Particularly, see the description \hyperlink{withgroupbbox}{below}
%     at \secref{transparencygroup} on the analogous macro |withgroupbbox|.
%   \end{description}
%   An example:
%\startmpfig
%\mpfig draw btex \includegraphics[width=100bp]{mill} etex withfademethod  "circular" withfaderadius (20, 50) withfadeopacity (1, 0); \endmpfig
%\stopmpfig{3}
%\begin{verbatim}
%    draw
%      btex \includegraphics[width=100bp]{mill} etex
%      withfademethod  "circular"
%      withfaderadius  (20, 50)
%      withfadeopacity (1, 0) ;
%\end{verbatim}
%
% \subsubsection{Tiling pattern}
%   \label{sec:mppattern}
%   \TeX\ macros
%   \bsx\cs{mppattern}\marg{name}\esx\ |...| \bsx\cs{endmppattern}\esx\ define a tiling pattern cell
%   associated with the \meta{name}.
%   \metapost command |withmppattern|, the syntax being
%   \bsx\meta{cyclic path} \textbar\ \meta{textual picture} |withmppattern| \meta{string}\esx, will fill
%   the given path or text with the tiling pattern cell of the \meta{name}
%   by replicating it horizontally and vertically.\footnote{\relax
%     |withpattern| is an operator virtually the same as |withmppattern|,
%     but the former forces a \metapost picture. Therefore
%     you cannot but use |draw| command with |withpattern| operator.
%     On the other hand,
%     \meta{cyclic path} |withmppattern| \meta{string} works as intended
%     only with |fill| or |filldraw| command.
%   }
%   As said before at \secref{luamplibshading},
%   the \emph{textual picture} here means basically any text typeset by \TeX, mostly the result
%   of the |btex| command (and its derivatives) or the |infont| operator.
%
%   An \hypertarget{firstpatternexample}{example}:
%\begin{verbatim}
%    \mppattern{mypatt}               % or \begin{mppattern}{mypatt}
%      [                              % options: see below
%        xstep = 10,
%        ystep = 7,
%        matrix = "rotated 45",       % or "0.7 0.7 -0.7 0.7" or {0.7, 0.7, -0.7, 0.7}
%      ]
%      \mpfig                         % or any other TeX code
%        draw (up--down) scaled 5
%          withcolor 2/3[blue,white] ;
%        draw (left--right) scaled 5
%          withcolor 2/3[red,white] ;
%      \endmpfig
%    \endmppattern                    % or \end{mppattern}
%\end{verbatim}
%\startmpfig
%\mppattern{mypatt}[xstep=10, ystep=7, matrix="rotated 45",]\mpfig draw (up--down) scaled 5 withcolor 2/3[blue,white]; draw (left--right) scaled 5 withcolor 2/3[red,white];\endmpfig\endmppattern
%\mpfig mplibdrawglyph image(draw fullcircle scaled 100; draw reverse fullcircle scaled 40;) withmppattern "mypatt" withpen pencircle scaled 1 withcolor \mpcolor{red!50!blue!50}; \endmpfig
%\stopmpfig{1}
%\begin{verbatim}
%    \mpfig
%      mplibdrawglyph image(
%          draw fullcircle scaled 100;
%          draw reverse fullcircle scaled 40;
%        )
%        withmppattern "mypatt"
%        withpen pencircle scaled 1
%        withcolor \mpcolor{red!50!blue!50} ;
%    \endmpfig
%\end{verbatim}
%
%   The available options, actually elements of a Lua \emph{table},
%   are listed in Table~\ref{tab:mppatternoptions}.
%   For the sake of convenience, the width and height values of the tiling pattern cell will be written down
%   into the log file (depth is always zero). Users can refer to them for option setting.
%
%   \begin{table}
%   \centering
%   \caption{options for \cs{mppattern}}\label{tab:mppatternoptions}
%   \begin{tabular}{lll}\hline
%     Key   & Value Type & Explanation\\\hline
%     |xstep|    &\emph{number} & horizontal spacing between pattern cells\\
%     |ystep|    &\emph{number} & vertical spacing between pattern cells\\
%     |xshift|   &\emph{number} & horizontal shifting of pattern cells\\
%     |yshift|   &\emph{number} & vertical shifting of pattern cells\\
%     |bbox|     &\emph{table} or \emph{string} & |llx|, |lly|, |urx|, |ury| values\kern1pt*\\
%     |matrix|   &\emph{table} or \emph{string} & |xx|, |yx|, |xy|, |yy| values\kern1pt* or MP transform code\\
%     |resources|&\emph{string} & PDF resources if needed\\
%     |colored| or |coloured| &\emph{boolean}& |false| for uncolored pattern. default: |true|\\\hline
%                &                & \small *\,in string type, numbers are separated by spaces\\
%   \end{tabular}
%   \end{table}
%
%   As for |matrix| option, \metapost code such as |"rotated 30 slanted .2"| is allowed as well
%   as the string or table of four numbers. You can also set |xshift| and |yshift|
%   values by using `|shifted|' operator. But when |xshift| or |yshift| option is explicitly
%   given, they have precedence over the effect of `|shifted|' operator.
%
%   When you use special effect such as transparency in a pattern cell,
%   |resources| option is needed: for instance, |resources="/ExtGState| |<</MyObj| |5 0 R>>"|.
%   However, as \pkg{luamplib} automatically includes the resources of the current page, this option
%   is not needed in most cases.
%
%   Option \bsx|colored=false|\esx\ (or |coloured=false|) will generate an uncolored pattern cell which
%   shall have no color at all (i.e.\ |withoutcolor| command is needed for \metapost code).\relax
%   \footnote{When using DVI mode, |-c| option might be needed to the |dvipdfmx| command.}
%   Uncolored pattern will be painted later by the color of a \metapost object.
%   An example:
%\startmpfig
%\begin{mppattern}{pattnocolor}[colored=false, matrix="slanted .3 rotated 15",]\tiny\TeX\end{mppattern}
%\mpfig picture tex; tex = mpliboutlinetext("\bfseries \TeX"); for i=1 upto mpliboutlinenum: mplibfillandstrokeglyph mpliboutlinepic[i] scaled 8 withmppattern "pattnocolor" withpen pencircle scaled 1/2 withcolor (i/4)[red,blue]; endfor \endmpfig
%\stopmpfig{16}
%\begin{verbatim}
%    \begin{mppattern}{pattnocolor}
%      [
%        colored = false,
%        matrix = "slanted .3 rotated 15",
%      ]
%      \tiny\TeX
%    \end{mppattern}
%
%    \begin{mplibcode}
%      beginfig(1)
%        picture tex;
%        tex = mpliboutlinetext ("\bfseries \TeX");
%        for i=1 upto mpliboutlinenum:
%          mplibfillandstrokeglyph mpliboutlinepic[i]
%            scaled 8
%            withmppattern "pattnocolor"
%            withpen pencircle scaled 1/2
%            withcolor (i/4)[red,blue]      % paints the pattern
%            ;
%        endfor
%      endfig;
%    \end{mplibcode}
%\end{verbatim}
% A much simpler and efficient way to obtain a similar result
% (but without colorful characters in this example)
% is to give a \emph{textual picture} as the operand of |withmppattern|:
%\startmpfig
%\mpfig draw mplibgraphictext "\bfseries\TeX" fakebold 1/2 rotated 10 scaled 8 withmppattern "pattnocolor" withmplibcolors(2/3[red,white], 2/3red); \endmpfig
%\stopmpfig{7}
%\begin{verbatim}
%    \begin{mplibcode}
%      beginfig(2)
%        draw mplibgraphictext "\bfseries\TeX"
%          fakebold 1/2
%          rotated 10 scaled 8
%          withmppattern "pattnocolor"
%          withmplibcolors (
%            2/3[red,white],                % paints the pattern
%            2/3 red
%          ) ;
%      endfig;
%    \end{mplibcode}
%\end{verbatim}
%
% \subsubsection{Form XObject from \metapost side}\label{sec:transparencygroup}
% As said \hyperlink{metafunformat}{before} at \secref{mplibsetformat},
% transparency group is available with \emph{plain} as well as \emph{metafun}.
% It is called \emph{Transparency Group} because the objects contained in the group are composited
% to produce a single object, so that outer transparency effect, if any,
% will be applied to the group as a whole, not to the individual objects cumulatively.
%
% The syntax is basically the same as \emph{metafun}'s:
% \bsx\meta{picture} \textbar\ \meta{path} |asgroup| \meta{string}\esx, the latter being
% |""| (empty string)
% or any comma-separated combination of |isolated|, |knockout|, |wrapped| and |off|
% (for example, |"isolated,knockout,wrapped"|),
% which will return a \metapost picture.
% The additional features provided by \pkg{luamplib} are:
% \begin{itemize}
% \item As mentioned, in addition to those arguments mimicking \emph{metafun}'s,
%   we allow other optional arguments at the right-hand side:
%   \begin{itemize}
%   \item |asgroup| |"off"| will produce an ordinary \emph{form XObject}
%     rather than a transparency group XObject.
%     By contranst, a transparency group will be produced
%     when `|off|' is not given, including |""| (empty string)
%     in which case both of the PDF keys |/I| and |/K| are |false|.
%   \item |asgroup| |"wrapped"| will produce a transparency group XObject
%     in which an ordinary form XObject is wrapped up.
%     This option could be useful when a picture shading (\emph{shading pattern} in technical terms)
%     is used in the object of |asgroup|.
%     See the note about picture shading described \hyperlink{para:pictureshading}{below}.
%   \end{itemize}
% \item You can reuse the XObject as many times as you want
%   in the \TeX{} code or in other \metapost code chunks,
%   with infinitesimal increase in the size of PDF file.
%   For this functionality we provide \TeX{} and \metapost macros as follows:
% \end{itemize}
% \begin{description}
% \item[withgroupname \meta{string}] associates an XObject with the given name.
%   When this command is not appended to the sentence with |asgroup| operator,
%   the default name `|lastmplibgroup|' will be used.
%
% \item[\textbackslash usemplibgroup\marg{name}]
%   \hypertarget{usemplibgroup}{}\relax
%   is a \TeX\ command to reuse an XObject of the name
%   once used.
%   Note that the position of the XObject will be origin-based:
%   in other words, lower-left corner of the bounding box will be shifted to the origin.
%
% \item[usemplibgroup \meta{string}] is a \metapost command which will add
%   an XObject of the name to the |currentpicture|.
%   Contrary to the \TeX\ command just mentioned,
%   the position of the XObject is the same as the original XObject.
%
% \item[withgroupbbox (\meta{pair}, \meta{pair})]
%   \hypertarget{withgroupbbox}{}\relax
%   sets the bounding box of the XObject,
%   default value being |(llcorner p, urcorner p)|.
%   This option might be needed especially when you draw with a thick pen
%   a path that touches the boundary;
%   you would probably want to append to the sentence
%   `|withgroupbbox| |(bot| |lft| |llcorner| |p,| |top| |rt| |urcorner| |p)|',
%   supposing that the pen was selected by the |pickup| command.
%
% \end{description}
% An example showing the effect of transparency group and
% the difference between the \TeX\ and \metapost commands:
%\startmpfig
%   \hypertarget{noasgroupexample}{}\relax
%\mpfig picture pic; pic = image(drawarrow (left--right) scaled 5 withcolor red) scaled 10; draw pic asgroup "off" withtransparency (1, 1/2);\endmpfig
%\stopmpfig{5}
%\begin{verbatim}
%    \mpfig
%      picture pic;
%      pic = image(drawarrow (left--right) scaled 5 withcolor red) scaled 10 ;
%      draw pic
%        asgroup "off"
%        withtransparency (1, 1/2) ;
%    \endmpfig
%\end{verbatim}
%\startmpfig
%\mpfig draw pic asgroup "" withgroupname "mygroup" withtransparency (1, 1/2); draw (left--right) scaled 5; draw (up--down) scaled 5;\endmpfig
%\stopmpfig{5}
%\begin{verbatim}
%    \mpfig
%      draw pic
%        asgroup ""
%        withgroupname "mygroup"
%        withtransparency (1, 1/2) ;
%      draw (left--right) scaled 5 ;
%      draw (up--down) scaled 5 ;
%    \endmpfig
%\end{verbatim}
%\startmpfig
%\noindent\clap{\vrule width 10bp height .25bp depth .25bp}\clap{\vrule width .5bp height 5bp depth 5bp}\usemplibgroup{mygroup}
%\stopmpfig{3}
%\begin{verbatim}
%    \noindent
%    \clap{\vrule width 10bp height .25bp depth .25bp}%
%    \clap{\vrule width .5bp height 5bp depth 5bp}%
%    \usemplibgroup{mygroup}
%\end{verbatim}
%\startmpfig
%\mpfig usemplibgroup "mygroup" withtransparency (1,2/3); draw (left--right) scaled 5; draw (up--down) scaled 5;\endmpfig
%\stopmpfig{4}
%\begin{verbatim}
%    \mpfig
%      usemplibgroup "mygroup"
%        withtransparency (1, 2/3) ;
%      draw (left--right) scaled 5 ;
%      draw (up--down) scaled 5 ;
%    \endmpfig
%\end{verbatim}
%
% Also note that normally the XObjects are not affected by outer color commands.
% However, if you have made the original XObject using |withoutcolor| command,
% colors will have effects on its uncolored objects.
%
% \paragraph{Note on picture shading}
% \hypertarget{para:pictureshading}{When}
% you give shading effect upon a \emph{textual picture} (i.e.\ non-path object)
% inside or outside a transparency group, currently many of the PDF renderers,
% including Mac OS Preview and Foxit Editor, do not interprete PDF coordinates properly.
% If that happens, consider using other PDF viewer such as Adobe Acrobat or MuPDF. An example:
% \mpfig* picture pic[]; pic1 = image(fill fullcircle scaled 60 withoutcolor; fill fullcircle scaled 60 shifted 25right withoutcolor;); pic2 = image(draw pic1 withshadingmethod "linear" withshadingvector (2, 1) withshadingcolors (red, blue)); \endmpfig
%\begin{verbatim}
%    \mpfig*
%      picture pic[];
%      pic1 = image(
%          fill fullcircle scaled 60 withoutcolor;
%          fill fullcircle scaled 60 shifted 25right withoutcolor;
%        ) ;
%      pic2 = image(
%        draw pic1
%          withshadingmethod "linear"
%          withshadingvector (2, 1)
%          withshadingcolors (red, blue)
%      ) ;
%    \endmpfig
%\end{verbatim}
% \startmpfig
% \mpfig draw pic2 asgroup "" withtransparency (1, 2/3); \endmpfig\vskip15pt
% \stopmpfig{3}
%\begin{verbatim}
%    \mpfig                               % shading inside group
%      draw pic2
%        asgroup ""
%        withtransparency (1, 2/3) ;
%    \endmpfig
%\end{verbatim}
% \startmpfig
% \mpfig draw pic1 asgroup "" withshadingmethod "linear" withshadingvector (2, 1) withshadingcolors (red, blue) withtransparency (1, 2/3); \endmpfig
% \stopmpfig{4}
%\begin{verbatim}
%    \mpfig                               % shading outside group
%      draw pic1
%        asgroup ""
%        withshadingmethod "linear"
%        withshadingvector (2, 1)
%        withshadingcolors (red, blue)
%        withtransparency (1, 2/3) ;
%    \endmpfig
%\end{verbatim}
% After some experiment, however, it turned out that
% the best way to get picture shading with transparency group
% is to give shading effect within an ordinary form XObject
% and then wrap it up in a transparency group XObject.
% This sort of double wrapping will be done automatically when you specify `|wrapped|'
% as an optional argument of |asgroup|.
% Most PDF renderers do render it properly:
%\startmpfig
%\mpfig draw pic2 asgroup "wrapped" withtransparency (1, 2/3); \endmpfig
%\stopmpfig{3}
%\begin{verbatim}
%    \mpfig
%      draw pic2
%        asgroup "wrapped"
%        withtransparency (1, 2/3) ;
%    \endmpfig
%\end{verbatim}
% or preferably (see next subsection \secref{mplibgroupendmplibgroup} on \cs{mplibgroup}):
%\startmpfig
%\mplibgroup{myshading}[asgroup="wrapped"]
%\mpfig draw pic2; \endmpfig
%\endmplibgroup
%\mpfig usemplibgroup "myshading" rotated 15 withtransparency (1, 2/3); \endmpfig
%\stopmpfig{7}
%   \hypertarget{doublewrappingexample}{}\relax
%\begin{verbatim}
%    \mplibgroup{myshading}[asgroup="wrapped"]
%      \mpfig
%        draw pic2 ;
%      \endmpfig
%    \endmplibgroup
%
%    \mpfig
%      usemplibgroup "myshading" rotated 15
%        withtransparency (1, 2/3) ;
%    \endmpfig
%\end{verbatim}
%
% \subsubsection{Form XObject from \TeX\ side}
%   \label{sec:mplibgroupendmplibgroup}
% \TeX\ macros \bsx\cs{mplibgroup}|{|\meta{name}|}|\esx\ |...| \bsx\cs{endmplibgroup}\esx\ are
% described here in this subsection, as they are
% deeply related to the |asgroup| operator described just above at \secref{transparencygroup}.
% With these, users can define a transparency group or an ordinary \emph{form XObject}
% from \TeX\ side.
% The syntax is similar to the \cs{mppattern} command described above at \secref{mppattern}.
%
% An example:\footnote{Note that, here again by the option |asgroup="wrapped"|,
%   within a transparency group XObject
%   a tiling pattern is wrapped up in an inner, ordinary XObject.
%   This annoyance is especially for Mac OS Preview which misapplies
%   the transformation matrix to tiling or shading patterns
%   directly wrapped in a transparency group.
%   Most other PDF renderers seem to behave properly even with single wrapping. }
%\startmpfig
%\mplibgroup{texpatt}[asgroup="wrapped",]\mpfig filldraw fullcircle xscaled 100 yscaled 50 withmppattern "pattnocolor" withcolor 2/3red;\endmpfig \endmplibgroup
%\usemplibgroup{texpatt}
%\stopmpfig{11}
%\begin{verbatim}
%    \mplibgroup{texpatt}                  % or \begin{mplibgroup}{texpatt}
%      [                                   % options: see below
%        asgroup="wrapped",
%      ]
%      \mpfig                              % or any other TeX code
%        filldraw fullcircle
%          xscaled 100 yscaled 50
%          withmppattern "pattnocolor"
%          withcolor 2/3 red ;
%      \endmpfig
%    \endmplibgroup                        % or \end{mplibgroup}
%
%    \usemplibgroup{texpatt}
%\end{verbatim}
%\startmpfig
%\mpfig usemplibgroup "texpatt" rotated -15 scaled 1.2 withtransparency(1,2/3); \endmpfig
%\stopmpfig{3}
%\begin{verbatim}
%    \mpfig
%      usemplibgroup "texpatt"
%        rotated -15 scaled 1.2
%        withtransparency (1, 2/3) ;
%    \endmpfig
%\end{verbatim}
%
% Availabe options
% are listed in Table~\ref{tab:mplibgroupoptions}.
% Again, the width/height/depth values of the mplibgroup will be written down into the log file.
%
% \begin{table}
% \centering
% \caption{options for \cs{mplibgroup}}\label{tab:mplibgroupoptions}
% \begin{tabular}{lll}
% \hline
%   Key         & Value Type    & Explanation\\
% \hline
%   |asgroup|   &\emph{string} & \parbox{236bp}{\strut
%                              |""|, or any comma-separated combination of |isolated|,\\[-1pt]
%                              |knockout|, |wrapped| and |off|, or |"masking"|\strut}\\
%  |colorspace| &\emph{string} & \parbox{250bp}{\strut
%                              |/CS| entry to a transparency group, such as |"/DeviceGray"|,\\[-1pt]
%                              |"/DeviceRGB"| or |"/DeviceCMYK"|\strut}\\
%   |bbox|      &\emph{table} or \emph{string} & |llx|, |lly|, |urx|, |ury| values\kern1pt*\\
%   |matrix|    &\emph{table} or \emph{string} & |xx|, |yx|, |xy|, |yy| values\kern1pt*
%                                                or MP transform code\\
%   |resources| &\emph{string}  & PDF resources if needed\\
% \hline
%               &               & \small *\,in string type, numbers are separated by spaces\\
% \end{tabular}
% \end{table}
%
% When |asgroup| option is not given or is given as |"off"|,
% an ordinary form XObject will be generated rather than a transparency group.
% Thus the individual objects, not the XObject as a whole, will be affected
% by outer transparency command, just like the first figure
% in the \hyperlink{noasgroupexample}{example} above at \secref{transparencygroup}.
%
% Option |asgroup="wrapped"| can be regarded as a shortcut of double \cs{mplibgroup} command.
% The content will be wrapped up in an ordinary form XObject
% before being wrapped again in a transparency group.
% This option could be useful when a tiling pattern
% (see the example just above)
% or a picture shading
% (see the \hyperlink{doublewrappingexample}{example} above at \secref{transparencygroup})
% is used in the content.
%
% As for the option |asgroup="masking"|, see the next subsection \secref{withmaskinggroup}.
%
% With |colorspace| option,
% which is no-op for ordinary form XObject,
% users can specify the color space of a transparency group.
% For instance, the mplibgroup will be painted in grayscale model
% when |colorspace=|\allowbreak|"/DeviceGray"| is given to an \emph{isolated} transparency group.\relax
% \footnote{Note that some PDF renderers such as Mac OS Preview or Firefox
%   do not render properly this option.}
%
% As shown, you can reuse the mplibgroup
% using the \TeX\ command \cs{usemplibgroup} or
% the \metapost command |usemplibgroup|.
% The behavior of these commands is the same as that described \hyperlink{usemplibgroup}{above}
% at \secref{transparencygroup},
% excepting that the mplibgroup made by \TeX\ code (not by \metapost code) respects original height and depth.
%
%
% \subsubsection{Masking}
%   \label{sec:withmaskinggroup}
% Using the command |withmaskinggroup|,
% the mplibgroup (see above \secref{mplibgroupendmplibgroup})
% generated by the option \bsx|asgroup=|\allowbreak|"masking"|\esx\ (see Table~\ref{tab:mplibgroupoptions})
% can be utilized as a masking transparency group upon a picture or a path object.
% The syntax is \bsx\meta{picture} \textbar\ \meta{path} |withmaskinggroup| \meta{string}\esx,
% the latter being the name of a pre-defined masking group.
%
% Basically, the masking group should be prepared in \emph{grayscale} color model:
% the area painted with |1| ($\approx$ white: full luminosity) will preserve
% the full color of the object;
% the area painted with |0| ($\approx$ black: zero luminosity) will force
% full transparency, masking it invisibly.\relax
%   \footnote{In fact, colors in other color models are also allowed
%     (such as |white|, |black|, |red|, |green|, |blue|).
%     But they will be converted to grayscale model by the PDF renderer, so that
%     |"/DeviceGray"| is the default value of |colorspace| option to a masking transparency group
%     (see Table~\ref{tab:mplibgroupoptions} at \secref{mplibgroupendmplibgroup}). }
%
% By default, the background color of a masking group is |0| ($\approx$ black),
% which you can change by this macro:
% \begin{description}
% \item[withmaskingbgcolor \meta{color expr}] sets the background color of the masking group.
%   |0| denotes full transparency (masking invisibly); |1|, full color.\relax
%   \footnote{Color expressions in rgb or cmyk model are also allowed,
%     in accordance with the option |colorspace=|\allowbreak|"/DeviceRGB"|
%     or |colorspace=|\allowbreak|"/DeviceCMYK"| given to the masking transparency group.
%     This however we do not recommend as the luminosity is difficult to understand intuitively. }
% \end{description}
% An example:
%\begin{verbatim}
%    \mpfig*
%      picture pic;
%      pic = image(
%          fill fullcircle scaled 80 withcolor blue ;
%          fill fullcircle scaled 80 shifted (25,0) withcolor green ;
%          fill fullcircle scaled 80 shifted (50,0) withcolor red ;
%        );
%    \endmpfig
%
%    \mplibgroup{mymask}[asgroup="masking"]
%      \mpfig
%        label(TEX "\sffamily\bfseries\scshape\Huge Meta" scaled 2, center pic)
%          withcolor 1 ;
%      \endmpfig
%    \endmplibgroup
%\end{verbatim}
%\startmpfig
%\mpfig* picture pic; pic = image(fill fullcircle scaled 80 withcolor blue; fill fullcircle scaled 80 shifted (25,0) withcolor green; fill fullcircle scaled 80 shifted (50,0) withcolor red;);\endmpfig
%\mplibgroup{mymask}[asgroup="masking"]
%\mpfig label(TEX "\sffamily\bfseries\scshape\Huge Meta" scaled 2, center pic) withcolor 1;\endmpfig
%\endmplibgroup
%\mpfig fill bbox pic withshadingmethod "linear" withshadingcolors(red, blue); draw pic withmaskinggroup "mymask" withmaskingbgcolor 1/10 withtransparency (1, 0.8);\endmpfig
%\stopmpfig{4}
%\begin{verbatim}
%    \mpfig
%      fill bbox pic
%        withshadingmethod "linear"
%        withshadingcolors (red, blue) ;
%      draw pic
%        withmaskinggroup "mymask"
%        withmaskingbgcolor 1/10
%        withtransparency (1, 0.8) ;
%    \endmpfig
%\end{verbatim}
%
% \subsubsection{Free-form Gouraud-shaded triangle mesh shading}\label{sec:trianglemesh}
% The syntax of this type of shading is \bsx\meta{path} \textbar\ \meta{textual picture}
% |withshadingmethod| |"triangle"|\esx,
% as the area to be shaded is defined by a series of triangles.
% Among a number of optional macros for shading, only |withshadingmatrix| and |withshadingstroke|
% are available (see above \secref{luamplibshading}).
%
% Optional macros for triangle mesh shading:
% \begin{description}
% \item[withtrianglepatchinit (\meta{path} \textbar\ \meta{string},
%   \meta{color}, \meta{color}, \meta{color})]
%   The initial triangle.
%   This macro can be used multiple times.
%
%   The first argument shall be a path that has three (or more) vertices,
%   or a string composed of six numerics separated by spaces
%   ($x$ and $y$ coordinates at each point).
%   When this macro is not given, the default path value will be the object of shading.
%
%   Remaining arguments are three colors for each vertex in the order of the points.
%   When this macro is not given, the default color values will be |red|, |green|, and |blue|.
%
% \item[withtrianglepatchnext (\meta{number}, \meta{pair} \textbar\ \meta{string}, \meta{color})]
%   The new triangle attached to the previous one.
%   This optional macro requires |withtrianglepatchinit| specified beforehand.
%
%   The first argument shall be a number (|1| or |2|)
%   denoting the edge to which a new triangle will be attached.
%   Edge |1| is the last explicit segment of the previous triangle;
%   edge |2| is the implicit segment of the previous triangle.
%
%   For specifying a new vertex which determines the next triangle, the second argument
%   shall be a pair, or a string of two numerics separated by a space ($x$ and $y$ coordinates).
%
%   The third argument is the color for the new vertex.
% \end{description}
%
% Examples showing the triangle shading and
% illustrating the usage of the optional macros:
%\startmpfig
%\mpfig pair q; vardef qadd (expr a) = q := q shifted (dir a * 70); q enddef; q := origin; draw (q -- qadd(60) -- qadd(-60)) scaled 5/4 withshadingmethod "triangle"; \endmpfig
%\stopmpfig{6}
%\begin{verbatim}
%    \mpfig
%      pair q ;
%      vardef qadd (expr a) =
%        q := q shifted (dir a * 70) ;
%        q
%      enddef;
%
%      q := origin ;
%      draw ( q -- qadd(60) -- qadd(-60) ) scaled 5/4
%        withshadingmethod "triangle" ;
%    \endmpfig
%\end{verbatim}
%\startmpfig
%\mpfig q := origin; path p; p = q -- qadd(60) -- qadd(-60) -- qadd(60) -- qadd(-60) -- qadd(-120) -- qadd(-180) -- cycle; draw bbox p withshadingmethod "triangle" withtrianglepatchinit (p, red, blue, green) withtrianglepatchnext (1, point 3 of p, red) withtrianglepatchnext (1, point 4 of p, blue) withtrianglepatchnext (2, point 5 of p, red) withtrianglepatchnext (2, point 6 of p, blue) withtrianglepatchnext (2, point 0 of p, red); drawoptions(withpen pencircle scaled 2 withcolor 2/3white); drawarrow p; draw (point 2 of p--point 4 of p) dashed evenly; draw (point 2 of p--point 5 of p) dashed evenly;draw (point 2 of p--point 6 of p) dashed evenly; label.top(btex Edge 1 etex,origin) rotated -60 shifted(1/2[point 1 of p,point 2 of p]);label.bot(btex Edge 2 etex, 1/2[point 2 of p, point 4 of p]);\endmpfig
%\stopmpfig{9}
%\begin{verbatim}
%    \mpfig
%      q := origin ;
%      path p ;
%      p = q -- qadd(60) -- qadd(-60) -- qadd(60) --
%          qadd(-60) -- qadd(-120) -- qadd(-180) -- cycle ;
%
%      draw bbox p
%        withshadingmethod "triangle"
%        withtrianglepatchinit (p, red, blue, green)
%        withtrianglepatchnext (1, point 3 of p, red )
%        withtrianglepatchnext (1, point 4 of p, blue)
%        withtrianglepatchnext (2, point 5 of p, red )
%        withtrianglepatchnext (2, point 6 of p, blue)
%        withtrianglepatchnext (2, point 0 of p, red ) ;
%\endmpfig
%\end{verbatim}
%
% \subsubsection{Lattice-form Gouraud-shaded triangle mesh shading}\label{sec:latticemesh}
%
% This type of shading is similar to the triangle mesh shading described just above,
% but the vertices are are organized into rows, which need not be geometrically linear.
% The syntax is
% \bsx\meta{path} \textbar\ \meta{textual picture} |withshadingmethod| |"lattice"|\esx.
% Among a number of optional macros for shading, only |withshadingmatrix| and |withshadingstroke|
% are available (see above \secref{luamplibshading}).
%
% Optional macros for lattice-form shading:
% \begin{description}
% \item[withlatticeverticesperrow \meta{number}] The number of vertices in each row of the lattice.
%   The value should be greater than or equal to |2|. The default value is |2|.
% \item[withlatticeverticesdata (\meta{pair} \textbar\ \meta{string}, \meta{color}, |...|)]
%   A list of vertices and colors.
%
%   The first argument shall be a pair,
%   or a string of two numerics separated by a space ($x$ and $y$ coordinates).
%   The second argument shall be a color expression for the vertex.
%
%   This combination of a point and a color should be repeated multiple times, which
%   must be a multiple of the number of vertices in each row.
%
%   When this macro is not given,
%   the default values will be four points of the path obtained from the object of shading
%   and red, green, yellow, and blue for the colors at each point.
% \end{description}
%
% Examples showing the lattice-form shading and
% illustrating the usage of the optional macros:
%\startmpfig
%\mpfig u := 60; draw unitsquare scaled u withshadingmethod "lattice"; \endmpfig
%\stopmpfig{3}
%\begin{verbatim}
%    \mpfig
%      u := 60;
%      draw unitsquare scaled u
%        withshadingmethod "lattice" ;
%    \endmpfig
%\end{verbatim}
%\startmpfig
%\mpfig color yellow; yellow=(1,1,0); draw unitsquare scaled 2u withshadingmethod "lattice" withlatticeverticesperrow 3 withlatticeverticesdata ((0,2u),yellow, (u,2u),green, (2u,2u),yellow, (0, u),red, (u, u),blue, (2u, u),red, (0, 0),yellow, (u, 0),green, (2u, 0),yellow,); \endmpfig
%\stopmpfig{7}
%\begin{verbatim}
%    \mpfig
%      color yellow;
%      yellow = (1,1,0);
%      draw unitsquare scaled 2u
%        withshadingmethod "lattice"
%        withlatticeverticesperrow 3
%        withlatticeverticesdata (
%          (0,2u),yellow, (u,2u),green, (2u,2u),yellow,
%          (0, u),red   , (u, u),blue , (2u, u),red   ,
%          (0, 0),yellow, (u, 0),green, (2u, 0),yellow,
%        ) ;
%    \endmpfig
%\end{verbatim}
%
%
% \subsubsection{Coons patch mesh shading}\label{sec:coonspatchmesh}
%
% Coons patch mesh shadings are constructed from one or more color patches,
% each bounded by four cubic Bézier curves.
% The syntax is \bsx\meta{path} \textbar\ \meta{textual picture} |withshadingmethod| |"coons"|\esx.
% Among a number of optional macros for shading, only |withshadingmatrix| and |withshadingstroke|
% are available (see above \secref{luamplibshading}).
%
% Optional macros for Coons patch mesh shading:
% \begin{description}
% \item[withcoonspatchinit (\meta{path} \textbar\ \meta{string},
%   \meta{color}, \meta{color}, \meta{color}, \meta{color})]
%   The initial patch.
%   This macro can be used multiple times.
%
%   The first argument shall be a closed path that has four vertices,
%   or a string composed of twenty-four numerics separated by spaces
%   (|xpart| |point| |0| |of| |p|, |ypart| |point| |0| |of| |p|, |...|,
%   |xpart| |precontrol| |0| |of| |p|, |ypart| |precontrol| |0| |of| |p|).
%   When this macro is not given, the default path value will be obtained
%   from the object of shading.
%
%   Remaining arguments are four colors for each vertex in the order of the points.
%   When this macro is not given,
%   the default color values will be |red|, |green|, |blue|, and |yellow|.
%
% \item[withcoonspatchnext (\meta{number}, \meta{path} \textbar\ \meta{string},
%   \meta{color}, \meta{color})]
%   The new patch attached to the previous one.
%   This optional macro requires |withcoonspatchinit| specified beforehand.
%
%   The first argument shall be a number (|1|, |2|, or |3|)
%   denoting the previous patch's edge a new patch will be attached to.
%   For instance, edge |1| is the segment between point |1| and point |2| of the previous patch.
%
%   The second argument shall be a path that has four vertices,
%   or a string of sixteen numerics separated by spaces
%   (|xpart| |postcontrol| |1| |of| |p|, |ypart| |postcontrol| |1| |of| |p|, |...|,
%   |xpart| |precontrol| |0| |of| |p|, |ypart| |precontrol| |0| |of| |p|).
%   The orientation of the new path should be reversed from the previous one, and
%   it is assumed that the first segment of the new path coincides with the edge segment
%   just mentioned.
%
%   Remaining arguments are two color expressions for the new vertices.
% \end{description}
%
% Examples showing the Coons patch shading and
% illustrating the usage of the optional macros:
%\startmpfig
%\mpfig draw ((0,0){dir 30}..{dir 30}(100,0){dir 120}..{dir 120}(100,100){dir 210}..{dir 210}(0,100){dir 300}..{dir 300}cycle) withshadingmethod "coons"; \endmpfig
%\stopmpfig{6}
%\begin{verbatim}
%    \mpfig
%       draw (
%           (0,0) {dir 30} .. {dir 30}
%           (100,0) {dir 120} .. {dir 120}
%           (100,100) {dir 210} .. {dir 210}
%           (0,100) {dir 300} .. {dir 300}
%           cycle
%         )
%         withshadingmethod "coons" ;
%   \endmpfig
%\end{verbatim}
%\startmpfig
%\mpfig u:=80; path p; p = unitsquare scaled u; def fsquare (expr n) = (point n of p -- point n+1 of p -- point n+2 of p -- point n+3 of p -- cycle) enddef; def rsquare (expr n) = (point n of p -- point n-1 of p -- point n-2 of p -- point n-3 of p -- cycle) enddef; fill p scaled 2 withshadingmethod  "coons" withcoonspatchinit (fsquare(0), red, green, blue, yellow) withcoonspatchnext (1, rsquare(0) shifted (u,0), yellow, red) withcoonspatchnext (1, fsquare(0) shifted (u,u), red, green) withcoonspatchnext (3, rsquare(2) shifted (0,u), yellow, red); pickup pencircle scaled 2; drawarrow fsquare(0) shifted (-1,-1) withcolor (0,1,1); drawarrow rsquare(0) shifted (u+1,-1) withcolor (1,.6,1); drawarrow rsquare(2) shifted (-1,u+1) withcolor (1,.6,1); drawarrow fsquare(0) shifted (u+1,u+1) withcolor (0,1,1); label.lft(btex \sffamily Edge 1 etex rotated 90, (u,u/2)) withcolor (0,1,1); label.bot(btex \sffamily Edge 1 etex , (3/2u,u)) withcolor (1,.6,1); label.rt(btex \sffamily Edge 3 etex rotated -90, (u,3/2u)) withcolor (0,1,1); drawarrow (u,-12)--(u,-3) withpen pencircle scaled 1; label.bot(btex \sffamily\footnotesize point 1 of patch\textsubscript{1} $=$ point 0 of patch\textsubscript{2} etex, (u,-10)); label.top(btex \sffamily Edge 0 etex, (u/2,0)) withcolor (0,1,1);\endmpfig
%\stopmpfig{20}
%\begin{verbatim}
%   \mpfig
%     u := 80 ;
%     path p;
%     p = unitsquare scaled u;
%     def fsquare (expr n) =
%       ( point n of p --
%         point n+1 of p --
%         point n+2 of p --
%         point n+3 of p --
%         cycle )
%     enddef;
%     def rsquare (expr n) =
%       ( point n of p --
%         point n-1 of p --
%         point n-2 of p --
%         point n-3 of p --
%         cycle )
%     enddef;
%     fill p scaled 2
%       withshadingmethod "coons"
%       withcoonspatchinit
%         (fsquare(0), red, green, blue, yellow)
%       withcoonspatchnext
%         (1, rsquare(0) shifted (u,0), yellow, red)
%       withcoonspatchnext
%         (1, fsquare(0) shifted (u,u), red, green)
%       withcoonspatchnext
%         (3, rsquare(2) shifted (0,u), yellow, red) ;
%   \endmpfig
%\end{verbatim}
% \notabene Be aware that currently Mac OS Preview exposes its some bug in rendering
% the figure just above, especially when the edge flag is |3|.
% In order to circumvent the Preview's bug, you can use |withcoonspatchinit| multiple times:
%\startmpfig
%\mpfig u:=70; p:=unitsquare scaled u; fill p scaled 2 withshadingmethod "coons" withcoonspatchinit (p, red, green, blue, yellow) withcoonspatchinit (p shifted (u,0), green, red, yellow, blue) withcoonspatchinit (p shifted (u,u), blue, yellow, red, green) withcoonspatchinit (p shifted (0,u), yellow, blue, green, red) ;\endmpfig
%\stopmpfig{7}
%\begin{verbatim}
%   \mpfig
%     u := 70 ;
%     p := unitsquare scaled u ;
%     fill p scaled 2
%       withshadingmethod  "coons"
%       withcoonspatchinit
%         (p, red, green, blue, yellow)
%       withcoonspatchinit
%         (p shifted (u,0), green, red, yellow, blue)
%       withcoonspatchinit
%         (p shifted (u,u), blue, yellow, red, green)
%       withcoonspatchinit
%         (p shifted (0,u), yellow, blue, green, red) ;
%   \endmpfig
%\end{verbatim}
%
% \subsubsection{Tensor-product patch mesh shading}\label{sec:tensorpatchmesh}
% This type is almost identical to the Coons patch mesh shading, except that
% tensor-product patch has four additional, \emph{internal} control points
% to adjust the color mapping.
% The syntax is \bsx\meta{path} \textbar\ \meta{textual picture} |withshadingmethod| |"tensor"|\esx.
% Among a number of optional macros for shading, only |withshadingmatrix| and |withshadingstroke|
% are available (see above \secref{luamplibshading}).
%
% Optional macros for tensor-product patch mesh shading:
% \begin{description}
% \item[withtensorpatchinit (\meta{path} \textbar\ \meta{string},
%   \meta{path} \textbar\ \meta{string}, \meta{color}, \meta{color}, \meta{color}, \meta{color})]
%   The initial patch.
%   This macro can be used multiple times.
%
%   The only difference to |withcoonspatchinit| macro of Coons patch shading is the second argument
%   inserted for specifying four inner control points.
%   It shall be a path that has four points, or a string composed of eight numerics separated
%   by spaces ($x$ and $y$ coordinates of each point).
%   If the first argument is given in string type,
%   the second argument should also be given in string type.
%   It is assumed that the orientation of the inner path is the same as the outer path.
%   When this macro is not given, the default value will be an imaginary path
%   scaled by half the size of the outer path.
%
% \item[withtensorpatchnext (\meta{number}, \meta{path} \textbar\ \meta{string},
%   \meta{path} \textbar\ \meta{string}, \meta{color}, \meta{color})]
%   The new patch attached to the previous one.
%   This optional macro requires |withtensorpatchinit| specified beforehand.
%
%   The only difference to |withcoonspatchnext| macro of Coons patch shading is the third argument
%   inserted for specifying four inner control points.
%   The syntax is the same as the second argument of |withtensorpatchinit| described just above.
% \end{description}
%
% An example to show the effect of tensor-product patch mesh shading:
%\startmpfig
%\mpfig u := 80 ; path p, q; p = unitsquare scaled u ; q = p scaled 1/10 shifted (dir 45 * 1.2u) ; fill p scaled 2 shifted (-u,-u) withshadingmethod "tensor" withtensorpatchinit ( p, q, red, green, blue, yellow) withtensorpatchinit ( p rotated 90, q rotated 90, red, yellow, blue, green) withtensorpatchinit ( p rotated 180, q rotated 180, red, green, blue, yellow) withtensorpatchinit ( p rotated 270, q rotated 270, red, yellow, blue, green) ; \endmpfig
%\stopmpfig{10}
%\begin{verbatim}
%   \mpfig
%     u := 80 ;
%     path p, q ;
%     p = unitsquare scaled u ;
%     q = p scaled 1/10 shifted (dir 45 * 1.2u) ;
%     fill p scaled 2 shifted (-u,-u)
%       withshadingmethod "tensor"
%       withtensorpatchinit
%         (p, q, red, green, blue, yellow)
%       withtensorpatchinit
%         (p rotated 90, q rotated 90,
%          red, yellow, blue, green)
%       withtensorpatchinit
%         (p rotated 180, q rotated 180,
%          red, green, blue, yellow)
%       withtensorpatchinit
%         (p rotated 270, q rotated 270,
%          red, yellow, blue, green) ;
%   \endmpfig
%\end{verbatim}
% \notabene Be aware that currently Mac OS Preview or Foxit Editor
% does not render properly the figure above.
%
%
% \subsection{Lua}
%
% \subsubsection[\ttfamily runscript]{\ttfamily runscript ...}
%   A good many \metapost macros described in this documentation have been implemented
%   using the primitive |runscript|.
%   With \bsx|runscript| \meta{string}\esx, you can run a Lua code chunk from \metapost side
%   and get some \metapost code returned by Lua if you want.
%   As the functionality is provided by the \mplib library itself,
%   \pkg{luamplib} does not have much to say about it.
%
%   One thing is worth mentioning, however:
%   if you return a Lua \emph{table} to the \metapost process,
%   it is automatically converted to a relevant \metapost data type
%   such as pair, color, cmykcolor or transform.
%   So users can save some extra toil of converting a table to a string, though it's not a big deal.
%   For instance, |runscript| |"return {1,0,0}"| will give you the \metapost color expression |(1,0,0)|
%   automatically.
%
% \subsubsection{Accessing \metapost variables}
%   \label{sec:luamplibinstances}
%   Users can access the Lua table containing \mplib instances, |luamplib.instances|,
%   through which \metapost variables are also easily accessible from Lua side,
%   as documented in Lua\TeX{} manual \S\,11.2.8.4 (|texdoc luatex|).
%   The following example will print |false|, |3.0|, |MetaPost| and
%   the knots and the cyclicity of the path |unitsquare|.
%\begin{verbatim}
%    \begin{mplibcode}[myinstance]
%      boolean b; b = 1 > 2;
%      numeric n; n = 3;
%      string s;  s = "MetaPost";
%      path p;    p = unitsquare;
%    \end{mplibcode}
%
%    \directlua{
%      local myinstance = luamplib.instances.myinstance
%      print( myinstance:get_boolean "b" )
%      print( myinstance:get_numeric "n" )
%      print( myinstance:get_string  "s" )
%      local t = myinstance:get_path "p"
%      for k,v in pairs(t) do
%        print(k, type(v)=='table' and table.concat(v,' ') or v)
%      end
%    }
%\end{verbatim}
%   Of course, this sort of Lua code can also be run inside \metapost code using |runscript|
%   command.
%   Again, of course you can access a \metapost variable using your own \TeX\ macro. For example:
%\startmpfig
%\mpfig* numeric n; n = 3;\endmpfig
%\def\mpnumeric#1#2{\directlua{tex.sprint(tostring(luamplib.instances["#1"]:get_numeric"#2"))}}
%\mpnumeric{@mpfig}{n}\relax
%\stopmpfig{4}
%\begin{verbatim}
%    \def\mpnumeric#1#2{\directlua{
%      tex.sprint(tostring(luamplib.instances["#1"]:get_numeric"#2"))
%    }}
%    \mpnumeric{myinstance}{n}\relax
%\end{verbatim}
%
% \subsubsection{Running a \metapost code}
%   Users can run a \metapost code chunk from Lua side by using this function:
%\begin{verbatim}
%    luamplib.process_mplibcode (<string> metapost code, <string> instance name)
%\end{verbatim}
%
%   The second argument cannot be absent, but can be an empty string (|""|) which
%   means that it has no instance name.
%
%   Some other elements in the |luamplib| namespace, listed in Table~\ref{tab:elementsinluamplib},
%   can affect the process of |process_mplibcode|.
%
%   \begin{table}
%     \centering
%     \caption{elements in \texttt{luamplib} table (partial)}\label{tab:elementsinluamplib}
%   \begin{tabular}{llll}\hline
%     Key & Type & Related \TeX\ macro & Cf. \\\hline
%     |codeinherit|	& \emph{boolean} & \cs{mplibcodeinherit} & \secref{mplibcodeinherit}\\
%     |everyendmplib|	& \emph{table} & \cs{everyendmplib} & \secref{everymplib}\\
%     |everymplib| & \emph{table} & \cs{everymplib} & \secref{everymplib}\\
%     |getcachedir|	& \emph{function} \parg{string} & \cs{mplibcachedir} & \secref{mplibcachedir}\\
%     |globaltextext|	& \emph{boolean} & \cs{mplibglobaltextext} & \secref{mplibglobaltextext}\\
%     |legacyverbatimtex| & \emph{boolean} & \cs{mpliblegacybehavior} & \secref{mpliblegacybehavior}\\
%     |noneedtoreplace|	& \emph{table} & \cs{mplibmakenocache} & \secref{mplibcachedir}\\
%     |numbersystem|	& \emph{string} & \cs{mplibnumbersystem} & \secref{mplibnumbersystem}\\
%     |setformat|	& \emph{function} \parg{string} & \cs{mplibsetformat} & \secref{mplibsetformat}\\
%     |showlog|	& \emph{boolean} & \cs{mplibshowlog} & \secref{mplibshowlog}\\
%     |textextlabel| & \emph{boolean} & \cs{mplibtextextlabel} & \secref{mplibtextextlabel}\\
%     |verbatiminput| & \emph{boolean} & \cs{mplibverbatim} & \secref{mplibverbatim}\\\hline
%   \end{tabular}
%   \end{table}
%
% \subsubsection{Registering a tiling pattern}
%   This is the Lua interface for \cs{mppattern} |...| \cs{endmppattern}
%   described above at \secref{mppattern}.
%\begin{verbatim}
%    luamplib.registerpattern (<number> box register, <string> pattern name, <table> options)
%\end{verbatim}
%
%   The first argument is the register of a box containing a pattern cell,
%   which should be prepared in advance by the user. For instance,
%   \cs{setbox0=}\allowbreak\cs{hbox}\allowbreak|{\tiny\TeX}|,
%   or corresponding Lua code using |tex.setbox| function; then the argument should be |0|.\relax
%     \footnote{In DVI mode, \TeX\ macro `|mplibpatternname|' should be set as \meta{pattern name}
%     before preparing the box, if shading pattern (i.e.\ shading on picture) is used
%     in the pattern cell. }
%
%   As for the third argument, see above Table~\ref{tab:mppatternoptions}.
%   The argument cannot be absent, but can be an empty table, i.e.\ |{ }|.
%
% \subsubsection{Registering a form XObject}
%   This is the Lua interface for \cs{mplibgroup} |...| \cs{endmplibgroup}
%   described above at \secref{mplibgroupendmplibgroup}.
%\begin{verbatim}
%    luamplib.registergroup (<number> box register, <string> group name, <table> options)
%\end{verbatim}
%
%   The first argument is the register of a box prepared in advance by the user.
%   When the contents of the box have been generated from \TeX\ (not \metapost) code,
%   please make sure that both of the \TeX\ macros `|MPllx|' and `|MPlly|' are defined as `|0|'
%   before invoking the Lua function.\footnote{\relax
%     In DVI mode, \TeX\ macro `|mplibgroupname|' also should be set as \meta{group name} before
%     preparing the box, if shading pattern (i.e.\ shading on picture) is used in the mplibgroup. }
%
%   As for the third argument, see above Table~\ref{tab:mplibgroupoptions}.
%   The argument cannot be absent, but can be an empty table, i.e.\ |{ }|.
%
%   Reusing an mplibgroup, \cs{usemplibgroup}\marg{name}, is basically the same as
%   running the \TeX\ macro `|luamplib.group.|\meta{name}'.
%   If you need the boxresource index,
%   inspect this macro using |token.get_macro| function.
%
%
%    \section{Implementation}
%
%    \subsection{Lua module}
%
% \iffalse
%<*lua>
% \fi
%
%    \begin{macrocode}

luatexbase.provides_module {
  name          = "luamplib",
  version       = "2.42.4",
  date          = "2026/07/10",
  description   = "Lua package to typeset Metapost with LuaTeX's MPLib.",
}

%    \end{macrocode}
%
%    Use the |luamplib| namespace, since |mplib| is for the \metapost library
%    itself. \ConTeXt{} uses |metapost|.
%    \begin{macrocode}
luamplib          = luamplib or { }
local luamplib    = luamplib

local format, abs = string.format, math.abs

%    \end{macrocode}
%
%    Use our own function for warn/info/err.
%    \begin{macrocode}
local function termorlog (target, text, kind)
  if text then
    local mod, write, append = "luamplib", texio.write_nl, texio.write
    kind = kind
        or target == "term" and "Warning (more info in the log)"
        or target == "log" and "Info"
        or target == "term and log" and "Warning"
        or "Error"
    target = kind == "Error" and "term and log" or target
    local t = text:explode"\n+"
    write(target, format("Module %s %s:", mod, kind))
    if #t == 1 then
      append(target, format(" %s", t[1]))
    else
      for _,line in ipairs(t) do
        write(target, line)
      end
      write(target, format("(%s)     ", mod))
    end
    append(target, format(" on input line %s", tex.inputlineno))
    write(target, "")
    if kind == "Error" then error() end
  end
end
local function warn (...) -- beware '%' symbol
  termorlog("term and log", select("#",...) > 1 and format(...) or ...)
end
local function info (...)
  termorlog("log", select("#",...) > 1 and format(...) or ...)
end
local function err (...)
  termorlog("error", select("#",...) > 1 and format(...) or ...)
end

luamplib.showlog  = luamplib.showlog or false

%    \end{macrocode}
%
%   Provide a few ``shortcuts'' expected by the code.
%    \begin{macrocode}
local tableconcat = table.concat
local tableinsert = table.insert
local tableunpack = table.unpack
local texsprint   = tex.sprint
local texgettoks  = tex.gettoks
local texgetbox   = tex.getbox
local texruntoks  = tex.runtoks
if not texruntoks then
  err("Your LuaTeX version is too old. Please upgrade it to the latest")
end
local is_defined  = token.is_defined
local get_macro   = token.get_macro
local mplib = require ('mplib')
local kpse  = require ('kpse')
local lfs   = require ('lfs')
local lfsattributes = lfs.attributes
local lfsisdir      = lfs.isdir
local lfsmkdir      = lfs.mkdir
local lfstouch      = lfs.touch
local ioopen        = io.open

%    \end{macrocode}
%
%    Some helper functions, prepared for the case when |l-file| etc
%    is not loaded.
%    \begin{macrocode}
local file = file or { }
local replacesuffix = file.replacesuffix or function(filename, suffix)
  return (filename:gsub("%.[%a%d]+$","")) .. "." .. suffix
end
local is_writable = file.is_writable or function(name)
  if lfsisdir(name) then
    name = name .. "/_luam_plib_temp_file_"
    local fh = ioopen(name,"w")
    if fh then
      fh:close(); os.remove(name)
      return true
    end
  end
end
local mk_full_path = lfs.mkdirp or lfs.mkdirs or function(path)
  local full = ""
  for sub in path:gmatch("(/*[^\\/]+)") do
    full = full .. sub
    lfsmkdir(full)
  end
end

%    \end{macrocode}
%
%    |btex| |...| |etex| in input |.mp| files will be replaced in finder.
%    Because of the limitation of \mplib regarding |make_text|,
%    we might have to make cache files modified from input files.
%
%     First of all, determine the directory to store cache files.
%    \begin{macrocode}
local cachedir
local function outputdir ()
  if lfstouch then
    for i,v in ipairs{'TEXMFVAR','TEXMF_OUTPUT_DIRECTORY','.','TEXMFOUTPUT'} do
      local var = i == 3 and v or kpse.var_value(v)
      if var and var ~= "" then
        for _,vv in ipairs(var:explode(os.type == "unix" and ":" or ";")) do
          local dir = format("%s/%s",vv,"luamplib_cache")
          if not lfsisdir(dir) then
            mk_full_path(dir)
          end
          if is_writable(dir) then
            cachedir = dir; return cachedir
          end
        end
      end
    end
  end
  cachedir = "."; return cachedir
end
function luamplib.getcachedir(dir)
  dir = dir:gsub("##","#")
  dir = dir:gsub("^~",
    os.type == "windows" and os.getenv("UserProfile") or os.getenv("HOME"))
  if lfstouch and dir then
    if lfsisdir(dir) then
      if is_writable(dir) then
        cachedir = dir
      else
        warn("Directory '%s' is not writable!", dir)
      end
    else
      warn("Directory '%s' does not exist!", dir)
    end
  end
end
%    \end{macrocode}
%    Some basic \metapost files not necessary to make cache files.
%    \begin{macrocode}
local noneedtoreplace = {
  ["boxes.mp"] = true, --  ["format.mp"] = true,
  ["graph.mp"] = true, ["marith.mp"] = true, ["mfplain.mp"] = true,
  ["mpost.mp"] = true, ["plain.mp"] = true, ["rboxes.mp"] = true,
  ["sarith.mp"] = true, ["string.mp"] = true, -- ["TEX.mp"] = true,
  ["metafun.mp"] = true, ["metafun.mpiv"] = true, ["mp-abck.mpiv"] = true,
  ["mp-apos.mpiv"] = true, ["mp-asnc.mpiv"] = true, ["mp-bare.mpiv"] = true,
  ["mp-base.mpiv"] = true, ["mp-blob.mpiv"] = true, ["mp-butt.mpiv"] = true,
  ["mp-char.mpiv"] = true, ["mp-chem.mpiv"] = true, ["mp-core.mpiv"] = true,
  ["mp-crop.mpiv"] = true, ["mp-figs.mpiv"] = true, ["mp-form.mpiv"] = true,
  ["mp-func.mpiv"] = true, ["mp-grap.mpiv"] = true, ["mp-grid.mpiv"] = true,
  ["mp-grph.mpiv"] = true, ["mp-idea.mpiv"] = true, ["mp-luas.mpiv"] = true,
  ["mp-mlib.mpiv"] = true, ["mp-node.mpiv"] = true, ["mp-page.mpiv"] = true,
  ["mp-shap.mpiv"] = true, ["mp-step.mpiv"] = true, ["mp-text.mpiv"] = true,
  ["mp-tool.mpiv"] = true, ["mp-cont.mpiv"] = true,
}
luamplib.noneedtoreplace = noneedtoreplace

%    \end{macrocode}
%    Pattern formats to replace |btex| and |verbatimtex| |...| |etex| in input files,
%    if needed.
%    \begin{macrocode}
local name_b = "%f[%a_]"
local name_e = "%f[^%a_]"
local btex_etex = name_b.."btex"..name_e.."%s*(.-)%s*"..name_b.."etex"..name_e
local verbatimtex_etex = name_b.."verbatimtex"..name_e.."%s*(.-)%s*"..name_b.."etex"..name_e

%    \end{macrocode}
%   Function |luamplib.finder|
%    \begin{macrocode}
local currenttime = os.time()
do
  local luamplibtime = lfsattributes(kpse.find_file"luamplib.lua", "modification")
%    \end{macrocode}
%    |format.mp| is much complicated, so specially treated.
%    \begin{macrocode}
  local function replaceformatmp(file,newfile,ofmodify)
    local fh = ioopen(file,"r")
    if not fh then return file end
    local data = fh:read("*all"); fh:close()
    fh = ioopen(newfile,"w")
    if not fh then return file end
    fh:write(
      "let normalinfont = infont;\n",
      "primarydef str infont name = rawtextext(str) enddef;\n",
      data,
      "vardef Fmant_(expr x) = rawtextext(decimal abs x) enddef;\n",
      "vardef Fexp_(expr x) = rawtextext(\"$^{\"&decimal x&\"}$\") enddef;\n",
      "let infont = normalinfont;\n"
    ); fh:close()
    lfstouch(newfile,currenttime,ofmodify)
    return newfile
  end
  local function replaceinputmpfile (name,file)
    local ofmodify = lfsattributes(file,"modification")
    if not ofmodify then return file end
    local newfile = name:gsub("%W","_")
    newfile = format("%s/luamplib_input_%s", cachedir or outputdir(), newfile)
    if newfile and luamplibtime then
      local nf = lfsattributes(newfile)
      if nf and nf.mode == "file" and
        ofmodify == nf.modification and luamplibtime < nf.access then
        return nf.size == 0 and file or newfile
      end
    end
    if name == "format.mp" then return replaceformatmp(file,newfile,ofmodify) end
    local fh = ioopen(file,"r")
    if not fh then return file end
    local data = fh:read("*all"); fh:close()
%    \end{macrocode}
%    ``|etex|'' must be preceded by a space and followed by a space or semicolon as specified in
%    \LuaTeX\ manual, which is not the case of standalone \metapost though.
%    \begin{macrocode}
    local count,cnt = 0,0
    data, cnt = data:gsub(btex_etex, "btex %1 etex ") -- space
    count = count + cnt
    data, cnt = data:gsub(verbatimtex_etex, "verbatimtex %1 etex;") -- semicolon
    count = count + cnt
    if count == 0 then
      noneedtoreplace[name] = true
      fh = ioopen(newfile,"w");
      if fh then
        fh:close()
        lfstouch(newfile,currenttime,ofmodify)
      end
      return file
    end
    fh = ioopen(newfile,"w")
    if not fh then return file end
    fh:write(data); fh:close()
    lfstouch(newfile,currenttime,ofmodify)
    return newfile
  end
%    \end{macrocode}
%    As the finder function for \mplib, use the |kpse| library and
%    make it behave like as if \metapost was used. And replace |.mp| files with
%    cache files if needed.
%    See also \#74, \#97.
%    \begin{macrocode}
  local mpkpse
  do
    local exe = 0
    while arg[exe-1] do
      exe = exe-1
    end
    mpkpse = kpse.new(arg[exe], "mpost")
  end
  local special_ftype = {
    pfb = "type1 fonts",
    enc = "enc files",
  }
  function luamplib.finder (name, mode, ftype)
    if mode == "w" then
      if name and name ~= "mpout.log" then
        kpse.record_output_file(name) -- recorder
      end
      return name
    else
      ftype = special_ftype[ftype] or ftype
      local file = mpkpse:find_file(name,ftype)
      if file then
        if lfstouch and ftype == "mp" and not noneedtoreplace[name] and not noneedtoreplace["*.mp"] then
          file = replaceinputmpfile(name,file)
        end
      else
        file = mpkpse:find_file(name, name:match("%a+$"))
      end
      if file then
        kpse.record_input_file(file) -- recorder
      end
      return file
    end
  end
end

%    \end{macrocode}
%
%    For the main function: |process|
%
%    \emph{plain} or \emph{metafun},
%    though we cannot support \emph{metafun} format fully.
%    \begin{macrocode}
local currentformat = "plain"
function luamplib.setformat (name)
  currentformat = name
end
%    \end{macrocode}
%    v2.9 has introduced the concept of ``code inherit''
%    \begin{macrocode}
luamplib.codeinherit = false
local mplibinstances = {}
luamplib.instances = mplibinstances
local has_instancename = false

local process
do
  local function reporterror (result, prevlog)
    if not result then
      err("no result object returned")
    else
      local t, e, l = result.term, result.error, result.log
%    \end{macrocode}
%    log has more information than term, so log first (2021/08/02)
%    \begin{macrocode}
      local log = l or t or "no-term"
      log = log:gsub("%(Please type a command or say `end'%)",""):gsub("\n+","\n")
      if result.status > 0 then
        local first = log:match"(.-\n! .-)\n! "
        if first then
          termorlog("term", first)
          termorlog("log", log, "Warning")
        else
          warn(log)
        end
        if result.status > 1 then
          err(e or "see above messages")
        end
      elseif prevlog then
        log = prevlog..log
%    \end{macrocode}
%    v2.6.1: now luamplib does not disregard |show| command,
%    even when |luamplib.showlog| is false.  Incidentally,
%    it does not raise error nor prints an info,
%    even if output has no figure.
%    \begin{macrocode}
        local show = log:match"\n>>? .+"
        if show then
          termorlog("term", show, "Info (more info in the log)")
          info(log)
        elseif luamplib.showlog and log:find"%g" then
          info(log)
        end
      end
      return log
    end
  end
%    \end{macrocode}
%     |lualibs-os.lua| installs a randomseed. When this file is not loaded,
%     we should explicitly seed a unique integer to get random randomseed for each run.
%    \begin{macrocode}
  if not math.initialseed then math.randomseed(currenttime) end
  local function luamplibload (name)
    local mpx = mplib.new {
      ini_version = true,
      find_file   = luamplib.finder,
%    \end{macrocode}
%    Make use of |make_text| and |run_script|. And we provide |numbersystem| option since v2.4.
%    See \url{https://github.com/lualatex/luamplib/issues/21}.
%    \begin{macrocode}
      make_text   = luamplib.maketext,
      run_script  = luamplib.runscript,
      math_mode   = luamplib.numbersystem,
      job_name    = tex.jobname,
      random_seed = math.random(4095),
      utf8_mode   = true,
      extensions  = 1,
    }
%    \end{macrocode}
%    Append our own \metapost preamble to the preamble loading plain/metafun format.
%    \begin{macrocode}
    local preamble = tableconcat{
      format(luamplib.preambles.preamble, replacesuffix(name,"mp")),
      luamplib.preambles.mplibcode,
      luamplib.legacyverbatimtex and luamplib.preambles.legacyverbatimtex or "",
      luamplib.textextlabel and luamplib.preambles.textextlabel or "",
    }
    local result, log
    if not mpx then
      result = { status = 99, error = "out of memory"}
    else
      result = mpx:execute(preamble)
    end
    log = reporterror(result)
    return mpx, result, log
  end
%    \end{macrocode}
%    Here, excute each |mplibcode| data,
%    ie |\begin{mplibcode} ... \end{mplibcode}|.
%    \begin{macrocode}
  local process_stack = 0
  function process (data, instancename)
    local currfmt
    process_stack = process_stack + 1
    if instancename and instancename ~= "" then
      currfmt = instancename
      has_instancename = true
    else
      currfmt = tableconcat{
        currentformat,
        luamplib.numbersystem or "scaled",
        tostring(luamplib.textextlabel),
        tostring(luamplib.legacyverbatimtex),
        tostring(process_stack), -- try to address #63
      }
      has_instancename = false
    end
    local mpx = mplibinstances[currfmt]
    local standalone = not (has_instancename or luamplib.codeinherit)
    if mpx and standalone then
      mpx:finish()
    end
    local log = ""
    if standalone or not mpx then
      mpx, _, log = luamplibload(currentformat)
      mplibinstances[currfmt] = mpx
    end
    local converted, result = false, {}
    if mpx and data then
      result = mpx:execute(data)
      local log = reporterror(result, log)
      if log then
        if result.fig then
          converted = luamplib.convert(result)
        end
      end
    else
      err"Mem file unloadable. Maybe generated with a different version of mplib?"
    end
    process_stack = process_stack - 1
    return converted, result
  end
end

%    \end{macrocode}
%
%    |dvipdfmx| is supported, though nobody seems to use it.
%    \begin{macrocode}
local pdfmode = tex.outputmode > 0

%    \end{macrocode}
%
%    |make_text| and some |run_script| uses \LuaTeX's |tex.runtoks|.
%    \begin{macrocode}
local catlatex = luatexbase.registernumber("catcodetable@latex")
local catat11  = luatexbase.registernumber("catcodetable@atletter")
%    \end{macrocode}
%    |tex.scantoks| sometimes fail to read catcode properly, especially
%    |\#|, |\&|, or |\%|. After some experiment, we dropped using it.
%    Instead, a function containing |tex.sprint| seems to work nicely.
%    \begin{macrocode}
local function run_tex_code (str, cat)
  texruntoks(function() texsprint(cat or catlatex, str) end)
end
%    \end{macrocode}
%    For conversion of |sp| to |bp|.
%    \begin{macrocode}
local factor = 65536*(7227/7200)

%    \end{macrocode}
%
%    Prepare textext box number containers, locals and globals.
%    |localid| can be any number. They are local anyway.
%    The number will be reset at the start of a new code chunk.
%    Global boxes will use |\newbox| command in |tex.runtoks| process.
%    This is the same when |codeinherit| is true.
%    Boxes in instances with name will also be global, so that
%    their tex boxes can be shared among instances of the same name.
%    \begin{macrocode}
local texboxes = { globalid = 0, localid = 4096 }
local process_tex_text
do
  local textext_fmt = 'image(addto currentpicture doublepath unitsquare \z
    xscaled %f yscaled %f shifted (0,-%f) \z
    withprescript "mplibtexboxid=%i:%f:%f")'
  function process_tex_text (str, maketext)
    if str then
      if not maketext then str = str:gsub("\r.-$","") end
      local global = (has_instancename or luamplib.globaltextext or luamplib.codeinherit)
                     and "\\global" or ""
      local tex_box_id
      if global == "" then
        tex_box_id = texboxes.localid + 1
        texboxes.localid = tex_box_id
      else
        local boxid = texboxes.globalid + 1
        texboxes.globalid = boxid
        run_tex_code(format([[\expandafter\newbox\csname luamplib.box.%s\endcsname]], boxid))
        tex_box_id = tex.getcount'allocationnumber'
      end
      if str:find"^%[taggingoff%]" then
        str = str:gsub("^%[taggingoff%]%s*","")
        run_tex_code(format("\\luamplibnotagtextboxset{%i}{%s\\setbox%i\\hbox{%s}}",
                            tex_box_id, global, tex_box_id, str))
      else
        run_tex_code(format("\\luamplibtagtextboxset{%i}{%s\\setbox%i\\hbox{%s}}",
                            tex_box_id, global, tex_box_id, str))
      end
      local box = texgetbox(tex_box_id)
      local wd  = box.width  / factor
      local ht  = box.height / factor
      local dp  = box.depth  / factor
      return textext_fmt:format(wd, ht+dp, dp, tex_box_id, wd, ht+dp)
    end
    return ""
  end
end

%    \end{macrocode}
%
%    Make |color| or |xcolor|'s color expressions usable,
%    with \cs{mpcolor} or |mplibcolor|. These commands should be used
%    with graphical objects.
%    Attempt to support l3color as well.
%    \begin{macrocode}
if is_defined'color_select:n' then
  run_tex_code{
    "\\newcatcodetable\\luamplibcctabexplat",
    "\\begingroup",
    "\\catcode`@=11 ",
    "\\catcode`_=11 ",
    "\\catcode`:=11 ",
    "\\savecatcodetable\\luamplibcctabexplat",
    "\\endgroup",
  }
end
local ccexplat = luatexbase.registernumber"luamplibcctabexplat"

local process_color, process_mplibcolor
%    \end{macrocode}
% A common function for color functions
%    \begin{macrocode}
local function is_xcolor (str)
  for _,v in ipairs(str:explode"!") do
    if not v:find("^%s*%d+%s*$") and is_defined("\\color@"..v) then -- priority to xcolor
      return true
    end
  end
  return false
end
local function colorsplit (res)
  local t, tt = { }, res:gsub("[%[%]]","",2):explode()
  local be = tt[1]:find"^%d" and 1 or 2
  for i=be, #tt do
    if not tonumber(tt[i]) then break end
    t[#t+1] = tt[i]
  end
  return t
end
do
  local colfmt = ccexplat and "l3color" or "xcolor"
  local mplibcolorfmt = {
    xcolor = [[{\setbox0\hbox{{\color%s\global\mplibtmptoks\expandafter{\current@color}}}}]],
    l3color = is_defined"__color_select:nn" and tableconcat{ -- to be cleaned up
      [[\begingroup\def\__color_select:N#1{\expandafter\__color_select:nn#1}]],
      [[\def\__color_backend_select:nn#1#2{\global\mplibtmptoks{#1 #2}}]],
      [[\def\__kernel_backend_literal:e#1{\global\mplibtmptoks\expandafter{\expanded{#1}}}]],
      [[\color_select:n%s\endgroup]],
    } or is_defined"__color_export_format_raw:nnN" and
      [[\color_export:nnN%s{raw}\l_tmpa_tl\mplibtmptoks\expandafter{\l_tmpa_tl}]]
    or [[{\setbox0\hbox{{\color_select:n%s\global\mplibtmptoks\expanded{{\current@color}}}}}]]
  }
  function process_color (str)
    if str then
      if not str:find("%b{}") then
        str = format("{%s}",str)
      end
      local myfmt = mplibcolorfmt[colfmt]
      if colfmt == "l3color" and is_defined"color" then
        if str:find("%b[]") or is_xcolor(str:match"{(.+)}") then
          myfmt = mplibcolorfmt.xcolor
        end
      end
      run_tex_code(myfmt:format(str), ccexplat or catat11)
      local t = texgettoks"mplibtmptoks"
      if not pdfmode then
        if t:find" cs " then -- spot color raw export
          local name = t:match("^/(.-) cs ")
          texsprint(ccexplat, {
            "\\pdfmanagement_add:nnn{Page/Resources/ColorSpace}{",
            name, "}{\\pdf_object_ref:n{", name, "}}"
          })
        elseif t:find"^hsb" then
          local spec = t:gsub("^hsb ",""):gsub(" ",",")
          run_tex_code{"\\convertcolorspec{hsb}{", spec, "}{rgb}\\mplibtmpa"}
          t = format("rgb %s", get_macro"mplibtmpa":gsub(","," "))
        elseif not t:find"%d" then -- named color
          run_tex_code{"\\extractcolorspecs{", t, "}\\mplibtmpa\\mplibtmpb"}
          t = format("%s %s", get_macro"mplibtmpa", get_macro"mplibtmpb":gsub(","," "))
        end
      elseif is_defined"ver@colorspace.sty" and t:find"^/&" then
        local a,b,c,d = t:match"^(.- cs) (.- CS) (.- scn?) (.- SCN?)$"
        if a and b and c and d then
          t = tableconcat({ a, c, b, d }, " ")
        end
      end
      return format('1 withprescript "mpliboverridecolor=%s"', t)
    end
    return ""
  end
  function process_mplibcolor(str)
    local res = process_color(str)
    if res:find" cs " or res:find"@pdf.obj" then return res end
    res = colorsplit(res:match'"mpliboverridecolor=(.+)"')
    return format("(%s)", tableconcat(res, ","))
  end
end

%    \end{macrocode}
%
%    for \cs{mpdim} or |mplibdimen|
%    \begin{macrocode}
local function process_dimen (str)
  if str then
    str = str:gsub("{(.+)}","%1")
    run_tex_code(format([[\mplibtmptoks\expandafter{\the\dimexpr %s\relax}]], str))
    return format("begingroup %s endgroup", texgettoks"mplibtmptoks")
  end
  return ""
end

%    \end{macrocode}
%
%    Newly introduced method of processing |verbatimtex| |...| |etex|.
%    This function is used when |\mpliblegacybehavior{false}| is declared.
%    \begin{macrocode}
local function process_verbatimtex_text (str)
  if str then
    run_tex_code(str)
  end
  return ""
end

%    \end{macrocode}
%
%    For legacy verbatimtex process.
%    |verbatimtex| |...| |etex| before |beginfig()|
%    is inserted just before the \mplib box. And
%    \TeX\ code inside |beginfig() ... endfig| is inserted after the \mplib box.
%    \begin{macrocode}
local tex_code_pre_mplib = {}
luamplib.figid = 1
luamplib.in_the_fig = false
local function process_verbatimtex_prefig (str)
  if str then
    tex_code_pre_mplib[luamplib.figid] = str
  end
  return ""
end
local function process_verbatimtex_infig (str)
  if str then
    return format('special "postmplibverbtex=%s";', str)
  end
  return ""
end

%    \end{macrocode}
%
%    For \emph{metafun} format. see issue \#79.
%    \begin{macrocode}
mp = mp or {}
local mp = mp
mp.mf_path_reset = mp.mf_path_reset or function() end
mp.mf_finish_saving_data = mp.mf_finish_saving_data or function() end
mp.report = mp.report or info
%    \end{macrocode}
%    \emph{metafun} 2021-03-09 changes crashes luamplib.
%    \begin{macrocode}
catcodes = catcodes or {}
local catcodes = catcodes
catcodes.numbers = catcodes.numbers or {}
catcodes.numbers.ctxcatcodes = catcodes.numbers.ctxcatcodes or catlatex
catcodes.numbers.texcatcodes = catcodes.numbers.texcatcodes or catlatex
catcodes.numbers.luacatcodes = catcodes.numbers.luacatcodes or catlatex
catcodes.numbers.notcatcodes = catcodes.numbers.notcatcodes or catlatex
catcodes.numbers.vrbcatcodes = catcodes.numbers.vrbcatcodes or catlatex
catcodes.numbers.prtcatcodes = catcodes.numbers.prtcatcodes or catlatex
catcodes.numbers.txtcatcodes = catcodes.numbers.txtcatcodes or catlatex

%    \end{macrocode}
%
%    Now |luamplib.runscript|
%    \begin{macrocode}
do
  local runscript_funcs = {
    luamplibtext    = process_tex_text,
    luamplibcolor   = process_mplibcolor,
    luamplibdimen   = process_dimen,
    luamplibprefig  = process_verbatimtex_prefig,
    luamplibinfig   = process_verbatimtex_infig,
    luamplibverbtex = process_verbatimtex_text,
  }
%    \end{macrocode}
%    A function from \ConTeXt\ general.
%    \begin{macrocode}
  local function mpprint(buffer,...)
    for i=1,select("#",...) do
      local value = select(i,...)
      if value ~= nil then
        local t = type(value)
        if t == "number" then
          buffer[#buffer+1] = format("%.16f",value)
        elseif t == "string" then
          buffer[#buffer+1] = value
        elseif t == "table" then
          buffer[#buffer+1] = "(" .. tableconcat(value,",") .. ")"
        else -- boolean or whatever
          buffer[#buffer+1] = tostring(value)
        end
      end
    end
  end
  function luamplib.runscript (code)
    local id, str = code:match("(.-){(.*)}")
    if id and str then
      local f = runscript_funcs[id]
      if f then
        local t = f(str)
        if t then return t end
      end
    end
    local f = loadstring(code)
    if type(f) == "function" then
      local buffer = {}
      function mp.print(...)
        mpprint(buffer,...)
      end
      local res = {f()}
      buffer = tableconcat(buffer)
      if buffer and buffer ~= "" then
        return buffer
      end
      buffer = {}
      mpprint(buffer, tableunpack(res))
      return tableconcat(buffer)
    end
    return ""
  end
end

%    \end{macrocode}
%
%    |luamplib.maketext|
%    \begin{macrocode}
luamplib.legacyverbatimtex = true
do
%    \end{macrocode}
%    |make_text| must be one liner, so comment sign is not allowed.
%    \begin{macrocode}
  local function protecttexcontents (str)
    return str:gsub("\\%%", "\0PerCent\0")
              :gsub("%%.-\n", "")
              :gsub("%%.-$",  "")
              :gsub("%zPerCent%z", "\\%%")
              :gsub("\r.-$",  "")
              :gsub("%s+", " ")
  end
  function luamplib.maketext (str, what)
    if str and str ~= "" then
      str = protecttexcontents(str)
      if what == 1 then
        if not str:find("\\documentclass"..name_e) and
           not str:find("\\begin%s*{document}") and
           not str:find("\\documentstyle"..name_e) and
           not str:find("\\usepackage"..name_e) then
          if luamplib.legacyverbatimtex then
            if luamplib.in_the_fig then
              return process_verbatimtex_infig(str)
            else
              return process_verbatimtex_prefig(str)
            end
          else
            return process_verbatimtex_text(str)
          end
        end
      else
        return process_tex_text(str, true) -- bool is for 'char13'
      end
    end
    return ""
  end
end

%    \end{macrocode}
%
%    luamplib's \metapost color operators
%    \begin{macrocode}
local function get_spc_name_obj (spot)
  if is_defined"spc@csall" then
    for v in get_macro"spc@csall":gmatch"{(.-)}" do
      local n, r = get_macro("spc@ir@"..v):match"^(.-) (%d+ 0 R)"
      if spot == n then
        return v, r
      end
    end
  else
    err"only l3color or colorspace is supported for spot color"
  end
end
do
  local function cmyk2rgb (t)
    return {
      1 - math.min(1, t[1]+t[4]),
      1 - math.min(1, t[2]+t[4]),
      1 - math.min(1, t[3]+t[4]),
    }
  end
  luamplib.gettexcolor = function (str, rgb)
    local res = process_color(str):match'"mpliboverridecolor=(.+)"'
    if res:find" cs " or res:find"@pdf.obj" then
      if not rgb then
        warn("%s is a spot color. Forced to CMYK", str)
      end
      if not is_xcolor(str) then
        run_tex_code({
          "\\color_export:nnN{",
          str,
          "}{",
          rgb and "space-sep-rgb" or "space-sep-cmyk",
          "}\\mplib@tempa",
        },ccexplat)
        return get_macro"mplib@tempa":explode()
      else
        local name, value = res:match"^(.-) cs (.-) sc"
        local t = get_macro("spc@ascmyk@"..get_spc_name_obj(name)):explode"," -- mixed not work
        for i = 1, #t do
          t[i] = t[i] * value
        end
        return rgb and cmyk2rgb(t) or t
      end
    end
    local t = colorsplit(res)
    if #t == 3 or not rgb then return t end
    if #t == 4 then return cmyk2rgb(t) end
    return { t[1], t[1], t[1] }
  end
end
luamplib.shadecolor = function (str)
  local res = process_color(str):match'"mpliboverridecolor=(.+)"'
  if res:find" cs " or res:find"@pdf.obj" then -- spot color shade
%    \end{macrocode}
%   An example of spot color shading:
%\begin{verbatim}
%    \DocumentMetadata{ }
%    \documentclass{article}
%    \usepackage{luamplib}
%    \ExplSyntaxOn
%    \color_model_new:nnn { pantone3005 }
%      { Separation }
%      {
%        name = PANTONE~3005~U ,
%        alternative-model = cmyk ,
%        alternative-values = {1, 0.56, 0, 0}
%      }
%      \color_set:nnn{spotA}{pantone3005}{1}
%      \color_set:nnn{spotB}{pantone3005}{0.6}
%    \color_model_new:nnn { pantone1215 }
%      { Separation }
%      {
%        name = PANTONE~1215~U ,
%        alternative-model = cmyk ,
%        alternative-values = {0, 0.15, 0.51, 0}
%      }
%      \color_set:nnn{spotC}{pantone1215}{1}
%    \ExplSyntaxOff
%    \begin{document}
%    \begin{mplibcode}
%    beginfig(1)
%      fill unitsquare xscaled \mpdim\textwidth yscaled 1cm
%        withshadingmethod "linear"
%        withshadingvector (0,1)
%        withshadingstep (
%          withshadingfraction .5
%          withshadingcolors ("spotB","spotC")
%        )
%        withshadingstep (
%          withshadingfraction 1
%          withshadingcolors ("spotC","magenta!50")
%        )
%        ;
%    endfig;
%    \end{mplibcode}
%    \end{document}
%\end{verbatim}
%   another one: user-defined DeviceN colorspace
%\begin{verbatim}
%    \DocumentMetadata{ }
%    \documentclass{article}
%    \usepackage{luamplib}
%    \ExplSyntaxOn
%    \color_model_new:nnn { pantone1215 }
%      { Separation }
%      {
%        name = PANTONE~1215~U ,
%        alternative-model = cmyk ,
%        alternative-values = {0, 0.15, 0.51, 0}
%      }
%    \color_model_new:nnn { pantone+black }
%      { DeviceN }
%      { names = {pantone1215,black} }
%    \color_set:nnn{purepantone}{pantone+black}{1,0}
%    \color_set:nnn{pureblack}  {pantone+black}{0,1}
%    \ExplSyntaxOff
%    \begin{document}
%    \mpfig
%      fill unitsquare xscaled \mpdim{\textwidth} yscaled 30
%        withshadingmethod "linear"
%        withshadingcolors ("purepantone","pureblack")
%        ;
%    \endmpfig
%    \end{document}
%\end{verbatim}
%    \begin{macrocode}
    local name, value, obj
    if not is_xcolor(str) then
      run_tex_code({ "\\color_export:nnN{", str, "}{backend}\\mplib@tempa" }, ccexplat)
      name, value = get_macro'mplib@tempa':match'{(.-)}{(.-)}'
      local t = res:explode()
      local refname = t[1]:sub(2,-1)
      if pdfmode then
        obj = format("%s 0 R", ltx.pdf.object_id(refname))
      elseif t[2] == "cs" then -- raw export
        run_tex_code({ "\\mplibtmptoks\\expanded{{\\pdf_object_ref:n{", refname, "}}}" }, ccexplat)
        obj = texgettoks"mplibtmptoks"
      else -- legacy: to be removed
        obj = t[2]
      end
    else -- colorspace: mixing is allowed only in preamble
      name, value = res:match"^(.-) cs (.-) sc"
      name, obj = get_spc_name_obj(name)
    end
    return format('(1) withprescript"mplib_spotcolor=%s:%s:%s"', value,obj,name)
  end
  return format('(%s) withprescript"mplib_texcolor=%s"', tableconcat(colorsplit(res),","), str)
end

%    \end{macrocode}
% |luamplib.fillandstrokecolor|
%    \begin{macrocode}
do
  local function graphictextcolor (col, filldraw)
    if col:find"^[%d%.:]+$" then
      col = col:explode":"
      for i=1,#col do
        col[i] = format("%.3f", col[i])
      end
      if pdfmode then
        local op = #col == 4 and "k" or #col == 3 and "rg" or "g"
        col[#col+1] = filldraw == "fill" and op or op:upper()
        return tableconcat(col," ")
      end
      return format("[%s]", tableconcat(col," "))
    end
    col = process_color(col):match'"mpliboverridecolor=(.+)"'
    if pdfmode or col:find" cs " then
      local t = col:explode()
      local b = filldraw == "fill" and 1 or #t/2+1
      local e = b == 1 and #t/2 or #t
      return tableconcat(t," ", b, e)
    end
    if col:find"@pdf.obj" then
      return col:gsub("pdf:bc%s*","",1)
    else
      return format("[%s]", tableconcat(colorsplit(col)," "))
    end
  end
  function luamplib.fillandstrokecolor (fill, stroke)
    fill   = graphictextcolor(fill, "fill")
    stroke = graphictextcolor(stroke, "stroke")
    local bc = (pdfmode or fill:find" cs ") and "" or "pdf:bc "
    return format('withprescript "mpliboverridecolor=%s%s %s"', bc, fill, stroke)
  end
end

%    \end{macrocode}
%
% Remove trailing zeros for smaller PDF
%    \begin{macrocode}
local decimals = "%.%d+"
local function rmzeros(str) return str:gsub("%.?0+$","") end

%    \end{macrocode}
%
%    common function for mplibgraphictext and mpliboutlinetext
%    \begin{macrocode}
local function getrulemetric (box, curr, bp)
  local running = -1073741824
  local wd,ht,dp = curr.width, curr.height, curr.depth
  wd = wd == running and box.width  or wd
  ht = ht == running and box.height or ht
  dp = dp == running and box.depth  or dp
  if bp then
    return wd/factor, ht/factor, dp/factor
  end
  return wd, ht, dp
end

%    \end{macrocode}
%
%    luamplib's mplibgraphictext operator
%    \begin{macrocode}
do
  if not math.round then
    function math.round(x) return x < 0 and -math.floor(-x + 0.5) or math.floor(x + 0.5) end
  end
  local emboldenfonts = { }
  local function roundupwidth (f, fb)
    local wd = math.round(f.size * fb / factor * 10)
    if wd == 0 and fb ~= 0 then
      wd = 1
    end
    emboldenfonts.width = wd
    return wd
  end
  local function getemboldenwidth (curr, fakebold)
    local width = emboldenfonts.width
    if not width then
      local f
      local function getglyph(n)
        while n do
          if n.head then
            getglyph(n.head)
          elseif n.font and n.font > 0 then
            f = n.font; break
          end
          n = node.getnext(n)
        end
      end
      getglyph(curr)
      width = roundupwidth(font.getcopy(f or font.current()), fakebold)
    end
    return width
  end
  local function getrulewhatsit (line, wd, ht, dp)
    line, wd, ht, dp = line/1000, wd/factor, ht/factor, dp/factor
    line = line == 0 and "" or ("%f w"):format(line)
    local pl
    local fmt = "q %s %f %f %f %f re B Q"
    if pdfmode then
      pl = node.new("whatsit","pdf_literal")
      pl.mode = 0
    else
      fmt = "pdf:content "..fmt
      pl = node.new("whatsit","special")
    end
    pl.data = fmt:format(line, 0, -dp, wd, ht+dp) :gsub(decimals,rmzeros)
    local ss = node.new"glue"
    node.setglue(ss, 0, 65536, 65536, 2, 2)
    pl.next = ss
    return pl
  end
%    \end{macrocode}
% copying attributes of rule/glue node to improve tagging of mplibgraphictext
%    \begin{macrocode}
  local tag_update_attrs
  if is_defined"ver@tagpdf.sty" then
    tag_update_attrs = function (n, curr)
      while n do
        n.attr = curr.attr
        if n.head then
          tag_update_attrs(n.head, curr)
        end
        n = node.getnext(n)
      end
    end
  else
    tag_update_attrs = function() end
  end
  local function embolden (box, curr, fakebold)
    local head = curr
    while curr do
      if curr.head then
        curr.head = embolden(curr, curr.head, fakebold)
      elseif curr.replace then
        curr.replace = embolden(box, curr.replace, fakebold)
      elseif curr.leader then
        if curr.leader.head then
          curr.leader.head = embolden(curr.leader, curr.leader.head, fakebold)
        elseif curr.leader.id == node.id"rule" then
          local glue = node.effective_glue(curr, box)
          local line = getemboldenwidth(curr, fakebold)
          local wd,ht,dp = getrulemetric(box, curr.leader)
          if box.id == node.id"hlist" then
            wd = glue
          else
            ht, dp = 0, glue
          end
          local pl = getrulewhatsit(line, wd, ht, dp)
          local pack = box.id == node.id"hlist" and node.hpack or node.vpack
          local list = pack(pl, glue, "exactly")
          tag_update_attrs(list,curr)
          head = node.insert_after(head, curr, list)
          head, curr = node.remove(head, curr)
        end
      elseif curr.id == node.id"rule" and curr.subtype == 0 then
        local line = getemboldenwidth(curr, fakebold)
        local wd,ht,dp = getrulemetric(box, curr)
        if box.id == node.id"vlist" then
          ht, dp = 0, ht+dp
        end
        local pl = getrulewhatsit(line, wd, ht, dp)
        local list
        if box.id == node.id"hlist" then
          list = node.hpack(pl, wd, "exactly")
        else
          list = node.vpack(pl, ht+dp, "exactly")
        end
        tag_update_attrs(list,curr)
        head = node.insert_after(head, curr, list)
        head, curr = node.remove(head, curr)
      elseif curr.id == node.id"glyph" and curr.font > 0 then
        local f = curr.font
        local key = format("%s:%s",f,fakebold)
        local i = emboldenfonts[key]
        if not i then
          local ft = font.getfont(f) or font.getcopy(f)
          local width = roundupwidth(ft, fakebold)
          if ft.format == "opentype" or ft.format == "truetype" then
            local name = ft.name:gsub('"',''):gsub(';$','')
            local t = name:gsub("^file:",""):gsub("^name:",""):gsub("^kpse:",""):gsub("^my:","")
            name = format('%s%sembolden=%s;',name, t:find":" and ";" or ":", fakebold)
            _, i = fonts.constructors.readanddefine(name,ft.size)
          elseif pdfmode then
            local ft = table.copy(ft)
            ft.mode, ft.width = 2, width
            i = font.define(ft)
          else
            goto skip_type1
          end
          emboldenfonts[key] = i
        end
        curr.font = i
      end
      ::skip_type1::
      curr = node.getnext(curr)
    end
    return head
  end
  luamplib.graphictext = function (text, fakebold, fc, dc)
    local fmt = process_tex_text(text):sub(1,-2)
    local id = tonumber(fmt:match"mplibtexboxid=(%d+):")
    emboldenfonts.width = nil
    local box = texgetbox(id)
    box.head = embolden(box, box.head, fakebold)
    local colors = luamplib.fillandstrokecolor(fc, dc)
    return format('%s %s)', fmt, colors)
  end
end

%    \end{macrocode}
%
%    luamplib's mplibglyph operator
%    \begin{macrocode}
do
  local function mperr (str)
    return format("hide(errmessage %q)", str)
  end
  local function getangle (a,b,c)
    local r = math.deg(math.atan(c.y-b.y, c.x-b.x) - math.atan(b.y-a.y, b.x-a.x))
    if r > 180 then
      r = r - 360
    elseif r < -180 then
      r = r + 360
    end
    return r
  end
  local function turning (t)
    local r, n = 0, #t
    for i=1,2 do
      tableinsert(t, t[i])
    end
    for i=1,n do
      r = r + getangle(t[i], t[i+1], t[i+2])
    end
    return r/360
  end
  local function glyphimage(t, fmt)
    local q, p, r, towarn = {{},{}}
    local function closepath(dots)
      tableinsert(p, format("%scycle", dots or "--"))
      tableinsert(q[ turning(r) > 0 and 1 or 2 ], tableconcat(p))
    end
    for i,v in ipairs(t) do
      local cmd = v[#v]
      local nt = t[i+1]
      local final = not nt or nt[#nt] ~= "l" and nt[#nt] ~= "c"
      if cmd == "m" then
        if final then towarn = true end
        p = {format('(%s,%s)',v[1],v[2])}
        r = {{x=v[1],y=v[2]}}
      else
        if cmd == "l" then
          local pt = t[i-1]
          if (final or pt and pt[#pt] == "m") and r[1].x == v[1] and r[1].y == v[2] then
          else
            tableinsert(p, format('--(%s,%s)',v[1],v[2]))
            tableinsert(r, {x=v[1],y=v[2]})
          end
          if final then closepath() end
        elseif cmd == "c" then
          tableinsert(p, format('..controls(%s,%s)and(%s,%s)',v[1],v[2],v[3],v[4]))
          if final and r[1].x == v[5] and r[1].y == v[6] then
            closepath ".."
          else
            tableinsert(p, format('..(%s,%s)',v[5],v[6]))
            tableinsert(r, {x=v[5],y=v[6]})
            if final then closepath() end
          end
        elseif cmd == "path" or cmd == "move" then
        else
          return mperr"unknown operator"
        end
      end
    end
    r = { }
    if fmt == "opentype" then
      for _,v in ipairs(q[1]) do
        tableinsert(r, format('addto currentpicture contour %s;',v))
      end
      for _,v in ipairs(q[2]) do
        tableinsert(r, format('addto currentpicture contour %s withcolor background;',v))
      end
    else
      for _,v in ipairs(q[2]) do
        tableinsert(r, format('addto currentpicture contour %s;',v))
      end
      for _,v in ipairs(q[1]) do
        tableinsert(r, format('addto currentpicture contour %s withcolor background;',v))
      end
    end
    return format('image(%s)', tableconcat(r)), towarn
  end
  if not table.tofile then require"lualibs-lpeg"; require"lualibs-table"; end
  function luamplib.glyph (f, c)
    local filename, subfont, instance, kind, shapedata
    local fid = tonumber(f) or font.id(f)
    if fid > 0 then
      local fontdata = font.getfont(fid) or font.getcopy(fid)
      filename, subfont, kind = fontdata.filename, fontdata.subfont, fontdata.format
      instance = fontdata.specification and fontdata.specification.instance
        or fontdata.shared and fontdata.shared.features.axis
      filename = filename and filename:gsub("^harfloaded:","")
    else
      local name
      f = f:match"^%s*(.+)%s*$"
      name, subfont, instance = f:match"(.+)%((%d+)%)%[(.-)%]$"
      if not name then
        name, instance = f:match"(.+)%[(.-)%]$" -- SourceHanSansK-VF.otf[Heavy]
      end
      if not name then
        name, subfont = f:match"(.+)%((%d+)%)$" -- Times.ttc(2)
      end
      name = name or f
      subfont = (subfont or 0)+1
      instance = instance and instance:lower()
      for _,ftype in ipairs{"opentype", "truetype"} do
        filename = kpse.find_file(name, ftype.." fonts")
        if filename then
          kind = ftype; break
        end
      end
    end
    if kind ~= "opentype" and kind ~= "truetype" then
      f = fid and fid > 0 and tex.fontname(fid) or f
      if kpse.find_file(f, "tfm") then
        return format("glyph %s of %q", tonumber(c) or format("%q",c), f)
      else
        filename = kpse.find_file(f, "type1 fonts")
        if filename then
          kind = "type1" -- there's bug in processing cmr family
        else
          return mperr"font not found"
        end
      end
    end
    local time = lfsattributes(filename,"modification")
%    \end{macrocode}
%\begin{verbatim}
%   local k = format("shapes_%s(%s)[%s]%s", filename, subfont or "", instance or "",
%                     luaotfload and luaotfload.version or "")
%\end{verbatim}
%  ^^X replace next line with prev one in 2027.
%    \begin{macrocode}
    local k = format("shapes_%s(%s)[%s]", filename, subfont or "", instance or "")
    local h = format(string.rep('%02x', 256/8), string.byte(sha2.digest256(k), 1, -1))
    local newname = format("%s/%s.lua", cachedir or outputdir(), h)
    local newtime = lfsattributes(newname,"modification") or 0
    if time == newtime then
      shapedata = require(newname)
    end
    if not shapedata then
      if fonts then
        local handler = kind == "type1" and fonts.handlers.afm or fonts.handlers.otf
        shapedata = handler.readers.loadshapes(filename,subfont,instance)
      end
      if not shapedata then return mperr"loadshapes() failed. luaotfload not loaded?" end
      table.tofile(newname, shapedata, "return")
      lfstouch(newname, time, time)
    end
    local gid = tonumber(c)
    if not gid then
      local uni = utf8.codepoint(c)
      for i,v in pairs(shapedata.glyphs) do
        if c == v.name or uni == v.unicode then
          gid = i; break
        end
      end
    end
    if not gid then return mperr"cannot get GID (glyph id)" end
    local fac = 1000 / (shapedata.units or 1000)
    local t = shapedata.glyphs[gid]; t = t and t.segments
    if not t then return "image()" end
    for i,v in ipairs(t) do
      if type(v) == "table" then
        for ii,vv in ipairs(v) do
          if type(vv) == "number" then
            t[i][ii] = format("%.0f", vv * fac)
          end
        end
      end
    end
    local result, towarn = glyphimage(t, shapedata.format or kind)
    if towarn then
      warn("mplibglyph %s not working properly. Use glyph instead", f)
    end
    return result
  end
end

%    \end{macrocode}
%
%    mpliboutlinetext : based on mkiv's font-mps.lua
%    \begin{macrocode}
do
  local rulefmt = "mpliboutlinepic[%i]:=image(addto currentpicture contour \z
    unitsquare shifted - center unitsquare;) xscaled %f yscaled %f shifted (%f,%f);"
  local outline_horz, outline_vert
  function outline_vert (res, box, curr, xshift, yshift)
    local b2u = box.dir == "LTL"
    local dy = (b2u and -box.depth or box.height)/factor
    local ody = dy
    while curr do
      if curr.id == node.id"rule" then
        local wd, ht, dp = getrulemetric(box, curr, true)
        local hd = ht + dp
        if hd ~= 0 then
          dy = dy + (b2u and dp or -ht)
          if wd ~= 0 and curr.subtype == 0 then
            res[#res+1] = rulefmt:format(#res+1, wd, hd, xshift+wd/2, yshift+dy+(ht-dp)/2)
          end
          dy = dy + (b2u and ht or -dp)
        end
      elseif curr.id == node.id"glue" then
        local vwidth = node.effective_glue(curr,box)/factor
        if curr.leader then
          local curr, kind = curr.leader, curr.subtype
          if curr.id == node.id"rule" then
            local wd = getrulemetric(box, curr, true)
            if wd ~= 0 then
              local hd = vwidth
              local dy = dy + (b2u and 0 or -hd)
              if hd ~= 0 and curr.subtype == 0 then
                res[#res+1] = rulefmt:format(#res+1, wd, hd, xshift+wd/2, yshift+dy+hd/2)
              end
            end
          elseif curr.head then
            local hd = (curr.height + curr.depth)/factor
            if hd <= vwidth then
              local dy, n, iy = dy, 0, 0
              if kind == 100 or kind == 103 then -- todo: gleaders
                local ady = abs(ody - dy)
                local ndy = math.ceil(ady / hd) * hd
                local diff = ndy - ady
                n = math.floor((vwidth-diff) / hd)
                dy = dy + (b2u and diff or -diff)
              else
                n = math.floor(vwidth / hd)
                if kind == 101 then
                  local side = vwidth % hd / 2
                  dy = dy + (b2u and side or -side)
                elseif kind == 102 then
                  iy = vwidth % hd / (n+1)
                  dy = dy + (b2u and iy or -iy)
                end
              end
              dy = dy + (b2u and curr.depth or -curr.height)/factor
              hd = b2u and hd or -hd
              iy = b2u and iy or -iy
              local func = curr.id == node.id"hlist" and outline_horz or outline_vert
              for i=1,n do
                res = func(res, curr, curr.head, xshift+curr.shift/factor, yshift+dy)
                dy = dy + hd + iy
              end
            end
          end
        end
        dy = dy + (b2u and vwidth or -vwidth)
      elseif curr.id == node.id"kern" then
        dy = dy + curr.kern/factor * (b2u and 1 or -1)
      elseif curr.id == node.id"vlist" then
        dy = dy + (b2u and curr.depth or -curr.height)/factor
        res = outline_vert(res, curr, curr.head, xshift+curr.shift/factor, yshift+dy)
        dy = dy + (b2u and curr.height or -curr.depth)/factor
      elseif curr.id == node.id"hlist" then
        dy = dy + (b2u and curr.depth or -curr.height)/factor
        res = outline_horz(res, curr, curr.head, xshift+curr.shift/factor, yshift+dy)
        dy = dy + (b2u and curr.height or -curr.depth)/factor
      end
      curr = node.getnext(curr)
    end
    return res
  end
  function outline_horz (res, box, curr, xshift, yshift, discwd)
    local r2l = box.dir == "TRT"
    local dx = r2l and (discwd or box.width/factor) or 0
    local dirs = { { dir = r2l, dx = dx } }
    while curr do
      if curr.id == node.id"dir" then
        local sign, dir = curr.dir:match"(.)(...)"
        local level, newdir = curr.level, r2l
        if sign == "+" then
          newdir = dir == "TRT"
          if r2l ~= newdir then
            local n = node.getnext(curr)
            while n do
              if n.id == node.id"dir" and n.level+1 == level then break end
              n = node.getnext(n)
            end
            n = n or node.tail(curr)
            dx = dx + node.rangedimensions(box, curr, n)/factor * (newdir and 1 or -1)
          end
          dirs[level] = { dir = r2l, dx = dx }
        else
          local level = level + 1
          newdir = dirs[level].dir
          if r2l ~= newdir then
            dx = dirs[level].dx
          end
        end
        r2l = newdir
      elseif curr.char and curr.font and curr.font > 0 then
        local ft = font.getfont(curr.font) or font.getcopy(curr.font)
        local gid = ft.characters[curr.char].index or curr.char
        local scale = ft.size / factor / 1000
        local slant   = (ft.slant or 0)/1000
        local extend  = (ft.extend or 1000)/1000
        local squeeze = (ft.squeeze or 1000)/1000
        local expand  = 1 + (curr.expansion_factor or 0)/1000000
        local xscale, yscale = scale * extend * expand, scale * squeeze
        dx = dx - (r2l and curr.width/factor*expand or 0)
        local xoff, yoff = (curr.xoffset or 0)/factor, (curr.yoffset or 0)/factor
        local xpos, ypos = dx + xshift + xoff, yshift + yoff
        local vertical = ""
        if ft.shared and (ft.shared.features.vert or ft.shared.features.vrt2) then
          if ft.shared.features.vertical then -- luatexko
            vertical = "rotated 90"
            local data = ft.characters[curr.char] or { }
            if ft.hb then
              local hoff, voff = (data.luatexko_hoff or 0)/factor, (data.luatexko_voff or 0)/factor
              local charraise = (ft.luatexko_charraise or 0)/factor
              xpos, ypos = xpos - voff + hoff - charraise, ypos + hoff + voff + charraise
            else
              local cmds = data.commands or { {0,0}, {0,0} }
              local voff, hoff = -cmds[1][2]/factor, cmds[2][2]/factor
              xpos, ypos = xpos + hoff, ypos + voff
            end
          elseif curr ~= box.head then -- luatexja
            vertical = "rotated 90"
            local en = ft.parameters.quad/factor/2
            xpos, ypos = xpos - xoff - yoff + en, ypos - yoff + xoff - en
          end
        end
        local image
        if ft.format == "opentype" or ft.format == "truetype" then
          image = luamplib.glyph(curr.font, gid)
        else
          local name, scale = ft.name, 1
          local vf = font.read_vf(name, ft.size)
          if vf and vf.characters[gid] then
            local cmds = vf.characters[gid].commands or {}
            for _,v in ipairs(cmds) do
              if v[1] == "char" then
                gid = v[2]
              elseif v[1] == "font" and vf.fonts[v[2]] then
                name  = vf.fonts[v[2]].name
                scale = vf.fonts[v[2]].size / ft.size
              end
            end
          end
          image = format("glyph %s of %q scaled %f", gid, name, scale)
        end
        res[#res+1] = format("mpliboutlinepic[%i]:=%s xscaled %f yscaled %f slanted %f %s shifted (%f,%f);",
                             #res+1, image, xscale, yscale, slant, vertical, xpos, ypos)
        dx = dx + (r2l and 0 or curr.width/factor*expand)
      elseif curr.replace then
        local width = node.dimensions(curr.replace)/factor
        dx = dx - (r2l and width or 0)
        res = outline_horz(res, box, curr.replace, xshift+dx, yshift, width)
        dx = dx + (r2l and 0 or width)
      elseif curr.id == node.id"rule" then
        local wd, ht, dp = getrulemetric(box, curr, true)
        if wd ~= 0 then
          local hd = ht + dp
          dx = dx - (r2l and wd or 0)
          if hd ~= 0 and curr.subtype == 0 then
            res[#res+1] = rulefmt:format(#res+1, wd, hd, xshift+dx+wd/2, yshift+(ht-dp)/2)
          end
          dx = dx + (r2l and 0 or wd)
        end
      elseif curr.id == node.id"glue" then
        local width = node.effective_glue(curr, box)/factor
        dx = dx - (r2l and width or 0)
        if curr.leader then
          local curr, kind = curr.leader, curr.subtype
          if curr.id == node.id"rule" then
            local wd, ht, dp = getrulemetric(box, curr, true)
            local hd = ht + dp
            if hd ~= 0 then
              wd = width
              if wd ~= 0 and curr.subtype == 0 then
                res[#res+1] = rulefmt:format(#res+1, wd, hd, xshift+dx+wd/2, yshift+(ht-dp)/2)
              end
            end
          elseif curr.head then
            local wd = curr.width/factor
            if wd <= width then
              local dx = r2l and dx+width or dx
              local n, ix = 0, 0
              if kind == 100 or kind == 103 then -- todo: gleaders
                local adx = abs(dx-dirs[1].dx)
                local ndx = math.ceil(adx / wd) * wd
                local diff = ndx - adx
                n = math.floor((width-diff) / wd)
                dx = dx + (r2l and -diff-wd or diff)
              else
                n = math.floor(width / wd)
                if kind == 101 then
                  local side = width % wd /2
                  dx = dx + (r2l and -side-wd or side)
                elseif kind == 102 then
                  ix = width % wd / (n+1)
                  dx = dx + (r2l and -ix-wd or ix)
                end
              end
              wd = r2l and -wd or wd
              ix = r2l and -ix or ix
              local func = curr.id == node.id"hlist" and outline_horz or outline_vert
              for i=1,n do
                res = func(res, curr, curr.head, xshift+dx, yshift-curr.shift/factor)
                dx = dx + wd + ix
              end
            end
          end
        end
        dx = dx + (r2l and 0 or width)
      elseif curr.id == node.id"kern" then
        dx = dx + curr.kern/factor * (r2l and -1 or 1)
      elseif curr.id == node.id"math" then
        dx = dx + curr.surround/factor * (r2l and -1 or 1)
      elseif curr.id == node.id"vlist" then
        dx = dx - (r2l and curr.width/factor or 0)
        res = outline_vert(res, curr, curr.head, xshift+dx, yshift-curr.shift/factor)
        dx = dx + (r2l and 0 or curr.width/factor)
      elseif curr.id == node.id"hlist" then
        dx = dx - (r2l and curr.width/factor or 0)
        res = outline_horz(res, curr, curr.head, xshift+dx, yshift-curr.shift/factor)
        dx = dx + (r2l and 0 or curr.width/factor)
      end
      curr = node.getnext(curr)
    end
    return res
  end
  function luamplib.outlinetext (text)
    local fmt = process_tex_text(text)
    local id  = tonumber(fmt:match"mplibtexboxid=(%d+):")
    local box = texgetbox(id)
    local res = outline_horz({ }, box, box.head, 0, 0)
    if #res == 0 then res = { "mpliboutlinepic[1]:=image();" } end
    return tableconcat(res) .. format("mpliboutlinenum:=%i;", #res)
  end
end

%    \end{macrocode}
%
%    lua functions for |mplib(uc)substring ... of ...|
%    \begin{macrocode}
function luamplib.getunicodegraphemes (s)
  local t = { }
  local graphemes = require'lua-uni-graphemes'
  for _, _, c in graphemes.graphemes(s) do
    table.insert(t, c)
  end
  return t
end
function luamplib.unicodesubstring (s,b,e,grph)
  local tt, t, step = { }
  if grph then
    t = luamplib.getunicodegraphemes(s)
  else
    t = { }
    for _, c in utf8.codes(s) do
      table.insert(t, utf8.char(c))
    end
  end
  if b <= e then
    b, step = b+1, 1
  else
    e, step = e+1, -1
  end
  for i = b, e, step do
    table.insert(tt, t[i])
  end
  s = table.concat(tt):gsub('"','"&ditto&"')
  return string.format('"%s"', s)
end

%    \end{macrocode}
%
%    \metapost preambles
%    \begin{macrocode}
luamplib.preambles = {
  preamble = [[
boolean mplib ; mplib := true ;
let dump = endinput ;
let normalfontsize = fontsize;
input %s ;
]],
  mplibcode = [[
texscriptmode := 2;
def rawtextext primary t = runscript("luamplibtext{"&t&"}") enddef;
def mplibcolor primary t = runscript("luamplibcolor{"&t&"}") enddef;
def mplibdimen primary t = runscript("luamplibdimen{"&t&"}") enddef;
def VerbatimTeX primary t = runscript("luamplibverbtex{"&t&"}") enddef;
if known context_mlib:
  defaultfont := "cmtt10";
  let infont = normalinfont;
  let fontsize = normalfontsize;
  vardef thelabel@#(expr p,z) =
    if string p :
      thelabel@#(p infont defaultfont scaled defaultscale,z)
    else :
      p shifted (z + labeloffset*mfun_laboff@# -
        (mfun_labxf@#*lrcorner p + mfun_labyf@#*ulcorner p +
        (1-mfun_labxf@#-mfun_labyf@#)*llcorner p))
    fi
  enddef;
else:
  vardef textext@# primary t = rawtextext (t) enddef;
  def message expr t =
    if string t: runscript("mp.report[=["&t&"]=]") else: errmessage "Not a string" fi
  enddef;
  def withtransparency (expr a, t) =
    withprescript "tr_alternative=" & if numeric a: decimal fi a
    withprescript "tr_transparency=" & decimal t
  enddef;
  vardef ddecimal primary p =
    decimal xpart p & " " & decimal ypart p
  enddef;
  vardef boundingbox primary p =
    if (path p) or (picture p) :
      llcorner p -- lrcorner p -- urcorner p -- ulcorner p
    else :
      origin
    fi -- cycle
  enddef;
fi
def resolvedcolor(expr s) =
  runscript("return luamplib.shadecolor('"& s &"')")
enddef;
def colordecimals primary c =
  if cmykcolor c:
    decimal cyanpart c & ":" & decimal magentapart c & ":" &
    decimal yellowpart c & ":" & decimal blackpart c
  elseif rgbcolor c:
    decimal redpart c & ":" & decimal greenpart c & ":" & decimal bluepart c
  elseif string c:
    if known graphictextpic: c else: colordecimals resolvedcolor(c) fi
  else:
    decimal c
  fi
enddef;
def externalfigure primary filename =
  draw rawtextext("\includegraphics{"& filename &"}")
enddef;
def TEX = textext enddef;
def mplibtexcolor primary c =
  runscript("return luamplib.gettexcolor('"& c &"')")
enddef;
def mplibrgbtexcolor primary c =
  runscript("return luamplib.gettexcolor('"& c &"','rgb')")
enddef;
def mplibgraphictext primary t =
  begingroup;
  mplibgraphictext_ (t)
enddef;
def mplibgraphictext_ (expr t) text rest =
  save fakebold, scale, fillcolor, drawcolor, withfillcolor, withdrawcolor, strokecolor,
    fb, fc, dc, graphictextpic, alsoordoublepath;
  picture graphictextpic; graphictextpic := nullpicture;
  numeric fb; string fc, dc; fb:=2; fc:="white"; dc:="black";
  let scale = scaled;
  def fakebold  primary c = hide(fb:=c;) enddef;
  def fillcolor primary c = hide(fc:=colordecimals c;) enddef;
  def drawcolor primary c = hide(dc:=colordecimals c;) enddef;
  let withfillcolor = fillcolor; let withdrawcolor = drawcolor; let strokecolor = drawcolor;
  def alsoordoublepath expr p = if picture p: also else: doublepath fi p enddef;
  addto graphictextpic alsoordoublepath (origin--cycle) rest; graphictextpic:=nullpicture;
  def fakebold  primary c = enddef;
  let fillcolor = fakebold; let drawcolor = fakebold;
  let withfillcolor = fillcolor; let withdrawcolor = drawcolor; let strokecolor = drawcolor;
  image(draw runscript("return luamplib.graphictext([===["&t&"]===],"
    & decimal fb &",'"& fc &"','"& dc &"')") rest;)
  endgroup;
enddef;
def mplibglyph expr c of f =
  runscript (
    "return luamplib.glyph('"
    & if numeric f: decimal fi f
    & "','"
    & if numeric c: decimal fi c
    & "')"
  )
enddef;
numeric luamplib_tmp_num_; luamplib_tmp_num_ = 0;
def mplibdrawglyph expr g =
  luamplib_tmp_num_ := 0;
  for item within g:
    fill pathpart item
    if incr luamplib_tmp_num_ < length g: withpostscript "collect"; fi
  endfor
enddef;
let mplibfillglyph = mplibdrawglyph;
def mplibstrokeglyph expr g =
  luamplib_tmp_num_ := 0;
  for item within g:
    draw pathpart item
    if incr luamplib_tmp_num_ < length g: withpostscript "collect"; fi
  endfor
enddef;
def mplibfillandstrokeglyph expr g =
  luamplib_tmp_num_ := 0;
  for item within g:
    draw pathpart item withpostscript
    if incr luamplib_tmp_num_ < length g: "collect"; else: "both" fi
  endfor
enddef;
def withmplibcolors (expr f, s) =
  runscript("return luamplib.fillandstrokecolor('" &
    if not string f: colordecimals fi f & "','" &
    if not string s: colordecimals fi s & "')")
enddef;
def withmplibopacities (expr a, f, s) =
  withprescript "tr_alternative=" & if numeric a: decimal fi a
  withprescript "tr_transparency=" & decimal f & ":" & decimal s
enddef;
def mplib_do_outline_text_set_b (text f) (text d) text r =
  def mplib_do_outline_options_f = f enddef;
  def mplib_do_outline_options_d = d enddef;
  def mplib_do_outline_options_r = r enddef;
enddef;
def mplib_do_outline_text_set_f (text f) text r =
  def mplib_do_outline_options_f = f enddef;
  def mplib_do_outline_options_r = r enddef;
enddef;
def mplib_do_outline_text_set_u (text f) text r =
  def mplib_do_outline_options_f = f enddef;
enddef;
def mplib_do_outline_text_set_d (text d) text r =
  def mplib_do_outline_options_d = d enddef;
  def mplib_do_outline_options_r = r enddef;
enddef;
def mplib_do_outline_text_set_r (text d) (text f) text r =
  def mplib_do_outline_options_d = d enddef;
  def mplib_do_outline_options_f = f enddef;
  def mplib_do_outline_options_r = r enddef;
enddef;
def mplib_do_outline_text_set_n text r =
  def mplib_do_outline_options_r = r enddef;
enddef;
def mplib_do_outline_text_set_p = enddef;
def mplib_fill_outline_text =
  for n=1 upto mpliboutlinenum:
    i:=0;
    for item within mpliboutlinepic[n]:
      i:=i+1;
      fill pathpart item mplib_do_outline_options_f withpen pencircle scaled 0
      if (n<mpliboutlinenum) or (i<length mpliboutlinepic[n]): withpostscript "collect"; fi
    endfor
  endfor
enddef;
def mplib_draw_outline_text =
  for n=1 upto mpliboutlinenum:
    for item within mpliboutlinepic[n]:
      draw pathpart item mplib_do_outline_options_d;
    endfor
  endfor
enddef;
def mplib_filldraw_outline_text =
  for n=1 upto mpliboutlinenum:
    i:=0;
    for item within mpliboutlinepic[n]:
      i:=i+1;
      if (n<mpliboutlinenum) or (i<length mpliboutlinepic[n]):
        fill pathpart item mplib_do_outline_options_f withpostscript "collect";
      else:
        draw pathpart item mplib_do_outline_options_f withpostscript "both";
      fi
    endfor
  endfor
enddef;
vardef mpliboutlinetext@# (expr t) text rest =
  save kind; string kind; kind := str @#;
  save i; numeric i;
  picture mpliboutlinepic[]; numeric mpliboutlinenum;
  def mplib_do_outline_options_d = enddef;
  def mplib_do_outline_options_f = enddef;
  def mplib_do_outline_options_r = enddef;
  runscript("return luamplib.outlinetext[===["&t&"]===]");
  image ( addto currentpicture also image (
    if kind = "f":
      mplib_do_outline_text_set_f rest;
      mplib_fill_outline_text;
    elseif kind = "d":
      mplib_do_outline_text_set_d rest;
      mplib_draw_outline_text;
    elseif kind = "b":
      mplib_do_outline_text_set_b rest;
      mplib_fill_outline_text;
      mplib_draw_outline_text;
    elseif kind = "u":
      mplib_do_outline_text_set_u rest;
      mplib_filldraw_outline_text;
    elseif kind = "r":
      mplib_do_outline_text_set_r rest;
      mplib_draw_outline_text;
      mplib_fill_outline_text;
    elseif kind = "p":
      mplib_do_outline_text_set_p;
      mplib_draw_outline_text;
    else:
      mplib_do_outline_text_set_n rest;
      mplib_fill_outline_text;
    fi;
  ) mplib_do_outline_options_r; )
enddef ;
def withmppattern primary p =
  withprescript "mplibpattern=" & if numeric p: decimal fi p
enddef;
primarydef t withpattern p =
  image(
    if cycle t:
      fill
    else:
      draw
    fi
    t withprescript "mplibpattern=" & if numeric p: decimal fi p; )
enddef;
vardef mplibtransformmatrix (text e) =
  save t; transform t;
  t = identity e;
  runscript("luamplib.transformmatrix = {"
  & decimal xxpart t & ","
  & decimal yxpart t & ","
  & decimal xypart t & ","
  & decimal yypart t & ","
  & decimal xpart  t & ","
  & decimal ypart  t & ","
  & "}");
enddef;
primarydef p withmaskinggroup s =
  if picture p:
    image(
      draw p;
      draw center p withprescript "mplibfadestate=stop";
    )
  else:
    p withprescript "mplibfadestate=stop"
  fi
  withprescript "mplibfadetype=masking"
  withprescript "mplibmaskname=" & s
enddef;
def withmaskingbgcolor expr c =
  withprescript "mplibmaskingbgcolor=" & colordecimals c
enddef;
primarydef p withfademethod s =
  if picture p:
    image(
      draw p;
      draw center p withprescript "mplibfadestate=stop";
    )
  else:
    p withprescript "mplibfadestate=stop"
  fi
    withprescript "mplibfadetype=" & s
    hide(mplib_shade_step := 1;)
    withprescript "sh_color_a=1"
    withprescript "sh_color_b=0"
    withprescript "mplibfadebbox=" &
      decimal (xpart llcorner p -1/4) & ":" &
      decimal (ypart llcorner p -1/4) & ":" &
      decimal (xpart urcorner p +1/4) & ":" &
      decimal (ypart urcorner p +1/4)
enddef;
def withfadevector (expr a,b) =
  withprescript "mplibfadevector=" &
    decimal xpart a & ":" &
    decimal ypart a & ":" &
    decimal xpart b & ":" &
    decimal ypart b
enddef;
let withfadecenter = withfadevector;
def withfaderadius (expr a,b) =
  withprescript "mplibfaderadius=" &
    decimal a & ":" &
    decimal b
enddef;
def withfadebbox (expr a,b) =
  withprescript "mplibfadebbox=" &
    decimal xpart a & ":" &
    decimal ypart a & ":" &
    decimal xpart b & ":" &
    decimal ypart b
enddef;
primarydef p asgroup s =
  image(
    draw center p
      withprescript "mplibgroupbbox=" &
        decimal (xpart llcorner p -1/4) & ":" &
        decimal (ypart llcorner p -1/4) & ":" &
        decimal (xpart urcorner p +1/4) & ":" &
        decimal (ypart urcorner p +1/4)
      withprescript "gr_state=start"
      withprescript "gr_type=" & s;
    draw p withprescript "sh_in_xobj=yes";
    draw center p withprescript "gr_state=stop";
  )
enddef;
def withgroupbbox (expr a,b) =
  withprescript "mplibgroupbbox=" &
    decimal xpart a & ":" &
    decimal ypart a & ":" &
    decimal xpart b & ":" &
    decimal ypart b
enddef;
def withgroupname expr s =
  withprescript "mplibgroupname=" & s
enddef;
def usemplibgroup primary s =
  draw maketext("\luamplibtagasgroupput{"& s &"}{\csname luamplib.group."& s &"\endcsname}")
    shifted runscript("return luamplib.trgroupshifts['" & s & "']")
enddef;
path    mplib_shade_path ;
numeric mplib_shade_step ; mplib_shade_step := 0 ;
numeric mplib_shade_fx, mplib_shade_fy ;
numeric mplib_shade_lx, mplib_shade_ly ;
numeric mplib_shade_nx, mplib_shade_ny ;
numeric mplib_shade_dx, mplib_shade_dy ;
numeric mplib_shade_tx, mplib_shade_ty ;
primarydef p withshadingmethod m =
  p
  if picture p :
    withprescript "sh_operand_type=picture"
    if textual p or (length p > 1):
      withprescript "sh_transform=no"
      mplib_with_shade_method (boundingbox p, m)
    else:
      withprescript "sh_transform=yes"
      mplib_with_shade_method (pathpart p, m)
    fi
  else :
    withprescript "sh_transform=yes"
    mplib_with_shade_method (p, m)
  fi
enddef;
def mplib_with_shade_method (expr p, m) =
  hide(mplib_with_shade_method_analyze(p))
  withprescript "sh_domain=0 1"
  withprescript "sh_color=into"
  withprescript "sh_color_a=" & colordecimals white
  withprescript "sh_color_b=" & colordecimals black
  withprescript "sh_first=" & ddecimal point 0 of p
  withprescript "sh_set_x=" & ddecimal (mplib_shade_nx,mplib_shade_lx)
  withprescript "sh_set_y=" & ddecimal (mplib_shade_ny,mplib_shade_ly)
  if m = "linear" :
    withprescript "sh_type=linear"
    withprescript "sh_factor=1"
    withprescript "sh_center_a=" & ddecimal llcorner p
    withprescript "sh_center_b=" & ddecimal urcorner p
  elseif m = "coons":
    withprescript "sh_type=coons"
    withprescript "sh_transform=no"
  elseif m = "triangle":
    withprescript "sh_type=triangle"
    withprescript "sh_transform=no"
  elseif m = "lattice":
    withprescript "sh_type=lattice"
    withprescript "sh_transform=no"
  elseif m = "tensor":
    withprescript "sh_type=tensor"
    withprescript "sh_transform=no"
    withprescript "sh_tensor_path=" &
      ddecimal 1/4[point 0 of p, point 2 of p] & " " &
      ddecimal 1/4[point 1 of p, point 3 of p] & " " &
      ddecimal 1/4[point 2 of p, point 0 of p] & " " &
      ddecimal 1/4[point 3 of p, point 1 of p]
  else :
    withprescript "sh_type=circular"
    withprescript "sh_factor=1.2"
    withprescript "sh_center_a=" & ddecimal center p
    withprescript "sh_center_b=" & ddecimal center p
    withprescript "sh_radius_a=" & decimal 0
    withprescript "sh_radius_b=" & decimal mplib_max_radius(p)
  fi
enddef;
def withlatticeverticesperrow expr n =
  withprescript "sh_lattice_perrow=" & decimal n
enddef;
def withlatticeverticesdata (text t) =
  hide(luamplib_tmp_num_ := 0;)
  withprescript "sh_lattice_data=" &
  for item = t:
    if odd(incr luamplib_tmp_num_):
      if pair item: ddecimal fi item & " " &
    fi
  endfor ""
  hide(luamplib_tmp_num_ := 0; mplib_shade_step := 0;)
  for item = t:
    if not odd(incr luamplib_tmp_num_):
      withshadingstep( withshadingcolors(item,item) )
    fi
  endfor
enddef;
def withtrianglepatchinit (expr p, a, b, c) =
  if string p:
    withtrianglepatchnext(0, p , a)
    withtrianglepatchnext(0, "", b)
    withtrianglepatchnext(0, "", c)
  else:
    withtrianglepatchnext(0, point 0 of p, a)
    withtrianglepatchnext(0, point 1 of p, b)
    withtrianglepatchnext(0, point 2 of p, c)
  fi
enddef;
def withtrianglepatchnext (expr f, p, a) =
  withshadingstep(
    withprescript "sh_triangle_edge_" & decimal mplib_shade_step & "=" & decimal f
    withprescript "sh_triangle_vertex_" & decimal mplib_shade_step & "=" &
      if string p: p  else: ddecimal p  fi
    withshadingcolors (a, a)
  )
enddef;
def withcoonspatchinit (expr p, a, b, c, d) =
  withshadingstep(
    withprescript "sh_coons_edge_" & decimal mplib_shade_step & "=0"
    withprescript "sh_coons_path_" & decimal mplib_shade_step & "=" &
      if string p: p
      else:
        ddecimal point       0 of p & " " &
        ddecimal postcontrol 0 of p & " " &
        ddecimal precontrol  1 of p & " " &
        ddecimal point       1 of p & " " &
        ddecimal postcontrol 1 of p & " " &
        ddecimal precontrol  2 of p & " " &
        ddecimal point       2 of p & " " &
        ddecimal postcontrol 2 of p & " " &
        ddecimal precontrol  3 of p & " " &
        ddecimal point       3 of p & " " &
        ddecimal postcontrol 3 of p & " " &
        ddecimal precontrol  0 of p
      fi
    withshadingcolors (a, b)
  )
  withshadingstep ( withshadingcolors (c, d) )
enddef;
def withtensorpatchinit (expr p, q, a, b, c, d) =
  withshadingstep(
    withprescript "sh_coons_edge_" & decimal mplib_shade_step & "=0"
    withprescript "sh_coons_path_" & decimal mplib_shade_step & "=" &
      if string p: p & " " & q
      else:
        ddecimal point       0 of p & " " &
        ddecimal postcontrol 0 of p & " " &
        ddecimal precontrol  1 of p & " " &
        ddecimal point       1 of p & " " &
        ddecimal postcontrol 1 of p & " " &
        ddecimal precontrol  2 of p & " " &
        ddecimal point       2 of p & " " &
        ddecimal postcontrol 2 of p & " " &
        ddecimal precontrol  3 of p & " " &
        ddecimal point       3 of p & " " &
        ddecimal postcontrol 3 of p & " " &
        ddecimal precontrol  0 of p & " " &
        ddecimal point       0 of q & " " &
        ddecimal point       1 of q & " " &
        ddecimal point       2 of q & " " &
        ddecimal point       3 of q
      fi
    withshadingcolors (a, b)
  )
  withshadingstep ( withshadingcolors (c, d) )
enddef;
def withcoonspatchnext (expr f, p, a, b) =
  withshadingstep(
    withprescript "sh_coons_edge_" & decimal mplib_shade_step & "=" & decimal f
    withprescript "sh_coons_path_" & decimal mplib_shade_step & "=" &
      if string p: p
      else:
        ddecimal postcontrol 1 of p & " " &
        ddecimal precontrol  2 of p & " " &
        ddecimal point       2 of p & " " &
        ddecimal postcontrol 2 of p & " " &
        ddecimal precontrol  3 of p & " " &
        ddecimal point       3 of p & " " &
        ddecimal postcontrol 3 of p & " " &
        ddecimal precontrol  0 of p
      fi
    withshadingcolors (a, b)
  )
enddef;
def withtensorpatchnext (expr f, p, q, a, b) =
  withshadingstep(
    withprescript "sh_coons_edge_" & decimal mplib_shade_step & "=" & decimal f
    withprescript "sh_coons_path_" & decimal mplib_shade_step & "=" &
      if string p: p & " " & q
      else:
        ddecimal postcontrol 1 of p & " " &
        ddecimal precontrol  2 of p & " " &
        ddecimal point       2 of p & " " &
        ddecimal postcontrol 2 of p & " " &
        ddecimal precontrol  3 of p & " " &
        ddecimal point       3 of p & " " &
        ddecimal postcontrol 3 of p & " " &
        ddecimal precontrol  0 of p & " " &
        ddecimal point       0 of q & " " &
        ddecimal point       1 of q & " " &
        ddecimal point       2 of q & " " &
        ddecimal point       3 of q
      fi
    withshadingcolors (a, b)
  )
enddef;
def mplib_with_shade_method_analyze(expr p) =
  mplib_shade_path := p ;
  mplib_shade_step := 1 ;
  mplib_shade_fx   := xpart point 0 of p ;
  mplib_shade_fy   := ypart point 0 of p ;
  mplib_shade_lx   := mplib_shade_fx ;
  mplib_shade_ly   := mplib_shade_fy ;
  mplib_shade_nx   := 0 ;
  mplib_shade_ny   := 0 ;
  mplib_shade_dx   := abs(mplib_shade_fx - mplib_shade_lx) ;
  mplib_shade_dy   := abs(mplib_shade_fy - mplib_shade_ly) ;
  for i=1 upto length(p) :
    mplib_shade_tx := abs(mplib_shade_fx - xpart point i of p) ;
    mplib_shade_ty := abs(mplib_shade_fy - ypart point i of p) ;
    if mplib_shade_tx > mplib_shade_dx :
      mplib_shade_nx := i + 1 ;
      mplib_shade_lx := xpart point i of p ;
      mplib_shade_dx := mplib_shade_tx ;
    fi ;
    if mplib_shade_ty > mplib_shade_dy :
      mplib_shade_ny := i + 1 ;
      mplib_shade_ly := ypart point i of p ;
      mplib_shade_dy := mplib_shade_ty ;
    fi ;
  endfor ;
enddef;
vardef mplib_max_radius(expr p) =
  max (
    (xpart center   p - xpart llcorner p) ++ (ypart center   p - ypart llcorner p),
    (xpart center   p - xpart ulcorner p) ++ (ypart ulcorner p - ypart center   p),
    (xpart lrcorner p - xpart center   p) ++ (ypart center   p - ypart lrcorner p),
    (xpart urcorner p - xpart center   p) ++ (ypart urcorner p - ypart center   p)
  )
enddef;
def withshadingstep (text t) =
  hide(mplib_shade_step := mplib_shade_step + 1 ;)
  withprescript "sh_step=" & decimal mplib_shade_step
  t
enddef;
let withfadestep = withshadingstep;
def withshadingradius expr a =
  withprescript "sh_radius_a=" & decimal (xpart a)
  withprescript "sh_radius_b=" & decimal (ypart a)
enddef;
def withshadingorigin expr a =
  withprescript "sh_center_a=" & ddecimal a
  withprescript "sh_center_b=" & ddecimal a
enddef;
def withshadingvector expr a =
  withprescript "sh_center_a=" & ddecimal (point xpart a of mplib_shade_path)
  withprescript "sh_center_b=" & ddecimal (point ypart a of mplib_shade_path)
enddef;
def withshadingdirection expr a =
  withprescript "sh_center_a=" & ddecimal (point xpart a of boundingbox(mplib_shade_path))
  withprescript "sh_center_b=" & ddecimal (point ypart a of boundingbox(mplib_shade_path))
enddef;
def withshadingtransform expr a =
  withprescript "sh_transform=" & a
enddef;
def withshadingcenter expr a =
  withprescript "sh_center_a=" & ddecimal (
    center mplib_shade_path shifted (
      xpart a * xpart (lrcorner mplib_shade_path - llcorner mplib_shade_path)/2,
      ypart a * ypart (urcorner mplib_shade_path - lrcorner mplib_shade_path)/2
    )
  )
enddef;
def withshadingcenters (expr a, b) =
  withprescript "sh_center_a=" & ddecimal a
  withprescript "sh_center_b=" & ddecimal b
  withshadingtransform "no"
  withshadingfactor 1
enddef;
let withshadingpoints = withshadingcenters;
def withshadingextend (expr a, b) =
  withprescript "sh_extend=" &
    if a: "true" else: "false" fi & " " &
    if b: "true" else: "false" fi
enddef;
let withfadeextend = withshadingextend;
def withshadingdomain expr d =
  withprescript "sh_domain=" & ddecimal d
enddef;
def withshadingfactor expr f =
  withprescript "sh_factor=" & decimal f
enddef;
def withshadingfraction expr a =
  if mplib_shade_step > 0 :
    withprescript "sh_fraction_" & decimal mplib_shade_step & "=" & decimal a
  fi
enddef;
let withfadefraction = withshadingfraction;
def withshadingcolors (expr a, b) =
  if mplib_shade_step > 0 :
    withprescript "sh_color=into"
    withprescript "sh_color_a_" & decimal mplib_shade_step & "=" & colordecimals a
    withprescript "sh_color_b_" & decimal mplib_shade_step & "=" & colordecimals b
  else :
    withprescript "sh_color=into"
    withprescript "sh_color_a=" & colordecimals a
    withprescript "sh_color_b=" & colordecimals b
  fi
enddef;
let withfadeopacity = withshadingcolors;
def withshadingstroke expr a =
  withprescript "sh_stroking=" & a
enddef;
def withshadingmatrix expr s =
  withprescript "sh_matrix=" & s
enddef;
let withfadematrix = withshadingmatrix;
def mpliblength primary t =
  runscript("return utf8.len[===[" & t & "]===]")
enddef;
def mplibsubstring expr p of t =
  runscript("return luamplib.unicodesubstring([===[" & t & "]===],"
    & decimal xpart p & ","
    & decimal ypart p & ")")
enddef;
def mplibuclength primary t =
  runscript("return #luamplib.getunicodegraphemes[===[" & t & "]===]")
enddef;
def mplibucsubstring expr p of t =
  runscript("return luamplib.unicodesubstring([===[" & t & "]===],"
    & decimal xpart p & ","
    & decimal ypart p & ",true)")
enddef;
]],
  legacyverbatimtex = [[
def specialVerbatimTeX (text t) = runscript("luamplibprefig{"&t&"}") enddef;
def normalVerbatimTeX  (text t) = runscript("luamplibinfig{"&t&"}") enddef;
let VerbatimTeX = specialVerbatimTeX;
extra_beginfig := extra_beginfig & " let VerbatimTeX = normalVerbatimTeX;"&
  "runscript(" &ditto& "luamplib.in_the_fig=true" &ditto& ");";
extra_endfig := extra_endfig & " let VerbatimTeX = specialVerbatimTeX;"&
  "runscript(" &ditto&
  "if luamplib.in_the_fig then luamplib.figid=luamplib.figid+1 end "&
  "luamplib.in_the_fig=false" &ditto& ");";
]],
  textextlabel = [[
let luampliboriginalinfont = infont;
primarydef s infont f =
  if   (s < char 32)
    or (s = char 35) % #
    or (s = char 36) % $
    or (s = char 37) % %
    or (s = char 38) % &
    or (s = char 92) % \
    or (s = char 94) % ^
    or (s = char 95) % _
    or (s = char 123) % {
    or (s = char 125) % }
    or (s = char 126) % ~
    or (s = char 127) :
    s luampliboriginalinfont f
  else :
    rawtextext(s)
  fi
enddef;
def fontsize expr f =
  begingroup
  save size; numeric size;
  size := mplibdimen("1em");
  if size = 0: 10pt else: size fi
  endgroup
enddef;
]],
}

%    \end{macrocode}
%
%   |process_mplibcode|
%
%    When \cs{mplibverbatim} is enabled, do not expand |mplibcode| data.
%    \begin{macrocode}
luamplib.verbatiminput = false
luamplib.everymplib    = setmetatable({ [""] = "" },{ __index = function(t) return t[""] end })
luamplib.everyendmplib = setmetatable({ [""] = "" },{ __index = function(t) return t[""] end })
function luamplib.process_mplibcode (data, instancename)
  texboxes.localid = 4096
%    \end{macrocode}
%    This is needed for legacy behavior
%    \begin{macrocode}
  if luamplib.legacyverbatimtex then
    luamplib.figid, tex_code_pre_mplib = 1, {}
  end
  local everymplib    = luamplib.everymplib[instancename]
  local everyendmplib = luamplib.everyendmplib[instancename]
  data = format("\n%s\n%s\n%s\n",everymplib, data, everyendmplib)
  :gsub("\r","\n")
%    \end{macrocode}
%    These five lines are needed for |mplibverbatim| mode.
%    \begin{macrocode}
  if luamplib.verbatiminput then
    data = data:gsub("\\mpcolor%s+(.-%b{})","mplibcolor(\"%1\")")
    :gsub("\\mpdim%s+(%b{})", "mplibdimen(\"%1\")")
    :gsub("\\mpdim%s+(\\%a+)","mplibdimen(\"%1\")")
    :gsub(btex_etex, "btex %1 etex ")
    :gsub(verbatimtex_etex, "verbatimtex %1 etex;")
  else
%    \end{macrocode}
%    If not |mplibverbatim|, expand |mplibcode| data,
%    so that users can use \TeX\ codes in it.
%    It has turned out that no comment sign is allowed.
%    However, we do not expand |btex| |...| |etex|, |verbatimtex| |...| |etex|, and
%    string expressions.
%    \begin{macrocode}
    local t = { } -- to store btex, verbatimtex, string
    data = data:gsub(btex_etex, function(str)
      t[#t+1] = str
      return format("btex \\unexpanded{!l!u!a!%s!m!p!l!} etex ",  #t) -- space
    end)
    :gsub(verbatimtex_etex, function(str)
      t[#t+1] = str
      return format("verbatimtex \\unexpanded{!l!u!a!%s!m!p!l!} etex;", #t) -- semicolon
    end)
    :gsub('"(.-)"', function(str)
      t[#t+1] = str
      return format('"\\unexpanded{!l!u!a!%s!m!p!l!}"', #t)
    end)
    :gsub("\\%%", "\0PerCent\0")
    :gsub("%%.-\n","\n")
    :gsub("%zPerCent%z", "\\%%")
    run_tex_code(format("\\mplibtmptoks\\expandafter{\\expanded{%s}}",data))
    data = texgettoks"mplibtmptoks"
%    \end{macrocode}
%    Next line to address issue \#55
%    \begin{macrocode}
    :gsub("##", "#")
    :gsub("!l!u!a!(%d+)!m!p!l!", function(str) return t[tonumber(str)] or str end)
  end
  process(data, instancename)
end

%    \end{macrocode}
%
%     pdfliterals will be stored in |figcontents| table, and written to pdf in one go
%     at the end of the flushing figure.
%     Subtable |post| is for the legacy behavior.
%    \begin{macrocode}
local figcontents = { post = { } }
local function put2output(a,...)
  figcontents[#figcontents+1] = type(a) == "string" and format(a,...) or a
end
local function pdf_startfigure(n,llx,lly,urx,ury)
  put2output("\\mplibstarttoPDF{%f}{%f}{%f}{%f}",llx,lly,urx,ury)
end
local function pdf_stopfigure()
  put2output("\\mplibstoptoPDF")
end
%    \end{macrocode}
%    |tex.sprint| with catcode regime -2, as sometimes |#| gets doubled
%    in the argument of pdfliteral.
%    \begin{macrocode}
local function pdf_literalcode (...)
  put2output{ -2, (format(...) :gsub(decimals,rmzeros)) }
end
local start_pdf_code = pdfmode
  and function() pdf_literalcode"q" end
  or  function() put2output"\\special{pdf:bcontent}" end
local stop_pdf_code = pdfmode
  and function() pdf_literalcode"Q" end
  or  function() put2output"\\special{pdf:econtent}" end

%    \end{macrocode}
%
%    Now we process hboxes created from |btex| |...| |etex| or
%    |textext(...)| or |TEX(...)| etc.
%    \begin{macrocode}
local function put_tex_boxes (object,prescript)
  local box = prescript.mplibtexboxid:explode":"
  local n,tw,th = box[1],tonumber(box[2]),tonumber(box[3])
  if n and tw and th then
    local op = object.path
    local first, second, fourth = op[1], op[2], op[4]
    local tx, ty = first.x_coord, first.y_coord
    local sx, rx, ry, sy = 1, 0, 0, 1
    if tw ~= 0 then
      sx = (second.x_coord - tx)/tw
      rx = (second.y_coord - ty)/tw
      if sx == 0 then sx = 0.00001 end
    end
    if th ~= 0 then
      sy = (fourth.y_coord - ty)/th
      ry = (fourth.x_coord - tx)/th
      if sy == 0 then sy = 0.00001 end
    end

%    \end{macrocode}
% Attempt to address \#189, the displacement issue of pdf link boxes.
%    \begin{macrocode}
    local matrix = format("%f %f %f %f", sx, rx, ry, sy) :gsub(decimals,rmzeros)
    put2output("\\mplibputtextbox{%i}{%f}{%f}{%s}", n, tx, ty, matrix)
  end
end

%    \end{macrocode}
%
%    Colors
%    \begin{macrocode}
local do_preobj_CR
do
  local prev_override_color
  function do_preobj_CR(object,prescript)
    if object.postscript == "collect" then return end
    local override = prescript and prescript.mpliboverridecolor
    if override then
      if pdfmode or override:find" cs " then
        pdf_literalcode(override)
        override = nil
      else
        put2output("\\special{%s}",override)
        prev_override_color = override
      end
    else
      local cs = object.color
      if cs and #cs > 0 then
        pdf_literalcode(luamplib.colorconverter(cs))
        prev_override_color = nil
      elseif not pdfmode then
        override = prev_override_color
        if override then
          put2output("\\special{%s}",override)
        end
      end
    end
    return override
  end
end

%    \end{macrocode}
%
%    For transparency, shading, fading, and pattern
%    \begin{macrocode}
local pdfmanagement = is_defined'pdfmanagement_add:nnn'
local pdfobjs, pdfetcs = {}, {}
pdfetcs.pgfextgs = "pgf@sys@addpdfresource@extgs@plain"
pdfetcs.pgfpattern = "pgf@sys@addpdfresource@patterns@plain"
pdfetcs.pgfcolorspace = "pgf@sys@addpdfresource@colorspaces@plain"
local update_pdfobjs
if pdfmode then
  function update_pdfobjs (os, stream)
    local key = os
    if stream then key = key..stream end
    local on = key and pdfobjs[key]
    if on then
      return on,false
    end
    if stream then
      on = pdf.immediateobj("stream",stream,os)
    elseif os then
      on = pdf.immediateobj(os)
    else
      on = pdf.reserveobj()
    end
    if key then
      pdfobjs[key] = on
    end
    return on,true
  end
else
  function update_pdfobjs (os, stream, hex)
    local key = os
    if stream then key = key..stream end
    local on = key and pdfobjs[key]
    if on then
      return on,false
    end
    on = pdfetcs.cnt or 1
    if stream then
      if hex then
        texsprint(format("\\special{pdf:stream @mplibpdfobj%s <%s> <<%s>>}",on,stream,os))
      else
        texsprint(format("\\special{pdf:stream @mplibpdfobj%s (%s) <<%s>>}",on,stream,os))
      end
    elseif os then
      texsprint(format("\\special{pdf:obj @mplibpdfobj%s %s}",on,os))
    else
      texsprint(format("\\special{pdf:obj @mplibpdfobj%s <<>>}",on))
    end
    pdfetcs.cnt = on + 1
    if key then
      pdfobjs[key] = on
    end
    return on,true
  end
end
pdfetcs.resfmt = pdfmode and "%s 0 R" or "@mplibpdfobj%s"
if pdfmode then
  pdfetcs.getpageres = pdf.getpageresources or function() return pdf.pageresources end
  local getpageres = pdfetcs.getpageres
  local setpageres = pdf.setpageresources or function(s) pdf.pageresources = s end
  local initialize_resources = function (name)
    local tabname = format("%s_res",name)
    pdfetcs[tabname] = { }
    if luatexbase.callbacktypes.finish_pdffile then -- ltluatex
      local obj = pdf.reserveobj()
      setpageres(format("%s/%s %i 0 R", getpageres() or "", name, obj))
      luatexbase.add_to_callback("finish_pdffile", function()
        pdf.immediateobj(obj, format("<<%s>>", tableconcat(pdfetcs[tabname])))
      end,
      format("luamplib.%s.finish_pdffile",name))
    end
  end
  pdfetcs.fallback_update_resources = function (name, res)
    local tabname = format("%s_res",name)
    if not pdfetcs[tabname] then
      initialize_resources(name)
    end
    if luatexbase.callbacktypes.finish_pdffile then
      local t = pdfetcs[tabname]
      t[#t+1] = res
    else
      local tpr, n = getpageres() or "", 0
      tpr, n = tpr:gsub(format("/%s<<",name), "%1"..res)
      if n == 0 then
        tpr = format("%s/%s<<%s>>", tpr, name, res)
      end
      setpageres(tpr)
    end
  end
else
  texsprint {
    "\\luamplibatfirstshipout{",
    "\\special{pdf:obj @MPlibTr<<>>}",
    "\\special{pdf:obj @MPlibSh<<>>}",
    "\\special{pdf:obj @MPlibCS<<>>}",
    "\\special{pdf:obj @MPlibPt<<>>}}",
  }
  pdfetcs.fallback_update_resources = function (name,res,obj)
    texsprint{"\\special{pdf:put ", obj, " <<", res, ">>}"}
    local tabname = format("%s_res",name)
    if not pdfetcs[tabname] then
      texsprint{"\\luamplibateveryshipout{\\special{pdf:put @resources <</", name, " ", obj, ">>}}"}
      pdfetcs[tabname] = { }
    end
    tableinsert(pdfetcs[tabname], res)
  end
end

%    \end{macrocode}
%
%    Transparency
%    \begin{macrocode}
local function add_extgs_resources (on, new)
  local key = format("MPlibTr%s", on)
  if new then
    local val = format(pdfetcs.resfmt, on)
    if pdfmanagement then
      texsprint {
        "\\csname pdfmanagement_add:nnn\\endcsname{Page/Resources/ExtGState}{", key, "}{", val, "}"
      }
    else
      local tr = format("/%s %s", key, val)
      if is_defined(pdfetcs.pgfextgs) then
        texsprint { "\\csname ", pdfetcs.pgfextgs, "\\endcsname{", tr, "}" }
      elseif is_defined"TRP@list" then
        texsprint(catat11,{
          [[\if@filesw\immediate\write\@auxout{]],
          [[\string\g@addto@macro\string\TRP@list{]],
          tr,
          [[}}\fi]],
        })
        if not get_macro"TRP@list":find(tr) then
          texsprint(catat11,[[\global\TRP@reruntrue]])
        end
      else
        pdfetcs.fallback_update_resources("ExtGState",tr,"@MPlibTr")
      end
    end
  end
  return key
end

local do_preobj_TR
do
  local transparancy_modes = {
    [0] = "Normal",
    "Normal",       "Multiply",     "Screen",       "Overlay",
    "SoftLight",    "HardLight",    "ColorDodge",   "ColorBurn",
    "Darken",       "Lighten",      "Difference",   "Exclusion",
    "Hue",          "Saturation",   "Color",        "Luminosity",
    "Compatible",
    normal     = "Normal",     multiply   = "Multiply",   screen    = "Screen",
    overlay    = "Overlay",    softlight  = "SoftLight",  hardlight = "HardLight",
    colordodge = "ColorDodge", colorburn  = "ColorBurn",  darken    = "Darken",
    lighten    = "Lighten",    difference = "Difference", exclusion = "Exclusion",
    hue        = "Hue",        saturation = "Saturation", color     = "Color",
    luminosity = "Luminosity", compatible = "Compatible",
  }
  function do_preobj_TR(object,prescript)
    if object.postscript == "collect" then return end
    local opaq = prescript and prescript.tr_transparency
    if not opaq then return end

    local key, on, os, new
    local mode = prescript.tr_alternative or 1
    mode = transparancy_modes[tonumber(mode) or mode:lower()]
    if not mode then
      mode = prescript.tr_alternative
      warn("unsupported blend mode: '%s'", mode)
    end
    opaq = opaq:explode":"
    for i,v in ipairs(opaq) do
      opaq[i] = format("%.3f", v) :gsub(decimals,rmzeros)
    end
    for i,v in ipairs{ {mode,opaq[1],opaq[2] or opaq[1]},{"Normal",1,1} } do
      os = format("<</BM/%s/ca %s/CA %s/AIS false>>",v[1],v[2],v[3])
      on, new = update_pdfobjs(os)
      key = add_extgs_resources(on,new)
      if i == 1 then
        pdf_literalcode("/%s gs",key)
      else
        return format("/%s gs",key)
      end
    end
  end
end

%    \end{macrocode}
%
%    Shading with \emph{metafun} format.
%    \begin{macrocode}
local function add_shading_resources (on, new)
  if new then
    local key, val = format("MPlibSh%s", on), format(pdfetcs.resfmt, on)
    if pdfmanagement then
      texsprint {
        "\\csname pdfmanagement_add:nnn\\endcsname{Page/Resources/Shading}{", key, "}{", val, "}"
      }
    else
      local res = format("/%s %s", key, val)
      pdfetcs.fallback_update_resources("Shading",res,"@MPlibSh")
    end
  end
end
local function sh_pdfpageresources(shtype,domain,colorspace,ca,cb,coordinates,steps,fractions,extend)
  for _,v in ipairs{ca,cb} do
    for i,vv in ipairs(v) do
      for ii,vvv in ipairs(vv) do
        v[i][ii] = tonumber(vvv) and format("%.3f",vvv) or vvv
      end
    end
  end
  local fun2fmt,os = "<</FunctionType 2/Domain[%s]/C0[%s]/C1[%s]/N 1>>"
  if steps > 1 then
    local list,bounds,encode = { },{ },{ }
    for i=1,steps do
      if i < steps then
        bounds[i] = format("%.3f", fractions[i] or 1)
      end
      encode[2*i-1] = 0
      encode[2*i]   = 1
      os = fun2fmt:format(domain,tableconcat(ca[i],' '),tableconcat(cb[i],' '))
        :gsub(decimals,rmzeros)
      list[i] = format(pdfetcs.resfmt, update_pdfobjs(os))
    end
    os = tableconcat {
      "<</FunctionType 3",
      format("/Bounds[%s]",    tableconcat(bounds,' ')),
      format("/Encode[%s]",    tableconcat(encode,' ')),
      format("/Functions[%s]", tableconcat(list,  ' ')),
      format("/Domain[%s]>>",  domain),
    } :gsub(decimals,rmzeros)
  else
    os = fun2fmt:format(domain,tableconcat(ca[1],' '),tableconcat(cb[1],' '))
      :gsub(decimals,rmzeros)
  end
  local objref = format(pdfetcs.resfmt, update_pdfobjs(os))
  os = tableconcat {
    format("<</ShadingType %i", shtype),
    format("/ColorSpace %s",    colorspace),
    format("/Function %s",      objref),
    format("/Coords[%s]",       coordinates),
    format("/Extend[%s]/AntiAlias true>>", extend or "true true")
  } :gsub(decimals,rmzeros)
  local on, new = update_pdfobjs(os)
  add_shading_resources(on, new)
  return on
end

local function get_mp_matrix (matrix)
  process(("mplibtransformmatrix(%s);"):format(matrix), "@mplibtransformmatrix")
  return luamplib.transformmatrix
end

local do_preobj_SH
do
  pdfetcs.clrspcs = setmetatable({ }, { __index = function(t,names)
    run_tex_code({
      [[\color_model_new:nnn]],
      format("{mplibcolorspace_%s}", names:gsub(",","_")),
      format("{DeviceN}{names={%s}}", names),
      [[\mplibtmptoks\expanded{{\pdf_object_ref_last:}}]],
    }, ccexplat)
    local colorspace = texgettoks"mplibtmptoks"
    t[names] = colorspace
    return colorspace
  end })
  local function color_normalize(ca,cb)
    if #cb == 1 then
      if #ca == 4 then
        cb[1], cb[2], cb[3], cb[4] = 0, 0, 0, 1-cb[1]
      else -- #ca = 3
        cb[1], cb[2], cb[3] = cb[1], cb[1], cb[1]
      end
    elseif #cb == 3 then -- #ca == 4 : rgb to cmyk
      local c, m, y =  1-cb[1], 1-cb[2], 1-cb[3]
      local k = math.min(c, m, y)
      if k == 1 then
        c, m, y = 0, 0, 0
      else
        c, m, y = (c-k)/(1-k), (m-k)/(1-k), (y-k)/(1-k)
      end
      cb[1], cb[2], cb[3], cb[4] = c, m, y, k
    end
  end
  local function write_mesh_objs (shtype, colorspace, stream, devicen, perrow)
    local cc = colorspace == "/DeviceCMYK" and 4
            or colorspace == "/DeviceRGB" and 3
            or devicen or 1
    local dict = format(
      "/ShadingType %d/%s/BitsPerCoordinate 16/BitsPerComponent 8/ColorSpace %s/Decode[0 1 0 1%s]",
      shtype,
      perrow and format("VerticesPerRow %d", perrow) or "BitsPerFlag 8",
      colorspace,
      (" 0 1"):rep(cc))
    local on, new
    if pdfmode then
      on, new = update_pdfobjs(dict, stream)
    else
      stream = format(("%02X"):rep(stream:len()), stream:byte(1,stream:len()))
      on, new = update_pdfobjs(dict, stream, true) -- hex is true
    end
    add_shading_resources(on, new)
    return on
  end
  local function colors_ff (colors)
    local t, devicen = { }
    for i,v in ipairs(colors) do
      t[i] = { }
      for _,vv in ipairs(v) do
        local tt = tableconcat(vv," "):explode()
        for _,vvv in ipairs(tt) do
          tableinsert(t[i], string.char(math.floor(vvv * 0xFF)))
        end
        devicen = #tt
      end
    end
    return t, devicen
  end
  local function coords_ffff (coords)
    local Xt, Yt = { }, { }
    for i,v in ipairs(coords) do
      for ii,vv in ipairs(v) do
        local t = ii % 2 == 1 and Xt or Yt
        t[#t+1] = tonumber(vv)
      end
    end
    local xmin, xmax = math.min(tableunpack(Xt)), math.max(tableunpack(Xt))
    local ymin, ymax = math.min(tableunpack(Yt)), math.max(tableunpack(Yt))
    local wd, ht = xmax - xmin, ymax - ymin
    for i,v in ipairs(coords) do
      for ii,vv in ipairs(v) do
        local min = ii % 2 == 1 and xmin or ymin
        local dim = ii % 2 == 1 and wd or ht
        coords[i][ii] = string.pack(">H", math.floor((vv - min)/dim * 0xFFFF ))
      end
    end
    return coords, xmin, ymin, wd, ht
  end
  local function do_shading_lattice_mesh (object, prescript, colorspace, ca, cb)
    local perrow = prescript.sh_lattice_perrow or 2
    local steps = tonumber(prescript.sh_step) or 0
    local coords, colors = { }, { }
    if steps < 4 then
      local path = object.path
      for _,i in ipairs{1,2,4,3} do
        coords[#coords+1] = { path[i].x_coord, path[i].y_coord }
      end
      colors = { {{1,0,0}}, {{0,1,0}}, {{0,0,1}}, {{1,1,0}} }
      colorspace = "/DeviceRGB"
    else
      local t = prescript.sh_lattice_data:explode()
      for i = 1, #t, 2 do
        coords[#coords+1] = { t[i], t[i+1] }
      end
      for i = 1, steps do
        if not ca[i] then err"colorspace mismatch?" end
        colors[#colors+1] = { ca[i] }
      end
    end
    if #coords % perrow ~= 0 then err"vertices_per_row mismatches lattice_coords" end

    local stream, xmin, ymin, wd, ht, devicen = { }
    coords, xmin, ymin, wd, ht = coords_ffff(coords)
    colors, devicen = colors_ff(colors)
    for i = 1, #coords do
      stream[#stream+1] = tableconcat(coords[i])
      stream[#stream+1] = tableconcat(colors[i])
    end

    local matrix = format("%f 0 0 %f %f %f", wd, ht, xmin, ymin) :gsub(decimals,rmzeros)
    local on = write_mesh_objs (5, colorspace, tableconcat(stream), devicen, perrow)
    return on, matrix
  end
  local function do_shading_triangle_mesh (object, prescript, colorspace, ca, cb)
    local steps = tonumber(prescript.sh_step) or 0
    local coords, colors, edges = { }, { }, { }
    if steps < 3 then
      local path = object.path
      for i = 1, 3 do
        coords[#coords+1] = { path[i].x_coord, path[i].y_coord }
      end
      colors = { {{1,0,0}}, {{0,1,0}}, {{0,0,1}} }
      edges = { 0, 0, 0 }
      colorspace = "/DeviceRGB"
    else
      for i = 1, steps do
        local t = prescript["sh_triangle_vertex_" .. i]:explode()
        if #t == 2 then
          coords[#coords+1] = { t[1], t[2] }
        elseif #t == 6 then
          coords[#coords+1] = { t[1], t[2] }
          coords[#coords+1] = { t[3], t[4] }
          coords[#coords+1] = { t[5], t[6] }
        end
        if not ca[i] then err"colorspace mismatch?" end
        colors[#colors+1] = { ca[i] }
        edges[#edges+1] = tonumber(prescript["sh_triangle_edge_" .. i])
      end
    end

    local stream, xmin, ymin, wd, ht, devicen = { }
    coords, xmin, ymin, wd, ht = coords_ffff(coords)
    colors, devicen = colors_ff(colors)
    for i = 1, #coords do
      stream[#stream+1] = string.char(edges[i])
      stream[#stream+1] = tableconcat(coords[i])
      stream[#stream+1] = tableconcat(colors[i])
    end

    local matrix = format("%f 0 0 %f %f %f", wd, ht, xmin, ymin) :gsub(decimals,rmzeros)
    local on = write_mesh_objs (4, colorspace, tableconcat(stream), devicen)
    return on, matrix
  end
  local function do_shading_coons_patch (object, prescript, colorspace, ca, cb)
    local tensor = prescript.sh_type == "tensor"
    local steps = tonumber(prescript.sh_step) or 0
    local coords, colors, edges = { }, { }, { }
    if steps < 2 then
      local path, t = object.path, { }
      for i = 1, 4 do
        t[#t+1] = path[i].x_coord
        t[#t+1] = path[i].y_coord
        t[#t+1] = path[i].right_x
        t[#t+1] = path[i].right_y
        local j = i == 4 and 1 or i+1
        t[#t+1] = path[j].left_x
        t[#t+1] = path[j].left_y
      end
      if tensor then
        local tt = prescript.sh_tensor_path:explode()
        t = table.move(tt, 1, #tt, #t+1, t)
      end
      coords = { t }
      colors = {{ {1,0,0}, {0,1,0}, {0,0,1}, {1,1,0} }}
      edges  = { 0 }
      colorspace = "/DeviceRGB"
    else
      for i = 1, steps do
        local edge = tonumber(prescript["sh_coons_edge_"..i])
        if edge then
          edges[#edges+1] = edge
          coords[#coords+1] = prescript["sh_coons_path_"..i]:explode()
          if edge == 0 then
            if not (ca[i] and cb[i] and ca[i+1] and cb[i+1]) then err"colorspace mismatch?" end
            colors[#colors+1] = { ca[i], cb[i], ca[i+1], cb[i+1] }
          else
            if not (ca[i] and cb[i]) then err"colorspace mismatch?" end
            colors[#colors+1] = { ca[i], cb[i] }
          end
        end
      end
    end

    local stream, xmin, ymin, wd, ht, devicen = { }
    coords, xmin, ymin, wd, ht = coords_ffff(coords)
    colors, devicen = colors_ff(colors)
    for i = 1, #coords do
      stream[#stream+1] = string.char(edges[i])
      stream[#stream+1] = tableconcat(coords[i])
      stream[#stream+1] = tableconcat(colors[i])
    end

    local matrix = format("%f 0 0 %f %f %f", wd, ht, xmin, ymin) :gsub(decimals,rmzeros)
    local on = write_mesh_objs (tensor and 7 or 6, colorspace, tableconcat(stream), devicen)
    return on, matrix
  end
  function do_preobj_SH(object, prescript)
    local shade_no
    local sh_type = prescript and prescript.sh_type
    if not sh_type then return end

    local domain  = prescript.sh_domain or "0 1"
    local centera = (prescript.sh_center_a or "0 0"):explode()
    local centerb = (prescript.sh_center_b or "0 0"):explode()
    local transform = prescript.sh_transform == "yes"
    local sx,sy,sr,dx,dy = 1,1,1,0,0
    if transform then
      local first = (prescript.sh_first or "0 0"):explode()
      local setx  = (prescript.sh_set_x or "0 0"):explode()
      local sety  = (prescript.sh_set_y or "0 0"):explode()
      local x,y = tonumber(setx[1]) or 0, tonumber(sety[1]) or 0
      if x ~= 0 and y ~= 0 then
        local path = object.path
        local path1x = path[1].x_coord
        local path1y = path[1].y_coord
        local path2x = path[x].x_coord
        local path2y = path[y].y_coord
        local dxa = path2x - path1x
        local dya = path2y - path1y
        local dxb = setx[2] - first[1]
        local dyb = sety[2] - first[2]
        if dxa ~= 0 and dya ~= 0 and dxb ~= 0 and dyb ~= 0 then
          sx = dxa / dxb ; if sx < 0 then sx = - sx end
          sy = dya / dyb ; if sy < 0 then sy = - sy end
          sr = math.sqrt(sx^2 + sy^2)
          dx = path1x - sx*first[1]
          dy = path1y - sy*first[2]
        end
      end
    end
    local ca, cb, colorspace, steps, fractions
    ca = { (prescript.sh_color_a_1 or prescript.sh_color_a or "0"):explode":" }
    cb = { (prescript.sh_color_b_1 or prescript.sh_color_b or "1"):explode":" }
    steps = tonumber(prescript.sh_step) or 1
    if steps > 1 then
      fractions = { prescript.sh_fraction_1 or 0 }
      for i=2,steps do
        fractions[i] = prescript[format("sh_fraction_%i",i)] or (i/steps)
        ca[i] = (prescript[format("sh_color_a_%i",i)] or "0"):explode":"
        cb[i] = (prescript[format("sh_color_b_%i",i)] or "1"):explode":"
      end
    end
    if prescript.mplib_spotcolor then
      ca, cb = { }, { }
      local names, pos, objref = { }, -1, ""
      local script = object.prescript:explode"\13+"
      for i=#script,1,-1 do
        local name, value
        if script[i]:find"mplib_spotcolor" then
          local t = script[i]:explode"="[2]:explode":"
          value, objref, name = t[1], t[2], t[3]
        elseif script[i]:find"mplib_texcolor" then
          local t = script[i]:explode"="[2]:explode"!"
          name, value = t[1], (t[2] or 100)/100
        end
        if name and value then
          if not names[name] then
            pos = pos+1
            names[name] = pos
            names[#names+1] = name
          end
          local t = { }
          for j=1,names[name] do t[#t+1] = 0 end
          t[#t+1] = value
          tableinsert(#ca == #cb and ca or cb, t)
        end
      end
      for _,t in ipairs{ca,cb} do
        for _,tt in ipairs(t) do
          for i=1,#names-#tt do tt[#tt+1] = 0 end
        end
      end
      if #names == 1 then
        colorspace = objref
      else
        colorspace = pdfetcs.clrspcs[ tableconcat(names,",") ]
      end
    else
      local model = 0
      for _,t in ipairs{ca,cb} do
        for _,tt in ipairs(t) do
          model = model > #tt and model or #tt
        end
      end
      for _,t in ipairs{ca,cb} do
        for _,tt in ipairs(t) do
          if #tt < model then
            color_normalize(model == 4 and {1,1,1,1} or {1,1,1},tt)
          end
        end
      end
      colorspace = model == 4 and "/DeviceCMYK"
                or model == 3 and "/DeviceRGB"
                or model == 1 and "/DeviceGray"
                or err"unknown color model"
    end
    local extend = prescript.sh_extend
    local mesh_matrix
    if sh_type == "linear" then
      local coordinates = format("%f %f %f %f",
        dx + sx*centera[1], dy + sy*centera[2],
        dx + sx*centerb[1], dy + sy*centerb[2])
      shade_no = sh_pdfpageresources(2,domain,colorspace,ca,cb,coordinates,steps,fractions,extend)
    elseif sh_type == "circular" then
      local factor = prescript.sh_factor or 1
      local radiusa = factor * prescript.sh_radius_a
      local radiusb = factor * prescript.sh_radius_b
      local coordinates = format("%f %f %f %f %f %f",
        dx + sx*centera[1], dy + sy*centera[2], sr*radiusa,
        dx + sx*centerb[1], dy + sy*centerb[2], sr*radiusb)
      shade_no = sh_pdfpageresources(3,domain,colorspace,ca,cb,coordinates,steps,fractions,extend)
    elseif sh_type == "coons" or sh_type == "tensor" then
      shade_no, mesh_matrix = do_shading_coons_patch(object, prescript, colorspace, ca, cb)
    elseif sh_type == "triangle" then
      shade_no, mesh_matrix = do_shading_triangle_mesh(object, prescript, colorspace, ca, cb)
    elseif sh_type == "lattice" then
      shade_no, mesh_matrix = do_shading_lattice_mesh(object, prescript, colorspace, ca, cb)
    else
      err"unknown shading type"
    end

    local matrix = prescript.sh_matrix
    if matrix and matrix:find"%a" then
      local t = get_mp_matrix(matrix)
      matrix = format("%f %f %f %f %f %f", tableunpack(t)) :gsub(decimals,rmzeros)
    end
    if mesh_matrix and matrix then
      local a, b = mesh_matrix:explode(), matrix:explode()
      matrix = format("%f %f %f %f %f %f",
        a[1]*b[1]+a[2]*b[3], a[1]*b[2]+a[2]*b[4], a[3]*b[1]+a[4]*b[3], a[3]*b[2]+a[4]*b[4],
        a[5]+b[5], a[6]+b[6]) :gsub(decimals,rmzeros)
    end

    return shade_no, prescript.sh_stroking == "yes", matrix or mesh_matrix
  end
end

%    \end{macrocode}
%
% Shading Patterns:
% we can apply shading to textual pictures as well as paths.
%    \begin{macrocode}
local function add_pattern_resources (key, val)
  if pdfmanagement then
    texsprint {
      "\\csname pdfmanagement_add:nnn\\endcsname{Page/Resources/Pattern}{", key, "}{", val, "}"
    }
  else
    local res = format("/%s %s", key, val)
    if is_defined(pdfetcs.pgfpattern) then
      texsprint { "\\csname ", pdfetcs.pgfpattern, "\\endcsname{", res, "}" }
    else
      pdfetcs.fallback_update_resources("Pattern",res,"@MPlibPt")
    end
  end
end
if pdfmode then
  function luamplib.dolatelua (on, os, matrix)
    local h, v = pdf.getpos()
    local t = matrix and matrix:explode() or {1,0,0,1,0,0}
    matrix = format("%f %f %f %f %f %f", t[1], t[2], t[3], t[4], t[5]+h/factor, t[6]+v/factor)
      :gsub(decimals,rmzeros)
    pdf.obj(on, format("<<%s/Matrix[%s]>>", os, matrix))
    pdf.refobj(on)
  end
else
  pdfetcs.shadingpatterns = { }
  pdfetcs.shadingpatterninit_r, pdfetcs.shadingpatterninit_w = true, true
  function luamplib.dolatelua (on, kind, xobj)
    local h, v
    local t = pdfetcs.shadingpatterns[on] or { }
    local shift = kind == "group" and pdfetcs.tr_group.shifts[xobj]
               or kind == "pattern" and pdfetcs.patterns[xobj].shifts
    if shift then
      h, v = -shift[1], -shift[2] -- engine bug in dvi mode?
    else
      h, v = pdf.getpos()
      h = format("%f", h/factor) :gsub(decimals,rmzeros)
      v = format("%f", v/factor) :gsub(decimals,rmzeros)
    end
    if tonumber(h) ~= tonumber(t[1]) or tonumber(v) ~= tonumber(t[2]) then
      warn"Rerun to get correct shading pattern"
    end
    local name = format("%s/%s_shadingpatterns.aux", cachedir or outputdir(), tex.jobname)
    local init = pdfetcs.shadingpatterninit_w
    if init then pdfetcs.shadingpatterninit_w = nil end
    local f = ioopen(name, init and "w" or "a")
    if f then
      f:write(("%s %s %s\n"):format(on, h, v))
      f:close()
    else
      err"cannot write a file. check the cache dir path"
    end
  end
end
local function do_preobj_shading (object, prescript)
  if not prescript or not prescript.sh_operand_type then return end
  local on,_,matrix = do_preobj_SH(object, prescript)
  local os = format("/PatternType 2/Shading %s", format(pdfetcs.resfmt, on))
  matrix = matrix or "1 0 0 1 0 0"
  if prescript.sh_in_xobj == "yes" then
    on = update_pdfobjs(("<<%s/Matrix[%s]>>"):format(os, matrix))
    goto skip_latelua
  end
  on = update_pdfobjs()
  if pdfmode then
    put2output(tableconcat{"\\latelua{luamplib.dolatelua(",on,",[[",os,"]],[[",matrix,"]])}"})
  else
    local xobj = is_defined"mplibgroupname" and {"group", get_macro"mplibgroupname"}
              or is_defined"mplibpatternname" and {"pattern", get_macro"mplibpatternname"}
    local init = pdfetcs.shadingpatterninit_r
    if init then
      pdfetcs.shadingpatterninit_r = nil
      local name = format("%s/%s_shadingpatterns.aux", cachedir or outputdir(), tex.jobname)
      local f = ioopen(name)
      if f then
        for line in f:lines() do
          local t = line:explode()
          pdfetcs.shadingpatterns[ tonumber(t[1]) ] = { t[2], t[3] }
        end
        f:close()
      end
    end
%    \end{macrocode}
% This seems to be needed for proper functioning:
%\begin{verbatim}
%    \pagewidth=\paperwidth
%    \pageheight=\paperheight
%    \special{papersize=\the\paperwidth,\the\paperheight}
%\end{verbatim}
%    \begin{macrocode}
    local t = pdfetcs.shadingpatterns[on] or { 0, 0 }
    local mt = matrix:explode()
    matrix = format("%s %s %s %s %s %s", mt[1], mt[2], mt[3], mt[4], mt[5]+t[1], mt[6]+t[2])
    texsprint{ "\\special{pdf:put ", format(pdfetcs.resfmt, on),
              format(" <<%s/Matrix[%s]>>}", os, matrix) }
    put2output("\\latelua{ luamplib.dolatelua(%s,%s) }", on,
              xobj and ("'%s',[[%s]]"):format(xobj[1], xobj[2]))
  end
  ::skip_latelua::
  local key, val = format("MPlibPt%s", on), format(pdfetcs.resfmt, on)
  add_pattern_resources(key,val)
  pdf_literalcode("/Pattern cs/%s scn", key)
%    \end{macrocode}
% To avoid possible double execution, once by Pattern gs, once by Sh operator.
%    \begin{macrocode}
  prescript.sh_type = nil
end

%    \end{macrocode}
%
%    Tiling Patterns
%    \begin{macrocode}
pdfetcs.patterns = { }
local function gather_resources (optres, ispattern)
  local t = { }
  if pdfmanagement then
    for _,v in ipairs {"ExtGState","ColorSpace","Pattern","Shading"} do
      local mytoks
      run_tex_code ({
        "\\mplibtmptoks\\expanded{{",
        "\\pdfdict_if_empty:nF{g__pdf_Core/Page/Resources/",v,"}",
        "{\\pdfdict_use:n{g__pdf_Core/Page/Resources/",v,"}}", "}}",
      },ccexplat)
      mytoks = texgettoks"mplibtmptoks"
      if not pdfmode then
        mytoks = mytoks:gsub("\\str_convert_pdfname:n%s*{(.-)}","%1") -- why not expanded?
      end
      mytoks = mytoks and mytoks:gsub("^%s*(.-)%s*$", "%1")
      if mytoks and mytoks ~= "" then
        t[#t+1] = ("/%s<<%s>>"):format(v, mytoks)
      end
    end
  elseif is_defined(pdfetcs.pgfextgs) then
    run_tex_code"\\relax" -- flush tex.sprint queue
    if pdfmode then
      for k,v in pairs { ExtGState  = "pgf@sys@pgf@resource@list@extgs",
                         ColorSpace = "pgf@sys@pgf@resource@list@colorspaces",
                         Pattern    = "pgf@sys@pgf@resource@list@patterns", } do
        local res = (get_macro(v) or ""):gsub("^%s*(.-)%s*$","%1")
        if res ~= "" then
          t[#t+1] = ("/%s<<%s>>"):format(k, res )
        end
      end
    else
      local abc = get_macro"pgfutil@abc" or ""
      for k,v in pairs { ExtGState  = "@pgfextgs",
                         ColorSpace = "@pgfcolorspaces",
                         Pattern    = "@pgfpatterns", } do
        local tt = { }
        for vv in abc:gmatch( v .. "%s*(%b<>)" ) do
          tt[#tt+1] = vv:match("^<<%s*(.-)%s*>>$")
        end
        if #tt > 0 then
          t[#t+1] = ("/%s<<%s>>"):format(k, tableconcat(tt) )
        end
      end
    end
%    \end{macrocode}
% We still have to deal with Shading resources.
%    \begin{macrocode}
    if luatexbase.callbacktypes.finish_pdffile then
      if pdfetcs.Shading_res then
        t[#t+1] = ("/Shading<<%s>>"):format( tableconcat(pdfetcs.Shading_res) )
      end
    else
      local res = pdfetcs.getpageres()
      res = res and res:match"/Shading%s*%b<>"
      if res then
        t[#t+1] = res
      end
    end
  else
    if ispattern and is_defined"TRP@list" then
%    \end{macrocode}
% We do not gather \pkg{transparent} package's \cs{TRP@list}
% as Acrobat glitches on tiling pattern plus masking group,
% so warn users and recommend \cs{DocumentMetadata}
%    \begin{macrocode}
      warn"transparent package is not fully functional without pdfmanagement code."
    end
    if luatexbase.callbacktypes.finish_pdffile then
      for _,v in ipairs {"ExtGState","ColorSpace","Pattern","Shading"} do
        local tt = pdfetcs[v.."_res"]
        if tt then
          t[#t+1] = ("/%s<<%s>>"):format(v, tableconcat(tt))
        end
      end
    else
      local res = pdfetcs.getpageres()
      if res then
        t[#t+1] = res
      end
    end
  end
  local result = tableconcat(t)
  if optres ~= "" then
    for _,v in ipairs {"ExtGState","ColorSpace","Pattern","Shading"} do
      local res = optres:match("/"..v.."%s*%b<>")
      if res then
        if result:find("/"..v) then
          res = res:match("<<(.+)>>$")
          result = result:gsub("/"..v.."%s*<<", "%1"..res, 1)
        else
          result = result .. res
        end
      end
    end
  end
  return result
end
function luamplib.registerpattern ( boxid, name, opts )
  local box = texgetbox(boxid)
  local wd = format("%.3f",box.width/factor)
  local hd = format("%.3f",(box.height+box.depth)/factor)
  info("w/h/d of pattern '%s': %s 0", name, format("%s %s",wd, hd):gsub(decimals,rmzeros))
  if opts.xstep == 0 then opts.xstep = nil end
  if opts.ystep == 0 then opts.ystep = nil end
  if opts.colored == nil then
    opts.colored = opts.coloured
    if opts.colored == nil then
      opts.colored = true
    end
  end
  if type(opts.matrix) == "table" then opts.matrix = tableconcat(opts.matrix," ") end
  if type(opts.bbox) == "table" then opts.bbox = tableconcat(opts.bbox," ") end
  if opts.matrix and opts.matrix:find"%a" then
    local t = get_mp_matrix(opts.matrix)
    opts.matrix = format("%f %f %f %f", t[1], t[2], t[3], t[4])
    opts.xshift = opts.xshift or format("%f",t[5])
    opts.yshift = opts.yshift or format("%f",t[6])
  end
  local attr = {
    "/Type/Pattern",
    "/PatternType 1",
    format("/PaintType %i", opts.colored and 1 or 2),
    "/TilingType 2",
    format("/XStep %s", opts.xstep or wd),
    format("/YStep %s", opts.ystep or hd),
    format("/Matrix[%s %s %s]", opts.matrix or "1 0 0 1", opts.xshift or 0, opts.yshift or 0),
  }
  local optres = opts.resources or ""
  optres = gather_resources(optres, true) -- tiling pattern plus masking glitches with acrobat
  local patterns = pdfetcs.patterns
  if pdfmode then
    if opts.bbox then
      attr[#attr+1] = format("/BBox[%s]", opts.bbox)
    end
    attr = tableconcat(attr) :gsub(decimals,rmzeros)
    local index = tex.saveboxresource(boxid, attr, optres, true, opts.bbox and 4 or 1)
    patterns[name] = { id = index, colored = opts.colored }
  else
    local cnt = #patterns + 1
    local objname = "@mplibpattern" .. cnt
    local metric = format("bbox %s", opts.bbox or format("0 0 %s %s",wd,hd))
    texsprint {
      "\\expandafter\\newbox\\csname luamplib.patternbox.", cnt, "\\endcsname",
      "\\global\\setbox\\csname luamplib.patternbox.", cnt, "\\endcsname",
      "\\hbox{\\unhbox ", boxid, "}\\luamplibatnextshipout{",
      "\\special{pdf:bcontent}",
      "\\special{pdf:bxobj ", objname, " ", metric, "}",
      "\\raise\\dp\\csname luamplib.patternbox.", cnt, "\\endcsname",
      "\\box\\csname luamplib.patternbox.", cnt, "\\endcsname",
      "\\special{pdf:put @resources <<", optres, ">>}",
      "\\special{pdf:exobj <<", tableconcat(attr), ">>}",
      "\\special{pdf:econtent}}",
    }
    patterns[cnt] = objname
    patterns[name] = { id = cnt, colored = opts.colored }
    patterns[name].shifts = { get_macro"MPllx", get_macro"MPlly" } -- for shading patterns above
  end
end

local do_preobj_PAT
do
  local function pattern_colorspace (cs)
    local on, new = update_pdfobjs(format("[/Pattern %s]", cs))
    if new then
      local key, val = format("MPlibCS%i",on), format(pdfetcs.resfmt,on)
      if pdfmanagement then
        texsprint {
          "\\csname pdfmanagement_add:nnn\\endcsname{Page/Resources/ColorSpace}{", key, "}{", val, "}"
        }
      else
        local res = format("/%s %s", key, val)
        if is_defined(pdfetcs.pgfcolorspace) then
          texsprint { "\\csname ", pdfetcs.pgfcolorspace, "\\endcsname{", res, "}" }
        else
          pdfetcs.fallback_update_resources("ColorSpace",res,"@MPlibCS")
        end
      end
    end
    return on
  end
  function do_preobj_PAT(object, prescript)
    local name = prescript and prescript.mplibpattern
    if not name then return end
    local patterns = pdfetcs.patterns
    local patt = patterns[name]
    local index = patt and patt.id or err("cannot get pattern object '%s'", name)
    local key = format("MPlibPt%s",index)
    if patt.colored then
      pdf_literalcode("/Pattern cs /%s scn", key)
    else
      local color = prescript.mpliboverridecolor
      if not color then
        local t = object.color
        color = t and #t>0 and luamplib.colorconverter(t)
      end
      if not color then return end
      local cs
      if color:find" cs " or color:find"@pdf.obj" then
        if pdfmode then
          local name
          name, color = color:match"^(.-) cs (.-) sc"
          cs = format("%s 0 R", ltx.pdf.object_id( name:sub(2,-1) ))
          if cs == "0 0 R" then -- assumes colorspace.sty
            _, cs = get_spc_name_obj(name)
          end
        elseif color:find" cs " then
          local name
          name, color = color:match"^/(.-) cs (.-) sc"
          run_tex_code({ "\\mplibtmptoks\\expanded{{\\pdf_object_ref:n{", name, "}}}" }, ccexplat)
          cs = texgettoks"mplibtmptoks"
        else -- legacy: to be removed
          cs, color = color:match"pdf:bc (.-) %[(.-)%]"
        end
      else
        local t = colorsplit(color)
        cs = #t == 4 and "/DeviceCMYK" or #t == 3 and "/DeviceRGB" or "/DeviceGray"
        color = tableconcat(t," ")
      end
      pdf_literalcode("/MPlibCS%i cs %s /%s scn", pattern_colorspace(cs), color, key)
    end
    if not patt.done then
      local val = pdfmode and format("%s 0 R",index) or patterns[index]
      add_pattern_resources(key,val)
    end
    patt.done = true
  end
end

%    \end{macrocode}
%
%    Fading
%    \begin{macrocode}
pdfetcs.fading = { }
local function do_preobj_FADE (object, prescript)
  local fd_type = prescript and prescript.mplibfadetype
  local fd_stop = prescript and prescript.mplibfadestate
  if not fd_type then
    return fd_stop -- returns "stop" (if picture) or nil
  end
  local on, os, new
  if fd_type == "masking" then
    local mac = get_macro("luamplib.group."..prescript.mplibmaskname)
    on = mac:match(pdfmode and "%d+" or "{pdf:uxobj (.-)}")
    local bc = prescript.mplibmaskingbgcolor
    bc = bc and bc:gsub(":"," ")
    bc = bc and ("/BC[%s]"):format(bc):gsub(decimals,rmzeros) or ""
    os = format("<</SMask<</S/Luminosity/G %s%s>>>>",
                pdfmode and format(pdfetcs.resfmt, on) or on, bc)
  else
    local bbox = prescript.mplibfadebbox:explode":"
    local dx, dy = -bbox[1], -bbox[2]
    local vec = prescript.mplibfadevector; vec = vec and vec:explode":"
    if not vec then
      if fd_type == "linear" then
        vec = {bbox[1], bbox[2], bbox[3], bbox[2]} -- left to right
      else
        local centerx, centery = (bbox[1]+bbox[3])/2, (bbox[2]+bbox[4])/2
        vec = {centerx, centery, centerx, centery} -- center for both circles
      end
    end
    local coords = { vec[1], vec[2], vec[3], vec[4] }
    if fd_type == "linear" then
      coords = format("%f %f %f %f", tableunpack(coords))
    elseif fd_type == "circular" then
      local width, height = bbox[3]-bbox[1], bbox[4]-bbox[2]
      local radius = (prescript.mplibfaderadius or "0:"..math.sqrt(width^2+height^2)/2):explode":"
      tableinsert(coords, 3, radius[1])
      tableinsert(coords, radius[2])
      coords = format("%f %f %f %f %f %f", tableunpack(coords))
    else
      err("unknown fading method '%s'", fd_type)
    end
    fd_type = fd_type == "linear" and 2 or 3
    local extend, steps, fractions = prescript.sh_extend, tonumber(prescript.sh_step) or 1
    local ca = { (prescript.sh_color_a_1 or prescript.sh_color_a or "1"):explode":" }
    local cb = { (prescript.sh_color_b_1 or prescript.sh_color_b or "0"):explode":" }
    if steps > 1 then
      fractions = { prescript.sh_fraction_1 or 0 }
      for i=2,steps do
        fractions[i] = prescript[format("sh_fraction_%i",i)] or (i/steps)
        ca[i] = (prescript[format("sh_color_a_%i",i)] or "1"):explode":"
        cb[i] = (prescript[format("sh_color_b_%i",i)] or "0"):explode":"
      end
    end
    local matrix = prescript.sh_matrix or "1 0 0 1 0 0"
    matrix = matrix:find"%a" and get_mp_matrix(matrix) or matrix:explode()
    matrix[5] = matrix[5] + dx
    matrix[6] = matrix[6] + dy
    matrix = format("%f %f %f %f %f %f", tableunpack(matrix)) :gsub(decimals,rmzeros)
    on = sh_pdfpageresources(fd_type,"0 1","/DeviceGray",ca,cb,coords,steps,fractions,extend)
    os = format("<</PatternType 2/Shading %s/Matrix[%s]>>", format(pdfetcs.resfmt, on), matrix)
    on = update_pdfobjs(os)
    bbox = format("0 0 %f %f", bbox[3]+dx, bbox[4]+dy)
    local streamtext = format("q /Pattern cs/MPlibFd%s scn %s re f Q", on, bbox)
      :gsub(decimals,rmzeros)
    os = format("<</Pattern<</MPlibFd%s %s>>>>", on, format(pdfetcs.resfmt, on))
    on = update_pdfobjs(os)
    local resources = format(pdfetcs.resfmt, on)
    on = update_pdfobjs"<</S/Transparency/CS/DeviceGray>>"
    local attr = tableconcat{
      "/Subtype/Form",
      "/BBox[", bbox, "]",
      "/Matrix[1 0 0 1 ", format("%f %f", -dx,-dy), "]",
      "/Resources ", resources,
      "/Group ", format(pdfetcs.resfmt, on),
    } :gsub(decimals,rmzeros)
    on = update_pdfobjs(attr, streamtext)
    os = format("<</SMask<</S/Luminosity/G %s>>>>", format(pdfetcs.resfmt, on))
  end
  on, new = update_pdfobjs(os)
  local key = add_extgs_resources(on,new)
  start_pdf_code()
  pdf_literalcode("/%s gs", key)
  if fd_stop then return "standalone" end
  return "start"
end

%    \end{macrocode}
%
%    Transparency Group
%    \begin{macrocode}
pdfetcs.tr_group = { shifts = { } }
luamplib.trgroupshifts = pdfetcs.tr_group.shifts
local function do_preobj_GRP (object, prescript)
  local grstate = prescript and prescript.gr_state
  if not grstate then return end
  local trgroup = pdfetcs.tr_group
  if grstate == "start" then
    trgroup.name = prescript.mplibgroupname or "lastmplibgroup"
    trgroup.isolated, trgroup.knockout, trgroup.off = false, false, false
    trgroup.wrapped = false
    for _,v in ipairs(prescript.gr_type:gsub("%s",""):explode",+") do
      trgroup[v] = true
    end
    trgroup.bbox = prescript.mplibgroupbbox:explode":"
    put2output[[\begingroup\setbox\mplibscratchbox\hbox\bgroup\luamplibtagasgroupset]]
  elseif grstate == "stop" then
    local llx,lly,urx,ury = tableunpack(trgroup.bbox)
    put2output(tableconcat{
      "\\egroup",
      format("\\wd\\mplibscratchbox %fbp", urx-llx),
      format("\\ht\\mplibscratchbox %fbp", ury-lly),
      "\\dp\\mplibscratchbox 0pt",
    })
    local grattr
    if trgroup.off then
      grattr = ""
    else
      local on = update_pdfobjs(format("<</S/Transparency/I %s/K %s>>",
                                       trgroup.isolated, trgroup.knockout))
      grattr = format("/Group %s", pdfetcs.resfmt:format(on))
    end
    local res = gather_resources("")
    local bbox = format("%f %f %f %f", llx,lly,urx,ury) :gsub(decimals,rmzeros)
    if pdfmode then
      if trgroup.wrapped then
        put2output(tableconcat{
          "\\saveboxresource type 2 attr{/Type/XObject/Subtype/Form/FormType 1",
          "/BBox[", bbox, "]} resources{", res, "}\\mplibscratchbox",
          "\\setbox\\mplibscratchbox\\hbox{\\useboxresource\\lastsavedboxresourceindex}",
        })
      end
      put2output(tableconcat{
        "\\saveboxresource type 2 attr{/Type/XObject/Subtype/Form/FormType 1",
        "/BBox[", bbox, "]", grattr, "} resources{", res, "}\\mplibscratchbox",
        "\\luamplibtagasgroupput{",trgroup.name,"}{",
        [[\setbox\mplibscratchbox\hbox{\useboxresource\lastsavedboxresourceindex}]],
        [[\wd\mplibscratchbox 0pt\ht\mplibscratchbox 0pt\dp\mplibscratchbox 0pt]],
        [[\box\mplibscratchbox]],
        "}\\endgroup",
        "\\expandafter\\xdef\\csname luamplib.group.", trgroup.name, "\\endcsname{",
        "\\setbox\\mplibscratchbox\\hbox{\\hskip",-llx,"bp\\raise",-lly,"bp\\hbox{",
        "\\useboxresource \\the\\lastsavedboxresourceindex",
        "}}\\wd\\mplibscratchbox",urx-llx,"bp\\ht\\mplibscratchbox",ury-lly,"bp",
        "\\box\\mplibscratchbox}",
      })
    else
      if trgroup.wrapped then
        trgroup.cnt = (trgroup.cnt or 0) + 1
        local objname = format("@mplibtrgr%s", trgroup.cnt)
        put2output(tableconcat{
          "\\special{pdf:bxobj ", objname, " bbox ", bbox, "}",
          "\\unhbox\\mplibscratchbox",
          "\\special{pdf:put @resources <<", res, ">>}",
          "\\special{pdf:exobj}",
          "\\setbox\\mplibscratchbox\\hbox{\\special{pdf:uxobj ", objname, "}}",
        })
      end
      trgroup.cnt = (trgroup.cnt or 0) + 1
      local objname = format("@mplibtrgr%s", trgroup.cnt)
      put2output(tableconcat{
        "\\special{pdf:bxobj ", objname, " bbox ", bbox, "}",
        "\\unhbox\\mplibscratchbox",
        "\\special{pdf:put @resources <<", res, ">>}",
        "\\special{pdf:exobj <<", grattr, ">>}",
        "\\luamplibtagasgroupput{",trgroup.name,"}{",
        "\\special{pdf:uxobj ", objname, "}",
        "}\\endgroup",
      })
      token.set_macro("luamplib.group."..trgroup.name, tableconcat{
        "\\setbox\\mplibscratchbox\\hbox{\\hskip",-llx,"bp\\raise",-lly,"bp\\hbox{",
        "\\special{pdf:uxobj ", objname, "}",
        "}}\\wd\\mplibscratchbox",urx-llx,"bp\\ht\\mplibscratchbox",ury-lly,"bp",
        "\\box\\mplibscratchbox",
      }, "global")
    end
    trgroup.shifts[trgroup.name] = { llx, lly }
  end
  return grstate
end
function luamplib.registergroup (boxid, name, opts)
  if opts.asgroup and opts.asgroup:find"wrapped" then
    luamplib.registergroup(boxid, name, {bbox=opts.bbox, resources=opts.resources})
    run_tex_code{"\\setbox", boxid, "\\hbox bdir0{\\csname luamplib.group.", name, "\\endcsname}"}
    opts.asgroup = opts.asgroup:gsub("wrapped","")
  end
  local box = texgetbox(boxid)
  local wd, ht, dp = node.getwhd(box)
  local is_mask = opts.asgroup and opts.asgroup:find"masking"
  local res = opts.resources or ""
  res = gather_resources(res)
  local attr = { "/Type/XObject/Subtype/Form/FormType 1" }
  if type(opts.matrix) == "table" then opts.matrix = tableconcat(opts.matrix," ") end
  if type(opts.bbox) == "table" then opts.bbox = tableconcat(opts.bbox," ") end
  if opts.matrix and opts.matrix:find"%a" then
    local t = get_mp_matrix(opts.matrix)
    opts.matrix = format("%f %f %f %f %f %f",tableunpack(t))
  end
  local grtype = 3
  if opts.bbox then
    attr[#attr+1] = format("/BBox[%s]", opts.bbox)
    grtype = 2
  end
  local mpllx, mplly = get_macro'MPllx', get_macro'MPlly'
  if is_mask then
    local t = opts.matrix and opts.matrix:explode() or {1, 0, 0, 1, 0, 0}
    t[5], t[6] = t[5]+mpllx, t[6]+mplly
    opts.matrix = format("%f %f %f %f %f %f",tableunpack(t))
    mpllx, mplly = 0, 0
  end
  if opts.matrix then
    attr[#attr+1] = format("/Matrix[%s]", opts.matrix)
    grtype = opts.bbox and 4 or 1
  end
  if opts.asgroup and not opts.asgroup:find"off" then
    local t = { isolated = false, knockout = false, masking = false }
    for _,v in ipairs(opts.asgroup:gsub("%s",""):explode",+") do t[v] = true end
    local on
    if t.masking then
      on = update_pdfobjs(format("<</S/Transparency/CS%s>>", opts.colorspace or "/DeviceGray"))
    else
      local cs = opts.colorspace and ("/CS%s"):format(opts.colorspace) or ""
      on = update_pdfobjs(format("<</S/Transparency%s/I %s/K %s>>", cs, t.isolated, t.knockout))
    end
    attr[#attr+1] = format("/Group %s", pdfetcs.resfmt:format(on))
  end
  local trgroup = pdfetcs.tr_group
  trgroup.shifts[name] = { mpllx, mplly }
  local whd
  if pdfmode then
    attr = tableconcat(attr) :gsub(decimals,rmzeros)
    local index = tex.saveboxresource(boxid, attr, res, true, grtype)
    token.set_macro("luamplib.group."..name, tableconcat{
      "\\useboxresource ", index,
    }, "global")
    whd = format("%.3f %.3f 0", wd/factor, (ht+dp)/factor) :gsub(decimals,rmzeros)
  else
    trgroup.cnt = (trgroup.cnt or 0) + 1
    local objname = format("@mplibtrgr%s", trgroup.cnt)
    texsprint {
      "\\expandafter\\newbox\\csname luamplib.groupbox.", trgroup.cnt, "\\endcsname",
      "\\global\\setbox\\csname luamplib.groupbox.", trgroup.cnt, "\\endcsname",
      "\\hbox{\\unhbox ", boxid, "}\\luamplibatnextshipout{",
      "\\special{pdf:bcontent}",
      "\\special{pdf:bxobj ", objname, " width ", wd, "sp height ", ht, "sp depth ", dp, "sp}",
      "\\unhbox\\csname luamplib.groupbox.", trgroup.cnt, "\\endcsname",
      "\\special{pdf:put @resources <<", res, ">>}",
      "\\special{pdf:exobj <<", tableconcat(attr), ">>}",
      "\\special{pdf:econtent}}",
    }
    token.set_macro("luamplib.group."..name, tableconcat{
      "\\setbox\\mplibscratchbox\\hbox{\\special{pdf:uxobj ", objname, "}}",
      "\\wd\\mplibscratchbox ", wd, "sp",
      "\\ht\\mplibscratchbox ", ht, "sp",
      "\\dp\\mplibscratchbox ", dp, "sp",
      "\\box\\mplibscratchbox",
    }, "global")
    whd = format("%.3f %.3f %.3f", wd/factor, ht/factor, dp/factor) :gsub(decimals,rmzeros)
  end
  info("w/h/d of group '%s': %s", name, whd)
end

%    \end{macrocode}
%
%    |luamplib.convert|: flushing figures
%    \begin{macrocode}
do
  local function stop_special_effects(fade,opaq,over)
    if fade then -- fading
      stop_pdf_code()
    end
    if opaq then -- opacity
      pdf_literalcode(opaq)
    end
    if over then -- color
      if over:find"pdf:bc" then
        put2output"\\special{pdf:ec}"
      else
        put2output"\\special{color pop}"
      end
    end
  end

%    \end{macrocode}
%    For parsing |prescript| materials.
%    \begin{macrocode}
  local function script2table(s)
    local t = {}
    for _,i in ipairs(s:explode("\13+")) do
      local k,v = i:match("(.-)=(.*)") -- v may contain = or empty.
      if k and v and k ~= "" and not t[k] then
        t[k] = v
      end
    end
    return t
  end

%    \end{macrocode}
%    Codes below to insert PDF lieterals are mostly from \ConTeXt general,
%    with small changes when needed.
%    \begin{macrocode}
  local function pdf_textfigure(font,size,text,width,height,depth)
    text = text:gsub(".",function(c)
      return format("\\hbox{\\char%i}",string.byte(c)) -- kerning happens in metapost : false
    end)
    put2output("\\mplibtextext{%s}{%f}{%s}{%s}{%s}",font,size,text,0,0)
  end

  local bend_tolerance = 131/65536

  local rx, sx, sy, ry, tx, ty, divider = 1, 0, 0, 1, 0, 0, 1

  local function pen_characteristics(object)
    local t = mplib.pen_info(object)
    rx, ry, sx, sy, tx, ty = t.rx, t.ry, t.sx, t.sy, t.tx, t.ty
    divider = sx*sy - rx*ry
    return not (sx==1 and rx==0 and ry==0 and sy==1 and tx==0 and ty==0), t.width
  end

  local function concat(px, py) -- no tx, ty here
    return (sy*px-ry*py)/divider,(sx*py-rx*px)/divider
  end

  local function curved(ith,pth)
    local d = pth.left_x - ith.right_x
    if abs(ith.right_x - ith.x_coord - d) <= bend_tolerance and
       abs(pth.x_coord - pth.left_x - d) <= bend_tolerance then
      d = pth.left_y - ith.right_y
      if abs(ith.right_y - ith.y_coord - d) <= bend_tolerance and
         abs(pth.y_coord - pth.left_y - d) <= bend_tolerance then
        return false
      end
    end
    return true
  end

  local function flushnormalpath(path,open)
    local pth, ith
    for i=1,#path do
      pth = path[i]
      if not ith then
        pdf_literalcode("%f %f m",pth.x_coord,pth.y_coord)
      elseif curved(ith,pth) then
        pdf_literalcode("%f %f %f %f %f %f c",
          ith.right_x,ith.right_y,pth.left_x,pth.left_y,pth.x_coord,pth.y_coord)
      else
        pdf_literalcode("%f %f l",pth.x_coord,pth.y_coord)
      end
      ith = pth
    end
    if not open then
      local one = path[1]
      if curved(pth,one) then
        pdf_literalcode("%f %f %f %f %f %f c",
          pth.right_x,pth.right_y,one.left_x,one.left_y,one.x_coord,one.y_coord )
      else
        pdf_literalcode("%f %f l",one.x_coord,one.y_coord)
      end
    elseif #path == 1 then -- special case .. draw point
      local one = path[1]
      pdf_literalcode("%f %f l",one.x_coord,one.y_coord)
    end
  end

  local function flushconcatpath(path,open)
    pdf_literalcode("%f %f %f %f %f %f cm", sx, rx, ry, sy, tx ,ty)
    local pth, ith
    for i=1,#path do
      pth = path[i]
      if not ith then
        pdf_literalcode("%f %f m",concat(pth.x_coord,pth.y_coord))
      elseif curved(ith,pth) then
        local a, b = concat(ith.right_x,ith.right_y)
        local c, d = concat(pth.left_x,pth.left_y)
        pdf_literalcode("%f %f %f %f %f %f c",a,b,c,d,concat(pth.x_coord, pth.y_coord))
      else
        pdf_literalcode("%f %f l",concat(pth.x_coord, pth.y_coord))
      end
      ith = pth
    end
    if not open then
      local one = path[1]
      if curved(pth,one) then
        local a, b = concat(pth.right_x,pth.right_y)
        local c, d = concat(one.left_x,one.left_y)
        pdf_literalcode("%f %f %f %f %f %f c",a,b,c,d,concat(one.x_coord, one.y_coord))
      else
        pdf_literalcode("%f %f l",concat(one.x_coord,one.y_coord))
      end
    elseif #path == 1 then -- special case .. draw point
      local one = path[1]
      pdf_literalcode("%f %f l",concat(one.x_coord,one.y_coord))
    end
  end

%    \end{macrocode}
%    Finally, flush figures by inserting PDF literals.
%    \begin{macrocode}
  local function flush (result,flusher)
    if result then
      local figures = result.fig
      if figures then
        for f=1, #figures do
          info("flushing figure %s",f)
          local figure = figures[f]
          local objects = figure:objects()
          local fignum = tonumber(figure:filename():match("([%d]+)$") or figure:charcode() or 0)
          local miterlimit, linecap, linejoin, dashed = -1, -1, -1, false
          local bbox = figure:boundingbox()
          local llx, lly, urx, ury = bbox[1], bbox[2], bbox[3], bbox[4] -- faster than unpack
          if urx < llx then
%    \end{macrocode}
%    luamplib silently ignores this invalid figure for those
%    that do not contain |beginfig ... endfig|. (issue \#70)
%    Original code of \ConTeXt general was:
%\begin{verbatim}
%    -- invalid
%    pdf_startfigure(fignum,0,0,0,0)
%    pdf_stopfigure()
%\end{verbatim}
%    \begin{macrocode}
          else
%    \end{macrocode}
%    For legacy behavior, insert `pre-fig' \TeX\ code here.
%    \begin{macrocode}
            if tex_code_pre_mplib[f] then
              put2output(tex_code_pre_mplib[f])
            end
            pdf_startfigure(fignum,llx,lly,urx,ury)
            start_pdf_code()
            if objects then
              local savedpath = nil
              local savedhtap = nil
              for o=1,#objects do
                local object        = objects[o]
                local objecttype    = object.type
%    \end{macrocode}
%    The following 10 lines are part of |btex...etex| patch.
%    Again, colors are processed at this stage.
%    \begin{macrocode}
                local prescript     = object.prescript
                prescript = prescript and script2table(prescript) -- prescript is now a table
                local cr_over = do_preobj_CR(object,prescript) -- color
                local tr_opaq = do_preobj_TR(object,prescript) -- opacity
                local fading_ = do_preobj_FADE(object,prescript) -- fading
                local pattern_ = do_preobj_PAT(object,prescript) -- tiling pattern
                local shading_ = do_preobj_shading(object,prescript) -- shading pattern
                local trgroup = do_preobj_GRP(object,prescript) -- transparency group
                if prescript and prescript.mplibtexboxid then
                  put_tex_boxes(object,prescript)
                elseif objecttype == "start_bounds" or objecttype == "stop_bounds" then --skip
                elseif objecttype == "start_clip" then
                  local evenodd = not object.istext and object.postscript == "evenodd"
                  start_pdf_code()
                  flushnormalpath(object.path,false)
                  pdf_literalcode(evenodd and "W* n" or "W n")
                elseif objecttype == "stop_clip" then
                  stop_pdf_code()
                  miterlimit, linecap, linejoin, dashed = -1, -1, -1, false
                elseif objecttype == "special" then
%    \end{macrocode}
%    Collect \TeX\ codes that will be executed after flushing.
%    Legacy behavior.
%    \begin{macrocode}
                  if prescript and prescript.postmplibverbtex then
                    figcontents.post[#figcontents.post+1] = prescript.postmplibverbtex
                  end
                elseif objecttype == "text" then
                  local ot = object.transform -- 3,4,5,6,1,2
                  start_pdf_code()
                  pdf_literalcode("%f %f %f %f %f %f cm",ot[3],ot[4],ot[5],ot[6],ot[1],ot[2])
                  pdf_textfigure(object.font,object.dsize,object.text,object.width,object.height,object.depth)
                  stop_pdf_code()
                elseif not trgroup and fading_ ~= "stop" then
                  local evenodd, collect, both = false, false, false
                  local postscript = object.postscript
                  if not object.istext then
                    if postscript == "evenodd" then
                      evenodd = true
                    elseif postscript == "collect" then
                      collect = true
                    elseif postscript == "both" then
                      both = true
                    elseif postscript == "eoboth" then
                      evenodd = true
                      both    = true
                    end
                  end
                  if collect then
                    if not savedpath then
                      savedpath = { object.path or false }
                      savedhtap = { object.htap or false }
                    else
                      savedpath[#savedpath+1] = object.path or false
                      savedhtap[#savedhtap+1] = object.htap or false
                    end
                  else
%    \end{macrocode}
%    Removed from \ConTeXt general: color stuff.
%    \begin{macrocode}
                    local ml = object.miterlimit
                    if ml and ml ~= miterlimit then
                      miterlimit = ml
                      pdf_literalcode("%f M",ml)
                    end
                    local lj = object.linejoin
                    if lj and lj ~= linejoin then
                      linejoin = lj
                      pdf_literalcode("%i j",lj)
                    end
                    local lc = object.linecap
                    if lc and lc ~= linecap then
                      linecap = lc
                      pdf_literalcode("%i J",lc)
                    end
                    local dl = object.dash
                    if dl then
                      local d = format("[%s] %f d",tableconcat(dl.dashes or {}," "),dl.offset)
                      if d ~= dashed then
                        dashed = d
                        pdf_literalcode(dashed)
                      end
                    elseif dashed then
                      pdf_literalcode("[] 0 d")
                      dashed = false
                    end
                    local path = object.path
                    local transformed, penwidth = false, 1
                    local open = path and path[1].left_type and path[#path].right_type
                    local pen = object.pen
                    if pen then
                      if pen.type == 'elliptical' then
                        transformed, penwidth = pen_characteristics(object) -- boolean, value
                        pdf_literalcode("%f w",penwidth)
                        if objecttype == 'fill' then
                          objecttype = 'both'
                        end
                      else -- calculated by mplib itself
                        objecttype = 'fill'
                      end
                    end
%    \end{macrocode}
%    Added : shading
%    \begin{macrocode}
                    local shade_no, shade_stroke, shade_cm = do_preobj_SH(object,prescript) -- shading
                    if shade_no then
                      pdf_literalcode"q /Pattern cs"
                      objecttype = false
                    end
                    if transformed then
                      start_pdf_code()
                    end
                    if path then
                      if savedpath then
                        for i=1,#savedpath do
                          local path = savedpath[i]
                          if transformed then
                            flushconcatpath(path,open)
                          else
                            flushnormalpath(path,open)
                          end
                        end
                        savedpath = nil
                      end
                      if transformed then
                        flushconcatpath(path,open)
                      else
                        flushnormalpath(path,open)
                      end
                      if objecttype == "fill" then
                        pdf_literalcode(evenodd and "h f*" or "h f")
                      elseif objecttype == "outline" then
                        if both then
                          pdf_literalcode(evenodd and "h B*" or "h B")
                        else
                          pdf_literalcode(open and "S" or "h S")
                        end
                      elseif objecttype == "both" then
                        pdf_literalcode(evenodd and "h B*" or "h B")
                      end
                    end
                    if transformed then
                      stop_pdf_code()
                    end
                    local path = object.htap
%    \end{macrocode}
%    How can we generate an |htap| object? Please let us know if you have succeeded.
%    \begin{macrocode}
                    if path then
                      if transformed then
                        start_pdf_code()
                      end
                      if savedhtap then
                        for i=1,#savedhtap do
                          local path = savedhtap[i]
                          if transformed then
                            flushconcatpath(path,open)
                          else
                            flushnormalpath(path,open)
                          end
                        end
                        savedhtap = nil
                        evenodd   = true
                      end
                      if transformed then
                        flushconcatpath(path,open)
                      else
                        flushnormalpath(path,open)
                      end
                      if objecttype == "fill" then
                        pdf_literalcode(evenodd and "h f*" or "h f")
                      elseif objecttype == "outline" then
                        pdf_literalcode(open and "S" or "h S")
                      elseif objecttype == "both" then
                        pdf_literalcode(evenodd and "h B*" or "h B")
                      end
                      if transformed then
                        stop_pdf_code()
                      end
                    end
%    \end{macrocode}
%    Added to \ConTeXt general: post-object colors and shading stuff.
%    Beware |q ... Q| scope.
%    \begin{macrocode}
                    if shade_no then -- shading
                      pdf_literalcode("W%s %s %s/MPlibSh%s sh Q",
                        evenodd and "*" or "",
                        shade_stroke and "s" or "n",
                        shade_cm and shade_cm.." cm " or "",
                        shade_no)
                    end
                  end
                end
                if fading_ == "start" then
                  pdfetcs.fading.specialeffects = {fading_, tr_opaq, cr_over}
                elseif trgroup == "start" then
                  pdfetcs.tr_group.specialeffects = {fading_, tr_opaq, cr_over}
                elseif fading_ == "stop" then
                  local se = pdfetcs.fading.specialeffects
                  if se then stop_special_effects(se[1], se[2], se[3]) end
                elseif trgroup == "stop" then
                  local se = pdfetcs.tr_group.specialeffects
                  if se then stop_special_effects(se[1], se[2], se[3]) end
                else
                  stop_special_effects(fading_, tr_opaq, cr_over)
                end
                if fading_ or trgroup then -- extgs resetted
                  miterlimit, linecap, linejoin, dashed = -1, -1, -1, false
                end
              end
            end
            stop_pdf_code()
            pdf_stopfigure()
%    \end{macrocode}
%    output collected materials to PDF, plus legacy |verbatimtex| code.
%    \begin{macrocode}
            for _,v in ipairs(figcontents) do
              if type(v) == "table" then
                texsprint"\\mplibtoPDF{"; texsprint(v[1], v[2]); texsprint"}"
              else
                texsprint(v)
              end
            end
            if #figcontents.post > 0 then texsprint(figcontents.post) end
            figcontents = { post = { } }
          end
        end
      end
    end
  end

  function luamplib.convert (result, flusher)
    flush(result, flusher)
    return true -- done
  end
end

function luamplib.colorconverter (cr)
  local n = #cr
  if n == 4 then
    local c, m, y, k = cr[1], cr[2], cr[3], cr[4]
    return format("%.3f %.3f %.3f %.3f k %.3f %.3f %.3f %.3f K",c,m,y,k,c,m,y,k), "0 g 0 G"
  elseif n == 3 then
    local r, g, b = cr[1], cr[2], cr[3]
    return format("%.3f %.3f %.3f rg %.3f %.3f %.3f RG",r,g,b,r,g,b), "0 g 0 G"
  else
    local s = cr[1]
    return format("%.3f g %.3f G",s,s), "0 g 0 G"
  end
end
%    \end{macrocode}
%
% \iffalse
%</lua>
% \fi
%
%    \subsection{\texorpdfstring{\TeX}{TeX} package}
%
%
% \iffalse
%<*package>
% \fi
%
%    First we need to load some packages.
%
%    \begin{macrocode}
\ifcsname ProvidesPackage\endcsname
%    \end{macrocode}
% We need \LaTeX\ 2024-06-01 as we use |ltx.pdf.object_id| when pdfmanagement is loaded.
% But as \pkg{fp} package does not accept an option, we do not append the date option.
%    \begin{macrocode}
  \NeedsTeXFormat{LaTeX2e}
  \ProvidesPackage{luamplib}
    [2026/07/10 v2.42.4 mplib package for LuaTeX]
\fi
\ifdefined\newluafunction\else
  \input ltluatex
\fi
%    \end{macrocode}
%
% In DVI mode, a new XObject (mppattern, mplibgroup) must be encapsulated in an \cs{hbox}.
% But this should not affect typesetting. So we use Hook mechanism provided by \LaTeX\ kernel.
% In Plain, \pkg{atbegshi.sty} is loaded.
%    \begin{macrocode}
\ifnum\outputmode=0
  \ifdefined\AddToHookNext
    \def\luamplibatnextshipout{\AddToHookNext{shipout/background}}
    \def\luamplibatfirstshipout{\AddToHook{shipout/firstpage}}
    \def\luamplibateveryshipout{\AddToHook{shipout/background}}
  \else
    \input atbegshi.sty
    \def\luamplibatnextshipout#1{\AtBeginShipoutNext{\AtBeginShipoutAddToBox{#1}}}
    \let\luamplibatfirstshipout\AtBeginShipoutFirst
    \def\luamplibateveryshipout#1{\AtBeginShipout{\AtBeginShipoutAddToBox{#1}}}
  \fi
\fi
%    \end{macrocode}
%
%    Loading of lua code.
%    \begin{macrocode}
\directlua{require("luamplib")}
%    \end{macrocode}
%
%    legacy commands. Seems we don't need it, but no harm.
%    \begin{macrocode}
\ifx\pdfoutput\undefined
  \let\pdfoutput\outputmode
\fi
\ifx\pdfliteral\undefined
  \protected\def\pdfliteral{\pdfextension literal}
\fi
%    \end{macrocode}
%
%    Set the format for \metapost.
%    \begin{macrocode}
\def\mplibsetformat#1{\directlua{luamplib.setformat("#1")}}
%    \end{macrocode}
%
%    luamplib works in both PDF and DVI mode,
%    but only DVIPDFMx is supported currently among a number of DVI tools.
%    So we output a info.
%    \begin{macrocode}
\ifnum\pdfoutput>0
  \let\mplibtoPDF\pdfliteral
\else
  \def\mplibtoPDF#1{\special{pdf:literal direct #1}}
  \ifcsname PackageInfo\endcsname
    \PackageInfo{luamplib}{only dvipdfmx is supported currently}
  \else
    \immediate\write-1{luamplib Info: only dvipdfmx is supported currently}
  \fi
\fi
%    \end{macrocode}
%
%    To make |mplibcode| typeset always in horizontal mode.
%    \begin{macrocode}
\def\mplibforcehmode{\let\prependtomplibbox\leavevmode}
\def\mplibnoforcehmode{\let\prependtomplibbox\relax}
\mplibnoforcehmode
%    \end{macrocode}
%
%    Catcode. We want to allow comment sign in |mplibcode|.
%    \begin{macrocode}
\def\mplibsetupcatcodes{%
  %catcode`\{=12 %catcode`\}=12
  \catcode`\#=12 \catcode`\^=12 \catcode`\~=12 \catcode`\_=12
  \catcode`\&=12 \catcode`\$=12 \catcode`\%=12 \catcode`\^^M=12
}
%    \end{macrocode}
%
%    Make |btex...etex| box zero-metric. Plus address the issue \#189.
%    |bdir 0| seems to be redundant, but no harm.
%    \begin{macrocode}
\ifnum\outputmode>0 %
  \def\mplibputtextbox#1#2#3#4{%
    \pdfextension save\relax
    \vbox to 0pt{\vss
      \hbox bdir0 to 0pt{\kern#2bp\pdfextension setmatrix{#4}\raise\dp#1\copy#1\hss}%
      \kern#3bp}%
    \pdfextension restore\relax}
\else
  \def\mplibputtextbox#1#2#3#4{%
    \special{pdf:btrans matrix #4 #2 #3}%
    \vbox to 0pt{\vss\hbox bdir0 to 0pt{\raise\dp#1\copy#1\hss}}%
    \special{pdf:etrans}}
\fi
%    \end{macrocode}
%
%    use Transparency Group
%    \begin{macrocode}
\protected\def\usemplibgroup#1#{\usemplibgroupmain}
\def\usemplibgroupmain#1{%
  \prependtomplibbox\hbox dir TLT\bgroup
  \csname luamplib.group.#1\endcsname
  \egroup
}
\protected\def\mplibgroup#1{%
  \begingroup
  \def\MPllx{0}\def\MPlly{0}%
  \def\mplibgroupname{#1}%
  \mplibgroupgetnexttok
}
\def\mplibgroupgetnexttok{\futurelet\nexttok\mplibgroupbranch}
\def\mplibgroupskipspace{\afterassignment\mplibgroupgetnexttok\let\nexttok= }
\def\mplibgroupbranch{%
  \ifx [\nexttok
    \expandafter\mplibgroupopts
  \else
    \ifx\mplibsptoken\nexttok
      \expandafter\expandafter\expandafter\mplibgroupskipspace
    \else
      \let\mplibgroupoptions\empty
      \expandafter\expandafter\expandafter\mplibgroupmain
    \fi
  \fi
}
\def\mplibgroupopts[#1]{\def\mplibgroupoptions{#1}\mplibgroupmain}
\def\mplibgroupmain{\setbox\mplibscratchbox\hbox\bgroup\ignorespaces}
\protected\def\endmplibgroup{\egroup
  \directlua{ luamplib.registergroup(
    \the\mplibscratchbox, '\mplibgroupname', {\mplibgroupoptions}
  )}%
  \endgroup
}
%    \end{macrocode}
%
%    Patterns
%    \begin{macrocode}
{\def\:{\global\let\mplibsptoken= } \: }
\protected\def\mppattern#1{%
  \begingroup
  \def\MPllx{0}\def\MPlly{0}%
  \def\mplibpatternname{#1}%
  \mplibpatterngetnexttok
}
\def\mplibpatterngetnexttok{\futurelet\nexttok\mplibpatternbranch}
\def\mplibpatternskipspace{\afterassignment\mplibpatterngetnexttok\let\nexttok= }
\def\mplibpatternbranch{%
  \ifx [\nexttok
    \expandafter\mplibpatternopts
  \else
    \ifx\mplibsptoken\nexttok
      \expandafter\expandafter\expandafter\mplibpatternskipspace
    \else
      \let\mplibpatternoptions\empty
      \expandafter\expandafter\expandafter\mplibpatternmain
    \fi
  \fi
}
\def\mplibpatternopts[#1]{%
  \def\mplibpatternoptions{#1}%
  \mplibpatternmain
}
\def\mplibpatternmain{%
  \setbox\mplibscratchbox\hbox\bgroup\ignorespaces
}
\protected\def\endmppattern{%
  \egroup
  \directlua{ luamplib.registerpattern(
    \the\mplibscratchbox, '\mplibpatternname', {\mplibpatternoptions}
  )}%
  \endgroup
}
%    \end{macrocode}
%
%    simple way to use \mplib:
%    |\mpfig draw fullcircle scaled 10; \endmpfig|
%    \begin{macrocode}
\def\mpfiginstancename{@mpfig}
\protected\def\mpfig{%
  \begingroup
  \futurelet\nexttok\mplibmpfigbranch
}
\def\mplibmpfigbranch{%
  \ifx *\nexttok
    \expandafter\mplibprempfig
  \else
    \ifx [\nexttok
      \expandafter\expandafter\expandafter\mplibgobbleoptsmpfig
    \else
      \expandafter\expandafter\expandafter\mplibmainmpfig
    \fi
  \fi
}
\def\mplibgobbleoptsmpfig[#1]{\mplibmainmpfig}
\def\mplibmainmpfig{%
  \begingroup
  \mplibsetupcatcodes
  \mplibdomainmpfig
}
\long\def\mplibdomainmpfig#1\endmpfig{%
  \endgroup
  \directlua{
    local legacy = luamplib.legacyverbatimtex
    local everympfig = luamplib.everymplib["\mpfiginstancename"] or ""
    local everyendmpfig = luamplib.everyendmplib["\mpfiginstancename"] or ""
    luamplib.legacyverbatimtex = false
    luamplib.everymplib["\mpfiginstancename"] = ""
    luamplib.everyendmplib["\mpfiginstancename"] = ""
    luamplib.process_mplibcode(
    "beginfig(0) "..everympfig.." "..[===[\unexpanded{#1}]===].." "..everyendmpfig.." endfig;",
    "\mpfiginstancename")
    luamplib.legacyverbatimtex = legacy
    luamplib.everymplib["\mpfiginstancename"] = everympfig
    luamplib.everyendmplib["\mpfiginstancename"] = everyendmpfig
  }%
  \endgroup
}
\def\mplibprempfig#1{%
  \begingroup
  \mplibsetupcatcodes
  \mplibdoprempfig
}
\long\def\mplibdoprempfig#1\endmpfig{%
  \endgroup
  \directlua{
    local legacy = luamplib.legacyverbatimtex
    local everympfig = luamplib.everymplib["\mpfiginstancename"]
    local everyendmpfig = luamplib.everyendmplib["\mpfiginstancename"]
    luamplib.legacyverbatimtex = false
    luamplib.everymplib["\mpfiginstancename"] = ""
    luamplib.everyendmplib["\mpfiginstancename"] = ""
    luamplib.process_mplibcode([===[\unexpanded{#1}]===],"\mpfiginstancename")
    luamplib.legacyverbatimtex = legacy
    luamplib.everymplib["\mpfiginstancename"] = everympfig
    luamplib.everyendmplib["\mpfiginstancename"] = everyendmpfig
  }%
  \endgroup
}
\protected\def\endmpfig{endmpfig}
%    \end{macrocode}
%
%    The Plain-specific stuff.
%    \begin{macrocode}
\unless\ifcsname ver@luamplib.sty\endcsname
  \def\mplibcodegetinstancename[#1]{\xdef\currentmpinstancename{#1}\mplibcodeindeed}
  \protected\def\mplibcode{%
    \begingroup
    \futurelet\nexttok\mplibcodebranch
  }
  \def\mplibcodebranch{%
    \ifx [\nexttok
      \expandafter\mplibcodegetinstancename
    \else
      \global\let\currentmpinstancename\empty
      \expandafter\mplibcodeindeed
    \fi
  }
  \def\mplibcodeindeed{%
    \begingroup
    \mplibsetupcatcodes
    \mplibdocode
  }
  \long\def\mplibdocode#1\endmplibcode{%
    \endgroup
    \directlua{luamplib.process_mplibcode([===[\unexpanded{#1}]===],"\currentmpinstancename")}%
    \endgroup
  }
  \protected\def\endmplibcode{endmplibcode}
\else
%    \end{macrocode}
%
%    The \LaTeX-specific part: a new environment.
%    \begin{macrocode}
  \newenvironment{mplibcode}[1][]{%
    \xdef\currentmpinstancename{#1}%
    \mplibtmptoks{}\ltxdomplibcode
  }{}
  \def\ltxdomplibcode{%
    \begingroup
    \mplibsetupcatcodes
    \ltxdomplibcodeindeed
  }
  \def\mplib@mplibcode{mplibcode}
  \long\def\ltxdomplibcodeindeed#1\end#2{%
    \endgroup
    \mplibtmptoks\expandafter{\the\mplibtmptoks#1}%
    \def\mplibtemp@a{#2}%
    \ifx\mplib@mplibcode\mplibtemp@a
      \directlua{luamplib.process_mplibcode([===[\the\mplibtmptoks]===],"\currentmpinstancename")}%
      \end{mplibcode}%
    \else
      \mplibtmptoks\expandafter{\the\mplibtmptoks\end{#2}}%
      \expandafter\ltxdomplibcode
    \fi
  }
\fi
%    \end{macrocode}
%
%    User settings.
%    \begin{macrocode}
\def\mplibshowlog#1{\directlua{
    local s = string.lower("#1")
    if s == "enable" or s == "true" or s == "yes" then
      luamplib.showlog = true
    else
      luamplib.showlog = false
    end
}}
\def\mpliblegacybehavior#1{\directlua{
    local s = string.lower("#1")
    if s == "enable" or s == "true" or s == "yes" then
      luamplib.legacyverbatimtex = true
    else
      luamplib.legacyverbatimtex = false
    end
}}
\def\mplibverbatim#1{\directlua{
    local s = string.lower("#1")
    if s == "enable" or s == "true" or s == "yes" then
      luamplib.verbatiminput = true
    else
      luamplib.verbatiminput = false
    end
}}
\newtoks\mplibtmptoks
%    \end{macrocode}
%
%    \cs{everymplib} \& \cs{everyendmplib}: macros resetting
%    |luamplib.every(end)mplib| tables
%
%    \begin{macrocode}
\ifcsname ver@luamplib.sty\endcsname
  \protected\def\everymplib{%
    \begingroup
    \mplibsetupcatcodes
    \mplibdoeverymplib
  }
  \protected\def\everyendmplib{%
    \begingroup
    \mplibsetupcatcodes
    \mplibdoeveryendmplib
  }
  \newcommand\mplibdoeverymplib[2][]{%
    \endgroup
    \directlua{
      luamplib.everymplib["#1"] = [===[\unexpanded{#2}]===]
    }%
  }
  \newcommand\mplibdoeveryendmplib[2][]{%
    \endgroup
    \directlua{
      luamplib.everyendmplib["#1"] = [===[\unexpanded{#2}]===]
    }%
  }
\else
  \def\mplibgetinstancename[#1]{\def\currentmpinstancename{#1}}
  \protected\def\everymplib#1#{%
    \ifx\empty#1\empty \mplibgetinstancename[]\else \mplibgetinstancename#1\fi
    \begingroup
    \mplibsetupcatcodes
    \mplibdoeverymplib
  }
  \long\def\mplibdoeverymplib#1{%
    \endgroup
    \directlua{
      luamplib.everymplib["\currentmpinstancename"] = [===[\unexpanded{#1}]===]
    }%
  }
  \protected\def\everyendmplib#1#{%
    \ifx\empty#1\empty \mplibgetinstancename[]\else \mplibgetinstancename#1\fi
    \begingroup
    \mplibsetupcatcodes
    \mplibdoeveryendmplib
  }
  \long\def\mplibdoeveryendmplib#1{%
    \endgroup
    \directlua{
      luamplib.everyendmplib["\currentmpinstancename"] = [===[\unexpanded{#1}]===]
    }%
  }
\fi
%    \end{macrocode}
%
%    \TeX\ macros for dimen/color
%    \begin{macrocode}
\def\mpdim#1{ runscript("luamplibdimen{#1}") }
\def\mpcolor#1#{\domplibcolor{#1}}
\def\domplibcolor#1#2{ runscript("luamplibcolor{#1{#2}}") }
%    \end{macrocode}
%
%    \mplib's number system. Now |binary| has gone away.
%    \begin{macrocode}
\def\mplibnumbersystem#1{\directlua{
  local t = "#1"
  if t == "binary" then t = "decimal" end
  luamplib.numbersystem = t
}}
%    \end{macrocode}
%
%    Settings for |.mp| cache files.
%    \begin{macrocode}
\def\mplibmakenocache#1{\mplibdomakenocache #1,\stop,}
\def\mplibdomakenocache#1,{%
  \ifx\empty#1\empty
    \expandafter\mplibdomakenocache
  \else
    \ifx\stop#1\else
      \directlua{luamplib.noneedtoreplace["#1.mp"]=true}%
      \expandafter\expandafter\expandafter\mplibdomakenocache
    \fi
  \fi
}
\def\mplibcancelnocache#1{\mplibdocancelnocache #1,\stop,}
\def\mplibdocancelnocache#1,{%
  \ifx\empty#1\empty
    \expandafter\mplibdocancelnocache
  \else
    \ifx\stop#1\else
      \directlua{luamplib.noneedtoreplace["#1.mp"]=false}%
      \expandafter\expandafter\expandafter\mplibdocancelnocache
    \fi
  \fi
}
\def\mplibcachedir#1{\directlua{luamplib.getcachedir("\unexpanded{#1}")}}
%    \end{macrocode}
%
%    More user settings.
%    \begin{macrocode}
\def\mplibtextextlabel#1{\directlua{
    local s = string.lower("#1")
    if s == "enable" or s == "true" or s == "yes" then
      luamplib.textextlabel = true
    else
      luamplib.textextlabel = false
    end
}}
\def\mplibcodeinherit#1{\directlua{
    local s = string.lower("#1")
    if s == "enable" or s == "true" or s == "yes" then
      luamplib.codeinherit = true
    else
      luamplib.codeinherit = false
    end
}}
\def\mplibglobaltextext#1{\directlua{
    local s = string.lower("#1")
    if s == "enable" or s == "true" or s == "yes" then
      luamplib.globaltextext = true
    else
      luamplib.globaltextext = false
    end
}}
%    \end{macrocode}
%
%    The followings are from \ConTeXt general, mostly.
%
%    We use a dedicated scratchbox.
%    \begin{macrocode}
\ifx\mplibscratchbox\undefined \newbox\mplibscratchbox \fi
%    \end{macrocode}
%
%    We encapsulate the literals.
%    \begin{macrocode}
\def\mplibstarttoPDF#1#2#3#4{%
  \prependtomplibbox
  \hbox dir TLT\bgroup
  \xdef\MPllx{#1}\xdef\MPlly{#2}%
  \xdef\MPurx{#3}\xdef\MPury{#4}%
  \xdef\MPwidth{\the\dimexpr#3bp-#1bp\relax}%
  \xdef\MPheight{\the\dimexpr#4bp-#2bp\relax}%
  \parskip0pt%
  \leftskip0pt%
  \parindent0pt%
  \everypar{}%
  \setbox\mplibscratchbox\vbox\bgroup
  \noindent
}
\def\mplibstoptoPDF{%
  \par
  \egroup %
  \setbox\mplibscratchbox\hbox %
    {\hskip-\MPllx bp%
     \raise-\MPlly bp%
     \box\mplibscratchbox}%
  \setbox\mplibscratchbox\vbox to \MPheight
    {\vfill
     \hsize\MPwidth
     \wd\mplibscratchbox0pt%
     \ht\mplibscratchbox0pt%
     \dp\mplibscratchbox0pt%
     \box\mplibscratchbox}%
  \wd\mplibscratchbox\MPwidth
  \ht\mplibscratchbox\MPheight
  \box\mplibscratchbox
  \egroup
}
%    \end{macrocode}
%
%    Text items have a special handler.
%    \begin{macrocode}
\def\mplibtextext#1#2#3#4#5{%
  \begingroup
  \setbox\mplibscratchbox\hbox
    {\font\temp=#1 at #2bp%
     \temp
     #3}%
  \setbox\mplibscratchbox\hbox
    {\hskip#4 bp%
     \raise#5 bp%
     \box\mplibscratchbox}%
  \wd\mplibscratchbox0pt%
  \ht\mplibscratchbox0pt%
  \dp\mplibscratchbox0pt%
  \box\mplibscratchbox
  \endgroup
}
%    \end{macrocode}
%
%    Input |luamplib.cfg| when it exists.
%    \begin{macrocode}
\openin0=luamplib.cfg
\ifeof0 \else
  \closein0
  \input luamplib.cfg
\fi
%    \end{macrocode}
%
%    Code for \pkg{tagpdf}
%    \begin{macrocode}
\def\luamplibtagtextboxset#1#2{#2}
\let\luamplibnotagtextboxset\luamplibtagtextboxset
\let\luamplibtagasgroupset\relax
\let\luamplibtagasgroupput\luamplibtagtextboxset
\ifcsname SuspendTagging\endcsname\else\endinput\fi
\ifcsname ver@tagpdf.sty\endcsname \else
  \ExplSyntaxOn
  \keys_define:nn{luamplib/tagging}
    {
      ,alt          .code:n = { }
      ,actualtext   .code:n = { }
      ,artifact     .code:n = { }
      ,text         .code:n = { }
      ,off          .code:n = { }
      ,tag          .code:n = { }
      ,adjust-BBox  .code:n = { }
      ,tagging-setup .code:n = { }
      ,instance     .code:n = { \tl_gset:Nn \currentmpinstancename {#1} }
      ,instancename .meta:n = { instance = {#1} }
      ,unknown      .code:n = { \tl_gset:NV \currentmpinstancename \l_keys_key_str }
    }
  \RenewDocumentCommand\mplibcode{O{}}
    {
      \tl_gclear:N \currentmpinstancename
      \keys_set:ne{luamplib/tagging}{#1}
      \mplibtmptoks{}\ltxdomplibcode
    }
  \cs_set_eq:NN \mplibalttext \use_none:n
  \cs_set_eq:NN \mplibactualtext \use_none:n
%    \end{macrocode}
%  2025/12/05:
%  |\begin{center}\mpfig ...\endmpfig\end{center}| raises an Error!
%  as we issue |\everypar{}| before flushing literals out.
%  It is related to \cs{partokencontext=2} recently introduced by \LaTeX.
%  Why we used |vbox| initially? where |hbox| seems to be sufficient.  Anyway,
%  among various solutions including |\partokencontext\z@|, |\let\par\@@par|, and \cs{endgraf},
%  we here attempt to address the issue by adding the following line,
%  which \LaTeX's \cs{everypar} should have done.
%    \begin{macrocode}
  \tl_put_left:Nn \mplibstoptoPDF \@newlistfalse
  \ExplSyntaxOff
  \endinput\fi
\ExplSyntaxOn
\tl_new:N \l__luamplib_tag_envname_tl
\tl_new:N \l__luamplib_tag_alt_tl
\tl_new:N \l__luamplib_tag_alt_dflt_tl
\tl_new:N \l__luamplib_tag_actual_tl
\tl_new:N \l__luamplib_tag_struct_tl
\tl_set:Nn\l__luamplib_tag_struct_tl {Figure}
\bool_new:N \l__luamplib_tag_usetext_bool
\bool_new:N \l__luamplib_tag_bboxcorr_bool
\seq_new:N  \l__luamplib_tag_bboxcorr_seq
\tl_new:N \l__luamplib_tag_bbox_draw_tl
\tl_new:N \l__luamplib_BBox_llx_tl
\tl_new:N \l__luamplib_BBox_lly_tl
\tl_new:N \l__luamplib_BBox_urx_tl
\tl_new:N \l__luamplib_BBox_ury_tl
\msg_new:nnn {luamplib}{figure-text-reuse}
{
  tex-text~box~#1~probably~is~incorrectly~tagged.~
  Reusing~a~box~in~text~mode~is~strongly~discouraged.~
  Check~the~resulting~PDF.
}
\msg_new:nnn {luamplib}{mplibgroup-text-mode}
{
  mplibgroup~'#1'~probably~is~incorrectly~tagged.~
  Using~mplibgroup~with~text~mode~is~not~recommended.~
  Check~the~resulting~PDF.
}
\msg_new:nnn{luamplib}{alt-text-missing}
{
  Alternate~text~for~#1~is~missing.~
  Using~the~default~value~'#2'~instead.
}
%    \end{macrocode}
% Sockets for tex-text boxes.
%    \begin{macrocode}
\socket_new:nn{tagsupport/luamplib/textext/set}{2}
\socket_new:nn{tagsupport/luamplib/textext/put}{2}
\socket_new_plug:nnn{tagsupport/luamplib/textext/set}{default}
{
%    \end{macrocode}
% TODO: we check |text| mode here.
% If we tag text boxes for all modes, we will get a lot of structure-has-no-parent warning;
% no good-looking, though it seems to be no harm.
%    \begin{macrocode}
  \bool_if:NTF \l__luamplib_tag_usetext_bool
  {
    \tag_mc_end_push:
    \tag_struct_begin:n{tag=NonStruct, stash, parent-tag=text}
    \cs_gset_nopar:cpe {luamplib.taggedbox.#1} {\tag_get:n{struct_num}}
%    \end{macrocode}
% TODO: We force an MC. Otherwise |a| and |b| in |btex a $x$ b etex| are not tagged.
%    \begin{macrocode}
    \tag_mc_begin:n{tag=text}
    #2
    \tag_mc_end:
    \tag_struct_end:
    \tag_mc_begin_pop:n{}
  }
  {
    \tag_suspend:n{\luamplibtagtextboxset}
    #2
    \tag_resume:n{\luamplibtagtextboxset}
  }
}
\socket_new_plug:nnn{tagsupport/luamplib/textext/put}{default}
{
  \bool_lazy_and:nnTF
  { \l__luamplib_tag_usetext_bool }
  { \cs_if_free_p:c {luamplib.notaggedbox.#1} }
  {
    \tag_resume:n{\mplibputtextbox}
    \tag_mc_end:
    \cs_if_exist:cTF {luamplib.taggedbox.#1}
    {
      \exp_args:Nc \tag_struct_use_num:n {luamplib.taggedbox.#1}
      #2
      \cs_undefine:c {luamplib.taggedbox.#1}
    }
    {
      \msg_warning:nnn{luamplib}{figure-text-reuse}{#1}
      \tag_mc_begin:n{}
      \int_set:Nn \l_tmpa_int {#1}
      \tag_mc_reset_box:N \l_tmpa_int
      #2
      \tag_mc_end:
    }
    \tag_mc_begin:n{artifact}
  }
  {
    \int_set:Nn \l_tmpa_int {#1}
    \tag_mc_reset_box:N \l_tmpa_int
    #2
  }
}
\socket_assign_plug:nn{tagsupport/luamplib/textext/set}{default}
\socket_assign_plug:nn{tagsupport/luamplib/textext/put}{default}
\cs_set_nopar:Npn \luamplibtagtextboxset
{
  \tag_socket_use:nnn{luamplib/textext/set}
}
%    \end{macrocode}
% For tex-text boxes starting with |[taggingoff]|, which we will not tag at all.
% They will be just in the artifact MC-chunks.
%    \begin{macrocode}
\cs_set_nopar:Npn \luamplibnotagtextboxset #1 #2
{
  \bool_set_eq:NN \l_tmpa_bool \l__luamplib_tag_usetext_bool
  \bool_set_false:N \l__luamplib_tag_usetext_bool
  \tag_socket_use:nnn{luamplib/textext/set}{#1}{#2}
  \cs_gset_nopar:cpn {luamplib.notaggedbox.#1}{#1}
  \bool_set_eq:NN \l__luamplib_tag_usetext_bool \l_tmpa_bool
}
\sys_if_output_pdf:TF
{
  \cs_set_nopar:Npn \mplibputtextbox #1 #2 #3 #4
  {
    \pdfextension save\relax
    \vbox to 0pt{\vss
      \hbox bdir0 to 0pt{\kern #2bp \pdfextension setmatrix {#4}
        \socket_use:nnn{tagsupport/luamplib/textext/put}{#1}{\raise\dp#1\copy#1}\hss}
      \kern #3bp}
    \pdfextension restore\relax
  }
}
{
  \cs_set_nopar:Npn \mplibputtextbox #1 #2 #3 #4
  {
    \special{pdf:btrans~matrix~#4~#2~#3}
    \vbox to 0pt{\vss\hbox bdir0 to 0pt{
      \socket_use:nnn{tagsupport/luamplib/textext/put}{#1}{\raise\dp#1\copy#1}\hss}}
    \special{pdf:etrans}
  }
}
%    \end{macrocode}
% TODO: Not sure whether asgroup/mplibgroup with |text| mode will be tagged correctly.
%       Probably not. At least, this will raise a warning.
%    \begin{macrocode}
\cs_set_nopar:Npn \luamplibtagasgroupset
{
  \bool_set_false:N \l__luamplib_tag_usetext_bool
}
\cs_set_nopar:Npn \luamplibtagasgroupput
{
  \bool_if:NT \l__luamplib_tag_usetext_bool { \tag_resume:n{\luamplibtagasgroupput} }
  \tag_socket_use:nnn{luamplib/mplibgroup/put}
}
%    \end{macrocode}
% A socket for mplibgroup. Again, we issue a warning upon |text| mode.
%    \begin{macrocode}
\socket_new:nn{tagsupport/luamplib/mplibgroup/put}{2}
\socket_new_plug:nnn{tagsupport/luamplib/mplibgroup/put}{default}
{
  \cs_if_free:cT {luamplib.mplibgroup.text.#1}
  {
    \msg_warning:nnn {luamplib} {mplibgroup-text-mode} {#1}
    \cs_gset_nopar:cpn {luamplib.mplibgroup.text.#1} {#1}
  }
  \tag_mc_end:
  \tag_mc_begin:n{tag=text}
  #2
  \tag_mc_end:
  \tag_mc_begin:n{artifact}
}
\socket_assign_plug:nn{tagsupport/luamplib/mplibgroup/put}{default}
%    \end{macrocode}
% A macro for BBox attribute
%    \begin{macrocode}
\cs_set_nopar:Npn \__luamplib_tag_bbox_attribute:n #1
{
  \tl_set:Ne \l_tmpa_tl {luamplib.BBox.\tag_get:n{struct_num}}
  \tex_savepos:D
  \property_record:ee{\l_tmpa_tl}{xpos,ypos}
  \tl_set:Ne \l__luamplib_BBox_llx_tl
    { \dim_to_decimal_in_bp:n { \property_ref:een {\l_tmpa_tl}{xpos}{0}sp } }
  \tl_set:Ne \l__luamplib_BBox_lly_tl
    { \dim_to_decimal_in_bp:n { \property_ref:een {\l_tmpa_tl}{ypos}{0}sp - \dp#1 } }
  \tl_set:Ne \l__luamplib_BBox_urx_tl
    { \dim_to_decimal_in_bp:n { \l__luamplib_BBox_llx_tl bp + \wd#1 } }
  \tl_set:Ne \l__luamplib_BBox_ury_tl
    { \dim_to_decimal_in_bp:n { \l__luamplib_BBox_lly_tl bp + \ht#1 + \dp#1 } }
  \bool_if:NT \l__luamplib_tag_bboxcorr_bool
  {
    \int_zero:N \l_tmpa_int
    \tl_map_inline:nn
    {
      \l__luamplib_BBox_llx_tl
      \l__luamplib_BBox_lly_tl
      \l__luamplib_BBox_urx_tl
      \l__luamplib_BBox_ury_tl
    }
    {
      \int_incr:N \l_tmpa_int
      \tl_set:Ne ##1
      {
        \fp_eval:n
        {
          ##1
          +
          \dim_to_decimal_in_bp:n { \seq_item:NV \l__luamplib_tag_bboxcorr_seq \l_tmpa_int }
        }
      }
    }
  }
  \tag_struct_gput:ene {\tag_get:n{struct_num}} {attribute}
  {
    /O /Layout /BBox [
      \l__luamplib_BBox_llx_tl\c_space_tl
      \l__luamplib_BBox_lly_tl\c_space_tl
      \l__luamplib_BBox_urx_tl\c_space_tl
      \l__luamplib_BBox_ury_tl
    ]
  }
  \bool_if:NT \l__tag_graphic_debug_bool
  {
    \iow_log:e
    {
      luamplib/tagging~debug:~BBox~of~structure~\tag_get:n{struct_num}~is~
      \l__luamplib_BBox_llx_tl\c_space_tl
      \l__luamplib_BBox_lly_tl\c_space_tl
      \l__luamplib_BBox_urx_tl\c_space_tl
      \l__luamplib_BBox_ury_tl
    }
    \sys_if_output_pdf:TF
    {
      \tl_set:Ne \l__luamplib_tag_bbox_draw_tl
      {
        \pdfextension save\relax
        \opacity_select:n{0.5} \color_select:n{red}
        \pdfextension literal~text
        {
          \l__luamplib_BBox_llx_tl\c_space_tl
          \l__luamplib_BBox_lly_tl\c_space_tl
          \fp_eval:n { \l__luamplib_BBox_urx_tl - \l__luamplib_BBox_llx_tl }~
          \fp_eval:n { \l__luamplib_BBox_ury_tl - \l__luamplib_BBox_lly_tl }~
          re~f
        }
        \pdfextension restore\relax
      }
    }
    {
      \tl_set:Ne \l__luamplib_tag_bbox_draw_tl
      {
        \special{pdf:bcontent}
        \opacity_select:n{0.5} \color_select:n{red}
        \special{pdf:code~
          1~0~0~1~
          -\dim_to_decimal_in_bp:n { \property_ref:een{\l_tmpa_tl}{xpos}{0}sp + \wd#1 }~
          -\dim_to_decimal_in_bp:n { \property_ref:een{\l_tmpa_tl}{ypos}{0}sp }~
          cm
        }
        \special{pdf:code~
          \l__luamplib_BBox_llx_tl\c_space_tl
          \l__luamplib_BBox_lly_tl\c_space_tl
          \fp_eval:n { \l__luamplib_BBox_urx_tl - \l__luamplib_BBox_llx_tl }~
          \fp_eval:n { \l__luamplib_BBox_ury_tl - \l__luamplib_BBox_lly_tl }~
          re~f
        }
        \special{pdf:econtent}
      }
    }
  }
}
%    \end{macrocode}
% Sockets for main process
%    \begin{macrocode}
\socket_new:nn{tagsupport/luamplib/figure/begin}{1}
\socket_new:nn{tagsupport/luamplib/figure/end}{2}
\socket_new_plug:nnn{tagsupport/luamplib/figure/end}{transparent}{#2}
\socket_new_plug:nnn{tagsupport/luamplib/figure/begin}{alt}
{
    \tag_mc_end_push:
    \tl_if_empty:NT\l__luamplib_tag_alt_tl
    {
      \tl_if_empty:eTF{#1}
        { \tl_set:Nn \l__luamplib_tag_alt_tl {metapost~figure} }
        { \tl_set:Ne \l__luamplib_tag_alt_tl {metapost~figure~\text_purify:n{#1}} }
      \msg_warning:nnVV{luamplib}{alt-text-missing}
                       \l__luamplib_tag_envname_tl \l__luamplib_tag_alt_tl
    }
    \tag_struct_begin:n
    {
      tag=\l__luamplib_tag_struct_tl,
      alt=\l__luamplib_tag_alt_tl,
    }
    \tag_mc_begin:n{}
}
\socket_new_plug:nnn{tagsupport/luamplib/figure/end}{alt}
{
    \__luamplib_tag_bbox_attribute:n {#1}
    #2
    \tl_use:N \l__luamplib_tag_bbox_draw_tl
    \tag_mc_end:
    \tag_struct_end:
    \tag_mc_begin_pop:n{}
}
\socket_new_plug:nnn{tagsupport/luamplib/figure/begin}{actualtext}
{
    \tag_mc_end_push:
    \tag_struct_begin:n
    {
      tag=Span,
      actualtext=\l__luamplib_tag_actual_tl,
    }
    \tag_mc_begin:n{}
}
\socket_new_plug:nnn{tagsupport/luamplib/figure/end}{actualtext}
{
    #2
    \tag_mc_end:
    \tag_struct_end:
    \tag_mc_begin_pop:n{}
}
\socket_new_plug:nnn{tagsupport/luamplib/figure/begin}{artifact}
{
    \tag_mc_end_push:
    \tag_mc_begin:n{artifact}
}
\socket_new_plug:nnn{tagsupport/luamplib/figure/end}{artifact}
{
    #2
    \tag_mc_end:
    \tag_mc_begin_pop:n{}
}
%    \end{macrocode}
% A socket for tagging init, so that we can declare
% |\SetKeys[luamplib/tagging]{...}| anywhere in the document.
%    \begin{macrocode}
\socket_new:nn{tagsupport/luamplib/figure/init}{0}
\socket_new_plug:nnn{tagsupport/luamplib/figure/init}{alt}
{
  \socket_assign_plug:nn{tagsupport/luamplib/figure/begin}{alt}
  \socket_assign_plug:nn{tagsupport/luamplib/figure/end}{alt}
}
\socket_new_plug:nnn{tagsupport/luamplib/figure/init}{actualtext}
{
  \socket_assign_plug:nn{tagsupport/luamplib/figure/begin}{actualtext}
  \socket_assign_plug:nn{tagsupport/luamplib/figure/end}{actualtext}
%    \end{macrocode}
% In vmode, hmode will be forced by \cs{noindent} upon |actualtext| and |text| modes.
%    \begin{macrocode}
  \prependtomplibbox \mplibnoforcehmode
  \mode_if_vertical:T { \noindent \aftergroup\par }
}
\socket_new_plug:nnn{tagsupport/luamplib/figure/init}{artifact}
{
  \socket_assign_plug:nn{tagsupport/luamplib/figure/begin}{artifact}
  \socket_assign_plug:nn{tagsupport/luamplib/figure/end}{artifact}
}
\socket_new_plug:nnn{tagsupport/luamplib/figure/init}{text}
{
  \bool_set_true:N \l__luamplib_tag_usetext_bool
  \socket_assign_plug:nn{tagsupport/luamplib/figure/begin}{artifact}
  \socket_assign_plug:nn{tagsupport/luamplib/figure/end}{artifact}
  \prependtomplibbox \mplibnoforcehmode
  \mode_if_vertical:T { \noindent \aftergroup\par }
}
\socket_new_plug:nnn{tagsupport/luamplib/figure/init}{off}
{
  \socket_assign_plug:nn{tagsupport/luamplib/figure/begin}{noop}
  \socket_assign_plug:nn{tagsupport/luamplib/figure/end}{transparent}
}
\socket_assign_plug:nn{tagsupport/luamplib/figure/init}{alt}
%    \end{macrocode}
% Key-value options
%    \begin{macrocode}
\keys_define:nn{luamplib/tagging}
{
  ,alt .code:n =
  {
    \tl_set:Ne\l__luamplib_tag_alt_tl{\text_purify:n{#1}}
    \socket_assign_plug:nn{tagsupport/luamplib/figure/init}{alt}
  }
  ,actualtext .code:n =
  {
    \tl_set:Ne\l__luamplib_tag_actual_tl{\text_purify:n{#1}}
    \socket_assign_plug:nn{tagsupport/luamplib/figure/init}{actualtext}
  }
  ,artifact .code:n = { \socket_assign_plug:nn{tagsupport/luamplib/figure/init}{artifact} }
  ,text     .code:n = { \socket_assign_plug:nn{tagsupport/luamplib/figure/init}{text} }
  ,off      .code:n = { \socket_assign_plug:nn{tagsupport/luamplib/figure/init}{off} }
  ,tag      .code:n =
  {
    \str_case:nnF {#1}
    {
      {false}    { \keys_set:nn {luamplib/tagging} {off} }
      {artifact} { \keys_set:nn {luamplib/tagging} {artifact} }
    }
    {
      \tl_set:Nn\l__luamplib_tag_struct_tl{#1}
      \socket_assign_plug:nn{tagsupport/luamplib/figure/init}{alt}
    }
  }
  ,adjust-BBox .code:n =
  {
    \bool_set_true:N \l__luamplib_tag_bboxcorr_bool
    \seq_set_split:Nnn \l__luamplib_tag_bboxcorr_seq{~}{#1~0pt~0pt~0pt~0pt}
  }
  ,tagging-setup .code:n = { \keys_set_known:nn {luamplib/tagging} {#1} }
}
\keys_define:nn {luamplib/instance}
{
  ,instance     .code:n = { \tl_gset:Nn \currentmpinstancename {#1} }
  ,instancename .meta:n = { instance = {#1} }
  ,unknown      .code:n = { \tl_gset:NV \currentmpinstancename \l_keys_key_str }
}
%    \end{macrocode}
% Redefine our macros
%    \begin{macrocode}
\cs_set_nopar:Npn \mplibstarttoPDF #1 #2 #3 #4
{
  \prependtomplibbox
  \hbox dir~TLT\bgroup
    \tag_socket_use:nn{luamplib/figure/begin}\l__luamplib_tag_alt_dflt_tl
    \xdef\MPllx{#1}\xdef\MPlly{#2}%
    \xdef\MPurx{#3}\xdef\MPury{#4}%
    \xdef\MPwidth{\the\dimexpr#3bp-#1bp\relax}%
    \xdef\MPheight{\the\dimexpr#4bp-#2bp\relax}%
    \parskip0pt
    \leftskip0pt
    \parindent0pt
    \everypar{}%
    \setbox\mplibscratchbox\vbox\bgroup
      \tag_suspend:n{\mplibstarttoPDF}
      \noindent
}
\cs_set_nopar:Npn \mplibstoptoPDF
{
      \par
    \egroup
    \setbox\mplibscratchbox\hbox
      {\hskip-\MPllx bp
       \raise-\MPlly bp
       \box\mplibscratchbox}%
    \setbox\mplibscratchbox\vbox to \MPheight
      {\vfill
       \hsize\MPwidth
       \wd\mplibscratchbox0pt
       \ht\mplibscratchbox0pt
       \dp\mplibscratchbox0pt
       \box\mplibscratchbox}%
    \wd\mplibscratchbox\MPwidth
    \ht\mplibscratchbox\MPheight
    \tag_socket_use:nnn{luamplib/figure/end}{\mplibscratchbox}{\box\mplibscratchbox}
  \egroup
}
\RenewDocumentCommand\mplibcode{O{}}
{
  \tl_set:Nn \l__luamplib_tag_envname_tl {mplibcode}
  \tl_gclear:N \currentmpinstancename
  \keys_set_known:neN {luamplib/tagging} {#1} \l_tmpa_tl
  \keys_set:nV {luamplib/instance} \l_tmpa_tl
  \tl_set_eq:NN \l__luamplib_tag_alt_dflt_tl \currentmpinstancename
  \tag_socket_use:n{luamplib/figure/init}
  \mplibtmptoks{}\ltxdomplibcode
}
\RenewDocumentCommand\mpfig{s O{}}
{
  \begingroup
  \tl_set:Nn \l__luamplib_tag_envname_tl {mpfig}
  \keys_set_known:ne {luamplib/tagging} {#2}
  \tl_set_eq:NN \l__luamplib_tag_alt_dflt_tl \mpfiginstancename
  \tag_socket_use:n{luamplib/figure/init}
  \IfBooleanTF{#1} { \mplibprempfig * }
                   { \mplibmainmpfig }
}
\RenewDocumentCommand\usemplibgroup{O{} m}
{
  \begingroup
  \tl_set:Nn \l__luamplib_tag_envname_tl {usemplibgroup}
  \keys_set_known:ne {luamplib/tagging} {#1}
  \tag_socket_use:n{luamplib/figure/init}
  \prependtomplibbox\hbox dir~TLT\bgroup
    \tag_socket_use:nn{luamplib/figure/begin}{#2}
    \setbox\mplibscratchbox\hbox\bgroup
      \bool_if:NF \l__luamplib_tag_usetext_bool { \tag_suspend:n{\usemplibgroup} }
      \tag_socket_use:nnn{luamplib/mplibgroup/put}{#2}{\csname luamplib.group.#2\endcsname}
    \egroup
    \tag_socket_use:nnn{luamplib/figure/end}{\mplibscratchbox}{\unhbox\mplibscratchbox}
  \egroup
  \endgroup
}
%    \end{macrocode}
% Allow setting alt/actual text within \metapost code.
% Of course we can use them in \TeX\ code as well.
%    \begin{macrocode}
\cs_new_nopar:Npn \mplibalttext #1
{
  \tl_set:Ne \l__luamplib_tag_alt_tl {\text_purify:n{#1}}
}
\cs_new_nopar:Npn \mplibactualtext #1
{
  \tl_set:Ne \l__luamplib_tag_actual_tl {\text_purify:n{#1}}
}
\ExplSyntaxOff
%    \end{macrocode}
%
%    That's all folks!
%
% \iffalse
%</package>
% \fi
%
% \clearpage
% \section{The GNU GPL License v2}
%
% The GPL requires the complete license text to be distributed along
% with the code. I recommend the canonical source, instead:
% \url{http://www.gnu.org/licenses/old-licenses/gpl-2.0.html}.
% But if you insist on an included copy, here it is.
% You might want to zoom in.
%
% \newsavebox{\gpl}
% \begin{lrbox}{\gpl}
% \begin{minipage}{3\textwidth}
% \columnsep=3\columnsep
% \begin{multicols}{3}
% \begin{center}
% {\Large GNU GENERAL PUBLIC LICENSE\par}
% \bigskip
% {Version 2, June 1991}
% \end{center}
%
% \begin{center}
% {\parindent 0in
%
% Copyright \textcopyright\ 1989, 1991 Free Software Foundation, Inc.
%
% \bigskip
%
% 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
%
% \bigskip
%
% Everyone is permitted to copy and distribute verbatim copies
% of this license document, but changing it is not allowed.
% }
% \end{center}
%
% \begin{center}
% {\bf\large Preamble}
% \end{center}
%
%
% The licenses for most software are designed to take away your freedom to
% share and change it.  By contrast, the GNU General Public License is
% intended to guarantee your freedom to share and change free software---to
% make sure the software is free for all its users.  This General Public
% License applies to most of the Free Software Foundation's software and to
% any other program whose authors commit to using it.  (Some other Free
% Software Foundation software is covered by the GNU Library General Public
% License instead.)  You can apply it to your programs, too.
%
% When we speak of free software, we are referring to freedom, not price.
% Our General Public Licenses are designed to make sure that you have the
% freedom to distribute copies of free software (and charge for this service
% if you wish), that you receive source code or can get it if you want it,
% that you can change the software or use pieces of it in new free programs;
% and that you know you can do these things.
%
% To protect your rights, we need to make restrictions that forbid anyone to
% deny you these rights or to ask you to surrender the rights.  These
% restrictions translate to certain responsibilities for you if you
% distribute copies of the software, or if you modify it.
%
% For example, if you distribute copies of such a program, whether gratis or
% for a fee, you must give the recipients all the rights that you have.  You
% must make sure that they, too, receive or can get the source code.  And
% you must show them these terms so they know their rights.
%
% We protect your rights with two steps: (1) copyright the software, and (2)
% offer you this license which gives you legal permission to copy,
% distribute and/or modify the software.
%
% Also, for each author's protection and ours, we want to make certain that
% everyone understands that there is no warranty for this free software.  If
% the software is modified by someone else and passed on, we want its
% recipients to know that what they have is not the original, so that any
% problems introduced by others will not reflect on the original authors'
% reputations.
%
% Finally, any free program is threatened constantly by software patents.
% We wish to avoid the danger that redistributors of a free program will
% individually obtain patent licenses, in effect making the program
% proprietary.  To prevent this, we have made it clear that any patent must
% be licensed for everyone's free use or not licensed at all.
%
% The precise terms and conditions for copying, distribution and
% modification follow.
%
% \begin{center}
% {\Large \sc Terms and Conditions For Copying, Distribution and
%   Modification}
% \end{center}
%
% \begin{enumerate}
% \item
% This License applies to any program or other work which contains a notice
% placed by the copyright holder saying it may be distributed under the
% terms of this General Public License.  The ``Program'', below, refers to
% any such program or work, and a ``work based on the Program'' means either
% the Program or any derivative work under copyright law: that is to say, a
% work containing the Program or a portion of it, either verbatim or with
% modifications and/or translated into another language.  (Hereinafter,
% translation is included without limitation in the term ``modification''.)
% Each licensee is addressed as ``you''.
%
% Activities other than copying, distribution and modification are not
% covered by this License; they are outside its scope.  The act of
% running the Program is not restricted, and the output from the Program
% is covered only if its contents constitute a work based on the
% Program (independent of having been made by running the Program).
% Whether that is true depends on what the Program does.
%
% \item You may copy and distribute verbatim copies of the Program's source
%   code as you receive it, in any medium, provided that you conspicuously
%   and appropriately publish on each copy an appropriate copyright notice
%   and disclaimer of warranty; keep intact all the notices that refer to
%   this License and to the absence of any warranty; and give any other
%   recipients of the Program a copy of this License along with the Program.
%
% You may charge a fee for the physical act of transferring a copy, and you
% may at your option offer warranty protection in exchange for a fee.
%
% \item
% You may modify your copy or copies of the Program or any portion
% of it, thus forming a work based on the Program, and copy and
% distribute such modifications or work under the terms of Section 1
% above, provided that you also meet all of these conditions:
%
% \begin{enumerate}
%
% \item
% You must cause the modified files to carry prominent notices stating that
% you changed the files and the date of any change.
%
% \item
% You must cause any work that you distribute or publish, that in
% whole or in part contains or is derived from the Program or any
% part thereof, to be licensed as a whole at no charge to all third
% parties under the terms of this License.
%
% \item
% If the modified program normally reads commands interactively
% when run, you must cause it, when started running for such
% interactive use in the most ordinary way, to print or display an
% announcement including an appropriate copyright notice and a
% notice that there is no warranty (or else, saying that you provide
% a warranty) and that users may redistribute the program under
% these conditions, and telling the user how to view a copy of this
% License.  (Exception: if the Program itself is interactive but
% does not normally print such an announcement, your work based on
% the Program is not required to print an announcement.)
%
% \end{enumerate}
%
%
% These requirements apply to the modified work as a whole.  If
% identifiable sections of that work are not derived from the Program,
% and can be reasonably considered independent and separate works in
% themselves, then this License, and its terms, do not apply to those
% sections when you distribute them as separate works.  But when you
% distribute the same sections as part of a whole which is a work based
% on the Program, the distribution of the whole must be on the terms of
% this License, whose permissions for other licensees extend to the
% entire whole, and thus to each and every part regardless of who wrote it.
%
% Thus, it is not the intent of this section to claim rights or contest
% your rights to work written entirely by you; rather, the intent is to
% exercise the right to control the distribution of derivative or
% collective works based on the Program.
%
% In addition, mere aggregation of another work not based on the Program
% with the Program (or with a work based on the Program) on a volume of
% a storage or distribution medium does not bring the other work under
% the scope of this License.
%
% \item
% You may copy and distribute the Program (or a work based on it,
% under Section 2) in object code or executable form under the terms of
% Sections 1 and 2 above provided that you also do one of the following:
%
% \begin{enumerate}
%
% \item
%
% Accompany it with the complete corresponding machine-readable
% source code, which must be distributed under the terms of Sections
% 1 and 2 above on a medium customarily used for software interchange; or,
%
% \item
%
% Accompany it with a written offer, valid for at least three
% years, to give any third party, for a charge no more than your
% cost of physically performing source distribution, a complete
% machine-readable copy of the corresponding source code, to be
% distributed under the terms of Sections 1 and 2 above on a medium
% customarily used for software interchange; or,
%
% \item
%
% Accompany it with the information you received as to the offer
% to distribute corresponding source code.  (This alternative is
% allowed only for noncommercial distribution and only if you
% received the program in object code or executable form with such
% an offer, in accord with Subsection b above.)
%
% \end{enumerate}
%
%
% The source code for a work means the preferred form of the work for
% making modifications to it.  For an executable work, complete source
% code means all the source code for all modules it contains, plus any
% associated interface definition files, plus the scripts used to
% control compilation and installation of the executable.  However, as a
% special exception, the source code distributed need not include
% anything that is normally distributed (in either source or binary
% form) with the major components (compiler, kernel, and so on) of the
% operating system on which the executable runs, unless that component
% itself accompanies the executable.
%
% If distribution of executable or object code is made by offering
% access to copy from a designated place, then offering equivalent
% access to copy the source code from the same place counts as
% distribution of the source code, even though third parties are not
% compelled to copy the source along with the object code.
%
% \item
% You may not copy, modify, sublicense, or distribute the Program
% except as expressly provided under this License.  Any attempt
% otherwise to copy, modify, sublicense or distribute the Program is
% void, and will automatically terminate your rights under this License.
% However, parties who have received copies, or rights, from you under
% this License will not have their licenses terminated so long as such
% parties remain in full compliance.
%
% \item
% You are not required to accept this License, since you have not
% signed it.  However, nothing else grants you permission to modify or
% distribute the Program or its derivative works.  These actions are
% prohibited by law if you do not accept this License.  Therefore, by
% modifying or distributing the Program (or any work based on the
% Program), you indicate your acceptance of this License to do so, and
% all its terms and conditions for copying, distributing or modifying
% the Program or works based on it.
%
% \item
% Each time you redistribute the Program (or any work based on the
% Program), the recipient automatically receives a license from the
% original licensor to copy, distribute or modify the Program subject to
% these terms and conditions.  You may not impose any further
% restrictions on the recipients' exercise of the rights granted herein.
% You are not responsible for enforcing compliance by third parties to
% this License.
%
% \item
% If, as a consequence of a court judgment or allegation of patent
% infringement or for any other reason (not limited to patent issues),
% conditions are imposed on you (whether by court order, agreement or
% otherwise) that contradict the conditions of this License, they do not
% excuse you from the conditions of this License.  If you cannot
% distribute so as to satisfy simultaneously your obligations under this
% License and any other pertinent obligations, then as a consequence you
% may not distribute the Program at all.  For example, if a patent
% license would not permit royalty-free redistribution of the Program by
% all those who receive copies directly or indirectly through you, then
% the only way you could satisfy both it and this License would be to
% refrain entirely from distribution of the Program.
%
% If any portion of this section is held invalid or unenforceable under
% any particular circumstance, the balance of the section is intended to
% apply and the section as a whole is intended to apply in other
% circumstances.
%
% It is not the purpose of this section to induce you to infringe any
% patents or other property right claims or to contest validity of any
% such claims; this section has the sole purpose of protecting the
% integrity of the free software distribution system, which is
% implemented by public license practices.  Many people have made
% generous contributions to the wide range of software distributed
% through that system in reliance on consistent application of that
% system; it is up to the author/donor to decide if he or she is willing
% to distribute software through any other system and a licensee cannot
% impose that choice.
%
% This section is intended to make thoroughly clear what is believed to
% be a consequence of the rest of this License.
%
% \item
% If the distribution and/or use of the Program is restricted in
% certain countries either by patents or by copyrighted interfaces, the
% original copyright holder who places the Program under this License
% may add an explicit geographical distribution limitation excluding
% those countries, so that distribution is permitted only in or among
% countries not thus excluded.  In such case, this License incorporates
% the limitation as if written in the body of this License.
%
% \item
% The Free Software Foundation may publish revised and/or new versions
% of the General Public License from time to time.  Such new versions will
% be similar in spirit to the present version, but may differ in detail to
% address new problems or concerns.
%
% Each version is given a distinguishing version number.  If the Program
% specifies a version number of this License which applies to it and ``any
% later version'', you have the option of following the terms and conditions
% either of that version or of any later version published by the Free
% Software Foundation.  If the Program does not specify a version number of
% this License, you may choose any version ever published by the Free Software
% Foundation.
%
% \item
% If you wish to incorporate parts of the Program into other free
% programs whose distribution conditions are different, write to the author
% to ask for permission.  For software which is copyrighted by the Free
% Software Foundation, write to the Free Software Foundation; we sometimes
% make exceptions for this.  Our decision will be guided by the two goals
% of preserving the free status of all derivatives of our free software and
% of promoting the sharing and reuse of software generally.
%
% \begin{center}
% {\Large\sc
% No Warranty
% }
% \end{center}
%
% \item
% {\sc Because the program is licensed free of charge, there is no warranty
% for the program, to the extent permitted by applicable law.  Except when
% otherwise stated in writing the copyright holders and/or other parties
% provide the program ``as is'' without warranty of any kind, either expressed
% or implied, including, but not limited to, the implied warranties of
% merchantability and fitness for a particular purpose.  The entire risk as
% to the quality and performance of the program is with you.  Should the
% program prove defective, you assume the cost of all necessary servicing,
% repair or correction.}
%
% \item
% {\sc In no event unless required by applicable law or agreed to in writing
% will any copyright holder, or any other party who may modify and/or
% redistribute the program as permitted above, be liable to you for damages,
% including any general, special, incidental or consequential damages arising
% out of the use or inability to use the program (including but not limited
% to loss of data or data being rendered inaccurate or losses sustained by
% you or third parties or a failure of the program to operate with any other
% programs), even if such holder or other party has been advised of the
% possibility of such damages.}
%
% \end{enumerate}
%
%
% \begin{center}
% {\Large\sc End of Terms and Conditions}
% \end{center}
%
%
% \pagebreak[2]
%
% \section*{Appendix: How to Apply These Terms to Your New Programs}
%
% If you develop a new program, and you want it to be of the greatest
% possible use to the public, the best way to achieve this is to make it
% free software which everyone can redistribute and change under these
% terms.
%
%   To do so, attach the following notices to the program.  It is safest to
%   attach them to the start of each source file to most effectively convey
%   the exclusion of warranty; and each file should have at least the
%   ``copyright'' line and a pointer to where the full notice is found.
%
% \begin{quote}
% one line to give the program's name and a brief idea of what it does. \\
% Copyright (C) yyyy  name of author \\
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 2 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
% \end{quote}
%
% Also add information on how to contact you by electronic and paper mail.
%
% If the program is interactive, make it output a short notice like this
% when it starts in an interactive mode:
%
% \begin{quote}
% Gnomovision version 69, Copyright (C) yyyy  name of author \\
% Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. \\
% This is free software, and you are welcome to redistribute it
% under certain conditions; type `show c' for details.
% \end{quote}
%
%
% The hypothetical commands {\tt show w} and {\tt show c} should show the
% appropriate parts of the General Public License.  Of course, the commands
% you use may be called something other than {\tt show w} and {\tt show c};
% they could even be mouse-clicks or menu items---whatever suits your
% program.
%
% You should also get your employer (if you work as a programmer) or your
% school, if any, to sign a ``copyright disclaimer'' for the program, if
% necessary.  Here is a sample; alter the names:
%
% \begin{quote}
% Yoyodyne, Inc., hereby disclaims all copyright interest in the program \\
% `Gnomovision' (which makes passes at compilers) written by James Hacker. \\
%
% signature of Ty Coon, 1 April 1989 \\
% Ty Coon, President of Vice
% \end{quote}
%
%
% This General Public License does not permit incorporating your program
% into proprietary programs.  If your program is a subroutine library, you
% may consider it more useful to permit linking proprietary applications
% with the library.  If this is what you want to do, use the GNU Library
% General Public License instead of this License.
%
% \end{multicols}
% \end{minipage}
% \end{lrbox}
%
% \begin{center}
% \scalebox{0.33}{\usebox{\gpl}}
% \end{center}
%
% \Finale
\endinput
