Chapter 9 of the XSL-FO Tutorial
9. RenderX Extensions
9.1. Document Info
PDF documents can have a number of information fields associated to them; these are displayed in Acrobat Reader when you choose Document Info / General from the File menu. Standard fields are ‘Author’, ‘Title’, ‘Creator’, ‘Subject’, and ‘Keywords’. XSL FO provides no means to set these values.
IREn 4.9 provides a mechanism to set these fields from within the XSL FO document, using additional formatting objects. To trace a clear distinction between objects comprised in the XSL 1.0 Recommendation and those added by RenderX, the namespace mechanism is used: any and all additional elements or properties will have a different namespace prefix, associated to another namespace:
xmlns:rx="http://www.renderx.com/XSL/Extensions"
We need two extension elements to express document information fields: <rx:meta-info> and <rx:meta-field>.
-
<rx:meta-info>This element is merely a container for one or more<rx:meta-field>elements. It should be the first child of<fo:root>. -
<rx:meta-field>This element specifies a single name/value pair. It has two mandatory attributes:nameandvalue. Current implementation of the PDF and PostScript generators recognizes four possible values forname:-
name="author"fills the ‘Author’ field in the resulting PDF file with a string specified by thevalueproperty; -
name="title"fills the ‘Title’ field; -
name="subject"fills the ‘Subject’ field; -
name="keywords"fills the ‘Keywords’ field.
All other values for
nameare ignored. The ‘Creator’ field in the PDF file is set to the name of the application that created the document —"XEP 4.9". There is no method to control it from the source file. -
Let's see a code example containing this extension:
<?xml version="1.0" encoding="iso-8859-1"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:rx="http://www.renderx.com/XSL/Extensions"❶>
<rx:meta-info>❷
<rx:meta-field name="author" value="John Doe"/>
<rx:meta-field name="title" value="Sample Document"/>
<rx:meta-field name="subject" value="PDF Generation Test"/>
<rx:meta-field name="keywords" value="XSL, PDF, FO Extensions"/>
</rx:meta-info>
<fo:layout-master-set>
…
…
…
| ❶ | Here's the namespace declaration for RenderX additional elements. It is obligatory if you use extensions. |
| ❷ | Note the different namespace prefix. |
9.2. PDF Bookmarks
PDF documents can have an associated outline of their structure, commonly referred to as bookmarks. Being specific to a particular output format, bookmarks aren't covered by the XSL 1.0 Recommendation. IREn 4.9 implements three extension elements that enable bookmarks in PDF output:
-
<rx:outline>Top-level element of the document outline tree. It should be located before any<fo:page-sequence>elements, but after<fo:layout-master-set>and<fo:declarations>(if present). Contains one or more<rx:bookmark>elements. -
<rx:bookmark>This element holds information about a single bookmark. It contains a mandatory<rx:bookmark-label>element as its first child. Bookmark destination is expressed either byinternal-destinationproperty (for internal navigation), or byexternal-destination(for extra-document links).This element can also contain nested
<rx:bookmark>elements that produce subordinate bookmarks. -
<rx:bookmark-label>This element contains text of a bookmark label; it must be the first child of its parent<fo:bookmark>. Contents of this element is plain text.
Shown below is a typical markup of bookmarks for a document consisting of two chapters, each of them having two subchapters:
…
…
…
</fo:layout-master-set>
<rx:outline>
<rx:bookmark internal-destination="chap_1">❶
<rx:bookmark-label>Chapter 1</rx:bookmark-label>
<rx:bookmark internal-destination="subchap_1_1">❷
<rx:bookmark-label>Subchapter 1.1</rx:bookmark-label>
</rx:bookmark>
<rx:bookmark internal-destination="subchap_1_2">❸
<rx:bookmark-label>Subchapter 1.2</rx:bookmark-label>
</rx:bookmark>
</rx:bookmark>
<rx:bookmark internal-destination="chap_2">❹
<rx:bookmark-label>Chapter 2</rx:bookmark-label>
<rx:bookmark internal-destination="subchap_2_1">
<rx:bookmark-label>Subchapter 2.1</rx:bookmark-label>
</rx:bookmark>
<rx:bookmark internal-destination="subchap_2_2">
<rx:bookmark-label>Subchapter 2.2</rx:bookmark-label>
</rx:bookmark>
</rx:bookmark>
</rx:outline>
<fo:page-sequence master-reference="page">❺
…
…
…
| ❶ | Bookmark for first chapter. |
| ❷ | Bookmark for first subchapter. Note that is nested into the bookmark for chapter. In Acrobat Reader, the chapter bookmark will be shown as a parent of the subchapter one. |
| ❸ | Bookmark for second subchapter of the first chapter. |
| ❹ | Bookmark for second chapter. |
| ❺ | Remember that <rx:outline> should be located immediately before the first page sequence. |
9.3. Indexes
Building page number lists for back-of-the-book indexes is a common task. It is relatively easy to collect a list of references to locations of index terms in the text; but then, to turn it into a real index entry, one should exclude repeated page numbers and merge adjacent numbers into ranges. Neither of these two operations can be done by pure XSLT/XSL-FO means. In this situation, introducing an extension looks inevitable.
The task of building an index can be split in two fairly independent subtasks:
-
mark up occurrences of index terms in the main text;
-
specify composition and formatting of page number lists in the index.
For the first task, IREn introduces a special extension attribute: rx:key. It can be specified on any element that can carry an id; unlike the latter, it need not be unique across the document. Its value is used as a key to select elements for the page number list.
If the element bearing rx:key completely fits onto one page, it will be represented as a single page number in the page number list. If it spans multiple pages, its entry in the page number list will be formatted as a range from the first to the last of the spanned pages.
There is also a mechanism to specify ranges explicitly. Two extension elements serve for the purpose:
-
<rx:begin-index-range>Starts a range. Has two attributes, both required:-
ida unique identifier used to establish coupling; -
rx:keyindex key used to select the range into a page number list.
-
-
<rx:end-index-range>Ends a range. Has one attribute, required:ref-ida reference to a<rx:begin-index-range>that started the range.
These two elements always form a pair: <rx:begin-index-range> is required to have an id attribute, and <rx:end-index-range> should have a ref-id with the same value. In the page number list, the pair is represented by a range from the page where <rx:begin-index-range> is located to the page where its matching <rx:end-index-range> resides. These elements may be located anywhere inside <fo:flow>: there are no constraints on their nesting with respect to other elements.
The actual index entry is created by another extension element, <rx:page-index>. It picks elements from the text by their rx:key properties, and produces a sorted list of unique page numbers. Items in the list are separated by an intercalary text, specified by the list-separator property; default is comma+space (", ").
<rx:page-index> should contain one or more <rx:index-item> elements as children. Each <rx:index-item> has a required ref-key attribute, and selects elements that have an rx:key with the same value.
Note: In IREn 4.9,
<rx:index-item>only searches elements preceding it in the document; no forward references are implemented. This limitation is likely to persist in future versions of IREn.
A basic entry in an index will look like this:
<fo:inline rx:key="key.elephant">Elephants</fo:inline> live in Africa. …
<fo:inline rx:key="key.elephant">African elephants</fo:inline> have big ears …
…
<fo:block text-align="center" font="bold 16pt Futura">INDEX</fo:block>
<fo:block>
Elephants <rx:page-index>
<rx:index-item ref-key="key.elephant"/>
</rx:page-index>
</fo:block>
There are more attributes of <rx:index-item> to control the look of the index entry:
-
range-separatorString used to separate page numbers that form a continuous range. Default is en dash:"–"(U+2013). -
merge-subsequent-page-numbersControls whether sequences of adjacent page numbers should be merged into ranges. Default is"false". -
link-backIf set to"true", page numbers are formatted as hyperlinks back to the location of their correspondent index terms. Default is"false".
Besides that, <rx:index-item> can bear additional inline properties, applied individually to each page number generated from this element. This gives a possibility to differentiate presentation styles across the list, e.g. make references to primary definitions bold. The following example illustrates it:
<fo:inline rx:key="key.elephant">Elephants</fo:inline> live
in Africa. …
<fo:inline rx:key="key.elephant.primary">Elephant</fo:inline> is
a quadruped mammal. …
<fo:inline rx:key="key.elephant">African elephants</fo:inline> have big
ears …
…
<fo:block text-align="center" font="bold 16pt Futura">INDEX</fo:block>
<fo:block>
Elephants <rx:page-index>
<rx:index-item ref-key="key.elephant.primary"
font-weight="bold"
link-back="true"/>❶
<rx:index-item ref-key="key.elephant"/>
</rx:page-index>
</fo:block>
| ❶ | When duplicates are removed, initial <rx:index-item> entries take precedence. Therefore, references to primary rx:keys should be placed first inside <rx:page-index>. |
9.4. Flow Sections
<rx:flow-section> is a generalization of a <fo:block> with span="all" attribute: it changes the column count for a part of <fo:flow>. Unlike the case of span="all", the number of columns inside <rx:flow-section> can be set to any value.
The syntax for this extension is straightforward:
-
<rx:flow-section>may only occur as a direct child of<fo:flow>; -
only block-level elements can be descendants of an
<rx:flow-section>; -
<rx:flow-section>can takecolumn-countandcolumn-gapattributes to control the number of columns and the separation between them. Columns in a<rx:flow-section>are always balanced.
Here is a short example. A text is located on a two-column page; it starts with a title that spans all columns, and then continues with an abstract formatted into three columns. After the abstract, the text turns back to its primordial two-column state.
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:rx="http://www.renderx.com/XSL/Extensions">
<fo:layout-master-set>
<fo:simple-page-master>
<fo:region-body margin="1in" column-count="2"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="xsl-region-body">
<fo:flow flow-name="xsl-region-body">
<fo:block font-size="16pt" span="all">TITLE</fo:block>
<rx:flow-section column-count="3" column-gap="18pt">
<fo:block font-size="10pt">
Abstract occupies three columns …
</fo:block>
</rx:flow-section>
<fo:block>
Normal text continues in two columns as specified in the page master …