# -*- mode: Perl -*-
# /=====================================================================\ #
# | mathtools | #
# | Implementation for LaTeXML | #
# |=====================================================================| #
# | Part of LaTeXML: | #
# | Public domain software, produced as part of work done by the | #
# | United States Government & not subject to copyright in the US. | #
# |---------------------------------------------------------------------| #
# | Thanks to Kim Philipp Jablonski <kpjkpjkpjkpjkpjkpj@gmail.com> | #
# | of the arXMLiv group for initial implementation | #
# | http://arxmliv.kwarc.info/ | #
# | Released to the Public Domain | #
# |---------------------------------------------------------------------| #
# | Bruce Miller <bruce.miller@nist.gov> #_# | #
# | http://dlmf.nist.gov/LaTeXML/ (o o) | #
# \=========================================================ooo==U==ooo=/ #
package LaTeXML::Package::Pool;
use strict;
use warnings;
use LaTeXML::Package;
######################################################
###########
# Options #
###########
# handle special mathtools options
foreach my $option (qw(fixamsmath donotfixamsmathbugs allowspaces disallowspaces)) {
DeclareOption($option, undef); }
# pass all other options to amsmath
DeclareOption(undef, sub {
PassOptions('amsmath', 'sty', ToString(Expand(T_CS('\CurrentOption')))); });
ProcessOptions();
RequirePackage('keyval');
RequirePackage('calc');
# TODO: add support for mhsetup
#RequirePackage('mhsetup');
RequirePackage('amsmath', withoptions => 1);
AtBeginDocument(sub {
RequirePackage('graphicx'); });
##########
# Macros #
##########
# 3
# Note: showonlyrefs doesn't work, as it requires a 2-pass process
DefPrimitive('\mathtoolsset RequiredKeyVals', sub {
my ($stomach, $keyval) = @_;
my @pairs = $keyval->getPairs();
while (@pairs) {
my ($key, $val) = (shift(@pairs), shift(@pairs));
$val = $val ? ToString($val) : '\@mt@true';
DefMacro('\csname @mt@mathtoolsset@' . $key . '\endcsname', $val); }
return; });
#Lookup function for mathtoolsset
DefMacro('\@mt@getmtoption{}', '\ifcsname @mt@mathtoolsset@#1\endcsname'
. '\expandafter\let\expandafter\@mt@currentvalue\csname @mt@mathtoolsset@#1\endcsname\else'
. '\let\@mt@currentvalue\relax\fi'
. '\@mt@currentvalue');
## 3.1
# ?
DefConstructor('\mathmbox {}', "#1");
# NOTE: Optional argument is one of \displaystyle,... !!!
DefConstructor('\mathllap [] {}',
"<ltx:XMArg width='0pt' xoffset='#xoffset'>#2</ltx:XMArg>",
afterDigest => sub {
my ($stomach, $whatsit) = @_;
$whatsit->setProperty(xoffset => $whatsit->getWidth->negate);
$whatsit->setWidth(Dimension(0));
return; });
DefConstructor('\mathrlap [] {}',
"<ltx:XMArg width='0pt' xoffset='#xoffset'>#2</ltx:XMArg>",
afterDigest => sub {
my ($stomach, $whatsit) = @_;
$whatsit->setWidth(Dimension(0));
return; });
DefConstructor('\mathclap [] {}',
"<ltx:XMArg width='0pt' xoffset='#xoffset'>#2</ltx:XMArg>",
afterDigest => sub {
my ($stomach, $whatsit) = @_;
$whatsit->setProperty(xoffset => $whatsit->getWidth->multiply(-0.5));
$whatsit->setWidth(Dimension(0));
return; });
# ? related ?
DefConstructor('\clap {}', "#1");
DefConstructor('\mathmakebox [] [] {}', "#3");
# Ignoring cramped, for now?
DefConstructor('\cramped [] {}', "#2");
# Same as \mathllap, etc (but also cramped!)
Let('\crampedllap', '\mathllap');
Let('\crampedrlap', '\mathrlap');
Let('\crampedclap', '\mathclap');
# another variant?
# This has to destructure the argument to recognize where operators and scripts are!!!
# Can be: \mathop? op \limits? suborsup suporsub?
# where suborsup and suporsub would be _{arg} or ^{arg}
#DefConstructor('\smashoperator [] {}', "#2");
DefMacro('\smashoperator [] {}', '\lx@smashoperator{#1}#2{}{}{}{}{}{}\end');
DefMacro('\lx@smashoperator{} {}{}{}{}{}{} Until:\end', sub {
my ($gullet, $align, @body) = @_;
my ($op, $sub, $sup);
# in equality tests, note that the args are Tokens(), not single Token's!
if (@body && $body[0]->equals(Tokens(T_CS('\mathop')))) {
$op = Tokens(shift(@body), shift(@body)); }
else {
$op = shift(@body); }
if (@body && $body[0]->equals(Tokens(T_CS('\limits')))) {
shift(@body); }
while (@body && $body[0]->unlist) {
my $script = shift(@body);
if ($script->equals(Tokens(T_SUB)) && !$sub) {
$sub = shift(@body); }
elsif ($script->equals(Tokens(T_SUPER)) && !$sup) {
$sup = shift(@body); }
else {
Error('expected', 'sub-superscripts', "\\smashoperator expected sub and superscripts",
"Got " . ToString($script)); } }
Invocation(T_CS('\lx@@smashoperator'), $align, $op, $sub || Tokens(), $sup || Tokens())->unlist; });
DefConstructor('\lx@@smashoperator{}{} InScriptStyle InScriptStyle',
"<ltx:XMApp role='SUMOP' rpadding='#rpadding' lpadding='#lpadding' scriptpos='mid'>"
. "?#sub(<ltx:XMTok role='SUBSCRIPTOP' scriptpos='#scriptpos'/>)()"
. "?#both(<ltx:XMApp scriptpos='mid'>)()"
. "?#sup(<ltx:XMTok role='SUPERSCRIPTOP' scriptpos='#scriptpos'/>)()"
. "#2"
. "?#sup(<ltx:XMArg rule='Superscript' width='#supwidth' xoffset='#supoffset'>#sup</ltx:XMArg>)()"
. "?#both(</ltx:XMApp>)()"
. "?#sub(<ltx:XMArg rule='Subscript' width='#subwidth' xoffset='#suboffset'>#sub</ltx:XMArg>)()"
. "</ltx:XMApp>",
afterDigest => sub {
my ($stomach, $whatsit) = @_;
my ($align, $op, $sub, $sup) = $whatsit->getArgs;
my $opw = $op->getWidth;
my $width = $opw;
$align = ToString($align) || 'lr';
my ($hadsub, $hadsup);
if (!IsEmpty($sub)) {
my $w = $sub->getWidth;
$whatsit->setProperties(sub => $sub,
suboffset => $w->multiply(-0.5),
subwidth => Dimension(0));
$sub->setWidth(Dimension(0));
$width = $width->larger($w);
$hadsub = 1; }
if (!IsEmpty($sup)) {
my $w = $sup->getWidth;
$whatsit->setProperties(sup => $sup,
supoffset => $w->multiply(-0.5),
supwidth => Dimension(0));
$sup->setWidth(Dimension(0));
$width = $width->larger($w);
$hadsup = 1 }
my $pad = $width->subtract($opw)->multiply(0.5);
if ($pad->valueOf > 0) {
if ($align eq 'l') {
$whatsit->setProperties(rpadding => $pad); }
elsif ($align eq 'r') {
$whatsit->setProperties(lpadding => $pad); } }
$whatsit->setProperties(both => ($hadsub && $hadsup),
scriptpos => "mid" . $stomach->getScriptLevel);
return; },
reversion => '\smashoperator[#1]{#2_{#3}^{#4}}'); # not exactly, but...
# \adjustlimits \lim_{somelimit} \lim_{somelimit}
DefConstructor('\adjustlimits {} DefToken InScriptStyle {} DefToken InScriptStyle',
"<ltx:XMApp>"
. "<ltx:XMTok role='SUBSCRIPTOP' scriptpos='mid'/>"
. "<ltx:XMArg depth='#limdepth'>#1</ltx:XMArg>"
. "<ltx:XMArg height='#subheight'>#3</ltx:XMArg>"
. "</ltx:XMApp>"
. "<ltx:XMApp>"
. "<ltx:XMTok role='SUBSCRIPTOP' scriptpos='mid'/>"
. "<ltx:XMArg depth='#limdepth'>#4</ltx:XMArg>"
. "<ltx:XMArg height='#subheight'>#6</ltx:XMArg>"
. "</ltx:XMApp>",
afterDigest => sub {
my ($lim1, $op1, $sub1, $lim2, $op2, $sub2) = $_[1]->getArgs;
my $d1 = $lim1->getDepth;
my $d2 = $lim2->getDepth;
my $h1 = $sub1->getHeight;
my $h2 = $sub2->getHeight;
$_[1]->setProperties(limdepth => ($d1->valueOf > $d2->valueOf ? $d1 : $d2),
subheight => ($h1->valueOf > $h2->valueOf ? $h1 : $h2));
return; });
DefConstructor('\SwapAboveDisplaySkip', "");
## 3.2
sub defMTTagForm {
my ($stomach, $name, $style, $open, $close) = @_;
my $cs = T_CS('\@MTStag@' . $name);
DefMacroI('\fnum@equation@MT@' . $name, undef,
Tokens(T_BEGIN, $open->unlist,
($style ? $style->unlist : ()), T_BEGIN,
T_CS('\theequation'),
T_END, $close->unlist, T_END),
scope => 'global');
DefMacroI('\ref@equation@MT@' . $name, 'Semiverbatim',
Tokens(T_BEGIN, $open->unlist,
($style ? $style->unlist : ()), T_BEGIN,
#T_OTHER('*'),
T_CS('\ref'), T_BEGIN, T_PARAM, T_OTHER(1), T_END,
T_END, $close->unlist, T_END),
scope => 'global');
DefMacroI($cs, undef, sub {
DefMacroI('\fnum@equation', undef, T_CS('\fnum@equation@MT@' . $name), scope => 'global');
DefMacroI('\eqref', 'Semiverbatim',
Tokens(T_CS('\ref@equation@MT@' . $name), T_BEGIN, T_PARAM, T_OTHER(1), T_END),
scope => 'global'); });
return; }
DefPrimitive('\newtagform {} [] {} {}', sub {
my ($stomach, $name, $style, $open, $close) = @_;
$name = ToString($name);
my $cs = T_CS('\@MTStag@' . $name);
if (!isDefinable($cs)) {
Error('ignore', $cs, $stomach,
"Ignoring redefinition (\\newtagform) of '$name'");
return; }
defMTTagForm($stomach, $name, $style, $open, $close); });
DefPrimitive('\renewtagform {} [] {} {}', sub {
my ($stomach, $name, $style, $open, $close) = @_;
defMTTagForm($stomach, ToString($name), $style, $open, $close); });
DefMacro('\usetagform {}', '\csname @MTStag@#1\endcsname');
RawTeX('\newtagform{default}{(}{)}');
Let('\refeq', '\ref');
DefMacro('\noeqref{}', '');
## 3.3
DefConstructor('\xleftrightarrow[]{}', # not centered anymore
"?#1("
. "<ltx:XMApp role='METARELOP'>"
. "<ltx:XMWrap role='UNDERACCENT'>#1</ltx:XMWrap>"
. "<ltx:XMApp>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xleftrightarrow' role='METARELOP' stretchy='true'>\x{2194}</ltx:XMTok>"
. "</ltx:XMApp>"
. "</ltx:XMApp>"
. ")("
. "<ltx:XMApp role='METARELOP'>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xleftrightarrow' role='METARELOP'>\x{2194}</ltx:XMTok>"
. "</ltx:XMApp>"
. ")",
properties => { font => sub { LookupValue('font')->specialize("\x{2026}"); } });
DefConstructor('\xLeftarrow[]{}',
"?#1("
. "<ltx:XMApp role='ARROW'>"
. "<ltx:XMWrap role='UNDERACCENT'>#1</ltx:XMWrap>"
. "<ltx:XMApp>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xLeftarrow' role='ARROW' stretchy='true'>\x{21D0}</ltx:XMTok>"
. "</ltx:XMApp>"
. "</ltx:XMApp>"
. ")("
. "<ltx:XMApp role='ARROW'>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xLeftarrow' role='ARROW'>\x{21D0}</ltx:XMTok>"
. "</ltx:XMApp>"
. ")",
properties => { font => sub { LookupValue('font')->specialize("\x{2026}"); } });
DefConstructor('\xRightarrow[]{}',
"?#1("
. "<ltx:XMApp role='ARROW'>"
. "<ltx:XMWrap role='UNDERACCENT'>#1</ltx:XMWrap>"
. "<ltx:XMApp>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xRightarrow' role='ARROW' stretchy='true'>\x{21D2}</ltx:XMTok>"
. "</ltx:XMApp>"
. "</ltx:XMApp>"
. ")("
. "<ltx:XMApp role='ARROW'>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xRightarrow' role='ARROW'>\x{21D2}</ltx:XMTok>"
. "</ltx:XMApp>"
. ")",
properties => { font => sub { LookupValue('font')->specialize("\x{2026}"); } });
DefConstructor('\xLeftrightarrow[]{}', # looks stretched
"?#1("
. "<ltx:XMApp role='ARROW'>"
. "<ltx:XMWrap role='UNDERACCENT'>#1</ltx:XMWrap>"
. "<ltx:XMApp>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xLeftrightarrow' role='ARROW' stretchy='true'>\x{21D4}</ltx:XMTok>"
. "</ltx:XMApp>"
. "</ltx:XMApp>"
. ")("
. "<ltx:XMApp role='ARROW'>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xLeftrightarrow' role='ARROW'>\x{21D4}</ltx:XMTok>"
. "</ltx:XMApp>"
. ")",
properties => { font => sub { LookupValue('font')->specialize("\x{2026}"); } });
DefConstructor('\xhookleftarrow[]{}',
"?#1("
. "<ltx:XMApp role='ARROW'>"
. "<ltx:XMWrap role='UNDERACCENT'>#1</ltx:XMWrap>"
. "<ltx:XMApp>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xhookleftarrow' role='ARROW' stretchy='true'>\x{21A9}</ltx:XMTok>"
. "</ltx:XMApp>"
. "</ltx:XMApp>"
. ")("
. "<ltx:XMApp role='ARROW'>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xhookleftarrow' role='ARROW'>\x{21A9}</ltx:XMTok>"
. "</ltx:XMApp>"
. ")",
properties => { font => sub { LookupValue('font')->specialize("\x{2026}"); } });
DefConstructor('\xhookrightarrow[]{}',
"?#1("
. "<ltx:XMApp role='ARROW'>"
. "<ltx:XMWrap role='UNDERACCENT'>#1</ltx:XMWrap>"
. "<ltx:XMApp>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xhookrightarrow' role='ARROW' stretchy='true'>\x{21AA}</ltx:XMTok>"
. "</ltx:XMApp>"
. "</ltx:XMApp>"
. ")("
. "<ltx:XMApp role='ARROW'>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xhookrightarrow' role='ARROW'>\x{21AA}</ltx:XMTok>"
. "</ltx:XMApp>"
. ")",
properties => { font => sub { LookupValue('font')->specialize("\x{2026}"); } });
DefConstructor('\xmapsto[]{}',
"?#1("
. "<ltx:XMApp role='ARROW'>"
. "<ltx:XMWrap role='UNDERACCENT'>#1</ltx:XMWrap>"
. "<ltx:XMApp>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xmapsto' role='ARROW' stretchy='true'>\x{21A6}</ltx:XMTok>"
. "</ltx:XMApp>"
. "</ltx:XMApp>"
. ")("
. "<ltx:XMApp role='ARROW'>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xmapsto' role='ARROW'>\x{21A6}</ltx:XMTok>"
. "</ltx:XMApp>"
. ")",
properties => { font => sub { LookupValue('font')->specialize("\x{2026}"); } });
DefConstructor('\xrightharpoondown[]{}',
"?#1("
. "<ltx:XMApp role='ARROW'>"
. "<ltx:XMWrap role='UNDERACCENT'>#1</ltx:XMWrap>"
. "<ltx:XMApp>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xrightharpoondown' role='ARROW' stretchy='true'>\x{21C1}</ltx:XMTok>"
. "</ltx:XMApp>"
. "</ltx:XMApp>"
. ")("
. "<ltx:XMApp role='ARROW'>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xrightharpoondown' role='ARROW' stretchy='true'>\x{21C1}</ltx:XMTok>"
. "</ltx:XMApp>"
. ")",
properties => { font => sub { LookupValue('font')->specialize("\x{2026}"); } });
DefConstructor('\xrightharpoonup[]{}',
"?#1("
. "<ltx:XMApp role='ARROW'>"
. "<ltx:XMWrap role='UNDERACCENT'>#1</ltx:XMWrap>"
. "<ltx:XMApp>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xrightharpoonup' role='ARROW' stretchy='true'>\x{21C0}</ltx:XMTok>"
. "</ltx:XMApp>"
. "</ltx:XMApp>"
. ")("
. "<ltx:XMApp role='ARROW'>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xrightharpoonup' role='ARROW'>\x{21C0}</ltx:XMTok>"
. "</ltx:XMApp>"
. ")",
properties => { font => sub { LookupValue('font')->specialize("\x{2026}"); } });
DefConstructor('\xleftharpoondown[]{}',
"?#1("
. "<ltx:XMApp role='ARROW'>"
. "<ltx:XMWrap role='UNDERACCENT'>#1</ltx:XMWrap>"
. "<ltx:XMApp>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xleftharpoondown' role='ARROW' stretchy='true'>\x{21BD}</ltx:XMTok>"
. "</ltx:XMApp>"
. "</ltx:XMApp>"
. ")("
. "<ltx:XMApp role='ARROW'>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xlefttharpoondown' role='ARROW'>\x{21BD}</ltx:XMTok>"
. "</ltx:XMApp>"
. ")",
properties => { font => sub { LookupValue('font')->specialize("\x{2026}"); } });
DefConstructor('\xleftharpoonup[]{}',
"?#1("
. "<ltx:XMApp role='ARROW'>"
. "<ltx:XMWrap role='UNDERACCENT'>#1</ltx:XMWrap>"
. "<ltx:XMApp>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xleftharpoonup' role='ARROW' stretchy='true'>\x{21BC}</ltx:XMTok>"
. "</ltx:XMApp>"
. "</ltx:XMApp>"
. ")("
. "<ltx:XMApp role='ARROW'>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xleftharpoonup' role='ARROW'>\x{21BC}</ltx:XMTok>"
. "</ltx:XMApp>"
. ")",
properties => { font => sub { LookupValue('font')->specialize("\x{2026}"); } });
DefConstructor('\xrightleftharpoons[]{}',
"?#1("
. "<ltx:XMApp role='METARELOP'>"
. "<ltx:XMWrap role='UNDERACCENT'>#1</ltx:XMWrap>"
. "<ltx:XMApp>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xrightleftharpoons' role='METARELOP' stretchy='true'>\x{21CC}</ltx:XMTok>"
. "</ltx:XMApp>"
. "</ltx:XMApp>"
. ")("
. "<ltx:XMApp role='METARELOP'>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xrightleftharpoons' role='METARELOP'>\x{21CC}</ltx:XMTok>"
. "</ltx:XMApp>"
. ")",
properties => { font => sub { LookupValue('font')->specialize("\x{2026}"); } });
DefConstructor('\xleftrightharpoons[]{}',
"?#1("
. "<ltx:XMApp role='METARELOP'>"
. "<ltx:XMWrap role='UNDERACCENT'>#1</ltx:XMWrap>"
. "<ltx:XMApp>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xleftrightharpoons' role='METARELOP' stretchy='true'>\x{21CB}</ltx:XMTok>"
. "</ltx:XMApp>"
. "</ltx:XMApp>"
. ")("
. "<ltx:XMApp role='METARELOP'>"
. "<ltx:XMWrap role='OVERACCENT'>#2</ltx:XMWrap>"
. "<ltx:XMTok name='xleftrightharpoons' role='METARELOP'>\x{21CB}</ltx:XMTok>"
. "</ltx:XMApp>"
. ")",
properties => { font => sub { LookupValue('font')->specialize("\x{2026}"); } });
# args: [<rule thickness>] [<bracket height>] {<arg>}
DefMacro('\overbracket [] [] {}', '\lx@mt@overbracket {#3}');
DefMacro('\underbracket [] [] {}', '\lx@mt@underbracket {#3}');
DefMath('\lx@mt@overbracket {}', "\x{FE47}", operator_role => 'OVERACCENT', scriptpos => 'mid',
alias => '\overbracket');
DefMath('\lx@mt@underbracket {}', "\x{FE48}", operator_role => 'UNDERACCENT', scriptpos => 'mid',
alias => '\underbracket');
Let('\LaTeXunderbrace', '\underbrace');
Let('\LaTeXoverbrace', '\overbrace');
## 3.4
# starred normal matrices
DefMacro('\csname matrix*\endcsname []', '\lx@ams@matrix{name=matrix,datameaning=matrix,alignment=#1}');
DefMacro('\csname endmatrix*\endcsname', '\lx@end@ams@matrix');
DefMacro('\csname pmatrix*\endcsname []', '\lx@ams@matrix{name=pmatrix,datameaning=matrix,alignment=#1,left=\@left(,right=\@right)}');
DefMacro('\csname endpmatrix*\endcsname', '\lx@end@ams@matrix');
DefMacro('\csname bmatrix*\endcsname []', '\lx@ams@matrix{name=bmatrix,datameaning=matrix,alignment=#1,left=\@left[,right=\@right]}');
DefMacro('\csname endbmatrix*\endcsname', '\lx@end@ams@matrix');
DefMacro('\csname Bmatrix*\endcsname []', '\lx@ams@matrix{name=Bmatrix,datameaning=matrix,alignment=#1,left=\@left\{,right=\@right\}}');
DefMacro('\csname endBmatrix*\endcsname', '\lx@end@ams@matrix');
DefMacro('\csname vmatrix*\endcsname []', '\lx@ams@matrix{name=vmatrix,delimitermeaning=determinant,datameaning=matrix,alignment=#1,left=\@left|,right=\@right|}');
DefMacro('\csname endvmatrix*\endcsname', '\lx@end@ams@matrix');
DefMacro('\csname Vmatrix*\endcsname []', '\lx@ams@matrix{name=Vmatrix,delimitermeaning=norm,datameaning=matrix,alignment=#1,left=\@left\|,right=\@right\|}');
DefMacro('\csname endVmatrix*\endcsname', '\lx@end@ams@matrix');
# starred small matrices
DefMacroI('\@smallmatrix@star@tmp', 'RequiredKeyVals', sub {
my ($gullet, $kv) = @_;
my %dict = $kv->getPairs;
my $alignment = (defined $dict{alignment}) ? ToString($dict{alignment}) : ToString(Digest('\@mt@getmtoption{smallmatrix-align}'));
my $delimitermeaning = (defined $dict{delimitermeaning}) ? ToString($dict{delimitermeaning}) : '';
my $left = (defined $dict{left}) ? ToString($dict{left}) : '';
my $right = (defined $dict{right}) ? ToString($dict{right}) : '';
return TokenizeInternal('\lx@ams@matrix{'
. 'name=' . ToString($dict{name}) . ','
. 'datameaning=' . ToString($dict{datameaning}) . ','
. 'style=' . ToString($dict{style})
. (($delimitermeaning ne '') ? (',delimitermeaning=' . $delimitermeaning) : '')
. (($left ne '') ? (',left=' . $left) : '')
. (($right ne '') ? (',right=' . $right) : '')
. (($alignment ne '') ? (',alignment=' . $alignment) : '')
. '}'); });
DefMacro('\csname smallmatrix*\endcsname []', '\@smallmatrix@star@tmp{name=matrix,datameaning=matrix,style=\scriptsize,\ifx/#1/\else alignment=#1,\fi}');
DefMacro('\csname endsmallmatrix*\endcsname', '\lx@end@ams@matrix');
DefMacro('\csname psmallmatrix*\endcsname []', '\@smallmatrix@star@tmp{name=pmatrix,datameaning=matrix,left=\@left(,right=\@right),style=\scriptsize,\ifx/#1/\else alignment=#1,\fi}');
DefMacro('\csname endpsmallmatrix*\endcsname', '\lx@end@ams@matrix');
DefMacro('\csname bsmallmatrix*\endcsname []', '\@smallmatrix@star@tmp{name=bmatrix,datameaning=matrix,left=\@left[,right=\@right],style=\scriptsize,\ifx/#1/\else alignment=#1,\fi}');
DefMacro('\csname endbsmallmatrix*\endcsname', '\lx@end@ams@matrix');
DefMacro('\csname Bsmallmatrix*\endcsname []', '\@smallmatrix@star@tmp{name=Bmatrix,datameaning=matrix,left=\@left\{,right=\@right\},style=\scriptsize,\ifx/#1/\else alignment=#1,\fi}');
DefMacro('\csname endBsmallmatrix*\endcsname', '\lx@end@ams@matrix');
DefMacro('\csname vsmallmatrix*\endcsname []', '\@smallmatrix@star@tmp{name=vmatrix,delimitermeaning=determinant,datameaning=matrix,left=\@left|,right=\@right|,style=\scriptsize,\ifx/#1/\else alignment=#1,\fi}');
DefMacro('\csname endvsmallmatrix*\endcsname', '\lx@end@ams@matrix');
DefMacro('\csname Vsmallmatrix*\endcsname []', '\@smallmatrix@star@tmp{name=Vmatrix,delimitermeaning=norm,datameaning=matrix,left=\@left\|,right=\@right\|,style=\scriptsize,\ifx/#1/\else alignment=#1,\fi}');
DefMacro('\csname endVsmallmatrix*\endcsname', '\lx@end@ams@matrix');
# non-starred small matrices
DefMacro('\psmallmatrix', '\lx@ams@matrix{name=pmatrix,datameaning=matrix,left=\@left(,right=\@right),style=\scriptsize}');
DefMacro('\endpsmallmatrix', '\lx@end@ams@matrix');
DefMacro('\bsmallmatrix', '\lx@ams@matrix{name=bmatrix,datameaning=matrix,left=\@left[,right=\@right],style=\scriptsize}');
DefMacro('\endbsmallmatrix', '\lx@end@ams@matrix');
DefMacro('\Bsmallmatrix', '\lx@ams@matrix{name=Bmatrix,datameaning=matrix,left=\@left\{,right=\@right\},style=\scriptsize}');
DefMacro('\endBsmallmatrix', '\lx@end@ams@matrix');
DefMacro('\vsmallmatrix', '\lx@ams@matrix{name=vmatrix,delimitermeaning=determinant,datameaning=matrix,left=\@left|,right=\@right|,style=\scriptsize}');
DefMacro('\endvsmallmatrix', '\lx@end@ams@matrix');
DefMacro('\Vsmallmatrix', '\lx@ams@matrix{name=Vmatrix,delimitermeaning=norm,datameaning=matrix,left=\@left\|,right=\@right\|,style=\scriptsize}');
DefMacro('\endVsmallmatrix', '\lx@end@ams@matrix');
# {multlined}
DefConstructor('\@@multlined DigestedBody',
"#1",
beforeDigest => sub {
AssignValue('MULTIROW_ALIGNMENT_RULE' => { 'default' => 'center', '0' => 'left', '-1' => 'right' });
$_[0]->bgroup; },
afterDigest => sub {
$_[1]->setProperty('MULTIROW_ALIGNMENT_RULE' => LookupValue('MULTIROW_ALIGNMENT_RULE'));
$_[1]->setBody($_[1]->getArg(1)->unlist, undef); },
afterConstruct => sub {
rearrangeAMSMultirow($_[0], $_[1], $_[0]->getNode->lastChild); },
reversion => '\begin{multlined}#1\end{multlined}');
DefMacroI('\@multlined@tmp', 'RequiredKeyVals', sub {
my ($gullet, $kv) = @_;
my %dict = $kv->getPairs;
my $vattach = (defined $dict{vattach}) ? ToString($dict{vattach}) : ToString(Digest('\@mt@getmtoption{multlined-pos}'));
my $width = (defined $dict{width}) ? ToString($dict{width}) : ToString(Digest('\@mt@getmtoption{multlined-width}'));
return TokenizeInternal('\@ams@multirow@bindings{'
. 'name=multlined'
. (($vattach ne '') ? (',vattach=' . $vattach) : '')
. (($width ne '') ? (',width=' . $width) : '')
. '}'); });
DefMacroI('\multlined', '[][]', '\@multlined@tmp{'
. 'name=multlined,'
. '\ifx/#1/\else vattach=#1,\fi'
. '\ifx/#2/\else width=#2,\fi'
. '}\@@multlined\@start@alignment');
DefMacroI('\endmultlined', undef, '\@finish@alignment\@end@multlined');
DefPrimitiveI('\@end@multlined', undef, sub { $_[0]->egroup; });
# Is currently ignore the shoving dimension
DefMacro('\@MT@shove{}', sub {
my ($gullet, $shove_dir) = @_;
my $cur_row_num = LookupValue('Alignment')->currentRowNumber;
my $rowalignment = LookupValue('MULTIROW_ALIGNMENT_RULE');
$$rowalignment{$cur_row_num} = ToString($shove_dir);
return; });
DefMacro('\shoveright[]{}', '\@MT@shove{right}#2');
DefMacro('\shoveleft[]{}', '\@MT@shove{left}#2');
# cases
DefMacro('\dcases',
'\lx@ams@cases{name=dcases,meaning=cases,left=\@left\{,style=\displaystyle,conditionmode=math}');
DefMacro('\enddcases',
'\lx@end@ams@cases');
DefMacro('\csname dcases*\endcsname',
'\lx@ams@cases{name=dcases*,meaning=cases,left=\@left\{,style=\displaystyle,conditionmode=text}');
DefMacro('\csname enddcases*\endcsname',
'\lx@end@ams@cases');
DefMacro('\csname rcases\endcsname',
'\lx@ams@cases{name=rcases,meaning=cases,right=\@right\},style=\textstyle,conditionmode=math}');
DefMacro('\csname endrcases\endcsname',
'\lx@end@ams@cases');
DefMacro('\csname rcases*\endcsname',
'\lx@ams@cases{name=rcases*,meaning=cases,right=\@right\},style=\textstyle,conditionmode=text}');
DefMacro('\csname endrcases*\endcsname',
'\lx@end@ams@cases');
DefMacro('\csname drcases\endcsname',
'\lx@ams@cases{name=drcases,meaning=cases,right=\@right\},style=\displaystyle,conditionmode=math}');
DefMacro('\csname enddrcases\endcsname',
'\lx@end@ams@cases');
DefMacro('\csname drcases*\endcsname',
'\lx@ams@cases{name=drcases*,meaning=cases,right=\@right\},style=\displaystyle,conditionmode=text}');
DefMacro('\csname enddrcases*\endcsname',
'\lx@end@ams@cases');
DefMacro('\csname cases*\endcsname',
'\lx@ams@cases{name=cases*,meaning=cases,left=\@left\{,style=\textstyle,conditionmode=text}');
DefMacro('\csname endcases*\endcsname',
'\lx@end@ams@cases');
# TODO: Make this actually shift the equation
DefMacro('\MoveEqLeft[]', T_ALIGN);
# TODO: Properly implement \Aboxed
DefMacro('\Aboxed{}', '#1');
# TODO: Make this actually do something
DefMacro('\ArrowBetweenLines[]', '');
DefMacro('\csname ArrowBetweenLines*\endcsname[]', '');
DefMacro('\vdotswithin{}', '\mathmakebox[\widthof{\ensuremath{{}#1{}}}][c]{\vdots}');
DefMacro('\shortvdotswithin{}',
'\MTFlushSpaceAbove
& \vdotswithin{#1}
\MTFlushSpaceBelow');
DefMacro('\csname shortvdotswithin*\endcsname{}',
'\MTFlushSpaceAbove
\vdotswithin{#1} &
\MTFlushSpaceBelow');
DefMacro('\MTFlushSpaceAbove', '');
DefMacro('\MTFlushSpaceBelow', '\\\\');
## 3.5
Let('\shortintertext', '\@ams@intertext');
## 3.6
DefPrimitive('\DeclarePairedDelimiter DefToken {}{}', sub {
my ($gullet, $cs, $ldel, $rdel) = @_;
my $cmd = substr(ToString($cs), 1);
DefMacroI('\MT@delim@' . $cmd . '@star@wrapper', '{}{}{}', '#1#2#3');
DefMacroI('\MT@delim@' . $cmd . '@nostar@wrapper', '{}{}{}', '#1#2#3');
DefMacroI('\MT@delim@' . $cmd . '@star', '{}',
'\MT@delim@' . $cmd . '@star@wrapper{\left' . ToString($ldel) . '}{#1}{\right' . ToString($rdel) . '}');
DefMacroI('\MT@delim@' . $cmd . '@nostar', '[]{}',
'\MT@delim@' . $cmd . '@nostar@wrapper{#1' . ToString($ldel) . '}{#2}{#1' . ToString($rdel) . '}');
DefMacroI($cs, undef, '\@ifstar{\MT@delim@' . $cmd . '@star}{\MT@delim@' . $cmd . '@nostar}'); });
DefPrimitive('\DeclarePairedDelimiterX DefToken [] {} {} {}', sub {
my ($stomach, $cs, $nargs, $ldel, $rdel, $body) = @_;
# fix $nargs
$nargs = $nargs->toString if ref $nargs;
$nargs = 0 unless $nargs;
DefMacroI(T_CS(ToString($cs) . '@inner'), join('', map { '{}' } (1 .. $nargs)),
Tokens($body->unlist, T_CS($cs . '@after')));
DefMacroI($cs, 'OptionalMatch:* []', sub {
my ($gullet, $star, $opt) = @_;
return (($star ? T_CS('\left') : ((defined $opt) ? $opt->unlist : ())),
$ldel->unlist,
T_CS('\def'), T_CS('\delimsize'), T_BEGIN, ($opt ? $opt->unlist : ($star ? T_CS('\middle') : ())), T_END,
T_CS('\def'), T_CS($cs . '@after'), T_BEGIN, ($star ? T_CS('\right') : ((defined $opt) ? $opt->unlist : ())), $rdel->unlist, T_END,
T_CS(ToString($cs) . '@inner')); }); });
DefPrimitive('\DeclarePairedDelimiterXPP DefToken [] {} {} {} {} {}', sub {
my ($stomach, $cs, $nargs, $precode, $ldel, $rdel, $postcode, $body) = @_;
# fix $nargs
$nargs = $nargs->toString if ref $nargs;
$nargs = 0 unless $nargs;
DefMacroI(T_CS(ToString($cs) . '@inner'), join('', map { '{}' } (1 .. $nargs)),
Tokens($body->unlist, T_CS($cs . '@after'), $postcode->unlist));
DefMacroI($cs, 'OptionalMatch:* []', sub {
my ($gullet, $star, $opt) = @_;
return ($precode->unlist,
($star ? T_CS('\left') : ((defined $opt) ? $opt->unlist : ())),
$ldel->unlist,
T_CS('\def'), T_CS('\delimsize'), T_BEGIN, ($opt ? $opt->unlist : ($star ? T_CS('\middle') : ())), T_END,
T_CS('\def'), T_CS($cs . '@after'), T_BEGIN, ($star ? T_CS('\right') : ((defined $opt) ? $opt->unlist : ())), $rdel->unlist, T_END,
T_CS(ToString($cs) . '@inner')); }); });
# only applies to macros defined with \DeclarePairedDelimiter
DefPrimitive('\reDeclarePairedDelimiterInnerWrapper DefToken {} {}', sub {
my ($stomach, $cs, $nstar, $body) = @_;
DefMacroI('\MT@delim@' . (substr(ToString($cs), 1)) . '@' . ToString($nstar) . '@wrapper', '{}{}{}', $body); });
## 3.7
DefMath('\lparen', '(', role => 'OPEN', stretchy => 'false');
DefMath('\rparen', ')', role => 'CLOSE', stretchy => 'false');
DefMathI('\vcentcolon', undef, ':', role => 'RELOP');
DefMathI('\ordinarycolon', undef, ':', role => 'RELOP');
DefMath('\dblcolon', "::", role => 'RELOP');
# taken from txfonts.sty.ltxml
DefMath('\coloneqq', "\x{2254}", role => 'RELOP');
DefMath('\Coloneqq', "\x{2A74}", role => 'RELOP');
DefMath('\coloneq', ":-", role => 'RELOP');
DefMath('\Coloneq', "::-", role => 'RELOP');
DefMath('\eqqcolon', "\x{2255}", role => 'RELOP');
DefMath('\Eqqcolon', "=::", role => 'RELOP');
DefMath('\eqcolon', "-:", role => 'RELOP');
DefMath('\Eqcolon', "-::", role => 'RELOP');
DefMath('\colonapprox', ":\x{2248}", role => 'RELOP');
DefMath('\Colonapprox', "::\x{2248}", role => 'RELOP');
DefMath('\colonsim', ":\x{223C}", role => 'RELOP');
DefMath('\Colonsim', "::\x{223C}", role => 'RELOP');
DefMathI('\nuparrow', undef, UTF(0x2909), role => 'ARROW'); # could not find arrow with vertical line
DefMathI('\ndownarrow', undef, UTF(0x2908), role => 'ARROW'); # see above
DefMathI('\bigtimes', undef, UTF(0xD7), role => 'MULOP', meaning => 'times', font => { size => 'Big' }, scriptpos => \&doScriptpos);
# 4
## 4.2
# An XMDual is inappropriate here, since there's no discernable semantics,
# plus it messes up formatting.
# However, can we really guarantee that the parser will see this as prescripts???
DefMacroI('\prescript', '{}{}{}',
'\@ams@prescript{#1}{#2}{#3}{'
. '{}^{\@mt@getmtoption{prescript-sup-format}{#1}}'
. '_{\@mt@getmtoption{prescript-sub-format}{#2}}'
. '{\@mt@getmtoption{prescript-arg-format}{#3}}'
. '}');
# wrapper to get reversion
DefConstructor('\@ams@prescript{}{}{}{}', '#4',
reversion => '\prescript{#1}{#2}{#3}');
## 4.4
DefMacroI('\begin{spreadlines}', '{Dimension}', '\begingroup\jot=#1\relax');
DefMacroI('\end{spreadlines}', undef, '\endgroup');
## 4.5
DefConstructor('\@@lgathered DigestedBody',
"#1",
beforeDigest => sub { $_[0]->bgroup; },
afterDigest => sub {
$_[1]->setProperty('MULTIROW_ALIGNMENT_RULE' => { 'default' => 'left' });
$_[1]->setBody($_[1]->getArg(1)->unlist, undef); },
afterConstruct => sub {
rearrangeAMSMultirow($_[0], $_[1], $_[0]->getNode->lastChild); },
reversion => '\begin{lgathered}#1\end{lgathered}');
DefMacroI('\lgathered', '[]', '\@ams@multirow@bindings{name=lgathered,vattach=#1}\@@lgathered\@start@alignment');
DefMacroI('\endlgathered', undef, '\@finish@alignment\@end@gathered');
DefConstructor('\@@rgathered DigestedBody',
"#1",
beforeDigest => sub { $_[0]->bgroup; },
afterDigest => sub {
$_[1]->setProperty('MULTIROW_ALIGNMENT_RULE' => { 'default' => 'right' });
$_[1]->setBody($_[1]->getArg(1)->unlist, undef); },
afterConstruct => sub {
rearrangeAMSMultirow($_[0], $_[1], $_[0]->getNode->lastChild); },
reversion => '\begin{rgathered}#1\end{rgathered}');
DefMacroI('\rgathered', '[]', '\@ams@multirow@bindings{name=rgathered,vattach=#1}\@@rgathered\@start@alignment');
DefMacroI('\endrgathered', undef, '\@finish@alignment\@end@gathered');
DefConstructor('\@@newgathered@dummy DigestedBody',
'#1',
beforeDigest => sub { $_[0]->bgroup; },
afterConstruct => sub {
my ($document) = @_;
my $array = $document->getNode->lastChild;
# Insert a \qquad between the rows to encourage the parser to see them as separate eqns.
my @stuff = extractXMArrayCells($document, $array);
$document->replaceTree(['ltx:XMDual', {},
['ltx:XMWrap', { rule => 'Anything,' },
createXMRefs($document, @stuff)],
$array],
$array);
return; },
reversion => '\begin{gathered}#1\end{gathered}');
DefMacro('\newgathered{}{}{}{}', sub {
my ($gullet, $name, $pre_line, $post_line, $after) = @_;
$name = ToString($name);
# Really should be making the pre/post_line into separate columns!!!
DefMacroI('\\' . $name, undef,
'\@ams@multirow@bindings{name=' . $name . '}['
. 'before_row=\hidden@noalign{' . UnTeX($pre_line) . '},'
. 'after_row=\hidden@noalign{' . UnTeX($post_line) . '}'
. ']\@@newgathered@dummy\@start@alignment');
DefMacroI('\end' . $name, undef,
Tokens(T_CS('\@finish@alignment'), T_CS('\@end@gathered'), $after)); });
Let('\renewgathered', '\newgathered');
## 4.6
DefMacro('\splitfrac{}{}',
'\@ams@multirow@bindings{name=splitfrac}\@@multlined\@start@alignment #1 \\\\ #2 \@finish@alignment\@end@multline');
DefMacro('\splitdfrac{}{}',
'\displaystyle\@ams@multirow@bindings{name=splitdfrac}\@@multlined\@start@alignment #1 \\\\ #2 \@finish@alignment\@end@multline');
######################################################
1;
# vim: ft=perl: expandtab: