![]() | CONTENTS | ![]() |
Blocks represent smaller parts of a document, familiar as features such as paragraphs, lists, and tables. Using these pieces, you can structure your documents and present them within the page contexts you've established.
Think of the last document you styled. Each major space-separated block of contiguous text, graphic, table, or list is most likely to be a block when styled with XSL-FO. fo:block could be called the basic building block of page content. Simply inserting content into an fo:block element produces a simple paragraph style with all the default properties. Blocks are most commonly used within the page layout you have specified, specifically within the fo:flow element.
To appreciate the flexibility of blocks, it's necessary first to select the right type of block, then to select from its list of available properties.
The top-level blocks include:
fo:block
fo:block-container
fo:list
fo:table
These are the major divisions, each producing an area within the block-progression-direction, visually separated by a new line. I'll cover each of these in turn.
The content model for a block consists of other blocks, inlines, or textual content. The simple block, acting as a paragraph, is likely to be your most used element in the fo namespace, for normal text-heavy documents. Note that the same fo:block may be used for any content that requires whitespace separation in the block-progression-direction. This ranges from the title of a document on a page by itself to list item contents. The block is a versatile element.
The stylesheet snippet in Example 5-1 picks out the para elements in an XML source document, styles them as blocks with no start indent, has fairly typical spacing between its predecessor and successor, uses the Times font, and has simple content. The border around the block is simply to outline its area, as I'll be referring to this again.
<xsl:template match="para"> <fo:block border-style="solid" border-width=".1mm" font-family="Times" font-size="12pt" space-before="12pt" space-after="12pt" text-align="justify"> <xsl:apply-templates/> </fo:block> </xsl:template>
This delivers a fairly standard paragraph, as shown in Figure 5-1.
The basic syntax is simple, but choosing the correct suite of attributes for any particular context is not quite so simple. I've selected the font-family, font-size, and text-align (basic alignment) properties, a minimal set of properties for consistent display. I've set the space-before and space-after properties, rather than using the default (which places almost no space between succeeding paragraphs). Without the borders, spacing the same size as the font (or slightly bigger ) is a reasonable choice.
Now back to that border. It should show up as black, but I'm cheating here, relying on an inherited attribute to make the color. To ensure that it is, I normally add color='black' to the fo:flow element containing the region-body. The example specifies a border of 0.1mm width. I could have followed older HTML principles and specified 1px (1 pixel), but there is a downside to this. The specification, in Section 5.19, warns about the pixel as a unit of measure: stylesheet authors should understand a pixel's actual size may vary between devices. Stylesheets using pixels may not produce consistent results across different implementations or different output devices from a single implementation.
The text alignment specified by the text-align attribute can be start, center, end, justify, inside, outside, left, right, or inherit. The more common choices are start (the lefthand side in a left-to-right layout), which provides what is sometimes called "ragged right" layout; center, which centers text; end, which provides right justification; and justify, which provides both left and right justification, creating the simplest of paragraphs, without any special features. inside and outside are peculiar to bound documents. When documents are bound, particularly with stitched bindings, the text can be aligned relative to the binding. For example, where the binding is toward the start side, the text is aligned to that side.
Indentation is provided by the two properties: start-indent and end-indent. Each takes a length specification as its value and has an initial value of 0pt, or no indentation. This is not the same as text-indent, which indents only the first line of text within the block. A negative value of text-indent will produce the hanging indent. With start-indent, indentation is applied to the whole block. Possible uses are to indent a quotation, perhaps similar to Example 5-2.
<xsl:template match="quote"> <fo:block start-indent="6em" end-indent="6em" font-family="Times" font-size="12pt" space-before="12pt" space-after="12pt" text-align="start"> "<xsl:apply-templates/>" </fo:block> </xsl:template>
This creates an indented block (indented at the start and end directions), offset by 6em from its nearest ancestor. I've added the quote characters around content as decoration. Perhaps if the quotation is attributed to a specific author, it could be offset to the right, in its own block, using text-align="end".
An alternate use for this form of indentation is to create a wrapper block element, with the indentation set to whatever offset is needed from the edge of the printed area of the body, then to nest all other blocks within it, which reduces the need to specify it for all the children of this block.
First line indentation, a common style in some texts, is achieved by using the text-indent attribute. To keep it proportional to the font size in use, the em length specification is again useful. Example 5-3 and its result, Figure 5-2, illustrate this.
<xsl:template match="para"> <fo:block space-before="12pt" space-after="12pt" text-indent="3em" text-align="justify"> <xsl:apply-templates/> </fo:block> </xsl:template>
A variation on this is the hanging indent, using a combination of the text-indent and start-indent properties as in Example 5-4.
<fo:block text-indent='-4em' start-indent='4em'> An example of a hanging indent, a paragraph with the first line left aligned, and the remainder of the paragraph indented by a fixed amount. Set by using the start indent and text-indent properties on a block. The text-indent is set to a negative value, the start indent to a positive value. </fo:block>
Similarly, the last line of a block may be given special treatment using the last-line-end-indent attribute. This assures a fixed end-space on the last line. The formatter may fit in more text than normal or may break one line to form another. Either way, your last line is guaranteed minimum whitespace as specified by this property. Due care is needed, because a positive value will indent the end-edge, and a negative value will extend it beyond the normal finish, creating an outdent.
Another line property is text-align-last, whose value may be relative, start, center, end, justify, inside, outside, left, or right. These values affect the layout in the line-progression-direction. Justified text with this property may appear strange if the content of the last line is insufficient to reasonably fill the line. The other values are self-explanatory; note that inside and outside values relate to the page binding edge. I prefer the relative value, with the majority of a paragraph set with the text-align property having the value justify, which provides start justification or a last line. Figure 5-3 shows these two options. The first option has text-align set to justify, the second has text-align-last set to start.
A simple use of fo:block just creates a break. This is certainly not a recommended practice, because using fo:block to insert a break risks destroying the paragraph's indents, widow/orphan handling, or line-height calculations. Other alternatives would be (as in the next example) to make each line a block using nl for a new line and adjust the block separation accordingly, perhaps within a wrapper block. The problem here is that we use visual properties in the source XML; <nl/>, for a newline, is hardly semantic markup.
Reusing this quote template with additional processing for the nl element produces the layout for the poem, as shown in Example 5-5 and the output, in Figure 5-4.
<para>This paragraph includes a quotation <quote>The fair breeze blew, the white foam flew <nl/> The furrow followed free; <nl/> We were the first that ever burst<nl/> Into that silent sea. </quote> <author>Samuel Taylor Coleridge</author> And continues after it. </para>
The associated template for the end of line element is shown in Example 5-6.
<xsl:template match="nl"> <fo:block /> </xsl:template>
The nl element is used to indicate that a line break is needed. Because this is visual rather than semantic, check your output when you use this. The transformation used here is simply an empty block element, whose initial values provided the needed styling. The template for the author element uses a large start-indent value (12em) to provide the indentation for the author, because the lines themselves are short. For longer lines it may be appropriate to use the text-align attribute value of end to ensure that the block is right aligned.
Although in this release of the specification there is no special treatment for an <fo:character character='&#xOA'/> (it is treated the same as any other whitespace in content), there could be a case for using some special character (perhaps U+2028, Line Separator) for this purpose. However, until it becomes a part of the specification, I can't recommend it. The processing is shown in Example 5-7.
<xsl:template match="nl"> <fo:character character = '
' /> </xsl:template>
We often want to space paragraphs or other blocks of text to create the best visual effect. This is achieved by using the space-before and space-after properties of a block, each of which takes a length specification. If two succeeding paragraphs have space specified before and after, the formatter needs to combine the two specified values to obtain a final resolved value. The properties that interact here are the space-before (or space-after) precendence and conditionality. The specification is quite clear on the resolution: usually when there are two values with similar precedence, you will see the larger one. A higher precedence (or special value force) may be used to override this, if needed. To increase a value's precedence, use the value space-after.precedence="n", where n is some number larger than the default value of zero; the more you need it to increase the precedence, the higher the number. If you use a value of force, you completely change the precedence-mediated merging semantic to an additive one. Note that padding does not interact with this space resolution.
The first precedence rule may throw you: it states that if a space specifier is the first (outer) one and is conditional, it is discarded. So when you try to create a title page that is blank except for the title block, the space above it is eaten, which is quite frustrating. To invalidate this rule, use the retain value. Example 5-8 shows one way to ensure a 2-inch space prior to a block.
<fo:block space-before="2in" space-before.conditionality = 'retain'> ...
The first of the three paragraphs has no specification of space-after; the second paragraph has a 12pt space-before specification and a space-after specification, with no precedence specified; and the third paragraph specifies a space-before with a stated precedence of 5. You can see the result in Example 5-9 and Figure 5-5.
<fo:block space-after="12pt" border-style="solid" border-width=".1mm" > This is the first of three blocks .</fo:block> <fo:block border-style="solid" border-width=".1mm" space-after="12pt"> This block has the space-after set to 12pt, so there will be an interaction.</fo:block> <fo:block border-style="solid" border-width=".1mm" space-before="18pt" space-before.precedence="5"> This block really must have the larger (18pt) space before .</fo:block>
Authors often need to ensure some form of break in the flow of a document. Within blocks, two properties are used for this: break-before and break-after. Possible values are auto, column, page, even-page, and odd-page. auto is the default value, allowing the normal flow of content.
Typical use would be to ensure that a chapter starts on a new page or to start a new column in a multicolumn layout. The specification of recto or verso pages is done by stating that the break should be to a new odd or even page.
If each chapter has its own block, specifying:
<fo:block break-before="odd-page">
will, if necessary, add the blank page to ensure that the chapter starts on the new, odd-numbered page.
To decorate a block of text, rather than operate at the lower level, it's often easier to wrap a series of blocks in another block to apply formatting to the outer wrapper block. This enables the styling of contained blocks within this frame. A highlighted section of a page is a case in point. If a border is applied to the wrapper block, normal blocks can be offset from this wrapper and will be presented as normal blocks within this box.
Example 5-10 shows the containing block with a border and two internal blocks, each with an indent at the start and end directions to offset them from this border. Space is allocated before and after to ensure the separation of each contained block.
<fo:block border-style="solid" border-width="2mm"> <fo:block start-indent="4em" space-before="2em" space-after="1em" end-indent="4em">First contained block </fo:block> <fo:block start-indent="4em" space-after="2em" end-indent="4em">Second contained block. The significant border together with the indents offsets the content, seperating it visually from the main flow. </fo:block> </fo:block>
Example 5-10 uses two of the indent properties to offset the block content from its surroundings. The indent property can be applied to both the start and end directions, as shown in the example. Here, the indent property it is used to offset the content by 4em. Figure 5-6 illustrates this.
XSL-FO also permits the use of an absolute position, fixed relative to the containing area. Note that this property is a part of complete compliance and, hence, is likely to reduce portability. In the case of paged media, the area is fixed with respect to the page. For this, there is the absolute-position property. With this property set to fixed, the top property specifies how far the content's top margin edge is offset below the top of the page. To produce content at a fixed location, such as the signature line on a letter, we might see stylesheet content like that in Example 5-11.
<xsl:template match="signature"> <fo:block-container absolute-position="fixed" top="240mm" left="100mm"> <fo:block xsl:use-attribute-sets="para"> Yours Sincerely: <xsl:apply-templates/> </fo:block> </fo:block-container> </xsl:template>
Note the use of fo:block-container here. One of the stated purposes of this formatting object is to change the writing mode or orientation. I've used it here to use the absolute-position property, to position the signature line 240mm from the top and 100mm from the left edge of the body. This achieves a regular positioning of content to ensure that (in this case) the signature line is always seen in the same place for any letter. A dot leader might be provided if the document was meant for a hand written signature.
The use of blocks for some purposes may not be readily obvious. A simple heading in a larger font may not be seen as a variant of a block, but it is. It may be used as the main heading of a document.
A title page is simply a single block that has break-before and break-after set to the value page, and the space-before.conditionality set to retain. Example 5-12 shows a specific example of this practice.
<fo:block break-before="page" break-after="page" space-after="4in" space-before="3in" space-before.conditionality="retain" font="24pt Times bold" text-align="center"> Document Title, using single or multiple lines. </fo:block>
Note the use of the break condition in the containing block. This causes the page to appear on its own. This may also be used to create a blank page.
Other content that needs special layout might include a quotation, a heavily emphasized block, or even images, which often form the only content of a block.
Each needs consideration in terms of borders, offset spacing (using space-before, padding, or indentation), text alignment, or other decorations.
Other special uses for which the block is ideal include the indented quotation (often called the blockquote), end aligned blocks, emphasized text that is not inline, and simpler headings. Each of these is a block for which the properties are selected appropriately. When selecting how you want a block laid out, a virtual ritual is needed to choose which of the myriad properties are needed.
One possible approach is to run through the following sequence:
Block separation: from the previous and next block vertically. Use space-before or space-after.
Horizontal alignment within the block. Use text-align and text-align-last.
Indentation: use text-indent for the first line, or use start-indent and end-indent for a block.
Finally, select the font-family , font-weight, and font-size.
This sequence considers the positioning of the block vertically (relative to previous and subsequent blocks) and horizontally (in the line-progression-direction), sets up the content alignment (start, end, or justified), and then finally set the font, font weight, and size. For many blocks, this will be sufficient. Once you have selected these properties, all that remains for you to do is the decoration on the block, which I'll discuss shortly. These properties will satisfy most of your basic layout needs for simple blocks. Lists and tables also will be addressed.
A background can be specified for any block, and takes the following list of properties. Most of these properties really apply only to the use of images as a background, with the exception of color, which may be used on its own or as a fallback for the image.
background-attachment: scroll or fixed. Not really relevant to print, but specifies what happens when an image is used as a background.
background-color sets the color.
background-image specifies the image to use.
background-position-vertical (or -horizontal) specifies the position of the background image with respect to the containing rectangle.
background-repeat specifies if, and how, the image is to be repeated in the content rectangle. This is discussed later in this chapter.
Probably the most common one is background-color, which simply specifies the color to be used for the background of the block.
For figures, images, illustrations, or graphics whatever you want to call them XSL-FO uses a single element, fo:external-graphic ("external" simply because the actual graphics file is external to the XML source document), to add it to the flow. If it is wrapped in a block or placed inline with the content, the graphic is appropriately laid out by the formatter, as shown in Example 5-13 and illustrated in Figure 5-7.
<fo:block> <fo:external-graphic src='/books/4/225/1/html/2/url(pig.jpg)'/> </fo:block> <fo:block> <fo:external-graphic src="url(pig.jpg)"/> </fo:block> <fo:block>A block containing the graphic wrapped in an inline container, <fo:inline alignment-baseline="before-edge"> <fo:external-graphic src="url(images/1.png)"/> </fo:inline> </fo:block>
Note the syntax of the src attribute. The url function, as shown here, simply returns the local (relative) file. It has raised some controversy in regards to its necessity, because most files will be local, although it does offer the option of a web-based image. Be aware of the display capabilities of your formatter. Common ones are JPEG and PNG.
The two images are wrapped, one in a block and one inline. Their sizes are determined automatically in this case, although sizes can be specified in applicable units using the content-height and content-width properties of the fo:external-graphic. The specification recommends also using background-color, which will fill in the rectangle outside of the actual image to the extent of the containing rectangle when the image is available and will be visible when the image is unavailable. When the image is available, it is rendered on top of the background color. (Thus, the color is visible in any transparent parts of the image.)
Figures can also use the block to create a title for the image. Nest both the title and the external graphic within a single block to keep them together (to share alignment or any other special properties), as in Example 5-14.
<fo:block> <fo:block>Title for the figure</fo:block> <fo:external-graphic src="url(images/filename.ext)"/> </fo:block>
Also, an image may be scaled by using the content-height and scaling properties:
<fo:external-graphic content-height="120%" scaling="uniform" ...
The aspect ratio of the graphic is maintained using the scaling property with a value of uniform, as above. Note that uniform scaling is the default even when only the content-height is specified to be scaled.
Images may be used as a background for a block using the block property background-image. This provides the image over which the content is written. The extent of the image is controlled with the background-repeat property, which takes one of the following values: repeat, repeat-x, repeat-y, or no-repeat. These produce an x and y repeat, an x direction only repeat, a y direction only repeat or no repetition, respectively. Figure 5-8 shows an example using the no-repeat option, which produces a single image. The image may be positioned readily using the background-position-vertical and background-position-horizontal properties.
By judicious selection of the correct image and the background-repeat property, you will find out (probably by experimentation) which alternative provides the effect you need. This property on blocks may be used either decoratively or for a more targeted purpose, such as indicating a property of the output to inform the reader that certain content is not valid. This is often seen with the background images on checks that say sample only, to avoid fraudulent use of the content.
Finally, a variant on an image, the instream-foreign-object element, is commonly used for SVG, but could be used for other inline approaches. It contains any markup format that isn't in the fo namespace and that the FO processor can invoke some other processor to handle. But the idea is that it creates a black box that is simply positioned by the FO processor, just as with external-graphic. SVG usage is strong in some processors as an XML-based vector graphic format. Other options are XHTML and the Synchronized Multimedia Integration Language (SMIL), both W3C recommendations. Note that this is purely a function of the renderer you are using. Ensure that your readers have that renderer prior to using this.
Cross-references are generated at the transform stage, using perhaps the id and idref pairings from XML. The XSLT stylesheet should produce similarly identified pairs of references and identified content, as discussed in Chapter 9. The target of a cross-reference is identified by the id attribute on the block. This is shown in Example 5-15. A chapter element creates a block with an id value taken directly from the XML source document. This may then be referenced later using this id value.
|
<xsl:template match="chapter"> <fo:block id="{@id}"> .....process content </fo:block> </xsl:template>
This is discussed further in Chapter 10.
The title of this section may seem inappropriate to some readers. What one person may view as decoration, another may view as essential content. In this section, I discuss borders, padding, color, etc., which some may view as decoration.
First, I must clarify. From outside to inside, there is a border, padding, and then content. This is illustrated in Figure 5-9.
We can refine the breakdown of borders and padding by specifying only one side. Hence, the full list of borders is:
border-before
border-after
border-start
border-end
You should be aware that the simplest specification of a border (the form border='black solid 1pt') is available only in a complete implementation. If portability is an issue, I might suggest using the basic options in combination with the inherited color, which defaults to black. The basic version is shown in Example 5-16. It might appear to be grossly inefficient, but if used as a part of your attribute sets as shown, it will not be a problem.
<xsl:attribute-set name='border'> <xsl:attribute name='border-before-style'>solid</xsl:attribute> <xsl:attribute name='border-after-style'>solid</xsl:attribute> <xsl:attribute name='border-start-style'>solid</xsl:attribute> <xsl:attribute name='border-end-style'>solid</xsl:attribute> <xsl:attribute name='border-before-width'>.1mm</xsl:attribute> <xsl:attribute name='border-after-width'>.1mm</xsl:attribute> <xsl:attribute name='border-start-width'>.1mm</xsl:attribute> <xsl:attribute name='border-end-width'>.1mm</xsl:attribute> </xsl:attribute-set>
This is called up using:
<fo:block xsl:use-attribute-sets='border'> ....
Padding is specified in the same way. To keep it simple, I will continue using the shorthand form, using simply border, which the formatter then expands to specify all four properties. Further alternatives are provided by border-top, border-bottom, border-left, and border-right, which are writing-mode relative. For example, border-before.length would equate to border-top.length in left-to-right, top-to-bottom writing. Feel free to interchange these if you prefer or if you are concerned about portability.
Having specified which border you want, the properties available for borders are:
Specifies the color required for the border.
Specifies the extent of the border.
Specifies the appearance of the border. Possible values are none, hidden, dotted, dashed, solid, double, groove, ridge, inset, and outset.
Most of the border properties are self-explanatory, though the style property may be less so. Each style offers a different visual appearance. inset and outset attempt to provide a three-dimensional effect. Example 5-17 shows the basics of using the style property on a block.
<fo:block border-style="solid" border-width=".1mm" width="2in"> A simple narrow border </fo:block>
This example uses the extended option of specifying style and width, relying on the inherited color, because the basic specification requires a lot of typing.
border-style="solid" border-before-style="solid" border-after-style="solid" border-start-style="solid" border-end-style="solid" border-before-width=".1mm" border-after-width=".1mm" border-start-width=".1mm" border-end-width=".1mm" border-before-color="black" border-after-color="black" border-start-color="black" border-end-color="black"
An explicit and more comprehensive example is shown in Example 5-18; its formatted output is shown in Figure 5-10. I have added a little padding simply to separate the content from the border.
<fo:block border-after-color="red" border-after-style="outset" border-after-width="1em" border-before-color="blue" border-before-style="outset" border-before-width="1.5em" border-end-color="silver" border-end-style="outset" border-end-width="2em" border-start-color="green" border-start-style="outset" border-start-width="2em" padding="6pt" width="4in"> A paragraph with a completely specified border. Style is shared as 'outset', color varies on each edge, as does width. </fo:block>
Although the simpified forms can appear complex, for most users, they will suffice. If you want to use them more fully, the specification is reasonably clear, despite the references to the Cascading Style Sheets specification.
Specifying padding is similar to borders. Its use is to offset content from its surrounding border or container if no border is specified. It could be seen as an alternative to space-before and space-after, as it has a similar visual effect, though it isn't meant for that. If padding is not specified, the default value of 0pt is used, which gives an appearance of content being placed very close to area boundaries. A more natural appearance is to provide padding on all blocks, which will be formatted in close proximity. Figure 5-11 indicates this difference clearly. The first block has no padding, and the second block has padding applied in the start and end directions, as shown in Example 5-19.
<fo:block space-before="18pt">A simple block with no padding. Note the proximity of the content to the page edge (the containing area). This may be too close when large blocks of text are used. </fo:block> <fo:block padding-end="6pt" padding-start="6pt">A simple block with padding specified as 6pt. Note the proximity of the content to the page edge (the containing area). This may be better when large blocks of text are used. </fo:block>
As with borders, padding may be individually specified or used in its shortform (padding="6pt"), as in the example. The full list reads nearly the same as with borders, with the exception of the color specification.
padding-before (padding-top)
padding-after (padding-bottom)
padding-start (padding-left)
padding-end (padding-right)
The parenthesized alternatives provide the direction-oriented alternatives for left-to-right, top-to-bottom layout.
The explicit values using padding-before are:
padding-before.length
padding-before.conditionality (retain or discard)
The length property specifies the actual length of the padding, again using the common length specification and unit format.
The conditionality property is logically consistent with spaces, i.e., if the padding is the first in a series of spaces, and if the conditionality has the value discard, the space is discarded. If its value is set to retain, it is kept.
One property that may be of use on a block when output is not desired is the visibility property. visibility="hidden" produces no output for that block. An area is allocated for the block, but no content is inserted into it. This may be useful for a limited distribution document, where confidential data is made available only to authorized personnel.
The XSL-FO specification is reasonably clear when describing lists. It needs to be, because in XSL-FO, lists are certainly not intuitively easy. The basic syntax is shown in Example 5-20, using the Unicode symbol for the bullet, ߦ, as the label. The constituent parts of the list are the list-block containing list-item, which has both a list-item-label and list-item-body.
<fo:list-block> <fo:list-item> <fo:list-item-label> <fo:block>•</fo:block> </fo:list-item-label> <fo:list-item-body> <fo:block>List item contents.</fo:block> </fo:list-item-body> </fo:list-item> </fo:list-block>
The list-block contains the entire list. Each list entry contains both a list-item-label and a list-item-body. The label contains the item marker or content, and the body holds the actual contents. Within both the label and body, blocks wrap actual content. This provides the hierarchy shown in this example.
Laying out the list to the required spacing uses the properties on the contained elements, which the specification demonstrates very well. Figure 5-12, which repeats the figure from the specification, illustrates the properties clearly, though the combinations can be tricky. Using default values produces a list that some may think is too close to the margins for normal use, because lists are usually indented. Indentation is achieved through the start-indent property of the list-block. The provisional-distance-between-starts property of the list-block specifies the distance between the start of the label (the bullet in Example 5-20) and the actual start of the list content. The separation between the label and the body is specified by the provisional-label-separation property. As you can see, there is interaction between them and the size of the areas produced by the formatter for whatever label you have chosen. This is less important for simple, bulleted lists than it is for glossary items, for example, where the label is actual text content. The label start-indent offsets the label from the containing area (useful for nested lists) to indent secondary lists. The final property I will mention is the body end-indent. This is useful when the extent of the list in the end direction is required to be less than the preceding content, showing the list in a narrower space than the surrounding content.
Two key properties are the label end-indent and the body start-indent. For a simple list, I'd suggest for the label end-indent, using label-end( ), a function that returns a value of the end point of the label. For the body start-indent, I'd suggest using body-start( ), a function that returns the relative position of the start point of the body. These provide a reasonably successful initial positioning. This is also suitable for nested lists, as shown in Example 5-21 and Figure 5-13.
<fo:list-block start-indent="5mm" provisional-distance-between-starts="10mm"> <fo:list-item> <fo:list-item-label end-indent="label-end( )"> <fo:block>•</fo:block> </fo:list-item-label> <fo:list-item-body start-indent="body-start( )"> <fo:block>List item contents.</fo:block> [1] <fo:list-block> <fo:list-item> <fo:list-item-label end-indent="label-end( )"> <fo:block font-family="ZapfDingbats">➘ </fo:block> </fo:list-item-label> <fo:list-item-body start-indent="body-start( )"> <fo:block>List item contents of nested list. </fo:block> </fo:list-item-body> </fo:list-item> [2] </fo:list-block> </fo:list-item-body> </fo:list-item> </fo:list-block>
Start of inner list
End of inner list
More specific layout options will use the length specifications of the list properties. These are shown quite clearly in Figure 5-12 and can fully specify list layouts, including the use of this syntax for items other than lists. Experimentation with these properties may be necessary to get the result you want. The specification, realizing how complex the syntax is, contains a few examples in Section 6.8.
Numbered lists use the contents of the label element, during the transformation phase, to number the list item, either as a single numeric value or as part of some sequence. Often, this might use the node-set that constitutes the children of a list element. If lists are split up by intervening content, XSLT provides the facilities to continue numbering after the break by collecting the right node-set and numbering it. I refer you to Michael Kay's XSLT Programmer's Reference (Wrox Press) for the definitive book on such use of XSLT, particularly the sections on xsl:number and the position( ) function.
Display or definition lists are those often used for a glossary form, where a word or phrase is referenced in the main body of text and linked to a definition of that word or phrase. The word is placed in the list-item-label and some additional material is added as the list-item-body. Ensure that the size of the label's content is provided for when using list-item-body. The provisional-distance-between-starts property must be sufficient to fit the largest candidate if the list is to appear even. Word wrapping will take place if there is insufficient space for the label and if steps are not taken to account for it. Overflow conditions are easy to find, so ensure that each list item is given its own space and that the wrapping meets your needs. Note the wrapping in Example 5-22 and the output of Figure 5-14.
<fo:list-block start-indent="5mm" provisional-distance-between-starts="40mm"> <fo:list-item space-after="1em"> <fo:list-item-label > <fo:block font-weight="bold">A long label which wraps.</fo:block> </fo:list-item-label> <fo:list-item-body start-indent="body-start( )" end-indent="5mm"> <fo:block>List item contents. Note the text flow which takes place when sufficient text is placed to ensure wrapping. Note the text flow which takes place when sufficient text is placed to ensure wrapping.</fo:block> </fo:list-item-body> </fo:list-item> <fo:list-item> <fo:list-item-label > <fo:block font-weight="bold"> Another long label which wraps.</fo:block> </fo:list-item-label> <fo:list-item-body start-indent="body-start( )"> <fo:block>Second content. </fo:block> </fo:list-item-body> </fo:list-item> </fo:list-block>
The example has the provisional-distance-between-starts property set to 40mm, which may still be too small for some. With care, the specification can produce very attractive lists and works for most layout options. It's even possible to use the list format in places where lists would not be considered. An example of this is in left- and right-aligned text in a header or footer, using the label as a left header and the body as the right header.
Finally, remember that the list-item-label may be empty, if it's needed to vertically align the body items without a marker or other content in the label field.
In some circumstances, you may need to consider the label-to-body alignment. This is not possible in the present version of the recommendation. You can, however, set a value of relative-align on the fo:list-item to be either before (the default) or baseline. This allows you to align either the initial lines' tops (e.g., cap-heights) or baselines. So, there is some flexibility here.
XSL-FO is not like HTML. It is mostly unnecessary to use tables for layout as has been done in HTML. The XSL-FO vocabulary provides so much more than HTML that you shouldn't fall back on tables simply to obtain an effect you have achieved on the Web. The table-formatting object is for formatting tabular data. There may be circumstances where table-based formatting is necessary, but they will be rare.
The table model used by XSL-FO is row first, which matches most though not all common table models in XML markup. One option is to use two properties on a table cell, starts-row and ends-row (each set to either true or false), included for use where no row wrapper is available, for example, where a particular type of content or markup indicates this change. The outline sequence is table, table-header, table-footer, table-body, table-row, table-cell. table-header keeps headings available at the top of each new page of a multipage table, and table-footer allows a "Continued on next page" message in the footer; these properties are optional.
The basic table without a caption has a table-body child, which has table-row children, which have table-cell children. This provides a simple transform from a standard table with a similar model. The only caveat is that the table cells need their content wrapping in block elements. The transform shown in Example 5-23 provides this simplest of table models. Example 5-24 shows a snippet of the stylesheet.
<table> <tbody> <row><td>R1C1</td><td>R1C2</td><td>R1C3</td></row> <row><td>R2C1</td><td>R2C2</td><td>R2C3</td></row> </tbody> </table>
<xsl:template match="table"> <fo:table width="3in"> <xsl:apply-templates/> </fo:table> </xsl:template> <xsl:template match="tbody"> <fo:table-body> <xsl:apply-templates/> </fo:table-body> </xsl:template> <xsl:template match="row"> <fo:table-row> <xsl:apply-templates/> </fo:table-row> </xsl:template> <xsl:template match="td"> <fo:table-cell border="solid 1px black"> <fo:block><xsl:apply-templates/></fo:block> </fo:table-cell> </xsl:template>
I have restricted the table size to 3 inches and added a border to each of the cells.
The table may be suitably captioned using the outer wrapper, table-and-caption, which holds both the table-caption and the table. This provides a bold, centered heading:
<fo:table-and-caption> <fo:table-caption text-align="center"> <fo:block font-weight="bold" >Table Caption.</fo:block> </fo:table-caption> <fo:table> .....
The caption may be placed on any side of the table, using the caption-side property table-caption. I find the use of an appropriate block after the table quite suitable for these purposes. It is probably useful only if you need the caption to one side of the table.
One of the difficulties with the various table models currently available is that column headings are often not placed conveniently within the table body itself. If column headings are needed, use the transform stage to reorder the content.
The part of the model designated for column headers is the table-header element, the first child of the table element. To specify a bold, centered heading, use:
<fo:table-header> <fo:table-cell border="solid black 1px" padding="1em" border-collapse="collapse"> <fo:block text-align="justify" font-weight="bold">Column 1</fo:block> </fo:table-cell> .....
And so on for each column. This provides the hook for repeating headers at page breaks. The border-collapse property is set such that the borders are merged rather than separated. The fo:table property table-omit-header-at-break="false" tells the formatter that these headings are required to be redisplayed at each new page. The treatment of footers is handled similarly. The footers use the table-footer element, with contained table-cells and follow the table-header element. If you have to omit the footer prior to a break, use the table-omit-footer-at-break property with a value of true. This improves the visual appearance and indicates that the table continues.
Tables with fixed widths depend on two table properties. The first is the inline-progression-dimension, the second, the table-layout. Both of these default to auto. To produce a fixed-width table, set table-layout to either a length specification, such as 100mm, or to a percentage, then set the table-layout property to a value of fixed. So, to get a 3 inch-wide table, specify:
<fo:table inline-progression-dimension="3in" table-layout="fixed">
Similarly, to obtain a table half the width of the page, use:
<fo:table inline-progression-dimension="50%" table-layout="fixed">
To specify the column widths separately, use the element fo:table-column set to a value of, say, 50, with attributes of column-number to specify the column under discussion and column-width set to a value using the proportional-column-width function. There should be one entry per column of the table being set out, prior to the fo:table-header entry. This isn't a percentage, but, to quote the specification, "The column widths are first determined ignoring the proportional measures. The difference between the table-width and the sum of the column widths is the available proportional width. One unit of proportional measure is the available proportional width divided by the sum of the proportional factors." For example:
<fo:table width="3in"> <fo:table-column column-label="1" column-width="proportional-column-width(50)"/> <fo:table-column column-label="2" column-width="proportional-column-width(25)"/> <fo:table-column column-label="3" column-width="proportional-column-width(25)"/> <fo:table-body>
This is often a good choice because it doesn't have to add up to 100% and it is easier to use where fixed-width columns and proportional-width columns are mixed.
Proportional-width table columns may be specified using a column-width value of proportional-column-width, as in Example 5-25.
<table-column column-width="proportional-column-width(1)"/> <table-column column-width="proportional-column-width(3)"/> <table-column column-width="proportional-column-width(1)"/>
This will make the middle column three times the width of each other column.
An alternative approach to table layout is to specify the required width of a table column. The table-column element is a first child of table and has attribute values that apply to the named column and spans. Example 5-26 shows a simple extract, using the column-width attribute values set to percentages. The values may take percentages or widths, although percentages are generally easier to handle. A table column is specified by number, starting at one, with each column width specified using the column-width attribute. Note that this property is ignored if the number-columns-spanned is greater than one.
<fo:table> <fo:table-column column-label="1" column-width="20%"/> <fo:table-column column-label="2" column-width="60%"/> <fo:table-column column-label="3" column-width="20%"/> ... Remainder of table specification.
An alternative use of this element is to ensure that table columns are of equal width. This may be specified using the number-columns-repeated attribute of table-column. This is simply a shorthand to save rewriting the table-columns for all succeeding columns. The effect is to take this table-column specification and repeat it, with the column-number increasing by increments of one. So for this three-column table, a simple specification requiring one third for each column is shown in Example 5-27.
<fo:table> <fo:table-column column-label="1" column-width="33%" number-columns-repeated="3"/> ... Remainder of table specification.
Two attributes are used for spanning. To span columns, use the number-columns-spanned property on either table-column or table-cell elements to specify the number of columns required to be spanned. The default value is 1. This provides a flexible way to mix single and multiple spans. Similarly for rows, use the number-rows-spanned property of table-cell to span rows.
As an example, consider a 5 by 5 table, R1C1 to R5C5, where the requirement is to span R2C1 and R2C2 (column span) and to span R4C1 and R5C1 (row span). So, the table-cell that specifies R2C1 will have the property number-columns-spanned set to 2, with R2C2 omitted (its area now covered by R2C1). The table-cell that specifies R4C1 will have the property number-rows-spanned set to 2, with cell R5C1 omitted.
Hence, the relevant stylesheet parts will be as shown in Example 5-28.
<fo:table-row> <fo:table-cell border="solid black 1px" border-collapse="collapse" number-columns-spanned="2"> <fo:block>R2C1</fo:block> </fo:table-cell> <!-- R2C2 omitted--> .... and for row 4, <fo:table-row> <fo:table-cell border="solid black 1px" border-collapse="collapse" number-rows-spanned="2"> <fo:block>R4C1</fo:block> </fo:table-cell> ... and for row 5 <fo:table-row> <!-- R5C1 omitted -->
This produces the table shown in Figure 5-15.
Note that the borders on each of the relevant table cells are realigned as needed to provide for the spanning.
Two options are offered for empty cells. The empty-cells property may be set to either show or hide. This will affect borders that are either hidden or shown for each empty cell.
Within any cell, content may be aligned horizontally using the text-align property, as mentioned earlier for example, to center the contents of a cell with values of start, center, end, or justify.
Vertical alignment within a cell is achieved using the display-align property, with a value of before, center, or after. Figure 5-16 repeats the previous example, with R5C3 having the two attributes set as in Example 5-29.
<fo:table-cell border="solid black 1px" display-align="center" border-collapse="collapse" text-align="center"> <fo:block font-weight="bold">XXX</fo:block> </fo:table-cell>
Other properties needed to address table layout problems include keeps and breaks, which keep content together over page breaks or ensure a break after or before a specific row, respectively.
Within any table cell, the blocks that contain the content may use any of the available properties to lay out the cell content.
While the descriptions above work well in most situations, there are a few more details about blocks that are worth explaining.
One of the ways to specify lots of things in XSL-FO is via the length specification.
The spec says:
<length> A signed length value where a `length' is a real number plus a unit qualification. A property may define additional constraints on the value.
Got that? No? One approach is just to see what your formatter makes of your attempts and adjust as necessary. Remember that fonts, for example, are not infinitely flexible, so a request for 11.6 point text is highly likely to result in 12 point text. Section 5.9.13 of the Recommendation lists the permitted units of measurement, providing centimeters (cm), millimeters (mm), inches (in), points (pt), picas (pc), pixels (px), and em (em). A pixel is taken as 0.28mm; a point, as 1/72 of an inch; and a pica is a printer's unit of type size, equal to 12 points or about 1/6 of an inch.
Throughout the specification, you can read the dotted notation used for properties, for example, space-before.optimum. Generally, the first term may be used alone to set the value. The fuller, dotted form is used when tight requirements on formatting are needed, and it is required that some value lie between the X.minimum and X.maximum values.
A short form provides a value for all the length specifications and the initial value to all the non-length specifications.
Section 5.11 of the Recommendation details the various datatypes, should you need them. The shortform of each length is quite acceptable in most cases, for example, space-before="4pt". This is shown fully expanded in Example 5-30.
space-before.minimum="4.0pt" space-before.optimum="4.0pt" space-before.maximum="4.0pt" space-before.precedence="0" space-before.conditionality="discard"
The optimum, minimum, and maximum values together comprise a length-range that provides the formatter with limits which should meet your needs.
![]() | CONTENTS | ![]() |