Page numbers: Page x of y

There are two ways to get the last page number. The first is the simple way and is recommended. The second one was the way to go until version 3.9.26 and can be used as a reference to store other data needed for subsequent runs.

The simple way: $_lastpage

Just use the internal variable $_lastpage to get the pagenumber of the last page of the previous run. That means that you have to use at least two runs to get the correct value for the variable. This works since version 3.9.26.

The more complicated way

To specify the length of the document in pages, a second pass of the Publisher is necessary: At the end of the first pass, the current (= last) page number is stored, which can then be used in subsequent passes.

The following example creates some pages with output in the form Page 1 of ??. This serves as a basis for the additions.

<Layout xmlns="urn:speedata.de:2009/publisher/en"
  xmlns:sd="urn:speedata:2009/publisher/functions/en">

  <Record element="data">
    <!-- ?? is a placeholder that gets overridden -->
    <SetVariable variable="maxpages" select="'??'"/>

    <Loop select="10" variable="i">
      <PlaceObject>
        <Textblock>
          <Paragraph>
            <Value
              select="concat('Page ', sd:current-page(), ' of ', $maxpages )"/>
          </Paragraph>
        </Textblock>
      </PlaceObject>
      <SetVariable variable="lastpage" select="sd:current-page()"/>
      <ClearPage/>
    </Loop>
  </Record>
</Layout>

At the end of the last page the information can be cached for the next run using <SaveDataset>:

<SetVariable variable="attrPagenumber">
  <Attribute name="numberofpages" select="$lastpage"/>
</SetVariable>
<SaveDataset
  name="pagecount"
  elementname="pageinfo"
  attributes="$attrPagenumber"/>

<SaveDataset> expects an XML structure at attributes can be stored in the element `<Attributes>' and, elements in `<Element>', whereby this command can in turn have `<Attributes>' as child elements. This structure is saved to disk as XML and has the following form in this example:

<pageinfo numberofpages="10" />

At the beginning of the run, the file can now be read in if it exists (i.e. no error is generated in the first run because the file has not yet been created):

<Record element="data">
  <SetVariable variable="maxpages" select="'??'"/>
  <LoadDataset name="pagecount"/>
  ...

and in addition

<Record element="pageinfo">
  <SetVariable variable="maxpages" select="@numberofpages"/>
</Record>

The record 'pageinfo' is called when the XML file 'pagecount' is read. Nothing else is done than overwriting the recently defined variable maxpages with the correct content.

The complete example now looks like this:

<Layout
  xmlns="urn:speedata.de:2009/publisher/en"
  xmlns:sd="urn:speedata:2009/publisher/functions/en">

  <!-- only evaluated if the file `pagecount` is found (second run) -->
  <Record element="pageinfo">
    <SetVariable variable="maxpages" select="@numberofpages"/>
  </Record>

  <!-- start of data processing -->
  <Record element="data">
    <SetVariable variable="maxpages" select="'??'"/>
    <LoadDataset name="pagecount"/>
    <Loop select="10" variable="i">
      <PlaceObject>
        <Textblock>
          <Paragraph>
            <Value
              select="concat('Page ', sd:current-page(), ' of ', $maxpages )"/>
          </Paragraph>
        </Textblock>
      </PlaceObject>
      <SetVariable variable="lastpage" select="sd:current-page()"/>
      <ClearPage/>
    </Loop>
    <!--  Now we know the total number of pages  -->
    <SetVariable variable="attrPagenumber">
      <Attribute name="numberofpages" select="$lastpage"/>
    </SetVariable>
    <SaveDataset
      name="pagecount"
      elementname="pageinfo"
      attributes="$attrPagenumber"/>
  </Record>
</Layout>