diff --git a/lib/LaTeXML/Core/Document.pm b/lib/LaTeXML/Core/Document.pm index 26df5a8d2..8a69a0fef 100644 --- a/lib/LaTeXML/Core/Document.pm +++ b/lib/LaTeXML/Core/Document.pm @@ -162,7 +162,7 @@ sub canContain { sub canContainIndirect { my ($self, $tag, $child) = @_; my $model = $$self{model}; - $tag = $model->getNodeQName($tag) if ref $tag; # In case tag is a node. + $tag = $model->getNodeQName($tag) if ref $tag; # In case tag is a node. $child = $model->getNodeQName($child) if ref $child; # In case child is a node. # $imodel{$tag}{$child} => $intermediate || $child my $imodel = $STATE->lookupValue('INDIRECT_MODEL'); @@ -325,7 +325,7 @@ sub doctest_head { my ($self, $parent, $node, $severe) = @_; # Check consistency of document, parent & type, before proceeding print STDERR " NODE $$node [" if $severe; # BEFORE checking nodeType! - print STDERR "d" if $severe; + print STDERR "d" if $severe; if (!$node->ownerDocument->isSameNode($self->getDocument)) { print STDERR "!" if $severe; } print STDERR "p" if $severe; @@ -1183,6 +1183,14 @@ sub closeNode_internal { # $self->autoCollapseChildren($node); return $$self{node}; } +# If these attributes are present on both of two nodes, +# it should inhibit merging those two nodes (typically a child into parent). +our %non_mergeable_attributes = map { $_ => 1; } + qw(about aboutlabelref aboutidref + resource resourcelabelref resourceidref + property rel rev tyupeof datatype content + data datamimetype dataencoding); + # Avoid redundant nesting of font switching elements: # If we're closing a node that can take font switches and it contains # a single FONT_ELEMENT_NAME node; pull it up. @@ -1197,6 +1205,8 @@ sub autoCollapseChildren { # AND, $node can have all the attributes that the child has (but at least 'font') && !(grep { !$model->canHaveAttribute($qname, $_) } ('font', grep { /^[^_]/ } map { $_->nodeName } $c[0]->attributes)) + # AND, $node doesn't have any attributes which collide! + && !(grep { $non_mergeable_attributes{ $_->nodeName }; } $c[0]->attributes) # BUT, it isn't being forced somehow && !$c[0]->hasAttribute('_force_font')) { my $c = $c[0]; @@ -1206,36 +1216,55 @@ sub autoCollapseChildren { $node->appendChild($gc); $self->recordNodeIDs($node); } # Merge the attributes from the child onto $node - foreach my $attr ($c->attributes()) { - if ($attr->nodeType == XML_ATTRIBUTE_NODE) { - my $key = $attr->nodeName; - my $val = $attr->getValue; - # Special case attributes - if ($key eq 'xml:id') { # Use the replacement id - if (!$node->hasAttribute($key)) { - $val = $self->recordID($val, $node); - $node->setAttribute($key, $val); } } - elsif ($key eq 'class') { # combine $class - if (my $class = $node->getAttribute($key)) { - $node->setAttribute($key, $class . ' ' . $val); } - else { - $node->setAttribute($key, $val); } } - # xoffset, yoffset should sum up, if present on both. - elsif ($key =~ /^(xoffset|yoffset)$/) { - if (my $val2 = $node->getAttribute($key)) { - my $v1 = $val =~ /^([\+\-\d\.]*)pt$/ && $1; - my $v2 = $val2 =~ /^([\+\-\d\.]*)pt$/ && $1; - $node->setAttribute($key => ($v1 + $v2) . 'pt'); } - else { - $node->setAttribute($key => $val); } } - # Remaining attributes should prefer the inner (child's) values, if any - # (font, size, color, framed) - # (width,height, depth, align, vattach, float) - elsif (my $ns = $attr->namespaceURI) { - $node->setAttributeNS($ns, $attr->name, $val); } + $self->mergeAttributes($c, $node); } + return; } + +# When merging attributes of two nodes, some attributes should be combined +our %merge_attribute_spacejoin = map { $_ => 1; } # Merged space separated + qw(class lists inlist labels); +our %merge_attribute_semicolonjoin = map { $_ => 1; } # Merged ";" separated + qw(cssstyle); +our %merge_attribute_sumlength = map { $_ => 1; } # Summed lengths + qw(xoffset yoffset lpadding rpadding xtranslate ytranslate); +# Merge the attributes from node $from into those of the node $to. +# The presumption is that node $from will be removed afterwards. +# If an attribute is already present on $to, it will be ignored, unless named in $override. +sub mergeAttributes { + my ($self, $from, $to, $override) = @_; + # Merge the attributes from the node $from onto the node $to + foreach my $attr ($from->attributes()) { + if ($attr->nodeType == XML_ATTRIBUTE_NODE) { + my $key = $attr->nodeName; + my $val = $attr->getValue; + # Special case attributes + if ($key eq 'xml:id') { # Use the replacement id + if (!$to->hasAttribute($key) || ($override && $$override{$key})) { + # BUT: If $to DID have an attribute, we really should patch any idrefs!!!!!!! + $self->unRecordID($val); # presuming that $from will be going away. + $val = $self->recordID($val, $to); + $to->setAttribute($key, $val); } } + elsif ($merge_attribute_spacejoin{$key}) { # combine space separated values + $self->addSSValues($to, $key, $val); } + elsif ($merge_attribute_semicolonjoin{$key}) { # combine space separated values + my $oldval = $to->getAttribute($key); + if ($oldval) { # if duplicate? + $to->setAttribute($key, $oldval . '; ' . $val); } else { - $node->setAttribute($attr->localname, $val); } } } - } + $to->setAttribute($key, $val); } } + # Several length attributes should be cummulative; sum them up, if present on both. + elsif ($merge_attribute_sumlength{$key}) { + if (my $val2 = $to->getAttribute($key)) { + my $v1 = $val =~ /^([\+\-\d\.]*)pt$/ && $1; + my $v2 = $val2 =~ /^([\+\-\d\.]*)pt$/ && $1; + $to->setAttribute($key => ($v1 + $v2) . 'pt'); } + else { + $to->setAttribute($key => $val); } } + # Else if attribute not present on $to, or if we specificallly override it, just copy + elsif ((!$to->hasAttribute($key)) || ($override && $$override{$key})) { + if (my $ns = $attr->namespaceURI) { + $to->setAttributeNS($ns, $attr->name, $val); } + else { + $to->setAttribute($attr->localname, $val); } } } } return; } #====================================================================== @@ -1266,7 +1295,7 @@ sub setAttribute { $value = $value->toAttribute if ref $value; if ((defined $value) && ($value ne '')) { # Skip if `empty'; but 0 is OK! if ($key eq 'xml:id') { # If it's an ID attribute - $value = $self->recordID($value, $node); # Do id book keeping + $value = $self->recordID($value, $node); # Do id book keeping $node->setAttributeNS($LaTeXML::Common::XML::XML_NS, 'id', $value); } # and bypass all ns stuff elsif ($key !~ /:/) { # No colon; no namespace (the common case!) # Ignore attributes not allowed by the model, @@ -1280,7 +1309,7 @@ sub setAttribute { if ($ns) { # If namespaced attribute (must have prefix! my $prefix = $node->lookupNamespacePrefix($ns); # namespace already declared? if (!$prefix) { # if namespace not already declared - $prefix = $$self{model}->getDocumentNamespacePrefix($ns, 1); # get the prefix to use + $prefix = $$self{model}->getDocumentNamespacePrefix($ns, 1); # get the prefix to use $self->getDocument->documentElement->setNamespace($ns, $prefix, 0); } # and declare it if ($prefix eq '#default') { # Probably shouldn't happen...? $node->setAttribute($name => $value); } @@ -1443,11 +1472,23 @@ sub pruneXMDuals { $self->compactXMDual($dual, $content, $presentation); } } return; } +our $content_transfer_overrides = { map { ($_ => 1) } qw(decl_id meaning name omcd) }; +our $dual_transfer_overrides = { %$content_transfer_overrides, + map { ($_ => 1) } qw(xml:id role) }; + sub compactXMDual { my ($self, $dual, $content, $presentation) = @_; - # For now only handle compacting mirror applications - return if ($self->getNodeQName($content) ne 'ltx:XMApp') || - ($self->getNodeQName($presentation) ne 'ltx:XMApp'); + my $c_name = $self->getNodeQName($content); + my $p_name = $self->getNodeQName($presentation); + # 1.Quick fix: merge two tokens + if (($c_name eq 'ltx:XMTok') && ($p_name eq 'ltx:XMTok')) { + $self->mergeAttributes($content, $presentation, $content_transfer_overrides); + $self->mergeAttributes($dual, $presentation, $dual_transfer_overrides); + $self->replaceNode($dual, $presentation); + return; } + + # 2.For now, only main use case is compacting mirror XMApp nodes + return if ($c_name ne 'ltx:XMApp') || ($p_name ne 'ltx:XMApp'); my @content_args = element_nodes($content); my @pres_args = element_nodes($presentation); return if scalar(@content_args) != scalar(@pres_args); @@ -1482,23 +1523,13 @@ sub compactXMDual { # one of the args has our dual node that needs compacting if (ref $n_arg eq 'ARRAY') { my ($c_arg, $p_arg) = @$n_arg; - # Transfer all c_arg attributes over, it should be primary? - for my $attr_key (qw(decl_id meaning name omcd)) { - if (my $attr_val = $c_arg->getAttribute($attr_key)) { - $c_arg->removeAttribute($attr_key); - $p_arg->setAttribute($attr_key, $attr_val); } } + $self->mergeAttributes($c_arg, $p_arg, $content_transfer_overrides); $n_arg = $p_arg; } $n_arg->unbindNode; $compact_apply->appendChild($n_arg); } # if the dual has any attributes migrate them to the new XMApp - my %transfer_attrs = (); - for my $attr ($dual->attributes) { - if ($attr->nodeType == XML_ATTRIBUTE_NODE) { - $transfer_attrs{ $attr->nodeName } = $attr->getValue; } } + $self->mergeAttributes($dual, $compact_apply, $dual_transfer_overrides); $self->replaceNode($dual, $compact_apply); - # transfer the attributes after replacing, so that the bookkeeping has been undone - for my $key (keys %transfer_attrs) { - $self->setAttribute($compact_apply, $key, $transfer_attrs{$key}); } $self->closeElementAt($compact_apply); return; } @@ -1508,7 +1539,7 @@ sub collapseXMDual { # The other branch is not visible, nor referenced, # but the dual may have an id and be referenced if (my $dualid = $dual->getAttribute('xml:id')) { - $self->unRecordID($dualid); # We'll move or remove the ID from the dual + $self->unRecordID($dualid); # We'll move or remove the ID from the dual if (my $branchid = $branch->getAttribute('xml:id')) { # branch has id too! foreach my $ref ($self->findnodes("//*[\@idref='$dualid']")) { $ref->setAttribute(idref => $branchid); } } # Change dualid refs to branchid @@ -1635,7 +1666,7 @@ sub openElementAt { my $font = $attributes{_font} || $attributes{font}; my $box = $attributes{_box}; $box = $$self{node_boxes}{$box} if $box && !ref $box; # may already be the string key - # If this will be the document root node, things are slightly more involved. + # If this will be the document root node, things are slightly more involved. if ($point->nodeType == XML_DOCUMENT_NODE) { # First node! (?) $$self{model}->addSchemaDeclaration($self, $tag); map { $$self{document}->appendChild($_) } @{ $$self{pending} }; # Add saved comments, PI's @@ -1662,7 +1693,7 @@ sub openElementAt { next if $key eq 'locator'; # !!! $self->setAttribute($newnode, $key, $attributes{$key}); } $self->setNodeFont($newnode, $font) if $font; - $self->setNodeBox($newnode, $box) if $box; + $self->setNodeBox($newnode, $box) if $box; print STDERR "Inserting " . Stringify($newnode) . " into " . Stringify($point) . "\n" if $LaTeXML::Core::Document::DEBUG; # Run afterOpen operations diff --git a/t/alignment/listing.xml b/t/alignment/listing.xml index 56e7aa7cb..95f4e3a9f 100644 --- a/t/alignment/listing.xml +++ b/t/alignment/listing.xml @@ -30,17 +30,17 @@ <tag close=" ">2</tag>Inline Listings -

Various delimiters: a_word, -a_word, a_word, -a_word and even a_word done.

+

Various delimiters: a_word, +a_word, a_word, +a_word and even a_word done.

-

Indirectly: a_word; +

Indirectly: a_word; and with messed up braces foo { bar.

And also as an environment: -a_word; done.

+a_word; done.

diff --git a/t/complex/aliceblog.xml b/t/complex/aliceblog.xml index e9221d082..66f957dc1 100644 --- a/t/complex/aliceblog.xml +++ b/t/complex/aliceblog.xml @@ -33,7 +33,7 @@ 1st item -

Bob

+

Bob

@@ -42,7 +42,7 @@ 2nd item -

Eve

+

Eve

@@ -51,7 +51,7 @@ 3rd item -

Manu

+

Manu

diff --git a/t/complex/physics.xml b/t/complex/physics.xml index b78072480..32b612a59 100644 --- a/t/complex/physics.xml +++ b/t/complex/physics.xml @@ -1117,10 +1117,7 @@ - - - - + @@ -1719,10 +1716,7 @@ - - - sin - + sin x @@ -1754,10 +1748,7 @@ - - - sin - + sin [ @@ -1782,10 +1773,7 @@ - - - sin - + sin [ x @@ -1851,10 +1839,7 @@ - - - sin - + sin { @@ -1879,10 +1864,7 @@ - - - sin - + sin [ x @@ -2657,10 +2639,7 @@ - - - tr - + tr ρ @@ -2709,10 +2688,7 @@ - - - Tr - + Tr ρ @@ -2728,10 +2704,7 @@ - - - rank - + rank M @@ -2778,10 +2751,7 @@ - - - Res - + Res [ @@ -2988,10 +2958,7 @@ - - - Re - + Re ( @@ -3014,10 +2981,7 @@ - - - Re - + Re [ @@ -3040,10 +3004,7 @@ - - - Im - + Im ( @@ -3066,10 +3027,7 @@ - - - Im - + Im [ @@ -3307,10 +3265,7 @@ - - - d - + d @@ -3325,10 +3280,7 @@ - - - d - + d x @@ -3399,10 +3351,7 @@ ( - - - cos - + cos θ ) @@ -6616,19 +6565,13 @@ - - - - i - + i - - - i - + i 0 diff --git a/t/complex/si.xml b/t/complex/si.xml index 3394c0bee..cf03e1904 100644 --- a/t/complex/si.xml +++ b/t/complex/si.xml @@ -2247,10 +2247,7 @@ ty

- - - - + 2.67 ° @@ -2280,23 +2277,14 @@ ty

- - - - + - - - - + 6 ° - - - - + 7 @@ -2306,10 +2294,7 @@ ty - - - - + 6.5 @@ -2328,18 +2313,12 @@ ty - - - - + 6 ° - - - - + 7 @@ -2349,10 +2328,7 @@ ty - - - - + 6.5 @@ -2379,10 +2355,7 @@ ty - - - - - + 1 ° @@ -2393,10 +2366,7 @@ ty - - - - - + 2 @@ -2412,10 +2382,7 @@ ty - - - - - + 3 @@ -2433,10 +2400,7 @@ ty - - - - - + 1 ° @@ -2445,26 +2409,17 @@ ty - - - - + - - - - - + 0 ° - - - - + 2 @@ -2478,26 +2433,17 @@ ty - - - - + - - - - - + 0 ° - - - - + 3 @@ -2514,26 +2460,17 @@ ty - - - - + - - - - - + 1 ° - - - - + 0 @@ -2549,10 +2486,7 @@ ty - - - - - + 2 @@ -2566,17 +2500,11 @@ ty - - - - + - - - - - + 0 @@ -2587,10 +2515,7 @@ ty - - - - + 3 @@ -2607,26 +2532,17 @@ ty - - - - + - - - - - + 1 ° - - - - + 0 @@ -2640,35 +2556,20 @@ ty - - - - - + ° - - - - + 2 - - - - + - - - - + 0 - - - - + @@ -2681,10 +2582,7 @@ ty - - - - - + 3 @@ -2699,10 +2597,7 @@ ty - - - - + 45.697 ° @@ -2721,10 +2616,7 @@ ty

- - - - + 45.697 ° @@ -2732,23 +2624,14 @@ ty - - - - + - - - - + 6 ° - - - - + 7 @@ -2758,10 +2641,7 @@ ty - - - - + 6.5 @@ -2796,23 +2676,14 @@ ty - - - - + - - - - + 6 ° - - - - + 7 diff --git a/t/fonts/mathbbol.xml b/t/fonts/mathbbol.xml index 6b77e7bc7..8543f23b2 100644 --- a/t/fonts/mathbbol.xml +++ b/t/fonts/mathbbol.xml @@ -10,48 +10,21 @@ - - - < - - - - 1 - - - - > - + < + 1 + > , - - - [ - - - - 1 - - - - ] - + [ + 1 + ] , - - - ( - - - - 1 - - - - ) - + ( + 1 + ) , @@ -89,122 +62,53 @@ - - - α - + α , - - - β - + β , - - - β - - - - χ - + β + χ , - - - δ - - , - - - ϵ - - , - - - γ - - , - - - i - - , - - - κ - - , - - - λ - - , - - - μ - - , - - - ν - - , - - - ω - - , - - - ϕ - - , - - - π - - , - - - ψ - - , - - - ρ - - , - - - σ - - , - - - τ - - , - - - θ - - , - - - υ - - , - - - ξ - - , - - - ζ - + δ + , + ϵ + , + γ + , + i + , + κ + , + λ + , + μ + , + ν + , + ω + , + ϕ + , + π + , + ψ + , + ρ + , + σ + , + τ + , + θ + , + υ + , + ξ + , + ζ diff --git a/t/fonts/stmaryrd.xml b/t/fonts/stmaryrd.xml index 192d5a784..26b1d83cb 100644 --- a/t/fonts/stmaryrd.xml +++ b/t/fonts/stmaryrd.xml @@ -17,10 +17,7 @@ - - - \Ydown - + \Ydown a b @@ -33,10 +30,7 @@ - - - \Yleft - + \Yleft a b @@ -49,10 +43,7 @@ - - - \Yright - + \Yright a b @@ -222,10 +213,7 @@ - - - \curlyveedownarrow - + \curlyveedownarrow a b @@ -238,10 +226,7 @@ - - - \curlyveeuparrow - + \curlyveeuparrow a b @@ -254,19 +239,13 @@ - - - \curlywedgedownarrow - + \curlywedgedownarrow a b - - - \curlywedgeuparrow - + \curlywedgeuparrow a b @@ -290,10 +269,7 @@ - - - \fatbslash - + \fatbslash a b @@ -319,10 +295,7 @@ - - - \fatslash - + \fatslash a b @@ -386,10 +359,7 @@ a - - - \moo - + \moo b @@ -1169,10 +1139,7 @@ - - - \nnearrow - + \nnearrow a b @@ -1185,10 +1152,7 @@ - - - \nnwarrow - + \nnwarrow a b @@ -1201,19 +1165,13 @@ - - - \ssearrow - + \ssearrow a b - - - \sswarrow - + \sswarrow a b @@ -1375,10 +1333,7 @@ - - - / - + / a b @@ -1391,10 +1346,7 @@ - - - / - + / a b @@ -1407,19 +1359,13 @@ - - - / - + / a b - - - / - + / a b