# -*- CPERL -*-
# /=====================================================================\ #
# |  LaTeX                                                              | #
# | 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.     | #
# |---------------------------------------------------------------------| #
# | Bruce Miller <bruce.miller@nist.gov>                        #_#     | #
# | http://dlmf.nist.gov/LaTeXML/                              (o o)    | #
# \=========================================================ooo==U==ooo=/ #

#**********************************************************************
# Organized following 
#  "LaTeX: A Document Preparation System"
#   by Leslie Lamport
#   2nd edition
# Addison Wesley, 1994
# Appendix C. Reference Manual
#**********************************************************************
# NOTE: This will be loaded after TeX.pool.ltxml, so it inherits.
#**********************************************************************
package LaTeXML::Package::Pool;
use strict;
use LaTeXML::Package;
use LaTeXML::Util::Pathname;
no warnings 'redefine';

#**********************************************************************
# Basic \documentclass & \documentstyle

#AssignValue('2.09_COMPATIBILITY'=>0);
DefMacroI('\if@compatibility',undef,sub { ifHandler($_[0],LookupValue('2.09_COMPATIBILITY')); },
	 isConditional=>1);

# Let's try just starting with this set (since we've loaded LaTeX)
AssignValue(inPreamble=>1);	# \begin{document} will clear this.

DefConstructor('\documentclass OptionalSemiverbatim SkipSpaces Semiverbatim []',
	       "<?latexml class='#2' ?#1(options='#1')?>",
  afterDigest=>sub {
    my($stomach,$whatsit)=@_;
    my $options = $whatsit->getArg(1);
    LoadClass(ToString($whatsit->getArg(2)),
	      options=>[ ($options ? split(/,\s*/, ToString($options)) : ()) ],
	      after=>Tokens(T_CS('\warn@unusedclassoptions')));
    return;});

AssignValue('@unusedoptionlist',[]);
DefPrimitiveI('\warn@unusedclassoptions',undef,sub {
  my $name = ToString(Digest(T_CS('\@currname')));
  my $type = ToString(Digest(T_CS('\@currext')));
  foreach my $option (@{LookupValue('@unusedoptionlist')}){
    Info(":unexpected:<option>:$option Unexpected option $option passed to $name.$type"); }
  AssignValue('@unusedoptionlist',[]); });

DefConstructor('\documentstyle OptionalSemiverbatim SkipSpaces Semiverbatim []',
	       "<?latexml class='#2' ?#1(options='#1') oldstyle='true'?>",
  beforeDigest=>sub {
    Info(":unexpected:\\documentstyle LaTeX 2.09 Compatibility mode");
    AssignValue('2.09_COMPATIBILITY'=>1,'global'); },
  afterDigest=>sub {
    my($stomach,$whatsit)=@_;
    my $class   = ToString($whatsit->getArg(2));
    my $options = $whatsit->getArg(1);
    $options = [ ($options ? split(/,\s*/, ToString($options)) : ()) ];
    # Watch out; compatibility mode in principle wants a .sty, not a .cls!!! But, we'll try .cls first, anyway
    if(my $classfile = FindFile($class, type=>'cls')){
      LoadClass($class, options=>$options, after=>Tokens(T_CS('\compat@loadpackages'))); }
    elsif(my $stylefile = FindFile($class, type=>'sty')){
      LoadClass('article'); # Just to be safe, but w/o options ?
      # Load the package, but note that we're pretending it's a class (to some extent!)
      RequirePackage($class, options=>$options, as_class=>1, after=>Tokens(T_CS('\compat@loadpackages'))); }
    else {
      Warn(":missing_file:$class.cls.ltxml No LaTeXML implementation of class $class found, using article");
      LoadClass('article', options=>$options, after=>Tokens(T_CS('\compat@loadpackages'))); }
    return;});

DefPrimitiveI('\compat@loadpackages',undef,sub {
  my $name = ToString(Digest(T_CS('\@currname')));
  my $type = ToString(Digest(T_CS('\@currext')));
  foreach my $option (@{LookupValue('@unusedoptionlist')}){
    if(FindFile($option,type=>'sty')){
      RequirePackage($option); }
    else {
      Info(":unexpected:<option>:$option Unexpected option $option passed to $name.$type"); }}
  AssignValue('@unusedoptionlist',[]); });

sub onlyPreamble {
  Error(":unexpected The current command can only appear in the preamble")
    unless LookupValue("inPreamble"); 
  return; }
#**********************************************************************
# C.1.  Commands and Environments.
#**********************************************************************

#======================================================================
# C.1.1 Command Names and Arguments
#======================================================================
# Nothing...

#======================================================================
# C.1.2 Environments
#======================================================================

# In LaTeX, \newenvironment{env} defines \env and \endenv.
# \begin{env} & \end{env} open/close a group, and invoke these.
# In fact, the \env & \endenv don't have to have been created by
# \newenvironment; And in fact \endenv doesn't even have to be defined!
# [it is created by \csname, and equiv to \relax if no previous defn]

# We need to respect these usages here, but we also want to be able
# to define environment constructors that `capture' the body so that
# it can be processed specially, if needed.  These are the magic
# '\begin{env}', '\end{env}' control sequences created by DefEnvironment.

DefMacro('\begin{}',sub { 
  my($gullet,$env)=@_;
  my $name = $env && ToString($env);
  if(LookupDefinition(T_CS("\\begin{$name}"))){
    T_CS("\\begin{$name}"); } # Magic cs!
  else {			# If not defined, let stomach handle it.
    (T_CS('\begingroup'),T_CS("\\$name")); }});

DefMacro('\end{}',  sub { 
  my($gullet,$env)=@_;
  my $name = $env && ToString($env);
  my $t;
  if   (LookupDefinition($t=T_CS("\\end{$name}"))){ $t; } # Magic CS!
  elsif(LookupDefinition($t=T_CS("\\end$name")))  { ($t, T_CS('\endgroup')); }
  else { ( T_CS('\endgroup')); }});

#======================================================================
# C.1.3 Fragile Commands
#======================================================================
# Because of the way we `move information', revertable and pre-processed,
# I don't think we actually need to do anything ... 
# [Course that means we're not _really_ TeX!]

# \protect is already in TeX for obscure reasons...

#======================================================================
# C.1.4 Declarations
#======================================================================
# actual implementation later.
#======================================================================
# C.1.5 Invisible Commands
#======================================================================
# actual implementation later.

#======================================================================
# C.1.6 The \\ Command
#======================================================================
# In math, \\ is just a formatting hint, unless within an array, cases, .. environment.
DefConstructor("\\\\ OptionalMatch:* [Glue]",
	       "?#isMath(<ltx:XMHint name='\\'/>)(<ltx:break/>)",
	       reversion=>Tokens(T_CS("\\\\"),T_CR));

#**********************************************************************
# C.2. The Structure of the Document
#**********************************************************************
#   prepended files (using filecontents environment)
#   preamble (starting with \documentclass)
#   \begin{document}
#    text
#   \end{document}

AssignValue('@at@begin@document',[]);
AssignValue('@at@end@document',[]);
DefEnvironment('{document}', sub {
#	       "<ltx:document xml:id='#id'>#body</ltx:document>",
  my($document,%props)=@_;
  my $id = ToString($props{id});
  my $body = $props{body};
  if(my $docel = $document->findnode('/ltx:document')){ # Already (auto) created?
    $docel->setAttribute('xml:id'=>$id) if $id;
    $document->absorb($body); }
  else {
    $document->insertElement('ltx:document',$body,id=>$id); }},
	       beforeDigest=> sub { AssignValue(inPreamble=>0); },
	       afterDigestBegin=>sub {
		 $_[1]->setProperty(id=>Expand(T_CS('\thedocument@ID')));
		 Digest(Tokens(@{LookupValue('@at@begin@document')}));  },
	       afterDigest=> sub {
		 $_[0]->getGullet->flush; 
		 Digest(Tokens(@{LookupValue('@at@end@document')}));  });

#**********************************************************************
# C.3. Sentences and Paragraphs
#**********************************************************************

#======================================================================
# C.3.1 Making Sentences
#======================================================================
# quotes;  should these be handled in DOM/construction?
# dashes:  We'll need some sort of Ligature analog, or something like
# Omega's OTP, to combine sequences of "-" into endash, emdash,
# Perhaps it also applies more semantically?
# Such as interpreting certain sequences as section headings,
# or math constructs.

# Spacing; in TeX.pool.ltxml

# Special Characters; in TeX.pool.ltxml

# Logos
# \TeX is in TeX.pool.ltxml
DefConstructorI('\LaTeX',  undef,'LaTeX');
DefConstructorI('\LaTeXe', undef,'LaTeX2e');
DefMacroI('\fmtname',      undef,'LaTeXML');
DefMacroI('\fmtversion',   undef, 'XXXX/XX/XX');

DefMacroI('\today', undef, sub { Explode(today()); });

DefConstructor('\emph{}', "<ltx:emph>#1</ltx:emph>", mode=>'text');

#======================================================================
# C.3.2 Making Paragraphs
#======================================================================
# \noindent, \indent, \par in TeX.pool.ltxml

# Style parameters
# \parindent, \baselineskip, \parskip alreadin in TeX.pool.ltxml

DefPrimitive('\linespread{}', undef);

#======================================================================
# C.3.3 Footnotes
#======================================================================

NewCounter('footnote');
DefMacroI('\thefootnote',undef,'\arabic{footnote}');
NewCounter('mpfootnote');
DefMacroI('\thempfn',undef,'\thefootnote');
DefMacroI('\thempfootnote',undef,'\arabic{mpfootnote}');


DefConstructor('\footnote[]{}',
	       "<ltx:note class='footnote' mark='#refnum'>#2</ltx:note>",
	       mode=>'text',
	       properties=> sub { 
		 ($_[1] ? (refnum=>$_[1]) : RefStepCounter('footnote')) });
# NOTE: DOM should reattach the footnotetext to the footnotemark ?
#  Hmm, in general the mark & text _could_ come in either order...
DefConstructor('\footnotemark[]',""); # ????
DefConstructor('\footnotetext[]{}',
	       "<ltx:note class='footnote' ?#1(mark='#1')>#2</ltx:note>",
	       mode=>'text');

# Style parameters
DefRegister('\footnotesep' => Dimension(0));
DefPrimitiveI('\footnoterule', undef,undef);

#======================================================================
# C.3.4 Accents and Special Symbols
#======================================================================
# See TeX.pool.ltxml

# See Section 3.3.2 Mathematical Symbols, below

# Should this be here?
DefMath('\mathring{}', "\x{030A}", operator_role=>'OVERACCENT');

#**********************************************************************
# C.4 Sectioning and Table of Contents
#**********************************************************************

#======================================================================
# C.4.1 Sectioning Commands.
#======================================================================
# Note that LaTeX allows fairly arbitrary stuff in \the<ctr>, although
# it can get you in trouble.  However, in almost all cases, the result
# is plain text.  So, I'm putting refnum as an attribute, where I like it!
# You want something else? Redefine!

# Also, we're adding an id to each, that is parallel to the refnum, but
# valid as an ID.  You can tune the representation by defining, eg. \thesection@ID

# A little more messy than seems necessary:
#  We don't know whether to step the counter and update \@currentlabel until we see the '*',
# but we have to know it before we digest the title, since \label can be there!

# These are defined in terms of \@startsection so that
# casual user redefinitions work, too.
DefMacroI('\chapter',       undef, '\@startsection{chapter}{}{}{}{}{}', locked=>1);
DefMacroI('\part',          undef, '\@startsection{part}{}{}{}{}{}'); # not locked since sometimes redefined as partition?
DefMacroI('\section',       undef, '\@startsection{section}{}{}{}{}{}', locked=>1);
DefMacroI('\subsection',    undef, '\@startsection{subsection}{}{}{}{}{}', locked=>1);
DefMacroI('\subsubsection', undef, '\@startsection{subsubsection}{}{}{}{}{}', locked=>1);
DefMacroI('\paragraph',     undef, '\@startsection{paragraph}{}{}{}{}{}', locked=>1);
DefMacroI('\subparagraph',  undef, '\@startsection{subparagraph}{}{}{}{}{}', locked=>1);
map(Tag("ltx:$_", autoClose=>1),
    qw(part chapter section subsection subsubsection paragraph subparagraph));

DefMacro('\secdef {}{} OptionalMatch:*', sub { ($_[3] ? ($_[2]):($_[1])); });

DefMacroI('\@startsection@hook', undef, Tokens());

DefMacro('\@startsection{}{}{}{}{}{} OptionalMatch:*', sub {
  my($gullet,$type,$level,$ignore3,$ignore4,$ignore5,$ignore6,$flag)=@_;
  my $ctr = LookupValue('counter_for_'.ToString($type)) || ToString($type);
  if($flag){
    RefStepID($ctr);
    (T_CS('\@startsection@hook'),
     T_CS('\\@@section'),
     T_BEGIN,$type->unlist,T_END,
     T_OTHER('['),T_OTHER(']'),
     T_OTHER('['),T_CS('\@currentID'),T_OTHER(']')); }
  else {
    RefStepCounter($ctr);
    (T_CS('\@startsection@hook'),
     T_CS('\\@@section'),
     T_BEGIN,$type->unlist,T_END,
     T_OTHER('['),T_CS('\@currentlabel'),T_OTHER(']'),
     T_OTHER('['),T_CS('\@currentID'),T_OTHER(']')); }});

# \@@section{type}[refnum][id][toctitle]{title}
DefConstructor('\\@@section{}[][][]{}',sub {
  my($document,$type,$refnum,$id,$toctitle,$title)=@_;
  $document->openElement("ltx:".ToString($type),refnum=>$refnum,'xml:id'=>$id);
  $document->insertElement('ltx:title',$title);
  $document->insertElement('ltx:toctitle',$toctitle) if $toctitle; },
  properties=>{refnum=>'#2',id=>'#3'},
  bounded=>1);

# Not sure if this is best, but if no explicit \section'ing...
#### Tag('ltx:section',autoOpen=>1);

#======================================================================
# C.4.2 The Appendix
#======================================================================
# Handled in article,report or book.
DefMacroI('\appendixname',  undef,'Appendix'); # never used?
DefMacroI('\appendixesname',undef, 'Appendixes');

#======================================================================
# C.4.3 Table of Contents
#======================================================================
# Ignore these; leave it up to post processing.
DefPrimitiveI('\tableofcontents', undef, undef);
DefMacroI('\contentsname',  undef, 'Contents');
DefPrimitiveI('\listoffigures',   undef, undef);
DefMacroI('\listfigurename',undef, 'List of Figures');
DefPrimitiveI('\listoftables',    undef, undef);
DefMacroI('\listtablename',undef, 'List of Tables');

DefPrimitive('\addcontentsline{}{}{}', undef);
DefPrimitive('\numberline{}{}', undef);
DefPrimitive('\addtocontents{}{}', undef);

#======================================================================
# C.4.4 Style registers
#======================================================================
NewCounter('secnumdepth');
NewCounter('tocdepth');

#**********************************************************************
# C.5 Classes, Packages and Page Styles
#**********************************************************************

#======================================================================
# C.5.1 Document Class
#======================================================================
# Style Parameters
DefRegister('\bibindent'    => Dimension(0));
DefRegister('\columnsep'    => Dimension(0));
DefRegister('\columnseprule'=> Dimension(0));
DefRegister('\mathindent'   => Dimension(0));

#======================================================================
# C.5.2 Packages
#======================================================================
# We'll prefer to load package.pm, but will try package.sty or 
# package.tex (the latter being unlikely to work, but....)
# See Stomach.pm for details
# Ignorable packages ??
# pre-defined packages??

DefConstructor('\usepackage[] Semiverbatim',
	       "<?latexml package='#2' ?#1(options='#1')?>",
	       beforeDigest=>\&onlyPreamble,
	       afterDigest=>sub { my($stomach,$whatsit)=@_;
				  my $options=$whatsit->getArg(1);
				  my $packages=$whatsit->getArg(2);

				  my @pkgs = grep($_,grep(!/^\s*%/,split(/,\s*/,ToString($packages))));
				  $options = [($options ? split(/,\s*/,(ToString($options))) :())];
				  map(RequirePackage($_,options=>$options),@pkgs);
				  return});
DefConstructor('\RequirePackage[] Semiverbatim []',
	       "<?latexml package='#2' ?#1(options='#1')?>",
	       beforeDigest=>\&onlyPreamble,
	       afterDigest=>sub { my($stomach,$whatsit)=@_;
				  my $options=$whatsit->getArg(1);
				  my $package=$whatsit->getArg(2);
				  $options = [($options ? split(/,\s*/,(ToString($options))) :())];
				  RequirePackage(ToString($package), options=>$options); });
DefConstructor('\LoadClass[] Semiverbatim []',
	       "<?latexml class='#2' ?#1(options='#1')?>",
	       beforeDigest=>\&onlyPreamble,
	       afterDigest=>sub { my($stomach,$whatsit)=@_;
				  my $options=$whatsit->getArg(1);
				  my $class=$whatsit->getArg(2);
				  $options = [($options ? split(/,\s*/,(ToString($options))) :())];
				  LoadClass(ToString($class), options=>$options); });

# Related internal macros for package definition
# Internals used in Packages
DefMacro('\NeedsTeXFormat{}[]', Tokens());
DefMacro('\ProvidesClass{}[]',  Tokens());
DefMacro('\ProvidesPackage{}[]',Tokens());
DefMacro('\ProvidesFile{}[]',   Tokens());

DefPrimitive('\DeclareOption{}{}', sub {
  my($stomach,$option,$code)=@_;
  DeclareOption(ToString($option),$code); });

DefPrimitive('\PassOptionsToPackage{}{}', sub {
  my($stomach,$name,$options)=@_;
  PassOptions($name,'pkg',split(/,\s*/,ToString(Digest($options)))); });

DefPrimitive('\PassOptionsToClass{}{}', sub {
  my($stomach,$name,$options)=@_;
  PassOptions($name,'cls',split(/,\s*/,ToString(Digest($options)))); });

DefConstructor('\RequirePackageWithOptions Semiverbatim []',
	       "<?latexml package='#1'?>",
	       beforeDigest=>\&onlyPreamble,
	       afterDigest=>sub { my($stomach,$whatsit)=@_;
				  my $package=$whatsit->getArg(1);
				  RequirePackage(ToString($package), withoptions=>1); });

DefConstructor('\LoadClassWithOptions Semiverbatim []',
	       "<?latexml class='#1'?>",
	       beforeDigest=>\&onlyPreamble,
	       afterDigest=>sub { my($stomach,$whatsit)=@_;
				  my $class=$whatsit->getArg(1);
				  LoadClass(ToString($class), withoptions=>1); });

DefMacroI('\CurrentOption',undef,Tokens());

DefPrimitiveI('\OptionNotUsed',undef, sub {
  if(my $option = ToString(Digest(T_CS('\CurrentOption')))){
    my $name = ToString(Digest(T_CS('\@currname')));
    my $type = ToString(Digest(T_CS('\@currext')));
    if($type eq 'cls'){
      PushValue('@unusedoptionlist',$option); }}
  return; });

DefPrimitiveI('\@unknownoptionerror',undef, sub {
  if(my $option = ToString(Digest(T_CS('\CurrentOption')))){
    my $name = ToString(Digest(T_CS('\@currname')));
    my $type = ToString(Digest(T_CS('\@currext')));
    Info(":unexpected:<option>:$option Unexpected option $option passed to $name.$type"); }
  return; });

DefPrimitive('\ExecuteOptions{}', sub{
  my($gullet,$options)=@_;
  ExecuteOptions(split(/,\s*/,ToString(Digest($options)))); });

DefPrimitive('\ProcessOptions OptionalMatch:*',sub {
  my($stomach,$star)=@_;
 ProcessOptions( ($star ? (inorder=>1):()) ); });

DefMacro('\AtEndOfPackage{}',sub {
  my($gullet,$code)=@_;
  my $name = ToString(Digest(T_CS('\@currname')));
  my $type = ToString(Digest(T_CS('\@currext')));
  AddToMacro(T_CS("\\$name.$type-hook"),$code); });

Let(T_CS('\AtEndOfClass'),T_CS('\AtEndOfPackage'));

DefMacro('\AtBeginDocument{}',sub { PushValue('@at@begin@document', $_[1]->unlist); });
DefMacro('\AtEndDocument{}',  sub { PushValue('@at@end@document', $_[1]->unlist); });
DefMacro('\AtBeginDvi {}', Tokens());

#======================================================================
# C.5.3 Page Styles
#======================================================================
# Ignored
NewCounter('page');
DefPrimitive('\pagestyle{}',    undef);
DefPrimitive('\thispagestyle{}',undef);
DefPrimitive('\markright{}',    undef);
DefPrimitive('\markboth{}{}',   undef);
DefConstructorI('\leftmark',    undef,  '');
DefConstructorI('\rightmark',   undef,  '');
DefPrimitive('\pagenumbering{}',undef);
DefPrimitive('\twocolumn[]',    undef);
DefPrimitiveI('\onecolumn',      undef, undef);

# Style parameters from Fig. C.3, p.182
DefRegister('\paperheight'   => Dimension(0));
DefRegister('\paperwidth'    => Dimension(0));
DefRegister('\textheight'    => Dimension(0));
DefRegister('\textwidth'     => Dimension('6in'));
DefRegister('\topmargin'     => Dimension(0));
DefRegister('\headheight'    => Dimension(0));
DefRegister('\headsep'       => Dimension(0));
DefRegister('\footskip'      => Dimension(0));
DefRegister('\footheight'    => Dimension(0));
DefRegister('\evensidemargin'=> Dimension(0));
DefRegister('\oddsidemargin' => Dimension(0));
DefRegister('\marginparwidth'=> Dimension(0));
DefRegister('\marginparsep'  => Dimension(0));
DefRegister('\columnwidth'   => Dimension('6in'));
DefRegister('\linewidth  '   => Dimension('6in'));
DefRegister('\baselinestretch'   => Dimension(0));

#======================================================================
# C.5.4 The Title Page and Abstract
#======================================================================

# See frontmatter support in TeX.ltxml
DefMacro('\title{}', '\@add@frontmatter{ltx:title}{#1}');
DefMacro('\date{}', '\@add@frontmatter{ltx:date}[role=creation]{#1}');

DefConstructor('\person@thanks{}', "^ <ltx:contact role='thanks'>#1</ltx:contact>",
	       alias=>'\thanks', mode=>'text');
DefConstructor('\@personname{}', "<ltx:personname>#1</ltx:personname>",
	      beforeDigest=>sub { Let(T_CS('\thanks'),T_CS('\person@thanks')); },
	      bounded=>1, mode=>'text');

DefConstructorI('\and',undef,  " and ");
sub andSplit {
  my($cs,$tokens)=@_;
  my @items = ();
  my @toks=();
  if($tokens){
    foreach my $t ($tokens->unlist){
      if(Equals($t,T_CS('\and'))){
	push(@items,[@toks]); @toks=();}
      else {
	push(@toks,$t); }}}
  map( ($cs,T_BEGIN,@$_,T_END), @items,[@toks]); }

DefMacro('\@author{}', '\@add@frontmatter{ltx:creator}[role=author]{\@personname{#1}}');
DefMacro('\author{}', sub { andSplit(T_CS('\@author'),$_[1]); });

DefMacro('\@add@conversion@date','\@add@frontmatter{ltx:date}[role=creation]{\today}');

# Doesn't produce anything (we're already inserting frontmatter),
# But, it does make the various frontmatter macros into no-ops.
DefMacroI('\maketitle',undef, 
	 '\@startsection@hook'	
	 .'\global\let\thanks\relax'
	 .'\global\let\maketitle\relax'
	 .'\global\let\@maketitle\relax'
	 .'\global\let\@thanks\@empty'
	 .'\global\let\@author\@empty'
	 .'\global\let\@date\@empty'
	 .'\global\let\@title\@empty'
	 .'\global\let\title\relax'
	 .'\global\let\author\relax'
	 .'\global\let\date\relax'
	 .'\global\let\and\relax');

DefConstructor('\thanks{}', "<ltx:note class='thanks'>#1</ltx:note>");

# DefEnvironment('{abstract}','<ltx:abstract>#body</ltx:abstract>');
# Annoying special case; 2 problems.
# (1) environment doesn't allow me to get the material to do add@frontmatter
#   so we've got to do it a bit more manually.
# (2) Apparently, LaTeX _perversely_ allows \abstract to work!
#   [ie. no \begin{},\end{}, and not even \endabstract!]
#   And of course, if it works, people do it.
#   LaTeXML, however, MUST know when the abstract ends!
#   So, we use the section hook, to say it must have ended by now (at least)
# Probably there are other places (eg in titlepage?) that should force the close??
DefEnvironment('{abstract}','',
 	       afterDigestBegin=>sub {
 		 AssignValue(inPreamble=>0);
		 AddToMacro(T_CS('\@startsection@hook'),T_CS('\maybe@end@abstract')); },
 	       afterDigest=> sub {
		 my $frontmatter = LookupValue('frontmatter');
 		 push(@{$$frontmatter{'ltx:abstract'}},
 		      ['ltx:abstract',undef,@LaTeXML::LIST]);
		 DefMacroI('\maybe@end@abstract',undef,Tokens());
 		 return; },
	       locked=>1);
DefMacroI('\maybe@end@abstract',undef, '\endabstract');

DefMacroI('\abstractname',undef, 'Abstract'); # never used?

# Hmm, titlepage is likely to be hairy, low-level markup,
# without even title, author, etc, specified as such!
# Hmm, should this even redefine author, title, etc so that they
# are simply output?
# This is horrible hackery; What we really need, I think, is the
# ability to bind some sort of "Do <this> when we create a text box"...
# ON Second Thought...
# For the time being, ignore titlepage!
# Maybe we could do some of this if there is no title/author
# otherwise defined? Ugh!

#DefEnvironment('{titlepage}','');
# Or perhaps it's better just to ignore the markers?
#DefMacro('\titlepage','');
#DefMacro('\endtitlepage','');

# Or perhaps not....
# There's a title and other stuff in here, but how could we guess?
# Well, there's likely to be a sequence of <p><text font="xx" size="yy">...</text></p>
# Presumably the earlier, larger one is title, rest are authors/affiliations...
# Particularly, if they start with a pseudo superscript or other "marker", they're probably affil!
# For now, we just give an info message
DefEnvironment('{titlepage}','<ltx:titlepage>#body',
	      beforeDigest=>sub { Let(T_CS('\centering'),T_CS('\relax'));
				  DefEnvironment('{abstract}',
						 '<ltx:abstract>#body</ltx:abstract>');
				  Info(":unexpected:titlepage Frontmatter will not be well-structured");
				return; },
	       beforeDigestEnd=>sub { Digest(T_CS('\maybe@end@title')); },
	       locked=>1);
#DefConstructor('\title@cr[]',"<ltx:subtitle>",
#	       beforeDigest=>sub {  Digest(T_CS('\maybe@end@title')); });

# DefConstructor('\maybe@end@title',sub {
#   my($document)=@_;
#   if($document->isCloseable('ltx:title')){
#     $document->closeElement('ltx:title'); }
#   elsif($document->isCloseable('ltx:subtitle')){
#     $document->closeElement('ltx:subtitle'); }});
DefConstructorI('\maybe@end@title',undef, sub {
  my($document)=@_;
  if($document->isCloseable('ltx:titlepage')){
    $document->closeElement('ltx:titlepage'); }});

DefMacro('\sectionmark{}',      Tokens());
DefMacro('\subsectionmark{}',   Tokens());
DefMacro('\subsubsectionmark{}',Tokens());
DefMacro('\paragraphmark{}',    Tokens());
DefMacro('\subparagraphmark{}', Tokens());
DefMacroI('\@oddfoot', undef,   Tokens());
DefMacroI('\@oddhed',  undef,   Tokens());
DefMacroI('\@evenfoot',undef,   Tokens());
DefMacroI('\@evenfoot',undef,   Tokens());
#**********************************************************************
# C.6 Displayed Paragraphs
#**********************************************************************
sub aligningEnvironment {
  my($class,$document,%props)=@_;
  my $prev = $document->getNode->lastChild;
  $document->absorb($props{body});
  $document->maybeCloseElement('ltx:p');
  my @new = $document->getChildElements($document->getNode);
  if($prev && grep($$prev == $$_, @new)){ # $prev is sibling of added nodes
    while( ${$new[0]} != $$prev){ shift(@new); }
    shift(@new); }
  map(addClass($_,$class), @new);		  # Add class attribute to any remaining nodes.
}

sub addClass {
  my($node,$class)=@_;
  if($node->hasAttribute('class')){
    $node->setAttribute(class=>$node->getAttribute('class').' '.$class); }
  else {
    $node->setAttribute(class=>$class); }}

DefEnvironment('{center}',sub { aligningEnvironment('centering',@_); });
DefEnvironment('{flushleft}',sub { aligningEnvironment('flushleft',@_); });
DefEnvironment('{flushright}',sub { aligningEnvironment('flushright',@_); });

# These add an operation to be carried out when the current group ends.
# These operators will add alignment (class) attributes to each "line" in the current block.
DefPrimitiveI('\centering',   undef, sub { UnshiftValue(afterGroup=>T_CS('\@add@centering')); });
DefPrimitiveI('\raggedright', undef, sub { UnshiftValue(afterGroup=>T_CS('\@add@flushright')); });
DefPrimitiveI('\raggedleft',  undef, sub { UnshiftValue(afterGroup=>T_CS('\@add@flushleft')); });
DefConstructorI('\@add@centering',  undef,
		sub { map(addClass($_,'centering'),  $_[0]->getChildElements($_[0]->getNode)); });
DefConstructorI('\@add@flushright', undef,
		sub { map(addClass($_,'flushright'), $_[0]->getChildElements($_[0]->getNode)); });
DefConstructorI('\@add@flushleft',  undef,
		sub { map(addClass($_,'flushleft'),  $_[0]->getChildElements($_[0]->getNode)); });

#======================================================================-
# C.6.1 Quotations and Verse
#======================================================================-
DefConstructorI('\@block@cr',undef,  "<ltx:break/>",
	       reversion=>Tokens(T_CS("\\\\"),T_CR));
DefEnvironment('{quote}',   
	       '<ltx:quote>#body</ltx:quote>',
	       beforeDigest=>sub{ Let(T_CS('\\\\'),T_CS('\@block@cr'));});
DefEnvironment('{quotation}',
	       '<ltx:quote>#body</ltx:quote>',
	       beforeDigest=>sub{ Let(T_CS('\\\\'),T_CS('\@block@cr')); });
# NOTE: Handling of \\ within these environments?
DefEnvironment('{verse}',
	       '<ltx:quote class="verse">#body</ltx:quote>',
	       beforeDigest=>sub{ Let(T_CS('\\\\'),T_CS('\@block@cr'));});

#======================================================================
# C.6.2 List-Making environments
#======================================================================
Tag('ltx:item', autoClose=>1);
DefConstructor('\item[]',
	       "<ltx:item>?#1(<ltx:tag>#1</ltx:tag>)");
DefConstructor('\subitem[]',
	       "<ltx:item>?#1(<ltx:tag>#1</ltx:tag>)");
DefConstructor('\subsubitem[]',
	       "<ltx:item>?#1(<ltx:tag>#1</ltx:tag>)");

AssignValue(itemlevel=>0,'global');

sub beginItemize {
  my($type)=@_;
  my $level = LookupValue('itemlevel')+1;
  AssignValue(itemlevel=>$level);
  my $postfix = ToString(Tokens(roman($level)));
  Let(T_CS('\item')=>T_CS("\\".$type.'@item'));
  ResetCounter('enum'.$postfix);
  RefStepCounter('@itemize'.$postfix); }

sub RefStepItemCounter {
  RefStepCounter('enum'.ToString(Tokens(roman(LookupValue('itemlevel'))))); }

DefConstructor('\itemize@item[]',
	       "<ltx:item xml:id='#id'>?#1(<ltx:tag>#1</ltx:tag>)",
	       properties=>sub{ RefStepItemCounter(); });
DefConstructor('\enumerate@item[]',
	       "<ltx:item xml:id='#id' refnum='#refnum'>?#1(<ltx:tag>#1</ltx:tag>)",
	       properties=>sub{ RefStepItemCounter(); });
DefConstructor('\description@item[]',
	       "<ltx:item xml:id='#id'>?#1(<ltx:tag>#1</ltx:tag>)",
	       properties=>sub{ RefStepItemCounter(); });
  
DefEnvironment('{itemize}',
	       "<ltx:itemize xml:id='#id'>#body</ltx:itemize>",
	       properties=>sub { beginItemize('itemize'); },
	       locked=>1);
DefEnvironment('{enumerate}',
	       "<ltx:enumerate  xml:id='#id'>#body</ltx:enumerate>",
	       properties=>sub { beginItemize('enumerate'); },
	       locked=>1);
DefEnvironment('{description}',
	       "<ltx:description  xml:id='#id'>#body</ltx:description>",
	       properties=>sub { beginItemize('description'); },
	       locked=>1);
DefMacro('\descriptionlabel{}','#1');

# NOTE: Do I need to do anything with
# \labelitemi, labelitemii, labelitemiii or labelitemiv ?
# Probably would be useful (once I've got counters properly implemented)
# to add a number to the items.

#======================================================================
# C.6.3 The list and trivlist environments.
#======================================================================
# These are unlikely to generate what is wanted, but list, trivlist
# don't give enough idea of the logical structure.
# And, they can contain \item, so we need some list-like structure.
# \begin{list}{number counter}{prelim}
DefEnvironment('{list}{}{}',
	       "<ltx:itemize>#body</ltx:itemize>",
	       properties=>sub { beginItemize('list'); });

DefConstructor('\list@item[]',
	       "<ltx:item xml:id='#id'>?#1(<ltx:tag>#1</ltx:tag>)",
	       properties=>sub{ RefStepItemCounter(); });

DefEnvironment('{trivlist}',
	       '<ltx:itemize><ltx:item>#body</ltx:itemize>');

DefRegister('\topsep'        => Glue(0));
DefRegister('\partopsep'     => Glue(0));
DefRegister('\itemsep'       => Glue(0));
DefRegister('\parsep'        => Glue(0));
DefRegister('\@topsep'       => Glue(0));
DefRegister('\@topsepadd'    => Glue(0));
DefRegister('\@outerparskip' => Glue(0));
DefRegister('\leftmargin'    => Dimension(0));
DefRegister('\rightmargin'   => Dimension(0));
DefRegister('\listparindent' => Dimension(0));
DefRegister('\itemindent'    => Dimension(0));
DefRegister('\labelwidth'    => Dimension(0));
DefRegister('\labelsep'      => Dimension(0));
DefRegister('\@totalleftmargin'=> Dimension(0));
DefRegister('\leftmargini'   => Dimension(0));
DefRegister('\leftmarginii'  => Dimension(0));
DefRegister('\leftmarginiii' => Dimension(0));
DefRegister('\leftmarginiv'  => Dimension(0));
DefRegister('\leftmarginv'   => Dimension(0));
DefRegister('\leftmarginvi'  => Dimension(0));
DefRegister('\labelwidthi'   => Dimension(0));
DefRegister('\labelwidthii'  => Dimension(0));
DefRegister('\labelwidthiii' => Dimension(0));
DefRegister('\labelwidthiv'  => Dimension(0));
DefRegister('\labelwidthv'   => Dimension(0));
DefRegister('\labelwidthvi'  => Dimension(0));

# NOTE: Eventually use \makelabel to construct the list ?
#\makelabel{label}
# Could do something with this, but it's a headache.
DefPrimitive('\usecounter{}', undef);

#======================================================================
# C.6.4 Verbatim
#======================================================================

# NOTE: how's the best way to get verbatim material through?
DefEnvironment('{verbatim}', '<ltx:verbatim>#body</ltx:verbatim>');
DefEnvironment('{verbatim*}','<ltx:verbatim>#body</ltx:verbatim>');

# verbatim is a bit of special case;
# It looks like an environment, but it only ends with an explicit "\end{verbatim}" on it's own line.
# So, we'll end up doing things more manually.
# We're going to sidestep the Gullet for inputting, 
# and also the usual environment capture.
DefConstructorI(T_CS('\begin{verbatim}'),undef,
		"<ltx:verbatim font='#font'>#body</ltx:verbatim>",
		beforeDigest=>[sub { $_[0]->bgroup; 
				     AssignValue(current_environment=>'verbatim');
				     MergeFont(family=>'typewriter');
				     # Digest(T_CS('\par')); # NO! See beforeConstruct!
				   }],
		afterDigest=>[sub {
				my($stomach,$whatsit)=@_;
				$stomach->egroup;
				my $font = $whatsit->getFont;
				my $loc  = $whatsit->getLocator;
				my $end = "\\end{verbatim}";
				my @lines = $stomach->getGullet->getMouth->readRawLines($end);
				# Note last line ends up as Whatsit's "trailer"
				$whatsit->setBody(map(LaTeXML::Box->new($_,$font,$loc,T_OTHER($_)),
						      @lines, $end));
				return; }],
		beforeConstruct=>sub { $_[0]->maybeCloseElement('ltx:p'); });

# WARNING: This code binds $STATE to a simple State object with known fixed catcodes.
# The State normally contains ALL the bindings, etc and links to other important objects.
# We CAN do that here, since we are ONLY tokenizing from a new Mouth, bypassing stomach & gullet.
# However, be careful with any changes.

# Hmm, this should really be constructor or something so that at least typewriter font
# is used, if not wrapped in a <verb> something???
our $EMPTY_CATTABLE=LaTeXML::State->new(catcodes=>'none');

DefMacroI('\verb', undef, sub {
  my($gullet)=@_;
  my $mouth = $gullet->getMouth;
  my ($init,$body);
  { local $STATE = $EMPTY_CATTABLE;
    $init = $mouth->readToken;
    $init = $mouth->readToken if ToString($init) eq '*'; # Should I bother handling \verb* ?
    $body = $mouth->readTokens($init); }
  Invocation(T_CS('\@verb'),Tokens($init),$body)->unlist; });
DefConstructor('\@verb{}{}', "<ltx:verbatim font='#font'>#2</ltx:verbatim>",
	       beforeDigest=>[sub { $_[0]->bgroup; MergeFont(family=>'typewriter'); }],
	       afterDigest=>sub { $_[0]->egroup; },
	       # Since ltx:verbatim is both inline & block, we have to fudge inline mode
	       beforeConstruct=>sub { $_[0]->getModel->canContain($_[0]->getElement,'#PCDATA')
					|| $_[0]->openElement('ltx:p'); },
	       reversion=>'\verb#1#2#1');

# This is defined by the alltt package.
# Environment('alltt', ?);

# Actually, latex sets catcode to 13 ... is this close enough?
DefPrimitiveI('\obeycr',undef,sub {AssignValue('PRESERVE_NEWLINES'=>1);});
DefPrimitiveI('\restorecr',undef,sub {AssignValue('PRESERVE_NEWLINES'=>0);});

DefPrimitiveI('\dospecials',undef,sub {
  map($LaTeXML::STATE->assignCatcode($_=>CC_OTHER,'local'),'^','_','@','~','&','$','#','%',"'",' ');
  return; });
DefMacroI('\normalsfcodes',undef,Tokens());

#**********************************************************************
# C.7 Mathematical Formulas
#**********************************************************************

#======================================================================
# C.7.1 Math Mode Environments
#======================================================================
DefEnvironment('{displaymath}', 
	       "<ltx:equation xml:id='#id'>"
	       . "<ltx:Math mode='display'>"
	       .  "<ltx:XMath>"
	       .   "#body"
	       .  "</ltx:XMath>"
	       . "</ltx:Math>"
	       ."</ltx:equation>",
	       mode=>'display_math',
	       properties=> sub { RefStepID('equation') },
	       locked=>1);
DefEnvironment('{math}',
	       "<ltx:Math mode='inline'>"
	       . "<ltx:XMath>"
	       .  "#body"
	       . "</ltx:XMath>"
	       ."</ltx:Math>",
	       mode=>'inline_math',
	       locked=>1);
DefEnvironment('{equation}',
	       "<ltx:equation xml:id='#id' refnum='#refnum'>"
	       . "<ltx:Math mode='display'>"
	       .  "<ltx:XMath>"
	       .   "#body"
	       .  "</ltx:XMath>"
	       . "</ltx:Math>"
	       ."</ltx:equation>",
	       mode=>'display_math',
	       properties=> sub { RefStepCounter('equation') },
	       locked=>1);
DefEnvironment('{equation*}',
	       "<ltx:equation xml:id='#id'>"
	       . "<ltx:Math mode='display'>"
	       .  "<ltx:XMath>"
	       .   "#body"
	       .  "</ltx:XMath>"
	       . "</ltx:Math>"
	       ."</ltx:equation>",
	       mode=>'display_math',
	       properties=> sub { RefStepID('equation') },
	       locked=>1);

# Define \( ..\) and \[ ... \] to act like environments.
# I would have thought these should be locked, but it seems relatively common to
# redefine them as \left[ \right] and \left( \right) !
DefConstructorI('\[',undef, 
	       "<ltx:equation xml:id='#id'>"
	       . "<ltx:Math mode='display'>"
	       .  "<ltx:XMath>"
	       .   "#body"
	       .  "</ltx:XMath>"
	       . "</ltx:Math>"
	       ."</ltx:equation>",
	       beforeDigest=> sub{ $_[0]->beginMode('display_math'); },
	       captureBody=>1,
	       properties=> sub { RefStepID('equation') });
DefConstructorI('\]'  ,undef, "",beforeDigest=> sub{ $_[0]->endMode('display_math'); });
DefConstructorI('\(' ,undef, 
	       "<ltx:Math mode='inline'>"
	       . "<ltx:XMath>"
	       .  "#body"
	       . "</ltx:XMath>"
	       ."</ltx:Math>",
	       beforeDigest=> sub{ $_[0]->beginMode('inline_math'); },
	       captureBody=>1);
DefConstructorI('\)'   ,undef, "", beforeDigest=> sub{ $_[0]->endMode('inline_math'); });

# Keep from expanding too early, if in alignments, or such.
DefMacroI('\ensuremath', undef,
	  Tokens(T_CS('\protect'),T_CS('\@ensuremath')));
DefMacro('\@ensuremath{}', sub {
  my($gullet,$stuff)=@_;
  if(LookupValue('IN_MATH')){ $stuff->unlist; }
  else { (T_MATH,$stuff->unlist,T_MATH); }});

# Since the arXMLiv folks keep wanting ids on all math, let's try this!
Tag('ltx:Math',afterOpen=>sub { GenerateID(@_,'m'); });

# ========================================
# eqnarray, etal
# Tricky! There's a conflict between a math-level alignment (which
# intermingles non-math things like labels, refnums, intertext),
# and a text-level alignment (which fragments the math's logical structure).
# Our solution is to attempt to synthesize a logical structure of
# an equationgroup containing equations, but using MathFork structures
# to hide the intended aligmnents.
# Then, XSLT can be used in the end to either display in a logical
# (but non-aligned format), or display fully aligned (but hiding the semantics).
#======================================================================
# Equation Groups
#   <equationgroup> representing aligned collections of equations
#   in particular, eqnarray and amsmath's align, ...
# The intended usage is a sequence of patterns like
#     LHS & REL & RHS
# When the LHS is empty, it likely implies a continuation of the previous (or multirelation).
# When both the LHS & REL are empty, it likely implies a continuation of the previous RHS
#======================================================================
# The strategy here is to use the alignment mechanism to construct
# an initial form:
#  <equationgroup>   as the overall container
#    <equation>      for each row; this can receive id, refnum & label attributes
#      <_Capture_>   to capture each columns math.
# After the initial construction (but before any rewriting & parsing)
# we scan through the equations combining the ones that appear (heuristically)
# to be single equations on several rows (either as multi-relations, or
# rhs's of multiple rows)
#  The combinations are represented by a MathFork container which
# holds both the apparently meaningful complete equation, along with
# the rows & columns that show the desired alignment.
# Each of those columns contains a <Math>, thus these can also be parsed,
# and converted to MathML or images. 
#
# Thus, both forms are present: A presentation-oriented stylesheet can
# thus represent the eqnarray by a table with aligned math chunks.
# A content-oriented stylesheet can select the composed meaningful pieces.

# ========================================
# The following set deal with numbering the equations (rows) that make up an equationgroup.
DefPrimitiveI('\@equationgroup@number',   undef, sub { AssignValue(EQUATIONGROUP_NUMBER=>'_AUTO_'); });
DefPrimitiveI('\@equationgroup@nonumber', undef, sub { AssignValue(EQUATIONGROUP_NUMBER=>0); });
DefPrimitiveI('\nonumber',                undef, sub { AssignValue(EQUATIONROW_NUMBER=>0, 'global'); } );

# Alternate versions of alignment open/close@row that deal with numbering.
# Depending on how an eqnarray ends, we might end up with an empty equation, which will be deleted.
# If non-empty, we'll set an id, and perhaps a refnum.
DefPrimitiveI('\@equationgroup@close@row', undef, sub { $_[0]->egroup;} );
DefConstructor('\@equationgroup@open@row SkipSpaces DigestedBody',
	       "",
	       reversion=>'#1',
	       beforeDigest=>sub { $_[0]->bgroup;
				   LookupValue('Alignment')->newRow;
				   AssignValue(EQUATIONROW_NUMBER
					       =>LookupValue('EQUATIONGROUP_NUMBER'),'global');
				   return; },
	       afterDigest=>sub {
 		 my($stomach,$whatsit)=@_;
 		 if($whatsit->getArg(1)->unlist){ # If non-empty row, possibly we'll number
		   my $num = LookupValue('EQUATIONROW_NUMBER');
		   my %props=();
		   if(!$num){               %props = RefStepID('equation'); }
		   elsif($num eq '_AUTO_'){ %props = RefStepCounter('equation'); }
		   else { %props = RefStepID('equation');
			  $props{refnum} = $num; }
		   # GACK: Store the refnum & id in the alignment's row structure!
		   # [wouldn't it be nicer to be using Whatsits inside the alignment?]
		   my $row = LookupValue('Alignment')->currentRow;
		   $$row{id} = $props{id};
		   $$row{refnum} = $props{refnum}; }
		 return; });

# ========================================
# Some special kinds of rows...
DefMacroI('\if@in@firstcolumn',undef, sub {
	   my $x = LookupValue('Alignment');
	   $x = ($x ? $x->currentColumnNumber : 9);
	   ifHandler($_[0],$x < 2); },
	 isConditional=>1);
# A bit more defensiveness, since people abuse eqnarray so badly.
# Eg. &&\lefteqn{...}  whatever?!?!
DefMacro('\lefteqn{}',
	 '\if@in@firstcolumn\multicolumn{3}{l}{$\displaystyle #1$\mbox{}}\else\rlap{$\displaystyle #1$}\fi');
# \intertext (in amsmath)

# ========================================
# eqnarray
DefMacroI('\eqnarray',undef, 
	  '\@eqnarray@bindings\@@eqnarray'
	  .'\@equationgroup@number\@start@alignment',
	 locked=>1);
DefMacroI('\endeqnarray',undef, 
	  '\@finish@alignment\end@eqnarray',
	 locked=>1);
DefMacro('\csname eqnarray*\endcsname',
	 '\@eqnarray@bindings\@@eqnarray\@equationgroup@nonumber\@start@alignment',
	 locked=>1);
DefMacro('\csname endeqnarray*\endcsname',
	 '\@finish@alignment\end@eqnarray',
	 locked=>1);

DefPrimitive('\@eqnarray@bindings', sub {
  eqnarrayBindings(); });

sub eqnarrayBindings {
  my $col1 = {before=>Tokens(T_CS('\hfil'),T_MATH,T_CS('\@hidden@bgroup'),T_CS('\displaystyle')),
	      after=>Tokens(T_CS('\@hidden@egroup'),T_MATH)};
  my $col2 = {before=>Tokens(T_CS('\hfil'),T_MATH,T_CS('\@hidden@bgroup'),T_CS('\displaystyle')),
	      after=>Tokens(T_CS('\@hidden@egroup'),T_MATH,T_CS('\hfil'))};
  my $col3 = {before=>Tokens(T_MATH,T_CS('\@hidden@bgroup'),T_CS('\displaystyle')),
	      after=>Tokens(T_CS('\@hidden@egroup'),T_MATH,T_CS('\hfil'))};
  my $template = LaTeXML::AlignmentTemplate->new(columns=>[$col1,$col2,$col3]);
  my $alignment = LaTeXML::Util::Alignment
    ->new(openContainer =>sub{ $_[0]->openElement('ltx:equationgroup',@_[1..$#_]); },
	  closeContainer=>sub{ $_[0]->closeElement('ltx:equationgroup'); },
	  openRow       =>sub{ $_[0]->openElement('ltx:equation',@_[1..$#_]); },
	  closeRow      =>sub{ $_[0]->closeElement('ltx:equation'); },
	  openColumn    =>sub{ $_[0]->openElement('_Capture_',@_[1..$#_]); },
	  closeColumn   =>sub{ $_[0]->closeElement('_Capture_'); });

  AssignValue(Alignment=>$alignment);
  $alignment->setTemplate($template);
  Let(T_ALIGN,        T_CS('\@alignment@align'));
  Let(T_CS("\\\\"),   T_CS('\@alignment@newline'));
  Let(T_CS('\cr'),    T_CS('\@alignment@cr'));
  Let(T_CS('\@open@row'),T_CS('\@equationgroup@open@row'));
  Let(T_CS('\@close@row'),T_CS('\@equationgroup@close@row'));
  return; }

DefConstructor('\@@eqnarray SkipSpaces DigestedBody',
	       sub {
		 my($document,$body,%props)=@_;
		 eqnarrayRegroupEquations($document,constructAlignment($document,$body,
								      class=>'eqnarray')); },
	       beforeDigest=>sub { $_[0]->bgroup; });
DefPrimitiveI('\end@eqnarray',undef, sub{ $_[0]->egroup; });

# ========================================
# Some tools for analyzing the equationgroup after we've constructed it.
# ========================================

# ========================================
# For eqnarray, the "meaningful" unit will be at least one row,
# but often multiple rows.
# When the 1st column is empty, the row is assumed to continue the previous equation (if any!)
# When the 2nd column is also empty, it presumably continues the previous RHS.
# We'll combine these cases into a single equation, but remember the alignment structure.
# However, if more than 1 such row has refnums, we probably don't want to combine.
# NOTE: intertext are not (yet) handled here; Can they appear?

sub eqnarrayRegroupEquations {
  my($document,$equationgroup)=@_;
  # Scan the "equations" to see what pattern of columns are present.
  my @rows = ();
  foreach my $rownode ($document->getChildElements($equationgroup)){
    my @cells = $document->getChildElements($rownode); # The _Capture_'s representing each column.
    push(@rows,{node=>$rownode, cols=>[@cells],
		L=>($cells[0] && $cells[0]->hasChildNodes),
		M=>($cells[1] && $cells[1]->hasChildNodes),
		R=>($cells[2] && $cells[2]->hasChildNodes),
		numbered=>$rownode->hasAttribute('refnum'),
		labelled=>$rownode->hasAttribute('label') });  }
  my $nL = scalar(grep($$_{L}, @rows));
  my $nM = scalar(grep($$_{M}, @rows));
  my $nR = scalar(grep($$_{R}, @rows));

  # NOTE: A common (mis-)use of eqnarray seems to be to have all the 1st or last 2 columns empty.
  # This amounts to simply a bunch of left aligned equations.
  # However, if some of these start with a relation,
  # presumably it's a single equation. (?)
  # [Another heuristic might be the presence of \left. or \right.?]
  my $t;
  if($nL && !$nM && !$nR){	# All LHS!
    if(grep( ($t=$$_{cols}[0])&&($t=$t->firstChild)&& (($t->getAttribute('role') || '') eq 'RELOP'),
	     @rows)){
      eqnarrayJoinRows($document,$equationgroup,map($$_{node},@rows)); }
    else {
      foreach my $row (@rows){
	equationgroupJoinCols($document,3,$$row{node}); }}
    return; }
  if($nR && !$nM && !$nL){	# All RHS!
    if(grep( ($t=$$_{cols}[2])&&($t=$t->firstChild)&&(($t->getAttribute('role') || '') eq 'RELOP'),
	     @rows)){
      eqnarrayJoinRows($document,$equationgroup,map($$_{node},@rows)); }
    else {
      foreach my $row (@rows){
	equationgroupJoinCols($document,3,$$row{node}); }}
    return; }

  my @eqs = ();
  my $numbered=0;
  my $oddness=0;
  foreach my $row (@rows){
    my $class = 'unknown';
    if($$row{L}){ # 1st column non-empty; Presumably a new "equation"
      $class = 'new'; }
    elsif($$row{M}){ # 1st is empty & 2nd non-empty (Rel?); Probably continues
      if(!scalar(@eqs)){	     # But if no previous equation?
	$class = 'odd'; }
      elsif($numbered && $$row{numbered}){ # Separately numbered continuation?
	$class = 'new';  } # Keep it as separate equation, even though missing LHS
      else {
	$class = 'continue'; }} # Continues as multiequation
    else{			# 1st & 2nd is empty
      if(!scalar(@eqs)){	     # But if no previous equation?
	$class = 'odd'; }
      elsif($numbered && $$row{numbered} # Separately numbered AND labeled?
	 && $$row{labelled}){
	$class = 'odd'; }	# must keep separate, but weird!
      else {
	$class = 'continue'; }} # Else, continues RHS.

    if(($class eq 'new') || ($class eq 'odd')){
      $numbered = $$row{numbered};
      push(@eqs,[$$row{node}]);
      $oddness ++ if $class eq 'odd'; }
    else {
      $numbered |= $$row{numbered};
      push(@{$eqs[$#eqs]},$$row{node}); }
  }

  Warn(":unexpected:<eqnarray> Unrecognized equation patterns ($oddness) in eqnarray")
    if (scalar(@rows) > 1) && $oddness;
  # Now rearrange things appropriately.
  foreach my $eqset (@eqs){
    my @subeqs = @$eqset;
    if(scalar(@subeqs) > 1){    # Combine equations with multiple rows
      eqnarrayJoinRows($document,$equationgroup,@$eqset); }
    else {			# Else fixup columns of single-row equations
      equationgroupJoinCols($document,3,$subeqs[0]); }}
}

# ========================================
# Tools for rearranging equations in equationgroups

# Given a set of rows to be collected into a single equation,
# create the equation w/appropriate attributes, and
# collect the math fragments into a MathFork structure.
sub eqnarrayJoinRows {
  my($document,$equationgroup,@equations)=@_;
  # Make a new equation, with a single MathFork container
  my $equation = addNewChildLate($document,$equationgroup,'ltx:equation');
  $equationgroup->insertBefore($equation,$equations[0]); # Move to correct position.
  # move labels, id, refnum to new equation
  my($labels,$id,$refnum);
  foreach my $eq (@equations){
    if(my $l = $eq->getAttribute('labels')){
      $labels = ($labels ? "$labels $l" : $l); }
    $id     = $eq->getAttribute('xml:id') if $eq->hasAttribute('xml:id');
    $refnum = $eq->getAttribute('refnum') if $eq->hasAttribute('refnum');
  }
  $equation->setAttribute(labels=>$labels) if $labels;
  $equation->setAttribute('xml:id'=>$id) if $id;
  $equation->setAttribute(refnum=>$refnum) if $refnum;

  # Scan equations to see which ones likely are continuations of previous
  my($main,$branch)=addMathFork($document,$equation);
  foreach my $eq (@equations){
    $eq->unbindNode;
    my $tr = addNewChildLate($document,$branch,'ltx:tr');
    foreach my $cell ($document->getChildElements($eq)){
      addColumnToMathFork($document,$main,$tr,$cell); }}
}

# Tricky bit for creating nodes late in the game,
# 
sub addNewChildLate {
  my($document,$node,$tag)=@_;
  my ($nsuri,$localname) = (undef,$tag);
  ($nsuri,$localname)= $document->getModel->decodeQName($tag) unless $tag eq '_Capture_';
  my $newnode = $node->addNewChild($nsuri,$localname);
  # Copy essentials to new node.
  if(my $font = $node->getAttribute('_font')){
    $newnode->setAttribute(_font=>$font); }
  if(my $box = $node->getAttribute('_box')){
    $newnode->setAttribute(_box=>$box); }
  $newnode; }

# Given an equation generated in an equationgroup, 
# collect each $ncols columns into a MathFork structure,
# with the formatted portion being the columns.
sub equationgroupJoinCols {
  my($document,$ncols,$equation)=@_;
  my ($col,$main,$branch)=(0,undef,undef);
  foreach my $cell ($document->getChildElements($equation)){
    next unless $document->getNodeQName($cell) eq '_Capture_';
    if(($col++ % $ncols) == 0){	# Create new MathFork every $ncols cells.
      ($main,$branch)=addMathFork($document,$equation); }
    addColumnToMathFork($document,$main,$branch,$cell); }}

# Use this where you don't want any MathFork stuff.
# It just pulls the contents out of the _Capture_.
sub equationgroupFlattenCols {
  my($document,$equation)=@_;
  my @cells = $document->getChildElements($equation);
  foreach my $cell (@cells){
    foreach my $c ($document->getChildElements($cell)){
      $equation->insertBefore($c,$cell);
    $cell->unbindNode; }}}

# ========================================
# Support for above.

# Add a MathFork to the given equation,
# returning the new nodes for the XMath in the main branch,
# and the 1st alternate branch.
sub addMathFork {
  my($document,$equation)=@_;
  my $fork  = addNewChildLate($document,$equation,'ltx:MathFork');
  my $main  = addNewChildLate($document,$fork,'ltx:Math');
  my $xmath = addNewChildLate($document,$main,'ltx:XMath');
  my $branch= addNewChildLate($document,$fork,'ltx:MathBranch');
  ($main,$branch); }

# Add a td (table column) to $branch in a MathFork,
# and also to the main branch.
sub addColumnToMathFork {
  my($document,$main,$branch,$cell)=@_;
  my $td = addNewChildLate($document,$branch,'ltx:td');
  if(my $align = $cell->getAttribute('align')){
    $td->setAttribute(align=>$align); }
  if(my $colspan = $cell->getAttribute('colspan')){
    $td->setAttribute(colspan=>$colspan); }
  addToMathFork($document,$main,$td,$cell); }

# Here's the really nasty part.
# The content of each cell can end up in the main branch of MathFork
# and in the td's tucked into the MathBranch. Thus, any id's in the math,
# (especially from XMDual/XMArg+XMRef constructs) will be duplicated!
# So, we have to clone the node, and patch up those ids, and refs to them.
# Worse, if any id's are referenced externally, they presumably should
# point to the main MathFork branch!!
#
# Given a _Capture_ node $cell containing math, put it's contents
# into $xcontent (an XMath in the main branch of the MathFork),
# AND add a cloned td to $row (in a MathBranc of the MathFork).
sub cloneMath {
  my($document,$math)=@_;
  my $copy = $math->cloneNode(1);
  my %idmap=();
  # Find all id's defined in the copy and change the id.
  foreach my $node ($document->findnodes('.//*[@xml:id]',$copy)){
    my $id = $node->getAttribute('xml:id');
    # Trust document to bump the id to a unique one!
    my $newid = $document->modifyID($id);
    $document->recordID($newid,$node);
    $node->setAttribute('xml:id'=>$newid);
    $idmap{$id}=$newid; }
  # Now, replace all references to those modified ids.
  foreach my $node ($document->findnodes('.//*[@idref]',$copy)){
    my $id = $node->getAttribute('idref');
    $node->setAttribute(idref=>$idmap{$id}) if $idmap{$id}; }
  $copy; }

sub addToMathFork {
  my($document,$main,$branch,$cell)=@_;
  $cell->unbindNode;
  if(my $math  = $cell->firstChild){
    if(my $xmath = $math->firstChild){
      my $copy = cloneMath($document,$cell);
      # Now, we can add the contents of the cell to the main branch
      foreach my $item ($document->getChildElements($xmath)){
	$main->firstChild->appendChild($item); }
      # And we need to add the tex!!
      # Since each cell was captured in its own Math, it has a valid chunk of tex.
      # We just need to paste them together appropriately.
      if(my $x_tex = $math->getAttribute('tex')){
	my $x_ctex = $math->getAttribute('content-tex') || $x_tex; 
	my $tex = $main->getAttribute('tex') || '';
	my $ctex =$main->getAttribute('content-tex') || $tex;
	$tex = join(' ',grep($_,map((s/^\s*\\displaystyle\s*//&&$_)||$_,$tex,$x_tex)));
	$ctex = join(' ',grep($_,$ctex,$x_ctex));
	$ctex =~ s/\\displaystyle\s*//g;   # strip all \displaystyle
	$main->setAttribute(tex=>'\displaystyle '.$tex);
	$main->setAttribute('content-tex'=>$ctex) unless $ctex eq $tex; }
      # and the modified copy to the alternative branch.
      $branch->appendChild($copy->firstChild); }
  }}

# Style Parameters
#  \abovedisplayskip \abovedisplayshortskip, \jot are in TeX.pool
DefRegister('\mathindent' =>Dimension(0)); 

#======================================================================
# C.7.2 Common Structures
#======================================================================
# sub, superscript and prime are in TeX.pool
# Underlying support in TeX.pool.ltxml
DefConstructor('\frac{}{}',
	       "<ltx:XMApp>"
	       . "<ltx:XMTok meaning='divide' role='MULOP' style='#style'/>"
	       . "<ltx:XMArg>#1</ltx:XMArg><ltx:XMArg>#2</ltx:XMArg>"
	       ."</ltx:XMApp>",
	       beforeDigest=>\&beforeFrac, afterDigest =>\&afterFrac);

# Ellipsis: See TeX.pool

#======================================================================
# C.7.3 Mathematical Symbols
#======================================================================
# See Tables 3.3 through 3.8 (pp 41--44)
# Defined in TeX.pool
# [Possibly some are strictly LaTeX and should be moved here?]

#======================================================================
# C.7.4 Arrays
#======================================================================
#  See Section C.10.2

#======================================================================-
# C.7.5 Delimiters
#======================================================================-
# All this is already in TeX.pool

DefConstructor('\stackrel{}{}',
	       "<ltx:XMApp role='RELOP'>"
	       .  "<ltx:XMTok role='SUPERSCRIPTOP' scriptpos='#scriptpos'/>"
	       .  "<ltx:XMWrap>#2</ltx:XMWrap>"
	       .  "<ltx:XMWrap>#1</ltx:XMWrap>"
	       ."</ltx:XMApp>",
#	       properties=>{scriptpos=>sub{ "mid".$_[1]->getProperty('level'); }}
	       properties=>{scriptpos=>sub{ "mid".$_[0]->getBoxingLevel; }}
);
#======================================================================-
# C.7.6 Putting One Thing Above Another
#======================================================================-
# All this is already in TeX.pool

#======================================================================-
# C.7.7 Spacing
#======================================================================-
# All this is already in TeX.pool

#======================================================================
# C.7.8 Changing Style
#======================================================================
# For Math style changes, we record the current font, which is then merged
# into the Whatsit's created for letters, etc.  The merging depends on
# the type of letter, greek, symbol, etc.
# Apparently, with the normal TeX setup, these fonts don't really merge,
# rather they override all of family, series and shape.

DefConstructor('\mathrm{}', '#1',  bounded=>1, requireMath=>1, font=>{family=>'serif'});
DefConstructor('\mathit{}',  '#1', bounded=>1, requireMath=>1, font=>{shape=>'italic'});
DefConstructor('\mathbf{}',  '#1', bounded=>1, requireMath=>1, font=>{series=>'bold'});
DefConstructor('\mathsf{}',  '#1', bounded=>1, requireMath=>1, font=>{family=>'sansserif'});
DefConstructor('\mathtt{}',  '#1', bounded=>1, requireMath=>1, font=>{family=>'typewriter'});
DefConstructor('\mathcal{}', '#1', bounded=>1, requireMath=>1, font=>{family=>'caligraphic'});
DefConstructor('\mathscr{}', '#1', bounded=>1, requireMath=>1, font=>{family=>'script'});
DefConstructor('\mathnormal{}', '#1', bounded=>1, requireMath=>1,
	       font=>{family=>'math',shape=>'italic'});
DefMacroI('\fontsubfuzz',undef,'.4pt');
DefMacroI('\oldstylenums',undef,Tokens());


#**********************************************************************
# C.8 Definitions, Numbering and Programming
#**********************************************************************

#======================================================================
# C.8.1 Defining Commands
#======================================================================

DefPrimitive('\newcommand OptionalMatch:* DefToken [][]{}', sub {
  my($stomach,$star,$cs,$nargs,$opt,$body)=@_;
  my $key = "defined:".Stringify($cs);
  Error(":unexpected:\\newcommand ".Stringify($cs)." is already defined") if LookupValue($key);
  AssignValue($key=>1);		# only if defined by \newcommand, not \def!
  DefMacroI($cs,convertLaTeXArgs($nargs,$opt),$body); });

DefPrimitive('\CheckCommand OptionalMatch:* DefToken [][]{}', undef);

DefPrimitive('\renewcommand OptionalMatch:* DefToken [][]{}', sub {
  my($stomach,$star,$cs,$nargs,$opt,$body)=@_;
  DefMacroI($cs,convertLaTeXArgs($nargs,$opt),$body); });

DefPrimitive('\providecommand OptionalMatch:* DefToken [][]{}', sub {
  my($stomach,$star,$cs,$nargs,$opt,$body)=@_;
  my $key = "defined:".Stringify($cs);
  return if LookupValue($key);
  DefMacroI($cs, convertLaTeXArgs($nargs,$opt),$body); });

# Need to figure out exactly what `robust' means to LaTeXML...
DefPrimitive('\DeclareRobustCommand OptionalMatch:* DefToken [][]{}', sub {
  my($stomach,$star,$cs,$nargs,$opt,$body)=@_;
  return if LookupDefinition($cs);
  DefMacroI($cs, convertLaTeXArgs($nargs,$opt),$body); });

# There are a bunch of ways in LaTeX to assign a command to
# a particular point within a font (which has one of many encodings)
# Since we have no practical way of knowing what that point is,
# and we really want to create unicode, we just ignore this stuff (but warn).
sub ignoredDefinition {
  my($stomach,$command,$cs)=@_;
  Warn(":unexpected:".ToString($cs)." ignoring ".ToString($command)." definition of ".ToString($cs)); }

DefPrimitive('\DeclareTextAccent{}{}{}',
	     sub { ignoredDefinition('DeclareTextAccent',$_[1]);});
DefPrimitive('\DeclareTextAccentDefault{}{}',
	     sub { ignoredDefinition('DeclareTextAccentDefault',$_[1]);});
DefPrimitive('\DeclareTextCommand{}{}{}',
	     sub { ignoredDefinition('DeclareTextCommand',$_[1]);});
DefPrimitive('\DeclareTextCommandDefault{}[][]{}',
	     sub { ignoredDefinition('DeclareTextCommandDefault',$_[1]);});
DefPrimitive('\ProvideTextCommand{}{}{}',
	     sub { ignoredDefinition('ProvideTextCommand',$_[1]);});
DefPrimitive('\ProvideTextCommandDefault{}[][]{}',
	     sub { ignoredDefinition('ProvideTextCommandDefault',$_[1]);});
DefPrimitive('\DeclareTextComposite{}{}{}{}',
	     sub { ignoredDefinition('DeclareTextComposite',$_[1]);});
DefPrimitive('\DeclareTextCompositeCommand{}{}{}{}',
	     sub { ignoredDefinition('DeclareTextCompositeCommand',$_[1]);});
DefPrimitive('\DeclareTextSymbol{}{}{}',
	     sub { ignoredDefinition('DeclareTextSymbol',$_[1]);});
DefPrimitive('\DeclareTextSymbolDefault{}{}',
	     sub { ignoredDefinition('DeclareTextSymbolDefault',$_[1]);});
DefPrimitive('\UndeclareTextCommand{}{}',undef);
DefPrimitive('\UseTextSymbol{}{}',undef);
DefPrimitive('\UseTextAccent{}{}',undef);

DefPrimitive('\DeclareMathAccent{}{}{}{}',
	     sub { ignoredDefinition('DeclareMathAccent',$_[1]);});
DefPrimitive('\DeclareMathDelimiter{}{}{}{}',
	     sub { ignoredDefinition('DeclareMathAccent',$_[1]);});
DefPrimitive('\DeclareMathRadical{}{}{}{}{}',
	     sub { ignoredDefinition('DeclareMathAccent',$_[1]);});
DefPrimitive('\DeclareMathVersion{}',undef);
DefPrimitive('\DeclarePreloadSizes{}{}{}{}{}',undef);

# The next font declaration commands are based on
# http://tex.loria.fr/general/new/fntguide.html
# we ignore font encoding
DefPrimitive('\DeclareSymbolFont{}{}{}{}{}', sub {
  my ($stomach, $name, $enc, $family, $series, $shape) = @_;
  AssignValue('fontdeclaration@'.ToString($name),
	      {family=>ToString($family),
	       series=>ToString($series),
	       shape =>ToString($shape)}); });
DefPrimitive('\DeclareSymbolFontAlphabet{}{}', sub {
  my ($stomach, $cs, $name) = @_;
  my $font = LookupValue('fontdeclarations@'.ToString($name)) || {};
  DefConstructorI(T_CS(ToString($cs)),undef,'', font=>$font); });

DefPrimitive('\DeclareMathSizes{}{}{}{}',undef);
DefPrimitive('\DeclareMathAlphabet{}{}{}{}{}', sub {
  my ($stomach, $cs, $enc, $family, $series, $shape) = @_;
  DefConstructorI(T_CS(ToString($cs)),undef, '', font=>{family=>ToString($family),
							series=>ToString($series),
							shape =>ToString($shape)}); });
DefMacro('\newmathalphabet{}{}{}',Tokens()); # or expand int DeclareMathAlphabet?
DefPrimitive('\DeclareFontShape{}{}{}{}{}{}', undef);
DefPrimitive('\DeclareFontFamily{}{}{}', undef);
DefPrimitive('\DeclareSizeFunction{}{}', undef);

DefPrimitive('\DeclareMathSymbol{}{}{}{}',
	     sub { ignoredDefinition('DeclareMathSymbol',$_[1]);});

DefPrimitive('\DeclareFixedFont{}{}{}{}{}{}',undef);
DefPrimitive('\DeclareErrorFont{}{}{}{}{}',undef);
DefPrimitive('\DeclareFontEncoding{}{}{}',undef);
DefPrimitive('\DeclareFontSubstitution{}{}{}{}',undef);
DefPrimitive('\DeclareFontEncodingDefaults{}{}',undef);
DefMacroI('\LastDeclaredEncoding',undef,Tokens());

DefPrimitive('\SetSymbolFont{}{}{}{}{}{}',undef);
DefPrimitive('\SetMathAlphabet{}{}{}{}{}{}',undef);
DefPrimitive('\addtoversion{}{}',undef);
DefPrimitive('\TextSymbolUnavailable{}',undef);

#======================================================================
# C.8.2 Defining Environments
#======================================================================
# Note that \env & \endenv defined by \newenvironment CAN be 
# invoked directly.

DefPrimitive('\newenvironment OptionalMatch:* {}[][]{}{}', sub {
  my($stomach,$star,$name,$nargs,$opt,$begin,$end)=@_;
  $name = ToString($name);
  Error(":unexpected:\\newenvironment Environment $name is already defined") if LookupDefinition(T_CS("\\$name"));
  DefMacroI(T_CS("\\$name"), convertLaTeXArgs($nargs,$opt),$begin);
  DefMacroI(T_CS("\\end$name"),undef,$end); });

DefPrimitive('\renewenvironment OptionalMatch:* {}[][]{}{}', sub {
  my($stomach,$star,$name,$nargs,$opt,$begin,$end)=@_;
  $name = ToString($name);
  DefMacroI(T_CS("\\$name"), convertLaTeXArgs($nargs,$opt),$begin);
  DefMacroI(T_CS("\\end$name"),undef,$end); });

#======================================================================
# C.8.3 Theorem-like Environments
#======================================================================
# The core non-customizable part is defined here.
# For customizable theorems, see amsthm.
AssignValue('thm@swap' => 'N');
AssignValue('thm@style'=>'plain');
DefRegister('\thm@headfont'=>Tokens());
DefRegister('\thm@bodyfont'=>Tokens());
DefRegister('\thm@headpunct'=>Tokens());

DefPrimitive('\th@plain', sub {
  AssignValue('\thm@headfont' => T_CS('\bfseries'));
  AssignValue('\thm@bodyfont' => T_CS('\itshape'));
  AssignValue('\thm@headpunct'=> T_OTHER('.'));
  return; });

DefMacroI('\@thmcountersep',undef,'.');

DefMacro('\newtheorem OptionalMatch:* {}[]{}[]', sub {
  my ($stomach, $flag, $thmset, $otherthmset, $text, $reset) = @_;
  $thmset = ToString($thmset);
  $otherthmset = $otherthmset && ToString($otherthmset);
  $text   = undef unless $text->unlist;
  $reset  = $reset ? ToString($reset) : undef;

  my $counter = $otherthmset || $thmset;
  my $swap    = LookupValue('thm@swap') eq 'S';
  my $style   = LookupValue('thm@style');

  if(!$otherthmset){
    NewCounter($counter, $reset,
	       idprefix => "Thm$counter");
    DefMacroI(T_CS("\\the$counter"),undef,
	      "\\csname the$reset\\endcsname\\\@thmcountersep\\arabic{$counter}",
	      scope=>'global')
      if $reset; }

  DefEnvironment("{$thmset} OptionalUndigested",
		 "<ltx:theorem xml:id='#id' refnum='#refnum'>"
		 . "<ltx:title>#title</ltx:title>"
		 .  "#body"
		 ."</ltx:theorem>",

		 beforeDigest=>sub { Digest(Tokens(T_CS('\th@'.$style))); },
		 afterDigestBegin=> sub {
		   my($stomach,$whatsit)=@_;
		   my $note = $whatsit->getArg(1);
		   my $refnum = $whatsit->getProperty('refnum');
		   my($a,$b) = ($swap ? ($refnum,$text) : ($text,$refnum));
		   my @toks=(T_BEGIN,T_CS('\the'),T_CS('\thm@headfont'));
		   push(@toks,
			($a ? $a->unlist :()),
			($a && $b ? (T_SPACE):()),
			($b ? $b->unlist : ()));
		   if($note){
		     push(@toks,T_SPACE,T_OTHER('(')) if $text;
		     push(@toks,$note->unlist);
		     push(@toks,T_OTHER(')')) if $text; }
		   push(@toks,T_CS('\the'),T_CS('\thm@headpunct'),T_END);
		   $whatsit->setProperties(title=>Digest(Tokens(@toks)));

		   Digest(Tokens(T_CS('\the'),T_CS('\thm@bodyfont'))); },
		 ($counter 
		  ? (properties => sub {
		       ($flag ? RefStepID($counter) : RefStepCounter($counter));})
		  :()),
		 scope => 'global');
});


#======================================================================
# C.8.4 Numbering
#======================================================================
# For LaTeX documents, We want id's on para, as well as sectional units.
# However, para get created implicitly on Document construction, rather than
# explicitly during digestion (via a whatsit), we can't use the usual LaTeX counter mechanism.
Tag('ltx:para',afterOpen=>sub { GenerateID(@_,'p'); });
 
DefPrimitive('\newcounter{}[]',sub { shift; NewCounter(map($_ && ToString($_),@_)); return;});

DefPrimitive('\setcounter{}{Number}',sub {
  my($stomach,$ctr,$value)=@_;
  $ctr = ToString($ctr);
  AssignValue('\c@'.$ctr=>$value,'global');
  DefMacroI(T_CS("\\\@$ctr\@ID"),undef, Tokens(Explode($value->valueOf)),scope=>'global');
  return});

DefPrimitive('\addtocounter{}{Number}',sub {
  my($stomach,$ctr,$value)=@_;
  $ctr = ToString($ctr);
  my $v = CounterValue($ctr)->add($value);
  AssignValue('\c@'.$ctr=>$v,'global'); 
  DefMacroI(T_CS("\\\@$ctr\@ID"),undef, Tokens(Explode($v->valueOf)),scope=>'global');
  return; });

DefPrimitive('\stepcounter{}',   sub { StepCounter(ToString($_[1])); return; });
DefPrimitive('\refstepcounter{}',sub { RefStepCounter(ToString($_[1])); return; });

DefPrimitive('\@addtoreset{}{}',sub {
  my($stomach,$ctr,$within)=@_;
  $ctr = ToString($ctr);
  $within = ToString($within);
  my $unctr = "UN$ctr"; # UNctr is counter for generating ID's for UN-numbered items.
  AssignValue("\\cl\@$within" =>
	      Tokens(T_CS($ctr),T_CS($unctr),
		     (LookupValue("\\cl\@$within") ? LookupValue("\\cl\@$within")->unlist :())),
	      'global'); 
  # This counter might be doing double duty generating ID's as well, so we may need to patch up.
  my $prefix = LookupValue('@ID@prefix@'.$ctr);
  if(defined $prefix){
    DefMacroI(T_CS("\\the$ctr\@ID"),undef,
	      "\\expandafter\\ifx\\csname the$within\@ID\\endcsname\\\@empty"
	      ."\\else\\csname the$within\@ID\\endcsname.\\fi"
	      ." $prefix\\csname \@$ctr\@ID\\endcsname",
	      scope=>'global');
    DefMacroI(T_CS("\\\@$ctr\@ID"),undef,"0",scope=>'global'); }
  return; });

DefMacro('\value{}',   sub {  Explode(CounterValue($_[1])->valueOf);});
DefMacro('\@arabic{}', sub {  Explode(LookupValue(ToString($_[1]))->valueOf); });
DefMacro('\arabic{}',  sub {  Explode(CounterValue($_[1])->valueOf); });
DefMacro('\@roman{}',  sub {  roman(LookupValue(ToString($_[1]))->valueOf); });
DefMacro('\roman{}',   sub {  roman(CounterValue($_[1])->valueOf); });
DefMacro('\@Roman{}',  sub {  Roman(LookupValue(ToString($_[1]))->valueOf); });
DefMacro('\Roman{}',   sub {  Roman(CounterValue($_[1])->valueOf); });
DefMacro('\@alph{}',  sub {
  T_OTHER(chr(LookupValue(ToString($_[1]))->valueOf+ord('a')-1));});
DefMacro('\alph{}',  sub {
  T_OTHER(chr(CounterValue($_[1])->valueOf+ord('a')-1));});
DefMacro('\@Alph{}',  sub {
  T_OTHER(chr(LookupValue(ToString($_[1]))->valueOf+ord('A')-1));});
DefMacro('\Alph{}',  sub {
  T_OTHER(chr(CounterValue($_[1])->valueOf+ord('A')-1));});
our @fnsymbols=("*", "\x{2020}", "\x{2021}", UTF(0xA7), UTF(0xB6),
		"\x{2225}", "**", "\x{2020}\x{2020}","\x{2021}\x{2021}");
DefMacro('\@fnsymbol{}',sub { 
		T_OTHER($fnsymbols[LookupValue(ToString($_[1]))->valueOf-1]);});
DefMacro('\fnsymbol{}',sub { 
		T_OTHER($fnsymbols[CounterValue($_[1])->valueOf-1]);});

#======================================================================
# C.8.5 The ifthen Package.
#======================================================================
# \ifthenelse
# and sundry conditionals...
#
# Yeah, maybe this'll get done someday....

#**********************************************************************
# C.9 Figures and Other Floating Bodies
#**********************************************************************

#======================================================================
# C.9.1 Figures and Tables
#======================================================================

# Note that, technically, the number is associated with the caption.
# (to allow multiple figures per figure environment?).
# We'll have to sort that out later....
DefConstructor('\caption[]{}',
	       "<ltx:caption>#2</ltx:caption>\n?#1(<ltx:toccaption>#1</ltx:toccaption>)");
DefEnvironment('{figure}[]',
	       "<ltx:figure xml:id='#id' refnum='#refnum' ?#1(placement='#1')>"
	       . "#body"
	       ."</ltx:figure>",
	       properties=> sub { RefStepCounter('figure') });
DefEnvironment('{figure*}[]',
	       "<ltx:figure xml:id='#id' refnum='#refnum' ?#1(placement='#1')>"
	       . "#body"
	       ."</ltx:figure>",
	       properties=> sub { RefStepCounter('figure') });
DefEnvironment('{table}[]',
	       "<ltx:table xml:id='#id' refnum='#refnum' ?#1(placement='#1')>"
	       . "#body"
	       ."</ltx:table>",
	       properties=> sub { RefStepCounter('table')} );
DefEnvironment('{table*}[]',
	       "<ltx:table xml:id='#id' refnum='#refnum' ?#1(placement='#1')>"
	       . "#body"
	       ."</ltx:table>",
	       properties=> sub { RefStepCounter('table')} );

DefPrimitive('\flushbottom',undef);
DefPrimitive('\suppressfloats[]', undef);

NewCounter('topnumber');
DefMacroI('\topfraction',         undef, "0.25");
NewCounter('bottomnumber');
DefMacroI('\bottomfraction',      undef, "0.25");
NewCounter('totalnumber');
DefMacroI('\textfraction',        undef, "0.25");
DefMacroI('\floatpagefraction',   undef, "0.25");
NewCounter('dbltopnumber');
DefMacroI('\dbltopfraction',      undef, "0.7");
DefMacroI('\dblfloatpagefraction',undef, "0.25");
DefRegister('\floatsep'     => Glue(0));
DefRegister('\textfloatsep' => Glue(0));
DefRegister('\intextsep'    => Glue(0));
DefRegister('\dblfloatsep'  => Glue(0));
DefRegister('\dbltextfloatsep' => Glue(0));
DefRegister('\@maxsep'      => Dimension(0));
DefRegister('\@dblmaxsep'   => Dimension(0));
DefRegister('\@fptop'      => Glue(0));
DefRegister('\@fpsep'      => Glue(0));
DefRegister('\@fpbot'      => Glue(0));
DefRegister('\@dblfptop'   => Glue(0));
DefRegister('\@dblfpsep'   => Glue(0));
DefRegister('\@dblfpbot'   => Glue(0));
Let(T_CS('\topfigrule'),T_CS('\relax'));
Let(T_CS('\botfigrule'),T_CS('\relax'));
Let(T_CS('\dblfigrule'),T_CS('\relax'));

DefMacroI('\figurename', undef, 'Figure'); # Never used?
DefMacroI('\figuresname',undef, 'Figures'); # Never used?
DefMacroI('\tablename',  undef, 'Table'); # Never used?
DefMacroI('\tablesname', undef, 'Tables');

#======================================================================
# C.9.2 Marginal Notes
#======================================================================

DefConstructor('\marginpar[]{}', "<ltx:note class='margin'>#2</ltx:note>");
DefPrimitiveI('\reversemarginpar',undef, undef);
DefPrimitiveI('\normalmarginpar', undef, undef);
DefRegister('\marginparpush',Dimension(0));

#**********************************************************************
# C.10 Lining It Up in Columns
#**********************************************************************

#======================================================================
# C.10.1 The tabbing Environment
#======================================================================
DefRegister('\tabbingsep'=>Dimension(0));

DefMacroI('\tabbing', undef,
	'\@tabbing@bindings\@@tabbing\@start@alignment');
DefMacroI('\endtabbing',undef,
	  '\@finish@alignment\@end@tabbing');
DefPrimitiveI('\@end@tabbing', undef, sub{ $_[0]->egroup; });
DefConstructor('\@@tabbing SkipSpaces DigestedBody',
	       sub { my($document,$body,%props)=@_;
		     constructAlignment($document,$body); },
	       reversion=>'\begin{tabbing}#1\end{tabbing}',
	       beforeDigest=>sub { $_[0]->bgroup; },
	       mode=>'text');

DefMacroI('\@tabbing@tabset',undef, 
	  '\@close@inner@column\@close@column'
	  .'\@tabbing@tabset@marker'
	  .'\@open@column\@open@inner@column');
DefConstructorI('\@tabbing@tabset@marker',undef, '',reversion=>'\=');
DefMacroI('\@tabbing@nexttab',undef, 
	  '\@close@inner@column.\@close@column'
	  .'\@tabbing@nexttab@marker'
	  .'\@open@column\@open@inner@column');
DefConstructorI('\@tabbing@nexttab@marker',undef, '',reversion=>'\>');

DefMacro('\@tabbing@newline OptionalMatch:* [Dimension]',
	 '\@close@inner@column\@close@column\@close@row'
	 .'\@tabbing@newline@marker'
	 .'\@open@row\@open@column\@open@inner@column\@tabbing@start@tabs');
DefConstructorI('\@tabbing@newline@marker',undef, '',reversion=>Tokens(T_CS("\\\\"),T_CR));

DefMacroI('\@tabbing@kill',undef, 
	  '\@close@inner@column\@close@column\@close@row'
	  .'\@tabbing@kill@marker'
	  .'\@open@row\@open@column\@open@inner@column\@tabbing@start@tabs');
DefConstructorI('\@tabbing@kill@marker',undef, '',reversion=>'\kill',
	       afterDigest=>sub { LookupValue('Alignment')->removeRow; return; });
AssignValue(tabbing_start_tabs=>Tokens());
DefMacroI('\@tabbing@start@tabs',undef, sub { LookupValue('tabbing_start_tabs')->unlist; });
DefPrimitiveI('\@tabbing@increment',undef, sub {
  my @tabs = LookupValue('tabbing_start_tabs')->unlist;
  AssignValue(tabbing_start_tabs=>Tokens(@tabs,T_CS('\>')),'global'); });
DefPrimitiveI('\@tabbing@decrement',undef, sub {
  my ($ignore,@tabs) = LookupValue('tabbing_start_tabs')->unlist;
  AssignValue(tabbing_start_tabs=>Tokens(@tabs),'global'); });

# NOTE: \< is NOT currently handled!!!
# Ugh!! The way we're setting the initial tabs, we can't really handle this!
DefPrimitiveI('\@tabbing@untab',undef, undef);

# NOTE: \' and \` are NOT currently handled
DefPrimitiveI('\@tabbing@flushright',undef, undef);
DefPrimitiveI('\@tabbing@hfil',      undef, undef);
# NOTE: \pushtabs and \poptabs are NOT currently handled.
DefPrimitiveI('\@tabbing@pushtabs',  undef, undef);
DefPrimitiveI('\@tabbing@poptabs',   undef, undef);

sub tabbingBindings {
#  my $alignment = LaTeXML::Util::Alignment->new(containerElement=>'ltx:tabular',
#						rowElement=>'ltx:tr',
#						colElement=>'ltx:td');
  my $alignment = LaTeXML::Util::Alignment
    ->new(openContainer =>sub{ $_[0]->openElement('ltx:tabular',@_[1..$#_]); },
	  closeContainer=>sub{ $_[0]->closeElement('ltx:tabular'); },
	  openRow       =>sub{ $_[0]->openElement('ltx:tr',@_[1..$#_]); },
	  closeRow      =>sub{ $_[0]->closeElement('ltx:tr'); },
	  openColumn    =>sub{ $_[0]->openElement('ltx:td',@_[1..$#_]); },
	  closeColumn   =>sub{ $_[0]->closeElement('ltx:td'); });

  my $template = LaTeXML::AlignmentTemplate->new(repeated=>[{after=>Tokens(T_CS('\hfil'))}]);
  $alignment->setTemplate($template);
  AssignValue(Alignment=>$alignment);
  Let(T_CS("\\="),       T_CS('\@tabbing@tabset'));
  Let(T_CS("\\>"),       T_CS('\@tabbing@nexttab'));
  Let(T_CS("\\\\"),      T_CS('\@tabbing@newline'));
  Let(T_CS('\kill'),     T_CS('\@tabbing@kill'));
  Let(T_CS("\\+"),       T_CS('\@tabbing@increment'));
  Let(T_CS("\\-"),       T_CS('\@tabbing@decrement'));
  Let(T_CS("\\<"),       T_CS('\@tabbing@untab'));
  Let(T_CS("\\'"),       T_CS('\@tabbing@flushright'));
  Let(T_CS("\\`"),       T_CS('\@tabbing@hfil'));
  Let(T_CS('\pushtabs'), T_CS('\@tabbing@pushtabs'));
  Let(T_CS('\poptabs'),  T_CS('\@tabbing@poptabs'));
#  Let(T_CS('\a'),        T_CS('\@tabbing@accent'));
  Let(T_CS('\@open@row'),T_CS('\default@open@row'));
  Let(T_CS('\@close@row'),T_CS('\default@close@row'));

  return; }

DefMacroI('\pushtabs',undef,Tokens());
DefMacroI('\poptabs',undef,Tokens());
DefMacroI('\kill',undef,Tokens());

DefPrimitiveI('\@tabbing@bindings',undef, sub {
  tabbingBindings(); });

# NOTE: Do it!!

#======================================================================
# C.10.2 The array and tabular Environments
#======================================================================
# Tabular are a bit tricky in that we have to arrange for tr and td to
# be openned and closed at the right times; the only real markup is
# the & and \\. Also \multicolumn has to be cooperative.
# Along with this, we have to track which column specification applies
# to the current column.
# To simulate LaTeX's tabular borders & hlines, we simply add border
# attributes to all cells.  For HTML, CSS will be necessary to display them.
# [We'll ignore HTML's frame, rules and colgroup mechanisms.]

# The Core alignment support is in LaTeXML::Util::Alignment and in TeX.ltxml
DefMacro('\tabular[]{}',  '\@alignment@bindings{#2}[text]\@@tabular[#1]{#2}\@start@alignment');
DefMacroI('\endtabular',undef,    '\@finish@alignment\@end@tabular');
DefPrimitiveI('\@end@tabular',undef, sub{ $_[0]->egroup; });
# Note that the pattern will already have been interpreted by \@alignment@bindings,
# so make it Undigested here!

sub translateAttachment {
  my($pos)=@_;
  $pos = ($pos ? ToString($pos) : '');
  ($pos eq 't' ? 'top' : ($pos eq 'b' ? 'bottom' : undef)); } # undef meaning 'middle'

DefConstructor('\@@tabular[] Undigested DigestedBody',
	       sub { my($document,$pos,$pat,$body,%props)=@_;
		     constructAlignment($document,$body,
					attributes=>{vattach=>translateAttachment($pos)},
					guess_headers=>1); },
	       reversion=>'\begin{tabular}[#1]{#2}#3\end{tabular}',
	       beforeDigest=>sub { $_[0]->bgroup; },
	       mode=>'text');
DefMacro('\csname tabular*\endcsname{Dimension}[]{}',
	 '\@alignment@bindings{#3}[text]\@@tabular@{#1}[#2]{#3}\@start@alignment');
DefMacro('\csname endtabular*\endcsname',
	 '\@finish@alignment\@end@tabular@');
DefConstructor('\@@tabular@{Dimension}[] Undigested DigestedBody',
	       sub { my($document,$width,$pos,$pat,$body,%props)=@_;
		     constructAlignment($document,$body,
					attributes=>{vattach=>translateAttachment($pos),
						     width=>$width},
					guess_headers=>1); },
	       beforeDigest=>sub { $_[0]->bgroup; },
	       reversion=>'\begin{tabular*}{#1}[#2]{#3}#4\end{tabular*}',
	       mode=>'text');
DefPrimitive('\@end@tabular@',sub{ $_[0]->egroup; });
Let(T_CS('\multicolumn'),T_CS('\@multicolumn'));

DefPrimitiveI('\hline',undef, undef);	# Redefined inside tabular

DefConstructor('\cline{}','',
	       afterDigest=>sub {
		 my($stomach,$whatsit)=@_;
		 my $cols = ToString($whatsit->getArg(1));
		 my @cols=();
		 while($cols=~ s/^,?(\d+)//){
		   my $n = $1;
		   push(@cols, ($cols =~ s/^-(\d+)// ? ($n..$1) : ($n))); }
		 my $alignment = LookupValue('Alignment');
		 $alignment->addLine('t',@cols) if $alignment;
		 return; },
	       properties=>{isHorizontalRule=>1});

DefConstructorI('\vline',undef, "",	# ???
	       properties=>{isVerticalRule=>1},);
DefRegister('\arraycolsep',   Dimension(0));
DefRegister('\tabcolsep',     Dimension(0));
DefRegister('\arrayrulewidth',Dimension(0));
DefRegister('\doublerulesep', Dimension(0));
DefMacro('\extracolsep{}',Tokens());
DefMacroI('\arraystretch',undef, "1");
DefMacroI('\tabularnewline',undef,Tokens());

#======================================================================
# Array and similar environments

DefMacro('\array[]{}',
	 '\@alignment@bindings{#2}\@@array[#1]{#2}\@start@alignment');
DefMacroI('\endarray',undef, 
	  '\@finish@alignment\@end@array');
DefPrimitiveI('\@end@array',undef, sub{ $_[0]->egroup; });
DefConstructor('\@@array[] Undigested DigestedBody',
	       sub { my($document,$pos,$pat,$body,%props)=@_;
		     constructAlignment($document,$body,
					attributes=>{vattach=>translateAttachment($pos),
						     role=>'ARRAY'}); },
	       beforeDigest=>sub { $_[0]->bgroup; },
	       reversion=>'\begin{array}[#1]{#2}#3\end{array}');

#**********************************************************************
# C.11 Moving Information Around
#**********************************************************************

#======================================================================
# C.11.1 Files
#======================================================================
DefPrimitive('\nofiles',undef);

#======================================================================
# C.11.2 Cross-References
#======================================================================

# \label attaches a label to the nearest parent that can accept a labels attribute
# Note that latex essentially allows redundant labels, but we can record only one!!!
# No; next version. Find nearest that can accept a refnum attribute.
# Or, better yet (more latex-like??) nearest that HAS a refnum!?!
DefConstructor('\label Semiverbatim',  sub {
 		 my($document,$olabel,%props)=@_;
# 		 $label = CleanLabel($label);
		 my $label = $props{label};
##		 my $savenode = $document->floatToAttribute('refnum');
 		 if(my $savenode = $document->floatToAttribute('labels')){
		   my $node = $document->getNode;
		   my $prev = $node->getAttribute('labels');
		   $document->setAttribute($node,labels=>($prev ? $prev." ".$label : $label)); 
		   $document->setNode($savenode); }},
###	       "^ labels='#label'",
	       reversion=>'',
	       properties=>{alignmentSkippable=>1},
	       afterDigest=>sub {
#		 my $label = ToString($_[1]->getArg(1));
		 my $label = CleanLabel(ToString($_[1]->getArg(1)));
		 $_[1]->setProperty(label=>$label);
		 my $scope = $label; $scope =~ s/^LABEL:/label:/;
		 if(my $ctr = LookupValue('current_counter')){
		   unshift(@{LookupValue('scopes_for_counter:'.$ctr)},$scope);
		   $STATE->activateScope($scope);
		   $_[0]->beginMode('text');
		   AssignValue('LABEL@'.$label, Digest(T_CS('\@currentlabel')),'global');
		   $_[0]->endMode('text'); }
		 return; });

# These will get filled in during postprocessing.
# * is added to accommodate hyperref
DefConstructor('\ref OptionalMatch:* Semiverbatim', "<ltx:ref labelref='#label'/>",
	      properties=>sub { (label=>CleanLabel($_[2])); });
DefConstructor('\pageref OptionalMatch:* Semiverbatim', "<ltx:ref labelref='#label'/>", # Same??
	      properties=>sub { (label=>CleanLabel($_[2])); });
#======================================================================
# C.11.3 Bibliography and Citation
#======================================================================

DefMacroI('\bibname',undef, 'Bibliography');

DefMacroI('\thebibliography@ID',undef, Tokens());
# NOTE: Not really complete, since I haven't dealt with bibliographies!
DefMacro('\bibliography Semiverbatim', '\ifx.#1.\else\@bibliography{#1}\fi');
DefConstructor('\@bibliography Semiverbatim',
	       "<ltx:bibliography files='#1' xml:id='#id'/>",
	       afterDigest=>sub { 
		 my $docid = ToString(Expand(T_CS('\thedocument@ID')));
		 DefMacroI(T_CS('\thebibliography@ID'),undef,($docid ? "$docid.bib" : 'bib'));
		 $_[1]->setProperty(id=>ToString(Expand(T_CS('\thebibliography@ID')))); });
DefMacroI('\refname',undef, 'References');
DefPrimitive('\bibstyle{}', undef);
DefMacro('\bibliographystyle{}', '\bibstyle{#1}');

# Should be an environment, but people seem to want to misuse it.
DefConstructorI('\thebibliography',undef,
	       "<ltx:bibliography xml:id='#id'>"
	       . "<ltx:title>#refname</ltx:title>"
	       . "<ltx:biblist>",
	       beforeDigest=> sub { AssignValue(inPreamble=>0); },
	       afterDigest=>sub { 
		 my($stomach)=@_;
		 # NOTE that in some perverse situations (revtex?)
		 # it seems to be allowable to omit the argument
		 # It's ignorable for latexml anyway, so we'll just read it if its there.
		 my $gullet = $stomach->getGullet;
		 $gullet->skipSpaces;
		 $gullet->readArg if $gullet->ifNext(T_BEGIN);
#		 $gullet->skipSpaces; # And AFTER to avoid prematurely triggering \par!!!
		 my $docid = ToString(Expand(T_CS('\thedocument@ID')));
		 ResetCounter('enumiv');
		 DefMacroI(T_CS('\thebibliography@ID'),undef,($docid ? "$docid.bib" : 'bib'));
		 $_[1]->setProperty(id=>ToString(Expand(T_CS('\thebibliography@ID'))));
		 $_[1]->setProperty(refname=>Digest(T_CS('\refname')));  
		 # Fix for missing \bibitems!
		 setupPseudoBibitem(); },
	      locked=>1);

# Close the bibliography
DefConstructorI('\endthebibliography',undef,
	       "</ltx:biblist></ltx:bibliography>",
	       afterDigest=>sub { Digest(T_CS('\appendix')); },
	      locked=>1);
# auto close the bibliography and contained biblist.
Tag('ltx:biblist',autoClose=>1);
Tag('ltx:bibliography',autoClose=>1);

# Since SOME people seem to write bibliographies w/o \bibitem,
# just blank lines between apparent entries,
# Making \par do a \bibitem{} works, but screws up valid
# bibliographies with blank lines!
# So, let's do some redirection!
sub setupPseudoBibitem {
  Let(T_CS('\save@bibitem'),T_CS('\bibitem'));
  Let(T_CS('\save@par'),T_CS('\par'));
  Let(T_CS('\bibitem'),T_CS('\restoring@bibitem'));
  Let(T_CS('\par'),T_CS('\par@in@bibliography'));
  # Moreover, some people use \item instead of \bibitem
  Let(T_CS('\item'),T_CS('\item@in@bibliography'));
  }

DefMacroI('\par@in@bibliography',undef, sub {
  my($gullet)=@_;
  $gullet->skipSpaces;
  my $tok = $gullet->readToken;
  # If next token is another \par, or a REAL \bibitem, 
  if(Equals($tok,T_CS('\par')) || Equals($tok,T_CS('\bibitem'))){
    ($tok); }			# then this \par expands into what followed
  else {			# Else, put it back, and start a bibitem.
    $gullet->unread($tok);
    (T_CS('\save@bibitem'),T_BEGIN,T_END); }});

DefMacroI('\item@in@bibliography', undef, '\save@bibitem{}');

# If we hit a real \bibitem, put \par & \bibitem back to correct defn, and then \bibitem.
# A bibitem with now key or label...
DefMacro('\restoring@bibitem',
	 '\let\bibitem\save@bibitem\let\par\save@par\bibitem');

NewCounter('@bibitem','bibliography',idprefix=>'bib');
DefMacroI('\the@bibitem',undef, '\arabic{@bibitem}');
DefConstructor('\bibitem[] Semiverbatim',
	       "<ltx:bibitem key='#key' xml:id='#id'>"
	       .  "<ltx:bibtag role='refnum'>#refnum</ltx:bibtag>"
	       .  "<ltx:bibblock>",
	       afterDigest=>sub {
		 my $tag = $_[1]->getArg(1);
		 my $key = CleanBibKey($_[1]->getArg(2));
		 $_[1]->setProperties(key=>$key,
				      ($tag ? (RefStepID('@bibitem'),refnum=>$tag) : RefStepCounter('@bibitem'))); });
DefConstructorI('\newblock',undef, "<ltx:bibblock>");
Tag('ltx:bibitem', autoClose=>1);
Tag('ltx:bibblock', autoClose=>1);

#----------------------------------------------------------------------
# We've got the same problem as LaTeX: Lather, Rinse, Repeat.
# It would be nice to know the bib info at digestion time
#  * whether author lists will collapse
#  * whether there are "a","b".. extensions on the year.
# We could process the bibliography first, (IF it is a separate *.bib!)
# but won't know which entries are included (and so can't resolve the a/b/c..)
# until we've finished looking at (all of) the source(s) that will refer to them!
#
# We can do this in 2 passes, however
#  (1) convert (latexml) both the source document(s) and the bibliography
#  (2) extract the required bibitems and integrate (latexmlpost) it into the documents.
# [Note that for mult-document sites, step (2) becomes 2 stages: scan and integrate]
#
# Here's the general layout.
#   <ltx:cite> contains everything that the citations produce,
#     including parens, pre-note, punctunation that precede the <ltx:bibcite>
#     and punctuation, post-note, parens, that follow it.
#   <ltx:bibcite show="string" bibrefs="keys" sep="" yysep="">phrases</ltx:bibcite>
#     encodes the actual citation.
#
#     bibrefs : lists the bibliographic keys that will be used
#     show    : gives the pattern for formatting using data from the bibliography
#       It can contain:
#         authors or fullauthors
#         year
#         number
#         phrase1,phrase2,... selects one of the phrases from the content of the <ltx:bibref>
#     This format is used as follows:
#       If author and year is present, and a subset of the citations share the same authors,
#         then the format is used, but the year is repeated for each citation in the subset,
#         as a link to the bib entry.
#       Otherwise, the format is applied to each entry.
#
# The design is intended to support natbib, as well as plain LaTeX.

AssignValue(CITE_OPEN=>T_OTHER('['));
AssignValue(CITE_CLOSE=>T_OTHER(']'));
AssignValue(CITE_SEPARATOR=>T_OTHER(','));
AssignValue(CITE_YY_SEPARATOR=>T_OTHER(','));
AssignValue(CITE_NOTE_SEPARATOR=>T_OTHER(','));

DefConstructor('\@@cite{}',"<ltx:cite>#1</ltx:cite>",
	       mode=>'text');

DefConstructor('\@@bibref{} Semiverbatim {}{}',
	       "<ltx:bibref show='#1' bibrefs='#bibrefs'"
	       .          " separator='#separator' yyseparator='#yyseparator'>#3#4</ltx:bibref>",
	      properties=>sub { (bibrefs=>CleanBibKey($_[2]),
				 separator=>ToString(Digest(LookupValue('CITE_SEPARATOR'))),
				 yyseparator=>ToString(Digest(LookupValue('CITE_YY_SEPARATOR')))); });
DefConstructor('\@@citephrase{}',"<ltx:text _noautoclose='1'>#1</ltx:text>",
	       mode=>'text');

DefMacro('\cite[] Semiverbatim', sub {
  my($gullet,$post,$keys)=@_;
  my($open,$close,$ns)=map(LookupValue($_),qw(CITE_OPEN CITE_CLOSE CITE_NOTE_SEPARATOR));
  $post = undef unless $post && $post->unlist;
  Invocation(T_CS('\@@cite'),
	     Tokens($open,
		    Invocation(T_CS('\@@bibref'),T_OTHER("Number"),$keys, undef,undef),
		    ($post ? ($ns,T_SPACE,$post):()),$close)); });

# NOTE: Eventually needs to be recognized by MakeBibliography
DefConstructor('\nocite Semiverbatim', '');

#======================================================================
# C.11.4 Splitting the input
#======================================================================
Let(T_CS('\@@input'),T_CS('\input'));	# Save TeX's version.
# LaTeX's \input is a bit different...
DefPrimitive('\input', sub {
  my($stomach)=@_;
  my $gullet=$stomach->getGullet;
  my $path;
  if($gullet->ifNext(T_BEGIN)){ # Read LaTeX style
    $path=Expand($gullet->readArg); }
  else {			# Read TeX style.
    my ($token,@tokens)=();
    while(($token = $gullet->readXToken) && ($token->getCatcode != CC_SPACE)){
      push(@tokens,$token); }
    $path = Tokens(@tokens); }
  $path = ToString($path);
  # HEURISTIC! First check if equivalent style file, but only IFF we are in preamble
  my ($dir,$name,$type) = pathname_split($path);
  my $newpath;
  if((! $dir) && (!$type || ($type eq 'tex')) # Attempt to load raw TeX
     && (LookupValue('inPreamble') || !FindFile($name)) # And should/could be style file
     && ($newpath=FindFile($name,type=>'sty'))){	# and there IS such a style file
    Info(":override Overriding input of $path with $newpath");
    RequirePackage($name); }
  elsif(my $file = FindFile($path)){
    $gullet->input($file); }
  else {
    $STATE->noteStatus(missing=>$path);
    Error(":missing_file:$path Cannot find file $path in paths "
	  .join(', ',@{$STATE->lookupValue('SEARCHPATHS')})); }
  return; });

# Note that even excluded files SHOULD have the effects of their inclusion
# simulated by having read the corresponding aux file;
# But we're not bothering with that.
DefPrimitive('\include{}',sub { 
  my($stomach,$path)=@_;
  $path = ToString($path);
  my $table = LookupValue('including@only');
  if(!$table || $$table{$path}){
    if(my $file = FindFile($path)){
      $stomach->getGullet->input($file); }
    else {
      $STATE->noteStatus(missing=>$path);
      Error(":missing_file:$path Cannot find file $path in paths "
	    .join(', ',@{$STATE->lookupValue('SEARCHPATHS')})); }}
  return; });

# [note, this will load name.tex, if it exists, else name]
DefPrimitive('\includeonly{}',sub {
  my($stomach,$paths)=@_;
  $paths = ToString($paths);
  my $table = LookupValue('including@only');
  AssignValue('including@only', $table = {}, 'global') unless $table;
  map( $$table{$_}=1, split(/,\s*/,$paths));
  return; });

#Environment('filecontents','{}',?
#Environment('filecontents*','{}',?

DefPrimitive('\listfiles',undef);

#======================================================================
# C.11.5 Index and Glossary
#======================================================================

# ---- The index commands
# Format of Index entries:
#   \index{entry!entry}  gives multilevel index
# Each entry:
#   foo@bar  sorts on "foo" but prints "bar"
# The entries can end with a |expression:
#   \index{...|(}    this page starts a range for foo
#   \index{...|)}    this page ends a range
#           The last two aren't handled in any particular way.
#           We _could_ mark start & end, and then the postprocessor would
#           need to fill in all likely links... ???
#   \index{...|see{key}}  cross reference.
#   \index{...|textbf}  (etc) causes the number to be printed in bold!
#
# I guess the formula is that
#    \index{foo|whatever{pi}{pa}{po}}  => \whatever{pi}{pa}{po}{page}
# How should this get interpreted??
our %index_style = (textbf=>'bold', bf=>'bold', textrm=>'', rm=>'',
		    textit=>'italic', it=>'italic', emph=>'italic'); # What else?
# A bit screwy, but....
# Expand \index{a!b!...} into \@index{\@indexphrase{a}\@indexphrase{b}...}
sub process_index_phrases {
  my($gullet,$phrases)=@_;
  my @expansion = ();
  # Split the text into phrases, separated by "!"
  my @tokens = $phrases->unlist;
  push(@tokens,T_OTHER('!')) unless $tokens[$#tokens]->getString eq '!'; # Add terminal !
  my @phrase=();
  my @sortas=();
  my $style;
  while(@tokens){
    my $tok = shift(@tokens);
    my $string = $tok->getString;
    if($string eq '"'){
      push(@phrase,shift(@tokens)); }
    elsif($string eq '@'){
      while(@phrase && ($phrase[$#phrase]->getString =~ /\s/)){ pop(@phrase); }	# Trim
      @sortas=@phrase; @phrase=(); }
    elsif(($string eq '!') || ($string eq '|')){
      while(@phrase && ($phrase[$#phrase]->getString =~ /\s/)){ pop(@phrase); }	# Trim
      push(@expansion,T_CS('\@indexphrase'),
	   (@sortas ? (T_OTHER('['),@sortas,T_OTHER(']')) :()),
	   T_BEGIN,@phrase,T_END)
	if @phrase;
      @phrase=(); @sortas=();
      if($string eq '|'){
	pop(@tokens);		# Remove the extra "!" stopbit.
	my $extra = ToString(Tokens(@tokens));
	if($extra =~ /^see/){
	  push(@expansion,T_CS('\@indexsee'),@tokens[3..$#tokens]); }
	elsif($extra eq '('){}	# ?
	elsif($extra eq ')'){}	# ?
	else { $style = $index_style{$extra} || $extra; }
	@tokens=(); }}
    elsif(!@phrase && ($string =~ /\s/)){}	# Skip leading whitespace
    else { 
      push(@phrase,$tok); }}
  @expansion = (T_CS('\@index'),
		($style ? (T_OTHER('['),T_OTHER($style),T_OTHER(']')):()),
		T_BEGIN,@expansion,T_END);
  @expansion; }

DefMacro('\index{}', \&process_index_phrases);
# NOTE: ignore \glossary for now (the arg is like index entry)
DefMacro('\glossary{}', Tokens());

DefConstructor('\@index[]{}',"^<ltx:indexmark style='#1'>#2</ltx:indexmark>",
	       mode=>'text', reversion=>'');
DefConstructor('\@indexphrase[]{}',
	       "<ltx:indexphrase key='#key'>#2</ltx:indexphrase>",
	       properties=>{key=>sub { CleanIndexKey($_[1]); }});
DefConstructor('\@indexsee{}',
	       "<ltx:indexphrase see_also_marker='1'>#1</ltx:indexphrase>");

Tag('ltx:indexphrase', afterClose=>\&addIndexKey);
sub addIndexKey {
  my($document,$node)=@_;
  # Do something about See ????
  # [Presumably compute key and add it to the index element and delete]
  # Or introduce a special element?
  if($node->getAttribute('see_also_marker')){
    my $key = $node->getAttribute('key') || indexify($node,$document);
    my $indexmark = $node->parentNode;
    $document->setAttribute($indexmark,see_also=>$key);
    $indexmark->removeChild($node); }
  elsif(!$node->getAttribute('key')) {
    $document->setAttribute($node,key=>indexify($node,$document)); }}

#======================================================================
# This converts an indexphrase node into a sortable string.
# Seems the XML nodes are the best place to handle it (rather than Boxes),
# although some of the special cases (see, @, may end up tricky)
sub indexify {
  my($node,$document)=@_;
  my $type = $node->nodeType;
  if($type == XML_TEXT_NODE){
    my $string = $node->textContent; 
    $string =~ s/\W//g;		# to be safe (if perhaps non-unique?)
    $string =~ s/\s//g;		# Or remove entirely? Eventually worry about many=>1 mapping???
#    lc($string); }		# Worry..
    $string;}
  elsif($type == XML_ELEMENT_NODE){
    if($document->getModel->getNodeQName($node) eq 'ltx:Math'){
      indexify_tex($node->getAttribute('tex'));}
    else {
      join('',map(indexify($_,$document),$node->childNodes)); }}
  elsif($type == XML_DOCUMENT_FRAG_NODE){
    join('',map(indexify($_,$document),content_nodes($node))); }
  else { ""; }}

# Try to clean up a TeX string into something
# Could walk the math tree and handle XMDual specially, but need to xref args.
# But also we'd have unicode showing up, which we'd like to latinize...
sub indexify_tex {
  my($string)=@_;
  $string =~ s/(\\\@|\\,|\\:|\\;|\\!|\\ |\\\/|)//g;
  $string =~ s/(\\mathrm|\\mathit|\\mathbf|\\mathsf|\\mathtt|\\mathcal|\\mathscr|\\mbox|\\rm|\\it|\\bf|\\tt|\\small|\\tiny)//g;
  $string =~ s/\\left\b//g;  $string =~ s/\\right\b//g;
  $string =~ s/(\\|\{|\})//g;
  $string =~ s/\W//g;		# to be safe (if perhaps non-unique?)
  $string =~ s/\s//g;		# Or remove entirely? Eventually worry about many=>1 mapping???
#  lc($string); }		# Worry..
  $string; }

# ---- Creating the index itself

AssignValue(INDEXLEVEL=>0);

Tag('ltx:indexentry', autoClose=>1);
sub closeIndexPhrase {
  my($document)=@_;
  if($document->isCloseable('ltx:indexphrase')){
    $document->closeElement('ltx:indexphrase'); }}

sub doIndexItem {
  my($document,$level)=@_;
  $document->closeElement('ltx:indexrefs') if $document->isCloseable('ltx:indexrefs');
  closeIndexPhrase($document);
  my $l = LookupValue('INDEXLEVEL');
  while($l < $level){
    $document->openElement('ltx:indexlist'); $l++; }
  while($l > $level){
    $document->closeElement('ltx:indexlist'); $l--;}
  AssignValue(INDEXLEVEL=>$l);
  if($level){
    $document->openElement('ltx:indexentry');
    $document->openElement('ltx:indexphrase'); }}

DefConstructorI('\index@dotfill',   undef, sub {
  my($document)=@_;
  closeIndexPhrase($document);
  $document->openElement('ltx:indexrefs'); });
DefConstructorI('\index@item',      undef, sub { doIndexItem($_[0],1); });
DefConstructorI('\index@subitem',   undef, sub { doIndexItem($_[0],2); });
DefConstructorI('\index@subsubitem',undef, sub { doIndexItem($_[0],3); });
DefConstructorI('\index@done',      undef, sub { doIndexItem($_[0],0); });

DefMacroI('\indexname',undef, 'Index');

DefEnvironment('{theindex}', 
	       "<ltx:index xml:id='#id'>"
	       .  "<ltx:title>Index</ltx:title>"
	       .  "#body"
	       ."</ltx:index>",
	       beforeDigest=>sub { Let(T_CS('\item'),      T_CS('\index@item'));
				   Let(T_CS('\subitem'),   T_CS('\index@subitem'));
				   Let(T_CS('\subsubitem'),T_CS('\index@subsubitem'));
				   Let(T_CS('\dotfill'),   T_CS('\index@dotfill')); },
	       beforeDigestEnd=>sub { Digest(T_CS('\index@done'));},
	       afterDigestBegin=>sub { 
		 my $docid = ToString(Expand(T_CS('\thedocument@ID')));
		 $_[1]->setProperty(id=>($docid ? "$docid.idx" : 'idx')); });

DefPrimitiveI('\indexspace',   undef, undef);
DefPrimitiveI('\makeindex',    undef, undef);
DefPrimitiveI('\makeglossary', undef, undef);

#======================================================================
# C.11.6 Terminal Input and Output
#======================================================================

DefPrimitive('\typeout{}',sub {
  my($stomach,$stuff)=@_;
  print STDERR ToString(Expand($stuff));
  return; });

DefPrimitive('\typein[]{}',undef);
#  print STDERR ToString($_[2]); 
#  # Then setValue $_[1] to read ???
#	       });

#**********************************************************************
# C.12 Line and Page Breaking
#**********************************************************************

#======================================================================
# C.12.1 Line Breaking
#======================================================================
DefPrimitive('\linebreak[]',undef);
DefPrimitive('\nolinebreak[]',undef);
# \\ already defined
DefConstructorI('\newline',undef, "\n");
DefPrimitiveI('\-',undef, undef);	# We don't do hyphenation.
# \hyphenation in TeX.pool

DefPrimitiveI('\sloppy',undef, undef);
DefPrimitiveI('\fussy', undef, undef);
DefEnvironment('{sloppypar}','#body');
DefMacroI('\nobreakdashes',undef, T_OTHER('-'));

DefMacro('\showhyphens{}','#1'); # ?
#======================================================================
# C.12.2 Page Breaking
#======================================================================
DefPrimitive('\pagebreak[]',undef);
DefPrimitive('\nopagebreak[]',undef);
DefPrimitiveI('\columnbreak',undef, undef); # latex? or multicol?
DefPrimitive('\enlargethispage OptionalMatch:* {}',undef);

DefPrimitiveI('\clearpage',      undef, undef); # Could possibly mean something?
DefPrimitiveI('\cleardoublepage',undef, undef);
DefPrimitiveI('\samepage',       undef, undef);

#**********************************************************************
# C.13 Lengths, Spaces and Boxes
#**********************************************************************

#####
#####
#  Complete to here
#  [except for NOTE'd entries, of course]
#####
#####

#======================================================================
# C.13.1 Length
#======================================================================
# \fill
DefMacro('\stretch{}','0pt plus #1fill\relax');

DefPrimitive('\@check@length DefToken',sub {
  my($stomach,$cs)=@_;
  my $defn = LookupDefinition($cs);
  if(!$defn || !$defn->isRegister){
    Warn(":undefined:".ToString($cs)." is not a length; defining it now");
    DefRegisterI($cs,undef,Dimension(0)); }
  return; });

DefPrimitive('\newlength DefToken',sub {
  my($stomach,$cs)=@_;
  DefRegisterI($cs,undef,Dimension(0)); });
DefMacro('\setlength{}{}','\@check@length{#1}#1#2\relax');
DefMacro('\addtolength{}{}','\@check@length{#1}\advance#1 #2\relax');
DefMacro('\settowidth{}{}','\@check@length{#1}#1 0pt\relax'); # Till we have an approximation of width
DefMacro('\settoheight{}{}','\@check@length{#1}#1 0pt\relax'); # "
DefMacro('\settodepth{}{}','\@check@length{#1}#1 0pt\relax'); # "

# Assuming noone tries to get clever with figuring out the allocation of 
# numbers, these become simple DefRegister's
DefPrimitive('\newcount DefToken', sub { DefRegisterI($_[1],undef,Number(0)); });
DefPrimitive('\newdimen DefToken', sub { DefRegisterI($_[1],undef,Dimension(0)); });
DefPrimitive('\newskip DefToken',  sub { DefRegisterI($_[1],undef,Glue(0)); });
DefPrimitive('\newmuskip DefToken',sub { DefRegisterI($_[1],undef,MuGlue(0)); });
#DefPrimitive('\newbox DefToken',  sub { DefRegisterI($_[1],undef,LaTeXML::Box->new()); });
DefPrimitive('\newtoks DefToken',  sub { DefRegisterI($_[1],undef,Tokens()); });

DefRegister('\fill',Glue(0,'1fill'));

#======================================================================
# C.13.2 Space
#======================================================================
DefPrimitive('\hspace OptionalMatch:* {}',undef);
DefPrimitive('\vspace OptionalMatch:* {}',undef);
DefPrimitive('\addvspace {}',undef);
DefPrimitive('\addpenalty {}',undef);
# \hfill, \vfill

#======================================================================
# C.13.3 Boxes
#======================================================================
# Can't really get these!
DefMacroI('\height',     undef, '0pt');
DefMacroI('\totalheight',undef, '0pt');
DefMacroI('\depth',      undef, '0pt');
DefMacroI('\width',      undef, '0pt');

DefConstructor('\mbox {}',
	       "<ltx:text _noautoclose='1'>#1</ltx:text>", mode=>'text');
DefConstructor('\makebox[Dimension][]{}',
	       "<ltx:text ?#1(width='#1') ?#2(pos='#2') _noautoclose='1'>#3</ltx:text>",
	       mode=>'text');
DefRegister('\fboxrule', Dimension(0));
DefRegister('\fboxsep',  Dimension(0));

DefConstructor('\fbox{}',
	       "<ltx:text framed='rectangle' _noautoclose='1'>#1</ltx:text>", mode=>'text');
DefConstructor('\framebox[Dimension][]{}',
	       "<ltx:text ?#1(width='#1') ?#2(pos='#2') framed='rectangle' _noautoclose='1'>#3</ltx:text>",
	       mode=>'text');
DefPrimitive('\newsavebox DefToken',sub { AssignValue('box'.ToString($_[1]), LaTeXML::List->new()); });
DefPrimitive('\sbox DefToken{}',sub {
   AssignValue('box'.ToString($_[1]), Digest($_[2])); return;});
DefPrimitive('\savebox DefToken[][]{}',sub {
   AssignValue('box'.ToString($_[1]), Digest($_[4])); return;});

DefMacroI('\begin{lrbox}',LaTeXML::Parameters::parseParameters('{Token}','\begin{lrbox}'),
	  '\@begin@lrbox #1');
DefPrimitiveI('\end{lrbox}',undef,sub { $_[0]->egroup; });
DefPrimitive('\@begin@lrbox Token', sub {
  my($stomach,$token)=@_;
  $stomach->bgroup;
#  my $font = $STATE->lookupValue('font');
#  my $loc  = $stomach->getGullet->getLocator;
#  my $box = LaTeXML::Box->new($stomach->digestNextBody(),$font,$loc);
  my $box = LaTeXML::List->new($stomach->digestNextBody());
  AssignValue('box'.ToString($token), $box); });

DefPrimitive('\usebox DefToken', sub { 
  LookupValue('box'.ToString($_[1])) || LaTeXML::Box->new();});

# NOTE: There are 2 extra arguments (See LaTeX Companion, p.866)
# for height and inner-pos.  We're ignoring them, for now, though.
DefConstructor('\parbox[][][]{Dimension}{}',
	       "<ltx:inline-block ?#4(width='#4') vattach='#vattach' align='justified'>#5</ltx:inline-block>",
	       mode=>'text', bounded=>1,
	       properties=>sub { (vattach=>translateAttachment($_[1])); },
	       beforeDigest=>sub {
		 Let(T_CS("\\\\"),T_CS('\@block@cr')); });

DefEnvironment('{minipage}[][][]{Dimension}',
	       "<ltx:inline-block ?#4(width='#4') vattach='#vattach' align='justified'>#body</ltx:inline-block>",
	       mode=>'text',
	       properties=>sub { (vattach=>translateAttachment($_[1])); },
	       beforeDigest=>sub {
		 Let(T_CS("\\\\"),T_CS('\@block@cr')); });
DefConstructor('\rule[Dimension]{Dimension}{Dimension}', 
	       "<ltx:rule ?#1(yoffset='#1') width='#2' height='#3'/>");
DefConstructor('\raisebox{Dimension}[Dimension][Dimension]{}',
	       "<ltx:text yoffset='#1' _noautoclose='1'>#4</ltx:text>",
	       mode=>'text');

#**********************************************************************
# C.14 Pictures and Color
#**********************************************************************
#======================================================================
# C.14.1 The picture environment
#======================================================================

#----------------------------------------------------------------------

sub ResolveReader {
  my ($itemtype) = @_;
  if(ref $itemtype eq 'CODE'){ $itemtype; }
  else {
    $itemtype = ToString($itemtype);
    LaTeXML::Parameters::checkReaderFunction('Read'.$itemtype); }}

sub ReadPair {
  my($gullet,$itemtype, $xarg, $yarg)=@_;
  my $itemreader;
  if(!$itemtype){ $itemreader=\&ReadFloat; }
  else { $itemreader = ResolveReader($itemtype); }
  if(!$itemreader){ Error(":misdefined:<unknown> Cannot find reader for Pair items from $itemtype"); }
  $gullet->skipSpaces;
  if($gullet->ifNext(T_OTHER('('))){
    $gullet->readToken;  $gullet->skipSpaces;
    my $x = &$itemreader($gullet,$xarg);
    $gullet->skipSpaces; $gullet->readUntil(T_OTHER(',')); $gullet->skipSpaces;
    my $y = &$itemreader($gullet,$yarg);
    $gullet->skipSpaces; $gullet->readUntil(T_OTHER(')')); $gullet->skipSpaces;
    Pair($x,$y); }}

sub ReadFloat { $_[0]->skipSpaces; ($_[0]->readFloat || Float(0)); }

sub ptValue { $_[0]?$_[0]->ptValue:undef; }

# This eases conversion of a Pair to 2 attributes.
sub PairAttr {
  my($pair,$xattr,$yattr)=@_;
  ($pair ? { $xattr=>ptValue($pair->getX), $yattr=>ptValue($pair->getY) } : {}); }

sub SimplePairAttr {
  my($pair,$xattr,$yattr)=@_;
  ($pair ? { $xattr=>$pair->getX, $yattr=>$pair->getY } : {}); }

#----------------------------------------------------------------------
# Picture parameters.
DefRegister('\unitlength', Dimension('1pt'));
DefPrimitiveI('\thinlines',  undef, sub { AssignValue('\@wholewidth',Dimension('0.4pt')); });
DefPrimitiveI('\thicklines', undef, sub { AssignValue('\@wholewidth',Dimension('0.8pt')); });
DefRegister('\@wholewidth'=>Dimension('0.4pt'));
DefMacro('\linethickness{}', '\@wholewidth #1\relax');

DefPrimitive('\arrowlength{Dimension}', sub { AssignValue('\arrowlength',$_[1]); });

#----------------------------------------------------------------------
# Picture transformation support
sub slopeToPicCoord {
  my ($slope,$xlength) = @_;
  my($mx,$my)=($slope->getX,$slope->getY);
  my $s = $mx->sign();
  $xlength = picScale($xlength);
  Pair($xlength->multiply($s),
       $xlength->multiply(($s==0)?$my->sign():
			  $my->valueOf/$mx->absolute->valueOf)); }

sub picScale {
  ($_[0] ? $_[0]->multiply(LookupValue('\unitlength')): undef); }

sub picProperties {
  my (%props) = @_;
  if(($props{stroke}||'black') ne 'none'){
    $props{thick}=ptValue(LookupValue('\@wholewidth')); }
  if (my $arrowlength=LookupValue('\arrowlength')){
    $props{arrowlength}=ptValue($arrowlength); }
  %props; }

#----------------------------------------------------------------------
# the code
DefMacroI('\qbeziermax',undef,'500');
# Careful; some of these have meanings outside of picture!
DefPrimitive('\vector Pair {Float}',
	     sub { Error(":unexpected:\\vector cannot appear outside of picture environment"); });
DefPrimitive('\circle OptionalMatch:* {Float}',
	     sub { Error(":unexpected:\circle cannot appear outside of picture environment"); });
DefPrimitive('\oval [Float] Pair []',
	     sub { Error(":unexpected:\\oval cannot appear outside of picture environment"); });
DefMacro('\qbezier[0]','\bezier{#1}');
DefPrimitive('\bezier {Number} Pair Pair Pair',
	     sub { Error(":unexpected:\\qbezier cannot appear outside of picture environment"); });
DefPrimitive('\dashbox {Float} Pair [] {}',
	     sub { Error(":unexpected:\\dashbox cannot appear outside of picture environment"); });
DefPrimitive('\frame{}',
	     sub { Error(":unexpected:\\frame cannot appear outside of picture environment"); });
sub before_picture {
  Let(T_CS('\line'),     T_CS('\pic@line'));
  Let(T_CS('\vector'),   T_CS('\pic@vector'));
  Let(T_CS('\circle'),   T_CS('\pic@circle'));
  Let(T_CS('\oval'),     T_CS('\pic@oval'));
  Let(T_CS('\qbezier'),  T_CS('\pic@qbezier'));
  Let(T_CS('\makebox'),  T_CS('\pic@makebox'));
  Let(T_CS('\framebox'), T_CS('\pic@framebox'));
  Let(T_CS('\raisebox'), T_CS('\pic@raisebox'));
  Let(T_CS('\dashbox'),  T_CS('\pic@dashbox'));
  Let(T_CS('\frame'),    T_CS('\pic@frame'));
}

sub after_picture {}

# NOTE: Untex should prefix a setting of unitlength!!!
DefEnvironment('{picture} Pair OptionalPair',
	       "<ltx:picture %&SimplePairAttr(#size,width,height)"
	       ." fill='black' stroke='black' unitlength='#unitlength'>"
	       . "#body"
	       ."</ltx:picture>",
	       mode=>'text',
	       beforeDigest=>\&before_picture,
	       properties=>sub { (unitlength=>LookupValue('\unitlength'),
				  picProperties(size=>picScale($_[1]))); },
	       afterDigest=>\&after_picture);

DefConstructor('\put Pair {}',
	       "<ltx:g transform='#pos'>#2</ltx:g>",
	       properties=>sub { pos=>'translate('.ptValue(picScale($_[1])).')'; },
	       mode=>'text');

DefConstructor('\pic@line Pair {Float}',
	       "<ltx:line points='#points' stroke-width='#thick'/>",
	       alias=>'\line',
	       properties=>sub { picProperties(points=>'0,0 '.slopeToPicCoord($_[1],$_[2])->ptValue()); });

DefConstructor('\pic@vector Pair {Float}',
	       "<ltx:line points='#points' stroke-width='#thick' terminators='->'"
	       . " arrowlength='#arrowlength'/>",
	       alias=>'\vector',
	       properties=>sub { picProperties(points=>'0,0 '.slopeToPicCoord($_[1],$_[2])->ptValue()); });

DefConstructor('\pic@circle OptionalMatch:* {Float}',
	       "<ltx:circle x='0' y='0' r='&ptValue(#radius)' fill='#fill' stroke='#stroke'"
	       .  " stroke-width='#thick'/>",
	       alias=>'\circle',
	       properties=>sub {
		 my ($stomach,$filled,$dia) = @_;
		 $dia=picScale($dia);
		 $dia=$dia->add(LookupValue('\@wholewidth')) unless $filled;
		 picProperties(radius=>$dia->multiply(0.5),
			       ($filled ? 'stroke' : 'fill')=>'none'); });

DefConstructor('\pic@oval [Float] Pair []',
	       "<ltx:rect %&PairAttr(#pos,x,y) %&PairAttr(#size,width,height) rx='&ptValue(#radius)'"
	       . "  fill='none' part='#3' stroke-width='#thick'/>",
	       alias=>'\oval',
	       properties=>sub{
		 my ($stomach,$r,$size,$part) = @_;
		 $size = picScale($size);
		 my $halfsize = $size->multiply(0.5);
		 my $pos = Pair($halfsize->getX->negate,$halfsize->getY->negate);
		 $r = ($r ? picScale($r) : Dimension('40pt'));
		 $r=$r->smaller($halfsize->getX->absolute);
		 $r=$r->smaller($halfsize->getY->absolute);
		 picProperties(size=>$size, pos=>$pos, radius=>$r); });

DefConstructor('\pic@qbezier [Number] Pair Pair Pair',
               "<ltx:bezier ?#1(displayedpoints='#1') points='&ptValue(#pt)' stroke-width='#thick' />",
	       alias=>'\qbezier',
	       properties=>sub {
		 picProperties(pt=>PairList(picScale($_[2]),picScale($_[3]),picScale($_[4]))); });

DefConstructor('\pic@makebox Pair []{}',
	       "<ltx:g %&PairAttr(#size,width,height) pos='#2'>#3</ltx:g>",
	       alias=>'\makebox',
	       properties=>sub { picProperties(size=>picScale($_[1])); });

DefConstructor('\pic@raisebox{Dimension}[Dimension][Dimension]{}',
	       "<ltx:g y='#1'>#4</ltx:g>",
	       alias=>'\raisebox');

DefConstructor('\pic@framebox Pair []{}',
	       "<ltx:rect x='0' y='0' %&PairAttr(#size,width,height) stroke-width='#thick' fill='none'/>"
	       . "<ltx:g %&PairAttr(#size,width,height) pos='#2'> #3 </ltx:g>",
	       alias=>'\framebox',
	       properties=>sub { picProperties(size=>picScale($_[1])); });

DefConstructor('\pic@dashbox {Float} Pair [] {}',
	       "<ltx:rect x='0' y='0' %&PairAttr(#size,width,height)"
	       . " stroke-width='#thick' stroke-dasharray='&ptValue(#dash)' fill='none'/>".
	       "<ltx:g %&PairAttr(#size,width,height) pos='#3'> #4 </ltx:g>",
	       alias=>'\dashbox',
	       properties=>sub { picProperties(dash=>picScale($_[1]),size=>picScale($_[2])); });

DefConstructor('\pic@frame{}',
	       "<ltx:g framed='true' stroke-width='#thick'>#1</ltx:g>",
	       alias=>'\frame',
	       properties=>sub { picProperties(); });

our %alignments = (l=>'left',c=>'center',r=>'right');
DefConstructor('\shortstack[]{}',
	       "<ltx:inline-block align='#align'><ltx:text _noautoclose='1'>#2</ltx:text></ltx:inline-block>",
	       bounded=>1,
	       beforeDigest=>sub{ DefConstructor("\\\\ OptionalMatch:* [Dimension]",
						 "</ltx:text><ltx:text _noautoclose='1'>",
						 reversion=>Tokens(T_CS("\\\\"),T_CR)); 
				  Let(T_MATH, T_CS('\@text@alignment$')); },
	       properties=>{ align=>sub { ($_[1] ? $alignments{ToString($_[0])} : undef); }},
	       mode=>'text');

DefMacro('\multiput Pair Pair {}{}',  sub {
  my($gullet,$pos,$d,$nt,$body)=@_;
  my($x,$y,$dx,$dy,$n)=map(ToString($_), ($pos->getX,$pos->getY,$d->getX,$d->getY,$nt));
  my @exp=();
  for(my $i=0; $i<$n; $i++){
    push(@exp,T_CS('\put'),T_OTHER('('),Explode($x),T_OTHER(','),Explode($y),T_OTHER(')'),
	 T_BEGIN,$body->unlist,T_END);
    $x += $dx; $y+=$dy; }
  @exp; });

Tag('ltx:picture',
    afterOpen=>sub {
      my($document,$node,$thing)=@_;
      if($thing && (ref $thing eq 'LaTeXML::Whatsit') && !$thing->getProperty('_added_tex')){
	my $tex = UnTeX($thing);
	$tex =~ s/\n//sg;		# no cr's in pictures?
	$document->setAttribute($node,tex=>$tex);
	$thing->setProperty('_added_tex',1); }});

Tag('ltx:g', afterClose=> sub {
      my ($document,$node) = @_;
      $node->parentNode->removeChild($node) unless $node->hasChildNodes; });

# \savebox -- already defined differntly in C.13 above ? 

#**********************************************************************
# C.15 Font Selection
#**********************************************************************
#======================================================================
# C.15.1 Changing the Type Style
#======================================================================
# Text styles.

# What about \f@family, etc....?
# These are not allowed in math.
DefConstructorI('\mdseries', undef, '',  forbidMath=>1, font=>{series=>'medium'});
DefConstructorI('\bfseries', undef, '',  forbidMath=>1, font=>{series=>'bold'});

DefConstructorI('\rmfamily', undef, '',  forbidMath=>1, font=>{family=>'serif'});
DefConstructorI('\sffamily', undef, '',  forbidMath=>1, font=>{family=>'sansserif'});
DefConstructorI('\ttfamily', undef, '',  forbidMath=>1, font=>{family=>'typewriter'});

DefConstructorI('\upshape',  undef, '', forbidMath=>1, font=>{shape=>'upright'});
DefConstructorI('\itshape',  undef, '', forbidMath=>1, font=>{shape=>'italic'});
DefConstructorI('\slshape',  undef, '', forbidMath=>1, font=>{shape=>'slanted'});
DefConstructorI('\scshape',  undef, '', forbidMath=>1, font=>{shape=>'smallcaps'});

DefConstructorI('\normalfont',    undef, '', font=>{family=>'serif', series=>'medium', shape=>'upright'});
DefConstructorI('\verbatim@font', undef, '', font=>{family=>'typewriter', series=>'medium', shape=>'upright'});
Let(T_CS('\reset@font'),T_CS('\normalfont'));

DefMacroI('\rmdefault',undef,'cmr');
DefMacroI('\sfdefault',undef,'cmss');
DefMacroI('\ttdefault',undef,'cmtt');
DefMacroI('\bfdefault',undef,'bx');
DefMacroI('\mddefault',undef,'m');
DefMacroI('\itdefault',undef,'it');
DefMacroI('\sldefault',undef,'sl');
DefMacroI('\scdefault',undef,'sc');
DefMacroI('\updefault',undef,'n');
DefMacroI('\encodingdefault',undef,'OT1');
DefMacroI('\familydefault',undef,'\rmdefault');
DefMacroI('\seriesdefault',undef,'\mddefault');
DefMacroI('\shapedefault',undef,'\updefault');

Let(T_CS('\mediumseries'),T_CS('\mdseries'));
Let(T_CS('\normalshape'),T_CS('\upshape'));

DefPrimitive('\fontencoding{}',undef);
# These should be looking up from the TeX tables!
DefConstructor('\fontfamily{}','',
	       afterDigest=>sub { 
		 my $family = ToString($_[1]->getArg(1));
		 if(my $fam = lookupFontFamily($family)){ MergeFont(family=>$fam); }
		else { Info(":unexpected:$family Unrecognized font family."); }});
DefConstructor('\fontseries{}','',
	       afterDigest=>sub { 
		 my $series = ToString($_[1]->getArg(1));
		 if(my $ser = lookupFontSeries($series)){ MergeFont(series=>$ser); }
		else { Info(":unexpected:$series Unrecognized font series."); }});
DefConstructor('\fontshape{}','',
	       afterDigest=>sub { 
		 my $shape = ToString($_[1]->getArg(1));
		 if(my $sh = lookupFontShape($shape)){ MergeFont(shape=>$sh); }
		else { Info(":unexpected:$shape Unrecognized font shape."); }});

DefMacroI('\selectfont',    undef, Tokens());
DefMacro('\usefont{}{}{}{}{}',
	 '\fontencoding{#1}\fontfamily{#2}\fontseries{#3}\fontshape{#4}\selectfont');
 
# If these series or shapes appear in math, they revert it to roman, medium, upright (?)
DefConstructor('\textmd{}', "?#isMath(<ltx:text _noautoclose='1'>#1</ltx:text>)(#1)", mode=>'text',
	       bounded=>1, font=>{series=>'medium'});
DefConstructor('\textbf{}', "?#isMath(<ltx:text _noautoclose='1'>#1</ltx:text>)(#1)", mode=>'text',
	       bounded=>1, font=>{series=>'bold'});
DefConstructor('\textrm{}', "?#isMath(<ltx:text _noautoclose='1'>#1</ltx:text>)(#1)", mode=>'text',
	       bounded=>1, font=>{family=>'serif'});
DefConstructor('\textsf{}', "?#isMath(<ltx:text _noautoclose='1'>#1</ltx:text>)(#1)", mode=>'text',
	       bounded=>1, font=>{family=>'sansserif'});
DefConstructor('\texttt{}', "?#isMath(<ltx:text _noautoclose='1'>#1</ltx:text>)(#1)", mode=>'text',
	       bounded=>1, font=>{family=>'typewriter'});

DefConstructor('\textup{}', "?#isMath(<ltx:text _noautoclose='1'>#1</ltx:text>)(#1)", mode=>'text',
	       bounded=>1, font=>{shape=>'upright'});
DefConstructor('\textit{}', "?#isMath(<ltx:text _noautoclose='1'>#1</ltx:text>)(#1)", mode=>'text',
	       bounded=>1, font=>{shape=>'italic'});
DefConstructor('\textsl{}', "?#isMath(<ltx:text _noautoclose='1'>#1</ltx:text>)(#1)", mode=>'text',
	       bounded=>1, font=>{shape=>'slanted'});
DefConstructor('\textsc{}', "?#isMath(<ltx:text _noautoclose='1'>#1</ltx:text>)(#1)", mode=>'text',
	       bounded=>1, font=>{shape=>'smallcaps'});
DefConstructor('\textnormal{}', "?#isMath(<ltx:text _noautoclose='1'>#1</ltx:text>)(#1)", mode=>'text',
	       bounded=>1, font=>{family=>'serif',series=>'medium',shape=>'upright'});

DefPrimitive('\DeclareTextFontCommand{}{}', sub {
  my($stomach,$cmd,$font)=@_;
  DefConstructor(ToString($cmd)."{}","?#isMath(<ltx:text _noautoclose='1'>#1</ltx:text>)(#1)", mode=>'text',
	       bounded=>1, beforeDigest=>sub { Digest($font); }); });

DefPrimitive('\mathversion{}',sub {
  my($stomach,$version)=@_;
  $version = ToString($version);
  if($version eq 'bold'     ){ 
    AssignValue(mathfont=>LookupValue('mathfont')->merge(forcebold=>1), 'local'); }
  elsif($version eq 'normal'){ 
    AssignValue(mathfont=>LookupValue('mathfont')->merge(forcebold=>0), 'local'); }
  else { Error(":unexpected:$version Unknown math verison \"$version\""); }});

DefMacro('\newfont{}{}', '\font#1=#2\relax');

Let(T_CS('\normalcolor'),T_CS('\relax'));

#======================================================================
# C.15.2 Changing the Type Size
#======================================================================
# Handled in TeX.pool.ltxml

#======================================================================
# C.15.3 Special Symbol
#======================================================================
# \symbol{num} ????

# These in LaTeX, but not in the book...
DefConstructorI('\textdollar',       undef, "\$");
DefConstructorI('\textemdash',       undef, "\x{2014}"); # EM DASH
DefConstructorI('\textendash',       undef, "\x{2013}"); # EN DASH
DefConstructorI('\textexclamdown',   undef, UTF(0xA1)); # INVERTED EXCLAMATION MARK
DefConstructorI('\textquestiondown', undef, UTF(0xBF));	# INVERTED QUESTION MARK
DefConstructorI('\textquotedblleft', undef, "\x{201C}"); # LEFT DOUBLE QUOTATION MARK
DefConstructorI('\textquotedblright',undef, "\x{201D}"); # RIGHT DOUBLE QUOTATION MARK
DefConstructorI('\textquotedbl',     undef, "\"");       # plain ascii DOUBLE QUOTATION
DefConstructorI('\textquoteleft',    undef, "\x{2018}");     # LEFT SINGLE QUOTATION MARK
DefConstructorI('\textquoteright',   undef, "\x{2019}");    # RIGHT SINGLE QUOTATION MARK
DefConstructorI('\textsterling',     undef, UTF(0xA3));	 # POUND SIGN
DefConstructorI('\textasteriskcentered',undef, "*");
DefConstructorI('\textbackslash',    undef, UTF(0x5C)); # REVERSE SOLIDUS
DefConstructorI('\textbar',          undef, "|");
DefConstructorI('\textbraceleft',    undef, "{");
DefConstructorI('\textbraceright',   undef, "}");
DefConstructorI('\textbullet',       undef, "\x{2022}");    # BULLET
DefConstructorI('\textdaggerdbl',    undef, "\x{2020}"); # DOUBLE DAGGER
DefConstructorI('\textdagger',       undef, "\x{2021}");    # DAGGER
DefConstructorI('\textparagraph',    undef, UTF(0xB6)); # PILCROW SIGN
DefConstructorI('\textperiodcentered',undef, "\x{22C5}"); # DOT OPERATOR
DefConstructorI('\textsection',      undef, UTF(0xA7));	      # SECTION SIGN
DefAccent('\textcircled',UTF(0x20DD),UTF(0x25EF)); # Defined in TeX.pool
DefConstructorI('\textless',         undef, "<");
DefConstructorI('\textgreater',      undef, ">");
DefConstructorI('\textcopyright',    undef, UTF(0xA9)); # COPYRIGHT SIGN
DefConstructorI('\textasciicircum',  undef, "^");
DefConstructorI('\textasciitilde',   undef, "~");
DefConstructorI('\textcompwordmark', undef, ""); # ???
DefConstructorI('\textunderscore',   undef, "_");
DefConstructorI('\textvisiblespace', undef, "\x{2420}"); # SYMBOL FOR SPACE;  Not really the right symbol!
DefConstructorI('\textellipsis',     undef, "\x{2026}"); # HORIZONTAL ELLIPSIS
DefConstructorI('\textregistered',   undef, UTF(0xAE)); # REGISTERED SIGN
DefConstructorI('\texttrademark',    undef, "\x{2122}");  # TRADE MARK SIGN
DefConstructor('\textsuperscript{}', "<ltx:text yoffset='0.5em' _noautoclose='1'>#1</ltx:text>");
DefConstructorI('\textordfeminine',  undef, UTF(0xAA)); # FEMININE ORDINAL INDICATOR
DefConstructorI('\textordmasculine', undef, UTF(0xBA)); # MASCULINE ORDINAL INDICATOR
DefMacroI('\SS',undef, 'SS');			       # ?


DefConstructor('\sqrtsign Digested',
	       "<ltx:XMApp><ltx:XMTok meaning='square-root'/><ltx:XMArg>#1</ltx:XMArg></ltx:XMApp>");


DefConstructorI('\mathparagraph',undef,UTF(0xB6));
DefConstructorI('\mathsection',undef,UTF(0xA7));
DefConstructorI('\mathdollar',undef,'$');
DefConstructorI('\mathsterling',undef,UTF(0xA3));
DefConstructorI('\mathunderscore',undef,'_');
DefConstructorI('\mathellipsis',undef,"\x{2026}");

# Are these glyph "pieces" or use alone?
DefMathI('\arrowvert', undef, "|",       role=>'VERTBAR');
DefMathI('\Arrowvert', undef, "\x{2225}",role=>'VERTBAR');

# The following are glyph "pieces"...
DefConstructorI('\braceld',undef,"\x{239D}");#   left brace down part
DefConstructorI('\bracelu',undef,"\x{239B}");#   left brace up part
DefConstructorI('\bracerd',undef,"\x{23A0}");#   right brace down part
DefConstructorI('\braceru',undef,"\x{239E}");#   right brace up part

DefMathI('\cdotp',      undef,"\x{22C5}", role=>'MULOP');
DefMathI('\ldotp',      undef,".",        role=>'MULOP');
DefMathI('\intop',      undef,"\x{222B}", role=>'INTOP', scriptpos=>\&doScriptpos, meaning=>'integral');
DefMathI('\ointop',     undef,"\x{222E}", role=>'INTOP', scriptpos=>\&doScriptpos, meaning=>'contour-integral');

# WHat are these? They look like superscripted parentheses, or combining accents!
# \lhook
# \rhook
Let(T_CS('\gets'),T_CS('\leftarrow'));

DefConstructorI('\lmoustache',undef,"\x{23B0}");
DefConstructorI('\rmoustache',undef,"\x{23B1}");
DefMathI('\mapstochar',  undef,"\x{21A6}", role=>'ARROW', meaning=>'maps-to');
DefMathI('\owns',        undef,"\x{220B}", role=>'RELOP', meaning=>'contains');

# \skew{}{}{} ????

# \symbol lookup symbol in font by index?
#**********************************************************************
# Other stuff
#**********************************************************************
# Some stuff that got missed in the appendices ?

DefMacro('\@namedef{}','\expandafter\def\csname #1\endcsname');
DefMacro('\@nameuse{}','\csname #1\endcsname');

DefMacroI('\@spaces',undef, '\space\space\space\space');
Let(T_CS('\@sptoken'),T_SPACE);

Let(T_CS('\MakeUppercase'),T_CS('\uppercase'));
Let(T_CS('\MakeLowercase'),T_CS('\lowercase'));

#======================================================================

DefMacroI('\@ehc', undef, "I can't help");

DefMacro('\@gobble{}',Tokens());

sub make_message {
  my($cs,@args)=@_;
  ":latex ".join("\n",map(ToString(Expand($_)),@args)); }

DefPrimitive('\@onlypreamble{}',\&onlyPreamble); # Don't bother enforcing this.

DefMacro('\GenericError{}{}{}{}', sub{ Fatal(make_message(@_)); });
DefMacro('\GenericWarning{}{}',   sub{ Warn(make_message(@_)); });
DefMacro('\GenericInfo{}{}{}{}',  sub{ print STDERR "Info: ".make_message(@_)."\n"; });

Let(T_CS('\MessageBreak'),T_CS('\relax'));
DefMacro('\PackageError{}{}{}',
  ' \GenericError{%
      (#1)\@spaces\@spaces\@spaces\@spaces
   }{%
      Package #1 Error: #2%
   }{%
      See the #1 package documentation for explanation.%
   }{#3}');
DefMacro('\PackageWarning{}{}',
   '\GenericWarning{%
      (#1)\@spaces\@spaces\@spaces\@spaces
   }{%
      Package #1 Warning: #2%
   }');
DefMacro('\PackageWarningNoLine{}{}',
  ' \PackageWarning{#1}{#2\@gobble}');
DefMacro('\PackageInfo{}{}',
  '\GenericInfo{%
      (#1) \@spaces\@spaces\@spaces
   }{%
      Package #1 Info: #2%
   }');
DefMacro('\ClassError{}{}{}',
  '\GenericError{%
      (#1) \space\@spaces\@spaces\@spaces
   }{%
      Class #1 Error: #2%
   }{%
      See the #1 class documentation for explanation.%
   }{#3}');
DefMacro('\ClassWarning{}{}',
  '\GenericWarning{%
      (#1) \space\@spaces\@spaces\@spaces
   }{%
      Class #1 Warning: #2%
   }');
DefMacro('\ClassWarningNoLine{}{}',
  '\ClassWarning{#1}{#2\@gobble}');
DefMacro('\ClassInfo{}{}',
  '\GenericInfo{%
      (#1) \space\space\@spaces\@spaces
   }{%
      Class #1 Info: #2%
   }');
DefMacro('\@latex@error{}{}',
  '\GenericError{%
      \space\space\space\@spaces\@spaces\@spaces
   }{%
      LaTeX Error: #1%
   }{%
      See the LaTeX manual or LaTeX Companion for explanation.%
   }{#2}');
DefMacro('\@latex@warning{}',
  '\GenericWarning{%
      \space\space\space\@spaces\@spaces\@spaces
   }{%
      LaTeX Warning: #1%
   }');
DefMacro('\@latex@warning@no@line{}',
  '\@latex@warning{#1\@gobble}');
DefMacro('\@latex@info{}',
   '\GenericInfo{%
      \@spaces\@spaces\@spaces
   }{%
      LaTeX Info: #1%
   }');
DefMacro('\@latex@info@no@line{}',
  '\@latex@info{#1\@gobble}');

DefPrimitive('\@setsize{}{}{}{}',undef);
Let(T_CS('\@warning'),  T_CS('\@latex@warning'));
Let(T_CS('\@@warning'), T_CS('\@latex@warning@no@line'));

#======================================================================

RawTeX(<<'EOTeX');
\newif\if@insert
\newif\if@colmade
\newif\if@specialpage   \@specialpagefalse
\newif\if@firstcolumn   \@firstcolumntrue
\newif\if@twocolumn     \@twocolumnfalse
\newif\if@twoside       \@twosidefalse
\newif\if@reversemargin \@reversemarginfalse
\newif\if@mparswitch    \@mparswitchfalse
\newcount\col@number    \@ne
\newif\if@filesw \@fileswtrue
\newif\if@partsw \@partswfalse
\def\@tempswafalse{\let\if@tempswa\iffalse}
\def\@tempswatrue{\let\if@tempswa\iftrue}
\let\if@tempswa\iffalse
\long\def\@firstoftwo#1#2{#1}
\long\def\@secondoftwo#1#2{#2}
\newcount\@tempcnta
\newcount\@tempcntb
\newif\if@tempswa
\newdimen\@tempdima
\newdimen\@tempdimb
\newdimen\@tempdimc
\newbox\@tempboxa
\newskip\@tempskipa
\newskip\@tempskipb
\newtoks\@temptokena
\newskip\@flushglue \@flushglue = 0pt plus 1fil
\newbox\rootbox

\newcount\@eqcnt
\newcount\@eqpen
\newif\if@eqnsw\@eqnswtrue
\newskip\@centering
\@centering = 0pt plus 1000pt
\let\@eqnsel=\relax

% Some control structure from latex.ltx
% (amazing that it actually works!)
% How much of this control struture will I need?
\def\@nnil{\@nil}
\def\@fornoop#1\@@#2#3{}
\long\def\@for#1:=#2\do#3{%
  \expandafter\def\expandafter\@fortmp\expandafter{#2}%
  \ifx\@fortmp\@empty \else
    \expandafter\@forloop#2,\@nil,\@nil\@@#1{#3}\fi}
\long\def\@forloop#1,#2,#3\@@#4#5{\def#4{#1}\ifx #4\@nnil \else
       #5\def#4{#2}\ifx #4\@nnil \else#5\@iforloop #3\@@#4{#5}\fi\fi}
\long\def\@iforloop#1,#2\@@#3#4{\def#3{#1}\ifx #3\@nnil
       \expandafter\@fornoop \else
      #4\relax\expandafter\@iforloop\fi#2\@@#3{#4}}
EOTeX

#======================================================================

DefMacroI('\check@mathfonts',undef, Tokens());
DefMacro('\fontsize{}{}',Tokens());
DefMacroI('\math@fontsfalse',undef, Tokens());
DefMacroI('\math@fontstrue',undef, Tokens());

DefMacroI('\defaultscriptratio',      undef, '.7');
DefMacroI('\defaultscriptscriptratio',undef, '.5');

#======================================================================
DefMacroI('\loggingoutput',undef, Tokens());
DefMacroI('\loggingall',   undef, Tokens());
DefMacroI('\tracingfonts', undef, Tokens());
DefMacroI('\showoverfull', undef, Tokens());
DefMacroI('\showoutput',   undef, Tokens());
DefMacro('\wlog{}', Tokens());

#======================================================================
# Various symbols, accents, etc from Chapter 3 defined in TeX.pool

#**********************************************************************
# Semi-Undocumented stuff
#**********************************************************************
DefMacro('\@ifnextchar Token {}{}', sub {
  my($gullet,$token,$if,$else)=@_;
  my $next = $gullet->readNonSpace;
  # NOTE: Not actually substituting, but collapsing ## pairs!!!!
  ( LaTeXML::Expandable::substituteTokens($token->equals($next) ? $if : $else),$next); });

Let(T_CS('\@ifnext'), T_CS('\@ifnextchar')); # ????

DefMacro('\@ifstar {}{}', sub {
  my($gullet,$if,$else)=@_;
  my $next = $gullet->readNonSpace;
  # NOTE: Not actually substituting, but collapsing ## pairs!!!!
  if(T_OTHER('*')->equals($next)){
    LaTeXML::Expandable::substituteTokens($if); }
  else {
    (LaTeXML::Expandable::substituteTokens($else),$next); }});

DefMacro('\@ifundefined{}{}{}',sub {
  my($gullet,$name,$if,$else)=@_;
  my $cs = T_CS('\\'.ToString($name));
  my $meaning = LookupMeaning($cs);
  (!(defined $meaning) || ($meaning ne $cs) ? $if->unlist : $else->unlist); });

DefMacro('\IfFileExists{}{}{}', sub{
  my($gullet,$file,$if,$else)=@_;
  (FindFile($file) ? $if->unlist : $else->unlist); });

DefMacro('\InputIfFileExists{}{}{}', sub{
  my($gullet,$file,$if,$else)=@_;
  if(FindFile($file)){
    $gullet->input($file);
    $if->unlist; }
  else { $else->unlist; }});

#======================================================================
# Hair
DefPrimitiveI('\makeatletter',undef, sub { AssignCatcode('@'=>CC_LETTER,'local'); });
DefPrimitiveI('\makeatother', undef, sub { AssignCatcode('@'=>CC_OTHER, 'local'); });

#**********************************************************************
#**********************************************************************
# Sundry (is this ams ?)
DefConstructor('\textprime',UTF(0xB4)); # ACUTE ACCENT

Let(T_CS('\endgraf'),T_CS('\par'));
Let(T_CS('\endline'),T_CS('\cr'));
#**********************************************************************
# Should be defined in each (or many) package, but it's not going to
# get set correctly or maintained, so...
DefMacroI('\fileversion',undef, Tokens());
DefMacroI('\filedate',   undef, Tokens());

# Following isn't used anywhere, but should be defined.
DefMacroI('\labelenumi',undef,'\theenumi.');
DefMacroI('\labelenumii',undef,'(\theenumii)');
DefMacroI('\labelenumiii',undef,'\theenumiii.');
DefMacroI('\labelenumiv',undef,'\theenumiv.');
DefMacroI('\labelitemi',undef,'\textbullet');
DefMacroI('\labelitemii',undef,'\normalfont\bfseries \textendash');
DefMacroI('\labelitemiii',undef,'\textasteriskcentered');
DefMacroI('\labelitemiv',undef,'\textperiodcentered');


# Ultimately these may be overridden by babel,
# various of these are defined in various places by different classes.
# And, at any rate, they're currently unused.
DefMacroI('\chaptername',undef,'Chapter');
DefMacroI('\partname',undef,'Part');
DefMacroI('\sectionname',undef,Tokens());
DefMacroI('\subsectionname',undef,Tokens());
DefMacroI('\subsubsectionname',undef,Tokens());
DefMacroI('\paragraphname',undef,Tokens());
DefMacroI('\subparagraphname',undef,Tokens());

#**********************************************************************
# Stuff that would appear in the aux file... maybe somebody uses it?
DefMacro('\bibdata{}',Tokens());
DefMacro('\bibcite{}{}',Tokens());
DefMacro('\citation{}',Tokens());
DefMacro('\contentsline{}{}{}',Tokens());
DefMacro('\newlabel{}{}',Tokens());

DefMacroI('\stop',undef,sub { $_[0]->closeMouth(1); return; });
DefMacroI('\ignorespacesafterend',undef, Tokens());
DefMacroI('\mathgroup',undef,Tokens());
DefMacroI('\mathalpha',undef,Tokens());

#\def\mathhexbox#1#2#3{\mbox{$\m@th \mathchar"#1#2#3$}}
DefMacroI('\nocorrlist',undef,',.');

#**********************************************************************
1;
