From 6f92d94a5042e650bd192f3e0f7f63c42de6b091 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Mon, 13 Nov 2006 09:39:19 +0000 Subject: [PATCH] Patch for the implementation of the improved float-placement algorithm Changes: - merge in changes up to revision 432403 of the trunk - extract LineBreakingAlgorithm and LineBreakPosition from LineLayoutManager and put them in the breaking subpackage (was in preparation for the side-floats implementation) New features: - minimum accepted fill ratio for pages: underfull pages which have at least this fill ratio are considered to be feasible breaks - float-only pages; this may be parameterized - big floats may now be split on several pages - shrink- and stretchability of out-of-line object are now taken into account git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_Floats@474218 13f79535-47bb-0310-9956-ffa450edef68 --- .../content/xdocs/trunk/configuration.xml | 14 + .../content/xdocs/trunk/pdfx.xml | 7 +- src/foschema/fop-configuration.xsd | 185 +-- .../org/apache/fop/image/ImageIOImage.java | 5 +- .../org/apache/fop/fo/FOEventHandler.java | 43 +- src/java/org/apache/fop/fo/FONode.java | 37 +- .../org/apache/fop/fo/FOPropertyMapping.java | 24 +- src/java/org/apache/fop/fo/FOText.java | 45 +- src/java/org/apache/fop/fo/FOTreeBuilder.java | 75 +- src/java/org/apache/fop/fo/FObj.java | 41 +- src/java/org/apache/fop/fo/FObjMixed.java | 67 +- src/java/org/apache/fop/fo/PropertyList.java | 155 ++- src/java/org/apache/fop/fo/PropertySets.java | 1192 ----------------- .../org/apache/fop/fo/StaticPropertyList.java | 2 +- .../apache/fop/fo/XMLWhiteSpaceHandler.java | 49 +- .../fop/fo/expr/FopPropValFunction.java | 58 - .../fop/fo/expr/FromTableColumnFunction.java | 2 +- .../fop/fo/expr/PPColWidthFunction.java | 20 +- .../apache/fop/fo/expr/PropertyParser.java | 2 - .../fop/fo/flow/AbstractListItemPart.java | 17 +- src/java/org/apache/fop/fo/flow/Block.java | 57 +- src/java/org/apache/fop/fo/flow/Marker.java | 289 +++- .../apache/fop/fo/flow/RetrieveMarker.java | 192 ++- src/java/org/apache/fop/fo/flow/Table.java | 43 +- .../org/apache/fop/fo/flow/TableBody.java | 129 +- .../org/apache/fop/fo/flow/TableCell.java | 126 +- .../org/apache/fop/fo/flow/TableColumn.java | 12 +- .../org/apache/fop/fo/flow/TableFObj.java | 217 ++- .../org/apache/fop/fo/flow/TableFooter.java | 1 - .../org/apache/fop/fo/flow/TableHeader.java | 1 - src/java/org/apache/fop/fo/flow/TableRow.java | 55 +- .../fop/fo/pagination/PageSequence.java | 8 + .../properties/ColumnNumberPropertyMaker.java | 112 -- .../fo/properties/FontShorthandProperty.java | 240 ++-- src/java/org/apache/fop/fonts/FontSetup.java | 20 + .../apache/fop/layoutmgr/AbstractBreaker.java | 11 +- .../BalancingColumnBreakingAlgorithm.java | 5 +- .../fop/layoutmgr/BreakingAlgorithm.java | 248 ++-- .../fop/layoutmgr/PageBreakingAlgorithm.java | 1052 ++++++++++----- .../layoutmgr/PageSequenceLayoutManager.java | 37 +- .../apache/fop/layoutmgr/SpaceResolver.java | 2 +- .../breaking/BeforeFloatsRecord.java | 237 ++++ .../fop/layoutmgr/breaking/ElasticLength.java | 146 ++ .../layoutmgr/breaking/FootnotesRecord.java | 204 +++ .../layoutmgr/breaking/LineBreakPosition.java | 51 + .../breaking/LineBreakingAlgorithm.java | 259 ++++ .../layoutmgr/breaking/OutOfLineRecord.java | 676 +++------- .../layoutmgr/inline/LineLayoutManager.java | 298 +---- .../list/ListItemContentLayoutManager.java | 3 + .../layoutmgr/list/ListItemLayoutManager.java | 15 +- .../table/TableContentLayoutManager.java | 38 +- .../fop/layoutmgr/table/TableStepper.java | 2 +- .../apache/fop/render/pdf/PDFSVGHandler.java | 82 +- .../org/apache/fop/svg/PDFAElementBridge.java | 5 + .../apache/fop/svg/PDFDocumentGraphics2D.java | 2 +- .../org/apache/fop/svg/PDFGraphics2D.java | 16 +- status.xml | 28 + ...ble-cell_column-number_rowspan_bug38397.fo | 98 ++ .../org/apache/fop/memory/MemoryEater.java | 108 ++ test/layoutengine/disabled-testcases.xml | 31 +- .../before-float_footnote_last-page.xml | 109 ++ .../standard-testcases/before-float_large.xml | 103 ++ .../before-float_not-deferred_stretch.xml | 2 +- .../block_keep-together_overflow_1.xml | 85 ++ .../standard-testcases/marker_font-size.xml | 11 +- .../marker_percentage-resolution.xml | 106 ++ .../marker_white-space-collapse.xml | 13 +- .../standard-testcases/markers_7.xml | 10 +- .../standard-testcases/markers_8.xml | 6 +- .../standard-testcases/markers_9.xml | 8 +- .../standard-testcases/table-body_basic_1.xml | 2 +- .../table-cell_background-image.xml | 2 +- .../table-cell_padding_percentages.xml | 2 +- .../table-header_in_list_bug.xml | 115 ++ .../table-row_background-image.xml | 2 +- .../table_background-image.xml | 6 +- .../table_padding_percentages.xml | 3 +- test/xsl/fo-page-sequence-splitter.xsl | 45 + test/xsl/fo-replicator.xsl | 72 + 79 files changed, 4526 insertions(+), 3372 deletions(-) delete mode 100644 src/java/org/apache/fop/fo/PropertySets.java delete mode 100644 src/java/org/apache/fop/fo/expr/FopPropValFunction.java delete mode 100644 src/java/org/apache/fop/fo/properties/ColumnNumberPropertyMaker.java create mode 100644 src/java/org/apache/fop/layoutmgr/breaking/BeforeFloatsRecord.java create mode 100644 src/java/org/apache/fop/layoutmgr/breaking/ElasticLength.java create mode 100644 src/java/org/apache/fop/layoutmgr/breaking/FootnotesRecord.java create mode 100644 src/java/org/apache/fop/layoutmgr/breaking/LineBreakPosition.java create mode 100644 src/java/org/apache/fop/layoutmgr/breaking/LineBreakingAlgorithm.java create mode 100644 test/fotree/testcases/table-cell_column-number_rowspan_bug38397.fo create mode 100644 test/java/org/apache/fop/memory/MemoryEater.java create mode 100644 test/layoutengine/standard-testcases/before-float_footnote_last-page.xml create mode 100644 test/layoutengine/standard-testcases/before-float_large.xml create mode 100644 test/layoutengine/standard-testcases/block_keep-together_overflow_1.xml create mode 100644 test/layoutengine/standard-testcases/marker_percentage-resolution.xml create mode 100644 test/layoutengine/standard-testcases/table-header_in_list_bug.xml create mode 100644 test/xsl/fo-page-sequence-splitter.xsl create mode 100644 test/xsl/fo-replicator.xsl diff --git a/src/documentation/content/xdocs/trunk/configuration.xml b/src/documentation/content/xdocs/trunk/configuration.xml index 53a48b2e8..99b92c7d3 100644 --- a/src/documentation/content/xdocs/trunk/configuration.xml +++ b/src/documentation/content/xdocs/trunk/configuration.xml @@ -221,6 +221,20 @@ ]]> +

+ Another (optional) setting specific to the PDF Renderer is an output color profile, an ICC + color profile which indicates the target color space the PDF file is generated for. This + setting is mainly used in conjunction with the PDF/X feature. + An example: +

+ + C:\FOP\Color\EuropeISOCoatedFOGRA27.icc + + ]]>
Special Settings for the PostScript Renderer diff --git a/src/documentation/content/xdocs/trunk/pdfx.xml b/src/documentation/content/xdocs/trunk/pdfx.xml index 7a526f8f6..cf796c74d 100644 --- a/src/documentation/content/xdocs/trunk/pdfx.xml +++ b/src/documentation/content/xdocs/trunk/pdfx.xml @@ -108,9 +108,10 @@ Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, userAgent);
  • Don't use CMYK images without an ICC color profile. PDF/X doesn't allow mixing color spaces and FOP currently only properly supports the sRGB color space. However, - you will need to specify an output device profile (usually a CMYK profile) in the - configuration. sRGB won't work here since it's a display device profile, not an - output device profile. + you will need to specify an + output device profile + (usually a CMYK profile) in the configuration. sRGB won't work here since it's a + display device profile, not an output device profile.
  • Don't use non-RGB colors in SVG images. Same issue as with CMYK images. diff --git a/src/foschema/fop-configuration.xsd b/src/foschema/fop-configuration.xsd index 2cc0fca56..f1e792858 100644 --- a/src/foschema/fop-configuration.xsd +++ b/src/foschema/fop-configuration.xsd @@ -60,126 +60,159 @@ - + - - - This type is stricter than required by FOP, in that it imposes an + + This type is stricter than required by FOP, in that it imposes an order of the elements, which is not required by FOP. - - + + - - - filterLists are used by the PDF renderer, MIME type - application/pdf. - - - - - - - - - - - - - - - - - - auto-rotate-landscape is used by the PostScript renderer, - MIME type application/postscript. - - - - - - - - - - rendering and text-rendering are used by the PCL renderer, + + Configuration elements used by the PDF renderer, + MIME type application/pdf + + + + filterLists are used by the PDF renderer, MIME type + application/pdf. + + + + + + + + + + + + + + + + + + + + + + + + Output color profile used by the PDF renderer. Specifies a + filename to an ICC file. + + + + + + Configuration elements used by the PostScript renderer, + MIME type application/postscript + + + + auto-rotate-landscape is used by the PostScript renderer, + MIME type application/postscript. + + + + + + + + + + + + rendering and text-rendering are used by the PCL renderer, MIME type application/vnd.hp-PCL - - + + - - + + - - + + + + + The elements in this sequence apply only to the text renderer, + MIME type text/plain. + + + - - + + + + + + + + - + - - + - - - + + + - + + - - - - + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - diff --git a/src/java-1.4/org/apache/fop/image/ImageIOImage.java b/src/java-1.4/org/apache/fop/image/ImageIOImage.java index db9262adc..463331530 100644 --- a/src/java-1.4/org/apache/fop/image/ImageIOImage.java +++ b/src/java-1.4/org/apache/fop/image/ImageIOImage.java @@ -21,6 +21,7 @@ package org.apache.fop.image; // AWT import java.awt.Color; +import java.awt.color.ColorSpace; import java.awt.image.ColorModel; import java.awt.image.IndexColorModel; import java.awt.image.BufferedImage; @@ -121,7 +122,9 @@ public class ImageIOImage extends AbstractFopImage { ColorModel cm = imageData.getColorModel(); this.bitsPerPixel = cm.getComponentSize(0); //only use first, we assume all are equal - this.colorSpace = cm.getColorSpace(); + //this.colorSpace = cm.getColorSpace(); + //We currently force the image to sRGB + this.colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); int[] tmpMap = imageData.getRGB(0, 0, this.width, this.height, null, 0, this.width); diff --git a/src/java/org/apache/fop/fo/FOEventHandler.java b/src/java/org/apache/fop/fo/FOEventHandler.java index 69cb7d06b..28dd32ba0 100644 --- a/src/java/org/apache/fop/fo/FOEventHandler.java +++ b/src/java/org/apache/fop/fo/FOEventHandler.java @@ -40,9 +40,9 @@ import org.apache.fop.fo.flow.ListBlock; import org.apache.fop.fo.flow.ListItem; import org.apache.fop.fo.flow.PageNumber; import org.apache.fop.fo.flow.Table; -import org.apache.fop.fo.flow.TableColumn; import org.apache.fop.fo.flow.TableBody; import org.apache.fop.fo.flow.TableCell; +import org.apache.fop.fo.flow.TableColumn; import org.apache.fop.fo.flow.TableRow; import org.apache.fop.fo.pagination.Flow; import org.apache.fop.fo.pagination.PageSequence; @@ -78,7 +78,7 @@ public abstract class FOEventHandler { */ private Set idReferences = new HashSet(); - /* + /** * The property list maker. */ protected PropertyListMaker propertyListMaker; @@ -88,6 +88,11 @@ public abstract class FOEventHandler { */ protected XMLWhiteSpaceHandler whiteSpaceHandler = new XMLWhiteSpaceHandler(); + /** + * Indicates whether processing descendants of a marker + */ + private boolean inMarker = false; + /** * Main constructor * @param foUserAgent the apps.FOUserAgent instance for this process @@ -143,6 +148,23 @@ public abstract class FOEventHandler { return whiteSpaceHandler; } + /** + * Switch to or from marker context + * (used by FOTreeBuilder when processing + * a marker) + * + */ + protected void switchMarkerContext(boolean inMarker) { + this.inMarker = inMarker; + } + + /** + * Check whether in marker context + */ + protected boolean inMarker() { + return this.inMarker; + } + /** * This method is called to indicate the start of a new document run. * @throws SAXException In case of a problem @@ -185,9 +207,10 @@ public abstract class FOEventHandler { } /** - * This method is called to indicate the start of a new fo:flow or fo:static-content. - * This method also handles fo:static-content tags, because the StaticContent class - * is derived from the Flow class. + * This method is called to indicate the start of a new fo:flow + * or fo:static-content. + * This method also handles fo:static-content tags, because the + * StaticContent class is derived from the Flow class. * * @param fl Flow that is starting. */ @@ -219,15 +242,15 @@ public abstract class FOEventHandler { * * @param blc BlockContainer that is starting. */ - public void startBlockContainer(BlockContainer blc) { - } + public void startBlockContainer(BlockContainer blc) { + } - /** + /** * * @param blc BlockContainer that is ending. */ - public void endBlockContainer(BlockContainer blc) { - } + public void endBlockContainer(BlockContainer blc) { + } /** * diff --git a/src/java/org/apache/fop/fo/FONode.java b/src/java/org/apache/fop/fo/FONode.java index be041254f..39d42423c 100644 --- a/src/java/org/apache/fop/fo/FONode.java +++ b/src/java/org/apache/fop/fo/FONode.java @@ -81,7 +81,6 @@ public abstract class FONode implements Cloneable { throws FOPException { FONode foNode = (FONode) clone(); foNode.parent = cloneparent; - cloneparent.addChildNode(foNode); return foNode; } @@ -124,6 +123,10 @@ public abstract class FONode implements Cloneable { public FOEventHandler getFOEventHandler() { return parent.getFOEventHandler(); } + + protected boolean inMarker() { + return getFOEventHandler().inMarker(); + } /** * Returns the user agent for the node. @@ -258,7 +261,7 @@ public abstract class FONode implements Cloneable { /** * Return an iterator over the object's child nodes starting - * at the pased node. + * at the passed node. * @param childNode First node in the iterator * @return A ListIterator or null if child node isn't a child of * this FObj. @@ -581,5 +584,35 @@ public abstract class FONode implements Cloneable { return null; } + /** + * @return true if markers are valid children + */ + protected boolean canHaveMarkers() { + int foId = getNameId(); + switch (foId) { + case Constants.FO_BASIC_LINK: + case Constants.FO_BIDI_OVERRIDE: + case Constants.FO_BLOCK: + case Constants.FO_BLOCK_CONTAINER: + case Constants.FO_FLOW: + case Constants.FO_INLINE: + case Constants.FO_INLINE_CONTAINER: + case Constants.FO_LIST_BLOCK: + case Constants.FO_LIST_ITEM: + case Constants.FO_LIST_ITEM_BODY: + case Constants.FO_LIST_ITEM_LABEL: + case Constants.FO_TABLE: + case Constants.FO_TABLE_BODY: + case Constants.FO_TABLE_HEADER: + case Constants.FO_TABLE_FOOTER: + case Constants.FO_TABLE_CELL: + case Constants.FO_TABLE_AND_CAPTION: + case Constants.FO_TABLE_CAPTION: + case Constants.FO_WRAPPER: + return true; + default: + return false; + } + } } diff --git a/src/java/org/apache/fop/fo/FOPropertyMapping.java b/src/java/org/apache/fop/fo/FOPropertyMapping.java index 7c1bbc773..bf23e4d97 100644 --- a/src/java/org/apache/fop/fo/FOPropertyMapping.java +++ b/src/java/org/apache/fop/fo/FOPropertyMapping.java @@ -30,7 +30,7 @@ import org.apache.fop.fo.properties.BorderWidthPropertyMaker; import org.apache.fop.fo.properties.BoxPropShorthandParser; import org.apache.fop.fo.properties.CharacterProperty; import org.apache.fop.fo.properties.ColorProperty; -import org.apache.fop.fo.properties.ColumnNumberPropertyMaker; +import org.apache.fop.fo.flow.TableFObj.ColumnNumberPropertyMaker; import org.apache.fop.fo.properties.CondLengthProperty; import org.apache.fop.fo.properties.CorrespondingPropertyMaker; import org.apache.fop.fo.properties.DimensionPropertyMaker; @@ -326,11 +326,13 @@ public final class FOPropertyMapping implements Constants { * @return a propId that matches the property name. */ public static int getPropertyId(String name) { - Integer i = (Integer) s_htPropNames.get(name); - if (i == null) { - return -1; + if (name != null) { + Integer i = (Integer) s_htPropNames.get(name); + if (i != null) { + return i.intValue(); + } } - return i.intValue(); + return -1; } /** @@ -339,11 +341,13 @@ public final class FOPropertyMapping implements Constants { * @return a subpropId that matches the subproperty name. */ public static int getSubPropertyId(String name) { - Integer i = (Integer) s_htSubPropNames.get(name); - if (i == null) { - return -1; + if (name != null) { + Integer i = (Integer) s_htSubPropNames.get(name); + if (i != null) { + return i.intValue(); + } } - return i.intValue(); + return -1; } // returns a property, compound, or property.compound name @@ -2263,8 +2267,8 @@ public final class FOPropertyMapping implements Constants { sub = new LengthProperty.Maker(CP_INLINE_PROGRESSION_DIRECTION); sub.setDefault("0pt"); - m.addSubpropMaker(sub); sub.setByShorthand(true); + m.addSubpropMaker(sub); addPropertyMaker("border-separation", m); // border-start-precedence diff --git a/src/java/org/apache/fop/fo/FOText.java b/src/java/org/apache/fop/fo/FOText.java index 8b578b146..ec8fcfb7e 100644 --- a/src/java/org/apache/fop/fo/FOText.java +++ b/src/java/org/apache/fop/fo/FOText.java @@ -150,6 +150,23 @@ public class FOText extends FONode { this.ca = nca; } + /** + * @see org.apache.fop.fo.FONode#clone(FONode, boolean) + */ + public FONode clone(FONode parent, boolean removeChildren) + throws FOPException { + FOText ft = (FOText) super.clone(parent, removeChildren); + if (removeChildren) { + //not really removing, but just make sure the char array + //pointed to is really a different one + if (ca != null) { + ft.ca = new char[ca.length]; + System.arraycopy(ca, 0, ft.ca, 0, ca.length); + } + } + return ft; + } + /** * @see org.apache.fop.fo.FObj#bind(PropertyList) */ @@ -171,8 +188,8 @@ public class FOText extends FONode { /** @see org.apache.fop.fo.FONode#endOfNode() */ protected void endOfNode() throws FOPException { - createBlockPointers(); textTransform(); + getFOEventHandler().characters(ca, startIndex, endIndex); } /** @@ -210,28 +227,11 @@ public class FOText extends FONode { } /** - * This method is run as part of the Constructor, to create xref pointers to + * This method is run as part of the ancestor Block's flushText(), to create xref pointers to * the previous FOText objects within the same Block */ - private void createBlockPointers() { - // build pointers between the FOText objects withing the same Block - // - // find the ancestorBlock of the current node - FONode ancestorFONode = this; - while (this.ancestorBlock == null) { - ancestorFONode = ancestorFONode.parent; - if (ancestorFONode instanceof org.apache.fop.fo.pagination.Title) { - return; - } else if (ancestorFONode instanceof org.apache.fop.fo.flow.Marker) { - return; - } else if (ancestorFONode instanceof Root) { - getLogger().warn("Unexpected: fo:text with no fo:block ancestor. The text is: " - + new String(ca)); - return; - } else if (ancestorFONode instanceof Block) { - this.ancestorBlock = (Block)ancestorFONode; - } - } + protected void createBlockPointers(Block ancestorBlock) { + this.ancestorBlock = ancestorBlock; // if the last FOText is a sibling, point to it, and have it point here if (lastFOTextProcessed != null) { if (lastFOTextProcessed.ancestorBlock == this.ancestorBlock) { @@ -252,7 +252,8 @@ public class FOText extends FONode { * text-transform property. */ private void textTransform() { - if (textTransform == Constants.EN_NONE) { + if (getFOEventHandler().inMarker() + || textTransform == Constants.EN_NONE) { return; } for (int i = 0; i < endIndex; i++) { diff --git a/src/java/org/apache/fop/fo/FOTreeBuilder.java b/src/java/org/apache/fop/fo/FOTreeBuilder.java index 0f2874b4b..4c754520d 100644 --- a/src/java/org/apache/fop/fo/FOTreeBuilder.java +++ b/src/java/org/apache/fop/fo/FOTreeBuilder.java @@ -246,7 +246,7 @@ public class FOTreeBuilder extends DefaultHandler { */ public FormattingResults getResults() { if (getEventHandler() instanceof AreaTreeHandler) { - return ((AreaTreeHandler)getEventHandler()).getResults(); + return ((AreaTreeHandler) getEventHandler()).getResults(); } else { //No formatting results available for output formats no //involving the layout engine. @@ -268,6 +268,11 @@ public class FOTreeBuilder extends DefaultHandler { * Current propertyList for the node being handled. */ protected PropertyList currentPropertyList; + + /** + * Current marker nesting-depth + */ + private int nestedMarkerDepth = 0; /** * SAX Handler for the start of an element @@ -278,16 +283,17 @@ public class FOTreeBuilder extends DefaultHandler { /* the node found in the FO document */ FONode foNode; - PropertyList propertyList; + PropertyList propertyList = null; // Check to ensure first node encountered is an fo:root if (rootFObj == null) { if (!namespaceURI.equals(FOElementMapping.URI) || !localName.equals("root")) { - throw new SAXException(new ValidationException( - "Error: First element must be the fo:root formatting object. Found " - + FONode.getNodeString(namespaceURI, localName) + " instead." - + " Please make sure you're producing a valid XSL-FO document.")); + throw new ValidationException( + "Error: First element must be the fo:root formatting object. " + + "Found " + FONode.getNodeString(namespaceURI, localName) + + " instead." + + " Please make sure you're producing a valid XSL-FO document."); } } else { // check that incoming node is valid for currentFObj if (namespaceURI.equals(FOElementMapping.URI)) { @@ -301,7 +307,8 @@ public class FOTreeBuilder extends DefaultHandler { } } - ElementMapping.Maker fobjMaker = findFOMaker(namespaceURI, localName); + ElementMapping.Maker fobjMaker = + findFOMaker(namespaceURI, localName); try { foNode = fobjMaker.make(currentFObj); @@ -309,8 +316,17 @@ public class FOTreeBuilder extends DefaultHandler { rootFObj = (Root) foNode; rootFObj.setFOEventHandler(foEventHandler); } - propertyList = foNode.createPropertyList(currentPropertyList, foEventHandler); - foNode.processNode(localName, getEffectiveLocator(), attlist, propertyList); + propertyList = foNode.createPropertyList( + currentPropertyList, foEventHandler); + foNode.processNode(localName, getEffectiveLocator(), + attlist, propertyList); + if (foNode.getNameId() == Constants.FO_MARKER) { + if (foEventHandler.inMarker()) { + nestedMarkerDepth++; + } else { + foEventHandler.switchMarkerContext(true); + } + } foNode.startOfNode(); } catch (IllegalArgumentException e) { throw new SAXException(e); @@ -321,11 +337,13 @@ public class FOTreeBuilder extends DefaultHandler { ContentHandler subHandler = chFactory.createContentHandler(); if (subHandler instanceof ObjectSource && foNode instanceof ObjectBuiltListener) { - ((ObjectSource)subHandler).setObjectBuiltListener((ObjectBuiltListener)foNode); + ((ObjectSource) subHandler).setObjectBuiltListener( + (ObjectBuiltListener) foNode); } subHandler.startDocument(); - subHandler.startElement(namespaceURI, localName, rawName, attlist); + subHandler.startElement(namespaceURI, localName, + rawName, attlist); depth = 1; delegate = subHandler; } @@ -335,7 +353,7 @@ public class FOTreeBuilder extends DefaultHandler { } currentFObj = foNode; - if (propertyList != null) { + if (propertyList != null && !foEventHandler.inMarker()) { currentPropertyList = propertyList; } } @@ -356,11 +374,24 @@ public class FOTreeBuilder extends DefaultHandler { + " (" + currentFObj.getNamespaceURI() + ") vs. " + localName + " (" + uri + ")"); } + currentFObj.endOfNode(); - - if (currentPropertyList.getFObj() == currentFObj) { - currentPropertyList = currentPropertyList.getParentPropertyList(); + + if (currentPropertyList != null + && currentPropertyList.getFObj() == currentFObj + && !foEventHandler.inMarker()) { + currentPropertyList = + currentPropertyList.getParentPropertyList(); + } + + if (currentFObj.getNameId() == Constants.FO_MARKER) { + if (nestedMarkerDepth == 0) { + foEventHandler.switchMarkerContext(false); + } else { + nestedMarkerDepth--; + } } + if (currentFObj.getParent() == null) { log.debug("endElement for top-level " + currentFObj.getName()); } @@ -373,19 +404,15 @@ public class FOTreeBuilder extends DefaultHandler { */ public void characters(char[] data, int start, int length) throws FOPException { - if (currentFObj != null) { - currentFObj.addCharacters(data, start, start + length, - currentPropertyList, getEffectiveLocator()); - } + if (currentFObj != null) { + currentFObj.addCharacters(data, start, start + length, + currentPropertyList, getEffectiveLocator()); + } } public void endDocument() throws SAXException { currentFObj = null; - } - - - + } } - } diff --git a/src/java/org/apache/fop/fo/FObj.java b/src/java/org/apache/fop/fo/FObj.java index 29278b1b8..1372b65c4 100644 --- a/src/java/org/apache/fop/fo/FObj.java +++ b/src/java/org/apache/fop/fo/FObj.java @@ -20,7 +20,6 @@ package org.apache.fop.fo; import java.util.Collections; -import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.ListIterator; @@ -119,11 +118,15 @@ public abstract class FObj extends FONode implements Constants { * @see org.apache.fop.fo.FONode#processNode */ public void processNode(String elementName, Locator locator, - Attributes attlist, PropertyList pList) throws FOPException { + Attributes attlist, PropertyList pList) + throws FOPException { setLocator(locator); pList.addAttributesToList(attlist); - pList.setWritingMode(); - bind(pList); + if (!inMarker() + || "marker".equals(elementName)) { + pList.setWritingMode(); + bind(pList); + } } /** @@ -154,7 +157,7 @@ public abstract class FObj extends FONode implements Constants { * @throws ValidationException if the ID is already defined elsewhere */ protected void checkId(String id) throws ValidationException { - if (!id.equals("")) { + if (!inMarker() && !id.equals("")) { Set idrefs = getFOEventHandler().getIDReferences(); if (!idrefs.contains(id)) { idrefs.add(id); @@ -178,13 +181,15 @@ public abstract class FObj extends FONode implements Constants { * @see org.apache.fop.fo.FONode#addChildNode(FONode) */ protected void addChildNode(FONode child) throws FOPException { - if (PropertySets.canHaveMarkers(getNameId()) && child.getNameId() == FO_MARKER) { - addMarker((Marker)child); + if (canHaveMarkers() && child.getNameId() == FO_MARKER) { + addMarker((Marker) child); } else { ExtensionAttachment attachment = child.getExtensionAttachment(); if (attachment != null) { - //This removes the element from the normal children, so no layout manager - //is being created for them as they are only additional information. + /* This removes the element from the normal children, + * so no layout manager is being created for them + * as they are only additional information. + */ addExtensionAttachment(attachment); } else { if (childNodes == null) { @@ -195,6 +200,10 @@ public abstract class FObj extends FONode implements Constants { } } + protected static void addChildTo(FONode child, FObj parent) throws FOPException { + parent.addChildNode(child); + } + /** @see org.apache.fop.fo.FONode#removeChild(org.apache.fop.fo.FONode) */ public void removeChild(FONode child) { if (childNodes != null) { @@ -250,6 +259,20 @@ public abstract class FObj extends FONode implements Constants { return null; } + /** + * Return a FONode based on the index in the list of childNodes. + * @param nodeIndex index of the node to return + * @return the node or null if the index is invalid + */ + public FONode getChildNodeAt(int nodeIndex) { + if (childNodes != null) { + if (nodeIndex >= 0 && nodeIndex < childNodes.size()) { + return (FONode) childNodes.get(nodeIndex); + } + } + return null; + } + /** * Notifies a FObj that one of it's children is removed. * This method is subclassed by Block to clear the firstInlineChild variable. diff --git a/src/java/org/apache/fop/fo/FObjMixed.java b/src/java/org/apache/fop/fo/FObjMixed.java index 7efcbf59f..84f27fbb9 100644 --- a/src/java/org/apache/fop/fo/FObjMixed.java +++ b/src/java/org/apache/fop/fo/FObjMixed.java @@ -42,7 +42,7 @@ public abstract class FObjMixed extends FObj { protected FObjMixed(FONode parent) { super(parent); } - + /** @see org.apache.fop.fo.FONode */ protected void addCharacters(char[] data, int start, int end, PropertyList pList, @@ -50,7 +50,9 @@ public abstract class FObjMixed extends FObj { if (ft == null) { ft = new FOText(this); ft.setLocator(locator); - ft.bind(pList); + if (!inMarker()) { + ft.bind(pList); + } } ft.addCharacters(data, start, end, null, null); } @@ -58,11 +60,26 @@ public abstract class FObjMixed extends FObj { /** @see org.apache.fop.fo.FONode#endOfNode() */ protected void endOfNode() throws FOPException { flushText(); - getFOEventHandler().whiteSpaceHandler - .handleWhiteSpace(this, currentTextNode); + if (!inMarker() + || getNameId() == FO_MARKER) { + getFOEventHandler().whiteSpaceHandler + .handleWhiteSpace(this, currentTextNode); + } super.endOfNode(); } + /** + * Handles white-space for the node that is passed in, + * starting at its current text-node + * (used by RetrieveMarker to trigger 'end-of-node' white-space + * handling) + * @param fobj the node for which to handle white-space + */ + protected static void handleWhiteSpaceFor(FObjMixed fobj) { + fobj.getFOEventHandler().getXMLWhiteSpaceHandler() + .handleWhiteSpace(fobj, fobj.currentTextNode); + } + /** * Adds accumulated text as one FOText instance. * Makes sure that nested calls to itself do nothing. @@ -72,8 +89,29 @@ public abstract class FObjMixed extends FObj { if (ft != null) { FOText lft = ft; ft = null; + if (getNameId() == FO_BLOCK) { + lft.createBlockPointers((org.apache.fop.fo.flow.Block) this); + } else if (getNameId() != FO_MARKER + && getNameId() != FO_TITLE + && getNameId() != FO_BOOKMARK_TITLE) { + FONode fo = parent; + int foNameId = fo.getNameId(); + while (foNameId != FO_BLOCK + && foNameId != FO_MARKER + && foNameId != FO_TITLE + && foNameId != FO_BOOKMARK_TITLE + && foNameId != FO_PAGE_SEQUENCE) { + fo = fo.getParent(); + foNameId = fo.getNameId(); + } + if (foNameId == FO_BLOCK) { + lft.createBlockPointers((org.apache.fop.fo.flow.Block) fo); + } else if (foNameId == FO_PAGE_SEQUENCE) { + log.error("Could not create block pointers." + + " FOText w/o Block ancestor."); + } + } lft.endOfNode(); - getFOEventHandler().characters(lft.ca, lft.startIndex, lft.endIndex); addChildNode(lft); } } @@ -83,15 +121,18 @@ public abstract class FObjMixed extends FObj { */ protected void addChildNode(FONode child) throws FOPException { flushText(); - if (child instanceof FOText || child.getNameId() == FO_CHARACTER) { - if (currentTextNode == null) { - currentTextNode = child; + if (!inMarker() + || getNameId() == FO_MARKER) { + if (child instanceof FOText || child.getNameId() == FO_CHARACTER) { + if (currentTextNode == null) { + currentTextNode = child; + } + } else { + // handle white-space for all text up to here + getFOEventHandler().whiteSpaceHandler + .handleWhiteSpace(this, currentTextNode, child); + currentTextNode = null; } - } else { - // handle white-space for all text up to here - getFOEventHandler().whiteSpaceHandler - .handleWhiteSpace(this, currentTextNode, child); - currentTextNode = null; } super.addChildNode(child); } diff --git a/src/java/org/apache/fop/fo/PropertyList.java b/src/java/org/apache/fop/fo/PropertyList.java index 6fdd1bf21..0600601c7 100644 --- a/src/java/org/apache/fop/fo/PropertyList.java +++ b/src/java/org/apache/fop/fo/PropertyList.java @@ -169,8 +169,11 @@ public abstract class PropertyList { boolean bTryDefault) throws PropertyException { PropertyMaker propertyMaker = findMaker(propId & Constants.PROPERTY_MASK); - return propertyMaker.get(propId & Constants.COMPOUND_MASK, this, - bTryInherit, bTryDefault); + if (propertyMaker != null) { + return propertyMaker.get(propId & Constants.COMPOUND_MASK, this, + bTryInherit, bTryDefault); + } + return null; } /** @@ -260,8 +263,11 @@ public abstract class PropertyList { * Adds the attributes, passed in by the parser to the PropertyList * * @param attributes Collection of attributes passed to us from the parser. + * @throws ValidationException if there is an attribute that does not + * map to a property id (strict validation only) */ - public void addAttributesToList(Attributes attributes) { + public void addAttributesToList(Attributes attributes) + throws ValidationException { /* * If column-number/number-columns-spanned are specified, then we * need them before all others (possible from-table-column() on any @@ -308,69 +314,104 @@ public abstract class PropertyList { if (factory.getElementMappingRegistry().isKnownNamespace(attributeNS)) { getFObj().addForeignAttribute(attributeNS, attributeName, attributeValue); } else { - handleInvalidProperty(attributeName); + handleInvalidProperty( + "Error processing foreign attribute: " + + attributeNS + "/@" + attributeName, attributeName); } } } } + + /** + * Validates a property name. + * @param propertyName the property name to check + * @return true if the base property name and the subproperty name (if any) + * can be correctly mapped to an id + * @throws ValidationException in case the property name + * is invalid for the FO namespace + */ + protected boolean isValidPropertyName(String propertyName) + throws ValidationException { + + int propId = FOPropertyMapping.getPropertyId( + findBasePropertyName(propertyName)); + int subpropId = FOPropertyMapping.getSubPropertyId( + findSubPropertyName(propertyName)); + + if (propId == -1 + || (subpropId == -1 + && findSubPropertyName(propertyName) != null)) { + StringBuffer errorMessage = new StringBuffer().append( + "Invalid property name \'").append(propertyName); + handleInvalidProperty(errorMessage.toString(), propertyName); + return false; + } + return true; + } /** * * @param attributes Collection of attributes * @param attributeName Attribute name to convert * @param attributeValue Attribute value to assign to property + * @throws ValidationException in case the property name is invalid + * for the FO namespace */ private void convertAttributeToProperty(Attributes attributes, String attributeName, - String attributeValue) { - - PropertyMaker propertyMaker = null; - FObj parentFO = fobj.findNearestAncestorFObj(); + String attributeValue) + throws ValidationException { - /* Handle "compound" properties, ex. space-before.minimum */ - String basePropertyName = findBasePropertyName(attributeName); - String subPropertyName = findSubPropertyName(attributeName); + if (attributeValue != null) { + + if (!isValidPropertyName(attributeName)) { + //will log an error or throw an exception + return; + } + FObj parentFO = fobj.findNearestAncestorFObj(); + + + /* Handle "compound" properties, ex. space-before.minimum */ + String basePropertyName = findBasePropertyName(attributeName); + String subPropertyName = findSubPropertyName(attributeName); - int propId = FOPropertyMapping.getPropertyId(basePropertyName); + int propId = FOPropertyMapping.getPropertyId(basePropertyName); + int subpropId = FOPropertyMapping.getSubPropertyId(subPropertyName); + + PropertyMaker propertyMaker = findMaker(propId); + if (propertyMaker == null) { + log.warn("No PropertyMaker registered for " + attributeName + + ". Ignoring property."); + return; + } - propertyMaker = findMaker(propId); - if (propertyMaker == null) { - handleInvalidProperty(attributeName); - return; - } - if (attributeValue == null) { - return; - } - try { - Property prop = null; - if (subPropertyName == null) { // base attribute only found - /* Do nothing if the base property has already been created. - * This is e.g. the case when a compound attribute was - * specified before the base attribute; in these cases - * the base attribute was already created in - * findBaseProperty() - */ - if (getExplicit(propId) != null) { - return; + try { + Property prop = null; + if (subPropertyName == null) { // base attribute only found + /* Do nothing if the base property has already been created. + * This is e.g. the case when a compound attribute was + * specified before the base attribute; in these cases + * the base attribute was already created in + * findBaseProperty() + */ + if (getExplicit(propId) != null) { + return; + } + prop = propertyMaker.make(this, attributeValue, parentFO); + } else { // e.g. "leader-length.maximum" + Property baseProperty = + findBaseProperty(attributes, parentFO, propId, + basePropertyName, propertyMaker); + prop = propertyMaker.make(baseProperty, subpropId, + this, attributeValue, parentFO); } - prop = propertyMaker.make(this, attributeValue, parentFO); - } else { // e.g. "leader-length.maximum" - Property baseProperty = findBaseProperty(attributes, - parentFO, propId, basePropertyName, propertyMaker); - int subpropertyId = FOPropertyMapping.getSubPropertyId(subPropertyName); - if (subpropertyId == -1) { - handleInvalidProperty(attributeName); - return; + if (prop != null) { + putExplicit(propId, prop); } - prop = propertyMaker.make(baseProperty, subpropertyId, - this, attributeValue, parentFO); - } - if (prop != null) { - putExplicit(propId, prop); + } catch (PropertyException e) { + log.error("Ignoring property: " + + attributeName + "=\"" + attributeValue + "\""); } - } catch (PropertyException e) { - // TODO: Add strict validation. - log.error(e.getMessage()); } } @@ -405,9 +446,19 @@ public abstract class PropertyList { return null; // could not find base property } - private void handleInvalidProperty(String attributeName) { - if (!attributeName.startsWith("xmlns")) { - log.error("property '" + attributeName + "' ignored"); + /** + * @param message ... + * @param propName ... + * @throws ValidationException ... + */ + protected void handleInvalidProperty(String message, String propName) + throws ValidationException { + if (!propName.startsWith("xmlns")) { + if (fobj.getUserAgent().validateStrictly()) { + fobj.attributeError(message); + } else { + log.error(message + " Property ignored."); + } } } @@ -418,7 +469,7 @@ public abstract class PropertyList { * @param attributeName String to be atomized * @return the base portion of the attribute */ - private static String findBasePropertyName(String attributeName) { + protected static String findBasePropertyName(String attributeName) { int separatorCharIndex = attributeName.indexOf('.'); String basePropertyName = attributeName; if (separatorCharIndex > -1) { @@ -434,7 +485,7 @@ public abstract class PropertyList { * @param attributeName String to be atomized * @return the sub portion of the attribute */ - private static String findSubPropertyName(String attributeName) { + protected static String findSubPropertyName(String attributeName) { int separatorCharIndex = attributeName.indexOf('.'); String subpropertyName = null; if (separatorCharIndex > -1) { diff --git a/src/java/org/apache/fop/fo/PropertySets.java b/src/java/org/apache/fop/fo/PropertySets.java deleted file mode 100644 index 29455add1..000000000 --- a/src/java/org/apache/fop/fo/PropertySets.java +++ /dev/null @@ -1,1192 +0,0 @@ -/* - * 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.fo; - -import org.apache.fop.fo.Constants; -import java.util.BitSet; -import java.util.ArrayList; - -public class PropertySets { - private static short[][] mapping = null; - private static BitSet can_have_markers = null; - private static BitSet no_inline_areas = null; - - private Element[] elements = new Element[Constants.FRM_OBJ_COUNT + 1]; - private BitSet block_elems = new BitSet(); - private BitSet inline_elems = new BitSet(); - - BitSet CommonAccessibilityProperties = new BitSet(); - BitSet CommonAbsolutePositionProperties = new BitSet(); - BitSet CommonAuralProperties = new BitSet(); - BitSet CommonBorderPaddingBackgroundProperties = new BitSet(); - BitSet CommonFontProperties = new BitSet(); - BitSet CommonHyphenationProperties = new BitSet(); - BitSet CommonMarginPropertiesBlock = new BitSet(); - BitSet CommonMarginPropertiesInline = new BitSet(); - BitSet CommonRelativePositionProperties = new BitSet(); - - public void initializeElements() { - block_elems.set(Constants.FO_BLOCK); - block_elems.set(Constants.FO_BLOCK_CONTAINER); - block_elems.set(Constants.FO_TABLE_AND_CAPTION); - block_elems.set(Constants.FO_TABLE); - block_elems.set(Constants.FO_LIST_BLOCK); - - inline_elems.set(Constants.FO_BIDI_OVERRIDE); - inline_elems.set(Constants.FO_CHARACTER); - inline_elems.set(Constants.FO_EXTERNAL_GRAPHIC); - inline_elems.set(Constants.FO_INSTREAM_FOREIGN_OBJECT); - inline_elems.set(Constants.FO_INLINE); - inline_elems.set(Constants.FO_INLINE_CONTAINER); - inline_elems.set(Constants.FO_LEADER); - inline_elems.set(Constants.FO_PAGE_NUMBER); - inline_elems.set(Constants.FO_PAGE_NUMBER_CITATION); - inline_elems.set(Constants.FO_PAGE_NUMBER_CITATION_LAST); - inline_elems.set(Constants.FO_BASIC_LINK); - inline_elems.set(Constants.FO_MULTI_TOGGLE); - } - - public void initializeCommon() { - CommonAccessibilityProperties.set(Constants.PR_SOURCE_DOCUMENT); - CommonAccessibilityProperties.set(Constants.PR_ROLE); - - CommonAbsolutePositionProperties.set(Constants.PR_ABSOLUTE_POSITION); - CommonAbsolutePositionProperties.set(Constants.PR_POSITION); - CommonAbsolutePositionProperties.set(Constants.PR_TOP); - CommonAbsolutePositionProperties.set(Constants.PR_RIGHT); - CommonAbsolutePositionProperties.set(Constants.PR_BOTTOM); - CommonAbsolutePositionProperties.set(Constants.PR_LEFT); - - CommonAuralProperties.set(Constants.PR_AZIMUTH); - CommonAuralProperties.set(Constants.PR_CUE_AFTER); - CommonAuralProperties.set(Constants.PR_CUE_BEFORE); - CommonAuralProperties.set(Constants.PR_CUE); - CommonAuralProperties.set(Constants.PR_ELEVATION); - CommonAuralProperties.set(Constants.PR_PAUSE_AFTER); - CommonAuralProperties.set(Constants.PR_PAUSE_BEFORE); - CommonAuralProperties.set(Constants.PR_PAUSE); - CommonAuralProperties.set(Constants.PR_PITCH); - CommonAuralProperties.set(Constants.PR_PITCH_RANGE); - CommonAuralProperties.set(Constants.PR_PLAY_DURING); - CommonAuralProperties.set(Constants.PR_RICHNESS); - CommonAuralProperties.set(Constants.PR_SPEAK); - CommonAuralProperties.set(Constants.PR_SPEAK_HEADER); - CommonAuralProperties.set(Constants.PR_SPEAK_NUMERAL); - CommonAuralProperties.set(Constants.PR_SPEAK_PUNCTUATION); - CommonAuralProperties.set(Constants.PR_SPEECH_RATE); - CommonAuralProperties.set(Constants.PR_STRESS); - CommonAuralProperties.set(Constants.PR_VOICE_FAMILY); - CommonAuralProperties.set(Constants.PR_VOLUME); - - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BACKGROUND_ATTACHMENT); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BACKGROUND_COLOR); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BACKGROUND_IMAGE); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BACKGROUND_REPEAT); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BACKGROUND_POSITION_HORIZONTAL); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BACKGROUND_POSITION_VERTICAL); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BEFORE_COLOR); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BEFORE_STYLE); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BEFORE_WIDTH); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_AFTER_COLOR); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_AFTER_STYLE); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_AFTER_WIDTH); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_START_COLOR); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_START_STYLE); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_START_WIDTH); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_END_COLOR); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_END_STYLE); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_END_WIDTH); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_TOP_COLOR); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_TOP_STYLE); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_TOP_WIDTH); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BOTTOM_COLOR); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BOTTOM_STYLE); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BOTTOM_WIDTH); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_LEFT_COLOR); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_LEFT_STYLE); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_LEFT_WIDTH); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_RIGHT_COLOR); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_RIGHT_STYLE); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_RIGHT_WIDTH); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_BEFORE); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_AFTER); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_START); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_END); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_TOP); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_BOTTOM); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_LEFT); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_RIGHT); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_STYLE); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_WIDTH); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_COLOR); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_TOP); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BOTTOM); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_LEFT); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_RIGHT); - CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING); - - CommonFontProperties.set(Constants.PR_FONT); - CommonFontProperties.set(Constants.PR_FONT_FAMILY); - CommonFontProperties.set(Constants.PR_FONT_SELECTION_STRATEGY); - CommonFontProperties.set(Constants.PR_FONT_SIZE); - CommonFontProperties.set(Constants.PR_FONT_STRETCH); - CommonFontProperties.set(Constants.PR_FONT_SIZE_ADJUST); - CommonFontProperties.set(Constants.PR_FONT_STYLE); - CommonFontProperties.set(Constants.PR_FONT_VARIANT); - CommonFontProperties.set(Constants.PR_FONT_WEIGHT); - - CommonHyphenationProperties.set(Constants.PR_COUNTRY); - CommonHyphenationProperties.set(Constants.PR_LANGUAGE); - CommonHyphenationProperties.set(Constants.PR_SCRIPT); - CommonHyphenationProperties.set(Constants.PR_HYPHENATE); - CommonHyphenationProperties.set(Constants.PR_HYPHENATION_CHARACTER); - CommonHyphenationProperties.set(Constants.PR_HYPHENATION_PUSH_CHARACTER_COUNT); - CommonHyphenationProperties.set(Constants.PR_HYPHENATION_REMAIN_CHARACTER_COUNT); - - CommonMarginPropertiesBlock.set(Constants.PR_MARGIN); - CommonMarginPropertiesBlock.set(Constants.PR_MARGIN_TOP); - CommonMarginPropertiesBlock.set(Constants.PR_MARGIN_BOTTOM); - CommonMarginPropertiesBlock.set(Constants.PR_MARGIN_LEFT); - CommonMarginPropertiesBlock.set(Constants.PR_MARGIN_RIGHT); - CommonMarginPropertiesBlock.set(Constants.PR_SPACE_BEFORE); - CommonMarginPropertiesBlock.set(Constants.PR_SPACE_AFTER); - CommonMarginPropertiesBlock.set(Constants.PR_START_INDENT); - CommonMarginPropertiesBlock.set(Constants.PR_END_INDENT); - - CommonMarginPropertiesInline.set(Constants.PR_SPACE_END); - CommonMarginPropertiesInline.set(Constants.PR_SPACE_START); - - CommonRelativePositionProperties.set(Constants.PR_RELATIVE_POSITION); - - - } - - public void initialize() { - // define the fo: elements - for (int i = 1; i < elements.length; i++) { - elements[i] = new Element(i); - } - - // populate the elements with properties and content elements. - Element elem; - elem = elements[Constants.FO_ROOT]; - elem.addProperty(Constants.PR_MEDIA_USAGE); - elem.addContent(Constants.FO_LAYOUT_MASTER_SET); - elem.addContent(Constants.FO_DECLARATIONS); - elem.addContent(Constants.FO_PAGE_SEQUENCE); - - elem = elements[Constants.FO_DECLARATIONS]; - elem.addContent(Constants.FO_COLOR_PROFILE); - - elem = elements[Constants.FO_COLOR_PROFILE]; - elem.addProperty(Constants.PR_SRC); - elem.addProperty(Constants.PR_COLOR_PROFILE_NAME); - elem.addProperty(Constants.PR_RENDERING_INTENT); - - elem = elements[Constants.FO_BOOKMARK_TREE]; - elem.addContent(Constants.FO_BOOKMARK); - - elem = elements[Constants.FO_BOOKMARK]; - elem.addContent(Constants.FO_BOOKMARK_TITLE); - elem.addContent(Constants.FO_BOOKMARK); - elem.addProperties(CommonAccessibilityProperties); - elem.addProperty(Constants.PR_EXTERNAL_DESTINATION); - elem.addProperty(Constants.PR_INTERNAL_DESTINATION); - elem.addProperty(Constants.PR_STARTING_STATE); - - elem = elements[Constants.FO_BOOKMARK_TITLE]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperty(Constants.PR_FONT_STYLE); - elem.addProperty(Constants.PR_FONT_WEIGHT); - - elem = elements[Constants.FO_PAGE_SEQUENCE_WRAPPER]; - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_INDEX_CLASS); - elem.addProperty(Constants.PR_INDEX_KEY); - - elem = elements[Constants.FO_PAGE_SEQUENCE]; - elem.addProperty(Constants.PR_COUNTRY); - elem.addProperty(Constants.PR_FORMAT); - elem.addProperty(Constants.PR_LANGUAGE); - elem.addProperty(Constants.PR_LETTER_VALUE); - elem.addProperty(Constants.PR_GROUPING_SEPARATOR); - elem.addProperty(Constants.PR_GROUPING_SIZE); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_INITIAL_PAGE_NUMBER); - elem.addProperty(Constants.PR_FORCE_PAGE_COUNT); - elem.addProperty(Constants.PR_MASTER_REFERENCE); - elem.addContent(Constants.FO_TITLE); - elem.addContent(Constants.FO_STATIC_CONTENT); - elem.addContent(Constants.FO_FLOW); - - elem = elements[Constants.FO_LAYOUT_MASTER_SET]; - elem.addProperty(Constants.PR_MASTER_NAME); - elem.addContent(Constants.FO_SIMPLE_PAGE_MASTER); - elem.addContent(Constants.FO_PAGE_SEQUENCE_MASTER); - - elem = elements[Constants.FO_PAGE_SEQUENCE_MASTER]; - elem.addProperty(Constants.PR_MASTER_NAME); - elem.addContent(Constants.FO_SINGLE_PAGE_MASTER_REFERENCE); - elem.addContent(Constants.FO_REPEATABLE_PAGE_MASTER_REFERENCE); - elem.addContent(Constants.FO_REPEATABLE_PAGE_MASTER_ALTERNATIVES); - - elem = elements[Constants.FO_SINGLE_PAGE_MASTER_REFERENCE]; - elem.addProperty(Constants.PR_MASTER_REFERENCE); - - elem = elements[Constants.FO_REPEATABLE_PAGE_MASTER_REFERENCE]; - elem.addProperty(Constants.PR_MASTER_REFERENCE); - elem.addProperty(Constants.PR_MAXIMUM_REPEATS); - - elem = elements[Constants.FO_REPEATABLE_PAGE_MASTER_ALTERNATIVES]; - elem.addProperty(Constants.PR_MAXIMUM_REPEATS); - elem.addContent(Constants.FO_CONDITIONAL_PAGE_MASTER_REFERENCE); - - elem = elements[Constants.FO_CONDITIONAL_PAGE_MASTER_REFERENCE]; - elem.addProperty(Constants.PR_MASTER_REFERENCE); - elem.addProperty(Constants.PR_PAGE_POSITION); - elem.addProperty(Constants.PR_ODD_OR_EVEN); - elem.addProperty(Constants.PR_BLANK_OR_NOT_BLANK); - - elem = elements[Constants.FO_SIMPLE_PAGE_MASTER]; - elem.addProperties(CommonMarginPropertiesBlock); - elem.addProperty(Constants.PR_MASTER_NAME); - elem.addProperty(Constants.PR_PAGE_HEIGHT); - elem.addProperty(Constants.PR_PAGE_WIDTH); - elem.addProperty(Constants.PR_REFERENCE_ORIENTATION); - elem.addProperty(Constants.PR_WRITING_MODE); - elem.addContent(Constants.FO_REGION_BODY); - elem.addContent(Constants.FO_REGION_BEFORE); - elem.addContent(Constants.FO_REGION_AFTER); - elem.addContent(Constants.FO_REGION_START); - elem.addContent(Constants.FO_REGION_END); - - elem = elements[Constants.FO_REGION_BODY]; - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonMarginPropertiesBlock); - elem.addProperty(Constants.PR_CLIP); - elem.addProperty(Constants.PR_COLUMN_COUNT); - elem.addProperty(Constants.PR_COLUMN_GAP); - elem.addProperty(Constants.PR_DISPLAY_ALIGN); - elem.addProperty(Constants.PR_OVERFLOW); - elem.addProperty(Constants.PR_REGION_NAME); - elem.addProperty(Constants.PR_REFERENCE_ORIENTATION); - elem.addProperty(Constants.PR_WRITING_MODE); - - elem = elements[Constants.FO_REGION_BEFORE]; - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperty(Constants.PR_CLIP); - elem.addProperty(Constants.PR_DISPLAY_ALIGN); - elem.addProperty(Constants.PR_EXTENT); - elem.addProperty(Constants.PR_OVERFLOW); - elem.addProperty(Constants.PR_PRECEDENCE); - elem.addProperty(Constants.PR_REGION_NAME); - elem.addProperty(Constants.PR_REFERENCE_ORIENTATION); - elem.addProperty(Constants.PR_WRITING_MODE); - - elem = elements[Constants.FO_REGION_AFTER]; - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperty(Constants.PR_CLIP); - elem.addProperty(Constants.PR_DISPLAY_ALIGN); - elem.addProperty(Constants.PR_EXTENT); - elem.addProperty(Constants.PR_OVERFLOW); - elem.addProperty(Constants.PR_PRECEDENCE); - elem.addProperty(Constants.PR_REGION_NAME); - elem.addProperty(Constants.PR_REFERENCE_ORIENTATION); - elem.addProperty(Constants.PR_WRITING_MODE); - - elem = elements[Constants.FO_REGION_START]; - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperty(Constants.PR_CLIP); - elem.addProperty(Constants.PR_DISPLAY_ALIGN); - elem.addProperty(Constants.PR_EXTENT); - elem.addProperty(Constants.PR_OVERFLOW); - elem.addProperty(Constants.PR_REGION_NAME); - elem.addProperty(Constants.PR_REFERENCE_ORIENTATION); - elem.addProperty(Constants.PR_WRITING_MODE); - - elem = elements[Constants.FO_REGION_END]; - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperty(Constants.PR_CLIP); - elem.addProperty(Constants.PR_DISPLAY_ALIGN); - elem.addProperty(Constants.PR_EXTENT); - elem.addProperty(Constants.PR_OVERFLOW); - elem.addProperty(Constants.PR_REGION_NAME); - elem.addProperty(Constants.PR_REFERENCE_ORIENTATION); - elem.addProperty(Constants.PR_WRITING_MODE); - - elem = elements[Constants.FO_FLOW]; - elem.addProperty(Constants.PR_FLOW_NAME); - elem.addContent(block_elems); - - elem = elements[Constants.FO_STATIC_CONTENT]; - elem.addProperty(Constants.PR_FLOW_NAME); - elem.addContent(block_elems); - - elem = elements[Constants.FO_TITLE]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonFontProperties); - elem.addProperties(CommonMarginPropertiesInline); - elem.addProperty(Constants.PR_COLOR); - elem.addProperty(Constants.PR_LINE_HEIGHT); - elem.addProperty(Constants.PR_VISIBILITY); - elem.addContent(inline_elems); - - elem = elements[Constants.FO_BLOCK]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonFontProperties); - elem.addProperties(CommonHyphenationProperties); - elem.addProperties(CommonMarginPropertiesBlock); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_PAGE_BREAK_AFTER); - elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE); - elem.addProperty(Constants.PR_BREAK_AFTER); - elem.addProperty(Constants.PR_BREAK_BEFORE); - elem.addProperty(Constants.PR_COLOR); - elem.addProperty(Constants.PR_TEXT_DEPTH); - elem.addProperty(Constants.PR_TEXT_ALTITUDE); - elem.addProperty(Constants.PR_HYPHENATION_KEEP); - elem.addProperty(Constants.PR_HYPHENATION_LADDER_COUNT); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE); - elem.addProperty(Constants.PR_KEEP_TOGETHER); - elem.addProperty(Constants.PR_KEEP_WITH_NEXT); - elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS); - elem.addProperty(Constants.PR_LAST_LINE_END_INDENT); - elem.addProperty(Constants.PR_LINEFEED_TREATMENT); - elem.addProperty(Constants.PR_LINE_HEIGHT); - elem.addProperty(Constants.PR_LINE_HEIGHT_SHIFT_ADJUSTMENT); - elem.addProperty(Constants.PR_LINE_STACKING_STRATEGY); - elem.addProperty(Constants.PR_ORPHANS); - elem.addProperty(Constants.PR_WHITE_SPACE_TREATMENT); - elem.addProperty(Constants.PR_SPAN); - elem.addProperty(Constants.PR_TEXT_ALIGN); - elem.addProperty(Constants.PR_TEXT_ALIGN_LAST); - elem.addProperty(Constants.PR_TEXT_INDENT); - elem.addProperty(Constants.PR_VISIBILITY); - elem.addProperty(Constants.PR_WHITE_SPACE_COLLAPSE); - elem.addProperty(Constants.PR_WIDOWS); - elem.addProperty(Constants.PR_WRAP_OPTION); - elem.addContent(inline_elems); - elem.addContent(block_elems); - - elem = elements[Constants.FO_BLOCK_CONTAINER]; - elem.addProperties(CommonAbsolutePositionProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonMarginPropertiesBlock); - elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION); - elem.addProperty(Constants.PR_X_BLOCK_PROGRESSION_UNIT); - elem.addProperty(Constants.PR_PAGE_BREAK_AFTER); - elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE); - elem.addProperty(Constants.PR_BREAK_AFTER); - elem.addProperty(Constants.PR_BREAK_BEFORE); - elem.addProperty(Constants.PR_CLIP); - elem.addProperty(Constants.PR_DISPLAY_ALIGN); - elem.addProperty(Constants.PR_HEIGHT); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION); - elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE); - elem.addProperty(Constants.PR_KEEP_TOGETHER); - elem.addProperty(Constants.PR_KEEP_WITH_NEXT); - elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS); - elem.addProperty(Constants.PR_OVERFLOW); - elem.addProperty(Constants.PR_REFERENCE_ORIENTATION); - elem.addProperty(Constants.PR_SPAN); - elem.addProperty(Constants.PR_WIDTH); - elem.addProperty(Constants.PR_WRITING_MODE); - elem.addProperty(Constants.PR_Z_INDEX); - elem.addContent(block_elems); - - elem = elements[Constants.FO_BIDI_OVERRIDE]; - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonFontProperties); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_COLOR); - elem.addProperty(Constants.PR_DIRECTION); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_LETTER_SPACING); - elem.addProperty(Constants.PR_LINE_HEIGHT); - elem.addProperty(Constants.PR_SCORE_SPACES); - elem.addProperty(Constants.PR_UNICODE_BIDI); - elem.addProperty(Constants.PR_WORD_SPACING); - elem.addContent(inline_elems); - elem.addContent(block_elems); - - elem = elements[Constants.FO_CHARACTER]; - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonFontProperties); - elem.addProperties(CommonHyphenationProperties); - elem.addProperties(CommonMarginPropertiesInline); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_VERTICAL_ALIGN); - elem.addProperty(Constants.PR_ALIGNMENT_ADJUST); - elem.addProperty(Constants.PR_TREAT_AS_WORD_SPACE); - elem.addProperty(Constants.PR_ALIGNMENT_BASELINE); - elem.addProperty(Constants.PR_BASELINE_SHIFT); - elem.addProperty(Constants.PR_CHARACTER); - elem.addProperty(Constants.PR_COLOR); - elem.addProperty(Constants.PR_DOMINANT_BASELINE); - elem.addProperty(Constants.PR_TEXT_DEPTH); - elem.addProperty(Constants.PR_TEXT_ALTITUDE); - elem.addProperty(Constants.PR_GLYPH_ORIENTATION_HORIZONTAL); - elem.addProperty(Constants.PR_GLYPH_ORIENTATION_VERTICAL); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_KEEP_WITH_NEXT); - elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS); - elem.addProperty(Constants.PR_LETTER_SPACING); - elem.addProperty(Constants.PR_LINE_HEIGHT); - elem.addProperty(Constants.PR_SCORE_SPACES); - elem.addProperty(Constants.PR_SUPPRESS_AT_LINE_BREAK); - elem.addProperty(Constants.PR_TEXT_DECORATION); - elem.addProperty(Constants.PR_TEXT_SHADOW); - elem.addProperty(Constants.PR_TEXT_TRANSFORM); - elem.addProperty(Constants.PR_VISIBILITY); - elem.addProperty(Constants.PR_WORD_SPACING); - - elem = elements[Constants.FO_INITIAL_PROPERTY_SET]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonFontProperties); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_COLOR); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_LETTER_SPACING); - elem.addProperty(Constants.PR_LINE_HEIGHT); - elem.addProperty(Constants.PR_SCORE_SPACES); - elem.addProperty(Constants.PR_TEXT_DECORATION); - elem.addProperty(Constants.PR_TEXT_SHADOW); - elem.addProperty(Constants.PR_TEXT_TRANSFORM); - elem.addProperty(Constants.PR_WORD_SPACING); - - elem = elements[Constants.FO_EXTERNAL_GRAPHIC]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonMarginPropertiesInline); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_VERTICAL_ALIGN); - elem.addProperty(Constants.PR_ALIGNMENT_ADJUST); - elem.addProperty(Constants.PR_ALIGNMENT_BASELINE); - elem.addProperty(Constants.PR_BASELINE_SHIFT); - elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION); - elem.addProperty(Constants.PR_CLIP); - elem.addProperty(Constants.PR_CONTENT_HEIGHT); - elem.addProperty(Constants.PR_CONTENT_TYPE); - elem.addProperty(Constants.PR_CONTENT_WIDTH); - elem.addProperty(Constants.PR_DISPLAY_ALIGN); - elem.addProperty(Constants.PR_DOMINANT_BASELINE); - elem.addProperty(Constants.PR_HEIGHT); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION); - elem.addProperty(Constants.PR_KEEP_WITH_NEXT); - elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS); - elem.addProperty(Constants.PR_LINE_HEIGHT); - elem.addProperty(Constants.PR_OVERFLOW); - elem.addProperty(Constants.PR_SCALING); - elem.addProperty(Constants.PR_SCALING_METHOD); - elem.addProperty(Constants.PR_SRC); - elem.addProperty(Constants.PR_TEXT_ALIGN); - elem.addProperty(Constants.PR_WIDTH); - - elem = elements[Constants.FO_INSTREAM_FOREIGN_OBJECT]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonMarginPropertiesInline); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_VERTICAL_ALIGN); - elem.addProperty(Constants.PR_ALIGNMENT_ADJUST); - elem.addProperty(Constants.PR_ALIGNMENT_BASELINE); - elem.addProperty(Constants.PR_BASELINE_SHIFT); - elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION); - elem.addProperty(Constants.PR_CLIP); - elem.addProperty(Constants.PR_CONTENT_HEIGHT); - elem.addProperty(Constants.PR_CONTENT_TYPE); - elem.addProperty(Constants.PR_CONTENT_WIDTH); - elem.addProperty(Constants.PR_DISPLAY_ALIGN); - elem.addProperty(Constants.PR_DOMINANT_BASELINE); - elem.addProperty(Constants.PR_HEIGHT); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION); - elem.addProperty(Constants.PR_KEEP_WITH_NEXT); - elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS); - elem.addProperty(Constants.PR_LINE_HEIGHT); - elem.addProperty(Constants.PR_OVERFLOW); - elem.addProperty(Constants.PR_SCALING); - elem.addProperty(Constants.PR_SCALING_METHOD); - elem.addProperty(Constants.PR_TEXT_ALIGN); - elem.addProperty(Constants.PR_WIDTH); - - elem = elements[Constants.FO_INLINE]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonFontProperties); - elem.addProperties(CommonMarginPropertiesInline); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_VERTICAL_ALIGN); - elem.addProperty(Constants.PR_ALIGNMENT_ADJUST); - elem.addProperty(Constants.PR_ALIGNMENT_BASELINE); - elem.addProperty(Constants.PR_BASELINE_SHIFT); - elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION); - elem.addProperty(Constants.PR_COLOR); - elem.addProperty(Constants.PR_DOMINANT_BASELINE); - elem.addProperty(Constants.PR_HEIGHT); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION); - elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE); - elem.addProperty(Constants.PR_KEEP_TOGETHER); - elem.addProperty(Constants.PR_KEEP_WITH_NEXT); - elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS); - elem.addProperty(Constants.PR_LINE_HEIGHT); - elem.addProperty(Constants.PR_TEXT_DECORATION); - elem.addProperty(Constants.PR_VISIBILITY); - elem.addProperty(Constants.PR_WIDTH); - elem.addProperty(Constants.PR_WRAP_OPTION); - elem.addContent(inline_elems); - elem.addContent(block_elems); - - elem = elements[Constants.FO_INLINE_CONTAINER]; - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonMarginPropertiesInline); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_VERTICAL_ALIGN); - elem.addProperty(Constants.PR_ALIGNMENT_ADJUST); - elem.addProperty(Constants.PR_ALIGNMENT_BASELINE); - elem.addProperty(Constants.PR_BASELINE_SHIFT); - elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION); - elem.addProperty(Constants.PR_CLIP); - elem.addProperty(Constants.PR_DISPLAY_ALIGN); - elem.addProperty(Constants.PR_DOMINANT_BASELINE); - elem.addProperty(Constants.PR_HEIGHT); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION); - elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE); - elem.addProperty(Constants.PR_KEEP_TOGETHER); - elem.addProperty(Constants.PR_KEEP_WITH_NEXT); - elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS); - elem.addProperty(Constants.PR_LINE_HEIGHT); - elem.addProperty(Constants.PR_OVERFLOW); - elem.addProperty(Constants.PR_REFERENCE_ORIENTATION); - elem.addProperty(Constants.PR_WIDTH); - elem.addProperty(Constants.PR_WRITING_MODE); - elem.addContent(block_elems); - - elem = elements[Constants.FO_LEADER]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonFontProperties); - elem.addProperties(CommonMarginPropertiesInline); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_VERTICAL_ALIGN); - elem.addProperty(Constants.PR_ALIGNMENT_ADJUST); - elem.addProperty(Constants.PR_ALIGNMENT_BASELINE); - elem.addProperty(Constants.PR_BASELINE_SHIFT); - elem.addProperty(Constants.PR_COLOR); - elem.addProperty(Constants.PR_DOMINANT_BASELINE); - elem.addProperty(Constants.PR_TEXT_DEPTH); - elem.addProperty(Constants.PR_TEXT_ALTITUDE); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_KEEP_WITH_NEXT); - elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS); - elem.addProperty(Constants.PR_LEADER_ALIGNMENT); - elem.addProperty(Constants.PR_LEADER_LENGTH); - elem.addProperty(Constants.PR_LEADER_PATTERN); - elem.addProperty(Constants.PR_LEADER_PATTERN_WIDTH); - elem.addProperty(Constants.PR_RULE_STYLE); - elem.addProperty(Constants.PR_RULE_THICKNESS); - elem.addProperty(Constants.PR_LETTER_SPACING); - elem.addProperty(Constants.PR_LINE_HEIGHT); - elem.addProperty(Constants.PR_TEXT_SHADOW); - elem.addProperty(Constants.PR_VISIBILITY); - elem.addProperty(Constants.PR_WORD_SPACING); - elem.addContent(inline_elems); - - elem = elements[Constants.FO_PAGE_NUMBER]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonFontProperties); - elem.addProperties(CommonMarginPropertiesInline); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_VERTICAL_ALIGN); - elem.addProperty(Constants.PR_ALIGNMENT_ADJUST); - elem.addProperty(Constants.PR_ALIGNMENT_BASELINE); - elem.addProperty(Constants.PR_BASELINE_SHIFT); - elem.addProperty(Constants.PR_DOMINANT_BASELINE); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_KEEP_WITH_NEXT); - elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS); - elem.addProperty(Constants.PR_LETTER_SPACING); - elem.addProperty(Constants.PR_LINE_HEIGHT); - elem.addProperty(Constants.PR_SCORE_SPACES); - elem.addProperty(Constants.PR_TEXT_ALTITUDE); - elem.addProperty(Constants.PR_TEXT_DECORATION); - elem.addProperty(Constants.PR_TEXT_DEPTH); - elem.addProperty(Constants.PR_TEXT_SHADOW); - elem.addProperty(Constants.PR_TEXT_TRANSFORM); - elem.addProperty(Constants.PR_VISIBILITY); - elem.addProperty(Constants.PR_WORD_SPACING); - elem.addProperty(Constants.PR_WRAP_OPTION); - - elem = elements[Constants.FO_PAGE_NUMBER_CITATION]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonFontProperties); - elem.addProperties(CommonMarginPropertiesInline); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_VERTICAL_ALIGN); - elem.addProperty(Constants.PR_ALIGNMENT_ADJUST); - elem.addProperty(Constants.PR_ALIGNMENT_BASELINE); - elem.addProperty(Constants.PR_BASELINE_SHIFT); - elem.addProperty(Constants.PR_DOMINANT_BASELINE); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_KEEP_WITH_NEXT); - elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS); - elem.addProperty(Constants.PR_LETTER_SPACING); - elem.addProperty(Constants.PR_LINE_HEIGHT); - elem.addProperty(Constants.PR_REF_ID); - elem.addProperty(Constants.PR_SCORE_SPACES); - elem.addProperty(Constants.PR_TEXT_ALTITUDE); - elem.addProperty(Constants.PR_TEXT_DECORATION); - elem.addProperty(Constants.PR_TEXT_DEPTH); - elem.addProperty(Constants.PR_TEXT_SHADOW); - elem.addProperty(Constants.PR_TEXT_TRANSFORM); - elem.addProperty(Constants.PR_VISIBILITY); - elem.addProperty(Constants.PR_WORD_SPACING); - elem.addProperty(Constants.PR_WRAP_OPTION); - - elem = elements[Constants.FO_PAGE_NUMBER_CITATION_LAST]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonFontProperties); - elem.addProperties(CommonMarginPropertiesInline); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_VERTICAL_ALIGN); - elem.addProperty(Constants.PR_ALIGNMENT_ADJUST); - elem.addProperty(Constants.PR_ALIGNMENT_BASELINE); - elem.addProperty(Constants.PR_BASELINE_SHIFT); - elem.addProperty(Constants.PR_DOMINANT_BASELINE); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_KEEP_WITH_NEXT); - elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS); - elem.addProperty(Constants.PR_LETTER_SPACING); - elem.addProperty(Constants.PR_LINE_HEIGHT); - elem.addProperty(Constants.PR_REF_ID); - elem.addProperty(Constants.PR_SCORE_SPACES); - elem.addProperty(Constants.PR_TEXT_ALTITUDE); - elem.addProperty(Constants.PR_TEXT_DECORATION); - elem.addProperty(Constants.PR_TEXT_DEPTH); - elem.addProperty(Constants.PR_TEXT_SHADOW); - elem.addProperty(Constants.PR_TEXT_TRANSFORM); - elem.addProperty(Constants.PR_VISIBILITY); - elem.addProperty(Constants.PR_WORD_SPACING); - elem.addProperty(Constants.PR_WRAP_OPTION); - - elem = elements[Constants.FO_TABLE_AND_CAPTION]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonMarginPropertiesBlock); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_PAGE_BREAK_AFTER); - elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE); - elem.addProperty(Constants.PR_BREAK_AFTER); - elem.addProperty(Constants.PR_BREAK_BEFORE); - elem.addProperty(Constants.PR_CAPTION_SIDE); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE); - elem.addProperty(Constants.PR_KEEP_TOGETHER); - elem.addProperty(Constants.PR_KEEP_WITH_NEXT); - elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS); - elem.addProperty(Constants.PR_TEXT_ALIGN); - elem.addContent(Constants.FO_TABLE_CAPTION); - elem.addContent(Constants.FO_TABLE); - - elem = elements[Constants.FO_TABLE]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonMarginPropertiesBlock); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION); - elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_SPACING); - elem.addProperty(Constants.PR_BORDER_COLLAPSE); - elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_SEPARATION); - elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE); - elem.addProperty(Constants.PR_PAGE_BREAK_AFTER); - elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE); - elem.addProperty(Constants.PR_BREAK_AFTER); - elem.addProperty(Constants.PR_BREAK_BEFORE); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION); - elem.addProperty(Constants.PR_HEIGHT); - elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE); - elem.addProperty(Constants.PR_KEEP_TOGETHER); - elem.addProperty(Constants.PR_KEEP_WITH_NEXT); - elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS); - elem.addProperty(Constants.PR_TABLE_LAYOUT); - elem.addProperty(Constants.PR_TABLE_OMIT_FOOTER_AT_BREAK); - elem.addProperty(Constants.PR_TABLE_OMIT_HEADER_AT_BREAK); - elem.addProperty(Constants.PR_WIDTH); - elem.addProperty(Constants.PR_WRITING_MODE); - elem.addContent(Constants.FO_TABLE_COLUMN); - elem.addContent(Constants.FO_TABLE_HEADER); - elem.addContent(Constants.FO_TABLE_FOOTER); - elem.addContent(Constants.FO_TABLE_BODY); - - elem = elements[Constants.FO_TABLE_COLUMN]; - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE); - elem.addProperty(Constants.PR_COLUMN_NUMBER); - elem.addProperty(Constants.PR_COLUMN_WIDTH); - elem.addProperty(Constants.PR_NUMBER_COLUMNS_REPEATED); - elem.addProperty(Constants.PR_NUMBER_COLUMNS_SPANNED); - elem.addProperty(Constants.PR_VISIBILITY); - - elem = elements[Constants.FO_TABLE_CAPTION]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION); - elem.addProperty(Constants.PR_HEIGHT); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION); - elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE); - elem.addProperty(Constants.PR_KEEP_TOGETHER); - elem.addProperty(Constants.PR_WIDTH); - elem.addContent(block_elems); - - elem = elements[Constants.FO_TABLE_HEADER]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_VISIBILITY); - elem.addContent(Constants.FO_TABLE_ROW); - elem.addContent(Constants.FO_TABLE_CELL); - - elem = elements[Constants.FO_TABLE_FOOTER]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_VISIBILITY); - elem.addContent(Constants.FO_TABLE_ROW); - elem.addContent(Constants.FO_TABLE_CELL); - - elem = elements[Constants.FO_TABLE_BODY]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_VISIBILITY); - elem.addContent(Constants.FO_TABLE_ROW); - elem.addContent(Constants.FO_TABLE_CELL); - - elem = elements[Constants.FO_TABLE_ROW]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE); - elem.addProperty(Constants.PR_PAGE_BREAK_AFTER); - elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE); - elem.addProperty(Constants.PR_BREAK_AFTER); - elem.addProperty(Constants.PR_BREAK_BEFORE); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_HEIGHT); - elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE); - elem.addProperty(Constants.PR_KEEP_TOGETHER); - elem.addProperty(Constants.PR_KEEP_WITH_NEXT); - elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS); - elem.addProperty(Constants.PR_VISIBILITY); - elem.addContent(Constants.FO_TABLE_CELL); - - elem = elements[Constants.FO_TABLE_CELL]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE); - elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE); - elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION); - elem.addProperty(Constants.PR_COLUMN_NUMBER); - elem.addProperty(Constants.PR_DISPLAY_ALIGN); - elem.addProperty(Constants.PR_RELATIVE_ALIGN); - elem.addProperty(Constants.PR_EMPTY_CELLS); - elem.addProperty(Constants.PR_ENDS_ROW); - elem.addProperty(Constants.PR_HEIGHT); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION); - elem.addProperty(Constants.PR_NUMBER_COLUMNS_SPANNED); - elem.addProperty(Constants.PR_NUMBER_ROWS_SPANNED); - elem.addProperty(Constants.PR_STARTS_ROW); - elem.addProperty(Constants.PR_WIDTH); - elem.addContent(block_elems); - - elem = elements[Constants.FO_LIST_BLOCK]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonMarginPropertiesBlock); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_PAGE_BREAK_AFTER); - elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE); - elem.addProperty(Constants.PR_BREAK_AFTER); - elem.addProperty(Constants.PR_BREAK_BEFORE); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE); - elem.addProperty(Constants.PR_KEEP_TOGETHER); - elem.addProperty(Constants.PR_KEEP_WITH_NEXT); - elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS); - elem.addProperty(Constants.PR_PROVISIONAL_DISTANCE_BETWEEN_STARTS); - elem.addProperty(Constants.PR_PROVISIONAL_LABEL_SEPARATION); - elem.addContent(Constants.FO_LIST_ITEM); - - elem = elements[Constants.FO_LIST_ITEM]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonMarginPropertiesBlock); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_PAGE_BREAK_AFTER); - elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE); - elem.addProperty(Constants.PR_BREAK_AFTER); - elem.addProperty(Constants.PR_BREAK_BEFORE); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE); - elem.addProperty(Constants.PR_KEEP_TOGETHER); - elem.addProperty(Constants.PR_KEEP_WITH_NEXT); - elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS); - elem.addProperty(Constants.PR_RELATIVE_ALIGN); - elem.addContent(Constants.FO_LIST_ITEM_LABEL); - elem.addContent(Constants.FO_LIST_ITEM_BODY); - - elem = elements[Constants.FO_LIST_ITEM_BODY]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE); - elem.addProperty(Constants.PR_KEEP_TOGETHER); - elem.addContent(block_elems); - - elem = elements[Constants.FO_LIST_ITEM_LABEL]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE); - elem.addProperty(Constants.PR_KEEP_TOGETHER); - elem.addContent(block_elems); - - elem = elements[Constants.FO_BASIC_LINK]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperties(CommonAuralProperties); - elem.addProperties(CommonBorderPaddingBackgroundProperties); - elem.addProperties(CommonMarginPropertiesInline); - elem.addProperties(CommonRelativePositionProperties); - elem.addProperty(Constants.PR_VERTICAL_ALIGN); - elem.addProperty(Constants.PR_ALIGNMENT_ADJUST); - elem.addProperty(Constants.PR_ALIGNMENT_BASELINE); - elem.addProperty(Constants.PR_BASELINE_SHIFT); - elem.addProperty(Constants.PR_DESTINATION_PLACEMENT_OFFSET); - elem.addProperty(Constants.PR_DOMINANT_BASELINE); - elem.addProperty(Constants.PR_EXTERNAL_DESTINATION); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_INDICATE_DESTINATION); - elem.addProperty(Constants.PR_INTERNAL_DESTINATION); - elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE); - elem.addProperty(Constants.PR_KEEP_TOGETHER); - elem.addProperty(Constants.PR_KEEP_WITH_NEXT); - elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS); - elem.addProperty(Constants.PR_LINE_HEIGHT); - elem.addProperty(Constants.PR_SHOW_DESTINATION); - elem.addProperty(Constants.PR_TARGET_PROCESSING_CONTEXT); - elem.addProperty(Constants.PR_TARGET_PRESENTATION_CONTEXT); - elem.addProperty(Constants.PR_TARGET_STYLESHEET); - elem.addContent(inline_elems); - elem.addContent(block_elems); - - elem = elements[Constants.FO_MULTI_SWITCH]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperty(Constants.PR_AUTO_RESTORE); - elem.addProperty(Constants.PR_ID); - elem.addContent(Constants.FO_MULTI_CASE); - - elem = elements[Constants.FO_MULTI_CASE]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_STARTING_STATE); - elem.addProperty(Constants.PR_CASE_NAME); - elem.addProperty(Constants.PR_CASE_TITLE); - elem.addContent(inline_elems); - elem.addContent(block_elems); - - elem = elements[Constants.FO_MULTI_TOGGLE]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_SWITCH_TO); - elem.addContent(inline_elems); - elem.addContent(block_elems); - - elem = elements[Constants.FO_MULTI_PROPERTIES]; - elem.addProperties(CommonAccessibilityProperties); - elem.addProperty(Constants.PR_ID); - elem.addContent(Constants.FO_MULTI_PROPERTY_SET); - elem.addContent(Constants.FO_WRAPPER); - - elem = elements[Constants.FO_MULTI_PROPERTY_SET]; - elem.addProperty(Constants.PR_ID); - elem.addProperty(Constants.PR_ACTIVE_STATE); - - elem = elements[Constants.FO_FLOAT]; - elem.addProperty(Constants.PR_FLOAT); - elem.addProperty(Constants.PR_CLEAR); - elem.addContent(block_elems); - - elem = elements[Constants.FO_FOOTNOTE]; - elem.addProperties(CommonAccessibilityProperties); - elem.addContent(Constants.FO_INLINE); - elem.addContent(Constants.FO_FOOTNOTE_BODY); - - elem = elements[Constants.FO_FOOTNOTE_BODY]; - elem.addProperties(CommonAccessibilityProperties); - elem.addContent(block_elems); - - elem = elements[Constants.FO_WRAPPER]; - elem.addProperty(Constants.PR_ID); - elem.addContent(inline_elems); - elem.addContent(block_elems); - - elem = elements[Constants.FO_MARKER]; - elem.addProperty(Constants.PR_MARKER_CLASS_NAME); - elem.addContent(inline_elems); - elem.addContent(block_elems); - - elem = elements[Constants.FO_RETRIEVE_MARKER]; - elem.addProperty(Constants.PR_RETRIEVE_CLASS_NAME); - elem.addProperty(Constants.PR_RETRIEVE_POSITION); - elem.addProperty(Constants.PR_RETRIEVE_BOUNDARY); - - // Merge the attributes from the children into the parent. - for (boolean dirty = true; dirty;) { - dirty = false; - for (int i = 1; i < elements.length; i++) { - dirty = dirty || elements[i].merge(); - } - } - // Calculate the sparse indices for each element. - for (int i = 1; i < elements.length; i++) { - mapping[i] = makeSparseIndices(elements[i].valid); - } - } - - /** - * Turn a BitSet into an array of shorts with the first element - * on the array the number of set bits in the BitSet. - */ - private static short[] makeSparseIndices(BitSet set) { - short[] indices = new short[Constants.PROPERTY_COUNT + 1]; - int j = 1; - for (int i = 0; i < Constants.PROPERTY_COUNT + 1; i++) { - if (set.get(i)) { - indices[i] = (short) j++; - } - } - indices[0] = (short)j; - return indices; - } - - public static short[] getPropertySet(int elementId) { - if (mapping == null) { - mapping = new short[Constants.FRM_OBJ_COUNT + 1][]; - PropertySets ps = new PropertySets(); - ps.initializeElements(); - ps.initializeCommon(); - ps.initialize(); - } - return mapping[elementId]; - } - - /** - * Determines if fo:markers are allowed as children for the given FO - * @param elementId Constants enumeration ID of the FO (e.g., FO_ROOT) - * @return true if fo:markers allowed, false otherwise - * @todo check if still needed after validateChildNode() fully implemented - */ - public static boolean canHaveMarkers(int elementId) { - if (can_have_markers == null) { - can_have_markers = new BitSet(); - can_have_markers.set(Constants.FO_BASIC_LINK); - can_have_markers.set(Constants.FO_BIDI_OVERRIDE); - can_have_markers.set(Constants.FO_BLOCK); - can_have_markers.set(Constants.FO_BLOCK_CONTAINER); - can_have_markers.set(Constants.FO_FLOW); - can_have_markers.set(Constants.FO_INLINE); - can_have_markers.set(Constants.FO_INLINE_CONTAINER); - can_have_markers.set(Constants.FO_LIST_BLOCK); - can_have_markers.set(Constants.FO_LIST_ITEM); - can_have_markers.set(Constants.FO_LIST_ITEM_BODY); - can_have_markers.set(Constants.FO_LIST_ITEM_LABEL); - can_have_markers.set(Constants.FO_TABLE); - can_have_markers.set(Constants.FO_TABLE_BODY); - can_have_markers.set(Constants.FO_TABLE_HEADER); - can_have_markers.set(Constants.FO_TABLE_FOOTER); - can_have_markers.set(Constants.FO_TABLE_CELL); - can_have_markers.set(Constants.FO_TABLE_AND_CAPTION); - can_have_markers.set(Constants.FO_TABLE_CAPTION); - can_have_markers.set(Constants.FO_WRAPPER); - } - return can_have_markers.get(elementId); - } - - /** - * Determines if the FO generates inline areas. Used only within flow.Block - * for whitespace handling - * @param elementId Constants enumeration ID of the FO (e.g., FO_ROOT) - * @return true if id property is applicable, false otherwise - * @todo see if more values need to be entered here (copied values over - * from legacy code, list of FO's below probably incomplete) - * @todo see if still needed (LM has a similar generatesInlineAreas() - * method) - */ - public static boolean generatesInlineAreas(int elementId) { - if (no_inline_areas == null) { - no_inline_areas = new BitSet(); - no_inline_areas.set(Constants.FO_UNKNOWN_NODE); - no_inline_areas.set(Constants.FO_BLOCK); - no_inline_areas.set(Constants.FO_BLOCK_CONTAINER); - no_inline_areas.set(Constants.FO_LIST_BLOCK); - no_inline_areas.set(Constants.FO_LIST_ITEM); - no_inline_areas.set(Constants.FO_TABLE); - no_inline_areas.set(Constants.FO_TABLE_AND_CAPTION); - } - return !(no_inline_areas.get(elementId)); - } - - /** - * An object that represent the properties and contents of a fo element - */ - class Element { - BitSet relevant = new BitSet(); - BitSet valid = new BitSet(); - int elementId; - ArrayList childFOs; - - Element(int elementId) { - this.elementId = elementId; - } - - /** - * Add a single property to the element. - */ - public void addProperty(int propId) { - relevant.set(propId); - valid.set(propId); - } - - /** - * Add a set of properties to the element. - */ - public void addProperties(BitSet properties) { - relevant.or(properties); - valid.or(properties); - } - - /** - * Add a single fo element as a content child. - */ - public void addContent(int elementId) { - if (childFOs == null) { - childFOs = new ArrayList(); - } - childFOs.add(elements[elementId]); - } - - /** - * Add a set of fo elements as content childFOs. - */ - public void addContent(BitSet elements) { - for (int i = 0; i < elements.size(); i++) { - if (elements.get(i)) { - addContent(i); - } - } - } - - /** - * Merge the properties from the child FO's into the set of valid - * properties. Return true if at least one property could be added. - */ - public boolean merge() { - if (childFOs == null) { - return false; - } - boolean dirty = false; - for (int i = 0; i < childFOs.size(); i++) { - Element child = (Element) childFOs.get(i); - BitSet childValid = child.valid; - int n = childValid.length(); - for (int j = 0; j < n; j++) { - if (childValid.get(j) && !valid.get(j)) { - dirty = true; - valid.set(j); - } - } - } - return dirty; - } - } -} diff --git a/src/java/org/apache/fop/fo/StaticPropertyList.java b/src/java/org/apache/fop/fo/StaticPropertyList.java index f4b8f7ad6..1ef3abd73 100755 --- a/src/java/org/apache/fop/fo/StaticPropertyList.java +++ b/src/java/org/apache/fop/fo/StaticPropertyList.java @@ -21,7 +21,7 @@ import org.apache.fop.fo.properties.Property; /** * A very fast implementation of PropertyList that uses arrays to store - * the explit set properties and another array to store cached values. + * the explicit set properties and another array to store cached values. */ public class StaticPropertyList extends PropertyList { private Property[] explicit; diff --git a/src/java/org/apache/fop/fo/XMLWhiteSpaceHandler.java b/src/java/org/apache/fop/fo/XMLWhiteSpaceHandler.java index 9055057a9..183224ce1 100644 --- a/src/java/org/apache/fop/fo/XMLWhiteSpaceHandler.java +++ b/src/java/org/apache/fop/fo/XMLWhiteSpaceHandler.java @@ -72,11 +72,34 @@ public class XMLWhiteSpaceHandler { * @param firstTextNode the node at which to start */ public void handleWhiteSpace(FObjMixed fo, FONode firstTextNode) { - if (fo.getNameId() == Constants.FO_BLOCK) { - this.currentBlock = (Block) fo; - this.linefeedTreatment = currentBlock.getLinefeedTreatment(); - this.whiteSpaceCollapse = currentBlock.getWhitespaceCollapse(); - this.whiteSpaceTreatment = currentBlock.getWhitespaceTreatment(); + if (fo.getNameId() == Constants.FO_BLOCK + || fo.getNameId() == Constants.FO_RETRIEVE_MARKER) { + if (fo.getNameId() == Constants.FO_BLOCK) { + this.currentBlock = (Block) fo; + } else { + FONode ancestor = fo.parent; + while (ancestor.getNameId() != Constants.FO_BLOCK + && ancestor.getNameId() != Constants.FO_STATIC_CONTENT) { + ancestor = ancestor.getParent(); + } + if (ancestor.getNameId() == Constants.FO_BLOCK) { + this.currentBlock = (Block) ancestor; + } + } + if (currentBlock != null) { + this.linefeedTreatment = currentBlock.getLinefeedTreatment(); + this.whiteSpaceCollapse = currentBlock.getWhitespaceCollapse(); + this.whiteSpaceTreatment = + currentBlock.getWhitespaceTreatment(); + } else { + /* fo:retrieve-marker as direct child of static-content + * set properties to their initial values + */ + this.linefeedTreatment = Constants.EN_TREAT_AS_SPACE; + this.whiteSpaceCollapse = Constants.EN_TRUE; + this.whiteSpaceTreatment = + Constants.EN_IGNORE_IF_SURROUNDING_LINEFEED; + } } else if (fo.getNameId() == Constants.FO_TITLE || fo.getNameId() == Constants.FO_BOOKMARK_TITLE) { /* Two special types of FO that can contain #PCDATA @@ -84,7 +107,8 @@ public class XMLWhiteSpaceHandler { */ this.linefeedTreatment = Constants.EN_TREAT_AS_SPACE; this.whiteSpaceCollapse = Constants.EN_TRUE; - this.whiteSpaceTreatment = Constants.EN_IGNORE_IF_SURROUNDING_LINEFEED; + this.whiteSpaceTreatment = + Constants.EN_IGNORE_IF_SURROUNDING_LINEFEED; } currentFO = fo; if (firstTextNode == null) { @@ -94,7 +118,10 @@ public class XMLWhiteSpaceHandler { charIter = new RecursiveCharIterator(fo, firstTextNode); inWhiteSpace = false; int textNodeIndex = -1; - if (currentFO == currentBlock) { + if (currentFO == currentBlock + || currentBlock == null + || (currentFO.getNameId() == Constants.FO_RETRIEVE_MARKER + && currentFO.getParent() == currentBlock)) { textNodeIndex = fo.childNodes.indexOf(firstTextNode); afterLinefeed = ((textNodeIndex == 0) || (textNodeIndex > 0 @@ -104,7 +131,8 @@ public class XMLWhiteSpaceHandler { endOfBlock = (nextChild == null && currentFO == currentBlock); if (nextChild != null) { int nextChildId = nextChild.getNameId(); - nextChildIsBlockLevel = (nextChildId == Constants.FO_BLOCK + nextChildIsBlockLevel = ( + nextChildId == Constants.FO_BLOCK || nextChildId == Constants.FO_TABLE_AND_CAPTION || nextChildId == Constants.FO_TABLE || nextChildId == Constants.FO_LIST_BLOCK @@ -149,6 +177,11 @@ public class XMLWhiteSpaceHandler { addPendingInline(fo); } } + + if (currentFO == currentBlock && nextChild == null) { + /* end of block: clear the reference */ + currentBlock = null; + } } /** diff --git a/src/java/org/apache/fop/fo/expr/FopPropValFunction.java b/src/java/org/apache/fop/fo/expr/FopPropValFunction.java deleted file mode 100644 index 47f5afd92..000000000 --- a/src/java/org/apache/fop/fo/expr/FopPropValFunction.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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.fo.expr; - -import org.apache.fop.fo.FOPropertyMapping; -import org.apache.fop.fo.properties.Property; - - -/** - * This appears to be an artificial function, which handles the specified - * or initial value of the property on this object. - */ -public class FopPropValFunction extends FunctionBase { - - /** - * @return 1 (the maximum number of arguments) - */ - public int nbArgs() { - return 1; - } - - /** - * - * @param args array of arguments, which should either be empty, or the - * first of which should be an NCName corresponding to a property name - * @param pInfo PropertyInfo object to be evaluated - * @return the Property corresponding to the input - * @throws PropertyException for incorrect parameters - */ - public Property eval(Property[] args, - PropertyInfo pInfo) throws PropertyException { - String propName = args[0].getString(); - if (propName == null) { - throw new PropertyException("Incorrect parameter to _int-property-value function"); - } - - int propId = FOPropertyMapping.getPropertyId(propName); - return pInfo.getPropertyList().get(propId); - } - -} diff --git a/src/java/org/apache/fop/fo/expr/FromTableColumnFunction.java b/src/java/org/apache/fop/fo/expr/FromTableColumnFunction.java index 731b403f1..8b14a0c64 100644 --- a/src/java/org/apache/fop/fo/expr/FromTableColumnFunction.java +++ b/src/java/org/apache/fop/fo/expr/FromTableColumnFunction.java @@ -24,9 +24,9 @@ import org.apache.fop.fo.Constants; import org.apache.fop.fo.FObj; import org.apache.fop.fo.FOPropertyMapping; import org.apache.fop.fo.flow.Table; -import org.apache.fop.fo.flow.TableFObj; import org.apache.fop.fo.flow.TableCell; import org.apache.fop.fo.flow.TableColumn; +import org.apache.fop.fo.flow.TableFObj; import org.apache.fop.fo.properties.Property; /** diff --git a/src/java/org/apache/fop/fo/expr/PPColWidthFunction.java b/src/java/org/apache/fop/fo/expr/PPColWidthFunction.java index ba9ae2ddf..856dd4b03 100644 --- a/src/java/org/apache/fop/fo/expr/PPColWidthFunction.java +++ b/src/java/org/apache/fop/fo/expr/PPColWidthFunction.java @@ -19,7 +19,8 @@ package org.apache.fop.fo.expr; - +import org.apache.fop.fo.PropertyList; +import org.apache.fop.fo.flow.Table; import org.apache.fop.fo.properties.Property; import org.apache.fop.fo.properties.TableColLength; @@ -51,13 +52,20 @@ public class PPColWidthFunction extends FunctionBase { Number d = args[0].getNumber(); if (d == null) { throw new PropertyException("Non numeric operand to " - + "proportional-column-width function"); + + "proportional-column-width() function."); + } + PropertyList pList = pInfo.getPropertyList(); + if (!"fo:table-column".equals(pList.getFObj().getName())) { + throw new PropertyException("proportional-column-width() function " + + "may only be used on fo:table-column."); } - if (!pInfo.getPropertyList().getFObj().getName().equals("fo:table-column")) { - throw new PropertyException("proportional-column-width function " - + "may only be used on table-column FO"); + + Table t = (Table) pList.getParentFObj(); + if (t.isAutoLayout()) { + throw new PropertyException("proportional-column-width() function " + + "may only be used when fo:table has " + + "table-layout=\"fixed\"."); } - // Check if table-layout is "fixed"... return new TableColLength(d.doubleValue(), pInfo.getFO()); } diff --git a/src/java/org/apache/fop/fo/expr/PropertyParser.java b/src/java/org/apache/fop/fo/expr/PropertyParser.java index 77b35a845..699094cc7 100644 --- a/src/java/org/apache/fop/fo/expr/PropertyParser.java +++ b/src/java/org/apache/fop/fo/expr/PropertyParser.java @@ -62,8 +62,6 @@ public final class PropertyParser extends PropertyTokenizer { new PPColWidthFunction()); FUNCTION_TABLE.put("label-end", new LabelEndFunction()); FUNCTION_TABLE.put("body-start", new BodyStartFunction()); - // NOTE: used from code generated for corresponding properties - FUNCTION_TABLE.put("_fop-property-value", new FopPropValFunction()); /** * * NOT YET IMPLEMENTED!!! diff --git a/src/java/org/apache/fop/fo/flow/AbstractListItemPart.java b/src/java/org/apache/fop/fo/flow/AbstractListItemPart.java index 7ef26adbd..03b9e96a7 100644 --- a/src/java/org/apache/fop/fo/flow/AbstractListItemPart.java +++ b/src/java/org/apache/fop/fo/flow/AbstractListItemPart.java @@ -86,8 +86,19 @@ public abstract class AbstractListItemPart extends FObj { * @see org.apache.fop.fo.FONode#endOfNode */ protected void endOfNode() throws FOPException { - if (!blockItemFound) { - missingChildElementError("marker* (%block;)+"); + if (!this.blockItemFound) { + String contentModel = "marker* (%block;)+"; + if (getUserAgent().validateStrictly()) { + missingChildElementError(contentModel); + } else { + StringBuffer message = new StringBuffer( + errorText(getLocator())); + message.append(getName()) + .append(" is missing child elements. ") + .append("Required Content Model: ") + .append(contentModel); + getLogger().warn(message.toString()); + } } } @@ -100,6 +111,6 @@ public abstract class AbstractListItemPart extends FObj { public String getId() { return id; } - + } diff --git a/src/java/org/apache/fop/fo/flow/Block.java b/src/java/org/apache/fop/fo/flow/Block.java index 6e9e5d662..b5dd2626f 100644 --- a/src/java/org/apache/fop/fo/flow/Block.java +++ b/src/java/org/apache/fop/fo/flow/Block.java @@ -42,20 +42,6 @@ import org.apache.fop.fo.properties.CommonRelativePosition; import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.fo.properties.SpaceProperty; -/* - Modified by Mark Lillywhite mark-fop@inomial.com. The changes - here are based on memory profiling and do not change functionality. - Essentially, the Block object had a pointer to a BlockArea object - that it created. The BlockArea was not referenced after the Block - was finished except to determine the size of the BlockArea, however - a reference to the BlockArea was maintained and this caused a lot of - GC problems, and was a major reason for FOP memory leaks. So, - the reference to BlockArea was made local, the required information - is now stored (instead of a reference to the complex BlockArea object) - and it appears that there are a lot of changes in this file, in fact - there are only a few sematic changes; mostly I just got rid of - "this." from blockArea since BlockArea is now local. - */ /** * Class modelling the fo:block object. */ @@ -361,6 +347,48 @@ public class Block extends FObjMixed { return whiteSpaceCollapse; } + /** + * @return Returns the commonAccessibility. + */ + public CommonAccessibility getCommonAccessibility() { + return this.commonAccessibility; + } + + /** + * @return Returns the commonAural. + */ + public CommonAural getCommonAural() { + return this.commonAural; + } + + /** + * @return Returns the commonRelativePosition. + */ + public CommonRelativePosition getCommonRelativePosition() { + return this.commonRelativePosition; + } + + /** + * @return Returns the hyphenationKeep. + */ + public int getHyphenationKeep() { + return this.hyphenationKeep; + } + + /** + * @return Returns the intrusionDisplace. + */ + public int getIntrusionDisplace() { + return this.intrusionDisplace; + } + + /** + * @return Returns the lineHeightShiftAdjustment. + */ + public int getLineHeightShiftAdjustment() { + return this.lineHeightShiftAdjustment; + } + /** @see org.apache.fop.fo.FONode#charIterator() */ public CharIterator charIterator() { return NullCharIterator.getInstance(); @@ -377,4 +405,5 @@ public class Block extends FObjMixed { public int getNameId() { return FO_BLOCK; } + } diff --git a/src/java/org/apache/fop/fo/flow/Marker.java b/src/java/org/apache/fop/fo/flow/Marker.java index d848e281d..f9c61d90f 100644 --- a/src/java/org/apache/fop/fo/flow/Marker.java +++ b/src/java/org/apache/fop/fo/flow/Marker.java @@ -20,9 +20,8 @@ package org.apache.fop.fo.flow; import java.util.HashMap; -import java.util.Iterator; - +import org.xml.sax.Attributes; import org.xml.sax.Locator; import org.apache.fop.apps.FOPException; @@ -30,6 +29,7 @@ import org.apache.fop.fo.FOEventHandler; import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; import org.apache.fop.fo.FObjMixed; +import org.apache.fop.fo.FOPropertyMapping; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.PropertyListMaker; import org.apache.fop.fo.ValidationException; @@ -44,7 +44,7 @@ public class Marker extends FObjMixed { // End of property values private PropertyListMaker savePropertyListMaker; - private HashMap descPLists = new HashMap(); + private HashMap descendantPropertyLists = new HashMap(); /** * Create a marker fo. @@ -76,14 +76,11 @@ public class Marker extends FObjMixed { * @param foNode the FO node whose property list is requested * @return the MarkerPropertyList of foNode */ - protected MarkerPropertyList getPList(FONode foNode) { - return (MarkerPropertyList) descPLists.get(foNode); - } - - protected PropertyList createPropertyList(PropertyList parent, FOEventHandler foEventHandler) throws FOPException { - return new MarkerPropertyList(this, parent); + protected MarkerPropertyList getPropertyListFor(FONode foNode) { + return (MarkerPropertyList) + descendantPropertyLists.get(foNode); } - + /** @see org.apache.fop.fo.FONode#startOfNode() */ protected void startOfNode() { FOEventHandler foEventHandler = getFOEventHandler(); @@ -92,30 +89,18 @@ public class Marker extends FObjMixed { foEventHandler.setPropertyListMaker(new PropertyListMaker() { public PropertyList make(FObj fobj, PropertyList parentPropertyList) { PropertyList pList = new MarkerPropertyList(fobj, parentPropertyList); - descPLists.put(fobj, pList); + descendantPropertyLists.put(fobj, pList); return pList; } }); } - + /** @see org.apache.fop.fo.FONode#endOfNode() */ protected void endOfNode() throws FOPException { super.endOfNode(); // Pop the MarkerPropertyList maker. getFOEventHandler().setPropertyListMaker(savePropertyListMaker); savePropertyListMaker = null; - // unparent the child property lists - Iterator iter = getChildNodes(); - if (iter != null) { - while (iter.hasNext()) { - FONode child = (FONode) iter.next(); - MarkerPropertyList pList - = (MarkerPropertyList) descPLists.get(child); - if (pList != null) { - pList.setParentPropertyList(null); - } - } - } } /** @@ -132,7 +117,11 @@ public class Marker extends FObjMixed { invalidChildError(loc, nsURI, localName); } } - + + protected boolean inMarker() { + return true; + } + /** * Return the "marker-class-name" property. */ @@ -160,30 +149,252 @@ public class Marker extends FObjMixed { } /** - * An implementation of PropertyList which only stores the explicit - * assigned properties. It is memory efficient but slow. + * An implementation of PropertyList which only stores the explicitly + * specified properties/attributes as bundles of name-value-namespace + * strings */ - public class MarkerPropertyList extends PropertyList { - HashMap explicit = new HashMap(); - public MarkerPropertyList(FObj fobj, PropertyList parentPropertyList) { - super(fobj, parentPropertyList); + protected class MarkerPropertyList extends PropertyList + implements Attributes { + + protected class MarkerAttribute { + + protected String namespace; + protected String qname; + protected String name; + protected String value; + + /** + * Main constructor + * @param namespace the namespace URI + * @param qname the qualified name + * @param name the name + * @param value the value + */ + public MarkerAttribute(String namespace, String qname, + String name, String value) { + this.namespace = namespace; + this.qname = qname; + this.name = (name == null ? qname : name); + this.value = value; + } + + /** + * Convenience constructor for FO attributes + * @param name the attribute name + * @param value the attribute value + */ + public MarkerAttribute(String name, String value) { + this.namespace = null; + this.qname = name; + this.name = name; + this.value = value; + } } + /** the array of attributes **/ + private MarkerAttribute[] attribs; + /** - * Set the parent property list. Used to assign a new parent - * before re-binding all the child elements. + * Overriding default constructor + * + * @param fobj the FObj to attach + * @param parentPropertyList ignored */ - public void setParentPropertyList(PropertyList parentPropertyList) { - this.parentPropertyList = parentPropertyList; + public MarkerPropertyList(FObj fobj, PropertyList parentPropertyList) { + /* ignore parentPropertyList + * won't be used because the attributes will be stored + * without resolving + */ + super(fobj, null); } + + /** + * Override that doesn't convert the attributes to Property instances, + * but simply stores the attributes for later processing; + * + * @see org.apache.fop.fo.PropertyList#addAttributesToList(Attributes) + */ + public void addAttributesToList(Attributes attributes) + throws ValidationException { + + this.attribs = new MarkerAttribute[attributes.getLength()]; + String name; + String value; + String namespace; + String qname; + + for (int i = attributes.getLength(); --i >= 0;) { + namespace = attributes.getURI(i); + qname = attributes.getQName(i); + name = attributes.getLocalName(i); + value = attributes.getValue(i); + + this.attribs[i] = + new MarkerAttribute(namespace, qname, name, value); + } + } + + /** + * Null implementation; not used by this type of PropertyList + * @see org.apache.fop.fo.PropertyList#putExplicit(int, Property) + */ public void putExplicit(int propId, Property value) { - explicit.put(new Integer(propId), value); + //nop } + /** + * Null implementation; not used by this type of PropertyList + * @see org.apache.fop.fo.PropertyList#getExplicit(int) + */ public Property getExplicit(int propId) { - return (Property) explicit.get(new Integer(propId)); + return null; + } + + /** + * @see org.xml.sax.Attributes#getLength() + */ + public int getLength() { + if (attribs == null) { + return 0; + } else { + return attribs.length; + } + } + + /** + * @see org.xml.sax.Attributes#getURI() + */ + public String getURI(int index) { + if (attribs != null + && index < attribs.length + && index >= 0 + && attribs[index] != null) { + return attribs[index].namespace; + } else { + return null; + } + } + + /** + * @see org.xml.sax.Attributes#getLocalName() + */ + public String getLocalName(int index) { + if (attribs != null + && index < attribs.length + && index >= 0 + && attribs[index] != null) { + return attribs[index].name; + } else { + return null; + } + } + + /** + * @see org.xml.sax.Attributes#getQName() + */ + public String getQName(int index) { + if (attribs != null + && index < attribs.length + && index >= 0 + && attribs[index] != null) { + return attribs[index].qname; + } else { + return null; + } } - } -} + /** + * Default implementation; not used + * @see org.xml.sax.Attributes#getType() + */ + public String getType(int index) { + return "CDATA"; + } + + /** + * @see org.xml.sax.Attributes#getValue() + */ + public String getValue(int index) { + if (attribs != null + && index < attribs.length + && index >= 0 + && attribs[index] != null) { + return attribs[index].value; + } else { + return null; + } + } + + /** + * @see org.xml.sax.Attributes#getIndex() + */ + public int getIndex(String name, String namespace) { + int index = -1; + if (attribs != null && name != null && namespace != null) { + for (int i = attribs.length; --i >= 0;) { + if (attribs[i] != null + && namespace.equals(attribs[i].namespace) + && name.equals(attribs[i].name)) { + break; + } + } + } + return index; + } + + /** + * @see org.xml.sax.Attributes#getIndex() + */ + public int getIndex(String qname) { + int index = -1; + if (attribs != null && qname != null) { + for (int i = attribs.length; --i >= 0;) { + if (attribs[i] != null + && qname.equals(attribs[i].qname)) { + break; + } + } + } + return index; + } + + /** + * Default implementation; not used + * @see org.xml.sax.Attributes#getType() + */ + public String getType(String name, String namespace) { + return "CDATA"; + } + + /** + * Default implementation; not used + * @see org.xml.sax.Attributes#getType() + */ + public String getType(String qname) { + return "CDATA"; + } + + /** + * @see org.xml.sax.Attributes#getValue() + */ + public String getValue(String name, String namespace) { + int index = getIndex(name, namespace); + if (index > 0) { + return getValue(index); + } + return null; + } + + /** + * @see org.xml.sax.Attributes#getValue() + */ + public String getValue(String qname) { + int index = getIndex(qname); + if (index > 0) { + return getValue(index); + } + return null; + } + } +} \ No newline at end of file diff --git a/src/java/org/apache/fop/fo/flow/RetrieveMarker.java b/src/java/org/apache/fop/fo/flow/RetrieveMarker.java index bbdaa24a7..8665ed810 100644 --- a/src/java/org/apache/fop/fo/flow/RetrieveMarker.java +++ b/src/java/org/apache/fop/fo/flow/RetrieveMarker.java @@ -19,24 +19,24 @@ package org.apache.fop.fo.flow; -import java.util.Map; import java.util.HashMap; import java.util.Iterator; -import java.util.ArrayList; - -import org.xml.sax.Locator; - -import org.apache.commons.logging.Log; import org.apache.fop.apps.FOPException; -import org.apache.fop.fo.FOEventHandler; import org.apache.fop.fo.FONode; -import org.apache.fop.fo.FOText; +import org.apache.fop.fo.FOPropertyMapping; import org.apache.fop.fo.FObj; import org.apache.fop.fo.FObjMixed; +import org.apache.fop.fo.FOText; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.StaticPropertyList; import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.expr.PropertyException; +import org.apache.fop.fo.properties.Property; +import org.apache.fop.fo.properties.PropertyMaker; +import org.xml.sax.Attributes; +import org.xml.sax.Locator; + /** @@ -44,7 +44,7 @@ import org.apache.fop.fo.ValidationException; * This will create a layout manager that will retrieve * a marker based on the information. */ -public class RetrieveMarker extends FObj { +public class RetrieveMarker extends FObjMixed { // The value of properties relevant for fo:retrieve-marker. private String retrieveClassName; private int retrievePosition; @@ -78,7 +78,9 @@ public class RetrieveMarker extends FObj { if (retrieveClassName == null || retrieveClassName.equals("")) { missingPropertyError("retrieve-class-name"); - } + } + + propertyList = pList.getParentPropertyList(); } /** @@ -90,40 +92,70 @@ public class RetrieveMarker extends FObj { invalidChildError(loc, nsURI, localName); } - protected PropertyList createPropertyList(PropertyList parent, - FOEventHandler foEventHandler) throws FOPException { - // TODO: A special RetrieveMarkerPropertyList would be more memory - // efficient. Storing a StaticPropertyList like this will keep all - // the parent PropertyLists alive. - propertyList = new StaticPropertyList(this, parent); - return propertyList; - } - - public PropertyList getPropertyList() { - return propertyList; - } - /** - * Return the "retrieve-class-name" property. + * @return the "retrieve-class-name" property. */ public String getRetrieveClassName() { return retrieveClassName; } /** - * Return the "retrieve-position" property. + * @return the "retrieve-position" property (enum value). */ public int getRetrievePosition() { return retrievePosition; } /** - * Return the "retrieve-boundry" property. + * @return the "retrieve-boundary" property (enum value). */ public int getRetrieveBoundary() { return retrieveBoundary; } + + private PropertyList createPropertyListFor(FObj fo, PropertyList parent) { + return getFOEventHandler().getPropertyListMaker().make(fo, parent); + } + + private void cloneSingleNode(FONode child, FONode newParent, + Marker marker, PropertyList parentPropertyList) + throws FOPException { + if (child != null) { + FONode newChild = child.clone(newParent, true); + if (child instanceof FObj) { + Marker.MarkerPropertyList pList; + PropertyList newPropertyList = createPropertyListFor( + (FObj) newChild, parentPropertyList); + + pList = marker.getPropertyListFor(child); + newChild.processNode( + child.getLocalName(), + getLocator(), + pList, + newPropertyList); + if (newChild.getNameId() == FO_TABLE) { + Table t = (Table) child; + cloneSubtree(t.getColumns().listIterator(), + newChild, marker, newPropertyList); + cloneSingleNode(t.getTableHeader(), + newChild, marker, newPropertyList); + cloneSingleNode(t.getTableFooter(), + newChild, marker, newPropertyList); + } + cloneSubtree(child.getChildNodes(), newChild, + marker, newPropertyList); + if (newChild instanceof FObjMixed) { + handleWhiteSpaceFor((FObjMixed) newChild); + } + } else if (child instanceof FOText) { + FOText ft = (FOText) newChild; + ft.bind(parentPropertyList); + } + addChildTo(newChild, (FObj) newParent); + } + } + /** * Clone the FO nodes in the parent iterator, * attach the new nodes to the new parent, @@ -136,94 +168,48 @@ public class RetrieveMarker extends FObj { * @param descPLists the map of the new nodes to property lists */ private void cloneSubtree(Iterator parentIter, FONode newParent, - Marker marker, Map descPLists) - throws FOPException { - if (parentIter == null) return; - while (parentIter.hasNext()) { - FONode child = (FONode) parentIter.next(); - FONode newChild = child.clone(newParent, true); - descPLists.put(newChild, marker.getPList(child)); - cloneSubtree(child.getChildNodes(), newChild, marker, descPLists); - } - } - - /** - * Clone the subtree of marker, - * and attach the new subtree to this node. - * The property lists are not cloned; - * the existing property lists of the direct children - * are reparented to the property list of this node. - * @param marker the marker that is to be cloned - * @param descPLists the map of the new nodes to property lists - */ - private void cloneFromMarker(Marker marker, Map descPLists) + Marker marker, PropertyList parentPropertyList) throws FOPException { - // release child nodes from a possible earlier layout - childNodes = new ArrayList(); - Iterator markerIter = marker.getChildNodes(); - cloneSubtree(markerIter, this, marker, descPLists); - // reparent the property lists of the direct children - for (Iterator iter = getChildNodes(); iter.hasNext(); ) { - FONode child = (FONode) iter.next(); - Marker.MarkerPropertyList pList - = (Marker.MarkerPropertyList) descPLists.get(child); - if (pList != null) { - pList.setParentPropertyList(propertyList); + if (parentIter != null) { + FONode child; + while (parentIter.hasNext()) { + child = (FONode) parentIter.next(); + cloneSingleNode(child, newParent, + marker, parentPropertyList); } } } - /** - * Bind the new nodes to the property values in this context - * @param descPLists the map of the new nodes to property lists - */ - private void bindChildren(Map descPLists) throws FOPException { - for (Iterator i = descPLists.keySet().iterator(); i.hasNext(); ) { - FONode desc = (FONode) i.next(); - PropertyList descPList; - if (desc instanceof FObj) { - descPList = (PropertyList) descPLists.get(desc); - ((FObj) desc).bind(descPList); - } else if (desc instanceof FOText) { - descPList = (PropertyList) descPLists.get(desc.getParent()); - if (descPList == null) { - descPList = propertyList; - } - ((FOText) desc).bind(descPList); - } + private void cloneFromMarker(Marker marker) + throws FOPException { + // clean up remnants from a possible earlier layout + if (childNodes != null) { + currentTextNode = null; + childNodes.removeAll(childNodes); } + cloneSubtree(marker.getChildNodes(), this, + marker, propertyList); } /** - * Clone the subtree of marker - * and bind the nodes to the property values in this context. - * The property lists are not cloned, - * but the subtree is attached to the property list of this node. - * This is only needed for the binding of the FO nodes. - * After that a subsequent retrieve-marker - * may reparent the property lists. + * Clone the subtree of the given marker + * * @param marker the marker that is to be cloned */ public void bindMarker(Marker marker) { - // assert(marker != null); - // catch empty marker - if (marker.getChildNodes() == null) { - return; - } - HashMap descPLists = new HashMap(); - try { - cloneFromMarker(marker, descPLists); - } catch (FOPException exc) { - Log log = getLogger(); - log.error("fo:retrieve-marker unable to clone subtree of fo:marker", exc); - return; - } - try { - bindChildren(descPLists); - } catch (FOPException exc) { - Log log = getLogger(); - log.error("fo:retrieve-marker unable to rebind property values", exc); + if (marker.getChildNodes() != null) { + try { + cloneFromMarker(marker); + } catch (FOPException exc) { + log.error("fo:retrieve-marker unable to clone " + + "subtree of fo:marker (marker-class-name=" + + marker.getMarkerClassName() + ")", exc); + return; + } + } else if (log.isInfoEnabled()) { + log.info("Empty marker retrieved..."); } + return; } /** @see org.apache.fop.fo.FONode#getLocalName() */ @@ -236,5 +222,5 @@ public class RetrieveMarker extends FObj { */ public int getNameId() { return FO_RETRIEVE_MARKER; - } -} + } +} \ No newline at end of file diff --git a/src/java/org/apache/fop/fo/flow/Table.java b/src/java/org/apache/fop/fo/flow/Table.java index 954c3ba0b..9a4724ed4 100644 --- a/src/java/org/apache/fop/fo/flow/Table.java +++ b/src/java/org/apache/fop/fo/flow/Table.java @@ -27,6 +27,7 @@ import org.xml.sax.Locator; import org.apache.fop.apps.FOPException; import org.apache.fop.datatypes.ValidationPercentBaseContext; import org.apache.fop.fo.FONode; +import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.StaticPropertyList; import org.apache.fop.fo.ValidationException; @@ -209,15 +210,19 @@ public class Table extends TableFObj { * @see org.apache.fop.fo.FONode#endOfNode */ protected void endOfNode() throws FOPException { + if (!tableBodyFound) { missingChildElementError( "(marker*,table-column*,table-header?,table-footer?" + ",table-body+)"); } - if (columns != null && !columns.isEmpty()) { - for (int i = columns.size(); --i >= 0;) { - if (isColumnNumberUsed(i + 1)) { - ((TableColumn) columns.get(i)).releasePropertyList(); + if (!inMarker()) { + if (columns != null && !columns.isEmpty()) { + for (int i = columns.size(); --i >= 0;) { + TableColumn col = (TableColumn) columns.get(i); + if (col != null) { + col.releasePropertyList(); + } } } } @@ -229,7 +234,14 @@ public class Table extends TableFObj { */ protected void addChildNode(FONode child) throws FOPException { if ("fo:table-column".equals(child.getName())) { - addColumnNode((TableColumn) child); + if (columns == null) { + columns = new java.util.ArrayList(); + } + if (!inMarker()) { + addColumnNode((TableColumn) child); + } else { + columns.add((TableColumn) child); + } } else { if ("fo:table-footer".equals(child.getName())) { tableFooter = (TableBody) child; @@ -252,9 +264,6 @@ public class Table extends TableFObj { private void addColumnNode(TableColumn col) { int colNumber = col.getColumnNumber(); int colRepeat = col.getNumberColumnsRepeated(); - if (columns == null) { - columns = new java.util.ArrayList(); - } if (columns.size() < colNumber) { //add nulls for non-occupied indices between //the last column up to and including the current one @@ -273,6 +282,10 @@ public class Table extends TableFObj { columns.add(col); } } + //flag column indices used by this column + int startIndex = columnIndex - 1; + int endIndex = startIndex + colRepeat; + flagColumnIndices(startIndex, endIndex); } /** @return true of table-layout="auto" */ @@ -458,9 +471,17 @@ public class Table extends TableFObj { } /** - * @see org.apache.fop.fo.flow.TableFObj#existsUsedColumnIndices() + * @see org.apache.fop.fo.FONode#clone(FONode, boolean) */ - protected boolean existsUsedColumnIndices() { - return (usedColumnIndices != null); + public FONode clone(FONode parent, boolean removeChildren) + throws FOPException { + FObj fobj = (FObj) super.clone(parent, removeChildren); + if (removeChildren) { + Table t = (Table) fobj; + t.columns = null; + t.tableHeader = null; + t.tableFooter = null; + } + return fobj; } } diff --git a/src/java/org/apache/fop/fo/flow/TableBody.java b/src/java/org/apache/fop/fo/flow/TableBody.java index 655279ee8..0a50b946d 100644 --- a/src/java/org/apache/fop/fo/flow/TableBody.java +++ b/src/java/org/apache/fop/fo/flow/TableBody.java @@ -25,6 +25,7 @@ import java.util.Iterator; import java.util.List; import java.util.ListIterator; +import org.xml.sax.Attributes; import org.xml.sax.Locator; import org.apache.fop.apps.FOPException; @@ -33,6 +34,7 @@ import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.StaticPropertyList; import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.flow.TableFObj.PendingSpan; import org.apache.fop.fo.properties.CommonAccessibility; import org.apache.fop.fo.properties.CommonAural; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; @@ -62,7 +64,7 @@ public class TableBody extends TableFObj { * used for initial values of column-number property */ protected List pendingSpans; - protected BitSet usedColumnIndices = new BitSet(); + protected BitSet usedColumnIndices; private int columnIndex = 1; protected boolean firstRow = true; @@ -87,11 +89,30 @@ public class TableBody extends TableFObj { savedPropertyList = pList; } + /** + * @see org.apache.fop.fo.FONode#processNode() + */ + public void processNode(String elementName, Locator locator, + Attributes attlist, PropertyList pList) + throws FOPException { + if (!inMarker()) { + if (getTable().columns != null) { + int cap = getTable().columns.size(); + pendingSpans = new java.util.ArrayList(cap); + usedColumnIndices = new java.util.BitSet(cap); + } else { + pendingSpans = new java.util.ArrayList(); + usedColumnIndices = new java.util.BitSet(); + } + setNextColumnIndex(); + } + super.processNode(elementName, locator, attlist, pList); + } + /** * @see org.apache.fop.fo.FONode#startOfNode */ protected void startOfNode() throws FOPException { - initPendingSpans(); getFOEventHandler().startBody(this); } @@ -99,7 +120,16 @@ public class TableBody extends TableFObj { * @see org.apache.fop.fo.FONode#endOfNode */ protected void endOfNode() throws FOPException { + + if (!inMarker()) { + // clean up + savedPropertyList = null; + pendingSpans = null; + usedColumnIndices = null; + } + getFOEventHandler().endBody(this); + if (!(tableRowsFound || tableCellsFound)) { if (getUserAgent().validateStrictly()) { missingChildElementError("marker* (table-row+|table-cell+)"); @@ -110,18 +140,11 @@ public class TableBody extends TableFObj { } } + /* if (tableCellsFound) { convertCellsToRows(); } - - //reset column index (so that it would be - //correct if the table is cloned during - //marker retrieval) - resetColumnIndex(); - //release references - savedPropertyList = null; - pendingSpans = null; - usedColumnIndices = null; + */ } /** @@ -158,6 +181,18 @@ public class TableBody extends TableFObj { } } + /** + * @see org.apache.fop.fo.FONode#addChildNode(FONode) + */ + protected void addChildNode(FONode child) throws FOPException { + if (!inMarker()) { + if (firstRow && child.getNameId() == FO_TABLE_ROW) { + firstRow = false; + } + } + super.addChildNode(child); + } + /** * If table-cells are used as direct children of a table-body|header|footer * they are replaced in this method by proper table-rows. @@ -243,15 +278,17 @@ public class TableBody extends TableFObj { * column-number of 2, since the first column is already * occupied...) */ - protected void initPendingSpans() { - if (getTable().columns != null) { - List tableCols = getTable().columns; - pendingSpans = new java.util.ArrayList(tableCols.size()); - for (int i = tableCols.size(); --i >= 0;) { - pendingSpans.add(null); - } - } else { - if (firstRow && pendingSpans == null) { + protected void initPendingSpans(FONode child) { + if (child.getNameId() == FO_TABLE_ROW) { + pendingSpans = ((TableRow) child).pendingSpans; + } else if (pendingSpans == null) { + if (getTable().columns != null) { + List tableCols = getTable().columns; + pendingSpans = new java.util.ArrayList(tableCols.size()); + for (int i = tableCols.size(); --i >= 0;) { + pendingSpans.add(null); + } + } else { pendingSpans = new java.util.ArrayList(); } } @@ -262,7 +299,7 @@ public class TableBody extends TableFObj { * * @return the next column number to use */ - public int getCurrentColumnIndex() { + protected int getCurrentColumnIndex() { return columnIndex; } @@ -273,7 +310,7 @@ public class TableBody extends TableFObj { * * @param newIndex the new column index */ - public void setCurrentColumnIndex(int newIndex) { + protected void setCurrentColumnIndex(int newIndex) { columnIndex = newIndex; } @@ -281,11 +318,12 @@ public class TableBody extends TableFObj { * Resets the current column index for the TableBody * */ - public void resetColumnIndex() { + protected void resetColumnIndex() { columnIndex = 1; - for (int i = 0; i < usedColumnIndices.size(); i++) { + for (int i = usedColumnIndices.length(); --i >= 0;) { usedColumnIndices.clear(i); } + PendingSpan pSpan; for (int i = pendingSpans.size(); --i >= 0;) { pSpan = (PendingSpan) pendingSpans.get(i); @@ -293,13 +331,10 @@ public class TableBody extends TableFObj { pSpan.rowsLeft--; if (pSpan.rowsLeft == 0) { pendingSpans.set(i, null); + } else { + usedColumnIndices.set(i); } } - if (pendingSpans.get(i) != null) { - usedColumnIndices.set(i); - } else { - usedColumnIndices.clear(i); - } } if (!firstRow) { setNextColumnIndex(); @@ -310,19 +345,19 @@ public class TableBody extends TableFObj { * Increases columnIndex to the next available value * */ - private void setNextColumnIndex() { + protected void setNextColumnIndex() { while (usedColumnIndices.get(columnIndex - 1)) { //increment columnIndex columnIndex++; - //if the table has explicit columns, and - //the updated index is not assigned to any - //column, increment further until the next - //index occupied by a column... - if (getTable().columns != null) { - while (columnIndex <= getTable().columns.size() - && !getTable().isColumnNumberUsed(columnIndex) ) { - columnIndex++; - } + } + //if the table has explicit columns, and + //the index is not assigned to any + //column, increment further until the next + //index occupied by a column... + if (getTable().columns != null) { + while (columnIndex <= getTable().columns.size() + && !getTable().isColumnNumberUsed(columnIndex) ) { + columnIndex++; } } } @@ -337,11 +372,10 @@ public class TableBody extends TableFObj { * b) there is no previous cell (implicit * start of row) */ - protected boolean lastCellEndedRow(TableCell currentCell) { - if (childNodes != null && childNodes.indexOf(currentCell) > 0) { - FONode prevNode = (FONode) childNodes.get( - childNodes.indexOf(currentCell) - 1); - if (prevNode != null && prevNode.getNameId() == FO_TABLE_CELL) { + protected boolean previousCellEndedRow() { + if (childNodes != null) { + FONode prevNode = (FONode) childNodes.get(childNodes.size() - 1); + if (prevNode.getNameId() == FO_TABLE_CELL) { return ((TableCell) prevNode).endsRow(); } } @@ -368,11 +402,4 @@ public class TableBody extends TableFObj { } setNextColumnIndex(); } - - /** - * @see org.apache.fop.fo.flow.TableFObj#existsUsedColumnIndices() - */ - protected boolean existsUsedColumnIndices() { - return (usedColumnIndices != null); - } } diff --git a/src/java/org/apache/fop/fo/flow/TableCell.java b/src/java/org/apache/fop/fo/flow/TableCell.java index 15cbac391..467a2a856 100644 --- a/src/java/org/apache/fop/fo/flow/TableCell.java +++ b/src/java/org/apache/fop/fo/flow/TableCell.java @@ -22,6 +22,7 @@ package org.apache.fop.fo.flow; import java.util.BitSet; import java.util.List; +import org.xml.sax.Attributes; import org.xml.sax.Locator; import org.apache.fop.apps.FOPException; @@ -30,10 +31,12 @@ import org.apache.fop.datatypes.Numeric; import org.apache.fop.fo.FONode; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.expr.PropertyException; import org.apache.fop.fo.properties.CommonAccessibility; import org.apache.fop.fo.properties.CommonAural; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonRelativePosition; +import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.fo.properties.LengthRangeProperty; /** @@ -59,6 +62,9 @@ public class TableCell extends TableFObj { private Numeric numberRowsSpanned; private int startsRow; private Length width; + private KeepProperty keepTogether; + private KeepProperty keepWithNext; + private KeepProperty keepWithPrevious; // End of property values /** used for FO validation */ @@ -94,11 +100,6 @@ public class TableCell extends TableFObj { /** Ypos of cell ??? */ protected int top; - /** - * Set to true if all content completely laid out. - */ - private boolean bDone = false; - /** * @param parent FONode that is the parent of this object */ @@ -122,16 +123,14 @@ public class TableCell extends TableFObj { height = pList.get(PR_HEIGHT).getLength(); id = pList.get(PR_ID).getString(); inlineProgressionDimension = pList.get(PR_INLINE_PROGRESSION_DIMENSION).getLengthRange(); + columnNumber = pList.get(PR_COLUMN_NUMBER).getNumeric(); numberColumnsSpanned = pList.get(PR_NUMBER_COLUMNS_SPANNED).getNumeric(); numberRowsSpanned = pList.get(PR_NUMBER_ROWS_SPANNED).getNumeric(); startsRow = pList.get(PR_STARTS_ROW).getEnum(); width = pList.get(PR_WIDTH).getLength(); - - //Check to make sure we're not in retrieve-marker context - //TODO: Can this be generalized/extended to other FOs/Properties? - if (((TableFObj) parent).existsUsedColumnIndices()) { - columnNumber = pList.get(PR_COLUMN_NUMBER).getNumeric(); - } + keepTogether = pList.get(PR_KEEP_TOGETHER).getKeep(); + keepWithNext = pList.get(PR_KEEP_WITH_NEXT).getKeep(); + keepWithPrevious = pList.get(PR_KEEP_WITH_PREVIOUS).getKeep(); super.bind(pList); } @@ -163,112 +162,9 @@ public class TableCell extends TableFObj { getLogger().warn("starts-row/ends-row for fo:table-cells " + "non-applicable for children of an fo:table-row."); } - updateParentColumnIndex(); getFOEventHandler().endCell(this); } - private void updateParentColumnIndex() { - - int rowSpan = getNumberRowsSpanned(); - int colSpan = getNumberColumnsSpanned(); - int columnIndex = ((TableFObj) parent).getCurrentColumnIndex(); - - int i = -1; - while (++i < colSpan) { - //if table has explicit columns and the column-number isn't - //assigned to any column, increment further until the next - //column is encountered - if (getTable().getColumns() != null) { - while (columnIndex <= getTable().getColumns().size() - && !getTable().isColumnNumberUsed(columnIndex)) { - columnIndex++; - } - } - //if column-number is already in use by another cell - //in the current row => error! - if (((TableFObj) parent).isColumnNumberUsed(columnIndex + i)) { - log.error("fo:table-cell overlaps in column " - + (columnIndex + i)); - } - } - - if (parent.getNameId() == FO_TABLE_ROW) { - /* parent is a fo:table-row */ - TableRow row = (TableRow) parent; - TableBody body = (TableBody) parent.getParent(); - - if (body.isFirst(row) && getTable().columns == null ) { - row.pendingSpans.add(null); - if (row.usedColumnIndices == null) { - row.usedColumnIndices = new BitSet(); - } - } - //if the current cell spans more than one row, - //update pending span list for the next row - if (rowSpan > 1) { - for (i = colSpan; --i >= 0;) { - row.pendingSpans.set(columnIndex - 1 + i, - new PendingSpan(rowSpan)); - } - } - } else { - /* parent is (should be) a fo:table-body/-header/-footer */ - TableBody body = (TableBody) parent; - - /* if body.firstRow is still true, and : - * a) the cell starts a row, - * b) there was a previous cell - * c) that previous cell didn't explicitly end the previous row - * => set firstRow flag to false - */ - if (startsRow() && body.firstRow) { - if (!body.lastCellEndedRow(this)) { - body.firstRow = false; - } - } - - /* if there were no explicit columns, pendingSpans - * will not be properly initialized for the first row... - */ - if (body.firstRow && getTable().columns == null) { - for (i = colSpan; --i >= 0;) { - body.pendingSpans.add(null); - } - } - - /* if the current cell spans more than one row, - * update pending span list for the next row - */ - if (rowSpan > 1) { - for (i = colSpan; --i >= 0;) { - body.pendingSpans.set(columnIndex - 1 + i, - new PendingSpan(rowSpan)); - } - } - } - //flag column indices used by this cell, - //take into account that possibly not all column-numbers - //are used by columns in the parent table (if any), - //so a cell spanning three columns, might actually - //take up more than three columnIndices... - int startIndex = columnIndex - 1; - int endIndex = startIndex + colSpan; - if (getTable().columns != null) { - List cols = getTable().columns; - int tmpIndex = endIndex; - for (i = startIndex; i <= tmpIndex; ++i) { - if (i < cols.size() && cols.get(i) == null) { - endIndex++; - } - } - } - ((TableFObj) parent).flagColumnIndices(startIndex, endIndex); - if (endsRow() && parent.getNameId() != FO_TABLE_ROW) { - ((TableBody) parent).firstRow = false; - ((TableBody) parent).resetColumnIndex(); - } - } - /** * @see org.apache.fop.fo.FONode#validateChildNode(Locator, String, String) * XSL Content Model: marker* (%block;)+ @@ -304,7 +200,7 @@ public class TableCell extends TableFObj { * @return the Common Border, Padding, and Background Properties. */ public CommonBorderPaddingBackground getCommonBorderPaddingBackground() { - return commonBorderPaddingBackground; + return this.commonBorderPaddingBackground; } /** diff --git a/src/java/org/apache/fop/fo/flow/TableColumn.java b/src/java/org/apache/fop/fo/flow/TableColumn.java index 9fbce1849..c82eb474a 100644 --- a/src/java/org/apache/fop/fo/flow/TableColumn.java +++ b/src/java/org/apache/fop/fo/flow/TableColumn.java @@ -78,10 +78,6 @@ public class TableColumn extends TableFObj { visibility = pList.get(PR_VISIBILITY).getEnum(); super.bind(pList); - if (getTable().isColumnNumberUsed(columnNumber.getValue())) { - throw new PropertyException("column-number \"" + columnNumber - + "\" has already been assigned to a previous column"); - } if (numberColumnsRepeated.getValue() <= 0) { throw new PropertyException("number-columns-repeated must be 1 or bigger, " + "but got " + numberColumnsRepeated.getValue()); @@ -104,10 +100,6 @@ public class TableColumn extends TableFObj { * @see org.apache.fop.fo.FONode#endOfNode */ protected void endOfNode() throws FOPException { - //flag column indices used by this column - int startIndex = getColumnNumber() - 1; - int endIndex = startIndex + getNumberColumnsRepeated(); - getTable().flagColumnIndices(startIndex, endIndex); getFOEventHandler().endColumn(this); } @@ -211,6 +203,4 @@ public class TableColumn extends TableFObj { protected void releasePropertyList() { this.pList = null; } - -} - +} \ No newline at end of file diff --git a/src/java/org/apache/fop/fo/flow/TableFObj.java b/src/java/org/apache/fop/fo/flow/TableFObj.java index 1497f2c6e..d2ea03d35 100644 --- a/src/java/org/apache/fop/fo/flow/TableFObj.java +++ b/src/java/org/apache/fop/fo/flow/TableFObj.java @@ -19,13 +19,23 @@ package org.apache.fop.fo.flow; +import java.util.BitSet; +import java.util.Iterator; +import java.util.List; + import org.apache.fop.apps.FOPException; import org.apache.fop.datatypes.Numeric; import org.apache.fop.datatypes.ValidationPercentBaseContext; +import org.apache.fop.fo.Constants; import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; +import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.expr.PropertyException; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; +import org.apache.fop.fo.properties.NumberProperty; +import org.apache.fop.fo.properties.Property; +import org.apache.fop.fo.properties.PropertyMaker; /** * Superclass for table-related FOs @@ -89,12 +99,128 @@ public abstract class TableFObj extends FObj { if (getNameId() != FO_TABLE //Separate check for fo:table in Table.java && getNameId() != FO_TABLE_CELL && getCommonBorderPaddingBackground().hasPadding( - ValidationPercentBaseContext.getPseudoContextForValidationPurposes())) { - attributeWarning("padding-* properties are not applicable to " + getName() + ValidationPercentBaseContext + .getPseudoContextForValidationPurposes())) { + attributeWarning( + "padding-* properties are not applicable to " + getName() + ", but a non-zero value for padding was found."); } } + /** + * @see org.apache.fop.fo.FONode#addChildNode(FONode) + */ + protected void addChildNode(FONode child) throws FOPException { + if (!inMarker() + && child.getNameId() == FO_TABLE_CELL) { + /* update current column index for the table-body/table-row */ + updateColumnIndex((TableCell) child); + } + super.addChildNode(child); + } + + private void updateColumnIndex(TableCell cell) + throws ValidationException { + + int rowSpan = cell.getNumberRowsSpanned(); + int colSpan = cell.getNumberColumnsSpanned(); + int columnIndex = getCurrentColumnIndex(); + + int i = -1; + while (++i < colSpan) { + if (isColumnNumberUsed(columnIndex + i)) { + /* if column-number is already in use by another cell + * in the current row => error! + */ + StringBuffer errorMessage = new StringBuffer(); + errorMessage.append("fo:table-cell overlaps in column ") + .append(columnIndex + i); + if (locator.getLineNumber() != -1) { + errorMessage.append(" (line #") + .append(locator.getLineNumber()).append(", column #") + .append(locator.getColumnNumber()).append(")"); + } + throw new ValidationException(errorMessage.toString()); + } + } + + if (getNameId() == FO_TABLE_ROW) { + + TableRow row = (TableRow) this; + TableBody body = (TableBody) parent; + + for (i = colSpan; --i >= 0;) { + row.pendingSpans.add(null); + } + + /* if the current cell spans more than one row, + * update pending span list for the next row + */ + if (rowSpan > 1) { + for (i = colSpan; --i >= 0;) { + row.pendingSpans.set(columnIndex - 1 + i, + new PendingSpan(rowSpan)); + } + } + } else { + + TableBody body = (TableBody) this; + + /* if body.firstRow is still true, and : + * a) the cell starts a row, + * b) there was a previous cell + * c) that previous cell didn't explicitly end the previous row + * => set firstRow flag to false + */ + if (body.firstRow && cell.startsRow()) { + if (!body.previousCellEndedRow()) { + body.firstRow = false; + } + } + + /* pendingSpans not initialized for the first row... + */ + if (body.firstRow) { + for (i = colSpan; --i >= 0;) { + body.pendingSpans.add(null); + } + } + + /* if the current cell spans more than one row, + * update pending span list for the next row + */ + if (rowSpan > 1) { + for (i = colSpan; --i >= 0;) { + body.pendingSpans.set(columnIndex - 1 + i, + new PendingSpan(rowSpan)); + } + } + } + + /* flag column indices used by this cell, + * take into account that possibly not all column-numbers + * are used by columns in the parent table (if any), + * so a cell spanning three columns, might actually + * take up more than three columnIndices... + */ + int startIndex = columnIndex - 1; + int endIndex = startIndex + colSpan; + if (getTable().columns != null) { + List cols = getTable().columns; + int tmpIndex = endIndex; + for (i = startIndex; i <= tmpIndex; ++i) { + if (i < cols.size() && cols.get(i) == null) { + endIndex++; + } + } + } + flagColumnIndices(startIndex, endIndex); + if (getNameId() != FO_TABLE_ROW && cell.endsRow()) { + ((TableBody) this).firstRow = false; + ((TableBody) this).resetColumnIndex(); + } + } + /** * * @param side the side for which to return the border precedence @@ -121,7 +247,7 @@ public abstract class TableFObj extends FObj { * * @return the next column number to use */ - public int getCurrentColumnIndex() { + protected int getCurrentColumnIndex() { return 0; } @@ -133,10 +259,10 @@ public abstract class TableFObj extends FObj { * * @param newIndex new value for column index */ - public void setCurrentColumnIndex(int newIndex) { + protected void setCurrentColumnIndex(int newIndex) { //do nothing by default } - + /** * Checks if a certain column-number is already occupied * (overridden for Table, TableBody, TableRow) @@ -184,10 +310,83 @@ public abstract class TableFObj extends FObj { } /** - * Overridden for Table, TableBody, TableRow - * @return true if the usedColumnIndices BitSet exists, and is initialized + * PropertyMaker subclass for the column-number property + * */ - protected boolean existsUsedColumnIndices() { - return false; + public static class ColumnNumberPropertyMaker extends NumberProperty.Maker { + + /** + * Constructor + * @param propId the id of the property for which the maker should + * be created + */ + public ColumnNumberPropertyMaker(int propId) { + super(propId); + } + + /** + * @see PropertyMaker#make(PropertyList) + */ + public Property make(PropertyList propertyList) + throws PropertyException { + FObj fo = propertyList.getFObj(); + + if (fo.getNameId() == Constants.FO_TABLE_CELL + || fo.getNameId() == Constants.FO_TABLE_COLUMN) { + if (fo.getNameId() == Constants.FO_TABLE_CELL + && fo.getParent().getNameId() != Constants.FO_TABLE_ROW + && (propertyList.get(Constants.PR_STARTS_ROW).getEnum() + == Constants.EN_TRUE)) { + TableBody parent = (TableBody) fo.getParent(); + if (!parent.previousCellEndedRow()) { + parent.resetColumnIndex(); + } + } + return new NumberProperty(((TableFObj) fo.getParent()) + .getCurrentColumnIndex()); + } else { + throw new PropertyException( + "column-number property is only allowed" + + " on fo:table-cell or fo:table-column, not on " + + fo.getName()); + } + } + + /** + * Check the value of the column-number property. + * Return the parent's column index (initial value) in case + * of a negative or zero value + * + * @see org.apache.fop.fo.properties.PropertyMaker#get( + * int, PropertyList, boolean, boolean) + */ + public Property get(int subpropId, PropertyList propertyList, + boolean tryInherit, boolean tryDefault) + throws PropertyException { + + Property p = super.get(0, propertyList, tryInherit, tryDefault); + TableFObj fo = (TableFObj) propertyList.getFObj(); + TableFObj parent = (TableFObj) propertyList.getParentFObj(); + int columnIndex = p.getNumeric().getValue(); + + if (columnIndex <= 0) { + fo.getLogger().warn("Specified negative or zero value for " + + "column-number on " + fo.getName() + ": " + + columnIndex + " forced to " + + parent.getCurrentColumnIndex()); + return new NumberProperty(parent.getCurrentColumnIndex()); + } + //TODO: check for non-integer value and round + + /* if column-number was explicitly specified, force the + * parent's current column index to the specified value, + * so that the updated index will be the correct initial + * value for the next cell/column (see Rec 7.26.8) + */ + if (propertyList.getExplicit(Constants.PR_COLUMN_NUMBER) != null) { + parent.setCurrentColumnIndex(p.getNumeric().getValue()); + } + return p; + } } } diff --git a/src/java/org/apache/fop/fo/flow/TableFooter.java b/src/java/org/apache/fop/fo/flow/TableFooter.java index e698b1d55..82c698c0f 100644 --- a/src/java/org/apache/fop/fo/flow/TableFooter.java +++ b/src/java/org/apache/fop/fo/flow/TableFooter.java @@ -40,7 +40,6 @@ public class TableFooter extends TableBody { * @see org.apache.fop.fo.FONode#startOfNode */ protected void startOfNode() throws FOPException { - initPendingSpans(); //getFOEventHandler().startBody(this); } diff --git a/src/java/org/apache/fop/fo/flow/TableHeader.java b/src/java/org/apache/fop/fo/flow/TableHeader.java index ac6e96871..a487dd072 100644 --- a/src/java/org/apache/fop/fo/flow/TableHeader.java +++ b/src/java/org/apache/fop/fo/flow/TableHeader.java @@ -40,7 +40,6 @@ public class TableHeader extends TableBody { * @see org.apache.fop.fo.FONode#startOfNode */ protected void startOfNode() throws FOPException { - initPendingSpans(); //getFOEventHandler().startHeader(this); } diff --git a/src/java/org/apache/fop/fo/flow/TableRow.java b/src/java/org/apache/fop/fo/flow/TableRow.java index f93720545..1f0cb14ae 100644 --- a/src/java/org/apache/fop/fo/flow/TableRow.java +++ b/src/java/org/apache/fop/fo/flow/TableRow.java @@ -22,6 +22,7 @@ package org.apache.fop.fo.flow; import java.util.BitSet; import java.util.List; +import org.xml.sax.Attributes; import org.xml.sax.Locator; import org.apache.fop.apps.FOPException; @@ -104,24 +105,30 @@ public class TableRow extends TableFObj { childNodes.add(cell); } + /** + * @see org.apache.fop.fo.FONode#processNode(String, Locator, + * Attributes, PropertyList) + */ + public void processNode(String elementName, Locator locator, + Attributes attlist, PropertyList pList) throws FOPException { + if (!inMarker()) { + TableBody body = (TableBody) parent; + body.resetColumnIndex(); + pendingSpans = body.pendingSpans; + usedColumnIndices = body.usedColumnIndices; + while (usedColumnIndices.get(columnIndex - 1)) { + columnIndex++; + } + } + super.processNode(elementName, locator, attlist, pList); + } + /** * @see org.apache.fop.fo.FONode#startOfNode */ protected void startOfNode() throws FOPException { - pendingSpans = ((TableBody) parent).pendingSpans; - usedColumnIndices = ((TableBody) parent).usedColumnIndices; - while (usedColumnIndices.get(columnIndex - 1)) { - columnIndex++; - } - checkId(id); getFOEventHandler().startRow(this); - if (((TableBody) parent).isFirst(this) - && getTable().columns == null ) { - if (pendingSpans == null) { - pendingSpans = new java.util.ArrayList(); - } - } } /** @@ -131,17 +138,10 @@ public class TableRow extends TableFObj { if (childNodes == null) { missingChildElementError("(table-cell+)"); } - if (((TableBody) parent).isFirst(this) - && getTable().columns == null ) { - //force parent body's pendingSpans - //to the one accumulated after processing this row - ((TableBody) parent).pendingSpans = pendingSpans; + if (!inMarker()) { + pendingSpans = null; + usedColumnIndices = null; } - ((TableBody) parent).resetColumnIndex(); - columnIndex = 1; - //release references - pendingSpans = null; - usedColumnIndices = null; getFOEventHandler().endRow(this); } @@ -155,7 +155,7 @@ public class TableRow extends TableFObj { if (!(FO_URI.equals(nsURI) && localName.equals("table-cell"))) { invalidChildError(loc, nsURI, localName); } - } + } /** * @return the "id" property. @@ -292,12 +292,5 @@ public class TableRow extends TableFObj { while (usedColumnIndices.get(columnIndex - 1)) { columnIndex++; } - } - - /** - * @see org.apache.fop.fo.flow.TableFObj#existsUsedColumnIndices() - */ - protected boolean existsUsedColumnIndices() { - return (usedColumnIndices != null); - } + } } diff --git a/src/java/org/apache/fop/fo/pagination/PageSequence.java b/src/java/org/apache/fop/fo/pagination/PageSequence.java index 56796f0da..386ed694b 100644 --- a/src/java/org/apache/fop/fo/pagination/PageSequence.java +++ b/src/java/org/apache/fop/fo/pagination/PageSequence.java @@ -539,4 +539,12 @@ public class PageSequence extends FObj { return this.language; } + /** + * Releases a page-sequence's children after the page-sequence has been fully processed. + */ + public void releasePageSequence() { + this.mainFlow = null; + this.flowMap.clear(); + } + } diff --git a/src/java/org/apache/fop/fo/properties/ColumnNumberPropertyMaker.java b/src/java/org/apache/fop/fo/properties/ColumnNumberPropertyMaker.java deleted file mode 100644 index e1f6928cc..000000000 --- a/src/java/org/apache/fop/fo/properties/ColumnNumberPropertyMaker.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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.fo.properties; - -import java.util.Iterator; - -import org.apache.fop.fo.Constants; -import org.apache.fop.fo.FObj; -import org.apache.fop.fo.PropertyList; -import org.apache.fop.fo.expr.PropertyException; -import org.apache.fop.fo.flow.TableBody; -import org.apache.fop.fo.flow.TableCell; -import org.apache.fop.fo.flow.TableFObj; - -/** - * Maker class for the column-number property on table-cells and - * table-columns - * - */ -public class ColumnNumberPropertyMaker extends NumberProperty.Maker { - - /** - * Constructor - * @param propId the id of the property for which the maker should be created - */ - public ColumnNumberPropertyMaker(int propId) { - super(propId); - } - - /** - * @see PropertyMaker#make(PropertyList) - */ - public Property make(PropertyList propertyList) throws PropertyException { - FObj fo = propertyList.getFObj(); - - if (fo.getNameId() == Constants.FO_TABLE_CELL - || fo.getNameId() == Constants.FO_TABLE_COLUMN) { - if (fo.getNameId() == Constants.FO_TABLE_CELL - && fo.getParent().getNameId() != Constants.FO_TABLE_ROW - && (propertyList.get(Constants.PR_STARTS_ROW).getEnum() - == Constants.EN_TRUE)) { - TableBody parent = (TableBody) fo.getParent(); - TableCell prevCell = null; - for (Iterator i = parent.getChildNodes(); - (i != null && i.hasNext());) { - prevCell = (TableCell) i.next(); - } - if (prevCell != null && !prevCell.endsRow()) { - parent.resetColumnIndex(); - } - } - return new NumberProperty(((TableFObj) fo.getParent()) - .getCurrentColumnIndex()); - } else { - throw new PropertyException("column-number property is only allowed" - + " on fo:table-cell or fo:table-column, not on " - + fo.getName()); - } - } - - /** - * Check the value of the column-number property. - * Return the parent's column index (initial value) in case - * of a negative or zero value - * - * @see org.apache.fop.fo.properties.PropertyMaker#get( - * int, PropertyList, boolean, boolean) - */ - public Property get(int subpropId, PropertyList propertyList, - boolean tryInherit, boolean tryDefault) - throws PropertyException { - - Property p = super.get(0, propertyList, tryInherit, tryDefault); - TableFObj fo = (TableFObj) propertyList.getFObj(); - TableFObj parent = (TableFObj) propertyList.getParentFObj(); - int columnIndex = p.getNumeric().getValue(); - - if (columnIndex <= 0) { - fo.getLogger().warn("Specified negative or zero value for " - + "column-number on " + fo.getName() + ": " - + columnIndex + " forced to " - + parent.getCurrentColumnIndex()); - return new NumberProperty(parent.getCurrentColumnIndex()); - } - //TODO: check for non-integer value and round - - //if column-number was explicitly specified, force the parent's current - //column index to the specified value, so that the updated index will - //be the correct initial value for the next cell/column (see Rec 7.26.8) - if (propertyList.getExplicit(Constants.PR_COLUMN_NUMBER) != null) { - parent.setCurrentColumnIndex(p.getNumeric().getValue()); - } - return p; - } -} diff --git a/src/java/org/apache/fop/fo/properties/FontShorthandProperty.java b/src/java/org/apache/fop/fo/properties/FontShorthandProperty.java index e652d153d..69177ad29 100644 --- a/src/java/org/apache/fop/fo/properties/FontShorthandProperty.java +++ b/src/java/org/apache/fop/fo/properties/FontShorthandProperty.java @@ -53,136 +53,142 @@ public class FontShorthandProperty extends ListProperty { public Property make(PropertyList propertyList, String value, FObj fo) throws PropertyException { - FontShorthandProperty newProp = new FontShorthandProperty(); - newProp.setSpecifiedValue(value); - - String specVal = value; - Property prop = null; - if ("inherit".equals(specVal)) { - for (int i = PROP_IDS.length; --i >= 0;) { - prop = propertyList.getFromParent(PROP_IDS[i]); - newProp.addProperty(prop, i); - } - } else { - /* initialize list with nulls */ - for (int pos = 6; --pos >= 0;) { - newProp.addProperty(null, pos); - } - prop = checkEnumValues(specVal); - if (prop == null) { - /* not an enum: - * value should consist at least of font-size and font-family - * separated by a space - * mind the possible spaces from quoted font-family names - */ - int spaceIndex = value.indexOf(' '); - int quoteIndex = (value.indexOf('\'') == -1) - ? value.indexOf('\"') : value.indexOf('\''); - if (spaceIndex == -1 - || (quoteIndex != -1 && spaceIndex > quoteIndex)) { - /* no spaces or first space appears after the first - * single/double quote, so malformed value string + try { + FontShorthandProperty newProp = new FontShorthandProperty(); + newProp.setSpecifiedValue(value); + + String specVal = value; + Property prop = null; + if ("inherit".equals(specVal)) { + /* fill the list with the individual properties from the parent */ + for (int i = PROP_IDS.length; --i >= 0;) { + prop = propertyList.getFromParent(PROP_IDS[i]); + newProp.addProperty(prop, i); + } + } else { + /* initialize list with nulls */ + for (int pos = PROP_IDS.length; --pos >= 0;) { + newProp.addProperty(null, pos); + } + prop = checkEnumValues(specVal); + if (prop == null) { + /* not an enum: + * value should consist at least of font-size and font-family + * separated by a space + * mind the possible spaces from quoted font-family names */ - throw new PropertyException("Invalid property value: " - + "font=\"" + value + "\""); - } - PropertyMaker m = null; - int fromIndex = spaceIndex + 1; - int toIndex = specVal.length(); - /* at least one space that appears before the first - * single/double quote, so extract the individual properties - */ - boolean fontFamilyParsed = false; - int commaIndex = value.indexOf(','); - while (!fontFamilyParsed) { - /* value contains a (list of) possibly quoted - * font-family name(s) + int spaceIndex = value.indexOf(' '); + int quoteIndex = (value.indexOf('\'') == -1) + ? value.indexOf('\"') : value.indexOf('\''); + if (spaceIndex == -1 + || (quoteIndex != -1 && spaceIndex > quoteIndex)) { + /* no spaces or first space appears after the first + * single/double quote, so malformed value string + */ + throw new PropertyException("Invalid property value: " + + "font=\"" + value + "\""); + } + PropertyMaker m = null; + int fromIndex = spaceIndex + 1; + int toIndex = specVal.length(); + /* at least one space that appears before the first + * single/double quote, so extract the individual properties */ - if (commaIndex == -1) { - /* no list, just a single name - * (or first name in the list) + boolean fontFamilyParsed = false; + int commaIndex = value.indexOf(','); + while (!fontFamilyParsed) { + /* value contains a (list of) possibly quoted + * font-family name(s) */ - if (quoteIndex != -1) { - /* a single name, quoted + if (commaIndex == -1) { + /* no list, just a single name + * (or first name in the list) */ - fromIndex = quoteIndex; - } - m = FObj.getPropertyMakerFor(PROP_IDS[1]); - prop = m.make(propertyList, specVal.substring(fromIndex), fo); - newProp.addProperty(prop, 1); - fontFamilyParsed = true; - } else { - if (quoteIndex != -1 && quoteIndex < commaIndex) { - /* a quoted font-family name as first name - * in the comma-separated list - * fromIndex = index of the first quote - */ - fromIndex = quoteIndex; - quoteIndex = -1; + if (quoteIndex != -1) { + /* a single name, quoted + */ + fromIndex = quoteIndex; + } + m = FObj.getPropertyMakerFor(PROP_IDS[1]); + prop = m.make(propertyList, specVal.substring(fromIndex), fo); + newProp.addProperty(prop, 1); + fontFamilyParsed = true; } else { - fromIndex = value.lastIndexOf(' ', commaIndex) + 1; + if (quoteIndex != -1 && quoteIndex < commaIndex) { + /* a quoted font-family name as first name + * in the comma-separated list + * fromIndex = index of the first quote + */ + fromIndex = quoteIndex; + quoteIndex = -1; + } else { + fromIndex = value.lastIndexOf(' ', commaIndex) + 1; + } + commaIndex = -1; } - commaIndex = -1; } - } - toIndex = fromIndex - 1; - fromIndex = value.lastIndexOf(' ', toIndex - 1) + 1; - value = specVal.substring(fromIndex, toIndex); - int slashIndex = value.indexOf('/'); - String fontSize = value.substring(0, - (slashIndex == -1) ? value.length() : slashIndex); - m = FObj.getPropertyMakerFor(PROP_IDS[0]); - prop = m.make(propertyList, fontSize, fo); - /* need to make sure subsequent call to LineHeightPropertyMaker.make() - * doesn't generate the default font-size property... - */ - propertyList.putExplicit(PROP_IDS[0], prop); - newProp.addProperty(prop, 0); - if (slashIndex != -1) { - /* line-height */ - String lineHeight = value.substring(slashIndex + 1); - m = FObj.getPropertyMakerFor(PROP_IDS[2]); - prop = m.make(propertyList, lineHeight, fo); - newProp.addProperty(prop, 2); - } - if (fromIndex != 0) { toIndex = fromIndex - 1; - value = specVal.substring(0, toIndex); - fromIndex = 0; - spaceIndex = value.indexOf(' '); - do { - toIndex = (spaceIndex == -1) ? value.length() : spaceIndex; - String val = value.substring(fromIndex, toIndex); - for (int i = 6; --i >= 3;) { - if (newProp.list.get(i) == null) { - /* not set */ - m = FObj.getPropertyMakerFor(PROP_IDS[i]); - val = m.checkValueKeywords(val); - prop = m.checkEnumValues(val); - if (prop != null) { - newProp.addProperty(prop, i); + fromIndex = value.lastIndexOf(' ', toIndex - 1) + 1; + value = specVal.substring(fromIndex, toIndex); + int slashIndex = value.indexOf('/'); + String fontSize = value.substring(0, + (slashIndex == -1) ? value.length() : slashIndex); + m = FObj.getPropertyMakerFor(PROP_IDS[0]); + prop = m.make(propertyList, fontSize, fo); + /* need to make sure subsequent call to LineHeightPropertyMaker.make() + * doesn't generate the default font-size property... + */ + propertyList.putExplicit(PROP_IDS[0], prop); + newProp.addProperty(prop, 0); + if (slashIndex != -1) { + /* line-height */ + String lineHeight = value.substring(slashIndex + 1); + m = FObj.getPropertyMakerFor(PROP_IDS[2]); + prop = m.make(propertyList, lineHeight, fo); + newProp.addProperty(prop, 2); + } + if (fromIndex != 0) { + toIndex = fromIndex - 1; + value = specVal.substring(0, toIndex); + fromIndex = 0; + spaceIndex = value.indexOf(' '); + do { + toIndex = (spaceIndex == -1) ? value.length() : spaceIndex; + String val = value.substring(fromIndex, toIndex); + for (int i = 6; --i >= 3;) { + if (newProp.list.get(i) == null) { + /* not set */ + m = FObj.getPropertyMakerFor(PROP_IDS[i]); + val = m.checkValueKeywords(val); + prop = m.checkEnumValues(val); + if (prop != null) { + newProp.addProperty(prop, i); + } } } - } - fromIndex = toIndex + 1; - spaceIndex = value.indexOf(' ', fromIndex); - } while (toIndex != value.length()); + fromIndex = toIndex + 1; + spaceIndex = value.indexOf(' ', fromIndex); + } while (toIndex != value.length()); + } + } else { + //TODO: implement enum values + log.warn("Enum values other than \"inherit\"" + + " not yet supported for the font shorthand."); + return null; } - } else { - //TODO: implement enum values - log.warn("Enum values other than \"inherit\"" - + " not yet supported for the font shorthand."); - return null; } - } - if (newProp.list.get(0) == null || newProp.list.get(1) == null) { - throw new PropertyException("Invalid property value: " - + "font-size and font-family are required for the font shorthand" - + "\nfont=" + value); - } - return newProp; + if (newProp.list.get(0) == null || newProp.list.get(1) == null) { + throw new PropertyException("Invalid property value: " + + "font-size and font-family are required for the font shorthand" + + "\nfont=\"" + value + "\""); + } + return newProp; + } catch (PropertyException pe) { + pe.setLocator(propertyList.getFObj().getLocator()); + pe.setPropertyName(getName()); + throw pe; + } } - } private void addProperty(Property prop, int pos) { diff --git a/src/java/org/apache/fop/fonts/FontSetup.java b/src/java/org/apache/fop/fonts/FontSetup.java index fffcf9c62..4c9e3b768 100644 --- a/src/java/org/apache/fop/fonts/FontSetup.java +++ b/src/java/org/apache/fop/fonts/FontSetup.java @@ -46,6 +46,9 @@ import org.apache.avalon.framework.configuration.ConfigurationException; // Java import java.util.List; +import javax.xml.transform.Source; +import javax.xml.transform.stream.StreamSource; + /** * Default fonts for FOP application; currently this uses PDF's fonts * by default. @@ -194,6 +197,11 @@ public class FontSetup { return; //No fonts to process } + if (resolver == null) { + //Ensure that we have minimal font resolution capabilities + resolver = createMinimalFontResolver(); + } + String internalName = null; //FontReader reader = null; @@ -229,6 +237,18 @@ public class FontSetup { } } + /** @return a new FontResolver to be used by the font subsystem */ + private static FontResolver createMinimalFontResolver() { + return new FontResolver() { + + /** @see org.apache.fop.fonts.FontResolver#resolve(java.lang.String) */ + public Source resolve(String href) { + //Minimal functionality here + return new StreamSource(href); + } + + }; + } /** * Builds a list of EmbedFontInfo objects for use with the setup() method. * @param cfg Configuration object diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java index 9e51715c6..651392e87 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java @@ -202,6 +202,15 @@ public abstract class AbstractBreaker { return null; } + /** + * Returns a PageBreakingLayoutListener for the PageBreakingAlgorithm to notify about layout + * problems. + * @return the listener instance or null if no notifications are needed + */ + protected PageBreakingAlgorithm.PageBreakingLayoutListener getLayoutListener() { + return null; + } + /* * This method is to contain the logic to determine the LM's * getNextKnuthElements() implementation(s) that are to be called. @@ -316,7 +325,7 @@ public abstract class AbstractBreaker { log.debug("PLM> start of algorithm (" + this.getClass().getName() + "), flow BPD =" + flowBPD); PageBreakingAlgorithm alg = new PageBreakingAlgorithm(getTopLevelLM(), - getPageProvider(), + getPageProvider(), getLayoutListener(), alignment, alignmentLast, footnoteSeparatorLength, floatSeparatorLength, isPartOverflowRecoveryActivated(), autoHeight, isSinglePartFavored()); int iOptPageCount; diff --git a/src/java/org/apache/fop/layoutmgr/BalancingColumnBreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/BalancingColumnBreakingAlgorithm.java index b82a6a6c4..632a3781d 100644 --- a/src/java/org/apache/fop/layoutmgr/BalancingColumnBreakingAlgorithm.java +++ b/src/java/org/apache/fop/layoutmgr/BalancingColumnBreakingAlgorithm.java @@ -21,6 +21,7 @@ package org.apache.fop.layoutmgr; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener; import org.apache.fop.traits.MinOptMax; /** @@ -37,12 +38,14 @@ public class BalancingColumnBreakingAlgorithm extends PageBreakingAlgorithm { public BalancingColumnBreakingAlgorithm(LayoutManager topLevelLM, PageSequenceLayoutManager.PageProvider pageProvider, + PageBreakingLayoutListener layoutListener, int alignment, int alignmentLast, MinOptMax footnoteSeparatorLength, MinOptMax floatSeparatorLength, boolean partOverflowRecovery, int columnCount) { - super(topLevelLM, pageProvider, alignment, alignmentLast, + super(topLevelLM, pageProvider, layoutListener, + alignment, alignmentLast, footnoteSeparatorLength, floatSeparatorLength, partOverflowRecovery, false, false); this.columnCount = columnCount; this.considerTooShort = true; //This is important! diff --git a/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java index 70104a787..7d6c77836 100644 --- a/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java +++ b/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java @@ -50,7 +50,7 @@ public abstract class BreakingAlgorithm { /** Maximum adjustment ration */ protected static final int INFINITE_RATIO = 1000; - private static final int MAX_RECOVERY_ATTEMPTS = 50; + private static final int MAX_RECOVERY_ATTEMPTS = 5; // constants identifying a subset of the feasible breaks /** All feasible breaks are ok. */ @@ -76,7 +76,7 @@ public abstract class BreakingAlgorithm { * The threshold for considering breaks to be acceptable. The adjustment ratio must be * inferior to this threshold. */ - private double threshold; + protected double threshold; /** * The paragraph of KnuthElements. @@ -91,22 +91,22 @@ public abstract class BreakingAlgorithm { /** Force the algorithm to find a set of breakpoints, even if no feasible breakpoints * exist. */ - private boolean force = false; + boolean force = false; /** If set to true, doesn't ignore break possibilities which are definitely too short. */ protected boolean considerTooShort = false; /** When in forced mode, the best node leading to a too long line. The line will be * too long anyway, but this one will lead to a paragraph with fewest demerits. */ - private KnuthNode lastTooLong; + protected KnuthNode lastTooLong; /** When in forced mode, the best node leading to a too short line. The line will be * too short anyway, but this one will lead to a paragraph with fewest demerits. */ - private KnuthNode lastTooShort; + protected KnuthNode lastTooShort; /** The node to be reactivated if no set of feasible breakpoints can be found for this * paragraph. */ - private KnuthNode lastDeactivated; + protected KnuthNode lastDeactivated; /** Alignment of the paragraph/page. One of EN_START, EN_JUSTIFY, etc. */ protected int alignment; @@ -152,10 +152,13 @@ public abstract class BreakingAlgorithm { */ protected int totalShrink = 0; + protected SumsAfter sumsAfter = new SumsAfter(); + protected BestRecords best; /** @see #isPartOverflowRecoveryActivated() */ private boolean partOverflowRecoveryActivated = true; + private KnuthNode lastRecovered; /** * Create a new instance. @@ -175,15 +178,62 @@ public abstract class BreakingAlgorithm { alignmentLast = alignLast; bFirst = first; this.partOverflowRecoveryActivated = partOverflowRecovery; - this.best = new BestRecords(); + this.best = new BestRecords(log); maxFlaggedPenaltiesCount = maxFlagCount; } + protected static class SumsAfter { + private KnuthSequence par; + private int widthAfter; + private int stretchAfter; + private int shrinkAfter; + private int currentElementIndex = -1; + + void compute(int elementIndex, int totalWidth, int totalStretch, int totalShrink) { + if (currentElementIndex == elementIndex) { + return; + } + currentElementIndex = elementIndex; + widthAfter = totalWidth; + stretchAfter = totalStretch; + shrinkAfter = totalShrink; + for (int i = elementIndex; i < par.size(); i++) { + KnuthElement element = (KnuthElement) par.get(i); + if (element.isBox()) { + break; + } else if (element.isGlue()) { + widthAfter += element.getW(); + stretchAfter += element.getY(); + shrinkAfter += element.getZ(); + } else if (element.isForcedBreak() && i != elementIndex) { + break; + } + } + } + + void initialize(KnuthSequence par) { + this.par = par; + currentElementIndex = -1; + } + + int getWidthAfter() { + return widthAfter; + } + + int getStretchAfter() { + return stretchAfter; + } + + int getShrinkAfter() { + return shrinkAfter; + } + } + /** * Class recording all the informations of a feasible breaking point. */ - public class KnuthNode { + public static class KnuthNode { /** index of the breakpoint represented by this node */ public int position; @@ -258,10 +308,11 @@ public abstract class BreakingAlgorithm { /** Class that stores, for each fitness class, the best active node that could start * a line of the corresponding fitness ending at the current element. */ - protected class BestRecords { + protected static class BestRecords { private static final double INFINITE_DEMERITS = Double.POSITIVE_INFINITY; //private static final double INFINITE_DEMERITS = 1E11; + private Log log; private double[] bestDemerits = new double[4]; private KnuthNode[] bestNode = new KnuthNode[4]; private double[] bestAdjust = new double[4]; @@ -271,7 +322,8 @@ public abstract class BreakingAlgorithm { /** Points to the fitness class which currently leads to the best demerits. */ private int bestIndex = -1; - public BestRecords() { + public BestRecords(Log log) { + this.log = log; reset(); } @@ -446,7 +498,7 @@ public abstract class BreakingAlgorithm { // create an active node representing the starting point activeLines = new KnuthNode[20]; - addNode(0, createNode(firstBoxIndex, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, null)); + registerActiveNode(createNode(firstBoxIndex, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, null)); if (log.isTraceEnabled()) { log.trace("Looping over " + (par.size() - startIndex) + " elements"); @@ -495,6 +547,16 @@ public abstract class BreakingAlgorithm { } if (lastTooShort == null || lastForced.position == lastTooShort.position) { if (isPartOverflowRecoveryActivated()) { + if (this.lastRecovered == null) { + this.lastRecovered = lastTooLong; + if (log.isDebugEnabled()) { + log.debug("Recovery point: " + lastRecovered); + } + } + if (lastTooLong == null) { + log.debug("lastTooLong null! lastTooShort: " + + new Boolean(lastTooShort != null)); + } // content would overflow, insert empty line/page and try again KnuthNode node = createNode( lastTooLong.previous.position, lastTooLong.previous.line + 1, 1, @@ -503,23 +565,34 @@ public abstract class BreakingAlgorithm { 0, 0, lastTooLong.previous); lastForced = node; node.fitRecoveryCounter = lastTooLong.previous.fitRecoveryCounter + 1; - log.debug("first part doesn't fit into line, recovering: " - + node.fitRecoveryCounter); + if (log.isDebugEnabled()) { + log.debug("first part doesn't fit into line, recovering: " + + node.fitRecoveryCounter); + } if (node.fitRecoveryCounter > getMaxRecoveryAttempts()) { - FONode contextFO = findContextFO(par, node.position + 1); - throw new RuntimeException(FONode.decorateWithContextInfo( - "Some content could not fit " - + "into a line/page after " + getMaxRecoveryAttempts() - + " attempts. Giving up to avoid an endless loop.", contextFO)); + while (lastForced.fitRecoveryCounter > 0) { + lastForced = lastForced.previous; + lastDeactivated = lastForced.previous; + startLine--; + endLine--; + } + lastForced = this.lastRecovered; + this.lastRecovered = null; + startLine = lastForced.line; + endLine = lastForced.line; + log.debug("rolled back..."); } } else { lastForced = lastTooLong; } } else { lastForced = lastTooShort; + this.lastRecovered = null; } - log.debug("Restarting at node " + lastForced); + if (log.isDebugEnabled()) { + log.debug("Restarting at node " + lastForced); + } i = restartFrom(lastForced, i); } } @@ -577,6 +650,7 @@ public abstract class BreakingAlgorithm { this.totalWidth = 0; this.totalStretch = 0; this.totalShrink = 0; + sumsAfter.initialize(par); } /** Creates a new active node for a feasible breakpoint at the given position. Only @@ -626,7 +700,7 @@ public abstract class BreakingAlgorithm { protected int restartFrom(KnuthNode restartingNode, int currentIndex) { restartingNode.totalDemerits = 0; - addNode(restartingNode.line, restartingNode); + registerActiveNode(restartingNode); startLine = restartingNode.line; endLine = startLine + 1; totalWidth = restartingNode.totalWidth; @@ -682,7 +756,7 @@ public abstract class BreakingAlgorithm { if (log.isTraceEnabled()) { log.trace("Removing " + node); } - removeNode(line, node); + deactivateNode(node); lastDeactivated = compareNodes(lastDeactivated, node); } @@ -709,33 +783,36 @@ public abstract class BreakingAlgorithm { if (force && (r <= -1 || r > threshold)) { int fitnessClass = computeFitness(r); double demerits = computeDemerits(node, element, fitnessClass, r); - int newWidth = totalWidth; - int newStretch = totalStretch; - int newShrink = totalShrink; - - // add the width, stretch and shrink of glue elements after - // the break - // this does not affect the dimension of the line / page, only - // the values stored in the node; these would be as if the break - // was just before the next box element, thus ignoring glues and - // penalties between the "real" break and the following box - for (int i = elementIdx; i < par.size(); i++) { - KnuthElement tempElement = getElement(i); - if (tempElement.isBox()) { - break; - } else if (tempElement.isGlue()) { - newWidth += tempElement.getW(); - newStretch += tempElement.getY(); - newShrink += tempElement.getZ(); - } else if (tempElement.isForcedBreak() && i != elementIdx) { - break; - } - } + sumsAfter.compute(elementIdx, totalWidth, totalStretch, totalShrink); +// int newWidth = totalWidth; +// int newStretch = totalStretch; +// int newShrink = totalShrink; +// +// // add the width, stretch and shrink of glue elements after +// // the break +// // this does not affect the dimension of the line / page, only +// // the values stored in the node; these would be as if the break +// // was just before the next box element, thus ignoring glues and +// // penalties between the "real" break and the following box +// for (int i = elementIdx; i < par.size(); i++) { +// KnuthElement tempElement = getElement(i); +// if (tempElement.isBox()) { +// break; +// } else if (tempElement.isGlue()) { +// newWidth += tempElement.getW(); +// newStretch += tempElement.getY(); +// newShrink += tempElement.getZ(); +// } else if (tempElement.isForcedBreak() && i != elementIdx) { +// break; +// } +// } if (r <= -1) { if (lastTooLong == null || demerits < lastTooLong.totalDemerits) { lastTooLong = createNode(elementIdx, line + 1, fitnessClass, - newWidth, newStretch, newShrink, + sumsAfter.getWidthAfter(), + sumsAfter.getStretchAfter(), + sumsAfter.getShrinkAfter(), r, availableShrink, availableStretch, difference, demerits, node); if (log.isTraceEnabled()) { @@ -751,7 +828,9 @@ public abstract class BreakingAlgorithm { difference, fitnessClass); } lastTooShort = createNode(elementIdx, line + 1, fitnessClass, - newWidth, newStretch, newShrink, + sumsAfter.getWidthAfter(), + sumsAfter.getStretchAfter(), + sumsAfter.getShrinkAfter(), r, availableShrink, availableStretch, difference, demerits, node); if (log.isTraceEnabled()) { @@ -770,33 +849,34 @@ public abstract class BreakingAlgorithm { * @param line number of the previous line; this element will end line number (line+1) * @param elementIdx the element's index */ - private void addBreaks(int line, int elementIdx) { + void addBreaks(int line, int elementIdx) { if (!best.hasRecords()) { return; } - int newWidth = totalWidth; - int newStretch = totalStretch; - int newShrink = totalShrink; - - // add the width, stretch and shrink of glue elements after - // the break - // this does not affect the dimension of the line / page, only - // the values stored in the node; these would be as if the break - // was just before the next box element, thus ignoring glues and - // penalties between the "real" break and the following box - for (int i = elementIdx; i < par.size(); i++) { - KnuthElement tempElement = getElement(i); - if (tempElement.isBox()) { - break; - } else if (tempElement.isGlue()) { - newWidth += tempElement.getW(); - newStretch += tempElement.getY(); - newShrink += tempElement.getZ(); - } else if (tempElement.isForcedBreak() && i != elementIdx) { - break; - } - } + sumsAfter.compute(elementIdx, totalWidth, totalStretch, totalShrink); +// int newWidth = totalWidth; +// int newStretch = totalStretch; +// int newShrink = totalShrink; +// +// // add the width, stretch and shrink of glue elements after +// // the break +// // this does not affect the dimension of the line / page, only +// // the values stored in the node; these would be as if the break +// // was just before the next box element, thus ignoring glues and +// // penalties between the "real" break and the following box +// for (int i = elementIdx; i < par.size(); i++) { +// KnuthElement tempElement = getElement(i); +// if (tempElement.isBox()) { +// break; +// } else if (tempElement.isGlue()) { +// newWidth += tempElement.getW(); +// newStretch += tempElement.getY(); +// newShrink += tempElement.getZ(); +// } else if (tempElement.isForcedBreak() && i != elementIdx) { +// break; +// } +// } // add nodes to the active nodes list double minimumDemerits = best.getMinDemerits() + incompatibleFitnessDemerit; @@ -809,8 +889,10 @@ public abstract class BreakingAlgorithm { + " from fitness class " + i); } KnuthNode newNode = createNode(elementIdx, line + 1, i, - newWidth, newStretch, newShrink); - addNode(line + 1, newNode); + sumsAfter.getWidthAfter(), + sumsAfter.getStretchAfter(), + sumsAfter.getShrinkAfter()); + registerActiveNode(newNode); } } best.reset(); @@ -878,7 +960,7 @@ public abstract class BreakingAlgorithm { * @param r * @return the fitness class */ - private int computeFitness(double r) { + int computeFitness(double r) { if (r < -0.5) { return 0; } else if (r <= 0.5) { @@ -987,13 +1069,12 @@ public abstract class BreakingAlgorithm { } /** - * Add a node at the end of the given line's existing active nodes. - * If this is the first node in the line, adjust endLine accordingly. - * @param line number of the line ending at the node's corresponding breakpoint - * @param node the active node to add + * Registers a new active node. If this is the first node in the corresponding line, + * adjust endLine accordingly. + * @param node the active node to register */ - public void addNode(int line, KnuthNode node) { - int headIdx = line * 2; + public void registerActiveNode(KnuthNode node) { + int headIdx = node.line * 2; if (headIdx >= activeLines.length) { KnuthNode[] oldList = activeLines; activeLines = new KnuthNode[headIdx + headIdx]; @@ -1004,21 +1085,20 @@ public abstract class BreakingAlgorithm { activeLines[headIdx + 1].next = node; } else { activeLines[headIdx] = node; - endLine = line + 1; + endLine = node.line + 1; } activeLines[headIdx + 1] = node; activeNodeCount++; } /** - * Remove the given active node registered for the given line. If there are no more active nodes - * for this line, adjust the startLine accordingly. - * @param line number of the line ending at the node's corresponding breakpoint + * Deactivates the given node. If there are no more active nodes for the corresponding + * line, adjust the startLine accordingly. * @param node the node to deactivate */ - public void removeNode(int line, KnuthNode node) { - int headIdx = line * 2; - KnuthNode n = getNode(line); + public void deactivateNode(KnuthNode node) { + int headIdx = node.line * 2; + KnuthNode n = getNode(node.line); if (n != node) { // nodes could be rightly deactivated in a different order KnuthNode prevNode = null; diff --git a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java index 30d30f4a9..1aa165b8e 100644 --- a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java +++ b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java @@ -19,7 +19,12 @@ package org.apache.fop.layoutmgr; +import java.text.NumberFormat; +import java.util.Iterator; import java.util.LinkedList; +import java.util.Locale; +import java.util.Stack; +import java.util.Vector; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -27,35 +32,97 @@ import org.apache.fop.fo.Constants; import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; import org.apache.fop.layoutmgr.AbstractBreaker.PageBreakPosition; -import org.apache.fop.layoutmgr.breaking.OutOfLineRecord; +import org.apache.fop.layoutmgr.breaking.BeforeFloatsRecord; +import org.apache.fop.layoutmgr.breaking.ElasticLength; +import org.apache.fop.layoutmgr.breaking.FootnotesRecord; +import org.apache.fop.layoutmgr.breaking.BeforeFloatsRecord.BeforeFloatsProgress; +import org.apache.fop.layoutmgr.breaking.FootnotesRecord.FootnotesProgress; import org.apache.fop.traits.MinOptMax; public class PageBreakingAlgorithm extends BreakingAlgorithm { + /* TODO vh: all of the following parameters should be available through a config + * option + */ + /** + * Minimum allowed fill ratio for pages. Underfull pages which are filled at + * least this ratio are considered to be feasible breaks. + */ + /* Set to 1.0 for now, otherwise this breaks testcases. */ + public static final double MIN_NORMAL_PAGE_FILL_RATIO = 1.0; + + /** + * Minimum allowed fill ratio for float-only pages. Underfull pages which are filled + * at least this ratio are considered to be feasible breaks. + */ + public static final double MIN_FLOAT_PAGE_FILL_RATIO = 1.0; + + /** + * Minimum acceptable ratio of normal content on pages containing both normal text and + * out-of-lines. + */ + public static final double TEXT_FRACTION = 0.05; + + /** + * Are float-only pages allowed? + */ + public static final boolean FLOAT_PAGES_ALLOWED = true; + + /** + * Are footnotes allowed on float-only pages? + */ + public static final boolean FOOTNOTES_ALLOWED_ON_FLOAT_PAGES = true; + + /** + * Are footnotes-only pages allowed? + */ + public static final boolean FOOTNOTES_ONLY_PAGES_ALLOWED = true; + + /** + * Additional demerits for an underfull page, which however has an acceptable fill ratio. + */ + private static final double UNDERFULL_PAGE_DEMERITS = 20000; + + /** + * This mode is chosen when out-of-lines must be typeset on a page containing normal + * content. + */ + public static final int NORMAL_MODE = 0; + + /** + * This mode is chosen when out-of-lines must be typeset on a float-only page. + */ + public static final int FLOAT_PAGE_MODE = 1; + + /** + * This mode is chosen when out-of-lines must be typeset on a float-only page at the + * end of a page-sequence. + */ + public static final int FLUSH_MODE = 2; + /** the logger for the class */ protected static Log classLog = LogFactory.getLog(PageBreakingAlgorithm.class); private LayoutManager topLevelLM; private PageSequenceLayoutManager.PageProvider pageProvider; + private PageBreakingLayoutListener layoutListener; /** List of PageBreakPosition elements. */ private LinkedList pageBreaks = null; - private OutOfLineRecord footnotes; - private OutOfLineRecord floats; + private NormalContentProgressInfo normalContentProgress = new NormalContentProgressInfo(); + private FootnotesRecord footnotesRecord; + private BeforeFloatsRecord beforeFloatsRecord; + private FootnotesRecord.FootnotesProgress footnotesProgress; + private BeforeFloatsRecord.BeforeFloatsProgress beforeFloatsProgress; + private ActiveNodeRecorder activeNodeRecorder = new ActiveNodeRecorder(); + // demerits for a page break that splits a footnote private int splitFootnoteDemerits = 5000; // demerits for a page break that defers a whole footnote to the following page private int deferredFootnoteDemerits = 10000; - private int deferredFloatDemerits = 10000; - - // the method noBreakBetween(int, int) uses these variables - // to store parameters and result of the last call, in order - // to reuse them and take less time - private int storedPrevBreakIndex = -1; - private int storedBreakIndex = -1; - private boolean storedValue = false; + private int deferredFloatDemerits = 2000; //Controls whether overflows should be warned about or not private boolean autoHeight = false; @@ -65,6 +132,7 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm { public PageBreakingAlgorithm(LayoutManager topLevelLM, PageSequenceLayoutManager.PageProvider pageProvider, + PageBreakingLayoutListener layoutListener, int alignment, int alignmentLast, MinOptMax footnoteSeparatorLength, MinOptMax floatSeparatorLength, boolean partOverflowRecovery, boolean autoHeight, @@ -73,9 +141,12 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm { this.log = classLog; this.topLevelLM = topLevelLM; this.pageProvider = pageProvider; - best = new BestPageRecords(); - footnotes = new OutOfLineRecord((MinOptMax) footnoteSeparatorLength.clone()); - floats = new OutOfLineRecord((MinOptMax) floatSeparatorLength.clone()); + this.layoutListener = layoutListener; + best = new BestPageRecords(log); + footnotesRecord = new FootnotesRecord(footnoteSeparatorLength); + beforeFloatsRecord = new BeforeFloatsRecord(floatSeparatorLength); + footnotesProgress = footnotesRecord.new FootnotesProgress(); + beforeFloatsProgress = beforeFloatsRecord.new BeforeFloatsProgress(); // add some stretch, to avoid a restart for every page containing footnotes if (footnoteSeparatorLength.min == footnoteSeparatorLength.max) { footnoteSeparatorLength.max += 10000; @@ -90,21 +161,21 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm { */ public class KnuthPageNode extends KnuthNode { - public OutOfLineRecord.ProgressInfo footnotesProgress; - public OutOfLineRecord.ProgressInfo floatsProgress; + public FootnotesRecord.FootnotesProgress footnotesProgress; + public BeforeFloatsRecord.BeforeFloatsProgress beforeFloatsProgress; public KnuthPageNode(int position, int line, int fitness, int totalWidth, int totalStretch, int totalShrink, - OutOfLineRecord.ProgressInfo footnotesProgress, - OutOfLineRecord.ProgressInfo floatsProgress, + FootnotesRecord.FootnotesProgress footnotesProgress, + BeforeFloatsRecord.BeforeFloatsProgress floatsProgress, double adjustRatio, int availableShrink, int availableStretch, int difference, double totalDemerits, KnuthNode previous) { super(position, line, fitness, totalWidth, totalStretch, totalShrink, adjustRatio, availableShrink, availableStretch, difference, totalDemerits, previous); - this.footnotesProgress = footnotesProgress.copy(); - this.floatsProgress = floatsProgress.copy(); + this.footnotesProgress = footnotesRecord.new FootnotesProgress(footnotesProgress); + this.beforeFloatsProgress = beforeFloatsRecord.new BeforeFloatsProgress(floatsProgress); } } @@ -115,46 +186,469 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm { */ protected class BestPageRecords extends BestRecords { - private OutOfLineRecord.ProgressInfo[] bestFootnotesProgress - = new OutOfLineRecord.ProgressInfo[4]; - private OutOfLineRecord.ProgressInfo[] bestFloatsProgress - = new OutOfLineRecord.ProgressInfo[4]; - + private FootnotesRecord.FootnotesProgress[] bestFootnotesProgress + = new FootnotesRecord.FootnotesProgress[4]; + private BeforeFloatsRecord.BeforeFloatsProgress[] bestFloatsProgress + = new BeforeFloatsRecord.BeforeFloatsProgress[4]; + + public BestPageRecords(Log log) { + super(log); + } + public void addRecord(double demerits, KnuthNode node, double adjust, int availableShrink, int availableStretch, - int difference, int fitness) { + int difference, int fitness, + FootnotesRecord.FootnotesProgress footnotesProgress, + BeforeFloatsRecord.BeforeFloatsProgress beforeFloatsProgress) { super.addRecord(demerits, node, adjust, availableShrink, availableStretch, difference, fitness); - bestFootnotesProgress[fitness] = footnotes.getProgress().copy(); - bestFloatsProgress[fitness] = floats.getProgress().copy(); + bestFootnotesProgress[fitness] + = footnotesRecord.new FootnotesProgress(footnotesProgress); + bestFloatsProgress[fitness] + = beforeFloatsRecord.new BeforeFloatsProgress(beforeFloatsProgress); } - public int getFootnotesLength(int fitness) { - return bestFootnotesProgress[fitness].getAlreadyInsertedLength(); + public FootnotesRecord.FootnotesProgress getFootnoteProgress(int fitness) { + return bestFootnotesProgress[fitness]; } - public int getFootnoteListIndex(int fitness) { - return bestFootnotesProgress[fitness].getLastInsertedIndex(); + public BeforeFloatsRecord.BeforeFloatsProgress getFloatProgress(int fitness) { + return bestFloatsProgress[fitness]; + } + } + + /** + * This class records information about the amount of normal content that has been + * handled so far. + */ + public class NormalContentProgressInfo { + + /** + * Position in the Knuth sequence. + */ + int position; + + /** + * Cumulative lengths of normal content inserted so far. This corresponds to the + * totalWidth, totalStretch, totalShrink described in Knuth's algorithm. + */ + ElasticLength insertedDims = new ElasticLength(); + + /** + * Initializes this record to handle the given Knuth sequence, such that no + * content has been inserted yet. + * + * @param par the sequence of normal content that will have to be typeset + */ + void initialize(KnuthSequence par) { + insertedDims.reset(); } - public int getFootnoteElementIndex(int fitness) { - return bestFootnotesProgress[fitness].getLastElementOfLastInsertedIndex(); + public String toString() { + return "Position: " + position + "; inserted: " + insertedDims; } + } - public OutOfLineRecord.ProgressInfo getFootnoteProgress(int fitness) { - return bestFootnotesProgress[fitness]; + /** + * Tests candidate nodes to determine whether they are feasible, and if so records + * them. + */ + public class ActiveNodeRecorder { + + /** Adjustment ratio for the currently tested page. */ + private double adjustmentRatio; + + /** Fill ratio of the currently tested page. */ + private double fillRatio; + + private int fitnessClass; + + /** + * Difference between the physical page's BPD and the BPD of the page's content. + */ + private int difference; + + /** Used to record feasible breaks in flush mode. */ + private LinkedList queue; + + /** + * true if a layout must be found, even if there is no feasible + * break. This usually consists of selecting a too-short or too-long node. + */ + private boolean force; + + /** + * Sets the behavior of the algorithm when no feasible break is found. + * + * @param force if true, a too-short or too-long node must be chosen + * as a feasible break; otherwise no node is created. + */ + void setForce(boolean force) { + this.force = force; } - public OutOfLineRecord.ProgressInfo getFloatProgress(int fitness) { - return bestFloatsProgress[fitness]; + /** + * Computes the adjustment ratio for the current page. + * + * @param totalLength total amount of content on the page (including floats and + * footnotes) + * @param pageBPD available space on the page + * @param minFillRatio minimum acceptable fill ratio for the page. If the content + * must be too much stretched to fill the page, it is allowed to stretch less + * provided the resulting fill ratio is superior or equal to this ratio + */ + void computeAdjustmentRatio(ElasticLength totalLength, int pageBPD, double minFillRatio) { + difference = pageBPD - totalLength.getLength(); + fillRatio = 1.0; + if (difference > 0) { // too short + double totalStretch = totalLength.getStretch(); + if (totalStretch <= 0) { + fillRatio = ((double) totalLength.getLength()) / pageBPD; + if (fillRatio >= minFillRatio) { + adjustmentRatio = 0; + } else { + adjustmentRatio = INFINITE_RATIO; + } + } else { + adjustmentRatio = difference / totalStretch; + if (adjustmentRatio > threshold) { + fillRatio = (totalLength.getLength() + totalStretch * threshold) / pageBPD; + if (fillRatio >= minFillRatio) { + adjustmentRatio = threshold; + } + } + } + } else if (difference < 0) { // too long + double totalShrink = totalLength.getShrink(); + if (totalShrink > 0) { + adjustmentRatio = difference / totalShrink; + } else { + adjustmentRatio = -INFINITE_RATIO; + } + } else { + adjustmentRatio = 0; + } + } + + /** + * Computes the total demerits of the page layout up to the current page. + * + * @param footnotes information about footnotes put on the current page + * @param beforeFloats information about before-floats put on the current page + * @param lastNormalElementIdx index of the last Knuth element representing normal + * content put on the current page + * @param activeNode node representing the previous page break + * @param mode one of {@link PageBreakingAlgorithm#NORMAL_MODE}, {@link + * PageBreakingAlgorithm#FLOAT_PAGE_MODE} or {@link + * PageBreakingAlgorithm#FLUSH_MODE} + */ + double computeDemerits(FootnotesRecord.FootnotesProgress footnotes, + BeforeFloatsRecord.BeforeFloatsProgress beforeFloats, + int lastNormalElementIdx, + KnuthPageNode activeNode, + int mode) { + // TODO penalty for footnotes of floats ending on penalty elements + KnuthElement lastNormalElement = (KnuthElement) par.get(lastNormalElementIdx); + double demerits = 0; + double f = Math.abs(adjustmentRatio); + /* + * If the adjustment ratio is too high, the demerits will be "almost + * infinite" (10^22). Adding demerits for a deferred float (10000) thus + * won't change the demerits value. We may end up with two breakpoints + * with the same demerits, whereas in one case there are deferred floats + * and not in the other case. The case with no deferred floats is still + * preferable, so we must have the possibility to distinguish it. By + * forcing f to threshold it becomes possible to make the difference + * when there are deferred floats. + */ + if (f > threshold) { + f = threshold; + } + f = 1 + 100 * f * f * f; + double minPageFillRatio; + if (mode == NORMAL_MODE) { + minPageFillRatio = MIN_NORMAL_PAGE_FILL_RATIO; + if (!lastNormalElement.isPenalty()) { + demerits = f * f; + } else { + double penalty = lastNormalElement.getP(); + if (penalty >= 0) { + f += penalty; + demerits = f * f; + } else if (!lastNormalElement.isForcedBreak()) { + demerits = f * f - penalty * penalty; + } else { + demerits = f * f; + } + if (((KnuthPenalty) lastNormalElement).isFlagged() + && getElement(activeNode.position).isPenalty() + && ((KnuthPenalty) getElement(activeNode.position)).isFlagged()) { + // add demerit for consecutive breaks at flagged penalties + demerits += repeatedFlaggedDemerit; + } + } + } else { + minPageFillRatio = MIN_FLOAT_PAGE_FILL_RATIO; + demerits = f * f; + } + fitnessClass = computeFitness(adjustmentRatio); + if (Math.abs(fitnessClass - activeNode.fitness) > 1) { + // add demerit for consecutive breaks + // with very different fitness classes + demerits += incompatibleFitnessDemerit; + } + + demerits += footnotes.getNbOfDeferred() * deferredFootnoteDemerits; + if (footnotes.isLastSplit()) { + demerits += footnotes.getNbSplit() * splitFootnoteDemerits; + } + demerits += beforeFloats.getNbOfDeferred() * deferredFloatDemerits; + if (beforeFloats.isLastSplit()) { + demerits += beforeFloats.getNbSplit() * splitFootnoteDemerits; + } + if (fillRatio < minPageFillRatio) { + /* To select too-short nodes among the least too underfull pages. This formula + * will give smaller results than below but, anyway, too-short nodes are + * handled separately + */ + demerits += (2.0 - fillRatio) * UNDERFULL_PAGE_DEMERITS; + } else if (minPageFillRatio < 1.0) { + /* demerits += x * UNDERFULL_PAGE_DEMERITS + * The idea is that x tends to 1.0 when fillRatio tends to + * MIN_PAGE_FILL_RATIO, to give the preference to full pages. Of course the + * following formula works only if MIN_PAGE_FILL_RATIO != 1.0, hence the test + */ + demerits += (1.0 - fillRatio) / (1.0 - minPageFillRatio) * UNDERFULL_PAGE_DEMERITS; + } + demerits += activeNode.totalDemerits; + return demerits; + } + + /** + * Tests if the node corresponding to the given parameters represents a feasible + * break, and if so computes its demerits and records it. This methods returns + * true if there is potential room for putting additional content on + * the page. Otherwise, this indicates that it is not even worth trying. + * + * @param mode one of {@link PageBreakingAlgorithm#NORMAL_MODE}, {@link + * PageBreakingAlgorithm#FLOAT_PAGE_MODE} or {@link + * PageBreakingAlgorithm#FLUSH_MODE} + * @param normalProgress progress information for the normal content + * @param footnotesProgress progress information for footnotes + * @param beforeFloatsProgress progress information for before-floats + * @param previousNode node representing the previous page break + * @return true if some additional content may potentially be added + * on the page (adjustment ratio > -1); otherwise false + * (adjustment ratio <= -1) + */ + public boolean handleNode(int mode, + NormalContentProgressInfo normalProgress, + FootnotesRecord.FootnotesProgress footnotesProgress, + BeforeFloatsRecord.BeforeFloatsProgress beforeFloatsProgress, + KnuthPageNode previousNode) { + int pageBPD = getLineWidth(previousNode.line); + ElasticLength totalLength = new ElasticLength(footnotesProgress.getInserted()); + totalLength.add(beforeFloatsProgress.getInserted()); + if (mode == NORMAL_MODE) { + totalLength.add(normalProgress.insertedDims); + computeAdjustmentRatio(totalLength, pageBPD, MIN_NORMAL_PAGE_FILL_RATIO); + } else { + computeAdjustmentRatio(totalLength, pageBPD, MIN_FLOAT_PAGE_FILL_RATIO); + } + switch (mode) { + case NORMAL_MODE: { + int beforeFloatActualBPD = beforeFloatsProgress.getInserted().getLength(); + if (adjustmentRatio < 0) { + beforeFloatActualBPD += beforeFloatsProgress.getInserted().getShrink() + * adjustmentRatio; + } else if (adjustmentRatio > 0) { + beforeFloatActualBPD += beforeFloatsProgress.getInserted().getStretch() + * adjustmentRatio; + } + if (((double) beforeFloatActualBPD) / pageBPD >= 1.0 - TEXT_FRACTION) { + // Not acceptable page, but if some further footnotes are + // added, may become feasible thanks to shrinking + double minBeforeFloatFraction + = ((double) (beforeFloatsProgress.getInserted().getLength() + - beforeFloatsProgress.getInserted().getShrink())) / pageBPD; + return adjustmentRatio > -1 && minBeforeFloatFraction < 1.0 - TEXT_FRACTION; + } else { + if (-1 <= adjustmentRatio && adjustmentRatio <= threshold) { + double demerits = computeDemerits(footnotesProgress, + beforeFloatsProgress, + normalProgress.position, + previousNode, + NORMAL_MODE); + if (demerits < best.getDemerits(fitnessClass)) { + ((BestPageRecords) best).addRecord(demerits, + previousNode, + adjustmentRatio, + totalLength.getShrink(), + totalLength.getStretch(), + difference, fitnessClass, + footnotesProgress, beforeFloatsProgress); + lastTooShort = null; + } + return true; + } else if (force) { + double demerits = computeDemerits(footnotesProgress, + beforeFloatsProgress, + normalProgress.position, + previousNode, + NORMAL_MODE); + sumsAfter.compute(normalProgress.position, + totalWidth, totalStretch, totalShrink); + if (adjustmentRatio < -1) { + if (lastTooLong == null || demerits < lastTooLong.totalDemerits) { + lastTooLong = createNode(normalProgress.position, + previousNode.line + 1, + fitnessClass, + sumsAfter.getWidthAfter(), + sumsAfter.getStretchAfter(), + sumsAfter.getShrinkAfter(), + adjustmentRatio, + totalLength.getShrink(), + totalLength.getStretch(), + difference, demerits, previousNode); + } + return false; + } else { + if (lastTooShort == null || demerits <= lastTooShort.totalDemerits) { + if (considerTooShort) { + ((BestPageRecords) best).addRecord(demerits, + previousNode, + adjustmentRatio, + totalLength.getShrink(), + totalLength.getStretch(), + difference, fitnessClass, + footnotesProgress, beforeFloatsProgress); + } + if (log.isDebugEnabled()) { + NumberFormat nf = NumberFormat.getInstance(Locale.ENGLISH); + nf.setMaximumFractionDigits(2); + nf.setGroupingUsed(false); + log.debug("Registering too-short node [demerits=" + + nf.format(demerits) + + ", adjRatio=" + nf.format(adjustmentRatio) + + ", page " + (previousNode.line + 1) + + "]"); + } + lastTooShort = createNode(normalProgress.position, + previousNode.line + 1, + fitnessClass, + sumsAfter.getWidthAfter(), + sumsAfter.getStretchAfter(), + sumsAfter.getShrinkAfter(), + adjustmentRatio, + totalLength.getShrink(), + totalLength.getStretch(), + difference, demerits, previousNode); + } + return true; + } + } + return adjustmentRatio >= -1; + } + } + case FLOAT_PAGE_MODE: + if (-1 <= adjustmentRatio && adjustmentRatio <= threshold) { + double demerits = computeDemerits(footnotesProgress, + beforeFloatsProgress, + normalProgress.position, + previousNode, + NORMAL_MODE); + KnuthNode node = createNode(normalProgress.position, + previousNode.line + 1, + fitnessClass, + previousNode.totalWidth, + previousNode.totalStretch, + previousNode.totalShrink, + adjustmentRatio, + totalLength.getShrink(), + totalLength.getStretch(), + difference, + demerits, + previousNode); + registerActiveNode(node); + lastTooShort = null; + } + return adjustmentRatio >= -1; + default: // case FLUSH_MODE: + if (-1 <= adjustmentRatio && adjustmentRatio <= threshold) { + double demerits = computeDemerits(footnotesProgress, + beforeFloatsProgress, + par.size() - 1, + previousNode, + NORMAL_MODE); + KnuthNode node = createNode(par.size() - 1, + previousNode.line + 1, + fitnessClass, + 0, 0, 0, + adjustmentRatio, + totalLength.getShrink(), totalLength.getStretch(), + difference, demerits, previousNode); + queue.addLast(node); + lastTooShort = null; + return true; + } else if (force) { + double demerits = computeDemerits(footnotesProgress, + beforeFloatsProgress, + par.size() - 1, + previousNode, + NORMAL_MODE); + if (adjustmentRatio < -1) { + if (lastTooLong == null || demerits < lastTooLong.totalDemerits) { + lastTooLong = createNode(par.size() - 1, + previousNode.line + 1, + fitnessClass, + 0, 0, 0, + adjustmentRatio, + totalLength.getShrink(), totalLength.getStretch(), + difference, demerits, previousNode); + } + return false; + } else { + if (lastTooShort == null || demerits <= lastTooShort.totalDemerits) { + lastTooShort = createNode(par.size() - 1, + previousNode.line + 1, + fitnessClass, + 0, 0, 0, + adjustmentRatio, + totalLength.getShrink(), totalLength.getStretch(), + difference, demerits, previousNode); + if (considerTooShort) { + queue.addLast(lastTooShort); + } + } + return true; + } + } + return adjustmentRatio >= -1; + } + } + + /** + * When in flush mode, uses the given queue for registering new active nodes. TODO + * vh: highly temporary! As in flush mode the handling is a bit different, + * activeLines cannot be re-used. Will have to unify the handling of active nodes + * eventually. + * + * @param queue FIFO structure for registering active nodes in flush mode + */ + void setQueue(LinkedList queue) { + this.queue = queue; } } protected void initialize() { super.initialize(); - footnotes.initialize(); - floats.initialize(); + normalContentProgress.initialize(par); + footnotesRecord.initialize(); + beforeFloatsRecord.initialize(); + footnotesProgress.initialize(); + beforeFloatsProgress.initialize(); + activeNodeRecorder.setForce(force); } public KnuthNode createNode(int position, int line, int fitness, @@ -163,7 +657,7 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm { int difference, double totalDemerits, KnuthNode previous) { return new KnuthPageNode(position, line, fitness, totalWidth, totalStretch, totalShrink, - footnotes.getProgress(), floats.getProgress(), + footnotesProgress, beforeFloatsProgress, adjustRatio, availableShrink, availableStretch, difference, totalDemerits, previous); } @@ -187,31 +681,29 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm { protected void handleBox(KnuthBox box) { if (box instanceof KnuthBlockBox && ((KnuthBlockBox) box).hasFootnoteAnchors()) { - footnotes.add(((KnuthBlockBox) box).getFootnoteElementLists()); + footnotesRecord.add(((KnuthBlockBox) box).getFootnoteElementLists()); } if (box instanceof KnuthBlockBox && ((KnuthBlockBox) box).hasFloatAnchors()) { - floats.add(((KnuthBlockBox) box).getFloatElementLists()); + beforeFloatsRecord.add(((KnuthBlockBox) box).getFloatElementLists()); } } protected int restartFrom(KnuthNode restartingNode, int currentIndex) { int returnValue = super.restartFrom(restartingNode, currentIndex); - footnotes.resetNewSinceLastBreakpoint(); - floats.resetNewSinceLastBreakpoint(); - if (footnotes.existing() || floats.existing()) { + if (footnotesRecord.existing() || beforeFloatsRecord.existing()) { // remove from footnotesList the note lists that will be met // after the restarting point for (int j = currentIndex; j >= restartingNode.position; j--) { - KnuthElement resettedElement = getElement(j); - if (resettedElement instanceof KnuthBlockBox - && ((KnuthBlockBox) resettedElement).hasFootnoteAnchors()) { - footnotes.reset(((KnuthBlockBox) resettedElement).getFootnoteElementLists()); + KnuthElement resetElement = getElement(j); + if (resetElement instanceof KnuthBlockBox + && ((KnuthBlockBox) resetElement).hasFootnoteAnchors()) { + footnotesRecord.reset(((KnuthBlockBox) resetElement).getFootnoteElementLists()); } - if (resettedElement instanceof KnuthBlockBox - && ((KnuthBlockBox) resettedElement).hasFloatAnchors()) { - floats.reset(((KnuthBlockBox) resettedElement).getFloatElementLists());//TODO + if (resetElement instanceof KnuthBlockBox + && ((KnuthBlockBox) resetElement).hasFloatAnchors()) { + beforeFloatsRecord.reset(((KnuthBlockBox) resetElement).getFloatElementLists()); } } } @@ -219,291 +711,177 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm { } protected void considerLegalBreak(KnuthElement element, int elementIdx) { - super.considerLegalBreak(element, elementIdx); - footnotes.resetNewSinceLastBreakpoint(); - floats.resetNewSinceLastBreakpoint(); - } - protected int computeDifference(KnuthNode activeNode, KnuthElement element, - int elementIndex) { - KnuthPageNode pageNode = (KnuthPageNode) activeNode; - int actualWidth = totalWidth - pageNode.totalWidth; - if (element.isPenalty()) { - actualWidth += element.getW(); + if (log.isTraceEnabled()) { + log.trace("considerLegalBreak() at " + elementIdx + + " (" + totalWidth + "+" + totalStretch + "-" + totalShrink + + "), parts/lines: " + startLine + "-" + endLine); + log.trace("\tCurrent active node list: " + activeNodeCount + " " + this.toString("\t")); } - if (footnotes.existing()) { - footnotes.setProgress(pageNode.footnotesProgress); - // compute the total length of the footnotes not yet inserted - int allFootnotes = footnotes.getTotalLength() - - pageNode.footnotesProgress.getAlreadyInsertedLength(); - if (allFootnotes > 0) { - // this page contains some footnote citations - // add the footnote separator width - actualWidth += footnotes.getSeparatorLength().opt; - if (actualWidth + allFootnotes <= getLineWidth()) { - // there is enough space to insert all footnotes: - // add the whole allFootnotes length - actualWidth += allFootnotes; - footnotes.insertAll(); - } else { - boolean canDeferOldFootnotes = checkCanDeferOldOutOfLines(footnotes, - pageNode.position, elementIndex); - int footnoteSplit; - if ((canDeferOldFootnotes || footnotes.newSinceLastBreakpoint()) - && (footnoteSplit = footnotes.getFootnoteSplit( - pageNode.footnotesProgress, - getLineWidth() - actualWidth, canDeferOldFootnotes)) > 0) { - // it is allowed to break or even defer footnotes if either: - // - there are new footnotes in the last piece of content, and - // there is space to add at least a piece of the first one - // - or the previous page break deferred some footnote lines, and - // this is the first feasible break; in this case it is allowed - // to break and defer, if necessary, old and new footnotes - actualWidth += footnoteSplit; - } else { - // there is no space to add the smallest piece of footnote, - // or we are trying to add a piece of content with no footnotes and - // it does not fit in the page, because of previous footnote bodies - // that cannot be broken: - // add the whole allFootnotes length, so this breakpoint will be discarded - actualWidth += allFootnotes; - footnotes.insertAll(); - } + + lastDeactivated = null; + lastTooLong = null; + for (int line = startLine; line < endLine; line++) { + for (KnuthPageNode node = (KnuthPageNode) getNode(line); + node != null; + node = (KnuthPageNode) node.next) { + if (node.position == elementIdx) { + continue; } - } // else: all footnotes have already been placed on previous pages - } - if (floats.existing()) { - floats.setProgress(pageNode.floatsProgress); - // compute the total length of the floats not yet inserted - int allFloats = floats.getTotalLength() - - pageNode.floatsProgress.getAlreadyInsertedLength(); - if (allFloats > 0 - && getLineWidth() - actualWidth - floats.getSeparatorLength().opt > 0) { - // this page contains some float citations - // add the float separator width - int split = floats.getFloatSplit(pageNode.floatsProgress, - getLineWidth() - actualWidth - floats.getSeparatorLength().opt); - if (split > 0) { - actualWidth += floats.getSeparatorLength().opt + split; + footnotesProgress.setPrevious(node.footnotesProgress); + beforeFloatsProgress.setPrevious(node.beforeFloatsProgress); + footnotesProgress.handleSplit(); + beforeFloatsProgress.handleSplit(); + normalContentProgress.insertedDims.set(totalShrink - node.totalShrink, + totalWidth - node.totalWidth, totalStretch - node.totalStretch); + normalContentProgress.position = elementIdx; + if (element.isPenalty()) { + normalContentProgress.insertedDims.add(0, element.getW(), 0); } - } - } - /* Another algorithm exactly mimicing the handling of footnotes: it should force - * more floats to be on the same page as their citations, at the price of more - * underfull pages (thus a higher total number of pages). If the current method - * works well enough, we may keep it. - */ -// if (floats.existing()) { -// floats.setProgress(pageNode.floatsProgress); -// // compute the total length of the floats not yet inserted -// int allFloats = floats.getTotalLength() -// - pageNode.floatsProgress.getAlreadyInsertedLength(); -// if (allFloats > 0) { -// // this page contains some float citations -// // add the float separator width -// actualWidth += floats.getSeparatorLength().opt; -// if (actualWidth + allFloats <= getLineWidth()) { -// // there is enough space to insert all floats: -// // add the whole allFloats length -// actualWidth += allFloats; -// floats.insertAll(); -// } else { -// boolean canDeferOldFloats = checkCanDeferOldOutOfLines(floats, -// pageNode.position, elementIndex); -// int floatSplit; -// if ((canDeferOldFloats || floats.newSinceLastBreakpoint()) -// && (floatSplit = floats.getFloatSplit( -// pageNode.floatsProgress, -// getLineWidth() - actualWidth)) > 0) { -// actualWidth += floatSplit; -// } else { -// actualWidth += allFloats; -// floats.insertAll(); -// } -// } -// } // else: all floats have already been placed on previous pages -// } - return getLineWidth(activeNode.line) - actualWidth; - } - - /** - * Checks whether out-of-line objects from preceding pages may be deferred - * to the page after the given element. - * - * @param outOfLine informations about the out-of-line objects - * @param activeNodePosition index in the Knuth sequence of the currently considered - * active node - * @param contentElementIndex index in the Knuth sequence of the currently considered - * legal breakpoint - * @return true if it is allowed to defer some out-of-line objects on - * following pages - */ - private boolean checkCanDeferOldOutOfLines(OutOfLineRecord outOfLine, - int activeNodePosition, - int contentElementIndex) { - return (noBreakBetween(activeNodePosition, contentElementIndex) - && outOfLine.deferred()); - } - - /** - * Returns true if there is no legal breakpoint between the two given elements. - * @param prevBreakIndex index of the element from the currently considered active - * node - * @param breakIndex index of the currently considered breakpoint - * @return true if no element between the two is a legal breakpoint - */ - private boolean noBreakBetween(int prevBreakIndex, int breakIndex) { - // this method stores the parameters and the return value from previous calls - // in order to avoid scanning the element list unnecessarily: - // - if there is no break between element #i and element #j - // there will not be a break between #(i+h) and #j too - // - if there is a break between element #i and element #j - // there will be a break between #(i-h) and #(j+k) too - if (storedPrevBreakIndex != -1 - && ((prevBreakIndex >= storedPrevBreakIndex - && breakIndex == storedBreakIndex - && storedValue) - || (prevBreakIndex <= storedPrevBreakIndex - && breakIndex >= storedBreakIndex - && !storedValue))) { - // use the stored value, do nothing - } else { - // compute the new value - int index; - // ignore suppressed elements - for (index = prevBreakIndex + 1; - !par.getElement(index).isBox(); - index++) { - //nop - } - // find the next break - for (; - index < breakIndex; - index++) { - if (par.getElement(index).isGlue() && par.getElement(index - 1).isBox() - || par.getElement(index).isPenalty() - && ((KnuthElement) par.getElement(index)).getP() < KnuthElement.INFINITE) { - // break found - break; + boolean recorded = activeNodeRecorder.handleNode(NORMAL_MODE, + normalContentProgress, footnotesProgress, beforeFloatsProgress, node); + if (!recorded || element.isForcedBreak()) { + deactivateNode(node); + lastDeactivated = compareNodes(lastDeactivated, node); + } + if (recorded) { + footnotesProgress.consider(NORMAL_MODE, activeNodeRecorder, + normalContentProgress, beforeFloatsProgress, node); } - } - // update stored parameters and value - storedPrevBreakIndex = prevBreakIndex; - storedBreakIndex = breakIndex; - storedValue = (index == breakIndex); - } - return storedValue; - } - protected double computeAdjustmentRatio(KnuthNode activeNode, int difference) { - // compute the adjustment ratio - if (difference > 0) { - int maxAdjustment = totalStretch - activeNode.totalStretch; - // add the footnote separator stretch if some footnote content will be added - if (((KnuthPageNode) activeNode).footnotesProgress.getAlreadyInsertedLength() < footnotes.getTotalLength()) { - maxAdjustment += footnotes.getSeparatorLength().max - footnotes.getSeparatorLength().opt; - } - // add the float separator stretch if some float content will be added - if (((KnuthPageNode) activeNode).floatsProgress.getAlreadyInsertedLength() < floats.getTotalLength()) { - maxAdjustment += floats.getSeparatorLength().max - floats.getSeparatorLength().opt; - } - if (maxAdjustment > 0) { - return (double) difference / maxAdjustment; - } else { - return INFINITE_RATIO; - } - } else if (difference < 0) { - int maxAdjustment = totalShrink - activeNode.totalShrink; - // add the footnote separator shrink if some footnote content will be added - if (((KnuthPageNode) activeNode).footnotesProgress.getAlreadyInsertedLength() < footnotes.getTotalLength()) { - maxAdjustment += footnotes.getSeparatorLength().opt - footnotes.getSeparatorLength().min; } - // add the float separator shrink if some float content will be added - if (((KnuthPageNode) activeNode).floatsProgress.getAlreadyInsertedLength() < floats.getTotalLength()) { - maxAdjustment += floats.getSeparatorLength().opt - floats.getSeparatorLength().min; - } - if (maxAdjustment > 0) { - return (double) difference / maxAdjustment; - } else { - return -INFINITE_RATIO; - } - } else { - return 0; + addBreaks(line, elementIdx); } } - protected double computeDemerits(KnuthNode activeNode, KnuthElement element, - int fitnessClass, double r) { - double demerits = 0; - // compute demerits - double f = Math.abs(r); - /* If the adjustment ratio is too high, the demerits will be "almost infinite" - * (10^22). Adding demerits for a deferred float (10000) thus won't change the - * demerits value. We may end up with two breakpoints with the same demerits, - * whereas in one case there are deferred floats and not in the other case. The - * case with no deferred floats is still preferable, so we must have the - * possibility to distinguish it. By forcing f to 1 it becomes possible to make - * the difference when there are deferred floats. - * TODO vh: use threshold instead of 1 (currently threshold == 1 but it might be - * configurable) - */ - if (f > 1) { - f = 1; - } - f = 1 + 100 * f * f * f; - if (element.isPenalty() && element.getP() >= 0) { - f += element.getP(); - demerits = f * f; - } else if (element.isPenalty() && !element.isForcedBreak()) { - double penalty = element.getP(); - demerits = f * f - penalty * penalty; - } else { - demerits = f * f; - } + // TODO vh: refactor + // It may happen that several successive float-only pages are created; in such cases + // the progress informations must be saved as they'll still be used after this method + // call + private Stack footnotesStack = new Stack(); - if (element.isPenalty() && ((KnuthPenalty) element).isFlagged() - && getElement(activeNode.position).isPenalty() - && ((KnuthPenalty) getElement(activeNode.position)).isFlagged()) { - // add demerit for consecutive breaks at flagged penalties - demerits += repeatedFlaggedDemerit; - } - if (Math.abs(fitnessClass - activeNode.fitness) > 1) { - // add demerit for consecutive breaks - // with very different fitness classes - demerits += incompatibleFitnessDemerit; - } + private Stack beforeFloatsStack = new Stack(); - if (footnotes.existing()) { - demerits += footnotes.getNbOfDeferred() * deferredFootnoteDemerits; - if (footnotes.isSplit()) { - demerits += splitFootnoteDemerits; - } - } - if (floats.existing()) { - demerits += floats.getNbOfDeferred() * deferredFloatDemerits; + public void registerActiveNode(KnuthNode node) { + super.registerActiveNode(node); + KnuthPageNode pageNode = (KnuthPageNode) node; + if (pageNode.position < par.size() - 1 + && (pageNode.footnotesProgress.remaining() || pageNode.beforeFloatsProgress.remaining()) + && FLOAT_PAGES_ALLOWED) { + footnotesStack.push(footnotesProgress); + footnotesProgress = footnotesRecord.new FootnotesProgress(); + beforeFloatsStack.push(beforeFloatsProgress); + beforeFloatsProgress = beforeFloatsRecord.new BeforeFloatsProgress(); + + footnotesProgress.setPrevious(pageNode.footnotesProgress); + beforeFloatsProgress.setPrevious(pageNode.beforeFloatsProgress); + footnotesProgress.handleSplit(FLOAT_PAGE_MODE, activeNodeRecorder, + normalContentProgress, beforeFloatsProgress, pageNode); + beforeFloatsProgress.handleSplit(FLOAT_PAGE_MODE, activeNodeRecorder, + normalContentProgress, footnotesProgress, pageNode); + footnotesProgress.consider(FLOAT_PAGE_MODE, activeNodeRecorder, + normalContentProgress, beforeFloatsProgress, pageNode); + + footnotesProgress = (FootnotesProgress) footnotesStack.pop(); + beforeFloatsProgress = (BeforeFloatsProgress) beforeFloatsStack.pop(); } - demerits += activeNode.totalDemerits; - return demerits; } + // TODO vh: refactor protected void finish() { - for (int i = startLine; i < endLine; i++) { - for (KnuthPageNode node = (KnuthPageNode) getNode(i); - node != null; - node = (KnuthPageNode) node.next) { - if (node.footnotesProgress.getAlreadyInsertedLength() - < footnotes.getTotalLength()) { - // layout remaining footnote bodies - footnotes.createFootnotePages(node, this, getLineWidth()); + lastTooShort = null; + lastTooLong = null; + Vector bestNodes = new Vector(); + int startIndex = startLine; + int endIndex = endLine; + bestNodes.setSize(endIndex - startIndex); + // Declared as a LinkedList as we need access to the queue-like methods defined + // in LinkedList + LinkedList queue = new LinkedList(); + activeNodeRecorder.setQueue(queue); + Object n = null; + do { + for (int i = startIndex; i < endIndex; i++) { + for (KnuthPageNode node = (KnuthPageNode) getNode(i); + node != null; + node = (KnuthPageNode) node.next) { + deactivateNode(node); + if (node.footnotesProgress.remaining() + || node.beforeFloatsProgress.remaining()) { + queue.addFirst(node); + flush(queue, bestNodes, startIndex); + } else { + if (bestNodes.get(i - startIndex) == null + || node.totalDemerits < ((KnuthNode) bestNodes.get(i - startIndex)).totalDemerits) { + bestNodes.set(i - startIndex, node); + } + } } - if (node.floatsProgress.getAlreadyInsertedLength() < floats.getTotalLength()) { - // layout remaining float bodies - floats.createFloatPages(node, this, getLineWidth()); + } + Iterator iter = bestNodes.iterator(); + while (iter.hasNext() && (n = iter.next()) == null); + if (n == null) { + log.debug("Recovering"); + KnuthNode recovered = null; + if (lastTooShort == null) { + if (lastTooLong == null) { + log.debug("Both lastTooShort and lastTooLong null!"); + } else { + recovered = lastTooLong; + } + } else { + recovered = lastTooShort; + } + recovered.totalDemerits = 0; + registerActiveNode(recovered); + startIndex = recovered.line; + endIndex = startIndex + 1; + lastTooLong = null; + lastTooShort = null; + } + } while (n == null); + Iterator nodeIter = bestNodes.iterator(); + while (nodeIter.hasNext()) { + KnuthNode node = (KnuthNode) nodeIter.next(); + if (node != null) { // TODO + int tmp = endLine; + registerActiveNode(node); + if (startLine > node.line) { + startLine = node.line; + } + if (endLine < tmp) { + endLine = tmp; } } } } + // TODO vh: refactor + private void flush(LinkedList queue, Vector bestNodes, int startIndex) { + do { + KnuthPageNode node = (KnuthPageNode) queue.removeFirst(); + if (node.footnotesProgress.remaining() || node.beforeFloatsProgress.remaining()) { + footnotesProgress.setPrevious(node.footnotesProgress); + beforeFloatsProgress.setPrevious(node.beforeFloatsProgress); + footnotesProgress.handleSplit(FLUSH_MODE, activeNodeRecorder, + null, beforeFloatsProgress, node); + beforeFloatsProgress.handleSplit(FLUSH_MODE, activeNodeRecorder, + null, footnotesProgress, node); + footnotesProgress.consider(FLUSH_MODE, activeNodeRecorder, + null, beforeFloatsProgress, node); + } else { + int index = node.line - startIndex; + if (index >= bestNodes.size()) { + bestNodes.setSize(index + 1); + } + if (bestNodes.get(index) == null + || node.totalDemerits < ((KnuthNode) bestNodes.get(index)).totalDemerits) { + bestNodes.set(index, node); + } + } + } while (!queue.isEmpty()); + } + /** * @return a list of PageBreakPosition elements */ @@ -536,11 +914,15 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm { // ? bestActiveNode.difference : bestActiveNode.difference + fillerMinWidth; int difference = bestActiveNode.difference; if (difference + bestActiveNode.availableShrink < 0) { - if (!autoHeight && log.isWarnEnabled()) { - log.warn(FONode.decorateWithContextInfo( - "Part/page " + (getPartCount() + 1) - + " overflows the available area in block-progression dimension.", - getFObj())); + if (!autoHeight) { + if (layoutListener != null) { + layoutListener.notifyOverflow(bestActiveNode.line - 1, getFObj()); + } else if (log.isWarnEnabled()) { + log.warn(FONode.decorateWithContextInfo( + "Part/page " + (bestActiveNode.line - 1) + + " overflows the available area in block-progression dimension.", + getFObj())); + } } } boolean isNonLastPage = (bestActiveNode.line < total); @@ -579,8 +961,8 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm { if (firstFootnoteListIndex == -1) { firstFootnoteListIndex++; firstFootnoteElementIndex = 0; - } else if (footnotes.getSequence(firstFootnoteListIndex) != null - && firstFootnoteElementIndex == ((LinkedList) footnotes. + } else if (footnotesRecord.getSequence(firstFootnoteListIndex) != null + && firstFootnoteElementIndex == ((LinkedList) footnotesRecord. getSequence(firstFootnoteListIndex)).size() - 1) { // advance to the next list firstFootnoteListIndex++; @@ -590,7 +972,7 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm { } // compute the indexes of the first float list int firstFloatListIndex = ((KnuthPageNode) bestActiveNode.previous). - floatsProgress.getLastInsertedIndex() + 1; + beforeFloatsProgress.getLastInsertedIndex() + 1; // add nodes at the beginning of the list, as they are found // backwards, from the last one to the first one @@ -605,7 +987,7 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm { ((KnuthPageNode) bestActiveNode).footnotesProgress. getLastElementOfLastInsertedIndex(), firstFloatListIndex, - ((KnuthPageNode) bestActiveNode).floatsProgress.getLastInsertedIndex(), + ((KnuthPageNode) bestActiveNode).beforeFloatsProgress.getLastInsertedIndex(), ratio, difference)); } @@ -624,7 +1006,7 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm { bestActiveNode = compareNodes(bestActiveNode, node); } if (node != bestActiveNode) { - removeNode(i, node); + deactivateNode(node); } } } @@ -632,11 +1014,11 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm { } public LinkedList getFootnoteList(int index) { - return (LinkedList) footnotes.getSequence(index); + return (LinkedList) footnotesRecord.getSequence(index); } public LinkedList getFloatList(int index) { - return (LinkedList) floats.getSequence(index); + return (LinkedList) beforeFloatsRecord.getSequence(index); } /** @return the associated top-level formatting object. */ @@ -658,4 +1040,18 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm { return bpd; } + /** + * Interface to notify about layout events during page breaking. + */ + public interface PageBreakingLayoutListener { + + /** + * Issued when an overflow is detected + * @param part the number of the part (page) this happens on + * @param obj the root FO object where this happens + */ + void notifyOverflow(int part, FObj obj); + + } + } diff --git a/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java b/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java index e1e40744b..92ce3d067 100644 --- a/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java @@ -34,6 +34,8 @@ import org.apache.fop.area.LineArea; import org.apache.fop.area.Resolvable; import org.apache.fop.fo.Constants; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.FObj; import org.apache.fop.fo.flow.Marker; import org.apache.fop.fo.flow.RetrieveMarker; @@ -44,6 +46,7 @@ import org.apache.fop.fo.pagination.RegionBody; import org.apache.fop.fo.pagination.SideRegion; import org.apache.fop.fo.pagination.SimplePageMaster; import org.apache.fop.fo.pagination.StaticContent; +import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener; import org.apache.fop.layoutmgr.inline.ContentLayoutManager; import org.apache.fop.traits.MinOptMax; @@ -170,6 +173,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { (currentPageNum - startPageNum) + 1); areaTreeHandler.notifyPageSequenceFinished(pageSeq, (currentPageNum - startPageNum) + 1); + pageSeq.releasePageSequence(); log.debug("Ending layout"); } @@ -194,8 +198,9 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { context.setRefIPD(flowIPD); } + /** @see org.apache.fop.layoutmgr.AbstractBreaker#getTopLevelLM() */ protected LayoutManager getTopLevelLM() { - return null; // unneeded for PSLM + return pslm; } /** @see org.apache.fop.layoutmgr.AbstractBreaker#getPageProvider() */ @@ -203,6 +208,32 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { return pageProvider; } + /** + * @see org.apache.fop.layoutmgr.AbstractBreaker#getLayoutListener() + */ + protected PageBreakingLayoutListener getLayoutListener() { + return new PageBreakingLayoutListener() { + + public void notifyOverflow(int part, FObj obj) { + Page p = pageProvider.getPage( + false, part, PageProvider.RELTO_CURRENT_ELEMENT_LIST); + RegionBody body = (RegionBody)p.getSimplePageMaster().getRegion( + Region.FO_REGION_BODY); + String err = FONode.decorateWithContextInfo( + "Content of the region-body on page " + + p.getPageViewport().getPageNumberString() + + " overflows the available area in block-progression dimension.", + obj); + if (body.getOverflow() == Constants.EN_ERROR_IF_OVERFLOW) { + throw new RuntimeException(err); + } else { + PageSequenceLayoutManager.log.warn(err); + } + } + + }; + } + /** @see org.apache.fop.layoutmgr.AbstractBreaker */ protected int handleSpanChange(LayoutContext childLC, int nextSequenceStartsOn) { needColumnBalancing = false; @@ -436,7 +467,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { //Restart last page PageBreakingAlgorithm algRestart = new PageBreakingAlgorithm( getTopLevelLM(), - getPageProvider(), + getPageProvider(), getLayoutListener(), alg.getAlignment(), alg.getAlignmentLast(), footnoteSeparatorLength, floatSeparatorLength, isPartOverflowRecoveryActivated(), false, false); @@ -496,7 +527,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { //Restart last page PageBreakingAlgorithm algRestart = new BalancingColumnBreakingAlgorithm( getTopLevelLM(), - getPageProvider(), + getPageProvider(), getLayoutListener(), alignment, Constants.EN_START, footnoteSeparatorLength, floatSeparatorLength, isPartOverflowRecoveryActivated(), getCurrentPV().getBodyRegion().getColumnCount()); diff --git a/src/java/org/apache/fop/layoutmgr/SpaceResolver.java b/src/java/org/apache/fop/layoutmgr/SpaceResolver.java index 222f20040..91a0634f5 100644 --- a/src/java/org/apache/fop/layoutmgr/SpaceResolver.java +++ b/src/java/org/apache/fop/layoutmgr/SpaceResolver.java @@ -609,7 +609,7 @@ public class SpaceResolver { * Resolves unresolved elements applying the space resolution rules defined in 4.3.1. * @param elems the element list */ - public static void resolveElementList(LinkedList elems) { + public static void resolveElementList(List elems) { if (log.isTraceEnabled()) { log.trace(elems); } diff --git a/src/java/org/apache/fop/layoutmgr/breaking/BeforeFloatsRecord.java b/src/java/org/apache/fop/layoutmgr/breaking/BeforeFloatsRecord.java new file mode 100644 index 000000000..78817722b --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/breaking/BeforeFloatsRecord.java @@ -0,0 +1,237 @@ +/* + * 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.layoutmgr.breaking; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import org.apache.fop.layoutmgr.KnuthElement; +import org.apache.fop.layoutmgr.PageBreakingAlgorithm; +import org.apache.fop.layoutmgr.SpaceResolver; +import org.apache.fop.layoutmgr.PageBreakingAlgorithm.KnuthPageNode; +import org.apache.fop.traits.MinOptMax; + +/** + * A class that handles the placement of before-floats. Stores informations about + * existing before-floats, already placed ones, split floats, etc. + */ +public class BeforeFloatsRecord extends OutOfLineRecord { + + /** + * Dimensions of the recorded before-floats. As the whole dimensions of floats are + * often required to determine placements, they are computed only once and stored in a + * dedicated field. + */ + private List elasticLengths = new ArrayList(); + + /** + * Progress informations for before-floats. Among other things, this class handles the + * splitting of floats over several pages. + */ + public class BeforeFloatsProgress extends ProgressInfo { + + /** + * Creates and initializes a new record. + */ + public BeforeFloatsProgress() { + super(); + } + + /** + * Creates a copy of the given record. + * + * @param beforeFloatProgress original progress information + */ + public BeforeFloatsProgress(BeforeFloatsProgress beforeFloatProgress) { + super(beforeFloatProgress); + } + + /** + * Checks whether there is still an entire float which has not been handled. + * + * @return true if there is still at least one entire non-handled + * float + */ + private boolean hasNextFull() { + return lastInsertedIndex < knuthSequences.size() - 1; + } + + /** + * Places on the current page the next entire float, without considering to split + * it. + */ + private void nextFull() { + lastInsertedIndex++; + lastElementOfLastInsertedIndex + = ((List) knuthSequences.get(lastInsertedIndex)).size() - 1; + alreadyInserted.add((ElasticLength) elasticLengths.get(lastInsertedIndex)); + } + + /** + * If the float on the previous page was split, put the rest of it on the current + * page. This method is only called when typesetting a page which also has normal + * content. If the float on the previous page was split, this implies that this + * was a float-only page with only one float (no normal content, no footnote, no + * other float). This is the only case where a before-float is allowed to be + * split. + */ + public void handleSplit() { + if (isLastSplit()) { + do { + nextInsideBreak(); + } while (!endOfOutOfLine()); + addSeparator(); + } + } + + /** + * If the float on the previous page was split, put the rest of it on the current + * page. This method is called when building a float-only page, either inside a + * page sequence or at the end of it. + * + * @param mode one of {@link PageBreakingAlgorithm#FLOAT_PAGE_MODE} or {@link + * PageBreakingAlgorithm#FLUSH_MODE} + * @param activeNodeRecorder + * @param normalContentProgress information about normal content already typeset + * @param footnotesProgress information about footnotes already typeset + * @param previousNode node ending the previous page + */ + public void handleSplit(int mode, + PageBreakingAlgorithm.ActiveNodeRecorder activeNodeRecorder, + PageBreakingAlgorithm.NormalContentProgressInfo normalContentProgress, + FootnotesRecord.FootnotesProgress footnotesProgress, + KnuthPageNode previousNode) { + if (isLastSplit()) { + do { + nextInsideBreak(); + if (!activeNodeRecorder.handleNode(mode, normalContentProgress, + footnotesProgress, this, previousNode)) { + break; + } + } while (!endOfOutOfLine()); + } + } + + /** + * Considers the placement of floats on the current page. + * + * @param mode one of {@link PageBreakingAlgorithm#NORMAL_MODE}, {@link + * PageBreakingAlgorithm#FLOAT_PAGE_MODE} or {@link + * PageBreakingAlgorithm#FLUSH_MODE} + * @param activeNodeRecorder + * @param normalContentProgress information about normal content already typeset + * @param footnotesProgress information about footnotes already typeset + * @param previousNode node ending the previous page + */ + public void consider(int mode, + PageBreakingAlgorithm.ActiveNodeRecorder activeNodeRecorder, + PageBreakingAlgorithm.NormalContentProgressInfo normalContentProgress, + FootnotesRecord.FootnotesProgress footnotesProgress, + KnuthPageNode previousNode) { + setPrevious(previousNode.beforeFloatsProgress); + switch (mode) { + case PageBreakingAlgorithm.NORMAL_MODE: + if (alreadyInserted.getLength() == 0) { + addSeparator(); + } + while (hasNextFull()) { + nextFull(); + if (!activeNodeRecorder.handleNode(mode, normalContentProgress, + footnotesProgress, this, previousNode)) { + break; + } + } + break; + case PageBreakingAlgorithm.FLOAT_PAGE_MODE: + case PageBreakingAlgorithm.FLUSH_MODE: + if (footnotesProgress.alreadyInserted.getLength() == 0 + && alreadyInserted.getLength() == 0 + && hasNextFull()) { // first split allowed + lastInsertedIndex++; + lastElementOfLastInsertedIndex = -1; + do { + nextInsideBreak(); + if (!activeNodeRecorder.handleNode(mode, normalContentProgress, + footnotesProgress, this, previousNode)) { + break; + } + } while (!endOfOutOfLine()); + } + while (hasNextFull()) { + nextFull(); + if (!activeNodeRecorder.handleNode(mode, normalContentProgress, + footnotesProgress, this, previousNode)) { + break; + } + } + break; + } + } + } + + /** + * Creates a new record for handling before-floats. + * + * @param separator dimensions of the separator between the before-float area + * and the normal content + */ + public BeforeFloatsRecord(MinOptMax separator) { + super(separator); + } + + public void initialize() { + super.initialize(); + elasticLengths = new ArrayList(); + } + + public void add(List elementLists) { + // compute the total length of the footnotes + ListIterator elementListsIterator = elementLists.listIterator(); + while (elementListsIterator.hasNext()) { + List knuthSequence = (List) elementListsIterator.next(); + + //Space resolution (Note: this does not respect possible stacking constraints + //between footnotes!) + SpaceResolver.resolveElementList(knuthSequence); + + knuthSequences.add(knuthSequence); + Iterator elementIter = knuthSequence.iterator(); + ElasticLength floatDims = new ElasticLength(); + while (elementIter.hasNext()) { + KnuthElement element = (KnuthElement) elementIter.next(); + if (element.isBox()) { + floatDims.add(0, element.getW(), 0); + } else if (element.isGlue()) { + floatDims.add(element.getZ(), element.getW(), element.getY()); + } + } + elasticLengths.add(floatDims); + } + } + + public void reset(List elementLists) { + super.reset(elementLists); + for (int i = 0; i < elementLists.size(); i++) { + elasticLengths.remove(elasticLengths.size() - 1); + } + } +} diff --git a/src/java/org/apache/fop/layoutmgr/breaking/ElasticLength.java b/src/java/org/apache/fop/layoutmgr/breaking/ElasticLength.java new file mode 100644 index 000000000..374b9b3ad --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/breaking/ElasticLength.java @@ -0,0 +1,146 @@ +/* + * 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.layoutmgr.breaking; + +/** + * A length with three components: the natural value, the amount of authorized shrink, the + * amount of authorized stretch. + */ +public class ElasticLength { + + private int shrink; + + private int length; + + private int stretch; + + /** + * Creates a new length of 0, with 0 shrink and 0 stretch. + */ + public ElasticLength() { + shrink = 0; + length = 0; + stretch = 0; + } + + /** + * Creates a copy of the given length. + * + * @param el an elastic length + */ + public ElasticLength(ElasticLength el) { + shrink = el.shrink; + length = el.length; + stretch = el.stretch; + } + + /** + * Creates a new length with the given components. + * + * @param shrink amount of authorized shrink + * @param length natural length + * @param stretch amount of authorized stretch + */ + public ElasticLength(int shrink, int length, int stretch) { + set(shrink, length, stretch); + } + + /** + * Returns the amount by which this length may be shrinked. + * + * @return the amount by which this length may be shrinked. + */ + public int getShrink() { + return shrink; + } + + /** + * Returns the natural value of this length. + * + * @return the natural value of this length + */ + public int getLength() { + return length; + } + + /** + * Returns the amount by which this length may be stretched. + * + * @return the amount by which this length may be stretched. + */ + public int getStretch() { + return stretch; + } + + /** + * Resets the three components of this length to 0. + */ + public void reset() { + set(0, 0, 0); + } + + /** + * Sets the components of this length to the given values. + * + * @param shrink authorized shrink + * @param length natural length + * @param stretch authorized stretch + */ + public void set(int shrink, int length, int stretch) { + this.shrink = shrink; + this.length = length; + this.stretch = stretch; + } + + /** + * Sets the components of this length to those of the given length. + * + * @param elasticLength a length + */ + public void set(ElasticLength elasticLength) { + set(elasticLength.shrink, elasticLength.length, elasticLength.stretch); + } + + /** + * Adds the given values to the components of this length. + * + * @param shrink additional authorized shrink + * @param length additional natural length + * @param stretch additional authorized stretch + */ + public void add(int shrink, int length, int stretch) { + this.shrink += shrink; + this.length += length; + this.stretch += stretch; + } + + /** + * Adds to the components of this length those of the given length. + * + * @param elasticLength a length of which each component will be added to this ones' + */ + public void add(ElasticLength elasticLength) { + add(elasticLength.shrink, elasticLength.length, elasticLength.stretch); + } + + public String toString() { + return "[" + shrink + "," + length + "," + stretch + "]"; + } +} diff --git a/src/java/org/apache/fop/layoutmgr/breaking/FootnotesRecord.java b/src/java/org/apache/fop/layoutmgr/breaking/FootnotesRecord.java new file mode 100644 index 000000000..0ff1869ea --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/breaking/FootnotesRecord.java @@ -0,0 +1,204 @@ +/* + * 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.layoutmgr.breaking; + +import org.apache.fop.layoutmgr.PageBreakingAlgorithm; +import org.apache.fop.layoutmgr.PageBreakingAlgorithm.KnuthPageNode; +import org.apache.fop.traits.MinOptMax; + +/** + * A class that handles the placement of footnotes. Stores informations about + * existing footnotes, already placed ones, split footnotes, etc. + */ +public class FootnotesRecord extends OutOfLineRecord { + + /** + * Progress informations for footnotes. When building pages, footnotes content will + * be put one unbreakable part at a time. + */ + public class FootnotesProgress extends ProgressInfo { + + /** + * Creates and initializes a new record. + */ + public FootnotesProgress() { + super(); + } + + /** + * Creates a copy of the given record. + * + * @param footnotesProgress original progress information + */ + public FootnotesProgress(FootnotesProgress footnotesProgress) { + super(footnotesProgress); + } + + /** + * Checks whether there is some not yet typeset footnote content. + * + * @return true if the end of the footnote element list has not been + * reached yet + */ + private boolean hasNext() { + return knuthSequences.size() > 0 + && (lastInsertedIndex < knuthSequences.size() - 1 || !endOfOutOfLine()); + } + + /** + * Insert footnote content on the current page up to the next legal break. This + * may be within a footnote or at the end of one. + */ + private void next() { + if (lastInsertedIndex < 0 || endOfOutOfLine()) { + // Go to the next footnote + lastInsertedIndex++; + lastElementOfLastInsertedIndex = -1; + nbSplit = 1; + } else { // We are still inside a footnote + nextInsideBreak(); + } + } + + /** + * If the last footnote of the previous page was split, places at least one more + * chunk of it on the current page. This would look very weird if the rest of a + * footnote split on one page would appear only two pages further. So this is + * necessary to put at least one chunk on the current page. If this leads to an + * unfeasible page, then the previous page will never appear in the optimal page + * layout anyway. + */ + public void handleSplit() { + if (isLastSplit()) { + next(); + addSeparator(); + } + } + + /** + * If the current page is a float-only page, handles the splitting of the last + * footnote of the previous page. Usually by adding at least a chunk of it on the + * current page, unless footnotes are not allowed on float-only pages (TODO this + * may lead to weird results (footnote continued only two pages further)). + * + * @param mode one of {@link PageBreakingAlgorithm#FLOAT_PAGE_MODE} or {@link + * PageBreakingAlgorithm#FLUSH_MODE} + * @param activeNodeRecorder + * @param normalContentProgress information about normal content already typeset + * @param beforeFloatsProgress information about before-floats already typeset + * @param previousNode node ending the previous page + */ + public void handleSplit(int mode, + PageBreakingAlgorithm.ActiveNodeRecorder activeNodeRecorder, + PageBreakingAlgorithm.NormalContentProgressInfo normalContentProgress, + BeforeFloatsRecord.BeforeFloatsProgress beforeFloatsProgress, + KnuthPageNode previousNode) { + if (isLastSplit()) { + if (mode == PageBreakingAlgorithm.FLOAT_PAGE_MODE) { + if (PageBreakingAlgorithm.FOOTNOTES_ALLOWED_ON_FLOAT_PAGES) { + next(); + addSeparator(); + if (PageBreakingAlgorithm.FOOTNOTES_ONLY_PAGES_ALLOWED) { + activeNodeRecorder.handleNode(mode, normalContentProgress, + this, beforeFloatsProgress, previousNode); + } + } + } else { // mode == PageBreakingAlgorithm.FLUSH_MODE + next(); + addSeparator(); + activeNodeRecorder.handleNode(mode, normalContentProgress, + this, beforeFloatsProgress, previousNode); + } + } + } + + /** + * Considers the placement of footnotes on the current page. + * + * @param mode one of {@link PageBreakingAlgorithm#NORMAL_MODE}, {@link + * PageBreakingAlgorithm#FLOAT_PAGE_MODE} or {@link + * PageBreakingAlgorithm#FLUSH_MODE} + * @param activeNodeRecorder + * @param normalContentProgress information about normal content already typeset + * @param beforeFloatsProgress information about before-floats already typeset + * @param previousNode node ending the previous page + */ + public void consider(int mode, + PageBreakingAlgorithm.ActiveNodeRecorder activeNodeRecorder, + PageBreakingAlgorithm.NormalContentProgressInfo normalContentProgress, + BeforeFloatsRecord.BeforeFloatsProgress beforeFloatsProgress, + KnuthPageNode previousNode) { + beforeFloatsProgress.consider(mode, activeNodeRecorder, + normalContentProgress, this, previousNode); + if (alreadyInserted.getLength() == 0) { + addSeparator(); + } + switch (mode) { + case PageBreakingAlgorithm.NORMAL_MODE: + while (hasNext()) { + next(); + if (!activeNodeRecorder.handleNode(mode, normalContentProgress, + this, beforeFloatsProgress, previousNode)) { + break; + } + beforeFloatsProgress.consider(mode, activeNodeRecorder, + normalContentProgress, this, previousNode); + } + break; + case PageBreakingAlgorithm.FLOAT_PAGE_MODE: + if (PageBreakingAlgorithm.FOOTNOTES_ALLOWED_ON_FLOAT_PAGES) { + while (hasNext()) { + next(); + if (PageBreakingAlgorithm.FOOTNOTES_ONLY_PAGES_ALLOWED) { + if (!activeNodeRecorder.handleNode(mode, normalContentProgress, + this, beforeFloatsProgress, previousNode)) { + break; + } + } + beforeFloatsProgress.consider(mode, activeNodeRecorder, + normalContentProgress, this, previousNode); + } + } + break; + case PageBreakingAlgorithm.FLUSH_MODE: + while (hasNext()) { + next(); + if (!activeNodeRecorder.handleNode(mode, normalContentProgress, + this, beforeFloatsProgress, previousNode)) { + break; + } + beforeFloatsProgress.consider(mode, activeNodeRecorder, + normalContentProgress, this, previousNode); + } + break; + } + } + } + + /** + * Creates a new record for handling footnotes. + * + * @param footnoteSeparator dimensions of the separator between the normal content and + * the footnote area + */ + public FootnotesRecord(MinOptMax footnoteSeparator) { + super(footnoteSeparator); + } +} diff --git a/src/java/org/apache/fop/layoutmgr/breaking/LineBreakPosition.java b/src/java/org/apache/fop/layoutmgr/breaking/LineBreakPosition.java new file mode 100644 index 000000000..387d5eab1 --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/breaking/LineBreakPosition.java @@ -0,0 +1,51 @@ +package org.apache.fop.layoutmgr.breaking; + +import org.apache.fop.layoutmgr.LayoutManager; +import org.apache.fop.layoutmgr.LeafPosition; + +/** + * Private class to store information about inline breaks. + * Each value holds the start and end indexes into a List of + * inline break positions. + */ +public class LineBreakPosition extends LeafPosition { + /* + * TODO vh: fields temporarily made public to ease the moving of + * LineBreakPosition from a LineLayoutManager inner class to a top-level + * class. + */ + public int iParIndex; // index of the Paragraph this Position refers to + public int iStartIndex; //index of the first element this Position refers to + public int availableShrink; + public int availableStretch; + public int difference; + public double dAdjust; // Percentage to adjust (stretch or shrink) + public double ipdAdjust; // Percentage to adjust (stretch or shrink) + public int startIndent; + public int lineHeight; + public int lineWidth; + public int spaceBefore; + public int spaceAfter; + public int baseline; + + LineBreakPosition(LayoutManager lm, int index, int iStartIndex, int iBreakIndex, + int shrink, int stretch, int diff, + double ipdA, double adjust, int ind, + int lh, int lw, int sb, int sa, int bl) { + super(lm, iBreakIndex); + availableShrink = shrink; + availableStretch = stretch; + difference = diff; + iParIndex = index; + this.iStartIndex = iStartIndex; + ipdAdjust = ipdA; + dAdjust = adjust; + startIndent = ind; + lineHeight = lh; + lineWidth = lw; + spaceBefore = sb; + spaceAfter = sa; + baseline = bl; + } + +} \ No newline at end of file diff --git a/src/java/org/apache/fop/layoutmgr/breaking/LineBreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/breaking/LineBreakingAlgorithm.java new file mode 100644 index 000000000..e7e48a166 --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/breaking/LineBreakingAlgorithm.java @@ -0,0 +1,259 @@ +/* + * 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.layoutmgr.breaking; + +import java.util.ListIterator; + +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.flow.Block; +import org.apache.fop.layoutmgr.BreakingAlgorithm; +import org.apache.fop.layoutmgr.KnuthElement; +import org.apache.fop.layoutmgr.KnuthSequence; +import org.apache.fop.layoutmgr.inline.AlignmentContext; +import org.apache.fop.layoutmgr.inline.KnuthInlineBox; +import org.apache.fop.layoutmgr.inline.LineLayoutManager; +import org.apache.fop.layoutmgr.inline.LineLayoutManager.Paragraph; + +public class LineBreakingAlgorithm extends BreakingAlgorithm { + private LineLayoutManager thisLLM; + private int pageAlignment; + private int activePossibility; + private int addedPositions; + private int textIndent; + private int fillerMinWidth; + private int lineHeight; + private int lead; + private int follow; +// private int maxDiff; + private static final double MAX_DEMERITS = 10e6; + + public LineBreakingAlgorithm (int pageAlign, + int textAlign, int textAlignLast, + int indent, int fillerWidth, + int lh, int ld, int fl, boolean first, + int maxFlagCount, LineLayoutManager llm) { + super(textAlign, textAlignLast, first, false, maxFlagCount); + pageAlignment = pageAlign; + textIndent = indent; + fillerMinWidth = fillerWidth; + lineHeight = lh; + lead = ld; + follow = fl; + thisLLM = llm; + activePossibility = -1; +// maxDiff = fobj.getWidows() >= fobj.getOrphans() +// ? fobj.getWidows() +// : fobj.getOrphans(); + } + + public void updateData1(int lineCount, double demerits) { + thisLLM.getLineLayouts().addPossibility(lineCount, demerits); + log.trace("Layout possibility in " + lineCount + " lines; break at position:"); + } + + public void updateData2(KnuthNode bestActiveNode, + KnuthSequence par, + int total) { + // compute indent and adjustment ratio, according to + // the value of text-align and text-align-last + int indent = 0; + int difference = bestActiveNode.difference; + int textAlign = (bestActiveNode.line < total) ? alignment : alignmentLast; + indent += (textAlign == Constants.EN_CENTER) + ? difference / 2 : (textAlign == Constants.EN_END) ? difference : 0; + indent += (bestActiveNode.line == 1 && bFirst && thisLLM.isFirstInBlock()) ? textIndent : 0; + double ratio = (textAlign == Constants.EN_JUSTIFY + || difference < 0 && -difference <= bestActiveNode.availableShrink) + ? bestActiveNode.adjustRatio : 0; + + // add nodes at the beginning of the list, as they are found + // backwards, from the last one to the first one + + // the first time this method is called, initialize activePossibility + if (activePossibility == -1) { + activePossibility = 0; + addedPositions = 0; + } + + if (addedPositions == thisLLM.getLineLayouts().getLineCount(activePossibility)) { + activePossibility++; + addedPositions = 0; + } + + if (difference + bestActiveNode.availableShrink < 0) { + if (log.isWarnEnabled()) { + log.warn(FONode.decorateWithContextInfo( + "Line " + (addedPositions + 1) + + " of a paragraph overflows the available area.", thisLLM.getFObj())); + } + } + + //log.debug("LLM> (" + (thisLLM.getLineLayouts().getLineNumber(activePossibility) - addedPositions) + // + ") difference = " + difference + " ratio = " + ratio); + thisLLM.getLineLayouts().addBreakPosition(makeLineBreakPosition(par, + (bestActiveNode.line > 1 ? bestActiveNode.previous.position + 1 : 0), + bestActiveNode.position, + bestActiveNode.availableShrink - (addedPositions > 0 + ? 0 : ((Paragraph)par).getLineFiller().opt - ((Paragraph)par).getLineFiller().min), + bestActiveNode.availableStretch, + difference, ratio, indent), activePossibility); + addedPositions++; + } + + /* reset activePossibility, as if breakpoints have not yet been computed + */ + public void resetAlgorithm() { + activePossibility = -1; + } + + private LineBreakPosition makeLineBreakPosition(KnuthSequence par, + int firstElementIndex, + int lastElementIndex, + int availableShrink, + int availableStretch, + int difference, + double ratio, + int indent) { + // line height calculation - spaceBefore may differ from spaceAfter + // by 1mpt due to rounding + int spaceBefore = (lineHeight - lead - follow) / 2; + int spaceAfter = lineHeight - lead - follow - spaceBefore; + // height before the main baseline + int lineLead = lead; + // maximum follow + int lineFollow = follow; + // true if this line contains only zero-height, auxiliary boxes + // and the actual line width is 0; in this case, the line "collapses" + // i.e. the line area will have bpd = 0 + boolean bZeroHeightLine = (difference == thisLLM.getLineWidth()); + + // if line-stacking-strategy is "font-height", the line height + // is not affected by its content + if (((Block) thisLLM.getFObj()).getLineStackingStrategy() != Constants.EN_FONT_HEIGHT) { + ListIterator inlineIterator + = par.listIterator(firstElementIndex); + AlignmentContext lastAC = null; + int maxIgnoredHeight = 0; // See spec 7.13 + for (int j = firstElementIndex; + j <= lastElementIndex; + j++) { + KnuthElement element = (KnuthElement) inlineIterator.next(); + if (element instanceof KnuthInlineBox ) { + AlignmentContext ac = ((KnuthInlineBox) element).getAlignmentContext(); + if (ac != null && lastAC != ac) { + if (!ac.usesInitialBaselineTable() + || ac.getAlignmentBaselineIdentifier() != Constants.EN_BEFORE_EDGE + && ac.getAlignmentBaselineIdentifier() != Constants.EN_AFTER_EDGE) { + int alignmentOffset = ac.getTotalAlignmentBaselineOffset(); + if (alignmentOffset + ac.getAltitude() > lineLead) { + lineLead = alignmentOffset + ac.getAltitude(); + } + if (ac.getDepth() - alignmentOffset > lineFollow) { + lineFollow = ac.getDepth() - alignmentOffset; + } + } else { + if (ac.getHeight() > maxIgnoredHeight) { + maxIgnoredHeight = ac.getHeight(); + } + } + lastAC = ac; + } + if (bZeroHeightLine + && (!element.isAuxiliary() || ac != null && ac.getHeight() > 0)) { + bZeroHeightLine = false; + } + } + } + + if (lineFollow < maxIgnoredHeight - lineLead) { + lineFollow = maxIgnoredHeight - lineLead; + } + } + + thisLLM.setConstantLineHeight(lineLead + lineFollow); + + if (bZeroHeightLine) { + return new LineBreakPosition(thisLLM, + thisLLM.getKnuthParagraphs().indexOf(par), + firstElementIndex, lastElementIndex, + availableShrink, availableStretch, + difference, ratio, 0, indent, + 0, thisLLM.getLineWidth(), 0, 0, 0); + } else { + return new LineBreakPosition(thisLLM, + thisLLM.getKnuthParagraphs().indexOf(par), + firstElementIndex, lastElementIndex, + availableShrink, availableStretch, + difference, ratio, 0, indent, + lineLead + lineFollow, + thisLLM.getLineWidth(), spaceBefore, spaceAfter, + lineLead); + } + } + + public int findBreakingPoints(Paragraph par, /*int lineWidth,*/ + double threshold, boolean force, + int allowedBreaks) { + return super.findBreakingPoints(par, /*lineWidth,*/ + threshold, force, allowedBreaks); + } + + protected int filterActiveNodes() { + KnuthNode bestActiveNode = null; + + if (pageAlignment == Constants.EN_JUSTIFY) { + // leave all active nodes and find the optimum line number + //log.debug("LBA.filterActiveNodes> " + activeNodeCount + " layouts"); + for (int i = startLine; i < endLine; i++) { + for (KnuthNode node = getNode(i); node != null; node = node.next) { + //log.debug(" + lines = " + node.line + " demerits = " + node.totalDemerits); + bestActiveNode = compareNodes(bestActiveNode, node); + } + } + + // scan the node set once again and remove some nodes + //log.debug("LBA.filterActiveList> layout selection"); + for (int i = startLine; i < endLine; i++) { + for (KnuthNode node = getNode(i); node != null; node = node.next) { + //if (Math.abs(node.line - bestActiveNode.line) > maxDiff) { + //if (false) { + if (node.line != bestActiveNode.line + && node.totalDemerits > MAX_DEMERITS) { + //log.debug(" XXX lines = " + node.line + " demerits = " + node.totalDemerits); + deactivateNode(node); + } else { + //log.debug(" ok lines = " + node.line + " demerits = " + node.totalDemerits); + } + } + } + } else { + // leave only the active node with fewest total demerits + for (int i = startLine; i < endLine; i++) { + for (KnuthNode node = getNode(i); node != null; node = node.next) { + bestActiveNode = compareNodes(bestActiveNode, node); + if (node != bestActiveNode) { + deactivateNode(node); + } + } + } + } + return bestActiveNode.line; + } +} \ No newline at end of file diff --git a/src/java/org/apache/fop/layoutmgr/breaking/OutOfLineRecord.java b/src/java/org/apache/fop/layoutmgr/breaking/OutOfLineRecord.java index 53e880552..7df760e0b 100644 --- a/src/java/org/apache/fop/layoutmgr/breaking/OutOfLineRecord.java +++ b/src/java/org/apache/fop/layoutmgr/breaking/OutOfLineRecord.java @@ -20,295 +20,292 @@ package org.apache.fop.layoutmgr.breaking; import java.util.ArrayList; -import java.util.LinkedList; +import java.util.Iterator; import java.util.List; import java.util.ListIterator; import org.apache.fop.layoutmgr.KnuthElement; import org.apache.fop.layoutmgr.PageBreakingAlgorithm; import org.apache.fop.layoutmgr.SpaceResolver; -import org.apache.fop.layoutmgr.PageBreakingAlgorithm.KnuthPageNode; import org.apache.fop.traits.MinOptMax; /** * Helper class for dealing with out-of-line objects (before-floats and footnotes) when * breaking text into pages. It stores the necessary informations to place out-of-line * objects, and provides methods to manipulate them. - * + * * @see PageBreakingAlgorithm */ public class OutOfLineRecord { - + /** - * Stores informations about how many out-of-line objects have already been handled. + * During page breaking, records which out-of-line objects have already been handled, + * and how much of them are placed on the current page. */ - public static class ProgressInfo { - - /** Cumulated BPD length of all out-of-line objects inserted so far. */ - private int alreadyInsertedLength = 0; + class ProgressInfo { /** Index of the last inserted out-of-line object. */ - private int lastInsertedIndex = -1; + int lastInsertedIndex; /** * Index of the last inserted Knuth element of the last inserted out-of-line - * object. Currently only used for footnotes, as before-floats may not be split on - * several pages. Might be useful when later dealing with floats that cannot even - * be put on a page alone, however. + * object. + */ + int lastElementOfLastInsertedIndex; + + /** + * Amount of out-of-lines put on the current page. */ - private int lastElementOfLastInsertedIndex = -1; - + ElasticLength alreadyInserted = new ElasticLength(); + /** - * Initializes this record, as if no out-of-line object were handled yet. + * Number of times the last out-of-line is split. The initial value is 1, and is + * reset each time the end of an out-of-line is reached. The purpose is to compute + * additional demerits for split out-of-lines. */ - private void initialize() { - alreadyInsertedLength = 0; - lastInsertedIndex = -1; - lastElementOfLastInsertedIndex = -1; + int nbSplit; + + /** + * Creates and initializes a new record. + * + * @see OutOfLineRecord#initialize() + */ + ProgressInfo() { + initialize(); } /** - * @return a copy of this record + * Creates a copy of the given record. + * + * @param progressInfo original record */ - public ProgressInfo copy() { - ProgressInfo info = new ProgressInfo(); - info.alreadyInsertedLength = alreadyInsertedLength; - info.lastInsertedIndex = lastInsertedIndex; - info.lastElementOfLastInsertedIndex = lastElementOfLastInsertedIndex; - return info; + ProgressInfo(ProgressInfo progressInfo) { + this.lastInsertedIndex = progressInfo.lastInsertedIndex; + this.lastElementOfLastInsertedIndex = progressInfo.lastElementOfLastInsertedIndex; + this.alreadyInserted.set(progressInfo.alreadyInserted); + this.nbSplit = progressInfo.nbSplit; } /** - * Returns the cumulated length of all already typeset out-of-line objects. - * @return the total length in the block-progression-direction + * Returns the amount of out-of-lines inserted on the current page. + * + * @return the amount of out-of-lines inserted on the current page */ - public int getAlreadyInsertedLength() { - return alreadyInsertedLength; + public ElasticLength getInserted() { + return alreadyInserted; } /** - * Returns the index of the last element of the last already typeset out-of-line - * object. - * @return the index of the last placed KnuthElement + * Returns the number of times the last out-of-line is split. + * + * @return the number of times the last out-of-line is split + */ + public int getNbSplit() { + return nbSplit; + } + + /** + * Returns the index of the last element of the last inserted out-of-line. + * + * @return the index of the last element of the last inserted out-of-line */ public int getLastElementOfLastInsertedIndex() { return lastElementOfLastInsertedIndex; } /** - * @return the index of the last already typeset out-of-line object. + * Returns the index of the last inserted out-of-line. + * + * @return the index of the last inserted out-of-line */ public int getLastInsertedIndex() { return lastInsertedIndex; } - public String toString() { - return "length=" + alreadyInsertedLength - + ", index=" + lastInsertedIndex - + ", elt=" + lastElementOfLastInsertedIndex; + /** + * Initializes this record such that no out-of-line has been inserted yet. + */ + public void initialize() { + lastInsertedIndex = -1; + lastElementOfLastInsertedIndex = -1; + alreadyInserted.reset(); + nbSplit = 1; } - } - - /** - * Sequences of KnuthElement corresponding to already encountered out-of-line objects. - * This is a List of List of KnuthElement. - */ - private List knuthSequences = null; - /** - * Each element of this list corresponds to the cumulated length in the BPD of all the - * out-of-line objects up to the given index. This is a List of Integer. - * - * @see OutOfLineRecord#knuthSequences - */ - private List cumulativeLengths = null; + /** + * Records progress status for out-of-lines up to the previous page. + * + * @param info progress informations for the previous page + */ + public void setPrevious(ProgressInfo info) { + lastInsertedIndex = info.lastInsertedIndex; + lastElementOfLastInsertedIndex = info.lastElementOfLastInsertedIndex; + if (lastInsertedIndex >= 0) { + List lastOutOfLine = ((List) knuthSequences.get(lastInsertedIndex)); + // If the last out-of-line was split, go just before the first next box + while (lastElementOfLastInsertedIndex < lastOutOfLine.size() - 2 + && !((KnuthElement) lastOutOfLine.get(lastElementOfLastInsertedIndex + 1)).isBox()) { + lastElementOfLastInsertedIndex++; + } + if (lastElementOfLastInsertedIndex < lastOutOfLine.size() - 1) { + // We haven't reached the end of the out-of-line yet + nbSplit = info.nbSplit + 1; + } else { + nbSplit = 1; + } + } + alreadyInserted.reset(); + } - /** - * True if new out-of-line objects are cited in the sequence of Knuth elements since - * the last encountered legal breakpoint. - * - * @see OutOfLineRecord#newSinceLastBreakpoint() - */ - private boolean newSinceLastBreakpoint = false; + /** + * Checks whether there are still out-of-line objects to be placed. + * + * @return true if not all out-of-lines have been placed yet, + * otherwise false + */ + public boolean remaining() { + return (lastInsertedIndex < knuthSequences.size() - 1) || isLastSplit(); + } - /** - * Index of the first newly encountered out-of-line object since the last legal - * breakpoint. - * - * @see OutOfLineRecord#knuthSequences - */ - private int firstNewIndex = 0; + /** + * Returns the number of still to-be-placed out-of-lines. + * @return the number of not yet typeset out-of-line objects. + */ + public int getNbOfDeferred() { + return knuthSequences.size() - 1 - lastInsertedIndex; + } - /** - * Dimension in the BPD of the separator between the out-of-line area and the main - * area. - */ - private MinOptMax separatorLength = null; + /** + * Checks whether the end of the current out-of-line has been reached. + * + * @return true if the whole out-of-line has been placed, otherwise + * false + */ + boolean endOfOutOfLine() { + return lastElementOfLastInsertedIndex + >= ((List) knuthSequences.get(lastInsertedIndex)).size() - 1; + } - /** - * Record of already handled out-of-line objects. - * - * @see ProgressInfo - */ - private ProgressInfo progressInfo; + /** + * Checks whether the last out-of-line placed on the current page must be split or + * not. + * + * @return true if the last typeset out-of-line object must be split on + * several pages. + */ + public boolean isLastSplit() { + return lastInsertedIndex >= 0 && !endOfOutOfLine(); + } - public OutOfLineRecord(MinOptMax separatorLength) { - this.separatorLength = separatorLength; - this.progressInfo = new ProgressInfo(); - } + /** + * Adds the dimensions of the separator between the out-of-line area and the main + * content to the amount of out-of-lines already placed on the current page. + */ + public void addSeparator() { + alreadyInserted.add(separator); + } - /** - * Initializes this record, as if no out-of-line object were handled yet. - */ - public void initialize() { - knuthSequences = null; - cumulativeLengths = null; - newSinceLastBreakpoint = false; - firstNewIndex = 0; - progressInfo.initialize(); - } + /** + * Places on the current page out-of-line content up to the next legal break in the current + * out-of-line. This method is meant to be called by subclasses of this class, not + * by external classes even in the same package. + *

    Pre-condition: it is supposed that we are inside + * the out-of-line, and that we haven't reached its end yet. + */ + void nextInsideBreak() { + List knuthSequence = (List) knuthSequences.get(lastInsertedIndex); + Iterator elementIter = knuthSequence.listIterator(lastElementOfLastInsertedIndex + 1); + boolean prevIsBox = false; + do { + lastElementOfLastInsertedIndex++; + KnuthElement element = (KnuthElement) elementIter.next(); + if (element.isBox()) { + alreadyInserted.add(0, element.getW(), 0); + prevIsBox = true; + } else if (element.isGlue()) { + if (prevIsBox) { + break; + } + alreadyInserted.add(element.getZ(), element.getW(), element.getY()); + prevIsBox = false; + } else { + if (element.getP() < KnuthElement.INFINITE) { + alreadyInserted.add(0, element.getW(), 0); + break; + } + prevIsBox = false; + } + } while (lastElementOfLastInsertedIndex < knuthSequence.size() - 1); + if (lastElementOfLastInsertedIndex == knuthSequence.size() - 1) { + nbSplit = 0; + } + } - /** - * @return the informations about already handled out-of-line objects - */ - public ProgressInfo getProgress() { - return this.progressInfo; + public String toString() { + return "index=" + lastInsertedIndex + + ", elt=" + lastElementOfLastInsertedIndex + + ", inserted=" + alreadyInserted + + ", splits=" + nbSplit; + } } /** - * @return the length in the BPD of the separator between the out-of-line area and the - * main area. + * Sequences of KnuthElement corresponding to already encountered out-of-line objects. + * This is a List of List of KnuthElement. */ - public MinOptMax getSeparatorLength() { - return separatorLength; - } + List knuthSequences = new ArrayList(); /** - * @return the total length of already encountered out-of-line objects + * Dimension in the BPD of the separator between the out-of-line area and the main + * area. */ - public int getTotalLength() { - if (cumulativeLengths == null || cumulativeLengths.size() == 0) { - return 0; - } else { - return ((Integer) cumulativeLengths.get(cumulativeLengths.size() - 1)).intValue(); - } - } + private ElasticLength separator = null; /** - * @return true if out-of-line objects have already been encountered (but not - * necessarily typeset yet) + * Creates a new record for a given type of out-of-lines. + * + * @param separator dimensions of the separator between the out-of-line area and the + * main area */ - public boolean existing() { - return (knuthSequences != null && knuthSequences.size() > 0); - } - - public void resetNewSinceLastBreakpoint() { - newSinceLastBreakpoint = false; + public OutOfLineRecord(MinOptMax separator) { + this.separator = new ElasticLength(separator.opt - separator.min, + separator.opt, + separator.max - separator.opt); } /** - * @return true if new out-of-line objects are cited in the sequence of Knuth - * elements since the last encountered legal breakpoint. + * Initializes this record, as if no out-of-line object were handled yet. */ - public boolean newSinceLastBreakpoint() { - return newSinceLastBreakpoint; + public void initialize() { + knuthSequences = new ArrayList(); } /** * Records one or more newly encountered out-of-line objects. - * @param elementLists the list of corresponding Knuth sequences + * + * @param elementLists the list of corresponding Knuth sequences. This is a + * List<List<KnuthElement>>. */ public void add(List elementLists) { - // Initialize stuff if necessary - if (knuthSequences == null) { - knuthSequences = new ArrayList(); - cumulativeLengths = new ArrayList(); - } - if (!newSinceLastBreakpoint) { - newSinceLastBreakpoint = true; - firstNewIndex = knuthSequences.size(); - } - // compute the total length of the footnotes ListIterator elementListsIterator = elementLists.listIterator(); while (elementListsIterator.hasNext()) { - LinkedList noteList = (LinkedList) elementListsIterator.next(); - - //Space resolution (Note: this does not respect possible stacking constraints - //between footnotes!) - SpaceResolver.resolveElementList(noteList); - - int noteLength = 0; - knuthSequences.add(noteList); - ListIterator noteListIterator = noteList.listIterator(); - while (noteListIterator.hasNext()) { - KnuthElement element = (KnuthElement) noteListIterator.next(); - if (element.isBox() || element.isGlue()) { - noteLength += element.getW(); - } - } - cumulativeLengths.add(new Integer(getTotalLength() + noteLength)); - } - } - - /** - * Sets the progress informations to the given values. Called whenever a new active - * node is considered; the informations regarding already handled out-of-line objects - * must be set to the active node's values in order to know from where to start the - * placement of further objects. - * - * @param info progress informations of the currently considered active node - */ - public void setProgress(ProgressInfo info) { - this.progressInfo.alreadyInsertedLength = info.alreadyInsertedLength; - this.progressInfo.lastElementOfLastInsertedIndex = info.lastElementOfLastInsertedIndex; - this.progressInfo.lastInsertedIndex = info.lastInsertedIndex; - } + List knuthSequence = (List) elementListsIterator.next(); - /* Unless I'm wrong, newOnThisPagePlusPiecesFromPrevious always implies - * notAllInserted. And if A => B, then A && B <=> B - * So this code may be simplified, see deferred() below - */ - /** -// * Returns true if their are (pieces of) footnotes to be typeset on the -// * current page. -// * @param listIndex index of the last inserted footnote for the -// * currently considered active node -// * @param elementIndex index of the last element of the last inserted footnote -// * @param length total length of all footnotes inserted so far -// */ -// public boolean deferredFootnotes(ProgressInfo progressInfo) { -// boolean newOnThisPagePlusPiecesFromPrevious = -// newSinceLastBreakpoint() -// && firstNewIndex != 0 -// && (progressInfo.lastInsertedIndex < firstNewIndex - 1 -// || progressInfo.lastElementOfLastInsertedIndex < -// ((LinkedList) knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1); -// boolean notAllInserted = progressInfo.alreadyInsertedLength < getTotalLength(); -// return notAllInserted; -// } + // Space resolution (Note: this does not respect possible stacking constraints + // /between/ out-of-lines!) + SpaceResolver.resolveElementList(knuthSequence); - /** - * @return true if some out-of-line objects have not already been - * typeset. - */ - public boolean deferred() { - return progressInfo.alreadyInsertedLength < getTotalLength(); - } - - /** - * @return the number of not yet typeset out-of-line objects. - */ - public int getNbOfDeferred() { - return knuthSequences.size() - 1 - progressInfo.lastInsertedIndex; + knuthSequences.add(knuthSequence); + } } /** - * @return true if the last typeset out-of-line object must be split on - * several pages. + * Returns true if out-of-lines have already been encountered. + * + * @return true if out-of-lines are recorded, possibly not yet typeset */ - public boolean isSplit() { - return (progressInfo.lastElementOfLastInsertedIndex - < ((LinkedList) knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1); + public boolean existing() { + return knuthSequences.size() > 0; } /** @@ -318,284 +315,19 @@ public class OutOfLineRecord { * it does not exist */ public List getSequence(int index) { - /*TODO vh: bof */ - if (knuthSequences == null) { - return null; - } else { - return (List) knuthSequences.get(index); - } + return (List) knuthSequences.get(index); } - /** - * Tries to split the flow of footnotes to put one part on the current page. - * @param prevNodeProgress informations about footnotes already inserted on the - * previous page - * @param availableLength available space for footnotes on this page - * @param canDeferOldFootnotes - * @return the length of footnotes which could be inserted on this page - */ - public int getFootnoteSplit(ProgressInfo prevNodeProgress, - int availableLength, boolean canDeferOldFootnotes) { - if (availableLength <= 0) { - progressInfo.alreadyInsertedLength = prevNodeProgress.getAlreadyInsertedLength(); - return 0; - } else { - // the split should contain a piece of the last footnote - // together with all previous, not yet inserted footnotes; - // but if this is not possible, try adding as much content as possible - int splitLength = 0; - ListIterator noteListIterator = null; - KnuthElement element = null; - boolean somethingAdded = false; - - // prevNodeProgress.lastInsertedIndex and - // prevNodeProgress.lastElementOfLastInsertedIndex points to the last footnote element - // already placed in a page: advance to the next element - int listIndex = prevNodeProgress.lastInsertedIndex; - int elementIndex = prevNodeProgress.lastElementOfLastInsertedIndex; - if (listIndex == -1 - || elementIndex == ((LinkedList) knuthSequences.get(listIndex)).size() - 1) { - listIndex++; - elementIndex = 0; - } else { - elementIndex++; - } - - // try adding whole notes - // if there are more than 1 footnote to insert - if (knuthSequences.size() - 1 > listIndex) { - // add the previous footnotes: these cannot be broken or deferred - if (!canDeferOldFootnotes - && newSinceLastBreakpoint() - && firstNewIndex > 0) { - splitLength = ((Integer) cumulativeLengths.get(firstNewIndex - 1)).intValue() - - prevNodeProgress.alreadyInsertedLength; - listIndex = firstNewIndex; - elementIndex = 0; - } - // try adding the new footnotes - while (((Integer) cumulativeLengths.get(listIndex)).intValue() - - prevNodeProgress.alreadyInsertedLength <= availableLength) { - splitLength = ((Integer) cumulativeLengths.get(listIndex)).intValue() - - prevNodeProgress.alreadyInsertedLength; - somethingAdded = true; - listIndex++; - elementIndex = 0; - } - // as this method is called only if it is not possible to insert - // all footnotes, at this point listIndex and elementIndex points to - // an existing element, the next one we will try to insert - } - - // try adding a split of the next note - noteListIterator = ((List) knuthSequences.get(listIndex)).listIterator(elementIndex); - - int prevSplitLength = 0; - int prevIndex = -1; - int index = -1; - - while (!somethingAdded || splitLength <= availableLength) { - if (!somethingAdded) { - somethingAdded = true; - } else { - prevSplitLength = splitLength; - prevIndex = index; - } - // get a sub-sequence from the note element list - boolean bPrevIsBox = false; - while (noteListIterator.hasNext()) { - // as this method is called only if it is not possible to insert - // all footnotes, and we have already tried (and failed) to insert - // this whole footnote, the while loop will never reach the end - // of the note sequence - element = (KnuthElement) noteListIterator.next(); - if (element.isBox()) { - // element is a box - splitLength += element.getW(); - bPrevIsBox = true; - } else if (element.isGlue()) { - // element is a glue - if (bPrevIsBox) { - // end of the sub-sequence - index = noteListIterator.previousIndex(); - break; - } - bPrevIsBox = false; - splitLength += element.getW(); - } else { - // element is a penalty - if (element.getP() < KnuthElement.INFINITE) { - // end of the sub-sequence - index = noteListIterator.previousIndex(); - break; - } - } - } - } - // if prevSplitLength is 0, this means that the available length isn't enough - // to insert even the smallest split of the last footnote, so we cannot end a - // page here - // if prevSplitLength is > 0 we can insert some footnote content in this page - // and insert the remaining in the following one - if (!somethingAdded) { - // there was not enough space to add a piece of the first new footnote - // this is not a good break - prevSplitLength = 0; - } else if (prevSplitLength > 0) { - // prevIndex is -1 if we have added only some whole footnotes - progressInfo.lastInsertedIndex = (prevIndex != -1) ? listIndex : listIndex - 1; - progressInfo.lastElementOfLastInsertedIndex = (prevIndex != -1) - ? prevIndex - : ((LinkedList) knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1; - } - progressInfo.alreadyInsertedLength - = prevNodeProgress.getAlreadyInsertedLength() + prevSplitLength; - return prevSplitLength; - } - } - - /** - * Tries to split the flow of floats to put some floats on the current page. - * @param prevProgress floats already inserted on the previous page - * @param availableLength available space for floats - * @return the length of floats which could be placed on the current page - */ - public int getFloatSplit(ProgressInfo prevProgress, int availableLength) { - /* - * Normally this method is called only when there is some place for - * floats => availableLength > 0 - */ - int splitLength = 0; - int listIndex = prevProgress.lastInsertedIndex + 1; - - while (listIndex < knuthSequences.size() - && ((Integer) cumulativeLengths.get(listIndex)).intValue() - - prevProgress.alreadyInsertedLength <= availableLength) { - splitLength = ((Integer) cumulativeLengths.get(listIndex)).intValue() - - prevProgress.alreadyInsertedLength; - listIndex++; - } - progressInfo.lastInsertedIndex = listIndex - 1; - progressInfo.alreadyInsertedLength = prevProgress.alreadyInsertedLength + splitLength; - return splitLength; - } - - /** - * Places on the current page all of the out-of-line objects not yet inserted. - */ - public void insertAll() { - progressInfo.alreadyInsertedLength = getTotalLength(); - progressInfo.lastInsertedIndex = knuthSequences.size() - 1; - progressInfo.lastElementOfLastInsertedIndex - = ((List) knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1; - } /** * When restarting the algorithm from a given point, reset the informations about * out-of-line objects to the values at that point. * @param elementLists out-of-line sequences which are met after the restarting point, - * and thus must be removed from the list of already encoutered objects. + * and thus must be removed from the list of already encountered objects. */ public void reset(List elementLists) { for (int i = 0; i < elementLists.size(); i++) { knuthSequences.remove(knuthSequences.size() - 1); - cumulativeLengths.remove(cumulativeLengths.size() - 1); - } - } - - /** - * When the whole normal flow has been typeset and there are still footnotes to be - * placed, creates as many pages as necessary to place them. - */ - public void createFootnotePages(KnuthPageNode lastNode, PageBreakingAlgorithm algo, int lineWidth) { - progressInfo.alreadyInsertedLength = lastNode.footnotesProgress.getAlreadyInsertedLength(); - progressInfo.lastInsertedIndex = lastNode.footnotesProgress.getLastInsertedIndex(); - progressInfo.lastElementOfLastInsertedIndex = lastNode.footnotesProgress.getLastElementOfLastInsertedIndex(); - int availableBPD = lineWidth; - int split = 0; - KnuthPageNode prevNode = lastNode; - - // create pages containing the remaining footnote bodies - while (progressInfo.alreadyInsertedLength < getTotalLength()) { - // try adding some more content - if (((Integer) cumulativeLengths.get(progressInfo.lastInsertedIndex)).intValue() - progressInfo.alreadyInsertedLength - <= availableBPD) { - // add a whole footnote - availableBPD -= ((Integer) cumulativeLengths.get(progressInfo.lastInsertedIndex)).intValue() - - progressInfo.alreadyInsertedLength; - progressInfo.alreadyInsertedLength = ((Integer)cumulativeLengths.get(progressInfo.lastInsertedIndex)).intValue(); - progressInfo.lastElementOfLastInsertedIndex - = ((LinkedList)knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1; - } else if ((split = getFootnoteSplit(progressInfo, availableBPD, true)) - > 0) { - // add a piece of a footnote - availableBPD -= split; - // footnoteListIndex has already been set in getFootnoteSplit() - // footnoteElementIndex has already been set in getFootnoteSplit() - } else { - // cannot add any content: create a new node and start again - KnuthPageNode node = (KnuthPageNode) - algo.createNode(lastNode.position, prevNode.line + 1, 1, - progressInfo.alreadyInsertedLength - prevNode.footnotesProgress.getAlreadyInsertedLength(), - 0, 0, - 0, 0, 0, - 0, 0, prevNode); - algo.addNode(node.line, node); - algo.removeNode(prevNode.line, prevNode); - - prevNode = node; - availableBPD = lineWidth; - } - } - // create the last node - KnuthPageNode node = (KnuthPageNode) - algo.createNode(lastNode.position, prevNode.line + 1, 1, - getTotalLength() - prevNode.footnotesProgress.getAlreadyInsertedLength(), 0, 0, - 0, 0, 0, - 0, 0, prevNode); - algo.addNode(node.line, node); - algo.removeNode(prevNode.line, prevNode); - } - - /* TODO vh: won't work when there are also footnotes. To be merged with createFootnotePages */ - public void createFloatPages(KnuthPageNode lastNode, PageBreakingAlgorithm algo, int lineWidth) { - progressInfo.alreadyInsertedLength = lastNode.floatsProgress.getAlreadyInsertedLength(); - progressInfo.lastInsertedIndex = lastNode.floatsProgress.getLastInsertedIndex(); - int availableBPD = lineWidth; - KnuthPageNode prevNode = lastNode; - - // create pages containing the remaining float bodies - while (progressInfo.alreadyInsertedLength < getTotalLength()) { - // try adding some more content - if (((Integer) cumulativeLengths.get(progressInfo.lastInsertedIndex + 1)).intValue() - progressInfo.alreadyInsertedLength - <= availableBPD) { - // add a whole float - progressInfo.lastInsertedIndex++; - availableBPD -= ((Integer) cumulativeLengths.get(progressInfo.lastInsertedIndex)).intValue() - - progressInfo.alreadyInsertedLength; - progressInfo.alreadyInsertedLength = ((Integer)cumulativeLengths.get(progressInfo.lastInsertedIndex)).intValue(); - } else { - // cannot add any content: create a new node and start again - KnuthPageNode node = (KnuthPageNode) - algo.createNode(lastNode.position, prevNode.line + 1, 1, - progressInfo.alreadyInsertedLength - prevNode.floatsProgress.getAlreadyInsertedLength(), - 0, 0, - 0, 0, 0, - 0, prevNode.totalDemerits + (progressInfo.lastInsertedIndex - prevNode.floatsProgress.lastInsertedIndex) * 10000, prevNode); - algo.addNode(node.line, node); - algo.removeNode(prevNode.line, prevNode); - - prevNode = node; - availableBPD = lineWidth; - } } - // create the last node - KnuthPageNode node = (KnuthPageNode) - algo.createNode(lastNode.position, prevNode.line + 1, 1, - getTotalLength() - prevNode.floatsProgress.getAlreadyInsertedLength(), 0, 0, - 0, 0, 0, - 0, prevNode.totalDemerits + (progressInfo.lastInsertedIndex - prevNode.floatsProgress.lastInsertedIndex) * 10000, prevNode); - algo.addNode(node.line, node); - algo.removeNode(prevNode.line, prevNode); } } diff --git a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java index aada86f8f..4f57e5bb6 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java @@ -47,6 +47,8 @@ import org.apache.fop.layoutmgr.NonLeafPosition; import org.apache.fop.layoutmgr.Position; import org.apache.fop.layoutmgr.PositionIterator; import org.apache.fop.layoutmgr.SpaceSpecifier; +import org.apache.fop.layoutmgr.breaking.LineBreakPosition; +import org.apache.fop.layoutmgr.breaking.LineBreakingAlgorithm; import org.apache.fop.area.Area; import org.apache.fop.area.LineArea; import org.apache.fop.area.inline.InlineArea; @@ -100,49 +102,6 @@ public class LineLayoutManager extends InlineStackingLayoutManager } } - /** - * Private class to store information about inline breaks. - * Each value holds the start and end indexes into a List of - * inline break positions. - */ - private static class LineBreakPosition extends LeafPosition { - private int iParIndex; // index of the Paragraph this Position refers to - private int iStartIndex; //index of the first element this Position refers to - private int availableShrink; - private int availableStretch; - private int difference; - private double dAdjust; // Percentage to adjust (stretch or shrink) - private double ipdAdjust; // Percentage to adjust (stretch or shrink) - private int startIndent; - private int lineHeight; - private int lineWidth; - private int spaceBefore; - private int spaceAfter; - private int baseline; - - LineBreakPosition(LayoutManager lm, int index, int iStartIndex, int iBreakIndex, - int shrink, int stretch, int diff, - double ipdA, double adjust, int ind, - int lh, int lw, int sb, int sa, int bl) { - super(lm, iBreakIndex); - availableShrink = shrink; - availableStretch = stretch; - difference = diff; - iParIndex = index; - this.iStartIndex = iStartIndex; - ipdAdjust = ipdA; - dAdjust = adjust; - startIndent = ind; - lineHeight = lh; - lineWidth = lw; - spaceBefore = sb; - spaceAfter = sa; - baseline = bl; - } - - } - - private int textAlignment = EN_JUSTIFY; private int textAlignmentLast; private int effectiveAlignment; @@ -195,7 +154,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager } // this class represents a paragraph - private class Paragraph extends InlineKnuthSequence { + public class Paragraph extends InlineKnuthSequence { /** Number of elements to ignore at the beginning of the list. */ private int ignoreAtStart = 0; /** Number of elements to ignore at the end of the list. */ @@ -221,6 +180,10 @@ public class LineLayoutManager extends InlineStackingLayoutManager lastLineEndIndent = endIndent; } + public MinOptMax getLineFiller() { + return lineFiller; + } + public void startParagraph(int lw) { lineWidth = lw; startSequence(); @@ -307,233 +270,6 @@ public class LineLayoutManager extends InlineStackingLayoutManager } } - private class LineBreakingAlgorithm extends BreakingAlgorithm { - private LineLayoutManager thisLLM; - private int pageAlignment; - private int activePossibility; - private int addedPositions; - private int textIndent; - private int fillerMinWidth; - private int lineHeight; - private int lead; - private int follow; - private int maxDiff; - private static final double MAX_DEMERITS = 10e6; - - public LineBreakingAlgorithm (int pageAlign, - int textAlign, int textAlignLast, - int indent, int fillerWidth, - int lh, int ld, int fl, boolean first, - int maxFlagCount, LineLayoutManager llm) { - super(textAlign, textAlignLast, first, false, maxFlagCount); - pageAlignment = pageAlign; - textIndent = indent; - fillerMinWidth = fillerWidth; - lineHeight = lh; - lead = ld; - follow = fl; - thisLLM = llm; - activePossibility = -1; - maxDiff = fobj.getWidows() >= fobj.getOrphans() - ? fobj.getWidows() - : fobj.getOrphans(); - } - - public void updateData1(int lineCount, double demerits) { - lineLayouts.addPossibility(lineCount, demerits); - log.trace("Layout possibility in " + lineCount + " lines; break at position:"); - } - - public void updateData2(KnuthNode bestActiveNode, - KnuthSequence par, - int total) { - // compute indent and adjustment ratio, according to - // the value of text-align and text-align-last - int indent = 0; - int difference = bestActiveNode.difference; - int textAlign = (bestActiveNode.line < total) ? alignment : alignmentLast; - indent += (textAlign == Constants.EN_CENTER) - ? difference / 2 : (textAlign == Constants.EN_END) ? difference : 0; - indent += (bestActiveNode.line == 1 && bFirst && isFirstInBlock) ? textIndent : 0; - double ratio = (textAlign == Constants.EN_JUSTIFY - || difference < 0 && -difference <= bestActiveNode.availableShrink) - ? bestActiveNode.adjustRatio : 0; - - // add nodes at the beginning of the list, as they are found - // backwards, from the last one to the first one - - // the first time this method is called, initialize activePossibility - if (activePossibility == -1) { - activePossibility = 0; - addedPositions = 0; - } - - if (addedPositions == lineLayouts.getLineCount(activePossibility)) { - activePossibility++; - addedPositions = 0; - } - - if (difference + bestActiveNode.availableShrink < 0) { - if (log.isWarnEnabled()) { - log.warn(FONode.decorateWithContextInfo( - "Line " + (addedPositions + 1) - + " of a paragraph overflows the available area.", getFObj())); - } - } - - //log.debug("LLM> (" + (lineLayouts.getLineNumber(activePossibility) - addedPositions) - // + ") difference = " + difference + " ratio = " + ratio); - lineLayouts.addBreakPosition(makeLineBreakPosition(par, - (bestActiveNode.line > 1 ? bestActiveNode.previous.position + 1 : 0), - bestActiveNode.position, - bestActiveNode.availableShrink - (addedPositions > 0 - ? 0 : ((Paragraph)par).lineFiller.opt - ((Paragraph)par).lineFiller.min), - bestActiveNode.availableStretch, - difference, ratio, indent), activePossibility); - addedPositions++; - } - - /* reset activePossibility, as if breakpoints have not yet been computed - */ - public void resetAlgorithm() { - activePossibility = -1; - } - - private LineBreakPosition makeLineBreakPosition(KnuthSequence par, - int firstElementIndex, - int lastElementIndex, - int availableShrink, - int availableStretch, - int difference, - double ratio, - int indent) { - // line height calculation - spaceBefore may differ from spaceAfter - // by 1mpt due to rounding - int spaceBefore = (lineHeight - lead - follow) / 2; - int spaceAfter = lineHeight - lead - follow - spaceBefore; - // height before the main baseline - int lineLead = lead; - // maximum follow - int lineFollow = follow; - // true if this line contains only zero-height, auxiliary boxes - // and the actual line width is 0; in this case, the line "collapses" - // i.e. the line area will have bpd = 0 - boolean bZeroHeightLine = (difference == iLineWidth); - - // if line-stacking-strategy is "font-height", the line height - // is not affected by its content - if (fobj.getLineStackingStrategy() != EN_FONT_HEIGHT) { - ListIterator inlineIterator - = par.listIterator(firstElementIndex); - AlignmentContext lastAC = null; - int maxIgnoredHeight = 0; // See spec 7.13 - for (int j = firstElementIndex; - j <= lastElementIndex; - j++) { - KnuthElement element = (KnuthElement) inlineIterator.next(); - if (element instanceof KnuthInlineBox ) { - AlignmentContext ac = ((KnuthInlineBox) element).getAlignmentContext(); - if (ac != null && lastAC != ac) { - if (!ac.usesInitialBaselineTable() - || ac.getAlignmentBaselineIdentifier() != EN_BEFORE_EDGE - && ac.getAlignmentBaselineIdentifier() != EN_AFTER_EDGE) { - int alignmentOffset = ac.getTotalAlignmentBaselineOffset(); - if (alignmentOffset + ac.getAltitude() > lineLead) { - lineLead = alignmentOffset + ac.getAltitude(); - } - if (ac.getDepth() - alignmentOffset > lineFollow) { - lineFollow = ac.getDepth() - alignmentOffset; - } - } else { - if (ac.getHeight() > maxIgnoredHeight) { - maxIgnoredHeight = ac.getHeight(); - } - } - lastAC = ac; - } - if (bZeroHeightLine - && (!element.isAuxiliary() || ac != null && ac.getHeight() > 0)) { - bZeroHeightLine = false; - } - } - } - - if (lineFollow < maxIgnoredHeight - lineLead) { - lineFollow = maxIgnoredHeight - lineLead; - } - } - - constantLineHeight = lineLead + lineFollow; - - if (bZeroHeightLine) { - return new LineBreakPosition(thisLLM, - knuthParagraphs.indexOf(par), - firstElementIndex, lastElementIndex, - availableShrink, availableStretch, - difference, ratio, 0, indent, - 0, iLineWidth, 0, 0, 0); - } else { - return new LineBreakPosition(thisLLM, - knuthParagraphs.indexOf(par), - firstElementIndex, lastElementIndex, - availableShrink, availableStretch, - difference, ratio, 0, indent, - lineLead + lineFollow, - iLineWidth, spaceBefore, spaceAfter, - lineLead); - } - } - - public int findBreakingPoints(Paragraph par, /*int lineWidth,*/ - double threshold, boolean force, - int allowedBreaks) { - return super.findBreakingPoints(par, /*lineWidth,*/ - threshold, force, allowedBreaks); - } - - protected int filterActiveNodes() { - KnuthNode bestActiveNode = null; - - if (pageAlignment == EN_JUSTIFY) { - // leave all active nodes and find the optimum line number - //log.debug("LBA.filterActiveNodes> " + activeNodeCount + " layouts"); - for (int i = startLine; i < endLine; i++) { - for (KnuthNode node = getNode(i); node != null; node = node.next) { - //log.debug(" + lines = " + node.line + " demerits = " + node.totalDemerits); - bestActiveNode = compareNodes(bestActiveNode, node); - } - } - - // scan the node set once again and remove some nodes - //log.debug("LBA.filterActiveList> layout selection"); - for (int i = startLine; i < endLine; i++) { - for (KnuthNode node = getNode(i); node != null; node = node.next) { - //if (Math.abs(node.line - bestActiveNode.line) > maxDiff) { - //if (false) { - if (node.line != bestActiveNode.line - && node.totalDemerits > MAX_DEMERITS) { - //log.debug(" XXX lines = " + node.line + " demerits = " + node.totalDemerits); - removeNode(i, node); - } else { - //log.debug(" ok lines = " + node.line + " demerits = " + node.totalDemerits); - } - } - } - } else { - // leave only the active node with fewest total demerits - for (int i = startLine; i < endLine; i++) { - for (KnuthNode node = getNode(i); node != null; node = node.next) { - bestActiveNode = compareNodes(bestActiveNode, node); - if (node != bestActiveNode) { - removeNode(i, node); - } - } - } - } - return bestActiveNode.line; - } - } - private int constantLineHeight = 12000; @@ -558,6 +294,26 @@ public class LineLayoutManager extends InlineStackingLayoutManager follow = f; } + public LineLayoutPossibilities getLineLayouts() { + return lineLayouts; + } + + public boolean isFirstInBlock() { + return isFirstInBlock; + } + + public int getLineWidth() { + return iLineWidth; + } + + public void setConstantLineHeight(int constantLineHeight) { + this.constantLineHeight = constantLineHeight; + } + + public List getKnuthParagraphs() { + return knuthParagraphs; + } + /** @see org.apache.fop.layoutmgr.LayoutManager */ public LinkedList getNextKnuthElements(LayoutContext context, int alignment) { Font fs = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo(), this); diff --git a/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java index 6e647353e..41ab67dcd 100644 --- a/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java @@ -30,6 +30,7 @@ import org.apache.fop.layoutmgr.PositionIterator; import org.apache.fop.layoutmgr.Position; import org.apache.fop.layoutmgr.NonLeafPosition; import org.apache.fop.layoutmgr.TraitSetter; +import org.apache.fop.layoutmgr.SpaceResolver.SpaceHandlingBreakPosition; import org.apache.fop.area.Area; import org.apache.fop.area.Block; @@ -145,6 +146,8 @@ public class ListItemContentLayoutManager extends BlockStackingLayoutManager { if (firstLM == null) { firstLM = lastLM; } + } else if (pos instanceof SpaceHandlingBreakPosition) { + positionList.add(pos); } else { // pos was created by this ListBlockLM, so it must be ignored } diff --git a/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java index 6bcbf4de0..336d34f30 100644 --- a/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java @@ -288,7 +288,21 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager int penaltyHeight = step + getMaxRemainingHeight(fullHeights, partialHeights) - totalHeight; + + //Additional penalty height from penalties in the source lists + int additionalPenaltyHeight = 0; + KnuthElement endEl = (KnuthElement)elementLists[0].get(end[0]); + if (endEl instanceof KnuthPenalty) { + additionalPenaltyHeight = ((KnuthPenalty)endEl).getW(); + } + endEl = (KnuthElement)elementLists[1].get(end[1]); + if (endEl instanceof KnuthPenalty) { + additionalPenaltyHeight = Math.max( + additionalPenaltyHeight, ((KnuthPenalty)endEl).getW()); + } + int boxHeight = step - addedBoxHeight - penaltyHeight; + penaltyHeight += additionalPenaltyHeight; //Add AFTER calculating boxHeight! // add the new elements addedBoxHeight += boxHeight; @@ -300,7 +314,6 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager if (keepWithNextActive || mustKeepTogether()) { p = KnuthPenalty.INFINITE; } - //returnList.add(new KnuthPenalty(penaltyHeight, p, false, stepPosition, false)); returnList.add(new BreakElement(stepPosition, penaltyHeight, p, -1, context)); } } diff --git a/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java index 27f3da0c9..1c3045e52 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java @@ -77,6 +77,8 @@ public class TableContentLayoutManager implements PercentBaseContext { private int startXOffset; private int usedBPD; + private TableStepper stepper = new TableStepper(this); + /** * Main constructor * @param parent Parent layout manager @@ -144,15 +146,18 @@ public class TableContentLayoutManager implements PercentBaseContext { this.headerList = getKnuthElementsForRowIterator( headerIter, context, alignment, TableRowIterator.HEADER); ElementListUtils.removeLegalBreaks(this.headerList); - this.headerNetHeight = ElementListUtils.calcContentLength(this.headerList); + this.headerNetHeight = + ElementListUtils.calcContentLength(this.headerList); if (log.isDebugEnabled()) { - log.debug("==> Header: " + headerNetHeight + " - " + this.headerList); + log.debug("==> Header: " + + headerNetHeight + " - " + this.headerList); } TableHeaderFooterPosition pos = new TableHeaderFooterPosition( getTableLM(), true, this.headerList); KnuthBox box = new KnuthBox(headerNetHeight, pos, false); if (getTableLM().getTable().omitHeaderAtBreak()) { - //We can simply add the table header at the beginning of the whole list + //We can simply add the table header at the start + //of the whole list headerAsFirst = box; } else { headerAsSecondToLast = box; @@ -162,17 +167,17 @@ public class TableContentLayoutManager implements PercentBaseContext { this.footerList = getKnuthElementsForRowIterator( footerIter, context, alignment, TableRowIterator.FOOTER); ElementListUtils.removeLegalBreaks(this.footerList); - this.footerNetHeight = ElementListUtils.calcContentLength(this.footerList); + this.footerNetHeight = + ElementListUtils.calcContentLength(this.footerList); if (log.isDebugEnabled()) { - log.debug("==> Footer: " + footerNetHeight + " - " + this.footerList); - } - if (true /*getTableLM().getTable().omitFooterAtBreak()*/) { - //We can simply add the table header at the end of the whole list - TableHeaderFooterPosition pos = new TableHeaderFooterPosition( - getTableLM(), false, this.footerList); - KnuthBox box = new KnuthBox(footerNetHeight, pos, false); - footerAsLast = box; + log.debug("==> Footer: " + + footerNetHeight + " - " + this.footerList); } + //We can simply add the table footer at the end of the whole list + TableHeaderFooterPosition pos = new TableHeaderFooterPosition( + getTableLM(), false, this.footerList); + KnuthBox box = new KnuthBox(footerNetHeight, pos, false); + footerAsLast = box; } LinkedList returnList = getKnuthElementsForRowIterator( trIter, context, alignment, TableRowIterator.BODY); @@ -192,7 +197,8 @@ public class TableContentLayoutManager implements PercentBaseContext { * @param iter TableRowIterator instance to fetch rows from * @param context Active LayoutContext * @param alignment alignment indicator - * @param bodyType Indicates what kind of body is being processed (BODY, HEADER or FOOTER) + * @param bodyType Indicates what kind of body is being processed + * (BODY, HEADER or FOOTER) * @return An element list */ private LinkedList getKnuthElementsForRowIterator(TableRowIterator iter, @@ -211,7 +217,7 @@ public class TableContentLayoutManager implements PercentBaseContext { pen.setP(-KnuthPenalty.INFINITE); pen.setBreakClass(rowFO.getBreakBefore()); } else if (last instanceof BreakElement) { - BreakElement breakPoss = (BreakElement)last; + BreakElement breakPoss = (BreakElement) last; breakPoss.setPenaltyValue(-KnuthPenalty.INFINITE); breakPoss.setBreakClass(rowFO.getBreakBefore()); } @@ -573,9 +579,7 @@ public class TableContentLayoutManager implements PercentBaseContext { log.debug(" height=" + rowHeights[i] + " explicit=" + explicitRowHeights[i]); } } - //TODO It may make sense to reuse the stepper since it allocates quite some space - TableStepper stepper = new TableStepper(this); - LinkedList returnedList = stepper.getCombinedKnuthElementsForRowGroup( + LinkedList returnedList = this.stepper.getCombinedKnuthElementsForRowGroup( context, rowGroup, maxColumnCount, bodyType); if (returnedList != null) { returnList.addAll(returnedList); diff --git a/src/java/org/apache/fop/layoutmgr/table/TableStepper.java b/src/java/org/apache/fop/layoutmgr/table/TableStepper.java index e4191732b..9d77293ed 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableStepper.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableStepper.java @@ -72,10 +72,10 @@ public class TableStepper { */ public TableStepper(TableContentLayoutManager tclm) { this.tclm = tclm; - this.activeRow = 0; } private void setup(int columnCount) { + this.activeRow = 0; elementLists = new List[columnCount]; startRow = new int[columnCount]; start = new int[columnCount]; diff --git a/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java b/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java index bf90191b2..c8f5f71a6 100644 --- a/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java +++ b/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java @@ -26,6 +26,7 @@ import java.awt.Color; import java.awt.geom.AffineTransform; import org.w3c.dom.Document; +import org.w3c.dom.svg.SVGAElement; import org.w3c.dom.svg.SVGDocument; import org.w3c.dom.svg.SVGSVGElement; @@ -39,6 +40,7 @@ import org.apache.fop.pdf.PDFPage; import org.apache.fop.pdf.PDFState; import org.apache.fop.pdf.PDFStream; import org.apache.fop.pdf.PDFResourceContext; +import org.apache.fop.svg.PDFAElementBridge; import org.apache.fop.svg.PDFBridgeContext; import org.apache.fop.svg.PDFGraphics2D; import org.apache.fop.svg.SVGUserAgent; @@ -55,7 +57,9 @@ import org.apache.avalon.framework.configuration.Configuration; import org.apache.batik.bridge.GVTBuilder; import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.ViewBox; +import org.apache.batik.dom.svg.SVGDOMImplementation; import org.apache.batik.gvt.GraphicsNode; +import org.apache.batik.util.SVGConstants; /** * PDF XML handler for SVG (uses Apache Batik). @@ -142,6 +146,7 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler */ protected void renderSVGDocument(RendererContext context, Document doc) { + PDFRenderer renderer = (PDFRenderer)context.getRenderer(); PDFInfo pdfInfo = getPDFInfo(context); if (pdfInfo.paintAsBitmap) { try { @@ -163,12 +168,18 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler final float uaResolution = context.getUserAgent().getSourceResolution(); SVGUserAgent ua = new SVGUserAgent(25.4f / uaResolution, new AffineTransform()); - GVTBuilder builder = new GVTBuilder(); + //Scale for higher resolution on-the-fly images from Batik + double s = uaResolution / deviceResolution; + AffineTransform resolutionScaling = new AffineTransform(); + resolutionScaling.scale(s, s); - //TODO This AffineTransform here has to be fixed!!! - AffineTransform linkTransform = pdfInfo.pdfState.getTransform(); - linkTransform.translate(xOffset / 1000f, yOffset / 1000f); + //Transformation matrix that establishes the local coordinate system for the SVG graphic + //in relation to PDF's initial coordinate system. + AffineTransform baseTransform = (AffineTransform)renderer.currentBasicTransform.clone(); + baseTransform.concatenate(pdfInfo.pdfState.getTransform()); + GVTBuilder builder = new GVTBuilder(); + //Controls whether text painted by Batik is generated using text or path operations boolean strokeText = false; Configuration cfg = pdfInfo.cfg; @@ -178,11 +189,12 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler BridgeContext ctx = new PDFBridgeContext(ua, (strokeText ? null : pdfInfo.fi), - linkTransform); + new AffineTransform()); GraphicsNode root; try { root = builder.build(ctx, doc); + builder = null; } catch (Exception e) { log.error("svg graphic could not be built: " + e.getMessage(), e); @@ -195,35 +207,36 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler float sx = pdfInfo.width / (float)w; float sy = pdfInfo.height / (float)h; - ctx = null; - builder = null; + //Scaling and translation for the bounding box of the image + AffineTransform scaling = new AffineTransform( + sx, 0, 0, sy, xOffset / 1000f, yOffset / 1000f); + + //Finish the baseTransform, now that we know everything + baseTransform.concatenate(scaling); + baseTransform.concatenate(resolutionScaling); + + //Now that we have the full baseTransform, we can update the transformation matrix for + //the AElementBridge. + PDFAElementBridge aBridge = (PDFAElementBridge)ctx.getBridge( + SVGDOMImplementation.SVG_NAMESPACE_URI, SVGConstants.SVG_A_TAG); + aBridge.getCurrentTransform().setTransform(baseTransform); /* * Clip to the svg area. * Note: To have the svg overlay (under) a text area then use * an fo:block-container */ - PDFRenderer renderer = (PDFRenderer)context.getRenderer(); + pdfInfo.currentStream.add("%SVG setup\n"); renderer.saveGraphicsState(); renderer.setColor(Color.black, false, null); renderer.setColor(Color.black, true, null); - // transform so that the coordinates (0,0) is from the top left - // and positive is down and to the right. (0,0) is where the - // viewBox puts it. - pdfInfo.currentStream.add(sx + " 0 0 " + sy + " " + xOffset / 1000f + " " - + yOffset / 1000f + " cm\n"); + + if (!scaling.isIdentity()) { + pdfInfo.currentStream.add("%viewbox\n"); + pdfInfo.currentStream.add(CTMHelper.toPDFString(scaling, false) + " cm\n"); + } SVGSVGElement svg = ((SVGDocument)doc).getRootElement(); - //AffineTransform at = ViewBox.getPreserveAspectRatioTransform( - // svg, w / 1000f, h / 1000f); - AffineTransform at = ViewBox.getPreserveAspectRatioTransform(svg, - pdfInfo.width / 1000f, pdfInfo.height / 1000f); - /* - if (!at.isIdentity()) { - double[] vals = new double[6]; - at.getMatrix(vals); - pdfInfo.currentStream.add(CTMHelper.toPDFString(at, false) + " cm\n"); - }*/ if (pdfInfo.pdfContext == null) { pdfInfo.pdfContext = pdfInfo.pdfPage; @@ -233,21 +246,18 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler pdfInfo.pdfContext, pdfInfo.pdfPage.referencePDF(), pdfInfo.currentFontName, pdfInfo.currentFontSize); graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext()); - pdfInfo.pdfState.push(); - AffineTransform transform = new AffineTransform(); - // scale to viewbox - transform.translate(xOffset / 1000f, yOffset / 1000f); - if (deviceResolution != uaResolution) { - //Scale for higher resolution on-the-fly images from Batik - double s = uaResolution / deviceResolution; - at.scale(s, s); - pdfInfo.currentStream.add("" + PDFNumber.doubleOut(s) + " 0 0 " - + PDFNumber.doubleOut(s) + " 0 0 cm\n"); + if (!resolutionScaling.isIdentity()) { + pdfInfo.currentStream.add("%resolution scaling for " + uaResolution + " -> " + deviceResolution + "\n"); + pdfInfo.currentStream.add( + CTMHelper.toPDFString(resolutionScaling, false) + " cm\n"); graphics.scale(1 / s, 1 / s); } + + pdfInfo.currentStream.add("%SVG start\n"); - pdfInfo.pdfState.setTransform(transform); + pdfInfo.pdfState.push(); + pdfInfo.pdfState.setTransform(baseTransform); graphics.setPDFState(pdfInfo.pdfState); graphics.setOutputStream(pdfInfo.outputStream); try { @@ -257,9 +267,9 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler log.error("svg graphic could not be rendered: " + e.getMessage(), e); } - - renderer.restoreGraphicsState(); pdfInfo.pdfState.pop(); + renderer.restoreGraphicsState(); + pdfInfo.currentStream.add("%SVG end\n"); } /** @see org.apache.fop.render.XMLHandler#supportsRenderer(org.apache.fop.render.Renderer) */ diff --git a/src/java/org/apache/fop/svg/PDFAElementBridge.java b/src/java/org/apache/fop/svg/PDFAElementBridge.java index 56576afa5..e9bb8a955 100644 --- a/src/java/org/apache/fop/svg/PDFAElementBridge.java +++ b/src/java/org/apache/fop/svg/PDFAElementBridge.java @@ -51,6 +51,11 @@ public class PDFAElementBridge extends AbstractGraphicsNodeBridge { transform = tf; } + /** @return the transformation matrix for links */ + public AffineTransform getCurrentTransform() { + return this.transform; + } + /** * Returns 'a'. * @return the name of this node diff --git a/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java b/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java index 4d7ac826c..74dac01fe 100644 --- a/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java +++ b/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java @@ -165,7 +165,7 @@ public class PDFDocumentGraphics2D extends PDFGraphics2D * @see org.apache.avalon.framework.activity.Initializable#initialize() */ public void initialize() throws Exception { - if (this.fontInfo == null) { + if (this.fontInfo == null || this.cfg != null) { fontInfo = new FontInfo(); FontSetup.setup(fontInfo, this.pdfContext.getFontList(), null); //FontState fontState = new FontState("Helvetica", "normal", diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java index af0a0cfaa..0e821975d 100644 --- a/src/java/org/apache/fop/svg/PDFGraphics2D.java +++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java @@ -342,6 +342,16 @@ public class PDFGraphics2D extends AbstractGraphics2D { + PDFNumber.doubleOut(matrix[4], DEC) + " " + PDFNumber.doubleOut(matrix[5], DEC) + " cm\n"); } + + /** + * This is mainly used for shading patterns which use the document-global coordinate system + * instead of the local one. + * @return the transformation matrix that established the basic user space for this document + */ + protected AffineTransform getBaseTransform() { + AffineTransform at = new AffineTransform(graphicsState.getTransform()); + return at; + } /** * This is a pdf specific method used to add a link to the @@ -897,7 +907,7 @@ public class PDFGraphics2D extends AbstractGraphics2D { // Build proper transform from gradient space to page space // ('Patterns' don't get userspace transform). AffineTransform transform; - transform = new AffineTransform(graphicsState.getTransform()); + transform = new AffineTransform(getBaseTransform()); transform.concatenate(getTransform()); transform.concatenate(gp.getTransform()); @@ -973,7 +983,7 @@ public class PDFGraphics2D extends AbstractGraphics2D { } AffineTransform transform; - transform = new AffineTransform(graphicsState.getTransform()); + transform = new AffineTransform(getBaseTransform()); transform.concatenate(getTransform()); transform.concatenate(rgp.getTransform()); @@ -1103,7 +1113,7 @@ public class PDFGraphics2D extends AbstractGraphics2D { bbox.add(new Double(rect.getY())); AffineTransform transform; - transform = new AffineTransform(graphicsState.getTransform()); + transform = new AffineTransform(getBaseTransform()); transform.concatenate(getTransform()); transform.concatenate(pp.getPatternTransform()); diff --git a/status.xml b/status.xml index 91afbd8c3..c6cef3964 100644 --- a/status.xml +++ b/status.xml @@ -28,6 +28,29 @@ + + Added relaxed validation for empty list-item-*, as suggested by Gary Reed. + + + Modified proportional-column-width() function to log an error if used + with table-layout=auto + + + Deferred property resolution for markers until they are actually retrieved, + which leads to percentages and relative font-sizes now getting the correct + values. Also deferred white-space-handling for markers. + + + Changed the way overflowing pages are handled. The overflow property on region-body + is now used to define the behaviour. + + + Fixed a memory-leak: The FO tree part of a page-sequence was not released when a + page-sequence was finished. + + + Bugfix: Table headers and footers were swallowed when a table was nested in a list-block. + Fixed a bug with indent handling when margins are used on a surrounding block and not start/end-indent. @@ -169,6 +192,11 @@ Initial support for page-number-citation-last (XSL 1.1). Works without problems only for page-sequence so far. + + Improved support of before-floats: allow float-only pages, take into + account floats' shrinkability/stretchability, handle the splitting of + big floats. + diff --git a/test/fotree/testcases/table-cell_column-number_rowspan_bug38397.fo b/test/fotree/testcases/table-cell_column-number_rowspan_bug38397.fo new file mode 100644 index 000000000..0cf275217 --- /dev/null +++ b/test/fotree/testcases/table-cell_column-number_rowspan_bug38397.fo @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + cell1 + + + + cell2 + + + + cell3 + + + + + + cell4 + + + + + + cell5 + + + + cell6 + + + + + + cell7 + + + + cell8 + + + + cell9 + + + + + + cell10 + + + + cell11 + + + + + + cell12 + + + + + + + diff --git a/test/java/org/apache/fop/memory/MemoryEater.java b/test/java/org/apache/fop/memory/MemoryEater.java new file mode 100644 index 000000000..ed8f46441 --- /dev/null +++ b/test/java/org/apache/fop/memory/MemoryEater.java @@ -0,0 +1,108 @@ +/* + * 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.memory; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; + +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.sax.SAXTransformerFactory; +import javax.xml.transform.stream.StreamSource; + +import org.apache.commons.io.output.NullOutputStream; +import org.apache.fop.apps.Fop; +import org.apache.fop.apps.FopFactory; +import org.apache.fop.apps.MimeConstants; + +/** + * Debug tool to create and process large FO files by replicating them a specified number of times. + */ +public class MemoryEater { + + private static void eatMemory(File foFile, int replicatorRepeats) throws Exception { + + SAXTransformerFactory tFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance(); + FopFactory fopFactory = FopFactory.newInstance(); + + File xsltFile = new File("test/xsl/fo-replicator.xsl"); + Source xslt = new StreamSource(xsltFile); + + Source src = new StreamSource(foFile); + + Transformer transformer = tFactory.newTransformer(xslt); + transformer.setParameter("repeats", new Integer(replicatorRepeats)); + + OutputStream out = new NullOutputStream(); //write to /dev/nul + Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out); + Result res = new SAXResult(fop.getDefaultHandler()); + + transformer.transform(src, res); + + System.out.println("Generated " + fop.getResults().getPageCount() + " pages."); + + } + + private static void prompt() throws IOException { + BufferedReader in = new BufferedReader(new java.io.InputStreamReader(System.in)); + System.out.print("Press return to continue..."); + in.readLine(); + } + + /** + * Main method. + * @param args the command-line arguments + */ + public static void main(String[] args) { + boolean doPrompt = true; //true if you want a chance to start the monitoring console + try { + int replicatorRepeats = 2; + if (args.length > 0) { + replicatorRepeats = Integer.parseInt(args[0]); + } + File testFile = new File("examples/fo/basic/readme.fo"); + + System.out.println("MemoryEater! About to replicate the test file " + + replicatorRepeats + " times..."); + if (doPrompt) { + prompt(); + } + + System.out.println("Processing..."); + long start = System.currentTimeMillis(); + + eatMemory(testFile, replicatorRepeats); + + long duration = System.currentTimeMillis() - start; + System.out.println("Success! Job took " + duration + " ms"); + + if (doPrompt) { + prompt(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/test/layoutengine/disabled-testcases.xml b/test/layoutengine/disabled-testcases.xml index f3d61eb2b..15c762961 100644 --- a/test/layoutengine/disabled-testcases.xml +++ b/test/layoutengine/disabled-testcases.xml @@ -194,20 +194,6 @@ Keep-with-previous doesn't work inside tables and lists, yet. - - Whitespace around markers is not handled correctly - marker_white-space-collapse.xml - Whitespace within markers is handled according to the value of - the white-space-collapse property in the context of the fo:marker and not according to - the value of the property in the fo:retrieve-marker context. - - - Relative font sizes within markers are not handled correctly - marker_font-size.xml - Relative font sizes within markers are evaluated according to the - font size in the fo:marker context and not the font size in the fo:retrieve-marker - context. - Page breaking doesn't deal with IPD changes page-breaking_4.xml @@ -334,7 +320,8 @@ table-cell empty area with marker.xml table-cell_empty_area_with_marker.xml - A table-cell producing an empty area does currently not add any markers to a page. See TODO entry in AreaAdditionUtil. + A table-cell producing an empty area does currently not add any markers to a page. + See TODO entry in AreaAdditionUtil. Border conditionality on table @@ -370,4 +357,18 @@ underfull page is created so that the citation be on the same page as the float. + + Before-floats plus footnotes on last page + before-float_footnote_last-page.xml + When an additional page must be created at the end + of a flow for placing both a remaining before-float and a footnote, an + IndexOutOfBoundException occurs. + + + Before-float too large to fit on a page alone + before-float_large.xml + When a before-float is too large to even fit on a page alone, + it should be split on several pages. Currently, there is an OutOfMemory + error. + diff --git a/test/layoutengine/standard-testcases/before-float_footnote_last-page.xml b/test/layoutengine/standard-testcases/before-float_footnote_last-page.xml new file mode 100644 index 000000000..ddc006122 --- /dev/null +++ b/test/layoutengine/standard-testcases/before-float_footnote_last-page.xml @@ -0,0 +1,109 @@ + + + + + +

    + This test checks that a when an additional page must be created at the end + of a flow for remaining out-of-line objects, then both the remaining + before-floats and footnotes are placed on this page. +

    +

    + Currently disabled as this is a non-working feature (footnote is deferred + instead of being split). +

    + + + + + + + + + + + + This is a block without a float. This is a block without a float. + This is a block without a float. This is a block without a float. + + + This is a block without a float. This is a block without a float. + This is a block without a float. This is a block without a float. + + + This is a block with a float and a footnote. + This is a block with a float and a footnote. + This is a block with a float and a footnote. + The footnote citation is here + 1 + + + Footnote body. Footnote body. Footnote body. Footnote body. + Footnote body. Footnote body. Footnote body. Footnote body. + Footnote body. Footnote body. Footnote body. Footnote body. + Footnote body. Footnote body. Footnote body. Footnote body. + + + . The float anchor is here + + This is the float content. This is the float content. + This is the float content. This is the float content. + This is the float content. This is the float content. + This is the float content. This is the float content. + + . + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/layoutengine/standard-testcases/before-float_large.xml b/test/layoutengine/standard-testcases/before-float_large.xml new file mode 100644 index 000000000..15d03c9ad --- /dev/null +++ b/test/layoutengine/standard-testcases/before-float_large.xml @@ -0,0 +1,103 @@ + + + + + +

    + This test checks that a before-float too large to fit on a page alone is + split on several pages. +

    +

    + Currently disabled as this feature still needs some debugging. +

    +
    + + + + + + + + + + + This is a block without a float. This is a block without a float. + This is a block without a float. This is a block without a float. + + + This is a block without a float. This is a block without a float. + This is a block without a float. This is a block without a float. + + + This is a block with a float. This is a block with a float. + This is a block with a float. This is a block with a float. + This is a block with a float. This is a block with a float. + The float anchor is here + + This is the float content. This is the float content. + This is the float content. This is the float content. + + + This is the float content. This is the float content. + This is the float content. This is the float content. + + + This is the float content. This is the float content. + This is the float content. This is the float content. + + + This is the float content. This is the float content. + This is the float content. This is the float content. + + + This is the float content. This is the float content. + This is the float content. This is the float content. + + . + This is a block with a float. This is a block with a float. + This is a block with a float. This is a block with a float. + + + This is a block without a float. This is a block without a float. + This is a block without a float. This is a block without a float. + + + This is a block without a float. This is a block without a float. + This is a block without a float. This is a block without a float. + + + + + + + + +
    diff --git a/test/layoutengine/standard-testcases/before-float_not-deferred_stretch.xml b/test/layoutengine/standard-testcases/before-float_not-deferred_stretch.xml index 1f7bb756a..6e35b1146 100644 --- a/test/layoutengine/standard-testcases/before-float_not-deferred_stretch.xml +++ b/test/layoutengine/standard-testcases/before-float_not-deferred_stretch.xml @@ -91,7 +91,7 @@ - + + + + +

    + This test checks keep-together with overflow conditions. +

    +

    + Widows and Orphans are disabled in this test to avoid side-effects. +

    +
    + + + + + + + + + + + + + + + + + + block1 + block2 + + block3 + block4 + block5 + block6 + block7 + + block8 + block9 + + block10 + block11 + block12 + block13 + block14 + + block15 + + + + + + + + + + + + + + + + + + + + + +
    diff --git a/test/layoutengine/standard-testcases/marker_font-size.xml b/test/layoutengine/standard-testcases/marker_font-size.xml index 26a0a172c..c4a7baba1 100644 --- a/test/layoutengine/standard-testcases/marker_font-size.xml +++ b/test/layoutengine/standard-testcases/marker_font-size.xml @@ -37,7 +37,9 @@ - 1. Marker + 1. Marker @@ -46,7 +48,7 @@ - + First marker with small font @@ -65,8 +67,9 @@ + - - + + diff --git a/test/layoutengine/standard-testcases/marker_percentage-resolution.xml b/test/layoutengine/standard-testcases/marker_percentage-resolution.xml new file mode 100644 index 000000000..6bb9343ad --- /dev/null +++ b/test/layoutengine/standard-testcases/marker_percentage-resolution.xml @@ -0,0 +1,106 @@ + + + + + +

    + This test checks markers and percentage resolution: + Percentages should be evaluated in the context in which the marker is + retrieved. +

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + Subtotal + + + 29.95 + + + + + + MemoryStick 32MB + + + 29.95 + + + + + + + Test + + + + + + + Subtotal + + + 49.95 + + + + + + Geek-Tool + + + 20.00 + + + + + + + + + + + + +
    diff --git a/test/layoutengine/standard-testcases/marker_white-space-collapse.xml b/test/layoutengine/standard-testcases/marker_white-space-collapse.xml index 645d34f4e..ddb9d23a2 100644 --- a/test/layoutengine/standard-testcases/marker_white-space-collapse.xml +++ b/test/layoutengine/standard-testcases/marker_white-space-collapse.xml @@ -52,7 +52,7 @@
    - + First marker with whitespace around @@ -71,11 +71,12 @@ - - - + + + - - + + diff --git a/test/layoutengine/standard-testcases/markers_7.xml b/test/layoutengine/standard-testcases/markers_7.xml index 3bb7af379..ef018f296 100644 --- a/test/layoutengine/standard-testcases/markers_7.xml +++ b/test/layoutengine/standard-testcases/markers_7.xml @@ -96,7 +96,7 @@ - + fo:table @@ -118,7 +118,7 @@ - + fo:table-body @@ -140,7 +140,7 @@ - + fo:table-header @@ -167,7 +167,7 @@
    - + fo:table-footer @@ -194,7 +194,7 @@
    - + fo:table-cell diff --git a/test/layoutengine/standard-testcases/markers_8.xml b/test/layoutengine/standard-testcases/markers_8.xml index f9de1bf02..ce2cac1d8 100644 --- a/test/layoutengine/standard-testcases/markers_8.xml +++ b/test/layoutengine/standard-testcases/markers_8.xml @@ -49,7 +49,7 @@ - + table1 @@ -77,7 +77,7 @@ - + table2 @@ -99,7 +99,7 @@ - + table3 diff --git a/test/layoutengine/standard-testcases/markers_9.xml b/test/layoutengine/standard-testcases/markers_9.xml index fd66dfb5e..5197665e5 100644 --- a/test/layoutengine/standard-testcases/markers_9.xml +++ b/test/layoutengine/standard-testcases/markers_9.xml @@ -49,7 +49,7 @@ - + body1 @@ -77,7 +77,7 @@ - + body2 @@ -99,7 +99,7 @@ - + body3 @@ -130,7 +130,7 @@ - + body5 diff --git a/test/layoutengine/standard-testcases/table-body_basic_1.xml b/test/layoutengine/standard-testcases/table-body_basic_1.xml index 5f7011979..3c2c61a1d 100644 --- a/test/layoutengine/standard-testcases/table-body_basic_1.xml +++ b/test/layoutengine/standard-testcases/table-body_basic_1.xml @@ -31,7 +31,7 @@ - + diff --git a/test/layoutengine/standard-testcases/table-cell_background-image.xml b/test/layoutengine/standard-testcases/table-cell_background-image.xml index 7bb180571..047728243 100644 --- a/test/layoutengine/standard-testcases/table-cell_background-image.xml +++ b/test/layoutengine/standard-testcases/table-cell_background-image.xml @@ -34,7 +34,7 @@ - + diff --git a/test/layoutengine/standard-testcases/table-cell_padding_percentages.xml b/test/layoutengine/standard-testcases/table-cell_padding_percentages.xml index acb795a62..2cc55afad 100644 --- a/test/layoutengine/standard-testcases/table-cell_padding_percentages.xml +++ b/test/layoutengine/standard-testcases/table-cell_padding_percentages.xml @@ -31,7 +31,7 @@ - + diff --git a/test/layoutengine/standard-testcases/table-header_in_list_bug.xml b/test/layoutengine/standard-testcases/table-header_in_list_bug.xml new file mode 100644 index 000000000..f23578628 --- /dev/null +++ b/test/layoutengine/standard-testcases/table-header_in_list_bug.xml @@ -0,0 +1,115 @@ + + + + + +

    + This test checks lists with nested fo:tables. Table headers used to be + swallowed. +

    +
    + + + + + + + + + + + + + 1. + + + + + + + + Header + + + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 7 + + + + + 8 + + + + + + + + + trailing text... + trailing text... + + + + + + + + + + + + + + + + +
    diff --git a/test/layoutengine/standard-testcases/table-row_background-image.xml b/test/layoutengine/standard-testcases/table-row_background-image.xml index 2145b43e8..eec4b6e17 100644 --- a/test/layoutengine/standard-testcases/table-row_background-image.xml +++ b/test/layoutengine/standard-testcases/table-row_background-image.xml @@ -34,7 +34,7 @@ - + diff --git a/test/layoutengine/standard-testcases/table_background-image.xml b/test/layoutengine/standard-testcases/table_background-image.xml index 78968dd03..0b808638e 100644 --- a/test/layoutengine/standard-testcases/table_background-image.xml +++ b/test/layoutengine/standard-testcases/table_background-image.xml @@ -34,7 +34,11 @@ - + diff --git a/test/layoutengine/standard-testcases/table_padding_percentages.xml b/test/layoutengine/standard-testcases/table_padding_percentages.xml index 091ed49da..69c8f19b4 100644 --- a/test/layoutengine/standard-testcases/table_padding_percentages.xml +++ b/test/layoutengine/standard-testcases/table_padding_percentages.xml @@ -31,7 +31,8 @@ - + diff --git a/test/xsl/fo-page-sequence-splitter.xsl b/test/xsl/fo-page-sequence-splitter.xsl new file mode 100644 index 000000000..d493ad2af --- /dev/null +++ b/test/xsl/fo-page-sequence-splitter.xsl @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/xsl/fo-replicator.xsl b/test/xsl/fo-replicator.xsl new file mode 100644 index 000000000..0f2736cd0 --- /dev/null +++ b/test/xsl/fo-replicator.xsl @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + - + + + + - + + + + + + + + + -- 2.39.5