diff --git a/lib/LaTeXML/Core/Document.pm b/lib/LaTeXML/Core/Document.pm index 63b1569be..637945909 100644 --- a/lib/LaTeXML/Core/Document.pm +++ b/lib/LaTeXML/Core/Document.pm @@ -498,10 +498,10 @@ sub serialize_aux { (map { $_ . '="' . $atnodes{$_} . '"' } sort keys %atnodes) ); my $noindent_children = ($heuristic - # This emulates libxml2's heuristic - # ? $noindent || grep { $_->nodeType != XML_ELEMENT_NODE } @children + # This emulates libxml2's heuristic + # ? $noindent || grep { $_->nodeType != XML_ELEMENT_NODE } @children ? $noindent || grep { $_->nodeType == XML_TEXT_NODE } @children - # This is the "Correct" way to determine whether to add indentation + # This is the "Correct" way to determine whether to add indentation : $model->canContain(getNodeQName($self, $node), '#PCDATA')); return join('', ($noindent ? '' : $indent), $start, @@ -1376,8 +1376,17 @@ sub makeError { # [xml:id and namespaced attributes are always allowed] sub setAttribute { my ($self, $node, $key, $value) = @_; - return if (ref $value) && ((!blessed($value)) || !$value->can('toAttribute')); - $value = $value->toAttribute if ref $value; + if (ref $value) { + if ($key eq '_box') { + return $self->setNodeBox($node, $value); } + elsif ($key eq '_font') { + return $self->setNodeFont($node, $value); } + elsif ((!blessed($value)) || !$value->can('toAttribute')) { + Warn('unexpected', (ref $value), $self, + "Don't know how to encode $value as an attribute value"); + return; } + else { + $value = $value->toAttribute; } } if ((defined $value) && ($value ne '')) { # Skip if `empty'; but 0 is OK! if ($key eq 'xml:id') { # If it's an ID attribute $value = recordID($self, $value, $node); # Do id book keeping diff --git a/lib/LaTeXML/Core/List.pm b/lib/LaTeXML/Core/List.pm index 34494341f..1c4b206e4 100644 --- a/lib/LaTeXML/Core/List.pm +++ b/lib/LaTeXML/Core/List.pm @@ -17,7 +17,7 @@ use LaTeXML::Common::Object; use LaTeXML::Common::Error; use LaTeXML::Common::Dimension; use List::Util qw(min max); -use base qw(Exporter LaTeXML::Core::Box); +use base qw(Exporter LaTeXML::Core::Box); our @EXPORT = (qw(&List)); # Tricky; don't really want a separate constructor for a Math List, @@ -71,6 +71,10 @@ sub toString { my ($self) = @_; return join('', grep { defined $_ } map { $_->toString } $self->unlist); } +sub toAttribute { + my ($self) = @_; + return join('', grep { defined $_ } map { $_->toAttribute } $self->unlist); } + # Methods for overloaded operators sub stringify { no warnings 'recursion'; diff --git a/lib/LaTeXML/Core/Whatsit.pm b/lib/LaTeXML/Core/Whatsit.pm index ee21008ed..c6af364c7 100644 --- a/lib/LaTeXML/Core/Whatsit.pm +++ b/lib/LaTeXML/Core/Whatsit.pm @@ -218,6 +218,31 @@ sub beAbsorbed { LaTeXML::Core::Definition::stopProfiling($profiled, 'absorb') if $profiled; return @result; } +# Similar to ->revert, but converts to pure string for use in an attribute value +sub toAttribute { + my ($self) = @_; + my $props = $$self{properties}; + my $defn = $self->getDefinition; + my $spec = $$props{attributeForm} || $$defn{attributeForm}; + if (!defined $spec) { + return $self->toString; } # Default + elsif (ref $spec eq 'CODE') { # If handled by CODE, call it + $spec = &$spec($self, $self->getArgs); } + # Now, similar to substituteParameters, but creating a string. + $spec =~ s/#(#|[1-9]|\w+)/ toAttribute_aux($self,$1)/eg; + return $spec; } + +sub toAttribute_aux { + my ($self, $code) = @_; + my $value; + if ($code eq '#') { return $code; } + elsif ((ord($code) > ord('0')) && (ord($code) <= ord('9'))) { + $value = $self->getArg(ord($code) - ord('0')); } + else { + $value = $self->getProperty($code); } + $value = $value->toAttribute if ref $value; + return $value; } + # See discussion in Box.pm sub computeSize { my ($self, %options) = @_; diff --git a/lib/LaTeXML/Engine/LaTeX.pool.ltxml b/lib/LaTeXML/Engine/LaTeX.pool.ltxml index 7841851bc..a1cb2e6ba 100644 --- a/lib/LaTeXML/Engine/LaTeX.pool.ltxml +++ b/lib/LaTeXML/Engine/LaTeX.pool.ltxml @@ -1211,9 +1211,9 @@ DefConstructor('\lx@author@prefix', sub { my $i = scalar(@{ $document->findnodes('//ltx:creator[@role="author"]') }); if ($i <= 1) { } elsif ($i == $nauthors) { - $document->setAttribute($node, before => ToString(Digest(T_CS('\lx@author@conj')))); } + $document->setAttribute($node, before => Digest(T_CS('\lx@author@conj'))); } else { - $document->setAttribute($node, before => ToString(Digest(T_CS('\lx@author@sep')))); } + $document->setAttribute($node, before => Digest(T_CS('\lx@author@sep'))); } }); DefMacro('\@author', '\@empty'); @@ -4376,8 +4376,8 @@ DefConstructor('\@@bibref Semiverbatim Semiverbatim {}{}', "#3#4", properties => sub { (bibrefs => CleanBibKey($_[2]), - separator => ToString(Digest(LookupValue('CITE_SEPARATOR'))), - yyseparator => ToString(Digest(LookupValue('CITE_YY_SEPARATOR'))), + separator => Digest(LookupValue('CITE_SEPARATOR')), + yyseparator => Digest(LookupValue('CITE_YY_SEPARATOR')), bibunit => LookupValue('CITE_UNIT')); }); # Simple container for any phrases used in the bibref diff --git a/lib/LaTeXML/Engine/TeX_Glue.pool.ltxml b/lib/LaTeXML/Engine/TeX_Glue.pool.ltxml index 7aa00e393..b4d0bef5b 100644 --- a/lib/LaTeXML/Engine/TeX_Glue.pool.ltxml +++ b/lib/LaTeXML/Engine/TeX_Glue.pool.ltxml @@ -79,8 +79,9 @@ DefConstructor('\hskip Glue', sub { else { # $document->openText(DimensionToSpaces($length), $props{font}); } }, $document->absorb(DimensionToSpaces($length)); } }, - reversion => sub { revertSkip(T_CS('\hskip'), $_[1]); }, - properties => sub { + reversion => sub { revertSkip(T_CS('\hskip'), $_[1]); }, + attributeForm => sub { DimensionToSpaces($_[1]); }, + properties => sub { my ($stomach, $length) = @_; (width => $length, isSpace => 1, isSkip => 1); }); @@ -95,7 +96,7 @@ DefConstructor('\vskip Glue', sub { elsif ($document->isOpenable('ltx:break')) { $document->insertElement('ltx:break'); } } return; }, - properties => sub { (height => $_[1], isSpace => 1,, isSkip => 1, isVerticalSpace => 1, isBreak => 1); }); + properties => sub { (height => $_[1], isSpace => 1, isSkip => 1, isVerticalSpace => 1, isBreak => 1); }); # Remove skip, if last on LIST DefPrimitiveI('\unskip', undef, sub { diff --git a/lib/LaTeXML/Package.pm b/lib/LaTeXML/Package.pm index 69f8f38e5..60f04b7e3 100644 --- a/lib/LaTeXML/Package.pm +++ b/lib/LaTeXML/Package.pm @@ -1381,12 +1381,12 @@ sub flatten { # properties : a hashref listing default values of properties to assign to the Whatsit. # These properties can be used in the constructor. my $constructor_options = { # [CONSTANT] - mode => 1, requireMath => 1, forbidMath => 1, font => 1, - alias => 1, reversion => 1, sizer => 1, properties => 1, - nargs => 1, - beforeDigest => 1, afterDigest => 1, beforeConstruct => 1, afterConstruct => 1, - captureBody => 1, scope => 1, bounded => 1, locked => 1, - outer => 1, long => 1, robust => 1 }; + mode => 1, requireMath => 1, forbidMath => 1, font => 1, + alias => 1, reversion => 1, sizer => 1, properties => 1, + nargs => 1, attributeForm => 1, + beforeDigest => 1, afterDigest => 1, beforeConstruct => 1, afterConstruct => 1, + captureBody => 1, scope => 1, bounded => 1, locked => 1, + outer => 1, long => 1, robust => 1 }; sub inferSizer { my ($sizer, $reversion) = @_; @@ -1424,12 +1424,13 @@ sub DefConstructorI { nargs => $options{nargs}, alias => (defined $options{alias} ? $options{alias} : ($options{robust} ? $cs : undef)), - reversion => $options{reversion}, - sizer => inferSizer($options{sizer}, $options{reversion}), - captureBody => $options{captureBody}, - properties => $options{properties} || {}, - outer => $options{outer}, - long => $options{long}), + reversion => $options{reversion}, + attributeForm => $options{attributeForm}, + sizer => inferSizer($options{sizer}, $options{reversion}), + captureBody => $options{captureBody}, + properties => $options{properties} || {}, + outer => $options{outer}, + long => $options{long}), $options{scope}); AssignValue(ToString($cs) . ":locked" => 1) if $options{locked}; return; } @@ -1798,7 +1799,7 @@ sub defmath_cons { ? $cs : $presentation->unlist); }; } $STATE->installDefinition(LaTeXML::Core::Definition::Constructor->new($defcs, $paramlist, ($nargs == 0 - # If trivial presentation, allow it in Text + # If trivial presentation, allow it in Text ? ($presentation !~ /(?:\(|\)|\\)/ ? "?#isMath( 1, afterDigest => 1, afterDigestBegin => 1, beforeDigestEnd => 1, afterDigestBody => 1, beforeConstruct => 1, afterConstruct => 1, - reversion => 1, sizer => 1, scope => 1, locked => 1 }; + reversion => 1, sizer => 1, scope => 1, locked => 1, + attributeForm => 1 }; sub DefEnvironment { my ($proto, $replacement, %options) = @_; @@ -1873,8 +1875,9 @@ sub DefEnvironmentI { nargs => $options{nargs}, captureBody => 1, properties => $options{properties} || {}, - (defined $options{reversion} ? (reversion => $options{reversion}) : ()), - (defined $sizer ? (sizer => $sizer) : ()), + (defined $options{reversion} ? (reversion => $options{reversion}) : ()), + (defined $options{attributeForm} ? (attributeForm => $options{attributeForm}) : ()), + (defined $sizer ? (sizer => $sizer) : ()), ), $options{scope}); $STATE->installDefinition(LaTeXML::Core::Definition::Constructor ->new(T_CS("\\end{$name}"), "", "", @@ -1914,8 +1917,9 @@ sub DefEnvironmentI { nargs => $options{nargs}, captureBody => T_CS("\\end$name"), # Required to capture!! properties => $options{properties} || {}, - (defined $options{reversion} ? (reversion => $options{reversion}) : ()), - (defined $sizer ? (sizer => $sizer) : ()), + (defined $options{reversion} ? (reversion => $options{reversion}) : ()), + (defined $options{attributeForm} ? (attributeForm => $options{attributeForm}) : ()), + (defined $sizer ? (sizer => $sizer) : ()), ), $options{scope}); $STATE->installDefinition(LaTeXML::Core::Definition::Constructor ->new(T_CS("\\end$name"), "", "", @@ -2475,7 +2479,7 @@ sub AddToMacro { else { local $LaTeXML::Core::State::UNLOCKED = 1; # ALLOW redefinitions that only adding to the macro DefMacroI($cs, undef, Tokens(map { $_->unlist } - map { (blessed $_ ? $_ : TokenizeInternal($_)) } ($defn->getExpansion, @tokens)), + map { (blessed $_ ? $_ : TokenizeInternal($_)) } ($defn->getExpansion, @tokens)), nopackParameters => 1, scope => 'global', locked => $$defn{locked}); } return; } @@ -3682,6 +3686,15 @@ the one defined in the C. This is a convenient alternative for reversion when a 'public' command conditionally expands into an internal one, but the reversion should be for the public command. +=item CI | I($whatsit,#1,#2,...)> + +specifies the conversion of the invocation back into plain text +for an attribute value, used by the C method +(the default is C). +The I string can include C<#1>, C<#2>... +The I is called with the C<$whatsit> and digested arguments +and must return a string. + =item CI | I($whatsit)> specifies how to compute (approximate) the displayed size of the object, @@ -3884,6 +3897,8 @@ These options are the same as for L =item CI>, +=item CI | I($whatsit,#1,#2,...)>, + =item CI>, =item CI>,