[xep-support] Another CoolTool - Line Numbers

From: Kevin Brown <kevin@renderx.com>
Date: Mon Sep 03 2012 - 22:26:07 PDT

It's been a long time and I realized I have not posted a "CoolTool" in a
while. Recently we have been doing some work that caused me to dig up an old
stylesheet I had written for line numbering. After looking at it, I thought
why not send this out to all? Not everyone wants to number lines in a
document and everyone has their own way of doing it, but this is at least
something people can learn from/adopt/adapt/or throw out for that matter.

RenderX does have a "hidden" extension, called "rx:ruler" which is for
laying out some equally spaced content within a block-container on a page.
This can also be used for line numbering but this is different than the
solution presented here. The "rx:ruler" extension knows nothing of the text
on a page, it is merely a way of placing a yardstick down on the page. It
does have some abilities to start and stop the numbers on that yardstick, as
well as reposition it's start. If that interests you, do not hesitate to ask
-- it's in there for everyone but we just never documented it.

The technique presented here focuses on the XEP Intermediate Format
(XEPOUT). Since text that is placed down on a page has to be represented the
XEPOUT, we could place line numbers for text not with some ruler, but by
actually numbering unique text lines. As with many CoolTools, it is done by
modifying the XEPOUT. Now, I first implemented this with XSL 1.0 because,
well, that is what I know. And I ended with a pretty complicated XSL. Now,
the new project caused me to re-examine this sheet and when I did -- WOW!
XSLT 2.0 made what I did before a snap it only a few lines of code. So let's
get started!

Take this sample fragment of FO (I have removed many attributes here that
result in spacing and such):

<fo:block font="bold 14pt Helvetica">Block Properties - Borders</fo:block>

It could result in this XEPOUT (with Kerning on):

<xep:text value="Bloc" x="199695" y="720373" width="30058" />
<xep:text value="k Pr" x="229753" y="720373" width="26180" />
<xep:text value="oper" x="255933" y="720373" width="30618" />
<xep:text value="ties - Bor" x="286551" y="720373" width="60396" />
<xep:text value="der" x="346947" y="720373" width="21574" />
<xep:text value="s" x="368521" y="720373" width="7784" />

Now all of this text fits on the same line - all the "y" attributes are the
same. So extending that to a page (of course as long as the page is not
multiple columns or dealing with multiple font sizes, baselines, etc.) all
the unique text lines would have unique "y" values. Note: the parenthetical
remark is relevant, this will not work for everything -- but that is OK, it
will not work for things not normally needed line numbers! Those that want
lines numbered in documents will get what they want.

Anyway, all we need then is an identity translate that does something when
<xep:text> is encountered. Of course, others may want <xep:image> numbered
or whatever ... this could easily be extended to do so in many ways.

Now, we are going to use 2.0 for this so, here we go (clip between the ****
for the XSL):

*****************************
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xep="http://www.renderx.com/XEP/xep" version="2.0">

<!-- First, in our identity translate, we set up a few params you may wish
to pass in (like the font, size, color and horizontal position of the actual
line numbers): -->

    <xsl:param name="ruler.font-family" select="'Arial'"/>
    <xsl:param name="ruler.font-size" select="10000"/>
    <xsl:param name="ruler.font-style" select="'normal'"/>
    <xsl:param name="ruler.font-weight" select="'400'"/>
    <xsl:param name="ruler.graycolor" select=".2"/>
    <xsl:param name="ruler.xpos" select="10000"/>

<!-- Those with me would know that the above is expressed in XEP units
(1/1000th of point), you could of course pass in different values and do an
internal conversion to the XEPOUT measurements.
And now the meat, what do we do with <xep:text> elements? We need to find
the first unique occurrence and also output a line number. So, let's break
it down with comments. -->

    <xsl:template match="xep:text">
<!-- record the current "y" -->
        <xsl:variable name="ypos" select="@y"/>
<!-- how many <text> elements are on the page before me? -->
        <xsl:variable name="textbeforeme"
select="preceding-sibling::xep:text[not(@y=$ypos)]"/>
<!-- the power of XSLT 2.0 ! How many unique <text @y/> elements are before
me -->
        <xsl:variable name="uniquelinesbeforeme"
select="distinct-values($textbeforeme/@y)"/>
<!-- OK, so my line number is? -->
        <xsl:variable name="linenum" select="count($uniquelinesbeforeme) +
1"/>
<!-- Oops, savvy folks will look into the XSL I will provide to see what
this is, but we have marked areas on the page like headers/footers to be
excluded from numbering -->
        <xsl:variable name="numbering"
select="string(preceding-sibling::xep:pinpoint[1]/@value)"/>
<!-- if the "Y" is unique, put down a linenumber -->
        <xsl:if
            test="not(preceding-sibling::xep:text/@y = $ypos) and
($numbering='' or $numbering='numbered')">
<!-- change to the line number font and color -->
            <xep:font family="{$ruler.font-family}"
weight="{$ruler.font-weight}"
                style="{$ruler.font-style}" variant="normal"
size="{$ruler.font-size}"/>
            <xep:word-spacing value="0"/>
            <xep:letter-spacing value="0"/>
            <xep:font-stretch value="1.0"/>
            <xep:gray-color gray="{$ruler.graycolor}"/>
<!-- put down the line number -->
            <xep:text>
                <xsl:attribute name="y" select="@y"/>
                <xsl:attribute name="x" select="$ruler.xpos"/>
                <xsl:attribute name="value" select="$linenum"/>
            </xep:text>
<!-- Restore the current font and color -->
            <xsl:copy-of
select="preceding-sibling::*[name()='xep:font'][1]"/>
            <xsl:copy-of
select="preceding-sibling::*[name()='xep:word-spacing'][1]"/>
            <xsl:copy-of
select="preceding-sibling::*[name()='xep:letter-spacing'][1]"/>
            <xsl:copy-of
select="preceding-sibling::*[name()='xep:font-stretch'][1]"/>
            <xsl:copy-of
select="preceding-sibling::*[contains(name(),'color')][1]"/>
        </xsl:if>
<!-- Now output the text for the line -->
        <xep:text>
            <xsl:apply-templates select="@*"/>
        </xep:text>
    </xsl:template>

<!-- Last, of course we need a catch all for identity to output everything
else as is: -->

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

<!-- That's all there is to it. Short and sweet. -->

</xsl:stylesheet>

************************

There ya go. If you would like a sample, just email me and I will provide
it.

Kevin Brown
RenderX
kevin@renderx.com
skype:kbrown01

_______________________________________________
(*) To unsubscribe, please visit http://lists.renderx.com/mailman/options/xep-support
(*) By using the Service, you expressly agree to these Terms of Service http://w
ww.renderx.com/terms-of-service.html
Received on Mon Sep 3 22:15:43 2012

This archive was generated by hypermail 2.1.8 : Mon Sep 03 2012 - 22:15:48 PDT