Re: [xsl] Best Way to Break Up Nested Structures Based On Inline Elements

Subject: Re: [xsl] Best Way to Break Up Nested Structures Based On Inline Elements
From: "G. Ken Holman g.ken.holman@xxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Wed, 18 Apr 2018 21:01:50 -0000
There are a number of users of my Page Sequence
Master Interleave (PSMI) code for the rotated tables:

https://cranesoftwrights.github.io/resources/psmi/index.htm

Perhaps this has some ideas you can exploit.

. . . . . . Ken

At 2018-04-18 20:36 +0000, Eliot Kimber ekimber@xxxxxxxxxxxx wrote:

I definitely owe Gerrit the beverage of his
choice. I was able to make this code work with my details.

While it's not obvious (at least not to me--I
still need to take some time to fully appreciate
how it works) it's definitely much more elegant
than the recursive approach I was thinking of.

I will probably also need to use this technique
to handle the challenge of changing page
sequences to render long rotated tables or foldout pages.

When generating FO it's much eaiser logically to
emit page sequence start markers and then come
back and allocate things to page sequences than
it is to try to work out the page sequence
mapping when there's not a simple mapping from
top-level elements to page sequences.

Cheers,

Eliot
--
Eliot Kimber
http://contrext.com

o;?On 4/18/18, 2:00 PM, "Imsieke, Gerrit, le-tex
gerrit.imsieke@xxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote:

Hold my beer.

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
       xmlns:xs="http://www.w3.org/2001/XMLSchema";
       xmlns:fo="http://www.w3.org/1999/XSL/Format";
       exclude-result-prefixes="xs"
       version="2.0">

<xsl:output indent="yes"/>

       <xsl:template match="node() | @*" mode="#default split">
         <xsl:copy>
           <xsl:apply-templates select="@* | node()" mode="#current"/>
         </xsl:copy>
       </xsl:template>

       <xsl:template match="fo:block[empty(ancestor::fo:block)]"
    mode="#default">
         <xsl:variable name="block-root" as="element(fo:block)"
select="."/>
         <xsl:for-each-group select="descendant::node()[empty(node())]"
    group-starting-with="two-column-start">
           <xsl:for-each-group select="current-group()"
    group-ending-with="two-column-end">
             <xsl:apply-templates select="$block-root" mode="split">
               <xsl:with-param name="restricted-to" as="node()*"
                 select="current-group()/ancestor-or-self::node()"
    tunnel="yes"/>
               <xsl:with-param name="two-col-start" as="xs:boolean"
    tunnel="yes"
                 select="exists(self::two-column-start)"/>
             </xsl:apply-templates>
           </xsl:for-each-group>
         </xsl:for-each-group>
       </xsl:template>

       <xsl:template match="node()" mode="split" priority="1">
         <xsl:param name="restricted-to" tunnel="yes" as="node()+"/>
         <xsl:if test="exists(. intersect $restricted-to)">
           <xsl:next-match/>
         </xsl:if>
       </xsl:template>

       <xsl:template
match="fo:block[empty(ancestor::fo:block)]" mode="split">
         <xsl:param name="restricted-to" tunnel="yes" as="node()*"/>
         <xsl:param name="two-col-start" tunnel="yes" as="xs:boolean"/>
         <xsl:copy>
           <xsl:apply-templates select="@*" mode="#current"/>
           <xsl:if test="$two-col-start">
             <xsl:attribute name="span" select="'none'"/>
           </xsl:if>
           <xsl:apply-templates mode="#current"/>
         </xsl:copy>
       </xsl:template>

<xsl:template match="two-column-start | two-column-end"
mode="split"/>

</xsl:stylesheet>



On 18/04/2018 20:23, Eliot Kimber ekimber@xxxxxxxxxxxx wrote: > Gerrit, > > I don't immediately see how the code in the referenced message works but it's simple enough to try in my context. > > In my case the number of switches is small relative to the total content, so it will only be applied occasionally and infrequently. > > Cheers, > > E. > > -- > Eliot Kimber > http://contrext.com > > o;?On 4/18/18, 1:15 PM, "Imsieke, Gerrit, le-tex gerrit.imsieke@xxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote: > > Hi Eliot, > > I think this can be tackled with what I call "upward projection", see > for ex. https://markmail.org/message/kmq2g4fidmw6cofz > > You'd identify all leaf elements and group them (maybe a combination of > group-starting-with="two-colum-start" and > group-ending-with="two-colum-end" is in order here). > > Then for each group you transform the top-level block in a certain mode, > with a tunneled parameter. The tunneled parameter contains the current > group and its ancestors. > > In the template that matches any element in this mode, you check whether > the tunneled parameter contains the current element. If so, the > next-match identity template will apply, if not, nothing will be written > to the result. > > There's a special template in this mode that matches the > two-column-start element. It will create <fo:block span="none"> and then > go on processing the children. > > I'm not 100% sure whether this works, but I have an intercontinental > flight ahead of me. It will give me some time to create working code, > unless you're faster implementing the suggested solution (or a better one). > > I guess that the solution scales with the number of splitting points > times the number of leaves, so performance may deteriorate for large > documents with frequently changing column spans. > > Gerrit > > On 18/04/2018 19:45, Eliot Kimber ekimber@xxxxxxxxxxxx wrote: > > Using XSLT 2 with Saxon. > > > > In the context of generating XSL-FO markup where there tree of fo:block elements can be quite deep, I need to break the blocks into a sequence of top-level blocks that specify @span based on the presence of markers anywhere in the heirarchy. This is to support FOP's strict implementation of the FO spec, which only allows specifying column spans on direct children of fo:flow. > > > > In my processing I'm emitting marker elements to signal the starts and ends of areas that need to change the column spanning, e.g.: > > > > <fo:block span="all"> > > <fo:block> > > <fo:block> > > <fo:block> > > <two-column-start/> > > </fo:block> > > ... > > <two-column-end/> > > </fo:block> > > <fo:block>... > > </fo:block> > > </fo:block> > > > > Where the result needs to be: > > > > <fo:block span="all"> > > <!-- Stuff before two-column start --> > > </fo:block> > > <fo:block span="none"> > > <!-- Stuff up to <two-column-end/> marker --> > > </fo:block> > > <fo:block span="all"> > > <!-- Stuff after <two-column-end> marker --> > > </fo:block> > > > > There must be a general pattern for solving this kind of transformation pattern but I'm not seeing it or not remembering it. > > > > I can think of a recursive algorithm to do it but is there a simpler or more efficient approach? Conceptually it's a for-each-group problem but the structure of the content doesn't see to lend itself to grouping. > > > > Thanks, > > > > Eliot > > -- > > Eliot Kimber > > http://contrext.com > > > > > >


--
Contact info, blog, articles, etc. http://www.CraneSoftwrights.com/s/ |
Check our site for free XML, XSLT, XSL-FO and UBL developer resources |
Streaming hands-on XSLT/XPath 2 training class @ US$45 (5 hours free) |

Current Thread