Chapter 8 of the XSL-FO Tutorial
8. Advanced Features
8.1. Containers and Reference Orientation
<fo:block-container> objects group blocks in a separate area that can have a different orientation of coordinate axes or writing direction. A special reference-orientation property sets the orientation of the "top" direction for the area. Its value is the rotation angle, measured in degrees: 0, 90, 180, 270, -90, -180, -270. Positive values rotate the coordinate system counterclockwise, and negative ones turn it clockwise.
Let us consider a simple example:
<fo:block-container width="250pt" height="20pt"
border="1pt solid black"
reference-orientation="0">❶
<fo:block text-align="left">❷
Regular text.
</fo:block>
</fo:block-container>
<fo:block-container width="250pt" height="20pt"
border="1pt solid black"
reference-orientation="180">❸
<fo:block text-align="left">❹
Text shown upside down.
</fo:block>
</fo:block-container>
| ❶ | This container has a default reference-orientation of 0. Note that the size of the rectangular area occupied by the container is explicitly specified. |
| ❸ | The text in this block should be shown upside down. |
| ❷ ❹ | Blocks in both containers have text-align="left". Since "left" is determined with respect to the "top", the text in this block should be aligned to the opposite side than in the previous one. |
Besides containers, reference-orientation property can apply to <fo:simple-page-master> and <fo:region-*> formatting objects. In the following example, I create a page master with a single <fo:region-body> whose contents is rotated 90° clockwise:
<?xml version="1.0" encoding="iso-8859-1"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="my-page">
<fo:region-body border="0.25pt solid silver"
margin="0.5in"
padding="12pt"
reference-orientation="-90"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="my-page">
<fo:flow flow-name="xsl-region-body">
<fo:block>
Text and images on the page
<fo:external-graphic src="url('smile.gif')"
content-height="100%"
content-width="100%"/>
</fo:block>
<fo:block-container width="250pt" height="20pt"
border="1pt solid black"
reference-orientation="180">❶
<fo:block>
Text flips upside down.
</fo:block>
</fo:block-container>
</fo:flow>
</fo:page-sequence>
</fo:root>
| ❶ | The rotation angle specified by reference-orientation is measured from the orientation of the parent area. In this case, 180° specified on the <fo:block-container> will be added to -90° already on the <fo:region-body>; the resulting text rotation will be 90° counterclockwise. |
8.2. Writing Mode and Bidirectionality
XSL FO has powerful means to deal with non-Western writing systems. Lines can be made horizontal or vertical; character can be ordered from any side; line stacking order on the page can also be varied. To define ordering of characters within lines and stacking direction of lines into paragraphs, we use writing-mode property. It can be specified on <fo:simple-page-master>, <fo:region-*>, <fo:table>, <fo:block-container>, and <fo:inline-container> elements. Its primary values are:
-
"lr-tb": left-to-right, top-to-bottom. This is the default writing mode in XSL-FO; it is used by the majority of world languages, including English; -
"rl-tb": right-to-left, top-to-bottom. This mode is used in Arabic writing system (adopted by many languages of the Middle East), Hebrew, and Syriac alphabets. -
"tb-rl": top-to-bottom, right-to-left. This way of writing is widely used for Japanese, but also for Chinese and other languages of the East Asia.
Note: As of version 4.9, IREn supports only horizontal writing modes:
"lr-tb"and"rl-tb".
writing-mode property defines every aspect of the document organization: binding edge, column ordering in tables, text alignment in blocks, etc. It also sets the correspondence between relative directions (before – after – start – end)and absolutely oriented ones (top – bottom – left – right).
However, internationalization issues are too complex to be described by a single property. In right-to-left writing systems (Arabic and Hebrew), it is not uncommon to include fragments of text written in Latin alphabet; such text progresses left-to-right. The mechanism that permits mixing writing directions within the same fragment of text is called bidirectionality, or bidi for short. The Unicode standard defines rules to calculate ordering of characters in bidirectional text; the respective part of the Unicode, Annex #9: The Bidirectional Algorithm, is adopted by XSL FO.
However, in certain cases the Unicode bidi algorithm is not enough to determine character ordering. For this purpose, XSL defines a special element <fo:bidi-override> that permit to alter the bidirectional behaviour of the whole text or its parts. It has two properties:
-
directionSets the dominant direction for a span of text. Possible values are:-
"ltr"— from left to right; -
rtlfrom right to left.
-
-
unicode-bidiSpecifies behaviour of a text span with respect to the Unicode bidi algorithm. Possible values are the following:-
"normal"— order characters by Unicode bidi; -
"embed"— open a new level of embedding; -
"bidi-override"— ignore directionality of the text and arrange characters in the order specified by thedirectionproperty.
-
The following example shows two block containers with different writing modes. Both contain a mixture of English (left-to-right) and Hebrew (right-to-left) text. The first container has writing-mode="lr-tb" (default): its content will be treated as an English phrase with inclusions of Hebrew words. The second container has writing-mode="rl-tb", so its content will be considered a Hebrew phrase with some English words in it.
<fo:block-container writing-mode="lr-tb">❶
<fo:block> The words of Queen Esther,
וכאשׁר
אבדתּי
אבדתּי
"If I perish [in trying to save my people], I perish"
</fo:block>
</fo:block-container>
<fo:block-container writing-mode="rl-tb">❷
<fo:block> The words of Queen Esther,
וכאשׁר
אבדתּי
אבדתּי
"If I perish [in trying to save my people], I perish"
</fo:block>
</fo:block-container>
In the following small code snippet, <fo:bidi-override> formatting object is used to turn English text inside out, writing Latin symbols right-to-left (as if they were Hebrew characters):
…
<fo:block>
<fo:bidi-override unicode-bidi="bidi-override" direction="rtl">❶
This text ought to be turned inside out.
</fo:bidi-override>
</fo:block>
…
| ❶ | direction property sets writing direction, and unicode-bidi="bidi-override" cancels the effects of the Unicode BIDI algorithm, forcing all characters in the text to go from right to left. |
8.3. Links
There are two kinds of links in XSL FO:
-
links to locations inside the document;
-
links to external entities/locations.
Both are achieved using the same formatting object — <fo:basic-link>. To make an internal link, the referenced object must have an id attribute that is cited in the internal-destination attribute of the link object:
<fo:basic-link internal-destination="smiley"
text-decoration="underline">Click here</fo:basic-link>
An external link must have an URI specified in the external-destination attribute:
<fo:basic-link external-destination="url('http://www.RenderX.com/')"
text-decoration="underline"
color="blue">RenderX Home</fo:basic-link>
Unlike HTML, no default formatting is applied to links in XSL FO; you should provide character-level properties on <fo:basic-link> to distinguish it from the rest of the text (in the example above, color and text-decoration make the link text blue and underlined, as in a browser).
Note the url('…') notation inside the external-destination attribute; this is required by the XSL 1.0 Recommendation. (IREn handles unwrapped URLs, too).
In IREn, URLs starting with explicit "file:" protocol specification are rendered as PDF inter-document links. All other links are treated as Internet URIs, and open in a browser.
8.4. Leaders
A leader is an object used in XSL FO to create horizontal rules, lengthy white spaces, dot-filled tabs etc. Here is an example of a horizontal inline rule used to form a nice separator:
<fo:block text-align="center">
<fo:leader leader-length="2in"
leader-pattern="rule"
alignment-baseline="middle"❶
rule-thickness="0.5pt" color="black"/>
<fo:inline font="16pt ZapfDingbats"
color="#E00000">❋❷</fo:inline>
<fo:leader leader-length="2in"
leader-pattern="rule"
alignment-baseline="middle"
rule-thickness="0.5pt" color="black"/>
</fo:block>
| ❶ | This aligns the leader vertically to the middle baseline — more or less at the level where strokes of a small x character cross. |
| ❷ | This draws a red eight-lobe asterisk. All dingbats should be referenced by their Unicode values; see IREn documentation for a list of codes assigned to ZapfDingbats glyphs. |
See also an example of leader usage in a section about page numbers.
8.5. Footnotes and Floats
To insert a footnote at the bottom of the page, you should use a <fo:footnote> formatting object. It contains two formatting objects as its children:
-
<fo:inline>contains an inline content used as a footnote anchor; -
<fo:footnote-body>object stores the text of the footnote body; its content will be placed at the bottom of the page.
Lists are often used to format footnote bodies. The example below shows a typical case:
<fo:block>
This text contains a footnote<fo:footnote>❶
<fo:inline baseline-shift="super"
font-size="smaller">(1)</fo:inline>
<fo:footnote-body>
<fo:list-block provisional-label-separation="0pt"
provisional-distance-between-starts="18pt"
space-after.optimum="6pt"❷>
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>(1)</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>Footnote text</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
</fo:footnote-body>
</fo:footnote>
after the word "footnote".
</fo:block>
| ❶ | Footnote opening tag is placed immediately after the word “footnote”; breaking a line here would cause the footnote citation to detach from the word. |
| ❷ | This serves to separate adjacent footnote bodies. |
Normally, footnotes are divided from the rest of the text by a separator. Separators are created in a special region named xsl-footnote-separator. You can insert content into it using a <fo:static-content> element. The example below shows a separator consisting of a solid line:
<fo:page-sequence>
<fo:static-content flow-name="xsl-footnote-separator">
<fo:block>
<fo:leader leader-pattern="rule"
leader-length="100%"
rule-style="solid"
rule-thickness="0.5pt"/>
</fo:block>
</fo:static-content>
…
…
…
Floats are similar to footnotes: they define a block that drifts to the top/left/right side of the page while the text flows around it. A typical use for floats is to put a picture, a table, etc. aside so that it does not disrupt the flow of the text. Here is an example of a top-float:
<fo:block>
This text includes a floating picture.
<fo:float float="before">
<fo:block text-align="center"
border="1pt solid gray"
font="bold italic 9pt Helvetica">
<fo:block>
<fo:external-graphic src="url('smile.gif')"/>
</fo:block>
<fo:block>
Fig. 1: A Smiling Face
</fo:block>
</fo:block>
</fo:float>
This text follows the float anchor.
</fo:block>
Note: Due to implementation restrictions of IREn 4.9, top floats (
float="before") appear on the top of the column next to the current one. For details, refer to XSL Formatting Objects in IREn 4.9 (doc/spec.pdfin the IREn package).
The next example shows how to create a dropcap using a side float:
<fo:block intrusion-displace="line"❶>
<fo:float float="start"❷>
<fo:block font="bold 50pt/38pt❸ Helvetica" color="red">T</fo:block>
</fo:float>
his text starts with a big red letter T that hangs beneath the
baseline. Few initial lines of text are shortened to make room
for the dropcap.
</fo:block>
| ❶ | Property intrusion-displace controls interaction of formatting objects with side floats. "line" is the default value for <fo:block> (in this case, it could be omitted): it forces lines of text to shrink, leaving room for the float. |
| ❷ | As in many places before, "start" means “start of line”; in the Western writing mode, this float will drift to the left. |
| ❸ | Notice that in the font shorthand attribute, the line-height is set to a smaller value than the font-size (38 pt vs. 50 pt). This serves to remove blank area (also known as leading) before and after the character on the line. |
8.6. Page Numbering and Page Number References
To insert the current page number, use <fo:page-number> element:
<fo:static-content flow-name="xsl-region-before">
<fo:block text-align="end">Page <fo:page-number/></fo:block>
</fo:static-content>
To insert a reference to a page where a certain element resides, that element must have a unique id property. Then you can reference it by a <fo:page-number-citation> element:
<fo:external-graphic id="smiley" src="url('smile.gif')"/>
…
…
…
As shown on the "Smiling Face" diagram
(see page <fo:page-number-citation ref-id="smiley"/>), …
Page number citation can be used to obtain the total number of pages in the document: just place an empty block at the end of the text and refer to its page number, like in the example below:
<fo:static-content flow-name="xsl-region-before">
<fo:block text-align="end">
Page <fo:page-number/>
of <fo:page-number-citation ref-id="terminator"/>
</fo:block>
</fo:static-content>
<fo:flow>
…
…
…
<fo:block id="terminator"/>
</fo:flow>
Another important use of page number citations is to create tables of contents and indices. The example below shows a typical TOC entry in IREn:
<fo:block text-align-last="justify"❶>
1. Introduction
<fo:leader leader-pattern="dots"/>❷
<fo:page-number-citation ref-id="intro"/>
</fo:block>
| ❶ | The text-align-last attribute makes the last line extend up to the right edge of the text. If a <fo:leader> element is present on the line, it will grow so as to absorb all the remaining free space on the line (but not more that its leader-length.maximum). |
| ❷ | This element creates a dotted fill. The default value for leader-length.maximum is "100%" (i.e. equal to the width of the surrounding block); that's why we need not explicitly specify a length here. |
8.7. Markers
Markers are used to change the contents of a side region according to the contents of the body region. A typical task performed with markers is to create running headers — to put the division title at the header of the page.
Actual use of markers involves two formatting objects:
-
<fo:marker>creates a fragment of the document tree and associates it with a span of text in the flow; -
<fo:retrieve-marker>picks up a fragment created by an<fo:marker>and pastes it into a side region.
The contents of a<fo:marker> can be any block-level or inline-level formatting objects. <fo:marker> elements (one or more) can be the initial children of any formatting object that produces areas (<fo:block>, <fo:inline>, etc., including <fo:wrapper>). A <fo:marker> can appear inside <fo:flow> only. It possesses an only attribute — marker-class-name, used as a key when retrieving it.
<fo:retrieve-marker> is an empty element that can occur inside <fo:static-content> only. When the formatter builds a page instance, it replaces occurrences of <fo:retrieve-markers> by matching <fo:markers> found in the text flow.
Here is a basic example of using markers:
<fo:static-content flow-name="xsl-region-before">
<fo:block text-align="center">
<fo:retrieve-marker retrieve-class-name="division"/>
</fo:block>
</fo:static-content>
…
…
…
<fo:block>❶
<fo:marker marker-class-name="division">❷
Introduction
</fo:marker>
<fo:block font-weight="bold" text-align="center">
1. Introduction
</fo:block>
<fo:block text-indent="0.5in">
Let me introduce you to something …
… … …
</fo:block>
</fo:block>