Layout Managers are created from the top down. First the
page sequence creates a PageLM and a FlowLM. The PageLM will manage
finding the right page model (with help from the PageSequenceMaster)
and managing the balancing act between before-floats, footnotes and
the normal text flow. The FlowLM will
manage the normal content in the main flow. We can think of it as a
galley manager.
In general, each LM asks its child LMs to return sucessive
break possibilities. It passes some
information to the child in a flags object and it gets back
a break possibility which contains the size in
the stacking direction as well as information about such things as
anchors, break conditions and span conditions which can change the
reference area environment. This process continues down to the lowest
level of the layout manager hierarchy which corresponds to atomic
inline-level FOs such as characters or graphics.
Each layout manager will repeatedly call getNextBreakPoss on its current
child LM until the child returns a BP with the ISLAST
flag set. Then the layout manager moves on to its next child LM (ie,
it asks the next child FO to generate a layout manager.) Galley level
layout managers which are Line and Flow will return to their parent
layout managers either when they have finished their content or when
they encounter a a BP which will fill one of their areas.
The break possibilities are generated from the bottom up.
All inline content must first be broken into
lines which are then stacked into block areas. This is done by the
LineLayoutManager, which creates line areas.
The LineLM asks its child LM to generate a break possibility, which
represents a place where the line can end. This
initially means each potential line-end (primarily spaces or forced
linefeeds and a few other potential line-end characters such as hard
hyphens.) The text LM returns an object which stores the size in the
stacking direction as a MinOptMax triplet
and a cost, which is based on how well this break
would satisfy the constraints. The Text LM keeps track of its position in
the text content and returns the total size of the text area it would
create if it were to break at a given point. The returned BP
object also contains information about whether the break is forced
(linefeed) or whether this is the last area which can be generated by
the LM (ISLAST flag). If a textFO ends on a non-break character, the
ISLAST flag is set, but the CAN_BREAK_AFTER flag isn't, since we don't
know if there is any following text in another inline object for
example.
Variable size content is taken into account from
the bottom up. Each LM returns a range of sizes in the stacking
direction, based on property values. For text, this comes from
variable word-space values or letter-space values. For other inline
objects, it may include variable space-start and space-end values
(after calculation of the entire sequence of space specifiers at a
particular break possibility.)
The main constraint for laying out
lines is the available inline-progression-dimension (IPD) for the line
area to be created. This
depends on the IPD of the reference area ancestor, on the indents of the
containing fo:block, and on any side-floats which may be intruding on
this line.
For now, let's assume that only the LineLM knows about the IPD
available to it. Therefore only it can make a decision about which BP
is the best one; the lower level inline layout managers can only
return potential break points.
| There are certainly optimizations to this model which can be
examined later. |
So the Line LM will ask its child LM(s) for break possibilities until
it gets back a BP whose stacking dimension could fill the
line. This means that the BP.stackdim.max >= LineIPD.min. It can look
for further BP, perhaps one whose stackdim.opt is closer to the
LineIPD.opt. If it isn't happy with the choice of break possibilities,
it can go past the end of the line to the next one, and then try to
find a hyphenation point between the last one which fits and the first
one which doesn't. If no possibility is found whose min/max values
enclose the available IPD, some constraint will be violated (and
reported in the log.) The actual strategy is up to the Line LM and
should be able to be easily replaced without changing the architecture
(Strategy pattern).
The definition of a good break possibility depends on the
properties at the block and inline level which govern things such as
wrapping behavior and justification mode. For example, if lines are
not to be wrapped, only an explicit linefeed can serve as a BP. If
lines are wrapped but not justified then there is no requirement to
completely fill the IPD on each line, but a sophisticated layout
manager will try to achieve "aesthetic rag".
Note that no areas have actually been created yet. Once the LineLM
has found a potential break point for the inline content, it can
calculate the total size of the line area which would be created. The
size in the IPD is determined by the Line LM based on the chosen BP.
The size of the line area in the the block-progression-dimension
depends on the size of the text (or other inline content). These
values are set by the inline-level LM
in their returned BP (in terms of ascender and descender heights with
respect to the baseline). The LineLM adds spacing implied by the
current line-stacking strategy and line-height property values. It
stores a reference to the chosen inline BP and "wraps" that in its own
Position object which it stores in the BP it returns to its parent LM
(the block layout manager).
The block LM now has a potential break position after its
first line. It assigns that possibility a cost, based on widow, orphan
and keep properties. It can also calculate the total size of the block
area it would create, were it to end the area after this line. It does
this by adding any padding and border (taking into account
conditionality). It also calculates space-before and space-after
values, or contributes to building up a sequence of such values.
With this information, the block LM creates a new BP (or
updates the existing one). It stores a Position object in this
BP which wraps the returned BP from its child Line LM.
It returns the new BP to its parent and so on, back up to the
FlowLM.
Obviously there is more complicated logic involved when dealing
with lists and tables. These cases need to be walked through in detail.
The FlowLM sees if the returned stacking dimension will still
fit in its available block-progression-dimension (BPD). It repeatedly calls
getNextBreakPoss on its
child LMs until it reaches the maximum BPD for the flow reference area
or until there is no more content to lay out. If one child LM is
finished, it moves on to the next until the last child LM has returned
a BP with the ISLAST flag set. If any child LM returns a
BP with a FORCE_BREAK_BEFORE or SPAN flag set, the FlowLM will
force layout of any pending break possibilities and return to its
parent (the PageLM) in order to handle the break or span condition.
If the returned BP has any new before-float or footnote anchors in
it (ANCHOR flag in the
BP), the FlowLM will also return to the PageLM. The PageLM must then
try to find space to place the floats, possibly asking the FlowLM for
help if the body contains multiple columns.