开发者

XSLT number only counts instances in current file of a multi-file document

I've been tasked with putting together a book (using XSL FO) from a number of XML-files, now I'm trying to number the figures in this book (simple numbering, no resets at chapters or whatever), my naive approach was to do this

<xsl:template match="figure">
  <fo:block xsl:use-attribute-sets="figure">
    .. stuff to deal with images ..
    <fo:block xsl:use-attribute-sets="figure-caption">
      Figure <xsl:number level="any"/>: <xsl:apply-templates/>
    </fo:block>
  <fo:block xsl:use-attribute-sets="figure-caption">
</xsl:template>

I have an aggregate XML file which selects the files to use using the document() function like so:

<xsl:template match="include">
  <xsl:apply-template开发者_如何学Cs select="document(@src)"/>
</xsl:template>

Now, my problem is that number seems to always only count the instances in the current file, which is not what I want (currently, there's only one or two images per file, resulting in all figures being 'Figure 1' or 'Figure 2').

I've considered two approaches, both being essentially two-pass XSLT. First, the straightforward approach, generate an intermediary XML containing the entire book using an identity transform, which I'm reluctant to do for other reasons.

Second, using node-set() extension, which I tried like this

<xsl:template match="include">
  <xsl:apply-templates select="ext:node-set(document(@src))"/>
</xsl:template>

but this produced the same result.

Any ideas? Perhaps something which isn't a two-pass transformation? Any help would be greatly appreciated.


The two -pass approach is the more logical and robust one.

One-pass approach is very challenging. One can provide an expression in the value attribute of <xsl:number> and this can be used to sum the "local number" with the maximum accumulated number so far from all previous documents.

However, this requires sequencing the documents (which is something bad in a functional language) and this only works for a flat numbering scheme. In case hierarchical numbering is used (3.4.2), I don't see an easy way to continue from the max number of a previous document.

Due to this considerations, I would definitely merge all documents into one before numbering.


I will also use a two phase transformation. But just for fun, with one include level and no repetition, this stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:variable name="vIncludes" select="//include"/>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="include">
        <xsl:apply-templates select="document(@src)"/>
    </xsl:template>
    <xsl:template match="picture">
        <xsl:variable name="vRoot" select="generate-id(/)"/>
        <xsl:variable name="vInclude"
                      select="$vIncludes[
                                 $vRoot = generate-id(document(@src))
                              ]"/>
        <xsl:copy>
            <xsl:value-of
                 select="count(
                            document(
                               (.|$vInclude)/preceding::include/@src
                            )//picture |
                            (.|$vInclude)/preceding::picture
                         ) + 1"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

With this input:

<master>
    <include src="child3.xml"/>
    <block>
        <include src="child1.xml"/>
        <picture/>
    </block>
    <include src="child2.xml"/>
    <picture/>
</master>

And 'child1.xml'

<child1>
    <picture/>
</child1>

And 'child2.xml'

<child2>
    <picture/>
</child2>

And 'child3.xml'

<child3>
    <picture/>
</child3>

Output:

<master>
    <child3>
        <picture>1</picture>
    </child3>
    <block>
        <child1>
            <picture>2</picture>
        </child1>
        <picture>3</picture>
    </block>
    <child2>
        <picture>4</picture>
    </child2>
    <picture>5</picture>
</master>


You could use an ancillary XML documentto keep track of the last figure number and load that file as a document from your stylesheet. Or, if you do not want to manage two output files from the same stylesheet (the "real" FOP output and the figure counter) you can simply load the previous chapter's FOP file and look for the MAX of the figure-caption.

Or you could pass the last figure number as a parameter with default zero and pass the parameter on the command line. The value of this parameter resulting from the parsing of the previous one in the ascending resulting document order.

All these alternatives suppose you are running the transformations in sequence in source document ascending order.

A more structured and robust solution would be to manage transverse document sections such as indices, table of contents and table of figures in as many separate FO documents that would be generated in a "second pass" run with their own XSLT.


I think I would do a prepass which outputs summary information about all the documents in a single XML file, and then use this as a secondary input to the number calculation. The summary information in your case might just be a count of how many figures each document contains, but in many cases it can be useful to hold other information as well such as the IDs of sections that will act as the target of hyperlinks.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜