texlive[72049] Master: luamml (16aug24)

commits+karl at tug.org commits+karl at tug.org
Fri Aug 16 23:35:16 CEST 2024


Revision: 72049
          https://tug.org/svn/texlive?view=revision&revision=72049
Author:   karl
Date:     2024-08-16 23:35:16 +0200 (Fri, 16 Aug 2024)
Log Message:
-----------
luamml (16aug24)

Modified Paths:
--------------
    trunk/Master/tlpkg/bin/tlpkg-ctan-check
    trunk/Master/tlpkg/libexec/ctan2tds
    trunk/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc

Added Paths:
-----------
    trunk/Master/texmf-dist/doc/lualatex/luamml/
    trunk/Master/texmf-dist/doc/lualatex/luamml/README.md
    trunk/Master/texmf-dist/doc/lualatex/luamml/luamml.pdf
    trunk/Master/texmf-dist/doc/lualatex/luamml/test-mathml-2.txt
    trunk/Master/texmf-dist/source/lualatex/luamml/
    trunk/Master/texmf-dist/source/lualatex/luamml/luamml.dtx
    trunk/Master/texmf-dist/tex/lualatex/luamml/
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-amsmath.lua
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-array.lua
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-convert.lua
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-data-combining.lua
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-data-stretchy.lua
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-demo.sty
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-legacy-mappings.lua
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-lr.lua
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-amsmath.sty
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-amstext.sty
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-array.sty
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-kernel.sty
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-lab-math.sty
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-pdf-demo.sty
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-pdf.sty
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-structelemwriter.lua
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-table.lua
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-tex-annotate.lua
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-tex.lua
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-xmlwriter.lua
    trunk/Master/texmf-dist/tex/lualatex/luamml/luamml.sty
    trunk/Master/tlpkg/tlpsrc/luamml.tlpsrc

Added: trunk/Master/texmf-dist/doc/lualatex/luamml/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/lualatex/luamml/README.md	                        (rev 0)
+++ trunk/Master/texmf-dist/doc/lualatex/luamml/README.md	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,14 @@
+# LuaMML: Automated LuaLaTeX math to MathML conversion
+This is an attempt to implement automatic conversion of LuaLaTeX inline and display math expressions into MathML code to aid with tagging.
+It works best with `unicode-math`, but it can also be used with traditional math fonts if mappings to Unicode are provided.
+
+## Installation
+Run `l3build install` to install `luamml` into your local `texmf` tree.
+
+## Usage
+Add `\usepackage[tracing]{luamml-demo}` to print MathML to the terminal or `\usepackage[files]{luamml-demo}` to generate separate files with MathML output.
+Alternatively it can be used with latex-lab to automatically integrate with tagging infrastucture.
+
+<!-- Also see a [`tagpdf` experiment using this to tag PDF formulas](https://github.com/u-fischer/tagpdf/blob/develop/experiments/exp-mathml-lua.tex). -->
+
+<!-- If you are very brave you can also try running `pdflatex test_pdf` and afterwards run `./pdfmml.lua test_pdf.lua` to get pdflatex formulas converted. -->


Property changes on: trunk/Master/texmf-dist/doc/lualatex/luamml/README.md
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/doc/lualatex/luamml/luamml.pdf
===================================================================
(Binary files differ)

Index: trunk/Master/texmf-dist/doc/lualatex/luamml/luamml.pdf
===================================================================
--- trunk/Master/texmf-dist/doc/lualatex/luamml/luamml.pdf	2024-08-16 21:32:58 UTC (rev 72048)
+++ trunk/Master/texmf-dist/doc/lualatex/luamml/luamml.pdf	2024-08-16 21:35:16 UTC (rev 72049)

Property changes on: trunk/Master/texmf-dist/doc/lualatex/luamml/luamml.pdf
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/pdf
\ No newline at end of property
Added: trunk/Master/texmf-dist/doc/lualatex/luamml/test-mathml-2.txt
===================================================================
--- trunk/Master/texmf-dist/doc/lualatex/luamml/test-mathml-2.txt	                        (rev 0)
+++ trunk/Master/texmf-dist/doc/lualatex/luamml/test-mathml-2.txt	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,8 @@
+<math xmlns:tex="http://typesetting.eu/2021/LuaMathML" xmlns="http://www.w3.org/1998/Math/MathML">
+  <mo lspace="0" rspace="0.167em" tex:class="opnolimits">
+    sin
+  </mo>
+  <mi>
+    𝑥
+  </mi>
+</math>


Property changes on: trunk/Master/texmf-dist/doc/lualatex/luamml/test-mathml-2.txt
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/source/lualatex/luamml/luamml.dtx
===================================================================
--- trunk/Master/texmf-dist/source/lualatex/luamml/luamml.dtx	                        (rev 0)
+++ trunk/Master/texmf-dist/source/lualatex/luamml/luamml.dtx	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,531 @@
+% \iffalse meta-comment
+%
+%% Copyright (C) 2020-2024 by Marcel Krueger
+%%
+%% This file may be distributed and/or modified under the
+%% conditions of the LaTeX Project Public License, either
+%% version 1.3c of this license or (at your option) any later
+%% version. The latest version of this license is in:
+%%
+%% http://www.latex-project.org/lppl.txt
+%%
+%% and version 1.3 or later is part of all distributions of
+%% LaTeX version 2005/12/01 or later.
+%
+%<*batch>
+%<*gobble>
+\ifx\jobname\relax\let\documentclass\undefined\fi
+\ifx\documentclass\undefined
+\csname fi\endcsname
+%</gobble>
+\input docstrip.tex
+\keepsilent
+\generate{
+  \file{luamml.sty}{\from{luamml.dtx}{package,luatex}}
+  \file{luamml-pdf.sty}{\from{luamml.dtx}{package,pdftex}}
+}
+\endbatchfile
+%</batch>
+%<*gobble>
+\fi
+\expandafter\ifx\csname @currname\endcsname\empty
+\csname fi\endcsname
+%</gobble>
+%<*driver>
+\documentclass{l3doc}
+\usepackage{luamml}
+\usepackage{csquotes,luacolor}
+\MakeShortVerb{\|}
+\RecordChanges
+\begin{document}
+\tracingmathml2
+\DocInput{luamml.dtx}
+\PrintIndex
+\PrintChanges
+\end{document}
+%</driver>
+%<*gobble>
+\fi
+%</gobble>
+% \fi
+%
+% \GetFileInfo{luamml.sty}
+% \title{The \pkg{luamml} package%
+%   \thanks{This document corresponds to \pkg{luamml}~\fileversion, dated~\filedate.}%
+% }
+% \author{Marcel Krüger}
+%
+% \maketitle
+%
+% \begin{documentation}
+% \section{Use case}
+% When generating output for the web or tagged output, mathematical content should often be represented as MathML.
+% This uses Lua\TeX~callbacks to automatically attempt to convert Lua\TeX~math mode output into MathML.
+%
+% \section{Usage}
+% The \pkg{luamml} package is designed to be used in automated ways by other packages and usually should not be invoked directly by the end user.
+% For experiments, \texttt{luamml-demo} is included which provides easier to use interfaces.
+%
+% Add in your preamble
+% \begin{verbatim}
+% \usepackage[files]{luamml-demo}
+% \end{verbatim}
+% This will trigger the output of individual files for each block of math output containing corresponding MathML.
+%
+% Alternatively
+% \begin{verbatim}
+% \usepackage[l3build]{luamml-demo}
+% \end{verbatim}
+% will generate a single file with a concatenation of all MathML blocks.
+%
+% For automated use, the \pkg{luamml} package can be included directly, followed by enclosing blocks which should generate files with \cmd{luamml_begin_single_file:} and \cmd{luamml_end_single_file:}.
+% The filename can be set with \cmd{luamml_set_filename:n}.
+%
+% \section{Improving MathML conversion}
+% When using constructs which do not automatically get converted in acceptable form, conversion hints can be provided with \cmd{luamml_annotate:en}.
+% This allows to provide a replacement MathML structure in Lua table form, for example
+% \begin{verbatim}
+% \luamml_annotate:en {
+%   nucleus = true,
+%   core = {[0] = 'mi', 'TeX'},
+% }{
+%   \hbox{\TeX}
+% }
+% \end{verbatim}
+% produces a |<mi>TeX</mi>| element in the output instead of trying to import \TeX~as a mathematical expression.
+% The table structure is explained in an appendix.
+%
+% \section{Features \& Limitations}
+% Currently all mathematical expressions which purely contain Unicode encoded math mode material without embedded non-math should get converted successfully.
+% Usage with non-Unicode math (\TeX's 8-bit math fonts) is highly experimental and undocumented.
+% Any attempt to build complicated structures by embedding arbitrary \TeX\ code in the middle of math mode needs to have a MathML replacement specified.
+% We try to automate more cases in the future.
+%
+% \appendix
+% \input{luamml-algorithm}
+% \end{documentation}
+%
+% \begin{implementation}
+% \section{Package Implementation}
+% \subsection{Initialization}
+% \iffalse
+%<*package>
+% \fi
+%    \begin{macrocode}
+%<@@=luamml>
+%<*luatex>
+\ProvidesExplPackage {luamml} {2024-08-14} {0.1.0}
+  {Automatically generate presentational MathML from LuaTeX math expressions}
+%</luatex>
+%<*pdftex>
+\ProvidesExplPackage {luamml-pdf} {2024-08-14} {0.1.0}
+  {MathML generation for L̶u̶a̶pdfLaTeX}
+%</pdftex>
+%    \end{macrocode}
+%
+% \subsection{Initialization}
+% These variable have to appear before the Lua module is loaded and will be used to
+% communicate information to the callback.
+%
+% Here \cs{tracingmathml} does not use a expl3 name since it is not intended for
+% programming use but only as a debugging helper for the user.
+% The other variables are internal, but we provide public interfaces for setting
+% them later.
+%    \begin{macrocode}
+\int_new:N \l__luamml_flag_int
+\int_new:N \l__luamml_pretty_int
+%<luatex>\tl_new:N \l__luamml_filename_tl
+\tl_new:N \l__luamml_root_tl
+\tl_set:Nn \l__luamml_root_tl { mrow }
+\tl_new:N \l__luamml_label_tl
+%<pdftex>\int_new:N \g__luamml_formula_id_int
+%<luatex>\int_new:N \tracingmathml
+
+\int_set:Nn \l__luamml_pretty_int { 1 }
+%    \end{macrocode}
+%
+% Now we can load the Lua module which defines the callback.
+% Of course until pdf\TeX starts implementing \cs{directlua} this is only
+% done in Lua\TeX.
+%    \begin{macrocode}
+%<luatex>\lua_now:n { require'luamml-tex' }
+%    \end{macrocode}
+%
+% \subsection{Hook}
+% We also call a hook with arguments at the end of every MathML conversion with the result.
+% Currently only implemented in Lua\TeX{} since it immediately provides the output.
+%    \begin{macrocode}
+%<*luatex>
+\hook_new_with_args:nn { luamml / converted } { 1 }
+
+\cs_new_protected:Npn \__luamml_output_hook:n {
+  \hook_use:nnw { luamml / converted } { 1 }
+}
+\__luamml_register_output_hook:N \__luamml_output_hook:n
+%</luatex>
+%    \end{macrocode}
+
+%
+% \subsection{Flags}
+% The most important interface is for setting the flag which controls how the
+% formulas should be converted.
+%
+% \begin{macro}{\luamml_flag_process:}
+%   Consider the current formula to be a complete, free-standing mathematical
+%   expression which should be converted to MathML. Additionally, the formula
+%   is also saved in the \texttt{start\_math} node as with
+%   \cs{luamml_flag_save:}.
+%    \begin{macrocode}
+\cs_new_protected:Npn \luamml_flag_process: {
+  \tl_set:Nn \l__luamml_label_tl {}
+  \int_set:Nn \l__luamml_flag_int { 3 }
+}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\__luamml_maybe_structelem:}
+% A internal helper which can be added to a tag to preserve the external state
+% of the structelem flag.
+%    \begin{macrocode}
+\cs_new:Npn \__luamml_maybe_structelem: {
+  (
+    8 * \int_mod:nn {
+      \int_div_truncate:nn { \l__luamml_flag_int } {8}
+    } {2}
+  ) +
+}
+% \end{macro}
+%
+% \begin{macro}{\__luamml_style_to_num:N}
+%    \begin{macrocode}
+\cs_new:Npn \__luamml_style_to_num:N #1 {
+%<luatex>  32 * #1
+%<*pdftex>
+  \token_case_meaning:NnF #1 {
+    \displaystyle {0}
+    \textstyle {32}
+    \scriptstyle {64}
+    \scriptscriptstyle {96}
+  } {
+    \Invalid_mathstyle
+  }
+%</pdftex>
+}
+%    \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}{\luamml_flag_save:n,
+%               \luamml_flag_save:nN,
+%               \luamml_flag_save:nn,
+%               \luamml_flag_save:nNn}
+%   Convert the current formula but only save it's representation in the math
+%   node without emitting it as a complete formula. This is useful when the
+%   expression forms part of a bigger formula and will be intergrated into it's
+%   MathML tables later by special code.
+%   It optinally accepts three parameters: A label, one math style command
+%   (\cs{displaystyle}, \cs{textstyle}, etc.) which is the implicit math style
+%   (so the style which the surrounding code expects this style to have) and a
+%   name for the root element (defaults to \texttt{mrow}).
+%   If the root element name is \texttt{mrow}, it will get suppressed in some
+%   cases.
+%    \begin{macrocode}
+\cs_new_protected:Npn \luamml_flag_save:n #1 {
+  \tl_set:Nn \l__luamml_label_tl {#1}
+  \int_set:Nn \l__luamml_flag_int { \__luamml_maybe_structelem: 1 }
+}
+\cs_new_protected:Npn \luamml_flag_save:nN #1#2 {
+  \tl_set:Nn \l__luamml_label_tl {#1}
+  \int_set:Nn \l__luamml_flag_int { \__luamml_maybe_structelem: 17 + \__luamml_style_to_num:N #2 }
+}
+\cs_new_protected:Npn \luamml_flag_save:nn #1 {
+  \tl_set:Nn \l__luamml_label_tl {#1}
+  \int_set:Nn \l__luamml_flag_int { \__luamml_maybe_structelem: 5 }
+  \tl_set:Nn \l__luamml_root_tl
+}
+\cs_new_protected:Npn \luamml_flag_save:nNn #1#2 {
+  \tl_set:Nn \l__luamml_label_tl {#1}
+  \int_set:Nn \l__luamml_flag_int { \__luamml_maybe_structelem: 21 + \__luamml_style_to_num:N #2 }
+  \tl_set:Nn \l__luamml_root_tl
+}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\luamml_flag_ignore:}
+%   Completely ignore the math mode material.
+%    \begin{macrocode}
+\cs_new_protected:Npn \luamml_flag_ignore: {
+  \int_set:Nn \l__luamml_flag_int { 0 }
+}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\luamml_flag_structelem:}
+%   Like \cs{luamml_flag_process:}, but additionally add PDF structure
+%   elements. This only works in Lua\TeX\ and requires that the \pkg{tagpdf} package
+%   has been loaded \emph{before} \texttt{luamml}.
+%    \begin{macrocode}
+%<*luatex>
+\cs_new_protected:Npn \luamml_flag_structelem: {
+  \tl_set:Nn \l__luamml_label_tl {}
+  \int_set:Nn \l__luamml_flag_int { 11 }
+}
+%</luatex>
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\luamml_set_filename:n}
+%   Allows to set a filename to which the generated MathML gets written.
+%   Previous content from the file will get overwritten. This includes results
+%   written by a previous formula. Therefore this has to be called separately
+%   for every formula or it must expand to different values to be useful.
+%   The value is fully expanded when the file is written.
+%   
+%   Only complete formulas get written into files (so formulas where
+%   \cs{luamml_flag_process:} or \cs{luamml_flag_structelem:} are in effect).
+%
+%   Only implemented in Lua\TeX, in pdf\TeX\ the arguments for \texttt{pdfmml}
+%   determine the output location.
+%    \begin{macrocode}
+%<*luatex>
+\cs_new_protected:Npn \luamml_set_filename:n {
+  \tl_set:Nn \l__luamml_filename_tl
+}
+%</luatex>
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\luamml_begin_single_file:, \luamml_end_single_file:}
+%   Everything between these two commands gets written into the same XML file.
+%   The filename is expanded when \cs{luamml_begin_single_file:} gets executed.
+%
+%   (Implemented in Lua)
+% \end{macro}
+%
+% By default, the flag is set to assume complete formulas.
+%    \begin{macrocode}
+\luamml_flag_process:
+%    \end{macrocode}
+%
+% \subsection{Annotations}
+% These are implemented very differently depending on the engine, but the interface
+% should be the same.
+% \subsubsection{Lua\TeX}
+%    \begin{macrocode}
+%<*luatex>
+%    \end{macrocode}
+% \begin{macro}{\luamml_annotate:nen, \luamml_annotate:en}
+% A simple annotation scheme: The first argument is the number of top level
+% noads to be annotated, the second parameter the annotation and the third
+% parameter the actual list of math tokens. The first argument can be omitted to
+% let Lua\TeX determine the number itself.
+%
+% Passing the first parameter explicitly is useful for any annotations which
+% should be compatible with fututre pdf\TeX versions of this functionality.
+%    \begin{macrocode}
+\cs_new_protected:Npn \luamml_annotate:nen #1#2#3 {
+  \__luamml_annotate_begin:
+    #3
+  \__luamml_annotate_end:we \tex_numexpr:D #1 \scan_stop: {#2}
+}
+
+\cs_new_protected:Npn \luamml_annotate:en #1#2 {
+  \__luamml_annotate_begin:
+    #2
+  \__luamml_annotate_end:e {#1}
+}
+%    \end{macrocode}
+% \end{macro}
+%
+%    \begin{macrocode}
+%</luatex>
+%    \end{macrocode}
+
+% \subsubsection{pdf\TeX}
+%    \begin{macrocode}
+%<*pdftex>
+%    \end{macrocode}
+% \begin{macro}{\__luamml_pdf_showlists:}
+% Here and in many other locations the \pdfTeX{} implementation is based on \cs{showlists},
+% so we define a internal wrapper which sets all relevant parameters.
+%    \begin{macrocode}
+\cs_if_exist:NTF \showstream {
+  \iow_new:N \l__luamml_pdf_stream
+  \iow_open:Nn \l__luamml_pdf_stream { \jobname .tml }
+  \cs_new_protected:Npn \__luamml_pdf_showlists: {
+    \group_begin:
+      \int_set:Nn \tex_showboxdepth:D { \c_max_int }
+      \int_set:Nn \tex_showboxbreadth:D { \c_max_int }
+      \showstream = \l__luamml_pdf_stream
+      \tex_showlists:D
+    \group_end:
+  }
+} {
+  \cs_set_eq:NN \l__luamml_pdf_stream \c_log_iow
+  \cs_set_eq:NN \__luamml_pdf_set_showstream: \scan_stop:
+  \cs_new_protected:Npn \__luamml_pdf_showlists: {
+    \group_begin:
+      \int_set:Nn \l_tmpa_int { \tex_interactionmode:D }
+      \int_set:Nn \tex_interactionmode:D { 0 }
+      \int_set:Nn \tex_showboxdepth:D { \c_max_int }
+      \int_set:Nn \tex_showboxbreadth:D { \c_max_int }
+      \tex_showlists:D
+      \int_set:Nn \tex_interactionmode:D { \l_tmpa_int }
+    \group_end:
+  }
+}
+%    \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}{\luamml_annotate:nen, \luamml_annotate:en}
+% Now we can define the annotation commands for pdf\TeX.
+%    \begin{macrocode}
+\cs_generate_variant:Nn \tl_to_str:n { e }
+\int_new:N \g__luamml_annotation_id_int
+\cs_new_protected:Npn \luamml_annotate:nen #1#2#3 {
+  \int_gincr:N \g__luamml_annotation_id_int
+  \iow_shipout_x:Nx \l__luamml_pdf_stream {
+    LUAMML_MARK_REF:
+    \int_use:N \g__luamml_annotation_id_int
+    :
+  }
+  \iow_now:Nx \l__luamml_pdf_stream {
+    LUAMML_MARK:
+    \int_use:N \g__luamml_annotation_id_int
+    :
+    count = \int_eval:n {#1},
+    #2
+    \iow_newline:
+    LUAMML_MARK_END
+  }
+  #3
+}
+\cs_new_protected:Npn \luamml_annotate:en #1#2 {
+  \int_gincr:N \g__luamml_annotation_id_int
+  \iow_shipout_x:Nx \l__luamml_pdf_stream {
+    LUAMML_MARK_REF:
+    \int_use:N \g__luamml_annotation_id_int
+    :
+  }
+  \iow_now:Nx \l__luamml_pdf_stream {
+    LUAMML_MARK:
+    \int_use:N \g__luamml_annotation_id_int
+    :
+    count = data.count[\int_use:N \g__luamml_annotation_id_int],
+    #1
+    \iow_newline:
+    LUAMML_MARK_END
+  }
+  \use:x {
+    \iow_now:Nn \l__luamml_pdf_stream {
+      LUAMML_COUNT:
+      \int_use:N \g__luamml_annotation_id_int
+    }
+    \__luamml_pdf_showlists:
+    \exp_not:n {#2}
+    \iow_now:Nn \l__luamml_pdf_stream {
+      LUAMML_COUNT_END:
+      \int_use:N \g__luamml_annotation_id_int
+    }
+    \__luamml_pdf_showlists:
+  }
+}
+%    \end{macrocode}
+% \end{macro}
+%
+%    \begin{macrocode}
+%</pdftex>
+%    \end{macrocode}
+%
+% \subsection{Trigger for specific formula}
+% This only applies for pdf\TeX\ since in Lua\TeX\ everything is controlled by the callback,
+% but for compatibility the function is defined anyway.
+%
+% \begin{macro}{\luamml_pdf_write:}
+% We could accept parameters for the flag and tag here, but for compatibility
+% with Lua\TeX they are passed in macros instead.
+%    \begin{macrocode}
+%<*pdftex>
+\cs_new_protected:Npn \luamml_pdf_write: {
+  \int_gincr:N \g__luamml_formula_id_int
+  \iow_now:Nx \l__luamml_pdf_stream {
+    LUAMML_FORMULA_BEGIN:
+    \int_use:N \g__luamml_formula_id_int
+    :
+    \int_use:N \l__luamml_flag_int
+    :
+    \l__luamml_root_tl
+    :
+    \l__luamml_label_tl
+  }
+  \__luamml_pdf_showlists:
+  \iow_now:Nx \l__luamml_pdf_stream {
+    LUAMML_FORMULA_END
+  }
+}
+%</pdftex>
+%<luatex>\cs_new_eq:NN \luamml_pdf_write: \scan_stop:
+%    \end{macrocode}
+% \end{macro}
+%
+%    \begin{macrocode}
+%    \end{macrocode}
+%
+% \subsection{Further helpers}
+%
+% \begin{macro}{\RegisterFamilyMapping}
+% The Lua version of this is defined in the Lua module.
+%    \begin{macrocode}
+%<*pdftex>
+\NewDocumentCommand \RegisterFamilyMapping {m m} {
+  \iow_now:Nx \l__luamml_pdf_stream {
+    LUAMML_INSTRUCTION:REGISTER_MAPPING: \int_use:N #1 : #2
+  }
+}
+%</pdftex>
+%    \end{macrocode}
+% \end{macro}
+%
+%
+% \subsection{Patching}
+% For some packages, we ship with patches to make them more compatible and to
+% demonstrate how other code can be patched to work with \texttt{luamml}.
+% 
+% These are either loaded directly if the packages are loaded or delayed using
+% \LaTeX's hook system otherwise.
+% \begin{macro}{\__luamml_patch_package:nn, \__luamml_patch_package:n}
+% For this, we use two helpers: First a wrapper which runs arbitrary code either
+% now (if the package is already loaded) or as soon as the package loads, second
+% an application of the first one to load packages following \texttt{luamml}'s
+% naming scheme for these patch packages.
+%    \begin{macrocode}
+\cs_new_protected:Npn \__luamml_patch_package:nn #1 #2 {
+  \@ifpackageloaded {#1} {#2} {
+    \hook_gput_code:nnn {package/#1/after} {luamml} {#2}
+  }
+}
+\cs_new_protected:Npn \__luamml_patch_package:n #1 {
+  \__luamml_patch_package:nn {#1} {
+    \RequirePackage { luamml-patches-#1 }
+  }
+}
+%    \end{macrocode}
+% \end{macro}
+%
+% We currently provide minimal patching for the kernel, \pkg{amsmath} and \pkg{array}.
+% Currently only the kernel code supports pdf\TeX, but it's planned to extend this.
+%    \begin{macrocode}
+\RequirePackage { luamml-patches-kernel }
+%<*luatex>
+\__luamml_patch_package:n {amstext}
+\__luamml_patch_package:n {amsmath}
+\__luamml_patch_package:n {array}
+%</luatex>
+%    \end{macrocode}
+
+% \iffalse
+%</package>
+% \fi
+% \end{implementation}
+% \Finale


Property changes on: trunk/Master/texmf-dist/source/lualatex/luamml/luamml.dtx
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-amsmath.lua
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-amsmath.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-amsmath.lua	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,133 @@
+local write_xml = require'luamml-xmlwriter'
+local make_root = require'luamml-convert'.make_root
+local save_result = require'luamml-tex'.save_result
+local store_column = require'luamml-table'.store_column
+local store_tag = require'luamml-table'.store_tag
+local get_table = require'luamml-table'.get_table
+local set_row_attribute = require'luamml-table'.set_row_attribute
+local to_text = require'luamml-lr'
+
+local properties = node.get_properties_table()
+
+local math_t = node.id'math'
+
+local funcid = luatexbase.new_luafunction'__luamml_amsmath_add_last_to_row:'
+token.set_lua('__luamml_amsmath_add_last_to_row:', funcid, 'protected')
+lua.get_functions_table()[funcid] = function()
+  -- TODO: Error handling etc
+  -- local box = token.scan_int()
+  local nest = tex.nest.top
+  local head, startmath = nest.head, nest.tail
+  repeat
+    startmath = startmath.prev
+  until startmath == head or (startmath.id == math_t and startmath.subtype == 0)
+  if startmath == head then return end
+  assert(startmath.id == node.id"math")
+  store_column(startmath)
+end
+
+local funcid = luatexbase.new_luafunction'__luamml_amsmath_add_box_to_row:'
+token.set_lua('__luamml_amsmath_add_box_to_row:', funcid, 'protected')
+lua.get_functions_table()[funcid] = function()
+  -- TODO: Error handling etc
+  -- local box = token.scan_int()
+  local boxnum = 0
+  local startmath = tex.box[boxnum].list
+  assert(startmath.id == math_t)
+  store_column(startmath)
+end
+
+local funcid = luatexbase.new_luafunction'__luamml_amsmath_set_row_columnalign:n'
+token.set_lua('__luamml_amsmath_set_row_columnalign:n', funcid, 'protected')
+lua.get_functions_table()[funcid] = function()
+  set_row_attribute('columnalign', token.scan_argument())
+end
+
+do
+  local saved
+  funcid = luatexbase.new_luafunction'__luamml_amsmath_save_inner_table:n'
+  token.set_lua('__luamml_amsmath_save_inner_table:n', funcid)
+  lua.get_functions_table()[funcid] = function()
+    -- TODO: Error handling etc
+    local kind = token.scan_argument()
+    local mml_table = get_table()
+    if not mml_table then return end
+    mml_table.displaystyle = true
+    local columns = node.count(node.id'align_record', tex.lists.align_head)//2
+    mml_table.columnalign = kind == 'gathered' and 'center' or string.rep('right left', columns, ' ')
+    local spacing = {}
+    for n in node.traverse_id(node.id'glue', tex.lists.align_head) do
+      spacing[#spacing+1] = n.width == 0 and '0' or string.format('%.3fpt', n.width/65781.76)
+    end
+    mml_table.columnspacing = #spacing > 3 and table.concat(spacing, ' ', 2, #spacing-2) or nil
+    saved = mml_table
+  end
+
+  funcid = luatexbase.new_luafunction'__luamml_amsmath_save_smallmatrix:'
+  token.set_lua('__luamml_amsmath_save_smallmatrix:', funcid)
+  lua.get_functions_table()[funcid] = function()
+    -- TODO: Error handling etc
+    local mml_table = get_table()
+    mml_table.align = 'axis'
+    mml_table.columnalign = 'center'
+    mml_table.columnspacing = '0.278em'
+    mml_table.rowspacing = string.format('%.3fpt', tex.lineskip.width/65781.76)
+    saved = {[0] = 'mpadded', width = '+0.333em', lspace = '0.167em', mml_table}
+    saved = mml_table
+  end
+
+  funcid = luatexbase.new_luafunction'__luamml_amsmath_finalize_inner_table:'
+  token.set_lua('__luamml_amsmath_finalize_inner_table:', funcid)
+  lua.get_functions_table()[funcid] = function()
+    -- TODO: Error handling etc
+    local vcenter = tex.nest.top.tail.nucleus
+    local props = properties[vcenter]
+    if not props then
+      props = {}
+      properties[vcenter] = props
+    end
+    props.mathml_table = assert(saved)
+    saved = nil
+  end
+end
+
+funcid = luatexbase.new_luafunction'__luamml_amsmath_finalize_table:n'
+token.set_lua('__luamml_amsmath_finalize_table:n', funcid)
+lua.get_functions_table()[funcid] = function()
+  -- TODO: Error handling etc
+  local kind = token.scan_argument()
+  local mml_table = get_table()
+  if not mml_table then return end
+  mml_table.displaystyle = true
+  local columns = node.count(node.id'align_record', tex.lists.align_head)//2
+  mml_table.columnalign = kind == 'align' and string.rep('right left', columns, ' ') or nil
+  mml_table.width = kind == 'multline' and '100%' or nil
+  -- mml_table.side = kind == 'multline' and 'rightoverlap' or nil
+  local spacing = {}
+  for n in node.traverse_id(node.id'glue', tex.lists.align_head) do
+    spacing[#spacing+1] = n.width == 0 and '0' or '.8em'
+  end
+  mml_table.columnspacing = #spacing > 3 and table.concat(spacing, ' ', 2, #spacing-2) or nil
+  save_result(mml_table, true)
+end
+
+local last_tag
+
+funcid = luatexbase.new_luafunction'__luamml_amsmath_save_tag:'
+token.set_lua('__luamml_amsmath_save_tag:', funcid, 'protected')
+lua.get_functions_table()[funcid] = function()
+  local nest = tex.nest.top
+  local chars = {}
+  last_tag = to_text(nest.head)
+end
+
+funcid = luatexbase.new_luafunction'__luamml_amsmath_set_tag:'
+token.set_lua('__luamml_amsmath_set_tag:', funcid, 'protected')
+lua.get_functions_table()[funcid] = function()
+  if not last_tag then
+    texio.write_nl'WARNING: Tag extraction failed'
+    return
+  end
+  store_tag({[0] = 'mtd', last_tag})
+  last_tag = nil
+end


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-amsmath.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-array.lua
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-array.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-array.lua	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,84 @@
+local write_xml = require'luamml-xmlwriter'
+local make_root = require'luamml-convert'.make_root
+local save_result = require'luamml-tex'.save_result
+local store_column = require'luamml-table'.store_column
+local store_column_xml = require'luamml-table'.store_column_xml
+local store_tag = require'luamml-table'.store_tag
+local get_table = require'luamml-table'.get_table
+local to_text = require'luamml-lr'
+
+local properties = node.get_properties_table()
+
+local funcid = luatexbase.new_luafunction'__luamml_array_init_col:'
+token.set_lua('__luamml_array_init_col:', funcid, 'protected')
+lua.get_functions_table()[funcid] = function()
+  -- TODO: Error handling etc
+  local nest = tex.nest[tex.nest.ptr-1]
+  -- The special will be deleted again, it just marks the right math list since the start math node is not there yet
+  local special = node.new('whatsit', 'special')
+  node.insert_after(nest.tail, nest.tail, special)
+  nest.tail = special
+  local temp = nest.head
+  local props = properties[temp]
+  if not props then
+    props = {}
+    properties[temp] = props
+  end
+  props.luamml_array_startmath = special
+end
+
+local funcid = luatexbase.new_luafunction'__luamml_array_finalize_col:w'
+token.set_lua('__luamml_array_finalize_col:w', funcid, 'protected')
+lua.get_functions_table()[funcid] = function()
+  local alignment = token.scan_int() -- Do it first to consume number even if we end early
+  -- TODO: Error handling etc
+  local temp = tex.nest.top.head
+  local props = properties[temp]
+  local special = props and props.luamml_array_startmath
+  if not special then return end
+  node.remove(tex.nest.top.head, special)
+  local startmath = node.free(special)
+  props.luamml_array_startmath = nil
+
+  alignment = alignment == 1 and 'left' or alignment == 2 and 'right' or nil
+
+  if node.end_of_math(startmath) == tex.nest.top.tail then
+    if startmath.next == tex.nest.top.tail then return end
+    store_column(startmath).columnalign = alignment
+  else
+    -- Oh no, we got text. Let't complain to the user, it's probably their fault
+    print'We are mathematicians, don\'t bother us with text'
+    store_column_xml(to_text(startmath, tex.nest.top.tail)).columnalign = alignment
+  end
+end
+
+local saved_array
+
+funcid = luatexbase.new_luafunction'__luamml_array_finalize_array:'
+token.set_lua('__luamml_array_save_array:', funcid)
+lua.get_functions_table()[funcid] = function()
+  -- TODO: Error handling etc.
+  local colsep = tex.dimen['col at sep']
+  saved_array = get_table()
+  if colsep ~= 0 then
+    saved_array = {[0] = 'mpadded',
+      width = string.format('%+.3fpt', 2*colsep/65781.76),
+      lspace = string.format('%+.3fpt', colsep/65781.76),
+      saved_array
+    }
+  end
+end
+
+funcid = luatexbase.new_luafunction'__luamml_array_finalize_array:'
+token.set_lua('__luamml_array_finalize_array:', funcid)
+lua.get_functions_table()[funcid] = function()
+  -- TODO: Error handling etc.
+  local nucl = tex.nest.top.tail.nucleus
+  local props = properties[nucl]
+  if not props then
+    props = {}
+    properties[nucl] = props
+  end
+  props.mathml_table = saved_array
+  saved_array = nil
+end


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-array.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-convert.lua
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-convert.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-convert.lua	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,662 @@
+local remap_comb = require'luamml-data-combining'
+local stretchy = require'luamml-data-stretchy'
+local to_text = require'luamml-lr'
+
+local properties = node.get_properties_table()
+
+local hlist_t, kern_t, glue_t, rule_t = node.id'hlist', node.id'kern', node.id'glue', node.id'rule'
+
+local noad_t, accent_t, style_t, choice_t = node.id'noad', node.id'accent', node.id'style', node.id'choice'
+local radical_t, fraction_t, fence_t = node.id'radical', node.id'fraction', node.id'fence'
+
+local math_char_t, sub_box_t, sub_mlist_t = node.id'math_char', node.id'sub_box', node.id'sub_mlist'
+
+local function invert_table(t)
+  local t_inv = {}
+  for k, v in next, t do
+    t_inv[v] = k
+  end
+  return t_inv
+end
+
+local noad_names = node.subtypes'noad'
+
+--[[ We could determine the noad subtypes dynamically:
+local noad_sub = invert_table(noad_names)
+local noad_ord = noad_sub.ord
+local noad_op = noad_sub.opdisplaylimits
+local noad_oplimits = noad_sub.oplimits
+local noad_opnolimits = noad_sub.opnolimits
+local noad_bin = noad_sub.bin
+local noad_rel = noad_sub.rel
+local noad_open = noad_sub.open
+local noad_close = noad_sub.close
+local noad_punct = noad_sub.punct
+local noad_inner = noad_sub.inner
+local noad_under = noad_sub.under
+local noad_over = noad_sub.over
+local noad_vcenter = noad_sub.vcenter
+-- But the spacing table depends on their specific values anyway, so we just verify the values
+]]
+local noad_ord, noad_op, noad_oplimits, noad_opnolimits = 0, 1, 2, 3
+local noad_bin, noad_rel, noad_open, noad_close, noad_punct = 4, 5, 6, 7, 8
+local noad_inner, noad_under, noad_over, noad_vcenter = 9, 10, 11, 12
+
+for i, n in ipairs{'ord', 'opdisplaylimits', 'oplimits', 'opnolimits', 'bin',
+    'rel', 'open', 'close', 'punct', 'inner', 'under', 'over', 'vcenter'} do
+  assert(noad_names[i-1] == n)
+end
+-- Attention, the spacing_table is indexed by subtype+1 since 1-based tables are faster in Lua
+local spacing_table = {
+  {0        , '0.167em', '0.167em', '0.167em', '0.222em', '0.278em', 0        , 0        , 0        , '0.167em', 0        , 0        , 0        , },
+  {'0.167em', '0.167em', '0.167em', '0.167em', nil      , '0.278em', 0        , 0        , 0        , '0.167em', '0.167em', '0.167em', '0.167em', },
+  nil,
+  nil,
+  {'0.222em', '0.222em', '0.222em', '0.222em', nil      , nil      , '0.222em', nil      , nil      , '0.222em', '0.222em', '0.222em', '0.222em', },
+  {'0.278em', '0.278em', '0.278em', '0.278em', nil      , 0        , '0.278em', 0        , 0        , '0.278em', '0.278em', '0.278em', '0.278em', },
+  {0        , 0        , 0        , 0        , nil      , 0        , 0        , 0        , 0        , 0        , 0        , 0        , 0        , },
+  {0        , '0.167em', '0.167em', '0.167em', '0.222em', '0.278em', 0        , 0        , 0        , '0.167em', 0        , 0        , 0        , },
+  {'0.167em', '0.167em', '0.167em', '0.167em', nil      , '0.167em', '0.167em', '0.167em', '0.167em', '0.167em', '0.167em', '0.167em', '0.167em', },
+  {'0.167em', '0.167em', '0.167em', '0.167em', '0.222em', '0.278em', '0.167em', 0        , '0.167em', '0.167em', '0.167em', '0.167em', '0.167em', },
+  nil,
+  nil,
+  nil,
+}
+local spacing_table_script = {
+  {0        , '0.167em', '0.167em', '0.167em', 0        , 0        , 0        , 0        , 0        , 0        , 0        , 0        , 0        , },
+  {'0.167em', '0.167em', '0.167em', '0.167em', nil      , 0        , 0        , 0        , 0        , 0        , '0.167em', '0.167em', '0.167em', },
+  nil,
+  nil,
+  {0        , 0        , 0        , 0        , nil      , nil      , 0        , nil      , nil      , 0        , 0        , 0        , 0        , },
+  {0        , 0        , 0        , 0        , nil      , 0        , 0        , 0        , 0        , 0        , 0        , 0        , 0        , },
+  {0        , 0        , 0        , 0        , nil      , 0        , 0        , 0        , 0        , 0        , 0        , 0        , 0        , },
+  {0        , '0.167em', '0.167em', '0.167em', 0        , 0        , 0        , 0        , 0        , 0        , 0        , 0        , 0        , },
+  {0        , 0        , 0        , 0        , nil      , 0        , 0        , 0        , 0        , 0        , 0        , 0        , 0        , },
+  {0        , '0.167em', '0.167em', '0.167em', 0        , 0        , 0        , 0        , 0        , 0        , 0        , 0        , 0        , },
+  nil,
+  nil,
+  nil,
+}
+do -- Fill the blanks
+  local st, sts = spacing_table, spacing_table_script
+
+  local st_op, sts_op = st[noad_op+1], sts[noad_op+1]
+  st[noad_oplimits+1], sts[noad_oplimits+1] = st_op, sts_op
+  st[noad_opnolimits+1], sts[noad_opnolimits+1] = st_op, sts_op
+
+  local st_ord, sts_ord = st[noad_ord+1], sts[noad_ord+1]
+  st[noad_under+1], sts[noad_under+1] = st_ord, sts_ord
+  st[noad_over+1], sts[noad_over+1] = st_ord, sts_ord
+  st[noad_vcenter+1], sts[noad_vcenter+1] = st_ord, sts_ord
+end
+
+
+local radical_sub = node.subtypes'radical'
+local fence_sub = node.subtypes'fence'
+
+local remap_lookup = setmetatable({}, {__index = function(t, k)
+  local ch = utf8.char(k & 0x1FFFFF)
+  t[k] = ch
+  return ch
+end})
+local digit_map = {["0"] = true, ["1"] = true,
+     ["2"] = true, ["3"] = true, ["4"] = true,
+     ["5"] = true, ["6"] = true, ["7"] = true,
+     ["8"] = true, ["9"] = true,}
+
+local always_mo = {["%"] = true, ["&"] = true, ["."] = true, ["/"] = true,
+    ["\\"] = true, ["¬"] = true, ["′"] = true, ["″"] = true, ["‴"] = true,
+    ["⁗"] = true, ["‵"] = true, ["‶"] = true, ["‷"] = true, ["|"] = true,
+    ["∀"] = true, ["∁"] = true, ["∃"] = true,  ["∂"] = true, ["∄"] = true,}
+
+-- Marker tables replacing the core operator for space like elements
+local space_like = {}
+
+local nodes_to_table
+
+local function sub_style(s) return s//4*2+5 end
+local function sup_style(s) return s//4*2+4+s%2 end
+
+-- The _to_table functions generally return a second argument which is
+-- could be (if it were a <mo>) a core operator of the embellishe operator
+-- or space_like
+-- acc_to_table is special since it's return value should
+-- always be considered a core operator
+
+-- We ignore large_... since they aren't used for modern fonts
+local function delim_to_table(delim)
+  if not delim then return end
+  local props = properties[delim]
+  local mathml_core = props and props.mathml_core
+  local mathml_table = props and (props.mathml_table or mathml_core)
+  if mathml_table ~= nil then return mathml_table, mathml_core end
+  local mathml_filter = props and props.mathml_filter -- Kind of pointless since the arguments are literals, but present for consistency
+  local char = delim.small_char
+  if char == 0 then
+    local result = {[0] = 'mspace', width = string.format("%.3fpt", tex.nulldelimiterspace/65781.76)}
+    if mathml_filter then
+      return mathml_filter(result, space_like)
+    else
+      return result, space_like
+    end
+  else
+    local fam = delim.small_fam
+    char = remap_lookup[fam << 21 | char]
+    local result = {[0] = 'mo', char, ['tex:family'] = fam ~= 0 and fam or nil, stretchy = not stretchy[char] or nil, lspace = 0, rspace = 0, [':nodes'] = {delim}, [':actual'] = char}
+    if mathml_filter then
+      return mathml_filter(result, result)
+    else
+      return result, result
+    end
+  end
+end
+
+-- Like kernel_to_table but always a math_char_t. Also creating a mo and potentially remapping to handle combining chars.
+-- No lspace or space is set here since these never appear as core operators in an mrow.
+local function acc_to_table(acc, cur_style, stretch)
+  if not acc then return end
+  local props = properties[acc]
+  local mathml_core = props and props.mathml_core
+  local mathml_table = props and (props.mathml_table or mathml_core)
+  if mathml_table ~= nil then return mathml_table, mathml_core end
+  if acc.id ~= math_char_t then
+    error'confusion'
+  end
+  local mathml_filter = props and props.mathml_filter -- Kind of pointless since the arguments are literals, but present for consistency
+  local fam = acc.fam
+  local char = remap_lookup[fam << 21 | acc.char]
+  char = remap_comb[char] or char
+  if stretch ~= not stretchy[char] then -- Handle nil gracefully in stretchy
+    stretch = nil
+  end
+  local result = {[0] = 'mo', char, ['tex:family'] = fam ~= 0 and fam or nil, stretchy = stretch, [':nodes'] = {acc}, [':actual'] = stretch and char or nil}
+  if mathml_filter then
+    return mathml_filter(result)
+  else
+    return result
+  end
+end
+
+local function kernel_to_table(kernel, cur_style, text_families)
+  if not kernel then return end
+  local props = properties[kernel]
+  local mathml_core = props and props.mathml_core
+  local mathml_table = props and (props.mathml_table or mathml_core)
+  if mathml_table ~= nil then return mathml_table, mathml_core end
+  local mathml_filter = props and props.mathml_filter -- Kind of pointless since the arguments are literals, but present for consistency
+  local id = kernel.id
+  if id == math_char_t then
+    local fam = kernel.fam
+    local char = remap_lookup[fam << 21 | kernel.char]
+    local elem = digit_map[char] and 'mn' or 'mi'
+    local result = {[0] = elem,
+      char,
+      ['tex:family'] = fam ~= 0 and fam or nil,
+      mathvariant = utf8.len(char) == 1 and elem == 'mi' and utf8.codepoint(char) < 0x10000 and 'normal' or nil,
+      [':nodes'] = {kernel},
+    }
+    if mathml_filter then
+      return mathml_filter(result, result)
+    else
+      return result, result
+    end
+  elseif id == sub_box_t then
+    local result
+    if kernel.list.id == hlist_t then -- We directly give up for vlists
+      result = to_text(kernel.list.head)
+    else
+      result = {[0] = 'mi', {[0] = 'mglyph', ['tex:box'] = kernel.list, [':nodes'] = {kernel}}}
+    end
+    if mathml_filter then
+      return mathml_filter(result, result)
+    else
+      return result, result
+    end
+  elseif id == sub_mlist_t then
+    if mathml_filter then
+      return mathml_filter(nodes_to_table(kernel.list, cur_style, text_families))
+    else
+      return nodes_to_table(kernel.list, cur_style, text_families)
+    end
+  else
+    error'confusion'
+  end
+end
+
+local function do_sub_sup(t, core, n, cur_style, text_families)
+  local sub = kernel_to_table(n.sub, sub_style(cur_style), text_families)
+  local sup = kernel_to_table(n.sup, sup_style(cur_style), text_families)
+  if sub then
+    if sup then
+      return {[0] = 'msubsup', t, sub, sup}, core
+    else
+      return {[0] = 'msub', t, sub}, core
+    end
+  elseif sup then
+    return {[0] = 'msup', t, sup}, core
+  else
+    return t, core
+  end
+end
+
+
+-- If we encounter a . or , after a number, test if it's followed by another number and in that case convert it into a mn
+local function maybe_to_mn(noad, core)
+  if noad.sub or noad.sup then return end
+  local after = noad.next
+  if not after then return end
+  if after.id ~= noad_t then return end
+  if after.subtype ~= noad_ord then return end
+  after = after.nucleus
+  if not after then return end
+  if after.id ~= math_char_t then return end
+  if not digit_map[remap_lookup[after.fam << 21 | after.char]] then return end
+  core[0] = 'mn'
+end
+
+local function noad_to_table(noad, sub, cur_style, joining, bin_replacements, text_families)
+  local nucleus, core = kernel_to_table(noad.nucleus, sub == noad_over and cur_style//2*2+1 or cur_style, text_families)
+  if not nucleus then return end
+  if core and core[0] == 'mo' and core.minsize and not core.maxsize then
+    core.maxsize = core.minsize -- This happens when a half-specified delimiter appears alone in a list.
+                                -- If it has a minimal size, it should be fixed to that size (since there is nothing bigger in it's list)
+  end
+  if sub == noad_ord and not (bin_replacements[node.direct.todirect(noad)] or (nucleus == core and #core == 1 and always_mo[core[1]])) then
+    if core and core[0] == 'mo' then
+      core['tex:class'] = nil
+      if not core.minsize and not core.movablelimits then
+        core[0] = 'mi'
+        core.movablelimits = nil
+        core.mathvariant = #core == 1 and type(core[1]) == 'string' and utf8.len(core[1]) == 1 and utf8.codepoint(core[1]) < 0x10000 and 'normal' or nil
+        core.stretchy, core.lspace, core.rspace = nil
+      end
+    end
+    if nucleus == core and #core == 1 then
+      if joining and joining[0] == 'mn' and core[0] == 'mi' and (core[1] == '.' or core[1] == ',') and maybe_to_mn(noad, core)
+          or core[0] == 'mn' or text_families[core['tex:family'] or 0] then
+        if joining and core[0] == joining[0] and core['tex:family'] == joining['tex:family'] then
+          joining[#joining+1] = core[1]
+          local cnodes = core[':nodes']
+          if cnodes then -- very likely
+            local jnodes = joining[':nodes']
+            if jnodes then -- very likely
+              table.move(cnodes, 1, #cnodes, #jnodes+1, jnodes)
+            else
+              joining[':nodes'] = cnodes
+            end
+          end
+          nucleus = do_sub_sup(joining, joining, noad, cur_style, text_families)
+          if nucleus == joining then
+            return nil, joining, joining
+          else
+            return nucleus, joining, false
+          end
+        elseif not noad.sub and not noad.sup then
+          return core, core, core
+        end
+      end
+    end
+  elseif sub == noad_op or sub == noad_oplimits or sub == noad_opnolimits or sub == noad_bin or sub == noad_rel or sub == noad_open
+      or sub == noad_close or sub == noad_punct or sub == noad_inner or sub == noad_ord then
+    if not core or not core[0] then
+      -- TODO
+    else
+      core[0] = 'mo'
+      if not core.minsize then
+        if stretchy[core[1]] then core.stretchy = false end
+      end
+      if core.mathvariant == 'normal' then core.mathvariant = nil end
+      core.lspace, core.rspace = 0, 0
+    end
+    nucleus['tex:class'] = noad_names[sub]
+
+    if (noad.sup or noad.sub) and (sub == noad_op or sub == noad_oplimits) then
+      if core and core[0] == 'mo' then core.movablelimits = sub == noad_op end
+      local sub = kernel_to_table(noad.sub, sub_style(cur_style), text_families)
+      local sup = kernel_to_table(noad.sup, sup_style(cur_style), text_families)
+      return {[0] = sup and (sub and 'munderover' or 'mover') or 'munder',
+        nucleus,
+        sub or sup,
+        sub and sup,
+      }, core
+    end
+  elseif sub == noad_under then
+    return {[0] = 'munder',
+      nucleus,
+      {[0] = 'mo', '_',},
+    }, core
+  elseif sub == noad_over then
+    return {[0] = 'mover',
+      nucleus,
+      {[0] = 'mo', '\u{203E}',},
+    }, core
+  elseif sub == noad_vcenter then -- Ignored. Nucleus will need special handling anyway
+  else
+    error[[confusion]]
+  end
+  return do_sub_sup(nucleus, core, noad, cur_style, text_families)
+end
+
+local function accent_to_table(accent, sub, cur_style, text_families)
+  local nucleus, core = kernel_to_table(accent.nucleus, cur_style//2*2+1, text_families)
+  local top_acc = acc_to_table(accent.accent, cur_style, sub & 1 == 1)
+  local bot_acc = acc_to_table(accent.bot_accent, cur_style, sub & 2 == 2)
+  return {[0] = top_acc and (bot_acc and 'munderover' or 'mover') or 'munder',
+    nucleus,
+    bot_acc or top_acc,
+    bot_acc and top_acc,
+  }, core
+end
+
+local style_table = {
+  display = {displaystyle = "true", scriptlevel = "0"},
+  text = {displaystyle = "false", scriptlevel = "0"},
+  script = {displaystyle = "false", scriptlevel = "1"},
+  scriptscript = {displaystyle = "false", scriptlevel = "2"},
+}
+
+style_table.crampeddisplay, style_table.crampedtext,
+style_table.crampedscript, style_table.crampedscriptscript = 
+  style_table.display, style_table.text,
+  style_table.script, style_table.scriptscript
+
+local function radical_to_table(radical, sub, cur_style, text_families)
+  local kind = radical_sub[sub]
+  local nucleus, core = kernel_to_table(radical.nucleus, cur_style//2*2+1, text_families)
+  local left = delim_to_table(radical.left)
+  local elem
+  if kind == 'radical' or kind == 'uradical' then
+    -- FIXME: Check that this is really a square root
+    elem, core = {[0] = 'msqrt', nucleus, }, nil
+  elseif kind == 'uroot' then
+    -- FIXME: Check that this is really a root
+    elem, core = {[0] = 'msqrt', nucleus, kernel_to_table(radical.degree, 7, text_families)}, nil
+  elseif kind == 'uunderdelimiter' then
+    elem, core = {[0] = 'munder', left, nucleus}, left
+  elseif kind == 'uoverdelimiter' then
+    elem, core = {[0] = 'mover', left, nucleus}, left
+  elseif kind == 'udelimiterunder' then
+    elem = {[0] = 'munder', nucleus, left}
+  elseif kind == 'udelimiterover' then
+    elem = {[0] = 'mover', nucleus, left}
+  else
+    error[[confusion]]
+  end
+  return do_sub_sup(elem, core, radical, cur_style, text_families)
+end
+
+local function fraction_to_table(fraction, sub, cur_style, text_families)
+  local num, core = kernel_to_table(fraction.num, cur_style + 2 - cur_style//6*2, text_families)
+  local denom = kernel_to_table(fraction.denom, cur_style//2*2 + 3 - cur_style//6*2, text_families)
+  local left = delim_to_table(fraction.left)
+  local right = delim_to_table(fraction.right)
+  local mfrac = {[0] = 'mfrac',
+    linethickness = fraction.width and fraction.width == 0 and 0 or nil,
+    bevelled = fraction.middle and "true" or nil,
+    num,
+    denom,
+  }
+  if left then
+    return {[0] = 'mrow',
+      left,
+      mfrac,
+      right, -- might be nil
+    }
+  elseif right then
+    return {[0] = 'mrow',
+      mfrac,
+      right,
+    }
+  else
+    return mfrac, core
+  end
+end
+
+local function fence_to_table(fence, sub, cur_style)
+  local delim, core = delim_to_table(fence.delim)
+  if core[0] ~= 'mo' then
+    return delim, core
+  end
+  core.fence, core.symmetric = 'true', 'true'
+  local options = fence.options
+  local axis
+  if fence.height ~= 0 or fence.depth ~= 0 then
+    axis = 0xA == options & 0xA
+    local exact = 0x18 == options & 0x18
+    -- We treat them always as exact. mpadded would allow us to support
+    -- non-exact ones too and I will implement that if I ever encounter
+    -- someone who does that intentionally. Until then, we warn people
+    -- since such fences are absurd.
+    if not exact then
+      texio.write_nl'luamml: The document uses a fence with \z
+          explicit dimensions but without the "exact" option. \z
+          This is probably a mistake.'
+    end
+    core.minsize = string.format("%.3fpt", (fence.height + fence.depth)/65781.76)
+    core.maxsize = core.minsize
+  else
+    axis = 0xC ~= options & 0xC
+  end
+  if not axis then
+    texio.write_nl'luamml: Baseline centered fence will be centered around math axis instead'
+  end
+  return delim, core
+end
+
+local function space_to_table(amount, sub, cur_style)
+  if amount == 0 then return end
+  if sub == 99 then -- TODO magic number
+    -- 18*2^16=1179648
+    return {[0] = 'mspace', width = string.format("%.3fem", amount/1179648)}, space_like
+  else
+    -- 65781.76=tex.sp'100bp'/100
+    return {[0] = 'mspace', width = string.format("%.3fpt", amount/65781.76)}, space_like
+  end
+end
+
+local running_length = -1073741824
+local function rule_to_table(rule, sub, cur_style)
+  local width = string.format("%.3fpt", rule.width/65781.76)
+  local height = rule.height
+  if height == running_length then
+    height = '0.8em'
+  else
+    height = string.format("%.3fpt", height/65781.76)
+  end
+  local depth = rule.depth
+  if depth == running_length then
+    depth = '0.2em'
+  else
+    depth = string.format("%.3fpt", depth/65781.76)
+  end
+  return {[0] = 'mspace', mathbackground = 'currentColor', width = width, height = height, depth = depth}, space_like
+end
+
+-- The only part which changes the nodelist, we are converting bin into ord
+-- nodes in the same way TeX would do it later anyway.
+local function cleanup_mathbin(head)
+  local replacements = {}
+  local last = 'open' -- last sub if id was noad_t, left fence acts fakes being a open noad, bin are themselves. Every other noad is ord
+  for n, id, sub in node.traverse(head) do
+    if id == noad_t then
+      if sub == noad_bin then
+        if node.is_node(last) or last == noad_opdisplaylimits
+            or last == noad_oplimits or last == noad_opnolimits
+            or last == noad_rel or last == noad_open or last == noad_punct then
+          replacements[node.direct.todirect(n)] = true
+          n.subtype, last = noad_ord, noad_ord
+        else
+          last = n
+        end
+      else
+        if (sub == noad_rel or sub == noad_close or sub == noad_punct)
+            and node.is_node(last) then
+          replacements[node.direct.todirect(last)] = true
+          last.subtype = noad_ord
+        end
+        last = sub
+      end
+    elseif id == fence_t then
+      if sub == fence_sub.left then
+        last = noad_open
+      else
+        if node.is_node(last) then
+          replacements[node.direct.todirect(last)] = true
+          last.subtype = noad_ord, noad_ord
+        end
+        last = noad_ord
+      end
+    elseif id == fraction_t or id == radical_t or id == accent_t then
+      last = noad_ord
+    end
+  end
+  if node.is_node(last) then
+    replacements[node.direct.todirect(last)] = true
+    last.subtype = noad_ord
+  end
+  return replacements
+end
+
+function nodes_to_table(head, cur_style, text_families)
+  local bin_replacements = cleanup_mathbin(head)
+  local t = {[0] = 'mrow'}
+  local result = t
+  local nonscript
+  local core, last_noad, last_core, joining = space_like, nil, nil, nil
+  for n, id, sub in node.traverse(head) do
+    local new_core, new_joining, new_node, new_noad
+    local props = properties[n]
+    local mathml_core = props and props.mathml_core
+    local mathml_table = props and (props.mathml_table or mathml_core)
+    if mathml_table ~= nil then
+      new_node, new_core = mathml_table, mathml_core
+    elseif id == noad_t then
+      local new_n
+      new_n, new_core, new_joining = noad_to_table(n, sub, cur_style, joining, bin_replacements, text_families)
+      if new_joining == false then
+        t[#t], new_joining = new_n, nil
+      else
+        new_node = new_n -- might be nil
+      end
+      new_noad = sub
+    elseif id == accent_t then
+      new_node, new_core = accent_to_table(n, sub, cur_style, text_families)
+      new_noad = noad_ord
+    elseif id == style_t then
+      if sub ~= cur_style then
+        if #t == 0 then
+          t[0] = 'mstyle'
+        else
+          local new_t = {[0] = 'mstyle'}
+          t[#t+1] = new_t
+          t = new_t
+        end
+        if sub < 2 then
+          t.displaystyle, t.scriptlevel = true, 0
+        else
+          t.displaystyle, t.scriptlevel = false, sub//2 - 1
+        end
+        cur_style = sub
+      end
+      new_core = space_like
+    elseif id == choice_t then
+      local size = cur_style//2
+      new_node, new_core = nodes_to_table(n[size == 0 and 'display'
+                                        or size == 1 and 'text'
+                                        or size == 2 and 'script'
+                                        or size == 3 and 'scriptscript'
+                                        or assert(false)], 2*size, text_families), space_like
+    elseif id == radical_t then
+      new_node, new_core = radical_to_table(n, sub, cur_style, text_families)
+      new_noad = noad_ord
+    elseif id == fraction_t then
+      new_node, new_core = fraction_to_table(n, sub, cur_style, text_families)
+      new_noad = noad_inner
+    elseif id == fence_t then
+      new_node, new_core = fence_to_table(n, sub, cur_style)
+      local class = n.class
+      new_noad = class >= 0 and class or sub == fence_sub.left and noad_open or noad_close
+    elseif id == kern_t then
+      if not nonscript then
+        new_node, new_core = space_to_table(n.kern, sub, cur_style)
+      end
+    elseif id == glue_t then
+      if cur_style >= 4 or not nonscript then
+        if sub == 98 then -- TODO magic number
+          nonscript = true
+        else
+          new_node, new_core = space_to_table(n.width, sub, cur_style)
+        end
+      end
+    elseif id == rule_t then
+      new_node, new_core = rule_to_table(n, sub, cur_style)
+    -- elseif id == disc_t then -- Uncommon, does not play nicely with math mode and no sensible mapping anyway
+    end -- The other possible ids are whatsit, penalty, adjust, ins, mark. Ignore them.
+    nonscript = nil
+    if core and new_core ~= space_like then
+      core = core == space_like and new_core or nil
+    end
+    if new_node then
+      if new_noad then
+        local space = last_noad and (cur_style >= 4 and spacing_table_script or spacing_table)[last_noad + 1][new_noad + 1] or 0
+        if assert(space) ~= 0 then
+          if new_core and new_core[0] == 'mo' then
+            new_core.lspace = space
+          elseif last_core and last_core[0] == 'mo' then
+            last_core.rspace = space
+          else
+            t[#t+1] = {[0] = 'mspace', width = space} -- TODO Move into operators whenever possible
+          end
+        end
+        last_noad, last_core = new_noad, new_core
+      elseif new_node[0] ~= 'mspace' or new_node.mathbackground then
+        last_core = nil
+      end
+      t[#t+1] = new_node
+    end
+    joining = new_joining
+  end
+  -- In TeX, groups are never space like, so we insert an artificial node instead.
+  -- This node should be ignored for most purposes
+  if core == space_like then
+    core = {[0] = 'mi', ['tex:ignore'] = 'true'}
+    result[#result+1] = core
+  end
+  if t[0] == 'mrow' and #t == 1 then
+    assert(t == result)
+    result = t[1]
+  end
+  local mathml_filter = props and props.mathml_filter
+  if mathml_filter then
+    return mathml_filter(result, core)
+  else
+    return result, core
+  end
+end
+
+local function register_remap(family, mapping)
+  family = family << 21
+  for from, to in next, mapping do
+    remap_lookup[family | from] = utf8.char(to)
+  end
+end
+
+local function to_math(root, style)
+  if root[0] == 'mrow' then
+    root[0] = 'math'
+  else
+    root = {[0] = 'math', root}
+  end
+  root.xmlns = 'http://www.w3.org/1998/Math/MathML'
+  root['xmlns:tex'] = 'http://typesetting.eu/2021/LuaMathML'
+  if style < 2 then
+    root.display = 'block'
+  end
+  return root
+end
+
+return {
+  register_family = register_remap,
+  process = function(head, style, families) return nodes_to_table(head, style or 2, families) end,
+  make_root = to_math,
+}


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-convert.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-data-combining.lua
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-data-combining.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-data-combining.lua	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,27 @@
+return {
+  ["\u{0332}"] = "\u{2212}",
+  ["\u{0330}"] = "\u{02DC}",
+  ["\u{0328}"] = "\u{02DB}",
+  ["\u{20EF}"] = "\u{2192}",
+  ["\u{032C}"] = "\u{02C7}",
+  ["\u{032E}"] = "\u{02D8}",
+  ["\u{0306}"] = "\u{02D8}",
+  ["\u{030B}"] = "\u{02DD}",
+  ["\u{0302}"] = "\u{02C6}",
+  ["\u{0324}"] = "\u{00A8}",
+  ["\u{0317}"] = "\u{00B4}",
+  ["\u{031F}"] = "\u{002B}",
+  ["\u{0307}"] = "\u{002E}",
+  ["\u{0305}"] = "\u{2212}",
+  ["\u{0303}"] = "\u{02DC}",
+  ["\u{0316}"] = "\u{0060}",
+  ["\u{0301}"] = "\u{00B4}",
+  ["\u{030C}"] = "\u{02C7}",
+  ["\u{0327}"] = "\u{00B8}",
+  ["\u{0308}"] = "\u{00A8}",
+  ["\u{0300}"] = "\u{0060}",
+  ["\u{0323}"] = "\u{002E}",
+  ["\u{0304}"] = "\u{00AF}",
+  ["\u{032D}"] = "\u{005E}",
+  ["\u{20D7}"] = "\u{2192}",
+}


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-data-combining.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-data-stretchy.lua
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-data-stretchy.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-data-stretchy.lua	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,500 @@
+return {
+  -- The following is
+  -- xq -r '.unicode.charlist.character[] | select(.["operator-dictionary"] | if type == "array" then .[] else . end | .["@stretchy"]) | .["@id"] | sub("U"; "") | "[\"\\u{\(.)}\"] = true," ' < unicode.xml
+["\u{00028}"] = true,
+["\u{00029}"] = true,
+["\u{0005B}"] = true,
+["\u{0005D}"] = true,
+["\u{0005E}"] = true,
+["\u{0005F}"] = true,
+["\u{0007B}"] = true,
+["\u{0007C}"] = true,
+["\u{0007C}"] = true,
+["\u{0007D}"] = true,
+["\u{0007E}"] = true,
+["\u{000AF}"] = true,
+["\u{002C6}"] = true,
+["\u{002C7}"] = true,
+["\u{002C9}"] = true,
+["\u{002CD}"] = true,
+["\u{002DC}"] = true,
+["\u{002F7}"] = true,
+["\u{00302}"] = true,
+["\u{02016}"] = true,
+["\u{02016}"] = true,
+["\u{0203E}"] = true,
+["\u{02190}"] = true,
+["\u{02191}"] = true,
+["\u{02192}"] = true,
+["\u{02193}"] = true,
+["\u{02194}"] = true,
+["\u{02195}"] = true,
+["\u{0219A}"] = true,
+["\u{0219B}"] = true,
+["\u{0219C}"] = true,
+["\u{0219D}"] = true,
+["\u{0219E}"] = true,
+["\u{0219F}"] = true,
+["\u{021A0}"] = true,
+["\u{021A1}"] = true,
+["\u{021A2}"] = true,
+["\u{021A3}"] = true,
+["\u{021A4}"] = true,
+["\u{021A5}"] = true,
+["\u{021A6}"] = true,
+["\u{021A7}"] = true,
+["\u{021A8}"] = true,
+["\u{021A9}"] = true,
+["\u{021AA}"] = true,
+["\u{021AB}"] = true,
+["\u{021AC}"] = true,
+["\u{021AD}"] = true,
+["\u{021AE}"] = true,
+["\u{021B0}"] = true,
+["\u{021B1}"] = true,
+["\u{021B2}"] = true,
+["\u{021B3}"] = true,
+["\u{021B4}"] = true,
+["\u{021B5}"] = true,
+["\u{021B9}"] = true,
+["\u{021BC}"] = true,
+["\u{021BD}"] = true,
+["\u{021BE}"] = true,
+["\u{021BF}"] = true,
+["\u{021C0}"] = true,
+["\u{021C1}"] = true,
+["\u{021C2}"] = true,
+["\u{021C3}"] = true,
+["\u{021C4}"] = true,
+["\u{021C5}"] = true,
+["\u{021C6}"] = true,
+["\u{021C7}"] = true,
+["\u{021C8}"] = true,
+["\u{021C9}"] = true,
+["\u{021CA}"] = true,
+["\u{021CB}"] = true,
+["\u{021CC}"] = true,
+["\u{021CD}"] = true,
+["\u{021CE}"] = true,
+["\u{021CF}"] = true,
+["\u{021D0}"] = true,
+["\u{021D1}"] = true,
+["\u{021D2}"] = true,
+["\u{021D3}"] = true,
+["\u{021D4}"] = true,
+["\u{021D5}"] = true,
+["\u{021DA}"] = true,
+["\u{021DB}"] = true,
+["\u{021DC}"] = true,
+["\u{021DD}"] = true,
+["\u{021DE}"] = true,
+["\u{021DF}"] = true,
+["\u{021E0}"] = true,
+["\u{021E1}"] = true,
+["\u{021E2}"] = true,
+["\u{021E3}"] = true,
+["\u{021E4}"] = true,
+["\u{021E5}"] = true,
+["\u{021E6}"] = true,
+["\u{021E7}"] = true,
+["\u{021E8}"] = true,
+["\u{021E9}"] = true,
+["\u{021EA}"] = true,
+["\u{021EB}"] = true,
+["\u{021EC}"] = true,
+["\u{021ED}"] = true,
+["\u{021EE}"] = true,
+["\u{021EF}"] = true,
+["\u{021F0}"] = true,
+["\u{021F3}"] = true,
+["\u{021F4}"] = true,
+["\u{021F5}"] = true,
+["\u{021F6}"] = true,
+["\u{021F7}"] = true,
+["\u{021F8}"] = true,
+["\u{021F9}"] = true,
+["\u{021FA}"] = true,
+["\u{021FB}"] = true,
+["\u{021FC}"] = true,
+["\u{021FD}"] = true,
+["\u{021FE}"] = true,
+["\u{021FF}"] = true,
+["\u{02308}"] = true,
+["\u{02309}"] = true,
+["\u{0230A}"] = true,
+["\u{0230B}"] = true,
+["\u{02322}"] = true,
+["\u{02323}"] = true,
+["\u{02329}"] = true,
+["\u{0232A}"] = true,
+["\u{023B4}"] = true,
+["\u{023B5}"] = true,
+["\u{023DC}"] = true,
+["\u{023DD}"] = true,
+["\u{023DE}"] = true,
+["\u{023DF}"] = true,
+["\u{023E0}"] = true,
+["\u{023E1}"] = true,
+["\u{02772}"] = true,
+["\u{02773}"] = true,
+["\u{02794}"] = true,
+["\u{02799}"] = true,
+["\u{0279B}"] = true,
+["\u{0279C}"] = true,
+["\u{0279D}"] = true,
+["\u{0279E}"] = true,
+["\u{0279F}"] = true,
+["\u{027A0}"] = true,
+["\u{027A1}"] = true,
+["\u{027A5}"] = true,
+["\u{027A6}"] = true,
+["\u{027A8}"] = true,
+["\u{027A9}"] = true,
+["\u{027AA}"] = true,
+["\u{027AB}"] = true,
+["\u{027AC}"] = true,
+["\u{027AD}"] = true,
+["\u{027AE}"] = true,
+["\u{027AF}"] = true,
+["\u{027B1}"] = true,
+["\u{027B3}"] = true,
+["\u{027B5}"] = true,
+["\u{027B8}"] = true,
+["\u{027BA}"] = true,
+["\u{027BB}"] = true,
+["\u{027BC}"] = true,
+["\u{027BD}"] = true,
+["\u{027BE}"] = true,
+["\u{027E6}"] = true,
+["\u{027E7}"] = true,
+["\u{027E8}"] = true,
+["\u{027E9}"] = true,
+["\u{027EA}"] = true,
+["\u{027EB}"] = true,
+["\u{027EC}"] = true,
+["\u{027ED}"] = true,
+["\u{027EE}"] = true,
+["\u{027EF}"] = true,
+["\u{027F0}"] = true,
+["\u{027F1}"] = true,
+["\u{027F4}"] = true,
+["\u{027F5}"] = true,
+["\u{027F6}"] = true,
+["\u{027F7}"] = true,
+["\u{027F8}"] = true,
+["\u{027F9}"] = true,
+["\u{027FA}"] = true,
+["\u{027FB}"] = true,
+["\u{027FC}"] = true,
+["\u{027FD}"] = true,
+["\u{027FE}"] = true,
+["\u{027FF}"] = true,
+["\u{02900}"] = true,
+["\u{02901}"] = true,
+["\u{02902}"] = true,
+["\u{02903}"] = true,
+["\u{02904}"] = true,
+["\u{02905}"] = true,
+["\u{02906}"] = true,
+["\u{02907}"] = true,
+["\u{02908}"] = true,
+["\u{02909}"] = true,
+["\u{0290A}"] = true,
+["\u{0290B}"] = true,
+["\u{0290C}"] = true,
+["\u{0290D}"] = true,
+["\u{0290E}"] = true,
+["\u{0290F}"] = true,
+["\u{02910}"] = true,
+["\u{02911}"] = true,
+["\u{02912}"] = true,
+["\u{02913}"] = true,
+["\u{02914}"] = true,
+["\u{02915}"] = true,
+["\u{02916}"] = true,
+["\u{02917}"] = true,
+["\u{02918}"] = true,
+["\u{02919}"] = true,
+["\u{0291A}"] = true,
+["\u{0291B}"] = true,
+["\u{0291C}"] = true,
+["\u{0291D}"] = true,
+["\u{0291E}"] = true,
+["\u{0291F}"] = true,
+["\u{02920}"] = true,
+["\u{02934}"] = true,
+["\u{02935}"] = true,
+["\u{02936}"] = true,
+["\u{02937}"] = true,
+["\u{02942}"] = true,
+["\u{02943}"] = true,
+["\u{02944}"] = true,
+["\u{02945}"] = true,
+["\u{02946}"] = true,
+["\u{02947}"] = true,
+["\u{02948}"] = true,
+["\u{02949}"] = true,
+["\u{0294A}"] = true,
+["\u{0294B}"] = true,
+["\u{0294C}"] = true,
+["\u{0294D}"] = true,
+["\u{0294E}"] = true,
+["\u{0294F}"] = true,
+["\u{02950}"] = true,
+["\u{02951}"] = true,
+["\u{02952}"] = true,
+["\u{02953}"] = true,
+["\u{02954}"] = true,
+["\u{02955}"] = true,
+["\u{02956}"] = true,
+["\u{02957}"] = true,
+["\u{02958}"] = true,
+["\u{02959}"] = true,
+["\u{0295A}"] = true,
+["\u{0295B}"] = true,
+["\u{0295C}"] = true,
+["\u{0295D}"] = true,
+["\u{0295E}"] = true,
+["\u{0295F}"] = true,
+["\u{02960}"] = true,
+["\u{02961}"] = true,
+["\u{02962}"] = true,
+["\u{02963}"] = true,
+["\u{02964}"] = true,
+["\u{02965}"] = true,
+["\u{02966}"] = true,
+["\u{02967}"] = true,
+["\u{02968}"] = true,
+["\u{02969}"] = true,
+["\u{0296A}"] = true,
+["\u{0296B}"] = true,
+["\u{0296C}"] = true,
+["\u{0296D}"] = true,
+["\u{0296E}"] = true,
+["\u{0296F}"] = true,
+["\u{02970}"] = true,
+["\u{02971}"] = true,
+["\u{02972}"] = true,
+["\u{02973}"] = true,
+["\u{02974}"] = true,
+["\u{02975}"] = true,
+["\u{0297C}"] = true,
+["\u{0297D}"] = true,
+["\u{0297E}"] = true,
+["\u{0297F}"] = true,
+["\u{02980}"] = true,
+["\u{02980}"] = true,
+["\u{02983}"] = true,
+["\u{02984}"] = true,
+["\u{02985}"] = true,
+["\u{02986}"] = true,
+["\u{02987}"] = true,
+["\u{02988}"] = true,
+["\u{02989}"] = true,
+["\u{0298A}"] = true,
+["\u{0298B}"] = true,
+["\u{0298C}"] = true,
+["\u{0298D}"] = true,
+["\u{0298E}"] = true,
+["\u{0298F}"] = true,
+["\u{02990}"] = true,
+["\u{02991}"] = true,
+["\u{02992}"] = true,
+["\u{02993}"] = true,
+["\u{02994}"] = true,
+["\u{02995}"] = true,
+["\u{02996}"] = true,
+["\u{02997}"] = true,
+["\u{02998}"] = true,
+["\u{02999}"] = true,
+["\u{02999}"] = true,
+["\u{029D8}"] = true,
+["\u{029D9}"] = true,
+["\u{029DA}"] = true,
+["\u{029DB}"] = true,
+["\u{029FC}"] = true,
+["\u{029FD}"] = true,
+["\u{02B04}"] = true,
+["\u{02B05}"] = true,
+["\u{02B06}"] = true,
+["\u{02B07}"] = true,
+["\u{02B0C}"] = true,
+["\u{02B0D}"] = true,
+["\u{02B0E}"] = true,
+["\u{02B0F}"] = true,
+["\u{02B10}"] = true,
+["\u{02B11}"] = true,
+["\u{02B30}"] = true,
+["\u{02B31}"] = true,
+["\u{02B32}"] = true,
+["\u{02B33}"] = true,
+["\u{02B34}"] = true,
+["\u{02B35}"] = true,
+["\u{02B36}"] = true,
+["\u{02B37}"] = true,
+["\u{02B38}"] = true,
+["\u{02B39}"] = true,
+["\u{02B3A}"] = true,
+["\u{02B3B}"] = true,
+["\u{02B3C}"] = true,
+["\u{02B3D}"] = true,
+["\u{02B3E}"] = true,
+["\u{02B40}"] = true,
+["\u{02B41}"] = true,
+["\u{02B42}"] = true,
+["\u{02B43}"] = true,
+["\u{02B44}"] = true,
+["\u{02B45}"] = true,
+["\u{02B46}"] = true,
+["\u{02B47}"] = true,
+["\u{02B48}"] = true,
+["\u{02B49}"] = true,
+["\u{02B4A}"] = true,
+["\u{02B4B}"] = true,
+["\u{02B4C}"] = true,
+["\u{02B60}"] = true,
+["\u{02B61}"] = true,
+["\u{02B62}"] = true,
+["\u{02B63}"] = true,
+["\u{02B64}"] = true,
+["\u{02B65}"] = true,
+["\u{02B6A}"] = true,
+["\u{02B6B}"] = true,
+["\u{02B6C}"] = true,
+["\u{02B6D}"] = true,
+["\u{02B70}"] = true,
+["\u{02B71}"] = true,
+["\u{02B72}"] = true,
+["\u{02B73}"] = true,
+["\u{02B7A}"] = true,
+["\u{02B7B}"] = true,
+["\u{02B7C}"] = true,
+["\u{02B7D}"] = true,
+["\u{02B80}"] = true,
+["\u{02B81}"] = true,
+["\u{02B82}"] = true,
+["\u{02B83}"] = true,
+["\u{02B84}"] = true,
+["\u{02B85}"] = true,
+["\u{02B86}"] = true,
+["\u{02B87}"] = true,
+["\u{02B95}"] = true,
+["\u{02BA0}"] = true,
+["\u{02BA1}"] = true,
+["\u{02BA2}"] = true,
+["\u{02BA3}"] = true,
+["\u{02BA4}"] = true,
+["\u{02BA5}"] = true,
+["\u{02BA6}"] = true,
+["\u{02BA7}"] = true,
+["\u{02BA8}"] = true,
+["\u{02BA9}"] = true,
+["\u{02BAA}"] = true,
+["\u{02BAB}"] = true,
+["\u{02BAC}"] = true,
+["\u{02BAD}"] = true,
+["\u{02BAE}"] = true,
+["\u{02BAF}"] = true,
+["\u{02BB8}"] = true,
+["\u{1EEF0}"] = true,
+["\u{1EEF1}"] = true,
+["\u{1F800}"] = true,
+["\u{1F801}"] = true,
+["\u{1F802}"] = true,
+["\u{1F803}"] = true,
+["\u{1F804}"] = true,
+["\u{1F805}"] = true,
+["\u{1F806}"] = true,
+["\u{1F807}"] = true,
+["\u{1F808}"] = true,
+["\u{1F809}"] = true,
+["\u{1F80A}"] = true,
+["\u{1F80B}"] = true,
+["\u{1F810}"] = true,
+["\u{1F811}"] = true,
+["\u{1F812}"] = true,
+["\u{1F813}"] = true,
+["\u{1F814}"] = true,
+["\u{1F815}"] = true,
+["\u{1F816}"] = true,
+["\u{1F817}"] = true,
+["\u{1F818}"] = true,
+["\u{1F819}"] = true,
+["\u{1F81A}"] = true,
+["\u{1F81B}"] = true,
+["\u{1F81C}"] = true,
+["\u{1F81D}"] = true,
+["\u{1F81E}"] = true,
+["\u{1F81F}"] = true,
+["\u{1F820}"] = true,
+["\u{1F821}"] = true,
+["\u{1F822}"] = true,
+["\u{1F823}"] = true,
+["\u{1F824}"] = true,
+["\u{1F825}"] = true,
+["\u{1F826}"] = true,
+["\u{1F827}"] = true,
+["\u{1F828}"] = true,
+["\u{1F829}"] = true,
+["\u{1F82A}"] = true,
+["\u{1F82B}"] = true,
+["\u{1F82C}"] = true,
+["\u{1F82D}"] = true,
+["\u{1F82E}"] = true,
+["\u{1F82F}"] = true,
+["\u{1F830}"] = true,
+["\u{1F831}"] = true,
+["\u{1F832}"] = true,
+["\u{1F833}"] = true,
+["\u{1F834}"] = true,
+["\u{1F835}"] = true,
+["\u{1F836}"] = true,
+["\u{1F837}"] = true,
+["\u{1F844}"] = true,
+["\u{1F845}"] = true,
+["\u{1F846}"] = true,
+["\u{1F847}"] = true,
+["\u{1F850}"] = true,
+["\u{1F851}"] = true,
+["\u{1F852}"] = true,
+["\u{1F853}"] = true,
+["\u{1F858}"] = true,
+["\u{1F859}"] = true,
+["\u{1F860}"] = true,
+["\u{1F861}"] = true,
+["\u{1F862}"] = true,
+["\u{1F863}"] = true,
+["\u{1F868}"] = true,
+["\u{1F869}"] = true,
+["\u{1F86A}"] = true,
+["\u{1F86B}"] = true,
+["\u{1F870}"] = true,
+["\u{1F871}"] = true,
+["\u{1F872}"] = true,
+["\u{1F873}"] = true,
+["\u{1F878}"] = true,
+["\u{1F879}"] = true,
+["\u{1F87A}"] = true,
+["\u{1F87B}"] = true,
+["\u{1F880}"] = true,
+["\u{1F881}"] = true,
+["\u{1F882}"] = true,
+["\u{1F883}"] = true,
+["\u{1F898}"] = true,
+["\u{1F899}"] = true,
+["\u{1F89A}"] = true,
+["\u{1F89B}"] = true,
+["\u{1F8A0}"] = true,
+["\u{1F8A1}"] = true,
+["\u{1F8A2}"] = true,
+["\u{1F8A3}"] = true,
+["\u{1F8A4}"] = true,
+["\u{1F8A5}"] = true,
+["\u{1F8A6}"] = true,
+["\u{1F8A7}"] = true,
+["\u{1F8A8}"] = true,
+["\u{1F8A9}"] = true,
+["\u{1F8AA}"] = true,
+["\u{1F8AB}"] = true,
+-- till here
+}


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-data-stretchy.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-demo.sty
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-demo.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-demo.sty	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,82 @@
+\NeedsTeXFormat{LaTeX2e}
+\ProvidesExplPackage{luamml-demo}{2024-08-14}{v0.1.0}{Reasonable default definitions for luamml}
+
+\sys_if_engine_luatex:F {
+  \msg_new:nnn {luamml-demo} {pdftex-option-ignored} {Option~`#1'~is~being~ignored~in~pdfTeX~mode.}
+  \DeclareOption*{\msg_warning:nnx {luamml-demo} {pdftex-option-ignored} {\CurrentOption}}
+  \ProcessOptions\relax
+  \RequirePackage{luamml-pdf-demo}
+  \endinput
+}
+
+\RequirePackage{luamml}% Loading luamml is pretty much the point
+\RequirePackage{amsmath,array}% These are more or less expected in luamml especially for advanced constructs
+
+\AtBeginDocument{%
+  \@ifpackageloaded{unicode-math}{}{%
+    \RegisterFamilyMapping\symsymbols{oms}%
+    \RegisterFamilyMapping\symletters{oml}%
+    \RegisterFamilyMapping\symlargesymbols{omx}%
+  }
+}
+
+\bool_new:N \l__luamml_demo_structelem_bool
+
+\DeclareOption{tracing}{
+  \tracingmathml=2
+}
+\DeclareOption{structelem}{
+  \bool_set_true:N \l__luamml_demo_structelem_bool
+  \luamml_flag_structelem:
+}
+\DeclareOption{files}{
+  \int_new:N \g__luamml_demo_mathml_int
+  \luamml_set_filename:n {
+    \immediateassignment \int_gincr:N \g__luamml_demo_mathml_int
+    \jobname -formula- \int_use:N \g__luamml_demo_mathml_int .xml
+  }
+}
+\DeclareOption{l3build}{
+  \luamml_set_filename:n {
+    \jobname .mml
+  }
+  \luamml_begin_single_file:
+}
+\ProcessOptions\relax
+
+\cs_new_eq:NN \LuaMMLSetFilename \luamml_set_filename:n
+
+\cs_generate_variant:Nn \pdffile_filespec:nnn {ene}
+\int_new:N \g__luamml_demo_af_int
+\cs_new_protected:Npn \LuaMMLTagAF #1#2 {
+  \tag_mc_end_push:
+  \int_gincr:N \g__luamml_demo_af_int
+  \exp_args:Ne \pdf_object_new:nn{__luamml_demo_\int_use:N \g__luamml_demo_af_int}{dict}
+  \exp_args:Ne \tag_struct_begin:n{tag=Formula,AF=__luamml_demo_\int_use:N \g__luamml_demo_af_int,#1}
+  \bool_if:NF \l__luamml_demo_structelem_bool {
+    \tag_mc_begin:n{tag=Formula}
+  }
+  #2
+  \group_begin:
+    \pdfdict_put:nnn {l_pdffile/Filespec} {AFRelationship}{/Supplement}
+    \pdffile_filespec:ene
+      { __luamml_demo_ \int_use:N \g__luamml_demo_af_int }
+      { test.xml }
+      { \luamml_get_last_mathml_stream:e{}\c_space_tl 0~R}
+  \group_end:
+  \bool_if:NF \l__luamml_demo_structelem_bool {
+    \tag_mc_end:
+  }
+  \tag_struct_end:
+  \tag_mc_begin_pop:n{}
+}
+
+\NewDocumentCommand\AnnotateFormula{ o m m }{%
+  \IfValueTF{#1}{%
+    \luamml_annotate:nen{#1}%
+  }{
+    \luamml_annotate:en
+  }{#2}{#3}
+}
+
+\cs_set_eq:NN \WriteoutFormula \luamml_pdf_write:


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-demo.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-legacy-mappings.lua
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-legacy-mappings.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-legacy-mappings.lua	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,73 @@
+-- local remap_ot1 = {
+  -- 0x0393, 0x0394, 0x0398, 0x039B, 0x039E, 0x03A0, 0x03A3, 0x03A5,
+  -- 0x03A6, 0x03A8, 0x03A9, nil, nil, nil, nil, nil,
+-- }
+
+local remap_oml = { [0] =
+  -- Greek italic
+  0x1D6E4, 0x1D6E5, 0x1D6E9, 0x1D6EC, 0x1D6EF, 0x1D6F1, 0x1D6F4, 0x1D6F6,
+  0x1D6F7, 0x1D6F9, 0x1D6FA, 0x1D6FC, 0x1D6FD, 0x1D6FE, 0x1D6FF, 0x1D716,
+  0x1D701, 0x1D702, 0x1D703, 0x1D704, 0x1D705, 0x1D706, 0x1D707, 0x1D708,
+  0x1D709, 0x1D70B, 0x1D70C, 0x1D70E, 0x1D70F, 0x1D710, 0x1D719, 0x1D712,
+  0x1D713, 0x1D714, 0x1D700, 0x1D717, 0x1D71B, 0x1D71A, 0x1D70D, 0x1D711,
+  -- Symbols. (The nils are hook parts)
+  0x21BC, 0x21BD, 0x21C0, 0x21C1, nil, nil, 0x22BB, 0x22BC,
+  -- old style numerals (nobody should ever use these in math) and some punctuation
+  nil, nil, nil, nil, nil, nil, nil, nil,
+  nil, nil, 0x2E, 0x2C, 0x3C, 0x2F, 0x3E, 0x226D,
+  -- letters filled up with symbols
+  0x2202, 0x1D434, 0x1D435, 0x1D436, 0x1D437, 0x1D438, 0x1D439, 0x1D43A,
+  0x1D43B, 0x1D43C, 0x1D43D, 0x1D43E, 0x1D43F, 0x1D440, 0x1D441, 0x1D442,
+  0x1D443, 0x1D444, 0x1D445, 0x1D446, 0x1D447, 0x1D448, 0x1D449, 0x1D44A,
+  0x1D44B, 0x1D44C, 0x1D44D, 0x266D, 0x266E, 0x266F, 0x2323, 0x2322,
+  0x2113, 0x1D44E, 0x1D44F, 0x1D450, 0x1D451, 0x1D452, 0x1D453, 0x1D454,
+  0x210E, 0x1D456, 0x1D457, 0x1D458, 0x1D459, 0x1D45A, 0x1D45B, 0x1D45C,
+  0x1D45D, 0x1D45E, 0x1D45F, 0x1D460, 0x1D461, 0x1D462, 0x1D463, 0x1D464,
+  0x1D465, 0x1D466, 0x1D467, 0x1D6A4, 0x1D6A5, 0x2118, 0x2192, nil,
+}
+
+-- Something fishy here. Starting with "3D the entries seem wrong
+local remap_oms = { [0] =
+  0x2212, 0x22C5, 0xD7, 0x2A, 0xF7, 0x22C4, 0xB1, 0x2213,
+  0x2295, 0x2296, 0x2297, 0x2298, 0x2299, 0x25CB, 0x2218, 0x2219,
+  0x224D, 0x2261, 0x2286, 0x2287, 0x2264, 0x2265, 0x2AAF, 0x2AB0,
+  0x223C, 0x2248, 0x2282, 0x2283, 0x226A, 0x226B, 0x227A, 0x227B,
+  0x2190, 0x2192, 0x2191, 0x2193, 0x2194, 0x2197, 0x2198, 0x2243,
+  0x21D0, 0x21D2, 0x21D1, 0x21D3, 0x21D4, 0x2196, 0x2199, 0x221D,
+  0x2032, 0x221E, 0x2208, 0x220B, 0x25B3, 0x25BD, 0x0338, 0x21A6,
+  0x2200, 0x2203, 0xAC, 0x2205, 0x211C, 0x2111, 0x22A4, 0x22A5,
+  0x2135, 0x1D49C, 0x212C, 0x1D49E, 0x1D49F, 0x2130, 0x2131, 0x1D4A2,
+  0x210B, 0x2110, 0x1D4A5, 0x1D4A6, 0x2112, 0x2133, 0x1D4A9, 0x1D4AA,
+  0x1D4AB, 0x1D4AC, 0x211B, 0x1D4AE, 0x1D4AF, 0x1D4B0, 0x1D4B1, 0x1D4B2,
+  0x1D4B3, 0x1D4B4, 0x1D4B5, 0x222A, 0x2229, 0x228E, 0x2227, 0x2228,
+  0x22A2, 0x22A3, 0x230A, 0x230B, 0x2308, 0x2309, 0x7B, 0x7D,
+  0x27E8, 0x27E9, 0x7C, 0x2016, 0x2195, 0x21D5, 0x5C, 0x2240,
+  0x221A, 0x2A3F, 0x2207, 0x222B, 0x2294, 0x2293, 0x2291, 0x2292,
+  0xA7, 0x2020, 0x2021, 0xB6, 0x2663, 0x2662, 0x2661, 0x2660,
+}
+
+-- We are not remapping symbols which are only used as large variants
+local remap_omx = { [0] =
+  nil, nil, nil, nil, nil, nil, nil, nil,
+  nil, nil, nil, nil, nil, nil, nil, nil,
+  nil, nil, nil, nil, nil, nil, nil, nil,
+  nil, nil, nil, nil, nil, nil, nil, nil,
+  nil, nil, nil, nil, nil, nil, nil, nil,
+  nil, nil, nil, nil, nil, nil, nil, nil,
+  nil, nil, nil, nil, nil, nil, nil, nil,
+  nil, nil, 0x27EE, 0x27EF, nil, nil, nil, nil,
+  nil, nil, nil, nil, nil, nil, 0x2A06, nil,
+  0x222E, nil, 0x2A00, nil, 0x2A01, nil, 0x2A02, nil,
+  0x2211, 0x220F, 0x222B, 0x22C3, 0x22C2, 0x2A04, 0x22C0, 0x22C1,
+  nil, nil, nil, nil, nil, nil, nil, nil,
+  0x2210, nil, 0x5E, 0x5E, 0x5E, 0x7E, 0x7E, 0x7E,
+  nil, nil, nil, nil, nil, nil, nil, nil,
+  nil, nil, nil, nil, nil, nil, nil, nil,
+  nil, nil, 0x23B0, 0x23B1, nil, nil, nil, nil,
+}
+
+return {
+  oml = remap_oml,
+  oms = remap_oms,
+  omx = remap_omx,
+}


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-legacy-mappings.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-lr.lua
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-lr.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-lr.lua	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,71 @@
+local properties = node.get_properties_table()
+
+local function to_unicode(head, tail)
+  local result, subresult, i = {[0] = 'mtext'}, {}, 0
+  local characters, last_fid
+  local iter, state, n = node.traverse(head)
+  while true do
+    local id, sub n, id, sub = iter(state, n)
+    if not n or n == tail then break end
+    local props = properties[n]
+    if props and props.glyph_info then
+      i = i+1
+      result[i] = glyph_info
+    else
+      local char, fid = node.is_glyph(n)
+      if char then
+        if fid ~= last_fid then
+          local fontdir = font.getfont(fid)
+          characters, last_fid = fontdir.characters, fid
+        end
+        local uni = characters[char]
+        local uni = uni and uni.unicode
+        i = i+1
+        if uni then
+          if type(uni) == 'number' then
+            result[i] = utf.char(uni)
+          else
+            result[i] = utf.char(table.unpack(uni))
+          end
+        else
+          if char < 0x110000 then
+            result[i] = utf.char(char)
+          else
+            result[i] = '\u{FFFD}'
+          end
+        end
+      elseif node.id'math' == id then
+        if props then
+          local mml = props.saved_mathml_table or props.saved_mathml_core
+          if mml then
+            i = i+1
+            result[i] = mml
+            n = node.end_of_math(n)
+          end
+        end
+      -- elseif node.id'whatsit' == id then
+        -- TODO(?)
+      elseif node.id'glue' == id then
+        if n.width > 1000 then -- FIXME: Coordinate constant with tagpdf
+          i = i+1
+          result[i] = '\u{00A0}' -- non breaking space... There is no real reason why it has to be non breaking, except that MathML often ignore other spaces
+        end
+      elseif node.id'hlist' == id then
+        local nested = to_unicode(n.head)
+        table.move(nested, 1, #nested, i+1, result)
+        i = i+#nested
+      elseif node.id'vlist' == id then
+        i = i+1
+        result[i] = '\u{FFFD}'
+      elseif node.id'rule' == id then
+        if n.width ~= 0 then
+          i = i+1
+          result[i] = '\u{FFFD}'
+        end
+      end -- CHECK: Everything else can probably be ignored, otherwise shout at me
+    end
+  end
+  return result
+end
+
+return to_unicode


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-lr.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-amsmath.sty
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-amsmath.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-amsmath.sty	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,417 @@
+\ProvidesExplPackage {luamml-patches-amsmath} {2024-08-14} {0.1.0}
+  {Feel free to add a description here}
+
+\lua_now:n { require'luamml-amsmath' }
+
+% For all of these changes, the redefinitions appear huge.
+% But they are almost identical to the original and only
+% add luamml commands in appropriate places, so they would
+% mostly disappear if there were enough hooks in amsmath.
+
+% aligned and friends
+\cs_set:Npn \start at aligned #1#2 {
+  \RIfM@
+  \else
+    \nonmatherr@ { \begin { \@currenvir } }
+  \fi
+  \savecolumn@ % Assumption: called inside a group
+  \luamml_annotate:en {
+    core = false
+  } {
+    \alignedspace at left
+  }
+  \ams at start@box {#1} \bgroup
+    \maxfields@ #2 \relax
+    \ifnum \maxfields@ > \m at ne
+      \multiply \maxfields@ \tw@
+      \let \math at cr@@@ \math at cr@@@alignedat
+      \alignsep@ \z at skip
+    \else
+      \let \math at cr@@@ \math at cr@@@aligned
+      \alignsep@ \minalignsep
+    \fi
+    \Let@ \chardef \dspbrk at context \@ne
+    \default at tag
+    \spread at equation % no-op if already called
+    \global \column@ \z@
+    \ialign \bgroup
+      & \column at plus
+        \hfil
+        \strut@
+        $
+          \m at th
+          \displaystyle
+          {##}
+          \luamml_flag_save:nNn {} \displaystyle {mtd}
+        $
+        \__luamml_amsmath_add_last_to_row:
+        \tabskip \z at skip
+      & \column at plus
+        $
+          \m at th
+          \displaystyle
+          {
+            {}
+            ##
+          }
+          \luamml_flag_save:nNn {} \displaystyle {mtd}
+        $
+        \__luamml_amsmath_add_last_to_row:
+        \hfil
+        \tabskip\alignsep@
+      \crcr
+      \ams at return@opt at arg
+}
+
+\renewcommand \gathered [1] [c] {
+  \RIfM@
+  \else
+    \nonmatherr@ { \begin {gathered} }
+  \fi
+  \luamml_annotate:en {
+    core = false
+  } {
+    \alignedspace at left
+  }
+  \ams at start@box {#1} \bgroup
+    \Let@
+    \chardef \dspbrk at context \@ne
+    \restore at math@cr
+    \spread at equation
+    \ialign \bgroup
+        \hfil
+        \strut@
+        $
+          \m at th
+          \displaystyle
+          ##
+          \luamml_flag_save:nNn {} \displaystyle {mtd}
+        $
+        \__luamml_amsmath_add_last_to_row:
+        \hfil
+      \crcr
+        \ams at return@opt at arg
+}
+
+\cs_set:Npn \endaligned {
+      \crcr
+      \__luamml_amsmath_save_inner_table:n \@currenvir
+    \egroup
+    \restorecolumn@
+  \egroup
+  \__luamml_amsmath_finalize_inner_table:
+}
+
+% gather
+\cs_set:Npn \gather@ #1 {
+  \ingather at true
+  \let \split \insplit@
+  \let \tag \tag at in@align
+  \let \label \label at in@display
+  \chardef \dspbrk at context \z@
+  \intertext@ \displ at y@ \Let@
+  \let \math at cr@@@ \math at cr@@@gather
+  \gmeasure@ {#1}
+  \global \shifttag at false
+  \tabskip \z at skip
+  \global \row@ \@ne
+  \halign to \displaywidth \bgroup
+      \strut@
+      \setboxz at h {
+        $
+          \m at th
+          \displaystyle
+          {##}
+          \luamml_flag_save:nNn {} \displaystyle {mtd}
+        $
+      }
+      \__luamml_amsmath_add_box_to_row:
+      \calc at shift@gather
+      \set at gather@field
+      \tabskip\@centering
+    &
+      \setboxz at h {
+        \strut@
+        {##}
+      }
+      \dim_compare:nNnF {0pt} = {
+        \box_wd:N \c_zero_int
+      } {
+        \__luamml_amsmath_set_tag:
+      }
+      \place at tag@gather
+      \tabskip \iftagsleft@
+        \gdisplaywidth@
+      \else
+        \z at skip
+      \span \fi
+      \crcr
+      #1
+}
+
+\cs_new_eq:NN \__luamml_amsmath_original_gmeasure:n \gmeasure@
+\cs_set:Npn \gmeasure@ #1 {
+  \exp_last_unbraced:Nno
+    \use_ii_i:nn
+    { \luamml_flag_ignore: }
+    { \__luamml_amsmath_original_gmeasure:n {#1} }
+}
+
+\cs_set:Npn \endgather {
+      \math at cr
+      \black@ \totwidth@
+      \__luamml_amsmath_finalize_table:n {gather}
+  \egroup
+  $$
+  \ignorespacesafterend
+}
+
+% align and friends
+\cs_set:Npn \align at preamble {
+  &
+    \hfil
+    \strut@
+    \setboxz at h {
+      \@lign
+      $
+        \m at th
+        \displaystyle
+        {##}
+        \ifmeasuring@
+          \luamml_flag_ignore:
+        \else
+          \luamml_flag_save:nNn {} \displaystyle {mtd}
+        \fi
+      $
+    }
+    \ifmeasuring@
+      \savefieldlength@
+    \else
+      \__luamml_amsmath_add_box_to_row:
+    \fi
+    \set at field
+    \tabskip\z at skip
+  &
+    \setboxz at h {
+      \@lign
+      $
+      \m at th
+      \displaystyle
+      {
+        {}
+        ##
+      }
+      \ifmeasuring@
+        \luamml_flag_ignore:
+      \else
+        \luamml_flag_save:nNn {} \displaystyle {mtd}
+      \fi
+      $
+    }
+    \ifmeasuring@
+      \savefieldlength@
+    \else
+      \__luamml_amsmath_add_box_to_row:
+    \fi
+    \set at field
+    \hfil
+    \tabskip\alignsep@
+}
+
+\cs_set:Npn \math at cr@@@align {
+  \ifst at rred
+    \nonumber
+  \fi
+  \if at eqnsw
+    \global \tag at true
+  \fi
+  \global \advance \row@ \@ne
+  \add at amps \maxfields@
+  \omit
+  \kern -\alignsep@
+  \iftag@
+    \setboxz at h { 
+      \@lign
+      \strut@
+      { \make at display@tag }
+    }
+    \place at tag
+    \__luamml_amsmath_set_tag:
+  \fi
+  \ifst at rred
+  \else
+    \global \@eqnswtrue
+  \fi
+  \global \lineht@ \z@
+  \cr
+}
+
+\cs_set:Npn \maketag@@@ #1 {
+  \hbox {
+    \m at th
+    \normalfont
+    #1
+    \__luamml_amsmath_save_tag:
+  }
+}
+
+\cs_set:Npn \endalign {
+  \math at cr
+  \black@ \totwidth@
+  \__luamml_amsmath_finalize_table:n {align}
+  \egroup
+  \ifingather@
+    \restorealignstate@
+    \egroup
+    \nonumber
+    \ifnum0=`{\fi\iffalse}\fi
+  \else
+    $$
+  \fi
+  \ignorespacesafterend
+}
+
+% For a more interesting one, let's consider multline:
+\cs_new_eq:NN \__luamml_amsmath_original_multline:n \multline@
+\cs_set:Npn \multline@ #1 {
+  \__luamml_amsmath_original_multline:n {
+    \ifmeasuring@ \else
+      \__luamml_amsmath_set_row_columnalign:n {left}
+    \fi
+    #1
+    \ifmeasuring@ \else
+      \__luamml_amsmath_set_row_columnalign:n {right}
+    \fi
+  }
+}
+
+\cs_new_eq:NN \__luamml_amsmath_original_mmeasure:n \mmeasure@
+\cs_set:Npn \mmeasure@ #1 {
+  \exp_last_unbraced:Nno
+    \use_ii_i:nn
+    { \luamml_flag_ignore: }
+    { \__luamml_amsmath_original_mmeasure:n {#1} }
+}
+
+% Luckily, {multline} uses \endmultline at math in exactly
+% the spot where we have to set the flag.
+% Less luckily, \endmultline at math sometimes get overwritten for the last line.
+% But that isn't a problem since we want special behavior there anyway.
+\cs_set:Npn \endmultline at math {
+  \luamml_flag_save:nNn {} \displaystyle {mtd}
+  $
+  \__luamml_amsmath_add_last_to_row:
+}
+
+\cs_set:Npn \rendmultline@ {
+    \iftag@
+      \luamml_flag_save:nNn {} \displaystyle {mtd}
+      $
+      \__luamml_amsmath_add_last_to_row:
+      \let \endmultline at math \relax
+      \ifshifttag@
+        \hskip \multlinegap
+        \llap {
+          \vtop {
+            \raise at tag
+            \normalbaselines
+            \setbox \@ne \null
+            \dp \@ne \lineht@
+            \box \@ne
+            \hbox {
+              \strut@
+              \make at display@tag
+            }
+          }
+        }
+      \else
+        \hskip \multlinetaggap
+        \make at display@tag
+      \fi
+      \__luamml_amsmath_set_tag:
+    \else
+      \hskip \multlinegap
+    \fi
+    \hfilneg
+    \math at cr
+    \__luamml_amsmath_finalize_table:n {multline}
+  \egroup
+  $$
+}
+
+\cs_set:Npn \lendmultline@ {
+    \hfilneg
+    \hskip\multlinegap
+    \math at cr
+    \__luamml_amsmath_finalize_table:n {multline}
+  \egroup
+  $$
+}
+
+% Finally some slightly different stuff.
+% While {matrix} is covered by {array}, we still have {smallmatrix}:
+\renewenvironment {smallmatrix} {
+  \luamml_annotate:en {
+    core = false
+  } {
+    \null
+    \,
+  }
+  \vcenter \bgroup
+    \Let@
+    \restore at math@cr
+    \default at tag
+    \baselineskip 6 \ex@
+    \lineskip 1.5 \ex@
+    \lineskiplimit \lineskip
+    \ialign \bgroup
+        \hfil
+        $
+        \m at th
+        \scriptstyle
+        ##
+        \luamml_flag_save:nn {} {mtd} % No \scriptsize here since we want to add the mstyle nodes
+        $
+        \__luamml_amsmath_add_last_to_row:
+        \hfil
+      &&
+        \thickspace
+        \hfil
+        $
+        \m at th
+        \scriptstyle
+        ##
+        \luamml_flag_save:nn {} {mtd} % No \scriptsize here since we want to add the mstyle nodes
+        $
+        \__luamml_amsmath_add_last_to_row:
+        \hfil
+      \crcr
+}{%
+      \crcr
+      \__luamml_amsmath_save_smallmatrix:
+    \egroup
+  \egroup
+  \__luamml_amsmath_finalize_inner_table:
+  \luamml_annotate:en {
+    core = false
+  } {
+    \,
+  }
+}
+
+% {cases} is defined by the kernel, but we patch the overwritten version by amsmath.
+\cs_set:Npn \env at cases {
+  \let \@ifnextchar \new at ifnextchar
+  \left \lbrace
+    \def \arraystretch {1.2}
+    \array {@{}l@{\quad \luamml_flag_ignore:}l@{}}
+}
+
+
+\cs_set:Npn \bBigg@ #1 #2 {
+  {
+    \ensuremath {
+      \Uvextensible height~#1 \big at size axis~exact~#2
+    }
+  }
+}


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-amsmath.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-amstext.sty
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-amstext.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-amstext.sty	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,37 @@
+\ProvidesExplPackage {luamml-patches-amstext} {2024-08-14} {0.1.0}
+  {Feel free to add a description here}
+
+\int_new:N \g__luamml_amsmath_text_struct_int
+\cs_set:Npn \textdef@ #1 #2 #3 {
+  \int_if_odd:nTF { \int_div_truncate:nn { \l__luamml_flag_int } { 8 } } {
+    \int_gincr:N \g__luamml_amsmath_text_struct_int
+    \tag_struct_begin:n {
+      tag = mtext/mathml,
+      stash,
+      label = __luamml_amsmath_text_ \int_use:N \g__luamml_amsmath_text_struct_int
+    }
+    \tag_mc_begin:n {
+      tag = mtext
+    }
+    \AnnotateFormula {
+      nucleus = true,
+      struct = "__luamml_amsmath_text_ \int_use:N \g__luamml_amsmath_text_struct_int"
+    }
+  } {
+    \use:n
+  }
+  {
+    \hbox {
+      {
+        \everymath {#1}
+        \let \f at size #2
+        \selectfont
+        #3
+      }
+    }
+  }
+  \int_if_odd:nT { \int_div_truncate:nn { \l__luamml_flag_int } { 8 } } {
+    \tag_mc_end:
+    \tag_struct_end:
+  }
+}


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-amstext.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-array.sty
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-array.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-array.sty	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,86 @@
+\ProvidesExplPackage {luamml-patches-array} {2024-08-14} {0.1.0}
+  {Feel free to add a description here}
+
+\lua_now:n { require'luamml-array' }
+
+\cs_set:Npn \@classz {
+  \@classx
+  \@tempcnta \count@
+  \prepnext at tok
+  \@addtopreamble {
+    \ifcase \@chnum
+      \hfil
+      \hskip 1sp
+      \d at llarbegin
+      \cs_if_eq:NNTF \d at llarbegin \begingroup {
+        \insert at column
+        \d at llarend
+      } {
+        \__luamml_array_init_col:
+        \insert at column
+        \luamml_flag_save:nn {} {mtd}
+        \d at llarend
+        \__luamml_array_finalize_col:w 0~
+      }
+      \do at row@strut
+      \hfil
+    \or
+      \hskip 1sp
+      \d at llarbegin
+      \cs_if_eq:NNTF \d at llarbegin \begingroup {
+        \insert at column
+        \d at llarend
+      } {
+        \__luamml_array_init_col:
+        \insert at column
+        \luamml_flag_save:nn {} {mtd}
+        \d at llarend
+        \__luamml_array_finalize_col:w 1~
+      }
+      \do at row@strut
+      \hfil
+    \or
+      \hfil
+      \hskip 1sp
+      \d at llarbegin
+      \cs_if_eq:NNTF \d at llarbegin \begingroup {
+        \insert at column
+        \d at llarend
+      } {
+        \__luamml_array_init_col:
+        \insert at column
+        \luamml_flag_save:nn {} {mtd}
+        \d at llarend
+        \__luamml_array_finalize_col:w 2~
+      }
+      \do at row@strut
+    \or
+      \setbox \ar at mcellbox \vbox \@startpbox { \@nextchar }
+        \insert at column
+      \@endpbox
+      \ar at align@mcell
+      \do at row@strut
+    \or
+      \vtop \@startpbox { \@nextchar }
+        \insert at column
+      \@endpbox
+      \do at row@strut
+    \or
+      \vbox \@startpbox { \@nextchar }
+        \insert at column
+      \@endpbox
+      \do at row@strut
+    \fi
+  }
+  \prepnext at tok
+}
+
+\cs_set:Npn \endarray {
+  \crcr
+  \__luamml_array_save_array:
+  \egroup
+  \egroup
+  \mode_if_math:T { \__luamml_array_finalize_array: }
+  \@arrayright
+  \gdef \@preamble {}
+}


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-array.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-kernel.sty
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-kernel.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-kernel.sty	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,63 @@
+\ProvidesExplPackage {luamml-patches-kernel} {2024-08-14} {0.1.0}
+  {Feel free to add a description here}
+
+\cs_new:Npn \__luamml_kernel_phantom:nnn #1#2#3 {
+  \setbox \z@ = \hbox {
+    $
+    \m at th
+    #2
+    {#3}
+    \luamml_flag_save:nNn {mathphant_#1} #2 {mphantom}
+    \luamml_pdf_write:
+    $
+  }
+  \luamml_annotate:nen {1} {
+    nucleus = true,
+    core = {[0] = 'mpadded',
+      \ifh@\else
+        width = 0,
+      \fi
+      \ifv@\else
+        height = 0, depth = 0,
+      \fi
+      data.mathml.mathphant_#1,
+    }
+  } {
+    \finph at nt
+  }
+}
+\cs_generate_variant:Nn \__luamml_kernel_phantom:nnn {V}
+
+\int_new:N \g__luamml_kernel_phantom_int
+\cs_set:Npn \mathph at nt {
+  \int_gincr:N \g__luamml_kernel_phantom_int
+  \__luamml_kernel_phantom:Vnn \g__luamml_kernel_phantom_int
+}
+
+\@ifpackageloaded {unicode-math} {} {
+  \cs_new:Npn \__luamml_kernel_define_character:Nnn #1#2#3 {
+    \cs_set:cpx { \cs_to_str:N #1 ~ } {
+      \luamml_annotate:nen {#2} {
+        nucleus = true, core = {[0] = 'mi', '\string\u{#3}'},
+      } {
+        \exp_not:v { \cs_to_str:N #1 ~ }
+      }
+    }
+  }
+
+  \__luamml_kernel_define_character:Nnn \models {3} {22a7}
+  \__luamml_kernel_define_character:Nnn \hookrightarrow {3} {21aa}
+  \__luamml_kernel_define_character:Nnn \hookleftarrow {3} {21a9}
+  \__luamml_kernel_define_character:Nnn \bowtie {3} {22c8}
+  \__luamml_kernel_define_character:Nnn \Longrightarrow {3} {27f9}
+  \__luamml_kernel_define_character:Nnn \longrightarrow {3} {27f6}
+  \__luamml_kernel_define_character:Nnn \Longleftarrow {3} {27f8}
+  \__luamml_kernel_define_character:Nnn \longleftarrow {3} {27f5}
+  \__luamml_kernel_define_character:Nnn \Longleftrightarrow {3} {27fa}
+  \__luamml_kernel_define_character:Nnn \longleftrightarrow {3} {27f7}
+  \__luamml_kernel_define_character:Nnn \longmapsto {4} {27fc}
+}
+
+\IfFileLoadedT {latex-lab-math.ltx} {
+  \RequirePackage{luamml-patches-lab-math}
+}


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-kernel.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-lab-math.sty
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-lab-math.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-lab-math.sty	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,20 @@
+\ProvidesExplPackage {luamml-patches-lab-math} {2024-08-14} {0.1.0}
+  {Feel free to add a description here}
+
+\AddToHook{begindocument} {
+  \cs_set:Npn \common at align@ending {
+    \math at cr
+    \black@ \totwidth@
+    \__luamml_amsmath_finalize_table:n {align}
+    \egroup
+    \ifingather@
+      \restorealignstate@
+      \egroup
+      \nonumber
+      \ifnum0=`{\fi\iffalse}\fi
+    \else
+      $$
+    \fi
+    \ignorespacesafterend
+  }
+}


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-patches-lab-math.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-pdf-demo.sty
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-pdf-demo.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-pdf-demo.sty	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,50 @@
+\NeedsTeXFormat{LaTeX2e}
+\ProvidesExplPackage{luamml-pdf-demo}{2024-08-14}{v0.1.0}{Reasonable default definitions for luamml-pdf}
+
+\RequirePackage{luamml-pdf}% Loading luamml-pdf is pretty much the point
+% \RequirePackage{amsmath,array}% May come back if the patches get ported
+
+% Delay family mappings to allow for replacements
+\AddToHook{begindocument/before}{%
+  \@ifpackageloaded{unicode-math}{}{%
+    \RegisterFamilyMapping\symsymbols{oms}%
+    \RegisterFamilyMapping\symletters{oml}%
+    \RegisterFamilyMapping\symlargesymbols{omx}%
+  }
+}
+
+\cs_new_protected:Npn \LuaMMLSetFilename #1 {}
+
+% TODO.
+% \cs_generate_variant:Nn \pdffile_filespec:nnn {ene}
+% \int_new:N \g__luamml_demo_af_int
+% \cs_new_protected:Npn \LuaMMLTagAF #1#2 {
+%   \int_gincr:N \g__luamml_demo_af_int
+%   \exp_args:Ne \pdf_object_new:nn{__luamml_demo_\int_use:N \g__luamml_demo_af_int}{dict}
+%   \exp_args:Ne \tagstructbegin{tag=Formula,AF=__luamml_demo_\int_use:N \g__luamml_demo_af_int,#1}
+%   \bool_if:NF \l__luamml_demo_structelem_bool {
+%     \tagmcbegin{tag=Formula}
+%   }
+%   #2
+%   \group_begin:
+%     \pdfdict_put:nnn {l_pdffile/Filespec} {AFRelationship}{/Supplement}
+%     \pdffile_filespec:ene
+%       { __luamml_demo_ \int_use:N \g__luamml_demo_af_int }
+%       { test.xml }
+%       { \luamml_get_last_mathml_stream:e{}\c_space_tl 0~R}
+%   \group_end:
+%   \bool_if:NF \l__luamml_demo_structelem_bool {
+%     \tagmcend
+%   }
+%   \tagstructend
+% }
+
+\NewDocumentCommand\AnnotateFormula{ o m m }{%
+  \IfValueTF{#1}{%
+    \luamml_annotate:nen{#1}%
+  }{
+    \luamml_annotate:en
+  }{#2}{#3}
+}
+
+\cs_set_eq:NN \WriteoutFormula \luamml_pdf_write:


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-pdf-demo.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-pdf.sty
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-pdf.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-pdf.sty	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,200 @@
+%%
+%% This is file `luamml-pdf.sty',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% luamml.dtx  (with options: `package,pdftex')
+%% 
+%% IMPORTANT NOTICE:
+%% 
+%% For the copyright see the source file.
+%% 
+%% Any modified versions of this file must be renamed
+%% with new filenames distinct from luamml-pdf.sty.
+%% 
+%% For distribution of the original source see the terms
+%% for copying and modification in the file luamml.dtx.
+%% 
+%% This generated file may be distributed as long as the
+%% original source files, as listed above, are part of the
+%% same distribution. (The sources need not necessarily be
+%% in the same archive or directory.)
+%% Copyright (C) 2020-2024 by Marcel Krueger
+%%
+%% This file may be distributed and/or modified under the
+%% conditions of the LaTeX Project Public License, either
+%% version 1.3c of this license or (at your option) any later
+%% version. The latest version of this license is in:
+%%
+%% http://www.latex-project.org/lppl.txt
+%%
+%% and version 1.3 or later is part of all distributions of
+%% LaTeX version 2005/12/01 or later.
+\ProvidesExplPackage {luamml-pdf} {2024-08-14} {0.1.0}
+  {MathML generation for L̶u̶a̶pdfLaTeX}
+\int_new:N \l__luamml_flag_int
+\int_new:N \l__luamml_pretty_int
+\tl_new:N \l__luamml_root_tl
+\tl_set:Nn \l__luamml_root_tl { mrow }
+\tl_new:N \l__luamml_label_tl
+\int_new:N \g__luamml_formula_id_int
+
+\int_set:Nn \l__luamml_pretty_int { 1 }
+
+\cs_new_protected:Npn \luamml_flag_process: {
+  \tl_set:Nn \l__luamml_label_tl {}
+  \int_set:Nn \l__luamml_flag_int { 3 }
+}
+\cs_new:Npn \__luamml_maybe_structelem: {
+  (
+    8 * \int_mod:nn {
+      \int_div_truncate:nn { \l__luamml_flag_int } {8}
+    } {2}
+  ) +
+}
+\cs_new:Npn \__luamml_style_to_num:N #1 {
+  \token_case_meaning:NnF #1 {
+    \displaystyle {0}
+    \textstyle {32}
+    \scriptstyle {64}
+    \scriptscriptstyle {96}
+  } {
+    \Invalid_mathstyle
+  }
+}
+\cs_new_protected:Npn \luamml_flag_save:n #1 {
+  \tl_set:Nn \l__luamml_label_tl {#1}
+  \int_set:Nn \l__luamml_flag_int { \__luamml_maybe_structelem: 1 }
+}
+\cs_new_protected:Npn \luamml_flag_save:nN #1#2 {
+  \tl_set:Nn \l__luamml_label_tl {#1}
+  \int_set:Nn \l__luamml_flag_int { \__luamml_maybe_structelem: 17 + \__luamml_style_to_num:N #2 }
+}
+\cs_new_protected:Npn \luamml_flag_save:nn #1 {
+  \tl_set:Nn \l__luamml_label_tl {#1}
+  \int_set:Nn \l__luamml_flag_int { \__luamml_maybe_structelem: 5 }
+  \tl_set:Nn \l__luamml_root_tl
+}
+\cs_new_protected:Npn \luamml_flag_save:nNn #1#2 {
+  \tl_set:Nn \l__luamml_label_tl {#1}
+  \int_set:Nn \l__luamml_flag_int { \__luamml_maybe_structelem: 21 + \__luamml_style_to_num:N #2 }
+  \tl_set:Nn \l__luamml_root_tl
+}
+\cs_new_protected:Npn \luamml_flag_ignore: {
+  \int_set:Nn \l__luamml_flag_int { 0 }
+}
+\luamml_flag_process:
+
+\cs_if_exist:NTF \showstream {
+  \iow_new:N \l__luamml_pdf_stream
+  \iow_open:Nn \l__luamml_pdf_stream { \jobname .tml }
+  \cs_new_protected:Npn \__luamml_pdf_showlists: {
+    \group_begin:
+      \int_set:Nn \tex_showboxdepth:D { \c_max_int }
+      \int_set:Nn \tex_showboxbreadth:D { \c_max_int }
+      \showstream = \l__luamml_pdf_stream
+      \tex_showlists:D
+    \group_end:
+  }
+} {
+  \cs_set_eq:NN \l__luamml_pdf_stream \c_log_iow
+  \cs_set_eq:NN \__luamml_pdf_set_showstream: \scan_stop:
+  \cs_new_protected:Npn \__luamml_pdf_showlists: {
+    \group_begin:
+      \int_set:Nn \l_tmpa_int { \tex_interactionmode:D }
+      \int_set:Nn \tex_interactionmode:D { 0 }
+      \int_set:Nn \tex_showboxdepth:D { \c_max_int }
+      \int_set:Nn \tex_showboxbreadth:D { \c_max_int }
+      \tex_showlists:D
+      \int_set:Nn \tex_interactionmode:D { \l_tmpa_int }
+    \group_end:
+  }
+}
+\cs_generate_variant:Nn \tl_to_str:n { e }
+\int_new:N \g__luamml_annotation_id_int
+\cs_new_protected:Npn \luamml_annotate:nen #1#2#3 {
+  \int_gincr:N \g__luamml_annotation_id_int
+  \iow_shipout_x:Nx \l__luamml_pdf_stream {
+    LUAMML_MARK_REF:
+    \int_use:N \g__luamml_annotation_id_int
+    :
+  }
+  \iow_now:Nx \l__luamml_pdf_stream {
+    LUAMML_MARK:
+    \int_use:N \g__luamml_annotation_id_int
+    :
+    count = \int_eval:n {#1},
+    #2
+    \iow_newline:
+    LUAMML_MARK_END
+  }
+  #3
+}
+\cs_new_protected:Npn \luamml_annotate:en #1#2 {
+  \int_gincr:N \g__luamml_annotation_id_int
+  \iow_shipout_x:Nx \l__luamml_pdf_stream {
+    LUAMML_MARK_REF:
+    \int_use:N \g__luamml_annotation_id_int
+    :
+  }
+  \iow_now:Nx \l__luamml_pdf_stream {
+    LUAMML_MARK:
+    \int_use:N \g__luamml_annotation_id_int
+    :
+    count = data.count[\int_use:N \g__luamml_annotation_id_int],
+    #1
+    \iow_newline:
+    LUAMML_MARK_END
+  }
+  \use:x {
+    \iow_now:Nn \l__luamml_pdf_stream {
+      LUAMML_COUNT:
+      \int_use:N \g__luamml_annotation_id_int
+    }
+    \__luamml_pdf_showlists:
+    \exp_not:n {#2}
+    \iow_now:Nn \l__luamml_pdf_stream {
+      LUAMML_COUNT_END:
+      \int_use:N \g__luamml_annotation_id_int
+    }
+    \__luamml_pdf_showlists:
+  }
+}
+\cs_new_protected:Npn \luamml_pdf_write: {
+  \int_gincr:N \g__luamml_formula_id_int
+  \iow_now:Nx \l__luamml_pdf_stream {
+    LUAMML_FORMULA_BEGIN:
+    \int_use:N \g__luamml_formula_id_int
+    :
+    \int_use:N \l__luamml_flag_int
+    :
+    \l__luamml_root_tl
+    :
+    \l__luamml_label_tl
+  }
+  \__luamml_pdf_showlists:
+  \iow_now:Nx \l__luamml_pdf_stream {
+    LUAMML_FORMULA_END
+  }
+}
+\NewDocumentCommand \RegisterFamilyMapping {m m} {
+  \iow_now:Nx \l__luamml_pdf_stream {
+    LUAMML_INSTRUCTION:REGISTER_MAPPING: \int_use:N #1 : #2
+  }
+}
+\cs_new_protected:Npn \__luamml_patch_package:nn #1 #2 {
+  \@ifpackageloaded {#1} {#2} {
+    \hook_gput_code:nnn {package/#1/after} {luamml} {#2}
+  }
+}
+\cs_new_protected:Npn \__luamml_patch_package:n #1 {
+  \__luamml_patch_package:nn {#1} {
+    \RequirePackage { luamml-patches-#1 }
+  }
+}
+\RequirePackage { luamml-patches-kernel }
+
+\endinput
+%%
+%% End of file `luamml-pdf.sty'.


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-pdf.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-structelemwriter.lua
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-structelemwriter.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-structelemwriter.lua	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,105 @@
+local struct_begin = token.create'tag_struct_begin:n'
+local struct_use = token.create'tag_struct_use:n'
+local struct_end = token.create'tag_struct_end:'
+
+local mc_begin = token.create'tag_mc_begin:n'
+local mc_end = token.create'tag_mc_end:'
+
+local function escape_name(name)
+  return name
+end
+
+local function escape_string(str)
+  return str
+end
+
+local mathml_ns_obj
+local function get_mathml_ns_obj()
+  mathml_ns_obj = mathml_ns_obj or token.create'c__pdf_backend_object_tag/NS/mathml_int'.index
+  return mathml_ns_obj
+end
+
+local attribute_counter = 0
+local attributes = setmetatable({}, {__index = function(t, k)
+  attribute_counter = attribute_counter + 1
+  local attr_name = string.format('luamml_attr_%i', attribute_counter)
+  t[k] = attr_name
+  tex.runtoks(function()
+    tex.sprint(string.format('\\tagpdfsetup{newattribute={%s}{/O/NSO/NS %i 0 R',
+        attr_name, mathml_ns_obj or get_mathml_ns_obj()))
+    -- tex.sprint(string.format('\\tagpdfsetup{newattribute={%s}{/O/MathML-3',
+    --     attr_name))
+    tex.cprint(12, k)
+    tex.sprint'}}'
+  end)
+  return attr_name
+end})
+
+local mc_type = luatexbase.attributes.g__tag_mc_type_attr
+local mc_cnt = luatexbase.attributes.g__tag_mc_cnt_attr
+-- print('!!!', mc_type, mc_cnt)
+
+local stash_cnt = 0
+local attrs = {}
+local function write_elem(tree, stash)
+  if tree[':struct'] then
+    return tex.runtoks(function()
+      return tex.sprint(struct_use, '{', tree[':struct'], '}')
+    end)
+  end
+  if not tree[0] then print('ERR', require'inspect'(tree)) end
+  local i = 0
+  for attr, val in next, tree do if type(attr) == 'string' and not string.find(attr, ':') and attr ~= 'xmlns' then
+  -- for attr, val in next, tree do if type(attr) == 'string' and string.byte(attr) ~= 0x3A then
+    i = i + 1
+    attrs[i] = string.format('/%s(%s)', escape_name(attr), escape_string(val))
+  end end
+  table.sort(attrs)
+
+  if stash then
+    stash_cnt = stash_cnt + 1
+    stash = '__luamml_stashed_' .. stash_cnt
+    tree[':struct'] = stash
+    stash = ', stash, label = ' .. stash
+  end
+
+  local attr_flag = i ~= 0 and ', attribute=' .. attributes[table.concat(attrs)]
+  tex.sprint(struct_begin, '{tag=' .. tree[0] .. '/mathml')
+  if stash then tex.sprint(stash) end
+  if attr_flag then tex.sprint(attr_flag) end
+  if tree[':actual'] then
+    tex.sprint(', actualtext = {')
+    tex.cprint(12, tree[':actual'])
+    tex.sprint'}'
+  end
+  tex.sprint'}'
+  for j = 1, i do attrs[j] = nil end
+
+  if tree[':nodes'] then
+    local n = tree[':nodes']
+    tex.runtoks(function()
+      tex.sprint{mc_begin, string.format('{tag=%s}', tree[0])}
+      -- NOTE: This will also flush all previous sprint's... That's often annoying, but in this case actually intentional.
+    end)
+    local mct, mcc = tex.attribute[mc_type], tex.attribute[mc_cnt]
+    for i = 1, #n do
+      node.set_attribute(n[i], mc_type, mct)
+      node.set_attribute(n[i], mc_cnt, mcc)
+    end
+    tex.runtoks(function()
+      tex.sprint(mc_end)
+    end)
+  end
+  for _, elem in ipairs(tree) do
+    if type(elem) ~= 'string' then
+      write_elem(elem)
+    end
+  end
+  tex.runtoks(function()
+    tex.sprint(struct_end)
+  end)
+end
+
+return function(element, stash)
+  return write_elem(element, stash)
+end


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-structelemwriter.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-table.lua
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-table.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-table.lua	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,110 @@
+local write_xml = require'luamml-xmlwriter'
+local make_root = require'luamml-convert'.make_root
+local save_result = require'luamml-tex'.save_result
+
+local properties = node.get_properties_table()
+
+local glue_id = node.id'glue'
+local tabskip_sub = 12
+assert(node.subtypes'glue'[tabskip_sub] == 'tabskip')
+
+local function store_get_row()
+  local row_temp
+  for i=tex.nest.ptr-1, 0, -1 do
+    local head = tex.nest[i].head
+    local glue = head.next
+    if glue and glue.id == glue_id and glue.subtype == tabskip_sub then
+      row_temp = head
+      break
+    end
+  end
+  if not row_temp then
+    error[[luamml_table's store function called outside of table]]
+  end
+  local props = properties[row_temp]
+  if not props then
+    props = {}
+    properties[row_temp] = props
+  end
+  local mml_row = props.mathml_row
+  if not mml_row then
+    mml_row = {[0] = 'mtr'}
+    props.mathml_row  = mml_row
+  end
+  return mml_row
+end
+
+local function store_column_xml(mml, display)
+  if mml[0] ~= 'mtd' then
+    if display and mml[0] == 'mstyle' and mml.displaystyle == true then
+      mml[0], mml.displaystyle, mml.scriptlevel = 'mtd', nil, nil
+    else
+      if display and mml[0] ~= 'mstyle' then
+        mml = {[0] = 'mstyle', displaystyle = false, mml}
+      end
+      mml = {[0] = 'mtd', mml}
+    end
+  end
+  table.insert(store_get_row(), mml)
+  return mml
+end
+
+local function store_column(startmath)
+  local props = properties[startmath]
+  if not props then return end
+  local mml = props.saved_mathml_table or props.saved_mathml_core
+  if mml then return store_column_xml(mml) end
+end
+
+local function store_tag(xml)
+  local mml_row = store_get_row()
+  mml_row[0] = 'mlabeledtr'
+  table.insert(mml_row, 1, xml)
+  last_tag = nil
+end
+
+local function set_row_attribute(name, value)
+  local mml_row = store_get_row()
+  mml_row[name] = value
+end
+
+luatexbase.add_to_callback('hpack_filter', function(_, group)
+  if group ~= 'fin_row' then return true end
+
+  local temp = tex.nest.top.head
+  local props = properties[temp]
+  if not props then return true end
+  local mml_row = props.mathml_row
+  if not mml_row then return true end
+  props.mathml_row = nil
+
+  props = properties[tex.lists.align_head]
+  if not props then
+    props = {}
+    properties[tex.lists.align_head] = props
+  end
+  local mml_table = props.mathml_table_node_table
+  if not mml_table then
+    mml_table = {[0] = 'mtable'}
+    props.mathml_table_node_table = mml_table
+  end
+  table.insert(mml_table, mml_row)
+  return true
+end, 'mathml amsmath processing')
+
+local function get_table()
+  -- TODO: Error handling etc
+  local props = properties[tex.lists.align_head]
+  if not props then return end
+  local mml_table = props.mathml_table_node_table
+  props.mathml_table_node_table = nil
+  return mml_table
+end
+
+return {
+  store_column = store_column,
+  store_column_xml = store_column_xml,
+  store_tag = store_tag,
+  set_row_attribute = set_row_attribute,
+  get_table = get_table,
+}


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-table.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-tex-annotate.lua
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-tex-annotate.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-tex-annotate.lua	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,119 @@
+local nest = tex.nest
+
+local properties = node.get_properties_table()
+
+local mark_environment = {
+  data = {
+  },
+}
+
+local function annotate()
+  local annotation, err = load( 'return {'
+      .. token.scan_argument()
+    .. '}', nil, 't', mark_environment)
+  if not annotation then
+    tex.error('Error while parsing LuaMML annotation', {err})
+    return 0
+  end
+  annotation = annotation()
+  local nesting = nest.top
+  local props = properties[nesting.head]
+  local current = props and props.luamml__annotate_context
+  if current then
+    current, props.luamml__annotate_context = current.head, current.prev
+  else
+    tex.error('Mismatched LuaMML annotation',
+      {'Something odd happened. Maybe you forgot braces around an annotated symbol in a subscript or superscript?'})
+    return 0
+  end
+  local after = nesting.tail
+  local count, offset = 0, annotation.offset
+  local marked
+  if current == after then
+    tex.error'Empty LuaMML annotation'
+  else
+    repeat
+      current = current.next
+      count = count + 1
+      if count == offset then
+        marked = current
+      elseif offset or current ~= after then
+        local props = properties[current]
+        if not props then
+          props = {}
+          properties[current] = props
+        end
+        props.mathml_table, props.mathml_core = nil, false
+      end
+    until current == after
+    if offset and not marked then
+      tex.error'Invalid offset in LuaMML annotation'
+    end
+    marked = marked or current
+    if annotation.nucleus then
+      marked = marked.nucleus
+    end
+    if marked then
+      local props = properties[marked]
+      if not props then
+        props = {}
+        properties[marked] = props
+      end
+      if annotation.core then
+        props.mathml_core = annotation.core
+      end
+      if annotation.struct then
+        local saved = props.mathml_filter
+        local struct = annotation.struct
+        function props.mathml_filter(mml, core)
+          mml[':struct'] = struct
+          if saved then
+            return saved(mml, core)
+          else
+            return mml, core
+          end
+        end
+      end
+    else
+      tex.error'Unable to annotate nucleus of node without nucleus'
+    end
+  end
+  return count
+end
+
+local funcid = luatexbase.new_luafunction'__luamml_annotate_begin:'
+token.set_lua('__luamml_annotate_begin:', funcid, 'protected')
+lua.get_functions_table()[funcid] = function()
+  local top = nest.top
+  local temp = top.head
+  local props = properties[temp]
+  if not props then
+    props = {}
+    properties[temp] = props
+  end
+  props.luamml__annotate_context = {
+    prev = props.luamml__annotate_context,
+    head = top.tail,
+  }
+end
+
+funcid = luatexbase.new_luafunction'__luamml_annotate_end:we'
+token.set_lua('__luamml_annotate_end:we', funcid, 'protected')
+lua.get_functions_table()[funcid] = function()
+  local count = token.scan_int()
+  local real_count = annotate()
+  if count ~= real_count then
+    tex.error('Incorrect count in LuaMML annotation', {
+        'A LuaMML annotation was discovered with an explicit count \z
+        which was not the same as the number of top-level nodes annotated.',
+        string.format('This can be fixed by changing the supplied count from %i to %i \z
+          or by omitting the count value entirely.', count, real_count)
+    })
+  end
+end
+
+funcid = luatexbase.new_luafunction'__luamml_annotate_end:e'
+token.set_lua('__luamml_annotate_end:e', funcid, 'protected')
+lua.get_functions_table()[funcid] = annotate
+
+return mark_environment


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-tex-annotate.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-tex.lua
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-tex.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-tex.lua	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,232 @@
+local mlist_to_mml = require'luamml-convert'
+local process_mlist = mlist_to_mml.process
+local make_root = mlist_to_mml.make_root
+local register_family = mlist_to_mml.register_family
+
+local mappings = require'luamml-legacy-mappings'
+local write_xml = require'luamml-xmlwriter'
+local write_struct = require'luamml-structelemwriter'
+
+local filename_token = token.create'l__luamml_filename_tl'
+local label_token = token.create'l__luamml_label_tl'
+local left_brace = token.new(string.byte'{', 1)
+local right_brace = token.new(string.byte'}', 2)
+
+local output_hook_token
+local global_text_families = {}
+local text_families_meta = {__index = function(t, fam)
+  if fam == nil then return nil end
+  local assignment = global_text_families[fam]
+  if assignment == nil then
+    local fid = node.family_font(fam)
+    local fontdir = font.getfont(fid)
+    if not fontdir then
+      -- FIXME(?): If there is no font...
+      error'Please load your fonts?!?'
+    end
+    assignment = not (fontdir.MathConstants and next(fontdir.MathConstants))
+  end
+  t[fam] = assignment
+  return assignment
+end}
+
+local properties = node.get_properties_table()
+local mmode, hmode, vmode do
+  local result, input = {}, tex.getmodevalues()
+  for k,v in next, tex.getmodevalues() do
+    if v == 'math' then mmode = k
+    elseif v == 'horizontal' then hmode = k
+    elseif v == 'vertical' then vmode = k
+    else assert(v == 'unset')
+    end
+  end
+  assert(mmode and hmode and vmode)
+end
+
+local funcid = luatexbase.new_luafunction'RegisterFamilyMapping'
+token.set_lua('RegisterFamilyMapping', funcid, 'protected')
+lua.get_functions_table()[funcid] = function()
+  local fam = token.scan_int()
+  local mapping = token.scan_string()
+  if mappings[mapping] then
+    register_family(fam, mappings[mapping])
+    if global_text_families[fam] == nil then
+      global_text_families[fam] = false
+    end
+  else
+    tex.error(string.format('Unknown font mapping %q', mapping))
+  end
+end
+
+local funcid = luatexbase.new_luafunction'RegisterFamilyMapping'
+token.set_lua('RegisterTextFamily', funcid, 'protected')
+lua.get_functions_table()[funcid] = function()
+  local fam = token.scan_int()
+  local _kind = token.scan_string()
+  global_text_families[fam] = true
+end
+
+local function shallow_copy(t)
+  local tt = {}
+  for k,v in next, t do
+    tt[k] = v
+  end
+  return tt
+end
+
+-- Possible flag values:
+--   0: Skip
+--   1: Generate MathML, but only save it for later usage in startmath node
+--   3: Normal (This is the only supported one in display mode)
+--  11: Generate MathML structure elements
+--
+--  More generally, flags is a bitfield with the defined bits:
+--    Bit 5-7: See Bit 4
+--    Bit 4: Overwrite mathstyle with bit 9-11
+--    Bit 3: Generate MathML structure elements
+--    Bit 2: Change root element name for saved element
+--    Bit 1: Save MathML as a fully converted formula
+--    Bit 0: Save MathML for later usage in startmath node. Ignored for display math.
+
+local out_file
+
+local mlist_result
+
+local undefined_cmd = token.command_id'undefined_cs'
+local call_cmd = token.command_id'call'
+
+local labelled_mathml = {}
+
+local function save_result(xml, display, structelem)
+  mlist_result = make_root(xml, display and 0 or 2)
+  if out_file then
+    out_file:write(write_xml(mlist_result, tex.count.l__luamml_pretty_int & 1 == 1):sub(2) .. '\n')
+  else
+    token.put_next(filename_token)
+    local filename = token.scan_argument()
+    if filename ~= '' then
+      assert(io.open(filename, 'w'))
+        :write(write_xml(mlist_result, tex.count.l__luamml_pretty_int & 1 == 1):sub(2) .. '\n')
+        :close()
+    end
+  end
+  local tracing = tex.count.tracingmathml > 1
+  if tracing then
+    texio.write_nl(write_xml(mlist_result, tex.count.l__luamml_pretty_int & 2 == 2) .. '\n')
+  end
+  if output_hook_token then
+    tex.runtoks(function()
+      tex.sprint(-2, output_hook_token, left_brace, write_xml(mlist_result, tex.count.l__luamml_pretty_int & 4 == 4), right_brace)
+    end)
+  end
+  if tex.count.l__luamml_flag_int & 8 == 8 then
+    write_struct(mlist_result)
+  end
+  return mlist_result
+end
+
+luatexbase.add_to_callback('pre_mlist_to_hlist_filter', function(mlist, style)
+  if tex.nest.top.mode == mmode then -- This is a equation label generated with \eqno
+    return true
+  end
+  local flag = tex.count.l__luamml_flag_int
+  if flag & 3 == 0 then
+    return true
+  end
+  local display = style == 'display'
+  local startmath = tex.nest.top.tail -- Must come before any write_struct calls which adds nodes
+  style = flag & 16 == 16 and flag>>5 & 0x7 or display and 0 or 2
+  local xml, core = process_mlist(mlist, style, setmetatable({}, text_families_meta))
+  if flag & 2 == 2 then
+    xml = save_result(shallow_copy(xml), display)
+  end
+  if flag & 4 == 4 then
+    local element_type = token.get_macro'l__luamml_root_tl'
+    if element_type ~= 'mrow' then
+      if xml[0] == 'mrow' then
+        xml[0] = element_type
+      else
+        xml = {[0] = element_type, xml}
+      end
+    end
+  end
+  if not display and flag & 1 == 1 then
+    local props = properties[startmath]
+    if not props then
+      props = {}
+      properties[startmath] = props
+    end
+    props.saved_mathml_table, props.saved_mathml_core = xml, core
+    token.put_next(label_token)
+    local label = token.scan_argument()
+    if label ~= '' then
+      if labelled_mathml[label] then
+        tex.error('MathML Label already in use', {
+            'A MathML expression has a label which is already used by another \z
+             formula. If you do not want to label this formula with a unique \z
+             label, set a empty label instead.'})
+      else
+        labelled_mathml[label] = xml
+      end
+    end
+    if flag & 10 == 8 then
+      write_struct(xml, true) -- This modifies xml in-place to reference the struture element
+    end
+  end
+  return true
+end, 'dump_list')
+
+funcid = luatexbase.new_luafunction'luamml_get_last_mathml_stream:e'
+token.set_lua('luamml_get_last_mathml_stream:e', funcid)
+lua.get_functions_table()[funcid] = function()
+  if not mlist_result then
+    tex.error('No current MathML data', {
+        "I was asked to provide MathML code for the last formula, but there weren't any new formulas since you last asked."
+      })
+  end
+  local mml = write_xml(mlist_result, tex.count.l__luamml_pretty_int & 8 == 8)
+  if tex.count.tracingmathml == 1 then
+    texio.write_nl(mml .. '\n')
+  end
+  tex.sprint(-2, tostring(pdf.immediateobj('stream', mml, '/Subtype/application#2Fmathml+xml' .. token.scan_argument(true))))
+  mlist_result = nil
+end
+
+funcid = luatexbase.new_luafunction'luamml_begin_single_file:'
+token.set_lua('luamml_begin_single_file:', funcid, 'protected')
+lua.get_functions_table()[funcid] = function()
+  token.put_next(filename_token)
+  local filename = token.scan_argument()
+  if filename ~= '' then
+    out_file = assert(io.open(filename, 'w'))
+  end
+end
+
+funcid = luatexbase.new_luafunction'luamml_end_single_file:'
+token.set_lua('luamml_end_single_file:', funcid, 'protected')
+lua.get_functions_table()[funcid] = function()
+  if out_file then
+    out_file:close()
+    out_file = nil
+  end
+end
+
+funcid = luatexbase.new_luafunction'luamml_register_output_hook:N'
+token.set_lua('__luamml_register_output_hook:N', funcid, 'protected')
+lua.get_functions_table()[funcid] = function()
+  output_hook_token = token.get_next()
+end
+
+funcid = luatexbase.new_luafunction'luamml_disable_output_hook:'
+token.set_lua('__luamml_disable_output_hook:', funcid, 'protected')
+lua.get_functions_table()[funcid] = function()
+  output_hook_token = nil
+end
+
+local annotate_context = require'luamml-tex-annotate'
+annotate_context.data.mathml = labelled_mathml
+
+return {
+  save_result = save_result,
+  labelled = labelled_mathml,
+}


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-tex.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-xmlwriter.lua
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-xmlwriter.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-xmlwriter.lua	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,58 @@
+-- FIXME: Not sure yet if this will be needed
+local function escape_name(name)
+  return name
+end
+
+local escapes = {
+  ['"'] = """,
+  ['<'] = "<",
+  ['>'] = ">",
+  ['&'] = "&",
+}
+local function escape_text(text)
+  return string.gsub(string.gsub(tostring(text), '["<>&]', escapes), '[\x00-\x08\x0B\x0C\x0E-\x1F]', function(x)
+    return string.format('^^%02x', string.byte(x))
+  end)
+end
+
+local attrs = {}
+local function write_elem(tree, indent)
+  if not tree[0] then print('ERR', require'inspect'(tree)) end
+  local escaped_name = escape_name(assert(tree[0]))
+  local i = 0
+  for attr, val in next, tree do if type(attr) == 'string' then
+    if not string.find(attr, ':', 1, true) then
+    -- if string.byte(attr) ~= 0x3A then
+      i = i + 1
+      attrs[i] = string.format(' %s="%s"', escape_name(attr), escape_text(val))
+    end
+  end end
+  table.sort(attrs)
+  local out = string.format('%s<%s%s', indent or '', escaped_name, table.concat(attrs))
+  for j = 1, i do attrs[j] = nil end
+  if not tree[1] then
+    return out .. '/>'
+  end
+  out = out .. '>'
+  local inner_indent = indent and indent .. '  '
+  local is_string
+  for _, elem in ipairs(tree) do
+    if type(elem) == 'string' then
+      if inner_indent and not is_string then
+        out = out .. inner_indent
+      end
+      out = out .. escape_text(elem)
+      is_string = true
+    else
+      out = out .. write_elem(elem, inner_indent)
+      is_string = nil
+    end
+  end
+  if indent then out = out .. indent end
+  return out .. '</' .. escaped_name .. '>'
+end
+
+return function(element, indent, version)
+  return (version == '11' and '<?xml version="1.1"?>' or '') ..
+    write_elem(element, indent and '\n' or nil)
+end


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml-xmlwriter.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml.sty
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luamml/luamml.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luamml/luamml.sty	2024-08-16 21:35:16 UTC (rev 72049)
@@ -0,0 +1,126 @@
+%%
+%% This is file `luamml.sty',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% luamml.dtx  (with options: `package,luatex')
+%% 
+%% IMPORTANT NOTICE:
+%% 
+%% For the copyright see the source file.
+%% 
+%% Any modified versions of this file must be renamed
+%% with new filenames distinct from luamml.sty.
+%% 
+%% For distribution of the original source see the terms
+%% for copying and modification in the file luamml.dtx.
+%% 
+%% This generated file may be distributed as long as the
+%% original source files, as listed above, are part of the
+%% same distribution. (The sources need not necessarily be
+%% in the same archive or directory.)
+%% Copyright (C) 2020-2024 by Marcel Krueger
+%%
+%% This file may be distributed and/or modified under the
+%% conditions of the LaTeX Project Public License, either
+%% version 1.3c of this license or (at your option) any later
+%% version. The latest version of this license is in:
+%%
+%% http://www.latex-project.org/lppl.txt
+%%
+%% and version 1.3 or later is part of all distributions of
+%% LaTeX version 2005/12/01 or later.
+\ProvidesExplPackage {luamml} {2024-08-14} {0.1.0}
+  {Automatically generate presentational MathML from LuaTeX math expressions}
+\int_new:N \l__luamml_flag_int
+\int_new:N \l__luamml_pretty_int
+\tl_new:N \l__luamml_filename_tl
+\tl_new:N \l__luamml_root_tl
+\tl_set:Nn \l__luamml_root_tl { mrow }
+\tl_new:N \l__luamml_label_tl
+\int_new:N \tracingmathml
+
+\int_set:Nn \l__luamml_pretty_int { 1 }
+\lua_now:n { require'luamml-tex' }
+\hook_new_with_args:nn { luamml / converted } { 1 }
+
+\cs_new_protected:Npn \__luamml_output_hook:n {
+  \hook_use:nnw { luamml / converted } { 1 }
+}
+\__luamml_register_output_hook:N \__luamml_output_hook:n
+
+\cs_new_protected:Npn \luamml_flag_process: {
+  \tl_set:Nn \l__luamml_label_tl {}
+  \int_set:Nn \l__luamml_flag_int { 3 }
+}
+\cs_new:Npn \__luamml_maybe_structelem: {
+  (
+    8 * \int_mod:nn {
+      \int_div_truncate:nn { \l__luamml_flag_int } {8}
+    } {2}
+  ) +
+}
+\cs_new:Npn \__luamml_style_to_num:N #1 {
+  32 * #1
+}
+\cs_new_protected:Npn \luamml_flag_save:n #1 {
+  \tl_set:Nn \l__luamml_label_tl {#1}
+  \int_set:Nn \l__luamml_flag_int { \__luamml_maybe_structelem: 1 }
+}
+\cs_new_protected:Npn \luamml_flag_save:nN #1#2 {
+  \tl_set:Nn \l__luamml_label_tl {#1}
+  \int_set:Nn \l__luamml_flag_int { \__luamml_maybe_structelem: 17 + \__luamml_style_to_num:N #2 }
+}
+\cs_new_protected:Npn \luamml_flag_save:nn #1 {
+  \tl_set:Nn \l__luamml_label_tl {#1}
+  \int_set:Nn \l__luamml_flag_int { \__luamml_maybe_structelem: 5 }
+  \tl_set:Nn \l__luamml_root_tl
+}
+\cs_new_protected:Npn \luamml_flag_save:nNn #1#2 {
+  \tl_set:Nn \l__luamml_label_tl {#1}
+  \int_set:Nn \l__luamml_flag_int { \__luamml_maybe_structelem: 21 + \__luamml_style_to_num:N #2 }
+  \tl_set:Nn \l__luamml_root_tl
+}
+\cs_new_protected:Npn \luamml_flag_ignore: {
+  \int_set:Nn \l__luamml_flag_int { 0 }
+}
+\cs_new_protected:Npn \luamml_flag_structelem: {
+  \tl_set:Nn \l__luamml_label_tl {}
+  \int_set:Nn \l__luamml_flag_int { 11 }
+}
+\cs_new_protected:Npn \luamml_set_filename:n {
+  \tl_set:Nn \l__luamml_filename_tl
+}
+\luamml_flag_process:
+\cs_new_protected:Npn \luamml_annotate:nen #1#2#3 {
+  \__luamml_annotate_begin:
+    #3
+  \__luamml_annotate_end:we \tex_numexpr:D #1 \scan_stop: {#2}
+}
+
+\cs_new_protected:Npn \luamml_annotate:en #1#2 {
+  \__luamml_annotate_begin:
+    #2
+  \__luamml_annotate_end:e {#1}
+}
+
+\cs_new_eq:NN \luamml_pdf_write: \scan_stop:
+\cs_new_protected:Npn \__luamml_patch_package:nn #1 #2 {
+  \@ifpackageloaded {#1} {#2} {
+    \hook_gput_code:nnn {package/#1/after} {luamml} {#2}
+  }
+}
+\cs_new_protected:Npn \__luamml_patch_package:n #1 {
+  \__luamml_patch_package:nn {#1} {
+    \RequirePackage { luamml-patches-#1 }
+  }
+}
+\RequirePackage { luamml-patches-kernel }
+\__luamml_patch_package:n {amstext}
+\__luamml_patch_package:n {amsmath}
+\__luamml_patch_package:n {array}
+
+\endinput
+%%
+%% End of file `luamml.sty'.


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luamml/luamml.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/tlpkg/bin/tlpkg-ctan-check
===================================================================
--- trunk/Master/tlpkg/bin/tlpkg-ctan-check	2024-08-16 21:32:58 UTC (rev 72048)
+++ trunk/Master/tlpkg/bin/tlpkg-ctan-check	2024-08-16 21:35:16 UTC (rev 72049)
@@ -536,7 +536,7 @@
     luaimageembed luaindex luainputenc luakeys
     lualatex-doc-de
     lualatex-math lualatex-truncate lualibs lualinalg luamathalign luamaths
-    luamesh luamodulartables luamplib luanumint luaoptions luaotfload
+    luamesh luamml luamodulartables luamplib luanumint luaoptions luaotfload
     luapackageloader luaplot luaprogtable luapstricks
     luaquotes luarandom
     luaset luasseq luatex85 luatexbase luatexja luatexko luatextra

Modified: trunk/Master/tlpkg/libexec/ctan2tds
===================================================================
--- trunk/Master/tlpkg/libexec/ctan2tds	2024-08-16 21:32:58 UTC (rev 72048)
+++ trunk/Master/tlpkg/libexec/ctan2tds	2024-08-16 21:35:16 UTC (rev 72049)
@@ -3359,6 +3359,7 @@
  'lua-ul'	=> 'etex-answer-y',	# https://github.com/latex3/latex2e/issues/558
  'luacolor'	=> 'etex',
  'luaindex'     => 'lualatex --shell-escape',
+ 'luamml'	=> 'etex-answer-y',
  'luamathalign'	=> 'etex-answer-y',
  'luatexja'     => 'lualatex',
  'magicwatermark' => 'etex',

Modified: trunk/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
===================================================================
--- trunk/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc	2024-08-16 21:32:58 UTC (rev 72048)
+++ trunk/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc	2024-08-16 21:35:16 UTC (rev 72049)
@@ -61,6 +61,7 @@
 depend lualinalg
 depend luamathalign
 depend luamaths
+depend luamml
 depend luamodulartables
 depend luamplib
 depend luaoptions

Added: trunk/Master/tlpkg/tlpsrc/luamml.tlpsrc
===================================================================


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