1# -*- mode: Perl -*- 2# /=====================================================================\ # 3# | LaTeX | # 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 <bruce.miller@nist.gov> #_# | # 11# | http://dlmf.nist.gov/LaTeXML/ (o o) | # 12# \=========================================================ooo==U==ooo=/ # 13package LaTeXML::Package::Pool; 14use strict; 15use warnings; 16use LaTeXML::Package; 17use LaTeXML::Util::Pathname; 18 19#********************************************************************** 20# Organized following 21# "LaTeX: A Document Preparation System" 22# by Leslie Lamport 23# 2nd edition 24# Addison Wesley, 1994 25# Appendix C. Reference Manual 26#********************************************************************** 27# NOTE: This will be loaded after TeX.pool.ltxml, so it inherits. 28#********************************************************************** 29 30LoadPool('TeX'); 31 32# Apparently LaTeX does NOT define \magnification, 33# and babel uses that to determine whether we're runing LaTeX!!! 34Let('\magnification', '\@undefined'); 35#********************************************************************** 36# Basic \documentclass & \documentstyle 37 38#AssignValue('2.09_COMPATIBILITY'=>0); 39DefConditionalI('\if@compatibility', undef, sub { LookupValue('2.09_COMPATIBILITY'); }); 40DefMacro('\@compatibilitytrue', ''); 41DefMacro('\@compatibilityfalse', ''); 42 43Let('\@currentlabel', '\@empty'); 44DefMacro('\@currdir', './'); 45 46# Let's try just starting with this set (since we've loaded LaTeX) 47AssignValue(inPreamble => 1); # \begin{document} will clear this. 48 49DefConstructor('\documentclass OptionalSemiverbatim SkipSpaces Semiverbatim []', 50 "<?latexml class='#2' ?#1(options='#1')?>", 51 beforeDigest => sub { onlyPreamble('\documentclass'); }, 52 afterDigest => sub { 53 my ($stomach, $whatsit) = @_; 54 my $options = $whatsit->getArg(1); 55 my $class = ToString($whatsit->getArg(2)); 56 $class =~ s/\s+//g; 57 LoadClass($class, 58 options => [($options ? split(/\s*,\s*/, ToString($options)) : ())], 59 after => Tokens(T_CS('\AtBeginDocument'), T_CS('\warn@unusedclassoptions'))); 60 return; }); 61 62AssignValue('@unusedoptionlist', []); 63DefPrimitiveI('\warn@unusedclassoptions', undef, sub { 64 if (my @unused = @{ LookupValue('@unusedoptionlist') }) { 65 Info('unexpected', 'options', $_[0], "Unused global options: " . join(',', @unused)); 66 AssignValue('@unusedoptionlist', []); } 67 return; }); 68 69DefConstructor('\documentstyle OptionalSemiverbatim SkipSpaces Semiverbatim []', 70 "<?latexml class='#2' ?#1(options='#1') oldstyle='true'?>", 71 beforeDigest => sub { 72 Info('unexpected', '\documentstyle', $_[0], "Entering LaTeX 2.09 Compatibility mode"); 73 AssignValue('2.09_COMPATIBILITY' => 1, 'global'); 74 onlyPreamble('\documentstyle'); }, 75 afterDigest => sub { 76 my ($stomach, $whatsit) = @_; 77 my $class = ToString($whatsit->getArg(2)); 78 $class =~ s/\s+//g; 79 my $options = $whatsit->getArg(1); 80 $options = [($options ? split(/\s*,\s*/, ToString($options)) : ())]; 81# Watch out; In principle, compatibility mode wants a .sty, not a .cls!!! 82# But, we'd prefer .cls, since we'll have better bindings. 83# And in fact, nobody's likely to write a binding for a .sty that wants to be a class anyway. 84# So, we'll just try for a .cls, punting to OmniBus if needed. 85# If we start wanting to read style files by default, we'll still need to handle this 86# specially, since class (or sty files pretending to be) cover so much more. 87# 88# EXCEPTION: of course there is an exception. Older arXiv articles use \documentstyle{aipproc} 89# to load a different aipproc.sty than the later evolved aipproc.cls. And of course we need to support that. 90 if (FindFile($class, type => 'sty', notex => !LookupValue('INCLUDE_CLASSES'))) { 91 InputDefinitions('article', type => 'cls', noerror => 1, 92 handleoptions => 1, options => $options); 93 RequirePackage($class, options => $options, as_class => 1, 94 after => Tokens(T_CS('\compat@loadpackages'))); } 95 elsif (FindFile($class, type => 'cls', notex => !LookupValue('INCLUDE_CLASSES'))) { 96 LoadClass($class, options => $options, 97 after => Tokens(T_CS('\compat@loadpackages'))); } 98 else { 99 InputDefinitions('OmniBus', type => 'cls', noerror => 1, 100 handleoptions => 1, options => $options, 101 after => Tokens(T_CS('\compat@loadpackages'))); 102 RequirePackage($class, options => $options, as_class => 1); } 103 return; }); 104 105DefPrimitiveI('\compat@loadpackages', undef, sub { 106 my $name = ToString(Expand(T_CS('\@currname'))); 107 my $type = ToString(Expand(T_CS('\@currext'))); 108 my $hadmissing = 0; 109 foreach my $option (@{ LookupValue('@unusedoptionlist') }) { 110 if (FindFile($option, type => 'sty')) { 111 RequirePackage($option); } 112 else { 113 $hadmissing = 1; 114 Info('unexpected', $option, $_[0], "Unexpected option '$option' passed to $name.$type"); } } 115 # Often, in compatibility mode, the options are used to load what are effectively 116 # document classes for specific journals, etc that introduce a bunch of new frontmatter! 117 # To try to recover from this, we'll go ahead & load the OmniBus class. 118 if ($hadmissing && !LookupValue('OmniBus.cls_loaded')) { 119 Info('note', 'OmniBus', $_[0], "Loading OmniBus class to attempt to cover missing options"); 120 LoadClass('OmniBus'); } 121 AssignValue('@unusedoptionlist', []); }); 122 123sub onlyPreamble { 124 my ($cs) = @_; 125 Error('unexpected', $cs, $STATE->getStomach, 126 "The current command '" . ToString($cs) . "' can only appear in the preamble") 127 unless LookupValue("inPreamble"); 128 return; } 129#********************************************************************** 130# C.1. Commands and Environments. 131#********************************************************************** 132 133#====================================================================== 134# C.1.1 Command Names and Arguments 135#====================================================================== 136# Nothing... 137 138#====================================================================== 139# C.1.2 Environments 140#====================================================================== 141 142# In LaTeX, \newenvironment{env} defines \env and \endenv. 143# \begin{env} & \end{env} open/close a group, and invoke these. 144# In fact, the \env & \endenv don't have to have been created by 145# \newenvironment; And in fact \endenv doesn't even have to be defined! 146# [it is created by \csname, and equiv to \relax if no previous defn] 147 148# We need to respect these usages here, but we also want to be able 149# to define environment constructors that `capture' the body so that 150# it can be processed specially, if needed. These are the magic 151# '\begin{env}', '\end{env}' control sequences created by DefEnvironment. 152 153AssignValue(current_environment => '', 'global'); 154DefMacro('\@currenvir', ''); 155DefPrimitive('\lx@setcurrenvir{}', sub { 156 DefMacroI('\@currenvir', undef, $_[1]); 157 AssignValue(current_environment => ToString($_[1])); }); 158Let('\@currenvline', '\@empty'); 159 160DefMacro('\begin{}', sub { 161 my ($gullet, $env) = @_; 162 my $name = $env && ToString(Expand($env)); 163 my $before = LookupValue('@environment@' . $name . '@beforebegin'); 164 my $after = LookupValue('@environment@' . $name . '@atbegin'); 165 if (IsDefined("\\begin{$name}")) { 166 (($before ? @$before : ()), 167 T_CS("\\begin{$name}")); } # Magic cs! 168 else { 169 my $token = T_CS("\\$name"); 170 if (!IsDefined($token)) { 171 my $undef = '{' . $name . '}'; 172 $STATE->noteStatus(undefined => $undef); 173 Error('undefined', $undef, $gullet, "The environment " . $undef . " is not defined."); 174 $STATE->installDefinition(LaTeXML::Core::Definition::Constructor->new($token, undef, 175 sub { $_[0]->makeError('undefined', $undef); })); } 176 (($before ? @$before : ()), 177 T_CS('\begingroup'), 178 ($after ? @$after : ()), 179 Invocation(T_CS('\lx@setcurrenvir'), $env), 180 $token,); } }); 181# robust => 1); # Not yet working well enough 182 183DefMacro('\end{}', sub { 184 my ($gullet, $env) = @_; 185 my $name = $env && ToString(Expand($env)); 186 my $before = LookupValue('@environment@' . $name . '@atend'); 187 my $after = LookupValue('@environment@' . $name . '@afterend'); 188 my $t; 189 190 if (IsDefined($t = T_CS("\\end{$name}"))) { 191 ($t, 192 ($after ? @$after : ())); } # Magic CS! 193 else { 194 $t = T_CS("\\end$name"); 195 (($before ? @$before : ()), 196 (IsDefined($t) ? $t : ()), 197 T_CS('\endgroup'), 198 ($after ? @$after : ())); } }); 199## robust => 1); # Isn't really robust, but something similar(?) 200 201#====================================================================== 202# C.1.3 Fragile Commands 203#====================================================================== 204# Because of the way we `move information', revertable and pre-processed, 205# I don't think we actually need to do anything ... 206# [Course that means we're not _really_ TeX!] 207 208# \protect is already in TeX for obscure reasons... 209#Let('\@typeset@protect','\relax'); 210RawTeX(<<'EOTeX'); 211\def\@ignorefalse{\global\let\if@ignore\iffalse} 212\def\@ignoretrue {\global\let\if@ignore\iftrue} 213\def\zap@space#1 #2{% 214 #1% 215 \ifx#2\@empty\else\expandafter\zap@space\fi 216 #2} 217\def\@unexpandable@protect{\noexpand\protect\noexpand} 218\def\x@protect#1{% 219 \ifx\protect\@typeset@protect\else 220 \@x@protect#1% 221 \fi 222} 223\def\@x@protect#1\fi#2#3{% 224 \fi\protect#1% 225} 226\let\@typeset@protect\relax 227\def\set@display@protect{\let\protect\string} 228\def\set@typeset@protect{\let\protect\@typeset@protect} 229\def\protected@edef{% 230 \let\@@protect\protect 231 \let\protect\@unexpandable@protect 232 \afterassignment\restore@protect 233 \edef 234} 235\def\protected@xdef{% 236 \let\@@protect\protect 237 \let\protect\@unexpandable@protect 238 \afterassignment\restore@protect 239 \xdef 240} 241\def\unrestored@protected@xdef{% 242 \let\protect\@unexpandable@protect 243 \xdef 244} 245\def\restore@protect{\let\protect\@@protect} 246\set@typeset@protect 247\def\@nobreakfalse{\global\let\if@nobreak\iffalse} 248\def\@nobreaktrue {\global\let\if@nobreak\iftrue} 249\@nobreakfalse 250 251\newif\ifv@ 252\newif\ifh@ 253\newif\ifdt@p 254\newif\if@pboxsw 255\newif\if@rjfield 256\newif\if@firstamp 257\newif\if@negarg 258\newif\if@ovt 259\newif\if@ovb 260\newif\if@ovl 261\newif\if@ovr 262\newdimen\@ovxx 263\newdimen\@ovyy 264\newdimen\@ovdx 265\newdimen\@ovdy 266\newdimen\@ovro 267\newdimen\@ovri 268\newif\if@noskipsec \@noskipsectrue 269 270EOTeX 271 272#====================================================================== 273# C.1.4 Declarations 274#====================================================================== 275# actual implementation later. 276#====================================================================== 277# C.1.5 Invisible Commands 278#====================================================================== 279# actual implementation later. 280 281#====================================================================== 282# C.1.6 The \\ Command 283#====================================================================== 284# In math, \\ is just a formatting hint, unless within an array, cases, .. environment. 285DefConstructor("\\\\ OptionalMatch:* [Glue]", 286 "?#isMath(<ltx:XMHint name='newline'/>)(<ltx:break/>)", 287 reversion => Tokens(T_CS("\\\\"), T_CR), 288 properties => { isBreak => 1 }); 289 290DefConstructor('\newline', 291 "?#isMath(<ltx:XMHint name='newline'/>)(<ltx:break/>)", 292 reversion => Tokens(T_CS('\newline'), T_CR), 293 properties => { isBreak => 1 }); 294Let('\@normalcr', "\\\\"); 295Let('\@normalnewline', '\newline'); 296##PushValue(TEXT_MODE_BINDINGS => [ 297## T_CS("\\\\"), T_CS('\@normalcr'), T_CS('\newline'), T_CS('\@normalnewline')]); 298 299DefMacro('\@nolnerr', ''); 300DefMacro('\@centercr', '\ifhmode\unskip\else\@nolnerr\fi' 301 . '\par\@ifstar{\nobreak\@xcentercr}\@xcentercr'); 302DefMacro('\@xcentercr', '\addvspace{-\parskip}\@ifnextchar[\@icentercr\ignorespaces'); 303DefMacro('\@icentercr[]', '\vskip #1\ignorespaces'); 304 305#********************************************************************** 306# C.2. The Structure of the Document 307#********************************************************************** 308# prepended files (using filecontents environment) 309# preamble (starting with \documentclass) 310# \begin{document} 311# text 312# \end{document} 313 314DefMacro('\AtBeginDocument{}', sub { 315 PushValue('@at@begin@document', $_[1]->unlist); }); 316DefMacro('\AtEndDocument{}', sub { 317 PushValue('@at@end@document', $_[1]->unlist); }); 318 319# Like "<ltx:document xml:id='#id'>#body</ltx:document>", 320# But more complicated due to id, at begin/end document and so forth. 321# AND, lower-level so that we can cope with common errors at document end. 322DefConstructorI(T_CS('\begin{document}'), undef, sub { 323 my ($document, %props) = @_; 324 my $id = ToString($props{id}); 325 if (my $docel = $document->findnode('/ltx:document')) { # Already (auto) created? 326 $document->setAttribute($docel, 'xml:id' => $id) if $id; } 327 else { 328 $document->openElement('ltx:document', 'xml:id' => $id); } }, 329 afterDigest => sub { 330 my ($stomach, $whatsit) = @_; 331 $stomach->beginMode('text'); 332 DefMacroI('\@currenvir', undef, 'document'); 333 AssignValue(current_environment => 'document'); 334 $_[1]->setProperty(id => Expand(T_CS('\thedocument@ID'))); 335 my @boxes = (); 336 if (my $ops = LookupValue('@document@preamble@atend')) { 337 push(@boxes, $stomach->digest(Tokens(@$ops))); } 338 if (my $ops = LookupValue('@at@begin@document')) { 339 push(@boxes, $stomach->digest(Tokens(@$ops))); } 340 AssignValue(inPreamble => 0); # atbegin is still (sorta) preamble 341 if (my $ops = LookupValue('@document@preamble@afterend')) { 342 push(@boxes, $stomach->digest(Tokens(@$ops))); } 343 $_[1]->setFont(LookupValue('font')); # Start w/ whatever font was last selected. 344 return @boxes; }); 345 346# \document is used directly in e.g. expl3.sty 347Let(T_CS('\document'), T_CS('\begin{document}'), 'global'); 348 349DefConstructorI(T_CS('\end{document}'), undef, sub { 350 my ($document) = @_; 351 $document->closeElement('ltx:document'); }, 352 beforeDigest => sub { 353 my ($stomach) = @_; 354 my @boxes = (); 355 if (my $ops = LookupValue('@at@end@document')) { 356 push(@boxes, $stomach->digest(Tokens(@$ops))); } 357 # Should we try to indent the last paragraph? If so, it goes like this: 358 push(@boxes, $stomach->digest(T_CS('\normal@par'))); 359 # Now we check whether we're down to the last stack frame. 360 # It is common for unclosed { or even environments 361 # and we want to at least compress & avoid unnecessary errors & warnings. 362 my $nframes = $STATE->getFrameDepth; 363 my $ifstack; 364 if ($STATE->isValueBound('current_environment', 0) 365 && ($STATE->valueInFrame('current_environment', 0) eq 'document') 366 && (!($ifstack = $STATE->lookupValue('if_stack')) || !$$ifstack[0])) { } # OK! 367 else { 368 my @lines = (); 369 while ((!$STATE->isValueBound('current_environment', 0) 370 || ($STATE->valueInFrame('current_environment', 0) ne 'document')) 371 && ($STATE->getFrameDepth > 0)) { 372 # my $nonbox = $STATE->valueInFrame('groupNonBoxing',0) || 0; 373 my $tok = $STATE->valueInFrame('groupInitiator', 0) || '<unknown>'; 374 my $loc = $STATE->valueInFrame('groupInitiatorLocator', 0); 375 $loc = defined $loc ? ToString($loc) : '<unknown>'; 376 my $env = $STATE->isValueBound('current_environment', 0) 377 && $STATE->valueInFrame('current_environment', 0); 378 if ($env) { 379 push(@lines, "Environment $env opened by " . ToString($tok) . ' ' . $loc); } 380 else { # but unclosed { is so common and latex itself doesn't Error! 381 push(@lines, "Group opened by " . ToString($tok) . ' ' . $loc); } 382 $STATE->popFrame; } 383 while (($ifstack = $STATE->lookupValue('if_stack')) && $$ifstack[0]) { 384 my $frame = $STATE->shiftValue('if_stack'); 385 push(@lines, "Conditional " . ToString($$frame{token}) 386 . "started " . ToString($$frame{start})); } 387 Warn('unexpected', '\end{document}', $stomach, 388 "Attempt to end document with open groups, environments or conditionals", @lines); 389 } 390 $stomach->endMode('text'); 391 # ???? don't push? or what 392 # if (my $ops = LookupValue('@after@end@document')) { 393 # push(@boxes, Digest(Tokens(@$ops))); } 394 $stomach->getGullet->flush; 395 return @boxes; }); 396 397# \enddocument is used directly in e.g. standalone.cls 398Let(T_CS('\enddocument'), T_CS('\end{document}'), 'global'); 399 400#********************************************************************** 401# C.3. Sentences and Paragraphs 402#********************************************************************** 403 404#====================================================================== 405# C.3.1 Making Sentences 406#====================================================================== 407# quotes; should these be handled in DOM/construction? 408# dashes: We'll need some sort of Ligature analog, or something like 409# Omega's OTP, to combine sequences of "-" into endash, emdash, 410# Perhaps it also applies more semantically? 411# Such as interpreting certain sequences as section headings, 412# or math constructs. 413 414# Spacing; in TeX.pool.ltxml 415 416# Special Characters; in TeX.pool.ltxml 417 418# Logos 419# \TeX is in TeX.pool.ltxml 420##DefConstructorI('\LaTeX', undef, 'LaTeX'); 421##DefConstructorI('\LaTeXe', undef, 'LaTeX2e'); 422DefMacroI('\LaTeX', undef, 'LaTeX'); 423DefMacroI('\LaTeXe', undef, 'LaTeX2e'); 424DefMacroI('\fmtname', undef, 'LaTeX2e'); 425DefMacroI('\fmtversion', undef, '2018/12/01'); 426 427DefMacroI('\today', undef, sub { ExplodeText(today()); }); 428 429# Use fonts (w/ special flag) to propogate emphasis as a font change, 430# but preserve it's "emph"-ness. 431DefConstructor('\emph{}', 432 "<ltx:emph _force_font='1'>#1", 433 mode => 'text', bounded => 1, font => { emph => 1 }, alias => '\emph', 434 afterConstruct => sub { $_[0]->maybeCloseElement('ltx:emph'); }, 435 beforeDigest => sub { 436 DefMacroI('\f@shape', undef, 437 (ToString(Tokens(Expand(T_CS('\f@shape')))) eq 'it' ? 'n' : 'it')); }); 438Tag('ltx:emph', autoClose => 1); 439 440#====================================================================== 441# C.3.2 Making Paragraphs 442#====================================================================== 443# \noindent, \indent, \par in TeX.pool.ltxml 444 445Let('\@@par', '\par'); 446# Style parameters 447# \parindent, \baselineskip, \parskip alreadin in TeX.pool.ltxml 448 449DefPrimitive('\linespread{}', undef); 450 451# ? 452DefMacro('\@noligs', ''); 453DefConditional('\if@endpe'); 454DefMacro('\@doendpe', ''); 455DefMacro('\@bsphack', '\relax'); # what else? 456DefMacro('\@esphack', '\relax'); 457DefMacro('\@Esphack', '\relax'); 458#====================================================================== 459# C.3.3 Footnotes 460#====================================================================== 461 462NewCounter('footnote'); 463DefMacroI('\thefootnote', undef, '\arabic{footnote}'); 464NewCounter('mpfootnote'); 465DefMacroI('\thempfn', undef, '\thefootnote'); 466DefMacroI('\thempfootnote', undef, '\arabic{mpfootnote}'); 467DefMacroI('\footnotetyperefname', undef, 'footnote'); 468 469sub makeNoteTags { 470 my ($counter, $mark, $tag) = @_; 471 if ($tag) { 472 return ( 473 RefStepID($counter), 474 mark => $mark || $tag, 475 tags => Digest(T_BEGIN, 476 T_CS('\def'), T_CS('\the' . $counter), T_BEGIN, Revert($tag), T_END, 477 T_CS('\def'), T_CS('\typerefnum@' . $counter), 478 T_BEGIN, T_CS('\\' . $counter . 'typerefname'), T_SPACE, Revert($tag), T_END, 479 Invocation(T_CS('\lx@make@tags'), T_OTHER($counter)), 480 T_END)); } 481 else { 482 return (RefStepCounter($counter), 483 mark => $mark || DigestText(T_CS('\the' . $counter))); } } 484 485DefMacroI('\ext@footnote', undef, undef); 486DefConstructor('\lx@note[]{}[]{}', 487 "^<ltx:note role='#role' mark='#mark' xml:id='#id' inlist='#list'>" 488 . "#tags" 489 . "#4" 490 . "</ltx:note>", 491 mode => 'text', bounded => 1, 492 beforeDigest => sub { reenterTextMode(1); Digest('\normalfont'); }, 493 properties => sub { 494 my $type = ToString($_[2]); 495 (role => $type, 496 list => DigestText(T_CS('\ext@' . $type)), 497 makeNoteTags($type, $_[1], $_[3])); }, 498 reversion => ''); 499DefConstructor('\lx@notemark[]{}[]', 500 "<ltx:note role='#role' mark='#mark' xml:id='#id' inlist='#list'>" 501 . "#tags" 502 . "</ltx:note>", 503 mode => 'text', 504 properties => sub { 505 my $type = ToString($_[2]); 506 (role => $type . 'mark', 507 list => DigestText(T_CS('\ext@' . $type)), 508 makeNoteTags($type, $_[1], $_[3])); }, 509 reversion => ''); 510DefConstructor('\lx@notetext[]{}[]{}', 511 "<ltx:note role='#role' mark='#mark' xml:id='#id'>#4</ltx:note>", 512 mode => 'text', 513 properties => sub { 514 my $type = ToString($_[2]); 515 (role => $type . 'text', makeNoteTags($type, $_[1], $_[3] || Digest(T_CS('\the' . $type)))); }, 516 reversion => ''); 517 518DefMacro('\footnote', '\lx@note{footnote}'); 519DefMacro('\footnotemark', '\lx@notemark{footnote}'); 520DefMacro('\footnotetext', '\lx@notetext{footnote}'); 521 522Tag('ltx:note', afterClose => \&relocateFootnote); 523 524# Find any pairs of footnotemark & footnotetext; 525# Move the contents of the text to the mark, removing the text node. 526sub relocateFootnote { 527 my ($document, $node) = @_; 528 if (($node->getAttribute('role') || '') =~ /^(\w+?)text$/) { 529 my $notetype = $1; # Eg "footnote", "endnote",... 530 if (my $mark = $node->getAttribute('mark')) { 531 foreach my $marknote ($document->findnodes(".//ltx:note[\@role='${notetype}mark'][\@mark='$mark']")) { 532 relocateFootnote_aux($document, $notetype, $marknote, $node); } } } 533 elsif (($node->getAttribute('role') || '') =~ /^(\w+?)mark$/) { 534 my $notetype = $1; # Eg "footnote", "endnote",... 535 if (my $mark = $node->getAttribute('mark')) { 536 foreach my $textnote ($document->findnodes(".//ltx:note[\@role='${notetype}text'][\@mark='$mark']")) { 537 relocateFootnote_aux($document, $notetype, $node, $textnote); } } } 538 return; } 539 540# Move the contents of the $textnote to the $marknote, remove $textnote. 541sub relocateFootnote_aux { 542 my ($document, $notetype, $marknote, $textnote) = @_; 543 $textnote->parentNode->removeChild($textnote); 544 $document->appendClone($marknote, $textnote->childNodes); 545 $document->setAttribute($marknote, role => $notetype); 546 if (my $labels = $textnote->getAttribute('labels')) { 547 GenerateID($document, $marknote); 548 $document->setAttribute($marknote, labels => $labels); } 549 return; } 550 551# Style parameters 552DefRegister('\footnotesep' => Dimension(0)); 553DefPrimitiveI('\footnoterule', undef, undef); 554 555#====================================================================== 556# C.3.4 Accents and Special Symbols 557#====================================================================== 558# See TeX.pool.ltxml 559 560# See Section 3.3.2 Mathematical Symbols, below 561 562# Should this be here? 563DefMath('\mathring{}', "\x{030A}", operator_role => 'OVERACCENT'); 564 565#********************************************************************** 566# C.4 Sectioning and Table of Contents 567#********************************************************************** 568 569#====================================================================== 570# C.4.1 Sectioning Commands. 571#====================================================================== 572# Note that LaTeX allows fairly arbitrary stuff in \the<ctr>, although 573# it can get you in trouble. However, in almost all cases, the result 574# is plain text. So, I'm putting refnum as an attribute, where I like it! 575# You want something else? Redefine! 576 577# Also, we're adding an id to each, that is parallel to the refnum, but 578# valid as an ID. You can tune the representation by defining, eg. \thesection@ID 579 580# A little more messy than seems necessary: 581# We don't know whether to step the counter and update \@currentlabel until we see the '*', 582# but we have to know it before we digest the title, since \label can be there! 583 584# These are defined in terms of \@startsection so that 585# casual user redefinitions work, too. 586DefMacroI('\chapter', undef, '\@startsection{chapter}{0}{}{}{}{}', locked => 1); 587DefMacroI('\part', undef, '\@startsection{part}{-1}{}{}{}{}'); # not locked since sometimes redefined as partition? 588DefMacroI('\section', undef, '\@startsection{section}{1}{}{}{}{}', locked => 1); 589DefMacroI('\subsection', undef, '\@startsection{subsection}{2}{}{}{}{}', locked => 1); 590DefMacroI('\subsubsection', undef, '\@startsection{subsubsection}{3}{}{}{}{}', locked => 1); 591DefMacroI('\paragraph', undef, '\@startsection{paragraph}{4}{}{}{}{}', locked => 1); 592DefMacroI('\subparagraph', undef, '\@startsection{subparagraph}{5}{}{}{}{}', locked => 1); 593map { Tag("ltx:$_", autoClose => 1) } 594 qw(part chapter section subsection subsubsection paragraph subparagraph); 595 596DefMacro('\secdef {}{} OptionalMatch:*', sub { ($_[3] ? ($_[2]) : ($_[1])); }); 597 598DefMacroI('\@startsection@hook', undef, Tokens()); 599NewCounter('secnumdepth'); 600SetCounter('secnumdepth', Number(3)); 601DefMacro('\@startsection{}{}{}{}{}{} OptionalMatch:*', sub { 602 my ($gullet, $type, $level, $ignore3, $ignore4, $ignore5, $ignore6, $flag) = @_; 603 my $ctr = LookupValue('counter_for_' . ToString($type)) || ToString($type); 604 $level = ToString($level); 605 if ($flag) { # No number, not in TOC 606 (T_CS('\par'), T_CS('\@startsection@hook'), T_CS('\\@@unnumbered@section'), 607 T_BEGIN, $type->unlist, T_END, 608 T_BEGIN, T_END); } 609 elsif ((($level ne '') && ($level > CounterValue('secnumdepth')->valueOf)) 610 || LookupValue('no_number_sections')) { 611 # No number, but in TOC 612 (T_CS('\par'), T_CS('\@startsection@hook'), T_CS('\\@@unnumbered@section'), 613 T_BEGIN, $type->unlist, T_END, 614 T_BEGIN, T_OTHER('toc'), T_END); } 615 else { # Number and in TOC 616 (T_CS('\par'), T_CS('\@startsection@hook'), T_CS('\\@@numbered@section'), 617 T_BEGIN, $type->unlist, T_END, 618 T_BEGIN, T_OTHER('toc'), T_END); } }, 619 locked => 1); 620 621# Not sure if this is best, but if no explicit \section'ing... 622#### Tag('ltx:section',autoOpen=>1); 623 624DefConstructor('\@@numbered@section{} Undigested OptionalUndigested Undigested', sub { 625 my ($document, $type, $inlist, $toctitle, $title, %props) = @_; 626 my $id = $props{id}; 627 if (my $asif = $props{backmatterelement}) { 628 $document->setNode($document->find_insertion_point($asif)); } 629 $document->openElement("ltx:" . ToString($type), 630 'xml:id' => CleanID($id), 631 inlist => ToString($inlist)); 632 if (my $tags = $props{tags}) { 633 $document->absorb($tags); } 634 $document->insertElement('ltx:title', $props{title}); 635 $document->insertElement('ltx:toctitle', $props{toctitle}) if $props{toctitle}; }, 636 properties => sub { 637 my ($stomach, $type, $inlist, $toctitle, $title) = @_; 638 MaybePeekLabel(); 639 $type = ToString($type); 640 my %props = RefStepCounter($type); 641 my $xtitle = Digest(Invocation(T_CS('\lx@format@title@@'), $type, $title)); 642 my $xtoctitle = Digest(Invocation(T_CS('\lx@format@toctitle@@'), $type, $toctitle || $title)); 643 if ($type eq 'appendix') { 644 $props{backmatterelement} = LookupMapping('BACKMATTER_ELEMENT', 'ltx:' . $type); } 645 $props{title} = $xtitle; 646 $props{toctitle} = $xtoctitle 647 if $xtoctitle && $xtoctitle->unlist && (ToString($xtoctitle) ne ToString($xtitle)); 648 return %props; }); 649 650# No tags, at all? Consider... 651DefConstructor('\@@unnumbered@section{} Undigested OptionalUndigested Undigested', sub { 652 my ($document, $type, $inlist, $toctitle, $title, %props) = @_; 653 my $id = $props{id}; 654 if (my $asif = $props{backmatterelement}) { 655 $document->setNode($document->find_insertion_point($asif)); } 656 $document->openElement("ltx:" . ToString($type), 657 'xml:id' => CleanID($id), 658 inlist => ToString($inlist)); 659 $document->insertElement('ltx:title', $props{title}); 660 $document->insertElement('ltx:toctitle', $props{toctitle}) if $props{toctitle}; }, 661 properties => sub { 662 my ($stomach, $type, $inlist, $toctitle, $title) = @_; 663 MaybePeekLabel(); 664 $type = ToString($type); 665 my %props = RefStepID($type); 666 if ($type eq 'appendix') { 667 $props{backmatterelement} = LookupMapping('BACKMATTER_ELEMENT', 'ltx:' . $type); } 668 $props{title} = Digest(T_CS('\@hidden@bgroup'), 669 Invocation(T_CS('\lx@format@title@font@@'), $type), $title, T_CS('\@hidden@egroup')); 670 $props{toctitle} = $toctitle 671 && Digest(T_CS('\@hidden@bgroup'), Invocation(T_CS('\lx@format@toctitle@font@@'), $type), 672 $toctitle, T_CS('\@hidden@egroup')); 673 return %props; }); 674 675#====================================================================== 676# C.4.2 The Appendix 677#====================================================================== 678# Handled in article,report or book. 679DefMacroI('\appendixname', undef, 'Appendix'); 680DefMacroI('\appendixesname', undef, 'Appendixes'); 681 682# Class files should define \@appendix to call this as startAppendices('section') or chapter... 683# counter is also the element name! 684 685sub startAppendices { beginAppendices(@_); return; } 686 687sub beginAppendices { 688 my ($counter) = @_; 689 Let('\lx@save@theappendex', '\the' . $counter, 'global'); 690 Let('\lx@save@theappendex@ID', '\the' . $counter . '@ID', 'global'); 691 Let('\lx@save@appendix', T_CS('\\' . $counter), 'global'); 692 Let('\lx@save@@appendix', T_CS('\@appendix'), 'global'); 693 AssignMapping('BACKMATTER_ELEMENT', 'ltx:appendix' => 'ltx:' . $counter); 694 if (LookupDefinition(T_CS('\c@chapter')) # Has \chapter defined 695 && ($counter ne 'chapter')) { # And appendices are below the chapter level. 696 NewCounter($counter, 'chapter', idprefix => 'A'); 697 DefMacroI('\the' . $counter, undef, '\thechapter.\Alph{' . $counter . '}', scope => 'global'); } 698 else { 699 NewCounter($counter, 'document', idprefix => 'A'); 700 DefMacroI('\the' . $counter, undef, '\Alph{' . $counter . '}', scope => 'global'); } 701 AssignMapping('counter_for_type', appendix => $counter); 702 Let(T_CS('\\' . $counter), T_CS('\@@appendix'), 'global'); 703 Let(T_CS('\@appendix'), T_CS('\relax'), 'global'); 704 return; } 705 706sub endAppendices { 707 if (my $counter = LookupMapping('BACKMATTER_ELEMENT', 'ltx:appendix')) { 708 $counter =~ s/^ltx://; 709 Let('\the' . $counter, '\lx@save@theappendex', 'global'); 710 Let('\the' . $counter . '@ID', '\lx@save@theappendex@ID', 'global'); 711 Let(T_CS('\\' . $counter), '\lx@save@appendix', 'global'); 712 Let(T_CS('\@appendix'), '\lx@save@@appendix', 'global'); } 713 return; } 714 715DefMacroI('\\@@appendix', undef, '\@startsection{appendix}{0}{}{}{}{}'); 716 717#====================================================================== 718# C.4.3 Table of Contents 719#====================================================================== 720# Insert stubs that will be filled in during post processing. 721DefMacroI('\contentsname', undef, 'Contents'); 722DefConstructorI('\tableofcontents', undef, 723 "<ltx:TOC lists='toc' select='#select'><ltx:title>#name</ltx:title></ltx:TOC>", 724 properties => sub { 725 my $td = CounterValue('tocdepth')->valueOf + 1; 726 my @s = (qw(ltx:part ltx:chapter ltx:section ltx:subsection ltx:subsubsection 727 ltx:paragraph ltx:subparagraph)); 728 $td = $#s if $#s < $td; 729 @s = map { $s[$_] } 0 .. $td; 730 push(@s, (qw(ltx:appendix ltx:index ltx:bibliography))) if @s; 731 (select => join(' | ', @s), 732 name => Digest(T_CS('\contentsname'))); }); 733 734DefMacroI('\listfigurename', undef, 'List of Figures'); 735DefConstructorI('\listoffigures', undef, 736 "<ltx:TOC lists='lof' scope='global'><ltx:title>#name</ltx:title></ltx:TOC>", 737 properties => sub { (name => Digest(T_CS('\listfigurename'))); }); 738 739DefMacroI('\listtablename', undef, 'List of Tables'); 740DefConstructorI('\listoftables', undef, 741 "<ltx:TOC lists='lot' scope='global'><ltx:title>#name</ltx:title></ltx:TOC>", 742 properties => sub { (name => Digest(T_CS('\listtablename'))); }); 743 744DefPrimitive('\numberline{}{}', undef); 745DefPrimitive('\addtocontents{}{}', undef); 746 747DefConstructor('\addcontentsline{}{}{}', sub { 748 my ($document, $inlist, $type, $title) = @_; 749 # Note that the node can be inlist $inlist. 750 # Could conceivably want to add $title as toctitle??? 751 if (my $savenode = $document->floatToLabel) { 752 my $node = $document->getNode; 753 my $lists = $node->getAttribute('inlist'); 754 $inlist = ToString($inlist); 755 $document->setAttribute($node, inlist => ($lists ? $lists . ' ' . $inlist : $inlist)); 756 $document->setNode($savenode); } }); 757 758#====================================================================== 759# C.4.4 Style registers 760#====================================================================== 761NewCounter('tocdepth'); 762 763#********************************************************************** 764# C.5 Classes, Packages and Page Styles 765#********************************************************************** 766 767#====================================================================== 768# C.5.1 Document Class 769#====================================================================== 770# Style Parameters 771DefRegister('\bibindent' => Dimension(0)); 772DefRegister('\columnsep' => Dimension(0)); 773DefRegister('\columnseprule' => Dimension(0)); 774DefRegister('\mathindent' => Dimension(0)); 775 776#====================================================================== 777# C.5.2 Packages 778#====================================================================== 779# We'll prefer to load package.pm, but will try package.sty or 780# package.tex (the latter being unlikely to work, but....) 781# See Stomach.pm for details 782# Ignorable packages ?? 783# pre-defined packages?? 784 785DefMacroI('\@clsextension', undef, 'cls'); 786DefMacroI('\@pkgextension', undef, 'sty'); 787Let('\@currext', '\@empty'); 788Let('\@currname', '\@empty'); 789Let('\@classoptionslist', '\relax'); 790# Note that there are variables used in Package.pmfor these, 791# but they are NOT tied to these macros. Do they need to be? 792DefMacroI('\@declaredoptions', undef, undef); 793DefMacroI('\@curroptions', undef, undef); 794 795DefConstructor('\usepackage OptionalSemiverbatim Semiverbatim []', 796 "<?latexml package='#2' ?#1(options='#1')?>", 797 beforeDigest => sub { onlyPreamble('\usepackage'); }, 798 afterDigest => sub { my ($stomach, $whatsit) = @_; 799 my $options = $whatsit->getArg(1); 800 my $packages = $whatsit->getArg(2); 801 $options = [($options ? split(/\s*,\s*/, (ToString($options))) : ())]; 802 for my $pkg (split(',', ToString($packages))) { 803 $pkg =~ s/\s+//g; 804 next if !$pkg || $pkg =~ /^%/; 805 RequirePackage($pkg, options => $options); } 806 return }); 807 808DefConstructor('\RequirePackage OptionalSemiverbatim Semiverbatim []', 809 "<?latexml package='#2' ?#1(options='#1')?>", 810 beforeDigest => sub { onlyPreamble('\RequirePackage'); }, 811 afterDigest => sub { my ($stomach, $whatsit) = @_; 812 my $options = $whatsit->getArg(1); 813 my $packages = $whatsit->getArg(2); 814 $options = [($options ? split(/\s*,\s*/, (ToString($options))) : ())]; 815 for my $pkg (split(',', ToString($packages))) { 816 $pkg =~ s/\s+//g; 817 next if !$pkg || $pkg =~ /^%/; 818 RequirePackage($pkg, options => $options); } 819 return }); 820 821DefConstructor('\LoadClass OptionalSemiverbatim Semiverbatim []', 822 "<?latexml class='#2' ?#1(options='#1')?>", 823 beforeDigest => sub { onlyPreamble('\LoadClass'); }, 824 afterDigest => sub { my ($stomach, $whatsit) = @_; 825 my $options = $whatsit->getArg(1); 826 my $class = ToString($whatsit->getArg(2)); 827 $class =~ s/\s+//g; 828 $options = [($options ? split(/\s*,\s*/, (ToString($options))) : ())]; 829 LoadClass($class, options => $options); 830 return; }); 831 832# Related internal macros for package definition 833# Internals used in Packages 834DefMacro('\NeedsTeXFormat{}[]', Tokens()); 835 836DefPrimitive('\ProvidesClass{}[]', sub { 837 my ($stomach, $class, $version) = @_; 838 DefMacroI("\\ver@" . ToString($class) . ".cls", undef, $version || Tokens(), scope => 'global'); 839 return; }); 840 841# Note that these, like LaTeX, define macros like \var@mypkg.sty to give the version info. 842DefMacro('\ProvidesPackage{}[]', sub { 843 my ($stomach, $package, $version) = @_; 844 DefMacroI("\\ver@" . ToString($package) . ".sty", undef, $version || Tokens(), scope => 'global'); 845 return; }); 846 847DefMacro('\ProvidesFile{}[]', sub { 848 my ($stomach, $file, $version) = @_; 849 DefMacroI("\\ver@" . ToString($file), undef, $version || Tokens(), scope => 'global'); 850 return; }); 851 852DefPrimitive('\DeclareOption{}{}', sub { 853 my ($stomach, $option, $code) = @_; 854 ((ToString($option) eq '*') ? 855 DeclareOption(undef, $code) : 856 DeclareOption(ToString($option), $code)); }); 857 858DefPrimitive('\PassOptionsToPackage{}{}', sub { 859 my ($stomach, $name, $options) = @_; 860 $name = ToString($name); 861 $name =~ s/\s+//g; 862 PassOptions($name, 'sty', split(/\s*,\s*/, ToString(Expand($options)))); }); 863 864DefPrimitive('\PassOptionsToClass{}{}', sub { 865 my ($stomach, $name, $options) = @_; 866 $name = ToString($name); 867 $name =~ s/\s+//g; 868 PassOptions($name, 'cls', split(/\s*,\s*/, ToString(Expand($options)))); }); 869 870DefConstructor('\RequirePackageWithOptions Semiverbatim []', 871 "<?latexml package='#1'?>", 872 beforeDigest => sub { onlyPreamble('\RequirePackage'); }, 873 afterDigest => sub { my ($stomach, $whatsit) = @_; 874 my $package = ToString($whatsit->getArg(1)); 875 $package =~ s/\s+//g; 876 RequirePackage($package, withoptions => 1); 877 return; }); 878 879DefConstructor('\LoadClassWithOptions Semiverbatim []', 880 "<?latexml class='#1'?>", 881 beforeDigest => sub { onlyPreamble('\LoadClassWithOptions'); }, 882 afterDigest => sub { my ($stomach, $whatsit) = @_; 883 my $class = ToString($whatsit->getArg(1)); 884 $class =~ s/\s+//g; 885 LoadClass($class, withoptions => 1); 886 return; }); 887 888DefPrimitive('\@onefilewithoptions {} [][] {}', sub { 889 my ($stomach, $name, $option1, $option2, $ext) = @_; 890 InputDefinitions(ToString(Expand($name)), type => ToString(Expand($ext)), options => $option1); 891 return; }); 892 893DefMacroI('\CurrentOption', undef, Tokens()); 894 895DefPrimitiveI('\OptionNotUsed', undef, sub { 896 if (my $option = ToString(Expand(T_CS('\CurrentOption')))) { 897 my $type = ToString(Expand(T_CS('\@currext'))); 898 if ($type eq 'cls') { 899 PushValue('@unusedoptionlist', $option); } } 900 return; }); 901 902DefPrimitiveI('\@unknownoptionerror', undef, sub { 903 if (my $option = ToString(Expand(T_CS('\CurrentOption')))) { 904 my $name = ToString(Expand(T_CS('\@currname'))); 905 my $type = ToString(Expand(T_CS('\@currext'))); 906 Info('unexpected', $option, $_[0], "Unexpected option '$option' passed to $name.$type"); } 907 return; }); 908 909DefPrimitive('\ExecuteOptions{}', sub { 910 my ($gullet, $options) = @_; 911 ExecuteOptions(split(/\s*,\s*/, ToString(Expand($options)))); }); 912 913DefPrimitive('\ProcessOptions OptionalMatch:*', sub { 914 my ($stomach, $star) = @_; 915 ProcessOptions(($star ? (inorder => 1) : ())); }); 916DefMacro('\@options', '\ProcessOptions*'); 917 918DefMacro('\AtEndOfPackage{}', sub { 919 my ($gullet, $code) = @_; 920 my $name = ToString(Expand(T_CS('\@currname'))); 921 my $type = ToString(Expand(T_CS('\@currext'))); 922 AddToMacro(T_CS('\\' . $name . '.' . $type . '-h@@k'), $code); }); 923 924DefMacro('\@ifpackageloaded', '\@ifl@aded\@pkgextension'); 925DefMacro('\@ifclassloaded', '\@ifl@aded\@clsextension'); 926DefMacro('\@ifl@aded{}{}', sub { 927 my ($gullet, $ext, $name) = @_; 928 my $path = ToString(Expand($name)) . '.' . ToString(Expand($ext)); 929 # If EITHER the raw TeX or ltxml version of this file was loaded. 930 if (LookupValue($path . '_loaded') || LookupValue($path . '.ltxml_loaded')) { 931 T_CS('\@firstoftwo'); } 932 else { 933 T_CS('\@secondoftwo'); } }); 934 935DefMacro('\@ifpackagewith', '\@if@ptions\@pkgextension'); 936DefMacro('\@ifclasswith', '\@if@ptions\@clsextension'); 937DefMacro('\@if@ptions{}{}{}', sub { 938 my ($gullet, $ext, $name, $option) = @_; 939 $option = ToString(Expand($option)); 940 my $values = LookupValue('opt@' . ToString(Expand($name)) . '.' . ToString(Expand($ext))); 941 if (grep { $option eq $_ } @$values) { 942 T_CS('\@firstoftwo'); } 943 else { 944 T_CS('\@secondoftwo'); } }); 945 946DefMacro('\@ptionlist {}', '\@ifundefined{opt@#1}\@empty{\csname opt@#1\endcsname}'); 947 948DefMacro('\g@addto@macro DefToken {}', sub { AddToMacro($_[1], $_[2]); }); 949DefMacro('\addto@hook DefToken {}', '#1\expandafter{\the#1#2}'); 950 951# Alas, we're not tracking versions, so we'll assume it's "later" & cross fingers.... 952DefMacro('\@ifpackagelater{}{}{}{}', '#3'); 953DefMacro('\@ifclasslater{}{}{}{}', '#3'); 954Let('\AtEndOfClass', '\AtEndOfPackage'); 955 956DefMacro('\AtBeginDvi {}', Tokens()); 957 958RawTeX(<<'EoTeX'); 959\def\@ifl@t@r#1#2{% 960 \ifnum\expandafter\@parse@version@#1//00\@nil<% 961 \expandafter\@parse@version@#2//00\@nil 962 \expandafter\@secondoftwo 963 \else 964 \expandafter\@firstoftwo 965 \fi} 966\def\@parse@version@#1{\@parse@version0#1} 967\def\@parse@version#1/#2/#3#4#5\@nil{% 968\@parse@version@dash#1-#2-#3#4\@nil 969} 970\def\@parse@version@dash#1-#2-#3#4#5\@nil{% 971 \if\relax#2\relax\else#1\fi#2#3#4 } 972EoTeX 973 974#====================================================================== 975# Somewhat related I/O stuff 976DefMacro('\filename@parse{}', sub { 977 my ($gullet, $pathname) = @_; 978 my ($dir, $name, $ext) = pathname_split(ToString(Expand($pathname))); 979 $dir .= '/' if $dir; 980 DefMacroI('\filename@area', undef, Tokens(ExplodeText($dir))); 981 DefMacroI('\filename@base', undef, Tokens(ExplodeText($name))); 982 DefMacroI('\filename@ext', undef, ($ext ? Tokens(ExplodeText($ext)) : T_CS('\relax'))); }); 983 984DefMacroI('\@filelist', undef, Tokens()); 985DefMacro('\@addtofilelist{}', sub { 986 DefMacroI('\@filelist', undef, 987 Expand(T_CS('\@filelist'), T_OTHER(','), $_[1]->unlist)); }); 988 989#====================================================================== 990# C.5.3 Page Styles 991#====================================================================== 992# Ignored 993NewCounter('page'); 994DefPrimitive('\pagestyle{}', undef); 995DefPrimitive('\thispagestyle{}', undef); 996DefPrimitive('\markright{}', undef); 997DefPrimitive('\markboth{}{}', undef); 998DefPrimitiveI('\leftmark', undef, undef); 999DefPrimitiveI('\rightmark', undef, undef); 1000DefPrimitive('\pagenumbering{}', undef); 1001DefMacro('\@mkboth', '\@gobbletwo'); # default, just in case 1002DefMacro('\ps@empty', 1003 '\let\@mkboth\@gobbletwo\let\@oddhead\@empty\let\@oddfoot\@empty' . 1004 '\let\@evenhead\@empty\let\@evenfoot\@empty'); 1005DefMacro('\ps@plain', '\let\@mkboth\@gobbletwo' . 1006 '\let\@oddhead\@empty\def\@oddfoot{\reset@font\hfil\thepage' . 1007 '\hfil}\let\@evenhead\@empty\let\@evenfoot\@oddfoot'); 1008Let(T_CS('\@leftmark'), T_CS('\@firstoftwo')); 1009Let(T_CS('\@rightmark'), T_CS('\@secondoftwo')); 1010# In normal latex, these should \clearpage; at least we want new paragraph? 1011# Optional arg is sortof a heading, but w/o any particular styling(?) 1012DefMacro('\twocolumn[]', '\ifx.#1.\else\par\noindent#1\fi\par'); 1013DefMacro('\onecolumn', '\par'); 1014DefMacro('\@topnewpage{}', '#1'); 1015# Style parameters from Fig. C.3, p.182 1016DefRegister('\paperheight' => Dimension(0)); 1017DefRegister('\paperwidth' => Dimension(0)); 1018DefRegister('\textheight' => Dimension(0)); 1019DefRegister('\textwidth' => Dimension('6in')); 1020DefRegister('\topmargin' => Dimension(0)); 1021DefRegister('\headheight' => Dimension(0)); 1022DefRegister('\headsep' => Dimension(0)); 1023DefRegister('\footskip' => Dimension(0)); 1024DefRegister('\footheight' => Dimension(0)); 1025DefRegister('\evensidemargin' => Dimension(0)); 1026DefRegister('\oddsidemargin' => Dimension(0)); 1027DefRegister('\marginparwidth' => Dimension(0)); 1028DefRegister('\marginparsep' => Dimension(0)); 1029DefRegister('\columnwidth' => Dimension('6in')); 1030DefRegister('\linewidth' => Dimension('6in')); 1031DefRegister('\baselinestretch' => Dimension(0)); 1032 1033#====================================================================== 1034# C.5.4 The Title Page and Abstract 1035#====================================================================== 1036 1037# See frontmatter support in TeX.ltxml 1038Let('\@title', '\@empty'); 1039DefMacro('\title{}', '\def\@title{#1}\@add@frontmatter{ltx:title}{#1}', locked => 1); 1040 1041DefMacro('\@date', '\@empty'); 1042DefMacro('\date{}', 1043 '\def\@date{#1}' 1044 . '\@add@frontmatter{ltx:date}[role=creation,' 1045 . 'name={\@ifundefined{datename}{}{\datename}}]{#1}'); 1046 1047DefConstructor('\person@thanks{}', "^ <ltx:contact role='thanks'>#1</ltx:contact>", 1048 alias => '\thanks', mode => 'text'); 1049DefConstructor('\@personname{}', "<ltx:personname>#1</ltx:personname>", 1050 beforeDigest => sub { Let('\thanks', '\person@thanks'); }, 1051 bounded => 1, mode => 'text'); 1052 1053# Sanitize person names for (obvious) punctuation abuse at start+end 1054Tag('ltx:personname', afterClose => sub { 1055 my ($document, $node) = @_; 1056 if (my $first = $node->firstChild) { 1057 if ($first->nodeType == XML_TEXT_NODE) { 1058 my $first_text = $first->data; 1059 my $new_text = $first_text; 1060 $new_text =~ s/^\W+//; 1061 if ($first_text ne $new_text) { 1062 $first->setData($new_text); } } } 1063 if (my $last = $node->lastChild) { 1064 if ($last->nodeType == XML_TEXT_NODE) { 1065 my $last_text = $last->data; 1066 my $new_text = $last_text; 1067 $new_text =~ s/\W+$//; 1068 if ($last_text ne $new_text) { 1069 $last->setData($new_text); } } } 1070 return; }); 1071 1072DefConstructorI('\and', undef, " and "); 1073 1074AssignValue(NUMBER_OF_AUTHORS => 0); 1075DefPrimitive('\lx@count@author', sub { 1076 AssignValue(NUMBER_OF_AUTHORS => LookupValue('NUMBER_OF_AUTHORS') + 1, 'global') }); 1077DefMacro('\lx@author{}', 1078 '\lx@count@author' 1079 . '\@add@frontmatter{ltx:creator}[role=author]{\lx@author@prefix\@personname{#1}}'); 1080 1081DefConstructor('\lx@@@contact{}{}', "^ <ltx:contact role='#1'>#2</ltx:contact>"); 1082DefMacro('\lx@contact{}{}', 1083 '\@add@to@frontmatter{ltx:creator}{\lx@@@contact{#1}{#2}}'); 1084 1085DefMacro('\lx@author@sep', '\qquad'); 1086DefMacro('\lx@author@conj', '\qquad'); 1087DefConstructor('\lx@author@prefix', sub { 1088 my ($document) = @_; 1089 my $node = $document->getElement; 1090 my $nauthors = LookupValue('NUMBER_OF_AUTHORS'); 1091 my $i = scalar(@{ $document->findnodes('//ltx:creator[@role="author"]') }); 1092 if ($i <= 1) { } 1093 elsif ($i == $nauthors) { 1094 $document->setAttribute($node, before => ToString(Digest(T_CS('\lx@author@conj')))); } 1095 else { 1096 $document->setAttribute($node, before => ToString(Digest(T_CS('\lx@author@sep')))); } 1097}); 1098 1099DefMacro('\@author', '\@empty'); 1100DefMacro('\author[]{}', '\def\@author{#2}\lx@make@authors@anded{#2}', locked => 1); 1101DefMacro('\lx@make@authors@anded{}', sub { andSplit(T_CS('\lx@author'), $_[1]); }); 1102DefPrimitive('\ltx@authors@oneline', sub { 1103 AssignMapping('DOCUMENT_CLASSES', ltx_authors_1line => 1); 1104 return; }); 1105DefPrimitive('\ltx@authors@multiline', sub { 1106 AssignMapping('DOCUMENT_CLASSES', ltx_authors_multiline => 1); 1107 return; }); 1108 1109DefMacro('\@add@conversion@date', '\@add@frontmatter{ltx:date}[role=creation]{\today}'); 1110 1111# Doesn't produce anything (we're already inserting frontmatter), 1112# But, it does make the various frontmatter macros into no-ops. 1113DefMacroI('\maketitle', undef, 1114 '\@startsection@hook' 1115 . '\global\let\thanks\relax' 1116 . '\global\let\maketitle\relax' 1117 . '\global\let\@maketitle\relax' 1118 . '\global\let\@thanks\@empty' 1119 . '\global\let\@author\@empty' 1120 . '\global\let\@date\@empty' 1121 . '\global\let\@title\@empty' 1122 . '\global\let\title\relax' 1123 . '\global\let\author\relax' 1124 . '\global\let\date\relax' 1125 . '\global\let\and\relax', locked => 1); 1126 1127DefMacro('\@thanks', '\@empty'); 1128DefMacro('\thanks{}', '\def\@thanks{#1}\lx@make@thanks{#1}'); 1129DefConstructor('\lx@make@thanks{}', "<ltx:note role='thanks'>#1</ltx:note>"); 1130 1131# Abstract SHOULD have been so simple, but seems to be a magnet for abuse. 1132# For one thing, we'd like to just write 1133# DefEnvironment('{abstract}','<ltx:abstract>#body</ltx:abstract>'); 1134# However, we don't want to place the <ltx:abstract> environment directly where 1135# we found it, but we want to add it to frontmatter. This requires capturing the 1136# recently digested list and storing it in the frontmatter structure. 1137 1138# The really messy stuff comes from the way authors -- and style designers -- misuse it. 1139# Basic LaTeX wants it to be an environment WITHIN the document environment, 1140# and AFTER the \maketitle. 1141# However, since all it really does is typeset "Abstract" in bold, it allows: 1142# \abstract stuff... 1143# without even an \endabstract! We MUST know when the abstract ends, so we've got 1144# to recognize when we've moved on to other stuff... \sections at the VERY LEAST. 1145 1146# Additional complications come from certain other classes and styles that 1147# redefine abstract to take the text as an argument. And some treat it 1148# like \title, \author, and such, that are expected to appear in the preamble!! 1149# The treatment below allows an abstract environment in the preamble, 1150# (even though straight latex doesn't) but does not cover the 1-arg case in preamble! 1151# 1152# Probably there are other places (eg in titlepage?) that should force the close?? 1153 1154DefEnvironment('{abstract}', '', 1155 afterDigestBegin => sub { 1156 AssignValue(inPreamble => 0); }, 1157 afterDigest => sub { 1158 my $frontmatter = LookupValue('frontmatter'); 1159 push(@{ $$frontmatter{'ltx:abstract'} }, 1160 ['ltx:abstract', 1161 { name => Digest(Tokens(T_CS('\format@title@abstract'), 1162 T_BEGIN, T_CS('\abstractname'), T_END)) }, 1163 @LaTeXML::LIST]); 1164 DefMacroI('\maybe@end@abstract', undef, Tokens(), scope => 'global'); 1165 return; }, 1166 afterConstruct => sub { 1167 if (LookupValue('standalone_abstract_in_frontmatter')) { 1168 insertFrontMatter(@_); } 1169 return; }, 1170 locked => 1, mode => 'text'); 1171# If we get a plain \abstract, instead of an environment, look for \abstract{the abstract} 1172AssignValue('\abstract:locked' => 0); # REDEFINE the above locked definition! 1173DefMacro('\abstract', sub { 1174 my ($gullet) = @_; 1175 ($gullet->ifNext(T_BEGIN) 1176 ? (T_CS('\abstract@onearg')) 1177 : (T_CS('\g@addto@macro'), T_CS('\@startsection@hook'), T_CS('\maybe@end@abstract'), 1178 T_CS('\begin{abstract}'))); }, 1179 locked => 1); 1180DefMacro('\abstract@onearg{}', '\begin{abstract}#1\end{abstract}\let\endabstract\relax'); 1181 1182DefMacroI('\maybe@end@abstract', undef, '\endabstract'); 1183 1184DefMacroI('\abstractname', undef, 'Abstract'); 1185DefMacro('\format@title@abstract{}', '#1'); 1186 1187# Hmm, titlepage is likely to be hairy, low-level markup, 1188# without even title, author, etc, specified as such! 1189# Hmm, should this even redefine author, title, etc so that they 1190# are simply output? 1191# This is horrible hackery; What we really need, I think, is the 1192# ability to bind some sort of "Do <this> when we create a text box"... 1193# ON Second Thought... 1194# For the time being, ignore titlepage! 1195# Maybe we could do some of this if there is no title/author 1196# otherwise defined? Ugh! 1197 1198#DefEnvironment('{titlepage}',''); 1199# Or perhaps it's better just to ignore the markers? 1200#DefMacro('\titlepage',''); 1201#DefMacro('\endtitlepage',''); 1202 1203# Or perhaps not.... 1204# There's a title and other stuff in here, but how could we guess? 1205# Well, there's likely to be a sequence of <p><text font="xx" fontsize="yy">...</text></p> 1206# Presumably the earlier, larger one is title, rest are authors/affiliations... 1207# Particularly, if they start with a pseudo superscript or other "marker", they're probably affil! 1208# For now, we just give an info message 1209DefEnvironment('{titlepage}', '<ltx:titlepage>#body', 1210 beforeDigest => sub { Let('\centering', '\relax'); 1211 AddToMacro(T_CS('\maketitle'), T_CS('\unwind@titlepage')); 1212 DefEnvironmentI('abstract', undef, 1213 '<ltx:abstract>#body</ltx:abstract>'); 1214 Info('unexpected', 'titlepage', $_[0], 1215 "When using titlepage, Frontmatter will not be well-structured"); 1216 return; }, 1217 beforeDigestEnd => sub { Digest(T_CS('\maybe@end@titlepage')); }, 1218 locked => 1, mode => 'text'); 1219 1220Tag('ltx:titlepage', autoClose => 1); 1221DefConstructorI('\maybe@end@titlepage', undef, sub { 1222 my ($document) = @_; 1223 $document->maybeCloseElement('ltx:titlepage'); }); 1224DefConstructorI('\unwind@titlepage', undef, sub { 1225 my ($document) = @_; 1226 if (my $titlepage = $document->maybeCloseElement('ltx:titlepage')) { 1227 $document->unwrapNodes($titlepage); 1228 } 1229}); 1230 1231DefMacro('\sectionmark{}', Tokens()); 1232DefMacro('\subsectionmark{}', Tokens()); 1233DefMacro('\subsubsectionmark{}', Tokens()); 1234DefMacro('\paragraphmark{}', Tokens()); 1235DefMacro('\subparagraphmark{}', Tokens()); 1236DefMacroI('\@oddfoot', undef, Tokens()); 1237DefMacroI('\@oddhed', undef, Tokens()); 1238DefMacroI('\@evenfoot', undef, Tokens()); 1239DefMacroI('\@evenfoot', undef, Tokens()); 1240#********************************************************************** 1241# C.6 Displayed Paragraphs 1242#********************************************************************** 1243 1244DefEnvironment('{center}', sub { 1245 $_[0]->maybeCloseElement('ltx:p'); # this starts a new vertical block 1246 aligningEnvironment('center', 'ltx_centering', @_); }, # aligning will take care of \\\\ "rows" 1247 beforeDigest => sub { 1248 Let('\par', '\inner@par'); 1249 Let('\\\\', '\inner@par'); }); 1250# HOWEVER, define a plain \center to act like \centering (?) 1251DefMacroI('\center', undef, '\centering'); 1252DefMacroI('\endcenter', undef, ''); 1253 1254DefEnvironment('{flushleft}', sub { 1255 $_[0]->maybeCloseElement('ltx:p'); # this starts a new vertical block 1256 aligningEnvironment('left', 'ltx_align_left', @_); }, 1257 beforeDigest => sub { 1258 Let('\par', '\inner@par'); 1259 Let('\\\\', '\inner@par'); }); 1260DefEnvironment('{flushright}', sub { 1261 $_[0]->maybeCloseElement('ltx:p'); # this starts a new vertical block 1262 aligningEnvironment('right', 'ltx_align_right', @_); }, 1263 beforeDigest => sub { 1264 Let('\par', '\inner@par'); 1265 Let('\\\\', '\inner@par'); }); 1266 1267# These add an operation to be carried out on the current node & following siblings, when the current group ends. 1268# These operators will add alignment (class) attributes to each "line" in the current block. 1269#DefPrimitiveI('\centering', undef, sub { UnshiftValue(beforeAfterGroup=>T_CS('\@add@centering')); }); 1270# NOTE: THere's a problem here. The current method seems to work right for these operators 1271# appearing within the typical environments. HOWEVER, it doesn't work for a simple \bgroup or \begingroup!!! 1272# (they don't create a node! or even a whatsit!) 1273DefConstructorI('\centering', undef, 1274 sub { AssignValue(ALIGNING_NODE => $_[0]->getElement); return; }, 1275 beforeDigest => sub { UnshiftValue(beforeAfterGroup => T_CS('\@add@centering')); }); 1276DefConstructorI('\raggedright', undef, 1277 sub { AssignValue(ALIGNING_NODE => $_[0]->getElement); return; }, 1278 beforeDigest => sub { UnshiftValue(beforeAfterGroup => T_CS('\@add@raggedright')); }); 1279DefConstructorI('\raggedleft', undef, 1280 sub { AssignValue(ALIGNING_NODE => $_[0]->getElement); return; }, 1281 beforeDigest => sub { UnshiftValue(beforeAfterGroup => T_CS('\@add@raggedleft')); }); 1282 1283DefConstructorI('\@add@centering', undef, 1284 sub { if (my $node = LookupValue('ALIGNING_NODE')) { 1285 map { setAlignOrClass($_[0], $_, 'center', 'ltx_centering') } 1286 $_[0]->getChildElements($node); } }); 1287# Note that \raggedright is essentially align left 1288DefConstructorI('\@add@raggedright', undef, 1289 sub { if (my $node = LookupValue('ALIGNING_NODE')) { 1290 map { setAlignOrClass($_[0], $_, undef, 'ltx_align_left') } 1291 $_[0]->getChildElements($node); } }); 1292DefConstructorI('\@add@raggedleft', undef, 1293 sub { if (my $node = LookupValue('ALIGNING_NODE')) { 1294 map { setAlignOrClass($_[0], $_, undef, 'ltx_align_right') } 1295 $_[0]->getChildElements($node); } }); 1296 1297DefConstructorI('\@add@flushright', undef, 1298 sub { if (my $node = LookupValue('ALIGNING_NODE')) { 1299 map { setAlignOrClass($_[0], $_, 'right', 'ltx_align_right') } 1300 $_[0]->getChildElements($node); } }); 1301DefConstructorI('\@add@flushleft', undef, 1302 sub { if (my $node = LookupValue('ALIGNING_NODE')) { 1303 map { setAlignOrClass($_[0], $_, 'left', 'ltx_align_left') } 1304 $_[0]->getChildElements($node); } }); 1305 1306#======================================================================- 1307# C.6.1 Quotations and Verse 1308#======================================================================- 1309DefConstructor('\@block@cr[Dimension]', "<ltx:break/>\n", 1310 reversion => Tokens(T_CS("\\\\"), T_CR)); 1311DefEnvironment('{quote}', 1312 '<ltx:quote>#body</ltx:quote>', 1313 beforeDigest => sub { Let('\\\\', '\@block@cr'); Let('\par', '\@block@cr') }, 1314 mode => 'text'); 1315DefEnvironment('{quotation}', 1316 '<ltx:quote>#body</ltx:quote>', 1317 beforeDigest => sub { Let('\\\\', '\@block@cr'); Let('\par', '\@block@cr') }, 1318 mode => 'text'); 1319# NOTE: Handling of \\ within these environments? 1320DefEnvironment('{verse}', 1321 '<ltx:quote role="verse">#body</ltx:quote>', 1322 beforeDigest => sub { Let('\\\\', '\@block@cr'); Let('\par', '\@block@cr') }, 1323 mode => 'text'); 1324 1325#====================================================================== 1326# C.6.2 List-Making environments 1327#====================================================================== 1328Tag('ltx:item', autoClose => 1, autoOpen => 1); 1329Tag('ltx:inline-item', autoClose => 1, autoOpen => 1); 1330 1331# These are for the (not quite legit) case where \item appears outside 1332# of an itemize, enumerate, etc, environment. 1333# DefConstructor('\item[]', 1334# "<ltx:item>?&defined(#1)(<ltx:tags><ltx:tag>#1</ltx:tag></ltx:tags>)"); 1335# DefConstructor('\subitem[]', 1336# "<ltx:item>?&defined(#1)(<ltx:tags><ltx:tag>#1</ltx:tag></ltx:tags>)"); 1337# DefConstructor('\subsubitem[]', 1338# "<ltx:item>?&defined(#1)(<ltx:tags><ltx:tag>#1</ltx:tag></ltx:tags>)"); 1339 1340# Or maybe best just to do \par ? 1341DefMacro('\item[]', '\par'); 1342DefMacro('\subitem[]', '\par'); 1343DefMacro('\subsubitem[]', '\par'); 1344 1345AssignValue('@itemlevel' => 0, 'global'); 1346AssignValue('enumlevel' => 0, 'global'); 1347AssignValue('@desclevel' => 0, 'global'); 1348# protection against lower-level code... 1349DefConditional('\if@noitemarg'); 1350DefMacro('\@item', '\item'); # Hopefully no circles... 1351DefMacro('\@itemlabel', ''); # Maybe needs to be same as \item will be using? 1352 1353# Prepare for an list (itemize/enumerate/description/etc) 1354# by determining the right counter (level) 1355# and binding the right \item ( \$type@item, if $type is defined) 1356sub beginItemize { 1357 my ($type, $counter, %options) = @_; 1358 # The list-type and level of the *containing* list (if any!) 1359 my $outercounter = LookupValue('itemcounter'); 1360 my $outerlevel = $outercounter && (LookupValue($outercounter . 'level') || 0); 1361 $counter = '@item' unless $counter; 1362 my $listlevel = (LookupValue('itemization_level') || 0) + 1; # level for this list overall 1363 my $level = (LookupValue($counter . 'level') || 0) + 1; # level for lists of specific type 1364 AssignRegister('\itemsep' => LookupDimension('\lx@default@itemsep')); 1365 AssignValue('itemization_level' => $listlevel); 1366 AssignValue($counter . 'level' => $level); 1367 AssignValue(itemization_items => 0); 1368 my $listpostfix = ToString(Tokens(roman($listlevel))); 1369 my $postfix = ToString(Tokens(roman($level))); 1370 my $usecounter = ($options{nolevel} ? $counter : $counter . $postfix); 1371 Let('\item' => "\\" . $type . '@item') if defined $type; 1372 Let('\par', '\normal@par'); # In case within odd environment. 1373 DefMacroI('\@listctr', undef, Tokens(Explode($usecounter))); 1374 # Now arrange that this list's id's are relative to the current (outer) item (if any) 1375 # And that the items within this list's id's are relative to this (new) list. 1376## if(! LookupDefinition(T_CS('\@listcontext'))){ 1377## Let(T_CS('\@listcontext'), T_CS('\@currentlabel')); } 1378 AssignValue(itemcounter => $usecounter); 1379 my $listcounter = '@itemize' . $listpostfix; 1380 if (!LookupValue('\c@' . $listcounter)) { # Create new list counters as needed 1381 NewCounter($listcounter); } #, $outercounter.ToString(Tokens(roman($outerlevel))), 1382 if ($outercounter) { # Make this list's ID relative to outer list's ID 1383 my $outerusecounter = $outercounter . ToString(Tokens(roman($outerlevel))); 1384 DefMacroI('\the' . $listcounter . '@ID', undef, 1385 '\the' . $outerusecounter . '@ID.I' . '\arabic{' . $listcounter . '}'); 1386 # AND reset this list's counter when the outer item is stepped 1387 my $x; 1388 AssignValue("\\cl\@$outerusecounter" => 1389 Tokens(T_CS($listcounter), (($x = LookupValue('\cl@' . $outerusecounter)) ? $x->unlist : ())), 1390 'global'); 1391 } 1392 # format the id of \item's relative to the id of this list 1393 DefMacroI('\the' . $usecounter . '@ID', undef, '\the' . $listcounter . '@ID.i' . '\@' . $usecounter . '@ID'); 1394 1395 my $series; 1396 if ($series = $options{series}) { 1397 $series = ToString($series); } 1398 if (my $start = $options{start}) { 1399 SetCounter($usecounter, $start); 1400 AddToCounter($usecounter, Number(-1)); } 1401 elsif (my $s = $options{resume} || $options{'resume*'}) { 1402 if (($s = ToString($s)) ne 'noseries') { 1403 $series = ToString($s); 1404 SetCounter($usecounter, 1405 LookupValue('enumitem_series_' . $series . '_last') || Number(0)); } } 1406 else { 1407 ResetCounter($usecounter); } 1408 1409## return RefStepCounter('@itemize' . $listpostfix); } 1410 return (RefStepCounter('@itemize' . $listpostfix), 1411 counter => $usecounter, series => $series); } # So end can save counter value 1412 1413# These counters are ONLY used for id's of ALL the various itemize, enumerate, etc elements 1414# Only create the 1st level (so that binding style can start numbering 'within' appropriately) 1415# Additional ones created by need. 1416NewCounter('@itemizei', 'section', idprefix => 'I'); 1417 1418# Create id, and tags for an itemize type \item 1419sub RefStepItemCounter { 1420 my ($tag) = @_; 1421 my $counter = LookupValue('itemcounter'); 1422 my $n = LookupValue('itemization_items'); 1423 AssignValue(itemization_items => $n + 1); 1424 my %attr = (); 1425 my $sep = LookupDimension('\itemsep'); 1426 if (($n > 0) && $sep && ($sep->valueOf != LookupDimension('\lx@default@itemsep')->valueOf)) { 1427 $attr{itemsep} = $sep; } 1428 if (defined $tag) { 1429 my @props = RefStepID($counter); 1430 if ((ref $tag) && !scalar($tag->unlist)) { # empty tag? 1431 return (@props); } 1432 else { 1433 my $ttag = (ref $tag ? $tag : T_OTHER($tag)); 1434 my $formatter = ($counter =~ /^\@desc/ ? T_CS('\descriptionlabel') : T_CS('\makelabel')); 1435 my $typename = (IsDefined(T_CS('\\' . $counter . 'name')) 1436 ? T_CS('\\' . $counter . 'name') : T_CS('\itemtyperefname')); 1437 my $tags = Digest(T_BEGIN, 1438 T_CS('\let'), T_CS('\the' . $counter), T_CS('\@empty'), 1439 T_CS('\def'), T_CS('\fnum@' . $counter), T_BEGIN, $formatter, T_BEGIN, Revert($tag), T_END, T_END, 1440 T_CS('\def'), T_CS('\typerefnum@' . $counter), 1441 T_BEGIN, $typename, T_SPACE, Revert($tag), T_END, 1442 Invocation(T_CS('\lx@make@tags'), T_OTHER($counter)), 1443 T_END); 1444 1445 return (@props, 1446 ($tags ? (tags => $tags) : ()), 1447 %attr); } } 1448 else { 1449 return (RefStepCounter($counter), %attr); } } 1450 1451# The following two aren't used here; they're defined here so they 1452# can be used in paralist.sty, enumerate.sty (perhaps others?) 1453 1454# This isn't really satisfactory. 1455# We should record the marker used for the item, 1456# but it really should NOT be #refnum (which should be quasi unique) 1457# and is not \theenumi.. (which should be a counter value) 1458sub setItemizationStyle { 1459 my ($stuff, $level) = @_; 1460 if (defined $stuff) { 1461 $level = LookupValue('@itemlevel') || 0 unless defined $level; 1462 $level = ToString(Tokens(roman($level))); 1463 DefMacroI('\labelitem' . $level, undef, $stuff); } 1464 return; } 1465 1466sub setEnumerationStyle { 1467 my ($stuff, $level) = @_; 1468 if (defined $stuff) { 1469 $level = LookupValue('enumlevel') || 0 unless defined $level; 1470 $level = ToString(Tokens(roman($level))); 1471 my @in = $stuff->unlist; 1472 my @out = (); 1473 my $ctr = T_OTHER('enum' . $level); 1474 1475 while (my $t = shift(@in)) { 1476 if (Equals($t, T_BEGIN)) { 1477 push(@out, $t); 1478 my $brlevel = 1; 1479 while ($brlevel && ($t = shift(@in))) { 1480 if (Equals($t, T_BEGIN)) { $brlevel++; } 1481 elsif (Equals($t, T_END)) { $brlevel--; } 1482 push(@out, $t); } } 1483 elsif (Equals($t, T_LETTER('A'))) { 1484 DefMacroI('\theenum' . $level, undef, Invocation(T_CS('\Alph'), $ctr)); 1485 push(@out, T_CS('\theenum' . $level)); } 1486 elsif (Equals($t, T_LETTER('a'))) { 1487 DefMacroI('\theenum' . $level, undef, Invocation(T_CS('\alph'), $ctr)); 1488 push(@out, T_CS('\theenum' . $level)); } 1489 elsif (Equals($t, T_LETTER('I'))) { 1490 DefMacroI('\theenum' . $level, undef, Invocation(T_CS('\Roman'), $ctr)); 1491 push(@out, T_CS('\theenum' . $level)); } 1492 elsif (Equals($t, T_LETTER('i'))) { 1493 DefMacroI('\theenum' . $level, undef, Invocation(T_CS('\roman'), $ctr)); 1494 push(@out, T_CS('\theenum' . $level)); } 1495 elsif (Equals($t, T_OTHER('1'))) { 1496 DefMacroI('\theenum' . $level, undef, Invocation(T_CS('\arabic'), $ctr)); 1497 push(@out, T_CS('\theenum' . $level)); } 1498 else { 1499 push(@out, $t); } } 1500 DefMacroI('\labelenum' . $level, undef, Tokens(T_BEGIN, @out, T_END)); } 1501 return; } 1502 1503# id, but NO refnum (et.al) attributes on itemize \item ... 1504# unless the optional tag argument was given! 1505# We'll make the <ltx:tag> from either the optional arg, or from \labelitemi.. 1506DefMacro('\itemize@item', '\par\itemize@item@'); 1507DefConstructor('\itemize@item@ OptionalUndigested', 1508 "<ltx:item xml:id='#id' itemsep='#itemsep'>#tags", 1509 properties => sub { RefStepItemCounter($_[1]); }); 1510DefConstructor('\inline@itemize@item OptionalUndigested', 1511 "<ltx:inline-item xml:id='#id'>#tags", 1512 properties => sub { RefStepItemCounter($_[1]); }); 1513 1514DefMacro('\enumerate@item', '\par\enumerate@item@'); 1515DefConstructor('\enumerate@item@ OptionalUndigested', 1516 "<ltx:item xml:id='#id' itemsep='#itemsep'>#tags", 1517 properties => sub { RefStepItemCounter($_[1]); }); 1518DefConstructor('\inline@enumerate@item OptionalUndigested', 1519 "<ltx:inline-item xml:id='#id'>#tags", 1520 properties => sub { RefStepItemCounter($_[1]); }); 1521 1522DefMacro('\description@item', '\par\description@item@'); 1523DefConstructor('\description@item@ OptionalUndigested', 1524 "<ltx:item xml:id='#id' itemsep='#itemsep'>#tags", 1525 properties => sub { RefStepItemCounter($_[1]); }); 1526DefConstructor('\inline@description@item OptionalUndigested', 1527 "<ltx:inline-item xml:id='#id'>#tags", 1528 properties => sub { RefStepItemCounter($_[1]); }); 1529 1530DefEnvironment('{itemize}', 1531 "<ltx:itemize xml:id='#id'>#body</ltx:itemize>", 1532 properties => sub { beginItemize('itemize', '@item'); }, 1533 beforeDigestEnd => sub { Digest('\par'); }, 1534 locked => 1, mode => 'text'); 1535DefEnvironment('{enumerate}', 1536 "<ltx:enumerate xml:id='#id'>#body</ltx:enumerate>", 1537 properties => sub { beginItemize('enumerate', 'enum'); }, 1538 beforeDigestEnd => sub { Digest('\par'); }, 1539 locked => 1, mode => 'text'); 1540DefEnvironment('{description}', 1541 "<ltx:description xml:id='#id'>#body</ltx:description>", 1542 beforeDigest => sub { Let('\makelabel', '\descriptionlabel'); }, 1543 properties => sub { beginItemize('description', '@desc'); }, 1544 beforeDigestEnd => sub { Digest('\par'); }, 1545 locked => 1, mode => 'text'); 1546 1547DefMacro('\makelabel{}', '#1'); 1548 1549#---------------------------------------------------------------------- 1550# Basic itemize bits 1551# Fake counter for itemize to give id's to ltx:item. 1552NewCounter('@itemi', undef, idwithin => '@itemizei', idprefix => 'i'); 1553NewCounter('@itemii', undef, idwithin => '@itemi', idprefix => 'i'); 1554NewCounter('@itemiii', undef, idwithin => '@itemii', idprefix => 'i'); 1555NewCounter('@itemiv', undef, idwithin => '@itemiii', idprefix => 'i'); 1556NewCounter('@itemv', undef, idwithin => '@itemiv', idprefix => 'i'); 1557NewCounter('@itemvi', undef, idwithin => '@itemv', idprefix => 'i'); 1558# These are empty to make the 'refnum' go away. 1559DefMacroI('\the@itemi', undef, ''); 1560DefMacroI('\the@itemii', undef, ''); 1561DefMacroI('\the@itemiii', undef, ''); 1562DefMacroI('\the@itemiv', undef, ''); 1563DefMacroI('\the@itemv', undef, ''); 1564DefMacroI('\the@itemvi', undef, ''); 1565 1566# Formatted item tags. 1567# Really should be in the class file, but already was here. 1568DefMacroI('\labelitemi', undef, '\textbullet'); 1569DefMacroI('\labelitemii', undef, '\normalfont\bfseries \textendash'); 1570DefMacroI('\labelitemiii', undef, '\textasteriskcentered'); 1571DefMacroI('\labelitemiv', undef, '\textperiodcentered'); 1572 1573# Make the fake counters point to the real labels 1574DefMacroI('\label@itemi', undef, '\labelitemi'); 1575DefMacroI('\label@itemii', undef, '\labelitemii'); 1576DefMacroI('\label@itemiii', undef, '\labelitemiii'); 1577DefMacroI('\label@itemiv', undef, '\labelitemiv'); 1578 1579# These hookup latexml's tagging to normal latex's \labelitemi... 1580DefMacroI('\fnum@@itemi', undef, '{\makelabel{\label@itemi}}'); 1581DefMacroI('\fnum@@itemii', undef, '{\makelabel{\label@itemii}}'); 1582DefMacroI('\fnum@@itemiii', undef, '{\makelabel{\label@itemiii}}'); 1583DefMacroI('\fnum@@itemiv', undef, '{\makelabel{\label@itemiv}}'); 1584 1585# These define the typerefnum form, for out-of-context \ref's 1586# Better would language sensitive! 1587my @pm_ordinal_suffices = ('th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th'); 1588DefMacro('\lx@poormans@ordinal{}', sub { 1589 my ($gullet, $ctr) = @_; 1590 my $n = CounterValue($ctr)->valueOf; 1591 my $string = "$n"; 1592 if ($string =~ /.*?(\d)$/) { 1593 $string .= $pm_ordinal_suffices[$1]; } 1594 T_OTHER($string); }); 1595DefMacroI('\itemtyperefname', undef, 'item'); 1596DefMacroI('\itemcontext', undef, '\space in \@listcontext'); 1597DefMacroI('\itemcontext', undef, ''); 1598# Probably would help to give a bit more context for the ii & higher? 1599DefMacroI('\typerefnum@@itemi', undef, '\lx@poormans@ordinal{@itemi} \itemtyperefname \itemcontext'); 1600DefMacroI('\typerefnum@@itemii', undef, '\lx@poormans@ordinal{@itemii} \itemtyperefname \itemcontext'); 1601DefMacroI('\typerefnum@@itemiii', undef, '\lx@poormans@ordinal{@itemiii} \itemtyperefname \itemcontext'); 1602DefMacroI('\typerefnum@@itemiv', undef, '\lx@poormans@ordinal{@itemiv} \itemtyperefname \itemcontext'); 1603#---------------------------------------------------------------------- 1604# Basic enumeration bits 1605 1606# Class file should have 1607# NewCounter for enumi,..., 1608# define \labelenumi,... and probably \p@enumii... 1609 1610# How the refnums look... (probably should be in class file, but already here) 1611DefMacroI('\p@enumii', undef, '\theenumi'); 1612DefMacroI('\p@enumiii', undef, '\theenumi(\theenumii)'); 1613DefMacroI('\p@enumiv', undef, '\p@enumii\theenumiii'); 1614 1615# Formatting of item tags (probably should be in the class file, but already here) 1616DefMacroI('\labelenumi', undef, '\theenumi.'); 1617DefMacroI('\labelenumii', undef, '(\theenumii)'); 1618DefMacroI('\labelenumiii', undef, '\theenumiii.'); 1619DefMacroI('\labelenumiv', undef, '\theenumiv.'); 1620 1621# These hookup latexml's tagging to normal latex's \labelenummi... 1622DefMacroI('\fnum@enumi', undef, '{\makelabel{\labelenumi}}'); 1623DefMacroI('\fnum@enumii', undef, '{\makelabel{\labelenumii}}'); 1624DefMacroI('\fnum@enumiii', undef, '{\makelabel{\labelenumiii}}'); 1625DefMacroI('\fnum@enumiv', undef, '{\makelabel{\labelenumiv}}'); 1626 1627# These define the typerefnum form, for out-of-context \ref's 1628DefMacroI('\enumtyperefname', undef, 'item'); 1629DefMacroI('\typerefnum@enumi', undef, '\enumtyperefname~\p@enumi\theenumi \itemcontext'); 1630DefMacroI('\typerefnum@enumii', undef, '\enumtyperefname~\p@enumii\theenumii \itemcontext'); 1631DefMacroI('\typerefnum@enumiii', undef, '\enumtyperefname~\p@enumiii\theenumiii \itemcontext'); 1632DefMacroI('\typerefnum@enumiv', undef, '\enumtyperefname~\p@enumiv\theenumiv \itemcontext'); 1633#DefMacroI('\typerefnum@enumi', undef, '\enumtyperefname~\p@enumi\labelenumi \itemcontext'); 1634#DefMacroI('\typerefnum@enumii', undef, '\enumtyperefname~\p@enumii\labelenumii \itemcontext'); 1635#DefMacroI('\typerefnum@enumiii', undef, '\enumtyperefname~\p@enumiii\labelenumiii \itemcontext'); 1636#DefMacroI('\typerefnum@enumiv', undef, '\enumtyperefname~\p@enumiv\labelenumiv \itemcontext'); 1637 1638#---------------------------------------------------------------------- 1639# Basic description list bits 1640# Fake counter for itemize to give id's to ltx:item. 1641NewCounter('@desci', undef, idwithin => '@itemizei', idprefix => 'i'); 1642NewCounter('@descii', undef, idwithin => '@desci', idprefix => 'i'); 1643NewCounter('@desciii', undef, idwithin => '@descii', idprefix => 'i'); 1644NewCounter('@desciv', undef, idwithin => '@desciii', idprefix => 'i'); 1645NewCounter('@descv', undef, idwithin => '@desciv', idprefix => 'i'); 1646NewCounter('@descvi', undef, idwithin => '@descv', idprefix => 'i'); 1647# No refnum's here, either 1648DefMacroI('\the@desci', undef, ''); 1649DefMacroI('\the@descii', undef, ''); 1650DefMacroI('\the@desciii', undef, ''); 1651DefMacroI('\the@desciv', undef, ''); 1652DefMacroI('\the@descv', undef, ''); 1653DefMacroI('\the@descvi', undef, ''); 1654# These hookup latexml's numbering to normal latex's 1655# Umm.... but they're not normally used, since \item usually gets an argument! 1656DefMacro('\descriptionlabel{}', '\normalfont\bfseries #1'); 1657DefMacroI('\fnum@@desci', undef, '{\descriptionlabel{}}'); 1658DefMacroI('\fnum@@descii', undef, '{\descriptionlabel{}}'); 1659DefMacroI('\fnum@@desciii', undef, '{\descriptionlabel{}}'); 1660DefMacroI('\fnum@@desciv', undef, '{\descriptionlabel{}}'); 1661 1662DefMacroI('\desctyperefname', undef, 'item'); 1663 1664# Blech 1665map { DefMacroI(T_CS('\\' . $_ . 'name'), undef, '\itemtyperefname'); } 1666 qw(@itemi @itemii @itemiii @itemiv @itemv @itemvi); 1667map { DefMacroI(T_CS('\\' . $_ . 'name'), undef, '\enumtyperefname'); } 1668 qw(enumi enumii enumiii enumiv); 1669map { DefMacroI(T_CS('\\' . $_ . 'name'), undef, '\desctyperefname'); } 1670 qw(@desci @descii @desciii @desciv @descv @descvi); 1671 1672#====================================================================== 1673# C.6.3 The list and trivlist environments. 1674#====================================================================== 1675# Generic lists are given a way to format the item label, and presumably 1676# a counter. 1677 1678DefConditional('\if@nmbrlist'); 1679DefMacro('\@listctr', ''); 1680DefPrimitive('\usecounter{}', sub { 1681 my ($stomach, $counter) = @_; 1682 $counter = ToString(Expand($counter)); 1683 beginItemize('list', $counter, ($counter ? (nolevel => 1) : ())); 1684 return; }); 1685 1686DefMacro('\list{}{}', 1687'\let\@listctr\@empty#2\ifx\@listctr\@empty\usecounter{}\fi\expandafter\def\csname fnum@\@listctr\endcsname{#1}\lx@list'); 1688DefMacro('\endlist', '\endlx@list'); 1689 1690DefConstructor('\lx@list DigestedBody', 1691 "<ltx:itemize>#1</ltx:itemize>", 1692 beforeDigest => sub { $_[0]->bgroup; }); 1693DefPrimitive('\endlx@list', sub { $_[0]->egroup; }); 1694 1695DefConstructor('\list@item OptionalUndigested', 1696 "<ltx:item xml:id='#id' itemsep='#itemsep'>#tags", 1697 properties => sub { RefStepItemCounter($_[1]); }); 1698 1699# This isn't quite right, although it seems right for deep, internal uses with a single \item. 1700# Perhaps we need to check trivlist's afterwards and if they are just a single item, 1701# reduce it to an ltx:p ?? 1702# DefMacro('\trivlist@item[]', ''); 1703# DefEnvironment('{trivlist}', 1704# '<ltx:p>#body</ltx:p>', 1705# beforeDigest => sub { Let('\item', '\trivlist@item'); }); 1706 1707DefEnvironment('{trivlist}', 1708 "<ltx:itemize>#body</ltx:itemize>", 1709 properties => sub { beginItemize('trivlist'); }, 1710 beforeDigestEnd => sub { Digest('\par'); }); 1711 1712DefMacro('\trivlist@item', '\par\trivlist@item@'); 1713DefConstructor('\trivlist@item@ OptionalUndigested', 1714 "<ltx:item xml:id='#id' itemsep='#itemsep'>" 1715 . "<ltx:tags><ltx:tag>#tag</ltx:tag></ltx:tags>", # At least an empty tag! ? 1716 properties => sub { ($_[1] ? (tag => Digest(Expand($_[1]))) : ()); }); 1717 1718DefRegister('\topsep' => Glue(0)); 1719DefRegister('\partopsep' => Glue(0)); 1720DefRegister('\lx@default@itemsep' => Glue(0)); 1721DefRegister('\itemsep' => Glue(0)); 1722DefRegister('\parsep' => Glue(0)); 1723DefRegister('\@topsep' => Glue(0)); 1724DefRegister('\@topsepadd' => Glue(0)); 1725DefRegister('\@outerparskip' => Glue(0)); 1726DefRegister('\leftmargin' => Dimension(0)); 1727DefRegister('\rightmargin' => Dimension(0)); 1728DefRegister('\listparindent' => Dimension(0)); 1729DefRegister('\itemindent' => Dimension(0)); 1730DefRegister('\labelwidth' => Dimension(0)); 1731DefRegister('\labelsep' => Dimension(0)); 1732DefRegister('\@totalleftmargin' => Dimension(0)); 1733DefRegister('\leftmargini' => Dimension(0)); 1734DefRegister('\leftmarginii' => Dimension(0)); 1735DefRegister('\leftmarginiii' => Dimension(0)); 1736DefRegister('\leftmarginiv' => Dimension(0)); 1737DefRegister('\leftmarginv' => Dimension(0)); 1738DefRegister('\leftmarginvi' => Dimension(0)); 1739DefRegister('\@listdepth' => Number(0)); 1740DefRegister('\@itempenalty' => Number(0)); 1741DefRegister('\@beginparpenalty' => Number(0)); 1742DefRegister('\@endparpenalty' => Number(0)); 1743DefRegister('\labelwidthi' => Dimension(0)); 1744DefRegister('\labelwidthii' => Dimension(0)); 1745DefRegister('\labelwidthiii' => Dimension(0)); 1746DefRegister('\labelwidthiv' => Dimension(0)); 1747DefRegister('\labelwidthv' => Dimension(0)); 1748DefRegister('\labelwidthvi' => Dimension(0)); 1749 1750DefRegister('\@itemdepth' => Number(0)); 1751 1752#====================================================================== 1753# C.6.4 Verbatim 1754#====================================================================== 1755 1756# NOTE: how's the best way to get verbatim material through? 1757DefEnvironment('{verbatim}', '<ltx:verbatim>#body</ltx:verbatim>'); 1758DefEnvironment('{verbatim*}', '<ltx:verbatim>#body</ltx:verbatim>'); 1759 1760DefMacroI('\@verbatim', undef, 1761 '\par\aftergroup\lx@end@verbatim\lx@@verbatim'); # Close enough? 1762DefConstructorI('\lx@@verbatim', undef, 1763 "<ltx:verbatim font='#font'>", 1764 beforeDigest => sub { 1765 my ($stomach) = @_; 1766 StartSemiverbatim('%', '\\', '{', '}'); 1767 MergeFont(family => 'typewriter', series => 'medium', shape => 'upright'); 1768 $STATE->assignCatcode(' ', CC_ACTIVE); # Do NOT (necessarily) skip spaces after \verb!!! 1769 Let(T_ACTIVE(' '), T_SPACE); }); 1770DefConstructorI('\lx@end@verbatim', undef, 1771 "</ltx:verbatim>", 1772 beforeDigest => sub { 1773 EndSemiverbatim(); }); 1774 1775# verbatim is a bit of special case; 1776# It looks like an environment, but it only ends with an explicit "\end{verbatim}" on it's own line. 1777# So, we'll end up doing things more manually. 1778# We're going to sidestep the Gullet for inputting, 1779# and also the usual environment capture. 1780DefConstructorI(T_CS('\begin{verbatim}'), undef, 1781 "<ltx:verbatim font='#font'>#body</ltx:verbatim>", 1782 beforeDigest => [sub { $_[0]->bgroup; 1783 my @stuff = (); 1784 if (my $b = LookupValue('@environment@verbatim@atbegin')) { 1785 push(@stuff, Digest(@$b)); } 1786 AssignValue(current_environment => 'verbatim'); 1787 DefMacroI('\@currenvir', undef, 'verbatim'); 1788 MergeFont(family => 'typewriter'); 1789 # Digest(T_CS('\par')); # NO! See beforeConstruct! 1790 @stuff; }], 1791 afterDigest => [sub { 1792 my ($stomach, $whatsit) = @_; 1793 # $stomach->egroup; 1794 my $font = $whatsit->getFont; 1795 my $loc = $whatsit->getLocator; 1796 my $end = "\\end{verbatim}"; 1797 my @lines = (); 1798 my $gullet = $stomach->getGullet; 1799 while (defined(my $line = $gullet->readRawLine)) { 1800 # The raw chars will still have to be decoded (but not space!!) 1801 $line = join('', map { ($_ eq ' ' ? ' ' : FontDecodeString($_, 'OT1_typewriter')) } 1802 split(//, $line)); 1803 if ($line =~ /^(.*?)\\end\{verbatim\}(.*?)$/) { 1804 push(@lines, $1 . "\n"); $gullet->unread(Tokenize($2), T_CR); 1805 last; } 1806 push(@lines, $line . "\n"); } 1807 pop(@lines) if $lines[-1] eq "\n"; 1808 # Note last line ends up as Whatsit's "trailer" 1809 if (my $b = LookupValue('@environment@verbatim@atend')) { 1810 push(@lines, ToString(Digest(@$b))); } 1811 $stomach->egroup; 1812 $whatsit->setBody(map { Box($_, $font, $loc, T_OTHER($_)) } @lines, $end); 1813 return; }], 1814 beforeConstruct => sub { $_[0]->maybeCloseElement('ltx:p'); }); 1815 1816DefPrimitiveI('\@vobeyspaces', undef, sub { 1817 AssignCatcode(" " => 13); 1818 Let(T_ACTIVE(" "), '\nobreakspace'); 1819 return }); 1820 1821# WARNING: Need to be careful about what catcodes are active here 1822# And clearly separate expansion from digestion 1823DefMacroI('\verb', undef, sub { 1824 my ($gullet) = @_; 1825 StartSemiverbatim('%', '\\', '{', '}'); 1826 $STATE->assignCatcode(' ', CC_ACTIVE); # Do NOT (necessarily) skip spaces after \verb!!! 1827 my $init = $gullet->readToken; 1828 my $starred = 0; 1829 if (T_OTHER('*')->equals($init)) { 1830 $starred = 1; 1831 $init = $gullet->readToken; } 1832 if (!$init) { # typically something read too far got \verb and the content is somewhere else..? 1833 Error('expected', 'delimiter', $gullet, 1834 "Verbatim argument lost", "Bindings for preceding code is probably broken"); 1835 EndSemiverbatim(); 1836 return (); } 1837 my $init_str = $init->getString(); 1838 AssignCatcode($init_str => CC_ACTIVE); 1839 my $delim = T_ACTIVE($init_str); 1840 my $body = $gullet->readUntil($delim); 1841 EndSemiverbatim(); 1842 Tokens( 1843 T_CS('\@hidden@bgroup'), 1844 ($starred ? T_CS('\lx@use@visiblespace') : ()), 1845 Invocation(T_CS('\@internal@verb'), 1846 ($starred ? T_OTHER('*') : Tokens()), 1847 Tokens($init), $body), 1848 T_CS('\@hidden@egroup'))->unlist; }); 1849 1850DefPrimitive('\lx@use@visiblespace', sub { 1851 my ($stomach, $star) = @_; 1852 $STATE->assignCatcode(' ', CC_ACTIVE); # Do NOT (necessarily) skip spaces after \verb!!! 1853 Let(T_ACTIVE(' '), T_OTHER("\x{2423}")); # Visible space 1854}); 1855 1856DefConstructor('\@internal@verb{} Undigested {}', 1857 "?#isMath(<ltx:XMTok font='#font'>#text</ltx:XMTok>)" 1858 . "(<ltx:verbatim font='#font'>#text</ltx:verbatim>)", 1859 properties => sub { (text => ToString($_[3])); }, 1860 font => { family => 'typewriter', series => 'medium', shape => 'upright' }, 1861 # bounded => 1, 1862 beforeConstruct => sub { 1863 my ($doc, $whatsit) = @_; 1864 if (!$whatsit->isMath) { 1865 $doc->canContain($doc->getElement, '#PCDATA') || $doc->openElement('ltx:p'); } }, 1866 reversion => '\verb#1#2#3#2'); 1867 1868# This is defined by the alltt package. 1869# Environment('alltt', ?); 1870 1871# Actually, latex sets catcode to 13 ... is this close enough? 1872DefPrimitiveI('\obeycr', undef, sub { AssignValue('PRESERVE_NEWLINES' => 1); }); 1873DefPrimitiveI('\restorecr', undef, sub { AssignValue('PRESERVE_NEWLINES' => 0); }); 1874 1875DefMacroI('\normalsfcodes', undef, Tokens()); 1876 1877#********************************************************************** 1878# C.7 Mathematical Formulas 1879#********************************************************************** 1880 1881#====================================================================== 1882# C.7.1 Math Mode Environments 1883#====================================================================== 1884DefMacroI('\@eqnnum', undef, '(\theequation)'); 1885DefMacro('\fnum@equation', '\@eqnnum'); 1886 1887# Redefined from TeX.pool, since with LaTeX we presumably have a more complete numbering system 1888DefConstructorI('\@@BEGINDISPLAYMATH', undef, 1889 "<ltx:equation xml:id='#id'>" 1890 . "<ltx:Math mode='display'>" 1891 . "<ltx:XMath>" 1892 . "#body" 1893 . "</ltx:XMath>" 1894 . "</ltx:Math>" 1895 . "</ltx:equation>", 1896 alias => '$$', 1897 beforeDigest => sub { 1898 $_[0]->beginMode('display_math'); 1899 if (my @everymath_toks = $STATE->lookupDefinition(T_CS('\everymath'))->valueOf->unlist()) { 1900 $_[0]->getGullet->unread(@everymath_toks); } 1901 if (my @everydisplay_toks = $STATE->lookupDefinition(T_CS('\everydisplay'))->valueOf->unlist()) { 1902 $_[0]->getGullet->unread(@everydisplay_toks); } 1903 return; }, 1904 properties => sub { RefStepID('equation') }, 1905 captureBody => 1); 1906 1907DefEnvironment('{displaymath}', 1908 "<ltx:equation xml:id='#id'>" 1909 . "<ltx:Math mode='display'>" 1910 . "<ltx:XMath>" 1911 . "#body" 1912 . "</ltx:XMath>" 1913 . "</ltx:Math>" 1914 . "</ltx:equation>", 1915 mode => 'display_math', 1916 properties => sub { RefStepID('equation') }, 1917 locked => 1); 1918DefEnvironment('{math}', 1919 "<ltx:Math mode='inline'>" 1920 . "<ltx:XMath>" 1921 . "#body" 1922 . "</ltx:XMath>" 1923 . "</ltx:Math>", 1924 mode => 'inline_math', 1925); 1926 1927Let('\curr@math@size', '\@empty'); 1928 1929# Equation Numbering turns out to be rather convoluted! 1930# numbered equations & friends want to IMMEDIATELY increment the equation counter 1931# (you can see by \theequation) 1932# However, \nonumber, \tag, set a different number and RETRACT stepping the counter! 1933NewCounter('subequation', 'equation', idprefix => 'E', idwithin => 'equation'); 1934DefMacro('\thesubequation', '\theequation\alph{subequation}'); 1935DefMacro('\fnum@subequation', '(\thesubequation)'); 1936 1937# This provides {equation} with the capabilities for tags, nonumber, etc 1938# even though stock LaTeX provides no means to override them. 1939# preset => boolean 1940# postset => boolean 1941# deferretract=>boolean 1942sub prepareEquationCounter { 1943 my (%options) = @_; 1944 AssignValue(EQUATION_NUMBERING => {%options}, 'global'); 1945 return; } 1946 1947sub beforeEquation { 1948 my $numbering = LookupValue('EQUATION_NUMBERING'); 1949 my $numbered = $$numbering{numbered}; 1950 my $ctr = $$numbering{counter} || 'equation'; 1951 MaybePeekLabel(); 1952 $$numbering{in_equation} = 1; 1953 if ($$numbering{preset}) { 1954 AssignValue(EQUATIONROW_TAGS => { 1955 preset => 1, 1956 ($numbered ? RefStepCounter($ctr) : RefStepID($ctr)) }, 'global'); } 1957 else { 1958 AssignValue(EQUATIONROW_TAGS => {}, 'global'); } 1959 Let('\lx@saved@BEGINDISPLAYMATH', '\@@BEGINDISPLAYMATH'); 1960 Let('\lx@saved@ENDDISPLAYMATH', '\@@ENDDISPLAYMATH'); 1961 Let('\@@ENDDISPLAYMATH', '\lx@eDM@in@equation'); 1962 Let('\@@BEGINDISPLAYMATH', '\lx@bDM@in@equation'); 1963 return; } 1964 1965# Note peculiar usages of \[,\],$$ WITHIN displayed math, like {equation}. 1966# Authors sometimes use \begin{equation} math \] some text \[ more math \end{equation} 1967# to create a cheap intertext. And, the refnum appears on the SECOND equation! 1968# But in redefining these, note that a "valid" \[ math \] might be used within some 1969# sort of text insertion (eg \footnote)! 1970# So, we've got to dance around with redefinitions!! 1971# SIDE NOTE: \[,\] are really just $$ w/ appropriate error checking! 1972DefMacro('\lx@bDM@in@equation', '\lx@saved@BEGINDISPLAYMATH\let\@@ENDDISPLAYMATH\lx@saved@ENDDISPLAYMATH'); 1973DefMacro('\lx@eDM@in@equation', 1974 '\lx@retract@eqnno\lx@begin@fake@intertext' 1975 . '\let\lx@saved@BEGINDISPLAYMATH\@@BEGINDISPLAYMATH\let\lx@saved@bdm\[' 1976 . '\let\@@BEGINDISPLAYMATH\lx@end@fake@intertext' 1977 . '\let\[\lx@end@fake@intertext'); 1978 1979DefMacro('\lx@begin@fake@intertext', '\end{equation}'); 1980DefMacro('\lx@end@fake@intertext', 1981 '\let\@@BEGINDISPLAYMATH\lx@saved@BEGINDISPLAYMATH\let\[\lx@saved@bdm' . 1982 '\begin{equation}'); 1983DefPrimitive('\lx@retract@eqnno', sub { retractEquation(); }); 1984 1985sub retractEquation { 1986 my $numbering = LookupValue('EQUATION_NUMBERING'); 1987 # What about scopes? Is that handled automagically? 1988 # What about \@currentID.... 1989 my $tags = LookupValue('EQUATIONROW_TAGS'); 1990 my $ctr = $$tags{counter} || $$numbering{counter} || 'equation'; 1991 if ($$tags{preset}) { # ONLY if the number was preset! 1992 # counter (or ID counter) was stepped, so decrement it. 1993 AddToCounter(($$numbering{numbered} ? $ctr : 'UN' . $ctr), Number(-1)); } 1994 AssignValue(EQUATIONROW_TAGS => { RefStepID($ctr), reset => 1 }, 'global'); 1995 return; } 1996 1997# Disable this equation's number 1998# Ahhhh, but in eqnarray, this isn't effected until \\ !!!!! 1999DefMacroI('\nonumber', undef, '\lx@equation@nonumber'); 2000DefPrimitiveI('\lx@equation@nonumber', undef, sub { 2001 my $numbering = LookupValue('EQUATION_NUMBERING'); 2002 if ($$numbering{in_equation}) { 2003 if ($$numbering{deferretract}) { 2004 my $tags = LookupValue('EQUATIONROW_TAGS'); 2005 $$tags{retract} = 1; } 2006 else { 2007 retractEquation(); } } 2008 return; }); 2009Let('\@LTX@nonumber', '\lx@equation@nonumber'); 2010 2011# Set this equation's number explicitly (eg ams's \tag) 2012DefMacroI('\lx@equation@settag', undef, '\lx@equation@retract\lx@equation@settag@'); 2013DefPrimitiveI('\lx@equation@retract', undef, sub { retractEquation(); }); 2014DefPrimitive('\lx@equation@settag@ Digested', sub { 2015 my $tags = LookupValue('EQUATIONROW_TAGS'); 2016 $$tags{tags} = $_[1]; 2017 return; }, 2018 mode => 'text'); 2019 2020sub afterEquation { 2021 my ($whatsit) = @_; 2022 my $numbering = LookupValue('EQUATION_NUMBERING'); 2023 my $tags = LookupValue('EQUATIONROW_TAGS'); 2024 my $ctr = $$tags{counter} || $$numbering{counter} || 'equation'; 2025 if (!$$tags{noretract} 2026 && ($$tags{retract} || ($$numbering{retract} && $$numbering{preset} && $$tags{preset}))) { 2027 retractEquation(); } 2028 elsif ($$numbering{postset} && !$$tags{reset}) { 2029 # my %props = (); 2030 # if ($$numbering{numbered}) { 2031 # %props = RefStepCounter($ctr); } 2032 # else { 2033 # %props = RefStepID($ctr); } 2034 # AssignValue(EQUATIONROW_TAGS => {%props}, 'global'); } 2035 AssignValue(EQUATIONROW_TAGS => { 2036 ($$numbering{numbered} ? RefStepCounter($ctr) : RefStepID($ctr)) }, 'global'); } 2037 elsif (!$$tags{reset} && $$numbering{numbered}) { 2038 $$tags{tags} = Digest(Invocation(T_CS('\lx@make@tags'), $ctr)); } 2039 2040 # Now install the tags in $whatsit or current Row, as appropriate. 2041 my $props = LookupValue('EQUATIONROW_TAGS'); 2042 if ($$numbering{aligned}) { 2043 if (my $alignment = LookupValue('Alignment')) { 2044 my $row = $alignment->currentRow; 2045 $$row{id} = $$props{id}; 2046 $$row{tags} = $$props{tags}; } } 2047 elsif ($whatsit) { 2048 $whatsit->setProperties(%{ LookupValue('EQUATIONROW_TAGS') }); } 2049 $$numbering{in_equation} = 0; 2050 return; } 2051 2052# My first inclination is to Lock {math}, but it is surprisingly common to redefine it in silly ways... So...? 2053DefEnvironment('{equation}', 2054 "<ltx:equation xml:id='#id'>" 2055 . "#tags" 2056 . "<ltx:Math mode='display'>" 2057 . "<ltx:XMath>" 2058 . "#body" 2059 . "</ltx:XMath>" 2060 . "</ltx:Math>" 2061 . "</ltx:equation>", 2062 mode => 'display_math', 2063 beforeDigest => sub { 2064 prepareEquationCounter(numbered => 1, preset => 1); 2065 beforeEquation(); }, 2066 afterDigestBody => sub { 2067 afterEquation($_[1]); }, 2068 locked => 1); 2069 2070# Note: In ams, this DOES get a number if \tag is used! 2071DefEnvironment('{equation*}', 2072 "<ltx:equation xml:id='#id'>" 2073 . "#tags" 2074 . "<ltx:Math mode='display'>" 2075 . "<ltx:XMath>" 2076 . "#body" 2077 . "</ltx:XMath>" 2078 . "</ltx:Math>" 2079 . "</ltx:equation>", 2080 mode => 'display_math', 2081 beforeDigest => sub { 2082 prepareEquationCounter(numbered => undef, preset => 1); 2083 beforeEquation(); }, 2084 afterDigestBody => sub { 2085 afterEquation($_[1]); }, 2086 locked => 1); 2087 2088DefMacro('\[', '\@@BEGINDISPLAYMATH'); 2089DefMacro('\]', '\@@ENDDISPLAYMATH'); 2090DefMacro('\(', '\@@BEGININLINEMATH'); 2091DefMacro('\)', '\@@ENDINLINEMATH'); 2092 2093# Keep from expanding too early, if in alignments, or such. 2094DefMacroI('\ensuremath', undef, 2095 Tokens(T_CS('\protect'), T_CS('\@ensuremath'))); 2096DefMacro('\@ensuremath{}', sub { 2097 my ($gullet, $stuff) = @_; 2098 if (LookupValue('IN_MATH')) { $stuff->unlist; } 2099 else { (T_MATH, $stuff->unlist, T_MATH); } }); 2100 2101# Magic check that math-mode trigger follows 2102our $MATHENVS = 'displaymath|equation*?|eqnarray*?' 2103 . '|multline*?|align*?|falign*?|alignat*?|xalignat*?|xxalignat*?|gather*?'; 2104DefMacro('\ensuremathfollows', sub { 2105 my ($gullet) = @_; 2106 $gullet->closeMouth unless ($gullet->getMouth->hasMoreInput); 2107 if (my $tok = $gullet->readToken()) { 2108 my $csname = $tok->getCSName; 2109 if ($csname eq '\begin') { 2110 my $arg = $gullet->readArg(); 2111 $csname = $arg->toString; 2112 $gullet->unread(T_BEGIN, $arg->unlist, T_END); 2113 } 2114 $gullet->unread($tok); 2115 # We need to determine whether the TeX we're given needs to be wrapped in \(...\) 2116 # Does it have $'s around it? Does it have a display math environment? 2117 if ($csname !~ /^Math|\\\(|\\\[|(?:$MATHENVS)/o) { 2118 AssignValue('automath_triggered' => 1, 'global'); 2119 return T_CS('\\('); } } 2120 return; 2121}); 2122 2123DefMacro('\ensuremathpreceeds', sub { 2124 return LookupValue('automath_triggered') ? T_CS('\\)') : (); 2125}); 2126 2127# Since the arXMLiv folks keep wanting ids on all math, let's try this! 2128Tag('ltx:Math', afterOpen => sub { GenerateID(@_, 'm'); }); 2129 2130#====================================================================== 2131# Sub-numbered equations 2132# Although LaTeX itself doesn't provide such an environment, 2133# many other packages & classes do, and they seem essentially equivalent. 2134# They provide an environment to wrap around equations, 2135# which hijack the equation counter to sub-number within the group. 2136DefConstructor('\lx@equationgroup@subnumbering@begin', 2137 "<ltx:equationgroup xml:id='#id'>#tags", 2138 afterDigest => sub { 2139 my ($stomach, $whatsit) = @_; 2140 my %eqn = RefStepCounter('equation'); 2141 my $eqnum = Expand(T_CS('\theequation')); 2142 AssignValue(SAVED_EQUATION_NUMBER => LookupValue('\c@equation')); 2143 $whatsit->setProperties(%eqn); 2144 ResetCounter('equation'); 2145 DefMacroI('\theequation', undef, UnTeX($eqnum) . '\alph{equation}'); 2146 DefMacroI('\theequation@ID', undef, UnTeX($eqn{id}) . '.\@equation@ID'); }); 2147Tag('ltx:equationgroup', autoClose => 1); 2148DefConstructor('\lx@equationgroup@subnumbering@end', sub { 2149 $_[0]->maybeCloseElement('ltx:equationgroup'); }, 2150 afterDigest => sub { 2151 AssignValue('\c@equation', LookupValue('SAVED_EQUATION_NUMBER'), 'global'); }); 2152 2153#====================================================================== 2154# ======================================== 2155# eqnarray, etal 2156# Tricky! There's a conflict between a math-level alignment (which 2157# intermingles non-math things like labels, refnums, intertext), 2158# and a text-level alignment (which fragments the math's logical structure). 2159# Our solution is to attempt to synthesize a logical structure of 2160# an equationgroup containing equations, but using MathFork structures 2161# to hide the intended aligmnents. 2162# Then, XSLT can be used in the end to either display in a logical 2163# (but non-aligned format), or display fully aligned (but hiding the semantics). 2164#====================================================================== 2165# Equation Groups 2166# <equationgroup> representing aligned collections of equations 2167# in particular, eqnarray and amsmath's align, ... 2168# The intended usage is a sequence of patterns like 2169# LHS & REL & RHS 2170# When the LHS is empty, it likely implies a continuation of the previous (or multirelation). 2171# When both the LHS & REL are empty, it likely implies a continuation of the previous RHS 2172#====================================================================== 2173# The strategy here is to use the alignment mechanism to construct 2174# an initial form: 2175# <equationgroup> as the overall container 2176# <equation> for each row; this can receive id, refnum & label attributes 2177# <_Capture_> to capture each columns math. 2178# After the initial construction (but before any rewriting & parsing) 2179# we scan through the equations combining the ones that appear (heuristically) 2180# to be single equations on several rows (either as multi-relations, or 2181# rhs's of multiple rows) 2182# The combinations are represented by a MathFork container which 2183# holds both the apparently meaningful complete equation, along with 2184# the rows & columns that show the desired alignment. 2185# Each of those columns contains a <Math>, thus these can also be parsed, 2186# and converted to MathML or images. 2187# 2188# Thus, both forms are present: A presentation-oriented stylesheet can 2189# thus represent the eqnarray by a table with aligned math chunks. 2190# A content-oriented stylesheet can select the composed meaningful pieces. 2191 2192# ======================================== 2193# The following set deal with numbering the equations (rows) that make up an equationgroup. 2194# Note EQUATIONGROUP_NUMBER controls the numbering of the equations contained within the equationgroup, 2195# not the numbering of the equationgroup itself. 2196 2197DefPrimitive('\@equationgroup@numbering RequiredKeyVals', sub { 2198 my $kv = GetKeyVals($_[1]); 2199 prepareEquationCounter(map { ($_ => ToString($$kv{$_})) } keys %$kv); }); 2200 2201# ======================================== 2202# Some special kinds of rows... 2203DefConditionalI('\if@in@firstcolumn', undef, sub { 2204 my $x = LookupValue('Alignment'); 2205 $x = ($x ? $x->currentColumnNumber : 9); 2206 $x < 2; }); 2207# A bit more defensiveness, since people abuse eqnarray so badly. 2208# Eg. &&\lefteqn{...} whatever?!?! 2209DefMacro('\lefteqn{}', 2210'\if@in@firstcolumn\multicolumn{3}{l}{\@ADDCLASS{ltx_eqn_lefteqn}\@@BEGININLINEMATH \displaystyle #1\@@ENDINLINEMATH\mbox{}}' 2211 . '\else\rlap{\@@BEGININLINEMATH\displaystyle #1\@@ENDINLINEMATH}\fi'); 2212# \intertext (in amsmath) 2213 2214# ======================================== 2215# eqnarray 2216DefMacroI('\eqnarray', undef, 2217 '\@eqnarray@bindings\@@eqnarray' 2218 . '\@equationgroup@numbering{numbered=1,preset=1,deferretract=1,grouped=1,aligned=1}' 2219 . '\@start@alignment', 2220 locked => 1); 2221DefMacroI('\endeqnarray', undef, 2222 '\cr\@finish@alignment\end@eqnarray', 2223 locked => 1); 2224DefMacro('\csname eqnarray*\endcsname', 2225 '\@eqnarray@bindings\@@eqnarray' 2226 . '\@equationgroup@numbering{numbered=1,preset=1,retract=1,grouped=1,aligned=1}' 2227 . '\@start@alignment', 2228 locked => 1); 2229DefMacro('\csname endeqnarray*\endcsname', 2230 '\@finish@alignment\end@eqnarray', 2231 locked => 1); 2232 2233DefPrimitive('\@eqnarray@bindings', sub { 2234 eqnarrayBindings(); }); 2235 2236DefPrimitiveI('\eqnarray@row@before', undef, sub { beforeEquation(); }); 2237DefPrimitiveI('\eqnarray@row@after', undef, sub { afterEquation(); }); 2238 2239DefPrimitiveI('\eqnarray@row@before@', undef, sub { beforeEquation(); }); 2240DefPrimitiveI('\eqnarray@row@after@', undef, sub { afterEquation(); }); 2241DefMacroI('\eqnarray@row@before', undef, '\hidden@noalign{\eqnarray@row@before@}'); 2242DefMacroI('\eqnarray@row@after', undef, '\hidden@noalign{\eqnarray@row@after@}'); 2243 2244sub eqnarrayBindings { 2245 my $col1 = { before => Tokens(T_CS('\hfil'), T_MATH, T_CS('\displaystyle')), 2246 after => Tokens(T_MATH) }; 2247 my $col2 = { before => Tokens(T_CS('\hfil'), T_MATH, T_CS('\displaystyle')), 2248 after => Tokens(T_MATH, T_CS('\hfil')) }; 2249 my $col3 = { before => Tokens(T_MATH, T_CS('\displaystyle')), 2250 after => Tokens(T_MATH, T_CS('\hfil')) }; 2251 2252 my %attributes = ( 2253 'class' => 'ltx_eqn_eqnarray', 2254 'colsep' => LookupDimension('\arraycolsep')->multiply(2)); 2255 my $cur_jot = LookupDimension('\jot'); 2256 if ($cur_jot && ($cur_jot->valueOf != LookupDimension('\lx@default@jot')->valueOf)) { 2257 $attributes{rowsep} = $cur_jot; } 2258 2259 AssignValue(Alignment => LaTeXML::Core::Alignment->new( 2260 template => LaTeXML::Core::Alignment::Template->new(columns => [$col1, $col2, $col3]), 2261 openContainer => sub { my %attr = RefStepID('@equationgroup'); 2262 $attr{'xml:id'} = $attr{id}; delete $attr{id}; 2263 $attr{class} = 'ltx_eqn_eqnarray'; 2264 $_[0]->openElement('ltx:equationgroup', %attr, @_[1 .. $#_]); }, 2265 closeContainer => sub { $_[0]->closeElement('ltx:equationgroup'); }, 2266 openRow => sub { 2267 my ($doc, %props) = @_; 2268 my $tags = $props{tags}; delete($props{tags}); 2269 $doc->openElement('ltx:equation', %props); 2270 $doc->absorb($tags) if $tags; }, 2271 closeRow => sub { $_[0]->closeElement('ltx:equation'); }, 2272 openColumn => sub { $_[0]->openElement('ltx:_Capture_', @_[1 .. $#_]); }, 2273 closeColumn => sub { $_[0]->closeElement('ltx:_Capture_'); }, 2274 properties => { preserve_structure => 1, attributes => {%attributes} })); 2275 2276 Let("\\\\", '\@alignment@newline'); 2277 Let('\@row@before', '\eqnarray@row@before'); 2278 Let('\@row@after', '\eqnarray@row@after'); 2279 Let('\lx@eqnarray@save@label', '\label'); 2280 Let('\label', '\lx@eqnarray@label'); 2281 return; } 2282 2283# A \label preceding \lefteqn throws of the implied \omit,e tc; so wrap in \hidden@align 2284DefMacro('\lx@eqnarray@label Semiverbatim', 2285'\ifnum1>\@alignment@column\hidden@noalign{\lx@eqnarray@save@label{#1}}\else\lx@eqnarray@save@label{#1}\fi'); 2286 2287DefConstructor('\@@eqnarray SkipSpaces DigestedBody', 2288 '#1', 2289 beforeDigest => sub { $_[0]->bgroup; }, 2290 afterConstruct => sub { rearrangeEqnarray($_[0], $_[0]->getNode->lastChild); }); 2291DefPrimitiveI('\end@eqnarray', undef, sub { $_[0]->egroup; }); 2292 2293# ======================================== 2294# Some tools for analyzing the equationgroup after we've constructed it. 2295# ======================================== 2296 2297# ======================================== 2298# For eqnarray, the "meaningful" unit will be at least one row, 2299# but often multiple rows. 2300# When the 1st column is empty, the row is assumed to continue the previous equation (if any!) 2301# When the 2nd column is also empty, it presumably continues the previous RHS. 2302# We'll combine these cases into a single equation, but remember the alignment structure. 2303# However, if more than 1 such row has refnums, we probably don't want to combine; 2304# But we really need to find a better way of representing the information! 2305# Note that there are common misuses of eqnarray; 2306# One type tries to get the equivalent of amsmath's gather environment by 2307# using a single column for the equations; the equations are right, centered or 2308# left aligned, depending on which column was used. 2309# Can we detect continuations, and can we distinguish continuations of equations vs. RHS? 2310# Probably a similar misuse where only 2 columns are used? 2311sub rearrangeEqnarray { 2312 my ($document, $equationgroup) = @_; 2313 # Scan the "equations" (rows) within the $equationgroup 2314 # to see what pattern of columns are present. 2315 my @rows = (); 2316 foreach my $rownode ($document->findnodes('ltx:equation', $equationgroup)) { 2317 my @cells = $document->findnodes('ltx:_Capture_', $rownode); # representing each column. 2318 push(@rows, { node => $rownode, cols => [@cells], 2319 L => ($cells[0] && $cells[0]->hasChildNodes), 2320 M => ($cells[1] && $cells[1]->hasChildNodes), 2321 R => ($cells[2] && $cells[2]->hasChildNodes), 2322 numbered => ($document->findnode('ltx:tags', $rownode) ? 1 : 0), 2323 labelled => $rownode->hasAttribute('label') }); } 2324 my $nL = scalar(grep { $$_{L} } @rows); 2325 my $nM = scalar(grep { $$_{M} } @rows); 2326 my $nR = scalar(grep { $$_{R} } @rows); 2327 2328 # Only a single column was used. Remove the empty ones. 2329 # A heuristic: if any rows begin with a relation, there are probably continuations, 2330 # but maybe we don't want to try to distinguish the kinds? 2331 if (($nL && !$nM && !$nR) # All left column 2332 || (!$nL && $nM && !$nR) # All center column 2333 || (!$nL && !$nM && $nR)) { # All right column 2334 # We REALLY should remove the empty columns entirely! 2335 my $keepcol = ($nL ? 0 : ($nM ? 1 : 2)); 2336 # REMOVE empty columns! 2337 foreach my $c (2, 1, 0) { 2338 next if $c == $keepcol; 2339 foreach my $row (@rows) { 2340 $document->removeNode($$row{cols}[$c]); 2341 splice(@{ $$row{cols} }, $c, 1); } } 2342 # If (some) columns begin with a relation, presumably a single equation? 2343 # Or maybe we should only connect those to previous row? 2344 my $t; 2345 if (grep { ($t = $$_{cols}[0]) && ($t = $document->getFirstChildElement($t)) 2346 && (($t->getAttribute('role') || '') eq 'RELOP') } @rows) { 2347 equationgroupJoinRows($document, $equationgroup, map { $$_{node} } @rows); } 2348 else { 2349 # Really, these shouldn't even end up with MathFork! 2350 foreach my $row (@rows) { 2351 equationgroupJoinCols($document, 1, $$row{node}); } } 2352 return; } 2353 # What about case where only TWO columns get used? Worth analyzing? 2354 # Remaining case is when all 3 columns are used. 2355 my @eqs = (); 2356 my $numbered = 0; 2357 my $oddness = 0; 2358 foreach my $row (@rows) { 2359 my $class = 'unknown'; 2360 if ($$row{L}) { # 1st column non-empty; Presumably a new "equation" 2361 $class = 'new'; } 2362 elsif ($$row{M}) { # 1st is empty & 2nd non-empty (Rel?); Probably continues 2363 if (!scalar(@eqs)) { # But if no previous equation? 2364 $class = 'odd'; } 2365 elsif ($numbered && $$row{numbered}) { # Separately numbered continuation? 2366 $class = 'new'; } # Keep it as separate equation, even though missing LHS 2367 else { 2368 $class = 'continue'; } } # Continues as multiequation 2369 elsif ($$row{R}) { # 1st & 2nd is empty, non empty 3rd 2370 if (!scalar(@eqs)) { # But if no previous equation? 2371 $class = 'odd'; } 2372 elsif ($numbered && $$row{numbered} # Separately numbered AND labeled? 2373 && $$row{labelled}) { 2374 $class = 'odd'; } # must keep separate, but weird! 2375 else { 2376 $class = 'continue'; } } # Else, continues RHS. 2377 else { # All columns empty 2378 # Probably the trailling //, but who knows... 2379 # Arguably: if it has a number &/or is labelled 2380 # we should either prserve (as odd) or move info to previous line (if any) 2381 $class = 'remove'; } 2382 2383 if ($class eq 'remove') { 2384 $document->removeNode($$row{node}); } 2385 elsif (($class eq 'new') || ($class eq 'odd')) { 2386 $numbered = $$row{numbered}; 2387 push(@eqs, [$$row{node}]); 2388 $oddness++ if $class eq 'odd'; } 2389 else { 2390 $numbered |= $$row{numbered}; 2391 push(@{ $eqs[-1] }, $$row{node}); } 2392 } 2393 2394 Warn('unexpected', 'eqnarray', $equationgroup, 2395 "Unrecognized equation patterns ($oddness) in eqnarray") 2396 if (scalar(@rows) > 1) && $oddness; 2397 # Now rearrange things appropriately. 2398 foreach my $eqset (@eqs) { 2399 equationgroupJoinRows($document, $equationgroup, @$eqset); } 2400 return; } 2401 2402# Style Parameters 2403# \abovedisplayskip \abovedisplayshortskip, \jot are in TeX.pool 2404DefRegister('\mathindent' => Dimension(0)); 2405 2406#====================================================================== 2407# C.7.2 Common Structures 2408#====================================================================== 2409# sub, superscript and prime are in TeX.pool 2410# Underlying support in TeX.pool.ltxml 2411DefConstructor('\frac InFractionStyle InFractionStyle', 2412 "<ltx:XMApp>" 2413 . "<ltx:XMTok meaning='divide' role='FRACOP' mathstyle='#mathstyle'/>" 2414 . "<ltx:XMArg>#1</ltx:XMArg><ltx:XMArg>#2</ltx:XMArg>" 2415 . "</ltx:XMApp>", 2416 sizer => sub { fracSizer($_[0]->getArg(1), $_[0]->getArg(2)); }, 2417 properties => { mathstyle => sub { LookupValue('font')->getMathstyle; } }); 2418 2419# Ellipsis: See TeX.pool 2420 2421#====================================================================== 2422# C.7.3 Mathematical Symbols 2423#====================================================================== 2424# See Tables 3.3 through 3.8 (pp 41--44) 2425# Defined in TeX.pool 2426# [Possibly some are strictly LaTeX and should be moved here?] 2427 2428#====================================================================== 2429# C.7.4 Arrays 2430#====================================================================== 2431# See Section C.10.2 2432 2433#======================================================================- 2434# C.7.5 Delimiters 2435#======================================================================- 2436# All this is already in TeX.pool 2437 2438DefMacro('\stackrel{}{}', '\lx@stackrel{{\scriptstyle #1}}{{#2}}'); 2439DefConstructor('\lx@stackrel{}{}', 2440 "<ltx:XMApp role='RELOP'>" 2441 . "<ltx:XMTok role='SUPERSCRIPTOP' scriptpos='#scriptpos'/>" 2442 . "<ltx:XMArg>#2</ltx:XMArg>" 2443 . "<ltx:XMArg>#1</ltx:XMArg>" 2444 . "</ltx:XMApp>", 2445 reversion => '\stackrel{#1}{#2}', 2446 properties => { scriptpos => sub { "mid" . $_[0]->getScriptLevel; } } 2447); 2448#======================================================================- 2449# C.7.6 Putting One Thing Above Another 2450#======================================================================- 2451# All this is already in TeX.pool 2452 2453#======================================================================- 2454# C.7.7 Spacing 2455#======================================================================- 2456# All this is already in TeX.pool 2457 2458#====================================================================== 2459# C.7.8 Changing Style 2460#====================================================================== 2461# For Math style changes, we record the current font, which is then merged 2462# into the Whatsit's created for letters, etc. The merging depends on 2463# the type of letter, greek, symbol, etc. 2464# Apparently, with the normal TeX setup, these fonts don't really merge, 2465# rather they override all of family, series and shape. 2466DefConstructor('\mathrm{}', '#1', bounded => 1, requireMath => 1, 2467 font => { family => 'serif', series => 'medium', shape => 'upright' }); 2468DefConstructor('\mathit{}', '#1', bounded => 1, requireMath => 1, 2469 font => { shape => 'italic', family => 'serif', series => 'medium' }); 2470DefConstructor('\mathbf{}', '#1', bounded => 1, requireMath => 1, 2471 font => { series => 'bold', family => 'serif', shape => 'upright' }); 2472DefConstructor('\mathsf{}', '#1', bounded => 1, requireMath => 1, 2473 font => { family => 'sansserif', series => 'medium', shape => 'upright' }); 2474DefConstructor('\mathtt{}', '#1', bounded => 1, requireMath => 1, 2475 font => { family => 'typewriter', series => 'medium', shape => 'upright' }); 2476DefConstructor('\mathcal{}', '#1', bounded => 1, requireMath => 1, 2477 font => { family => 'caligraphic', series => 'medium', shape => 'upright' }); 2478DefConstructor('\mathscr{}', '#1', bounded => 1, requireMath => 1, 2479 font => { family => 'script', series => 'medium', shape => 'upright' }); 2480DefConstructor('\mathnormal{}', '#1', bounded => 1, requireMath => 1, 2481 font => { family => 'math', shape => 'italic', series => 'medium' }); 2482 2483DefMacroI('\fontsubfuzz', undef, '.4pt'); 2484DefMacroI('\oldstylenums', undef, Tokens()); 2485 2486DefPrimitiveI('\operator@font', undef, undef, 2487 font => { family => 'serif', series => 'medium', shape => 'upright' }); 2488 2489#********************************************************************** 2490# C.8 Definitions, Numbering and Programming 2491#********************************************************************** 2492 2493#====================================================================== 2494# C.8.1 Defining Commands 2495#====================================================================== 2496 2497DefMacro('\@tabacckludge {}', '\csname\string#1\endcsname'); 2498 2499DefPrimitive('\newcommand OptionalMatch:* SkipSpaces DefToken [Number][]{}', sub { 2500 my ($stomach, $star, $cs, $nargs, $opt, $body) = @_; 2501 if (!isDefinable($cs)) { 2502 Info('ignore', $cs, $stomach, 2503 "Ignoring redefinition (\\newcommand) of '" . ToString($cs) . "'") 2504 unless LookupValue(ToString($cs) . ':locked'); 2505 return; } 2506 DefMacroI($cs, convertLaTeXArgs($nargs, $opt), $body); }); 2507 2508DefPrimitive('\CheckCommand OptionalMatch:* SkipSpaces DefToken [Number][]{}', undef); 2509 2510DefPrimitive('\renewcommand OptionalMatch:* SkipSpaces DefToken [Number][]{}', sub { 2511 my ($stomach, $star, $cs, $nargs, $opt, $body) = @_; 2512 DefMacroI($cs, convertLaTeXArgs($nargs, $opt), $body); }); 2513 2514# low-level implementation of both \newcommand and \renewcommand depends on \@argdef 2515# and robustness upgrades are often realized via redefining \l@ngrel@x 2516DefPrimitive('\@argdef DefToken []{}', sub { 2517 DefMacroI($_[1], convertLaTeXArgs($_[2]), $_[3]); }); 2518DefPrimitive('\@xargdef DefToken [][]{}', sub { 2519 DefMacroI($_[1], convertLaTeXArgs($_[2], $_[3]), $_[4]); }); 2520DefPrimitive('\@yargdef DefToken DefToken {}{}', sub { 2521 DefMacroI($_[1], 2522 (T_CS('\tw@')->equals($_[2]) 2523 ? convertLaTeXArgs($_[3], Tokens()) 2524 : convertLaTeXArgs($_[3])), 2525 $_[4]); }); 2526DefPrimitive('\@reargdef DefToken []{}', sub { 2527 DefMacroI($_[1], convertLaTeXArgs($_[2]), $_[3]); }); 2528 2529DefPrimitive('\providecommand OptionalMatch:* SkipSpaces DefToken [Number][]{}', sub { 2530 my ($stomach, $star, $cs, $nargs, $opt, $body) = @_; 2531 return unless isDefinable($cs); 2532 DefMacroI($cs, convertLaTeXArgs($nargs, $opt), $body); }); 2533 2534# Crazy; define \cs in terms of \cs[space] !!! 2535DefPrimitive('\DeclareRobustCommand OptionalMatch:* SkipSpaces DefToken [Number][]{}', sub { 2536 my ($stomach, $star, $cs, $nargs, $opt, $body) = @_; 2537 DefMacroI($cs, convertLaTeXArgs($nargs, $opt), $body, robust => 1); }); 2538DefPrimitive('\MakeRobust DefToken', sub { 2539 my ($stomach, $cs) = @_; 2540 my $mungedcs = T_CS($cs->getString . ' '); 2541 if (!LookupDefinition($cs)) { } # Not defined 2542 elsif (LookupDefinition($mungedcs)) { } # Already robust 2543 else { 2544 Let($mungedcs, $cs); 2545 DefMacroI($cs, undef, Tokens(T_CS('\protect'), $mungedcs)); } }); 2546 2547# There are a bunch of ways in LaTeX to assign a command to 2548# a particular point within a font (which has one of many encodings) 2549# Since we have no practical way of knowing what that point is, 2550# and we really want to create unicode, we just ignore this stuff (but warn). 2551sub ignoredDefinition { 2552 my ($stomach, $command, $cs) = @_; 2553 # Warn('ignore',$cs,$stomach,"ignoring ".ToString($command)." definition of ".ToString($cs)); 2554 return; } 2555 2556#------------------------------------------------------------ 2557# The following commands define encoding-specific expansions 2558# or glyphs. The control-sequence is defined to use the expansion for 2559# the current encoding, if any, or the default expansion (for encoding "?"). 2560# We don't want to redefine control-sequence if it already has a definition: 2561# It may be that we've already defined it to expand into the above conditional. 2562# But more importantly, we don't want to override a hand-written definition (if any). 2563#------------------------------------------------------------ 2564DefPrimitive('\DeclareTextCommand DefToken {}[Number][]{}', sub { 2565 my ($gullet, $cs, $encoding, $nargs, $opt, $expansion) = @_; 2566 my $css = ToString($cs); 2567 $encoding = ToString(Expand($encoding)); 2568 if (!IsDefined($cs)) { # If not already defined... 2569 DefMacroI($cs, undef, 2570'\expandafter\ifx\csname\cf@encoding\string' . $css . '\endcsname\relax\csname?\string' . $css . '\endcsname' 2571 . '\else\csname\cf@encoding\string' . $css . '\endcsname\fi'); } 2572 my $ecs = T_CS('\\' . $encoding . $css); 2573 DefMacroI($ecs, convertLaTeXArgs($nargs, $opt), $expansion); 2574 return; }); 2575 2576DefMacro('\DeclareTextCommandDefault DefToken', '\DeclareTextCommand{#1}{?}'); 2577 2578DefPrimitive('\ProvideTextCommand DefToken {}[Number][]{}', sub { 2579 my ($gullet, $cs, $encoding, $nargs, $opt, $expansion) = @_; 2580 my $css = ToString($cs); 2581 $encoding = ToString(Expand($encoding)); 2582 if (isDefinable($cs)) { # If not already defined... 2583 DefMacroI($cs, undef, 2584'\expandafter\ifx\csname\cf@encoding\string' . $css . '\endcsname\relax\csname?\string' . $css . '\endcsname' 2585 . '\else\csname\cf@encoding\string' . $css . '\endcsname\fi'); } 2586 my $ecs = T_CS('\\' . $encoding . $css); 2587 if (!IsDefined($ecs)) { # If not already defined... 2588 DefMacroI($ecs, convertLaTeXArgs($nargs, $opt), $expansion); } 2589 return; }); 2590 2591DefMacro('\ProvideTextCommandDefault DefToken', '\ProvideTextCommand{#1}{?}'); 2592 2593#------------------------------------------------------------ 2594 2595DefPrimitive('\DeclareTextSymbol DefToken {}{Number}', sub { 2596 my ($gullet, $cs, $encoding, $code) = @_; 2597 $code = $code->valueOf; 2598 my $css = ToString($cs); 2599 $encoding = ToString(Expand($encoding)); 2600 if (isDefinable($cs)) { # If not already defined... 2601 DefMacroI($cs, undef, 2602'\expandafter\ifx\csname\cf@encoding\string' . $css . '\endcsname\relax\csname?\string' . $css . '\endcsname' 2603 . '\else\csname\cf@encoding\string' . $css . '\endcsname\fi'); } 2604 my $ecs = T_CS('\\' . $encoding . $css); 2605 DefPrimitiveI($ecs, undef, FontDecode($code, $encoding)); 2606 return; }); 2607 2608# hmmm... what needs doing here; basically it means use this encoding as the default for the symbol 2609DefMacro('\DeclareTextSymbolDefault DefToken {}', ''); # '\DeclareTextSymbol{#1}{?}'); 2610 2611#------------------------------------------------------------ 2612DefPrimitive('\DeclareTextAccent DefToken {}{}', sub { 2613 ignoredDefinition('DeclareTextAccent', $_[1]); }); 2614DefPrimitive('\DeclareTextAccentDefault{}{}', 2615 sub { ignoredDefinition('DeclareTextAccentDefault', $_[1]); }); 2616 2617#------------------------------------------------------------ 2618# TODO: Need to convert these DefPrimitive's into DefConstroctors 2619# that add a preable PI!!!!!!!!! 2620 2621DefPrimitive('\DeclareTextComposite{}{}{}{}', 2622 sub { ignoredDefinition('DeclareTextComposite', $_[1]); }); 2623DefPrimitive('\DeclareTextCompositeCommand{}{}{}{}', 2624 sub { ignoredDefinition('DeclareTextCompositeCommand', $_[1]); }); 2625 2626DefPrimitive('\UndeclareTextCommand{}{}', undef); 2627DefMacro('\UseTextSymbol{}{}', '{\fontencoding{#1}#2}'); 2628DefMacro('\UseTextAccent{}{}', '{\fontencoding{#1}#2{#3}}'); 2629 2630DefPrimitive('\DeclareMathAccent DefToken {}{} {Number}', sub { 2631 my ($stomach, $cs, $kind, $class, $code) = @_; 2632 $class = ToString($class); 2633 my $info = LookupValue('fontdeclaration@' . $class); 2634 my $glyph = FontDecode($code->valueOf, ($info ? $$info{encoding} : $class)); 2635 DefMathI($cs, 'Digested', $glyph, operator_role => 'OVERACCENT'); 2636 return AddToPreamble('\DeclareMathAccent', $cs, $kind, $class, $code); }); 2637 2638DefPrimitive('\DeclareMathDelimiter{}{}{}{}', 2639 sub { ignoredDefinition('DeclareMathDelimiter', $_[1]); }); 2640DefPrimitive('\DeclareMathRadical{}{}{}{}{}', 2641 sub { ignoredDefinition('DeclareMathRadical', $_[1]); }); 2642DefPrimitive('\DeclareMathVersion{}', undef); 2643DefPrimitive('\DeclarePreloadSizes{}{}{}{}{}', undef); 2644 2645# The next font declaration commands are based on 2646# http://tex.loria.fr/general/new/fntguide.html 2647# we ignore font encoding 2648DefPrimitive('\DeclareSymbolFont{}{}{}{}{}', sub { 2649 my ($stomach, $name, $enc, $family, $series, $shape) = @_; 2650 AssignValue('fontdeclaration@' . ToString($name), 2651 { family => ToString($family), 2652 series => ToString($series), 2653 shape => ToString($shape), 2654 encoding => ToString($enc) }); }); 2655DefPrimitive('\DeclareSymbolFontAlphabet{}{}', sub { 2656 my ($stomach, $cs, $name) = @_; 2657 my $font = LookupValue('fontdeclarations@' . ToString($name)) || {}; 2658 DefPrimitiveI(T_CS(ToString($cs)), undef, undef, font => $font); }); 2659 2660DefPrimitive('\DeclareMathSizes{}{}{}{}', undef); 2661DefPrimitive('\DeclareMathAlphabet{}{}{}{}{}', sub { 2662 my ($stomach, $cs, $enc, $family, $series, $shape) = @_; 2663 my $csname = ToString($cs); 2664 # We won't override this, e.g. \mathrm by fouriernc.sty 2665 if (IsDefined($csname)) { 2666 Info('ignore', $csname, $stomach, 2667 "Ignoring redefinition (\\DeclareMathAlphabet) of '" . $csname . "'"); } 2668 else { 2669 my %font = LaTeXML::Common::Font::lookupTeXFont($family, $series, $shape); 2670 DefPrimitiveI(T_CS($csname), undef, undef, font => {%font}); } 2671 return; }); 2672 2673DefMacro('\newmathalphabet{}{}{}', Tokens()); # or expand int DeclareMathAlphabet? 2674DefPrimitive('\DeclareFontShape{}{}{}{}{}{}', undef); 2675DefPrimitive('\DeclareFontFamily{}{}{}', undef); 2676DefPrimitive('\DeclareSizeFunction{}{}', undef); 2677 2678my $symboltype_roles = { 2679 '\mathord' => 'ID', '\mathop' => 'BIGOP', '\mathbin' => 'BINOP', '\mathrel' => 'RELOP', 2680 '\mathopen' => 'OPEN', '\mathclose' => 'CLOSE', '\mathpunct' => 'PUNCT' }; 2681DefPrimitive('\DeclareMathSymbol DefToken SkipSpaces DefToken {}{Number}', sub { 2682 my ($stomach, $cs, $type, $font, $code) = @_; 2683 my $encoding = ToString($font); # Or maybe just a font name or class? 2684 if (my $decl = LookupValue('fontdeclaration@' . $encoding)) { 2685 $encoding = $$decl{encoding} if $$decl{encoding}; } 2686 my $glyph = FontDecode($code->valueOf, $encoding); 2687 my $role = $$symboltype_roles{ ToString($type) }; 2688 DefMathI($cs, undef, $glyph, role => $role); 2689 return; }); 2690 2691DefPrimitive('\DeclareFixedFont{}{}{}{}{}{}', undef); 2692DefPrimitive('\DeclareErrorFont{}{}{}{}{}', undef); 2693 2694DefMacroI('\cdp@list', undef, '\@empty'); 2695Let('\cdp@elt', '\relax'); 2696DefPrimitive('\DeclareFontEncoding{}{}{}', sub { 2697 my ($stomach, $encoding, $x, $y) = @_; 2698 AddToMacro(T_CS('\cdp@list'), T_CS('\cdp@elt'), 2699 T_BEGIN, $_[1]->unlist, T_END, 2700 T_BEGIN, T_CS('\default@family'), T_END, 2701 T_BEGIN, T_CS('\default@series'), T_END, 2702 T_BEGIN, T_CS('\default@shape'), T_END); 2703 my $encoding_expanded = Expand($encoding); 2704 my $encoding_str = ToString($encoding_expanded); 2705 DefMacroI('\LastDeclaredEncoding', undef, $encoding_expanded); 2706 DefMacroI('\T@' . $encoding_str, undef, $x); 2707 DefMacroI('\M@' . $encoding_str, undef, Tokens(T_CS('\default@M'), $y->unlist)); 2708 if (my $path = $encoding_str && FindFile(lc($encoding_str) . "enc", type => "dfu")) { 2709 InputDefinitions($path); 2710 } 2711 return; 2712}); 2713DefMacroI('\LastDeclaredEncoding', undef, ''); 2714DefPrimitive('\DeclareFontSubstitution{}{}{}{}', undef); 2715DefPrimitive('\DeclareFontEncodingDefaults{}{}', undef); 2716DefMacroI('\LastDeclaredEncoding', undef, Tokens()); 2717 2718DefPrimitive('\SetSymbolFont{}{}{}{}{}{}', undef); 2719DefPrimitive('\SetMathAlphabet{}{}{}{}{}{}', undef); 2720DefPrimitive('\addtoversion{}{}', undef); 2721DefPrimitive('\TextSymbolUnavailable{}', undef); 2722 2723RawTeX(<<'EoTeX'); 2724\DeclareSymbolFont{operators} {OT1}{cmr} {m}{n} 2725\DeclareSymbolFont{letters} {OML}{cmm} {m}{it} 2726\DeclareSymbolFont{symbols} {OMS}{cmsy}{m}{n} 2727\DeclareSymbolFont{largesymbols}{OMX}{cmex}{m}{n} 2728EoTeX 2729 2730# At least all things on uclclist need to be macros 2731DefPrimitiveI('\lx@utf@OE', undef, "\x{0152}", alias => '\OE'); # LATIN CAPITAL LIGATURE OE 2732DefPrimitiveI('\lx@utf@oe', undef, "\x{0153}", alias => '\oe'); # LATIN SMALL LIGATURE OE 2733DefPrimitiveI('\lx@utf@AE', undef, UTF(0xC6), alias => '\AE'); # LATIN CAPITAL LETTER AE 2734DefPrimitiveI('\lx@utf@ae', undef, UTF(0xE6), alias => '\ae'); # LATIN SMALL LETTER AE 2735DefPrimitiveI('\lx@utf@AA', undef, UTF(0xC5), alias => '\AA'); # LATIN CAPITAL LETTER A WITH RING ABOVE 2736DefPrimitiveI('\lx@utf@aa', undef, UTF(0xE5), alias => '\aa'); # LATIN SMALL LETTER A WITH RING ABOVE 2737DefPrimitiveI('\lx@utf@O', undef, UTF(0xD8), alias => '\O'); # LATIN CAPITAL LETTER O WITH STROKE 2738DefPrimitiveI('\lx@utf@o', undef, UTF(0xF8), alias => '\o'); # LATIN SMALL LETTER O WITH STROKE 2739DefPrimitiveI('\lx@utf@L', undef, "\x{0141}", alias => '\L'); # LATIN CAPITAL LETTER L WITH STROKE 2740DefPrimitiveI('\lx@utf@l', undef, "\x{0142}", alias => '\l'); # LATIN SMALL LETTER L WITH STROKE 2741DefPrimitiveI('\lx@utf@ss', undef, UTF(0xDF), alias => '\ss'); # LATIN SMALL LETTER SHARP S 2742DefPrimitiveI('\lx@utf@dh', undef, UTF(0xf0), alias => '\dh'); # eth 2743DefPrimitiveI('\lx@utf@DH', undef, UTF(0xd0), alias => '\DH'); # Eth (looks same as \DJ!) 2744DefPrimitiveI('\lx@utf@dj', undef, "\x{0111}", alias => '\dj'); # d with stroke 2745DefPrimitiveI('\lx@utf@DJ', undef, "\x{0110}", alias => '\DJ'); # D with stroke (looks sames as \DH!) 2746DefPrimitiveI('\lx@utf@ng', undef, "\x{014B}", alias => '\ng'); 2747DefPrimitiveI('\lx@utf@NG', undef, "\x{014A}", alias => '\NG'); 2748DefPrimitiveI('\lx@utf@th', undef, UTF(0xFE), alias => '\th'); 2749DefPrimitiveI('\lx@utf@TH', undef, UTF(0xDE), alias => '\TH'); 2750 2751DefMacroI('\OE', undef, '\lx@utf@OE'); 2752DefMacroI('\oe', undef, '\lx@utf@oe'); 2753DefMacroI('\AE', undef, '\lx@utf@AE'); 2754DefMacroI('\ae', undef, '\lx@utf@ae'); 2755DefMacroI('\ae', undef, '\lx@utf@ae'); 2756DefMacroI('\AA', undef, '\lx@utf@AA'); 2757DefMacroI('\aa', undef, '\lx@utf@aa'); 2758DefMacroI('\O', undef, '\lx@utf@O'); 2759DefMacroI('\o', undef, '\lx@utf@o'); 2760DefMacroI('\L', undef, '\lx@utf@L'); 2761DefMacroI('\l', undef, '\lx@utf@l'); 2762DefMacroI('\ss', undef, '\lx@utf@ss'); 2763DefMacroI('\dh', undef, '\lx@utf@dh'); # in latex? 2764DefMacroI('\DH', undef, '\lx@utf@DH'); 2765DefMacroI('\dj', undef, '\lx@utf@dj'); 2766DefMacroI('\DJ', undef, '\lx@utf@DJ'); 2767DefMacroI('\ng', undef, '\lx@utf@ng'); 2768DefMacroI('\NG', undef, '\lx@utf@NG'); 2769DefMacroI('\th', undef, '\lx@utf@th'); 2770DefMacroI('\TH', undef, '\lx@utf@TH'); 2771 2772#====================================================================== 2773# C.8.2 Defining Environments 2774#====================================================================== 2775# Note that \env & \endenv defined by \newenvironment CAN be 2776# invoked directly. 2777 2778DefPrimitive('\newenvironment OptionalMatch:* {}[Number][]{}{}', sub { 2779 my ($stomach, $star, $name, $nargs, $opt, $begin, $end) = @_; 2780 $name = ToString(Expand($name)); 2781 if (IsDefined(T_CS("\\$name"))) { 2782 Info('ignore', $name, $stomach, 2783 "Ignoring redefinition (\\newenvironment) of Environment '$name'") 2784 unless (LookupValue('\\' . $name . ':locked') 2785 || LookupValue('\\begin{' . $name . '}:locked')); 2786 return; } 2787 DefMacroI(T_CS("\\$name"), convertLaTeXArgs($nargs, $opt), $begin); 2788 DefMacroI(T_CS("\\end$name"), undef, $end); 2789 return; }); 2790 2791DefPrimitive('\renewenvironment OptionalMatch:* {}[Number][]{}{}', sub { 2792 my ($stomach, $star, $name, $nargs, $opt, $begin, $end) = @_; 2793 $name = ToString(Expand($name)); 2794 if (!LookupValue("\\$name:locked") 2795 && !LookupValue("\\begin{$name}:locked")) { 2796 DefMacroI(T_CS("\\$name"), convertLaTeXArgs($nargs, $opt), $begin); 2797 DefMacroI(T_CS("\\end$name"), undef, $end); } 2798 return; }); 2799 2800#====================================================================== 2801# C.8.3 Theorem-like Environments 2802#====================================================================== 2803# The core non-customizable part is defined here. 2804# For customizable theorems, see amsthm. 2805AssignValue('thm@swap' => 0); 2806DefRegister('\thm@style' => Tokenize('plain')); 2807DefRegister('\thm@headfont' => Tokens(T_CS('\bfseries'))); 2808DefRegister('\thm@notefont' => Tokens(T_CS('\the'), T_CS('\thm@headfont'))); 2809DefRegister('\thm@bodyfont' => Tokens(T_CS('\itshape'))); 2810DefRegister('\thm@headpunct' => Tokens()); 2811DefRegister('\thm@styling' => Tokens()); 2812DefRegister('\thm@headstyling' => Tokens()); 2813DefRegister('\thm@prework' => Tokens()); 2814DefRegister('\thm@postwork' => Tokens()); 2815DefRegister('\thm@symbol' => Tokens()); 2816DefRegister('\thm@numbering' => Tokens(T_CS('\arabic'))); 2817 2818DefPrimitive('\th@plain', sub { 2819 AssignValue('\thm@bodyfont' => T_CS('\itshape')); 2820 AssignValue('\thm@headstyling' => T_CS('\lx@makerunin')); 2821 return; }); 2822 2823DefMacroI('\lx@makerunin', undef, '\@ADDCLASS{ltx_runin}'); 2824DefMacroI('\lx@makeoutdent', undef, '\@ADDCLASS{ltx_outdent}'); 2825 2826DefMacroI('\@thmcountersep', undef, '.'); 2827DefMacroI('\thm@doendmark', undef, ''); 2828 2829DefPrimitive('\newtheorem OptionalMatch:* {}[]{}[]', sub { 2830 my ($stomach, $flag, $thmset, $otherthmset, $type, $reset) = @_; 2831 defineNewTheorem($stomach, $flag, $thmset, $otherthmset, $type, $reset); 2832 # Reset these! 2833 DefRegister('\thm@prework' => Tokens()); 2834 DefRegister('\thm@postwork' => Tokens()); }, 2835 locked => 1); 2836 2837# These record which style parameters CAN BE saved/restored for each style. 2838# (kinda weird, but different packages use different sets; particularly \thm@headfont) 2839# THEOREM_<theoremname>_PARAMETERS will store the actual values for those parameters 2840AssignValue('SAVABLE_THEOREM_PARAMETERS' => {}); 2841 2842sub setSavableTheoremParameters { 2843 my (@keys) = @_; # Noe that these are saved for each theorem type. 2844 AssignValue('SAVABLE_THEOREM_PARAMETERS' => { map { $_ => 1; } @keys }); 2845 return; } 2846 2847# Following amsthm's model, the various theorem customizations are stored in token registers 2848# Some packages may want to add '\thm@headfont' (or exclude it) 2849setSavableTheoremParameters(qw( 2850 \thm@bodyfont \thm@headpunct \thm@styling \thm@headstyling thm@swap)); 2851 2852sub useTheoremStyle { 2853 my ($name) = @_; 2854 my $savable = LookupValue('SAVABLE_THEOREM_PARAMETERS'); 2855 if (my $params = LookupValue('THEOREM_' . ToString($name) . '_PARAMETERS')) { 2856 for my $param_key (keys %$savable) { 2857 if (exists $$params{$param_key}) { 2858 AssignValue($param_key => $$params{$param_key}); } 2859 } } 2860 return; } 2861 2862sub saveTheoremStyle { 2863 my ($name, %parameters) = @_; 2864 $name = ToString($name); 2865 AssignValue('THEOREM_' . $name . '_PARAMETERS' => {%parameters}, 'global'); 2866 return; } 2867 2868RawTeX('\th@plain'); # Activate the default style. 2869# [or just make it's values the defaults...] 2870 2871Tag('ltx:theorem', autoClose => 1); 2872Tag('ltx:proof', autoClose => 1); 2873 2874sub defineNewTheorem { 2875 my ($stomach, $flag, $thmset, $otherthmset, $type, $within) = @_; 2876 $thmset = ToString($thmset); 2877 my $classname = CleanClassName($thmset); 2878 my $listname = "theorem:$thmset"; $listname =~ s/\s//g; 2879 $otherthmset = $otherthmset && ToString($otherthmset); 2880 $type = undef unless $type && $type->unlist; 2881 $within = $within ? ToString($within) : undef; 2882 2883 my $counter = $otherthmset || $thmset; 2884 $counter =~ s/\s+/./; # convert spaces to period? 2885 if ($counter ne $thmset) { 2886 AssignMapping('counter_for_type', $thmset => $counter); 2887 DefMacroI(T_CS('\the' . $thmset), undef, T_CS('\the' . $counter), scope => 'global'); 2888 } 2889 my $style = ToString(LookupValue('\thm@style')); # The name of the style 2890 my $numbering = ToString(LookupValue('\thm@numbering')); # The number style macro 2891 $flag = 1 unless $numbering; 2892 if (!$otherthmset) { 2893 my $idprefix = "Thm$classname"; 2894 $idprefix =~ s/\*/./g; 2895 2896 if (!LookupValue('\c@' . $counter)) { # if isn't already defined... 2897 NewCounter($counter, $within, idprefix => $idprefix); } 2898 DefMacroI(T_CS("\\the$counter"), undef, 2899 ($within 2900 ? "\\csname the" . $within . "\\endcsname\\\@thmcountersep" . $numbering . "{" . $counter . "}" 2901 : $numbering . "{" . $counter . "}"), 2902 scope => 'global') if $numbering; } 2903 2904 my %parameters = %{ LookupValue('SAVABLE_THEOREM_PARAMETERS') }; 2905 saveTheoremStyle($thmset, map { ($_ => LookupValue($_)) } keys %parameters); 2906 2907 my $thmname = '\\lx@name@' . $thmset; 2908 DefMacroI($thmname, undef, $type, scope => 'global'); 2909 my $swap = LookupValue('thm@swap'); 2910 2911 DefMacroI('\fnum@' . $thmset, undef, 2912 Tokens($flag || !$counter 2913 ? (T_CS($thmname)) 2914 : ($swap 2915 ? (T_CS('\the' . $counter), ($type ? (T_SPACE) : ()), T_CS($thmname)) 2916 : (T_CS($thmname), ($type ? (T_SPACE) : ()), T_CS('\the' . $counter)))), 2917 scope => 'global'); 2918 # amsthm allows you to define your own title formatter 2919 # if there was one defined, use it, else define a new one. 2920 my $headformatter = LookupValue('\thm@headformatter'); 2921 DefMacroI('\format@title@' . $thmset, convertLaTeXArgs(1, 0), 2922 ($headformatter 2923 ? Tokens( 2924 T_CS('\the'), T_CS('\thm@headfont'), 2925 $headformatter->unlist, 2926 T_BEGIN, ($type ? $type->unlist : ()), T_END, 2927 T_CS('\the' . $counter), T_BEGIN, Tokens(T_PARAM, T_OTHER('1')), T_END, 2928 T_CS('\the'), T_CS('\thm@headpunct')) 2929 : '{\the\thm@headfont\lx@tag{\csname fnum@' . $thmset . '\endcsname}' 2930 . '{' . ($type ? '\ifx.#1.\else\space\the\thm@notefont(#1)\fi' : '#1') . '}' 2931 . '\the\thm@headpunct}'), 2932 2933 scope => 'global'); 2934 2935 DefEnvironmentI($thmset, "OptionalUndigested", 2936 "<ltx:theorem xml:id='#id' inlist='thm $listname' class='ltx_theorem_$classname'>" 2937 . "#tags" 2938 . "<ltx:title font='#titlefont' _force_font='true'>#title</ltx:title>" 2939 . "#body", 2940 afterConstruct => sub { $_[0]->maybeCloseElement('ltx:theorem'); }, 2941 beforeDigest => sub { 2942 useTheoremStyle($thmset); 2943 Digest('\normalfont\the\thm@prework'); }, 2944 afterDigestBegin => sub { 2945 my $name = $_[1]->getArg(1); 2946 Digest('\the\thm@bodyfont\the\thm@styling' 2947 . '\def\lx@thistheorem{' . ($name ? UnTeX($name) : '') . '}'); }, 2948 beforeDigestEnd => sub { Digest('\thm@doendmark'); }, 2949 afterDigest => sub { Digest('\the\thm@postwork'); }, 2950 properties => sub { 2951 my %ctr = (); 2952 if ($counter) { 2953 if ($flag) { 2954 %ctr = RefStepID($counter); 2955 # Need(?) the refnum 2956 if ($type) { 2957 $ctr{tags} = Digest(T_BEGIN, 2958 T_CS('\let'), T_CS('\the' . $counter), T_CS('\@empty'), 2959 Invocation(T_CS('\lx@make@tags'), T_OTHER($thmset)), 2960 T_END); 2961 } } 2962 else { 2963 %ctr = RefStepCounter($thmset); } } 2964 my $title = Digest(Tokens(T_BEGIN, 2965 T_CS('\the'), T_CS('\thm@headstyling'), 2966 T_CS('\format@title@' . $thmset), #T_END, 2967 T_BEGIN, ($_[1] ? $_[1]->unlist : ()), T_END, 2968 T_END)); 2969 (%ctr, 2970 title => $title, 2971 titlefont => $title->getFont, 2972 ); }, 2973 scope => 'global'); 2974 return; } 2975 2976#====================================================================== 2977# C.8.4 Numbering 2978#====================================================================== 2979# For LaTeX documents, We want id's on para, as well as sectional units. 2980# However, para get created implicitly on Document construction, rather than 2981# explicitly during digestion (via a whatsit), we can't use the usual LaTeX counter mechanism. 2982Tag('ltx:para', afterOpen => sub { GenerateID(@_, 'p'); }); 2983 2984DefPrimitive('\newcounter{}[]', sub { 2985 NewCounter(ToString(Expand($_[1])), $_[2] && ToString(Expand($_[2]))); 2986 return; }); 2987Let(T_CS('\@definecounter'), T_CS('\newcounter')); 2988DefPrimitive('\setcounter{}{Number}', sub { SetCounter(ToString(Expand($_[1])), $_[2]); }); 2989DefPrimitive('\addtocounter{}{Number}', sub { AddToCounter(ToString(Expand($_[1])), $_[2]); }); 2990DefPrimitive('\stepcounter{}', sub { StepCounter(ToString(Expand($_[1]))); return; }); 2991DefPrimitive('\refstepcounter{}', sub { RefStepCounter(ToString(Expand($_[1]))); return; }); 2992 2993sub addtoCounterReset { 2994 my ($ctr, $within) = @_; 2995 my $reg = "\\cl\@$within"; 2996 my $t = T_CS($ctr); 2997 my $unt = T_CS('UN' . $ctr); 2998 my $prevreset = LookupValue($reg); 2999 AssignValue($reg => Tokens($t, $unt, ($prevreset ? $prevreset->unlist : ())), 'global'); 3000 return; } 3001 3002sub remfromCounterReset { 3003 my ($ctr, $within) = @_; 3004 my $reg = "\\cl\@$within"; 3005 my $t = T_CS($ctr); 3006 my $unt = T_CS('UN' . $ctr); 3007 my $prevreset = LookupValue($reg); 3008 AssignValue($reg => Tokens(grep { !$t->equals($_) && !$unt->equals($_) } $prevreset->unlist), 3009 'global') if $prevreset; 3010 return; } 3011 3012sub defCounterID { 3013 my ($ctr, $within) = @_; 3014 my $prefix = LookupValue('@ID@prefix@' . $ctr); 3015 if (defined $prefix) { 3016 if ($within) { 3017 DefMacroI(T_CS("\\the$ctr\@ID"), undef, 3018 "\\expandafter\\ifx\\csname the$within\@ID\\endcsname\\\@empty" 3019 . "\\else\\csname the$within\@ID\\endcsname.\\fi" 3020 . " $prefix\\csname \@$ctr\@ID\\endcsname", 3021 scope => 'global'); } 3022 else { 3023 DefMacroI(T_CS("\\the$ctr\@ID"), undef, "$prefix\\csname \@$ctr\@ID\\endcsname", 3024 scope => 'global'); } } 3025 return; } 3026 3027DefPrimitive('\@addtoreset{}{}', sub { 3028 my ($stomach, $ctr, $within) = @_; 3029 $ctr = ToString(Expand($ctr)); 3030 $within = ToString(Expand($within)); 3031 addtoCounterReset($ctr, $within); 3032 defCounterID($ctr, $within) unless LookupDefinition(T_CS("\\the$ctr\@ID")); 3033 return; }); 3034 3035DefMacro('\value{}', sub { 3036 T_CS("\\c@" . ToString(Expand($_[1]))) }); 3037DefMacro('\@arabic{Number}', sub { 3038 ExplodeText(ToString($_[1]->valueOf)); }); 3039DefMacro('\arabic{}', sub { 3040 ExplodeText(CounterValue(ToString(Expand($_[1])))->valueOf); }); 3041DefMacro('\@roman{Number}', sub { 3042 ExplodeText(radix_roman(ToString($_[1]->valueOf))); }); 3043DefMacro('\roman{}', sub { 3044 ExplodeText(radix_roman(CounterValue(ToString(Expand($_[1])))->valueOf)); }); 3045DefMacro('\@Roman{Number}', sub { 3046 ExplodeText(radix_Roman(ToString($_[1]->valueOf))); }); 3047DefMacro('\Roman{}', sub { 3048 ExplodeText(radix_Roman(CounterValue(ToString(Expand($_[1])))->valueOf)); }); 3049DefMacro('\@alph{Number}', sub { 3050 ExplodeText(radix_alpha($_[1]->valueOf)); }); 3051DefMacro('\alph{}', sub { 3052 ExplodeText(radix_alpha(CounterValue(ToString(Expand($_[1])))->valueOf)); }); 3053DefMacro('\@Alph{Number}', sub { 3054 ExplodeText(radix_Alpha($_[1]->valueOf)); }); 3055DefMacro('\Alph{}', sub { 3056 ExplodeText(radix_Alpha(CounterValue(ToString(Expand($_[1])))->valueOf)); }); 3057 3058our @fnsymbols = ("*", "\x{2020}", "\x{2021}", UTF(0xA7), UTF(0xB6), 3059 "\x{2225}", "**", "\x{2020}\x{2020}", "\x{2021}\x{2021}"); 3060DefMacro('\@fnsymbol{Number}', sub { 3061 ExplodeText(radix_format($_[1]->valueOf, @fnsymbols)); }); 3062DefMacro('\fnsymbol{}', sub { 3063 ExplodeText(radix_format(CounterValue(ToString(Expand($_[1])))->valueOf, @fnsymbols)); }); 3064 3065# The following two macros were originally defined in chngcntr.sty, 3066# but were moved to LaTeX core in 2018. 3067DefPrimitive('\counterwithin OptionalMatch:* {}{}', sub { 3068 my ($stomach, $starred, $ctr, $within) = @_; 3069 $ctr = ToString($ctr); 3070 $within = ToString($within); 3071 addtoCounterReset($ctr, $within); 3072 if (!$starred) { 3073 DefMacroI(T_CS("\\the$ctr"), undef, "\\the$within.\\arabic{$ctr}", scope => 'global'); 3074 defCounterID($ctr, $within); } 3075 return; }); 3076 3077DefPrimitive('\counterwithout OptionalMatch:* {}{}', sub { 3078 my ($stomach, $starred, $ctr, $within) = @_; 3079 $ctr = ToString($ctr); 3080 $within = ToString($within); 3081 remfromCounterReset($ctr, $within); 3082 if (!$starred) { 3083 DefMacroI(T_CS("\\the$ctr"), undef, "\\arabic{$ctr}", scope => 'global'); 3084 defCounterID($ctr); } 3085 return; }); 3086 3087DefPrimitive('\@removefromreset{}{}', sub { 3088 my ($stomach, $ctr, $within) = @_; 3089 $ctr = ToString($ctr); 3090 $within = ToString($within); 3091 remfromCounterReset($ctr, $within); 3092 # Should \the$ctr@ID be redefined? It really should track changes to \the$ctr! 3093 # defCounterID($ctr); 3094 return; }); 3095 3096DefMacroI('\cl@@ckpt', undef, '\@elt{page}'); 3097#====================================================================== 3098# C.8.5 The ifthen Package. 3099#====================================================================== 3100# \ifthenelse 3101# and sundry conditionals... 3102# 3103# Yeah, maybe this'll get done someday.... 3104 3105#********************************************************************** 3106# C.9 Figures and Other Floating Bodies 3107#********************************************************************** 3108 3109#====================================================================== 3110# C.9.1 Figures and Tables 3111#====================================================================== 3112 3113# Note that, the number is associated with the caption. 3114# (to allow multiple figures per figure environment?). 3115# Whatever reason, that causes complications: We can only increment 3116# counters with the caption, but then have to arrange for the counters, 3117# refnums, ids, get passed on to the figure, table when needed. 3118# AND, as soon as possible, since other items may base their id's on the id of the table! 3119 3120# Let the fonts for float be the default for all floats, figures, tables, etc. 3121DefMacro('\fnum@font@float', '\@empty'); 3122DefMacro('\format@title@font@float', '\@empty'); 3123 3124DefMacro('\fnum@font@figure', '\fnum@font@float'); 3125DefMacro('\fnum@font@table', '\fnum@font@float'); 3126DefMacro('\format@title@font@figure', '\format@title@font@float'); 3127DefMacro('\format@title@font@table', '\format@title@font@float'); 3128 3129# stubs for the latex float internals 3130for my $float (qw(@float @dblfloat)) { 3131 DefEnvironmentI($float, '[]{}', 3132 "<ltx:float xml:id='#id' inlist='#inlist' ?#1(placement='#1') class='ltx_float_#2'>" 3133 . "#body" 3134 . "</ltx:float>", 3135 afterDigestBegin => sub { 3136 DefMacroI('\@captype', undef, ToString($_[1]->getArg(2))); 3137 return; }, 3138 afterDigest => sub { 3139 RescueCaptionCounters(ToString(Expand(T_CS('\@captype'))), $_[1]); 3140 return; }); } 3141# Could perhaps parameterize further with a separator? 3142DefMacro('\format@title@figure{}', '\lx@tag[][: ]{\lx@fnum@@{figure}}#1'); 3143DefMacro('\format@title@table{}', '\lx@tag[][: ]{\lx@fnum@@{table}}#1'); 3144 3145DefMacro('\ext@figure', 'lof'); 3146DefMacro('\ext@table', 'lot'); 3147 3148DefConditional('\iflx@donecaption'); 3149DefMacro('\caption', 3150'\lx@donecaptiontrue\@ifundefined{@captype}{\@@generic@caption}{\expandafter\@caption\expandafter{\@captype}}'); 3151# First, check for trailing \label, move it into the caption as a standard position 3152# NOTE: If one day we want to unlock \@caption, make sure to test against arXiv:cond-mat/0001395 for a passing build. 3153DefMacro('\@caption{}[]{}', 3154 '\@ifnext\label{\@caption@postlabel{#1}{#2}{#3}}{\@caption@{#1}{#2}{#3}}', locked => 1); 3155 3156DefMacro('\@caption@postlabel{}{}{} SkipMatch:\label Semiverbatim', 3157 '\@caption@{#1}{#2}{#3\label{#4}}'); 3158 3159# Now, check for \label(s) inside to (potentially) record them. 3160DefMacro('\@caption@{}{}{}', 3161 '\@hack@caption@{#1}{#2}{}#3\label\endcaption'); 3162DefMacro('\@hack@caption@{}{}{} Until:\label Until:\endcaption', 3163 '\ifx.#5.\@caption@@@{#1}{#2}{#3#4}' # No more labels 3164 . '\else\@@@hack@caption@{#1}{#2}{#3#4}#5\endcaption\fi'); 3165DefMacro('\@@@hack@caption@{}{}{} Semiverbatim Until:\label Until:\endcaption', 3166 '\lx@note@caption@label{#4}' . 3167 '\@hack@caption@{#1}{#2}{#3\label{#4}#5}\label#6\endcaption'); 3168 3169DefPrimitive('\lx@note@caption@label{}', sub { MaybeNoteLabel($_[1]); }); 3170 3171DefMacro('\@caption@@@{}{}{}', 3172 '\@@add@caption@counters' 3173 . '\@@toccaption{\lx@format@toctitle@@{#1}{\ifx.#2.#3\else#2\fi}}' 3174 . '\@@caption{\lx@format@title@@{#1}{#3}}'); 3175 3176# Note that the counters only get incremented by \caption, NOT by \table, \figure, etc. 3177DefPrimitive('\@@add@caption@counters', sub { 3178 my $captype = ToString(Digest(T_CS('\@captype'))); 3179 my %props = RefStepCounter($captype); 3180 my $inlist = ToString(Digest(T_CS('\ext@' . $captype))); 3181 AssignValue($captype . "_tags" => $props{tags}, 'global'); 3182 AssignValue($captype . "_id" => $props{id}, 'global'); 3183 AssignValue($captype . "_inlist" => $inlist, 'global'); }); 3184 3185sub RescueCaptionCounters { 3186 my ($captype, $whatsit) = @_; 3187 if (my $tags = LookupValue($captype . "_tags")) { 3188 AssignValue($captype . "_tags" => undef, 'global'); 3189 $whatsit->setProperty(tags => $tags); } 3190 if (my $id = LookupValue($captype . "_id")) { 3191 AssignValue($captype . "_id" => undef, 'global'); 3192 $whatsit->setProperty(id => $id); } 3193 if (my $inlist = LookupValue($captype . "_inlist")) { 3194 AssignValue($captype . "_inlist" => undef, 'global'); 3195 $whatsit->setProperty(inlist => $inlist); } 3196 return; } 3197 3198DefConstructor('\@@generic@caption[]{}', "<ltx:text class='ltx_caption'>#2</ltx:text>", 3199 beforeDigest => sub { 3200 Error('unexpected', '\caption', $_[0], 3201 "Use of \\caption outside any known float"); }); 3202 3203# Note that even without \caption, we'd probably like to have xml:id. 3204Tag('ltx:figure', afterClose => sub { GenerateID(@_, 'fig'); }); 3205Tag('ltx:table', afterClose => sub { GenerateID(@_, 'tab'); }); 3206Tag('ltx:float', afterClose => sub { GenerateID(@_, 'tab'); }); 3207 3208# These may need to float up to where they're allowed, 3209# or they may need to close <p> or similar. 3210DefConstructor('\@@caption{}', "^^<ltx:caption>#1</ltx:caption>"); 3211DefConstructor('\@@toccaption{}', "^^<ltx:toccaption>#1</ltx:toccaption>", 3212 sizer => '0'); 3213 3214DefEnvironment('{figure}[]', 3215 "<ltx:figure xml:id='#id' inlist='#inlist' ?#1(placement='#1')>" 3216 . "#tags" 3217 . "#body" 3218 . "</ltx:figure>", 3219 properties => { layout => 'vertical' }, 3220 beforeDigest => sub { DefMacroI('\@captype', undef, 'figure'); }, 3221 afterDigest => sub { RescueCaptionCounters('figure', $_[1]); }, 3222 locked => 1); 3223DefEnvironment('{figure*}[]', 3224 "<ltx:figure xml:id='#id' inlist='#inlist' ?#1(placement='#1')>" 3225 . "#tags" 3226 . "#body" 3227 . "</ltx:figure>", 3228 properties => { layout => 'vertical' }, 3229 beforeDigest => sub { DefMacroI('\@captype', undef, 'figure'); }, 3230 afterDigest => sub { RescueCaptionCounters('figure', $_[1]); }, 3231 locked => 1); 3232DefEnvironment('{table}[]', 3233 "<ltx:table xml:id='#id' inlist='#inlist' ?#1(placement='#1')>" 3234 . "#tags" 3235 . "#body" 3236 . "</ltx:table>", 3237 properties => { layout => 'vertical' }, 3238 beforeDigest => sub { DefMacroI('\@captype', undef, 'table'); }, 3239 afterDigest => sub { RescueCaptionCounters('table', $_[1]); }, 3240 locked => 1); 3241DefEnvironment('{table*}[]', 3242 "<ltx:table xml:id='#id' inlist='#inlist' ?#1(placement='#1')>" 3243 . "#tags" 3244 . "#body" 3245 . "</ltx:table>", 3246 properties => { layout => 'vertical' }, 3247 beforeDigest => sub { DefMacroI('\@captype', undef, 'table'); }, 3248 afterDigest => sub { RescueCaptionCounters('table', $_[1]); }, 3249 locked => 1); 3250 3251# There are some floating figure/table packages that define environments 3252# to allow the figure to float left or right. It APPEARS that they allow 3253# authors to either wrap with {figure} or NOT, as they like! 3254# The result apparently makes sense so long as they only use one \caption. 3255# If we get a figure containing a single figure, we want to collaps it. 3256sub collapseFloat { 3257 my ($document, $float) = @_; 3258 my $qname = $document->getNodeQName($float); 3259 my @inners = $document->findnodes($qname, $float); 3260 if (scalar(@inners) == 1) { # Only 1 inner float of same type 3261 my $inner = $inners[0]; 3262 my $ocaption = $document->findnodes('ltx:caption', $float); 3263 my $icaption = $document->findnodes('ltx:caption', $inner); 3264 if (!($ocaption && $icaption)) { # If they don't both have captions. 3265 foreach my $attr (map { $_->nodeName } $inner->attributes) { 3266 next if $attr eq 'xml:id'; 3267 $document->setAttribute($float, $attr => $inner->getAttribute($attr)); } 3268 if ($icaption) { 3269 if (my $id = $inner->getAttribute('xml:id')) { 3270 $document->unRecordID($id); 3271 $document->setAttribute($float, 'xml:id' => $id); } } 3272 3273 # Finally, replace $inner by it's children! 3274 # Note that since we can only append new stuff, we've got to remove the following first. 3275 my @save = (); 3276 my $following; 3277 while (($following = $float->lastChild) && ($$following != $$inner)) { # Remove & Save following siblings. 3278 unshift(@save, $float->removeChild($following)); } 3279 unshift(@save, $inner->childNodes); 3280 $float->removeChild($inner); 3281 map { $float->appendChild($_) } @save; } # Put these back. 3282 } 3283 return; } 3284Tag('ltx:figure', afterClose => \&collapseFloat); 3285Tag('ltx:table', afterClose => \&collapseFloat); 3286Tag('ltx:float', afterClose => \&collapseFloat); 3287 3288DefPrimitive('\flushbottom', undef); 3289DefPrimitive('\suppressfloats[]', undef); 3290 3291NewCounter('topnumber'); 3292DefMacroI('\topfraction', undef, "0.25"); 3293NewCounter('bottomnumber'); 3294DefMacroI('\bottomfraction', undef, "0.25"); 3295NewCounter('totalnumber'); 3296DefMacroI('\textfraction', undef, "0.25"); 3297DefMacroI('\floatpagefraction', undef, "0.25"); 3298NewCounter('dbltopnumber'); 3299DefMacroI('\dbltopfraction', undef, "0.7"); 3300DefMacroI('\dblfloatpagefraction', undef, "0.25"); 3301DefRegister('\floatsep' => Glue(0)); 3302DefRegister('\textfloatsep' => Glue(0)); 3303DefRegister('\intextsep' => Glue(0)); 3304DefRegister('\dblfloatsep' => Glue(0)); 3305DefRegister('\dbltextfloatsep' => Glue(0)); 3306DefRegister('\@maxsep' => Dimension(0)); 3307DefRegister('\@dblmaxsep' => Dimension(0)); 3308DefRegister('\@fptop' => Glue(0)); 3309DefRegister('\@fpsep' => Glue(0)); 3310DefRegister('\@fpbot' => Glue(0)); 3311DefRegister('\@dblfptop' => Glue(0)); 3312DefRegister('\@dblfpsep' => Glue(0)); 3313DefRegister('\@dblfpbot' => Glue(0)); 3314DefRegister('\abovecaptionskip' => Glue(0)); 3315DefRegister('\belowcaptionskip' => Glue(0)); 3316Let('\topfigrule', '\relax'); 3317Let('\botfigrule', '\relax'); 3318Let('\dblfigrule', '\relax'); 3319 3320DefMacroI('\figurename', undef, 'Figure'); 3321DefMacroI('\figuresname', undef, 'Figures'); # Never used? 3322DefMacroI('\tablename', undef, 'Table'); 3323DefMacroI('\tablesname', undef, 'Tables'); 3324 3325Let('\outer@nobreak', '\@empty'); 3326DefMacro('\@dbflt{}', '#1'); 3327DefMacro('\@xdblfloat{}[]', '\@xfloat{#1}[#2]'); 3328DefMacro('\@floatplacement', ''); 3329DefMacro('\@dblfloatplacement', ''); 3330 3331#====================================================================== 3332# C.9.2 Marginal Notes 3333#====================================================================== 3334 3335AssignValue(marginparclass => undef); 3336DefConstructor('\marginpar[]{}', 3337 "<ltx:note role='margin' class='#class'><ltx:inline-para>#2</ltx:inline-para></ltx:note>", 3338 properties => sub { (class => LookupValue('marginparclass')); }); 3339DefPrimitiveI('\reversemarginpar', undef, sub { AssignValue(marginparclass => 'ltx_marginpar_reverse'); }); 3340DefPrimitiveI('\normalmarginpar', undef, sub { AssignValue(marginparclass => undef); }); 3341DefRegister('\marginparpush', Dimension(0)); 3342 3343#********************************************************************** 3344# C.10 Lining It Up in Columns 3345#********************************************************************** 3346 3347#====================================================================== 3348# C.10.1 The tabbing Environment 3349#====================================================================== 3350DefRegister('\tabbingsep' => Dimension(0)); 3351 3352DefMacroI('\tabbing', undef, 3353 '\@tabbing@bindings\@@tabbing\@start@alignment'); 3354DefMacroI('\endtabbing', undef, 3355 '\@finish@alignment\@end@tabbing'); 3356DefPrimitiveI('\@end@tabbing', undef, sub { $_[0]->egroup; }); 3357DefConstructor('\@@tabbing SkipSpaces DigestedBody', 3358 '#1', 3359 reversion => '\begin{tabbing}#1\end{tabbing}', 3360 beforeDigest => sub { $_[0]->bgroup; }, 3361 mode => 'text'); 3362 3363DefMacroI('\@tabbing@tabset', undef, '\@tabbing@tabset@marker&'); 3364DefMacroI('\@tabbing@nexttab', undef, '\@tabbing@nexttab@marker&'); 3365DefMacro('\@tabbing@newline OptionalMatch:* [Dimension]', '\@tabbing@newline@marker\cr'); 3366DefMacroI('\@tabbing@kill', undef, '\@tabbing@kill@marker\cr\@tabbing@start@tabs'); 3367 3368DefConstructorI('\@tabbing@tabset@marker', undef, '', reversion => '\='); 3369DefConstructorI('\@tabbing@nexttab@marker', undef, '', reversion => '\>'); 3370DefConstructorI('\@tabbing@newline@marker', undef, '', reversion => Tokens(T_CS("\\\\"), T_CR)); 3371DefConstructorI('\@tabbing@kill@marker', undef, '', reversion => '\kill', 3372 afterDigest => sub { LookupValue('Alignment')->removeRow; return; }); 3373 3374AssignValue(tabbing_start_tabs => Tokens()); 3375DefMacroI('\@tabbing@start@tabs', undef, sub { LookupValue('tabbing_start_tabs')->unlist; }); 3376DefPrimitiveI('\@tabbing@increment', undef, sub { 3377 my @tabs = LookupValue('tabbing_start_tabs')->unlist; 3378 AssignValue(tabbing_start_tabs => Tokens(@tabs, T_CS('\>')), 'global'); }); 3379DefPrimitiveI('\@tabbing@decrement', undef, sub { 3380 my ($ignore, @tabs) = LookupValue('tabbing_start_tabs')->unlist; 3381 AssignValue(tabbing_start_tabs => Tokens(@tabs), 'global'); }); 3382 3383# NOTE: \< is NOT currently handled!!! 3384# Ugh!! The way we're setting the initial tabs, we can't really handle this! 3385DefPrimitiveI('\@tabbing@untab', undef, undef); 3386 3387# NOTE: \' and \` are NOT currently handled 3388DefPrimitiveI('\@tabbing@flushright', undef, undef); 3389DefPrimitiveI('\@tabbing@hfil', undef, undef); 3390# NOTE: \pushtabs and \poptabs are NOT currently handled. 3391DefPrimitiveI('\@tabbing@pushtabs', undef, undef); 3392DefPrimitiveI('\@tabbing@poptabs', undef, undef); 3393 3394DefMacro('\@tabbing@accent{}', sub { T_CS('\@tabbing@' . ToString($_[1])); }); 3395 3396# Should there be some rowsep/colsep set here? 3397# Note that {tabbign} really shouldn't be handled by a tabular AT ALL.... 3398# Should be recording accumulated widths and wrapping in ltx:text, with specified widths. 3399sub tabbingBindings { 3400 AssignValue(Alignment => LaTeXML::Core::Alignment->new( 3401 template => LaTeXML::Core::Alignment::Template->new( 3402 repeated => [{ after => Tokens(T_CS('\hfil')) }]), 3403 openContainer => sub { $_[0]->openElement('ltx:tabular', @_[1 .. $#_]); }, 3404 closeContainer => sub { $_[0]->closeElement('ltx:tabular'); }, 3405 openRow => sub { $_[0]->openElement('ltx:tr', @_[1 .. $#_]); }, 3406 closeRow => sub { $_[0]->closeElement('ltx:tr'); }, 3407 openColumn => sub { $_[0]->openElement('ltx:td', @_[1 .. $#_]); }, 3408 closeColumn => sub { $_[0]->closeElement('ltx:td'); })); 3409 Let("\\=", '\@tabbing@tabset'); 3410 Let("\\>", '\@tabbing@nexttab'); 3411 Let("\\\\", '\@tabbing@newline'); 3412 Let('\kill', '\@tabbing@kill'); 3413 Let("\\+", '\@tabbing@increment'); 3414 Let("\\-", '\@tabbing@decrement'); 3415 Let("\\<", '\@tabbing@untab'); 3416 Let('\@tabbing@' . "'", "\\'"); 3417 Let('\@tabbing@' . "`", "\\`"); 3418 Let('\a', '\@tabbing@accent'); 3419 Let("\\'", '\@tabbing@flushright'); 3420 Let("\\`", '\@tabbing@hfil'); 3421 Let('\pushtabs', '\@tabbing@pushtabs'); 3422 Let('\poptabs', '\@tabbing@poptabs'); 3423 return; } 3424 3425DefMacroI('\pushtabs', undef, Tokens()); 3426DefMacroI('\poptabs', undef, Tokens()); 3427DefMacroI('\kill', undef, Tokens()); 3428 3429DefPrimitiveI('\@tabbing@bindings', undef, sub { 3430 tabbingBindings(); }); 3431 3432# NOTE: Do it!! 3433 3434#====================================================================== 3435# C.10.2 The array and tabular Environments 3436#====================================================================== 3437# Tabular are a bit tricky in that we have to arrange for tr and td to 3438# be openned and closed at the right times; the only real markup is 3439# the & and \\. Also \multicolumn has to be cooperative. 3440# Along with this, we have to track which column specification applies 3441# to the current column. 3442# To simulate LaTeX's tabular borders & hlines, we simply add border 3443# attributes to all cells. For HTML, CSS will be necessary to display them. 3444# [We'll ignore HTML's frame, rules and colgroup mechanisms.] 3445 3446DefRegister('\lx@arstrut', Dimension('0pt')); 3447DefRegister('\lx@default@tabcolsep', Dimension('6pt')); 3448DefRegister('\tabcolsep', Dimension('6pt')); 3449DefMacroI('\arraystretch', undef, "1"); 3450Let('\@tabularcr', '\@alignment@newline'); 3451AssignValue(GUESS_TABULAR_HEADERS => 1) # Defaults to yes 3452 unless defined LookupValue('GUESS_TABULAR_HEADERS'); 3453 3454sub tabularBindings { 3455 my ($template, %properties) = @_; 3456 $properties{guess_headers} = LookupValue('GUESS_TABULAR_HEADERS') 3457 unless defined $properties{guess_headers}; 3458 if (!defined $properties{attributes}{colsep}) { 3459 my $sep = LookupDimension('\tabcolsep'); 3460 if ($sep && ($sep->valueOf != LookupDimension('\lx@default@tabcolsep')->valueOf)) { 3461 $properties{attributes}{colsep} = $sep; } } 3462 if (!defined $properties{attributes}{rowsep}) { 3463 my $str = ToString(Expand(T_CS('\arraystretch'))); 3464 if ($str != 1) { 3465 $properties{attributes}{rowsep} = Dimension(($str - 1) . 'em'); } } 3466 if (!defined $properties{strut}) { 3467 $properties{strut} = LookupRegister('\baselineskip')->multiply(1.5); } # Account for html space 3468 alignmentBindings($template, 'text', %properties); 3469 Let("\\\\", '\@tabularcr'); 3470 Let('\tabularnewline', "\\\\"); 3471 # NOTE: Fit this back in!!!!!!! 3472 # # Do like AddToMacro, but NOT global! 3473 foreach my $name ('@row@before', '@row@after', '@column@before', '@column@after') { 3474 my $cs = '\\' . $name; 3475 DefMacroI($cs, undef, 3476 Tokens(LookupDefinition(T_CS($cs))->getExpansion->unlist, 3477 T_CS('\@tabular' . $name))); } 3478 return; } 3479 3480# Keyvals are for attributes for the alignment. 3481# Typical keys are width, vattach,... 3482DefKeyVal('tabular', 'width', 'Dimension'); 3483DefPrimitive('\@tabular@bindings AlignmentTemplate OptionalKeyVals:tabular', sub { 3484 my ($stomach, $template, $attributes) = @_; 3485 my %attr = ($attributes ? $attributes->getPairs : ()); 3486 if (my $va = $attr{vattach}) { 3487 $attr{vattach} = translateAttachment($va) || ToString($va); } 3488 tabularBindings($template, attributes => {%attr}); 3489 return; }); 3490 3491DefMacroI('\@tabular@before', undef, ''); 3492DefMacroI('\@tabular@after', undef, ''); 3493DefMacroI('\@tabular@row@before', undef, ''); 3494DefMacroI('\@tabular@row@after', undef, ''); 3495DefMacroI('\@tabular@column@before', undef, ''); 3496DefMacroI('\@tabular@column@after', undef, ''); 3497 3498# The Core alignment support is in LaTeXML::Core::Alignment and in TeX.ltxml 3499##DefMacro('\tabular[]{}', '\@tabular@bindings{#2}\@@tabular[#1]{#2}\@start@alignment'); 3500DefMacro('\tabular[]{}', 3501 '\@tabular@bindings{#2}[vattach=#1]\@@tabular[#1]{#2}\@start@alignment\@tabular@before', 3502 locked => 1); 3503DefMacroI('\endtabular', undef, 3504 '\@tabular@after\@finish@alignment\@end@tabular', 3505 locked => 1); 3506DefPrimitiveI('\@end@tabular', undef, sub { $_[0]->egroup; }); 3507#DefMacroI('\@end@tabular', undef, undef); 3508# Note that the pattern will already have been interpreted by \@tabular@bindings, 3509# so make it Undigested here! 3510 3511DefConstructor('\@@tabular[] Undigested DigestedBody', 3512 '#3', 3513 reversion => '\begin{tabular}[#1]{#2}#3\end{tabular}', 3514 beforeDigest => sub { $_[0]->bgroup; }, 3515 sizer => '#3', 3516 afterDigest => sub { 3517 my ($stomach, $whatsit) = @_; 3518 if (my $alignment = LookupValue('Alignment')) { 3519 my $attr = $alignment->getProperty('attributes'); 3520 $$attr{vattach} = translateAttachment($whatsit->getArg(1)); } 3521 return; }, 3522 mode => 'text'); 3523 3524DefMacro('\csname tabular*\endcsname{Dimension}[]{}', 3525### '\@tabular@bindings{#3}\@@tabular@{#1}[#2]{#3}\@start@alignment'); 3526 '\@tabular@bindings{#3}[width=#1,vattach=#2]\@@tabular@{#1}[#2]{#3}\@start@alignment'); 3527DefMacro('\csname endtabular*\endcsname', 3528 '\@finish@alignment\@end@tabular@'); 3529DefConstructor('\@@tabular@{Dimension}[] Undigested DigestedBody', 3530 '#4', 3531 beforeDigest => sub { $_[0]->bgroup; }, 3532 reversion => '\begin{tabular*}{#1}[#2]{#3}#4\end{tabular*}', 3533 mode => 'text'); 3534DefPrimitive('\@end@tabular@', sub { $_[0]->egroup; }); 3535Let('\multicolumn', '\@multicolumn'); 3536 3537DefPrimitiveI('\hline', undef, undef); # Redefined inside tabular 3538# A weird bit that sometimes gets invoked by Cargo Cult programmers... 3539# to \noalign in the defn of \hline! Bizarre! (see latex.ltx) 3540# However, the really weird thing is the way this provides the } to close the argument 3541DefMacroI('\@xhline', undef, '\ifnum0=`{\fi}'); 3542 3543#DefConstructor('\cline{}', '', 3544DefMacro('\cline{}', '\noalign{\@cline{#1}}'); 3545DefConstructor('\@cline{}', '', 3546 afterDigest => sub { 3547 my ($stomach, $whatsit) = @_; 3548 my $cols = ToString($whatsit->getArg(1)); 3549 my @cols = (); 3550 while ($cols =~ s/^,?(\d+)//) { 3551 my $n = $1; 3552 push(@cols, ($cols =~ s/^-(\d+)// ? ($n .. $1) : ($n))); } 3553 my $alignment = LookupValue('Alignment'); 3554 $alignment->addLine('t', @cols) if $alignment; 3555 return; }, 3556 sizer => 0, 3557 properties => { isHorizontalRule => 1 }); 3558 3559DefConstructorI('\vline', undef, "", # ??? 3560 properties => { isVerticalRule => 1 }, 3561 sizer => 0, 3562); 3563DefRegister('\lx@default@arraycolsep', Dimension('5pt')); 3564DefRegister('\arraycolsep', Dimension('5pt')); 3565DefRegister('\arrayrulewidth', Dimension('0.4pt')); 3566DefRegister('\doublerulesep', Dimension('2pt')); 3567DefMacro('\extracolsep{}', Tokens()); 3568#DefMacroI('\tabularnewline', undef, Tokens()); 3569#Let('\tabularnewline', '\cr'); 3570#DefMacroI('\tabularnewline', undef, '\cr'); # ??? 3571#====================================================================== 3572# Array and similar environments 3573 3574DefPrimitive('\@array@bindings [] AlignmentTemplate', sub { 3575 my ($stomach, $pos, $template) = @_; 3576 my $attr = { vattach => translateAttachment($pos), 3577 role => 'ARRAY' }; 3578 # Determine column and row separations, if non default 3579 my $colsep = LookupDimension('\arraycolsep'); 3580 if ($colsep && ($colsep->valueOf != LookupDimension('\lx@default@arraycolsep')->valueOf)) { 3581 $$attr{colsep} = $colsep; } 3582 my $str = ToString(Expand(T_CS('\arraystretch'))); 3583 if ($str != 1) { 3584 $$attr{rowsep} = Dimension(($str - 1) . 'em'); } 3585 alignmentBindings($template, 'math', attributes => $attr); 3586 MergeFont(mathstyle => 'text'); 3587 Let("\\\\", '\@alignment@newline'); 3588 return; }); 3589 3590DefMacro('\array[]{}', 3591 '\@array@bindings[#1]{#2}\@@array[#1]{#2}\@start@alignment'); 3592DefMacroI('\endarray', undef, 3593 '\@finish@alignment\@end@array'); 3594DefPrimitiveI('\@end@array', undef, sub { $_[0]->egroup; }); 3595DefConstructor('\@@array[] Undigested DigestedBody', 3596 '#3', 3597 beforeDigest => sub { $_[0]->bgroup; }, 3598 reversion => '\begin{array}[#1]{#2}#3\end{array}'); 3599 3600DefMacro('\@tabarray', '\m@th\@@array[c]'); 3601#********************************************************************** 3602# C.11 Moving Information Around 3603#********************************************************************** 3604 3605#====================================================================== 3606# C.11.1 Files 3607#====================================================================== 3608DefPrimitive('\nofiles', undef); 3609 3610#====================================================================== 3611# C.11.2 Cross-References 3612#====================================================================== 3613 3614# \label attaches a label to the nearest parent that can accept a labels attribute 3615# but only those that have an xml:id (but should this require a refnum and/or title ???) 3616# Note that latex essentially allows redundant labels, but we can record only one!!! 3617DefConstructor('\label Semiverbatim', sub { 3618 my ($document, $olabel, %props) = @_; 3619 my $label = $props{label}; 3620 if (my $savenode = $document->floatToLabel) { 3621 my $node = $document->getNode; 3622 my %labels = map { ($_ => 1) } $label, split(/\s+/, $node->getAttribute('labels') || ''); 3623 $document->setAttribute($node, labels => join(' ', sort keys %labels)); 3624 $document->setNode($savenode); } }, 3625 reversion => '', 3626 properties => { alignmentSkippable => 1, alignmentPreserve => 1 }, 3627 afterDigest => sub { 3628 MaybeNoteLabel($_[1]->getArg(1)); 3629 my $label = CleanLabel(ToString($_[1]->getArg(1))); 3630 $_[1]->setProperty(label => $label); 3631 my $scope = $label; $scope =~ s/^LABEL:/label:/; 3632 if (my $ctr = LookupValue('current_counter')) { 3633 unshift(@{ LookupValue('scopes_for_counter:' . $ctr) }, $scope); 3634 $STATE->activateScope($scope); 3635 $_[0]->beginMode('text'); 3636 AssignValue('LABEL@' . $label, Digest(T_CS('\@currentlabel')), 'global'); 3637 $_[0]->endMode('text'); } 3638 return; }); 3639 3640# If a node has been labeled, but still hasn't yet got an id by afterClose:late, 3641# we'd better generate an id for it. 3642Tag('ltx:*', 'afterClose:late' => sub { 3643 my ($document, $node) = @_; 3644 if ($node->hasAttribute('labels') && !($node->hasAttribute('xml:id'))) { 3645 GenerateID($document, $node); } }); 3646 3647# These will get filled in during postprocessing. 3648# * is added to accommodate hyperref 3649DefConstructor('\ref OptionalMatch:* Semiverbatim', "<ltx:ref labelref='#label' _force_font='true'/>", 3650 properties => sub { (label => CleanLabel($_[2])); }); 3651DefConstructor('\pageref OptionalMatch:* Semiverbatim', "<ltx:ref labelref='#label' _force_font='true'/>", # Same?? 3652 properties => sub { (label => CleanLabel($_[2])); }); 3653#====================================================================== 3654# C.11.3 Bibliography and Citation 3655#====================================================================== 3656 3657# Note that it's called \refname in LaTeX's article, but \bibname in report & book. 3658# And likewise, mixed up in various other classes! 3659 3660DefMacroI('\thebibliography@ID', undef, Tokens()); 3661 3662# Do this before digesting the body of a bibliography 3663sub beforeDigestBibliography { 3664 AssignValue(inPreamble => 0); Digest('\@lx@inbibliographytrue'); 3665 DefMacro('\bibliographystyle{}', ''); 3666 DefMacro('\bibliography {}', ''); 3667 DefConstructorI('\endthebibliography', undef, 3668 "</ltx:biblist></ltx:bibliography>", 3669 locked => 1); 3670 ResetCounter('@bibitem'); 3671 return; } 3672 3673# This sub does things that would commonly be needed when starting a bibliography 3674# setting the ID, etc... 3675sub beginBibliography { 3676 my ($whatsit) = @_; 3677 beginBibliography_clean($whatsit); 3678 # Fix for missing \bibitems! 3679 setupPseudoBibitem(); 3680 return; } 3681 3682sub beginBibliography_clean { 3683 my ($whatsit) = @_; 3684 # Check if \bibsection is defined and try to decipher it. 3685 # Expecting something like \section*{sometext} 3686 my $bibtitle = undef; 3687 my $bs = LookupDefinition(T_CS('\bibsection')); 3688 # FIXED: Sometimes $bs may be a Primitive. 3689 # Now guarding for that case or we get a perl:die for ->getExpansion 3690 if (my @t = $bs && ($bs->isExpandable ? $bs->getExpansion->unlist : $bs)) { 3691 our %bibunitmap = ( 3692 '\\part' => 'ltx:part', '\\chapter' => 'ltx:chapter', 3693 '\\section' => 'ltx:section', '\\subsection' => 'ltx:subsection', 3694 '\\subsubsection' => 'ltx:subsubsection', '\\paragraph' => 'ltx:paragraph', 3695 '\\subparagraph' => 'ltx:subparagraph'); 3696 if (my $unit = $bibunitmap{ ToString(shift(@t)) }) { # Found a sectional unit command. 3697 AssignMapping('BACKMATTER_ELEMENT', 'ltx:bibliography' => $unit); 3698 if (ToString($t[0]) eq '*') { shift(@t); } 3699 # Check for balanced? or just take balanced begining? 3700 $bibtitle = Tokens(@t); } } 3701 noteBackmatterElement($whatsit, 'ltx:bibliography'); 3702 # Try to compute a reasonable, but unique ID; 3703 # relative to the document's ID, if any. 3704 # But also, if there are multiple bibliographies, 3705 my $bibnumber = LookupValue('n_bibliographies') || 0; 3706 AssignValue(n_bibliographies => ++$bibnumber, 'global'); 3707 my $docid = ToString(Expand(T_CS('\thedocument@ID'))); 3708 my $bibid = ($docid ? $docid . '.' : '') . 'bib' . radix_alpha($bibnumber - 1); 3709 DefMacroI(T_CS('\thebibliography@ID'), undef, T_OTHER($bibid), scope => 'global'); 3710 # $whatsit->setProperty(id=>ToString(Expand(T_CS('\thebibliography@ID')))); 3711 $whatsit->setProperty(id => $bibid); 3712 my $title = ($bibtitle ? Digest($bibtitle) 3713 : DigestIf('\refname') || DigestIf('\bibname')); 3714 $whatsit->setProperty(title => $title) if $title; 3715 $whatsit->setProperty(titlefont => $title->getFont) if $title; 3716 $whatsit->setProperty(bibstyle => LookupValue('BIBSTYLE')); 3717 $whatsit->setProperty(citestyle => LookupValue('CITE_STYLE')); 3718 # $whatsit->setProperty(sort=> ??? 3719 # And prepare for the likely nonsense that appears within bibliographies 3720 ResetCounter('enumiv'); 3721 return; } 3722 3723DefMacro('\bibliography Semiverbatim', 3724 '\lx@ifusebbl{#1}{\input{\jobname.bbl}}{\lx@bibliography{#1}}'); 3725 3726DefMacro('\lx@ifusebbl{}{}{}', sub { 3727 my ($gullet, $bib_files, $bbl_clause, $bib_clause) = @_; 3728 $bib_files = ToString(Expand($bib_files)); 3729 return unless $bib_files; 3730 my $jobname = ToString(Expand(T_CS('\jobname'))); 3731 3732 my $bbl_path = FindFile($jobname, type => 'bbl'); 3733 my $missing_bibs = ''; 3734 if (LookupValue("NO_BIBTEX")) { 3735 if (not $bbl_path) { 3736 Info('expected', "bbl", $_[0], "Couldn't find bbl file, bibliography may be empty."); 3737 return Tokens(); } 3738 else { 3739 return $bbl_clause->unlist; } } 3740 else { 3741 for my $bf (split(',', $bib_files)) { 3742 my $bib_path = FindFile($bf, type => 'bib'); 3743 if (not $bib_path) { 3744 $missing_bibs .= ',' unless length($missing_bibs) == 0; 3745 $missing_bibs .= $bf; } } 3746 if (length($missing_bibs) == 0 or not $bbl_path) { 3747 return $bib_clause->unlist; } 3748 else { 3749 Info('expected', $missing_bibs, $_[0], "Couldn't find all bib files, using " . $jobname . ".bbl instead"); 3750 return $bbl_clause->unlist; } } }); 3751 3752AssignMapping('BACKMATTER_ELEMENT', 'ltx:bibliography' => 'ltx:section'); 3753AssignMapping('BACKMATTER_ELEMENT', 'ltx:index' => 'ltx:section'); 3754 3755sub noteBackmatterElement { 3756 my ($whatsit, $backelement) = @_; 3757 $whatsit->setProperties(backmatterelement => LookupMapping('BACKMATTER_ELEMENT', $backelement)); 3758 return; } 3759 3760sub adjustBackmatterElement { 3761 my ($document, $whatsit) = @_; 3762 if (my $asif = $whatsit->getProperty('backmatterelement')) { 3763 $document->setNode($document->find_insertion_point($asif)); } 3764 return; } 3765 3766DefConstructor('\lx@bibliography [] Semiverbatim', 3767 "<ltx:bibliography files='#2' xml:id='#id' " 3768 . "bibstyle='#bibstyle' citestyle='#citestyle' sort='#sort' lists='#1'>" 3769 . "<ltx:title font='#titlefont' _force_font='true'>#title</ltx:title>" 3770 . "</ltx:bibliography>", 3771 afterDigest => sub { $_[0]->begingroup; # wrapped so redefns don't take effect! 3772 beginBibliography($_[1]); 3773 $_[0]->endgroup; }, 3774 beforeConstruct => sub { adjustBackmatterElement($_[0], $_[1]); }); 3775 3776# NOTE: This totally needs to be made extensible (parsing *.bst!?!? OMG!) 3777our $BIBSTYLES = { 3778 plain => { citestyle => 'numbers', sort => 'true' }, 3779 unsrt => { citestyle => 'numbers', sort => 'false' }, 3780 alpha => { citestyle => 'AY', sort => 'true' }, 3781 abbrv => { citestyle => 'numbers', sort => 'true' }, 3782 plainnat => { citestyle => 'numbers', sort => 'true' }, 3783 unsrtnat => { citestyle => 'numbers', sort => 'false' }, 3784 alphanat => { citestyle => 'AY', sort => 'true' }, 3785 abbrvnat => { citestyle => 'numbers', sort => 'true' } }; 3786 3787sub setBibstyle { 3788 my ($style) = @_; 3789 $style = ToString($style); 3790 AssignValue(BIBSTYLE => $style); 3791 if (my $parms = $$BIBSTYLES{$style}) { 3792 AssignValue(CITE_STYLE => $$parms{citestyle}); 3793 AssignValue(CITE_SORT => $$parms{sort}); } 3794 return; } 3795 3796DefConstructor('\bibstyle{}', sub { 3797 my ($document, $style) = @_; 3798 setBibstyle($style); 3799 # Really ? 3800 if (my $bib = $document->findnode('//ltx:bibliography')) { 3801 $document->setAttribute($bib, bibstyle => LookupValue('BIBSTYLE')); 3802 $document->setAttribute($bib, citestyle => LookupValue('CITE_STYLE')); 3803 $document->setAttribute($bib, sort => LookupValue('CITE_SORT')); } 3804 }, 3805 afterDigest => sub { 3806 my $style = ToString($_[1]->getArg(1)); 3807 AssignValue(BIBSTYLE => $style, 'global'); 3808 if (my $parms = $$BIBSTYLES{$style}) { 3809 AssignValue(CITE_STYLE => $$parms{citestyle}); } 3810 else { 3811 Info('unexpected', $style, $_[0], "Unknown bibstyle '$style', it will be ignored"); } 3812 return; }); 3813 3814DefMacro('\bibliographystyle Semiverbatim', '\bibstyle{#1}'); 3815 3816DefConditional('\if@lx@inbibliography'); 3817# Should be an environment, but people seem to want to misuse it. 3818DefConstructorI('\thebibliography', undef, 3819 "<ltx:bibliography xml:id='#id'>" 3820 . "<ltx:title font='#titlefont' _force_font='true'>#title</ltx:title>" 3821 . "<ltx:biblist>", 3822 beforeDigest => sub { 3823 beforeDigestBibliography(); }, 3824 afterDigest => sub { 3825 my ($stomach, $whatsit) = @_; 3826 # NOTE that in some perverse situations (revtex?) 3827 # it seems to be allowable to omit the argument 3828 # It's ignorable for latexml anyway, so we'll just read it if its there . 3829 my $gullet = $stomach->getGullet; 3830 $gullet->skipSpaces; 3831 $gullet->readArg if $gullet->ifNext(T_BEGIN); 3832 beginBibliography($_[1]); 3833 }, 3834 beforeConstruct => sub { adjustBackmatterElement($_[0], $_[1]); }, 3835 locked => 1); 3836 3837# Close the bibliography 3838DefConstructorI('\endthebibliography', undef, 3839 "</ltx:biblist></ltx:bibliography>", 3840 locked => 1); 3841# auto close the bibliography and contained biblist. 3842Tag('ltx:biblist', autoClose => 1); 3843Tag('ltx:bibliography', autoClose => 1); 3844 3845# Since SOME people seem to write bibliographies w/o \bibitem, 3846# just blank lines between apparent entries, 3847# Making \par do a \bibitem{} works, but screws up valid 3848# bibliographies with blank lines! 3849# So, let's do some redirection! 3850sub setupPseudoBibitem { 3851 Let('\save@bibitem', '\bibitem'); 3852 Let('\save@par', '\par'); 3853 Let('\bibitem', '\restoring@bibitem'); 3854 Let('\par', '\par@in@bibliography'); 3855 Let('\vskip', '\vskip@in@bibliography'); 3856 # Moreover, some people use \item instead of \bibitem 3857 Let('\item', '\item@in@bibliography'); 3858 # And protect from redefinitions. 3859 Let('\newblock', '\lx@bibnewblock'); 3860 return; } 3861 3862DefMacroI('\par@in@bibliography', undef, sub { 3863 my ($gullet) = @_; 3864 $gullet->skipSpaces; 3865 my $tok = $gullet->readToken; 3866 # If next token is another \par, or a REAL \bibitem, 3867 if (Equals($tok, T_CS('\par')) || Equals($tok, T_CS('\bibitem'))) { 3868 ($tok); } # then this \par expands into what followed 3869 else { # Else, put it back, and start a bibitem. 3870 $gullet->unread($tok); 3871 (T_CS('\save@bibitem'), T_BEGIN, T_END); } }); 3872 3873DefMacro('\vskip@in@bibliography Glue', undef); 3874 3875DefMacroI('\item@in@bibliography', undef, '\save@bibitem{}'); 3876 3877# If we hit a real \bibitem, put \par & \bibitem back to correct defn, and then \bibitem. 3878# A bibitem with now key or label... 3879DefMacro('\restoring@bibitem', 3880 '\let\bibitem\save@bibitem\let\par\save@par\bibitem'); 3881 3882NewCounter('@bibitem', 'bibliography', idprefix => 'bib'); 3883DefMacroI('\the@bibitem', undef, '\arabic{@bibitem}'); 3884DefMacro('\@biblabel{}', '[#1]'); 3885DefMacroI('\fnum@@bibitem', undef, '{\@biblabel{\the@bibitem}}'); 3886# Hack for abused bibliographies; see below 3887DefMacro('\bibitem', 3888 '\if@lx@inbibliography\else\expandafter\lx@mung@bibliography\expandafter{\@currenvir}\fi' 3889 . '\lx@bibitem', locked => 1); 3890DefConstructor('\lx@bibitem[] Semiverbatim', 3891 "<ltx:bibitem key='#key' xml:id='#id'>" 3892 . "#tags" 3893 . "<ltx:bibblock>", 3894 afterDigest => sub { 3895 my $tag = $_[1]->getArg(1); 3896 my $key = CleanBibKey($_[1]->getArg(2)); 3897 if ($tag) { 3898 $_[1]->setProperties(key => $key, 3899 RefStepID('@bibitem'), 3900 tags => Digest(T_BEGIN, 3901 T_CS('\def'), T_CS('\the@bibitem'), T_BEGIN, Revert($tag), T_END, 3902 Invocation(T_CS('\lx@make@tags'), T_OTHER('@bibitem')), 3903 T_END)); } 3904 else { 3905 $_[1]->setProperties(key => $key, RefStepCounter('@bibitem')); } 3906 }); 3907 3908# This attempts to handle the case where folks put \bibitem's within an enumerate or such. 3909# We try to close the list and open the bibliography 3910DefMacro('\lx@mung@bibliography{}', sub { 3911 my ($gullet, $env) = @_; 3912 my $tag = ToString($env); 3913 my @tokens = (); 3914 # If we're in some sort of list environment, maybe we can recover 3915 if (($tag eq 'enumerate') || ($tag eq 'itemize') || ($tag eq 'description')) { 3916 # nDamn! We're in a list {$tag}; try to close it! 3917 push(@tokens, Invocation('\end', $env), 3918 T_CS('\let'), T_CS('\end' . $tag), T_CS('\endthebibliography'), 3919 T_CS('\let'), T_CS('\end{' . $tag . '}'), T_CS('\end{thebibliography}')); } 3920 # else ? it probably isn't going to work?? 3921 # Now, try to open {thebibliography} 3922 push(@tokens, Invocation('\begin', Tokenize('thebibliography'), Tokens())); 3923 return Tokens(@tokens); }); 3924 3925DefConstructorI('\lx@bibnewblock', undef, "<ltx:bibblock>"); 3926Let('\newblock', '\lx@bibnewblock'); 3927Tag('ltx:bibitem', autoClose => 1); 3928Tag('ltx:bibblock', autoClose => 1); 3929 3930#---------------------------------------------------------------------- 3931# We've got the same problem as LaTeX: Lather, Rinse, Repeat. 3932# It would be nice to know the bib info at digestion time 3933# * whether author lists will collapse 3934# * whether there are "a","b".. extensions on the year. 3935# We could process the bibliography first, (IF it is a separate *.bib!) 3936# but won't know which entries are included (and so can't resolve the a/b/c..) 3937# until we've finished looking at (all of) the source(s) that will refer to them! 3938# 3939# We can do this in 2 passes, however 3940# (1) convert (latexml) both the source document(s) and the bibliography 3941# (2) extract the required bibitems and integrate (latexmlpost) it into the documents. 3942# [Note that for mult-document sites, step (2) becomes 2 stages: scan and integrate] 3943# 3944# Here's the general layout. 3945# <ltx:cite> contains everything that the citations produce, 3946# including parens, pre-note, punctunation that precede the <ltx:bibcite> 3947# and punctuation, post-note, parens, that follow it. 3948# <ltx:bibcite show="string" bibrefs="keys" sep="" yysep="">phrases</ltx:bibcite> 3949# encodes the actual citation. 3950# 3951# bibrefs : lists the bibliographic keys that will be used 3952# show : gives the pattern for formatting using data from the bibliography 3953# It can contain: 3954# authors or fullauthors 3955# year 3956# number 3957# phrase1,phrase2,... selects one of the phrases from the content of the <ltx:bibref> 3958# This format is used as follows: 3959# If author and year is present, and a subset of the citations share the same authors, 3960# then the format is used, but the year is repeated for each citation in the subset, 3961# as a link to the bib entry. 3962# Otherwise, the format is applied to each entry. 3963# 3964# The design is intended to support natbib, as well as plain LaTeX. 3965 3966AssignValue(CITE_STYLE => 'numbers'); 3967AssignValue(CITE_OPEN => T_OTHER('[')); 3968AssignValue(CITE_CLOSE => T_OTHER(']')); 3969AssignValue(CITE_SEPARATOR => T_OTHER(',')); 3970AssignValue(CITE_YY_SEPARATOR => T_OTHER(',')); 3971AssignValue(CITE_NOTE_SEPARATOR => T_OTHER(',')); 3972AssignValue(CITE_UNIT => undef); 3973 3974DefMacro('\@cite{}{}', '[{#1\if@tempswa , #2\fi}]'); 3975DefConstructor('\@@cite []{}', "<ltx:cite ?#1(class='ltx_citemacro_#1')>#2</ltx:cite>", 3976 mode => 'text'); 3977 3978# \@@bibref{what to show}{bibkeys}{phrase1}{phrase2} 3979DefConstructor('\@@bibref Semiverbatim Semiverbatim {}{}', 3980 "<ltx:bibref show='#1' bibrefs='#bibrefs' inlist='#bibunit'" 3981 . " separator='#separator' yyseparator='#yyseparator'>#3#4</ltx:bibref>", 3982 properties => sub { (bibrefs => CleanBibKey($_[2]), 3983 separator => ToString(Digest(LookupValue('CITE_SEPARATOR'))), 3984 yyseparator => ToString(Digest(LookupValue('CITE_YY_SEPARATOR'))), 3985 bibunit => LookupValue('CITE_UNIT')); }); 3986 3987# Simple container for any phrases used in the bibref 3988DefConstructor('\@@citephrase{}', "<ltx:bibrefphrase>#1</ltx:bibrefphrase>", 3989 mode => 'text'); 3990 3991DefMacro('\cite[] Semiverbatim', sub { 3992 my ($gullet, $post, $keys) = @_; 3993 my ($style, $open, $close, $ns) 3994 = map { LookupValue($_) } qw(CITE_STYLE CITE_OPEN CITE_CLOSE CITE_NOTE_SEPARATOR); 3995 $post = undef unless $post && $post->unlist; 3996 Invocation(T_CS('\@@cite'), 3997 Tokens(Explode('cite')), 3998 Tokens($open, 3999 Invocation(T_CS('\@@bibref'), undef, $keys, undef, undef), 4000 ($post ? ($ns, T_SPACE, $post) : ()), $close)); }); 4001 4002# NOTE: Eventually needs to be recognized by MakeBibliography 4003DefConstructor('\nocite Semiverbatim', 4004 "<ltx:cite><ltx:bibref show='nothing' bibrefs='#bibrefs' inlist='#bibunit'/></ltx:cite>", 4005 properties => sub { 4006 (bibrefs => CleanBibKey($_[1]), 4007 bibunit => LookupValue('CITE_UNIT')); }); 4008#====================================================================== 4009# C.11.4 Splitting the input 4010#====================================================================== 4011Let('\@@input', '\input'); # Save TeX's version. 4012 # LaTeX's \input is a bit different... 4013DefMacroI('\input', undef, '\@ifnextchar\bgroup\@iinput\@@input'); 4014DefPrimitive('\@iinput {}', sub { Input(Expand($_[1])); }); 4015DefMacro('\@input{}', '\IfFileExists{#1}{\@@input\@filef@und}{\typeout{No file #1.}}'); 4016DefMacro('\@input@{}', '\InputIfFileExists{#1}{}{\typeout{No file #1.}}'); 4017 4018DefMacro('\quote@name{}', '"\quote@@name#1\@gobble""'); 4019DefMacro('\quote@@name{} Match:"', '#1\quote@@name'); 4020DefMacro('\unquote@name{}', '\quote@@name#1\@gobble"'); 4021 4022# Note that even excluded files SHOULD have the effects of their inclusion 4023# simulated by having read the corresponding aux file; 4024# But we're not bothering with that. 4025DefPrimitive('\include{}', sub { 4026 my ($stomach, $path) = @_; 4027 $path = ToString($path); 4028 my $table = LookupValue('including@only'); 4029 if (!$table || $$table{$path}) { 4030 Input($path); } 4031 return; }); 4032 4033# [note, this will load name.tex, if it exists, else name] 4034DefPrimitive('\includeonly{}', sub { 4035 my ($stomach, $paths) = @_; 4036 $paths = ToString($paths); 4037 my $table = LookupValue('including@only'); 4038 AssignValue('including@only', $table = {}, 'global') unless $table; 4039 map { $$table{$_} = 1 } map { /^\s*(.*?)\s*$/ && $1; } split(/,/, $paths); 4040 return; }); 4041 4042# NOTE: In the long run, we want to SAVE the contents and associate them with the given file name 4043# AND, arrange so that when a file is read, we'll use the contents! 4044DefConstructorI(T_CS("\\begin{filecontents}"), "Semiverbatim", 4045 '', 4046 reversion => '', 4047 afterDigest => [sub { 4048 my ($stomach, $whatsit) = @_; 4049 my $filename = ToString($whatsit->getArg(1)); 4050 my @lines = (); 4051 my $gullet = $stomach->getGullet; 4052 my $line; 4053 while (defined($line = $gullet->readRawLine) && ($line ne '\end{filecontents}')) { 4054 push(@lines, $line); } 4055 AssignValue($filename . '_contents' => join("\n", @lines), 'global'); 4056 NoteLog("Cached filecontents for $filename (" . scalar(@lines) . " lines)"); }]); 4057DefConstructorI(T_CS("\\begin{filecontents*}"), "Semiverbatim", 4058 '', 4059 reversion => '', 4060 afterDigest => [sub { 4061 my ($stomach, $whatsit) = @_; 4062 my $filename = ToString($whatsit->getArg(1)); 4063 my @lines = (); 4064 my $gullet = $stomach->getGullet; 4065 my $line; 4066 while (defined($line = $gullet->readRawLine) && ($line ne '\end{filecontents*}')) { 4067 push(@lines, $line); } 4068 AssignValue($filename . '_contents' => join("\n", @lines), 'global'); 4069 NoteLog("Cached filecontents* for $filename (" . scalar(@lines) . " lines)"); }]); 4070DefMacro('\endfilecontents', ''); 4071DefPrimitive('\listfiles', undef); 4072 4073#====================================================================== 4074# C.11.5 Index and Glossary 4075#====================================================================== 4076 4077# ---- The index commands 4078# Format of Index entries: 4079# \index{entry!entry} gives multilevel index 4080# Each entry: 4081# foo@bar sorts on "foo" but prints "bar" 4082# The entries can end with a |expression: 4083# \index{...|(} this page starts a range for foo 4084# \index{...|)} this page ends a range 4085# The last two aren't handled in any particular way. 4086# We _could_ mark start & end, and then the postprocessor would 4087# need to fill in all likely links... ??? 4088# \index{...|see{key}} cross reference. 4089# \index{...|seealso{key}} cross reference. 4090# \index{...|textbf} (etc) causes the number to be printed in bold! 4091# 4092# I guess the formula is that 4093# \index{foo|whatever{pi}{pa}{po}} => \whatever{pi}{pa}{po}{page} 4094# How should this get interpreted?? 4095our %index_style = (textbf => 'bold', bf => 'bold', textrm => '', rm => '', 4096 textit => 'italic', it => 'italic', emph => 'italic'); # What else? 4097 # A bit screwy, but.... 4098 # Expand \index{a!b!...} into \@index{\@indexphrase{a}\@indexphrase{b}...} 4099 4100sub process_index_phrases { 4101 my ($gullet, $phrases, $inlist) = @_; 4102 my @expansion = (); 4103 # Split the text into phrases, separated by "!" 4104 my @tokens = $phrases->unlist; 4105 return unless @tokens; 4106 push(@tokens, T_OTHER('!')) unless $tokens[-1]->getString eq '!'; # Add terminal ! 4107 my @phrase = (); 4108 my @sortas = (); 4109 my $style; 4110 while (@tokens) { 4111 my $tok = shift(@tokens); 4112 my $string = $tok->getString; 4113 if ($string eq '"') { 4114 push(@phrase, shift(@tokens)); } 4115 elsif ($string eq '@') { 4116 while (@phrase && ($phrase[-1]->getString =~ /\s/)) { pop(@phrase); } # Trim 4117 @sortas = @phrase; @phrase = (); } 4118 elsif (($string eq '!') || ($string eq '|')) { 4119 while (@phrase && ($phrase[-1]->getString =~ /\s/)) { pop(@phrase); } # Trim 4120 push(@expansion, T_CS('\@indexphrase'), 4121 (@sortas ? (T_OTHER('['), @sortas, T_OTHER(']')) : ()), 4122 T_BEGIN, @phrase, T_END) 4123 if @phrase; 4124 @phrase = (); @sortas = (); 4125 if ($string eq '|') { 4126 pop(@tokens); # Remove the extra "!" stopbit. 4127 my $extra = ToString(Tokens(@tokens)); 4128 if ($extra =~ /^see\s*{/) { push(@expansion, T_CS('\@indexsee'), @tokens[3 .. $#tokens]); } 4129 elsif ($extra =~ /^seealso\s*\{/) { push(@expansion, T_CS('\@indexseealso'), @tokens[7 .. $#tokens]); } 4130 elsif ($extra eq '(') { $style = 'rangestart'; } # ? 4131 elsif ($extra eq ')') { $style = 'rangeend'; } # ? 4132 else { $style = $index_style{$extra} || $extra; } 4133 @tokens = (); } } 4134 elsif (!@phrase && ($string =~ /\s/)) { } # Skip leading whitespace 4135 else { 4136 push(@phrase, $tok); } } 4137 @expansion = (T_CS('\@index'), 4138 ($style || $inlist ? (T_OTHER('['), ($style ? T_OTHER($style) : ()), T_OTHER(']')) : ()), 4139 ($inlist ? (T_OTHER('['), T_OTHER($inlist), T_OTHER(']')) : ()), 4140 T_BEGIN, @expansion, T_END); 4141 return (T_BEGIN, T_CS('\normalfont'), @expansion, T_END); } 4142 4143DefMacro('\index{}', \&process_index_phrases); 4144 4145Tag('ltx:indexphrase', afterClose => \&addIndexPhraseKey); 4146Tag('ltx:glossaryphrase', afterClose => \&addIndexPhraseKey); 4147### ltx:indexsee does NOT get a key (at this stage)! 4148 4149sub addIndexPhraseKey { 4150 my ($document, $node) = @_; 4151 if (!$node->getAttribute('key')) { 4152 $node->setAttribute(key => CleanIndexKey($node->textContent)); } 4153 return; } 4154 4155DefConstructor('\@index[][]{}', "^<ltx:indexmark style='#1' inlist='#2'>#3</ltx:indexmark>", 4156 mode => 'text', reversion => '', sizer => 0); 4157 4158DefConstructor('\@indexphrase[]{}', 4159 "<ltx:indexphrase key='#key' _standalone_font='true'>#2</ltx:indexphrase>", 4160 properties => { key => sub { CleanIndexKey($_[1]); } }); 4161DefConstructor('\@indexsee{}', 4162 "<ltx:indexsee key='#key' name='#name' _standalone_font='true'>#1</ltx:indexsee>", 4163 properties => { name => sub { DigestIf('\seename') } }); 4164 4165DefConstructor('\@indexseealso{}', 4166 "<ltx:indexsee key='#key' name='#name' _standalone_font='true'>#1</ltx:indexsee>", 4167 properties => { name => sub { DigestIf('\alsoname') } }); 4168 4169DefConstructor('\glossary{}', 4170 "<ltx:glossaryphrase role='glossary' key='#key'>#1</ltx:glossaryphrase>", 4171 properties => { key => sub { CleanIndexKey($_[1]); } }, 4172 sizer => 0); 4173 4174#====================================================================== 4175# This converts an indexphrase node into a sortable string. 4176# Seems the XML nodes are the best place to handle it (rather than Boxes), 4177# although some of the special cases (see, @, may end up tricky) 4178sub indexify { 4179 my ($node, $document) = @_; 4180 my $type = $node->nodeType; 4181 if ($type == XML_TEXT_NODE) { 4182 my $string = $node->textContent; 4183 $string =~ s/\W//g; # to be safe (if perhaps non-unique?) 4184 $string =~ s/\s//g; # Or remove entirely? Eventually worry about many=>1 mapping??? 4185 return $string; } 4186 elsif ($type == XML_ELEMENT_NODE) { 4187 if ($document->getModel->getNodeQName($node) eq 'ltx:Math') { 4188 return indexify_tex($node->getAttribute('tex')); } 4189 else { 4190 return join('', map { indexify($_, $document) } $node->childNodes); } } 4191 elsif ($type == XML_DOCUMENT_FRAG_NODE) { 4192 return join('', map { indexify($_, $document) } content_nodes($node)); } 4193 else { 4194 return ""; } } 4195 4196# Try to clean up a TeX string into something 4197# Could walk the math tree and handle XMDual specially, but need to xref args. 4198# But also we'd have unicode showing up, which we'd like to latinize... 4199sub indexify_tex { 4200 my ($string) = @_; 4201 $string =~ s/(\\\@|\\,|\\:|\\;|\\!|\\ |\\\/|)//g; 4202 $string =~ s/(\\mathrm|\\mathit|\\mathbf|\\mathsf|\\mathtt|\\mathcal|\\mathscr|\\mbox|\\rm|\\it|\\bf|\\tt|\\small|\\tiny)//g; 4203 $string =~ s/\\left\b//g; $string =~ s/\\right\b//g; 4204 $string =~ s/(\\|\{|\})//g; 4205 $string =~ s/\W//g; # to be safe (if perhaps non-unique?) 4206 $string =~ s/\s//g; # Or remove entirely? Eventually worry about many=>1 mapping??? 4207 return $string; } 4208 4209# ---- Creating the index itself 4210 4211AssignValue(INDEXLEVEL => 0); 4212 4213Tag('ltx:indexentry', autoClose => 1); 4214 4215sub closeIndexPhrase { 4216 my ($document) = @_; 4217 if ($document->isCloseable('ltx:indexphrase')) { 4218 $document->closeElement('ltx:indexphrase'); } 4219 return; } 4220 4221sub doIndexItem { 4222 my ($document, $level) = @_; 4223 $document->closeElement('ltx:indexrefs') if $document->isCloseable('ltx:indexrefs'); 4224 closeIndexPhrase($document); 4225 my $l = LookupValue('INDEXLEVEL'); 4226 while ($l < $level) { 4227 $document->openElement('ltx:indexlist'); $l++; } 4228 while ($l > $level) { 4229 $document->closeElement('ltx:indexlist'); $l--; } 4230 AssignValue(INDEXLEVEL => $l); 4231 if ($level) { 4232 $document->openElement('ltx:indexentry'); 4233 $document->openElement('ltx:indexphrase'); } 4234 return; } 4235 4236DefConstructorI('\index@dotfill', undef, sub { 4237 my ($document) = @_; 4238 closeIndexPhrase($document); 4239 $document->openElement('ltx:indexrefs'); }); 4240DefConstructorI('\index@item', undef, sub { doIndexItem($_[0], 1); }); 4241DefConstructorI('\index@subitem', undef, sub { doIndexItem($_[0], 2); }); 4242DefConstructorI('\index@subsubitem', undef, sub { doIndexItem($_[0], 3); }); 4243DefConstructorI('\index@done', undef, sub { doIndexItem($_[0], 0); }); 4244 4245DefMacroI('\indexname', undef, 'Index'); 4246DefEnvironment('{theindex}', 4247 "<ltx:index xml:id='#id'>" 4248 . "<ltx:title font='#titlefont' _force_font='true'>#title</ltx:title>" 4249 . "#body" 4250 . "</ltx:index>", 4251 beforeDigest => sub { 4252 Let('\item', '\index@item'); 4253 Let('\subitem', '\index@subitem'); 4254 Let('\subsubitem', '\index@subsubitem'); 4255 Let('\dotfill', '\index@dotfill'); }, 4256 beforeDigestEnd => sub { Digest(T_CS('\index@done')); }, 4257 afterDigestBegin => sub { 4258 my $docid = ToString(Expand(T_CS('\thedocument@ID'))); 4259 my $title = DigestIf('\indexname'); 4260 noteBackmatterElement($_[1], 'ltx:index'); 4261 $_[1]->setProperties(id => ($docid ? "$docid.idx" : 'idx'), 4262 title => $title, 4263 titlefont => $title->getFont); }, 4264 beforeConstruct => sub { adjustBackmatterElement($_[0], $_[1]); }); 4265 4266DefPrimitiveI('\indexspace', undef, undef); 4267DefPrimitiveI('\makeindex', undef, undef); 4268DefPrimitiveI('\makeglossary', undef, undef); 4269 4270#====================================================================== 4271# C.11.6 Terminal Input and Output 4272#====================================================================== 4273 4274DefPrimitive('\typeout{}', sub { 4275 my ($stomach, $stuff) = @_; 4276 Note(ToString(Expand($stuff))); 4277 return; }); 4278 4279DefPrimitive('\typein[]{}', undef); 4280 4281#********************************************************************** 4282# C.12 Line and Page Breaking 4283#********************************************************************** 4284 4285#====================================================================== 4286# C.12.1 Line Breaking 4287#====================================================================== 4288DefPrimitive('\linebreak[]', undef); 4289DefPrimitive('\nolinebreak[]', undef); 4290DefPrimitiveI('\-', undef, undef); # We don't do hyphenation. 4291 # \hyphenation in TeX.pool 4292 4293DefPrimitiveI('\sloppy', undef, undef); 4294DefPrimitiveI('\fussy', undef, undef); 4295# sloppypar can be used as an environment, or by itself. 4296DefMacro('\sloppypar', '\par\sloppy'); 4297DefMacro('\endsloppypar', '\par'); 4298DefMacroI('\nobreakdashes', undef, T_OTHER('-')); 4299 4300DefMacro('\showhyphens{}', '#1'); # ? 4301 #====================================================================== 4302 # C.12.2 Page Breaking 4303 #====================================================================== 4304DefMacro('\pagebreak[]', '\vadjust{\clearpage}'); 4305DefPrimitive('\nopagebreak[]', undef); 4306DefPrimitiveI('\columnbreak', undef, undef); # latex? or multicol? 4307DefPrimitive('\enlargethispage OptionalMatch:* {}', undef); 4308 4309DefMacroI('\clearpage', undef, '\LTX@newpage'); 4310DefMacroI('\cleardoublepage', undef, '\LTX@newpage'); 4311DefPrimitiveI('\samepage', undef, undef); 4312 4313#********************************************************************** 4314# C.13 Lengths, Spaces and Boxes 4315#********************************************************************** 4316 4317##### 4318##### 4319# Complete to here 4320# [except for NOTE'd entries, of course] 4321##### 4322##### 4323 4324#====================================================================== 4325# C.13.1 Length 4326#====================================================================== 4327# \fill 4328DefMacro('\stretch{}', '0pt plus #1fill\relax'); 4329 4330DefPrimitive('\newlength DefToken', sub { 4331 my ($stomach, $cs) = @_; 4332 DefRegisterI($cs, undef, Glue(0)); }); 4333DefPrimitive('\setlength {Variable}{Dimension}', sub { 4334 my ($stomach, $variable, $length) = @_; 4335 my ($defn, @params) = @$variable; 4336 if (ref $defn) { 4337 $defn->setValue($length, @params); } 4338 return; }); 4339DefPrimitive('\addtolength {Variable}{Dimension}', sub { 4340 my ($stomach, $variable, $length) = @_; 4341 my ($defn, @params) = @$variable; 4342 if (ref $defn) { 4343 my $oldlength = $defn->valueOf(@params); 4344 $defn->setValue($defn->valueOf(@params)->add($length), @params); } 4345 return; }); 4346 4347DefMacro('\@settodim{}{}{}', 4348 '\setbox\@tempboxa\hbox{{#3}}#2#1\@tempboxa\setbox\@tempboxa\box\voidb@x'); 4349DefMacroI('\settoheight', undef, '\@settodim\ht'); 4350DefMacroI('\settodepth', undef, '\@settodim\dp'); 4351DefMacroI('\settowidth', undef, '\@settodim\wd'); 4352DefMacro('\@settopoint{}', '\divide#1\p@\multiply#1\p@'); 4353 4354# Assuming noone tries to get clever with figuring out the allocation of 4355# numbers, these become simple DefRegister's 4356DefPrimitive('\newcount DefToken', sub { DefRegisterI($_[1], undef, Number(0)); }); 4357DefPrimitive('\newdimen DefToken', sub { DefRegisterI($_[1], undef, Dimension(0)); }); 4358DefPrimitive('\newskip DefToken', sub { DefRegisterI($_[1], undef, Glue(0)); }); 4359DefPrimitive('\newmuskip DefToken', sub { DefRegisterI($_[1], undef, MuGlue(0)); }); 4360DefPrimitive('\newtoks DefToken', sub { DefRegisterI($_[1], undef, Tokens()); }); 4361 4362DefRegister('\fill', Glue(0, '1fill')); 4363 4364#====================================================================== 4365# C.13.2 Space 4366#====================================================================== 4367DefPrimitive('\hspace OptionalMatch:* {Dimension}', sub { 4368 my ($stomach, $star, $length) = @_; 4369 my $s = DimensionToSpaces($length); 4370 return unless defined $s; 4371 Box($s, undef, undef, Invocation(T_CS('\hskip'), $length), 4372 width => $length, isSpace => 1); }); 4373 4374DefPrimitive('\vspace OptionalMatch:* {}', undef); 4375DefPrimitive('\addvspace {}', undef); 4376DefPrimitive('\addpenalty {}', undef); 4377# \hfill, \vfill 4378 4379#====================================================================== 4380# C.13.3 Boxes 4381#====================================================================== 4382# Can't really get these? 4383DefMacroI('\height', undef, '0pt'); 4384DefMacroI('\totalheight', undef, '0pt'); 4385DefMacroI('\depth', undef, '0pt'); 4386DefMacroI('\width', undef, '0pt'); 4387 4388DefConstructor('\mbox {}', 4389 "<ltx:text _noautoclose='1'>#1</ltx:text>", mode => 'text', bounded => 1, 4390 sizer => '#1', 4391 robust => 1, 4392 beforeDigest => sub { reenterTextMode(); }); 4393 4394our %makebox_alignment = (l => 'left', r => 'right', s => 'justified'); 4395DefMacro('\makebox', '\@ifnextchar(\pic@makebox\@makebox', robust => 1); 4396DefConstructor('\@makebox[Dimension][]{}', 4397 "<ltx:text ?#width(width='#width') ?#align(align='#align') _noautoclose='1'>#3</ltx:text>", 4398 mode => 'text', bounded => 1, alias => '\makebox', sizer => '#3', 4399 beforeDigest => sub { reenterTextMode(); }, 4400 properties => sub { 4401 (($_[2] ? (align => $makebox_alignment{ ToString($_[2]) }) : ()), 4402 ($_[1] ? (width => $_[1]) : ())) }); 4403 4404DefRegister('\fboxrule', Dimension('.4pt')); 4405DefRegister('\fboxsep', Dimension('3pt')); 4406 4407# Peculiar special case! 4408# These are nominally text mode macros. However, there is a somewhat common idiom: 4409# $ ... \framebox{$operator$} ... $ 4410# in which case the operator gets boxed and really should be treated as a math object. 4411# (and ultimately converted to mml:menclose) 4412# So, we need to switch to text mode, as usual, but FIRST note whether we started in math mode! 4413# Afterwards, if we were in math mode, and the content is math, we'll convert the whole thing 4414# to a framed math object. 4415# Second special issue: 4416# Although framebox doesn't allow flowed content inside, it is also somewhat common 4417# to put a vbox or some other block construct inside. 4418# Seemingly, the ultimate html gets somewhat tangled (browser bugs?) 4419# At any rate, since we're wrapping with an ltx:text, we'll try to unwrap it, 4420# if the contents are a single child that can handle the framing. 4421 4422DefMacro('\fbox{}', '\@framebox{#1}', robust => 1); 4423DefMacro('\framebox', '\@ifnextchar(\pic@framebox\@framebox', robust => 1); 4424DefConstructor('\@framebox[Dimension][]{}', 4425 "?#mathframe(<ltx:XMArg enclose='box'>#inner</ltx:XMArg>)" 4426 . "(<ltx:text ?#width(width='#width') ?#align(align='#align')" 4427 . " framed='rectangle' framecolor='#framecolor'" 4428 . " _noautoclose='1'>#3</ltx:text>)", 4429 alias => '\framebox', sizer => '#3', 4430 beforeDigest => sub { 4431 my ($stomach) = @_; 4432 my $wasmath = LookupValue('IN_MATH'); 4433 $stomach->beginMode('text'); 4434 AssignValue(FRAME_IN_MATH => $wasmath); }, 4435 properties => sub { 4436 (($_[2] ? (align => $makebox_alignment{ ToString($_[2]) }) : ()), 4437 framecolor => LookupValue('font')->getColor, 4438 ($_[1] ? (width => $_[1]) : ())); }, 4439 afterDigest => sub { 4440 my ($stomach, $whatsit) = @_; 4441 my $wasmath = LookupValue('FRAME_IN_MATH'); 4442 my $arg = $whatsit->getArg(3); 4443 $stomach->endMode('text'); 4444 if ($wasmath && $arg->isMath) { 4445 $whatsit->setProperties(mathframe => 1, inner => $arg->getBody); } 4446 return; }, 4447 afterConstruct => sub { 4448 my ($document, $whatsit) = @_; 4449 my $node = $document->getNode->lastChild; 4450 # If the generated node, has only a single (non space) child 4451 my @c = grep { ($_->nodeType != XML_TEXT_NODE) || ($_->textContent =~ /[^\s\n]/) } 4452 $node->childNodes; 4453 my $model = $document->getModel; 4454 # and that child can have the framed attribute 4455 if ((scalar(@c) == 1) 4456 && $document->canHaveAttribute($model->getNodeQName($c[0]), 'framed')) { 4457 # unwrap, copying the attributes 4458 $document->unwrapNodes($node); 4459 foreach my $k (qw(width align framed)) { 4460 if (my $v = $node->getAttribute($k)) { 4461 $document->setAttribute($c[0], $k => $v); } } } } 4462); 4463 4464DefPrimitive('\newsavebox DefToken', sub { 4465 my $n = LookupValue('allocated_boxes') + 1; 4466 AssignValue(allocated_boxes => $n, 'global'); 4467 DefRegisterI($_[1], undef, Number($n)); 4468 AssignValue('box' . $n, List()); }); 4469 4470RawTeX(<<'EOL'); 4471\def\newsavebox#1{\@ifdefinable{#1}{\newbox#1}} 4472\DeclareRobustCommand\savebox[1]{% 4473 \@ifnextchar(%) 4474 {\@savepicbox#1}{\@ifnextchar[{\@savebox#1}{\sbox#1}}}% 4475\DeclareRobustCommand\sbox[2]{\setbox#1\hbox{% 4476 \color@setgroup#2\color@endgroup}} 4477\def\@savebox#1[#2]{% 4478 \@ifnextchar [{\@isavebox#1[#2]}{\@isavebox#1[#2][c]}} 4479\long\def\@isavebox#1[#2][#3]#4{% 4480 \sbox#1{\@imakebox[#2][#3]{#4}}} 4481\def\@savepicbox#1(#2,#3){% 4482 \@ifnextchar[%] 4483 {\@isavepicbox#1(#2,#3)}{\@isavepicbox#1(#2,#3)[]}} 4484\long\def\@isavepicbox#1(#2,#3)[#4]#5{% 4485 \sbox#1{\@imakepicbox(#2,#3)[#4]{#5}}} 4486\def\lrbox#1{% 4487 \edef\reserved@a{% 4488 \endgroup 4489 \setbox#1\hbox{% 4490 \begingroup\aftergroup}% 4491 \def\noexpand\@currenvir{\@currenvir}% 4492 \def\noexpand\@currenvline{\on@line}}% 4493 \reserved@a 4494 \@endpefalse 4495 \color@setgroup 4496 \ignorespaces} 4497\def\endlrbox{\unskip\color@endgroup} 4498\DeclareRobustCommand\usebox[1]{\leavevmode\copy #1\relax} 4499EOL 4500 4501# A soft sorta \par that only closes an ltx:p, but not ltx:para 4502DefConstructor('\lx@parboxnewline[]', sub { 4503 my ($document, $ignore, %props) = @_; 4504 $document->maybeCloseElement('ltx:p'); 4505 return; }, 4506 properties => { isBreak => 1 }); 4507 4508# NOTE: There are 2 extra arguments (See LaTeX Companion, p.866) 4509# for height and inner-pos. We're ignoring them, for now, though. 4510DefConstructor('\parbox[] OptionalUndigested OptionalUndigested {Dimension} VBoxContents', sub { 4511 my ($document, $attachment, $b, $c, $width, $body, %props) = @_; 4512 insertBlock($document, $body, 4513 width => $width, 4514 vattach => $props{vattach}, 4515 class => 'ltx_parbox'); 4516 return; }, 4517 sizer => '#5', 4518 properties => sub { 4519 (width => $_[4], 4520 vattach => translateAttachment($_[1]), 4521 height => $_[2]); }, 4522 mode => 'text', bounded => 1, 4523 robust => 1, 4524 beforeDigest => sub { 4525 AssignValue('\hsize' => $_[4]); 4526 Let('\\\\', '\lx@parboxnewline'); }); 4527DefMacroI('\@parboxrestore', undef, Tokens()); 4528 4529DefConditional('\if@minipage'); 4530DefEnvironment('{minipage}[][][]{Dimension}', sub { 4531 my ($document, $attachment, $b, $c, $width, %props) = @_; 4532 my $vattach = translateAttachment($attachment); 4533 insertBlock($document, $props{body}, 4534 para => 1, 4535 width => $width, 4536 vattach => $vattach, 4537 class => 'ltx_minipage'); 4538 return; }, 4539 sizer => '#body', 4540 mode => 'text', 4541 properties => sub { ( 4542 width => $_[4], 4543 vattach => translateAttachment($_[1])); }, 4544 beforeDigest => sub { 4545 Digest(T_CS('\@minipagetrue')); 4546 AssignValue('\hsize' => $_[4]); 4547 # this conflicts (& not needed?) with insertBlock 4548 Let('\\\\', '\lx@parboxnewline'); }); 4549 4550DefConstructor('\rule[Dimension]{Dimension}{Dimension}', 4551 "<ltx:rule ?#offset(yoffset='#offset') width='#width' height='#height'/>", 4552 properties => sub { (offset => $_[1], width => $_[2], height => $_[3]) }); 4553DefConstructor('\raisebox{Dimension}[Dimension][Dimension]{}', 4554 "<ltx:text yoffset='#1' _noautoclose='1'>#4</ltx:text>", 4555 mode => 'text', bounded => 1, 4556 beforeDigest => sub { reenterTextMode(); }, 4557 sizer => sub { raisedSizer($_[0]->getArg(4), $_[0]->getArg(1)); }); 4558 4559sub raisedSizer { 4560 my ($box, $y) = @_; 4561 my ($w, $h, $d) = $box->getSize; 4562 my $z = Dimension(0); 4563 $h = $h->add($y)->larger($z); 4564 $d = $d->subtract($y)->larger($z); 4565 return ($w, $h, $d); } 4566 4567#********************************************************************** 4568# C.14 Pictures and Color 4569#********************************************************************** 4570 4571# These are stubs for color or xcolor packages 4572Let('\set@color', '\relax'); 4573Let('\color@begingroup', '\relax'); 4574Let('\color@endgroup', '\relax'); 4575Let('\color@setgroup', '\relax'); 4576Let('\color@hbox', '\relax'); 4577Let('\color@vbox', '\relax'); 4578Let('\color@endbox', '\relax'); 4579 4580#====================================================================== 4581# C.14.1 The picture environment 4582#====================================================================== 4583 4584#---------------------------------------------------------------------- 4585 4586sub ResolveReader { 4587 my ($type) = @_; 4588 return $type if ref $type eq 'CODE'; # It's already a reader (hopefully). 4589 $type = ToString($type); 4590 if (my $descriptor = LookupMapping('PARAMETER_TYPES', $type)) { 4591 return $$descriptor{reader}; } 4592 # Otherwise, try to find a function named Read<Type> 4593 else { 4594 return LaTeXML::Core::Parameter::checkReaderFunction('Read' . $type); } } 4595 4596# This defines a Pair parameter type, 4597# that reads a parenthesized, comma separated pair of subtype. 4598# By default, the subtype is Float, but you can write Pair:Number, or Pair:Dimension 4599sub ReadPair { 4600 my ($gullet, $itemtype, $xarg, $yarg) = @_; 4601 my $itemreader; 4602 if (!$itemtype) { $itemreader = \&ReadFloat; } 4603 else { $itemreader = ResolveReader($itemtype); } 4604 if (!$itemreader) { 4605 Error('misdefined', $itemtype, $gullet, "Can't find reader for Pair items from '$itemtype'"); 4606 return Pair(Dimension(0), Dimension(0)); } # Assume something like this? 4607 $gullet->skipSpaces; 4608 if ($gullet->ifNext(T_OTHER('('))) { 4609 $gullet->readToken; $gullet->skipSpaces; 4610 if ($gullet->ifNext(T_OTHER('!'))) { # maybe only for pstricks??? 4611 Error('unexpected', 'postscript', $gullet, 4612 "Cannot process escape to postscript"); 4613 $gullet->readUntil(T_OTHER(')')); $gullet->skipSpaces; 4614 return Pair(Dimension(0), Dimension(0)); } 4615 my $x = &$itemreader($gullet, $xarg); 4616 # This had also recognized ";" as a possible separator; if needed, will need to generalize 4617 # but we want to make readUntil accept only a single match 4618 $gullet->skipSpaces; $gullet->readUntil(T_OTHER(',')); $gullet->skipSpaces; 4619 my $y = &$itemreader($gullet, $yarg); 4620 $gullet->skipSpaces; $gullet->readUntil(T_OTHER(')')); $gullet->skipSpaces; 4621 return Pair($x, $y); } 4622 else { 4623 return; } } 4624 4625sub ptValue { 4626 my ($value) = @_; 4627 return ((defined $value) && (ref $value) ? $value->ptValue : $value); } 4628 4629sub pxValue { 4630 my ($value) = @_; 4631 return ((defined $value) && (ref $value) ? $value->pxValue : $value); } 4632 4633#---------------------------------------------------------------------- 4634# Picture parameters. 4635DefRegister('\unitlength', Dimension('1pt')); 4636DefPrimitiveI('\thinlines', undef, sub { AssignValue('\@wholewidth', Dimension('0.4pt')); }); 4637DefPrimitiveI('\thicklines', undef, sub { AssignValue('\@wholewidth', Dimension('0.8pt')); }); 4638DefRegister('\@wholewidth' => Dimension('0.4pt')); 4639DefRegister('\@halfwidth' => Dimension('0.2pt')); 4640DefMacro('\linethickness{}', '\@wholewidth #1\relax'); 4641 4642DefPrimitive('\arrowlength{Dimension}', sub { AssignValue('\arrowlength', $_[1]); }); 4643 4644#---------------------------------------------------------------------- 4645# Picture transformation support 4646sub slopeToPicCoord { 4647 my ($slope, $xlength) = @_; 4648 my ($mx, $my) = ($slope->getX, $slope->getY); 4649 my $s = $mx->sign(); 4650 $xlength = picScale($xlength); 4651 return Pair($xlength->multiply($s), 4652 $xlength->multiply(($s == 0) ? $my->sign() : 4653 $my->valueOf / $mx->absolute->valueOf)); } 4654 4655# Scale $value by \unitlength 4656sub picScale { 4657 my ($value) = @_; 4658 # Vain attempt at proper type coercion 4659 my $type = ref $value; 4660 my $unit = LookupValue('\unitlength'); 4661 if (!$value) { } 4662 elsif ($type eq 'LaTeXML::Common::Number') { 4663 return $unit->multiply($value); } 4664 elsif ($type eq 'LaTeXML::Common::Float') { 4665 return $unit->multiply($value); } 4666 elsif ($type eq 'LaTeXML::Common::Pair') { 4667 return Pair($unit->multiply($value->getX), $unit->multiply($value->getY)); } 4668 return $value->multiply($unit); } 4669 4670sub picProperties { 4671 my (%props) = @_; 4672 if (($props{stroke} || 'black') ne 'none') { 4673 $props{thick} = ptValue(LookupValue('\@wholewidth')); } 4674 if (my $arrowlength = LookupValue('\arrowlength')) { 4675 $props{arrowlength} = ptValue($arrowlength); } 4676 return %props; } 4677 4678#---------------------------------------------------------------------- 4679# the code 4680DefMacroI('\qbeziermax', undef, '500'); 4681 4682sub before_picture { 4683 Let('\raisebox', '\pic@raisebox'); # ? needs special treatment within picture 4684 return; } 4685 4686sub after_picture { 4687 return; } 4688 4689# Ugh... Is this safe? Apparently, picture stuff is allowed w/o a {picture} environment??? 4690Tag('ltx:picture', autoOpen => 0.5, autoClose => 1, 4691 afterOpen => sub { GenerateID(@_, 'pic'); }, 4692 afterClose => sub { 4693 my ($document, $node, $whatsit) = @_; 4694 # Make sure we get a size, in case autoOpen'd 4695 if ($whatsit) { 4696 my ($w, $h, $d) = $whatsit->getSize; 4697 $node->setAttribute(width => $w->pxValue) unless $node->hasAttribute('width'); 4698 $node->setAttribute(height => $h->add($d)->pxValue) unless $node->hasAttribute('height'); } }); 4699 4700# Note: Untex should prefix a setting of unitlength!!! 4701# First pair is (width,height) 4702# Second pair is the coordinate of the lower-left corner. 4703# [Note that for SVG the root viewport origin is at the TOP-left corner! 4704# but that is currently handled in the SVG postprocessing module] 4705DefEnvironment('{picture} Pair OptionalPair', 4706 "<ltx:picture width='#width' height='#height' origin-x='#origin-x' origin-y='#origin-y'" 4707 . " fill='none' stroke='none' unitlength='#unitlength'>" 4708 . "?#transform(<ltx:g transform='#transform'>#body</ltx:g>)(#body)" 4709 . "</ltx:picture>", 4710 mode => 'text', 4711 beforeDigest => \&before_picture, 4712 properties => sub { 4713 my ($stomach, $size, $pos) = @_; 4714 picProperties(unitlength => LookupValue('\unitlength'), 4715 width => $size && picScale($size->getX), 4716 height => $size && picScale($size->getY), 4717 depth => Dimension(0), 4718 'origin-x' => $pos && picScale($pos->getX), 4719 'origin-y' => $pos && picScale($pos->getY), 4720 ($pos ? (transform => 'translate(' . picScale($pos->negate)->pxValue . ')') : ())); }, 4721 afterDigest => \&after_picture); 4722 4723DefMacroI(T_CS('\Gin@driver'), undef, Tokens()); 4724 4725DefMacro('\put Until:){}', '\lx@pic@put#1){#2\relax}'); 4726DefConstructor('\lx@pic@put Pair{}', 4727 "<ltx:g transform='#transform'" 4728 . " innerwidth='#innerwidth' innerheight='#innerheight' innerdepth='#innerdepth'>#2</ltx:g>", 4729 properties => sub { 4730 my ($w, $h, $d) = $_[2]->getSize; 4731 $w = undef if $w && ($w->ptValue == 0); 4732 (transform => 'translate(' . picScale($_[1])->pxValue . ')', 4733 innerwidth => $w, innerheight => $h, innerdepth => $d); }, 4734 alias => '\put', 4735 mode => 'text'); 4736 4737DefConstructor('\line Pair:Number {Float}', 4738 "<ltx:line points='#points' stroke='black' stroke-width='#thick'/>", 4739 alias => '\line', 4740 properties => sub { picProperties(points => '0,0 ' . slopeToPicCoord($_[1], $_[2])->pxValue()); }); 4741DefConstructor('\vector Pair:Number {Float}', 4742 "<ltx:line points='#points' stroke='black' stroke-width='#thick' terminators='->'" 4743 . " arrowlength='#arrowlength'/>", 4744 alias => '\vector', 4745 properties => sub { picProperties(points => '0,0 ' . slopeToPicCoord($_[1], $_[2])->pxValue()); }); 4746DefConstructor('\circle OptionalMatch:* {Float}', 4747 "<ltx:circle x='0' y='0' r='#radius' fill='#fill' stroke='#stroke' stroke-width='#thick'/>", 4748 alias => '\circle', 4749 properties => sub { 4750 my ($stomach, $filled, $dia) = @_; 4751 picProperties(radius => picScale($dia)->multiply(0.5)->pxValue, 4752 ($filled ? 'fill' : 'stroke') => 'black'); }); 4753 4754DefConstructor('\oval [Float] Pair []', 4755 "<ltx:rect x='#x' y='#y' width='#width' height='#height' rx='#radius'" 4756 . " stroke='black' fill='none' part='#3' stroke-width='#thick'/>", 4757 alias => '\oval', 4758 properties => sub { 4759 my ($stomach, $r, $size, $part) = @_; 4760 $size = picScale($size); 4761 my $halfsize = $size->multiply(0.5); 4762 $r = ($r ? picScale($r) : Dimension('40pt')); 4763 $r = $r->smaller($halfsize->getX->absolute); 4764 $r = $r->smaller($halfsize->getY->absolute); 4765 picProperties(size => $size, radius => Dimension($r->valueOf), 4766 x => $halfsize->getX->negate, y => $halfsize->getY->negate, 4767 width => Dimension($size->getX->valueOf), 4768 height => Dimension($size->getY->valueOf), 4769 ); }); 4770 4771DefConstructor('\qbezier [Number] Pair Pair Pair', 4772"<ltx:bezier ?#1(displayedpoints='#1') points='&ptValue(#pt)' stroke='black' stroke-width='#thick' />", 4773 alias => '\qbezier', 4774 properties => sub { 4775 picProperties(pt => PairList(picScale($_[2]), picScale($_[3]), picScale($_[4]))); }); 4776 4777DefMacro('\bezier Until:(', '\ifx.#1.\lx@pic@bezier{0}(\else\lx@pic@bezier{#1}(\fi'); 4778DefConstructor('\lx@pic@bezier {Number} Pair Pair Pair', 4779 "<ltx:bezier ?#1(displayedpoints='#1') points='&ptValue(#pt)' stroke-width='#thick' />", 4780 alias => '\bezier', 4781 properties => sub { 4782 picProperties(pt => PairList(picScale($_[2]), picScale($_[3]), picScale($_[4]))); }); 4783 4784# Generic boxing command (frames, dash, etc) 4785DefConstructor('\pic@makebox@ Undigested RequiredKeyVals Pair []{}', 4786 "?#framed(<ltx:rect x='0' y='0' width='#fwidth' height='#fheight'" 4787 . " stroke='black' stroke-width='#thick' fill='none' stroke-dasharray='#dash'/>)()" 4788 . "<ltx:g class='makebox' innerwidth='#width' innerheight='#height' innerdepth='#depth'" 4789 . " transform='translate(#xshift,#yshift)'>#box</ltx:g>", 4790 reversion => '#1#3[#4]{#5}', 4791 beforeDigest => sub { reenterTextMode(); }, 4792 properties => sub { 4793 my ($stomach, $cs, $kv, $size, $pos, $box) = @_; 4794 my ($w, $h, $d) = $box->getSize; 4795 my ($ww, $hh) = ($w, $h); 4796 my ($x, $y) = (Dimension(0), Dimension(0)); 4797 my $ht = ($h ? ($d ? $h->add($d) : $h) : ($d ? $d : Dimension(0))); 4798 if ($size) { # && (($size->getX->valueOf != 0) || ($size->getY->valueOf != 0))) { 4799 $ww = picScale($size->getX); 4800 $hh = picScale($size->getY); 4801 $pos = ($pos ? lc(ToString($pos)) : ''); 4802 if ($pos =~ /l/) { $x = Number(0); } 4803 elsif ($pos =~ /r/) { $x = $ww->subtract($w); } 4804 else { $x = $ww->subtract($w)->divide(2); } 4805 if ($pos =~ /b/) { $y = Number(0); } 4806 elsif ($pos =~ /t/) { $y = $hh->subtract($ht); } 4807 else { $y = $hh->subtract($ht)->divide(2); } } 4808 my $fw = ($ww && $ww->valueOf ? $ww : $w); 4809 my $fh = ($hh && $hh->valueOf ? $hh : $h->add($d)); 4810 picProperties( 4811 box => $box, 4812 width => $w, height => $h, depth => $d, 4813 fwidth => $fw, fheight => $fh, 4814 xshift => $x->pxValue, yshift => $y->pxValue, 4815 framed => $kv->getValue('framed'), dash => $kv->getValue('dash') # dashed 4816 ); }); 4817 4818DefMacro('\pic@makebox', '\pic@makebox@{\makebox}{}'); 4819DefMacro('\pic@framebox', '\pic@makebox@{\framebox}{framed=true}'); 4820DefMacro('\lx@pic@dashbox{Float}', '\pic@makebox@{\dashbox(#1)}{framed=true,dash={#1}}'); 4821DefMacro('\dashbox Until:(', '\ifx.#1.\lx@pic@dashbox{0}(\else\lx@pic@dashbox{#1}(\fi'); 4822DefMacro('\frame{}', '\pic@makebox@{\framebox}{framed=true}(0,0)[bl]{#1}'); 4823 4824DefMacro('\pic@savebox DefToken Pair []{}', '\pic@@savebox{#1}{\pic@makebox #2[#3]{#4}}'); 4825DefPrimitive('\pic@@savebox DefToken {}', sub { 4826 AssignValue('box' . ToString($_[1]), Digest($_[2])); return; }); 4827DefMacro('\@savepicbox', '\pic@savebox'); 4828 4829DefConstructor('\pic@raisebox{Dimension}[Dimension][Dimension]{}', 4830 "<ltx:g y='#1'>#4</ltx:g>", 4831 alias => '\raisebox'); 4832 4833our %alignments = (l => 'left', c => 'center', r => 'right'); 4834# Not sure that ltx:p is the best to use here, but ... (see also \vbox, \vtop) 4835# This should be fairly compact vertically. 4836DefConstructor('\@shortstack@cr', 4837 "</ltx:p><ltx:p>", 4838 properties => { isBreak => 1 }, 4839 reversion => Tokens(T_CS("\\\\"), T_CR), 4840 beforeDigest => sub { $_[0]->egroup; }, 4841 afterDigest => sub { $_[0]->bgroup; }); 4842 4843DefConstructor('\shortstack[]{} OptionalMatch:* [Dimension]', 4844 "<ltx:inline-block align='#align'><ltx:p>#2</ltx:p></ltx:inline-block>", 4845 bounded => 1, 4846 sizer => '#2', 4847 beforeDigest => sub { reenterTextMode(); 4848 # then RE-RE-define this one!!! 4849 Let("\\\\", '\@shortstack@cr'); 4850 $_[0]->bgroup; }, 4851 afterDigest => sub { $_[0]->egroup; }, 4852 # Note: does not get layout=vertical, since linebreaks are explicit 4853 properties => { align => sub { ($_[1] ? $alignments{ ToString($_[1]) } : undef); }, 4854 vattach => 'top' }, # for size computation 4855 mode => 'text'); 4856 4857DefMacro('\multiput Pair Pair {}{}', sub { 4858 my ($gullet, $pos, $d, $nt, $body) = @_; 4859 my ($x, $y, $dx, $dy, $n) 4860 = map { ToString($_) } ($pos->getX, $pos->getY, $d->getX, $d->getY, $nt); 4861 my @exp = (); 4862 for (my $i = 0 ; $i < $n ; $i++) { 4863 push(@exp, T_CS('\put'), T_OTHER('('), Explode($x), T_OTHER(','), Explode($y), T_OTHER(')'), 4864 T_BEGIN, $body->unlist, T_END); 4865 $x += $dx; $y += $dy; } 4866 @exp; }); 4867 4868Tag('ltx:picture', 4869 afterOpen => sub { 4870 my ($document, $node, $thing) = @_; 4871 if ($thing) { 4872 $document->setAttribute($node, tex => UnTeX($thing)); } }); 4873 4874Tag('ltx:g', afterClose => sub { 4875 my ($document, $node) = @_; 4876 $node->parentNode->removeChild($node) unless $node->hasChildNodes; }); 4877 4878# \savebox -- already defined differntly in C.13 above ? 4879 4880#********************************************************************** 4881# C.15 Font Selection 4882#********************************************************************** 4883#====================================================================== 4884# C.15.1 Changing the Type Style 4885#====================================================================== 4886# Text styles. 4887 4888DefMacroI('\rmdefault', undef, 'cmr'); 4889DefMacroI('\sfdefault', undef, 'cmss'); 4890DefMacroI('\ttdefault', undef, 'cmtt'); 4891DefMacroI('\bfdefault', undef, 'bx'); 4892DefMacroI('\mddefault', undef, 'm'); 4893DefMacroI('\itdefault', undef, 'it'); 4894DefMacroI('\sldefault', undef, 'sl'); 4895DefMacroI('\scdefault', undef, 'sc'); 4896DefMacroI('\updefault', undef, 'n'); 4897DefMacroI('\encodingdefault', undef, 'OT1'); 4898DefMacroI('\familydefault', undef, '\rmdefault'); 4899DefMacroI('\seriesdefault', undef, '\mddefault'); 4900DefMacroI('\shapedefault', undef, '\updefault'); 4901 4902Let('\mediumseries', '\mdseries'); 4903Let('\normalshape', '\upshape'); 4904 4905# ? DefMacro('\f@encoding','cm'); 4906DefMacro('\f@family', 'cmr'); 4907DefMacro('\f@series', 'm'); 4908DefMacro('\f@shape', 'n'); 4909DefMacro('\f@size', '10'); 4910 4911# These do NOT immediately effect the font! 4912DefMacro('\fontfamily{}', '\edef\f@family{#1}'); 4913DefMacro('\fontseries{}', '\edef\f@series{#1}'); 4914DefMacro('\fontshape{}', '\edef\f@shape{#1}'); 4915 4916# For fonts not allowed in math!!! 4917DefPrimitive('\not@math@alphabet@@ {}', sub { 4918 if ($STATE->lookupValue('IN_MATH')) { 4919 my $c = ToString($_[1]); 4920 Warn('unexpected', $c, $_[0], "Command $c invalid in math mode"); } 4921 return; }); 4922 4923# These DO immediately effect the font! 4924DefMacroI('\mdseries', undef, '\not@math@alphabet@@{\mddefault}\fontseries{\mddefault}\selectfont'); 4925DefMacroI('\bfseries', undef, '\not@math@alphabet@@{\bfdefault}\fontseries{\bfdefault}\selectfont'); 4926 4927DefMacroI('\rmfamily', undef, '\not@math@alphabet@@{\rmdefault}\fontfamily{\rmdefault}\selectfont'); 4928DefMacroI('\sffamily', undef, '\not@math@alphabet@@{\sfdefault}\fontfamily{\sfdefault}\selectfont'); 4929DefMacroI('\ttfamily', undef, '\not@math@alphabet@@{\ttdefault}\fontfamily{\ttdefault}\selectfont'); 4930 4931DefMacroI('\upshape', undef, '\not@math@alphabet@@{\updefault}\fontshape{\updefault}\selectfont'); 4932DefMacroI('\itshape', undef, '\not@math@alphabet@@{\itdefault}\fontshape{\itdefault}\selectfont'); 4933DefMacroI('\slshape', undef, '\not@math@alphabet@@{\sldefault}\fontshape{\sldefault}\selectfont'); 4934DefMacroI('\scshape', undef, '\not@math@alphabet@@{\scdefault}\fontshape{\scdefault}\selectfont'); 4935 4936DefMacroI('\normalfont', undef, 4937 '\fontfamily{\rmdefault}\fontseries{\mddefault}\fontshape{\updefault}\selectfont'); 4938DefMacroI('\verbatim@font', undef, 4939 '\fontfamily{\ttdefault}\fontseries{\mddefault}\fontshape{\updefault}\selectfont'); 4940 4941Let('\reset@font', '\normalfont'); 4942 4943DefPrimitive('\selectfont', sub { 4944 my $family = ToString(Expand(T_CS('\f@family'))); 4945 my $series = ToString(Expand(T_CS('\f@series'))); 4946 my $shape = ToString(Expand(T_CS('\f@shape'))); 4947 if (my $sh = LaTeXML::Common::Font::lookupFontFamily($family)) { MergeFont(%$sh); } 4948 elsif (!LookupValue("reported_unrecognized_font_family_$family")) { 4949 AssignValue("reported_unrecognized_font_family_$family", 1, 'global'); 4950 Info('unexpected', $family, $_[0], "Unrecognized font family '$family'."); } 4951 if (my $sh = LaTeXML::Common::Font::lookupFontSeries($series)) { MergeFont(%$sh); } 4952 elsif (!LookupValue("reported_unrecognized_font_series_$series")) { 4953 AssignValue("reported_unrecognized_font_series_$series", 1, 'global'); 4954 Info('unexpected', $series, $_[0], "Unrecognized font series '$series'."); } 4955 if (my $sh = LaTeXML::Common::Font::lookupFontShape($shape)) { MergeFont(%$sh); } 4956 elsif (!LookupValue("reported_unrecognized_font_shape_$shape")) { 4957 AssignValue("reported_unrecognized_font_shape_$shape", 1, 'global'); 4958 Info('unexpected', $shape, $_[0], "Unrecognized font shape '$shape'."); } 4959 return; }); 4960 4961DefMacro('\usefont{}{}{}{}', 4962 '\fontencoding{#1}\fontfamily{#2}\fontseries{#3}\fontshape{#4}\selectfont'); 4963 4964# If these series or shapes appear in math, they revert it to roman, medium, upright (?) 4965DefConstructor('\textmd@math{}', "<ltx:text _noautoclose='1'>#1</ltx:text>", mode => 'text', 4966 bounded => 1, font => { series => 'medium' }, alias => '\textmd', 4967 beforeDigest => sub { DefMacro('\f@series', 'm'); }); 4968DefConstructor('\textbf@math{}', "<ltx:text _noautoclose='1'>#1</ltx:text>", mode => 'text', 4969 bounded => 1, font => { series => 'bold' }, alias => '\textbf', 4970 beforeDigest => sub { DefMacro('\f@series', 'b'); }); 4971DefConstructor('\textrm@math{}', "<ltx:text _noautoclose='1'>#1</ltx:text>", mode => 'text', 4972 bounded => 1, font => { family => 'serif' }, alias => '\textrm', 4973 beforeDigest => sub { DefMacro('\f@family', 'cm'); }); 4974DefConstructor('\textsf@math{}', "<ltx:text _noautoclose='1'>#1</ltx:text>", mode => 'text', 4975 bounded => 1, font => { family => 'sansserif' }, alias => '\textsf', 4976 beforeDigest => sub { DefMacro('\f@family', 'cmss'); }); 4977DefConstructor('\texttt@math{}', "<ltx:text _noautoclose='1'>#1</ltx:text>", mode => 'text', 4978 bounded => 1, font => { family => 'typewriter' }, alias => '\texttt', 4979 beforeDigest => sub { DefMacro('\f@family', 'cmtt'); }); 4980 4981DefConstructor('\textup@math{}', "<ltx:text _noautoclose='1'>#1</ltx:text>", mode => 'text', 4982 bounded => 1, font => { shape => 'upright' }, alias => '\textup', 4983 beforeDigest => sub { DefMacro('\f@shape', ''); }); 4984DefConstructor('\textit@math{}', "<ltx:text _noautoclose='1'>#1</ltx:text>", mode => 'text', 4985 bounded => 1, font => { shape => 'italic' }, alias => '\textit', 4986 beforeDigest => sub { DefMacro('\f@shape', 'i'); }); 4987DefConstructor('\textsl@math{}', "<ltx:text _noautoclose='1'>#1</ltx:text>", mode => 'text', 4988 bounded => 1, font => { shape => 'slanted' }, alias => '\textsl', 4989 beforeDigest => sub { DefMacro('\f@shape', 'sl'); }); 4990DefConstructor('\textsc@math{}', "<ltx:text _noautoclose='1'>#1</ltx:text>", mode => 'text', 4991 bounded => 1, font => { shape => 'smallcaps' }, alias => '\textsc', 4992 beforeDigest => sub { DefMacro('\f@shape', 'sc'); }); 4993DefConstructor('\textnormal@math{}', "<ltx:text _noautoclose='1'>#1</ltx:text>", mode => 'text', 4994 bounded => 1, font => { family => 'serif', series => 'medium', shape => 'upright' }, alias => '\textnormal', 4995 beforeDigest => sub { DefMacro('\f@family', 'cmtt'); 4996 DefMacro('\f@series', 'm'); 4997 DefMacro('\f@shape', 'n'); }); 4998# These really should be robust! which is a source of expand timing issues! 4999DefMacro('\textmd{}', '\ifmmode\textmd@math{#1}\else{\mdseries #1}\fi', protected => 1); 5000DefMacro('\textbf{}', '\ifmmode\textbf@math{#1}\else{\bfseries #1}\fi', protected => 1); 5001DefMacro('\textrm{}', '\ifmmode\textrm@math{#1}\else{\rmfamily #1}\fi', protected => 1); 5002DefMacro('\textsf{}', '\ifmmode\textsf@math{#1}\else{\sffamily #1}\fi', protected => 1); 5003DefMacro('\texttt{}', '\ifmmode\texttt@math{#1}\else{\ttfamily #1}\fi', protected => 1); 5004DefMacro('\textup{}', '\ifmmode\textup@math{#1}\else{\upshape #1}\fi', protected => 1); 5005DefMacro('\textit{}', '\ifmmode\textit@math{#1}\else{\itshape #1}\fi', protected => 1); 5006DefMacro('\textsl{}', '\ifmmode\textsl@math{#1}\else{\slshape #1}\fi', protected => 1); 5007DefMacro('\textsc{}', '\ifmmode\textsc@math{#1}\else{\scshape #1}\fi', protected => 1); 5008DefMacro('\textnormal{}', '\ifmmode\textnormal@math{#1}\else{\normalfont #1}\fi', protected => 1); 5009 5010DefPrimitive('\DeclareTextFontCommand{}{}', sub { 5011 my ($stomach, $cmd, $font) = @_; 5012 DefConstructorI($cmd, "{}", 5013 "?#isMath(<ltx:text _noautoclose='1'>#1</ltx:text>)(#1)", 5014 mode => 'text', bounded => 1, 5015 beforeDigest => sub { Digest($font); (); }); 5016 return; }); 5017 5018DefPrimitive('\mathversion{}', sub { 5019 my ($stomach, $version) = @_; 5020 $version = ToString($version); 5021 if ($version eq 'bold') { 5022 AssignValue(mathfont => LookupValue('mathfont')->merge(forcebold => 1), 'local'); } 5023 elsif ($version eq 'normal') { 5024 AssignValue(mathfont => LookupValue('mathfont')->merge(forcebold => 0), 'local'); } 5025 else { Error('unexpected', $version, $stomach, "Unknown math verison '$version'"); } }); 5026 5027DefPrimitive('\DeclareOldFontCommand{}{}{}', sub { 5028 my ($stomach, $cmd, $font, $mathcmd) = @_; 5029 DefMacroI($cmd, undef, sub { 5030 return (LookupValue('IN_MATH') ? $mathcmd->unlist : $font->unlist); }); 5031 # Tokens(T_CS('\ifmmode'), $mathcmd->unlist, T_CS('\else'), $font->unlist, T_CS('\fi'))); 5032 return; }); 5033 5034DefMacro('\newfont{}{}', '\font#1=#2\relax'); 5035 5036Let('\normalcolor', '\relax'); 5037 5038#====================================================================== 5039# C.15.2 Changing the Type Size 5040#====================================================================== 5041# Handled in TeX.pool.ltxml 5042 5043#====================================================================== 5044# C.15.3 Special Symbol 5045#====================================================================== 5046DefMacro('\symbol{}', '\char#1\relax'); 5047 5048# These in LaTeX, but not in the book... 5049DefPrimitiveI('\textdollar', undef, "\$"); 5050DefPrimitiveI('\textemdash', undef, "\x{2014}"); # EM DASH 5051DefPrimitiveI('\textendash', undef, "\x{2013}"); # EN DASH 5052DefPrimitiveI('\textexclamdown', undef, UTF(0xA1)); # INVERTED EXCLAMATION MARK 5053DefPrimitiveI('\textquestiondown', undef, UTF(0xBF)); # INVERTED QUESTION MARK 5054DefPrimitiveI('\textquotedblleft', undef, "\x{201C}"); # LEFT DOUBLE QUOTATION MARK 5055DefPrimitiveI('\textquotedblright', undef, "\x{201D}"); # RIGHT DOUBLE QUOTATION MARK 5056DefPrimitiveI('\textquotedbl', undef, "\""); # plain ascii DOUBLE QUOTATION 5057DefPrimitiveI('\textquoteleft', undef, "\x{2018}"); # LEFT SINGLE QUOTATION MARK 5058DefPrimitiveI('\textquoteright', undef, "\x{2019}"); # RIGHT SINGLE QUOTATION MARK 5059DefPrimitiveI('\textsterling', undef, UTF(0xA3)); # POUND SIGN 5060DefPrimitiveI('\textasteriskcentered', undef, "*"); 5061DefPrimitiveI('\textbackslash', undef, UTF(0x5C)); # REVERSE SOLIDUS 5062DefPrimitiveI('\textbar', undef, "|"); 5063DefPrimitiveI('\textbraceleft', undef, "{"); 5064DefPrimitiveI('\textbraceright', undef, "}"); 5065DefPrimitiveI('\textbullet', undef, "\x{2022}"); # BULLET 5066DefPrimitiveI('\textdaggerdbl', undef, "\x{2021}"); # DOUBLE DAGGER 5067DefPrimitiveI('\textdagger', undef, "\x{2020}"); # DAGGER 5068DefPrimitiveI('\textparagraph', undef, UTF(0xB6)); # PILCROW SIGN 5069DefPrimitiveI('\textsection', undef, UTF(0xA7)); # SECTION SIGN 5070DefAccent('\textcircled', UTF(0x20DD), UTF(0x25EF)); # Defined in TeX.pool 5071DefPrimitiveI('\textless', undef, "<"); 5072DefPrimitiveI('\textgreater', undef, ">"); 5073DefPrimitiveI('\textcopyright', undef, UTF(0xA9)); # COPYRIGHT SIGN 5074DefPrimitiveI('\textasciicircum', undef, "^"); 5075DefPrimitiveI('\textasciitilde', undef, "~"); 5076DefPrimitiveI('\textcompwordmark', undef, ""); # ??? 5077DefPrimitiveI('\textcapitalcompwordmark', undef, ""); # ??? 5078DefPrimitiveI('\textascendercompwordmark', undef, ""); # ??? 5079DefPrimitiveI('\textunderscore', undef, "_"); 5080DefPrimitiveI('\textvisiblespace', undef, "\x{2423}"); # SYMBOL FOR SPACE; Not really the right symbol! 5081DefPrimitiveI('\textellipsis', undef, "\x{2026}"); # HORIZONTAL ELLIPSIS 5082DefPrimitiveI('\textregistered', undef, UTF(0xAE)); # REGISTERED SIGN 5083DefPrimitiveI('\texttrademark', undef, "\x{2122}"); # TRADE MARK SIGN 5084DefConstructor('\textsuperscript{}', "<ltx:sup>#1</ltx:sup>", 5085 mode => 'text'); 5086# This is something coming from xetex/xelatex ? Why define this way? 5087#DefConstructor('\realsuperscript{}', "<ltx:text yoffset='0.5em' _noautoclose='1'>#1</ltx:text>"); 5088DefConstructor('\realsuperscript{}', "<ltx:sup>#1</ltx:sup>", 5089 mode => 'text'); 5090DefPrimitiveI('\textordfeminine', undef, UTF(0xAA)); # FEMININE ORDINAL INDICATOR 5091DefPrimitiveI('\textordmasculine', undef, UTF(0xBA)); # MASCULINE ORDINAL INDICATOR 5092DefPrimitiveI('\SS', undef, 'SS', # ? 5093 locked => 1); # cause it shows up in uclist! 5094DefMacroI('\dag', undef, '\ifmmode{\dagger}\else\textdagger\fi'); 5095DefMacroI('\ddag', undef, '\ifmmode{\ddagger}\else\textdaggerdbl\fi'); 5096 5097DefConstructor('\sqrtsign Digested', 5098 "<ltx:XMApp><ltx:XMTok meaning='square-root'/><ltx:XMArg>#1</ltx:XMArg></ltx:XMApp>"); 5099 5100DefPrimitiveI('\mathparagraph', undef, UTF(0xB6)); 5101DefPrimitiveI('\mathsection', undef, UTF(0xA7)); 5102DefPrimitiveI('\mathdollar', undef, '$'); 5103DefPrimitiveI('\mathsterling', undef, UTF(0xA3)); 5104DefPrimitiveI('\mathunderscore', undef, '_'); 5105DefPrimitiveI('\mathellipsis', undef, "\x{2026}"); 5106 5107# Are these glyph "pieces" or use alone? 5108DefMathI('\arrowvert', undef, "|", role => 'VERTBAR'); 5109DefMathI('\Arrowvert', undef, "\x{2225}", role => 'VERTBAR'); 5110 5111# The following are glyph "pieces"... 5112DefPrimitiveI('\braceld', undef, "\x{239D}"); # left brace down part 5113DefPrimitiveI('\bracelu', undef, "\x{239B}"); # left brace up part 5114DefPrimitiveI('\bracerd', undef, "\x{23A0}"); # right brace down part 5115DefPrimitiveI('\braceru', undef, "\x{239E}"); # right brace up part 5116 5117DefMathI('\cdotp', undef, "\x{22C5}", role => 'MULOP'); 5118DefMathI('\ldotp', undef, ".", role => 'MULOP'); 5119DefMathI('\intop', undef, "\x{222B}", role => 'INTOP', meaning => 'integral', 5120 scriptpos => \&doScriptpos, mathstyle => \&doVariablesizeOp); 5121DefMathI('\ointop', undef, "\x{222E}", role => 'INTOP', meaning => 'contour-integral', 5122 scriptpos => \&doScriptpos, mathstyle => \&doVariablesizeOp); 5123# WHat are these? They look like superscripted parentheses, or combining accents! 5124# \lhook 5125# \rhook 5126Let('\gets', '\leftarrow'); 5127 5128DefPrimitiveI('\lmoustache', undef, "\x{23B0}"); 5129DefPrimitiveI('\rmoustache', undef, "\x{23B1}"); 5130DefMathI('\mapstochar', undef, "\x{21A6}", role => 'ARROW', meaning => 'maps-to'); 5131DefMathI('\owns', undef, "\x{220B}", role => 'RELOP', meaning => 'contains'); 5132 5133# \skew{}{}{} ???? 5134 5135# \symbol lookup symbol in font by index? 5136#********************************************************************** 5137# Other stuff 5138#********************************************************************** 5139# Some stuff that got missed in the appendices ? 5140RawTeX(<<'EoTeX'); 5141\def\@namedef#1{\expandafter\def\csname #1\endcsname} 5142\def\@nameuse#1{\csname #1\endcsname} 5143\def\@cons#1#2{\begingroup\let\@elt\relax\xdef#1{#1\@elt #2}\endgroup} 5144\def\@car#1#2\@nil{#1} 5145\def\@cdr#1#2\@nil{#2} 5146\def\@carcube#1#2#3#4\@nil{#1#2#3} 5147\def\nfss@text#1{{\mbox{#1}}} 5148\def\@sect#1#2#3#4#5#6[#7]#8{} 5149EoTeX 5150 5151Let('\@begindocumenthook', '\@empty'); 5152DefMacroI('\@preamblecmds', undef, Tokens()); 5153DefMacro('\@ifdefinable DefToken {}', sub { 5154 my ($gullet, $token, $if) = @_; 5155 if (isDefinable($token)) { 5156 return $if->unlist } 5157 else { 5158 my ($slash, @s) = ExplodeText($token->toString); 5159 DefMacroI('\reserved@a', undef, Tokens(@s)); 5160 return (T_CS('\@notdefinable')); } }); 5161 5162Let('\@@ifdefinable', '\@ifdefinable'); 5163 5164DefMacro('\@rc@ifdefinable DefToken {}', sub { 5165 my ($gullet, $token, $if) = @_; 5166 Let('\@ifdefinable', '\@@ifdefinable'); 5167 return $if->unlist; }); 5168 5169DefMacroI('\@notdefinable', undef, <<'EOL'); 5170\@latex@error{% 5171 Command \@backslashchar\reserved@a\space 5172 already defined. 5173 Or name \@backslashchar\@qend... illegal, 5174 see p.192 of the manual} 5175EOL 5176 5177DefMacroI('\@qend', undef, Tokens(Explode('end'))); 5178DefMacroI('\@qrelax', undef, Tokens(Explode('relax'))); 5179DefMacroI('\@spaces', undef, '\space\space\space\space'); 5180Let('\@sptoken', T_SPACE); 5181 5182DefMacroI('\@uclclist', undef, '\oe\OE\o\O\ae\AE\dh\DH\dj\DJ\l\L\ng\NG\ss\SS\th\TH'); 5183 5184RawTeX(<<'EOL'); 5185\DeclareRobustCommand{\MakeUppercase}[1]{{% 5186 \def\i{I}\def\j{J}% 5187 \def\reserved@a##1##2{\let##1##2\reserved@a}% 5188 \expandafter\reserved@a\@uclclist\reserved@b{\reserved@b\@gobble}% 5189 \let\UTF@two@octets@noexpand\@empty 5190 \let\UTF@three@octets@noexpand\@empty 5191 \let\UTF@four@octets@noexpand\@empty 5192 \protected@edef\reserved@a{\uppercase{#1}}% 5193 \reserved@a 5194 }} 5195\DeclareRobustCommand{\MakeLowercase}[1]{{% 5196 \def\reserved@a##1##2{\let##2##1\reserved@a}% 5197 \expandafter\reserved@a\@uclclist\reserved@b{\reserved@b\@gobble}% 5198 \let\UTF@two@octets@noexpand\@empty 5199 \let\UTF@three@octets@noexpand\@empty 5200 \let\UTF@four@octets@noexpand\@empty 5201 \protected@edef\reserved@a{\lowercase{#1}}% 5202 \reserved@a 5203 }} 5204\protected@edef\MakeUppercase#1{\MakeUppercase{#1}} 5205\protected@edef\MakeLowercase#1{\MakeLowercase{#1}} 5206EOL 5207 5208#====================================================================== 5209 5210DefMacroI('\@ehc', undef, "I can't help"); 5211 5212DefMacro('\@gobble{}', Tokens()); 5213DefMacro('\@gobbletwo{}{}', Tokens()); 5214DefMacro('\@gobblefour{}{}{}{}', Tokens()); 5215DefMacro('\@firstofone{}', sub { $_[1]; }); 5216Let('\@iden', '\@firstofone'); 5217DefMacro('\@firstoftwo{}{}', sub { $_[1]; }); 5218DefMacro('\@secondoftwo{}{}', sub { $_[2]; }); 5219DefMacro('\@thirdofthree{}{}{}', sub { $_[3]; }); 5220DefMacro('\@expandtwoargs{}{}{}', sub { 5221 ($_[1]->unlist, T_BEGIN, Expand($_[2])->unlist, T_END, T_BEGIN, Expand($_[3])->unlist, T_END); }); 5222DefMacro('\@makeother{}', sub { 5223 my $letter = ToString($_[1]); $letter =~ s/^\\//; 5224 AssignCatcode($letter => CC_OTHER, 'local'); }); 5225 5226RawTeX(<<'EoTeX'); 5227{\catcode`\^^M=13 \gdef\obeycr{\catcode`\^^M13 \def^^M{\\\relax}% 5228 \@gobblecr}% 5229{\catcode`\^^M=13 \gdef\@gobblecr{\@ifnextchar 5230\@gobble\ignorespaces}}% 5231\gdef\restorecr{\catcode`\^^M5 }} 5232EoTeX 5233RawTeX(<<'EoTeX'); 5234\begingroup 5235 \catcode`P=12 5236 \catcode`T=12 5237 \lowercase{ 5238 \def\x{\def\rem@pt##1.##2PT{##1\ifnum##2>\z@.##2\fi}}} 5239 \expandafter\endgroup\x 5240\def\strip@pt{\expandafter\rem@pt\the} 5241\def\strip@prefix#1>{} 5242\def\@sanitize{\@makeother\ \@makeother\\\@makeother\$\@makeother\&% 5243\@makeother\#\@makeother\^\@makeother\_\@makeother\%\@makeother\~} 5244\def \@onelevel@sanitize #1{% 5245 \edef #1{\expandafter\strip@prefix 5246 \meaning #1}% 5247} 5248\def\dospecials{\do\ \do\\\do\{\do\}\do\$\do\&% 5249 \do\#\do\^\do\_\do\%\do\~} 5250EoTeX 5251 5252DefMacroI('\nfss@catcodes', undef, <<'EOMacro'); 5253 \makeatletter 5254 \catcode`\ 9% 5255 \catcode`\^^I9% 5256 \catcode`\^^M9% 5257 \catcode`\\\z@ 5258 \catcode`\{\@ne 5259 \catcode`\}\tw@ 5260 \catcode`\#6% 5261 \catcode`\^7% 5262 \catcode`\%14% 5263 \@makeother\<% 5264 \@makeother\>% 5265 \@makeother\*% 5266 \@makeother\.% 5267 \@makeother\-% 5268 \@makeother\/% 5269 \@makeother\[% 5270 \@makeother\]% 5271 \@makeother\`% 5272 \@makeother\'% 5273 \@makeother\"% 5274EOMacro 5275DefMacroI('\ltx@hard@MessageBreak', undef, '^^J'); 5276 5277sub make_message { 5278 my ($cmd, @args) = @_; 5279 my $stomach = $STATE->getStomach; 5280 my $lead_arg = ToString(shift(@args)) || ''; 5281 $lead_arg =~ s/(?:\\\@?spaces?)+//g; 5282 my $type = $lead_arg || $cmd; 5283 $stomach->bgroup; 5284 Let('\protect', '\string'); 5285 # Note that the arg really should be digested to get to the underlying text, 5286 # but why tempt fate, when we're already making an error message? 5287 my $message = join(" ", map { ToString(Expand($_)) } @args); 5288 $type =~ s/(?:\\\@?spaces?)+/ /g; 5289 $message =~ s/(?:\\\@?spaces?)+/ /g; 5290 $stomach->egroup; 5291 return ('latex', $type, $stomach, $message); } 5292 5293DefPrimitive('\@onlypreamble{}', sub { onlyPreamble('\@onlypreamble'); }); # Don't bother enforcing this. 5294DefPrimitive('\GenericError{}{}{}{}', sub { Error(make_message('\GenericError', $_[1], $_[2], $_[3], $_[4])); }); 5295DefPrimitive('\GenericWarning{}{}', sub { Warn(make_message('\GenericWarning', $_[1], $_[2])); }); 5296DefPrimitive('\GenericInfo{}{}', sub { Info(make_message('\GenericInfo', $_[1], $_[2])); }); 5297 5298Let('\MessageBreak', '\relax'); 5299RawTeX(<<'EoTeX'); 5300\gdef\PackageError#1#2#3{% 5301 \GenericError{% 5302 (#1)\@spaces\@spaces\@spaces\@spaces 5303 }{% 5304 Package #1 Error: #2% 5305 }{% 5306 See the #1 package documentation for explanation.% 5307 }{#3}% 5308} 5309\def\PackageWarning#1#2{% 5310 \GenericWarning{% 5311 (#1)\@spaces\@spaces\@spaces\@spaces 5312 }{% 5313 Package #1 Warning: #2% 5314 }% 5315} 5316\def\PackageWarningNoLine#1#2{% 5317 \PackageWarning{#1}{#2\@gobble}} 5318\def\PackageInfo#1#2{% 5319 \GenericInfo{% 5320 (#1) \@spaces\@spaces\@spaces 5321 }{% 5322 Package #1 Info: #2% 5323 }% 5324} 5325\def\ClassError#1#2#3{% 5326 \GenericError{% 5327 (#1) \space\@spaces\@spaces\@spaces 5328 }{% 5329 Class #1 Error: #2% 5330 }{% 5331 See the #1 class documentation for explanation.% 5332 }{#3}% 5333} 5334\def\ClassWarning#1#2{% 5335 \GenericWarning{% 5336 (#1) \space\@spaces\@spaces\@spaces 5337 }{% 5338 Class #1 Warning: #2% 5339 }% 5340} 5341\def\ClassWarningNoLine#1#2{% 5342 \ClassWarning{#1}{#2\@gobble}} 5343\def\ClassInfo#1#2{% 5344 \GenericInfo{% 5345 (#1) \space\space\@spaces\@spaces 5346 }{% 5347 Class #1 Info: #2% 5348 }% 5349} 5350\def\@latex@error#1#2{% 5351 \GenericError{% 5352 \space\space\space\@spaces\@spaces\@spaces 5353 }{% 5354 LaTeX Error: #1% 5355 }{% 5356 See the LaTeX manual or LaTeX Companion for explanation.% 5357 }{#2}% 5358} 5359\def\@latex@warning#1{% 5360 \GenericWarning{% 5361 \space\space\space\@spaces\@spaces\@spaces 5362 }{% 5363 LaTeX Warning: #1% 5364 }% 5365} 5366\def\@latex@warning@no@line#1{% 5367 \@latex@warning{#1\@gobble}} 5368\def\@latex@info#1{% 5369 \GenericInfo{% 5370 \@spaces\@spaces\@spaces 5371 }{% 5372 LaTeX Info: #1% 5373 }% 5374} 5375\def\@latex@info@no@line#1{% 5376 \@latex@info{#1\@gobble}} 5377EoTeX 5378 5379DefPrimitive('\@setsize{}{}{}{}', undef); 5380DefMacro('\on@line', ' on input line \the\inputlineno'); 5381Let('\@warning', '\@latex@warning'); 5382Let('\@@warning', '\@latex@warning@no@line'); 5383 5384DefMacro('\G@refundefinedtrue', ''); 5385 5386DefMacro('\@nomath{}', 5387 '\relax\ifmmode\@font@warning{Command \noexpand#1invalid in math mode}\fi'); 5388DefMacro('\@font@warning{}', 5389 '\GenericWarning{(Font)\@spaces\@spaces\@spaces\space\space}{LaTeX Font Warning: #1}'); 5390 5391#====================================================================== 5392 5393RawTeX(<<'EOTeX'); 5394 \chardef\@xxxii=32 5395 \mathchardef\@Mi=10001 5396 \mathchardef\@Mii=10002 5397 \mathchardef\@Miii=10003 5398 \mathchardef\@Miv=10004 5399 \def\@fontenc@load@list{\@elt{T1,OT1}} 5400EOTeX 5401DefMacroI('\@vpt', undef, '5'); 5402DefMacroI('\@vipt', undef, '6'); 5403DefMacroI('\@viipt', undef, '7'); 5404DefMacroI('\@viiipt', undef, '8'); 5405DefMacroI('\@ixpt', undef, '9'); 5406DefMacroI('\@xpt', undef, '10'); 5407DefMacroI('\@xipt', undef, '10.95'); 5408DefMacroI('\@xiipt', undef, '12'); 5409DefMacroI('\@xivpt', undef, '14.4'); 5410DefMacroI('\@xviipt', undef, '17.28'); 5411DefMacroI('\@xxpt', undef, '20.74'); 5412DefMacroI('\@xxvpt', undef, '24.88'); 5413 5414DefMacroI('\@tempa', undef, ''); 5415DefMacroI('\@tempb', undef, ''); 5416DefMacroI('\@tempc', undef, ''); 5417DefMacroI('\@gtempa', undef, ''); 5418 5419RawTeX(<<'EOTeX'); 5420\long\def \loop #1\repeat{% 5421 \def\iterate{#1\relax % Extra \relax 5422 \expandafter\iterate\fi 5423 }% 5424 \iterate 5425 \let\iterate\relax 5426} 5427\newdimen\@ydim 5428\let\@@hyph=\- 5429\newbox\@arstrutbox 5430\newbox\@begindvibox 5431\newcount\@botnum 5432\newdimen\@botroom 5433\newcount\@chclass 5434\newcount\@chnum 5435\newdimen\@clnht 5436\newdimen\@clnwd 5437\newdimen\@colht 5438\newcount\@colnum 5439\newdimen\@colroom 5440\newbox\@curfield 5441\newbox\@curline 5442\newcount\@currtype 5443\newcount\@curtab 5444\newcount\@curtabmar 5445\newbox\@dashbox 5446\newcount\@dashcnt 5447\newdimen\@dashdim 5448\newcount\@dbltopnum 5449\newdimen\@dbltoproom 5450\let\@dischyph=\- 5451\newcount\@enumdepth 5452\newcount\@floatpenalty 5453\newdimen\@fpmin 5454\newcount \@fpstype 5455\newcount\@highpenalty 5456\newcount\@hightab 5457\newbox\@holdpg 5458\newinsert \@kludgeins 5459\newcount\@lastchclass 5460\newbox\@leftcolumn 5461\newbox\@linechar 5462\newdimen\@linelen 5463\newcount\@lowpenalty 5464\newdimen\@maxdepth 5465\newcount\@medpenalty 5466\newdimen\@mparbottom \@mparbottom\z@ 5467\newinsert\@mpfootins 5468\newcount\@mplistdepth 5469\newcount\@multicnt 5470\newcount\@nxttabmar 5471\newbox\@outputbox 5472\newdimen\@pagedp 5473\newdimen\@pageht 5474\newbox\@picbox 5475\newdimen\@picht 5476\newdimen \@reqcolroom 5477\newskip\@rightskip \@rightskip \z@skip 5478\newcount\@savsf 5479\newdimen\@savsk 5480\newcount\@secpenalty 5481\def\@sqrt[#1]{\root #1\of} 5482\newbox\@tabfbox 5483\newcount\@tabpush 5484\newdimen \@textfloatsheight 5485\newdimen\@textmin 5486\newcount\@topnum 5487\newdimen\@toproom 5488\newcount\@xarg 5489\newdimen\@xdim 5490\newcount\@yarg 5491\newdimen\@ydim 5492\newcount\@yyarg 5493\newtoks\every@math@size 5494\newif \if@fcolmade 5495\newdimen\lower@bound 5496\newcount\par@deathcycles 5497\newdimen\upper@bound 5498\newif\if@insert 5499\newif\if@colmade 5500\newif\if@specialpage \@specialpagefalse 5501\newif\if@firstcolumn \@firstcolumntrue 5502\newif\if@twocolumn \@twocolumnfalse 5503\newif\if@twoside \@twosidefalse 5504\newif\if@reversemargin \@reversemarginfalse 5505\newif\if@mparswitch \@mparswitchfalse 5506\newif\if@firstamp \@firstampfalse 5507\newcount\col@number \@ne 5508\newread\@inputcheck 5509\newwrite\@unused 5510\newwrite\@mainaux 5511\newwrite\@partaux 5512\let\@auxout=\@mainaux 5513\openout\@mainaux\jobname.aux 5514\newcount\@clubpenalty 5515\@clubpenalty \clubpenalty 5516\newif\if@filesw \@fileswtrue 5517\newif\if@partsw \@partswfalse 5518\def\@tempswafalse{\let\if@tempswa\iffalse} 5519\def\@tempswatrue{\let\if@tempswa\iftrue} 5520\let\if@tempswa\iffalse 5521\newcount\@tempcnta 5522\newcount\@tempcntb 5523\newif\if@tempswa 5524\newdimen\@tempdima 5525\newdimen\@tempdimb 5526\newdimen\@tempdimc 5527\newbox\@tempboxa 5528\newskip\@tempskipa 5529\newskip\@tempskipb 5530\newtoks\@temptokena 5531\newskip\@flushglue \@flushglue = 0pt plus 1fil 5532\newif\if@afterindent\@afterindenttrue 5533\newbox\rootbox 5534 5535\newcount\@eqcnt 5536\newcount\@eqpen 5537\newif\if@eqnsw\@eqnswtrue 5538\newskip\@centering 5539\@centering = 0pt plus 1000pt 5540\let\@eqnsel=\relax 5541 5542 \long\def\@whilenum#1\do #2{\ifnum #1\relax #2\relax\@iwhilenum{#1\relax 5543 #2\relax}\fi} 5544 \long\def\@iwhilenum#1{\ifnum #1\expandafter\@iwhilenum 5545 \else\expandafter\@gobble\fi{#1}} 5546 \long\def\@whiledim#1\do #2{\ifdim #1\relax#2\@iwhiledim{#1\relax#2}\fi} 5547 \long\def\@iwhiledim#1{\ifdim #1\expandafter\@iwhiledim 5548 \else\expandafter\@gobble\fi{#1}} 5549 \long\def\@whilesw#1\fi#2{#1#2\@iwhilesw{#1#2}\fi\fi} 5550 \long\def\@iwhilesw#1\fi{#1\expandafter\@iwhilesw 5551 \else\@gobbletwo\fi{#1}\fi} 5552\def\@nnil{\@nil} 5553\def\@fornoop#1\@@#2#3{} 5554\long\def\@for#1:=#2\do#3{% 5555 \expandafter\def\expandafter\@fortmp\expandafter{#2}% 5556 \ifx\@fortmp\@empty \else 5557 \expandafter\@forloop#2,\@nil,\@nil\@@#1{#3}\fi} 5558\long\def\@forloop#1,#2,#3\@@#4#5{\def#4{#1}\ifx #4\@nnil \else 5559 #5\def#4{#2}\ifx #4\@nnil \else#5\@iforloop #3\@@#4{#5}\fi\fi} 5560\long\def\@iforloop#1,#2\@@#3#4{\def#3{#1}\ifx #3\@nnil 5561 \expandafter\@fornoop \else 5562 #4\relax\expandafter\@iforloop\fi#2\@@#3{#4}} 5563\def\@tfor#1:={\@tf@r#1 } 5564\long\def\@tf@r#1#2\do#3{\def\@fortmp{#2}\ifx\@fortmp\space\else 5565 \@tforloop#2\@nil\@nil\@@#1{#3}\fi} 5566\long\def\@tforloop#1#2\@@#3#4{\def#3{#1}\ifx #3\@nnil 5567 \expandafter\@fornoop \else 5568 #4\relax\expandafter\@tforloop\fi#2\@@#3{#4}} 5569\long\def\@break@tfor#1\@@#2#3{\fi\fi} 5570\def\remove@to@nnil#1\@nnil{} 5571\def\remove@angles#1>{\set@simple@size@args} 5572\def\remove@star#1*{#1} 5573\def\@defaultunits{\afterassignment\remove@to@nnil} 5574 5575\newif\ifmath@fonts \math@fontstrue 5576\newbox\@labels 5577\newif\if@inlabel \@inlabelfalse 5578\newif\if@newlist \@newlistfalse 5579\newif\if@noparitem \@noparitemfalse 5580\newif\if@noparlist \@noparlistfalse 5581\newif\if@noitemarg \@noitemargfalse 5582\newif\if@nmbrlist \@nmbrlistfalse 5583 5584\def\glb@settings{}% 5585EOTeX 5586 5587DefMacroI('\@height', undef, 'height'); 5588DefMacroI('\@width', undef, 'width'); 5589DefMacroI('\@depth', undef, 'depth'); 5590DefMacroI('\@minus', undef, 'minus'); 5591DefMacroI('\@plus', undef, 'plus'); 5592DefMacroI('\hb@xt@', undef, '\hbox to'); 5593DefMacroI('\hmode@bgroup', undef, '\leavevmode\bgroup'); 5594 5595DefMacroI('\@backslashchar', undef, T_OTHER('\\')); 5596DefMacroI('\@percentchar', undef, T_OTHER('%')); 5597DefMacroI('\@charlb', undef, T_LETTER('{')); 5598DefMacroI('\@charrb', undef, T_LETTER('}')); 5599#====================================================================== 5600 5601DefMacroI('\check@mathfonts', undef, Tokens()); 5602DefMacro('\fontsize{}{}', Tokens()); 5603# https://tex.stackexchange.com/questions/112492/setfontsize-vs-fontsize#112501 5604DefMacro('\@setfontsize{}{}{}', '\let\@currsize#1'); 5605 5606DefMacroI('\@vpt', undef, T_OTHER('5')); 5607DefMacroI('\@vipt', undef, T_OTHER('6')); 5608DefMacroI('\@viipt', undef, T_OTHER('7')); 5609DefMacroI('\@viiipt', undef, T_OTHER('8')); 5610DefMacroI('\@ixpt', undef, T_OTHER('9')); 5611DefMacro('\@xpt', '10'); 5612DefMacro('\@xipt', '10.95'); 5613DefMacro('\@xiipt', '12'); 5614DefMacro('\@xivpt', '14.4'); 5615DefMacro('\@xviipt', '17.28'); 5616DefMacro('\@xxpt', '20.74'); 5617DefMacro('\@xxvpt', '24.88'); 5618DefMacro('\vpt', '\edef\f@size{\@vpt}\rm'); 5619DefMacro('\vipt', '\edef\f@size{\@vipt}\rm'); 5620DefMacro('\viipt', '\edef\f@size{\@viipt}\rm'); 5621DefMacro('\viiipt', '\edef\f@size{\@viiipt}\rm'); 5622DefMacro('\ixpt', '\edef\f@size{\@ixpt}\rm'); 5623DefMacro('\xpt', '\edef\f@size{\@xpt}\rm'); 5624DefMacro('\xipt', '\edef\f@size{\@xipt}\rm'); 5625DefMacro('\xiipt', '\edef\f@size{\@xiipt}\rm'); 5626DefMacro('\xivpt', '\edef\f@size{\@xivpt}\rm'); 5627DefMacro('\xviipt', '\edef\f@size{\@xviipt}\rm'); 5628DefMacro('\xxpt', '\edef\f@size{\@xxpt}\rm'); 5629DefMacro('\xxvpt', '\edef\f@size{\@xxvpt}\rm'); 5630 5631DefMacroI('\defaultscriptratio', undef, '.7'); 5632DefMacroI('\defaultscriptscriptratio', undef, '.5'); 5633 5634#====================================================================== 5635DefMacroI('\loggingoutput', undef, Tokens()); 5636DefMacroI('\loggingall', undef, Tokens()); 5637DefMacroI('\tracingfonts', undef, Tokens()); 5638DefMacroI('\showoverfull', undef, Tokens()); 5639DefMacroI('\showoutput', undef, Tokens()); 5640DefMacro('\wlog{}', Tokens()); 5641 5642#====================================================================== 5643# Various symbols, accents, etc from Chapter 3 defined in TeX.pool 5644 5645#********************************************************************** 5646# Semi-Undocumented stuff 5647#********************************************************************** 5648DefMacro('\@ifnextchar DefToken {}{}', sub { 5649 my ($gullet, $token, $if, $else) = @_; 5650 my $next = $gullet->readNonSpace; 5651 # NOTE: Not actually substituting, but collapsing ## pairs!!!! 5652 # use \egroup for $next, if we've fallen off end? 5653 ((XEquals($token, (defined $next ? $next : T_END)) ? $if : $else)->unlist, 5654 (defined $next ? ($next) : ())); }); 5655Let('\kernel@ifnextchar', '\@ifnextchar'); 5656Let('\@ifnext', '\@ifnextchar'); # ???? 5657 5658# Hacky version matches multiple chars! but does NOT expand 5659DefMacro('\@ifnext@n {} {}{}', sub { 5660 my ($gullet, $tokens, $if, $else) = @_; 5661 my @toks = $tokens->unlist; 5662 my @read = (); 5663 while (my $t = $gullet->readToken) { 5664 push(@read, $t); 5665 if ($t->equals($toks[0])) { shift(@toks); } 5666 else { last; } } 5667 return Tokens((@toks ? $else->unlist : $if->unlist), @read); }); 5668 5669DefMacro('\@ifstar {}{}', sub { 5670 my ($gullet, $if, $else) = @_; 5671 my $next = $gullet->readNonSpace; 5672 if (T_OTHER('*')->equals($next)) { 5673 $if; } 5674 else { 5675 ($else, ($next ? $next : ())); } }); 5676 5677DefMacro('\@dblarg {}', '\kernel@ifnextchar[{#1}{\@xdblarg{#1}}'); 5678DefMacro('\@xdblarg {}{}', '#1[{#2}]{#2}'); 5679 5680DefMacro('\@testopt{}{}', sub { 5681 my ($gullet, $cmd, $option) = @_; 5682 ($gullet->ifNext(T_OTHER('[')) ? $cmd->unlist 5683 : ($cmd->unlist, T_OTHER('['), $option->unlist, T_OTHER(']'))); }); 5684RawTeX(<<'EoTeX'); 5685\def\@protected@testopt#1{%% 5686 \ifx\protect\@typeset@protect 5687 \expandafter\@testopt 5688 \else 5689 \@x@protect#1% 5690 \fi} 5691EoTeX 5692 5693Let('\l@ngrel@x', '\relax'); # Never actually used anywhere, but... 5694DefMacro('\@star@or@long{}', '\@ifstar{\let\l@ngrel@x\relax#1}{\let\l@ngrel@x\long#1}'); 5695 5696# maybe this is easiest just to punt. 5697RawTeX(<<'EoTeX'); 5698\def\in@#1#2{% 5699 \def\in@@##1#1##2##3\in@@{% 5700 \ifx\in@##2\in@false\else\in@true\fi}% 5701 \in@@#2#1\in@\in@@} 5702\newif\ifin@ 5703EoTeX 5704 5705DefMacro('\IfFileExists{}{}{}', sub { 5706 my ($gullet, $file, $if, $else) = @_; 5707 my $file_string = ToString(Expand($file)); 5708 if (FindFile($file_string)) { 5709 DefMacro('\@filef@und', '"' . $file_string . '" '); 5710 return ($if->unlist); } 5711 else { 5712 return ($else->unlist); } }); 5713 5714DefMacro('\InputIfFileExists{}{}{}', sub { 5715 my ($gullet, $file, $if, $else) = @_; 5716 my $file_string = ToString(Expand($file)); 5717 if (FindFile($file_string)) { 5718 DefMacro('\@filef@und', '"' . $file_string . '" '); 5719 Input($file_string); 5720 return ($if->unlist); } 5721 else { return ($else->unlist); } }); 5722 5723#====================================================================== 5724# Hair 5725DefPrimitiveI('\makeatletter', undef, sub { AssignCatcode('@' => CC_LETTER, 'local'); }); 5726DefPrimitiveI('\makeatother', undef, sub { AssignCatcode('@' => CC_OTHER, 'local'); }); 5727 5728#********************************************************************** 5729#********************************************************************** 5730# Sundry (is this ams ?) 5731DefPrimitiveI('\textprime', undef, UTF(0xB4)); # ACUTE ACCENT 5732 5733Let('\endgraf', '\par'); 5734Let('\endline', '\cr'); 5735#********************************************************************** 5736# Should be defined in each (or many) package, but it's not going to 5737# get set correctly or maintained, so... 5738DefMacroI('\fileversion', undef, Tokens()); 5739DefMacroI('\filedate', undef, Tokens()); 5740 5741# Ultimately these may be overridden by babel, or otherwise, 5742# various of these are defined in various places by different classes. 5743DefMacroI('\chaptername', undef, 'Chapter'); 5744DefMacroI('\partname', undef, 'Part'); 5745# The rest of these are defined in some classes, but not most. 5746#DefMacroI('\sectionname', undef, 'Section'); 5747#DefMacroI('\subsectionname', undef, 'Subsection'); 5748#DefMacroI('\subsubsectionname', undef, 'Subsubsection'); 5749#DefMacroI('\paragraphname', undef, 'Paragraph'); 5750#DefMacroI('\subparagraphname', undef, 'Subparagraph'); 5751 5752DefMacroI('\appendixname', undef, 'Appendix'); 5753# These aren't defined in LaTeX, 5754# these definitions will give us more meaningful typerefnum's 5755DefMacroI('\sectiontyperefname', undef, '\lx@sectionsign\lx@ignorehardspaces'); 5756DefMacroI('\subsectiontyperefname', undef, '\lx@sectionsign\lx@ignorehardspaces'); 5757DefMacroI('\subsubsectiontyperefname', undef, '\lx@sectionsign\lx@ignorehardspaces'); 5758DefMacroI('\paragraphtyperefname', undef, '\lx@paragraphsign\lx@ignorehardspaces'); 5759DefMacroI('\subparagraphtyperefname', undef, '\lx@paragraphsign\lx@ignorehardspaces'); 5760 5761#********************************************************************** 5762# Stuff that would appear in the aux file... maybe somebody uses it? 5763DefMacro('\bibdata{}', Tokens()); 5764DefMacro('\bibcite{}{}', Tokens()); 5765DefMacro('\citation{}', Tokens()); 5766DefMacro('\contentsline{}{}{}', Tokens()); 5767DefMacro('\newlabel{}{}', Tokens()); 5768 5769DefMacroI('\stop', undef, sub { $_[0]->closeMouth(1); return; }); 5770DefMacroI('\ignorespacesafterend', undef, Tokens()); 5771Let('\mathgroup', '\fam'); 5772Let('\mathalpha', '\relax'); 5773 5774#\def\mathhexbox#1#2#3{\mbox{$\m@th \mathchar"#1#2#3$}} 5775DefPrimitive('\mathhexbox {}{}{}', sub { 5776 my ($stomach, $a, $b, $c) = @_; 5777 my $n = ToString($a) * 256 + ToString($b) * 16 + ToString($c); 5778 my ($role, $glyph) = decodeMathChar($n); 5779 return Box($glyph, LookupValue('font')->specialize($glyph)); }); 5780 5781DefMacroI('\nocorrlist', undef, ',.'); 5782Let('\nocorr', '\relax'); 5783Let('\check@icl', '\@empty'); 5784Let('\check@icr', '\@empty'); 5785DefMacro('\text@command{}', ''); # ? 5786DefMacro('\check@nocorr@ Until:\nocorr Until:\@nil', ''); 5787RawTeX('\newif\ifmaybe@ic'); 5788 5789DefMacroI('\maybe@ic', undef, ''); 5790DefMacroI('\maybe@ic@', undef, ''); 5791# \t@st@ic 5792DefMacroI('\sw@slant', undef, ''); 5793DefMacroI('\fix@penalty', undef, ''); 5794 5795DefPrimitiveI('\@@end', undef, sub { $_[0]->getGullet->flush; return; }); 5796 5797#********************************************************************** 5798# Modern pdflatex seems to come with hyphenation tables predefined 5799# for many languages. We don't need or use hyphenation tables, 5800# but some (versions of some) software (babel), check for 5801# the presence of these \l@<language> macros 5802# But also see \iflanguage (re)defined in babel.def.ltxml 5803RawTeX(<<'EoTeX'); 5804\newlanguage\l@english 5805\newlanguage\l@usenglishmax 5806\newlanguage\l@USenglish 5807\newlanguage\l@dumylang 5808\newlanguage\l@nohyphenation 5809\newlanguage\l@arabic 5810\newlanguage\l@basque 5811\newlanguage\l@bulgarian 5812\newlanguage\l@coptic 5813\newlanguage\l@welsh 5814\newlanguage\l@czech 5815\newlanguage\l@slovak 5816\newlanguage\l@german 5817\newlanguage\l@ngerman 5818\newlanguage\l@danish 5819\newlanguage\l@esperanto 5820\newlanguage\l@spanish 5821\newlanguage\l@catalan 5822\newlanguage\l@galician 5823\newlanguage\l@estonian 5824\newlanguage\l@farsi 5825\newlanguage\l@finnish 5826\newlanguage\l@french 5827\newlanguage\l@greek 5828\newlanguage\l@monogreek 5829\newlanguage\l@ancientgreek 5830\newlanguage\l@croatian 5831\newlanguage\l@hungarian 5832\newlanguage\l@interlingua 5833\newlanguage\l@ibycus 5834\newlanguage\l@indonesian 5835\newlanguage\l@icelandic 5836\newlanguage\l@italian 5837\newlanguage\l@latin 5838\newlanguage\l@mongolian 5839\newlanguage\l@dutch 5840\newlanguage\l@norsk 5841\newlanguage\l@polish 5842\newlanguage\l@portuguese 5843\newlanguage\l@pinyin 5844\newlanguage\l@romanian 5845\newlanguage\l@russian 5846\newlanguage\l@slovenian 5847\newlanguage\l@uppersorbian 5848\newlanguage\l@serbian 5849\newlanguage\l@swedish 5850\newlanguage\l@turkish 5851\newlanguage\l@ukenglish 5852\newlanguage\l@ukrainiane 5853EoTeX 5854 5855#********************************************************************** 5856DefPrimitive('\protected@write Number {}{}', sub { 5857 my ($stomach, $port, $prelude, $tokens) = @_; 5858 $port = ToString($port); 5859 $stomach->bgroup; 5860 Let('\thepage', '\relax'); 5861 my @stuff = Digest($prelude); 5862 Let('\protect', '\@unexpandable@protect'); 5863 if (my $filename = LookupValue('output_file:' . $port)) { 5864 my $handle = $filename . '_contents'; 5865 my $contents = LookupValue($handle); 5866 AssignValue($handle => $contents . UnTeX($tokens) . "\n", 'global'); } 5867 else { 5868 Note(UnTeX($tokens)); } 5869 $stomach->egroup; 5870 return @stuff; }); 5871 5872#********************************************************************** 5873# LaTeX now includes fixltx2e by default. 5874# https://www.latex-project.org/news/latex2e-news/ltnews22.pdf 5875 5876# This package allows you to define the font used for 5877# emphasis (\emph) within emphasis. 5878# For latexml, that styling should be left to the ultimate output, 5879# so we just define the command as a dummy. 5880DefMacro('\eminnershape', ""); 5881 5882# Undoubtedly not good enough 5883DefMacro('\TextOrMath{}{}', '\ifmmode#2\else#1\fi'); 5884 5885DefConstructor('\textsubscript{}', "<ltx:sub>#1</ltx:sub>", 5886 mode => 'text'); 5887 5888#********************************************************************** 5889# We need this bit from utf8.def for textcomp 5890DefPrimitive('\DeclareUnicodeCharacter Expanded {}', sub { 5891 my ($stomach, $hexcode, $expansion) = @_; 5892 my $char = $hexcode->toString(); 5893 if ($char =~ /^[0-9a-fA-F]+$/) { 5894 if ((my $cp = hex($char)) <= 0x10FFFF) { 5895 $char = UTF($cp); 5896 AssignCatcode($char, CC_ACTIVE); 5897 DefMacroI(T_ACTIVE($char), undef, $expansion); } 5898 else { 5899 Error('unexpected', $char, $stomach, 5900 "$char too large for Unicode. Values between 0 and 10FFFF are permitted."); } } 5901 else { 5902 Error('unexpected', $char, $stomach, 5903 "'$char' is not a hexadecimal number."); } }); 5904 5905# LaTeX now includes textcomp by default. 5906RequirePackage('textcomp'); 5907 5908#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5909# Expl3 "Experimental LaTeX 3" is no longer Experimental! 5910# It is beginning to be built into latex.ltx 5911# We WILL need a new strategy to keep up; probably based in some form 5912# of pre-read/pre-processed latex.ltx ! 5913# 5914# For now, a few macros required by other packages will be included: 5915DefMacroI(T_CS('\hook_gput_code:nnn'), '{}{}{}', ''); 5916 5917#********************************************************************** 59181; 5919