# -*- mode: Perl -*-
# /=====================================================================\ #
# | algorithm2e | #
# | 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=/ #
package LaTeXML::Package::Pool;
use strict;
use warnings;
use LaTeXML::Package;
use LaTeXML::Util::Pathname;
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# NOTE: This package would actually be relatively easy to implement
# via loading algorithm2e.sty and overriding a few critical macros
# (in particular {algorithm} and \par), but that LaTeXML doesn't
# currently implement \par correctly; it should do nothing in vertical mode,
# but LaTeXML doesn't yet know anything about modes other than math!
# This package heavily (and redunantly) scatters \par about,
# not entirely consistently, on the one hand, they produce the individual
# algorithm lines AND add line numbers, but on the other hand, it
# relies on the fact that adjacent \par's effectively collapse.
# The naive (and clever attempts) to reproduce the behaviour w/o true modes,
# always results in blank (but numbered!) lines.
#
# The alternative, to implement the package completely from scratch,
# is undesirable since we'd have to duplicate all the command definitions,
# languages, etc.
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
RequirePackage('float');
#DefMacro('\algocf@new@command DefToken', '\renewcommand{#1}', locked => 1);
# To start, load algorithm2e...
# BUT! the stock algorithm2e is in latin1 encoding!
# We probably don't handle encodings entirely correctly,
# but (eg using turkish along with \usepackage[utf8]{inputenc})
# this can cause problems even for regular latex!
my $saved_encoding = LookupValue('PERL_INPUT_ENCODING');
AssignValue(PERL_INPUT_ENCODING => 'latin1');
InputDefinitions('algorithm2e', type => 'sty', noltxml => 1);
AssignValue(PERL_INPUT_ENCODING => $saved_encoding);
Let('\@mathsemicolon', '\;');
#NewCounter('algorithm');
NewCounter('algorithm', ToString(Digest(T_CS('\algocf@within'))));
RawTeX(<<'EoTeX');
\expandafter\ifx\csname algocf@within\endcsname\relax
\else\def\thealgorithm{\csname the\algocf@within\endcsname.\@arabic\c@algorithm}\fi
EoTeX
DefMacro('\fnum@algorithm', '\algorithmcfname\nobreakspace\thealgorithm');
DefMacro('\fnum@font@algorithm', '\bf');
DefMacro('\ext@algorithm', 'loa');
# {algorithm*}: same as {algorithm}, but used in a two columns text, so just map to original
for my $env (qw(algorithm2e algorithm algorithm*)) {
DefEnvironment("{$env}[]",
"<ltx:float xml:id='#id' class='ltx_algorithm'>"
. "#tags"
. "<ltx:listing class='ltx_lst_numbers_left'>"
. "<ltx:listingline>"
. "#body"
. "</ltx:listingline>"
. "</ltx:listing></ltx:float>",
properties => { layout => 'vertical' },
beforeDigest => sub {
DigestIf(T_CS('\@ResetCounterIfNeeded'));
DigestIf(T_CS('\algocf@linesnumbered'));
Let('\par', '\lx@algo@par');
Let('\parbox', '\lx@algo@parbox');
Let("\\\\", '\lx@algo@par');
Let('\strut', '\lx@algo@strut');
# AssignCatcode("\r" => CC_SPACE); # NOT CC_EOL, so newlines don't turn to \par!
# AssignCatcode("\r" => 13);
# Let(T_ACTIVE("\r"), '\relax'); # More appropriate than \par, I think?
# Let('\vtop','\relax'); # ?
DefMacro('\;', '\ifmmode\@mathsemicolon\else\@endalgoln\fi');
beforeFloat('algorithm');
},
afterDigest => sub {
if (ToString(DigestIf(T_CS('\algocf@style'))) =~ /box/) {
$_[1]->setProperty(frame => 'boxed'); }
afterFloat($_[1]); },
afterConstruct => sub { addFloatFrames($_[0], $_[1]->getProperty('frame')); }
);
}
DefMacro('\lx@algo@parbox[]{}{}', '#3');
# NEUTRALIZE most \par's that are built into agorythmic2e
DefMacro('\lx@algo@strut SkipMatch:\par', '');
#####DefMacro('\@marker{}',' \textit{#1} ');
DefMacro('\@marker{}', ''); # Debugging!
DefMacro('\lx@algo@par', '\@marker{PREPAR}\lx@algo@endline\lx@algo@startline\@marker{PAR}');
#DefMacro('\lx@algo@pars', '\@marker{PREPARs}\lx@algo@endline\lx@algo@startline \@marker{PARs}');
DefMacro('\lx@algo@parx', '\@marker{PREPARx}\lx@algo@endline\lx@algo@startline \@marker{PARx}');
DefMacro('\lx@algo@parb', '\@marker{PREPARb}\lx@algo@endline\lx@algo@startline \@marker{PARb}');
####\let\par\relax
DefConditional('\if@lx@algo@par SkipSpaces', sub { $STATE->getPrefix('didpar'); });
DefPrimitiveI('\lx@algo@setpar', undef, sub { $STATE->setPrefix('didpar'); return; }, isPrefix => 1);
DefMacro('\lx@algo@newpar{}{}', '\if@lx@algo@par\@marker{SKIP#1}\else\@marker{pre#1 }#2\@marker{post#1}\fi\lx@algo@setpar');
DefMacro('\lx@algo@par', '\lx@algo@newpar{PAR}{\lx@algo@endline\lx@algo@startline}');
#DefMacro('\lx@algo@pars', '\lx@algo@newpar{PARs}{\lx@algo@endline\lx@algo@startline}');
DefMacro('\lx@algo@parx', '\lx@algo@newpar{PARx}{\lx@algo@endline\lx@algo@startline}');
DefMacro('\lx@algo@parb', '\lx@algo@newpar{PARb}{\lx@algo@endline\lx@algo@startline}');
DefMacro('\algocf@Vline{}', '\lx@algo@endline\lx@algo@startline \@marker{V.L}\lx@algo@advline \lx@strippar{#1}\lx@algo@pop@indentation \@marker{ENDVLINE}');
DefMacro('\algocf@Vsline{}', '\lx@algo@endline\lx@algo@startline \@marker{VsL}\lx@algo@advline \lx@strippar{#1}\lx@algo@pop@indentation \@marker{ENDVsLINE}');
DefMacro('\algocf@Noline{}', '\lx@algo@endline\lx@algo@startline \@marker{NoL}\lx@algo@advlevel \lx@strippar{#1} \@marker{ENDNoLINE}');
#DefMacro('\@endalgocfline', 'EOL');
DefMacro('\algocf@endline', sub {
return LookupValue('algorithm_dont_print_semicolon') ? () : (T_OTHER(';')); },
locked => 1);
#DefMacro('\lx@algo@lastpar', 'PRELASTPAR\the\everypar\lx@algo@@endline');
#DefMacro('\@endalgoln', '\@endalgocfline\lx@algo@par');
DefMacro('\@endalgoln', '\@endalgocfline');
#DefMacro('\@endalgocfline', '\algocf@endline\@marker{EOL}\lx@algo@par\let\lx@algo@parx\relax');
DefMacro('\@endalgocfline', '\algocf@endline\@marker{EOL}\lx@algo@par');
DefMacro('\PrintSemicolon', sub {
AssignValue('algorithm_dont_print_semicolon', 0, 'global');
return (); }, locked => 1);
DefMacro('\DontPrintSemicolon', sub {
AssignValue('algorithm_dont_print_semicolon', 1, 'global');
return (); }, locked => 1);
# Annoying: sometimes these are ended by authors \; (which ends the line)
# sometimes the author omits that.
# And sometimes the enclosing caller appends a \par afterwards!
# So, how to we assure that we end the line ONLY ONCE!
DefMacro('\lx@strippar{}', sub {
my ($gullet, $tokens) = @_;
my @tokens = $tokens->unlist;
# return (@tokens,T_CS('\lx@algo@parx'));
return (@tokens, T_CS('\lx@algo@parx'), T_CS('\lx@algo@parx'), T_CS('\lx@algo@parx')); });
#DefMacro('\algocf@@@block{}{}','#1\bgroup\lx@algo@startline#2\lx@algo@endline\egroup');
#DefMacro('\algocf@@@block{}{}','BLOCK:#1ENDBLOCK END#2');
#DefMacro('\algocf@group{}','#1\bgroup\typeout{Block:#1}\egroup');
DefMacro('\algocf@group{}', '\@marker{GRP} #1 \@marker{ENDGRP}');
#DefMacro('\algocf@group{}', '\lx@prepend@indentation GRP #1 ENDGRP');
#DefMacro('\algocf@@@block{}{}','BLK #1 ENDBLOCK END#2 ENDEND\par');
#DefMacro('\algocf@@@block{}{}', '\@marker{BLK} #1 \@marker{ENDBLOCK}\lx@algo@parb\@marker{END}#2\@marker{ENDEND}\lx@algo@parb');
DefMacro('\algocf@@@block{}{}', '\@marker{BLK} #1 \@marker{ENDBLOCK}\@marker{END}#2\@marker{ENDEND}\lx@algo@parb');
#DefMacro('\algocf@@@block{}{}', '\lx@prepend@indentation BLK #1 ENDBLOCK END#2 ENDEND\par');
DefRegister('\lx@algo@indentation' => Tokens());
DefMacro('\lx@algo@push@indentation{}',
'\expandafter\lx@algo@indentation\expandafter{\the\lx@algo@indentation#1}');
DefMacro('\lx@algo@pop@indentation', sub {
my @toks = LookupRegister('\lx@algo@indentation')->unlist;
pop(@toks);
AssignRegister('\lx@algo@indentation', Tokens(@toks));
return; });
DefMacro('\lx@algo@advlevel', '\lx@algo@push@indentation{\lx@algo@indent}');
DefMacro('\lx@algo@advline', '\lx@algo@push@indentation{\lx@algo@indentline}');
#DefMacro('\lx@algo@startline', '\lx@algo@@startline\the\lx@algo@indentation');
DefMacro('\lx@algo@startline', '\lx@algo@@startline');
DefMacro('\lx@algo@endline', '\lx@prepend@indentation\the\everypar\lx@algo@@endline');
#DefMacro('\lx@algo@startline', '\bgroup\lx@algo@@startline');
#DefMacro('\lx@algo@endline', '\lx@prepend@indentation\the\everypar\lx@algo@@endline\egroup');
DefMacro('\lx@algo@indent', '\hskip\skiprule\hskip\skiptext');
DefMacro('\lx@algo@indentline', '\hskip\skiprule\lx@algo@rule\hskip\skiptext');
DefConstructor('\lx@algo@rule', "<ltx:rule width='1px' height='100%'/>");
DefConstructor('\lx@algo@@startline', "<ltx:listingline xml:id='#id' refnum='#refnum'>"
. "?&defined(#refnum)(<ltx:tags><ltx:tag>#refnum</ltx:tag></ltx:tags>)()",
properties => sub {
# RefStepCounter('AlgoLine'); # ?
my $refnum = Digest(T_CS('\theAlgoLine'));
my $id = Digest(T_CS('\theAlgoLine@ID'));
# (id => $id, refnum => $refnum); }
(); }
);
DefConstructor('\lx@algo@@endline', "</ltx:listingline>");
# DefConstructor('\lx@algo@@startline Digested',
# "<ltx:listingline xml:id='#id' refnum='#refnum'>#body</ltx:listingline>");
DefConstructor('\algocf@printnl{}', "<ltx:tags>ltx:tag>#1</ltx:tag></ltx:tags>");
DefMacro('\lx@prepend@indentation',
'\lx@prepend@indentation@{\the\lx@algo@indentation}');
DefConstructor('\lx@prepend@indentation@{}', sub {
my ($doc, $indentation) = @_;
# PREPEND an <ltx:tag> to the current ltx:listingline
my $savenode = $doc->floatToElement('ltx:tags');
my $line = $doc->getNode;
my @oldcontent = $line->childNodes;
map { $line->removeChild($_) } @oldcontent; # Remove old content
$doc->absorb($indentation);
map { $line->appendChild($_) } @oldcontent; # put old content back.
});
DefConstructor('\algocf@printnl{}', sub {
my ($doc, $number) = @_;
# PREPEND an <ltx:tag> to the current ltx:listingline
my $savenode = $doc->floatToElement('ltx:tags');
my $line = $doc->getNode;
my @oldcontent = $line->childNodes;
map { $line->removeChild($_) } @oldcontent; # Remove old content
$doc->openElement('ltx:tags'); # prepend tags
$doc->insertElement('ltx:tag', $number); # prepend tag
$doc->closeElement('ltx:tags');
map { $line->appendChild($_) } @oldcontent; # put old content back.
});
#DefConstructor('\algocf@printnl{}', "<ltx:listingline><ltx:tag>#1</ltx:tag>");
#DefConstructor('\algocf@printnl{}',"");
#Tag('ltx:listingline', autoOpen => 1, autoClose => 1); # ?
# \algocf@@block
# \algocf@group
# \Hlne
# or
# \algocf@@@block
# \algocf@{No|Vs}line
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1;