# -*-perl-*-
# $Id: latex.wrt 6298 2010-04-01 21:06:12Z mnodine $
# Copyright (C) 2002-2005 Freescale Semiconductor, Inc.
# Distributed under terms of the Perl license, which is the disjunction of
# the GNU General Public License (GPL) and the Artistic License.
=pod
=begin reST
=begin Description
This writer creates an output in LaTeX format. It is definitely in alpha
test state and will be reworked when the final docutils LaTeX writer
becomes available.
It uses the following output defines:
-W author-skip=<text>
The amount to skip after the author and before
address/organization (default is "12pt").
-W caption=<after|before>
Specify that a figure caption should appear
after or before the figure (default is "after").
-W chapter[=0|1] Specify that the top-level sections should be
\chapter (default is 0 if documentclass is
"article" and 1 otherwise).
-W cloak-email-addresses[=<0|1>]
Enables cloaking of email addresses to keep
spambots from harvesting email addresses.
Default is 0.
-W documentclass=<text> Specify documentclass for the output
(default is "article").
-W documentclass-opts=<text>
Specify the options for the documentclass
command (default is "").
-W footer[=1] If 1, specify that the footer decoration
generated by the RST transform should be
included in the document (default is 0).
-W footnote-links[=1] If 1, specify that link URIs should be placed
into footnotes (default is 0).
-W image-ext=<text> The file type to which to coerce figures (
default is "eps").
-W index[=1] If 1, specify that an index should be created
from inline targets and indirect references
to them (default is 0).
-W inputs=<list> Specify comma-separated list of files to \\input
-W max-unwrapped-colsize
The maximum length of a string in a
column of a table without forcing the
width of the column and turning on
wrapping for the entry. Setting it to 0
ensures that all tables will be exactly
the width of the text, even if the table's
natural width may be smaller. Setting it
too large may result in tables that
overflow the column boundary (default is 8).
-W omit-docinfo=<regexp>
Omit any docinfo section(s) that match the
regular expression.
-W packages=<list> Specify comma-separated list of packages to \\use
-W sidebar=<margin|float>
Whether a sidebar should be processed as
a paragraph in the margin or as a
floating box within the text.
Processing as a margin paragraph
requires that \\marginparwidth have a
reasonable value and may require a raw
directive with a \\vspace -<dist> at the
top to keep it from running off the page
(default is "float").
There are a number of commands defined which specify default
styles for rendering various items. These default styles can be
overridden by putting \\renewcommand definitions for them into
some file.tex and then invoking with ``-W inputs=file``. These
commands are
\\styleadmonitiontitle
Argument: the title (type) of the admonition. Default is
centered boldface followed by a colon.
\\styleaddress
Argument: an organization, address, or contact. Default is normal.
\\styleauthor
Argument: an author. Default is emphasized.
\\styleclassZZZ
Argument: the contents of a compound directive with class ZZZ.
\\stylefieldname
Argument: the name of a field. Default is boldface followed
by a colon.
\\stylelegendtitle
Argument: the word "Legend". Default is boldface followed
by a colon.
\\styleliteralblock
Argument: none, but encloses a verbatim section. Default is null.
\\styleoption
Arguments: the option string, the option argument(s) (may be
null). Default is teletype option string followed by
non-breaking space and italic option argument(s).
\\stylesidebartitles
Arguments: the title for a sidebar, the subtitle for a sidebar
(may be null). Default is centered boldface title followed by
centered italic subtitle. Is not invoked if sidebar's title
is null.
\\styleterm
Arguments: description term, description classifier (may be null).
Default is italic term followed by italic ": classifier" if
the classifier is not null.
\\styletitle
Arguments: the document's title, the document's subtitle (may
be null). Default is the title followed by ":", a newline and
the subtitle in a smaller font, if the subtitle is not null.
\\styletopictitle
Arguments: the title of a topic. Default is bold face.
It is probably easiest to define these by first creating a .tex
file with the writer and then copying the definition you want to
modify from the top of the generated file into your style file,
changing the "\\newcommand" to "\\renewcommand" and supplanting
the definition.
=end Description
=end reST
=cut
sub BEGIN {
# My -W flags
use vars qw($author_skip $caption $chapter $cloak_email_addresses
$documentclass $documentclass_opts $footer
$footnote_links $image_ext $index $inputs
$max_unwrapped_colsize $omit_docinfo $packages $sidebar);
# Static globals
use vars qw(%ENUMS @ROMAN @ROMANS %FOOTNOTE_SYMBOLS);
# Run-time globals
use vars qw(@SECTION_LEVELS @AUTHORS $DATE $DOCINFO $PREAMBLE
$SUBTITLE @LITBOXES @CITATIONS %SYSTEM_MESSAGE
%PACKAGES $BEGIN_DONE %COMPOUND_CLASSES %INLINE_CLASSES);
# Defaults for -W arguments
$author_skip = '12pt' unless defined $author_skip;
$author_skip = $$author_skip if ref $author_skip eq 'SCALAR';
$documentclass = 'article' unless defined $documentclass;
$documentclass_opts = '' unless defined $documentclass_opts;
$max_unwrapped_colsize = 8 unless defined $max_unwrapped_colsize;
$caption = 'after' unless defined $caption;
$cloak_email_addresses = '' unless defined $cloak_email_addresses;
$sidebar = 'float' unless defined $sidebar;
$footer = 0 unless defined $footer;
$footnote_links = 0 unless defined $footnote_links;
$index = 0 unless defined $index;
$inputs = '' unless defined $inputs;
$image_ext = "eps" unless defined $image_ext;
my @packages = split /,/, $packages if defined $packages;
$PACKAGES{$_} = 1 foreach @packages;
my @footnote_symbols = @Text::Restructured::docutils::transforms::references::FOOTNOTE_SYMBOLS;
@FOOTNOTE_SYMBOLS{@footnote_symbols} = (1 .. @footnote_symbols);
}
# Converts a symbol-style footnote to its numeric equivalent
# Inputs: label
# Outputs: number or undef
sub FootnoteSymbol {
my ($label) = @_;
$label =~ s/\\\#/\#/g;
my $sym = $FOOTNOTE_SYMBOLS{substr($label, 0, 1)};
return $sym ? ((keys %FOOTNOTE_SYMBOLS) * (length($label)-1) + $sym) :
undef;
}
# Makes a string safe by quoting special characters
sub ProtectString {
my ($string, $parsed_lit) = @_;
$_ = $string;
# Do some substitutions
# Do this one first...
s/([\\{}])/$1 eq "\\" ? '\ensuremath{\backslash}' : "\\$1"/ge;
s/([\$&%\#_~])/\\$1/g;
s/(\^)/\\textasciicircum /g;
s/([<>|])/\\ensuremath{$1}/g;
if (! $parsed_lit) {
s/ <= / \\ensuremath{\\leq} /g;
s/ >= / \\ensuremath{\\geq} /g;
s/ == / \\ensuremath{=} /g;
s/ != / \\ensuremath{\\neq} /g;
s/(\A|\s)(-\d+)/$1\\ensuremath{$2}/g;
s/(\s)\'/$1\`/gm;
s/(?![\(\[\{])(\S)\"/$1\'\'/g; # Needs matching } to parse schema
s/\"(\S)/\`\`$1/g;
}
return $_;
}
# Checks to see if the DOM has any associated classes with it, and if so,
# generates a styling command around the string
sub StyleClasses {
my ($dom, $str) = @_;
my $class = $dom->{attr}{classes} ? $dom->{attr}{classes}[0] : '';
$class =~ tr/a-zA-Z0-9//cd;
if ($class) {
$INLINE_CLASSES{$class} = 1;
return "\\styleinline$class\{$str}";
}
return $str;
}
sub Warn {
print STDERR "latex writer: Warning: $_[0]\n";
}
# This phase marks elements as being contained within footnotes or literals
# and computes nesting levels for sections and enumerated_lists.
phase MARK = {
sub document = { # MARK
my ($dom, $str) = @_;
my %info = ('bullet'=>0, 'enum'=>0, 'foot'=>0, 'nest'=>0, 'lit'=>0);
$dom->Recurse
(
sub {
my ($dom, $when, $info) = @_;
my $tag = $dom->tag;
if ($tag eq 'section') {
if ($when eq 'pre') {
$dom->{nest} = $info->{nest};
$info->{nest}++;
}
else { $info->{nest}--; }
}
elsif ($tag eq 'enumerated_list') {
if ($when eq 'pre') {
$info->{enum}++;
$dom->{nest} = $info->{enum};
}
else { $info->{enum}--; }
}
elsif ($tag eq 'bullet_list') {
if ($when eq 'pre') {
$info->{bullet}++;
$dom->{nest} = $info->{bullet};
}
else { $info->{bullet}--; }
}
elsif ($tag eq 'footnote') {
$info->{foot} += $when eq 'pre' ? 1 : -1;
}
elsif ($tag eq 'footnote_reference') {
$dom->{infootnote} = $info->{foot};
}
elsif ($tag =~ /^(literal|doctest)/) {
$info->{lit} += $when eq 'pre' ? 1 : -1;
}
elsif ($tag eq 'parsed_literal') {
$info->{parsedlit} += $when eq 'pre' ? 1 : -1;
}
elsif ($tag eq '#PCDATA') {
($dom->{inliteral}) =
(grep($info->{$_}, qw(lit parsedlit)), 0);
}
return 0;
}
, 'both', \%info);
return;
}
}
phase PREPROCESS = {
sub \#PCDATA = { # PREPROCESS
my ($dom, $str) = @_;
my $parent = $dom->parent();
$_ = $dom->{text};
if (! $dom->{inliteral} &&
$parent->tag !~ /raw|option_string/) {
my $indx = $parent->index($dom);
s/^\"/\'\'/ if $indx > 0;
s/\"$/\`\`/ if $indx < $parent->num_contents - 1;
$_ = ProtectString($_);
}
elsif ($dom->{inliteral} eq 'parsedlit') {
$_ = ProtectString($_, 'parsedlit');
}
return $_;
}
# sub section|topic = { # PREPROCESS
# my ($dom, $str) = @_;
# # Remove title from contents
# if ($dom->first->tag eq 'title') {
# my $titledom = shift $dom->contents;
# $dom->{title} = $titledom->{val};
# chomp $dom->{title};
# }
# }
sub title|subtitle|caption|label = { # PREPROCESS
my ($dom, $str) = @_;
return $str;
}
sub figure = { # PREPROCESS
my ($dom, $str) = @_;
my $d;
while ($d = shift @{$dom->{content}}) {
# uncoverable branch false count:3 note:Defensive programming
if ($d->tag eq 'image') {
$dom->{image} = $d->{image};
}
elsif ($d->tag eq 'caption') {
$dom->{caption} = $d;
}
elsif ($d->tag eq 'legend') {
$dom->{legend} = $d;
}
}
@{$dom->{content}} =
grep(defined $_,(map($dom->{$_}, qw(caption legend))));
return;
}
sub image = { # PREPROCESS
my ($dom, $str) = @_;
# If the type of the image is not $image_ext, see if we can by
# any chance convert it.
my $uri = $dom->{attr}{uri};
$uri =~ m!([^/]+\.)[^/]+$!;
my $local_uri = "$1$image_ext";
if ($uri !~ /\.$image_ext$/ && $uri ne $local_uri) {
`convert $uri $local_uri > /dev/null 2>&1`
if -r $uri && ! -r $local_uri;
$uri = $local_uri if -r $local_uri;
}
my %attr = (map(($_,"$dom->{attr}{$_}" .
($dom->{attr}{$_} =~ /[a-z]/ ? '' : 'pt')),
grep(defined $dom->{attr}{$_},
qw(height width))),
map(($_,$dom->{attr}{$_}/100),
grep(defined $dom->{attr}{$_},
qw(scale)))
);
my $attr = %attr ?
"[" . join(',',map("$_=$attr{$_}",keys %attr)) . "]" : '';
my $image = $uri =~ /\.$image_ext$/ ?
do { $PACKAGES{graphicx} = 1; "\\includegraphics$attr\{$uri}" }
: "{\\tt $uri}";
$dom->{image} = $image;
return;
}
sub tgroup = { # PREPROCESS
my ($dom, $str) = @_;
my @newcontents;
foreach (@{$dom->{content}}) {
if ($_->tag eq 'colspec') {
my $colwidth = $_->{attr}{colwidth};
$dom->{totwidth} += $colwidth;
push @{$dom->{colwidths}}, $colwidth;
}
else {
push @newcontents, $_;
}
}
@{$dom->{content}} = @newcontents;
}
sub thead|tbody = { # PREPROCESS
my ($dom, $str) = @_;
my $parent = $dom->parent();
# Create an array with one entry per column for keeping track of
# entries that are covered from previous rows.
$dom->{borrows} = [(0) x $parent->{attr}{cols}];
$dom->{borrowcols} = [(0) x $parent->{attr}{cols}];
}
sub footnote = { # PREPROCESS
my ($dom, $str) = @_;
my $label = shift @{$dom->{content}};
$dom->{label} = $label->{val};
return;
}
sub footnote_reference = { # PREPROCESS
my ($dom, $str) = @_;
$dom->{label} = $str;
@{$dom->{content}} = ();
return if $dom->{infootnote};
my $footnote = $dom->{resolved};
if (defined $footnote && ! $footnote->{'.latexref'}) {
my @content = @{$footnote->{content}};
# uncoverable branch false note:Assert has label
shift @content if $content[0]->tag eq 'label';
@{$dom->{content}} = @content;
$footnote->{'.latexref'} = 1;
}
# May need to trim trailing spaces from predecessor
my $parent = $dom->parent();
my $indx = $parent->index($dom);
if ($indx > 0 && $parent->child($indx-1)->tag eq '#PCDATA') {
$parent->child($indx-1)->{val} =~ s/ *$//;
}
return;
}
sub citation = { # PREPROCESS
my ($dom, $str) = @_;
push @CITATIONS, $dom;
# Remove label from contents
# uncoverable branch false note:Assert has label
if ($dom->first->tag eq 'label') {
my $labeldom = shift @{$dom->{content}};
$dom->{label} = $labeldom->{val};
chomp $dom->{label};
}
return;
}
sub sidebar = { # PREPROCESS
my ($dom, $str) = @_;
$dom->{title} = shift @{$dom->{content}}
if $dom->first->tag eq 'title';
$dom->{subtitle} = shift @{$dom->{content}}
if $dom->first->tag eq 'subtitle';
return;
}
sub system_message = { # PREPROCESS
my ($dom, $str) = @_;
$SYSTEM_MESSAGE{$dom->{attr}{ids}[0]} = $dom
if defined $dom->{attr}{ids}[0];
return "$dom->{attr}{type}: $str";
}
# For system_message
sub literal_block = { # PREPROCESS
my ($dom, $str) = @_;
return "\\begin{verbatim}\n$str\\end{verbatim}\n";
}
# For system_message
sub paragraph = { # PREPROCESS
my ($dom, $str) = @_;
return "$str";
}
sub raw|inline = { # PREPROCESS
my ($dom, $str) = @_;
return unless $dom->{attr}{format} &&
$dom->{attr}{format} =~ /\blatex\b/;
chomp $str;
return $str;
}
sub document = { # PREPROCESS
my ($dom, $str) = @_;
@SECTION_LEVELS =
qw(section subsection subsubsection paragraph subparagraph);
unshift @SECTION_LEVELS, 'chapter'
unless ($documentclass eq 'article' ||
defined $chapter && $chapter == 0) ;
# Put the document's title/subtitle under the document's docinfo
# (create the docinfo if it does not exist)
my $docinfo;
foreach (@{$dom->{content}}) {
if ($_->tag eq 'docinfo') {
$docinfo = $_;
last;
}
}
my $created_docinfo = ! $docinfo;
$docinfo ||= Text::Restructured::DOM->new('docinfo');
if ($dom->first->tag eq 'title') {
$docinfo->splice(0, 0, shift @{$dom->{content}});
$dom->{_latex}{has_title} = 1;
}
# Grab the subtitle if it exists
$docinfo->splice(1, 0, shift @{$dom->{content}})
if $dom->first->tag eq 'subtitle';
$dom->splice(0, 0, $docinfo) if $created_docinfo;
return;
}
}
phase PROCESS = {
sub \#PCDATA = { # PROCESS
my ($dom, $str) = @_;
return $dom->{val};
}
# Return the generated section number, without the garbage
# characters generated at the end.
sub generated = { # PROCESS
return;
}
# Store the section title in the dom.
sub section = { # PROCESS
my ($dom, $str) = @_;
return if defined $dom->{attr}{classes} &&
$dom->{attr}{classes}[0] eq 'system-messages';
my $nest = $dom->{nest};
my $section = $SECTION_LEVELS[$nest > $#SECTION_LEVELS ?
$#SECTION_LEVELS : $nest];
# Remove title from contents
# uncoverable branch false note:Assert title is present
if ($dom->first->tag eq 'title') {
my $titledom = shift @{$dom->{content}};
$dom->{title} = $titledom->{val};
chomp $dom->{title};
}
$str = join('',map(defined $_->{val} ? $_->{val} : '',
@{$dom->{content}}));
return "\n\\$section\{$dom->{title}}\n$str";
}
sub author = { # PROCESS
my ($dom, $str) = @_;
chomp $str;
push @AUTHORS, $str;
return;
}
sub docinfo = { # PROCESS
my ($dom, $str) = @_;
my $child;
my %docinfo;
my @fields;
foreach $child (@{$dom->{content}}) {
if ($child->tag ne 'field') {
$docinfo{$child->tag} = $child->{val};
}
else {
push @fields, $child->{val};
}
}
my $title = $docinfo{title} || '';
chomp $title;
my $st = $docinfo{subtitle} || '';
chomp $st;
$DOCINFO .= "\\title{\\styletitle{$title}{$st}}\n" if $title ne '';
my $authors = join(' \hfill ',map("\\styleauthor{$_}",@AUTHORS));
my @address = grep(defined $_,
map($docinfo{$_},
qw(organization address contact)));
my $address = sprintf "\\styleaddress{%s}", join("\\\\\n",@address)
if @address;
my @info = grep(defined $_,
map($docinfo{$_},
qw(copyright status revision version)));
my $info = join("\\\\[0pt]\n",
# map("\\parbox{\\textwidth}{\\center $_}",
map("\\styledocinfo{$_}",
@info,@fields))
if @info+@fields;
$author_skip .= 'pt' if $author_skip =~ /^\d+$/;
my $skip = $author_skip ? "[$author_skip]" : '';
my $author = join("\\\\$skip\n",
grep(defined $_, ($authors, $address, $info)));
$DOCINFO .= "\\author{$author}\n" if $author;
$DOCINFO .= "\\date{$DATE}\n" if $DATE;
return ;
}
sub date = { # PROCESS
my ($dom, $str) = @_;
($DATE) = $dom->first->{text} =~ m!(\d+[-/]\d+[-/]\d+)!;
return;
}
sub title|organization|contact = { # PROCESS
my ($dom, $str) = @_;
my $parent = $dom->parent();
chomp $str;
if ($parent->tag eq 'topic') {
$parent->{title} = $str;
return;
}
return $str;
}
sub copyright = { # PROCESS
my ($dom, $str) = @_;
chomp $str;
$str =~ s/\(C\)(opyright)?/\\copyright/g;
return $str;
}
sub status|revision|version = { # PROCESS
my ($dom, $str) = @_;
chomp $str;
my $tag = $dom->tag;
substr($tag,0,1) =~ tr/a-z/A-Z/;
return "\\stylefieldname{$tag} $str";
}
sub address = { # PROCESS
my ($dom, $str) = @_;
my @lines = split(/\n/, $str);
return join "\\\\\n", @lines;
}
sub field = { # PROCESS
my ($dom, $str) = @_;
my $parent = $dom->parent();
my $val = $dom->child(1)->{val};
chomp $val;
$val =~ s/\n\n/\\\\\n/g if $parent->tag ne 'field_list';
my $fieldname = $dom->first->{val};
chomp $fieldname;
$fieldname =~ s/^\n//;
return $parent->tag eq 'field_list' ?
"\\item[\\stylefieldname{$fieldname}] $val\n" :
"\\stylefieldname{$fieldname} $val";
}
sub field_name = { # PROCESS
my ($dom, $str) = @_;
substr($str,0,1) =~ tr/a-z/A-Z/;
return $str;
}
sub field_body = { # PROCESS
my ($dom, $str) = @_;
$str =~ s/^\n//;
return $str;
}
sub substitution_definition|authors = { # PROCESS
my ($dom, $str) = @_;
return;
}
sub comment = { # PROCESS
my ($dom, $str) = @_;
$str =~ s/^/% /gm;
$str .= "\n" unless $str =~ /\n$/;
$str;
}
sub paragraph = { # PROCESS
my ($dom, $str) = @_;
$str .= "\n" unless $str =~ /\n$/;
return "\n$str";
}
sub topic = { # PROCESS
my ($dom, $str) = @_;
$str =~ s/^\n//;
my $class = $dom->{attr}{classes} ? $dom->{attr}{classes}[0] : '';
if ($class eq 'abstract') {
my @topic =
("\\begin{abstract}\n",
$str,
"\\end{abstract}\n",
);
return join '', @topic;
}
elsif ($class eq 'dedication') {
my @topic =
("\\$SECTION_LEVELS[0]*{Dedication}\n",
$str,
);
return join '', @topic;
}
elsif ($class eq 'contents') {
return "\\tableofcontents\n";
}
Warn("unsupported type of topic: $class") if $class;
return "\\styletopictitle{$dom->{title}} $str"
}
sub citation_reference { # PROCESS
my ($dom, $str) = @_;
return "\\cite{$str}";
}
sub list_item { # PROCESS
my ($dom, $str) = @_;
my $parent = $dom->parent();
my $list_type = $parent->tag;
chomp $str;
return "\\item $str\n";
}
sub enumerated_list { # PROCESS
my ($dom, $str) = @_;
my @list;
my $nest = $dom->{nest};
my $roman = roman($nest);
my $counter = "enum$roman";
use vars qw(@ENUM_USED);
# Since LaTeX does not support enumerated lists
# nested more than 4 deep, create new counters for deeper ones.
my $predef = $nest > 4 && ! $ENUM_USED[$nest]++ ?
"\\newcounter{$counter}" : '';
my $start = defined $dom->{attr}{start} ?
"\\setcounter{$counter}{${\scalar($dom->{attr}{start}-1)}}" : '';
my $enum = $ENUMS{$dom->{attr}{enumtype}};
my ($pfx, $sfx) =
map(defined $dom->{attr}{$_} ? $dom->{attr}{$_} : '',
qw(prefix suffix));
@list =
("$predef\\renewcommand{\\the$counter}{\\$enum\{$counter}}\\begin{list}{$pfx\\the$counter$sfx}{\\usecounter{$counter}}$start\n",
$str,
"\\end{list}\n");
return join '', @list;
BEGIN {
@ROMAN = ('', 'i', 'ii', 'iii', 'iv');
%ENUMS = ('arabic'=>'arabic', 'loweralpha'=>'alph',
'lowerroman'=>'roman', 'upperalpha'=>'Alph',
'upperroman'=>'Roman');
@ROMANS = (['', qw(i ii iii iv v vi vii viii ix)],
['', qw(x xx xxx xl l lx lxx lxxx xc)],
['', qw(c cc ccc cd d dc dcc dccc cm)],
['', qw(m mm mmm)]);
}
# Given a number, returns a Roman numeral representing the number
sub roman {
my ($num) = @_;
# uncoverable branch true note:Defensive programming
return 0 if $num < 1 or $num > 3999;
my @digits = reverse split(//,$num);
return join('',
reverse map($ROMANS[$_][$digits[$_]], 0 .. $#digits));
}
}
sub bullet_list { # PROCESS
my ($dom, $str) = @_;
my @list;
if ($dom->{nest} > 4) {
@list = ("\\begin{list}{*}{}\n",
$str,
"\\end{list}\n",
);
}
else {
my $counter = "[\\labelitem$ROMAN[$dom->{nest}]]";
@list = ("\\begin{itemize}\n",
$str,
"\\end{itemize}\n");
}
return join '', @list;
}
sub definition_list_item { # PROCESS
my ($dom, $str) = @_;
my ($term, $classifier, $definition) = ('') x 3;
foreach (@{$dom->{content}}) {
if ($_->tag eq 'term') {
$term = $_->{val};
}
elsif ($_->tag eq 'classifier') {
$classifier = $_->{val};
}
else {
$definition = $_->{val};
chomp $definition;
}
}
return "\\item [\\styleterm{$term}{$classifier}] \\mbox{} \\\\ $definition\n";
}
sub definition_list { # PROCESS
my ($dom, $str) = @_;
my @list = ("\\begin{description}\n",
$str,
"\\end{description}\n",
);
return join '', @list;
}
sub field_list { # PROCESS
my ($dom, $str) = @_;
my @list = ("\\begin{description}\n",
$str,
"\\end{description}\n",
);
return join '', @list;
}
sub option_list { # PROCESS
my ($dom, $str) = @_;
my @list = (
"\\begin{description}\n",
$str,
"\\end{description}\n\n",
);
return join '', @list;
}
sub option_list_item { # PROCESS
my ($dom, $str) = @_;
my ($opt, $desc) = map($_->{val}, @{$dom->{content}});
chomp $desc;
return "\\item [$opt] \\mbox{} \\\\ $desc\n";
}
sub option_group { # PROCESS
my ($dom, $str) = @_;
$str = join(', ', map($_->{val}, @{$dom->{content}}))
if @{$dom->{content}} > 1;
return $str;
# return "\\styleoptstring{$str}";
}
sub option { # PROCESS
my ($dom, $str) = @_;
my @vals = map($_->{val},@{$dom->{content}});
my $opt = ProtectString(shift @vals);
my $args = join ' ', @vals;
return "\\styleoption{$opt}{$args}";
}
sub option_argument { # PROCESS
my ($dom, $str) = @_;
return $str;
# return "\\styleoptarg{$str}";
}
# These pups just return whatever their contents returned
sub term|classifier|definition|option_string|description|entry|caption|legend|decoration { # PROCESS
my ($dom, $str) = @_;
return $str;
}
sub block_quote { # PROCESS
my ($dom, $str) = @_;
return <<"EOS"
\\begin{quote}
$str
\\end{quote}
EOS
;
}
sub attribution { # PROCESS
my ($dom, $str) = @_;
chomp $str;
return <<"EOS"
\\begin{flushright}
---$str
\\end{flushright}
EOS
;
}
sub subscript { # PROCESS
my ($dom, $str) = @_;
return StyleClasses($dom, "\\ensuremath{_{$str}}");
}
sub superscript { # PROCESS
my ($dom, $str) = @_;
return StyleClasses($dom, "\\ensuremath{^{$str}}");
}
sub reference = { # PROCESS
my ($dom, $str) = @_;
chomp $str;
if ($cloak_email_addresses &&
($dom->{attr}{refuri} || '') =~ /^mailto:/) {
$str =~ s/\./\\raisebox{-.2em}{\$\\cdot\$}/g;
$str =~ s/(\@)/\$$1\$/g;
}
my $uri = $footnote_links && defined $dom->{attr}{refuri} ?
"\\renewcommand{\\thefootnote}{\\alph{footnote}}\\footnote{$dom->{attr}{refuri}}\\renewcommand{\\thefootnote}{\\arabic{footnote}}" : '';
return "$str$uri";
}
sub emphasis|title_reference = { # PROCESS
my ($dom, $str) = @_;
return StyleClasses($dom, "{\\em $str}");
}
sub strong = { # PROCESS
my ($dom, $str) = @_;
return StyleClasses($dom, "{\\bf $str}");
}
sub (?:doctest|literal)_block = { # PROCESS
my ($dom, $str) = @_;
return "{\\styleliteralblock\\begin{verbatim}\n$str\\end{verbatim}}\n";
}
sub literal = { # PROCESS
my ($dom, $str) = @_;
# The problem with \verb in LaTeX is that is cannot be used
# any the argument to any command, such as footnotes, column
# spans in tables, etc. SO, we need to predefine every
# literal string in its own box and then just use the box at
# the point where it occurs. We also need to handle each line
# of the string separately, to preserve line breaks.
my @str = split(/\n/, $str);
my @uses;
use vars qw($LAST_LITERAL %LITID);
foreach (@str) {
my $id;
if (! $LITID{$_}) {
$id = "\\lit" . roman(++$LAST_LITERAL);
$LITID{$_} = $id;
push @LITBOXES, "\\newsavebox{$id}\\begin{lrbox}{$id}\\verb\331$_\331\\end{lrbox}\n";
}
else { $id = $LITID{$_} }
push @uses, "\\usebox{$id}";
}
return join("\\\\\n",@uses);
}
sub target = { # PROCESS
my ($dom, $str) = @_;
return unless $str ne '';
return "\\index{$str}$str";
}
sub attention|caution|danger|error|hint|important|note|tip|warning { # PROCESS
my ($dom, $str) = @_;
my $title = $dom->tag;
substr($title,0,1) =~ tr/a-z/A-Z/;
$str .= "\n" unless $str =~ /\n$/;
my @list = ("\n\\noindent\\fbox{\\parbox{\\linewidth}{\n",
"\\styleadmonitiontitle{$title}\n",
$str,
,"}}\n",
);
return join '', @list;
}
sub image = { # PROCESS
my ($dom, $str) = @_;
return $dom->{image};
}
sub figure = { # PROCESS
my ($dom, $str) = @_;
my $image = $dom->{image} || '';
my $class = $dom->{attr}{classes} ? $dom->{attr}{classes}[0] : '';
my $env = $class || 'figure';
my @figure =
("\\begin{$env}\n",
defined $dom->{caption} && $caption eq 'before' ?
"\\caption{$dom->{caption}{val}}\n" : '',
"\\centerline{$image}\n",
$dom->{legend} ? "\\stylelegendtitle{Legend}\n$dom->{legend}{val}" : '',
defined $dom->{caption} && $caption ne 'before' ?
"\\caption{$dom->{caption}{val}}\n" : '',
"\\end{$env}\n",
);
return join '', @figure;
}
sub table = { # PROCESS
my ($dom, $str) = @_;
# Create a table/figure environment if there is a title
#my %content;
my @content = grep defined $_->{val}, $dom->contents;
my $title = $content[0]->tag eq 'title' ? shift @content : '';
my ($has_table) = grep $_->tag eq 'tgroup', @content;
my $env = $has_table ? 'table' : 'figure';
#$content{$_->tag} = $_ foreach $dom->contents;
my (@out1, @out2);
if ($title) {
@out1 = ("\\begin{$env}\n",
"\\center\n");
@out2 = ("\\end{$env}\n");
if ($caption eq 'before') {
push @out1, "\\caption{$title->{val}}\n";
}
else {
unshift @out2, "\\caption{$title->{val}}\n";
}
}
return join '', @out1, map($_->{val}, @content), @out2;
}
sub tgroup = { # PROCESS
my ($dom, $str) = @_;
# Need to compute our column widths
my $cols = $dom->{attr}{cols};
# my @colspecs = map(sprintf("p{%.4g\\dimen1}|",$_/$dom->{totwidth}),
# @{$dom->{colwidths}});
# $colspecs[0] = "|$colspecs[0]";
# my $colspec = join '',@colspecs;
my $colspec = "|" . ("l|" x $cols);
my @tgroup = ("\\par\\noindent\\dimen1=\\columnwidth\n",
"\\advance\\dimen1 -${\scalar(2*$cols)}\\tabcolsep\n",
"\\begin{tabular}{$colspec}\n",
"\\hline\n",
join("\\hline\n",
map($_->{val}, @{$dom->{content}})),
"\\end{tabular}\n",
);
return join '', @tgroup;
}
sub thead|tbody = { # PROCESS
my ($dom, $str) = @_;
return join '', map("$_->{val}", @{$dom->{content}});
}
sub row = { # PROCESS
my ($dom, $str) = @_;
my $parent = $dom->parent();
my @content = @{$dom->{content}};
my $borrows = $parent->{borrows} ;
my $borrowcols = $parent->{borrowcols} ;
# uncoverable branch false note:Parent produces borrows
my $cols = defined $borrows ? @$borrows : -1;
my $i;
my @entries;
for ($i=0; $i < $cols; $i++) {
if ($borrows->[$i]) {
if ($borrowcols->[$i] > 1) {
push @entries, "\\multicolumn{$borrowcols->[$i]}{l|}{}";
$i += $borrowcols->[$i]-1;
}
else {
push @entries, '';
}
}
else {
my $entry = shift @content;
my $attr = $entry->{attr};
my $rows = 1 + ($attr->{morerows} || 0);
my $cols = 1 + ($attr->{morecols} || 0);
my $j;
for ($j=0; $j < $cols; $j++) {
$borrows->[$i+$j] += $rows;
}
$borrowcols->[$i] = $rows if $cols > 1;
if ($attr->{align} && $attr->{align} !~ /^l/) {
my $colspec = ($i ? '' : '|') .
substr($attr->{align},0,1) . '|';
push @entries,
"\\multicolumn{$cols}{$colspec}{$entry->{val}}";
}
elsif ($cols > 1 || @{$entry->{content}} > 1 ||
length($entry->{val}) > $max_unwrapped_colsize+2) {
my $gramps = $parent->parent();
my $colwidths = $gramps->{colwidths};
my $totwidth = $gramps->{totwidth};
my $mywidth = 0;
for ($j=0; $j < $cols; $j++) {
$mywidth += $colwidths->[$i+$j];
}
my $colspec =
sprintf("%sp{%.4g\\dimen1}|", ($i == 0 ? '|' : ''),
$mywidth/$totwidth);
push @entries,
"\\multicolumn{$cols}{$colspec}{$entry->{val}}";
}
else {
push @entries, $entry->{val};
}
$i += ($attr->{morecols} || 0);
}
}
# Decrement each of the borrows numbers
@$borrows = map(--$_, @$borrows);
# Compute the hline/cline based upon the borrows
my $hline = "\\hline";
if (grep ($_,@$borrows)) {
# Need a series of cline's
my @lines;
my $i;
for ($i=0; $i < @$borrows; $i++) {
next if $borrows->[$i];
if (@lines && $i == $lines[-1][1]) {
$lines[-1][1]++;
}
else {
push @lines, [$i+1, $i+1];
}
}
$hline = join ' ', map("\\cline{$_->[0]-$_->[1]}", @lines);
}
return join(' & ', @entries) . " \\\\ $hline\n";
}
sub footnote_reference = { # PROCESS
my ($dom, $str) = @_;
# uncoverable branch false note:Assert has label
my $label = defined $dom->{label} ? $dom->{label}: '';
my $sym = FootnoteSymbol($label);
$label = $sym if $sym;
my $footnote = $str ne '' ? "\\footnote[$label]{$str}" :
"\\footnotemark[$label]";
return "\\renewcommand{\\thefootnote}{\\fnsymbol{footnote}}$footnote\\renewcommand{\\thefootnote}{\\arabic{footnote}}"
if $sym;
return "$footnote";
}
sub footnote = { # PROCESS
my ($dom, $str) = @_;
return if $dom->{'.latexref'};
# uncoverable branch false note:Assert has label
my $label = defined $dom->{label} ? $dom->{label} : '';
my $sym = FootnoteSymbol($label);
return "\\renewcommand{\\thefootnote}{\\fnsymbol{footnote}}\\footnotetext[$sym]{$str}\\renewcommand{\\thefootnote}{\\arabic{footnote}}"
if $sym;
return "\\footnotetext[$label]{$str}";
}
sub citation = { # PROCESS
my ($dom, $str) = @_;
my $n_citations = @CITATIONS;
use vars qw($DONE_CITATIONS);
if (++$DONE_CITATIONS == $n_citations) {
my @citations =
("\\begin{thebibliography}{$n_citations}\n",
map(do {my $bib = $_; "\\bibitem{$bib->{label}} " .
join('',map($_->{val},@{$bib->{content}}));},
@CITATIONS),
"\\end{thebibliography}\n",
);
return join '',@citations;
}
return;
}
sub transition = { # PROCESS
return "\\vspace{1em}\\hrule\\vspace{1em}\n\\par\\noindent";
}
sub parsed_literal = { # PROCESS
my ($dom, $str) = @_;
$str =~ s/^[ \t]+$//gm;
$str =~ s/(.)\n(?=(\n))?/
"$1\\\\" . ($2 ? "[\\baselineskip]" : '') . "\n"/eg;
$str =~ s/^(?!\\end)(.)\n(?!\n)/$1\\\\\n/gm;
$str =~ s/ /~/g;
$str =~ s/(\\[a-z]\w+)~/$1 /gi;
my @verse = ("\\begin{verse}{\\tt\\styleliteralblock\n",
$str,
"}\\end{verse}\n");
return join '', @verse;
}
sub line_block = { # PROCESS
my ($dom, $str) = @_;
my @verse = ("\\begin{verse}\n",
$str,
"\\end{verse}\n\n");
return join '', @verse;
}
sub line { # PROCESS
my ($dom, $str) = @_;
if ($str eq '') {
$str = "\\vspace{\\baselineskip}";
}
else {
chomp $str;
$str =~ s/ /~/g;
$str =~ s/(\\\w+)~/$1 /g;
$str = "$str\\\\";
}
return "$str\n";
}
sub sidebar = { # PROCESS
my ($dom, $str) = @_;
my ($sb_start, $sb_end) = $sidebar eq 'margin' ?
('\marginpar{\fbox{\parbox[t]{\marginparwidth}{', '}}}') :
('\begin{figure}\begin{center}\fbox{\fbox{\parbox{.8\columnwidth}{',
'}}}\end{center}\end{figure}');
my ($title, $subtitle) = map $dom->{$_}{val} || '', qw(title subtitle);
my @sidebar = ("$sb_start\n",
$title ne '' ?
"\\stylesidebartitles{$title}{$subtitle\\fi}\n" : '',
$str,
"$sb_end\n",
);
return join '', @sidebar;
}
sub inline = { # PROCESS
my ($dom, $str) = @_;
return StyleClasses($dom, $str);
}
sub compound = { # PROCESS
my ($dom, $str) = @_;
my $class = $dom->{attr}{classes} ? $dom->{attr}{classes}[0] : '';
if ($class) {
$COMPOUND_CLASSES{$class} = 1;
return "\\styleclass$class\{$str}\n";
}
return $str;
}
sub problematic = { # PROCESS
my ($dom, $str) = @_;
my @str = split(/\n/, $str);
my $sm = $SYSTEM_MESSAGE{$dom->{attr}{refid}};
my $message = $sm->{val} || '';
my $fn = "\\renewcommand{\\thefootnote}{\\alph{footnote}}\\footnote{$message}\\renewcommand{\\thefootnote}{\\arabic{footnote}}";
return join("\n",map("{\\tt $_}$fn", @str));
}
sub system_message = { # PROCESS
my ($dom, $str) = @_;
return;
}
sub footer = { # PROCESS
my ($dom, $str) = @_;
return $footer ?
"\\vspace{1em}\\hrule\\vspace{1em}\n\\par\\noindent{\\small $str}\n"
: '';
}
sub raw = { # PROCESS
my ($dom, $str) = @_;
return unless $dom->{attr}{format} =~ /\blatex\b/;
chomp $str;
my $uri = $footnote_links && defined $dom->{attr}{refuri} ?
"\\renewcommand{\\thefootnote}{\\alph{footnote}}\\footnote{$dom->{attr}{refuri}}\\renewcommand{\\thefootnote}{\\arabic{footnote}}" : '';
if ($dom->{attr}{head}) {
$PREAMBLE .= "$str$uri\n";
return;
}
return "$str$uri\n";
}
sub document = { # PROCESS
my ($dom, $str) = @_;
my @inputs = split(',',$inputs);
my $ind_file = $dom->{TOP_FILE};
$ind_file =~ s|\.[^/]+$|.idx|;
my $decoration;
# uncoverable branch false note:Assert PREPROCESS creates docinfo
shift @{$dom->{content}} if $dom->first->tag eq 'docinfo';
if ($dom->first->tag eq 'decoration') {
$decoration = $dom->first->{val};
shift @{$dom->{content}};
$str = join('', map($_->{val} || '', @{$dom->{content}}));
}
my @doc = ("\\documentclass" . ($documentclass_opts ne '' ?
"[$documentclass_opts]" : '') .
"{$documentclass}\n",
"\\usepackage{latexsym}\n",
"% Style definitions that can be overridden in a user\n",
"% input file\n",
"\\def\\ifEq#1#2{\\def\\testa{#1}\\def\\testb{#2}\\ifx\\testa\\testb}\n",
"\\newcommand{\\styleaddress}[1]{#1}\n",
"\\newcommand{\\styleadmonitiontitle}[1]{\\centerline{\\bf #1:}}\n",
"\\newcommand{\\styleauthor}[1]{{\\em #1}}\n",
map("\\newcommand{\\styleclass$_}[1]{#1}\n",
sort keys %COMPOUND_CLASSES),
map("\\newcommand{\\styleinline$_}[1]{#1}\n",
sort keys %INLINE_CLASSES),
"\\newcommand{\\styledocinfo}[1]{\\setbox0\\hbox{\\parbox{.7\\textwidth}{\\global\\dimen255=\\baselineskip #1}}\\ifdim \\ht0 < \\dimen255 \\centerline{\\hbox{#1}}\\else\\copy0 \\fi }\n",
# "\\newcommand{\\styledocinfo}[1]{\\parbox{\\textwidth}{\\center #1}}\n",
"\\newcommand{\\stylefieldname}[1]{{\\bf #1:}}\n",
"\\newcommand{\\stylelegendtitle}[1]{{\\bf #1:}}\n",
"\\newcommand{\\styleliteralblock}[0]{}\n",
"\\newcommand{\\styleoption}[2]{{\\tt #1}\\ifEq{#2}{}\\else ~{\\it #2}\\fi}\n",
"\\newcommand{\\stylesidebartitles}[2]{\\begin{center}{\\bf #1}\\end{center}\\ifEq{#2}{}\\else\\begin{center}{\\it #2}\\end{center}}\n",
"\\newcommand{\\styleterm}[2]{{\\it #1\\ifEq{#2}{}\\else: #2\\fi}}\n",
"\\newcommand{\\styletitle}[2]{#1\\ifEq{#2}{}\\else:\\\\ {\\Large #2}\\fi}\n",
"\\newcommand{\\styletopictitle}[1]{{\\bf #1}}\n",
map("\\input{$_}\n",@inputs),
map("\\usepackage{$_}\n", sort keys %PACKAGES),
$DOCINFO,
$PREAMBLE,
"\\frenchspacing\n",
$index ? "\\makeindex\n" : '',
"\\begin{document}\n",
$dom->{_latex}{has_title} ? "\\maketitle\n" : '',
join('',@LITBOXES),
$str,
$decoration,
$index ? "\\input{$ind_file}\n" : '',
"\\end{document}\n",
);
return join '', grep defined $_, @doc;
}
sub mathml = { # PROCESS
my ($dom, $str) = @_;
@PACKAGES{qw(dsfont amssymb)} = 1;
my $val = $dom->{attr}{mathml} ? $dom->{attr}{mathml}->latex : $str;
if ($dom->{attr}{mathml}) {
my $label = $dom->{attr}{mathml}{attr}{label} ?
"\\label{$dom->{attr}{mathml}{attr}{label}}"
: '';
my $mstyle = $dom->{attr}{mathml}->child(0);
if ((my $env = $mstyle->{attr}{env}) || $label) {
$env ||= 'equation';
# Use a special environment
$val =~ s/^\$\$(.*)\$\$$/\\begin{$env}$label\n$1\n\\end{$env}\n/;
}
}
return $val;
}
sub .* = { # PROCESS
my ($dom, $str) = @_;
use vars qw(%PRINTED);
Warn("no handler for tag ${\$dom->tag}")
unless defined $dom->{val} || $PRINTED{$dom->tag}++;
return $str;
}
}