Skip to content

Commit

Permalink
Block consistency (#2286)
Browse files Browse the repository at this point in the history
* Schema changes: More consistent naming and use of "block" elements,
which are typically (vertical) blocks of block, para orsectional material (according to the level)
for layout purposes.

* block can contain Block.model allowed in Block.class
* logical-block can contain Para.model allowed in Para.class
* sectional-block can contain sectional material, allowed in sections
Each has an inline- variant which is in Misc.class.

NOTE: that inline-para has been renamed inline-logical-block!
NOTE: that the content model of table cells (ltx:td) has been changed to Inline.model; if it needs to contain block level material, it needs to be contained in an appropriate inline-xxx element.

* Add XSLT for the new (or renamed) block elements

* Add support for an internal ltx:_CaptureBlock_ element useful for temporarily capturing block-like content at various levels to defer determining which level (block,logical,sectional) is needed

* Enhance document->renameNode with option to 're-insert' the contents with the new name allowing for automatic opening/closing of nodes

* Replace ltx:inline-para with ltx:inline-logical-block

* Replacement insertBlock based on _CaptureBlock

* \hbox creates inline-block if placed where #PCDATA is allowed

* Have p{} column type pass the desired width to \hbox, so that can create proper inline-blocks

* Simplify but generalize \normal@par, \lx@newline to recognize block mode avoiding unnecessary ltx:break; use these more consistently, obsoleting \inner@par, \lx@parboxnewline

* Fixup CSS styling for various blocks, inline, etc

* Enhance tabular test-case for interation between @ and p column specifications

* Enhance framed test-case to show framing of more awkward combinations

* Updated test-cases for changes to block processing

* always allow (internal) attributes that start with _; allow svg:g, svg:foreignObject in ltx:_CaptureBlock_

* Change svg:foreignObject content from Flow.model to Inline.model (using inlne- blocks when needed)

* In renameNode, only copy allowed attributes

* Fix copy mistake

* Careful about insertBlock into svg which may or may not need an svg:foreignObject
  • Loading branch information
brucemiller authored Dec 22, 2023
1 parent e42782a commit e3e4845
Show file tree
Hide file tree
Showing 42 changed files with 884 additions and 554 deletions.
34 changes: 32 additions & 2 deletions lib/LaTeXML/Common/Model.pm
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ sub loadSchema {
$self->loadCompiledSchema($compiled); }
else {
$$self{schema}->loadSchema; }
$self->loadInternalExtensions;
$self->describeModel if $LaTeXML::Common::Model::DEBUG;
$$self{schema_loaded} = 1;
return $$self{schema}; }
Expand All @@ -88,6 +89,33 @@ sub addSchemaDeclaration {
$$self{schema}->addSchemaDeclaration($document, $tag);
return; }

sub loadInternalExtensions {
my ($self) = @_;
if (!exists $$self{tagprop}{'ltx:_CaptureBlock_'}) {
# Synthesize ltx:_CaptureBlock_ to act like the union of ltx:block, ltx:para,
$self->synthesizeElement('ltx:_CaptureBlock_',
qw(ltx:block ltx:logical-block ltx:sectional-block Caption));
$$self{tagprop}{'ltx:_CaptureBlock_'}{model}{'svg:g'} = 1;
$$self{tagprop}{'ltx:_CaptureBlock_'}{model}{'svg:foreignObject'} = 1;
}
return; }

# Clone the tagprop's (allowed content & attributes) of @other to $tag
sub synthesizeElement {
my ($self, $tag, @others) = @_;
$$self{tagprop}{$tag} = {} unless $$self{tagprop}{$tag};
my $capture = $$self{tagprop}{$tag};
foreach my $other (@others) {
if (my $content = $$self{schemaclass}{$other}) {
foreach my $child (keys %$content) {
$$capture{model}{$child} = $$content{$child}; } }
elsif (my $entry = $$self{tagprop}{$other}) {
foreach my $child (keys %{ $$entry{model} }) {
$$capture{model}{$child} = $$entry{model}{$child}; }
foreach my $attr (keys %{ $$entry{attributes} }) {
$$capture{attributes}{$attr} = $$entry{attributes}{$attr}; } } }
return; }

#=====================================================================
# Make provision to precompile the schema.
sub compileSchema {
Expand Down Expand Up @@ -378,7 +406,8 @@ sub canContain {
return 0 if !$tag || ($tag eq '#PCDATA') || ($tag eq '#Comment');
return 1 if $tag =~ /(.*?:)?_Capture_$/; # with or without namespace prefix
return 1 if $tag eq '_WildCard_';
return 1 if $childtag =~ /(.*?:)?_Capture_$/;
return 1 if $childtag =~ /(.*?:)?_Capture_$/; # anything can contain these
return 1 if $childtag =~ /(.*?:)?_CaptureBlock_$/;
return 1 if $childtag eq '_WildCard_';
return 1 if $childtag eq '#Comment';
return 1 if $childtag eq '#ProcessingInstruction';
Expand Down Expand Up @@ -417,7 +446,8 @@ sub canHaveAttribute {
return 0 if $tag eq '#Document';
return 0 if $tag eq '#ProcessingInstruction';
return 0 if $tag eq '#DTD';
return 1 if $tag =~ /(.*?:)?_Capture_$/;
return 1 if $tag =~ /(.*?:)?_Capture_$/;
return 1 if $attrib =~ /^_/;
return 1 if $$self{permissive};
my ($tagns, $tagname) = ($tag =~ /^(\w+):(\w*)$/ ? ($1, $2) : ('', $tag));
my ($attrns, $attrname) = ($attrib =~ /^(\w+):(\w*)$/ ? ($1, $2) : ('', $attrib));
Expand Down
20 changes: 16 additions & 4 deletions lib/LaTeXML/Core/Document.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1957,7 +1957,7 @@ sub replaceNode {
# initially since $node->setNodeName was broken in XML::LibXML 1.58
# but this can provide for more options & correctness?
sub renameNode {
my ($self, $node, $newname) = @_;
my ($self, $node, $newname, $reinsert) = @_;
my $model = $$self{model};
my ($ns, $tag) = $model->decodeQName($newname);
my $parent = $node->parentNode;
Expand All @@ -1970,10 +1970,22 @@ sub renameNode {
my $key = $attr->getName;
my $value = $node->getAttribute($key);
$id = $value if $key eq 'xml:id'; # Save to register after removal of old node.
$new->setAttribute($key, $value); }
$new->setAttribute($key, $value) if $model->canHaveAttribute($newname, $key); }
# AND move all content from $node to $newnode
foreach my $child ($node->childNodes) {
$new->appendChild($child); }
if (!$reinsert) {
foreach my $child ($node->childNodes) {
$new->appendChild($child); } }
else {
my $savenode = $$self{node};
$$self{node} = $new;
foreach my $child ($node->childNodes) {
if ($child->nodeType == XML_TEXT_NODE) {
openText_internal($self, $child->data);
closeText_internal($self); }
else {
my $point = find_insertion_point($self, getNodeQName($self, $child));
$point->appendChild($child); } }
$$self{node} = $savenode; }
## THEN call afterOpen... ?
# It would normally be called before children added,
# but how can we know if we're duplicated auto-added stuff?
Expand Down
56 changes: 26 additions & 30 deletions lib/LaTeXML/Package/LaTeX.pool.ltxml
Original file line number Diff line number Diff line change
Expand Up @@ -283,10 +283,21 @@ EOTeX
# C.1.6 The \\ Command
#======================================================================
# In math, \\ is just a formatting hint, unless within an array, cases, .. environment.
DefConstructor("\\\\ OptionalMatch:* [Glue]",
"?#isMath(<ltx:XMHint name='newline'/>)(<ltx:break/>)",
DefConstructor('\lx@newline OptionalMatch:* [Glue]', sub {
my ($document, $star, $skip, %props) = @_;
if ($STATE->lookupValue('IN_MATH')) {
$document->insertElement('ltx:XMHint', undef, name => 'newline'); }
else {
my $context = $document->getElement;
if ($context->getAttribute('_vertical_mode_')) { }
elsif (($document->getNodeQName($context) eq 'ltx:p')
&& $context->parentNode->getAttribute('_vertical_mode_')) {
$document->maybeCloseElement('ltx:p'); }
elsif ($document->canContain($context, 'ltx:break')) {
$document->insertElement('ltx:break'); } } },
reversion => Tokens(T_CS("\\\\"), T_CR),
properties => { isBreak => 1 });
Let('\\\\', '\lx@newline');

DefConstructor('\newline',
"?#isMath(<ltx:XMHint name='newline'/>)(<ltx:break/>)",
Expand Down Expand Up @@ -1340,27 +1351,19 @@ DefMacroI('\@evenfoot', undef, Tokens());
#**********************************************************************

DefEnvironment('{center}', sub {
$_[0]->maybeCloseElement('ltx:p'); # this starts a new vertical block
aligningEnvironment('center', 'ltx_centering', @_); }, # aligning will take care of \\\\ "rows"
beforeDigest => sub {
Let('\par', '\inner@par');
Let('\\\\', '\inner@par'); });
$_[0]->maybeCloseElement('ltx:p'); # this starts a new vertical block
aligningEnvironment('center', 'ltx_centering', @_); }); # aligning will take care of \\\\ "rows"

# HOWEVER, define a plain \center to act like \centering (?)
DefMacroI('\center', undef, '\centering');
DefMacroI('\endcenter', undef, '');

DefEnvironment('{flushleft}', sub {
$_[0]->maybeCloseElement('ltx:p'); # this starts a new vertical block
aligningEnvironment('left', 'ltx_align_left', @_); },
beforeDigest => sub {
Let('\par', '\inner@par');
Let('\\\\', '\inner@par'); });
aligningEnvironment('left', 'ltx_align_left', @_); });
DefEnvironment('{flushright}', sub {
$_[0]->maybeCloseElement('ltx:p'); # this starts a new vertical block
aligningEnvironment('right', 'ltx_align_right', @_); },
beforeDigest => sub {
Let('\par', '\inner@par');
Let('\\\\', '\inner@par'); });
aligningEnvironment('right', 'ltx_align_right', @_); });

# These add an operation to be carried out on the current node & following siblings, when the current group ends.
# These operators will add alignment (class) attributes to each "line" in the current block.
Expand Down Expand Up @@ -3451,7 +3454,7 @@ sub SubfigureAndID {
# A little verbose, but much easier to do than XSLT 1.0 would allow.
my $contents_count = 0; # count with author breaks excluded
my @contents = grep { my $name = $document->getNodeQName($_);
my $ok = ($name =~ /^ltx:(?:graphics|float|figure|table|tabular|break|inline-para|inline-block|listing)$/);
my $ok = ($name =~ /^ltx:(?:graphics|float|figure|table|tabular|break|inline-logical-block|inline-block|listing)$/);
$contents_count += 1 if $ok and $name ne 'ltx:break';
$ok; } element_nodes($node);
if ($contents_count > 1) {
Expand Down Expand Up @@ -3618,9 +3621,9 @@ DefConditional('\if@reversemargin');
Let('\reversemarginpar', '\@reversemargintrue');
Let('\normalmarginpar', '\@reversemarginfalse');
DefConstructor('\marginpar[]{}',
"?#1(<ltx:note role='margin' class='ltx_marginpar_left'><ltx:inline-para>#1</ltx:inline-para></ltx:note>"
. "?#2(<ltx:note role='margin' class='ltx_marginpar_right'><ltx:inline-para>#2</ltx:inline-para></ltx:note>))"
. "(<ltx:note role='margin' class='ltx_marginpar'><ltx:inline-para>#2</ltx:inline-para></ltx:note>)");
"?#1(<ltx:note role='margin' class='ltx_marginpar_left'><ltx:inline-logical-block>#1</ltx:inline-logical-block></ltx:note>"
. "?#2(<ltx:note role='margin' class='ltx_marginpar_right'><ltx:inline-logical-block>#2</ltx:inline-logical-block></ltx:note>))"
. "(<ltx:note role='margin' class='ltx_marginpar'><ltx:inline-logical-block>#2</ltx:inline-logical-block></ltx:note>)");
DefRegister('\marginparpush', Dimension(0));

#**********************************************************************
Expand Down Expand Up @@ -4835,13 +4838,7 @@ RawTeX(<<'EOL');
\DeclareRobustCommand\usebox[1]{\leavevmode\copy #1\relax}
EOL

# A soft sorta \par that only closes an ltx:p, but not ltx:para
DefConstructor('\lx@parboxnewline[]', sub {
my ($document, $ignore, %props) = @_;
$document->maybeCloseElement('ltx:p');
return; },
properties => { isBreak => 1 });

Let('\lx@parboxnewline', '\lx@newline'); # Obsolete, but in case still used
# NOTE: There are 2 extra arguments (See LaTeX Companion, p.866)
# for height and inner-pos. We're ignoring them, for now, though.
DefConstructor('\parbox[] OptionalUndigested OptionalUndigested {Dimension} VBoxContents', sub {
Expand All @@ -4861,7 +4858,7 @@ DefConstructor('\parbox[] OptionalUndigested OptionalUndigested {Dimension} VBox
beforeDigest => sub {
reenterTextMode(1); # actually vertical mode!
AssignRegister('\hsize' => $_[4]);
Let('\\\\', '\lx@parboxnewline'); });
Let('\\\\', '\lx@newline'); });
DefMacroI('\@parboxrestore', undef, Tokens());

DefConditional('\if@minipage');
Expand All @@ -4870,7 +4867,6 @@ DefEnvironment('{minipage}[][][]{Dimension}', sub {
my ($document, $attachment, $b, $c, $width, %props) = @_;
my $vattach = translateAttachment($attachment);
insertBlock($document, $props{body},
para => 1,
width => $width,
vattach => $vattach,
class => 'ltx_minipage');
Expand All @@ -4884,8 +4880,8 @@ DefEnvironment('{minipage}[][][]{Dimension}', sub {
reenterTextMode(1); # actually vertical mode!
Digest(T_CS('\@minipagetrue'));
AssignRegister('\hsize' => $_[4]);
# this conflicts (& not needed?) with insertBlock
Let('\\\\', '\lx@parboxnewline'); });
Let('\\\\', '\lx@newline');
});

DefConstructor('\rule[Dimension]{Dimension}{Dimension}',
"<ltx:rule ?#offset(yoffset='#offset') width='#width' height='#height'/>",
Expand Down
Loading

0 comments on commit e3e4845

Please sign in to comment.