Hi all, Caution, long post ;-)
I’ve been thinking about the handling of keeps and breaks in tables for a while, and it seems to me that improvements could be done in that whole area. I’ll use break-before as an example but what I’ll be saying applies to break-after and keeps as well. Currently, for every object to which the break-before property applies, the value of the property is checked at the beginning of the getNextKnuthElements method and, if it corresponds to a hard-break, a Knuth penalty with -inf value is produced. This has several shortcomings: - this leads to much code duplication; - if, say, an fo:block has another fo:block as a child and both have break-before, two penalties will be generated instead of one (although I’m suddenly not so sure of that, more later) - this makes it difficult to improve breaks to distinguish columns from pages, and keeps to take integer values into account. In fact, we don’t so much care about the penalty element itself as whether there /should/ be a break between elements or not. I mean, the LMs which actually care are those which concatenate the elements: fo:flow, fo:block, fo:block-container, fo:table-cell, etc. And they all do it the same way: iterate over the children LMs for each LM: if there is a following LM, then: if the current LM has break-after or the following LM has break-before, then generate a Knuth forced break So the main question is to know whether there is a break before a LM. And here that’s quite simple, there are only a few shared behaviours: indeed there is a break-before on an element if: - the break-before property is set on the element itself or the first of its children: fo:block, fo:block-container, fo:list-block - it is set on the element itself or any of its children: fo:table-row, fo:list-item - it is set on the first of its children: fo:table-caption, fo:table-cell, fo:list-item-body, fo:list-item-label, fo:wrapper - special: - fo:table: on itself or first table-body - fo:table-body: on the first fo:table-row child if any; otherwise on any of fo:table-cell children making the first row - not applicable: fo:table-column, fo:table-header/footer, fo:float, fo:footnote-body So there would just be a couple of methods to write, for each behaviour. We could (for the moment) define a dedicated class with static methods, which would be called by each LM (some methods are imaginary, but you get the idea): public class KeepsAndBreaksHandler { public static boolean breakBeforeSelfOrFirstChild(LM lm) { return lm.getProperty(break-before).isForced() || lm.getFirstChild().getProperty(break-before).isForced(); } public static boolean breakBeforeSelfOrAnyChild(LM lm) { ... } public static boolean breakBeforeFirstChild(LM lm) { ... } ... } The method mustKeepWithPrevious would become: - for BlockLM: return KeepsAndBreaksHandler.breakBeforeSelfOrFirstChild(this) - for TableCellLM: return KeepsAndBreaksHandler.breakBeforeFirstChild(this) etc. The static method wouldn’t even need to be made synchronized, as the class is stateless. Later on we could replace this class with a Singleton object that would be passed to the LMs at their creation, and we would have some kind of “KeepsAndBreaksHandlerFactory”. But for now a simple class with static methods would be sufficient. We could also put in this class the code for producing the penalties elements: KnuthElement getElementForBreakBetween(LM firstLM, LM secondLM) { if (firstLM.hasBreakAfter() || secondLM().hasBreakBefore()) { return new KnuthPenalty(...); } else if (firstLM.mustKeepWithNext() || secondLM.mustKeepWithPrevious()) { return new KnuthForbiddenBreak() } } or something like that. The benefit would be that the whole handling of breaks can be found in just one place, instead of being spread among all the LMs. This is easier to correct bugs; this is easier to implement new features (column vs page break, integer keeps...); this simplifies the getNextKnuthElements methods of the LMs. And so on... WDYT? Vincent