Re: [xsl] RE: XSL-FO and Z-index

Subject: Re: [xsl] RE: XSL-FO and Z-index
From: Jesper Tverskov <jesper.tverskov@xxxxxxxxx>
Date: Thu, 20 Sep 2012 23:09:40 +0200
Let me answer my question myself then, can probably be improved, and I
might have forgotten something.

The functions return number of lines a text will be divided into (no
hyphenation) at a given line length. Should work for mono spaced
characters. For proportional text, $maxLineLength must be set to a
figure lower than real line length. I guess one could take a look at a
couple of pages of standard text and calculate average max line length
and use that for $maxLineLength.

Number of lines is not just character count including spaces divided
with line length. When there is not room for a word it moves to next
line. It could result in many additional lines depending on the length
of the text.

<?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";
exclude-result-prefixes="xs" version="2.0">
    <xsl:output indent="yes"/>

    <xsl:template name="start">
        <xsl:variable name="text">Lorem ipsum dolor sit amet,
consectetur adipisicing elit, sed do
            eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam,
            quis nostrud exercitation ullamco laboris nisi ut aliquip
ex ea commodo consequat. Duis
            aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla
            pariatur. Excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia
            deserunt mollit anim id est laborum.</xsl:variable>

        <xsl:variable name="number-of-lines">
            <xsl:call-template name="create-sequence-of-character-counts">
                <xsl:with-param name="text" select="$text"/>
                <xsl:with-param name="maxLineLength" select="80"/>
            </xsl:call-template>
        </xsl:variable>

        <!-- Output -->
        <xsl:value-of select="$number-of-lines"/>
    </xsl:template>

    <xsl:template name="create-sequence-of-character-counts">
        <xsl:param name="text"/>
        <xsl:param name="maxLineLength"/>
        <xsl:variable name="seq-of-words"
select="tokenize(normalize-space($text), ' ')"/>
        <!-- string-length of spaces + 1 = number of items -->
        <xsl:variable name="number-of-items"
            select="string-length(replace(normalize-space($text),
'\S', '')) + 1"/>

<!-- there must be a smarter way to do the following  -->
        <!-- we add the missing space (after the word) to
string-length to get character count for item -->
        <xsl:variable name="seqCount">
            <X>
                <xsl:for-each select="$seq-of-words[position()]">
                    <x>
                        <xsl:value-of select="concat((string-length(.)
+ 1), ' ')"/>
                    </x>
                </xsl:for-each>
            </X>
        </xsl:variable>

        <xsl:variable name="string-of-character-counts">
            <xsl:value-of select="normalize-space($seqCount)" separator=" "/>
        </xsl:variable>

        <xsl:variable name="seq-of-character-counts"
            select="tokenize($string-of-character-counts, ' ')" as="item()+"/>

        <xsl:for-each select="$seq-of-character-counts[position()]">
            <xsl:if test="$maxLineLength le xs:integer(.)">
                <xsl:message terminate="yes" select="'At least one
word is longer than maxLineLength'"/>
            </xsl:if>
        </xsl:for-each>

        <xsl:call-template name="seqCounts2lines">
            <xsl:with-param name="seqCounts"
select="$seq-of-character-counts" as="item()+"/>
            <xsl:with-param name="number-of-items" select="$number-of-items"/>
            <xsl:with-param name="maxLineLength" select="$maxLineLength"/>
        </xsl:call-template>
    </xsl:template>

    <xsl:template name="seqCounts2lines">
        <xsl:param name="seqCounts"/>
        <xsl:param name="number-of-items" as="xs:integer"/>
        <xsl:param name="itemSum" select="0" as="xs:integer"/>
        <xsl:param name="nextItem" select="1" as="xs:integer"/>
        <xsl:param name="lines" select="1" as="xs:integer"/>
        <xsl:param name="maxLineLength"/>
        <xsl:choose>
            <xsl:when test="$nextItem le ($number-of-items)">
                <xsl:choose>
                    <xsl:when
                        test="$itemSum +
xs:integer(subsequence($seqCounts, $nextItem, 1)) lt $maxLineLength">
                        <xsl:call-template name="seqCounts2lines">
                            <xsl:with-param name="itemSum"
                                select="$itemSum +
xs:integer(subsequence($seqCounts, $nextItem, 1))"/>
                            <xsl:with-param name="nextItem"
select="$nextItem + 1" as="xs:integer"/>
                            <xsl:with-param name="seqCounts"
select="$seqCounts" as="item()+"/>
                            <xsl:with-param name="number-of-items"
select="$number-of-items"/>
                            <xsl:with-param name="lines"
select="$lines" as="xs:integer"/>
                            <xsl:with-param name="maxLineLength"
select="$maxLineLength"/>
                        </xsl:call-template>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:call-template name="seqCounts2lines">
                            <xsl:with-param name="itemSum" select="0"/>
                            <xsl:with-param name="nextItem"
select="$nextItem" as="xs:integer"/>
                            <xsl:with-param name="seqCounts"
select="$seqCounts" as="item()+"/>
                            <xsl:with-param name="number-of-items"
select="$number-of-items"/>
                            <xsl:with-param name="lines"
select="$lines + 1" as="xs:integer"/>
                            <xsl:with-param name="maxLineLength"
select="$maxLineLength"/>
                        </xsl:call-template>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$lines"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>


Cheers
Jesper Tverskov
http://www.xmlplease.com

Current Thread