diff --git a/lib/LaTeXML/Common/Font.pm b/lib/LaTeXML/Common/Font.pm
index bf31f6fbb..5a0de3410 100644
--- a/lib/LaTeXML/Common/Font.pm
+++ b/lib/LaTeXML/Common/Font.pm
@@ -22,7 +22,7 @@ use LaTeXML::Common::Font::Metric;
use LaTeXML::Common::Font::StandardMetrics;
use LaTeXML::Common::Color;
use List::Util qw(min max sum);
-use base qw(LaTeXML::Common::Object);
+use base qw(LaTeXML::Common::Object);
# Note that this has evolved way beynond just "font",
# but covers text properties (or even display properties) in general
@@ -263,6 +263,7 @@ sub toString {
# Perhaps it is more useful to list only the non-default components?
sub stringify {
+ no warnings 'recursion';
my ($self) = @_;
my ($fam, $ser, $shp, $siz, $col, $bkg, $opa, $enc, $lang, $mstyle, $flags) = @$self;
$fam = 'serif' if $fam && ($fam eq 'math');
diff --git a/lib/LaTeXML/Common/Object.pm b/lib/LaTeXML/Common/Object.pm
index 5e365789e..8e7f34a00 100644
--- a/lib/LaTeXML/Common/Object.pm
+++ b/lib/LaTeXML/Common/Object.pm
@@ -28,6 +28,7 @@ my %NOBLESS = map { ($_ => 1) } qw( SCALAR HASH ARRAY CODE REF GLOB LVALUE);
# Since the next two are used in debugging and error messages,
# be careful to avoid recursive errors
sub Stringify {
+ no warnings 'recursion';
my ($object) = @_;
my $string = eval {
local $LaTeXML::IGNORE_ERRORS = 1;
diff --git a/lib/LaTeXML/Core/List.pm b/lib/LaTeXML/Core/List.pm
index 9a5c02e9e..34494341f 100644
--- a/lib/LaTeXML/Core/List.pm
+++ b/lib/LaTeXML/Core/List.pm
@@ -73,6 +73,7 @@ sub toString {
# Methods for overloaded operators
sub stringify {
+ no warnings 'recursion';
my ($self) = @_;
my $type = ref $self;
$type =~ s/^LaTeXML:://;
diff --git a/lib/LaTeXML/Core/Parameter.pm b/lib/LaTeXML/Core/Parameter.pm
index 31fbff912..bd05d30e0 100644
--- a/lib/LaTeXML/Core/Parameter.pm
+++ b/lib/LaTeXML/Core/Parameter.pm
@@ -126,6 +126,7 @@ sub reparse {
return $value; }); } }
sub digest {
+ no warnings 'recursion';
my ($self, $stomach, $value, $fordefn) = @_;
# If semiverbatim, Expand (before digest), so tokens can be neutralized; BLECH!!!!
if ($$self{semiverbatim}) {
diff --git a/lib/LaTeXML/Core/Tokens.pm b/lib/LaTeXML/Core/Tokens.pm
index 5fa66ef0c..0d29631b0 100644
--- a/lib/LaTeXML/Core/Tokens.pm
+++ b/lib/LaTeXML/Core/Tokens.pm
@@ -81,6 +81,7 @@ sub stringify {
return "Tokens[" . join(',', map { $_->toString } @$self) . "]"; }
sub beDigested {
+ no warnings 'recursion';
my ($self, $stomach) = @_;
return $stomach->digest($self); }
diff --git a/lib/LaTeXML/Core/Whatsit.pm b/lib/LaTeXML/Core/Whatsit.pm
index 0651c3308..ee21008ed 100644
--- a/lib/LaTeXML/Core/Whatsit.pm
+++ b/lib/LaTeXML/Core/Whatsit.pm
@@ -175,6 +175,7 @@ sub getString {
# Methods for overloaded operators
sub stringify {
+ no warnings 'recursion';
my ($self) = @_;
my $hasbody = defined $$self{properties}{body};
return "Whatsit[" . join(',', $self->getDefinition->getCS->getCSName,
@@ -243,6 +244,7 @@ sub computeSize {
@boxes = $boxes[0]->unlist; } }
else {
push(@boxes, $sizer); }
+ no warnings 'recursion';
return $$props{font}->computeBoxesSize([@boxes], %options); } }
#======================================================================
diff --git a/lib/LaTeXML/Package/IEEEtran.cls.ltxml b/lib/LaTeXML/Package/IEEEtran.cls.ltxml
index 7b62438da..fe42e7da5 100644
--- a/lib/LaTeXML/Package/IEEEtran.cls.ltxml
+++ b/lib/LaTeXML/Package/IEEEtran.cls.ltxml
@@ -180,8 +180,13 @@ RawTeX(<<'EoTeX');
\def\theparagraph{\thesubsubsection\alph{paragraph}} % I-A1a
\fi
EoTeX
-DefMacro('\format@title@font@section', '\sc');
-DefMacro('\format@title@font@subsection', '\it');
+DefPrimitiveI('\ltx@ieeetran@it', undef, undef,
+ font => { shape => 'italic', family => 'serif', series => 'medium' }, locked => 1);
+DefPrimitiveI('\ltx@ieeetran@sc', undef, undef,
+ font => { shape => 'smallcaps', family => 'serif', series => 'medium' }, locked => 1);
+
+DefMacro('\format@title@font@section', '\ltx@ieeetran@sc');
+DefMacro('\format@title@font@subsection', '\ltx@ieeetran@it');
DefMacro('\figurename', 'Fig.');
DefMacro('\tablename', 'TABLE');
DefMacro('\thetable', '\Roman{table}');
@@ -340,23 +345,39 @@ DefMacro('\IEEEiedlabeljustifyc', '');
DefMacro('\IEEEiedlabeljustifyl', '');
DefMacro('\IEEEiedlabeljustifyr', '');
-DefEnvironment('{IEEEitemize}',
+# TODO: Use the optional argument.
+# also, we skip the internal @-named variants for now.
+DefEnvironment('{IEEEitemize}[]',
"#body",
properties => sub { beginItemize('itemize', '@item'); },
beforeDigestEnd => sub { Digest('\par'); },
locked => 1, mode => 'text');
-DefEnvironment('{IEEEenumerate}',
+DefEnvironment('{IEEEenumerate}[]',
"#body",
properties => sub { beginItemize('enumerate', 'enum'); },
beforeDigestEnd => sub { Digest('\par'); },
locked => 1, mode => 'text');
-DefEnvironment('{IEEEdescription}',
+DefEnvironment('{IEEEdescription}[]',
"#body",
beforeDigest => sub { Let('\makelabel', '\descriptionlabel'); },
properties => sub { beginItemize('description', '@desc'); },
beforeDigestEnd => sub { Digest('\par'); },
locked => 1, mode => 'text');
+# override LaTeX's default IED lists
+Let('\itemize', '\IEEEitemize');
+Let('\enditemize', '\endIEEEitemize');
+Let('\enumerate', '\IEEEenumerate');
+Let('\endenumerate', '\endIEEEenumerate');
+Let('\description', '\IEEEdescription');
+Let('\enddescription', '\endIEEEdescription');
+Let(T_CS('\begin{itemize}'), '\IEEEitemize');
+Let(T_CS('\end{itemize}'), '\endIEEEitemize');
+Let(T_CS('\begin{enumerate}'), '\IEEEenumerate');
+Let(T_CS('\end{enumerate}'), '\endIEEEenumerate');
+Let(T_CS('\begin{description}'), '\IEEEdescription');
+Let(T_CS('\end{description}'), '\endIEEEdescription');
+
# V1.7 provide string macros as article.cls does
DefMacro('\contentsname', 'Contents');
DefMacro('\listfigurename', 'List of Figures');
diff --git a/lib/LaTeXML/Package/LaTeX.pool.ltxml b/lib/LaTeXML/Package/LaTeX.pool.ltxml
index e7aba2021..27e8eeb93 100644
--- a/lib/LaTeXML/Package/LaTeX.pool.ltxml
+++ b/lib/LaTeXML/Package/LaTeX.pool.ltxml
@@ -290,7 +290,7 @@ DefConstructor('\lx@newline OptionalMatch:* [Glue]', sub {
$document->insertElement('ltx:XMHint', undef, name => 'newline'); }
else {
my $context = $document->getElement;
- if ($context->getAttribute('_vertical_mode_')) { }
+ if (!$context || $context->getAttribute('_vertical_mode_')) { }
elsif (($document->getNodeQName($context) eq 'ltx:p')
&& $context->parentNode->getAttribute('_vertical_mode_')) {
$document->maybeCloseElement('ltx:p'); }
@@ -1372,7 +1372,7 @@ DefEnvironment('{flushright}', sub {
sub setupAligningContext {
my ($document) = @_;
my $node = $document->getElement;
- AssignValue(ALIGNING_NODE => [$node, $node->lastChild]);
+ AssignValue(ALIGNING_NODE => [$node, $node->lastChild]) if $node;
return; }
sub applyAligningContext {
@@ -4971,6 +4971,8 @@ DefConstructor('\raisebox{Dimension}[Dimension][Dimension]{}',
beforeDigest => sub { reenterTextMode(); },
sizer => sub { raisedSizer($_[0]->getArg(4), $_[0]->getArg(1)); });
+DefMacro('\@finalstrut{}', '\unskip\ifhmode\nobreak\fi\vrule\@width\z@\@height\z@\@depth\dp#1');
+
#**********************************************************************
# C.14 Pictures and Color
#**********************************************************************
@@ -5514,6 +5516,8 @@ DefPrimitiveI('\textregistered', undef, UTF(0xAE)); # REGISTERED SIGN
DefPrimitiveI('\texttrademark', undef, "\x{2122}"); # TRADE MARK SIGN
DefConstructor('\textsuperscript{}', "#1",
mode => 'text');
+DefConstructor('\@textsuperscript{}', "#1",
+ mode => 'text', locked => 1);
# This is something coming from xetex/xelatex ? Why define this way?
#DefConstructor('\realsuperscript{}', "#1");
DefConstructor('\realsuperscript{}', "#1",
diff --git a/lib/LaTeXML/Package/listings.sty.ltxml b/lib/LaTeXML/Package/listings.sty.ltxml
index 1c953d114..a912fbf76 100644
--- a/lib/LaTeXML/Package/listings.sty.ltxml
+++ b/lib/LaTeXML/Package/listings.sty.ltxml
@@ -1355,7 +1355,16 @@ sub lstProcess_internal {
my $classes = LookupValue('LST_CLASSES');
my $literate = LookupValue('LST_LITERATE');
my $lit_re = ($end_re ? $LaTeXML::LITERATE_INNER_RE : $LaTeXML::LITERATE_RE);
+ my $loop_guard = 'loop guard';
+
while ($LaTeXML::listing ne '') {
+ if ($LaTeXML::listing eq $loop_guard) {
+ # we must make headway on every step, or we risk infinite loops
+ Error('listing', 'infinite_loop', undef,
+ "lstProcess_internal failed to execute correctly.", "content was: '$loop_guard'");
+ $LaTeXML::listing = '';
+ last; }
+ $loop_guard = $LaTeXML::listing;
# Matched the ending regular expression? (typically a close delimiter)
if ($end_re && $LaTeXML::listing =~ s/^($end_re)//s) {
$LaTeXML::colnum += length($1);
@@ -1449,7 +1458,6 @@ sub lstProcess_internal {
elsif ($LaTeXML::QUOTED_RE && $LaTeXML::listing =~ s/^($LaTeXML::QUOTED_RE)//) { # Something quoted.
# Don't just past together, and watch for leading \ (a common quoter)
lstProcessPush(map { ($_ eq '\\' ? T_CS('\textbackslash') : T_OTHER($_)) } split('', $1));
-
$LaTeXML::colnum += length($1); }
else {
if ($LaTeXML::listing =~ s/^(.)//s) { # Anything else, just pass through.
diff --git a/lib/LaTeXML/Package/natbib.sty.ltxml b/lib/LaTeXML/Package/natbib.sty.ltxml
index 60aeeae88..31edf8583 100644
--- a/lib/LaTeXML/Package/natbib.sty.ltxml
+++ b/lib/LaTeXML/Package/natbib.sty.ltxml
@@ -485,10 +485,13 @@ DefMacro('\shortcites Semiverbatim', '');
# \bibitem[\protect\citename{Jones et al., }1990]{key}...
# \harvarditem[Jones et al.]{Jones, Baker, and Williams}{1990}{key}...
-DefMacro('\bibitem',
+DefMacro('\lx@nat@bibitem',
## '\reset@natbib@cites\refstepcounter{@bibitem}\@ifnextchar[{\@lbibitem}{\@lbibitem[\the@bibitem]}',
'\reset@natbib@cites\refstepcounter{@bibitem}\@ifnextchar[{\@lbibitem}{\@lbibitem[]}',
locked => 1);
+# Careful: since OmniBus also defines \bibitem and LaTeX.pool has a save/restore mechanism,
+# it is safer to \let to a constant natbib-specific macro definition. Test with arXiv:2007.09909
+Let('\bibitem', '\lx@nat@bibitem');
RawTeX(<<'EOTeX');
%%%
diff --git a/lib/LaTeXML/Package/pgfmath.code.tex.ltxml b/lib/LaTeXML/Package/pgfmath.code.tex.ltxml
index 7a76ba7ce..5cbe4ed2e 100644
--- a/lib/LaTeXML/Package/pgfmath.code.tex.ltxml
+++ b/lib/LaTeXML/Package/pgfmath.code.tex.ltxml
@@ -705,6 +705,7 @@ expr :
| /0b[01]+/ { oct($item[1]); } # !!!
| /0x[0-9a-fA-F]+/ { hex($item[1]); }
| /0[0-9]+/ { oct($item[1]); }
+ | /\./ { 0.0; } # pgf treats a single dot as a zero
UNIT :
/(?:ex|em|pt|pc|in|bp|cm|mm|dd|cc|sp)/
diff --git a/lib/LaTeXML/Package/xcolor.sty.ltxml b/lib/LaTeXML/Package/xcolor.sty.ltxml
index 71ff6bf41..ad57fd68e 100644
--- a/lib/LaTeXML/Package/xcolor.sty.ltxml
+++ b/lib/LaTeXML/Package/xcolor.sty.ltxml
@@ -290,7 +290,7 @@ sub DecodeColor {
my $color_re = qr/($color_expr_re)(($func_expr_re)*)/;
my $color;
- if ($expression =~ /^$color_re$/) {
+ if ($expression =~ /^$color_re\s*$/) {
#DG: Dear reader, I present to you: maintenance hell:
my $prefix = $2 || $10;
my $name = $3 || $11;
diff --git a/lib/LaTeXML/Post/MathML.pm b/lib/LaTeXML/Post/MathML.pm
index 66812a1f8..cf8477ab1 100644
--- a/lib/LaTeXML/Post/MathML.pm
+++ b/lib/LaTeXML/Post/MathML.pm
@@ -693,7 +693,8 @@ sub stylizeContent {
if (my $default = $role && $default_token_content{$role}) {
$text = $default; }
else {
- $text = ($iselement ? $item->getAttribute('name') || $item->getAttribute('meaning') || $role : '?');
+ $text = ($iselement ? ($item->getAttribute('name') || $item->getAttribute('meaning')
+ || $role || '') : '?');
$color = 'red'; } }
elsif (($text eq '-') && $role && (($role eq 'ADDOP') || ($role eq 'OPERATOR'))) { # MathML Core prefers unicode minus
$text = "\x{2212}"; }
@@ -720,7 +721,7 @@ sub stylizeContent {
: ($plane1 ? $variant : undef));
my $u_text = ($tag ne 'm:mtext') && $u_variant && unicode_convert($text, $u_variant);
if ((defined $u_text) && ($u_text ne '')) { # didn't remap the text ? Keep text & variant
- $text = $u_text;
+ $text = $u_text || '';
$variant = ($plane1hack && ($variant ne $u_variant) && ($variant =~ /^bold/)
? 'bold' : undef); } # Possibly keep variant bold
# Use class (css) to patchup some weak translations