From 2466f253400d1ec0f7c4bc6f68a8dab87a3ab87e Mon Sep 17 00:00:00 2001 From: Jeremias Maerki Date: Mon, 11 Sep 2006 18:24:35 +0000 Subject: [PATCH] Implemented limited support for extension properties through new methods on ElementMapping (backwards-compatible). Moved block-progression-unit into the fox: namespace. Implemented new extension properties: fox:orphan-content-limit and fox:widow-content-limit for fo:table and fo:list-block. See documentation for details. Bugfix in ElementListUtils.removeLegalBreaks (concerning box/glue combinations). Added a unit test to cover the problem. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@442282 13f79535-47bb-0310-9956-ffa450edef68 --- .../content/xdocs/trunk/extensions.xml | 22 ++- src/java/org/apache/fop/fo/Constants.java | 8 +- .../org/apache/fop/fo/ElementMapping.java | 16 ++ .../apache/fop/fo/ElementMappingRegistry.java | 10 ++ .../org/apache/fop/fo/FOElementMapping.java | 12 ++ .../org/apache/fop/fo/FOPropertyMapping.java | 22 ++- src/java/org/apache/fop/fo/FObj.java | 19 +-- src/java/org/apache/fop/fo/PropertyList.java | 15 +- .../extensions/ExtensionElementMapping.java | 25 +++ .../fo/extensions/svg/SVGElementMapping.java | 5 + .../org/apache/fop/fo/flow/ListBlock.java | 26 +++- src/java/org/apache/fop/fo/flow/Table.java | 26 +++- .../fop/layoutmgr/ElementListUtils.java | 94 +++++++++-- .../list/ListBlockLayoutManager.java | 26 ++-- .../table/TableContentLayoutManager.java | 14 +- status.xml | 4 + .../fop/util/ElementListUtilsTestCase.java | 147 ++++++++++++++++++ .../list-block_fox_orphan-content-limit_1.xml | 123 +++++++++++++++ .../list-block_fox_widow-content-limit_1.xml | 123 +++++++++++++++ .../table_fox_orphan-content-limit_1.xml | 133 ++++++++++++++++ .../table_fox_widow-content-limit_1.xml | 133 ++++++++++++++++ .../table_fox_widow-content-limit_2.xml | 97 ++++++++++++ 22 files changed, 1035 insertions(+), 65 deletions(-) create mode 100644 test/java/org/apache/fop/util/ElementListUtilsTestCase.java create mode 100644 test/layoutengine/standard-testcases/list-block_fox_orphan-content-limit_1.xml create mode 100644 test/layoutengine/standard-testcases/list-block_fox_widow-content-limit_1.xml create mode 100644 test/layoutengine/standard-testcases/table_fox_orphan-content-limit_1.xml create mode 100644 test/layoutengine/standard-testcases/table_fox_widow-content-limit_1.xml create mode 100644 test/layoutengine/standard-testcases/table_fox_widow-content-limit_2.xml diff --git a/src/documentation/content/xdocs/trunk/extensions.xml b/src/documentation/content/xdocs/trunk/extensions.xml index e2d19eca8..0c22ca123 100644 --- a/src/documentation/content/xdocs/trunk/extensions.xml +++ b/src/documentation/content/xdocs/trunk/extensions.xml @@ -51,8 +51,11 @@ http://xml.apache.org/fop/extensions to the root element:

]]> - Currently, no extensions are implemented in FOP Trunk which use the FOP extension namespace. + xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">]]> + + Currently, no extension elements are implemented in FOP Trunk which use the + FOP extension namespace. +
PDF Bookmarks @@ -101,6 +104,21 @@ to following pages. Here is an example of FO code creating such a table-header:< ]]>
+
+ fox:orphan-content-limit and fox:widow-content-limit +

+ The two proprietary extension properties, fox:orphan-content-limit and + fox:widow-content-limit, are used to improve the layout of list-blocks and tables. + If you have a table with many entries, you don't want a single row to be left over + on a page. You will want to make sure that at least two or three lines are kept + together. The properties take an absolute length which specifies the area at the + beginning (fox:widow-content-limit) or at the end (fox:orphan-content-limit) of a + table or list-block. The properties are inherited and only have an effect on fo:table + and fo:list-block. An example: fox:widow-content-limit="3 * 1.2em" would make sure + the you'll have at least three lines (assuming line-height="1.2") together on a table + or list-block. +

+
diff --git a/src/java/org/apache/fop/fo/Constants.java b/src/java/org/apache/fop/fo/Constants.java index 0e91b4efd..92cbe54da 100644 --- a/src/java/org/apache/fop/fo/Constants.java +++ b/src/java/org/apache/fop/fo/Constants.java @@ -682,10 +682,14 @@ public interface Constants { int PR_INDEX_CLASS = 249; /** Property constant - XSL 1.1 */ int PR_INDEX_KEY = 250; - /** Property constant - Custom extension */ + /** Property constant - FOP proprietary: Custom extension for line alignment */ int PR_X_BLOCK_PROGRESSION_UNIT = 251; + /** Property constant - FOP proprietary: limit for widow content in lists and tables */ + int PR_X_WIDOW_CONTENT_LIMIT = 252; + /** Property constant - FOP proprietary: limit for orphan content in lists and tables */ + int PR_X_ORPHAN_CONTENT_LIMIT = 253; /** Number of property constants defined */ - int PROPERTY_COUNT = 251; + int PROPERTY_COUNT = 253; // compound property constants diff --git a/src/java/org/apache/fop/fo/ElementMapping.java b/src/java/org/apache/fop/fo/ElementMapping.java index 06ed58201..0f436ae28 100644 --- a/src/java/org/apache/fop/fo/ElementMapping.java +++ b/src/java/org/apache/fop/fo/ElementMapping.java @@ -24,6 +24,7 @@ import java.util.HashMap; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import org.apache.fop.util.QName; import org.w3c.dom.DOMImplementation; /** @@ -88,6 +89,21 @@ public abstract class ElementMapping { "Cannot return default DOM implementation: " + e.getMessage()); } } + + /** @return the standard namespace prefix for this namespace or null if it is not known. */ + public String getStandardPrefix() { + return null; + } + + /** + * Indicates whether a particular attribute of the namespace is a property, i.e. the attribute + * value should be converted to a property value. + * @param attributeName the attribute name + * @return true if the attribute should be converted to a property + */ + public boolean isAttributeProperty(QName attributeName) { + return false; + } /** * Initializes the set of maker objects associated with this ElementMapping diff --git a/src/java/org/apache/fop/fo/ElementMappingRegistry.java b/src/java/org/apache/fop/fo/ElementMappingRegistry.java index 5cfa50ccb..105a956a2 100644 --- a/src/java/org/apache/fop/fo/ElementMappingRegistry.java +++ b/src/java/org/apache/fop/fo/ElementMappingRegistry.java @@ -168,6 +168,16 @@ public class ElementMappingRegistry { } } + /** + * Returns an ElementMapping class for a namespace URI if there is one. + * @param namespaceURI the namespace URI + * @return the requested ElementMapping or null, if no ElementMapping for the namespace is + * available. + */ + public ElementMapping getElementMapping(String namespaceURI) { + return (ElementMapping)this.namespaces.get(namespaceURI); + } + /** * Indicates whether a namespace is known to FOP. * @param namespaceURI the namespace URI diff --git a/src/java/org/apache/fop/fo/FOElementMapping.java b/src/java/org/apache/fop/fo/FOElementMapping.java index 015ff6f69..c3b99f6e4 100644 --- a/src/java/org/apache/fop/fo/FOElementMapping.java +++ b/src/java/org/apache/fop/fo/FOElementMapping.java @@ -22,6 +22,8 @@ package org.apache.fop.fo; // Java import java.util.HashMap; +import org.apache.fop.util.QName; + /** * Element mapping class for all XSL-FO elements. */ @@ -136,6 +138,16 @@ public class FOElementMapping extends ElementMapping { } } + /** @see org.apache.fop.fo.ElementMapping#getStandardPrefix() */ + public String getStandardPrefix() { + return "fo"; + } + + /** @see org.apache.fop.fo.ElementMapping#isAttributeProperty(org.apache.fop.util.QName) */ + public boolean isAttributeProperty(QName attributeName) { + return true; //All XSL-FO attributes are to be converted to properties. + } + static class RootMaker extends ElementMapping.Maker { public FONode make(FONode parent) { return new org.apache.fop.fo.pagination.Root(parent); diff --git a/src/java/org/apache/fop/fo/FOPropertyMapping.java b/src/java/org/apache/fop/fo/FOPropertyMapping.java index 8fce87083..2cb2da653 100644 --- a/src/java/org/apache/fop/fo/FOPropertyMapping.java +++ b/src/java/org/apache/fop/fo/FOPropertyMapping.java @@ -1456,11 +1456,11 @@ public final class FOPropertyMapping implements Constants { l.setDefault("auto"); addPropertyMaker("width", l); -/*LF*/ // block-progression-unit (**CUSTOM EXTENSION**) -/*LF*/ l = new LengthProperty.Maker(PR_X_BLOCK_PROGRESSION_UNIT); -/*LF*/ l.setInherited(false); -/*LF*/ l.setDefault("0pt"); -/*LF*/ addPropertyMaker("block-progression-unit", l); + // fox:block-progression-unit (**CUSTOM EXTENSION**) + l = new LengthProperty.Maker(PR_X_BLOCK_PROGRESSION_UNIT); + l.setInherited(false); + l.setDefault("0pt"); + addPropertyMaker("fox:block-progression-unit", l); } private void createBlockAndLineProperties() { @@ -1828,6 +1828,18 @@ public final class FOPropertyMapping implements Constants { m.setInherited(true); m.setDefault("2"); addPropertyMaker("widows", m); + + // fox:widow-content-limit + m = new LengthProperty.Maker(PR_X_WIDOW_CONTENT_LIMIT); + m.setInherited(true); + m.setDefault("0pt"); + addPropertyMaker("fox:widow-content-limit", m); + + // fox:orphan-content-limit + m = new LengthProperty.Maker(PR_X_ORPHAN_CONTENT_LIMIT); + m.setInherited(true); + m.setDefault("0pt"); + addPropertyMaker("fox:orphan-content-limit", m); } private void createLayoutProperties() { diff --git a/src/java/org/apache/fop/fo/FObj.java b/src/java/org/apache/fop/fo/FObj.java index 69eb2f38b..0d5b1fe65 100644 --- a/src/java/org/apache/fop/fo/FObj.java +++ b/src/java/org/apache/fop/fo/FObj.java @@ -513,29 +513,20 @@ public abstract class FObj extends FONode implements Constants { /** * Adds a foreign attribute to this FObj. - * @param uri the namespace URI - * @param qName the fully qualified name + * @param attributeName the attribute name as a QName instance * @param value the attribute value */ - public void addForeignAttribute(String uri, - String qName, String value) { + public void addForeignAttribute(QName attributeName, String value) { /* TODO: Handle this over FOP's property mechanism so we can use * inheritance. */ - if (qName == null) { - throw new NullPointerException("Parameter qName must not be null"); + if (attributeName == null) { + throw new NullPointerException("Parameter attributeName must not be null"); } if (foreignAttributes == null) { foreignAttributes = new java.util.HashMap(); } - String localName = qName; - String prefix = null; - int p = localName.indexOf(':'); - if (p > 0) { - prefix = localName.substring(0, p); - localName = localName.substring(p + 1); - } - foreignAttributes.put(new QName(uri, prefix, localName), value); + foreignAttributes.put(attributeName, value); } /** @return the map of foreign attributes */ diff --git a/src/java/org/apache/fop/fo/PropertyList.java b/src/java/org/apache/fop/fo/PropertyList.java index 0600601c7..8b7c67abd 100644 --- a/src/java/org/apache/fop/fo/PropertyList.java +++ b/src/java/org/apache/fop/fo/PropertyList.java @@ -39,6 +39,7 @@ import org.apache.fop.fo.properties.CommonRelativePosition; import org.apache.fop.fo.properties.CommonTextDecoration; import org.apache.fop.fo.properties.Property; import org.apache.fop.fo.properties.PropertyMaker; +import org.apache.fop.util.QName; /** * Class containing the collection of properties for a given FObj. @@ -311,8 +312,18 @@ public abstract class PropertyList { if (attributeNS == null || attributeNS.length() == 0) { convertAttributeToProperty(attributes, attributeName, attributeValue); } else if (!factory.isNamespaceIgnored(attributeNS)) { - if (factory.getElementMappingRegistry().isKnownNamespace(attributeNS)) { - getFObj().addForeignAttribute(attributeNS, attributeName, attributeValue); + ElementMapping mapping = factory.getElementMappingRegistry().getElementMapping( + attributeNS); + if (mapping != null) { + QName attName = new QName(attributeNS, attributeName); + if (mapping.isAttributeProperty(attName) + && mapping.getStandardPrefix() != null) { + convertAttributeToProperty(attributes, + mapping.getStandardPrefix() + ":" + attName.getLocalName(), + attributeValue); + } else { + getFObj().addForeignAttribute(attName, attributeValue); + } } else { handleInvalidProperty( "Error processing foreign attribute: " diff --git a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java index 0fafb0ca0..0b5ae827b 100644 --- a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java +++ b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java @@ -21,8 +21,10 @@ package org.apache.fop.fo.extensions; import org.apache.fop.fo.ElementMapping; import org.apache.fop.fo.UnknownXMLObj; +import org.apache.fop.util.QName; import java.util.HashMap; +import java.util.Set; /** * Element mapping for FOP's proprietary extension to XSL-FO. @@ -32,6 +34,15 @@ public class ExtensionElementMapping extends ElementMapping { /** The FOP extension namespace URI */ public static final String URI = "http://xmlgraphics.apache.org/fop/extensions"; + private static final Set propertyAttributes = new java.util.HashSet(); + + static { + //These are FOP's standard extension properties (fox:*) + propertyAttributes.add("block-progression-unit"); + propertyAttributes.add("widow-content-limit"); + propertyAttributes.add("orphan-content-limit"); + } + /** * Constructor. */ @@ -49,4 +60,18 @@ public class ExtensionElementMapping extends ElementMapping { foObjs.put("label", new UnknownXMLObj.Maker(URI)); } } + + /** @see org.apache.fop.fo.ElementMapping#getStandardPrefix() */ + public String getStandardPrefix() { + return "fox"; + } + + /** @see org.apache.fop.fo.ElementMapping#isAttributeProperty(org.apache.fop.util.QName) */ + public boolean isAttributeProperty(QName attributeName) { + if (!URI.equals(attributeName.getNamespaceURI())) { + throw new IllegalArgumentException("The namespace URIs don't match"); + } + return propertyAttributes.contains(attributeName.getLocalName()); + } + } diff --git a/src/java/org/apache/fop/fo/extensions/svg/SVGElementMapping.java b/src/java/org/apache/fop/fo/extensions/svg/SVGElementMapping.java index a9b07bc0d..7d2ca2f72 100644 --- a/src/java/org/apache/fop/fo/extensions/svg/SVGElementMapping.java +++ b/src/java/org/apache/fop/fo/extensions/svg/SVGElementMapping.java @@ -86,6 +86,11 @@ public class SVGElementMapping extends ElementMapping { } } + /** @see org.apache.fop.fo.ElementMapping#getStandardPrefix() */ + public String getStandardPrefix() { + return "svg"; + } + static class SVGMaker extends ElementMapping.Maker { public FONode make(FONode parent) { return new SVGObj(parent); diff --git a/src/java/org/apache/fop/fo/flow/ListBlock.java b/src/java/org/apache/fop/fo/flow/ListBlock.java index 2051451c5..2cbbb58ce 100644 --- a/src/java/org/apache/fop/fo/flow/ListBlock.java +++ b/src/java/org/apache/fop/fo/flow/ListBlock.java @@ -55,6 +55,10 @@ public class ListBlock extends FObj { private Length provisionalLabelSeparation; // End of property values + /** extension properties */ + private Length widowContentLimit; + private Length orphanContentLimit; + // used for child node validation private boolean hasListItem = false; @@ -85,6 +89,10 @@ public class ListBlock extends FObj { PR_PROVISIONAL_DISTANCE_BETWEEN_STARTS).getLength(); provisionalLabelSeparation = pList.get( PR_PROVISIONAL_LABEL_SEPARATION).getLength(); + + //Bind extension properties + widowContentLimit = pList.get(PR_X_WIDOW_CONTENT_LIMIT).getLength(); + orphanContentLimit = pList.get(PR_X_ORPHAN_CONTENT_LIMIT).getLength(); } /** @@ -167,9 +175,17 @@ public class ListBlock extends FObj { return keepTogether; } - /** - * @return the "id" property. - */ + /** @return the "fox:widow-content-limit" extension property */ + public Length getWidowContentLimit() { + return widowContentLimit; + } + + /** @return the "fox:orphan-content-limit" extension property */ + public Length getOrphanContentLimit() { + return orphanContentLimit; + } + + /** @return the "id" property. */ public String getId() { return id; } @@ -179,9 +195,7 @@ public class ListBlock extends FObj { return "list-block"; } - /** - * @see org.apache.fop.fo.FObj#getNameId() - */ + /** @see org.apache.fop.fo.FObj#getNameId() */ public int getNameId() { return FO_LIST_BLOCK; } diff --git a/src/java/org/apache/fop/fo/flow/Table.java b/src/java/org/apache/fop/fo/flow/Table.java index ac3fc95ee..079909c73 100644 --- a/src/java/org/apache/fop/fo/flow/Table.java +++ b/src/java/org/apache/fop/fo/flow/Table.java @@ -67,6 +67,10 @@ public class Table extends TableFObj { private int tableOmitFooterAtBreak; private int tableOmitHeaderAtBreak; private int writingMode; + + /** extension properties */ + private Length widowContentLimit; + private Length orphanContentLimit; private static final int MINCOLWIDTH = 10000; // 10pt @@ -126,6 +130,10 @@ public class Table extends TableFObj { tableOmitHeaderAtBreak = pList.get(PR_TABLE_OMIT_HEADER_AT_BREAK).getEnum(); writingMode = pList.get(PR_WRITING_MODE).getEnum(); super.bind(pList); + + //Bind extension properties + widowContentLimit = pList.get(PR_X_WIDOW_CONTENT_LIMIT).getLength(); + orphanContentLimit = pList.get(PR_X_ORPHAN_CONTENT_LIMIT).getLength(); if (borderCollapse != EN_SEPARATE) { //TODO Remove once the collapsing border is at least marginally working. @@ -449,10 +457,18 @@ public class Table extends TableFObj { public LengthPairProperty getBorderSeparation() { return borderSeparation; } + + /** @return the "fox:widow-content-limit" extension property */ + public Length getWidowContentLimit() { + return widowContentLimit; + } - /** - * @return the "id" property. - */ + /** @return the "fox:orphan-content-limit" extension property */ + public Length getOrphanContentLimit() { + return orphanContentLimit; + } + + /** @return the "id" property. */ public String getId() { return id; } @@ -462,9 +478,7 @@ public class Table extends TableFObj { return "table"; } - /** - * @see org.apache.fop.fo.FObj#getNameId() - */ + /** @see org.apache.fop.fo.FObj#getNameId() */ public int getNameId() { return FO_TABLE; } diff --git a/src/java/org/apache/fop/layoutmgr/ElementListUtils.java b/src/java/org/apache/fop/layoutmgr/ElementListUtils.java index 261974d6b..1166482a9 100644 --- a/src/java/org/apache/fop/layoutmgr/ElementListUtils.java +++ b/src/java/org/apache/fop/layoutmgr/ElementListUtils.java @@ -43,24 +43,20 @@ public class ElementListUtils { //Convert all penalties no break inhibitors if (breakPoss.getPenaltyValue() < KnuthPenalty.INFINITE) { breakPoss.setPenaltyValue(KnuthPenalty.INFINITE); - /* - i.set(new KnuthPenalty(penalty.getW(), KnuthPenalty.INFINITE, - penalty.isFlagged(), penalty.getPosition(), penalty.isAuxiliary())); - */ } } else if (el.isGlue()) { i.previous(); if (el.isBox()) { i.next(); i.add(new KnuthPenalty(0, KnuthPenalty.INFINITE, false, - /*new Position(getTableLM())*/null, false)); + null, false)); } } } } /** - * Removes all legal breaks in an element list. A constraint can be specified to limit the + * Removes legal breaks in an element list. A constraint can be specified to limit the * range in which the breaks are removed. Legal breaks occuring before at least * constraint.opt space is filled will be removed. * @param elements the element list @@ -68,29 +64,95 @@ public class ElementListUtils { * @return true if the opt constraint is bigger than the list contents */ public static boolean removeLegalBreaks(LinkedList elements, MinOptMax constraint) { + return removeLegalBreaks(elements, constraint.opt); + } + + /** + * Removes legal breaks in an element list. A constraint can be specified to limit the + * range in which the breaks are removed. Legal breaks occuring before at least + * constraint space is filled will be removed. + * @param elements the element list + * @param constraint value to restrict the range in which the breaks are removed. + * @return true if the constraint is bigger than the list contents + */ + public static boolean removeLegalBreaks(LinkedList elements, int constraint) { int len = 0; - ListIterator i = elements.listIterator(); - while (i.hasNext()) { - KnuthElement el = (KnuthElement)i.next(); + ListIterator iter = elements.listIterator(); + while (iter.hasNext()) { + ListElement el = (ListElement)iter.next(); if (el.isPenalty()) { KnuthPenalty penalty = (KnuthPenalty)el; - //Convert all penalties no break inhibitors + //Convert all penalties to break inhibitors + if (penalty.getP() < KnuthPenalty.INFINITE) { + iter.set(new KnuthPenalty(penalty.getW(), KnuthPenalty.INFINITE, + penalty.isFlagged(), penalty.getPosition(), penalty.isAuxiliary())); + } + } else if (el.isGlue()) { + KnuthGlue glue = (KnuthGlue)el; + len += glue.getW(); + iter.previous(); + el = (ListElement)iter.previous(); + iter.next(); + if (el.isBox()) { + iter.add(new KnuthPenalty(0, KnuthPenalty.INFINITE, false, + null, false)); + } + iter.next(); + } else if (el instanceof BreakElement) { + BreakElement breakEl = (BreakElement)el; + if (breakEl.getPenaltyValue() < KnuthPenalty.INFINITE) { + breakEl.setPenaltyValue(KnuthPenalty.INFINITE); + } + } else { + KnuthElement kel = (KnuthElement)el; + len += kel.getW(); + } + if (len >= constraint) { + return false; + } + } + return true; + } + + /** + * Removes legal breaks in an element list. A constraint can be specified to limit the + * range in which the breaks are removed. Legal breaks within the space specified through the + * constraint (starting from the end of the element list) will be removed. + * @param elements the element list + * @param constraint value to restrict the range in which the breaks are removed. + * @return true if the constraint is bigger than the list contents + */ + public static boolean removeLegalBreaksFromEnd(LinkedList elements, int constraint) { + int len = 0; + ListIterator i = elements.listIterator(elements.size()); + while (i.hasPrevious()) { + ListElement el = (ListElement)i.previous(); + if (el.isPenalty()) { + KnuthPenalty penalty = (KnuthPenalty)el; + //Convert all penalties to break inhibitors if (penalty.getP() < KnuthPenalty.INFINITE) { i.set(new KnuthPenalty(penalty.getW(), KnuthPenalty.INFINITE, penalty.isFlagged(), penalty.getPosition(), penalty.isAuxiliary())); } } else if (el.isGlue()) { - len += el.getW(); - i.previous(); + KnuthGlue glue = (KnuthGlue)el; + len += glue.getW(); + el = (ListElement)i.previous(); + i.next(); if (el.isBox()) { - i.next(); i.add(new KnuthPenalty(0, KnuthPenalty.INFINITE, false, - /*new Position(getTableLM())*/null, false)); + null, false)); + } + } else if (el instanceof BreakElement) { + BreakElement breakEl = (BreakElement)el; + if (breakEl.getPenaltyValue() < KnuthPenalty.INFINITE) { + breakEl.setPenaltyValue(KnuthPenalty.INFINITE); } } else { - len += el.getW(); + KnuthElement kel = (KnuthElement)el; + len += kel.getW(); } - if (len > constraint.opt) { + if (len >= constraint) { return false; } } diff --git a/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java index 709106dbc..67e609662 100644 --- a/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java @@ -23,6 +23,7 @@ import org.apache.fop.fo.flow.ListBlock; import org.apache.fop.layoutmgr.BlockLevelLayoutManager; import org.apache.fop.layoutmgr.BlockStackingLayoutManager; import org.apache.fop.layoutmgr.ConditionalElementListener; +import org.apache.fop.layoutmgr.ElementListUtils; import org.apache.fop.layoutmgr.LayoutManager; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.PositionIterator; @@ -70,15 +71,6 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager } } - /* - private class SectionPosition extends LeafPosition { - protected List list; - protected SectionPosition(LayoutManager lm, int pos, List l) { - super(lm, pos); - list = l; - } - }*/ - /** * Create a new list block layout manager. * @param node list-block to create the layout manager for @@ -117,7 +109,21 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager /** @see org.apache.fop.layoutmgr.BlockStackingLayoutManager */ public LinkedList getNextKnuthElements(LayoutContext context, int alignment) { resetSpaces(); - return super.getNextKnuthElements(context, alignment); + LinkedList returnList = super.getNextKnuthElements(context, alignment); + + //fox:widow-content-limit + int widowRowLimit = getListBlockFO().getWidowContentLimit().getValue(); + if (widowRowLimit != 0) { + ElementListUtils.removeLegalBreaks(returnList, widowRowLimit); + } + + //fox:orphan-content-limit + int orphanRowLimit = getListBlockFO().getOrphanContentLimit().getValue(); + if (orphanRowLimit != 0) { + ElementListUtils.removeLegalBreaksFromEnd(returnList, orphanRowLimit); + } + + return returnList; } /** @see org.apache.fop.layoutmgr.LayoutManager#getChangedKnuthElements(java.util.List, int) */ diff --git a/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java index 1c3045e52..8b07f6956 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java @@ -223,8 +223,6 @@ public class TableContentLayoutManager implements PercentBaseContext { } } else { if (!firstBreakBeforeServed) { - //returnList.add(new KnuthPenalty(0, -KnuthPenalty.INFINITE, - // false, rowFO.getBreakBefore(), new Position(getTableLM()), true)); returnList.add(new BreakElement(new Position(getTableLM()), 0, -KnuthPenalty.INFINITE, rowFO.getBreakBefore(), context)); iter.backToPreviousRow(); @@ -291,6 +289,18 @@ public class TableContentLayoutManager implements PercentBaseContext { } } } + + //fox:widow-content-limit + int widowContentLimit = getTableLM().getTable().getWidowContentLimit().getValue(); + if (widowContentLimit != 0 && bodyType == TableRowIterator.BODY) { + ElementListUtils.removeLegalBreaks(returnList, widowContentLimit); + } + //fox:orphan-content-limit + int orphanContentLimit = getTableLM().getTable().getOrphanContentLimit().getValue(); + if (orphanContentLimit != 0 && bodyType == TableRowIterator.BODY) { + ElementListUtils.removeLegalBreaksFromEnd(returnList, orphanContentLimit); + } + return returnList; } diff --git a/status.xml b/status.xml index 3e5bb48c2..ac283f784 100644 --- a/status.xml +++ b/status.xml @@ -28,6 +28,10 @@ + + Extension properties fox:orphan-content-limit and fox:widow-content-limit which + help with list-block and table layout. See the documentation for details. + Configuration option in the Java2D-based renderers that allows to disable the default white background in order to produce bitmap output with transparency. diff --git a/test/java/org/apache/fop/util/ElementListUtilsTestCase.java b/test/java/org/apache/fop/util/ElementListUtilsTestCase.java new file mode 100644 index 000000000..1ea0ac02d --- /dev/null +++ b/test/java/org/apache/fop/util/ElementListUtilsTestCase.java @@ -0,0 +1,147 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.util; + +import java.util.LinkedList; + +import org.apache.fop.layoutmgr.ElementListUtils; +import org.apache.fop.layoutmgr.KnuthBox; +import org.apache.fop.layoutmgr.KnuthElement; +import org.apache.fop.layoutmgr.KnuthGlue; +import org.apache.fop.layoutmgr.KnuthPenalty; + +import junit.framework.TestCase; + +/** + * Test class for ElementListUtils. + */ +public class ElementListUtilsTestCase extends TestCase { + + /** + * Tests ElementListUtils.removeLegalBreaks(). + * @throws Exception if the test fails + */ + public void testRemoveElementPenalty1() throws Exception { + LinkedList lst = new LinkedList(); + lst.add(new KnuthBox(4000, null, false)); + lst.add(new KnuthPenalty(0, 0, false, null, false)); + lst.add(new KnuthBox(4000, null, false)); + lst.add(new KnuthPenalty(0, 200, false, null, false)); + lst.add(new KnuthBox(4000, null, false)); + lst.add(new KnuthPenalty(0, 0, false, null, false)); + lst.add(new KnuthBox(4000, null, false)); + lst.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, null, false)); + lst.add(new KnuthGlue(0, Integer.MAX_VALUE, 0, null, false)); + lst.add(new KnuthPenalty(0, -KnuthElement.INFINITE, false, null, false)); + + boolean res = ElementListUtils.removeLegalBreaks(lst, 9000); + + assertFalse(res); + + assertEquals(KnuthElement.INFINITE, ((KnuthPenalty)lst.get(1)).getP()); + assertEquals(KnuthElement.INFINITE, ((KnuthPenalty)lst.get(3)).getP()); + assertEquals(0, ((KnuthPenalty)lst.get(5)).getP()); + } + + /** + * Tests ElementListUtils.removeLegalBreaks(). + * @throws Exception if the test fails + */ + public void testRemoveElementPenalty2() throws Exception { + LinkedList lst = new LinkedList(); + lst.add(new KnuthBox(4000, null, false)); + lst.add(new KnuthGlue(0, 0, 0, null, false)); + lst.add(new KnuthBox(4000, null, false)); + lst.add(new KnuthGlue(0, 0, 0, null, false)); + lst.add(new KnuthBox(4000, null, false)); + lst.add(new KnuthGlue(0, 0, 0, null, false)); + lst.add(new KnuthBox(4000, null, false)); + lst.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, null, false)); + lst.add(new KnuthGlue(0, Integer.MAX_VALUE, 0, null, false)); + lst.add(new KnuthPenalty(0, -KnuthElement.INFINITE, false, null, false)); + + boolean res = ElementListUtils.removeLegalBreaks(lst, 9000); + + assertFalse(res); + + //Must insert an INFINITE penalty + assertEquals(KnuthElement.INFINITE, ((KnuthPenalty)lst.get(1)).getP()); + assertEquals(0, ((KnuthGlue)lst.get(2)).getW()); + assertEquals(KnuthElement.INFINITE, ((KnuthPenalty)lst.get(4)).getP()); + assertEquals(0, ((KnuthGlue)lst.get(5)).getW()); + assertEquals(0, ((KnuthGlue)lst.get(7)).getW()); + } + + /** + * Tests ElementListUtils.removeLegalBreaksFromEnd(). + * @throws Exception if the test fails + */ + public void testRemoveElementFromEndPenalty1() throws Exception { + LinkedList lst = new LinkedList(); + lst.add(new KnuthBox(4000, null, false)); + lst.add(new KnuthPenalty(0, 0, false, null, false)); + lst.add(new KnuthBox(4000, null, false)); + lst.add(new KnuthPenalty(0, 200, false, null, false)); + lst.add(new KnuthBox(4000, null, false)); + lst.add(new KnuthPenalty(0, 0, false, null, false)); + lst.add(new KnuthBox(4000, null, false)); + lst.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, null, false)); + lst.add(new KnuthGlue(0, Integer.MAX_VALUE, 0, null, false)); + lst.add(new KnuthPenalty(0, -KnuthElement.INFINITE, false, null, false)); + + boolean res = ElementListUtils.removeLegalBreaksFromEnd(lst, 9000); + + assertFalse(res); + + assertEquals(0, ((KnuthPenalty)lst.get(1)).getP()); + assertEquals(KnuthElement.INFINITE, ((KnuthPenalty)lst.get(3)).getP()); + assertEquals(KnuthElement.INFINITE, ((KnuthPenalty)lst.get(5)).getP()); + } + + /** + * Tests ElementListUtils.removeLegalBreaksFromEnd(). + * @throws Exception if the test fails + */ + public void testRemoveElementFromEndPenalty2() throws Exception { + LinkedList lst = new LinkedList(); + lst.add(new KnuthBox(4000, null, false)); + lst.add(new KnuthPenalty(0, 0, false, null, false)); + lst.add(new KnuthBox(4000, null, false)); + lst.add(new KnuthPenalty(0, 200, false, null, false)); + lst.add(new KnuthBox(4000, null, false)); + lst.add(new KnuthGlue(0, 0, 0, null, false)); + lst.add(new KnuthBox(4000, null, false)); + lst.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, null, false)); + lst.add(new KnuthGlue(0, Integer.MAX_VALUE, 0, null, false)); + lst.add(new KnuthPenalty(0, -KnuthElement.INFINITE, false, null, false)); + + boolean res = ElementListUtils.removeLegalBreaksFromEnd(lst, 9000); + + assertFalse(res); + + //Must insert an INFINITE penalty + assertEquals(0, ((KnuthPenalty)lst.get(1)).getP()); + assertEquals(KnuthElement.INFINITE, ((KnuthPenalty)lst.get(3)).getP()); + assertEquals(KnuthElement.INFINITE, ((KnuthPenalty)lst.get(5)).getP()); + assertEquals(0, ((KnuthGlue)lst.get(6)).getW()); + } + + +} \ No newline at end of file diff --git a/test/layoutengine/standard-testcases/list-block_fox_orphan-content-limit_1.xml b/test/layoutengine/standard-testcases/list-block_fox_orphan-content-limit_1.xml new file mode 100644 index 000000000..bf4021bdb --- /dev/null +++ b/test/layoutengine/standard-testcases/list-block_fox_orphan-content-limit_1.xml @@ -0,0 +1,123 @@ + + + + + +

+ This test checks the effect of the proprietary fox:orphan-content-limit property. +

+
+ + + + + + + + + + + + + + • + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec felis ipsum, viverra ut, aliquam porttitor, convallis id, risus. Fusce malesuada nunc nec orci. + + + + + • + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec felis ipsum. + + + + + + + + + + + + + • + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec felis ipsum, viverra ut, aliquam porttitor, convallis id, risus. Fusce malesuada nunc nec orci. + + + + + • + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec felis ipsum. + + + + + + + + + + + + + + + + + + + + + + + + + 3 + + + + + + + + + + + + + + + + + + 3 + + +
diff --git a/test/layoutengine/standard-testcases/list-block_fox_widow-content-limit_1.xml b/test/layoutengine/standard-testcases/list-block_fox_widow-content-limit_1.xml new file mode 100644 index 000000000..d635ead40 --- /dev/null +++ b/test/layoutengine/standard-testcases/list-block_fox_widow-content-limit_1.xml @@ -0,0 +1,123 @@ + + + + + +

+ This test checks the effect of the proprietary fox:widow-content-limit property. +

+
+ + + + + + + + + + + + + + • + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec felis ipsum. + + + + + • + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec felis ipsum, viverra ut, aliquam porttitor, convallis id, risus. Fusce malesuada nunc nec orci. + + + + + + + + + + + + + • + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec felis ipsum. + + + + + • + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec felis ipsum, viverra ut, aliquam porttitor, convallis id, risus. Fusce malesuada nunc nec orci. + + + + + + + + + + + + + + + + + + + + + + + + + 3 + + + + + + + + + + + + + + + + + + 3 + + +
diff --git a/test/layoutengine/standard-testcases/table_fox_orphan-content-limit_1.xml b/test/layoutengine/standard-testcases/table_fox_orphan-content-limit_1.xml new file mode 100644 index 000000000..8314299fc --- /dev/null +++ b/test/layoutengine/standard-testcases/table_fox_orphan-content-limit_1.xml @@ -0,0 +1,133 @@ + + + + + +

+ This test checks the effect of the proprietary fox:orphan-content-limit property. +

+
+ + + + + + + + + + + + + + + + + row 1 + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec felis ipsum, viverra ut, aliquam porttitor, convallis id, risus. Fusce malesuada nunc nec orci. + + + + + row 2 + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. + + + + + + + + + + + + + + + + + row 1 + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec felis ipsum, viverra ut, aliquam porttitor, convallis id, risus. Fusce malesuada nunc nec orci. + + + + + row 2 + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. + + + + + + + + + + + + + + + + + + + + + + + + + + + + 3 + + + + + + + + + + + + + + + + + + + + 3 + + +
diff --git a/test/layoutengine/standard-testcases/table_fox_widow-content-limit_1.xml b/test/layoutengine/standard-testcases/table_fox_widow-content-limit_1.xml new file mode 100644 index 000000000..c41daaba8 --- /dev/null +++ b/test/layoutengine/standard-testcases/table_fox_widow-content-limit_1.xml @@ -0,0 +1,133 @@ + + + + + +

+ This test checks the effect of the proprietary fox:widow-content-limit property. +

+
+ + + + + + + + + + + + + + + + + row 1 + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. + + + + + row 2 + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec felis ipsum, viverra ut, aliquam porttitor, convallis id, risus. Fusce malesuada nunc nec orci. + + + + + + + + + + + + + + + + + row 1 + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. + + + + + row 2 + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec felis ipsum, viverra ut, aliquam porttitor, convallis id, risus. Fusce malesuada nunc nec orci. + + + + + + + + + + + + + + + + + + + + + + + + + + + + 3 + + + + + + + + + + + + + + + + + + + + 3 + + +
diff --git a/test/layoutengine/standard-testcases/table_fox_widow-content-limit_2.xml b/test/layoutengine/standard-testcases/table_fox_widow-content-limit_2.xml new file mode 100644 index 000000000..2684d75e0 --- /dev/null +++ b/test/layoutengine/standard-testcases/table_fox_widow-content-limit_2.xml @@ -0,0 +1,97 @@ + + + + + +

+ This test checks the effect of the proprietary fox:widow-content-limit property. +

+
+ + + + + + + + + + + + + + + + + row 1 + + + Lorem ipsum dolor sit amet. + + + + + row 2 + + + Lorem ipsum dolor sit amet. + + + + + row 3 + + + Lorem ipsum dolor sit amet. + + + + + row 4 + + + Lorem ipsum dolor sit amet. + + + + + + + + + + + + + + + + + + + + + + + + 3 + + +
-- 2.39.5