Skip to content

Commit 3f1b890

Browse files
authored
Tikz cd (#2325)
* Do NOT bump ALIGN_STATE when digesting braces * But rather maintain ALIGN_STATE while 'scanning', ie. when reading in Gullet, being careful about unread tokens * Add two pgfmath functions defined by tikz-cd (axis_height, rule_thickness) * Add new binding for tikz-cd * pgfsys's alternative \halign needs the ALIGN_STATE adjustments, as well * Recognize and convert math nested within ltx:XMText/ltx:picture * Adjust placement of tikz picture (for tikz-cd) * Fix unread oversight
1 parent 8046a13 commit 3f1b890

File tree

9 files changed

+92
-46
lines changed

9 files changed

+92
-46
lines changed

MANIFEST

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,7 @@ lib/LaTeXML/Package/titling.sty.ltxml
726726
lib/LaTeXML/Package/tikz-3dplot.sty.ltxml
727727
lib/LaTeXML/Package/tikz.sty.ltxml
728728
lib/LaTeXML/Package/tikzbricks.sty.ltxml
729+
lib/LaTeXML/Package/tikz-cd.sty.ltxml
729730
lib/LaTeXML/Package/times.sty.ltxml
730731
lib/LaTeXML/Package/tocbibind.sty.ltxml
731732
lib/LaTeXML/Package/todonotes.sty.ltxml

lib/LaTeXML/Core/Definition/Conditional.pm

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,16 @@ sub skipConditionalBody {
117117
my $level = 1;
118118
my $n_ors = 0;
119119
my $start = $gullet->getLocator;
120-
# NOTE: Open-coded manipulation of if_stack!, Gullet and Token's
120+
# NOTE: Open-coded manipulation of if_stack!, Gullet and Token's; Must be fast!
121121
# [we're only reading tokens & looking up, so State shouldn't change behind our backs]
122122
my $stack = $STATE->lookupValue('if_stack');
123123
while (1) {
124124
my ($t, $cond_type);
125125
while ($t = shift(@{ $$gullet{pushback} }) || $$gullet{mouth}->readToken()) {
126-
if ($LaTeXML::Core::State::CATCODE_ACTIVE_OR_CS[$$t[1]]
126+
my $cc = $$t[1];
127+
if ($cc == CC_BEGIN) { $LaTeXML::ALIGN_STATE++; }
128+
elsif ($cc == CC_END) { $LaTeXML::ALIGN_STATE--; }
129+
elsif ($LaTeXML::Core::State::CATCODE_ACTIVE_OR_CS[$cc]
127130
&& ($cond_type = $STATE->lookupConditional($t))) {
128131
last; } }
129132
last unless $cond_type;

lib/LaTeXML/Core/Gullet.pm

Lines changed: 50 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -300,18 +300,33 @@ sub readToken {
300300
return T_CS('\special_relax'); }
301301
else {
302302
last; } }
303+
if ($token) {
304+
$cc = $$token[1];
305+
if ($cc == CC_BEGIN) { $LaTeXML::ALIGN_STATE++; }
306+
elsif ($cc == CC_END) { $LaTeXML::ALIGN_STATE--; }
307+
}
303308
return $token; }
304309

305310
# Unread tokens are assumed to be not-yet expanded.
306311
sub unread {
307312
my ($self, @tokens) = @_;
308-
my $r;
309-
unshift(@{ $$self{pushback} },
310-
map { (!defined $_ ? ()
311-
: (($r = ref $_) eq 'LaTeXML::Core::Token' ? $_
312-
: ($r eq 'LaTeXML::Core::Tokens' ? @$_
313-
: Error('misdefined', $r, undef, "Expected a Token, got " . Stringify($_)) || T_OTHER(Stringify($_))))) }
314-
@tokens);
313+
my $level = 0;
314+
my $pb = $$self{pushback};
315+
while (@tokens) {
316+
my $token = pop(@tokens);
317+
my $r = ref $token;
318+
if (!defined $token) { }
319+
elsif ($r eq 'LaTeXML::Core::Tokens') {
320+
push(@tokens, @$token); }
321+
elsif ($r eq 'LaTeXML::Core::Token') {
322+
my $cc = $$token[1];
323+
if ($cc == CC_BEGIN) { $level--; } # Retract scanned braces
324+
elsif ($cc == CC_END) { $level++; }
325+
unshift(@$pb, $token); }
326+
else {
327+
Error('misdefined', $r, undef, "Expected a Token, got " . Stringify($_));
328+
unshift(@$pb, T_OTHER($token)); } }
329+
$LaTeXML::ALIGN_STATE += $level;
315330
return; }
316331

317332
# Read the next non-expandable token (expanding tokens until there's a non-expandable one).
@@ -368,11 +383,13 @@ sub readXToken {
368383
no warnings 'recursion';
369384
my $expansion = $defn->invoke($self);
370385
# add the newly expanded tokens back into the gullet stream, in the ordinary case.
371-
unshift(@{ $$self{pushback} }, @$expansion) if $expansion; } }
386+
unread($self, $expansion) if $expansion; } }
372387
elsif ($$token[1] == CC_CS && !(defined $defn)) {
373388
$STATE->generateErrorStub($self, $token); # cs SHOULD have defn by now; report early!
374389
return $token; }
375390
else {
391+
if ($cc == CC_BEGIN) { $LaTeXML::ALIGN_STATE++; }
392+
elsif ($cc == CC_END) { $LaTeXML::ALIGN_STATE--; }
376393
return $token; } # just return it
377394
}
378395
return; } # never get here.
@@ -392,6 +409,7 @@ our $DEFERRED_COMMANDS = {
392409

393410
sub readBalanced {
394411
my ($self, $expanded, $macrodef, $require_open) = @_;
412+
$LaTeXML::ALIGN_STATE-- unless $require_open; # assume matching } [BEFORE masking ALIGN_STATE]
395413
local $LaTeXML::ALIGN_STATE = 1000000;
396414
my $startloc = ($$self{verbosity} > 0) && getLocator($self);
397415
# Does we need to expand to get the { ???
@@ -423,11 +441,13 @@ sub readBalanced {
423441
elsif (($cc == CC_CS) && ($$token[0] eq '\dont_expand')) {
424442
push(@tokens, readToken($self)); } # Pass on NEXT token, unchanged.
425443
elsif ($cc == CC_END) {
444+
$LaTeXML::ALIGN_STATE--;
426445
$level--;
427446
if (!$level) {
428447
last; }
429448
push(@tokens, $token); }
430449
elsif ($cc == CC_BEGIN) {
450+
$LaTeXML::ALIGN_STATE++;
431451
$level++;
432452
push(@tokens, $token); }
433453
## Wow!!!!! See TeX the Program \S 309
@@ -460,7 +480,7 @@ sub readBalanced {
460480
push(@tokens, $t); } }
461481
}
462482
else { # otherwise, prepend to pushback to be expanded further.
463-
unshift(@{ $$self{pushback} }, @$expansion); } }
483+
unread($self, $expansion) if $expansion; } }
464484
else {
465485
if ($expanded && ($$token[1] == CC_CS) && !(defined $defn)) {
466486
$STATE->generateErrorStub($self, $token); } # cs SHOULD have defn by now; report early!
@@ -520,15 +540,15 @@ sub readXNonSpace {
520540
sub skipSpaces {
521541
my ($self) = @_;
522542
my $tok = readNonSpace($self);
523-
unshift(@{ $$self{pushback} }, $tok) if defined $tok; # Unread
543+
unread($self, $tok) if defined $tok;
524544
return; }
525545

526546
# Skip one space
527547
# if $expanded is true, it acts like <one optional space>, expanding the next token.
528548
sub skip1Space {
529549
my ($self, $expanded) = @_;
530550
my $token = ($expanded ? readXToken($self) : readToken($self));
531-
unshift(@{ $$self{pushback} }, $token) if $token && !$token->defined_as(T_SPACE);
551+
unread($self, $token) if $token && !$token->defined_as(T_SPACE);
532552
return; }
533553

534554
# <filler> = <optional spaces> | <filler>\relax<optional spaces>
@@ -539,15 +559,15 @@ sub skipFiller {
539559
return unless defined $tok;
540560
# Should \foo work too (where \let\foo\relax) ??
541561
if (!$tok->equals(T_CS('\relax'))) {
542-
unshift(@{ $$self{pushback} }, $tok); # Unread
562+
unread($self, $tok);
543563
return; }
544564
}
545565
return; }
546566

547567
sub ifNext {
548568
my ($self, $token) = @_;
549569
if (my $tok = readToken($self)) {
550-
unshift(@{ $$self{pushback} }, $tok); # Unread
570+
unread($self, $tok);
551571
return $tok->equals($token); }
552572
else { return 0; } }
553573

@@ -564,10 +584,9 @@ sub readMatch {
564584
if ($$token[1] == CC_SPACE) { # If this was space, SKIP any following!!!
565585
while (defined($token = readToken($self)) && ($$token[1] == CC_SPACE)) {
566586
push(@matched, $token); }
567-
unshift(@{ $$self{pushback} }, $token) if $token; } # Unread
568-
}
569-
return $choice unless @tomatch; # All matched!!!
570-
unshift(@{ $$self{pushback} }, @matched); # Put 'em back and try next!
587+
unread($self, $token) if defined $token; } }
588+
return $choice unless @tomatch; # All matched!!!
589+
unread($self, @matched); # Put 'em back and try next!
571590
}
572591
return; }
573592

@@ -585,9 +604,8 @@ sub readKeyword {
585604
while (@tomatch && defined($tok = readXToken($self, 0)) && push(@matched, $tok)
586605
&& (uc($$tok[0]) eq $tomatch[0])) {
587606
shift(@tomatch); }
588-
return $keyword unless @tomatch; # All matched!!!
589-
unshift(@{ $$self{pushback} }, @matched); # Put 'em back and try next!
590-
}
607+
return $keyword unless @tomatch; # All matched!!!
608+
unread($self, @matched); } # Put 'em back tand try next!
591609
return; }
592610

593611
# Return a (balanced) sequence tokens until a match against one of the Tokens in @delims.
@@ -603,9 +621,7 @@ sub readUntil {
603621
my $ntomatch = scalar(@want);
604622
if ($ntomatch == 1) { # Common, easy case: read till we match a single token
605623
my $want = $want[0];
606-
# while(($token = readToken($self)) && !$token->equals($want)){
607-
while (($token = shift(@{ $$self{pushback} }) || $$self{mouth}->readToken())
608-
&& !$token->equals($want)) {
624+
while (($token = readToken($self)) && !$token->equals($want)) {
609625
my $cc = $$token[1];
610626
if ($cc == CC_MARKER) { # would have been handled by readToken, but we're bypassing
611627
handleMarker($self, $token); }
@@ -647,6 +663,7 @@ sub readUntilBrace {
647663
my $token;
648664
while (defined($token = readToken($self))) {
649665
if ($$token[1] == CC_BEGIN) { # INLINE Catcode
666+
$LaTeXML::ALIGN_STATE--;
650667
unshift(@{ $$self{pushback} }, $token); # Unread
651668
last; }
652669
push(@tokens, $token); }
@@ -700,7 +717,7 @@ sub readOptional {
700717
elsif (($tok->equals(T_OTHER('[')))) {
701718
return readUntil($self, T_OTHER(']')); }
702719
else {
703-
unshift(@{ $$self{pushback} }, $tok); # Unread
720+
unread($self, $tok);
704721
return $default; } }
705722

706723
#**********************************************************************
@@ -752,7 +769,7 @@ sub readRegisterValue {
752769
else {
753770
return &$coercer($sign * $value->valueOf); } }
754771
else {
755-
unshift(@{ $$self{pushback} }, $token); # Unread
772+
unread($self, $token);
756773
return; } }
757774

758775
# Apparent behaviour of a token value (ie \toks#=<arg>)
@@ -791,7 +808,7 @@ sub readOptionalSigns {
791808
while (defined($t = readXToken($self))
792809
&& (($$t[0] eq '+') || ($$t[0] eq '-') || $t->defined_as(T_SPACE))) {
793810
$sign = -$sign if ($$t[0] eq '-'); }
794-
unshift(@{ $$self{pushback} }, $t) if $t; # Unread
811+
unread($self, $t) if $t;
795812
return $sign; }
796813

797814
# Read digits (within $range), while expanding and if $skip, skip <one optional space> (expanded!)
@@ -801,7 +818,7 @@ sub readDigits {
801818
my ($token, $digit);
802819
while (($token = readXToken($self)) && (($digit = $$token[0]) =~ /^[$range]$/)) {
803820
$string .= $digit; }
804-
unshift(@{ $$self{pushback} }, $token) if $token && !($skip && $token->defined_as(T_SPACE)); #Inline
821+
unread($self, $token) if $token && !($skip && $token->defined_as(T_SPACE)); #Inline
805822
return $string; }
806823

807824
# <factor> = <normal integer> | <decimal constant>
@@ -815,10 +832,10 @@ sub readFactor {
815832
$string .= '.' . readDigits($self, '0-9');
816833
$token = readXToken($self); }
817834
if (length($string) > 0) {
818-
unshift(@{ $$self{pushback} }, $token) if $token && $$token[1] != CC_SPACE; # Inline ->getCatcode, unread
835+
unread($self, $token) if $token && $$token[1] != CC_SPACE;
819836
return $string; }
820837
else {
821-
unshift(@{ $$self{pushback} }, $token); # Unread
838+
unread($self, $token);
822839
my $n = readNormalInteger($self);
823840
return (defined $n ? $n->valueOf : undef); } }
824841

@@ -836,7 +853,7 @@ sub readNumber {
836853
elsif (defined($n = readRegisterValue($self, 'Number', $s, 1))) { return $n; }
837854
else {
838855
my $next = readToken($self);
839-
unshift(@{ $$self{pushback} }, $next); # Unread
856+
unread($self, $next);
840857
Warn('expected', '<number>', $self, "Missing number, treated as zero",
841858
"while processing " . ToString($LaTeXML::CURRENT_TOKEN), showUnexpected($self));
842859
return Number(0); } }
@@ -863,7 +880,7 @@ sub readNormalInteger {
863880
skip1Space($self, 1);
864881
return Number(ord($s)); } # Only a character token!!! NOT expanded!!!!
865882
else {
866-
unshift(@{ $$self{pushback} }, $token); # Unread
883+
unread($self, $token);
867884
return readRegisterValue($self, 'Number'); } }
868885

869886
#======================================================================
@@ -880,10 +897,10 @@ sub readFloat {
880897
$token = readXToken($self); }
881898
my $n;
882899
if (length($string) > 0) {
883-
unshift(@{ $$self{pushback} }, $token) if $token && $$token[1] != CC_SPACE; # Inline ->getCatcode, unread
900+
unread($self, $token) if $token && $$token[1] != CC_SPACE;
884901
$n = $string; }
885902
else {
886-
unshift(@{ $$self{pushback} }, $token) if $token; # Unread
903+
unread($self, $token) if $token;
887904
$n = readNormalInteger($self);
888905
$n = $n->valueOf if defined $n; }
889906
return (defined $n ? Float($s * $n) : undef); }

lib/LaTeXML/Core/Stomach.pm

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -312,11 +312,6 @@ sub currentFrameMessage {
312312
sub bgroup {
313313
my ($self) = @_;
314314
pushStackFrame($self, 0);
315-
# NOTE: This is WRONG; should really only track "scanned" (not digested) braces
316-
# Alas, there're too many code structuring differences between TeX and LaTeXML
317-
# to find all the places to manage it.
318-
# So, let's try this for now...
319-
$LaTeXML::ALIGN_STATE++;
320315
return; }
321316

322317
sub egroup {
@@ -327,7 +322,6 @@ sub egroup {
327322
currentFrameMessage($self)); }
328323
else { # Don't pop if there's an error; maybe we'll recover?
329324
popStackFrame($self, 0); }
330-
$LaTeXML::ALIGN_STATE--;
331325
return; }
332326

333327
sub begingroup {

lib/LaTeXML/Package/TeX.pool.ltxml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2831,7 +2831,7 @@ DefColumnType('@{}', sub {
28312831
#----------------------------------------------------------------------
28322832
# This is where ALL alignments start & finish
28332833
# This creates the object representing the entire alignment!
2834-
DefConstructor('\@start@alignment SkipSpaces',
2834+
DefConstructor('\@start@alignment',
28352835
"#alignment",
28362836
reversion => sub { Revert($_[0]->getProperty('alignment')); },
28372837
sizer => '#alignment',
@@ -3192,6 +3192,7 @@ DefConstructor('\halign BoxSpecification',
31923192
attributes => { width => orNull(GetKeyVal($spec, 'to')) });
31933193
digestAlignmentBody($stomach, $whatsit);
31943194
$stomach->egroup;
3195+
$LaTeXML::ALIGN_STATE--; # Balance the opening { OUTSIDE of the masking of ALIGN_STATE
31953196
return; });
31963197

31973198
# Parse an \halign style alignment template from Gullet

lib/LaTeXML/Package/pgfmath.code.tex.ltxml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,9 @@ BEGIN {
640640
true => sub { 1; },
641641
veclen => sub { sqrt($_[0] * $_[0] + $_[1] * $_[1]); },
642642
# width => sub { },
643+
# Additional functions from tikz-cd; these need to get parameters from the current math font!
644+
axis_height => sub { "2.5"; }, # sigma[22]
645+
rule_thickness => sub { "0.39998"; }, # xi[8]
643646
};
644647

645648
$::RD_HINT = 1;
@@ -710,7 +713,7 @@ expr :
710713
UNIT :
711714
/(?:ex|em|pt|pc|in|bp|cm|mm|dd|cc|sp)/
712715
713-
FUNCTION0 : /(?:e|pi|false|rand|rnd|true)/
716+
FUNCTION0 : /(?:e|pi|false|rand|rnd|true|axis_height|rule_thickness)/
714717
| /([a-zA-Z][a-zA-Z0-9]*)/ { LaTeXML::Package::Pool::pgfmath_checkuserconstant($item[1]); }
715718
716719
FUNCTION : /(?:abs|acos|asin|atan2|atan|angle|bin|ceil|cos|cosec|cosh|cot|deg|exp|factorial|floor|frac|hex|Hex|int|iseven|isodd|isprime|ln|log10|log2|neg|not|oct|rad|real|round|sec|sign|sin|sinh|sqrt|tan|tanh|add|and|divide|div|equal|gcd|greater|less|max|min|mod|Mod|multiply|notequal|notgreater|notless|or|pow|random|subtract|ifthenelse|veclen)/

lib/LaTeXML/Package/pgfsys-latexml.def.ltxml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,8 @@ DefMacro('\pgfsys@typesetpicturebox{}', <<'EoTeX');
185185
%%% \lxSVG@insertpicture{\box#1\lxSVG@closescope}%
186186
\pgf@ya=\pgf@shift@baseline\relax%
187187
\advance\pgf@ya by-\pgf@picminy\relax%
188-
\lxSVG@insertpicture{\raise-\pgf@ya\box#1\lxSVG@closescope}%
188+
%%% \lxSVG@insertpicture{\raise-\pgf@ya\box#1\lxSVG@closescope}%
189+
\lxSVG@insertpicture{\box#1\lxSVG@closescope}%
189190
EoTeX
190191

191192
DefMacro('\pgfsys@beginpicture', '');
@@ -905,6 +906,7 @@ DefConstructor('\lxSVG@halign BoxSpecification',
905906
width => orNull(GetKeyVal($spec, 'to')) });
906907
digestAlignmentBody($stomach, $whatsit);
907908
$stomach->egroup;
909+
$LaTeXML::ALIGN_STATE--; # Balance the opening { OUTSIDE of the masking of ALIGN_STATE
908910
return; });
909911

910912
sub tikzAlignmentBindings {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# -*- CPERL -*-
2+
# /=====================================================================\ #
3+
# | tikz-cd.sty | #
4+
# | Implementation for LaTeXML | #
5+
# |=====================================================================| #
6+
# | Part of LaTeXML: | #
7+
# | Public domain software, produced as part of work done by the | #
8+
# | United States Government & not subject to copyright in the US. | #
9+
# |---------------------------------------------------------------------| #
10+
# | Bruce Miller <[email protected]> #_# | #
11+
# | http://dlmf.nist.gov/LaTeXML/ (o o) | #
12+
# \=========================================================ooo==U==ooo=/ #
13+
package LaTeXML::Package::Pool;
14+
use strict;
15+
use warnings;
16+
use LaTeXML::Package;
17+
18+
#**********************************************************************
19+
InputDefinitions('tikz-cd', type => 'sty', noltxml => 1);
20+
#**********************************************************************
21+
22+
1;

lib/LaTeXML/Post/MathML.pm

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ use LaTeXML::Util::Unicode;
1818
use LaTeXML::Post;
1919
use LaTeXML::Common::Font;
2020
use List::Util qw(max);
21-
use base qw(LaTeXML::Post::MathProcessor);
22-
use base qw(Exporter);
21+
use base qw(LaTeXML::Post::MathProcessor);
22+
use base qw(Exporter);
2323
our @EXPORT = (
2424
qw( &DefMathML ),
2525
qw( &pmml &pmml_scriptsize &pmml_smaller
@@ -1045,6 +1045,9 @@ sub pmml_text_aux {
10451045
elsif (($tag eq 'ltx:text') # ltx:text element is fine, if we can manage the attributes!
10461046
&& (!grep { $node->hasAttribute($_) } qw(framed framecolor))) {
10471047
return pmml_maybe_resize($node, pmml_row(map { pmml_text_aux($_, %attr) } $node->childNodes)); }
1048+
elsif ($tag eq 'ltx:picture') { # Embeded pictures might legitimately have nested math?
1049+
return ['m:mtext', {},
1050+
$LaTeXML::Post::MATHPROCESSOR->convertXMTextContent($LaTeXML::Post::DOCUMENT, 1, $node)]; }
10481051
else {
10491052
# We could just recurse on raw content like this, but it loses a lot...
10501053
### map(pmml_text_aux($_,%attr), $node->childNodes); }}

0 commit comments

Comments
 (0)