diff --git a/lib/LaTeXML/Engine/latex_constructs.pool.ltxml b/lib/LaTeXML/Engine/latex_constructs.pool.ltxml
index 97d5b676c..c9997ea4f 100644
--- a/lib/LaTeXML/Engine/latex_constructs.pool.ltxml
+++ b/lib/LaTeXML/Engine/latex_constructs.pool.ltxml
@@ -292,10 +292,11 @@ DefMacro('\@icentercr[]', '\vskip #1\ignorespaces');
# text
# \end{document}
-DefMacro('\AtBeginDocument{}', sub {
- PushValue('@at@begin@document', $_[1]->unlist); });
-DefMacro('\AtEndDocument{}', sub {
- PushValue('@at@end@document', $_[1]->unlist); });
+# Support optional [label] argument from modern LaTeX hooks system (e.g. \AtBeginDocument[tcolorbox]{...})
+DefMacro('\AtBeginDocument[]{}', sub {
+ PushValue('@at@begin@document', $_[2]->unlist); });
+DefMacro('\AtEndDocument[]{}', sub {
+ PushValue('@at@end@document', $_[2]->unlist); });
# Like "#body",
# But more complicated due to id, at begin/end document and so forth.
diff --git a/lib/LaTeXML/Engine/math_common.pool.ltxml b/lib/LaTeXML/Engine/math_common.pool.ltxml
index d1508bc4d..276ee1bce 100644
--- a/lib/LaTeXML/Engine/math_common.pool.ltxml
+++ b/lib/LaTeXML/Engine/math_common.pool.ltxml
@@ -356,7 +356,7 @@ DefRewrite(select => ["descendant-or-self::ltx:XMTok[text()='\x{FF0F}' and \@mea
if (my $id = $not->getAttribute('xml:id')) {
foreach my $n ($doc->findnodes("descendant-or-self::ltx:XMRef[\@idref='$id']")) {
$doc->removeNode($n); } } # ? Hopefully this is safe.
-} });
+ } });
#----------------------------------------------------------------------
# \joinrel
@@ -400,7 +400,7 @@ DefConstructor('\@@joinrel{}{}', sub {
my $role = (scalar(keys %roles) == 1 ? [keys %roles]->[0] : ($roles{ARROW} ? 'ARROW' : 'RELOP'));
map { $node->removeChild($_) } @rels;
$document->insertElement('ltx:XMTok', [map { $_->textContent } @rels], role => $role);
- } } },
+ } } },
reversion => '#1\joinrel #2');
#----------------------------------------------------------------------
@@ -468,9 +468,9 @@ DefConstructorI('\lx@ldots', undef,
# And so can \vdots
DefConstructorI('\vdots', undef,
"?#isMath(\x{22EE})(\x{22EE})",
- sizer => "\x{22EE}",
+ sizer => "\x{22EE}",
enterHorizontal => 1,
- properties => sub {
+ properties => sub {
(LookupValue('IN_MATH')
? (font => LookupValue('font')->merge(family => 'serif',
series => 'medium', shape => 'upright')->specialize("\x{22EE}"))
@@ -484,9 +484,9 @@ DefMathI('\colon', undef, ':', role => 'METARELOP'); # Seems like good
# Aha, also can be in text...
DefConstructorI('\dots', undef,
"?#isMath(\x{2026})(\x{2026})",
- sizer => "\x{2026}",
+ sizer => "\x{2026}",
enterHorizontal => 1,
- properties => sub {
+ properties => sub {
(LookupValue('IN_MATH')
? (font => LookupValue('font')->merge(family => 'serif',
series => 'medium', shape => 'upright')->specialize("\x{2026}"))
@@ -574,9 +574,9 @@ DefMathI('\langle', undef, "\x{27E8}", role => 'OPEN', stretchy => 'false'); # L
DefMathI('\rangle', undef, "\x{27E9}", role => 'CLOSE', stretchy => 'false'); # RIGHT-POINTING ANGLE BRACKET
# Not sure these should be defined here, or latex, or even latex compat mode.
-DefMathI('\lgroup', undef, "\x{27EE}", role => 'OPEN', stretchy => 'false');
-DefMathI('\rgroup', undef, "\x{27EF}", role => 'CLOSE', stretchy => 'false');
-DefMathI('\bracevert', undef, "|", font => { series => 'bold' }, role => 'VERTBAR');
+DefMathI('\lgroup', undef, "\x{27EE}", role => 'OPEN', stretchy => 'false');
+DefMathI('\rgroup', undef, "\x{27EF}", role => 'CLOSE', stretchy => 'false');
+DefMathI('\bracevert', undef, "|", font => { series => 'bold' }, role => 'VERTBAR');
## DefMath('\lmoustache',"???", font=>{series=>'bold'}, role=>'OPEN');
## DefMath('\rmoustache',"???", font=>{series=>'bold'}, role=>'OPEN');
@@ -587,9 +587,9 @@ DefMathI('\bracevert', undef, "|", font => { series => 'bold' }, role => 'VERTBA
# mapping. Unfortunately, this doesn't (yet) support people declaring thier own delimiters!
# These expand into \left#1, so the bracing disappears; only enlarge the 1st part of #1!
-DefConstructor('\big TeXDelimiter', '#1', bounded => 1, font => { size => 'big' },
+DefConstructor('\big TeXDelimiter', '#1', bounded => 1, font => { size => 'big' },
afterConstruct => sub { augmentDelimiterProperties($_[0], $_[1], undef, 0); });
-DefConstructor('\Big TeXDelimiter', '#1', bounded => 1, font => { size => 'Big' },
+DefConstructor('\Big TeXDelimiter', '#1', bounded => 1, font => { size => 'Big' },
afterConstruct => sub { augmentDelimiterProperties($_[0], $_[1], undef, 0); });
DefConstructor('\bigg TeXDelimiter', '#1', bounded => 1, font => { size => 'bigg' },
afterConstruct => sub { augmentDelimiterProperties($_[0], $_[1], undef, 0); });
@@ -655,9 +655,24 @@ DefConstructor('\phantom{}',
DefConstructor('\hphantom{}',
"?#isMath()"
. "(#1)", # !?!?!?!
- properties => { isSpace => 1 },
+ properties => { isSpace => 1 },
+ # In TeX, \hphantom always processes its argument inside an \hbox.
+ # In text/horizontal mode, we enforce restricted_horizontal to prevent
+ # display math from leaking through the argument (as can happen with
+ # complex TikZ library expansions like quantikz2).
+ # In math mode, we preserve the current mode for correct font metrics.
+ beforeDigest => sub {
+ my ($stomach) = @_;
+ if (!$STATE->lookupValue('IN_MATH')) {
+ $stomach->beginMode('restricted_horizontal');
+ AssignValue('_hphantom_mode_override' => 1); }
+ else {
+ AssignValue('_hphantom_mode_override' => 0); }
+ return; },
afterDigest => sub {
- my $whatsit = $_[1];
+ my ($stomach, $whatsit) = @_;
+ if (LookupValue('_hphantom_mode_override')) {
+ $stomach->endMode('restricted_horizontal'); }
my ($w, $h, $d) = $whatsit->getArg(1)->getSize;
$whatsit->setProperties(width => $w, height => $h, depth => $d);
return; });
@@ -795,7 +810,7 @@ DefRewrite(xpath => 'descendant-or-self::ltx:XMWrap[count(child::*)=1]',
$document->setNodeFont($wrap, $document->getNodeFont($node));
## WHY THIS????
$document->getNode->appendChild($node);
-} });
+ } });
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# Stop at
diff --git a/lib/LaTeXML/Package/color.sty.ltxml b/lib/LaTeXML/Package/color.sty.ltxml
index a97269b7c..1e7a1a275 100644
--- a/lib/LaTeXML/Package/color.sty.ltxml
+++ b/lib/LaTeXML/Package/color.sty.ltxml
@@ -108,7 +108,7 @@ DefMacro('\colorbox[]{}{}', '\hbox{\ifx.#1.\pagecolor{#2}\else\pagecolor[#1]{#2}
DefConstructor('\fcolorbox[]{}{} Undigested',
"#text",
- mode => 'internal_vertical',
+ mode => 'internal_vertical',
afterDigest => sub {
my ($stomach, $whatsit) = @_;
my ($model, $fspec, $bspec, $text) = $whatsit->getArgs;
@@ -119,8 +119,12 @@ DefConstructor('\fcolorbox[]{}{} Undigested',
#********************************************************************************
# Low-level stuff; redefined from LaTeX stubs
-# Not sure what \current@color should return... the string form?
-# \current@color
+# \current@color holds the current color specification for the DVI driver.
+# For LaTeXML, provide a safe default so packages using it (e.g. pgf/tikz) don't error.
+# The value must NOT contain braces since it may appear inside \csname...\endcsname.
+DefMacroI('\current@color', undef, '0 0 0');
+DefMacroI('\default@color', undef, '0 0 0');
+DefMacroI('\reset@color', undef, '');
# Similarly, I'm not sure what set@color needs to do that isn't redundant
DefMacroI('\set@color', undef, '');
diff --git a/lib/LaTeXML/Package/tcolorbox.sty.ltxml b/lib/LaTeXML/Package/tcolorbox.sty.ltxml
index acca67778..b9ca83dda 100644
--- a/lib/LaTeXML/Package/tcolorbox.sty.ltxml
+++ b/lib/LaTeXML/Package/tcolorbox.sty.ltxml
@@ -20,8 +20,9 @@ DefRegister('\doublecol@number' => Number(0));
# Ensure only unbreakable mode is possible
DefMacro('\tcb@init@breakable', '\tcb@init@unbreakable', locked => 1);
-RequirePackage('expl3');
-RequirePackage('xparse');
+# Pre-define \tcb@use@autoparskip before loading the raw tcolorbox.sty,
+# as a safety net in case the pgfkeys initialization doesn't fully complete.
+DefMacroI('\tcb@use@autoparskip', undef, '\relax');
InputDefinitions('tcolorbox', type => 'sty', noltxml => 1);