Declarative Amsterdam

XSL-FO: More declarative than you know

Tony Graham
Antenna House
Abstract

XSL-FO is more declarative than you know. When the processing model has no feedback between the XSLT stage and the final output and no conditionals in the expression language, everything has to be declarative. You declare up front everything from the page masters to use for left, right, first, last, and only pages (if the document makes any of those) to the index keys and their classes that are collected and potentially merged into page number ranges to initial letters that are sized align at the top with small capital letters on the first line and at the bottom with the baseline of a line further down the block. This paper covers all of those, and more, to show just how declarative XSL-FO can be.

Table of contents

1.What’s in a name?

XSL-FO is the name used11“XSL-FO” is not defined in the XSL 1.1 Recommendation. There are multiple instances of “FO” or “FOs”, as well as “XSL FO” to refer to an XSL-defined formatting object. The only “XSL-FO” are “XSL-FO result intstance” and “XSL-FO instance” in the explanation of a table markers example. Table markers were added in XSL 1.1, published five years after XSL 1.0. Five extra years was plenty of time for the term to become perhaps more established than had been intended initially. for the XML vocabulary of elements and attributes with semantics defined by the XSL 1.1 Recommendation. [XSL11]XSL11 Anders Berglund, Editor: Extensible Stylesheet Language (XSL) Version 1.1. Recommendation, World Wide Web Consortium, 5 December 2006. https://​www​.w3​.org​/TR​/xsl11/

XSL is the abbreviation for “Extensible Stylesheet Language”, which is a language for designing stylesheets. FO is the abbreviation for “formatting object”, which is the term for both the namespace of the elements that an XSL stylesheet processor understands and the elements in that namespace.

Strictly speaking, XSL defines both tree transformation and formatting. The tree transformation part proved generally useful for transforming arbitrary XML into other XML (or to non-XML text), standalone transformation processors were developed, and the tree transformation part was broken out as “XSL Transformations” (XSLT). XSLT took its own path, and now tree transformation in XSL 1.1 is two paragraphs that refer to XSLT 1.0 (which was current in 2006), while mentions of XSL in the XSLT 3.0 Recommendation amount to one paragraph and a non-normative reference. The remaining influences of XSL on XSLT are: calling an XSLT XML document a “stylesheet”; the convention of prefixing XSLT elements with xsl:; and majority use of xsl:stylesheet in preference to its xsl:transform synonym as the document element in an XSLT stylesheet.

These days, people tend to think of the XSL 1.1 Recommendation as defining only formatting, possibly irrespective of whether they know that XSL normatively includes XSLT. So “XSL-FO” often means XSL instead of just the formatting object vocabulary, and “XSL” means XSLT, except when it means XSL, but at least “XSLT” always means XSLT.

The title of this paper uses “XSL-FO” because that is what people understand,22Not just to get a bit of a rhyme. but this paper discusses both the transformation and formatting aspects of the XSL processing model.

2.Prehistory

The XSL 1.1 Recommendation notes that its design follows that of CSS and DSSSL [DSSSL]DSSSLhttp://​xml​.coverpages​.org​/dsssl96​-ps​.zip (from http://​xml​.coverpages​.org​/dsssl​.html):

XSL builds on the prior work on Cascading Style Sheets [CSS2]CSS2http://​www​.w3​.org​/TR​/1998​/REC​-CSS2​-19980512/.) and the Document Style Semantics and Specification Language [DSSSL]DSSSLhttp://​xml​.coverpages​.org​/dsssl96​-ps​.zip (from http://​xml​.coverpages​.org​/dsssl​.html).

Cascading Style Sheets, level 1, [CSS1]CSS1 World Wide Web Consortium. Cascading Style Sheets, level 1. W3C Recommendation 17 December 1996. URL: https://​www​.w3​.org​/TR​/REC​-CSS1/ became a Recommendation in 1996. CSS was co-invented by Håkon Wium Lie and Bert Bos. CSS 1 built upon previous style sheet proposals, including earlier separate proposals by Lie and Bos. References to the earlier proposals are at: the W3C Historical Style Sheet proposals [HSSP]HSSP World Wide Web Consortium. Historical Style Sheet proposals. 6 January 2021. URL: https://​www​.w3​.org​/Style​/History/ (http:/​/web​.archive​.org​/web​/20201031230541​/https://​www​.w3​.org​/Style​/History/) page; the The CSS saga [LIEBOS2E]LIEBOS2E Håkon Wium Lie and Bert Bos. The CSS saga. URL: https://​www​.w3​.org​/Style​/LieBos2e​/history/ (http:/​/web​.archive​.org​/web​/20201018013706​/https://​www​.w3​.org​/Style​/LieBos2e​/history/) chapter from Lie and Bos’s CSS book: and Lie’s Ph.D thesis [LIE]LIE Håkon Wium Lie. Cascading Style Sheets. Ph.D Thesis. 2005. URL: https://​wiumlie​.no​/2006​/phd/. The goals for CSS stated in 1995 [1995]1995 Bert Bos. Report on the W3C style sheet workshop, Paris ’95. URL: https://​www​.w3​.org​/Style​/951106​_Workshop​/report1​.html include: “CSS supports stream-based (or ‘incremental’ formatting) where possible”; “CSS offers both readers and authors control over the style”; as well as “avoiding an uncontrolled growth of HTML extensions”.

Cascading Style Sheets, level 2, [CSS2]CSS2http://​www​.w3​.org​/TR​/1998​/REC​-CSS2​-19980512/.) became a Recommendation in 1996. The XSL 1.0 Recommendation [XSL10]XSL10 World Wide Web Consortium. Extensible Stylesheet Language (XSL). W3C Recommendation. URL: https://​www​.w3​.org​/TR​/2001​/REC​-xsl​-20011015/ also incorporates CSS2 errata as at 4 April 2001.

DSSSL [DSSSL]DSSSLhttp://​xml​.coverpages​.org​/dsssl96​-ps​.zip (from http://​xml​.coverpages​.org​/dsssl​.html), the stylesheet language for SGML, became an International Standard in 1996. DSSSL defines a tree transformation process followed by a formatting process. In practice, the tree transformation process was not widely used. However, the most widely-used DSSSL formatter had an extension for performing a transformation as an alternative to formatting.

Styling for XML was always part of the development of XML. It was referred to by Jon Bosak, original XML Working Group (WG) Chair, in 1997 as “xml-style (Part 3 of the XML specification suite)” [XS]XS Jon Bosak. XS discussion begins. 22 May 1997. URL: http://​xml​.coverpages​.org​/xs​-970524​.html and “Part 3 of the W3C XML suite of specifications for the use of SGML, HyTime, and DSSSL subsets on the World Wide Web” [XSRTF]XSRTF Jon Bosak. XML Part 3: Style [NOT YET] Version 1.0. URL: http://​sunsite​.unc​.edu​/pub​/sun​-info​/standards​/dsssl​/xs​/xs970522​.rtf​.zip.

‘xml-style’ became ‘Extensible Stylesheet Language’ (XSL). The XSL WG was formed in 1998, and its charter stated its intention “to define a style specification language that covers at least the formatting functionality of both CSS and DSSSL.” [XSLCHARTER]XSLCHARTER World Wide Web Consortium. Charter - XSL Working Group. 22 February 2002. URL: https://​www​.w3​.org​/Style​/2000​/xsl​-charter​.html As stated previously, XSL encompasses both transformation and formatting, but transformation proved generally useful, and the transformation component was broken out as the XSLT series of Recommendations. The bulk of the XSL 1.0 and XSL 1.1 Recommendations concern the formatting objects (FO) and their properties. The XSL properties align as much as possible with the corresponding CSS properties, in keeping with the commitment in the XSL WG charter.

The need for consistency in properties was stated to be an architectural principle for the web in the Consistency of Formatting Property Names, Values, and Semantics TAG Finding [TAG]TAG World Wide Web Consortium. Consistency of Formatting Property Names, Values, and Semantics. TAG Finding. 25 July 2002. URL: https://​www​.w3​.org​/2001​/tag​/doc​/formatting​-properties​.html published in 2002.

3.XSL processing model

XSL’s linear processing model with transformation and formatting stages follows that of DSSSL:

Figure 1.DSSSL block diagram
Figure 2.XSL processing model
Figure 2.

Note how the arrows in both all go one way: there is no feedback from a later stage to an earlier one, and there is no way to modify the formatting mid-format. At least part of the reason is that DSSSL was designed to be able to be used with existing, non-DSSSL formatters. This ability is not explicit in either the original XSL Requirements Summary [XSLReq]XSLReq XSL Requirements Summary, http://​www​.w3​.org​/TR​/1998​/WD​-XSLReq​-19980511​.html or the XSL spec, but the linear processing model did allow XSL processing to be implemented on top of existing formatters. Of the six formatters for which test results [XSLCRTest]XSLCRTest XSL CR Test Suite -- Test Coverage, http://​www​.w3​.org​/Style​/XSL​/TestSuite​/coverage​/testcoverage​.html were provided so the XSL 1.0 specification could progress from Candidate Recommendation to Proposed Recommendation, two—Arbortext and PassiveTeX—used existing formatters to make their pages.

The stages within an XSL formatter also follow a one-way, linear sequence of objectification, refinement, and area generation, as shown in Figure 3: the formatting objects represented as XML elements and attributes in the FO vocabulary are turned into objects and properties; property value expressions are evaluated, and inherited or default values of properties are used as necessary, to produce values to use as traits of the formatting objects; then areas are generated based on the formatting objects and their traits.

Figure 3.Formatting stages
Figure 3.

The formatting objects and their properties very rarely, if ever, completely define the areas generated for more than the simplest of documents. You could write the FOs to completely specify a simple document with a few lines of text—monospace text would be easiest—at known positions, but even generating good line breaks for a few lines of proportional text would soon become more effort than its worth.

Aspects of formatting that the XSL formatter handles for you includes:

  • Applying specified font family, size, style, colour, line height, etc., to text

  • Handling superscripts, subscripts, and changes in alignment baseline

  • Applying specified height, width, margins, borders, and padding to areas

  • Selecting the correct page size and orientation for each page

  • Generating required headers and footers for each page

  • Breaking long lines of text

  • Stacking adjacent lines

  • Breaking long blocks of text across a column- or page-break

    • Keeping the required minimum number of lines before and after the break

  • Aligning text in one or all of the lines in a block

  • Stacking adjacent blocks

  • Formatting tables

    • Automatically optimising column widths to best fit the content if required

    • Applying properties specified on the table column to all cells in that column

    • Breaking a long table at the end of a column or page

    • Repeating table headers and/or footers at a break

  • Hyphenation

    • Correctly (we expect) hyphenating long words when at the ends of lines

    • Using the correct hyphenation rules for the current language

    • Altering the spelling of a hyphenated word if required

    • Not hyphenating:

      • Words specified as not to be hyphenated

      • Too-short words

      • The last word on a page

      • Too many consecutive lines

  • Honouring keeps and breaks

  • Formatting footnotes and sidenotes

  • Sizing and placing graphics

    • Float graphics to the top (or elsewhere) on the page

    • Adjusting lines of text so a side float does not overlap the text

  • Formatting two (or more) independent flows of text on the one page

  • Generating change bars

  • Generating the correct page number for page number cross references

  • Collating and coalescing index keys to generate a sequence of page numbers

  • Generating bookmarks in ouput formats that support bookmarks

Depending on the XSL formatter, it may also be possible to:

  • Add line numbers

  • Generate output files as multiple volumes

  • Generate drop capitals

  • Treat a two-page spread as one large page

  • Add marks at line breaks

  • Force the document to be a multiple of a fixed number of pages

  • Suppress content depending on whether it is or isn't the first content on a page

  • Make lines of text fit to a defined baseline grid

  • Hang punctation characters at the start and/or end of a line into the margin

  • Align text to defined tab stops

  • Modify properties of text that overflows an area to make text fit its area

4.Initial letters

An initial letter is an enlarged letter (or letters) at the start of a chapter or other body of text. Most frequently done as a drop capital, where the top of the enlarged letter aligns with the top of the first line of text and its baseline aligns with the baseline of a lower line of ordinary text, an initial letter can also be formatted to rise completely or partially above the first line of ordinary text. It could also or instead be hung fully or partially in the margin. Drop capitals, in particular, are often accompanied by the first line or the first few words of the text being rendered in small capitals. Small capital letters do not have ascenders or descenders, so they align with the top of the drop cap and, in effect, ease the transition between the drop cap and the (bumpy) ordinary text.

An initial letter with movable type involved a lot of work. To place ordinary text close to a drop capital often required either cutting off parts of the piece of type for the capital (and possibly never being able to use it again) or printing the capital separately. If the initial letter was to be printed in a different colour, as in Figure 5, then it definitely had to be printed separately.

Figure 5.Initial word, printed and type (mirrored)
Figure 5.

When printing is done with fixed-size pieces of lead or wood, then for best results, the size of the initial letter and the font size and leading of the normal text should be coordinated so that the baseline of the initial letter aligns with the baseline of one of the lines of ordinary text. In Figure 5, the leading was chosen so that when the first line is aligned with the baseline of “Type”, the second line aligns with the bottom of “yp”.

When an initial letter has a strong vertical line, such as the “T” in “Type” or the “L” in Figure 6, the initial letter might be moved so the vertical line aligns with the left margin of the text block. When the initial letter is adjacent to multiple lines of normal text, the lines after the first line might have an extra indent so they are not read as starting with the initial letter. When the right side of the initial letter is not particularly vertical, the first line might be outdented so that the first word can be read as one word: consider the difference between reading the start of a sentence as “Alone ...” versus “A lone ...”. As stated previously, to improve the appearance of the start of the text, particularly when the drop capital has a flat top or flat-topped serif, the first line or the first few words in the first line might be formatted in small capitals and the drop capital sized to align with the small capitals.

Fortunately, aligning the drop capial to the text block, indenting or outdenting the lines adjacent to the drop capital, and aligning to the small capital height are easy when done declaratively:

Example 1.XSL-FO for “L” drop capital followed by small capitals text

<fo:block xml:lang="en" keep-together.within-column="always" axf:initial-letters="3" font-family="serif" padding-left="12pt" text-indent="(236 div 2048) * -1dcem" axf:initial-letters-end-indent="-0.25dcem 0" padding="3pt" margin="0"> <fo:inline font-variant="small-caps" letter-spacing="0.05em" >Lorem ipsum</fo:inline> dolor sit amet, consectetur adipisicing elit. Fusce sit amet risus ut sapien vehicula aliquam molestie vitae lacus. In aliquam sem quis suscipit iaculis. In vitae nibh volutpat, blandit mi at, cursus ipsum. </fo:block>

It also becomes easy to declaratively play with those “rules”.

Figure 7.Partially-raised initial “g”
Example 2.XSL-FO for partially-raised initial “g”

<fo:block xml:lang="en" keep-together.within-column="always" padding="0 20%" margin="0" text-align="justify" text-align-last="justify" font-family="serif" font-size="0.5em" axf:initial-letters="12em 2" text-indent="-1.2em" axf:initial-letters-end-indent="-1.8em -0.2em 0 -0.6em" >gazing on that which seems to dim thy sight? What seest thou there? King Henry’s diadem, enchased with all the honours of the world? If so, gaze on, and grovel on thy face, until thy head be circled with the same.</fo:block>

5.Overflowing text

Figure 8 shows four examples of handling text that is contrived to overflow its fixed-size block at the default font size. In the first block, which uses overflow="hidden", the text is clipped at the size of the block, and the overflow is, well, hidden. The second block uses the Antenna House Formatter extension of overflow="condense", which selects to adjust one or more of the font properties to make the text fit. The initial axf:overflow-condense value is font-size, so the formatter reduced the font size (and line height, as here it is proportional to the font size) until all of the text fits in the block.

Continuing the example, the text in the second block is deemed too small, so the third block sets the lower limit of the font size as 9pt and specifies that the line height will be adjusted if the font size reaches its lower limit. Deeming the lines in the third block too close together, the fourth block limits the line height to no more than the font size and specifies to condense the text if the line height reaches its lower limit.

None of us want to think about how many iterations Antenna House Formatter had to do to get the fourth result, but we don't have to think about it because our part was done by declaring a few property values.

Figure 8.Declaratively handling overflowing text
Figure 8.

6.Comparison with CSS

A reviewer of the proposal for this paper requested a comparison with CSS and a discussion of whether CSS for print media is declarative to the same extent.

Firstly, if choosing between XSL-FO and CSS (though it is possible to use both in parallel), you should choose the one that works best for you, according to your own criteria. As you are reading a comparison in a paper from Declarative Amsterdam, the extent to which one or the other is declarative might be important to you. If so, read on. For a detailed comparison of other aspects of XSL-FO and CSS, see XSL-FO/CSS Comparison. [XFCC]XFCC Antenna House. XSL-FO/CSS Comparison, 7 June 2022. URL: https://​www​.antennahouse​.com​/xsl​-fo​-css​-comparison

The stated goal for CSS from [1995]1995 Bert Bos. Report on the W3C style sheet workshop, Paris ’95. URL: https://​www​.w3​.org​/Style​/951106​_Workshop​/report1​.html points to CSS being declarative: “CSS supports stream-based (or ‘incremental’ formatting) where possible”.

I doubt that anyone would argue that CSS syntax is not declarative:

div { font-size: 12pt; other-property: one of myriad micro-syntaxes and keywords; }

On the other hand, CSS styles and stylesheets can be manipulated from JavaScript through the CSS Object Model [CSSOM]CSSOM World Wide Web Consortium. CSS Object Model (CSSOM). W3C Working Draft, 26 August 2021. URL: https://​www​.w3​.org​/TR​/cssom​-1/:

[CSSOM]CSSOM World Wide Web Consortium. CSS Object Model (CSSOM). W3C Working Draft, 26 August 2021. URL: https://​www​.w3​.org​/TR​/cssom​-1/

The core features of the CSSOM are oriented towards providing basic capabilities to author-defined scripts to permit access to and manipulation of style related state information and processes.

Example 3 is the file of a Web Platform Tests test33https://​wpt​.live​/css​/css​-grid​/dynamic​-grid​-with​-auto​-fill​.html for CSS Grid. The test uses JavaScript to modify the height property of the element for the grid. Is CSS for paged media less declarative because this can be done? Is CSS for paged media less declarative if a CSS formatter does this? You decide.

Example 3.dynamic-grid-with-auto-fill.html

<!DOCTYPE html> <link rel="help" href="https://drafts.csswg.org/css-grid-1/"> <link rel="match" href="../reference/ref-filled-green-100px-square-only.html"> <p>Test passes if there is a filled green square.</p> <div xml:id="target" style="height: 150px;"> <div style="display: grid; width: 100px; grid-template-rows: repeat(auto-fill, 100px); min-height: 100%; background: green;"></div> </div> <script> document.body.offsetTop; document.getElementById('target').style.height = '50px'; </script>

Despite the goal for CSS to support stream-based formatting, and no matter your degree of aversion or affection for JavaScript, a paged media document of any complexity is going to require some degree of programmatic manipulation before being formatted and styled using CSS. The alternative would be finicky hand-editing of the source document that would be both burdensome and prone to error.

If you start with the authoring version of a document—that is, a document with just the useful content and without any repeated content or extra information that’s added just to make the paginated document work—then the manipulations needed to make a final document ready for formatting and styling using CSS typically includes:

  • Generating the table of contents based on the structure of the document

  • Generating markup for bookmarks, if the output format supports bookmarks

  • Generating the index by collecting, sorting, and deduping index terms in the text of the document. For CSS, this also requires generating cross-references to every index term, and at present only one CSS formatter can automatically merge repeated or sequential page numbers resulting from consecutive cross references.

  • Duplicating title markup to make “running” elements that the CSS formatter removes from the body of the document and uses when populating headers and footers

If you are used to the XML ecosystem, then you would probably choose to do those things using XSLT. You might even feel good about using declarative XSLT rather than imperative alternatives, even though “declarative” does not appear in the XSLT 1.0 Recommendation [XSLT10]XSLT10 World Wide Web Consortium. XSL Transformations (XSLT). W3C Recommendation. 16 November 1999. URL: https://​www​.w3​.org​/TR​/1999​/REC​-xslt​-19991116 and appears only once in the XSLT 3.0 Recommendation. [XSLT30]XSLT30 World Wide Web Consortium. XSL Transformations (XSLT) Version 3.0. W3C Recommendation. 8 June 2017. URL: https://​www​.w3​.org​/TR​/1999​/REC​-xslt​-30​-20170608

If you are used to the HTML ecosystem, however, you would probably choose to do those things using JavaScript. To the extent that you think about being declarative being a good thing, does the absence from the ecosystem of a standard declarative transformation mechanism make CSS itself any less declarative? You decide.

6.1.Lengths and spaces

Does expanding how lengths are specified, and leaving it to the formatter to resolve conflicting lengths, make XSL-FO more declarative than CSS?

Many CSS property values are fixed lengths, such as 12pt, that can also be written as values such as 2em, 1.2lh, or 20% that resolve to fixed lengths. XSL-FO has many properties with values that are lengths, just as in CSS (unsurprising when many of the properties are the same), but it also has properties that are expressed as a “length-range” with minimum, optimum, and maximum components that each are length values. The XSL-FO “length-conditional” datatype that is used with borders and padding has a single length component plus a conditionality component for overriding how the XSL formatter handles borders and padding at column- and page-breaks. The “space” datatype has minimum, optimum, and maximum components like a length-range, a conditionality component like a length-conditional, and an additional precedence component for specifying the precedence of this space relative to its adjacent spaces.

6.1.1.Length-range

XSL-FO has writing-mode-neutral inline-progression-dimension and block-progression-dimension properties to which the CSS-defined height and width properties map. They and the leader-length property can be specified as a length-range, and the formatter has to generate areas that satisfy the constraints expressed by the length range.

For example, leader-length applies to fo:leader, which is most commonly used to generate the dots between titles and page numbers in a Table of Contents. The initial leader-length value is “leader-length.minimum=0pt, .optimum=12.0pt, .maximum=100%”, meaning that the length of the area generated from <fo:leader/> will be between 0pt and the full width of the current line. Given how fo:leader works, the formatter can be expected to maximise the length, but the formatter can balance the leader-length constraints with any variability in, for example, word-spacing and letter-spacing space values that apply on the line, so it might not necessarily always maximise the leader length. If the fo:leader is specified as <fo:leader leader-length.minimum="20pt"/>, then the leader area length would not be less than 20pt, which might alter word- and letter-spacing elsewhere on the line, or might, if there is not 20pt available length, push the fo:leader to the next line, where it would then be expected to expand to fill the available width (within the bounds of any other constraints on other areas on that line).

6.1.2.Length-conditional

With XSL-FO, if a block area that has borders or padding breaks across a column or page, the XSL formatter will ordinarily treat the borders and padding at the break as being 0pt. That is, the break-after-width and padding-after applied at the after edge of the area before the break and the border-before-width and padding-before applied to the before edge of the area after the break are all treated as 0pt. Without that, the areas on each side of the break would have full borders, which could be confusing if the reader thinks each area is standalone. However, some users want to retain one or both of the border and padding at the break. So, for example, to retain the bottom border where a block breaks across a page or column, they would specify border-after-width.conditionaliy="retain" on the fo:block. Figure 9 shows the effect of .conditionality="retain".

Figure 9.Effect of .conditionality="retain"
Figure 9.

6.1.3.Space

For a block of “lr-tb” text such as this, the margin-top and margin-bottom properties that XSL-FO shares with CSS map to XSL-FO’s space-before and space-after properties. A margin property’s value may be a length or a percentage (or the auto keyword), whereas a space property’s value is the “space” datatype, which, as described previously, has minimum, optimum, maximum, conditionality, and precedence components. The precedence component, which can determine which of multiple adjacent space specifiers sets the distance, is either force or an integer value, with a initial value of 0.

So, unlike choosing between the fixed distances of adjacent margins in CSS, an XSL formatter can resolve not just all of the components of the space specifiers of two adjacent blocks, but possibly also including the space specifiers of the last child block within the first of the adjacent blocks and the first child block within the second of the adjacent blocks, continuing recursively to consider the space specifiers of the last or first child block within those blocks.

Figure 10 shows the difference between applying margin-top/margin-bottom and space-before/space-after to sequences of same-size blocks that are required to be kept on a single page. The margin values are applied as fixed distances, whereas the optimum space values are used when there is room, but the XSL formatter can alter the distance if that resolves other constraints.

Figure 10.Effect of margins and spaces for blocks on one page
Figure 10.

6.2.Page layout

Formatting on paged media obviously requires pages on which to do the formatting. XSL-FO and CSS have different mechanisms for defining the size and other characteristics of a page. Many documents use more than one type of page—for example, left-hand and right-hand pages can have different margins as well as different headers and footers—so XSL-FO and CSS each have mechanisms for choosing between page types.

6.2.1.Page master

A ‘page master’ is the specification of the size and overall layout of a page. A formatted document where all the pages are essentially the same could have just one page master. A formatted document could require multiple page masters if it has, for example: title page; different margins on right and left pages; one-column body text and a multi-column index; landscape pages for wide tables; or wider margins on table of contents pages.

Page masters in XSL-FO are defined using <fo:simple-page-master>. An <fo:simple-page-master> can define up to five types of regions. <fo:region-body> receives the main content of the document, and the other regions receive the content that makes up the headers and footers on each page. Omitted regions do not generate any areas when the <fo:simple-page-master> is used to generate pages. XSL 1.1 added the ability to define multiple <fo:region-body> on one page and to direct one or more flows to one or more regions on the page using <fo:flow-map>.

Figure 11.Page regions in XSL-FO

Page masters in CSS are defined using an @page rule. [CSS3-Page]CSS3-Page World Wide Web Consortium. CSS Paged Media Module Level 3. W3C Working Draft, 18 October 2018. URL: https://​www​.w3​.org​/TR​/css​-page​-3​/#page​-size​-prop

6.2.2.Page region layering

XSL 1.1 specifies that the regions follow a fixed sequence in the XSL-FO. AH Formatter relaxes this and allows the regions to appear in any sequence. AH Formatter renders the areas for the regions in the sequence in which the regions appear in the XSL-FO: z-index does not apply to the regions, so the declaration sequence is a way to control which regions can overlap other regions. If <fo:region-body> is last, it can overlap content from the other regions.

z-index applies to CSS page-margin boxes. It can be used to control the layering of page-margin boxes that have content that overlaps.

6.2.3.Spreads

A ‘spread’ is two facing pages.

Antenna House Formatter has an <axf:spread-page-master> extension for XSL-FO. The background of an <axf:spread-page-master> extends across both pages, and regions defined in the <axf:spread-page-master> can span across more than one page.

Figure 12.Two-page spread formatted using XSL-FO
Figure 12.

CSS does not have any support for specifying spreads. The Antenna House Formatter extension is not available with CSS because CSS does not provide the same control over the sequence of page masters as in XSL-FO.

6.2.4.Page size

Page size in XSL is specified with the page-height and page-width properties, or with the size shorthand for setting both at once. The XSL 1.1 size is based on the CSS 2 size property, but that size was removed in CSS 2.1 and not reinstated in CSS 2.2. Antenna House Formatter implements a superset of the CSS Paged Media Module Level 3 size property, which adds keywords for several standard page sizes.

Page size in CSS is specified using the size property. The extended set of page size keywords is similarly available in CSS.

6.2.5.Selecting page masters

The content in an XSL-FO document is contained in one or more <fo:page-sequence> FO. Each <fo:page-sequence> specifies which <fo:simple-page-master> to use or, indirectly, which set of <fo:simple-page-master> to use. The <fo:page-sequence> can refer to a <fo:page-sequence-master> that sets out which <fo:simple-page-master> to use and in what sequence.

The sequence of page masters can be any sequence of:

  • One page master, possibly with a limit to the number of times it can be repeated

  • A choice between alternative page masters, possibly with a limit to the number of times that the choice can be repeated. The references to the alternative page masters each have three sub-conditions for deciding whether to use that alternative. When a new page is to be generated, the alternatives are considered in sequence, and the first alternative for which all three subconditions is true is chosen.

    The sub-conditions are:

    • The page is either first, last, only (both first and last), neither first nor last, or any page in its page sequence

    • The page number is either odd, even, or any (i.e., whether it is odd or even will not affect the choice)

    • The page is blank, is not blank, or is any (i.e., whether or not it is blank will not affect the choice)

    The sub-conditions are expressed as properties. The any value is the default for each property, so when choosing an alternative, you only need to specify the sub-conditions that affect the choice.

For example, a document with different margins on odd and even pages could have two <fo:simple-page-master> named OddPage and EvenPage. The <fo:page-sequence-master> for choosing the correct page master could be:

<fo:page-sequence-master master-name="PageMaster"> <fo:repeatable-page-master-alternatives> <fo:conditional-page-master-reference master-reference="OddPage" odd-or-even="odd"/> <fo:conditional-page-master-reference master-reference="EvenPage" odd-or-even="even"/> </fo:repeatable-page-master-alternatives> </fo:page-sequence-master>

(Note that odd-or-even="even" is not strictly necessary here because any page that did not match the previous odd-or-even="odd" is, by definition, an even page.)

CSS does not have a separate mechanism for predetermining the sequence of page masters. (Even in XSL-FO, it is only partially predetermined, because which page is the last page, or which pages are blank, is not known until the document is formatted.)

In CSS, any element that can cause a page break before or after itself can cause a change in @page rule by specifying the page property with a value that is different from the value of its parent or preceding sibling element. As an extension to the current CSS definition, Antenna House Formatter allows a sequence of multiple names in the page property, and Antenna House Formatter uses each specified @page rule in turn when generating pages. If the element generates more pages than there are names in the page property value, Antenna House Formatter continues to use the last name for the remainder of the pages.

6.2.6.Nested page sequences

XSL 1.1 does not allow page sequences to be nested: all of the page masters to use and conditions for their sequence of use are determined by the <fo:page-sequence-master> referred to by an <fo:page-sequence>.

Antenna House Formatter allows <fo:flow> to contain children <fo:page-sequence> FOs. Formatting a nested <fo:page-sequence> is equivalent to ending the containing <fo:page-sequence>, formatting the <fo:page-sequence> as if it were another top-level <fo:page-sequence>, and then formatting the FOs after the <fo:page-sequence> as if in a copy of the original containing <fo:page-sequence>.

CSS does not differentiate between top-level elements and lower-level elements that can change the @page that is being used. As stated previously, any element that can cause a page break before or after itself can cause a change in the @page rule that is being used.

6.2.7.Multiple body regions on one page

XSL 1.1 added the ability to define multiple <fo:region-body> in an <fo:simple-page-master> and also multiple <fo:flow> within an <fo:page-sequence>. One or more <fo:flow> can be directed to one or more <fo:region-body> on a page. Which <fo:flow> are directed to which <fo:region-body>, as well as the sequence in which multiple <fo:flow> are directed to one (or more) <fo:region-body>, is controlled by an <fo:flow-map>, which was also added in XSL 1.1.

CSS does not have a comparable feature at present.

6.3.Headers & footers

6.3.1.XSL-FO

The areas generated from the four ‘outer’ regions—<fo:region-before>, <fo:region-after>, <fo:region-start>, and <fo:region-end>—receive content from <fo:static-content>. Each region has a default name based on its type, but different region-name values can be specified. An <fo:static-content> directs its content to a region with the same region-name value as the flow-name property value of the <fo:static-content>. This makes it possible to set up the static content for multiple sorts of pages and use only the applicable static content on each page. It also makes it possible to, for example, direct the contents of one <fo:static-content> to the outer, left-hand region on left-hand pages and to the outer, right-hand region on right-hand pages.

Figure 13.Static content directed to page regions in XSL-FO

Variable content that should appear in the header or footer, such as the current chapter title, is declared in an <fo:marker>. Any number of <fo:marker> can be included as the first children of <fo:flow> and of many of it descendent FOs. An <fo:marker> can contain any combination of text, inline-level FOs, and block-level FOs, but it does not generate any areas. Instead, its children are available to be retrieved and formatted by an <fo:retrieve-marker> that is within an <fo:static-content>.

As stated previously, the applicable <fo:static-content> provides the complete content for one of the outer regions on the page, and it is only those applicable <fo:static-content> that actually retrieve and format the children of an <fo:marker>. An <fo:static-content> contains one or more block-level FOs. One or more <fo:retrieve-marker> can be present as the only content of the <fo:static-content> or as siblings of block-level FOs. The block-level FO or FOs can contain any number of <fo:retrieve-marker> in any part of their content.

The correspondence between the marker-class-name property on the <fo:marker> and the retrieve-class-name property on the <fo:retrieve-marker> connects an <fo:marker> and an <fo:retrieve-marker>. Any name token can be used; for example, chapter-title and xyzzy are equally allowed. Sibling <fo:marker> cannot use the same marker-class-name value.

If the <fo:marker> for the chapter title is:

<fo:marker marker-class-name="chapter-title">A<fo:inline font-variant="swash(1)">&amp;</fo:inline>B</fo:marker>

then the <fo:static-content> of the header can be:

<fo:static-content flow-name="xsl-region-before"> <fo:block font-size="10pt" text-align="center"> <fo:retrieve-marker retrieve-class-name="chapter-title" /> </fo:block> </fo:static-content>

The content of the <fo:marker> is formatted in place of the empty <fo:retrieve-marker>. The content is formatted as if it had the same ancestors as the <fo:retrieve-marker>; that is, the content inherits property values from the context of the <fo:retrieve-marker> within its <fo:static-content> and not from the context of the <fo:marker> in its parent FO.

There can be multiple <fo:marker> with the same marker-class-name in the FO document (just not with the same parent FO). Multiple FOs that have <fo:marker> children with the same marker-class-name could generate areas on the same page. To continue with the example, unless each chapter starts on a new page, there could be both the end of one chapter and the start of the next chapter on the one page, and some books have complete chapters that are less than one page, so there could be part or all of three (or more) chapters on one page.

An XSL formatter considers <fo:marker> related to areas on the current page and, possibly, on preceding pages. It chooses a preferred <fo:marker> based on the retrieve-position and retrieve-boundary property values of an <fo:retrieve-marker>:

  • retrieve-position specifies a preference based on the areas generated by the parent FO of an <fo:marker> compared to the areas generated by other identically named <fo:marker>. retrieve-position can have values first-starting-within-page, first-including-carryover, last-starting-within-page, and last-ending-within-page.

  • retrieve-boundary specifies how far back to find a carried-over <fo:marker>. It can have values page, page-sequence, and document. retrieve-boundary can be useful to stop a marker ‘leaking’ into pages where it is not wanted: for example, an epilogue that does not need a running title in the header could use the same page masters as chapters if:

    • The epilogue is in a separate <fo:page-sequence>

    • The epilogue does not contain an <fo:marker> for the running title in the header

    • The <fo:retrieve-marker> for the running title in the header omits retrieve-boundary to use its initial value of page-sequence.

Page numbers in the header are generated using <fo:page-number>. The total number of pages shown in page numbers such as “Page 1 of 5” can be generated by using <fo:page-number-citation-last> with a ref-id value that refers to the ID of the <fo:page-sequence> or other FO that encompasses all of the FOs that generate those pages.

6.3.2.CSS

In CSS, every page has 16 page-margin boxes that are always available to receive content. The names of the page-margin boxes are fixed.

Figure 14.Location of each page-margin box

A page-margin box is generated only if it has content; that is, if the value of its content property is not none. none is the computed default, so page-margin boxes ordinarily do not generate any boxes. This differs from XSL-FO, where the areas from <fo:region-before>, etc. can generate an area that has, for example, borders and a background color even when it has no content generated from an <fo:static-content> (provided that the extent property of the region is not 0pt).

Content can be either:

  • Inline-level content that is any combination of literal strings, values of named strings, counter values, leaders, cross-reference text, or images

  • A running element that has been removed from the flow using position: running(…); and is retrieved using content: element(…);.

The string-set property sets one or more named strings. For example, to set the chapter named string to the text value of the current element:

H1 { string-set: chapter content(); }

The string name is followed by a list of string values that are concatenated to form the value of the named string. Setting a named string overwrites any previous value set earlier in the document. An element with display: none; can still set named strings.

string(chapter) in the content property of a page-margin box retrieves the value of the string named chapter. Multiple elements on one page can set the same named string, so string() has an optional second argument to select one of the possible strings. The defined values are first (default), start, last, and first-except.

Using position: running(name); is similar to using string-set, except that position: running; takes the current element out of the flow—similar to specifying display: none;. content: element(name); inserts the named running element into the page-margin box. element() also has an optional second argument to select one of the possible running elements with the same name: first (default), start, last, and first-except.

A running element inherits from its original position in the document. This is the opposite of <fo:marker>, where the marker contents inherit from the position of the <fo:retrieve-marker> in its <fo:static-content>.

counter(page) in the content value of a page-margin box generates the current page number.

In principle, CSS and XSL-FO can generate identical headers and footers. In practice, however, it’s not the case that CSS ‘just works’: you need a way to duplicate content to make the running elements to be used in headers and footers. While most of us would just reach for XSLT, CSS does not come with a standard mechanism for transforming the source document before formatting. Without that, the headers and footers will potentially be much plainer, as only named strings would be available, and they cannot represent font changes, subscripts, superscripts, MathML, etc.

Building up a complex header or footer can be easier with XSL-FO than with CSS. <fo:marker> can generate anything from a single character to multiple FOs as the content to replace an <fo:retrieve-marker> in its <fo:static-content> that becomes the content of a page region. Running elements, instead, always generate the complete contents of a page-margin box. As such, the XSL-FO for a complex header or footer containing multiple information items, such as the S1000D page header and page footer shown in Figure 15, can be built up by putting most of its markup in the <fo:static-content> and using <fo:retrieve-marker> to fill in the variable text. CSS, in contrast, would require a complete running element for every permutation of the header or footer as its information (other than the page number) changes through the course of the document. In practice, you are unlikely to read the transformed document containing all of those running elements—just as you are unlikely to read the XSL-FO resulting from an XSLT transformation of your source—but generating complete running elements each time is likely to be more programming work than generating <fo:marker> when required.

Figure 15.S1000D headers and footers
Figure 15.

6.4.Indexes

The key features of a ‘back-of-the-book’ index are:

  • Some books have multiple indexes

  • An index is a sorted, and usually also grouped, set of headwords

  • In paged media, headwords have an associated list of page numbers of the pages that contain content relevant to the headword

  • The list of page numbers can include either or both of: sequences of consecutive page numbers for multiple separate instances of content that is relevant to the headword; and page ranges for relevant content that spans several pages

  • Some page numbers can have different styles from others: for example, bold numbers to indicate the most important content, or italic numbers to indicate pages with figures or examples

  • When starting from XML or HTML, the index terms are usually included in the markup for the body of the document, and these are sorted and collated to generate the markup for the index. Alternatively, the index could be maintained as an external document with cross-references to IDs in the document being indexed.

  • Sorting and collating the index terms to produce a list with no duplicates can be done without formatting the document, but correct page number references to the original locations of the index terms can only be made once the document is formatted

  • When an index term appears twice on the one page, the formatter must be able to merge duplicate page numbers

  • In some cases, when the same index term appears on successive pages, the formatter must merge successive page numbers into a page range

  • Some typographic traditions have shorthand notations for an index term appearing on two or three consecutive pages: for example, ‘8f.’ for an index term on pages 8 and 9, or ‘8ff.’ for an index term on pages 8, 9, and 10.

6.4.1.XSL-FO

XSL 1.1 expects that an XSLT transformation will convert index terms in the source XML into index-key properties in the XSL-FO and also sort and collate the index terms to generate the FOs used for formatting the index (or indexes). The bulk of the FOs for a formatted index will be the general-purpose <fo:block>, and so on, for the list of headwords. The page references for each headword are generated from an <fo:index-page-citation-list>. These FOs are also expected to be generated by the XSLT transformation based on the index terms in the source XML. In fact, it is an error if a contained <fo:index-key-reference> does not match any index term in the document.

XSL 1.1 defines multiple FOs for the index reference and, optionally, any prefix or suffix to add to each page number reference. For example, the XSL 1.1 Recommendation shows the example of ‘[’ and ‘]’ around index references to figures. The page numbers, and any prefix or suffix, can be styled using inherited properties such as font-style and font-weight.

There are also properties that specify when and how to merge sequential page numbers and whether to merge page numbers and page ranges across different <fo:index-key-reference>. This controls, for example, whether index references to figures can be merged with ‘ordinary’ index references to the same or adjacent pages. Antenna House Formatter extends the merge-sequential-page-numbers property to allow shorthand notations to be used for two and three successive pages.

6.4.2.CSS

All of the page number references in the index must be included in the document being formatted. Unlike with XSL-FO, there is no facility for generating page number references based on an index key.

Antenna House Formatter provides the -ah-merge-sequential-page-numbers extension property for merging sequential page numbers. When specified on a block-level element, adjacent identical page numbers are merged into one number, and sequences of sequential page numbers are merged into ranges. The page references must be to pages earlier in the document. Generating the shorthand notations for two or three consecutive pages is also possible with CSS.

Suppose the following CSS is specified.

p.index-page-citation-list { -ah-merge-sequential-page-numbers: merge; } span.index-item { content: target-counter(attr(href), page); }

Then, suppose the following HTML exists.

<p class="index-page-citation-list"> <span class="index-item" href="#id1"/>, <span class="index-item" href="#id2"/>, <span class="index-item" href="#id3"/>, <span class="index-item" href="#id4"/>, <span class="index-item" href="#id5"/>, <span class="index-item" href="#id6"/>, <span class="index-item" href="#id7"/>, <span class="index-item" href="#id8"/>, <span class="index-item" href="#id9"/> </p>

Suppose the page numbers line up as follows:

1, 3, 4, 4, 5, 6, 8, 8, 9

With the Antenna House Formatter extension, the result will be:

1, 3–6, 8, 9

If -ah-merge-sequential-page-numbers: merge-f; is specified instead, the result will be:

1, 3–6, 8f.

7.Conclusion

An XSL formatter is a black box, by design. Everything about the design is declared up-front, as there is no feedback to a program or script running the formatter, and there are no if statements in the XSL-FO expression language. The FOs and properties in the input to the formatter lay out the parameters for the formatted document, but it is the formatter that lays out the pages. The formatter decides everything from when to break lines to which page design to use based on the input.

Whether CSS for paged media is as declarative as XSL-FO is neither the first nor the most important question that you should ask if you are trying to see if CSS will work for you. CSS is comparably declarative, to the extent that you can overlook the possibility that a companion JavaScript engine can modify the CSS properties. CSS and XSL-FO share a lot of common properties, but they do have different ways of doing things such as selecting which page design to use, and there are some aspects, such as collating index terms or making two-page spreads, that cannot be done with CSS.

Notes

1.“XSL-FO” is not defined in the XSL 1.1 Recommendation. There are multiple instances of “FO” or “FOs”, as well as “XSL FO” to refer to an XSL-defined formatting object. The only “XSL-FO” are “XSL-FO result intstance” and “XSL-FO instance” in the explanation of a table markers example. Table markers were added in XSL 1.1, published five years after XSL 1.0. Five extra years was plenty of time for the term to become perhaps more established than had been intended initially.
2.Not just to get a bit of a rhyme.

Bibliography

1995
Bert Bos. Report on the W3C style sheet workshop, Paris ’95. URL: https://​www​.w3​.org​/Style​/951106​_Workshop​/report1​.html
CSS1
World Wide Web Consortium. Cascading Style Sheets, level 1. W3C Recommendation 17 December 1996. URL: https://​www​.w3​.org​/TR​/REC​-CSS1/
CSSOM
World Wide Web Consortium. CSS Object Model (CSSOM). W3C Working Draft, 26 August 2021. URL: https://​www​.w3​.org​/TR​/cssom​-1/
CSS3-Page
World Wide Web Consortium. CSS Paged Media Module Level 3. W3C Working Draft, 18 October 2018. URL: https://​www​.w3​.org​/TR​/css​-page​-3​/#page​-size​-prop
LIE
Håkon Wium Lie. Cascading Style Sheets. Ph.D Thesis. 2005. URL: https://​wiumlie​.no​/2006​/phd/
TAG
World Wide Web Consortium. Consistency of Formatting Property Names, Values, and Semantics. TAG Finding. 25 July 2002. URL: https://​www​.w3​.org​/2001​/tag​/doc​/formatting​-properties​.html
XFCC
Antenna House. XSL-FO/CSS Comparison, 7 June 2022. URL: https://​www​.antennahouse​.com​/xsl​-fo​-css​-comparison
XS
Jon Bosak. XS discussion begins. 22 May 1997. URL: http://​xml​.coverpages​.org​/xs​-970524​.html
XSL10
World Wide Web Consortium. Extensible Stylesheet Language (XSL). W3C Recommendation. URL: https://​www​.w3​.org​/TR​/2001​/REC​-xsl​-20011015/
XSL11
Anders Berglund, Editor: Extensible Stylesheet Language (XSL) Version 1.1. Recommendation, World Wide Web Consortium, 5 December 2006. https://​www​.w3​.org​/TR​/xsl11/
XSLCHARTER
World Wide Web Consortium. Charter - XSL Working Group. 22 February 2002. URL: https://​www​.w3​.org​/Style​/2000​/xsl​-charter​.html
XSLT10
World Wide Web Consortium. XSL Transformations (XSLT). W3C Recommendation. 16 November 1999. URL: https://​www​.w3​.org​/TR​/1999​/REC​-xslt​-19991116
XSLT30
World Wide Web Consortium. XSL Transformations (XSLT) Version 3.0. W3C Recommendation. 8 June 2017. URL: https://​www​.w3​.org​/TR​/1999​/REC​-xslt​-30​-20170608