# -*- mode: Perl -*-
# /=====================================================================\ #
# | amsppt.sty | #
# | Implementation for LaTeXML | #
# |=====================================================================| #
# | Part of LaTeXML: | #
# | Public domain software, produced as part of work done by the | #
# | United States Government & not subject to copyright in the US. | #
# |---------------------------------------------------------------------| #
# | Thanks to the arXMLiv group for initial implementation | #
# | http://arxmliv.kwarc.info/ | #
# | Released to the Public Domain | #
# |---------------------------------------------------------------------| #
# | Bruce Miller <bruce.miller@nist.gov> #_# | #
# | http://dlmf.nist.gov/LaTeXML/ (o o) | #
# \=========================================================ooo==U==ooo=/ #
package LaTeXML::Package::Pool;
use strict;
use warnings;
use LaTeXML::Package;
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# This is a style file to go with AMSTeX (_NOT_ AMS LaTeX)
# it is roughly analogous to a LaTeX class file.
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
LoadPool("AmSTeX"); # Just in case...
DefMacro('\filename', 'amsppt.sty');
DefMacro('\fileversion', '2.1h');
DefMacro('\filedate', '1997/02/02');
DefMacro('\fileversiontest', '\fileversion\space(\filedate)');
DefMacro('\styname', 'AMSPPT');
DefMacro('\styversion', '\fileversion');
DefMacro('\plainend', '\end');
#======================================================================
DefMacro('\makeheadline', '');
DefMacro('\makefootline', '');
DefMacro('\leftheadline', '');
DefMacro('\rightheadline', '');
DefMacro('\leftheadtext{}', '');
DefMacro('\rightheadtext{}', '');
DefRegister('\leftheadtoks' => Tokens());
DefRegister('\rightheadtoks' => Tokens());
Let('\flheadline', '\hfil');
Let('\frheadline', '\hfil');
DefMacro('\headmark{}', '');
DefMacro('\nologo', '');
DefMacro('\pagecontents', '');
DefMacro('\cvolyear{}', ''); # ?
#======================================================================
# Specific front matter commands
DefMacro('\title Until:\endtitle', '\@add@frontmatter{ltx:title}{#1}');
DefConstructor('\@personname{}', "<ltx:personname>#1</ltx:personname>",
bounded => 1, mode => 'text');
DefMacro('\author Until:\endauthor', '\@add@frontmatter{ltx:creator}[role=author]{\@personname{#1}}');
DefConstructor('\@@@affil{}', "<ltx:contact role='affiliation'>#1</ltx:contact>");
DefMacro('\affil Until:\endaffil', '\@add@to@frontmatter{ltx:creator}{\@@@affil{#1}}');
DefConstructor('\@@@address{}', "<ltx:contact role='address'>#1</ltx:contact>");
DefMacro('\address Until:\endaddress', '\@add@to@frontmatter{ltx:creator}{\@@@address{#1}}');
DefConstructor('\@@@curraddr{}', "<ltx:contact role='current-address'>#1</ltx:contact>");
DefMacro('\curraddr Until:\endcurraddr', '\@add@to@frontmatter{ltx:creator}{\@@@curraddr{#1}}');
DefConstructor('\@@@email{}', "<ltx:contact role='email'>#1</ltx:contact>");
DefMacro('\email Until:\endemail', '\@add@to@frontmatter{ltx:creator}{\@@@email{#1}}');
DefConstructor('\@@@urladdr{}', "<ltx:contact role='url'>#1</ltx:contact>");
DefMacro('\urladdr Until:\endurladdr', '\@add@to@frontmatter{ltx:creator}{\@@@urladdr{#1}}');
DefMacro('\thanks Until:\endthanks', '\@add@frontmatter{ltx:note}[role=support]{#1}');
DefMacro('\date Until:\enddate', '\@add@frontmatter{ltx:date}[role=creation]{#1}');
DefMacro('\dedicatory Until:\enddedicatory', '\@add@frontmatter{ltx:note}[role=dedicatory]{#1}');
DefMacro('\translator Until:\endtranslator', '\@add@frontmatter{ltx:creator}[role=translator]{\@personname{#1}}');
DefMacro('\keywords Until:\endkeywords', '\@add@frontmatter{ltx:keywords}{#1}');
DefMacro('\subjclass Until:\endsubjclass', '\@add@frontmatter{ltx:classification}{#1}');
DefMacro('\abstract Until:\endabstract', '\@add@frontmatter{ltx:abstract}{#1}');
Let('\endabstract', '\relax');
DefMacro('\issueinfo{}{}{}{}', '');
DefMacro('\NoRunningHeads', '');
DefMacro('\Monograph', '');
# Hmm is the refnum until space ?
DefConstructor('\specialhead Until:\endspecialhead',
"<ltx:section><ltx:title>#1</ltx:title>");
Let('\pretitle', '\relax');
Let('\preauthor', '\relax');
Let('\preaffil', '\relax');
Let('\predate', '\relax');
Let('\preabstract', '\relax');
Let('\prepaper', '\relax');
#======================================================================
# Document Structure
# We go ahead and define counters;
# they're never (?) used for displayed reference numbers
# but are used for id generation.
NewCounter('chapter', 'document', idprefix => 'C', nested => ['section']);
NewCounter('section', 'chapter', idprefix => 'S', nested => ['subsection']);
NewCounter('subsection', 'section', idprefix => 'SS', nested => ['subsubsection']);
NewCounter('subsubsection', 'subsection', idprefix => 'SSS', nested => ['paragraph']);
NewCounter('equation', 'document', idprefix => 'E');
map { Tag("ltx:$_", autoClose => 1) }
qw(chapter section subsection subsubsection);
# Should also add any missing: \endroster, \enddefinition, \enddemo, \endexample,\endproclaim !!
DefConstructor('\specialhead Until:\endspecialhead',
"<ltx:chapter inlist='toc' xml:id='#id'>"
. "#tags"
. "<ltx:title>#1</ltx:title>",
bounded => 1,
properties => sub { RefStepID('chapter') });
Let('\endspecialhead', '\relax');
DefConstructor('\head Until:\endhead',
"<ltx:section inlist='toc' xml:id='#id'>"
. "#tags"
. "<ltx:title>#1</ltx:title>",
bounded => 1,
properties => sub { RefStepID('section') });
DefMacro('\heading Until:\endheading', '\head#1\endhead');
DefConstructor('\subhead Until:\endsubhead',
"<ltx:subsection inlist='toc' xml:id='#id'>"
. "#tags"
. "<ltx:title>#1</ltx:title>",
bounded => 1,
properties => sub { RefStepID('subsection') });
# TODO: This can be used as a simple macro \subheading{..}, see e.g. arxiv:math/0001062
DefMacro('\subheading', sub {
my ($gullet) = @_;
return ($gullet->ifNext(T_BEGIN)
? (T_CS('\subheading@onearg'))
: (T_CS('\subheading@env'))); },
locked => 1);
DefMacro('\subheading@onearg {}', '\subhead#1\endsubhead');
DefMacro('\subheading@env Until:\endsubheading', '\subhead#1\endsubhead');
DefConstructor('\subsubhead Until:\endsubsubhead',
"<ltx:subsubsection inlist='toc' xml:id='#id'>"
. "#tags"
. "<ltx:title>#1</ltx:title>",
bounded => 1,
properties => sub { RefStepID('subsubsection') });
DefMacro('\widestnumber Token {}', ''); # Eat the next thing (w/its args!) and ignore it!!
DefRegister('\aboveheadskip' => Glue(0));
DefRegister('\belowheadskip' => Dimension(0));
DefRegister('\abovespecialheadskip' => Glue(0));
DefRegister('\subheadskip' => Glue(0));
DefRegister('\subsubheadskip' => Glue(0));
DefRegister('\headlineheight' => Dimension(0));
DefRegister('\headlinespace' => Dimension(0));
DefRegister('\dropfoliodepth' => Dimension(0));
DefMacro('\nofrillscheck{}', '');
# I'm guessing toc can be ignored, since hopefully the above will be enough to create a better one.
DefMacro('\toc Until:\endtoc', '');
Let('\endtoc', '\relax');
NewCounter('theorem', 'document', idprefix => "Thm");
Tag('ltx:theorem', autoClose => 1);
Tag('ltx:proof', autoClose => 1);
DefConstructor('\proclaim Undigested DigestUntil:\endproclaim',
"<ltx:theorem class='ltx_theorem_proclaim' xml:id='#id'>"
. "#tags"
. "<ltx:title>#1</ltx:title>"
. "#2",
afterConstruct => sub { $_[0]->maybeCloseElement('ltx:theorem'); },
properties => sub { RefStepID('theorem') });
Let('\endproclaim', '\relax');
DefRegister('\preproclaimskip' => Glue(0));
DefRegister('\postproclaimskip' => Glue(0));
DefMacro('\proclaimfont', '\it');
# \remark ... \endremark
DefRegister('\remarkskip' => Glue(0));
DefRegister('\postdemoskip' => Glue(0));
# \definition ..\enddefinition
DefRegister('\predefinitionskip' => Glue(0));
DefRegister('\postdefinitionskip' => Glue(0));
DefMacro('\definitionfont', '\rm');
# Is there nothing about \definitionname (& \remarkname, \examplename, etc?)
DefMacroI('\definitionname', undef, 'Definition');
DefConstructor('\definition Undigested DigestUntil:\enddefinition',
"<ltx:theorem class='ltx_theorem_definition' xml:id='#id'>"
. "#tags"
. "<ltx:title font='#titlefont' _force_font='true'>#title</ltx:title>"
. "#2",
afterConstruct => sub { $_[0]->maybeCloseElement('ltx:theorem'); },
properties => sub {
my $title = Digest(Tokens(T_BEGIN, T_CS('\definitionname'), T_SPACE, $_[1], T_END));
(RefStepID('theorem'),
title => $title, titlefont => $title->getFont); });
Let('\enddefinition', '\relax');
# \remark..\endremark
DefMacro('\remarkfont', '\rm');
DefMacroI('\remarkname', undef, 'Remark');
DefConstructor('\remark Undigested DigestUntil:\endremark',
"<ltx:theorem class='ltx_theorem_remark' xml:id='#id'>"
. "#tags"
. "<ltx:title font='#titlefont' _force_font='true'>#title</ltx:title>"
. "#2",
afterConstruct => sub { $_[0]->maybeCloseElement('ltx:theorem'); },
properties => sub {
my $title = Digest(Tokens(T_BEGIN, T_CS('\remarkname'), T_SPACE, $_[1], T_END));
(RefStepID('theorem'),
title => $title, titlefont => $title->getFont); });
Let('\endremark', '\relax');
DefConstructor('\example Undigested DigestUntil:\endexample',
"<ltx:theorem class='ltx_theorem_example' xml:id='#id'>"
. "#tags"
. "<ltx:title font='#titlefont' _force_font='true'>#title</ltx:title>"
. "#2",
afterConstruct => sub { $_[0]->maybeCloseElement('ltx:theorem'); },
properties => sub {
my $title = Digest(Tokens(T_BEGIN, T_CS('\examplename'), T_SPACE, $_[1], T_END));
(RefStepID('theorem'),
title => $title, titlefont => $title->getFont); });
Let('\endexample', '\relax');
DefMacroI('\demonstrationname', undef, 'Demonstration');
DefConstructor('\demo Undigested DigestUntil:\enddemo',
"<ltx:theorem class='ltx_theorem_demonstration' xml:id='#id'>"
. "#tags"
. "<ltx:title font='#titlefont' _force_font='true'>#title</ltx:title>"
. "#2",
afterConstruct => sub { $_[0]->maybeCloseElement('ltx:theorem'); },
properties => sub {
my $title = Digest(Tokens(T_BEGIN, T_CS('\demonstrationname'), T_SPACE, $_[1]->unlist, T_END));
(RefStepID('theorem'),
title => $title, titlefont => $title->getFont); });
Let('\enddemo', '\relax');
# \roster ..\endroster (an itemization)
DefRegister('\rosteritemwd' => Dimension(0));
# \Runinitem ?
NewCounter('roster', 'document', idprefix => 'I');
NewCounter('rosteritem', 'roster', idprefix => 'i');
DefMacro('\therosteritem{}', '\rom{(#1)}');
DefConstructor('\roster DigestUntil:\endroster',
"<ltx:enumerate>#body</ltx:enumerate>",
bounded => 1,
properties => sub { RefStepID('roster') },
beforeDigest => sub { Let('\item', '\roster@item'); });
DefConstructor('\roster@item',
"<ltx:item xml:id='#id'>?#1(<ltx:tags><ltx:tag>#1</ltx:tag></ltx:tags>)",
properties => sub { RefStepID('rosteritem') });
Let('\endroster', '\relax');
DefConstructor('\block DigestUntil:\endblock',
"<ltx:quote>#1</ltx:quote>");
Let('\endblock', '\relax');
DefRegister('\pagenumwd' => Dimension(0));
DefRegister('\indenti' => Dimension(0));
DefRegister('\indentii' => Dimension(0));
DefMacro('\linespacing Number', '');
DefMacro('\endquotes', ''); # ?
NewCounter('footnote');
DefMacro('\thefootnote', sub { ExplodeText(ToString(LookupRegister('\c@footnote')->valueOf)); });
# same as LaTeX (but LaTeX.pool NOT included for amsppt!!!)
DefConstructor('\footnote[]{}',
"<ltx:note role='footnote' mark='#mark' xml:id='#id'>"
. "#tags"
. "#2"
. "</ltx:note>",
mode => 'text', bounded => 1,
beforeDigest => sub { reenterTextMode(1); neutralizeFont(); },
properties => sub {
($_[1]
? (mark => $_[1])
: (RefStepCounter('footnote'),
mark => DigestText(T_CS('\thefootnote')))) });
DefConstructor('\footnotemark[]',
"<ltx:note role='footnotemark' mark='#mark' xml:id='#id'>"
. "#tags"
. "</ltx:note>",
mode => 'text',
properties => sub {
($_[1]
? (mark => $_[1])
: (RefStepCounter('footnote'),
mark => DigestText(T_CS('\thefootnote')))) });
DefConstructor('\footnotetext[]{}',
"<ltx:note role='footnotetext' mark='#mark' xml:id='#id'>#2</ltx:note>",
mode => 'text',
properties => sub {
($_[1]
? (mark => $_[1])
: (mark => Digest(T_CS("\\thefootnote")))); });
Tag('ltx:note', afterClose => \&relocateFootnote);
sub relocateFootnote {
my ($document, $node) = @_;
if (($node->getAttribute('role') || '') =~ /^(\w+?)text$/) {
my $notetype = $1; # Eg "footnote", "endnote",...
if (my $mark = $node->getAttribute('mark')) {
if (my $note = $document->findnode(".//ltx:note[\@role='${notetype}mark'][\@mark='$mark']")) {
$node->parentNode->removeChild($node);
append_nodes($note, $node->childNodes);
$document->setAttribute($note, role => $notetype); } } }
return; }
#======================================================================
DefMacro('\rom{}', '{rm #1}');
DefMacro('\PSAMSFonts', '');
RawTeX('\newif\ifPSAMSFonts\PSAMSFontstrue');
DefMacro('\qed', '\ltx@qed');
DefConstructor('\ltx@qed', "?#isMath(<ltx:XMTok role='PUNCT'>\x{220E}</ltx:XMTok>)(\x{220E})", reversion => '\qed');
DefPrimitiveI('\tildechar', undef, "~", font => { family => 'typewriter' });
DefMacro('\breakcheck', '');
DefRegister('\normalparindent' => Dimension(0));
DefMacro('\usualspace', ' ');
#======================================================================
# Bibliography
DefMacro('\Refsname', 'References');
DefConstructor('\cite Semiverbatim', "<ltx:cite>[<ltx:bibref show='refnum' bibrefs='#1'/>]</ltx:cite>");
# arg or no arg?
DefMacro('\thebibliography@ID', '\ifx.\thedocument@id.\thedocument@id.bib\else bib\fi');
DefMacro('\@auto@Refs', '\Refs');
DefMacro('\Refs', '\@Refs\def\@auto@Refs{}');
DefConstructor('\@Refs',
"<ltx:bibliography xml:id='#id'>"
. "<ltx:title>References</ltx:title>"
. "<ltx:biblist>",
afterDigest => sub {
$_[1]->setProperty(id => ToString(Expand(T_CS('\thebibliography@ID')))); });
DefMacro('\endRefs', '\def\@auto@Refs{\Refs}\end@Refs');
DefConstructor('\end@Refs', "</ltx:bibliography>");
NewCounter('@bibitem', 'bibliography', idprefix => 'bib');
DefMacro('\the@bibitem', '\number\c@@bibitem');
# Since \Refs is apparently optional, we need to automatically open/close
## No that gives too many elements that can be auto-opened!
## try the \@auto@Refs bit ... ?
##Tag('ltx:bibliography',autoOpen=>1,autoClose=>1);
Tag('ltx:bibliography', autoClose => 1);
Tag('ltx:biblist', autoOpen => 1, autoClose => 1);
DefRegister('\refindentwd' => Dimension(0));
DefMacro('\refstyle{}', '');
DefMacro('\keyformat{}', '#1');
DefMacro('\refbreaks', '');
DefMacro('\defaultreftexts', '');
DefMacro('\ref', '\@auto@Refs\begingroup\@bibitem\@bibfield{random}');
DefMacro('\endref', '\@end@bibfield\@fill@bibitem\@end@bibitem\endgroup');
# \moreref is sorta like starting a bibitem within a bibitem
DefMacro('\moreref',
'\@end@bibfield\@fill@bibitem\@end@bibblock\endgroup'
. '\begingroup\@bibblock\@bibfield{moreref}nonempty\@end@bibfield\@bibfield{random}');
DefConstructor('\@bibitem', "<ltx:bibitem xml:id='#id'>",
properties => sub { RefStepID('@bibitem'); });
DefConstructor('\@end@bibitem', "</ltx:bibitem>");
DefConstructor('\@bibblock', "<ltx:bibblock>");
DefConstructor('\@end@bibblock', "</ltx:bibblock>");
DefMacro('\@fill@bibitem', sub {
my @body = ();
if (!LookupValue('amsbibitem@moreref')) {
push(@body, Invocation(T_CS('\@bibitem@tag'),
LookupValue('amsbibitem@key')
|| LookupValue('amsbibitem@refnum') || T_CS('\the@bibitem'))); }
my $ed;
if (!LookupValue('amsbibitem@authors') && ($ed = LookupValue('amsbibitem@editors'))) {
AssignValue('amsbibitem@authors' => Tokens($ed->unlist, Tokenize('\space(\edtext)'))); }
push(@body, ppunbox(undef, undef, 'authors'));
if (LookupValue('amsbibitem@book')) { # Case 1: Book, etc
push(@body, commaunbox('book'), commaunbox('bookinfo'),
ppunbox(undef, '\space(', 'proceedingsinfo', ')'),
ppunbox(',', '\space vol.~', 'volume'),
ppunbox(undef, '\space(', 'editors', ', \edtext)'),
commaunbox('publisher'), commaunbox('publisheraddr'), commaunbox('year'),
ppunbox(',', '\space\pagestext~', 'pages')); }
else { # Case 2, paper in book
push(@body, commaunbox('paper'), commaunbox('paperinfo'));
if (LookupValue('amsbibitem@inbook')) {
push(@body, commaunbox('inbook'),
ppunbox(undef, '\space(', 'proceedingsinfo', ')'),
ppunbox(undef, '\space(', 'editors', ', \edtext)'),
commaunbox('bookinfo'),
ppunbox(',', '\space\voltext~', 'volume'),
commaunbox('publisher'), commaunbox('publisheraddr'),
commaunbox('year'),
ppunbox(',', '\space\pagestext~', 'pages')); }
elsif (!LookupValue('amsbibitem@random')) { # Case 3, paper in journal
push(@body, commaunbox('journal'),
ppunbox(undef, '\space', 'volume'),
ppunbox(undef, '\space(', 'year', ')'),
ppunbox(',', '\space\issuetext~', 'issue'),
commaunbox('publisher'), commaunbox('publisheraddr'),
commaunbox('pages')); }
else { # Leftover Case: random text
push(@body, commaunbox('random')); }
}
push(@body, commaunbox('finalinfo'),
ppunbox(undef, '\space(', 'note', ')'),
T_OTHER('.'),
ppunbox(undef, '\space(', 'language', ')'),
commaunbox('mathreview'));
@body });
sub ppunbox {
my ($punct, $pre, $field, $post) = @_;
my $value = LookupValue('amsbibitem@' . $field);
if (!IsEmpty($value)) {
return ((defined $punct ? Tokenize($punct)->unlist : ()),
(defined $pre ? Tokenize($pre)->unlist : (T_SPACE)),
# Wrap this in text with field as a class or something?
Invocation(T_CS('\@bibitem@field'), T_OTHER($field), $value),
(defined $post ? Tokenize($post)->unlist : ())); }
else {
return (); } }
sub commaunbox {
my ($field) = @_;
return ppunbox(',', '\space', $field, undef); }
DefConstructor('\@bibitem@field{}{}', "<ltx:text class='#class' _noautoclose='1'>#2</ltx:text>",
properties => { class => sub { 'ltx_bib_' . ToString($_[1]); } });
# Both a tag, and a key!
DefConstructor('\@bibitem@tag{}', "^key='#1'<ltx:tags><ltx:tag role='refnum'>#1</ltx:tag></ltx:tags>");
DefMacro('\@bibfield{} XUntil:\@end@bibfield', sub {
my ($gullet, $field, $text) = @_;
my @tokens = $text->unlist;
while (@tokens && Equals($tokens[0], T_SPACE)) { shift(@tokens); }
while (@tokens && Equals($tokens[-1], T_SPACE)) { pop(@tokens); }
if (@tokens) {
AssignValue('amsbibitem@' . ToString($field) => Tokens(@tokens)); }
return; });
Let('\@end@bibfield', '\relax');
# open bibblock until we sort out these macros.
Tag('ltx:bibblock', autoOpen => 1, autoClose => 1);
DefRegister('\holdoverbox' => Tokens());
DefMacro('\holdover{}', '');
DefMacro('\key', '\@end@bibfield\@bibfield{key}');
DefMacro('\no', '\@end@bibfield\@bibfield{refnum}');
DefMacro('\by', '\@end@bibfield\@bibfield{authors}');
DefMacro('\bysame', '\by ---');
Let('\manyby', '\by');
DefMacro('\ed', '\@end@bibfield\@bibfield{editors}');
DefMacro('\eds', '\@end@bibfield\@bibfield{editors}');
DefMacro('\edtext', 'ed.');
DefMacro('\paper', '\@end@bibfield\@bibfield{paper}\it');
DefMacro('\paperinfo', '\@end@bibfield\@bibfield{paperinfo}');
DefMacro('\inbook', '\@end@bibfield\@bibfield{inbook}');
DefMacro('\book', '\@end@bibfield\@bibfield{book}\it');
DefMacro('\bookinfo', '\@end@bibfield\@bibfield{bookinfo}');
DefMacro('\procinfo', '\@end@bibfield\@bibfield{proceedingsinfo}');
DefMacro('\finalinfo', '\@end@bibfield\@bibfield{finalinfo}');
DefMacro('\jour', '\@end@bibfield\@bibfield{journal}');
DefMacro('\vol', '\@end@bibfield\@bibfield{volume}\bf');
DefMacro('\voltext', 'vol.');
DefMacro('\issue', '\@end@bibfield\@bibfield{issue}');
DefMacro('\issuetext', 'no.');
DefMacro('\yr', '\@end@bibfield\@bibfield{year}');
DefMacro('\page', '\@end@bibfield\@bibfield{pages}');
DefMacro('\pages', '\@end@bibfield\@bibfield{pages}');
DefMacro('\pagestext', 'pp.');
DefMacro('\lang', '\@end@bibfield\@bibfield{language}');
DefMacro('\publ', '\@end@bibfield\@bibfield{publisher}');
DefMacro('\publaddr', '\@end@bibfield\@bibfield{publisheraddress}');
DefMacro('\miscnote', '\@end@bibfield\@bibfield{note}');
DefMacro('\toappear', '\miscnote to appear');
DefMacro('\MR', '\@end@bibfield\@bibfield{mathreview}MR ');
DefMacro('\AMSPPS', '\@end@bibfield\@bibfield{ams-preprint}AMS-PPS ');
DefMacro('\CMP', '\@end@bibfield\@bibfield{CMP}CMP ');
# \transl ????
#======================================================================
DefPrimitiveI('\smc', undef, undef, font => { shape => 'smallcaps' });
#======================================================================
1;