%D \module %D [ file=x-mmp, %D version=2007.09.04, %D title=\CONTEXT\ XML Modules, %D subtitle=Presentation MathML Renderer, %D author=Hans Hagen, %D date=\currentdate, %D copyright={PRAGMA ADE}] %C %C This module is part of the \CONTEXT\ macro||package and is %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. \startluacode do lxml.mml = lxml.mml or { } local texsprint = tex.sprint -- an alternative is to remap to private codes, where we can have -- different properties .. to be done local n_replacements = { -- [" "] = utf.char(0x2002), -- "&textspace;" -> tricky, no &; in mkiv ["."] = "{.}", [","] = "{,}", [" "] = "", } local o_replacements = { ["{"] = "\\mmlleftdelimiter\\lbrace", ["}"] = "\\mmlrightdelimiter\\rbrace", ["("] = "\\mmlleftdelimiter(", [")"] = "\\mmlrightdelimiter)", ["["] = "\\mmlleftdelimiter[", ["]"] = "\\mmlrightdelimiter]", ["<"] = "\\mmlleftdelimiter<", [">"] = "\\mmlrightdelimiter>", ["#"] = "\\mmlchar{35}", ["$"] = "\\mmlchar{36}", ["%"] = "\\mmlchar{37}", ["&"] = "\\mmlchar{38}", ["^"] = "\\mmlchar{94}{}", -- strange, sometimes luatex math see the char instead of \char ["_"] = "\\mmlchar{95}{}", -- so we need the {} ["~"] = "\\mmlchar{126}", [" "] = "", } local i_replacements = { ["sin"] = "\\mathopnolimits{sin}", ["cos"] = "\\mathopnolimits{cos}", ["abs"] = "\\mathopnolimits{abs}", ["arg"] = "\\mathopnolimits{arg}", ["codomain"] = "\\mathopnolimits{codomain}", ["curl"] = "\\mathopnolimits{curl}", ["determinant"] = "\\mathopnolimits{det}", ["divergence"] = "\\mathopnolimits{div}", ["domain"] = "\\mathopnolimits{domain}", ["gcd"] = "\\mathopnolimits{gcd}", ["grad"] = "\\mathopnolimits{grad}", ["identity"] = "\\mathopnolimits{id}", ["image"] = "\\mathopnolimits{image}", ["lcm"] = "\\mathopnolimits{lcm}", ["max"] = "\\mathopnolimits{max}", ["median"] = "\\mathopnolimits{median}", ["min"] = "\\mathopnolimits{min}", ["mode"] = "\\mathopnolimits{mode}", ["mod"] = "\\mathopnolimits{mod}", ["polar"] = "\\mathopnolimits{Polar}", ["exp"] = "\\mathopnolimits{exp}", ["ln"] = "\\mathopnolimits{ln}", ["log"] = "\\mathopnolimits{log}", ["sin"] = "\\mathopnolimits{sin}", ["arcsin"] = "\\mathopnolimits{arcsin}", ["sinh"] = "\\mathopnolimits{sinh}", ["arcsinh"] = "\\mathopnolimits{arcsinh}", ["cos"] = "\\mathopnolimits{cos}", ["arccos"] = "\\mathopnolimits{arccos}", ["cosh"] = "\\mathopnolimits{cosh}", ["arccosh"] = "\\mathopnolimits{arccosh}", ["tan"] = "\\mathopnolimits{tan}", ["arctan"] = "\\mathopnolimits{arctan}", ["tanh"] = "\\mathopnolimits{tanh}", ["arctanh"] = "\\mathopnolimits{arctanh}", ["cot"] = "\\mathopnolimits{cot}", ["arccot"] = "\\mathopnolimits{arccot}", ["coth"] = "\\mathopnolimits{coth}", ["arccoth"] = "\\mathopnolimits{arccoth}", ["csc"] = "\\mathopnolimits{csc}", ["arccsc"] = "\\mathopnolimits{arccsc}", ["csch"] = "\\mathopnolimits{csch}", ["arccsch"] = "\\mathopnolimits{arccsch}", ["sec"] = "\\mathopnolimits{sec}", ["arcsec"] = "\\mathopnolimits{arcsec}", ["sech"] = "\\mathopnolimits{sech}", ["arcsech"] = "\\mathopnolimits{arcsech}", [" "] = "", ["false"] = "{\\mr false}", ["notanumber"] = "{\\mr NaN}", ["otherwise"] = "{\\mr otherwise}", ["true"] = "{\\mr true}", ["declare"] = "{\\mr declare}", ["as"] = "{\\mr as}", } function lxml.mml.prepare_number(id,pattern) local str = xml.content(lxml.id(id),pattern) or "" texsprint(tex.ctxcatcodes,(str:gsub(".",n_replacements))) end function lxml.mml.prepare_operator(id,pattern) local str = xml.content(lxml.id(id),pattern) or "" tex.sprint(tex.ctxcatcodes,(str:gsub(".",o_replacements))) end function lxml.mml.prepare_identifier(id,pattern) local str = xml.content(lxml.id(id),pattern) or "" str = str:gsub("^%s*(.*)%s*$","%1") local rep = i_replacements[str] if rep then tex.sprint(tex.ctxcatcodes,rep) else tex.sprint(tex.ctxcatcodes,(str:gsub(".",i_replacements))) end end function lxml.mml.connect(id,pattern,separators) -- multiple separators local n = xml.count(id,pattern) if n == 0 then -- skip elseif n == 1 then lxml.all(id,pattern) else local t = { } for s in utf.gmatch(separators,"([^%s])") do t[#t+1] = s end for i=1,n do if i > 1 then texsprint(tex.ctxcatcodes,"{") texsprint(t[i] or t[#t] or "") texsprint(tex.ctxcatcodes,"}") end lxml.idx(id,pattern,i) -- kind of slow, some day ... end end end local function flush(e,tag,toggle) -- texsprint(tex.ctxcatcodes,(toggle and "^{") or "_{") if toggle then texsprint(tex.ctxcatcodes,"^{") else texsprint(tex.ctxcatcodes,"_{") end if tag == "none" then texsprint(tex.ctxcatcodes,"{}") else xml.sprint(e.dt) end if not toggle then texsprint(tex.ctxcatcodes,"}") else texsprint(tex.ctxcatcodes,"}{}") end return not toggle end function lxml.mml.multiscripts(id) local done, toggle = false, false id = lxml.id(id) -- for i=1,#id.dt do local e = id.dt[i] if type(e) == table then ... for r, d, k in xml.elements(id,"/*") do local e = d[k] local tag = e.tg if tag == "mprescripts" then texsprint(tex.ctxcatcodes,"{}") done = true elseif done then toggle = flush(e,tag,toggle) end end local done, toggle = false, false for r, d, k in xml.elements(id,"/*") do local e = d[k] local tag = e.tg if tag == "mprescripts" then break elseif done then toggle = flush(e,tag,toggle) else xml.sprint(e.dt) done = true end end end end \stopluacode \unprotect % helpers % \let\myspecialnormalvert\myspecialstretchvert % \setupMMLappearance[scripts][\c!alternative=\v!a] {} rond base \mapXMLvalue{mmp}{normal} {\tf} \mapXMLvalue{mmp}{double-stuck} {\bf} \mapXMLvalue{mmp}{bolditalic} {\bi} \mapXMLvalue{mmp}{bold-italic} {\bi} \mapXMLvalue{mmp}{boldslanted}{\bs} \mapXMLvalue{mmp}{bold-slanted} {\bs} \mapXMLvalue{mmp}{boldnormal} {\bf} \mapXMLvalue{mmp}{bold} {\bf} \mapXMLvalue{mmp}{slanted} {\sl} \mapXMLvalue{mmp}{normalslanted}{\sl} \mapXMLvalue{mmp}{italic} {\it} \mapXMLvalue{mmp}{normalitalic} {\it} \mapXMLvalue{mmp}{fraktur} {\bf} \mapXMLvalue{mmp}{bold-fraktur} {\bf} \mapXMLvalue{mmp}{script} {\tf} \mapXMLvalue{mmp}{bold-script} {\bf} \def\setMMLptextstyle#1% {\XMLval{mmp}{\xmlatt{#1}{fontweight}\xmlatt{#1}{fontstyle}}{}} \def\setMMLpmathstyle#1% {\XMLval{mmp}{\xmlatt{#1}{mathvariant}}{}} \def\doMMPpcolor#1% {\doifelsenothing{\xmlatt{#1}{color}} \firstofoneargument {\color[\xmlatt{#1}{color}]}} \def\doMMPpbackground#1% {\doifelsenothing{\xmlatt{#1}{background}} {\firstofoneargument} {\inframed [\c!frame=\v!off, \c!background=\v!color, \c!backgroundcolor=\xmlatt{#1}{background}]}} \def\doMMPpsometext#1#2% {\doMMPpbackground{#1}{\doMMPpcolor{#1}{\setMMLptextstyle{#1}\ignorespaces#2\removeunwantedspaces}}} \def\doMMLfiller#1% bugged {\pushmacro\doMMLfiller \let\doMMLfiller\gobbleoneargument \gdef\dodoMMLfiller{\disablefiller\mathematics{#1}}% \hbox {\def\normalorfiller##1##2% {\gdef\dodoMMLfiller{\enablefiller#1}% \let\normalorfiller\gobbletwoarguments}% $#1$}% \popmacro\doMMLfiller} % setups % \defineXMLentity[_] {{\_{}}} % \defineXMLentity[^] {{\^{}}} % \defineXMLentity[_] {\string_} % \defineXMLentity[^] {\normalorfiller\hat\widehat} \startsetups mml:semantics % todo: width=ex/ex/pt \xmlflush{#1} \stopsetups % \startxmlsetups mml:mi % todo: mathvariant mathsize mathcolor mathbackground % \edef\MMPidentifier{\xmlstripped{#1}{*}} % \doifXMLentityelse{\detokenize\expandafter{\MMPidentifier}} { % \getXMLentity\MMPidentifier % } { % \MMPidentifier % } % \stopxmlsetups \startxmlsetups mml:mi % todo: mathvariant mathsize mathcolor mathbackground \ctxlua{lxml.mml.prepare_identifier("#1","*")} \stopxmlsetups \startxmlsetups mml:mn % todo: mathvariant mathsize mathcolor mathbackground \begingroup \mr \ctxlua{lxml.mml.prepare_number("#1","*")}% \endgroup \stopxmlsetups % hbox will make . and , behave ok, see rep patterns above as alternative % % \startxmlsetups mml:mn % todo: mathvariant mathsize mathcolor mathbackground % \mathop{\hbox{\mr \ctxlua{lxml.mml.prepare_number("#1","*")}}}% we need . and , properly spaced % \stopxmlsetups % \startxmlsetups mml:mo % \edef\MMPoperator{\ctxlua{lxml.mml.prepare_operator("#1","*")}} % \doifXMLentityelse{\detokenize\expandafter{\MMPoperator}} { % \getXMLentity\MMPoperator % } { % \MMPoperator % } % \stopxmlsetups % also ok: \startxmlsetups mml:mo \ctxlua{lxml.mml.prepare_operator("#1","*")} \stopxmlsetups \startxmlsetups mml:mrow \begingroup \ifcase\xmlcount{#1}{/mml:mo}\relax \xmlflush{#1} \else % no \let \def\MMLleft {\left } \def\MMLright{\right} \enabledelimiter \checkdelimiters{\xmlall{#1}{/mml:mo}} \fakeleftdelimiter \xmlflush{#1} \fakerightdelimiter \disabledelimiter \fi \endgroup \stopxmlsetups \startxmlsetups mml:mstyle \begingroup \setMMLpmathstyle{#1} \xmlflush{#1} \endgroup \stopxmlsetups \startxmlsetups mml:ms \xmlattdef{#1}{lquot}{"} \doMMPpsometext{#1}{\xmlflush{#1}} \xmlattdef{#1}{rquot}{"} \stopxmlsetups \startxmlsetups mml:mtext \hbox \bgroup \tf % else encoding problems, we can make mr an option \doMMPpsometext{#1}{\xmlflush{#1}} \egroup \stopxmlsetups \startxmlsetups mml:merror \hbox{$\displaystyle\xmlflush{#1}$} \stopxmlsetups \startxmlsetups mml:mphantom \phantom{{}\xmlflush{#1}{}} \stopxmlsetups \startxmlsetups mml:mpadded \xmlflush{#1} \stopxmlsetups \startxmlsetups mml:mfenced % {} around separator is needed for spacing \edef\MMPopen {\xmlatt{#1}{open}} \edef\MMPclose {\xmlatt{#1}{close}} \edef\MMPseparators{\xmlatt{#1}{separators}} \ifx\MMPseparators\empty \def\MMPseparators{,}\fi \doifelsenothing \MMPopen { \left. } { \left\MMPopen } \ctxlua{lxml.mml.connect("#1","/*","\MMPseparators")}% \doifelsenothing \MMPclose { \right. } { \right\MMPclose } \stopxmlsetups \startxmlsetups mml:menclose % notation=..... % we will implement this when we need it \xmlflush{#1} \stopxmlsetups \startxmlsetups mml:msqrt \sqrt{\xmlflush{#1}} \stopxmlsetups \startxmlsetups mml:mroot \root{\xmlindex{#1}{*}{1}}\of{\xmlindex{#1}{*}{2}} \stopxmlsetups \startsetups mml:mfrac % dodo: handle linethickness in lua + unit \begingroup \edef\MMPlinethickness{\xmlatt{#1}{linethickness}} \ifx\MMPlinethickness\empty \doifelse{\xmlatt{#1}{bevelled}}{true} { \xmlindex{#1}{/*}{1} \mathpunct{\kern-.2ex\left.\middle/\right.\kern-.25ex} \xmlindex{#1}{/*}{2} } { \frac{\xmlindex{#1}{/*}{1}}{\xmlindex{#1}{/*}{2}} } \else \processaction [\MMPlinethickness] [ thin=>\scratchdimen=.2pt, medium=>\scratchdimen=.4pt, thick=>\scratchdimen=.8pt, unknown=>\setdimensionwithunit\scratchdimen{\MMPlinethickness}{pt}] { {\xmlindex{#1}{/*}{1}} \above\scratchdimen {\xmlindex{#1}{/*}{2}} } \fi \endgroup \stopsetups \startsetups mml:mspace % todo: width=ex/ex/pt \hskip.5em\relax \stopsetups % {} around first component is essential! \startsetups mml:msub {\xmlindex{#1}{/*}{1}}_{\xmlindex{#1}{/*}{2}} \stopsetups \startsetups mml:msup {\xmlindex{#1}{/*}{1}}^{\xmlindex{#1}{/*}{2}} \stopsetups \startsetups mml:msubsup {\xmlindex{#1}{/*}{1}}_{\xmlindex{#1}{/*}{2}}^{\xmlindex{#1}{/*}{3}} \stopsetups \startsetups mml:mover \mathop { \edef\mmlovertoken{\xmlraw{#1}{/mml:mo[position()==2]}} \doifelse{\utfmathclass\mmlovertoken}{accent} { \utfmathcommand\mmlovertoken{\xmlindex{#1}{/*}{1}} } { \vbox { \m@th\ialign { \hss##\hss\crcr \noalign{\kern3\p@}% \disabledelimiter\doMMLfiller{ \xmlindex{#1}{/*}{2} }\crcr \noalign{\kern3\p@\nointerlineskip}% \disabledelimiter\doMMLfiller{ \xmlindex{#1}{/*}{1} }\crcr } } } } \limits \stopsetups \startsetups mml:munder \mathop { \vtop { \m@th\ialign{ \hss##\hss\crcr \disabledelimiter\doMMLfiller{\xmlindex{#1}{/*}{1}}\crcr \noalign{\kern3\p@\nointerlineskip}% \disabledelimiter\doMMLfiller{\xmlindex{#1}{/*}{2}}\crcr \noalign{\kern3\p@} } } } \limits \stopsetups \startsetups mml:munderover \edef\mmlunderovertoken{\xmlraw{#1}{/mml:mo[position()==1]}} % \doifelse{\utfmathclass\mmlunderovertoken}{limop} { % \utfmathcommand\mmlunderovertoken % } { % \xmlindex{#1}{/*}{1} % { \utfmathcommanddefault\mmlunderovertoken{xmlindex}{{#1}{/*}{1}} _{\disablefiller\disabledelimiter\xmlindex{#1}{/*}{2}} ^{\disablefiller\disabledelimiter\xmlindex{#1}{/*}{3}} \stopsetups \startxmlsetups mml:mlabeledtr \bTR \xmlall{#1}{/mml:mtd} \eTR \stopxmlsetups \startxmlsetups mml:mtr \bTR \xmlall{#1}{/mml:mtd} \eTR \stopxmlsetups \startxmlsetups mml:mtd \bTD$\MMLhack\xmlall{#1}{/*}\relax$\eTD \stopxmlsetups \startxmlsetups mml:mtable \vcenter \bgroup \bTABLE[\c!frame=\v!off] \xmlall{#1}{/mml:mtr|mml:mlabeledtr} \eTABLE \egroup \stopxmlsetups \startxmlsetups mml:mmultiscripts \ctxlua{lxml.mml.multiscripts("#1")} \stopxmlsetups \protect \endinput