|
|
CONTENTS |
|
In Chapter 3, I discussed how pages are organized into sequences, how page masters are selected for processing, and how the page area is divided into
As you have seen in Chapter 3, formatting objects contain data that should be rendered as a series of marks on the canvas — text, images, lines, etc. The formatter turns objects into series of imaginary rectangles on the page, called areas . One object may produce more than one area: e.g., an fo:block element produces two areas if split by a page break, as shown in Figure 4-1.
FOs have properties that specify constraints on the appearance and placement of areas generated by them. These constraints are used to calculate
area traits
, which are attributes of areas that uniquely identify their placement, appearance, and contents. Most properties and traits have one-to-one correspondence: e.g., the
|
Areas form a tree structure: a larger area can contain smaller subareas. Typically, the area tree closely resembles the source FO tree: an area generated by formatting object A contains subareas generated by descendant elements of A. Important exceptions are
Areas created by formatting objects can be of two principal types:
These areas
These areas correspond to text paragraphs, tables, lists, etc. Areas of this type are stacked on a page in the
block-progression-direction
(see Chapter 5). The following objects create only block-areas:
fo:block
,
fo:block-container
,
fo:table
,
fo:table-and-caption
, and
fo:
Each area has a set of font traits, derived from font properties of the respective formatting object. These traits uniquely define a nominal font associated with the area. The area need not actually contain glyphs from this font; parameters of the nominal font may be used in calculating area position. Two such traits are text-altitude and text-depth : they specify the inline-progression-dimension of glyph-areas and are used in line-stacking calculations. These are the low-level items that determine the area sizes.
Two more area types are useful for defining the area model:
These areas can be
The height of the nominal ascender of the font to which the glyph belongs
The depth of the nominal descender of the font
These are
Note that
text-altitude
and
text-depth
are
font
properties, common to all glyphs in a given font, rather than glyph properties: two glyph-areas containing glyphs from the same font will get identical values for these traits. The specification says conforming
These two traits determine the size of the content-rectangle for the glyph-area in the block-progression-dimension : from the top of text-altitude to the bottom of text-depth .
|
In the inline-progression-dimension , glyph-area size is determined by the glyph itself (for Western fonts, this would be the glyph width inclusive of left and right bearings). This is worth being aware of, even though the stylesheet author has no control over them.
This is a special kind of a block-area, corresponding to a single line of text. It has no corresponding formatting object; its traits are derived from properties of the embracing block-level object, such as
line-height
,
line-height-shift-adjustment
, and
line-stacking-strategy
. It is a useful abstraction to describe the switch between inline-level and block-level areas: inlines are stacked inside line-areas, which are packaged into the
There are other area types, used in constructing upper levels of the area tree, which also have no formatting objects directly corresponding to them:
Each region into which a page is divided ( region-before , region-after , region-start , region-end , and region-body ) forms an area. The dimensions of the regions are defined by the respective region descriptors in the fo:simple-page-master currently selected.
This implicit area is created inside the region area that accepts the contents of the
fo:flow
(typically,
region-body
) and
In a multicolumn layout, a block-level element may have an attribute
span="all"
making it span all
A span-reference-area is further subdivided into areas for single columns. For span-reference-areas with span="all" , there will be only one such area, coinciding with the span-reference-area itself. For span="none" , the number of areas is defined by the column-count property, and their separation by the column-gap property of the fo:region-body element in the current simple-page-master element.
Figure 4-4 shows the main areas of a normal page.
An area may have a border around it, with or without a background inside it (which may be an image or a color fill). The following are terms for rectangles that
This is the innermost part of an area. It represents the space actually available to host area contents, such as children areas, glyphs, and graphics.
This rectangle extends up to the inner boundary of the border. It includes the content rectangle plus padding offsets from all the four sides. This rectangle delimits the zones covered by the background of the area.
This rectangle is delimited by the external edge of the border frame. It includes the padding rectangle, plus border widths of all the four sides. Except for special cases (absolute/relative positioning, overflow, out-of-line elements, etc.), no marks are produced by a formatting object outside the border rectangle of its generated area(s) — the rectangle is
All these rectangles should be present in CSS2 box model. There is one more rectangle defined in CSS: a
margin rectangle
that incorporates margins around the border. In XSL, margins are not used for area positioning (they are
In CSS2, normally positioned blocks have properties that determine their placement with respect to the content rectangle of their parent box. All boxes are equivalent: children inside a box are always stacked in the same manner.
XSL takes a different approach: it designates some areas for use as reference for defining inline-progression-dimension and orientation of their descendant areas. Such areas are called reference areas ; I will refer to other areas as normal .
Reference areas have the following
They define starting points for start-indent and end-indent traits of all descendant normal block-areas.
They can set new writing-mode and reference-orientation (normal areas can change inline-progression-direction only via the direction property or bidi mechanism).
Their dimension is always bound in both directions, and the display-align trait can be set to align their contents in the block-progression-dimension .
All
region
and
column areas
are reference areas; areas produced by
fo:block
elements are normal.
table-
fo:table-cell
fo:block-container
fo:inline-container
The XSL specification defines a large variety of properties to express constraints over the appearance of areas on a page. The formatter
It is not uncommon that a single formatting object produces two or more areas. A single block of text may be split by a page break; an inline element may be
Borders and padding can be applied conditionally, using the extended property,
border-after-width.
This is another case where a
trailing
area (here, a border) may be discarded if it is the last in a reference area.
There are also a number of constraints that don't directly assign traits to the areas; rather, they control the number and the appearance of the areas produced by the formatting object that bears them. They are
The first area generated by a formatting object with break-before set to something other than auto should be placed first in the area-tree inside some reference-area. The stylesheet author specifies the condition, and the formatter determines the placements. The type of this reference-area depends on the value of the property:
No constraint is present; the property is discarded.
The object starts a column-reference-area.
The object starts a page-reference-area.
The object starts an odd-numbered page-reference-area.
The object starts an
This property triggers a break immediately before the first area generated by the current object, starting a new column, page, odd-page, or even-page. Note, however, that this property is not like a
form-feed
command for a printer; if the constraint on area placement had been generated without this property, no additional pages would be generated. In plain words, consecutive break constraints separated by objects that create no areas are merged.
An inversion of the
The
The object should not start a new line.
The object should not start a column.
The object should not start a page.
Each component may assume the following values:
Constraint disabled.
Specifies the strength of the constraint.
Specifies a strength value greater than any integer.
The strength of the constraint is used to arbitrate its conflicts with other constraints.
Inhibits break after the last area produced by the object. It has the same component structure and the same choice of values.
This property prescribes that the formatting object should generate only one area — in other terms, it inhibits respective breaks between areas generated by the formatting object. The components and the set of values are the same as for the preceding two values.
Like space specifiers, keep and break constraints can lead to overconstrained specification, when it is
All break constraints are satisfied first.
Keep constraints with lower strength are
The resulting set should satisfy the maximum number of constraints with the highest strength possible.
There are two more properties that constrain area placement inside reference areas: orphans and widows . They specify the minimum number of line-areas that can be left at the end of the page ( orphans ) or carried over to the next page ( widows ). The spec does not define how these properties should interact with other keep constraints.
Each area has two traits that define its dimension: inline-progression-dimension and block-progression-dimension . These traits define the width and height of the content rectangle of the area; which one is horizontal and which is vertical depends on the current writing-mode and reference-orientation . However, not all formatting objects may have properties that directly map to these traits; they are permitted only on objects that create reference areas or images:
fo:simple-page-master gets page-width and page-height properties, and margins. There is a size shorthand, too.
fo:region-body has margins to specify its offsets from the page-reference-area content rectangle (defined in its parent fo:simple-page-master ). While there are no traits for direct settings of dimensions, it is still possible to fix the size because the dimensions of page-reference-area are known.
fo:region-before , fo:region-after , fo:region-start , and fo:region-end cannot control their dimension in the direction parallel to the sheet edge to which they are attached. In the other direction, their size is given by the extent property.
fo:block-container
and
fo:inline-container
may have explicit attributes of
inline-progression-dimension
and
block-progression-dimension
, specified either as a percentage, range, or length.
width
and
height
may be also specified for these two elements. In addition, when an
fo:block-container
is
fo:external-graphic
and
fo:instream-foreign-object
have the same size-
fo:table and fo:table-cell have width and height .
|
All other elements can only specify their dimensions in terms of distance from the edges of a reference area. (This is more a feature than a limitation; to set any dimension explicitly, you always have an option of wrapping the desired element into a block- or an inline-container).
Each block-area has two traits that specify its position and size in the
inline-progression-dimension
:
start-indent
and
end-indent
. They specify the distance from an edge of the content rectangle of the area (
start-edge
for
start-indent
,
end-edge
for
end-indent
) to the respective edge of the content-rectangle of its
XSL has an alternative mechanism for setting indent traits: an area can be positioned using margin properties. There are four margin properties: margin-top , margin-bottom , margin-left , and margin-right . A margin property specifies the distance from the respective edge of the content-rectangle of the closest ancestor reference-area to the edge of the border-rectangle of the current area. Margin properties can only be absolutely oriented: no writing-mode relative equivalents are provided. There is also a short form [1] property margin that sets all the four margins simultaneously, using standard CSS2 rules. [2]
In XSL, indents are inheritable. Unless you set them explicitly (in either of the two ways described previously), the content-rectangle of a nested area gets the same inline-progression-dimension as that of an embracing area. Inheritance is discussed further in Appendix E. If you add padding and/or border to the nested area, its border-rectangle will extend outside the content-rectangle of its parent. Note that this differs from CSS2 habits: there, the border-rectangle of a contained box always fits inside the content-rectangle of the container one. If you want to enforce CSS2-style box nesting, you have to specify margin="0pt" explicitly on every contained block.
For the sake of completeness, let's now mention the effects that side floats have on the placement of block-areas. These are areas specified to float in the start or end direction, as might be found in an explanatory note. A side float F is said to intrude into a block-area B if:
B
and
F
have the same
There is a line parallel to the inline-progression-direction that intersects border rectangles of both B and F . Think of this as the float stealing space from the block.
Intruding floats make no impact on the placement of normal block-areas. However, they do influence the inline-progression-dimension of the following types of areas:
line-areas
reference-areas (generated by a fo:block-container element)
Areas generated by list-item-body
If an area belongs to one of these types, and has one or more side-floats intruding into it, then its start-indent and end-indent are calculated from the inner side of the float box, rather than from the respective edge of an ancestor-reference-area.
In a typical case, a paragraph of text with an intruding float will have lines
Let's now consider how block-areas are stacked one after another in the block-progression-dimension . We have already seen that the XSL model is based on spaces; now we will see which properties can be used to constrain spaces between areas, and how they interact.
What is a space? Informally speaking, a space is a distance between the area's border-rectangle and
the closest visible mark
left on the page by any normally positioned area. This mark may be left by a nonzero border, by padding (because padding may contain background), or by a
Spaces before and after a block-area are controlled by space-before and space-after properties. These two properties are compound , with the following components:
A length. Default is 0pt.
A length. Default is 0pt.
A length. Default is 0pt.
A number or a special token, force . Default is 0.
Either discard or retain . Default is discard .
There is also a shorthand notation: by specifying space-before="X" you set all numerical components ( .minimum , .optimum , and .maximum ) to the same value of X .
These compound properties represent a very versatile mechanism: you can constrain a range for the space (from
minimum
to
maximum
),
This complexity is due to the fact that space constraints are not independent. When two consecutive areas meet, there may be several space constraints
As I mentioned earlier, a space constraint between two consecutive areas A and B can be specified in any of the following ways (see Figure 4-8):
As a space-after of A
As a space-before of B
As a space-after of any block-area A1 descendant of A such that there is no other content, padding, or border between A1 and B
As a space-before of any block-area B1 descendant of B such that there is no other content, padding, or border between A and B1
All these constraints are considered together and merged into a single value, or better, a single range
The merged space specifier is calculated by the following algorithm:
If any space property has .precedence="force" (forcing spaces), all space specifiers but forcing ones are discarded. The resulting space specifier should have the sum of .minimum components as its .minimum , the sum of .optimum s as .optimum , and the sum of .maximum s as .maximum . In other words, forcing spaces are additive: they suppress all other spaces but don't merge with each other.
Otherwise, spaces with the maximum value of .precedence are selected, and others are discarded. If there is more than one space specifier with the same precedence, the one with the higher .optimum value is selected. If there are two or more specifiers with the same .precedence and .optimum values, the resulting space specifier will be equal to the intersection of their ranges; that is, the .minimum will be equal to the greatest of all .minimum s, and the .maximum will be set to the smallest of all .maximum s.
If an area happens to be the first on the page with no preceding marks from which to count space-before , space-before is counted from the before-edge of the content-rectangle of the closest ancestor reference-area. Remember that in this case, only specifiers with .conditionality="retain" are taken into consideration.
Now let's see an example of how all this works. Figure 4-9 represents a typical configuration. Two blocks, each specifies a space: the first specifies a
space-after
and the second specifies a
space-before
. Example 4-1
<fo:block space-after.minimum="3pt">
space-after.maximum="24pt"
space-after.optimum="12pt">
.....
</fo:block>
<fo:block space-before.minimum="6pt"
space-before.maximum="18pt"
space-before.optimum="12pt">
....
</fo:block>
The resolution is for the resultant minimum to be set to 6pt, the maximum to be set to 18pt, and the computed value, set to 12pt. This is shown in Figure 4-9, with the shaded area indicating the extents. When the optimum values are equal, take the greatest minimum and least maximum , so the resolved space in this case has a minimum of 6pt.
It is easy to create a contradictory set of space constraints. In such a case, formatter behavior is not described by the specification and remains application-specific. The stylesheet writer is responsible for the consistency of the constraint system. The space mechanism is powerful and versatile, but not fool proof.
This differs drastically from the CSS box model where top- and bottom-margins are a single value. Margins in CSS are self-contained and additive in most cases, and the margin merging algorithm is rudimentary — the widest margin is selected. XSL supports margin properties on before-edge and after-edge, too; they are converted into space-specifiers as
margin-{correspondent}="X":
.minimum = .optimum = .maximum = X
.conditionality="retain"
.precedence="force"
This
Stacking inline-areas inside a line-area is
Controls how much space is left before the start-edge of the inline-area; it is set by the space-start compound property;
Controls how much space is left after the end-edge of the inline-area; it is set by the space-end compound property.
space-start and space-end for inline-areas are calculated by the same rules as space-before and space-after for block-areas (accounting for possible change of direction). Any conditionality components control constraint suppression at the start or end of a line-area.
Alignment of inline-areas in the block-progression-direction is controlled by the baseline mechanism (see Chapter 6).
Stacking line-areas inside a block-area is less trivial. The distance between lines is controlled by the compound property line-height , with the same components as other space specifiers. Its interpretation depends on the algorithm selected for line-stacking. There are three algorithms available, switched by the line-stacking-strategy property on the parent block-area: font-height , max-height , and line-height .
To describe the placement of lines in terms of stacked areas, here are some terms. A line-area can be logically divided into three
The
allocation-rectangle
is the central part of the line-area around the baseline. It roughly corresponds to the content-rectangle, but doesn't
There are two spaces around the allocation-rectangle, which are both equal to half-leading . Half-leading spaces get .conditionality and .precedence attibutes from the line-height property and can be merged with other spaces and suppressed in the beginning or at the end of a reference area — just like other spaces. However, half-leading spaces never get merged with their likes.
Line-areas are stacked one after another inside a block-area, leaving no other space before or after them but the half-leadings from both sides. The gap between allocation rectangles of two adjacent line-areas consists of the half-leading-after of the first line plus the half-leading-before of the second line. (Note that these two spaces aren't merged.)
The available line-stacking strategies are line-height , font-height , and max-height .
Note that for lines built entirely of text using a nominal font, all three line-stacking strategies give the same value of base line separation (equal to the
line-height
). They
Allocation-rectangle for line-stacking-strategy="font-height" depends solely on the nominal font for the block. Its before-edge is offset by text-altitude from the baseline, and its after-edge is placed at text-depth from the baseline in the block-progression-direction . In the XSL spec, this is called nominal-requested-line-rectangle, and is likely to be the most common one used.
Half-leading is half the difference between the line-height chosen and the height of the allocation rectangle:
half-leading = (line-height - text-altitude - text-depth) / 2
In other words, this strategy stacks lines as if the whole block consisted entirely of plain text, with
text-altitude
and
text-depth
remaining constant across the block. Eventual inclusions of other font glyphs or large images don't influence the distance between the baselines. Using
font-height
will
max-height
is a more complicated strategy. The half-leading is calculated by the same formula as with
font-height
and remains constant for all lines in the block. The allocation-rectangle, instead, is calculated in a more complex way: its dimension in the
block-progression-dimension
is determined by the elements whose before-edge and after-edge are the most
One more trait influences line placement with this strategy: line-height-shift-adjustment . It controls processing of areas whose baselines are shifted from the common baseline of the line-area. There can be two values for this trait:
When calculating the allocation-rectangle, baseline shift elements are taken into account. For instance, if a line contains a subscript, the next line will be placed lower because the allocation-rectangle will extend to reach the after-edge of the subscript glyph.
Baseline shifts are disregarded. If a line contains subscripts or superscripts, its placement is not influenced by their presence.
The last strategy is line-height . This way of stacking lines comes from CSS and differs greatly from the preceding two. In this method, the line-height trait is considered a property of each inline-area, rather than the whole block-area, and may vary across the block: it is even possible that areas within the same line have different values of line-height . There is no common half-leading defined for the block; instead, every single area gets margins before and after it, depending on the local value of the line-height trait. The allocation-rectangle of the line-area is defined to be the least rectangle to include both the nominal-requested-line-rectangle and all inline-areas with their margins. Allocation-rectangles defined this way are stacked one after another, with no additional gap between them.
The algorithm to determine margins on inline-areas is similar to the method of calculating line separation in the max-height strategy:
For inline images and reference-areas (containers), margins are set by normal space-specifiers that are treated as CSS margins: conditionality and precedence are neglected, and no merging ever occurs.
For other types of areas, the half-leading is calculated as a function of line-height , text-altitude , and text-depth . The formula is the same as earlier for font-height and max-height , but trait values are taken from the inline-area. This half-leading is added before and after the content-rectangle (determined by text-altitude / text-depth traits). Note that the block-progression-dimension of the resulting margin-rectangle will be always equal to the line-height .
Within this strategy, line-height-shift-adjustment is also applicable and has the same meaning: when line-height-shift-adjustment="disregard-shifts" is set, all calculations are performed as if all inline-areas were aligned along the same baseline. This permits exclusion of inter-line gap widening due to subscripts and superscripts.
font-height neglects big elements — line separation is always fixed. This strategy gives the tightest line placement. Other strategies can only increase inter-line spaces.
max-height
line-height permits variation of the treatment of outstanding elements on a case-by-case basis.
[1] Setting all components of a compound property by omitting the component specification is termed a short form .
[2] In fact, margins are tolerated in XSL as a secondary mechanism to ensure CSS2 compatibility, but they don't integrate well with XSL layout model based on constrained spaces. We strongly
advise using indents instead of margins.[3] This is expected to change significantly in the next version of the XSL spec.
|
|
CONTENTS |
|