% \iffalse meta-comment % % Copyright 2011, 2012 by Philipp Stephani % % 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.3c or later is part of all distributions of % LaTeX version 2009/09/24 or later. % % \fi % % \iffalse %<*driver> \documentclass[a4paper, 10pt]{phst-doc} \usepackage{lualatex-math} \newcommand*{\thispackage}{\textsf{lualatex-math}\xspace} \begin{document} \DocInput{lualatex-math.dtx} \PrintChanges \PrintIndex \end{document} % % \fi % % \CheckSum{960} % % \CharacterTable % {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z % Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z % Digits \0\1\2\3\4\5\6\7\8\9 % Exclamation \! Double quote \" Hash (number) \# % Dollar \$ Percent \% Ampersand \& % Acute accent \' Left paren \( Right paren \) % Asterisk \* Plus \+ Comma \, % Minus \- Point \. Solidus \/ % Colon \: Semicolon \; Less than \< % Equals \= Greater than \> Question mark \? % Commercial at \@ Left bracket \[ Backslash \\ % Right bracket \] Circumflex \^ Underscore \_ % Grave accent \` Left brace \{ Vertical bar \| % Right brace \} Tilde \~} % % % \changes{v0.1}{2011/04/22}{Initial version} % \changes{v0.3a}{2011/09/13}{Updated for changes in \pkg{l3kernel}} % \changes{v1.0}{2012/08/27}{Switched to \pkg{l3docstrip}} % % \GetFileInfo{lualatex-math.sty} % % \title{The \thispackage package\thanks{This document corresponds to % \thispackage{}~\fileversion, dated~\filedate.}} % \author{Philipp Stephani \\ \texttt{p.stephani2@gmail.com}} % \date{\filedate} % % % \maketitle % \tableofcontents % % % \section{Introduction} % % \Hologo{LuaTeX} brings major improvements to all areas of \hologo{TeX} % typesetting and programming. They are made available through new primitives % or the embedded Lua interpreter, and combining them with existing % \hologo{LaTeX2e} packages is not a task the average \hologo{LaTeX} user % should have to care about. Therefore a multitude of \hologo{LaTeX2e} % packages have been written to bridge the gap between documents and the new % features. The \thispackage package focuses on the additional possibilities % for mathematical typesetting. The most eminent of the new features is the % ability to use Unicode and OpenType fonts, as provided by \Robertson’s % \pkg{unicode-math} package. However, there is a smaller group of changes % unrelated to Unicode: these are to be dealt with in this package. While in % principle most \hologo{TeX} documents written for traditional engines should % work just fine with \hologo{LuaTeX}, there is a small number of breaking % changes that require the attention of package authors. The \thispackage % package tries to fix some of the issues encountered while porting traditional % macro packages to \hologo{LuaLaTeX}. % % The decision to write patches for existing macro packages should not be made % lightly: monkey patching done by somebody different from the original package % author ties the patching package to the implementation details of the patched % functionality and breaks all rules of encapsulation. However, due to the % lack of alternatives, it has become an accepted way of providing new % functionality in \hologo{LaTeX}. To keep the negative impact as small as % possible, the \thispackage package patches only the \hologo{LaTeX2e} kernel % and a small number of popular packages. In general, this package should be % regarded as a temporary kludge that should be removed once the math-related % packages are updated to be usable with \hologo{LuaTeX}. By its very nature, % the package is likely to cause problems; in such cases, please refer to the % issue tracker\footnote{\url{https://github.com/phst/lualatex-math/issues}}. % % % \section{Interface} % % The \thispackage package can be loaded with \cmd{\usepackage} or % \cmd{\RequirePackage}, as usual. It has no options and no public interface; % the patching is always done when the package is loaded and cannot be % controlled. As a matter of course, the \thispackage package needs % \hologo{LuaLaTeX} to function; it will produce error messages and refuse to % load under other engines and formats. The package depends on the \pkg{expl3} % bundle, the \pkg{etoolbox} package, the \pkg{luatexbase} bundle and the % \pkg{filehook} package. The \thispackage package is independent of the % \pkg{unicode-math} package; the fixes provided here are valid for both % Unicode and legacy math typesetting. % % Currently patches for the \hologo{LaTeX2e} kernel and the \pkg{amsmath}, % \pkg{amsopn}, \pkg{mathtools} and \pkg{icomma} packages are provided. It is % not relevant whether you load these packages before or after \thispackage. % They should work as expected (and ideally you shouldn’t notice anything), but % if you load other packages that by themselves overwrite commands patched by % this package, bad things may happen, as it is usual with \hologo{LaTeX}. % % One user-visible change is that the new % \DescribeMacros{\mathstyle\luatexmathstyle}\cmd{\mathstyle} primitive % (usually called \cmd{\luatexmathstyle} in \hologo{LuaLaTeX}) should work in % all cases after the \thispackage package has been loaded, provided you use % the high-level macros \DescribeMacros{\frac\binom\genfrac}\cmd{\frac}, % \cmd{\binom}, and \cmd{\genfrac}. The fraction-like \hologo{TeX} primitives % like \cmd{\over} or \cmd{\atopwithdelims} and the \hologo{plainTeX} leftovers % like \cmd{\brack} or \cmd{\choose} cannot be patched, and you shouldn’t use % them. % % \StopEventually{} % % % \section{Implementation of the \hologo{LaTeX2e} package} % % \subsection{Requirements} % % \begin{macrocode} %<*package> %<@@=lltxmath> \NeedsTeXFormat{LaTeX2e}[2009/09/24] \RequirePackage{expl3}[2012/08/14] \ProvidesExplPackage{lualatex-math}{2012/10/13}{1.1}% {Patches for mathematics typesetting with LuaLaTeX} \RequirePackage { etoolbox } [ 2007/10/08 ] \RequirePackage { luatexbase } [ 2010/05/27 ] \RequirePackage { filehook } [ 2011/03/09 ] \RequireLuaModule { lualatex-math } [ 2011/05/05 ] % \end{macrocode} % % \begin{macro}{\@@_restore_catcode:N} % Executing the exhaustive expansion of % \cmd{\@@_restore_catcode:N}\meta{character token} restores the category % code of the \meta{character token} to its current value. % \begin{macrocode} \cs_new_nopar:Npn \@@_restore_catcode:N #1 { \char_set_catcode:nn { \int_eval:n { `#1 } } { \char_value_catcode:n { `#1 } } } % \end{macrocode} % \end{macro} % We use the macro defined above to restore the category code of the dollar % sign. There are packages that make the dollar sign active; hopefully they % get loaded after the packages we are trying to patch. % \begin{macrocode} \exp_args:Nx \AtEndOfPackage { \@@_restore_catcode:N \$ } \char_set_catcode_math_toggle:N \$ % \end{macrocode} % % % \subsection{Messages} % % \begin{l3message}{luatex-required} % Issued when not running under \hologo{LuaTeX}. % \begin{macrocode} \msg_new:nnn { lualatex-math } { luatex-required } { The~ lualatex-math~ package~ requires~ LuaTeX. \\ I~ will~ stop~ loading~ now. } % \end{macrocode} % \end{l3message} % % \begin{l3message}{different-meanings} % Issued when two control sequences have different meanings, but should not. % \begin{macrocode} \msg_new:nnnn { lualatex-math } { different-meanings } { I've~ expected~ the~ control~ sequences \\ #1~ and~ #3 \\ to~ have~ the~ same~ meaning,~ but~ their~ meanings~ are~ different. } { The~ meaning~ of~ #1~ is: \\ #2 \\ The~ meaning~ of~ #3~ is: \\ #4 } % \end{macrocode} % \end{l3message} % % \begin{l3message}{macro-expected} % Issued when trying to patch a non-macro. The first argument must be the % detokenized macro name. % \begin{macrocode} \msg_new:nnn { lualatex-math } { macro-expected } { I've~ expected~ that~ #1~ is~ a~ macro,~ but~ it~ isn't. } % \end{macrocode} % \end{l3message} % % \begin{l3message}{wrong-meaning} % Issued when trying to patch a macro with an unexpected meaning. The first % argument must be the detokenized macro name; the second argument must be % the actual detokenized meaning; and the thied argument must be the expected % detokenized meaning. % \begin{macrocode} \msg_new:nnn { lualatex-math } { wrong-meaning } { I've~ expected~ #1~ to~ have~ the~ meaning \\ #3, \\ but~ it~ has~ the~ meaning \\ #2. } % \end{macrocode} % \end{l3message} % % \begin{l3message}{patch-macro} % Issued when a macro is patched. The first argument must be the detokenized % macro name. % \begin{macrocode} \msg_new:nnn { lualatex-math } { patch-macro } { I'm~ going~ to~ patch~ macro~ #1. } % \end{macrocode} % \end{l3message} % % % \subsection{Initialization} % % Unless we are running under \hologo{LuaTeX}, we issue an error and quit % immediately. Loading the \pkg{luatexbase} module will already have produced % an error, but we issue another one for clarity. % \begin{macrocode} \luatex_if_engine:F { \msg_error:nn { lualatex-math } { luatex-required } \endinput } % \end{macrocode} % % % \subsection{Patching} % % \begin{macro}{\@@_temp:w} % A scratch macro. % \begin{macrocode} \cs_new_eq:NN \@@_temp:w \prg_do_nothing: % \end{macrocode} % \end{macro} % % \begin{macro}{\luatexUmathcode} % \begin{macro}{\luatexUmathcodenum} % \begin{macro}{\luatexUmathchardef} % We need the extended versions of \cmd{\mathcode} and \cmd{\mathchardef}. % The command \cmd{\luatexbase@ensure@primitive}\marg{name} makes sure that % the \hologo{LuaTeX} primitive \cs{\meta{name}} is available under the % qualified name \cs{luatex\meta{name}}. % \begin{macrocode} \luatexbase@ensure@primitive { Umathcode } \luatexbase@ensure@primitive { Umathcodenum } \luatexbase@ensure@primitive { Umathchardef } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\@@_assert_eq:NN} % The macro \cmd{\@@_assert_eq:NN}\meta{first command}\meta{second command} % tests whether the control sequences \meta{first command} and \meta{second % command} have the same meaning, and prints an error message if they do not. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_assert_eq:NN #1 #2 { \cs_if_eq:NNF #1 #2 { \msg_error:nnxxxx { lualatex-math } { different-meanings } { \token_to_str:N #1 } { \token_to_meaning:N #1 } { \token_to_str:N #2 } { \token_to_meaning:N #2 } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_patch:NNnnn} % \begin{macro}{\@@_patch:cNnnn} % The auxiliary macro \cmd{\@@_patch:NNnnn}\meta{command}\meta{factory % command}\marg{parameter text}\marg{expected replacement text}\marg{new % replacement text} tries to patch \meta{command}. If \meta{command} is % undefined, do nothing. Otherwise it must be a macro with the given % \meta{parameter text} and \meta{expected replacement text}, created by the % given \meta{factory command} or equivalent. In this case it will be % overwritten using the \meta{parameter text} and the \meta{new replacement % text}. Otherwise issue a warning and don’t overwrite. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_patch:NNnnn #1 #2 #3 #4 #5 { \cs_if_exist:NT #1 { \token_if_macro:NTF #1 { \group_begin: #2 \@@_temp:w #3 { #4 } \cs_if_eq:NNTF #1 \@@_temp:w { \msg_info:nnx { lualatex-math } { patch-macro } { \token_to_str:N #1 } \group_end: #2 #1 #3 { #5 } } { \msg_warning:nnxxx { lualatex-math } { wrong-meaning } { \token_to_str:N #1 } { \token_to_meaning:N #1 } { \token_to_meaning:N \@@_temp:w } \group_end: } } { \msg_warning:nnx { lualatex-math } { macro-expected } { \token_to_str:N #1 } } } } \cs_generate_variant:Nn \@@_patch:NNnnn { c } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_set_mathchar:NN} % The macro \cmd{\@@_set_mathchar:NN}\meta{control sequence}\meta{token} % defines the \meta{control sequence} as an extended mathematical character % shorthand whose mathematical code is given by the mathematical code of the % character \texttt{\textasciigrave}\meta{token}. We cannot use the % |\Umathcharnumdef| primitive here since we would then rely on the % |\Umathcodenum| primitive which is currently % broken.\footnote{\url{http://tug.org/pipermail/luatex/2012-October/003794.html}} % \changes{v0.3c}{2012/08/23}{\pkg{l3kernel} renamed \cs{lua_now:x} to % \cs{lua_now_x:n}} % \changes{v1.1}{2012/10/13}{Update reasoning why \cs{Umathcharnumdef} is not % used here} % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_set_mathchar:NN #1 #2 { \luatexUmathchardef #1 \lua_now_x:n { lualatex.math.print_class_fam_slot( \int_eval:n { `#2 } ) } \scan_stop: } % \end{macrocode} % \end{macro} % % % \subsection{\Hologo{LaTeX2e} kernel} % % \changes{v0.3}{2011/08/07}{Patched math group allocation to gain access to % all families} % In \hologo{LuaTeX}, we have 256 math families at our disposal. Therefore we % modify the \hologo{LaTeX} allocation macros \cmd{\newfam} and % \cmd{\new@mathgroup} accordingly. % % First we test whether \cmd{\newfam} and \cmd{\new@mathgroup} are equal. % \begin{macrocode} \@@_assert_eq:NN \newfam \new@mathgroup % \end{macrocode} % % \begin{macro}{\new@mathgroup} % It is enough to modify the maximum number of families known to the % allocation system; the macro \cmd{\alloc@} takes care of the rest. This % would work even if the \pkg{etex} package weren’t loaded. % \begin{macrocode} \@@_patch:NNnnn \new@mathgroup \cs_set_nopar:Npn { } { %<@@=> \alloc@ 8 \mathgroup \chardef \sixt@@n %<@@=lltxmath> } { \alloc@ 8 \mathgroup \chardef \c_two_hundred_fifty_six } % \end{macrocode} % \end{macro} % % \begin{macro}{\newfam} % We have to reset \cmd{\newfam} to equal \cmd{\new@mathgroup}. % \begin{macrocode} \cs_set_eq:NN \newfam \new@mathgroup % \end{macrocode} % \end{macro} % % \Hologo{LuaTeX} enables access to the current mathematical style via the % \cmd{\mathstyle} primitive. For this to work, fraction-like constructs (\eg, % \meta{numerator} \cmd{\over} \meta{denominator}) have to be enclosed in a % \cmd{\Ustack} group. \cmd{\frac} can be patched to do this, but the % \hologo{plainTeX} remnants \cmd{\choose}, \cmd{\brack} and \cmd{\brace} % should be discouraged. % % \begin{macro}{\luatexUstack} % First we make sure that we can use the \cmd{\Ustack} primitive (under the % name \cmd{\luatexUstack}). % \begin{macrocode} \luatexbase@ensure@primitive { Ustack } % \end{macrocode} % \end{macro} % % \begin{macro}{\frac} % Here we assume that nobody except \pkg{amsmath} redefines \cmd{\frac}. % This is obviously not the case, but we ignore other packages (\eg, % \pkg{nath}) for the moment. We only patch the \hologo{LaTeX2e} kernel % definition if the \pkg{amsmath} package is not loaded; the corresponding % patch for \pkg{amsmath} follows below. % \begin{macrocode} \AtEndPreamble { \@ifpackageloaded { amsmath } { } { \@@_patch:NNnnn \frac \cs_set_nopar:Npn { #1 #2 } { { \begingroup #1 \endgroup \over #2 } } { % \end{macrocode} % To do: do we need the additional set of braces around \cmd{\Ustack}? % \begin{macrocode} { \luatexUstack { \group_begin: #1 \group_end: \over #2 } } } } } % \end{macrocode} % \end{macro} % % % \subsection{\pkg{amsmath}} % % The popular \pkg{amsmath} package is subject to three \hologo{LuaTeX}-related problems: % \begin{itemize} % \item The \cmd{\mathcode} primitive is used several times, which fails for % Unicode math characters. \cmd{\Umathcode} should be used instead. % \item Legacy font dimensions are used for constructing stacks in the % \cmd{\substack} command and the \env{subarray} environment. This doesn’t % work if a Unicode math font is selected. % \item The fraction commands \cmd{\frac} and \cmd{\genfrac} don’t use the % \cmd{\Ustack} primitive. % \end{itemize} % % \begin{macro}{\luatexalignmark} % \begin{macro}{\luatexUstartmath} % \begin{macro}{\luatexUstopmath} % We use the primitives corresponding to the alignment mark (\verb+#+) and to % the inline math switches; this is more semantical and might lead to better % error messages. % \begin{macrocode} \luatexbase@ensure@primitive { alignmark } \luatexbase@ensure@primitive { Ustartmath } \luatexbase@ensure@primitive { Ustopmath } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\luatexUmathstacknumup} % \begin{macro}{\luatexUmathstackdenomdown} % \begin{macro}{\luatexUmathstackvgap} % Now we require the font parameters we will use. % \begin{macrocode} \luatexbase@ensure@primitive { Umathstacknumup } \luatexbase@ensure@primitive { Umathstackdenomdown } \luatexbase@ensure@primitive { Umathstackvgap } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\c_@@_std_minus_mathcode_int} % \begin{macro}{\c_@@_std_equal_mathcode_int} % These constants contain the standard \hologo{TeX} mathematical codes for % the minus and the equal signs. We temporarily set the math codes to these % constants before loading the \pkg{amsmath} package so that it can request % the legacy math code without error. % \begin{macrocode} \int_const:Nn \c_@@_std_minus_mathcode_int { "2200 } \int_const:Nn \c_@@_std_equal_mathcode_int { "303D } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_char_dim:NN} % The macro \cmd{\@@_char_dim:NN}\meta{primitive}\meta{token} expands to a % \meta{dimen} whose value is the metric of the mathematical character % corresponding to the character \texttt{\textasciigrave}\meta{token} % specified by \meta{primitive}, which must be one of \cmd{\fontcharwd}, % \cmd{\fontcharht} or \cmd{\fontchardp}, in the currently selected text % style font. % \changes{v0.3c}{2012/08/23}{\pkg{l3kernel} renamed \cs{lua_now:x} to % \cs{lua_now_x:n}} % \begin{macrocode} \cs_new_nopar:Npn \@@_char_dim:NN #1 #2 { #1 \textfont \lua_now_x:n { lualatex.math.print_fam_slot( \int_eval:n { `#2 } ) } } % \end{macrocode} % \end{macro} % % \begin{macro}{\l_@@_minus_mathchar} % \begin{macro}{\l_@@_equal_mathchar} % These mathematical characters are saved before \pkg{amsmath} is loaded so % that we can temporarily assign the \hologo{TeX} values to the mathematical % codes of the minus and equals signs. The \pkg{amsmath} package queries % these codes, and if they represent Unicode characters, the package loading % will fail. If \pkg{amsmath} has already been loaded, there is nothing we % can do, therefore we use the non-starred version of % \cmd{\AtBeginOfPackageFile}. % \begin{macrocode} \chk_if_free_cs:N \l_@@_minus_mathchar \chk_if_free_cs:N \l_@@_equal_mathchar \AtBeginOfPackageFile { amsmath } { \@@_set_mathchar:NN \l_@@_minus_mathchar \- \@@_set_mathchar:NN \l_@@_equal_mathchar \= % \end{macrocode} % \end{macro} % \end{macro} % Now we temporarily reset the mathematical codes. % \begin{macrocode} \char_set_mathcode:nn { `\- } { \c_@@_std_minus_mathcode_int } \char_set_mathcode:nn { `\= } { \c_@@_std_equal_mathcode_int } \AtEndOfPackageFile { amsmath } { % \end{macrocode} % \begin{macro}{\std@minus} % \begin{macro}{\std@equals} % The \pkg{amsmath} package defines the control sequences \cmd{\std@minus} % and \cmd{\std@equal} as mathematical character shorthands while loading, % but uses our restored mathematical codes, which must be fixed. % \begin{macrocode} \cs_set_eq:NN \std@minus \l_@@_minus_mathchar \cs_set_eq:NN \std@equal \l_@@_equal_mathchar % \end{macrocode} % \end{macro} % \end{macro} % Finally, we restore the original mathematical codes of the two signs. % \begin{macrocode} \luatexUmathcodenum `\- \l_@@_minus_mathchar \luatexUmathcodenum `\= \l_@@_equal_mathchar } } % \end{macrocode} % All of the following fixes work even if \pkg{amsmath} is already loaded. % \begin{macro}{\@begindocumenthook} % \changes{v0.3b}{2011/09/18}{Another update for a change in \pkg{l3kernel}} % \pkg{amsmath} repeats the definiton of \cmd{\std@minus} and % \cmd{\std@equal} at the beginning of the document, so we also have to patch % the internal kernel macro \cmd{\@begindocumenthook} which contains the hook % code. % \begin{macrocode} \AtEndOfPackageFile * { amsmath } { \tl_replace_once:Nnn \@begindocumenthook { \mathchardef \std@minus \mathcode `\- \relax \mathchardef \std@equal \mathcode `\= \relax } { \@@_set_mathchar:NN \std@minus \- \@@_set_mathchar:NN \std@equal \= } % \end{macrocode} % \end{macro} % % \begin{macro}{\resetMathstrut@} % \pkg{amsmath} uses the box \cmd{\Mathstrutbox@} for struts in mathematical % mode. This box is defined to have the height and depth of the opening % parenthesis taken from the current text font. The command % \cmd{\resetMathstrut@} is executed whenever the mathematical fonts are % changed and has to restore the correct dimensions. The original definition % uses a temporary mathematical character shorthand definition whose meaning % is queried to extract the family and slot. We can do this in Lua; % furthermore we can avoid a temporary box because \hologo{eTeX} allows us to % query glyph metrics directly. % \begin{macrocode} \@@_patch:NNnnn \resetMathstrut@ \cs_set_nopar:Npn { } { \setbox \z@ \hbox { \mathchardef \@tempa \mathcode `\( \relax % \) \def \@tempb ##1 "##2 ##3 { \the \textfont "##3 \char" } \expandafter \@tempb \meaning \@tempa \relax } \ht \Mathstrutbox@ \ht \z@ \dp \Mathstrutbox@ \dp \z@ } { \box_set_ht:Nn \Mathstrutbox@ { \@@_char_dim:NN \fontcharht \( % \) } \box_set_dp:Nn \Mathstrutbox@ { \@@_char_dim:NN \fontchardp \) } } % \end{macrocode} % \end{macro} % % \begin{environment}{subarray} % The \env{subarray} environment uses legacy font dimensions. We simply % patch it to use \hologo{LuaTeX} font parameters (and \hologo{LaTeX3} % expressions instead of \hologo{TeX} arithmetic). Since subscript arrays % are conceptually vertical stacks, we use the sum of top and bottom shift % for the default vertical baseline distance (\cmd{\baselineskip}) and the % minimum vertical gap for stack for the minimum baseline distance % (\cmd{\lineskip}). % \begin{macrocode} \@@_patch:NNnnn \subarray \cs_set:Npn { #1 } { \vcenter \bgroup \Let@ \restore@math@cr \default@tag \baselineskip \fontdimen 10~ \scriptfont \tw@ \advance \baselineskip \fontdimen 12~ \scriptfont \tw@ %<@@=> \lineskip \thr@@ \fontdimen 8~ \scriptfont \thr@@ %<@@=lltxmath> \lineskiplimit \lineskip \ialign \bgroup \ifx c #1 \hfil \fi $ \m@th \scriptstyle ## $ \hfil \crcr } { \vcenter \c_group_begin_token \Let@ \restore@math@cr \default@tag \skip_set:Nn \baselineskip { \luatexUmathstacknumup \scriptstyle + \luatexUmathstackdenomdown \scriptstyle } \lineskip \luatexUmathstackvgap \scriptstyle \lineskiplimit \lineskip \ialign \c_group_begin_token \token_if_eq_meaning:NNT c #1 { \hfil } \luatexUstartmath \m@th \scriptstyle \luatexalignmark \luatexalignmark \luatexUstopmath \hfil \crcr } % \end{macrocode} % \end{environment} % % \begin{macro}{\frac} % Since \cmd{\frac} is declared by \cmd{\DeclareRobustCommand}, we must patch % the macro \cmd{\frac\textvisiblespace}. % \begin{macrocode} \@@_patch:cNnnn { frac~ } \cs_set:Npn { #1 #2 } { { %<@@=> \begingroup #1 \endgroup \@@over #2 } } { { \luatexUstack { \group_begin: #1 \group_end: \@@over #2 } %<@@=lltxmath> } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@genfrac} % Generalized fractions are typeset by the internal \cmd{\@genfrac} command. % \begin{macrocode} \@@_patch:NNnnn \@genfrac \cs_set_nopar:Npn { #1 #2 #3 #4 #5 } { { #1 { \begingroup #4 \endgroup #2 #3 \relax #5 } } } { { #1 { \luatexUstack { \group_begin: #4 \group_end: #2 #3 \scan_stop: #5 } } } } } % \end{macrocode} % \end{macro} % % % \subsection{\pkg{amsopn}} % % \changes{v1.1}{2012/10/13}{Add fix and unit test for \pkg{amsopn}} % The \pkg{amsopn} package can be used standalone, but is also loaded by % \pkg{amsmath}. It provides the \cmd{\DeclareMathOperator} command which % breaks when the minus character is a Unicode math character; this issue was % brought to my attention by \Burnol. % % \begin{macro}{\newmcodes@} % We only need to patch one usage of |\mathcode| in the internal macro % |\newmcodes@|, which is called by all user-defined operators. % \begin{macrocode} \group_begin: \char_set_catcode_other:N \" \AtEndOfPackageFile * { amsopn } { \@@_patch:NNnnn \newmcodes@ \cs_gset_nopar:Npn { } { \mathcode `\' 39 \mathcode `\* 42 \mathcode `\. "613A \ifnum \mathcode `\- = 45 ~ \else \mathchardef \std@minus \mathcode `\- \relax \fi \mathcode `\- 45 \mathcode `\/ 47 \mathcode `\: "603A \relax } { \char_set_mathcode:nn { `\' } { 39 } \char_set_mathcode:nn { `\* } { 42 } \char_set_mathcode:nn { `\. } { "613A } \int_compare:nNnF { \luatexUmathcodenum `\- } = { 45 } { \@@_set_mathchar:NN \std@minus \- } \char_set_mathcode:nn { `\- } { 45 } \char_set_mathcode:nn { `\/ } { 47 } \char_set_mathcode:nn { `\: } { "603A } } } \group_end: % \end{macrocode} % \end{macro} % % % \subsection{\pkg{mathtools}} % % \pkg{mathtools}’ \cmd{\cramped} command and others that make use of its % internal version use a hack involving a null radical. \Hologo{LuaTeX} has % primitives for setting material in cramped mode, so we make use of them. % % \begin{macro}{\luatexcrampeddisplaystyle} % \begin{macro}{\luatexcrampedtextstyle} % \begin{macro}{\luatexcrampedscriptstyle} % \begin{macro}{\luatexcrampedscriptscriptstyle} % First we make sure that the needed primitives for cramped styles are % available. % \begin{macrocode} \luatexbase@ensure@primitive { crampeddisplaystyle } \luatexbase@ensure@primitive { crampedtextstyle } \luatexbase@ensure@primitive { crampedscriptstyle } \luatexbase@ensure@primitive { crampedscriptscriptstyle } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\MT_cramped_internal:Nn} % The macro \cmd{\MT_cramped_internal:Nn}\meta{style}\marg{expression} % typesets the \meta{expression} in the cramped style corresponding to the % given \meta{style} (\cmd{\displaystyle} etc.); all we have to do in % \hologo{LuaTeX} is to select the correct primitive. Rewriting the % user-level \cmd{\cramped} command and employing \cmd{\mathstyle} would be % possible as well, but we avoid this way since we want to patch only a % single command. % \begin{macrocode} \AtEndOfPackageFile * { mathtools } { \@@_patch:NNnnn \MT_cramped_internal:Nn \cs_set_nopar:Npn { #1 #2 } { \sbox \z@ { $ \m@th #1 \nulldelimiterspace = \z@ \radical \z@ { #2 } $ } \ifx #1 \displaystyle \dimen@ = \fontdimen 8 \textfont 3 \advance \dimen@ .25 \fontdimen 5 \textfont 2 \else \dimen@ = 1.25 \fontdimen 8 \ifx #1 \textstyle \textfont \else \ifx #1 \scriptstyle \scriptfont \else \scriptscriptfont \fi \fi 3 \fi \advance \dimen@ -\ht\z@ \ht\z@ = -\dimen@ \box\z@ } { % \end{macrocode} % Here the additional set of braces is absolutely necessary, otherwise the % changed mathematical style would be applied to the material after the % \cmd{\mathchoice} construct. % \begin{macrocode} { \use:c { luatexcramped \cs_to_str:N #1 } #2 } } } % \end{macrocode} % \end{macro} % % % \subsection{\pkg{icomma}} % % \changes{v0.2}{2011/07/03}{Added patch for the \pkg{icomma} package} % The \pkg{icomma} package uses |\mathchardef| to save the mathematical code of % the comma character. This breaks for Unicode fonts. The incompatibility was % noticed by % \Breitfeld.\footnote{\url{https://groups.google.com/forum/\#!topic/de.comp.text.tex/Cputk-AJS5I/discussion}} % % \begin{macro}{\mathcomma} % \pkg{icomma} defines the mathemathical character shorthand \cmd{\icomma} at % the beginning of the document, therefore we again patch % \cmd{\@begindocumenthook}. % \begin{macrocode} \AtEndOfPackageFile * { icomma } { \tl_replace_once:Nnn \@begindocumenthook { \mathchardef \mathcomma \mathcode `\, } { \@@_set_mathchar:NN \mathcomma \, } } % % \end{macrocode} % \end{macro} % % % \section{Implementation of the \hologo{LuaLaTeX} module} % % For the Lua module, we use the standard \pkg{luatexbase-modutils} template % and the \func{module} function. % \begin{macrocode} %<*lua> require("luatexbase.modutils") require("luatexbase.cctb") local err, warn, info, log = luatexbase.provides_module({ name = "lualatex-math", date = "2011/05/05", version = 0.1, description = "Patches for mathematics typesetting with LuaLaTeX", author = "Philipp Stephani", licence = "LPPL v1.3+" }) local unpack = unpack local string = string local tex = tex local cctb = luatexbase.catcodetables module("lualatex.math") % \end{macrocode} % % \begin{function}{print_fam_slot} % The function \func{print_fam_slot} takes one argument which must be a % number. It interprets the argument as a Unicode code point whose % mathematical code is printed in the form % \meta{family}\texttt{\textvisiblespace}\meta{slot}, suitable for the % right-hand side of \eg \verb|\fontcharht\textfont|. % \begin{macrocode} function print_fam_slot(char) local code = tex.getmathcode(char) local class, family, slot = unpack(code) local result = string.format("%i %i ", family, slot) tex.sprint(cctb.string, result) end % \end{macrocode} % \end{function} % % \begin{function}{print_class_fam_slot} % The function \func{print_class_fam_slot} takes one argument which must be a % number. It interprets the argument as a Unicode code point whose % mathematical code is printed in the form % \meta{class}\texttt{\textvisiblespace}\meta{family}\texttt{\textvisiblespace}\meta{slot}, % suitable for the right-hand side of \cmd{\Umathchardef}. % \begin{macrocode} function print_class_fam_slot(char) local code = tex.getmathcode(char) local class, family, slot = unpack(code) local result = string.format("%i %i %i ", class, family, slot) tex.sprint(cctb.string, result) end % % \end{macrocode} % \end{function} % % % \section{Test files} % % Finally six small test files—but not a real test suite. % % % \subsection{Common definitions} % % \begin{macrocode} %<*test> %<@@=test> \documentclass[pagesize=auto]{scrartcl} % \end{macrocode} % Only \pkg{xparse} starting with 2008/08/03 has \cmd{\NewDocumentCommand}. % \begin{macrocode} \usepackage{xparse}[2008/08/03] \usepackage{luacode} \ExplSyntaxOn \AtBeginDocument { \errorcontextlines = \c_fifteen } % \end{macrocode} % % \begin{l3message}{pass} % This message is issued when a test passed. % \begin{macrocode} \msg_new:nnn { test } { pass } { #1 } % \end{macrocode} % \end{l3message} % % \begin{macro}{\@@_pass:x} % The macro \cmd{\@@_pass:x}\marg{text} issues the \msg{pass} message with % description \meta{text}. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_pass:x #1 { \msg_info:nnx { test } { pass } { #1 } } % \end{macrocode} % \end{macro} % % \begin{l3message}{fail} % This message is issued when a test failed. % \begin{macrocode} \msg_new:nnn { test } { fail } { #1 } % \end{macrocode} % \end{l3message} % % \begin{macro}{\@@_fail:x} % The macro \cmd{\@@_fail:x}\marg{text} issues the \msg{fail} message with % description \meta{text}. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_fail:x #1 { \msg_error:nnx { test } { fail } { #1 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\tl_const:Nx} % We need expanding constants. % \begin{macrocode} \cs_generate_variant:Nn \tl_const:Nn { Nx } % \end{macrocode} % \end{macro} % % \begin{macro}{\c_@@_equal_tl} % \begin{macro}{\c_@@_not_equal_tl} % Two shorthands for pretty-printing test results. % \begin{macrocode} \tl_const:Nx \c_@@_equal_tl { \c_space_tl == \c_space_tl } \tl_const:Nx \c_@@_not_equal_tl { \c_space_tl != \c_space_tl } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_equal_pass:nxnx} % The macro \cmd{\@@_equal_pass:nxnx}\marg{first expression}\marg{first % value}\marg{second expression}\marg{second value} is called when the two % values arising from the two expressions are equal. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_equal_pass:nxnx #1 #2 #3 #4 { \@@_pass:x { \exp_not:n { #1 } \c_@@_equal_tl #2 \c_@@_equal_tl #4 \c_@@_equal_tl \exp_not:n { #3 } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_equal_fail:nxnx} % The macro \cmd{\@@_equal_pass:nxnx}\marg{first expression}\marg{first % value}\marg{second expression}\marg{second value} is called when the two % values arising from the two expressions are not equal. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_equal_fail:nxnx #1 #2 #3 #4 { \@@_fail:x { \exp_not:n { #1 } \c_@@_equal_tl #2 \c_@@_not_equal_tl #4 \c_@@_equal_tl \exp_not:n { #3 } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_assert_equal:NNNNNnn} % \begin{macro}{\@@_assert_equal:cccccnn} % The macro \cmd{\@@_assert_equal:NNNNNnn}\meta{set command}\meta{use % command}\meta{compare command}\meta{first temporary command}\meta{second % temporary command}\marg{first expression}\marg{second expression} asserts % that the two expressions are equal. The \meta{set command} must have the % argument specification \texttt{Nn}, the \meta{use command} \texttt{N}, and % the \meta{compare command} \texttt{nNnTF}. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_assert_equal:NNNNNnn #1 #2 #3 #4 #5 #6 #7 { #1 #4 { #6 } #1 #5 { #7 } #3 { #4 } = { #5 } { \@@_equal_pass:nxnx { #6 } { #2 #4 } { #7 } { #2 #5 } } { \@@_equal_fail:nxnx { #6 } { #2 #4 } { #7 } { #2 #5 } } } \cs_generate_variant:Nn \@@_assert_equal:NNNNNnn { ccccc } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_assert_equal:nnn} % The macro \cmd{\@@_assert_equal:nnn}\marg{data type}\marg{first % expression}\marg{second expression} is a simplified version of % \cmd{\@@_assert_equal:NNNNNnn} for data types following the \hologo{LaTeX3} % naming conventions; \meta{data type} must be \texttt{int}, \texttt{dim}, % \etc % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_assert_equal:nnn #1 #2 #3 { \@@_assert_equal:cccccnn { #1 _set:Nn } { #1 _use:N } { #1 _compare:nNnTF } { l_@@_tmpa_ #1 } { l_@@_tmpb_ #1 } { #2 } { #3 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\l_@@_tmpa_int} % \begin{macro}{\l_@@_tmpb_int} % Scratch registers for numbers. % \begin{macrocode} \int_new:N \l_@@_tmpa_int \int_new:N \l_@@_tmpb_int % \end{macrocode} % \end{macro} % \end{macro} % \begin{macro}{\AssertIntEqual} % The command \cmd{\AssertIntEqual}\marg{first expression}\marg{second % expression} asserts that the two integral expressions are equal. % \begin{macrocode} \NewDocumentCommand \AssertIntEqual { m m } { \@@_assert_equal:nnn { int } { #1 } { #2 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\l_@@_tmpa_int} % \begin{macro}{\l_@@_tmpb_int} % Scratch registers for dimensions. % \begin{macrocode} \dim_new:N \l_@@_tmpa_dim \dim_new:N \l_@@_tmpb_dim % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\AssertDimEqual} % The command \cmd{\AssertDimEqual}\marg{first expression}\marg{second % expression} asserts that the two dimension expressions are equal. % \begin{macrocode} \NewDocumentCommand \AssertDimEqual { m m } { \@@_assert_equal:nnn { dim } { #1 } { #2 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\AssertMathStyle} % The command \cmd{\AssertMathStyle}\marg{expression} asserts that the % current mathematical style is equal to the value of the integral % \meta{expression}. % \begin{macrocode} \NewDocumentCommand \AssertMathStyle { m } { \AssertIntEqual { \luatexmathstyle } { #1 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_assert_cramped:Nx} % The macro \cmd{\@@_assert_cramped:Nn}\meta{predicate}\marg{name} asserts % that we are in math mode and that the current style fulfills the % \meta{predicate} (identified by the \meta{name}) which must have the % argument specification \texttt{n}. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_assert_cramped:Nx #1 #2 { \int_set:Nn \l_@@_tmpa_int { \luatexmathstyle } \bool_if:nTF { \int_compare_p:nNn { \l_@@_tmpa_int } > { \c_minus_one } && #1 { \l_@@_tmpa_int } } { \@@_pass:x { \exp_not:N \luatexmathstyle \c_@@_equal_tl \int_use:N \l_@@_tmpa_int \c_space_tl is~ a~ #2~ style } } { \@@_fail:x { \exp_not:N \luatexmathstyle \c_@@_equal_tl \int_use:N \l_@@_tmpa_int \c_space_tl is~ not~ a~ #2~ style } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\AssertNoncrampedStyle} % The command \cmd{\AssertNoncrampedStyle} asserts that the current % mathematical style is one of the non-cramped styles. % \begin{macrocode} \NewDocumentCommand \AssertNoncrampedStyle { } { \@@_assert_cramped:Nx \int_if_even_p:n { non-cramped } } % \end{macrocode} % \end{macro} % % \begin{macro}{\AssertCrampedStyle} % The command \cmd{\AssertCrampedStyle} asserts that the current mathematical % style is one of the cramped styles. % \begin{macrocode} \NewDocumentCommand \AssertCrampedStyle { } { \@@_assert_cramped:Nx \int_if_odd_p:n { cramped } } % \end{macrocode} % \end{macro} % % \begin{macro}{\l_@@_tmpa_box} % \begin{macro}{\l_@@_tmpb_box} % Scratch registers for box constructions. % \begin{macrocode} \box_new:N \l_@@_tmpa_box \box_new:N \l_@@_tmpb_box % \end{macrocode} % \end{macro} % \end{macro} % % \begin{function}{contains_space} % The function \func{contains_space}|(head, width)| returns |true| if the % node list starting at |head| or any of its sublists contain a glue or kern % node of width |width|. If |width| is |nil|, returns |true| if there is any % glue or kern node. If |width| is the string |"nonzero"|, returns |true| if % there is any glue node or kern node of nonzero with. % \changes{v1.1}{2012/10/13}{Allow testing for nonzero kern nodes} % \begin{macrocode} \begin{luacode*} function contains_space(head, width) for n in node.traverse(head) do local id = n.id if id == 10 then -- glue node if width then if width == "nonzero" or n.spec.width == width then return true end end elseif id == 11 then -- kern node if width then if width == "nonzero" then if n.kern ~= 0 then return true end elseif n.kern == width then return true end end elseif id == 0 or id == 1 then -- sublist if contains_space(n.head, width) then return true end end end return false end \end{luacode*} % \end{macrocode} % \end{function} % % \begin{macro}{\AssertNoSpace} % The command \cmd{\AssertNoSpace}\marg{text} asserts that the node list that % is the result of typesetting \meta{text} contains no glue or kern nodes. % When called with a star, the command ignores zero-width kerns. % \changes{v0.3c}{2012/08/23}{\pkg{l3kernel} renamed \cs{lua_now:x} to % \cs{lua_now_x:n}} % \changes{v1.1}{2012/10/13}{Allow testing for nonzero kern nodes} % \begin{macrocode} \NewDocumentCommand \AssertNoSpace { s m } { \hbox_set:Nn \l_@@_tmpa_box { #2 } \int_if_odd:nTF { \lua_now_x:n { local~ b = tex.getbox(\int_use:N \l_@@_tmpa_box) if~ contains_space(b.head, \IfBooleanTF { #1 } { "nonzero" } { nil }) then~ tex.sprint("0") else~ tex.sprint("1") end } } { \@@_pass:x { \tl_to_str:n { #2 } ~ contains~ no~ skip~ or~ kern~ node } } { \@@_fail:x { \tl_to_str:n { #2 } ~ contains~ a~ skip~ or~ kern~ node } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\AssertMuSpace} % The command \cmd{\AssertMuSpace}\marg{text}\marg{muskip} asserts that the % node list that is the result of typesetting \meta{text} contains at least % one glue or kern node of with \meta{muskip}. % \changes{v0.3c}{2012/08/23}{\pkg{l3kernel} renamed \cs{lua_now:x} to % \cs{lua_now_x:n}} % \begin{macrocode} \makeatletter \NewDocumentCommand \AssertMuSpace { m m } { \hbox_set:Nn \l_@@_tmpa_box { #1 } \hbox_set:Nn \l_@@_tmpb_box { $ \mskip #2 \m@th $ } \int_if_odd:nTF { \lua_now_x:n { local~ b = tex.getbox(\int_use:N \l_@@_tmpa_box) local~ s = tex.getbox(\int_use:N \l_@@_tmpb_box) if~ contains_space(b.head, s.width) then~ tex.sprint("1") else~ tex.sprint("0") end } } { \@@_pass:x { \tl_to_str:n { #1 } ~ contains~ a~ skip~ or~ kern~ node~ of~ width~ \tl_to_str:n { #2 } } } { \@@_fail:x { \tl_to_str:n { #1 } ~ contains~ no~ skip~ or~ kern~ node~ of~ width~ \tl_to_str:n { #2 } } } } \makeatother \ExplSyntaxOff % % \end{macrocode} % \end{macro} % % % \subsection{\Hologo{LaTeX2e} kernel, allocation of math families} % % \changes{v0.3}{2011/08/07}{Added test file for modified family allocation % scheme} % The \hologo{LaTeX2e} kernel itself allocates four families (also known as % \enquote{math groups} in \hologo{LaTeX} parlance). Therefore we should still % be able to allocate 252 families. We do this alternately with \cmd{\newfam}, % \cmd{\new@mathgroup} and \cmd{\DeclareSymbolFont}. % \begin{macrocode} %<*test-kernel-alloc> \usepackage{lualatex-math} \makeatletter \ExplSyntaxOn \prg_stepwise_inline:nnnn { \c_four } { \c_one } { \c_two_hundred_fifty_five - \c_one } { \prg_case_int:nnn { \int_mod:nn { #1 } { \c_three } } { { \c_zero } { \chk_if_free_cs:N \g_@@_family_int \newfam \g_@@_family_int \AssertIntEqual { \g_@@_family_int } { #1 } \cs_undefine:N \g_@@_family_int } { \c_one } { \chk_if_free_cs:N \g_@@_mathgroup_int \new@mathgroup \g_@@_mathgroup_int \AssertIntEqual { \g_@@_mathgroup_int } { #1 } \cs_undefine:N \g_@@_mathgroup_int } { \c_two } { \DeclareSymbolFont { Test #1 } { OT1 } { cmr } { m } { n } \exp_args:Nc \AssertIntEqual { sym Test #1 } { #1 } } } { \@@_fail:x { This~ cannot~ happen } } } \DeclareSymbolFont { Test 255 } { OT1 } { cmr } { bx } { it } \DeclareSymbolFontAlphabet { \TestAlphabet } { Test 255 } \exp_args:Nc \AssertIntEqual { sym Test 255 } { \c_two_hundred_fifty_five } \ExplSyntaxOff \makeatother \begin{document} \[ \TestAlphabet{ abc \AssertIntEqual{\fam}{255} \AssertIntEqual{\mathgroup}{255} } \] \end{document} % % \end{macrocode} % % % \subsection{\Hologo{LaTeX2e} kernel, \cs{mathstyle} primitive} % % Here we only check whether different fractions and other style-changing % commands result in the correct mathematical style. % \begin{macrocode} %<*test-kernel-style> \usepackage{lualatex-math} \begin{document} \begin{displaymath} \AssertMathStyle{0} \sqrt{\AssertMathStyle{1}} \frac{\AssertMathStyle{2}}{\AssertMathStyle{3}} a^{\frac{\AssertMathStyle{6}}{\AssertMathStyle{7}}} \sqrt{\frac{\AssertMathStyle{3}}{\AssertMathStyle{3}}} \displaystyle \frac{\AssertMathStyle{2}}{\AssertMathStyle{3}} \luatexcrampeddisplaystyle \frac{\AssertMathStyle{3}}{\AssertMathStyle{3}} \textstyle \frac{\AssertMathStyle{4}}{\AssertMathStyle{5}} \luatexcrampedtextstyle \frac{\AssertMathStyle{5}}{\AssertMathStyle{5}} \scriptstyle \frac{\AssertMathStyle{6}}{\AssertMathStyle{7}} \luatexcrampedscriptstyle \frac{\AssertMathStyle{7}}{\AssertMathStyle{7}} \end{displaymath} \begin{math} \AssertMathStyle{2} \sqrt{\AssertMathStyle{3}} \frac{\AssertMathStyle{4}}{\AssertMathStyle{5}} a^{\frac{\AssertMathStyle{6}}{\AssertMathStyle{7}}} \sqrt{\frac{\AssertMathStyle{5}}{\AssertMathStyle{5}}} \displaystyle \frac{\AssertMathStyle{2}}{\AssertMathStyle{3}} \luatexcrampeddisplaystyle \frac{\AssertMathStyle{3}}{\AssertMathStyle{3}} \textstyle \frac{\AssertMathStyle{4}}{\AssertMathStyle{5}} \luatexcrampedtextstyle \frac{\AssertMathStyle{5}}{\AssertMathStyle{5}} \scriptstyle \frac{\AssertMathStyle{6}}{\AssertMathStyle{7}} \luatexcrampedscriptstyle \frac{\AssertMathStyle{7}}{\AssertMathStyle{7}} \end{math} \end{document} % % \end{macrocode} % % % \subsection{\pkg{amsmath}, \pkg{amsopn}, and \pkg{mathtools}} % % \changes{v1.1}{2012/10/13}{Add fix and unit test for \pkg{amsopn}} % Since \pkg{mathtools} loads \pkg{amsmath} and \pkg{amsopn} anyway, we test % all three in one file. % \begin{macro}{\testbox} % First a scratch box register. % \begin{macrocode} %<*test-amsmath> \usepackage{lualatex-math} \newsavebox{\testbox} % \end{macrocode} % \end{macro} % We set the mathematical code for the minus sign to some arbitrary Unicode % value to test whether the load-time patch works. % \begin{macrocode} \luatexUmathcode`\-="2 "33 "44444 \relax \usepackage{amsmath} \AssertIntEqual{\luatexUmathcode`\-}{"33444444} \makeatletter \AssertIntEqual{\std@minus}{"33444444} \makeatother % \end{macrocode} % Check that we can still declare operators. % \begin{macrocode} \DeclareMathOperator{\Operator}{*-/'a-b} \DeclareMathOperator*{\OperatorWithLimits}{01'*-/} \DeclareMathOperator{\OperatorWithPunctuation}{a:b*/'-.} \usepackage{mathtools} % \end{macrocode} % The same for the document begin hook. % \begin{macrocode} \luatexUmathcode`\="5 "66 "77777 \relax \begin{document} \AssertIntEqual{\luatexUmathcode`\=}{"66A77777} \makeatletter \AssertIntEqual{\std@equal}{"66A77777} \makeatother % \end{macrocode} % Here we test whether the strut box has the correct height and depth. % \begin{macrocode} \sbox{\testbox}{$($} % ) \makeatletter \AssertDimEqual{\ht\Mathstrutbox@}{\ht\testbox} \AssertDimEqual{\dp\Mathstrutbox@}{\dp\testbox} \makeatother % \end{macrocode} % Here we test for the various \pkg{amsmath} features that have to be patched: % sub-arrays and various kind of fraction-like objects. The \cmd{\substack} % command and \env{subarray} environment aren’t really tested since it is hard % to check whether the outcome looks right in an automated way. All tests are % done in both inline and display mode. % \begin{macrocode} \begin{equation*} \AssertMathStyle{0} \sqrt{\AssertMathStyle{1}} \sum_{ \substack{\frac12 \\ \frac34 \\ \frac56} } \sum_{ \begin{subarray}{l} \frac12 \\ \frac34 \\ \frac56 \end{subarray} } \frac{\AssertMathStyle{2}}{\AssertMathStyle{3}} a^{\frac{\AssertMathStyle{6}}{\AssertMathStyle{7}}} \dfrac{\AssertMathStyle{2}}{\AssertMathStyle{3}} \tfrac{\AssertMathStyle{4}}{\AssertMathStyle{5}} \binom{\AssertMathStyle{2}}{\AssertMathStyle{3}} a^{\binom{\AssertMathStyle{6}}{\AssertMathStyle{7}}} \dbinom{\AssertMathStyle{2}}{\AssertMathStyle{3}} \tbinom{\AssertMathStyle{4}}{\AssertMathStyle{5}} \genfrac{}{}{}{}{\AssertMathStyle{2}}{\AssertMathStyle{3}} \genfrac{<}{/}{0pt}{0}{\AssertMathStyle{2}}{\AssertMathStyle{3}} \genfrac{}{}{}{1}{\AssertMathStyle{4}}{\AssertMathStyle{5}} \genfrac{|}{]}{4pt}{2}{\AssertMathStyle{6}}{\AssertMathStyle{7}} \genfrac{}{}{}{3}{\AssertMathStyle{6}}{\AssertMathStyle{7}} \end{equation*} \begin{math} \AssertMathStyle{2} \sqrt{\AssertMathStyle{3}} \sum_{ \substack{\frac12 \\ \frac34 \\ \frac56} } \sum_{ \begin{subarray}{l} \frac12 \\ \frac34 \\ \frac56 \end{subarray} } \frac{\AssertMathStyle{4}}{\AssertMathStyle{5}} a^{\frac{\AssertMathStyle{6}}{\AssertMathStyle{7}}} \dfrac{\AssertMathStyle{2}}{\AssertMathStyle{3}} \tfrac{\AssertMathStyle{4}}{\AssertMathStyle{5}} \binom{\AssertMathStyle{4}}{\AssertMathStyle{5}} a^{\binom{\AssertMathStyle{6}}{\AssertMathStyle{7}}} \dbinom{\AssertMathStyle{2}}{\AssertMathStyle{3}} \tbinom{\AssertMathStyle{4}}{\AssertMathStyle{5}} \genfrac{}{}{}{}{\AssertMathStyle{4}}{\AssertMathStyle{5}} \genfrac{<}{/}{0pt}{0}{\AssertMathStyle{2}}{\AssertMathStyle{3}} \genfrac{}{}{}{1}{\AssertMathStyle{4}}{\AssertMathStyle{5}} \genfrac{|}{]}{4pt}{2}{\AssertMathStyle{6}}{\AssertMathStyle{7}} \genfrac{}{}{}{3}{\AssertMathStyle{6}}{\AssertMathStyle{7}} \end{math} % \end{macrocode} % Since \pkg{mathtools}’ \cmd{\cramped} command uses \cmd{\mathchoice}, we % cannot test for a single mathematical style since all of them are executed; % instead, we just verify that all styles encountered are cramped. % \begin{macrocode} \begin{equation*} \AssertMathStyle{0} a^{\AssertMathStyle{4} a} \cramped{\AssertCrampedStyle a^{\AssertCrampedStyle a}} a^{ \AssertMathStyle{4} a^a \cramped{\AssertCrampedStyle a^{\AssertCrampedStyle a}} a^a \AssertMathStyle{4} } a^{ a^{ \AssertMathStyle{6} a^a \cramped{\AssertCrampedStyle a^{\AssertCrampedStyle a}} a^a \AssertMathStyle{6} } } a^{\AssertMathStyle{4} a} \AssertMathStyle{0} \end{equation*} \begin{math} \AssertMathStyle{2} a^{\AssertMathStyle{4} a} \cramped{\AssertCrampedStyle a^{\AssertCrampedStyle a}} a^{ \AssertMathStyle{4} a^a \cramped{\AssertCrampedStyle a^{\AssertCrampedStyle a}} a^a \AssertMathStyle{4} } a^{ a^{ \AssertMathStyle{6} a^a \cramped{\AssertCrampedStyle a^{\AssertCrampedStyle a}} a^a \AssertMathStyle{6} } } a^{\AssertMathStyle{4} a} \AssertMathStyle{2} \end{math} % \end{macrocode} % The \pkg{amsopn} package uses |\mathcode| when executing a user-defined % operator command. Test that this was patched out. % \begin{macrocode} \AssertNoSpace*{$\Operator$} \AssertNoSpace*{$\OperatorWithLimits$} \AssertMuSpace{$\OperatorWithPunctuation$}{\thinmuskip} \mathcode`\-=45 \relax \AssertNoSpace*{$\Operator$} \AssertNoSpace*{$\OperatorWithLimits$} \AssertMuSpace{$\OperatorWithPunctuation$}{\thinmuskip} \end{document} % % \end{macrocode} % % % \subsection{\pkg{unicode-math}} % % This test file loads both \pkg{amsmath} and \pkg{unicode-math}. The latter % package contains fixes that somewhat overlap with ours. We have to take care % in all packages that no attempt is made to patch a single macro twice. % Therefore we treat warnings (that occur when trying to patch a macro with an % unknown meaning) as errors here. However, the auxiliary package % \pkg{fontspec-patches} uses \cmd{\RenewDocumentCommand} from the \pkg{xparse} % package, which generates a warning that we don't want to turn into an error. % Therefore we treat the offending message \msg{redefine-command} specially. % \changes{v0.3c}{2012/08/23}{Added special treatment for % \msg{redefine-command} warning} % % \begin{macrocode} %<*test-unicode> \ExplSyntaxOn \msg_redirect_class:nn { warning } { error } \msg_redirect_name:nnn { LaTeX } { xparse / redefine-command } { info } \ExplSyntaxOff \usepackage{amsmath} \usepackage{unicode-math}[2011/05/05] \setmathfont{XITS Math} \usepackage{lualatex-math} \begin{document} \begin{equation*} \AssertMathStyle{0} \sqrt{\AssertMathStyle{1}} \frac{\AssertMathStyle{2}}{\AssertMathStyle{3}} a^{\frac{\AssertMathStyle{6}}{\AssertMathStyle{7}}} \dfrac{\AssertMathStyle{2}}{\AssertMathStyle{3}} \tfrac{\AssertMathStyle{4}}{\AssertMathStyle{5}} \end{equation*} \end{document} % % \end{macrocode} % % % \subsection{\pkg{icomma} without \pkg{unicode-math}} % % \changes{v0.2}{2011/07/03}{Added test file for \pkg{icomma} without % \pkg{unicode-math}} % This test file loads only \pkg{icomma} to test whether our patch works for % Computer Modern. % % \begin{macrocode} %<*test-icomma> \usepackage{lualatex-math} \usepackage{icomma} \begin{document} $1,234 \; (x, y)$ \AssertNoSpace{$1,234$} \AssertMuSpace{$(x, y)$}{\thinmuskip} \AssertIntEqual{\mathcomma}{"1C0003B} \end{document} % % \end{macrocode} % % % \subsection{\pkg{icomma} with \pkg{unicode-math}} % % \changes{v0.2}{2011/07/03}{Added test file for \pkg{icomma} with % \pkg{unicode-math}} % This test file loads both \pkg{icomma} and \pkg{unicode-math} to test whether % they interact well. % % \begin{macrocode} %<*test-icomma-unicode> \usepackage{unicode-math}[2011/05/05] \setmathfont{XITS Math} \usepackage{lualatex-math} \usepackage{icomma} \begin{document} $1,234 \; (x, y)$ \AssertNoSpace{$1,234$} \AssertMuSpace{$(x, y)$}{\thinmuskip} \AssertIntEqual{\mathcomma}{"0C0002C} \end{document} % % \end{macrocode} % % \Finale \endinput % Local Variables: % mode: doctex % coding: utf-8-unix % TeX-engine: luatex % End: