Page numbers in xsl-fo (and apache fop) with multiple page-sequences

user1414745 picture user1414745 · Mar 26, 2014 · Viewed 14.6k times · Source

I have a document consisting of multiple sub-documents. Here is how a sub-document looks like:

1 page
2-n pages

Then comes the next sub-document with the same structure:

1 page
2-m pages

As I said, those sub-documents are combined in a single .pdf file.

Here is my xsl-fo tempalte:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
  <xsl:output method="xml" indent="yes" />
  <xsl:template match="/">
    <fo:root font-size="11pt" font-family="serif">
      <fo:layout-master-set>
        <fo:simple-page-master master-name="A4-portrait"
          page-height="29.7cm" page-width="21.0cm" margin-top="1cm"
          margin-left="1.5cm" margin-right="1cm" margin-bottom="1cm">
          <fo:region-body />
          <fo:region-after region-name="footer" extent="15mm"/>
        </fo:simple-page-master>
      </fo:layout-master-set>

      <!-- first sub-document -->
      <fo:page-sequence master-reference="A4-portrait" initial-page-number="1">
      ...........
        <fo:block text-align="center">
            Page <fo:page-number/>/<fo:page-number-citation-last ref-id="end"/>
        </fo:block>
      ...........
      </fo:page-sequence>
      <fo:page-sequence master-reference="A4-portrait" id="end">
      ...........
        <fo:block text-align="center">
            Page <fo:page-number/>/<fo:page-number-citation-last ref-id="end"/>
        </fo:block>
      ...........
      </fo:page-sequence>

      <!-- second sub-document -->
      <fo:page-sequence master-reference="A4-portrait" initial-page-number="1">
      ...........
        <fo:block text-align="center">
            Page <fo:page-number/>/<fo:page-number-citation-last ref-id="end"/>
        </fo:block>
      ...........
      </fo:page-sequence>
      <fo:page-sequence master-reference="A4-portrait" id="end">
      ...........
        <fo:block text-align="center">
            Page <fo:page-number/>/<fo:page-number-citation-last ref-id="end"/>
        </fo:block>
      ...........
      </fo:page-sequence>

    </fo:root>
  </xsl:template>
</xsl:stylesheet>

So, I have 2 identical blocks consisting of 2 page-sequences each. In my example, let's say sub-document 1 has 4 pages (n=4) and sub-document 2 has 2 pages (m=2). Here are the page numbers that I get:

1/4
2/4
3/4
4/4
1/4
2/2

Everything is OK, except the first page of the second sub-document. At that point, <fo:page-number-citation-last ref-id="end"/> returns 4, which is the value from sub-document 1. So, instead of 1/2, I get 1/4.

Any suggestions how I can fix this?

Answer

Mathias M&#252;ller picture Mathias Müller · Mar 26, 2014

Can you please write this as an answer (to both my questions), so that I can accept it?

Sure. The precondition for referring to a specific element in XSL-FO is that you have a means of identifying it unambiguously. The best way to do this is to use an ID attribute for the element you wish to identify.

An ID must consist of any sequence of alphanumerical characters and _ (that is, its value must be a valid NCName, see the specification here).

You can type out each ID by hand, but normally this is done with xsl:generate-id() (wich is a function of XSLT, not XSL-FO). generate-id() automatically ensures that the IDs are unique throughout a document.

Note that generate-id() actually generates an ID for either the current template match or the node that is being processed in an iteration of xsl:for-each.

You can put an ID on every element in XML and referring to something by ID is therefore possible for any element in XSL-FO.


Since you provide more context here, I'll provide a more extensive answer. It should now be clear how you can resolve the issue, but it is still unclear why the problem arises in the first place.

The reason why the page numbers are wrong in your original stylesheet is that in the first page sequence of the second sub-document, you intend to refer to an ID that occurs after you refer to it, while at the same time IDs are not unique in the document.

In other words, in this page sequence:

  <!-- second sub-document -->
  <fo:page-sequence master-reference="A4-portrait" initial-page-number="1">
  <!--...-->
    <fo:block text-align="center">
        Page <fo:page-number/>/<fo:page-number-citation-last ref-id="end"/>
    </fo:block>
  <!--...-->
  </fo:page-sequence>

when you reference an object by its ID (ref-id="end"), the XSL-FO document is searched for an object with this ID that occurs before the ref-id. As soon as a node with a corresponding ID is found, the search terminates and this node is handed to fo:page-number-citation.

In this case, the object with a matching ID occurring before ref-id is a page sequence of the first subdocument.