Next / Previous / Contents / Shipman's homepage

4. Structure of the generated XSL-FO

The full XSL specification is truly a monster. The author once read the entire standard and handwrote 57 pages of notes on green engineering pads, five lines per inch. Fortunately, Pawson's book (see Section 2, “XSL-FO resources”) greatly reduces the time it takes to get started producing quality documents.

As with most large packages, you probably won't need 80% of what is there. Much of the complexity comes in when you are supporting multiple languages in the same document. The general problem XSL-FO solves is quite large. Consider the problems of formatting a page that includes English (where lines are written left to right, and the lines progress from top to bottom) and Japanese (each line is written top to bottom and lines progress right to left). This leads to the important concepts of Inline Progression Direction (IPD), which is “lr” (left-to-right) for English and “tb” (top-to-bottom) for Japanese; and Block Progression Direction (BPD), “tb” for English and “rl” for Japanese. In the balance of this document we will assume English (lr-tb) writing order.

Keep in mind also that the purpose of XSL-FO is to format documents within specific page sizes, which is basic to the PDF format. Web rendering is a completely different problem: for example, a Web page can use any width, although most people find horizontal scrolling quite annoying, and there are lots of pretty tiny screens out there.

The author's preferred tool for understanding the structure of XML document types like XSL-FO is the Relax NG schema language, especially the RNC form (Relax NG Compact Format). See the NM Tech Computer Center's publication, Relax NG Compact Syntax (RNC). RenderX, maker of XEP, our locally preferred XSL-FO-to-PDF translator, has kindly made available a complete RNC schema for XSL-FO.

When you are generating an XSL-FO file, the first problem is understanding the overall structure of the file. Here, in RNC format, is a highly reduced schema for the top-level elements of the document. This schema shows only a tiny part of the elements and attributes in the full schema; it is intended strictly to familiarize you with the main pieces of the document.

The root element of the tree is named root. It has two kinds of children. The layout-master-set child defines all the different ways that pages can be formatted. This is followed by one or more page-sequence elements describing the actual content that must be formatted into those page layouts.

start = root
root = element root { layout-master-set, page-sequence+ }

The layout-master-set element must have at least one simple-page-master child; each one describes a specific page layout.

In most cases, you will also provide a page-sequence-master element that specifies what kind of content is routed to each simple-page-master's layout.

layout-master-set = element layout-master-set
  { simple-page-master+ & page-sequence-master? }

Simple page masters divide the page up into five regions:

The terminology here is relative to XSL-FO's generic directions: “before” and “after” refer to the BPD, and “start” and “end” are relative to the IPD.

Typically the “before” region is used for running headers; “after”, for running footers; and the content appears in the “body” region. Unless you are using the “start” and “end” regions for marginal notes, typically they will have zero width, and the body will have the same width as the headers and footers.

Each simple-page-master has a unique name that is defined by its master-name attribute. We won't describe the five child elements here, but basically the region-body child describes the size of the text area and the margins around it; the region-before and region-after children describe the heights of the header and footer; and the region-start and region-end elements describe the widths of the marginal note columns.

Each of these five child elements has a region-name attribute that defines its name. The static-content element (described below) will refer to this name when it provides the content for a region (other than the body region).

simple-page-master = element simple-page-master
  { attribute master-name{text},
    region-body, region-before?, region-after?, region-start?,
    region-end? }

The page-sequence-master element is a container for the rules that dictate how text is assigned to the different simple page masters.

page-sequence-master = element page-sequence-master
  { (single-page-master-reference |
     repeatable-page-master-reference |
     repeatable-page-master-alternatives)+ }

In the first two of the above cases, the element has a master-reference attribute that points to the simple page master that has that master-name.

single-page-master-reference = element single-page-master-reference
  { attribute master-reference{text}, empty }
repeatable-page-master-reference =
  element repeatable-page-master-reference
  { attribute master-reference{text}, empty }

In the general case, where you have multiple simple page masters, use a repeatable-page-master-alternatives element. This element in turn is a container for conditional-page-master-reference elements, each of which describes one situation and names the simple page master to be used in that situation.

repeatable-page-master-alternatives =
  element repeatable-page-master-alternatives
  { conditional-page-master-reference+ }
conditional-page-master-reference =
  element conditional-page-master-reference
  { attribute master-reference{text},
    attribute page-position { "first" | "last" | "rest" | "any" }?,
    attribute odd-or-even { "odd" | "even" | "any" }?,
    attribute blank-or-not-blank { "blank" | "not-blank" | "any" }?,
    empty }

Within a conditional-page-master-reference:

Here's an example that uses three simple masters: first for the first page, odd or even for the remaining pages.

      <simple-page-master master-name="first">...</simple-page-master>
      <simple-page-master master-name="odd">...</simple-page-master>
      <simple-page-master master-name="even">...</simple-page-master>
      <page-sequence-master master-name="N">
          <conditional-page-master-reference master-reference="first"
              page-position="first" />
          <conditional-page-master-reference master-reference="odd"
              page-position="rest" odd-or-even="odd" />
          <conditional-page-master-reference master-reference="even"
              page-position="rest" odd-or-even="even" />

Returning to our schema, that completes the layout-master-set part of the document. The rest is in the other child of the root element, the page-sequence element.

page-sequence = element page-sequence
  { static-content*, flow }

Use a static-content element to specify the content of the non-body regions of the page such as headers and footers.

static-content = element static-content
  { attribute flow-name{text},
    block+ }

In the static-content element, use the flow-name attribute to specify where this static content goes. The value of this attribute is the region name you specified with the region-name attribute in the element that defined the format of that region. Typically you will supply one block element that defines the content to be displayed in that region. This content can include the page-number element, which will display the page number.

For example, suppose you have a running head, and in the simple-page-master for odd-numbered pages, your region-before element had an attribute region-name="odd-head". To specify the content of the header, use a static-content element with a flow-name="odd-head" attribute.

The last element we'll discuss in this foreshortened grammar is the flow element.

flow = element flow {block+}

This element is the container for all the principal content of the document (excluding static content).

The content “block+” shown above is not technically correct. You may specify page body content using any number of block-level elements, including not only the actual block element but other elements such as tables, lists, or graphics.

This concludes our tour of the top-level structure of an XSL-FO file. The structure of the code you write that generates the file follows exactly the tree structure of the schema:

  1. First you will instantiate class sox.Sox, the object that will manage the generation of the XML output stream. The output file name must end with “.fo”. In the rest of this procedure, we'll call the Sox instance s. (For more information on the sox module, see Section 2, “XSL-FO resources”.)

  2. You'll call s.start('root') to generate the root element. This method returns a token that you will need later to generate the closing tag at the end of the output file.

  3. Generate the layout-master-set element and its substructure to describe your page layouts.

  4. Generate the page-sequence element and its children, the various static-content children that define header and footer content, and then the flow element that has the actual body of the document.

  5. Call the .end() method on the token you got back from the s.start('root') call to generate the final start tag, and close the output file with s.close().

  6. Do whatever you have to do on your local system to convert the .fo file to a .pdf.