# /=====================================================================\ #
# | LaTeXML::Post::MathML::Presentation | #
# | MathML generator 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::Post::MathML::Presentation;
use
strict;
use
warnings;
sub
preprocess {
my
(
$self
,
$doc
,
@maths
) =
@_
;
$self
->SUPER::preprocess(
$doc
,
@maths
);
if
(
$$self
{linelength}) {
# If we're doing linebreaking...
$self
->preprocess_linebreaking(
$doc
,
@maths
); }
return
; }
# This would be the non-linebreaking version
sub
convertNode_simple {
my
(
$self
,
$doc
,
$xmath
,
$style
) =
@_
;
return
$self
->pmml_top(
$xmath
,
$style
); }
# Convert a node and compute it's linebroken layout
sub
convertNode_linebreak {
my
(
$self
,
$doc
,
$xmath
,
$style
) =
@_
;
my
$breaker
=
$$self
{linebreaker};
if
(!
$breaker
) {
$breaker
=
$$self
{linebreaker} = LaTeXML::Post::MathML::Linebreaker->new(); }
my
$pmml
=
$self
->convertNode_simple(
$doc
,
$xmath
,
$style
);
my
$layout
=
$breaker
->bestFitToWidth(
$xmath
,
$pmml
,
$$self
{linelength}, 1);
if
(
$$layout
{hasbreak}) {
# YES it did linebreak!
$pmml
=
$breaker
->applyLayout(
$pmml
,
$layout
); }
return
(
$pmml
,
$$layout
{hasbreak}); }
sub
convertNode {
my
(
$self
,
$doc
,
$xmath
) =
@_
;
my
$style
= ((
$xmath
->parentNode->getAttribute(
'mode'
) ||
'inline'
) eq
'display'
?
'display'
:
'text'
);
my
$id
=
$xmath
->parentNode->getAttribute(
'xml:id'
);
# If this node has already been pre-converted
my
$pmml
;
if
(
$pmml
=
$id
&&
$$doc
{converted_pmml_cache}{
$id
}) { }
# A straight displayed Math will have been handled by preprocess_linebreaking (below),
# and, if it needed line-breaking, will have generated a MathFork/MathBranch.
# Other math, in the non-semantic side of a MathFork, may want to line break here as well.
# It presumably will NOT be display style(?)
# NEXT better strategy will be to scan columns of MathBranches to establish desired line length?
elsif
(
$$self
{linelength}
# If line breaking
&& (
$doc
->findnodes(
'ancestor::ltx:MathBranch'
,
$xmath
))
# In formatted side of MathFork?
# But ONLY if last column!! (until we can adapt LineBreaker!)
&& !
$doc
->findnodes(
'parent::ltx:Math/parent::ltx:td/following-sibling::ltx:td'
,
$xmath
)) {
my
(
$pmmlb
,
$broke
) =
$self
->convertNode_linebreak(
$doc
,
$xmath
,
$style
);
$pmml
=
$pmmlb
; }
else
{
$pmml
=
$self
->convertNode_simple(
$doc
,
$xmath
,
$style
); }
return
{
processor
=>
$self
,
xml
=>
$pmml
,
mimetype
=>
'application/mathml-presentation+xml'
}; }
sub
rawIDSuffix {
return
'.pmml'
; }
sub
associateNodeHook {
my
(
$self
,
$node
,
$sourcenode
) =
@_
;
# TODO: Shouldn't we have a single getQName shared for the entire latexml codebase
# in LaTeXML::Common or LaTeXML::Util ?
my
$name
= LaTeXML::Post::MathML::getQName(
$node
);
if
(
$name
=~ /^m:(?:mi|mo|mn)$/) {
if
(
my
$href
=
$sourcenode
->getAttribute(
'href'
)) {
if
(
ref
$node
eq
'ARRAY'
) {
$$node
[1]{href} =
$href
; }
else
{
$node
->setAttribute(
'href'
,
$href
); } }
if
(
my
$title
=
$sourcenode
->getAttribute(
'title'
)) {
if
(
ref
$node
eq
'ARRAY'
) {
$$node
[1]{title} =
$title
; }
else
{
$node
->setAttribute(
'title'
,
$title
); } } }
return
; }
#================================================================================
# Presentation MathML with Line breaking
# Not at all sure how this will integrate with Parallel markup...
# Any displayed formula is a candidate for line-breaking.
# If it is not already in a MathFork, and needs line-breaking,
# then we ought to wrap in a MathFork, so as to preserve the
# slightly "semantically meaningful" form.
# If we're mangling the document structure in this way,
# it needs to be done before the main scan-all-math's loop,
# since it moves the maths around.
# However, since we also have to check whether it NEEDS line breaking beforehand,
# we might as well linebreak & store that line-broken result alongside.
# [it will get stored WITHOUT an XMath expression, though, so we won't be asked to redo it]
# convertNode will be called later on the main fork (unbroken).
# Also, other subexpressions inside MathFork/MathBranch that were created by
# the usual means (bindings for eqnarray, or whatever) will still need to
# be converted (convertNode).
# And in fact they also should be line-broken -- we just don't know the width!!
sub
preprocess_linebreaking {
my
(
$self
,
$doc
,
@maths
) =
@_
;
# Rewrap every displayed ltx:Math in an ltx:MathFork (if it isn't ALREADY in a MathFork).
# This is so that we can preserve the "more semantic" non-linebroken form as the main branch.
foreach
my
$math
(
@maths
) {
my
$mode
=
$math
->getAttribute(
'mode'
) ||
'inline'
;
next
unless
$mode
eq
'display'
;
# SKIP if not in display mode?
my
$style
= (
$mode
eq
'display'
?
'display'
:
'text'
);
# If already has in a MathBranch, we can't really know if, or how wide, to line break!?!?!
next
if
$doc
->findnodes(
'ancestor::ltx:MathFork'
,
$math
);
# SKIP if already in a branch?
# Now let's do the layout & see if it actually needs line breaks!
# next if $math isn't really so wide ..
my
$id
=
$math
->getAttribute(
'xml:id'
);
my
$xmath
=
$doc
->findnode(
'ltx:XMath'
,
$math
);
my
(
$pmml
,
$broke
) =
$self
->convertNode_linebreak(
$doc
,
$xmath
,
$style
);
if
(
$broke
) {
# YES it did linebreak!
# Replace the Math node with a MathFork that contains the Math node.
# And a MathBranch that ONLY contains the line-broken pmml.
# That branch won't get other parallel markup,
# but the main, more semantic(?) one, will and will get the unbroken pmml (?), as well.
my
$p
=
$math
->parentNode;
$id
=
$id
.
".mbr"
if
$id
;
$doc
->replaceNode(
$math
, [
'ltx:MathFork'
, {},
$math
,
[
'ltx:MathBranch'
, {},
[
'ltx:Math'
, {
'xml:id'
=>
$id
},
$self
->outerWrapper(
$doc
,
$xmath
,
$pmml
)]]]);
# Now,RE-mark, since the insertion removed internal attributes!
$doc
->markXMNodeVisibility; }
# cache the converted pmml?
# But note that applyLayout MAY have MODIFIED the orignal $pmml, so it may have linebreaks!
# but then, it will have a modified id, as well!!! (.mbr appended)
if
(
$id
) {
$$doc
{converted_pmml_cache}{
$id
} =
$pmml
; }
}
return
; }
#================================================================================
1;