Change attribute value to position of another element with corresponding attribute value

Viewed 46

I have a single XHTML document that contains span and div elements that refer to page breaks of a print version using id and epub:type attributes. For example: <div epub:type="pagebreak" id="page-3"/>. The document also has links to those elements, for example: <a href="#page-3">3</a>.

This single XHTML document will be split into multiple XHTML documents to form an EPUB package. For this reason, the href attributes need to be updated to match the new location of the corresponding id. For example: <a href="02.xhtml#page-3">3</a>. The name of the new XHTML file is equal to the position of the body/section elements. So in the last example, the page break with id="page-3" is apparently in the second body/section element.

I'm using the following XSLT 2.0 stylesheet:

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

<!--variable to match id of elements with pagebreak values-->
<xsl:variable name="page-id" select="//*[@epub:type = 'pagebreak']/@id"/>

<!--update href attributes to match new filenames-->
<xsl:template match="a/@href">
    <xsl:choose>
        <xsl:when test="tokenize(., '#')[last()] = $page-id">
            <xsl:attribute name="href">
                <xsl:number count="//body/section[$page-id = tokenize(., '#')[last()]]" format="01"/>
                <xsl:value-of select="concat('.xhtml', .)"/>
            </xsl:attribute>
        </xsl:when>
        <xsl:otherwise>
            <xsl:copy-of select="."/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

It checks for href attributes that have a corresponding id using the $page-id variable. If there is a match, the href attribute should be updated using the count() function. Otherwise, the href should remain unchanged. The test seems to work, however, I'm not getting the result I want. This is the input:

<body>
    <section>
        <p>Link to page 3: <a href="#page-3">3</a></p>
    </section>
    <section>      
        <div epub:type="pagebreak" id="page-3"/>
    </section>
</body>

This is the output I get:

<body>
    <section>
        <p>Link to page 3: <a href=".xhtml#page-3">3</a></p>
    </section>
    <section>      
        <div epub:type="pagebreak" id="page-3"/>
    </section>
</body>

This is the output I want:

<body>
    <section>
        <p>Link to page 3: <a href="02.xhtml#page-3">3</a></p>
    </section>
    <section>      
        <div epub:type="pagebreak" id="page-3"/>
    </section>
</body>

It seems as if the XPath expression within xsl:number doesn't return a result, but I can't figure out why. Can anyone help me with this please?

1 Answers

I think you want e.g.

<xsl:template match="body/section" mode="number">
  <xsl:number format="01"/>
<xsl:template>

and then instead of

 <xsl:number count="//body/section[$page-id = tokenize(., '#')[last()]]" format="01"/>

use

 <xsl:apply-templates select="key('page-id', substring-after(., '#'))" mode="number"/>

plus a key declaration

<xsl:key name="page-id" match="body/section" use=".//*[@epub:type = 'pagebreak']/@id"/>
Related