sub
new {
my
(
$class
,
%options
) =
@_
;
my
$state
= LaTeXML::Core::State->new(
catcodes
=>
'standard'
,
stomach
=> LaTeXML::Core::Stomach->new(),
model
=>
$options
{model} || LaTeXML::Common::Model->new());
SetVerbosity(
$options
{verbosity})
if
defined
$options
{verbosity};
$state
->assignValue(
STRICT
=> (
defined
$options
{strict} ?
$options
{strict} : 0),
'global'
);
$state
->assignValue(
INCLUDE_COMMENTS
=> (
defined
$options
{includecomments} ?
$options
{includecomments} : 1),
'global'
);
$state
->assignValue(
INCLUDE_PATH_PIS
=> (
defined
$options
{includepathpis} ?
$options
{includepathpis} : 1),
'global'
);
$state
->assignValue(
DOCUMENTID
=> (
defined
$options
{documentid} ?
$options
{documentid} :
''
),
'global'
);
$state
->assignValue(
SEARCHPATHS
=> [
map
{ pathname_absolute(pathname_canonical(
$_
)) }
'.'
, @{
$options
{searchpaths} || [] }],
'global'
);
$state
->assignValue(
GRAPHICSPATHS
=> [
map
{ pathname_absolute(pathname_canonical(
$_
)) }
@{
$options
{graphicspaths} || [] }],
'global'
);
$state
->assignValue(
INCLUDE_STYLES
=>
$options
{includestyles} || 0,
'global'
);
$state
->assignValue(
INCLUDE_CLASSES
=>
$options
{includestyles} || 0,
'global'
);
$options
{inputencoding} =
"utf-8"
unless
$options
{inputencoding};
$state
->assignValue(
PERL_INPUT_ENCODING
=>
$options
{inputencoding});
$state
->assignValue(
NOMATHPARSE
=>
$options
{nomathparse} || 0,
'global'
);
return
bless
{
state
=>
$state
,
nomathparse
=>
$options
{nomathparse} || 0,
preload
=>
$options
{preload},
},
$class
; }
sub
convertAndWriteFile {
my
(
$self
,
$file
) =
@_
;
$file
=~ s/\.tex$//;
my
$doc
=
$self
->convertFile(
$file
);
$doc
->getDocument->toFile(
"$file.xml"
, 1)
if
$doc
;
return
$doc
; }
sub
convertFile {
my
(
$self
,
$file
) =
@_
;
my
$digested
=
$self
->digestFile(
$file
);
return
unless
$digested
;
return
$self
->convertDocument(
$digested
); }
sub
getStatusMessage {
my
(
$self
) =
@_
;
return
$$self
{state}->getStatusMessage; }
sub
getStatusCode {
my
(
$self
) =
@_
;
return
$$self
{state}->getStatusCode; }
sub
showProfile {
my
(
$self
,
$digested
) =
@_
;
return
$self
->withState(
sub
{
LaTeXML::Core::Definition::showProfile();
}); }
our
%MODE_EXTENSION
= (
TeX
=>
'tex'
,
LaTeX
=>
'tex'
,
AmSTeX
=>
'tex'
,
BibTeX
=>
'bib'
);
sub
digestFile {
my
(
$self
,
$request
,
%options
) =
@_
;
my
(
$dir
,
$name
,
$ext
);
my
$mode
=
$options
{mode} ||
'TeX'
;
if
(pathname_is_literaldata(
$request
)) {
$dir
=
undef
;
$ext
=
$MODE_EXTENSION
{
$mode
};
$name
=
"Anonymous String"
; }
elsif
(pathname_is_url(
$request
)) {
$dir
=
undef
;
$ext
=
$MODE_EXTENSION
{
$mode
};
$name
=
$request
;
}
else
{
$request
=~ s/\.\Q
$MODE_EXTENSION
{
$mode
}\E$//;
if
(
my
$pathname
= pathname_find(
$request
,
types
=> [
$MODE_EXTENSION
{
$mode
},
''
],
paths
=>
$$self
{state}->lookupValue(
'SEARCHPATHS'
))) {
$request
=
$pathname
;
(
$dir
,
$name
,
$ext
) = pathname_split(
$request
); }
else
{
$self
->withState(
sub
{
Fatal(
'missing_file'
,
$request
,
undef
,
"Can't find $mode file $request"
,
LaTeXML::Package::maybeReportSearchPaths()
); }); } }
return
$self
->withState(
sub
{
my
(
$state
) =
@_
;
ProgressSpinup(
"Digesting $mode $name"
);
$self
->initializeState(
$mode
.
".pool"
, @{
$$self
{preload} || [] })
unless
$options
{noinitialize};
$state
->assignValue(
SOURCEFILE
=>
$request
)
if
(!pathname_is_literaldata(
$request
));
$state
->assignValue(
SOURCEDIRECTORY
=>
$dir
)
if
defined
$dir
;
$state
->unshiftValue(
SEARCHPATHS
=>
$dir
)
if
defined
$dir
&& !
grep
{
$_
eq
$dir
} @{
$state
->lookupValue(
'SEARCHPATHS'
) };
$state
->unshiftValue(
GRAPHICSPATHS
=>
$dir
)
if
defined
$dir
&& !
grep
{
$_
eq
$dir
} @{
$state
->lookupValue(
'GRAPHICSPATHS'
) };
$state
->installDefinition(LaTeXML::Core::Definition::Expandable->new(T_CS(
'\jobname'
),
undef
,
Tokens(Explode(
$name
))));
$self
->loadPostamble(
$options
{postamble})
if
$options
{postamble};
LaTeXML::Package::InputContent(
$request
);
$self
->loadPreamble(
$options
{preamble})
if
$options
{preamble};
if
(
$mode
eq
'BibTeX'
) {
my
$bib
= LaTeXML::Pre::BibTeX->newFromGullet(
$name
,
$state
->getStomach->getGullet);
LaTeXML::Package::InputContent(
"literal:"
.
$bib
->toTeX); }
my
$list
=
$self
->finishDigestion;
ProgressSpindown(
"Digesting $mode $name"
);
return
$list
; });
}
sub
finishDigestion {
my
(
$self
) =
@_
;
my
$state
=
$$self
{state};
my
$stomach
=
$state
->getStomach;
my
@stuff
= ();
while
(
$stomach
->getGullet->getMouth->hasMoreInput) {
push
(
@stuff
,
$stomach
->digestNextBody); }
if
(
$$stomach
{rescued_boxes}) {
push
(
@stuff
, @{
$$stomach
{rescued_boxes} });
}
if
(
my
$env
=
$state
->lookupValue(
'current_environment'
)) {
Error(
'expected'
,
"\\end{$env}"
,
$stomach
,
"Input ended while environment $env was open"
); }
my
$ifstack
=
$state
->lookupValue(
'if_stack'
);
if
(
$ifstack
&&
$$ifstack
[0]) {
Error(
'expected'
,
'\fi'
,
$stomach
,
"Input ended while conditional "
. ToString(
$$ifstack
[0]{token}) .
" was incomplete"
,
"started at "
. ToString(
$$ifstack
[0]{start})); }
$stomach
->getGullet->flush;
return
List(
@stuff
); }
sub
loadPreamble {
my
(
$self
,
$preamble
) =
@_
;
my
$gullet
=
$$self
{state}->getStomach->getGullet;
if
(
$preamble
eq
'standard_preamble.tex'
) {
$preamble
=
'literal:\documentclass{article}\begin{document}'
; }
return
LaTeXML::Package::InputContent(
$preamble
); }
sub
loadPostamble {
my
(
$self
,
$postamble
) =
@_
;
my
$gullet
=
$$self
{state}->getStomach->getGullet;
if
(
$postamble
eq
'standard_postamble.tex'
) {
$postamble
=
'literal:\end{document}'
; }
return
LaTeXML::Package::InputContent(
$postamble
); }
sub
convertDocument {
my
(
$self
,
$digested
) =
@_
;
return
$self
->withState(
sub
{
my
(
$state
) =
@_
;
my
$model
=
$state
->getModel;
my
$document
= LaTeXML::Core::Document->new(
$model
);
local
$LaTeXML::DOCUMENT
=
$document
;
ProgressSpinup(
"Building"
);
$model
->loadSchema();
if
(
my
$paths
=
$state
->lookupValue(
'SEARCHPATHS'
)) {
if
(
$state
->lookupValue(
'INCLUDE_PATH_PIS'
)) {
my
@copy
= @{
$state
->lookupValue(
'SEARCHPATHS'
) };
my
@dedup
= ();
while
(
my
$check
=
shift
(
@copy
)) {
unshift
(
@dedup
,
$check
)
if
!(
grep
{
$_
eq
$check
}
@dedup
); }
$document
->insertPI(
'latexml'
,
searchpaths
=>
join
(
','
,
@dedup
)); } }
foreach
my
$preload_by_reference
(@{
$$self
{preload} }) {
my
$preload
=
$preload_by_reference
;
next
if
$preload
=~ /\.pool$/;
my
$options
=
undef
;
if
(
$preload
=~ s/^\[([^\]]*)\]//) {
$options
= $1; }
if
(
$preload
=~ s/\.cls$//) {
$document
->insertPI(
'latexml'
,
class
=>
$preload
, (
$options
? (
options
=>
$options
) : ())); }
else
{
$preload
=~ s/\.sty$//;
$document
->insertPI(
'latexml'
,
package
=>
$preload
, (
$options
? (
options
=>
$options
) : ())); } }
{
no
warnings
'recursion'
;
$document
->absorb(
$digested
); }
ProgressSpindown(
"Building"
);
if
(
my
$rules
=
$state
->lookupValue(
'DOCUMENT_REWRITE_RULES'
)) {
ProgressSpinup(
"Rewriting"
);
$document
->markXMNodeVisibility;
foreach
my
$rule
(
@$rules
) {
$rule
->rewrite(
$document
,
$document
->getDocument->documentElement); }
ProgressSpindown(
"Rewriting"
); }
LaTeXML::MathParser->new(
lexematize
=>
$state
->lookupValue(
'LEXEMATIZE_MATH'
))->parseMath(
$document
)
unless
$$self
{nomathparse};
ProgressSpinup(
"Finalizing"
);
my
$xmldoc
=
$document
->finalize();
ProgressSpindown(
"Finalizing"
);
return
$xmldoc
; }); }
sub
withState {
my
(
$self
,
$closure
) =
@_
;
local
$STATE
=
$$self
{state};
local
$SIG
{__DIE__} = \
&LaTeXML::Common::Error::perl_die_handler
;
local
$SIG
{INT} = \
&LaTeXML::Common::Error::perl_interrupt_handler
;
local
$SIG
{__WARN__} = \
&LaTeXML::Common::Error::perl_warn_handler
;
local
$SIG
{
'ALRM'
} = \
&LaTeXML::Common::Error::perl_timeout_handler
;
local
$SIG
{
'TERM'
} = \
&LaTeXML::Common::Error::perl_terminate_handler
;
local
$LaTeXML::DUAL_BRANCH
=
''
;
return
&$closure
(
$STATE
); }
sub
initializeState {
my
(
$self
,
@files
) =
@_
;
my
$state
=
$$self
{state};
my
$stomach
=
$state
->getStomach;
my
$gullet
=
$stomach
->getGullet;
$stomach
->initialize;
my
$paths
=
$state
->lookupValue(
'SEARCHPATHS'
);
$state
->assignValue(
'InitialPreloads'
=> 1,
'global'
);
foreach
my
$preload
(
@files
) {
my
(
$options
,
$type
);
$options
= $1
if
$preload
=~ s/^\[([^\]]*)\]//;
$type
= (
$preload
=~ s/\.(\w+)$// ? $1 :
'sty'
);
my
$handleoptions
= (
$type
eq
'sty'
) || (
$type
eq
'cls'
);
if
(
$options
) {
if
(
$handleoptions
) {
$options
= [
split
(/,/,
$options
)]; }
else
{
Warn(
'unexpected'
,
'options'
,
"Attempting to pass options to $preload.$type (not style or class)"
,
"The options were [$options]"
); } }
if
(pathname_is_url(
$preload
)) {
$preload
.=
'.'
.
$type
;
}
LaTeXML::Package::InputDefinitions(
$preload
,
type
=>
$type
,
handleoptions
=>
$handleoptions
,
options
=>
$options
);
}
$state
->assignValue(
'InitialPreloads'
=>
undef
,
'global'
);
return
; }
sub
writeDOM {
my
(
$self
,
$dom
,
$name
) =
@_
;
$dom
->toFile(
"$name.xml"
, 1);
return
1; }
1;