diff --git a/src/org/daisy/dotify/formatter/impl/core/FormatterCoreImpl.java b/src/org/daisy/dotify/formatter/impl/core/FormatterCoreImpl.java index 17b7a9b0..d769e516 100644 --- a/src/org/daisy/dotify/formatter/impl/core/FormatterCoreImpl.java +++ b/src/org/daisy/dotify/formatter/impl/core/FormatterCoreImpl.java @@ -71,8 +71,7 @@ public class FormatterCoreImpl extends Stack implements FormatterCore, Bl private final boolean discardIdentifiers; private Table table; protected final FormatterCoreContext fc; - //The code where this variable is used is not very nice, but it will do to get the feature running - private Boolean endStart = null; + // TODO: fix recursive keep problem // TODO: Implement floating elements public FormatterCoreImpl(FormatterCoreContext fc) { @@ -105,10 +104,6 @@ public void startBlock(BlockProperties p, String blockId) { if (table!=null) { throw new IllegalStateException("A table is open."); } - if (endStart!=null && endStart == true) { - getCurrentBlock().setAvoidVolumeBreakAfterPriority(getCurrentVolumeKeepPriority()); - } - endStart = null; String lb = ""; String rb = ""; if (p.getTextBorderStyle()!=null) { @@ -134,6 +129,9 @@ public void startBlock(BlockProperties p, String blockId) { margins(new BlockMargin(new Margin(Type.LEFT, leftMarginComps), new Margin(Type.RIGHT, rightMarginComps), fc.getSpaceCharacter())). outerSpaceBefore(p.getMargin().getTopSpacing()). underlineStyle(p.getUnderlineStyle()); + // We don't get the volume keep priority from block properties, because it could have been inherited from an ancestor + AncestorContext ac = new AncestorContext(p, inheritVolumeKeepPriority(p.getVolumeKeepPriority())); + setPrecedingVolumeKeepAfterPriority(ac.getVolumeKeepPriority()); Block c = newBlock(blockId, rdp.build()); if (propsContext.size()>0 && propsContext.peek().getBlockProperties().getListType()!=FormattingTypes.ListStyle.NONE) { String listLabel = p.getListItemLabel(); @@ -176,10 +174,8 @@ public void startBlock(BlockProperties p, String blockId) { } c.setKeepWithNextSheets(p.getKeepWithNextSheets()); c.setVerticalPosition(p.getVerticalPosition()); - AncestorContext ac = new AncestorContext(p, inheritVolumeKeepPriority(p.getVolumeKeepPriority())); - // We don't get the volume keep priority from block properties, because it could have been inherited from an ancestor c.setAvoidVolumeBreakInsidePriority(ac.getVolumeKeepPriority()); - c.setAvoidVolumeBreakAfterPriority(ac.getVolumeKeepPriority()); + c.setAvoidVolumeBreakAfterPriority(-1); // value will be overwritten later propsContext.push(ac); Block bi = getCurrentBlock(); RowDataProperties.Builder builder = new RowDataProperties.Builder(bi.getRowDataProperties()); @@ -202,8 +198,24 @@ private Integer getCurrentVolumeKeepPriority() { return propsContext.isEmpty()?null:propsContext.peek().getVolumeKeepPriority(); } - private Integer getParentVolumeKeepPriority() { - return propsContext.size()<2?null:propsContext.get(propsContext.size()-2).getVolumeKeepPriority(); + // Set volume-break-after priority of the preceding block to volume-break-inside of the current + // block if the new value is higher (lower priority). If the preceding block is empty, do the + // same with the block before it, etc. This is done because it's the RowGroup objects that will + // carry the priority information to the PageSequenceBuilder/SheetDataSource, and the priority + // information of empty blocks will be ignored. + private void setPrecedingVolumeKeepAfterPriority(Integer currentPriority) { + int i = size(); + while (i > 0) { + Block b = get(i - 1); + Integer pr = b.getAvoidVolumeBreakAfterPriority(); + if (currentPriority == null || (pr != null && currentPriority > pr)) { + b.setAvoidVolumeBreakAfterPriority(currentPriority); + } + if (!b.isEmpty()) { + break; + } + i--; + } } @Override @@ -214,11 +226,6 @@ public void endBlock() { if (listItem!=null) { addChars("", new TextProperties.Builder(null).build()); } - if (endStart == null) { - endStart = true; - } else { - endStart = false; - } { AncestorContext ac = propsContext.pop(); BlockProperties p = ac.getBlockProperties(); @@ -234,21 +241,10 @@ public void endBlock() { outerSpaceAfter(bi.getRowDataProperties().getOuterSpaceAfter()+p.getMargin().getBottomSpacing()); bi.setKeepWithPreviousSheets(p.getKeepWithPreviousSheets()); bi.setRowDataProperties(builder.build()); - //set the volume keep after for the closing block to the parent priority - bi.setAvoidVolumeBreakAfterPriority(getCurrentVolumeKeepPriority()); - if (bi.isEmpty()) { - // if this group doesn't have data, then - // apply this blocks volume break after priority to the previous block - // if that block's break after priority is equal to this block's break - // inside priority. - Block preceding = size()>1?get(size()-2):null; - if (preceding!=null && preceding.getAvoidVolumeBreakAfterPriority()==bi.getAvoidVolumeBreakInsidePriority()) { - preceding.setAvoidVolumeBreakAfterPriority(bi.getAvoidVolumeBreakAfterPriority()); - } - } } leftMarginComps.pop(); rightMarginComps.pop(); + setPrecedingVolumeKeepAfterPriority(getCurrentVolumeKeepPriority()); if (propsContext.size()>0) { AncestorContext ac = propsContext.peek(); BlockProperties p = ac.getBlockProperties(); @@ -275,7 +271,7 @@ public void endBlock() { c.setKeepWithNext(next); // We don't get the volume keep priority from the BlockProperties, as it could have been inherited from an ancestor c.setAvoidVolumeBreakInsidePriority(getCurrentVolumeKeepPriority()); - c.setAvoidVolumeBreakAfterPriority(getParentVolumeKeepPriority()); + c.setAvoidVolumeBreakAfterPriority(-1); // value will be overwritten later } //firstRow = true; } diff --git a/test/org/daisy/dotify/formatter/impl/core/FormatterCoreImplTest.java b/test/org/daisy/dotify/formatter/impl/core/FormatterCoreImplTest.java index 67fac26a..57303e52 100644 --- a/test/org/daisy/dotify/formatter/impl/core/FormatterCoreImplTest.java +++ b/test/org/daisy/dotify/formatter/impl/core/FormatterCoreImplTest.java @@ -65,30 +65,46 @@ public void testBlockPropertiesHierarchy() { assertEquals(expectedOuter, formatter.get(2).getRowDataProperties()); } + // The following tests are to verify that: + // + // 1) Blocks inherit their volume-break-inside value from their parent + // + // 2) Blocks get their volume-break-after value from the following block with the + // volume-break-inside value and that does not come after the first following non-empty + // block. If there is no following non-empty block, the block's volume-break-after value will + // be null. + // + // Note that if an volume-break-inside is absent (null) it is as if the block has a high value. + @Test public void testVolumeKeepProperties_01() { //Setup FormatterCoreImpl formatter = new FormatterCoreImpl(context); - formatter.startBlock(new BlockProperties.Builder().volumeKeepPriority(1).build()); - formatter.startBlock(new BlockProperties.Builder().volumeKeepPriority(2).build()); - formatter.startBlock(new BlockProperties.Builder().volumeKeepPriority(3).build()); - formatter.addChars(" ", UND_TEXT_PROPERTIES); // adds a segment to this block to remain in sync with previous test result - formatter.endBlock(); - formatter.addChars(" ", UND_TEXT_PROPERTIES); // adds a segment to this block to remain in sync with previous test result - formatter.endBlock(); - formatter.addChars(" ", UND_TEXT_PROPERTIES); // adds a segment to this block to remain in sync with previous test result - formatter.endBlock(); + formatter.startBlock(new BlockProperties.Builder().volumeKeepPriority(1).build()); // start block 1 + formatter.startBlock(new BlockProperties.Builder().volumeKeepPriority(2).build()); // start block 2 + formatter.startBlock(new BlockProperties.Builder().volumeKeepPriority(3).build()); // start block 3 + formatter.addChars(" ", UND_TEXT_PROPERTIES); + formatter.endBlock(); // end block 3 + formatter.addChars(" ", UND_TEXT_PROPERTIES); + formatter.endBlock(); // end block 2 + formatter.addChars(" ", UND_TEXT_PROPERTIES); + formatter.endBlock(); // end block 1 //Test assertEquals(5, formatter.size()); + // in block 1 before block 2: empty assertEquals(1, (int)formatter.get(0).getAvoidVolumeBreakInsidePriority()); - assertEquals(1, (int)formatter.get(0).getAvoidVolumeBreakAfterPriority()); + assertEquals(3, (int)formatter.get(0).getAvoidVolumeBreakAfterPriority()); + // in block 2 before block 3: empty assertEquals(2, (int)formatter.get(1).getAvoidVolumeBreakInsidePriority()); - assertEquals(2, (int)formatter.get(1).getAvoidVolumeBreakAfterPriority()); + assertEquals(3, (int)formatter.get(1).getAvoidVolumeBreakAfterPriority()); + // in block 3 assertEquals(3, (int)formatter.get(2).getAvoidVolumeBreakInsidePriority()); assertEquals(2, (int)formatter.get(2).getAvoidVolumeBreakAfterPriority()); + // in block 2 after block 3 assertEquals(2, (int)formatter.get(3).getAvoidVolumeBreakInsidePriority()); assertEquals(1, (int)formatter.get(3).getAvoidVolumeBreakAfterPriority()); + // in block 1 after block 2 assertEquals(1, (int)formatter.get(4).getAvoidVolumeBreakInsidePriority()); assertEquals(null, formatter.get(4).getAvoidVolumeBreakAfterPriority()); } @@ -97,19 +113,22 @@ public void testVolumeKeepProperties_01() { public void testVolumeKeepProperties_02() { //Setup FormatterCoreImpl formatter = new FormatterCoreImpl(context); - formatter.startBlock(new BlockProperties.Builder().volumeKeepPriority(1).build()); - formatter.endBlock(); - formatter.startBlock(new BlockProperties.Builder().volumeKeepPriority(2).build()); - formatter.endBlock(); - formatter.startBlock(new BlockProperties.Builder().volumeKeepPriority(3).build()); - formatter.endBlock(); + formatter.startBlock(new BlockProperties.Builder().volumeKeepPriority(1).build()); // start block 1 + formatter.endBlock(); // end block 1 + formatter.startBlock(new BlockProperties.Builder().volumeKeepPriority(2).build()); // start block 2 + formatter.endBlock(); // end block 2 + formatter.startBlock(new BlockProperties.Builder().volumeKeepPriority(3).build()); // start block 3 + formatter.endBlock(); // end block 3 //Test assertEquals(3, formatter.size()); + // in block 1: empty assertEquals(1, (int)formatter.get(0).getAvoidVolumeBreakInsidePriority()); assertEquals(null, formatter.get(0).getAvoidVolumeBreakAfterPriority()); + // in block 2: empty assertEquals(2, (int)formatter.get(1).getAvoidVolumeBreakInsidePriority()); assertEquals(null, formatter.get(1).getAvoidVolumeBreakAfterPriority()); + // in block 3: empty assertEquals(3, (int)formatter.get(2).getAvoidVolumeBreakInsidePriority()); assertEquals(null, formatter.get(2).getAvoidVolumeBreakAfterPriority()); @@ -119,31 +138,38 @@ public void testVolumeKeepProperties_02() { public void testVolumeKeepProperties_03() { //Setup FormatterCoreImpl formatter = new FormatterCoreImpl(context); - formatter.startBlock(new BlockProperties.Builder().volumeKeepPriority(1).build()); - formatter.startBlock(new BlockProperties.Builder().volumeKeepPriority(2).build()); - formatter.startBlock(new BlockProperties.Builder().build()); - formatter.endBlock(); - formatter.startBlock(new BlockProperties.Builder().build()); - formatter.endBlock(); - formatter.addChars(" ", UND_TEXT_PROPERTIES); // adds a segment to this block to remain in sync with previous test result - formatter.endBlock(); - formatter.addChars(" ", UND_TEXT_PROPERTIES); // adds a segment to this block to remain in sync with previous test result - formatter.endBlock(); + formatter.startBlock(new BlockProperties.Builder().volumeKeepPriority(1).build()); // start block 1 + formatter.startBlock(new BlockProperties.Builder().volumeKeepPriority(2).build()); // start block 2 + formatter.startBlock(new BlockProperties.Builder().build()); // start block 3 + formatter.endBlock(); // end block 3 + formatter.startBlock(new BlockProperties.Builder().build()); // start block 4 + formatter.endBlock(); // end block 4 + formatter.addChars(" ", UND_TEXT_PROPERTIES); + formatter.endBlock(); // end block 2 + formatter.addChars(" ", UND_TEXT_PROPERTIES); + formatter.endBlock(); // end block 1 //Test assertEquals(7, formatter.size()); + // in block 1 before block 2: empty assertEquals(1, (int)formatter.get(0).getAvoidVolumeBreakInsidePriority()); - assertEquals(1, (int)formatter.get(0).getAvoidVolumeBreakAfterPriority()); + assertEquals(2, (int)formatter.get(0).getAvoidVolumeBreakAfterPriority()); + // in block 2 before block 3: empty assertEquals(2, (int)formatter.get(1).getAvoidVolumeBreakInsidePriority()); assertEquals(2, (int)formatter.get(1).getAvoidVolumeBreakAfterPriority()); + // in block 3: empty assertEquals(2, (int)formatter.get(2).getAvoidVolumeBreakInsidePriority()); assertEquals(2, (int)formatter.get(2).getAvoidVolumeBreakAfterPriority()); + // in block 2 between block 3 and 4: empty assertEquals(2, (int)formatter.get(3).getAvoidVolumeBreakInsidePriority()); assertEquals(2, (int)formatter.get(3).getAvoidVolumeBreakAfterPriority()); + // in block 4: empty assertEquals(2, (int)formatter.get(4).getAvoidVolumeBreakInsidePriority()); assertEquals(2, (int)formatter.get(4).getAvoidVolumeBreakAfterPriority()); + // in block 2 after block 4 assertEquals(2, (int)formatter.get(5).getAvoidVolumeBreakInsidePriority()); assertEquals(1, (int)formatter.get(5).getAvoidVolumeBreakAfterPriority()); + // in block 1 after block 2 assertEquals(1, (int)formatter.get(6).getAvoidVolumeBreakInsidePriority()); assertEquals(null, formatter.get(6).getAvoidVolumeBreakAfterPriority()); } @@ -152,31 +178,36 @@ public void testVolumeKeepProperties_03() { public void testVolumeKeepProperties_04() { //Setup FormatterCoreImpl formatter = new FormatterCoreImpl(context); - formatter.startBlock(new BlockProperties.Builder().volumeKeepPriority(1).build()); - formatter.startBlock(new BlockProperties.Builder().volumeKeepPriority(2).build()); - formatter.startBlock(new BlockProperties.Builder().build()); - formatter.endBlock(); - formatter.startBlock(new BlockProperties.Builder().build()); - formatter.endBlock(); // this empty block will affect the volume keep priority - formatter.endBlock(); // this empty block will affect the volume keep priority - formatter.endBlock(); + formatter.startBlock(new BlockProperties.Builder().volumeKeepPriority(1).build()); // start block 1 + formatter.startBlock(new BlockProperties.Builder().volumeKeepPriority(2).build()); // start block 2 + formatter.startBlock(new BlockProperties.Builder().build()); // start block 3 + formatter.endBlock(); // end block 3 + formatter.startBlock(new BlockProperties.Builder().build()); // start block 4 + formatter.endBlock(); // end block 4 + formatter.endBlock(); // end block 2 + formatter.endBlock(); // end block 1 //Test assertEquals(7, formatter.size()); + // in block 1 before block 2: empty assertEquals(1, (int)formatter.get(0).getAvoidVolumeBreakInsidePriority()); - assertEquals(1, (int)formatter.get(0).getAvoidVolumeBreakAfterPriority()); + assertEquals(null, formatter.get(0).getAvoidVolumeBreakAfterPriority()); + // in block 2 before block 3: empty assertEquals(2, (int)formatter.get(1).getAvoidVolumeBreakInsidePriority()); - assertEquals(2, (int)formatter.get(1).getAvoidVolumeBreakAfterPriority()); + assertEquals(null, formatter.get(1).getAvoidVolumeBreakAfterPriority()); + // in block 3: empty assertEquals(2, (int)formatter.get(2).getAvoidVolumeBreakInsidePriority()); - assertEquals(2, (int)formatter.get(2).getAvoidVolumeBreakAfterPriority()); + assertEquals(null, formatter.get(2).getAvoidVolumeBreakAfterPriority()); + // in block 2 between block 3 and 4: empty assertEquals(2, (int)formatter.get(3).getAvoidVolumeBreakInsidePriority()); - assertEquals(2, (int)formatter.get(3).getAvoidVolumeBreakAfterPriority()); + assertEquals(null, formatter.get(3).getAvoidVolumeBreakAfterPriority()); + // in block 4: empty assertEquals(2, (int)formatter.get(4).getAvoidVolumeBreakInsidePriority()); - // this block gets its value from the following blocks break after priority, since it is empty - assertEquals(1, (int)formatter.get(4).getAvoidVolumeBreakAfterPriority()); + assertEquals(null, formatter.get(4).getAvoidVolumeBreakAfterPriority()); + // in block 2 after block 4: empty assertEquals(2, (int)formatter.get(5).getAvoidVolumeBreakInsidePriority()); - // this block gets its value from the following blocks break after priority, since it is empty assertEquals(null, formatter.get(5).getAvoidVolumeBreakAfterPriority()); + // in block 1 after block 2: empty assertEquals(1, (int)formatter.get(6).getAvoidVolumeBreakInsidePriority()); assertEquals(null, formatter.get(6).getAvoidVolumeBreakAfterPriority()); }