This page analyzes aspects around the space-resolution rules defined in 4.3.1 in the spec.
Please note that this page was not updated after the clarification about space traits by the XSL FO SG!!!
Legend:
- lh = line height
Spaces and Conditional Lengths - an Overview
Both spaces and conditional lengths (borders and padding) have conditional elements but they behave a little differently. For spaces the conditionality controls whether a space-specifier has effect at the beginning or end of a reference-area or a line-area (XSL 1.0, 4.3). For padding and borders the conditionality controls if the padding should be 0 or retained if its associated edge is a leading-edge in a reference-area for areas generated from this formatting object that have an is-first (or is-last) value of "false" (XSL 1.0, 7.7.9 and 7.7.31). The difference: While the conditionality applies to every space-specifier regardless of the is-first/is-last status of the generated area, it does matter for conditional lengths where the conditionality only applied if it's not the first or last generated area by a formatting object.
Like border and padding, space-before and space-after apply to every area generated by a formatting object, not just the first and the last (XSL 1.0, 7.10.5). This is further illustrated by the general area model described in XSL 1.0, 4.2.3.
General pattern for the Knuth layout system
In the most general situation, the elements needed to represent the space between two block-level formatting objects (say block 1 and block 2) are:
glue #1 - penalty - glue #2 - box - PENALTY - glue #3
with:
- glue #1 is the resolved space after block 1 if a break occurs
- glue #3 is the resolved space before block 2 if a break occurs
- penalty is a feasible break
- PENALTY forbids a break
- glue #2 is the difference between glue #1 + glue #3 and the resolved space between the blocks if there is no break
Note that glue #1 is a feasible break too, but it won't never be chosen as a page break as the following penalty is much better: it adds the stretch and shrink of glue #1 to the available stretch and shrink of the page, so the adjustment ratio will be smaller and the demerits fewer. (Note by JM: I observed that the glue often doesn't have any stretch and shrink and therefore becomes a likely break point. I'll insert an infinite penalty before glue #1 into the pattern.)
Particular situations
The sequence of elements can be simplified in some particular situations.
All spaces are conditional
If the resolved space after block 1 and the resolved space before block 2 are both conditional, one element is enough:
glue A
where glue A is the resolved space between block 1 and block 2 when there is no break.
If there is a keep condition between the block 1 and block 2, a penalty is needed in order to prevent a page break:
penalty glue A
where the penalty value can be +inf or a finite value.
The penalty could be added even if there is not a break condition between the blocks: this assures the presence of a feasible break, regardless of the previous element in the sequence.
Only the first space is conditional
If the resolved space after block 1 is conditional, i.e. it is discarded if the blocks are parted, the sequence can be simplified a little:
glue B - box - PENALTY - glue C
where:
- glue C is the resolved space before block 2 if a break occurs
- PENALTY forbids a break
- glue B is the difference between glue C and the resolved space between the blocks if there is no break
As in the previous situation, a penalty with a finite value can be added at the beginning of this sequence in order to assure the presence of a feasible break, or to represent a keep condition.
Only the second space is conditional
If the resolved space before block 2 is conditional, i.e. it is discarded if the blocks are parted, the sequence can be simplified much:
glue D - penalty - glue E
with:
- glue D is the resolved space after block 1 if a break occurs
- penalty is a feasible break
- glue E is the difference between glue D and the resolved space between the blocks if there is no break
In this case there is no need to have an initial penalty, as the "preferred" feasible break is the penalty element; should there be a keep condition, its value will be modified.
Examples
See the SpaceResolution Examples page.
Implementation
See also BreakPossibilityBuilding for more information about the interaction of space, borders and padding when building break possibilities with Knuth elements.
Ideas for the implementation can be found here: http://mail-archives.apache.org/mod_mbox/xmlgraphics-fop-dev/200509.mbox/%3cPine.LNX.4.62.0509091354320.29316@malatesta.cs.unibo.it%3e http://mail-archives.apache.org/mod_mbox/xmlgraphics-fop-dev/200509.mbox/%3c20050910195456.GC5644@oranjetip.leverkruid.nl%3e http://mail-archives.apache.org/mod_mbox/xmlgraphics-fop-dev/200509.mbox/%3c20050911101013.GA4716@oranjetip.leverkruid.nl%3e
It appears that border and padding handling needs to taken into consideration, too, as they can both have conditionality elements. Following Simon's suggestion for creating space objects that are placed into the returned element list, objects for border and padding have been created, too. A new ListElement class forms the new base class for KnuthElement and for the new UnresolvedListElement which is in turn the base class for a number of classes:
- BreakElement represents a break possibility and is very similar to KnuthPenalty but holds additional unresolved list elements which need to be added in the break condition. The SpaceResolver later transforms the BreakElement into a KnuthPenalty.
- BorderElement represents a (possibly conditional) border.
- PaddingElement represents (possibly conditional) padding.
- SpaceElement represents an unresolved space specifier
All these elements will be inspected by the SpaceResolver class where the space resolution and the handling of the conditional lengths is handled. The output is a modified element list with no unresolved elements. SpaceResolver.resolveElementList() is called to resolve all unresolved elements prior to the breaking process.
Two special Position descendants are used to hold the resolved space-specifier so the LM that will be generating the space will have all the necessary information in addAreas(). After all, a single LM does not know with which other space from other LMs his space interacted. SpaceHandlingBreakPosition is used to notify the involved layout managers about the resolved space specifiers and conditional lengths in a break situation. SpaceHandlingPosition does the same for the no-break situation. The layout managers are always informed through the ConditionalElementListener interface just before a part (page/flow/line) is painted (addAreas stage) by calling the SpaceResolver.performConditionalsNotification().
In the case where a penalty holds a special Position instance (like for tables) this Position has to be exchanged for the SpaceHandlingBreakPosition, but this class will hold the original Position object which will be restored by the parent LM's addAreas() method after all layout managers have been notified.
Remaining issues
Reminder: an empty block (<fo:block/>) will not split a space-sequence, but BlockStackingLayoutManager currently adds a box (w=0) to the element list so an "id" present on the empty block will still be registered by a later addAreas(). This will interfere with the correct detection of space-sequences. This will need to be looked at, eventually. (Case 3d under 4.2.5 Stacking constraints!) See: block_space-before_space-after_8.xml
Other issues:
- The line-height property does not yet have an influence on the space resolution.
- border and padding conditionality has not been implemented on table-cell, yet.
- Space resolution doesn't happen between individual footnotes, yet, only inside them.
Someone seems to have started space-start|-end support (see SpaceSpecifier and stuff in LayoutContext) long ago, before we introduced Knuth approach. We'll have to revisit that since this has currently no effect on the inline element list but only on the areas produced. That means that if you specify a space-start on an fo:inline you will get a space but line breaking will be off. I hope we'll be able to use the stuff I'm building for inline-level spaces, too.