diff options
35 files changed, 1779 insertions, 717 deletions
diff --git a/src/documentation/content/xdocs/trunk/extensions.xml b/src/documentation/content/xdocs/trunk/extensions.xml index dd4a79535..288695286 100644 --- a/src/documentation/content/xdocs/trunk/extensions.xml +++ b/src/documentation/content/xdocs/trunk/extensions.xml @@ -115,6 +115,79 @@ to following pages. Here is an example of FO code creating such a table-header:< or list-block. </p> </section> + <section id="external-document"> + <title>fox:external-document</title> + <note> + This feature is incomplete. Support for multi-page documents will be added shortly. + At the moment, only single-page images will work. And this will not work with RTF output. + </note> + <p> + This is a proprietary extension element which allows to add whole images as pages to + an FO document. For example, if you have a scanned document or a fax as multi-page TIFF + file, you can append or insert this document using the <code>fox:external-document</code> + element. Each page of the external document will create one full page in the target + format. + </p> + <p> + The <code>fox:external-document</code> element is structurally a peer to + <code>fo:page-sequence</code>, so wherever you can put an <code>fo:page-sequence</code> + you could also place a <code>fox:external-document</code>. + Therefore, the specified contents for <code>fo:root</code> change to: + </p> + <p> + <code> + (layout-master-set, declarations?, bookmark-tree?, (page-sequence|page-sequence-wrapper|fox:external-document)+) + </code> + </p> + <section> + <title>Specification</title> + <p> + The <code>fox:external-document</code> extension formatting object is used to specify + how to create a (sub-)sequence of pages within a document. The content of these pages + comes from the individual subimages/pages of an image or paged document (for example: + multi-page TIFF in the form of faxes or scanned documents, or PDF files). The + formatting object creates the necessary areas to display one image per page. + </p> + <p> + In terms of page numbers, the behaviour is the same as for + <code>fo:page-sequence</code>. The placement of the image inside the page is similar + to that of <code>fo:external-graphic</code> or <code>fo:instream-foreign-object</code>, + i.e. the viewport (and therefore the page size) is defined by either the intrinsic + size of the image or by the size properties that apply to this formatting object. + </p> + <p>Content: EMPTY</p> + <p>The following properties apply to this formatting object:</p> + <ul> + <li>(Common Accessibility Properties) (not implemented, yet)</li> + <li>(Common Aural Properties) (not implemented, yet)</li> + <li>block-progression-dimension</li> + <li>content-height</li> + <li>content-type</li> + <li>content-width</li> + <li>display-align</li> + <li>height</li> + <li>id</li> + <li>inline-progression-dimension</li> + <li>overflow</li> + <li>pages: <page-set> (see below) (not implemented, yet)</li> + <li>reference-orientation</li> + <li>scaling</li> + <li>scaling-method</li> + <li>src</li> + <li>text-align</li> + <li>width</li> + </ul> + <p> + Datatype "page-set": Value: auto | <integer-range>, + Default: "auto" which means all pages/subimages of the document. + <integer-range> allows values such as "7" or "1-3" + </p> + <note> + <code>fox:external-document</code> is not suitable for concatenating FO documents. + For this, XInclude is recommended. + </note> + </section> + </section> </section> </body> </document> diff --git a/src/java/org/apache/fop/apps/FormattingResults.java b/src/java/org/apache/fop/apps/FormattingResults.java index 80449bd4b..86ac72a97 100644 --- a/src/java/org/apache/fop/apps/FormattingResults.java +++ b/src/java/org/apache/fop/apps/FormattingResults.java @@ -21,7 +21,7 @@ package org.apache.fop.apps; import java.util.List;
-import org.apache.fop.fo.pagination.PageSequence;
+import org.apache.fop.fo.pagination.AbstractPageSequence;
/**
* Class for reporting back formatting results to the calling application.
@@ -69,10 +69,10 @@ public class FormattingResults { * Reports the result of one page sequence rendering
* back into this object.
*
- * @param pageSequence the PageSequence which just completed rendering
+ * @param pageSequence the page sequence which just completed rendering
* @param pageCount the number of pages rendered for that PageSequence
*/
- public void haveFormattedPageSequence(PageSequence pageSequence, int pageCount) {
+ public void haveFormattedPageSequence(AbstractPageSequence pageSequence, int pageCount) {
this.pageCount += pageCount;
if (this.pageSequences == null) {
this.pageSequences = new java.util.ArrayList();
diff --git a/src/java/org/apache/fop/area/AreaTreeHandler.java b/src/java/org/apache/fop/area/AreaTreeHandler.java index 6eb2d738b..a107da833 100644 --- a/src/java/org/apache/fop/area/AreaTreeHandler.java +++ b/src/java/org/apache/fop/area/AreaTreeHandler.java @@ -35,13 +35,17 @@ import org.apache.fop.apps.FormattingResults; import org.apache.fop.datatypes.Numeric; import org.apache.fop.fo.FOEventHandler; import org.apache.fop.fo.extensions.ExtensionAttachment; +import org.apache.fop.fo.extensions.ExternalDocument; import org.apache.fop.fo.extensions.destination.Destination; +import org.apache.fop.fo.pagination.AbstractPageSequence; import org.apache.fop.fo.pagination.PageSequence; import org.apache.fop.fo.pagination.Root; import org.apache.fop.fo.pagination.bookmarks.BookmarkTree; +import org.apache.fop.layoutmgr.ExternalDocumentLayoutManager; import org.apache.fop.layoutmgr.LayoutManagerMaker; import org.apache.fop.layoutmgr.LayoutManagerMapping; import org.apache.fop.layoutmgr.PageSequenceLayoutManager; +import org.apache.fop.layoutmgr.TopLevelLayoutManager; /** * Area tree handler for formatting objects. @@ -78,7 +82,7 @@ public class AreaTreeHandler extends FOEventHandler { // The formatting results to be handed back to the caller. private FormattingResults results = new FormattingResults(); - private PageSequenceLayoutManager prevPageSeqLM; + private TopLevelLayoutManager prevPageSeqLM; private int idGen = 0; @@ -235,13 +239,39 @@ public class AreaTreeHandler extends FOEventHandler { } /** + * @see org.apache.fop.fo.FOEventHandler#startExternalDocument(org.apache.fop.fo.extensions.ExternalDocument) + */ + public void startExternalDocument(ExternalDocument document) { + rootFObj = document.getRoot(); + finishPrevPageSequence(document.getInitialPageNumber()); + document.initPageNumber(); + } + + /** + * @see org.apache.fop.fo.FOEventHandler#endExternalDocument(org.apache.fop.fo.extensions.ExternalDocument) + */ + public void endExternalDocument(ExternalDocument document) { + if (statistics != null) { + statistics.end(); + } + + ExternalDocumentLayoutManager edLM; + edLM = getLayoutManagerMaker().makeExternalDocumentLayoutManager(this, document); + edLM.activateLayout(); + // preserve the current PageSequenceLayoutManger for the + // force-page-count check at the beginning of the next PageSequence + prevPageSeqLM = edLM; + + } + + /** * Called by the PageSequenceLayoutManager when it is finished with a * page-sequence. * * @param pageSequence the page-sequence just finished * @param pageCount The number of pages generated for the page-sequence */ - public void notifyPageSequenceFinished(PageSequence pageSequence, + public void notifyPageSequenceFinished(AbstractPageSequence pageSequence, int pageCount) { this.results.haveFormattedPageSequence(pageSequence, pageCount); if (log.isDebugEnabled()) { diff --git a/src/java/org/apache/fop/area/RenderPagesModel.java b/src/java/org/apache/fop/area/RenderPagesModel.java index a5257977a..1b3a22706 100644 --- a/src/java/org/apache/fop/area/RenderPagesModel.java +++ b/src/java/org/apache/fop/area/RenderPagesModel.java @@ -173,7 +173,7 @@ public class RenderPagesModel extends AreaTreeModel { } } catch (Exception e) { // use error handler to handle this FOP or IO Exception - log.error(e); + log.error("Error while rendering page " + pageViewport.getPageIndex(), e); if (e instanceof RuntimeException) { throw (RuntimeException)e; } diff --git a/src/java/org/apache/fop/fo/FOEventHandler.java b/src/java/org/apache/fop/fo/FOEventHandler.java index 4af703c12..d226cfb6c 100644 --- a/src/java/org/apache/fop/fo/FOEventHandler.java +++ b/src/java/org/apache/fop/fo/FOEventHandler.java @@ -25,6 +25,7 @@ import java.util.Set; import org.xml.sax.SAXException; import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.fo.extensions.ExternalDocument; import org.apache.fop.fo.flow.BasicLink; import org.apache.fop.fo.flow.Block; import org.apache.fop.fo.flow.BlockContainer; @@ -557,5 +558,19 @@ public abstract class FOEventHandler { public void characters(char data[], int start, int length) { } + /** + * Process the start of the external-document extension. + * @param document the external-document node + */ + public void startExternalDocument(ExternalDocument document) { + } + + /** + * Process the end of the external-document extension. + * @param document the external-document node + */ + public void endExternalDocument(ExternalDocument document) { + } + } diff --git a/src/java/org/apache/fop/fo/GraphicsProperties.java b/src/java/org/apache/fop/fo/GraphicsProperties.java new file mode 100644 index 000000000..5cee68679 --- /dev/null +++ b/src/java/org/apache/fop/fo/GraphicsProperties.java @@ -0,0 +1,81 @@ +/* + * 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.datatypes.Length; +import org.apache.fop.fo.properties.LengthRangeProperty; + +/** + * This interface provides access to properties necessary to calculate the size and positioning + * of images and graphics inside a viewport. + */ +public interface GraphicsProperties { + + /** + * @return the "inline-progression-dimension" property. + */ + LengthRangeProperty getInlineProgressionDimension(); + + /** + * @return the "block-progression-dimension" property. + */ + LengthRangeProperty getBlockProgressionDimension(); + + /** + * @return the "height" property. + */ + Length getHeight(); + + /** + * @return the "width" property. + */ + Length getWidth(); + + /** + * @return the "content-height" property. + */ + Length getContentHeight(); + + /** + * @return the "content-width" property. + */ + Length getContentWidth(); + + /** + * @return the "scaling" property. + */ + int getScaling(); + + /** + * @return the "overflow" property. + */ + int getOverflow(); + + /** + * @return the "display-align" property. + */ + int getDisplayAlign(); + + /** + * @return the "text-align" property. + */ + int getTextAlign(); + +} diff --git a/src/java/org/apache/fop/fo/PropertyList.java b/src/java/org/apache/fop/fo/PropertyList.java index 6d4dbd43c..1de74e2f0 100644 --- a/src/java/org/apache/fop/fo/PropertyList.java +++ b/src/java/org/apache/fop/fo/PropertyList.java @@ -20,6 +20,8 @@ package org.apache.fop.fo; // Java +import java.text.MessageFormat; + import org.xml.sax.Attributes; import org.apache.commons.logging.Log; @@ -355,9 +357,9 @@ public abstract class PropertyList { if (propId == -1 || (subpropId == -1 && findSubPropertyName(propertyName) != null)) { - StringBuffer errorMessage = new StringBuffer().append( - "Invalid property name \'").append(propertyName); - handleInvalidProperty(errorMessage.toString(), propertyName); + String errorMessage = MessageFormat.format( + "Invalid property name ''{0}''.", new Object[] {propertyName}); + handleInvalidProperty(errorMessage, propertyName); return false; } return true; diff --git a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java index 989726d22..de1d019f4 100644 --- a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java +++ b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java @@ -62,6 +62,7 @@ public class ExtensionElementMapping extends ElementMapping { foObjs.put("outline", new UnknownXMLObj.Maker(URI)); foObjs.put("label", new UnknownXMLObj.Maker(URI)); foObjs.put("destination", new DestinationMaker()); + foObjs.put("external-document", new ExternalDocumentMaker()); } } @@ -71,6 +72,12 @@ public class ExtensionElementMapping extends ElementMapping { } } + static class ExternalDocumentMaker extends ElementMapping.Maker { + public FONode make(FONode parent) { + return new ExternalDocument(parent); + } + } + /** {@inheritDoc} */ public String getStandardPrefix() { return "fox"; diff --git a/src/java/org/apache/fop/fo/extensions/ExternalDocument.java b/src/java/org/apache/fop/fo/extensions/ExternalDocument.java new file mode 100644 index 000000000..b8707726f --- /dev/null +++ b/src/java/org/apache/fop/fo/extensions/ExternalDocument.java @@ -0,0 +1,181 @@ +/* + * 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.extensions; + +import org.xml.sax.Locator; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.datatypes.Length; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.GraphicsProperties; +import org.apache.fop.fo.PropertyList; +import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.pagination.AbstractPageSequence; +import org.apache.fop.fo.properties.LengthRangeProperty; + +/** + * Class for the fox:external-document extenstion element. + */ +public class ExternalDocument extends AbstractPageSequence implements GraphicsProperties { + + // The value of properties relevant for fox:external-document + private LengthRangeProperty blockProgressionDimension; + private Length contentHeight; + private Length contentWidth; + private int displayAlign; + private Length height; + private LengthRangeProperty inlineProgressionDimension; + private int overflow; + private int scaling; + private String src; + private int textAlign; + private Length width; + // Unused but valid items, commented out for performance: + // private CommonAccessibility commonAccessibility; + // private CommonAural commonAural; + // private String contentType; + // private int scalingMethod; + // End of property values + + /** + * Constructs a ExternalDocument object (called by Maker). + * @param parent the parent formatting object + */ + public ExternalDocument(FONode parent) { + super(parent); + } + + /** {@inheritDoc} */ + public void bind(PropertyList pList) throws FOPException { + super.bind(pList); + blockProgressionDimension = pList.get(PR_BLOCK_PROGRESSION_DIMENSION).getLengthRange(); + contentHeight = pList.get(PR_CONTENT_HEIGHT).getLength(); + contentWidth = pList.get(PR_CONTENT_WIDTH).getLength(); + displayAlign = pList.get(PR_DISPLAY_ALIGN).getEnum(); + height = pList.get(PR_HEIGHT).getLength(); + inlineProgressionDimension = pList.get(PR_INLINE_PROGRESSION_DIMENSION).getLengthRange(); + overflow = pList.get(PR_OVERFLOW).getEnum(); + scaling = pList.get(PR_SCALING).getEnum(); + textAlign = pList.get(PR_TEXT_ALIGN).getEnum(); + width = pList.get(PR_WIDTH).getLength(); + src = pList.get(PR_SRC).getString(); + + if (this.src == null || this.src.length() == 0) { + missingPropertyError("src"); + } + } + + protected void startOfNode() throws FOPException { + super.startOfNode(); + getFOEventHandler().startExternalDocument(this); + } + + /** + * @see org.apache.fop.fo.FONode#endOfNode + */ + protected void endOfNode() throws FOPException { + getFOEventHandler().endExternalDocument(this); + super.endOfNode(); + } + + /** + * @see org.apache.fop.fo.FONode#validateChildNode(Locator, String, String) + XSL/FOP: empty + */ + protected void validateChildNode(Locator loc, String nsURI, String localName) + throws ValidationException { + invalidChildError(loc, nsURI, localName); + } + + /** + * Returns the src attribute (the URI to the embedded document). + * @return the src attribute + */ + public String getSrc() { + return this.src; + } + + /** {@inheritDoc} */ + public LengthRangeProperty getInlineProgressionDimension() { + return inlineProgressionDimension; + } + + /** {@inheritDoc} */ + public LengthRangeProperty getBlockProgressionDimension() { + return blockProgressionDimension; + } + + /** {@inheritDoc} */ + public Length getHeight() { + return height; + } + + /** {@inheritDoc} */ + public Length getWidth() { + return width; + } + + /** {@inheritDoc} */ + public Length getContentHeight() { + return contentHeight; + } + + /** {@inheritDoc} */ + public Length getContentWidth() { + return contentWidth; + } + + /** {@inheritDoc} */ + public int getScaling() { + return scaling; + } + + /** {@inheritDoc} */ + public int getOverflow() { + return overflow; + } + + /** {@inheritDoc} */ + public int getDisplayAlign() { + return displayAlign; + } + + /** {@inheritDoc} */ + public int getTextAlign() { + return textAlign; + } + + /** @see org.apache.fop.fo.FONode#getNamespaceURI() */ + public String getNamespaceURI() { + return ExtensionElementMapping.URI; + } + + /** @see org.apache.fop.fo.FONode#getNormalNamespacePrefix() */ + public String getNormalNamespacePrefix() { + return "fox"; + } + + /** @see org.apache.fop.fo.FONode#getLocalName() */ + public String getLocalName() { + return "external-document"; + } + +} + diff --git a/src/java/org/apache/fop/fo/flow/AbstractGraphics.java b/src/java/org/apache/fop/fo/flow/AbstractGraphics.java index 74b91eb53..a58cc08f2 100644 --- a/src/java/org/apache/fop/fo/flow/AbstractGraphics.java +++ b/src/java/org/apache/fop/fo/flow/AbstractGraphics.java @@ -23,6 +23,7 @@ import org.apache.fop.apps.FOPException; import org.apache.fop.datatypes.Length; import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; +import org.apache.fop.fo.GraphicsProperties; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.LengthRangeProperty; @@ -32,7 +33,7 @@ import org.apache.fop.fo.properties.SpaceProperty; * Common base class for instream-foreign-object and external-graphics * flow formatting objects. */ -public abstract class AbstractGraphics extends FObj { +public abstract class AbstractGraphics extends FObj implements GraphicsProperties { // The value of properties relevant for fo:instream-foreign-object // and external-graphics. @@ -101,56 +102,6 @@ public abstract class AbstractGraphics extends FObj { } /** - * Given the ipd and the content width calculates the - * required x offset based on the text-align property - * @param ipd the inline-progression-dimension of the object - * @param cwidth the calculated content width of the object - * @return the X offset - */ - public int computeXOffset (int ipd, int cwidth) { - int xoffset = 0; - switch (textAlign) { - case EN_CENTER: - xoffset = (ipd - cwidth) / 2; - break; - case EN_END: - xoffset = ipd - cwidth; - break; - case EN_START: - break; - case EN_JUSTIFY: - default: - break; - } - return xoffset; - } - - /** - * Given the bpd and the content height calculates the - * required y offset based on the display-align property - * @param bpd the block-progression-dimension of the object - * @param cheight the calculated content height of the object - * @return the Y offset - */ - public int computeYOffset(int bpd, int cheight) { - int yoffset = 0; - switch (displayAlign) { - case EN_BEFORE: - break; - case EN_AFTER: - yoffset = bpd - cheight; - break; - case EN_CENTER: - yoffset = (bpd - cheight) / 2; - break; - case EN_AUTO: - default: - break; - } - return yoffset; - } - - /** * @return the "id" property. */ public String getId() { @@ -171,16 +122,12 @@ public abstract class AbstractGraphics extends FObj { return lineHeight; } - /** - * @return the "inline-progression-dimension" property. - */ + /** {@inheritDoc} */ public LengthRangeProperty getInlineProgressionDimension() { return inlineProgressionDimension; } - /** - * @return the "block-progression-dimension" property. - */ + /** {@inheritDoc} */ public LengthRangeProperty getBlockProgressionDimension() { return blockProgressionDimension; } @@ -199,34 +146,36 @@ public abstract class AbstractGraphics extends FObj { return width; } - /** - * @return the "content-height" property. - */ + /** {@inheritDoc} */ public Length getContentHeight() { return contentHeight; } - /** - * @return the "content-width" property. - */ + /** {@inheritDoc} */ public Length getContentWidth() { return contentWidth; } - /** - * @return the "scaling" property. - */ + /** {@inheritDoc} */ public int getScaling() { return scaling; } - /** - * @return the "overflow" property. - */ + /** {@inheritDoc} */ public int getOverflow() { return overflow; } + /** {@inheritDoc} */ + public int getDisplayAlign() { + return displayAlign; + } + + /** {@inheritDoc} */ + public int getTextAlign() { + return textAlign; + } + /** * @return the "alignment-adjust" property */ diff --git a/src/java/org/apache/fop/fo/pagination/AbstractPageSequence.java b/src/java/org/apache/fop/fo/pagination/AbstractPageSequence.java new file mode 100644 index 000000000..70a09b720 --- /dev/null +++ b/src/java/org/apache/fop/fo/pagination/AbstractPageSequence.java @@ -0,0 +1,154 @@ +/* + * 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.pagination; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.datatypes.Numeric; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.FObj; +import org.apache.fop.fo.PropertyList; + +/** + * Abstract base class for the fo:page-sequence formatting object and the fox:external-document + * extension object. + */ +public abstract class AbstractPageSequence extends FObj { + + // The value of properties relevant for fo:page-sequence. + protected Numeric initialPageNumber; + protected int forcePageCount; + private String format; + private int letterValue; + private char groupingSeparator; + private int groupingSize; + private Numeric referenceOrientation; //XSL 1.1 + // End of property values + + private PageNumberGenerator pageNumberGenerator; + + protected int startingPageNumber = 0; + + /** + * Create a page sequence FO node. + * + * @param parent the parent FO node + */ + public AbstractPageSequence(FONode parent) { + super(parent); + } + + /** + * @see org.apache.fop.fo.FObj#bind(PropertyList) + */ + public void bind(PropertyList pList) throws FOPException { + super.bind(pList); + initialPageNumber = pList.get(PR_INITIAL_PAGE_NUMBER).getNumeric(); + forcePageCount = pList.get(PR_FORCE_PAGE_COUNT).getEnum(); + format = pList.get(PR_FORMAT).getString(); + letterValue = pList.get(PR_LETTER_VALUE).getEnum(); + groupingSeparator = pList.get(PR_GROUPING_SEPARATOR).getCharacter(); + groupingSize = pList.get(PR_GROUPING_SIZE).getNumber().intValue(); + referenceOrientation = pList.get(PR_REFERENCE_ORIENTATION).getNumeric(); + } + + /** + * @see org.apache.fop.fo.FONode#startOfNode() + */ + protected void startOfNode() throws FOPException { + this.pageNumberGenerator = new PageNumberGenerator( + format, groupingSeparator, groupingSize, letterValue); + + } + + /** @see org.apache.fop.fo.FONode#endOfNode() */ + protected void endOfNode() throws FOPException { + } + + /** + * Initialize the current page number for the start of the page sequence. + */ + public void initPageNumber() { + int pageNumberType = 0; + + if (initialPageNumber.getEnum() != 0) { + // auto | auto-odd | auto-even. + startingPageNumber = getRoot().getEndingPageNumberOfPreviousSequence() + 1; + pageNumberType = initialPageNumber.getEnum(); + if (pageNumberType == EN_AUTO_ODD) { + if (startingPageNumber % 2 == 0) { + startingPageNumber++; + } + } else if (pageNumberType == EN_AUTO_EVEN) { + if (startingPageNumber % 2 == 1) { + startingPageNumber++; + } + } + } else { // <integer> for explicit page number + int pageStart = initialPageNumber.getValue(); + startingPageNumber = (pageStart > 0) ? pageStart : 1; // spec rule + } + } + + /** + * Get the starting page number for this page sequence. + * + * @return the starting page number + */ + public int getStartingPageNumber() { + return startingPageNumber; + } + + /** + * Retrieves the string representation of a page number applicable + * for this page sequence + * @param pageNumber the page number + * @return string representation of the page number + */ + public String makeFormattedPageNumber(int pageNumber) { + return pageNumberGenerator.makeFormattedPageNumber(pageNumber); + } + + /** + * Public accessor for the ancestor Root. + * @return the ancestor Root + */ + public Root getRoot() { + return (Root)this.getParent(); + } + + /** @return the force-page-count value */ + public int getForcePageCount() { + return forcePageCount; + } + + /** @return the initial-page-number property value */ + public Numeric getInitialPageNumber() { + return initialPageNumber; + } + + /** + * @return the "reference-orientation" property. + * @since XSL 1.1 + */ + public int getReferenceOrientation() { + return referenceOrientation.getValue(); + } + +} diff --git a/src/java/org/apache/fop/fo/pagination/ConditionalPageMasterReference.java b/src/java/org/apache/fop/fo/pagination/ConditionalPageMasterReference.java index c3d705de2..dfb4ba70b 100644 --- a/src/java/org/apache/fop/fo/pagination/ConditionalPageMasterReference.java +++ b/src/java/org/apache/fop/fo/pagination/ConditionalPageMasterReference.java @@ -93,14 +93,20 @@ public class ConditionalPageMasterReference extends FObj { * @param isFirstPage True if page is first page * @param isLastPage True if page is last page * @param isBlankPage True if page is blank + * @param isOnlyPage True if page is the only page * @return True if the conditions for this reference are met */ protected boolean isValid(boolean isOddPage, boolean isFirstPage, boolean isLastPage, + boolean isOnlyPage, boolean isBlankPage) { // page-position - if (isFirstPage) { + if (isOnlyPage) { + if (pagePosition != EN_ONLY) { + return false; + } + } else if (isFirstPage) { if (pagePosition == EN_REST) { return false; } else if (pagePosition == EN_LAST) { diff --git a/src/java/org/apache/fop/fo/pagination/PageSequence.java b/src/java/org/apache/fop/fo/pagination/PageSequence.java index f8f9f9277..91649fbc5 100644 --- a/src/java/org/apache/fop/fo/pagination/PageSequence.java +++ b/src/java/org/apache/fop/fo/pagination/PageSequence.java @@ -25,32 +25,22 @@ import java.util.Map; import org.xml.sax.Locator; import org.apache.fop.apps.FOPException; -import org.apache.fop.datatypes.Numeric; import org.apache.fop.fo.FONode; -import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; /** - * Implementation of the fo:page-sequence formatting object. + * Abstract base implementation for page sequences. */ -public class PageSequence extends FObj { +public class PageSequence extends AbstractPageSequence { // The value of properties relevant for fo:page-sequence. private String country; - private String format; private String language; - private int letterValue; - private char groupingSeparator; - private int groupingSize; - private Numeric initialPageNumber; - private int forcePageCount; private String masterReference; + //private int writingMode; //XSL 1.1 // End of property values - /** The parent root object */ - private Root root; - // There doesn't seem to be anything in the spec requiring flows // to be in the order given, only that they map to the regions // defined in the page sequence, so all we need is this one hashmap @@ -59,9 +49,6 @@ public class PageSequence extends FObj { /** Map of flows to their flow name (flow-name, Flow) */ private Map flowMap; - private int startingPageNumber = 0; - private PageNumberGenerator pageNumberGenerator; - /** * The currentSimplePageMaster is either the page master for the * whole page sequence if master-reference refers to a simple-page-master, @@ -97,14 +84,9 @@ public class PageSequence extends FObj { public void bind(PropertyList pList) throws FOPException { super.bind(pList); country = pList.get(PR_COUNTRY).getString(); - format = pList.get(PR_FORMAT).getString(); language = pList.get(PR_LANGUAGE).getString(); - letterValue = pList.get(PR_LETTER_VALUE).getEnum(); - groupingSeparator = pList.get(PR_GROUPING_SEPARATOR).getCharacter(); - groupingSize = pList.get(PR_GROUPING_SIZE).getNumber().intValue(); - initialPageNumber = pList.get(PR_INITIAL_PAGE_NUMBER).getNumeric(); - forcePageCount = pList.get(PR_FORCE_PAGE_COUNT).getEnum(); masterReference = pList.get(PR_MASTER_REFERENCE).getString(); + //writingMode = pList.getWritingMode(); if (masterReference == null || masterReference.equals("")) { missingPropertyError("master-reference"); @@ -116,13 +98,12 @@ public class PageSequence extends FObj { */ protected void startOfNode() throws FOPException { super.startOfNode(); - this.root = (Root) parent; flowMap = new java.util.HashMap(); - this.simplePageMaster = root.getLayoutMasterSet().getSimplePageMaster(masterReference); + this.simplePageMaster = getRoot().getLayoutMasterSet().getSimplePageMaster(masterReference); if (this.simplePageMaster == null) { this.pageSequenceMaster - = root.getLayoutMasterSet().getPageSequenceMaster(masterReference); + = getRoot().getLayoutMasterSet().getPageSequenceMaster(masterReference); if (this.pageSequenceMaster == null) { throw new ValidationException("master-reference '" + masterReference + "' for fo:page-sequence matches no" @@ -130,9 +111,6 @@ public class PageSequence extends FObj { } } - this.pageNumberGenerator = new PageNumberGenerator( - format, groupingSeparator, groupingSize, letterValue); - getFOEventHandler().startPageSequence(this); } @@ -211,7 +189,7 @@ public class PageSequence extends FObj { + "\" found within fo:page-sequence", flow.getLocator()); } - if (!root.getLayoutMasterSet().regionNameExists(flowName) + if (!getRoot().getLayoutMasterSet().regionNameExists(flowName) && !flowName.equals("xsl-before-float-separator") && !flowName.equals("xsl-footnote-separator")) { throw new ValidationException("flow-name \"" @@ -221,31 +199,6 @@ public class PageSequence extends FObj { } } - /** - * Initialize the current page number for the start of the page sequence. - */ - public void initPageNumber() { - int pageNumberType = 0; - - if (initialPageNumber.getEnum() != 0) { - // auto | auto-odd | auto-even. - startingPageNumber = root.getEndingPageNumberOfPreviousSequence() + 1; - pageNumberType = initialPageNumber.getEnum(); - if (pageNumberType == EN_AUTO_ODD) { - if (startingPageNumber % 2 == 0) { - startingPageNumber++; - } - } else if (pageNumberType == EN_AUTO_EVEN) { - if (startingPageNumber % 2 == 1) { - startingPageNumber++; - } - } - } else { // <integer> for explicit page number - int pageStart = initialPageNumber.getValue(); - startingPageNumber = (pageStart > 0) ? pageStart : 1; // spec rule - } - } - // /** // * Returns true when there is more flow elements left to lay out. // */ @@ -306,15 +259,6 @@ public class PageSequence extends FObj { // return false; // } - /** - * Get the starting page number for this page sequence. - * - * @return the starting page number - */ - public int getStartingPageNumber() { - return startingPageNumber; - } - // private void forcePage(AreaTree areaTree, int firstAvailPageNumber) { // boolean makePage = false; // if (this.forcePageCount == ForcePageCount.AUTO) { @@ -433,6 +377,8 @@ public class PageSequence extends FObj { * page sequence * @param isLastPage indicator whether this page is the last page of the * page sequence + * @param isOnlyPage indicator whether this page is the only page of the + * page sequence * @param isBlank indicator whether the page will be blank * @return the SimplePageMaster to use for this page * @throws FOPException if there's a problem determining the page master @@ -440,6 +386,7 @@ public class PageSequence extends FObj { public SimplePageMaster getNextSimplePageMaster(int page, boolean isFirstPage, boolean isLastPage, + boolean isOnlyPage, boolean isBlank) throws FOPException { if (pageSequenceMaster == null) { @@ -450,11 +397,12 @@ public class PageSequence extends FObj { log.debug("getNextSimplePageMaster(page=" + page + " isOdd=" + isOddPage + " isFirst=" + isFirstPage - + " isLast=" + isLastPage + + " isLast=" + isLastPage + + " isOnly=" + isOnlyPage + " isBlank=" + isBlank + ")"); } return pageSequenceMaster.getNextSimplePageMaster(isOddPage, - isFirstPage, isLastPage, isBlank); + isFirstPage, isLastPage, isOnlyPage, isBlank); } /** @@ -478,24 +426,15 @@ public class PageSequence extends FObj { } } - /** - * Retrieves the string representation of a page number applicable - * for this page sequence - * @param pageNumber the page number - * @return string representation of the page number - */ - public String makeFormattedPageNumber(int pageNumber) { - return pageNumberGenerator.makeFormattedPageNumber(pageNumber); - } - - /** - * Public accessor for the ancestor Root. - * @return the ancestor Root - */ - public Root getRoot() { - return root; + /** @return true if the page-sequence has a page-master with page-position="only" */ + public boolean hasPagePositionOnly() { + if (pageSequenceMaster == null) { + return false; + } else { + return pageSequenceMaster.hasPagePositionOnly(); + } } - + /** @return the "master-reference" property. */ public String getMasterReference() { return masterReference; @@ -511,16 +450,6 @@ public class PageSequence extends FObj { return FO_PAGE_SEQUENCE; } - /** @return the force-page-count value */ - public int getForcePageCount() { - return forcePageCount; - } - - /** @return the initial-page-number property value */ - public Numeric getInitialPageNumber() { - return initialPageNumber; - } - /** @return the country property value */ public String getCountry() { return this.country; diff --git a/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java b/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java index fd3484ee6..34ad299bd 100644 --- a/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java +++ b/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java @@ -171,11 +171,21 @@ public class PageSequenceMaster extends FObj { } } + /** @return true if the page-sequence-master has a page-master with page-position="only" */ + public boolean hasPagePositionOnly() { + if (currentSubSequence != null) { + return currentSubSequence.hasPagePositionOnly(); + } else { + return false; + } + } + /** * Returns the next simple-page-master. * @param isOddPage True if the next page number is odd * @param isFirstPage True if the next page is the first * @param isLastPage True if the next page is the last + * @param isOnlyPage True if the next page is the only page * @param isBlankPage True if the next page is blank * @return the requested page master * @throws FOPException if there's a problem determining the next page master @@ -183,6 +193,7 @@ public class PageSequenceMaster extends FObj { public SimplePageMaster getNextSimplePageMaster(boolean isOddPage, boolean isFirstPage, boolean isLastPage, + boolean isOnlyPage, boolean isBlankPage) throws FOPException { if (currentSubSequence == null) { @@ -193,7 +204,7 @@ public class PageSequenceMaster extends FObj { } } String pageMasterName = currentSubSequence - .getNextPageMasterName(isOddPage, isFirstPage, isLastPage, isBlankPage); + .getNextPageMasterName(isOddPage, isFirstPage, isLastPage, isOnlyPage, isBlankPage); boolean canRecover = true; while (pageMasterName == null) { SubSequenceSpecifier nextSubSequence = getNextSubSequence(); @@ -212,7 +223,7 @@ public class PageSequenceMaster extends FObj { currentSubSequence = nextSubSequence; } pageMasterName = currentSubSequence - .getNextPageMasterName(isOddPage, isFirstPage, isLastPage, isBlankPage); + .getNextPageMasterName(isOddPage, isFirstPage, isLastPage, isOnlyPage, isBlankPage); } SimplePageMaster pageMaster = this.layoutMasterSet .getSimplePageMaster(pageMasterName); diff --git a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java index 012abd11a..9d2fe652c 100644 --- a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java +++ b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java @@ -49,6 +49,7 @@ public class RepeatablePageMasterAlternatives extends FObj private List conditionalPageMasterRefs; private boolean hasPagePositionLast = false; + private boolean hasPagePositionOnly = false; /** * @see org.apache.fop.fo.FONode#FONode(FONode) @@ -124,6 +125,7 @@ public class RepeatablePageMasterAlternatives extends FObj public String getNextPageMasterName(boolean isOddPage, boolean isFirstPage, boolean isLastPage, + boolean isOnlyPage, boolean isBlankPage) { if (getMaximumRepeats() != INFINITE) { if (numberConsumed < getMaximumRepeats()) { @@ -138,7 +140,7 @@ public class RepeatablePageMasterAlternatives extends FObj for (int i = 0; i < conditionalPageMasterRefs.size(); i++) { ConditionalPageMasterReference cpmr = (ConditionalPageMasterReference)conditionalPageMasterRefs.get(i); - if (cpmr.isValid(isOddPage, isFirstPage, isLastPage, isBlankPage)) { + if (cpmr.isValid(isOddPage, isFirstPage, isLastPage, isOnlyPage, isBlankPage)) { return cpmr.getMasterReference(); } } @@ -155,6 +157,9 @@ public class RepeatablePageMasterAlternatives extends FObj if (cpmr.getPagePosition() == EN_LAST) { this.hasPagePositionLast = true; } + if (cpmr.getPagePosition() == EN_ONLY) { + this.hasPagePositionOnly = true; + } } /** {@inheritDoc} */ @@ -178,6 +183,12 @@ public class RepeatablePageMasterAlternatives extends FObj } /** {@inheritDoc} */ + /** @see org.apache.fop.fo.pagination.SubSequenceSpecifier#hasPagePositionOnly() */ + public boolean hasPagePositionOnly() { + return this.hasPagePositionOnly; + } + + /** @see org.apache.fop.fo.FONode#getLocalName() */ public String getLocalName() { return "repeatable-page-master-alternatives"; } diff --git a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java index dfd6ff6a9..172324232 100644 --- a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java +++ b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java @@ -93,6 +93,7 @@ public class RepeatablePageMasterReference extends FObj public String getNextPageMasterName(boolean isOddPage, boolean isFirstPage, boolean isLastPage, + boolean isOnlyPage, boolean isEmptyPage) { if (getMaximumRepeats() != INFINITE) { if (numberConsumed < getMaximumRepeats()) { @@ -141,6 +142,11 @@ public class RepeatablePageMasterReference extends FObj } /** {@inheritDoc} */ + public boolean hasPagePositionOnly() { + return false; + } + + /** {@inheritDoc} */ public String getLocalName() { return "repeatable-page-master-reference"; } @@ -150,4 +156,5 @@ public class RepeatablePageMasterReference extends FObj return FO_REPEATABLE_PAGE_MASTER_REFERENCE; } + } diff --git a/src/java/org/apache/fop/fo/pagination/Root.java b/src/java/org/apache/fop/fo/pagination/Root.java index 2e2c38b82..139f7606a 100644 --- a/src/java/org/apache/fop/fo/pagination/Root.java +++ b/src/java/org/apache/fop/fo/pagination/Root.java @@ -25,6 +25,7 @@ import java.util.List; import org.xml.sax.Locator; import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.ElementMapping; import org.apache.fop.fo.FOEventHandler; import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; @@ -85,7 +86,7 @@ public class Root extends FObj { protected void endOfNode() throws FOPException { if (!pageSequenceFound || layoutMasterSet == null) { missingChildElementError("(layout-master-set, declarations?, " + - "bookmark-tree?, page-sequence+)"); + "bookmark-tree?, (page-sequence+|fox:external-document))"); } } @@ -129,7 +130,21 @@ public class Root extends FObj { invalidChildError(loc, nsURI, localName); } } else { - invalidChildError(loc, nsURI, localName); + if (FOX_URI.equals(nsURI)) { + if ("external-document".equals(localName)) { + pageSequenceFound = true; + } + } + //invalidChildError(loc, nsURI, localName); + //Ignore non-FO elements under root + } + } + + + /** @inheritDoc */ + protected void validateChildNode(Locator loc, FONode child) throws ValidationException { + if (child instanceof AbstractPageSequence) { + pageSequenceFound = true; } } diff --git a/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java b/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java index 68be2b82e..43d8e40dc 100644 --- a/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java +++ b/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java @@ -85,6 +85,7 @@ public class SinglePageMasterReference extends FObj public String getNextPageMasterName(boolean isOddPage, boolean isFirstPage, boolean isLastPage, + boolean isOnlyPage, boolean isEmptyPage) { if (this.state == FIRST) { this.state = DONE; @@ -117,6 +118,11 @@ public class SinglePageMasterReference extends FObj } /** {@inheritDoc} */ + public boolean hasPagePositionOnly() { + return false; + } + + /** {@inheritDoc} */ public String getLocalName() { return "single-page-master-reference"; } @@ -125,5 +131,6 @@ public class SinglePageMasterReference extends FObj public int getNameId() { return FO_SINGLE_PAGE_MASTER_REFERENCE; } + } diff --git a/src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java b/src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java index af045624e..5da4945f1 100644 --- a/src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java +++ b/src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java @@ -32,6 +32,7 @@ public interface SubSequenceSpecifier { * @param isOddPage True if the next page number is odd * @param isFirstPage True if the next page is the first * @param isLastPage True if the next page is the last + * @param isOnlyPage True if the next page is the only page * @param isBlankPage True if the next page is blank * @return the page master name * @throws FOPException if there's a problem determining the next page master @@ -39,6 +40,7 @@ public interface SubSequenceSpecifier { String getNextPageMasterName(boolean isOddPage, boolean isFirstPage, boolean isLastPage, + boolean isOnlyPage, boolean isBlankPage) throws FOPException; @@ -57,5 +59,8 @@ public interface SubSequenceSpecifier { /** @return true if the subsequence has a page master for page-position "last" */ boolean hasPagePositionLast(); + /** @return true if the subsequence has a page master for page-position "only" */ + boolean hasPagePositionOnly(); + } diff --git a/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java index f0365d0e4..f75fffc0d 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java @@ -224,9 +224,7 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager return newLMs; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public PageSequenceLayoutManager getPSLM() { return parentLM.getPSLM(); } diff --git a/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java new file mode 100644 index 000000000..2b61fb6c6 --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java @@ -0,0 +1,385 @@ +/* + * 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; + +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.fop.area.AreaTreeHandler; +import org.apache.fop.area.AreaTreeModel; +import org.apache.fop.area.IDTracker; +import org.apache.fop.area.PageViewport; +import org.apache.fop.area.Resolvable; +import org.apache.fop.datatypes.Numeric; +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.flow.Marker; +import org.apache.fop.fo.flow.RetrieveMarker; +import org.apache.fop.fo.pagination.AbstractPageSequence; + +/** + * Abstract base class for a page sequence layout manager. + */ +public abstract class AbstractPageSequenceLayoutManager extends AbstractLayoutManager + implements TopLevelLayoutManager { + + private static Log log = LogFactory.getLog(AbstractPageSequenceLayoutManager.class); + + /** + * AreaTreeHandler which activates the PSLM and controls + * the rendering of its pages. + */ + protected AreaTreeHandler areaTreeHandler; + + /** ID tracker supplied by the AreaTreeHandler */ + protected IDTracker idTracker; + + /** page sequence formatting object being processed by this class */ + protected AbstractPageSequence pageSeq; + + /** Current page with page-viewport-area being filled by the PSLM. */ + protected Page curPage; + + /** the current page number */ + protected int currentPageNum = 0; + /** The stating page number */ + protected int startPageNum = 0; + + /** + * Constructor + * + * @param ath the area tree handler object + * @param pseq fo:page-sequence to process + */ + public AbstractPageSequenceLayoutManager(AreaTreeHandler ath, AbstractPageSequence pseq) { + super(pseq); + this.areaTreeHandler = ath; + this.idTracker = ath.getIDTracker(); + this.pageSeq = pseq; + } + + /** + * @return the LayoutManagerMaker object associated to the areaTreeHandler + */ + public LayoutManagerMaker getLayoutManagerMaker() { + return areaTreeHandler.getLayoutManagerMaker(); + } + + /** + * Provides access to the current page. + * @return the current Page + */ + public Page getCurrentPage() { + return curPage; + } + + /** + * Provides access for setting the current page. + * @param currentPage the new current Page + */ + protected void setCurrentPage(Page currentPage) { + this.curPage = currentPage; + } + + /** + * Provides access to the current page number + * @return the current page number + */ + protected int getCurrentPageNum() { + return currentPageNum; + } + + /** {@inheritDoc} */ + public void initialize() { + startPageNum = pageSeq.getStartingPageNumber(); + currentPageNum = startPageNum - 1; + } + + /** + * This returns the first PageViewport that contains an id trait + * matching the idref argument, or null if no such PV exists. + * + * @param idref the idref trait needing to be resolved + * @return the first PageViewport that contains the ID trait + */ + public PageViewport getFirstPVWithID(String idref) { + List list = idTracker.getPageViewportsContainingID(idref); + if (list != null && list.size() > 0) { + return (PageViewport) list.get(0); + } + return null; + } + + /** + * This returns the last PageViewport that contains an id trait + * matching the idref argument, or null if no such PV exists. + * + * @param idref the idref trait needing to be resolved + * @return the last PageViewport that contains the ID trait + */ + public PageViewport getLastPVWithID(String idref) { + List list = idTracker.getPageViewportsContainingID(idref); + if (list != null && list.size() > 0) { + return (PageViewport) list.get(list.size() - 1); + } + return null; + } + + /** + * Add an ID reference to the current page. + * When adding areas the area adds its ID reference. + * For the page layout manager it adds the id reference + * with the current page to the area tree. + * + * @param id the ID reference to add + */ + public void addIDToPage(String id) { + if (id != null && id.length() > 0) { + idTracker.associateIDWithPageViewport(id, curPage.getPageViewport()); + } + } + + /** + * Add an id reference of the layout manager in the AreaTreeHandler, + * if the id hasn't been resolved yet + * @param id the id to track + * @return a boolean indicating if the id has already been resolved + * TODO Maybe give this a better name + */ + public boolean associateLayoutManagerID(String id) { + if (log.isDebugEnabled()) { + log.debug("associateLayoutManagerID(" + id + ")"); + } + if (!idTracker.alreadyResolvedID(id)) { + idTracker.signalPendingID(id); + return false; + } else { + return true; + } + } + + /** + * Notify the areaTreeHandler that the LayoutManagers containing + * idrefs have finished creating areas + * @param id the id for which layout has finished + */ + public void notifyEndOfLayout(String id) { + idTracker.signalIDProcessed(id); + } + + /** + * Identify an unresolved area (one needing an idref to be + * resolved, e.g. the internal-destination of an fo:basic-link) + * for both the AreaTreeHandler and PageViewport object. + * + * The IDTracker keeps a document-wide list of idref's + * and the PV's needing them to be resolved. It uses this to + * send notifications to the PV's when an id has been resolved. + * + * The PageViewport keeps lists of id's needing resolving, along + * with the child areas (page-number-citation, basic-link, etc.) + * of the PV needing their resolution. + * + * @param id the ID reference to add + * @param res the resolvable object that needs resolving + */ + public void addUnresolvedArea(String id, Resolvable res) { + curPage.getPageViewport().addUnresolvedIDRef(id, res); + idTracker.addUnresolvedIDRef(id, curPage.getPageViewport()); + } + + /** + * Bind the RetrieveMarker to the corresponding Marker subtree. + * If the boundary is page then it will only check the + * current page. For page-sequence and document it will + * lookup preceding pages from the area tree and try to find + * a marker. + * If we retrieve a marker from a preceding page, + * then the containing page does not have a qualifying area, + * and all qualifying areas have ended. + * Therefore we use last-ending-within-page (Constants.EN_LEWP) + * as the position. + * + * @param rm the RetrieveMarker instance whose properties are to + * used to find the matching Marker. + * @return a bound RetrieveMarker instance, or null if no Marker + * could be found. + */ + public RetrieveMarker resolveRetrieveMarker(RetrieveMarker rm) { + AreaTreeModel areaTreeModel = areaTreeHandler.getAreaTreeModel(); + String name = rm.getRetrieveClassName(); + int pos = rm.getRetrievePosition(); + int boundary = rm.getRetrieveBoundary(); + + // get marker from the current markers on area tree + Marker mark = (Marker)getCurrentPV().getMarker(name, pos); + if (mark == null && boundary != EN_PAGE) { + // go back over pages until mark found + // if document boundary then keep going + boolean doc = boundary == EN_DOCUMENT; + int seq = areaTreeModel.getPageSequenceCount(); + int page = areaTreeModel.getPageCount(seq) - 1; + while (page < 0 && doc && seq > 1) { + seq--; + page = areaTreeModel.getPageCount(seq) - 1; + } + while (page >= 0) { + PageViewport pv = areaTreeModel.getPage(seq, page); + mark = (Marker)pv.getMarker(name, Constants.EN_LEWP); + if (mark != null) { + break; + } + page--; + if (page < 0 && doc && seq > 1) { + seq--; + page = areaTreeModel.getPageCount(seq) - 1; + } + } + } + + if (mark == null) { + log.debug("found no marker with name: " + name); + return null; + } else { + rm.bindMarker(mark); + return rm; + } + } + + /** + * Creates and returns a new page. + * @param pageNumber the page number + * @param isBlank true if it's a blank page + * @return the newly created page + */ + protected abstract Page createPage(int pageNumber, boolean isBlank); + + /** + * Makes a new page + * + * @param bIsBlank whether this page is blank or not + * @param bIsLast whether this page is the last page or not + * @return a new page + */ + protected Page makeNewPage(boolean isBlank, boolean isLast) { + if (curPage != null) { + finishPage(); + } + + currentPageNum++; + + curPage = createPage(currentPageNum, isBlank); + + if (log.isDebugEnabled()) { + log.debug("[" + curPage.getPageViewport().getPageNumberString() + + (isBlank ? "*" : "") + "]"); + } + + addIDToPage(pageSeq.getId()); + return curPage; + } + + /** + * Finishes a page in preparation for a new page. + */ + protected void finishPage() { + if (log.isTraceEnabled()) { + curPage.getPageViewport().dumpMarkers(); + } + + // Try to resolve any unresolved IDs for the current page. + // + idTracker.tryIDResolution(curPage.getPageViewport()); + // Queue for ID resolution and rendering + areaTreeHandler.getAreaTreeModel().addPage(curPage.getPageViewport()); + if (log.isDebugEnabled()) { + log.debug("page finished: " + curPage.getPageViewport().getPageNumberString() + + ", current num: " + currentPageNum); + } + curPage = null; + } + + /** {@inheritDoc} */ + public void doForcePageCount(Numeric nextPageSeqInitialPageNumber) { + + int forcePageCount = pageSeq.getForcePageCount(); + + // xsl-spec version 1.0 (15.oct 2001) + // auto | even | odd | end-on-even | end-on-odd | no-force | inherit + // auto: + // Force the last page in this page-sequence to be an odd-page + // if the initial-page-number of the next page-sequence is even. + // Force it to be an even-page + // if the initial-page-number of the next page-sequence is odd. + // If there is no next page-sequence + // or if the value of its initial-page-number is "auto" do not force any page. + + // if force-page-count is auto then set the value of forcePageCount + // depending on the initial-page-number of the next page-sequence + if (nextPageSeqInitialPageNumber != null && forcePageCount == Constants.EN_AUTO) { + if (nextPageSeqInitialPageNumber.getEnum() != 0) { + // auto | auto-odd | auto-even + int nextPageSeqPageNumberType = nextPageSeqInitialPageNumber.getEnum(); + if (nextPageSeqPageNumberType == Constants.EN_AUTO_ODD) { + forcePageCount = Constants.EN_END_ON_EVEN; + } else if (nextPageSeqPageNumberType == Constants.EN_AUTO_EVEN) { + forcePageCount = Constants.EN_END_ON_ODD; + } else { // auto + forcePageCount = Constants.EN_NO_FORCE; + } + } else { // <integer> for explicit page number + int nextPageSeqPageStart = nextPageSeqInitialPageNumber.getValue(); + // spec rule + nextPageSeqPageStart = (nextPageSeqPageStart > 0) ? nextPageSeqPageStart : 1; + if (nextPageSeqPageStart % 2 == 0) { // explicit even startnumber + forcePageCount = Constants.EN_END_ON_ODD; + } else { // explicit odd startnumber + forcePageCount = Constants.EN_END_ON_EVEN; + } + } + } + + if (forcePageCount == Constants.EN_EVEN) { + if ((currentPageNum - startPageNum + 1) % 2 != 0) { // we have an odd number of pages + curPage = makeNewPage(true, false); + } + } else if (forcePageCount == Constants.EN_ODD) { + if ((currentPageNum - startPageNum + 1) % 2 == 0) { // we have an even number of pages + curPage = makeNewPage(true, false); + } + } else if (forcePageCount == Constants.EN_END_ON_EVEN) { + if (currentPageNum % 2 != 0) { // we are now on an odd page + curPage = makeNewPage(true, false); + } + } else if (forcePageCount == Constants.EN_END_ON_ODD) { + if (currentPageNum % 2 == 0) { // we are now on an even page + curPage = makeNewPage(true, false); + } + } else if (forcePageCount == Constants.EN_NO_FORCE) { + // i hope: nothing special at all + } + + if (curPage != null) { + finishPage(); + } + } + +} diff --git a/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java new file mode 100644 index 000000000..5668f81ef --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java @@ -0,0 +1,202 @@ +/* + * 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; + +import java.awt.Dimension; +import java.awt.Rectangle; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.area.AreaTreeHandler; +import org.apache.fop.area.Block; +import org.apache.fop.area.BodyRegion; +import org.apache.fop.area.CTM; +import org.apache.fop.area.LineArea; +import org.apache.fop.area.PageViewport; +import org.apache.fop.area.RegionViewport; +import org.apache.fop.area.inline.Image; +import org.apache.fop.area.inline.Viewport; +import org.apache.fop.datatypes.FODimension; +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.extensions.ExternalDocument; +import org.apache.fop.image.FopImage; +import org.apache.fop.image.ImageFactory; +import org.apache.fop.layoutmgr.inline.ImageLayout; + +/** + * LayoutManager for an external-document extension element. This class is instantiated by + * area.AreaTreeHandler for each fo:external-document found in the + * input document. + */ +public class ExternalDocumentLayoutManager extends AbstractPageSequenceLayoutManager { + + private static Log log = LogFactory.getLog(ExternalDocumentLayoutManager.class); + + private FopImage image; + private ImageLayout imageLayout; + + /** + * Constructor + * + * @param ath the area tree handler object + * @param pseq fo:page-sequence to process + */ + public ExternalDocumentLayoutManager(AreaTreeHandler ath, ExternalDocument document) { + super(ath, document); + } + + /** + * @return the ExternalDocument being managed by this layout manager + */ + protected ExternalDocument getExternalDocument() { + return (ExternalDocument)pageSeq; + } + + /** {@inheritDoc} */ + public PageSequenceLayoutManager getPSLM() { + throw new IllegalStateException("getPSLM() is illegal for " + getClass().getName()); + } + + /** {@inheritDoc} */ + public void activateLayout() { + initialize(); + + String uri = getExternalDocument().getSrc(); + FOUserAgent userAgent = pageSeq.getUserAgent(); + ImageFactory fact = userAgent.getFactory().getImageFactory(); + this.image = fact.getImage(uri, userAgent); + if (this.image == null) { + log.error("Image not available: " + uri); + return; + } else { + // load dimensions + if (!this.image.load(FopImage.DIMENSIONS)) { + log.error("Cannot read image dimensions: " + uri); + return; + } + } + Dimension intrinsicSize = new Dimension( + image.getIntrinsicWidth(), + image.getIntrinsicHeight()); + this.imageLayout = new ImageLayout(getExternalDocument(), this, intrinsicSize); + + areaTreeHandler.getAreaTreeModel().startPageSequence(null); + if (log.isDebugEnabled()) { + log.debug("Starting layout"); + } + + curPage = makeNewPage(false, false); + + fillPage(); //TODO Implement multi-page documents (using new image package) + + finishPage(); + } + + private void fillPage() { + + Dimension imageSize = this.imageLayout.getViewportSize(); + + Block blockArea = new Block(); + blockArea.setIPD(imageSize.width); + LineArea lineArea = new LineArea(); + + Image imageArea = new Image(getExternalDocument().getSrc()); + TraitSetter.setProducerID(imageArea, fobj.getId()); + transferForeignAttributes(imageArea); + + Viewport vp = new Viewport(imageArea); + TraitSetter.setProducerID(vp, fobj.getId()); + vp.setIPD(imageSize.width); + vp.setBPD(imageSize.height); + vp.setContentPosition(imageLayout.getPlacement()); + vp.setOffset(0); + + //Link them all together... + lineArea.addInlineArea(vp); + lineArea.updateExtentsFromChildren(); + blockArea.addLineArea(lineArea); + curPage.getPageViewport().getCurrentFlow().addBlock(blockArea); + curPage.getPageViewport().getCurrentSpan().notifyFlowsFinished(); + } + + /** {@inheritDoc} */ + public void finishPageSequence() { + if (pageSeq.hasId()) { + idTracker.signalIDProcessed(pageSeq.getId()); + } + + pageSeq.getRoot().notifyPageSequenceFinished(currentPageNum, + (currentPageNum - startPageNum) + 1); + areaTreeHandler.notifyPageSequenceFinished(pageSeq, + (currentPageNum - startPageNum) + 1); + + if (log.isDebugEnabled()) { + log.debug("Ending layout"); + } + } + + protected Page createPage(int pageNumber, boolean isBlank) { + String pageNumberString = pageSeq.makeFormattedPageNumber(pageNumber); + + Dimension imageSize = this.imageLayout.getViewportSize(); + + // Set up the CTM on the page reference area based on writing-mode + // and reference-orientation + Rectangle referenceRect; + if (pageSeq.getReferenceOrientation() % 180 == 0) { + referenceRect = new Rectangle(0, 0, imageSize.width, imageSize.height); + } else { + referenceRect = new Rectangle(0, 0, imageSize.height, imageSize.width); + } + FODimension reldims = new FODimension(0, 0); + CTM pageCTM = CTM.getCTMandRelDims(pageSeq.getReferenceOrientation(), + Constants.EN_LR_TB, referenceRect, reldims); + + Page page = new Page(referenceRect, pageNumber, pageNumberString, isBlank); + + PageViewport pv = page.getPageViewport(); + org.apache.fop.area.Page pageArea = new org.apache.fop.area.Page(); + pv.setPage(pageArea); + + RegionViewport rv = new RegionViewport(referenceRect); + rv.setIPD(referenceRect.width); + rv.setBPD(referenceRect.height); + rv.setClip(true); + + BodyRegion body = new BodyRegion(Constants.FO_REGION_BODY, + "fop-image-region", rv, 1, 0); + body.setIPD(imageSize.width); + body.setBPD(imageSize.height); + body.setCTM(pageCTM); + rv.setRegionReference(body); + pageArea.setRegionViewport( + Constants.FO_REGION_BODY, rv); + //Set unique key obtained from the AreaTreeHandler + pv.setKey(areaTreeHandler.generatePageViewportKey()); + + //Also creates first normal flow region + pv.createSpan(false); + + return page; + } + +} diff --git a/src/java/org/apache/fop/layoutmgr/LayoutManagerMaker.java b/src/java/org/apache/fop/layoutmgr/LayoutManagerMaker.java index b56aed866..abb7f0f04 100644 --- a/src/java/org/apache/fop/layoutmgr/LayoutManagerMaker.java +++ b/src/java/org/apache/fop/layoutmgr/LayoutManagerMaker.java @@ -20,6 +20,7 @@ package org.apache.fop.layoutmgr; import java.util.List; import org.apache.fop.fo.FONode; +import org.apache.fop.fo.extensions.ExternalDocument; import org.apache.fop.fo.pagination.Flow; import org.apache.fop.fo.pagination.PageSequence; import org.apache.fop.fo.pagination.SideRegion; @@ -62,6 +63,15 @@ public interface LayoutManagerMaker { AreaTreeHandler ath, PageSequence ps); /** + * Make a ExternalDocumentLayoutManager object for the fox:external-document extension. + * @param ath the AreaTreeHandler object the external-document interacts with + * @param ed the fox:external-document object to be processed + * @return The created ExternalDocumentLayoutManager object + */ + public ExternalDocumentLayoutManager makeExternalDocumentLayoutManager( + AreaTreeHandler ath, ExternalDocument ed); + + /** * Make a FlowLayoutManager object. * @param pslm the parent PageSequenceLayoutManager object * @param flow the fo:flow object this FLM will process diff --git a/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java b/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java index 575ab9fb8..ad7d48caf 100644 --- a/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java +++ b/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java @@ -32,6 +32,7 @@ import org.apache.fop.fo.FOElementMapping; import org.apache.fop.fo.FONode; import org.apache.fop.fo.FOText; import org.apache.fop.fo.FObjMixed; +import org.apache.fop.fo.extensions.ExternalDocument; import org.apache.fop.fo.flow.BasicLink; import org.apache.fop.fo.flow.BidiOverride; import org.apache.fop.fo.flow.Block; @@ -405,4 +406,9 @@ public class LayoutManagerMapping implements LayoutManagerMaker { } } + public ExternalDocumentLayoutManager makeExternalDocumentLayoutManager( + AreaTreeHandler ath, ExternalDocument ed) { + return new ExternalDocumentLayoutManager(ath, ed); + } + } diff --git a/src/java/org/apache/fop/layoutmgr/Page.java b/src/java/org/apache/fop/layoutmgr/Page.java index 5e9addd1e..6d6dbb233 100644 --- a/src/java/org/apache/fop/layoutmgr/Page.java +++ b/src/java/org/apache/fop/layoutmgr/Page.java @@ -19,6 +19,8 @@ package org.apache.fop.layoutmgr; +import java.awt.geom.Rectangle2D; + import org.apache.fop.area.PageViewport; import org.apache.fop.fo.pagination.SimplePageMaster; @@ -45,6 +47,18 @@ public class Page { this.pageViewport = new PageViewport(spm, pageNumber, pageNumberStr, blank); } + /** + * Auxiliary constructor used when there's no SimplePageMaster. + * @param viewArea the view area of the page + * @param pageNumber the page number (as an int) + * @param pageNumberStr the page number (as a String) + * @param blank true if this is a blank page + */ + public Page(Rectangle2D viewArea, int pageNumber, String pageNumberStr, boolean blank) { + this.spm = null; + this.pageViewport = new PageViewport(viewArea, pageNumber, pageNumberStr, null, blank); + } + /** @return the simple-page-master that created this page */ public SimplePageMaster getSimplePageMaster() { return this.spm; diff --git a/src/java/org/apache/fop/layoutmgr/PageProvider.java b/src/java/org/apache/fop/layoutmgr/PageProvider.java index 0360e2942..e16c3396a 100644 --- a/src/java/org/apache/fop/layoutmgr/PageProvider.java +++ b/src/java/org/apache/fop/layoutmgr/PageProvider.java @@ -252,7 +252,7 @@ public class PageProvider implements Constants { try { String pageNumberString = pageSeq.makeFormattedPageNumber(index); SimplePageMaster spm = pageSeq.getNextSimplePageMaster( - index, (startPageOfPageSequence == index), isLastPage, isBlank); + index, (startPageOfPageSequence == index), isLastPage, false, isBlank); Region body = spm.getRegion(FO_REGION_BODY); if (!pageSeq.getMainFlow().getFlowName().equals(body.getRegionName())) { diff --git a/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java b/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java index abf1644c9..efe64d284 100644 --- a/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java @@ -21,61 +21,26 @@ package org.apache.fop.layoutmgr; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.fop.datatypes.Numeric; import org.apache.fop.area.AreaTreeHandler; -import org.apache.fop.area.AreaTreeModel; -import org.apache.fop.area.IDTracker; -import org.apache.fop.area.PageViewport; import org.apache.fop.area.LineArea; -import org.apache.fop.area.Resolvable; - -import org.apache.fop.fo.Constants; -import org.apache.fop.fo.flow.Marker; -import org.apache.fop.fo.flow.RetrieveMarker; - import org.apache.fop.fo.pagination.PageSequence; import org.apache.fop.fo.pagination.PageSequenceMaster; import org.apache.fop.fo.pagination.SideRegion; import org.apache.fop.fo.pagination.StaticContent; import org.apache.fop.layoutmgr.inline.ContentLayoutManager; -import java.util.List; - /** * LayoutManager for a PageSequence. This class is instantiated by * area.AreaTreeHandler for each fo:page-sequence found in the * input document. */ -public class PageSequenceLayoutManager extends AbstractLayoutManager { +public class PageSequenceLayoutManager extends AbstractPageSequenceLayoutManager { private static Log log = LogFactory.getLog(PageSequenceLayoutManager.class); - /** - * AreaTreeHandler which activates the PSLM and controls - * the rendering of its pages. - */ - private AreaTreeHandler areaTreeHandler; - - /** - * fo:page-sequence formatting object being - * processed by this class - */ - private PageSequence pageSeq; - private PageProvider pageProvider; - private IDTracker idTracker; - - /** - * Current page with page-viewport-area being filled by - * the PSLM. - */ - private Page curPage; - - private int startPageNum = 0; - private int currentPageNum = 0; - /** * Constructor * @@ -83,20 +48,10 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { * @param pseq fo:page-sequence to process */ public PageSequenceLayoutManager(AreaTreeHandler ath, PageSequence pseq) { - super(pseq); - this.areaTreeHandler = ath; - this.idTracker = ath.getIDTracker(); - this.pageSeq = pseq; + super(ath, pseq); this.pageProvider = new PageProvider(ath, pseq); } - /** - * @return the LayoutManagerMaker object associated to the areaTreeHandler - */ - public LayoutManagerMaker getLayoutManagerMaker() { - return areaTreeHandler.getLayoutManagerMaker(); - } - /** @return the PageProvider applicable to this page-sequence. */ public PageProvider getPageProvider() { return this.pageProvider; @@ -106,25 +61,27 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { * @return the PageSequence being managed by this layout manager */ protected PageSequence getPageSequence() { - return pageSeq; + return (PageSequence)pageSeq; } /** - * Activate the layout of this page sequence. - * PageViewports corresponding to each page generated by this - * page sequence will be created and sent to the AreaTreeModel - * for rendering. + * Provides access to this object + * @return this PageSequenceLayoutManager instance */ + public PageSequenceLayoutManager getPSLM() { + return this; + } + + /** {@inheritDoc} */ public void activateLayout() { - startPageNum = pageSeq.getStartingPageNumber(); - currentPageNum = startPageNum - 1; + initialize(); LineArea title = null; - if (pageSeq.getTitleFO() != null) { + if (getPageSequence().getTitleFO() != null) { try { ContentLayoutManager clm = getLayoutManagerMaker(). - makeContentLayoutManager(this, pageSeq.getTitleFO()); + makeContentLayoutManager(this, getPageSequence().getTitleFO()); title = (LineArea) clm.getParentArea(null); } catch (IllegalStateException e) { // empty title; do nothing @@ -145,9 +102,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { finishPage(); } - /** - * Finished the page-sequence and notifies everyone about it. - */ + /** {@inheritDoc} */ public void finishPageSequence() { if (pageSeq.hasId()) { idTracker.signalIDProcessed(pageSeq.getId()); @@ -157,11 +112,11 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { (currentPageNum - startPageNum) + 1); areaTreeHandler.notifyPageSequenceFinished(pageSeq, (currentPageNum - startPageNum) + 1); - pageSeq.releasePageSequence(); + getPageSequence().releasePageSequence(); // If this sequence has a page sequence master so we must reset // it in preparation for the next sequence - String masterReference = pageSeq.getMasterReference(); + String masterReference = getPageSequence().getMasterReference(); PageSequenceMaster pageSeqMaster = pageSeq.getRoot().getLayoutMasterSet().getPageSequenceMaster(masterReference); if (pageSeqMaster != null) { @@ -173,229 +128,18 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { } } - /** - * Provides access to the current page. - * @return the current Page - */ - public Page getCurrentPage() { - return curPage; - } - - /** - * Provides access for setting the current page. - * @param currentPage the new current Page - */ - protected void setCurrentPage(Page currentPage) { - this.curPage = currentPage; - } - - /** - * Provides access to the current page number - * @return the current page number - */ - protected int getCurrentPageNum() { - return currentPageNum; - } - - /** - * Provides access to the current page viewport. - * @return the current PageViewport - *//* - public PageViewport getCurrentPageViewport() { - return curPage.getPageViewport(); - }*/ - - /** - * Provides access to this object - * @return this PageSequenceLayoutManager instance - */ - public PageSequenceLayoutManager getPSLM() { - return this; + /** {@inheritDoc} */ + protected Page createPage(int pageNumber, boolean isBlank) { + return pageProvider.getPage(isBlank, + pageNumber, PageProvider.RELTO_PAGE_SEQUENCE); } - /** - * This returns the first PageViewport that contains an id trait - * matching the idref argument, or null if no such PV exists. - * - * @param idref the idref trait needing to be resolved - * @return the first PageViewport that contains the ID trait - */ - public PageViewport getFirstPVWithID(String idref) { - List list = idTracker.getPageViewportsContainingID(idref); - if (list != null && list.size() > 0) { - return (PageViewport) list.get(0); - } - return null; - } - - /** - * This returns the last PageViewport that contains an id trait - * matching the idref argument, or null if no such PV exists. - * - * @param idref the idref trait needing to be resolved - * @return the last PageViewport that contains the ID trait - */ - public PageViewport getLastPVWithID(String idref) { - List list = idTracker.getPageViewportsContainingID(idref); - if (list != null && list.size() > 0) { - return (PageViewport) list.get(list.size() - 1); - } - return null; - } - - /** - * Add an ID reference to the current page. - * When adding areas the area adds its ID reference. - * For the page layout manager it adds the id reference - * with the current page to the area tree. - * - * @param id the ID reference to add - */ - public void addIDToPage(String id) { - if (id != null && id.length() > 0) { - idTracker.associateIDWithPageViewport(id, curPage.getPageViewport()); - } - } - - /** - * Add an id reference of the layout manager in the AreaTreeHandler, - * if the id hasn't been resolved yet - * @param id the id to track - * @return a boolean indicating if the id has already been resolved - * TODO Maybe give this a better name - */ - public boolean associateLayoutManagerID(String id) { - if (log.isDebugEnabled()) { - log.debug("associateLayoutManagerID(" + id + ")"); - } - if (!idTracker.alreadyResolvedID(id)) { - idTracker.signalPendingID(id); - return false; - } else { - return true; - } - } - - /** - * Notify the areaTreeHandler that the LayoutManagers containing - * idrefs have finished creating areas - * @param id the id for which layout has finished - */ - public void notifyEndOfLayout(String id) { - idTracker.signalIDProcessed(id); - } - - /** - * Identify an unresolved area (one needing an idref to be - * resolved, e.g. the internal-destination of an fo:basic-link) - * for both the AreaTreeHandler and PageViewport object. - * - * The IDTracker keeps a document-wide list of idref's - * and the PV's needing them to be resolved. It uses this to - * send notifications to the PV's when an id has been resolved. - * - * The PageViewport keeps lists of id's needing resolving, along - * with the child areas (page-number-citation, basic-link, etc.) - * of the PV needing their resolution. - * - * @param id the ID reference to add - * @param res the resolvable object that needs resolving - */ - public void addUnresolvedArea(String id, Resolvable res) { - curPage.getPageViewport().addUnresolvedIDRef(id, res); - idTracker.addUnresolvedIDRef(id, curPage.getPageViewport()); - } - - /** - * Bind the RetrieveMarker to the corresponding Marker subtree. - * If the boundary is page then it will only check the - * current page. For page-sequence and document it will - * lookup preceding pages from the area tree and try to find - * a marker. - * If we retrieve a marker from a preceding page, - * then the containing page does not have a qualifying area, - * and all qualifying areas have ended. - * Therefore we use last-ending-within-page (Constants.EN_LEWP) - * as the position. - * - * @param rm the RetrieveMarker instance whose properties are to - * used to find the matching Marker. - * @return a bound RetrieveMarker instance, or null if no Marker - * could be found. - */ - public RetrieveMarker resolveRetrieveMarker(RetrieveMarker rm) { - AreaTreeModel areaTreeModel = areaTreeHandler.getAreaTreeModel(); - String name = rm.getRetrieveClassName(); - int pos = rm.getRetrievePosition(); - int boundary = rm.getRetrieveBoundary(); - - // get marker from the current markers on area tree - Marker mark = (Marker)getCurrentPV().getMarker(name, pos); - if (mark == null && boundary != EN_PAGE) { - // go back over pages until mark found - // if document boundary then keep going - boolean doc = boundary == EN_DOCUMENT; - int seq = areaTreeModel.getPageSequenceCount(); - int page = areaTreeModel.getPageCount(seq) - 1; - while (page < 0 && doc && seq > 1) { - seq--; - page = areaTreeModel.getPageCount(seq) - 1; - } - while (page >= 0) { - PageViewport pv = areaTreeModel.getPage(seq, page); - mark = (Marker)pv.getMarker(name, Constants.EN_LEWP); - if (mark != null) { - break; - } - page--; - if (page < 0 && doc && seq > 1) { - seq--; - page = areaTreeModel.getPageCount(seq) - 1; - } - } - } - - if (mark == null) { - log.debug("found no marker with name: " + name); - return null; - } else { - rm.bindMarker(mark); - return rm; - } - } - - /** - * Makes a new page - * - * @param bIsBlank whether this page is blank or not - * @param bIsLast whether this page is the last page or not - * @return a new page - */ - protected Page makeNewPage(boolean bIsBlank, boolean bIsLast) { - if (curPage != null) { - finishPage(); - } - - currentPageNum++; - - curPage = pageProvider.getPage(bIsBlank, - currentPageNum, PageProvider.RELTO_PAGE_SEQUENCE); - - if (log.isDebugEnabled()) { - log.debug("[" + curPage.getPageViewport().getPageNumberString() - + (bIsBlank ? "*" : "") + "]"); - } - - addIDToPage(pageSeq.getId()); - return curPage; - } - private void layoutSideRegion(int regionID) { SideRegion reg = (SideRegion)curPage.getSimplePageMaster().getRegion(regionID); if (reg == null) { return; } - StaticContent sc = pageSeq.getStaticContent(reg.getRegionName()); + StaticContent sc = getPageSequence().getStaticContent(reg.getRegionName()); if (sc == null) { return; } @@ -406,94 +150,15 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { lm.doLayout(); } - private void finishPage() { - if (log.isTraceEnabled()) { - curPage.getPageViewport().dumpMarkers(); - } + /** {@inheritDoc} */ + protected void finishPage() { // Layout side regions layoutSideRegion(FO_REGION_BEFORE); layoutSideRegion(FO_REGION_AFTER); layoutSideRegion(FO_REGION_START); layoutSideRegion(FO_REGION_END); - // Try to resolve any unresolved IDs for the current page. - // - idTracker.tryIDResolution(curPage.getPageViewport()); - // Queue for ID resolution and rendering - areaTreeHandler.getAreaTreeModel().addPage(curPage.getPageViewport()); - if (log.isDebugEnabled()) { - log.debug("page finished: " + curPage.getPageViewport().getPageNumberString() - + ", current num: " + currentPageNum); - } - curPage = null; + super.finishPage(); } - /** - * Act upon the force-page-count trait, - * in relation to the initial-page-number trait of the following page-sequence. - * @param nextPageSeqInitialPageNumber initial-page-number trait of next page-sequence - */ - public void doForcePageCount(Numeric nextPageSeqInitialPageNumber) { - - int forcePageCount = pageSeq.getForcePageCount(); - - // xsl-spec version 1.0 (15.oct 2001) - // auto | even | odd | end-on-even | end-on-odd | no-force | inherit - // auto: - // Force the last page in this page-sequence to be an odd-page - // if the initial-page-number of the next page-sequence is even. - // Force it to be an even-page - // if the initial-page-number of the next page-sequence is odd. - // If there is no next page-sequence - // or if the value of its initial-page-number is "auto" do not force any page. - - // if force-page-count is auto then set the value of forcePageCount - // depending on the initial-page-number of the next page-sequence - if (nextPageSeqInitialPageNumber != null && forcePageCount == Constants.EN_AUTO) { - if (nextPageSeqInitialPageNumber.getEnum() != 0) { - // auto | auto-odd | auto-even - int nextPageSeqPageNumberType = nextPageSeqInitialPageNumber.getEnum(); - if (nextPageSeqPageNumberType == Constants.EN_AUTO_ODD) { - forcePageCount = Constants.EN_END_ON_EVEN; - } else if (nextPageSeqPageNumberType == Constants.EN_AUTO_EVEN) { - forcePageCount = Constants.EN_END_ON_ODD; - } else { // auto - forcePageCount = Constants.EN_NO_FORCE; - } - } else { // <integer> for explicit page number - int nextPageSeqPageStart = nextPageSeqInitialPageNumber.getValue(); - // spec rule - nextPageSeqPageStart = (nextPageSeqPageStart > 0) ? nextPageSeqPageStart : 1; - if (nextPageSeqPageStart % 2 == 0) { // explicit even startnumber - forcePageCount = Constants.EN_END_ON_ODD; - } else { // explicit odd startnumber - forcePageCount = Constants.EN_END_ON_EVEN; - } - } - } - - if (forcePageCount == Constants.EN_EVEN) { - if ((currentPageNum - startPageNum + 1) % 2 != 0) { // we have an odd number of pages - curPage = makeNewPage(true, false); - } - } else if (forcePageCount == Constants.EN_ODD) { - if ((currentPageNum - startPageNum + 1) % 2 == 0) { // we have an even number of pages - curPage = makeNewPage(true, false); - } - } else if (forcePageCount == Constants.EN_END_ON_EVEN) { - if (currentPageNum % 2 != 0) { // we are now on an odd page - curPage = makeNewPage(true, false); - } - } else if (forcePageCount == Constants.EN_END_ON_ODD) { - if (currentPageNum % 2 == 0) { // we are now on an even page - curPage = makeNewPage(true, false); - } - } else if (forcePageCount == Constants.EN_NO_FORCE) { - // i hope: nothing special at all - } - - if (curPage != null) { - finishPage(); - } - } } diff --git a/src/java/org/apache/fop/layoutmgr/TopLevelLayoutManager.java b/src/java/org/apache/fop/layoutmgr/TopLevelLayoutManager.java new file mode 100644 index 000000000..63c8ac158 --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/TopLevelLayoutManager.java @@ -0,0 +1,50 @@ +/* + * 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; + +import org.apache.fop.datatypes.Numeric; + +/** + * This interface is implemented by top-level layout managers such as the ones for fo:page-sequence + * and fox:external-document. + */ +public interface TopLevelLayoutManager { + + /** + * Activate the layout of this page sequence. + * PageViewports corresponding to each page generated by this + * page sequence will be created and sent to the AreaTreeModel + * for rendering. + */ + public void activateLayout(); + + /** + * Act upon the force-page-count trait, + * in relation to the initial-page-number trait of the following page-sequence. + * @param nextPageSeqInitialPageNumber initial-page-number trait of next page-sequence + */ + public void doForcePageCount(Numeric nextPageSeqInitialPageNumber); + + /** + * Finished the page-sequence and notifies everyone about it. + */ + public void finishPageSequence(); + +}
\ No newline at end of file diff --git a/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java index 75f852e06..6426f15db 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java @@ -19,12 +19,12 @@ package org.apache.fop.layoutmgr.inline; -import java.awt.geom.Rectangle2D; +import java.awt.Dimension; +import java.awt.Rectangle; import java.util.LinkedList; import org.apache.fop.area.Area; import org.apache.fop.area.inline.Viewport; -import org.apache.fop.datatypes.Length; import org.apache.fop.datatypes.LengthBase; import org.apache.fop.fo.FObj; import org.apache.fop.fo.flow.AbstractGraphics; @@ -57,180 +57,29 @@ public abstract class AbstractGraphicsLayoutManager extends LeafNodeLayoutManage * @return the viewport inline area */ private Viewport getInlineArea() { + Dimension intrinsicSize = new Dimension( + fobj.getIntrinsicWidth(), + fobj.getIntrinsicHeight()); - // viewport size is determined by block-progression-dimension - // and inline-progression-dimension + //TODO Investigate if the line-height property has to be taken into the calculation + //somehow. There was some code here that hints in this direction but it was disabled. - // if replaced then use height then ignore block-progression-dimension - //int h = this.propertyList.get("height").getLength().mvalue(); - - // use specified line-height then ignore dimension in height direction - boolean hasLH = false; //propertyList.get("line-height").getSpecifiedValue() != null; - - Length len; - - int bpd = -1; - int ipd = -1; - if (hasLH) { - bpd = fobj.getLineHeight().getOptimum(this).getLength().getValue(this); - } else { - // this property does not apply when the line-height applies - // isn't the block-progression-dimension always in the same - // direction as the line height? - len = fobj.getBlockProgressionDimension().getOptimum(this).getLength(); - if (len.getEnum() != EN_AUTO) { - bpd = len.getValue(this); - } else { - len = fobj.getHeight(); - if (len.getEnum() != EN_AUTO) { - bpd = len.getValue(this); - } - } - } - - len = fobj.getInlineProgressionDimension().getOptimum(this).getLength(); - if (len.getEnum() != EN_AUTO) { - ipd = len.getValue(this); - } else { - len = fobj.getWidth(); - if (len.getEnum() != EN_AUTO) { - ipd = len.getValue(this); - } - } - - // if auto then use the intrinsic size of the content scaled - // to the content-height and content-width - int cwidth = -1; - int cheight = -1; - len = fobj.getContentWidth(); - if (len.getEnum() != EN_AUTO) { - switch (len.getEnum()) { - case EN_SCALE_TO_FIT: - if (ipd != -1) { - cwidth = ipd; - } - break; - case EN_SCALE_DOWN_TO_FIT: - if (ipd != -1 && fobj.getIntrinsicWidth() > ipd) { - cwidth = ipd; - } - break; - case EN_SCALE_UP_TO_FIT: - if (ipd != -1 && fobj.getIntrinsicWidth() < ipd) { - cwidth = ipd; - } - break; - default: - cwidth = len.getValue(this); - } - } - len = fobj.getContentHeight(); - if (len.getEnum() != EN_AUTO) { - switch (len.getEnum()) { - case EN_SCALE_TO_FIT: - if (bpd != -1) { - cheight = bpd; - } - break; - case EN_SCALE_DOWN_TO_FIT: - if (bpd != -1 && fobj.getIntrinsicHeight() > bpd) { - cheight = bpd; - } - break; - case EN_SCALE_UP_TO_FIT: - if (bpd != -1 && fobj.getIntrinsicHeight() < bpd) { - cheight = bpd; - } - break; - default: - cheight = len.getValue(this); - } - } - - int scaling = fobj.getScaling(); - if ((scaling == EN_UNIFORM) || (cwidth == -1) || cheight == -1) { - if (cwidth == -1 && cheight == -1) { - cwidth = fobj.getIntrinsicWidth(); - cheight = fobj.getIntrinsicHeight(); - } else if (cwidth == -1) { - if (fobj.getIntrinsicHeight() == 0) { - cwidth = 0; - } else { - cwidth = (int)(fobj.getIntrinsicWidth() * (double)cheight - / fobj.getIntrinsicHeight()); - } - } else if (cheight == -1) { - if (fobj.getIntrinsicWidth() == 0) { - cheight = 0; - } else { - cheight = (int)(fobj.getIntrinsicHeight() * (double)cwidth - / fobj.getIntrinsicWidth()); - } - } else { - // adjust the larger - if (fobj.getIntrinsicWidth() == 0 || fobj.getIntrinsicHeight() == 0) { - cwidth = 0; - cheight = 0; - } else { - double rat1 = (double) cwidth / fobj.getIntrinsicWidth(); - double rat2 = (double) cheight / fobj.getIntrinsicHeight(); - if (rat1 < rat2) { - // reduce cheight - cheight = (int)(rat1 * fobj.getIntrinsicHeight()); - } else if (rat1 > rat2) { - cwidth = (int)(rat2 * fobj.getIntrinsicWidth()); - } - } - } - } - - if (ipd == -1) { - ipd = cwidth; - } - if (bpd == -1) { - bpd = cheight; - } - - boolean clip = false; - if (cwidth > ipd || cheight > bpd) { - int overflow = fobj.getOverflow(); - if (overflow == EN_HIDDEN) { - clip = true; - } else if (overflow == EN_ERROR_IF_OVERFLOW) { - log.error("Object overflows the viewport: clipping"); - clip = true; - } - } - - int xoffset = fobj.computeXOffset(ipd, cwidth); - int yoffset = fobj.computeYOffset(bpd, cheight); + ImageLayout imageLayout = new ImageLayout(fobj, this, intrinsicSize); + Rectangle placement = imageLayout.getPlacement(); CommonBorderPaddingBackground borderProps = fobj.getCommonBorderPaddingBackground(); - //Determine extra BPD from borders etc. + //Determine extra BPD from borders and padding int beforeBPD = borderProps.getPadding(CommonBorderPaddingBackground.BEFORE, false, this); - beforeBPD += borderProps.getBorderWidth(CommonBorderPaddingBackground.BEFORE, - false); - int afterBPD = borderProps.getPadding(CommonBorderPaddingBackground.AFTER, false, this); - afterBPD += borderProps.getBorderWidth(CommonBorderPaddingBackground.AFTER, false); + beforeBPD += borderProps.getBorderWidth(CommonBorderPaddingBackground.BEFORE, false); - yoffset += beforeBPD; - //bpd += beforeBPD; - //bpd += afterBPD; + placement.y += beforeBPD; - //Determine extra IPD from borders etc. - int startIPD = borderProps.getPadding(CommonBorderPaddingBackground.START, - false, this); - startIPD += borderProps.getBorderWidth(CommonBorderPaddingBackground.START, - false); - int endIPD = borderProps.getPadding(CommonBorderPaddingBackground.END, false, this); - endIPD += borderProps.getBorderWidth(CommonBorderPaddingBackground.END, false); + //Determine extra IPD from borders and padding + int startIPD = borderProps.getPadding(CommonBorderPaddingBackground.START, false, this); + startIPD += borderProps.getBorderWidth(CommonBorderPaddingBackground.START, false); - xoffset += startIPD; - //ipd += startIPD; - //ipd += endIPD; - - Rectangle2D placement = new Rectangle2D.Float(xoffset, yoffset, cwidth, cheight); + placement.x += startIPD; Area viewportArea = getChildArea(); TraitSetter.setProducerID(viewportArea, fobj.getId()); @@ -238,10 +87,10 @@ public abstract class AbstractGraphicsLayoutManager extends LeafNodeLayoutManage Viewport vp = new Viewport(viewportArea); TraitSetter.setProducerID(vp, fobj.getId()); - vp.setIPD(ipd); - vp.setBPD(bpd); + vp.setIPD(imageLayout.getViewportSize().width); + vp.setBPD(imageLayout.getViewportSize().height); vp.setContentPosition(placement); - vp.setClip(clip); + vp.setClip(imageLayout.isClipped()); vp.setOffset(0); // Common Border, Padding, and Background Properties @@ -254,9 +103,7 @@ public abstract class AbstractGraphicsLayoutManager extends LeafNodeLayoutManage return vp; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public LinkedList getNextKnuthElements(LayoutContext context, int alignment) { Viewport areaCurrent = getInlineArea(); @@ -264,9 +111,7 @@ public abstract class AbstractGraphicsLayoutManager extends LeafNodeLayoutManage return super.getNextKnuthElements(context, alignment); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ protected AlignmentContext makeAlignmentContext(LayoutContext context) { return new AlignmentContext( get(context).getAllocBPD() diff --git a/src/java/org/apache/fop/layoutmgr/inline/ImageLayout.java b/src/java/org/apache/fop/layoutmgr/inline/ImageLayout.java new file mode 100644 index 000000000..b3ccea628 --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/inline/ImageLayout.java @@ -0,0 +1,262 @@ +/* + * 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.inline; + +import java.awt.Dimension; +import java.awt.Rectangle; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.fop.datatypes.Length; +import org.apache.fop.datatypes.PercentBaseContext; +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.GraphicsProperties; + +public class ImageLayout implements Constants { + + /** logging instance */ + protected static Log log = LogFactory.getLog(ImageLayout.class); + + //Input + private GraphicsProperties props; + private PercentBaseContext percentBaseContext; + private Dimension intrinsicSize; + + //Output + private Rectangle placement; + private Dimension viewportSize = new Dimension(-1, -1); + private boolean clip; + + public ImageLayout(GraphicsProperties props, PercentBaseContext percentBaseContext, + Dimension intrinsicSize) { + this.props = props; + this.percentBaseContext = percentBaseContext; + this.intrinsicSize = intrinsicSize; + + doLayout(); + } + + protected void doLayout() { + Length len; + + int bpd = -1; + int ipd = -1; + + len = props.getBlockProgressionDimension().getOptimum(percentBaseContext).getLength(); + if (len.getEnum() != EN_AUTO) { + bpd = len.getValue(percentBaseContext); + } else { + len = props.getHeight(); + if (len.getEnum() != EN_AUTO) { + bpd = len.getValue(percentBaseContext); + } + } + + len = props.getInlineProgressionDimension().getOptimum(percentBaseContext).getLength(); + if (len.getEnum() != EN_AUTO) { + ipd = len.getValue(percentBaseContext); + } else { + len = props.getWidth(); + if (len.getEnum() != EN_AUTO) { + ipd = len.getValue(percentBaseContext); + } + } + + // if auto then use the intrinsic size of the content scaled + // to the content-height and content-width + int cwidth = -1; + int cheight = -1; + len = props.getContentWidth(); + if (len.getEnum() != EN_AUTO) { + switch (len.getEnum()) { + case EN_SCALE_TO_FIT: + if (ipd != -1) { + cwidth = ipd; + } + break; + case EN_SCALE_DOWN_TO_FIT: + if (ipd != -1 && intrinsicSize.width > ipd) { + cwidth = ipd; + } + break; + case EN_SCALE_UP_TO_FIT: + if (ipd != -1 && intrinsicSize.width < ipd) { + cwidth = ipd; + } + break; + default: + cwidth = len.getValue(percentBaseContext); + } + } + len = props.getContentHeight(); + if (len.getEnum() != EN_AUTO) { + switch (len.getEnum()) { + case EN_SCALE_TO_FIT: + if (bpd != -1) { + cheight = bpd; + } + break; + case EN_SCALE_DOWN_TO_FIT: + if (bpd != -1 && intrinsicSize.height > bpd) { + cheight = bpd; + } + break; + case EN_SCALE_UP_TO_FIT: + if (bpd != -1 && intrinsicSize.height < bpd) { + cheight = bpd; + } + break; + default: + cheight = len.getValue(percentBaseContext); + } + } + + int scaling = props.getScaling(); + if ((scaling == EN_UNIFORM) || (cwidth == -1) || cheight == -1) { + if (cwidth == -1 && cheight == -1) { + cwidth = intrinsicSize.width; + cheight = intrinsicSize.height; + } else if (cwidth == -1) { + if (intrinsicSize.height == 0) { + cwidth = 0; + } else { + cwidth = (int)(intrinsicSize.width * (double)cheight + / intrinsicSize.height); + } + } else if (cheight == -1) { + if (intrinsicSize.width == 0) { + cheight = 0; + } else { + cheight = (int)(intrinsicSize.height * (double)cwidth + / intrinsicSize.width); + } + } else { + // adjust the larger + if (intrinsicSize.width == 0 || intrinsicSize.height == 0) { + cwidth = 0; + cheight = 0; + } else { + double rat1 = (double) cwidth / intrinsicSize.width; + double rat2 = (double) cheight / intrinsicSize.height; + if (rat1 < rat2) { + // reduce cheight + cheight = (int)(rat1 * intrinsicSize.height); + } else if (rat1 > rat2) { + cwidth = (int)(rat2 * intrinsicSize.width); + } + } + } + } + + if (ipd == -1) { + ipd = cwidth; + } + if (bpd == -1) { + bpd = cheight; + } + + this.clip = false; + if (cwidth > ipd || cheight > bpd) { + int overflow = props.getOverflow(); + if (overflow == EN_HIDDEN) { + this.clip = true; + } else if (overflow == EN_ERROR_IF_OVERFLOW) { + //TODO Don't use logging to report error! + log.error("Object overflows the viewport: clipping"); + this.clip = true; + } + } + + int xoffset = computeXOffset(ipd, cwidth); + int yoffset = computeYOffset(bpd, cheight); + + //Build calculation results + this.viewportSize.setSize(ipd, bpd); + this.placement = new Rectangle(xoffset, yoffset, cwidth, cheight); + } + + /** + * Given the ipd and the content width calculates the + * required x offset based on the text-align property + * @param ipd the inline-progression-dimension of the object + * @param cwidth the calculated content width of the object + * @return the X offset + */ + public int computeXOffset (int ipd, int cwidth) { + int xoffset = 0; + switch (props.getTextAlign()) { + case EN_CENTER: + xoffset = (ipd - cwidth) / 2; + break; + case EN_END: + xoffset = ipd - cwidth; + break; + case EN_START: + break; + case EN_JUSTIFY: + default: + break; + } + return xoffset; + } + + /** + * Given the bpd and the content height calculates the + * required y offset based on the display-align property + * @param bpd the block-progression-dimension of the object + * @param cheight the calculated content height of the object + * @return the Y offset + */ + public int computeYOffset(int bpd, int cheight) { + int yoffset = 0; + switch (props.getDisplayAlign()) { + case EN_BEFORE: + break; + case EN_AFTER: + yoffset = bpd - cheight; + break; + case EN_CENTER: + yoffset = (bpd - cheight) / 2; + break; + case EN_AUTO: + default: + break; + } + return yoffset; + } + + public Rectangle getPlacement() { + return this.placement; + } + + public Dimension getViewportSize() { + return this.viewportSize; + } + + public Dimension getIntrinsicSize() { + return this.intrinsicSize; + } + + public boolean isClipped() { + return this.clip; + } + +}
\ No newline at end of file diff --git a/src/java/org/apache/fop/render/rtf/RTFHandler.java b/src/java/org/apache/fop/render/rtf/RTFHandler.java index 36fbc235a..eb50a3ea7 100644 --- a/src/java/org/apache/fop/render/rtf/RTFHandler.java +++ b/src/java/org/apache/fop/render/rtf/RTFHandler.java @@ -199,7 +199,8 @@ public class RTFHandler extends FOEventHandler { log.warn("Using default simple-page-master from page-sequence-master..."); PageSequenceMaster master = pageSeq.getRoot().getLayoutMasterSet().getPageSequenceMaster(reference); - this.pagemaster = master.getNextSimplePageMaster(false, false, false, false); + this.pagemaster = master.getNextSimplePageMaster( + false, false, false, false, false); } } diff --git a/src/java/org/apache/fop/render/xml/XMLRenderer.java b/src/java/org/apache/fop/render/xml/XMLRenderer.java index 9e5fd8dbc..b969afac1 100644 --- a/src/java/org/apache/fop/render/xml/XMLRenderer.java +++ b/src/java/org/apache/fop/render/xml/XMLRenderer.java @@ -524,7 +524,9 @@ public class XMLRenderer extends PrintRenderer { addAttribute("key", page.getKey()); addAttribute("nr", page.getPageNumber()); addAttribute("formatted-nr", page.getPageNumberString()); - addAttribute("simple-page-master-name", page.getSimplePageMasterName()); + if (page.getSimplePageMasterName() != null) { + addAttribute("simple-page-master-name", page.getSimplePageMasterName()); + } if (page.isBlank()) { addAttribute("blank", "true"); } diff --git a/status.xml b/status.xml index 0537ad75d..ff1069164 100644 --- a/status.xml +++ b/status.xml @@ -29,6 +29,11 @@ <changes> <release version="FOP Trunk"> <action context="Code" dev="JM" type="add"> + Added new extension element: fox:external-document. It allows to add whole documents + such as multi-page TIFF images to be inserted as peers to a page-sequence. Each image + will make up an entire page. See the documentation for details. + </action> + <action context="Code" dev="JM" type="add"> Added support for scale-down-to-fit and scale-up-to-fit (introduced in XSL 1.1). </action> <action context="Code" dev="VH" type="fix" fixes-bug="43633"> diff --git a/test/layoutengine/standard-testcases/fox_external-document_1.xml b/test/layoutengine/standard-testcases/fox_external-document_1.xml new file mode 100644 index 000000000..ca73806e8 --- /dev/null +++ b/test/layoutengine/standard-testcases/fox_external-document_1.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks the basics of fox:external-document.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="5in">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fox:external-document id="img" src="../../resources/images/bgimg72dpi.png"/>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="1" xpath="count(/areaTree/pageSequence)"/>
+ <eval expected="1" xpath="count(//pageViewport)"/>
+ <eval expected="1" xpath="/areaTree/pageSequence/pageViewport/@formatted-nr"/>
+ <eval expected="1" xpath="/areaTree/pageSequence/pageViewport/@nr"/>
+ <eval expected="0 0 191975 191975" xpath="/areaTree/pageSequence/pageViewport/@bounds"/>
+
+ <eval expected="191975" xpath="//regionViewport/@ipd"/>
+ <eval expected="191975" xpath="//regionViewport/@bpd"/>
+ <eval expected="191975" xpath="//lineArea/@ipd"/>
+ <eval expected="191975" xpath="//lineArea/@bpd"/>
+ <eval expected="191975" xpath="//viewport/@ipd"/>
+ <eval expected="191975" xpath="//viewport/@bpd"/>
+ <eval expected="0 0 191975 191975" xpath="//viewport/@pos"/>
+ <eval expected="img" xpath="//viewport/@prod-id"/>
+ <eval expected="img" xpath="//image/@prod-id"/>
+ </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/fox_external-document_2.xml b/test/layoutengine/standard-testcases/fox_external-document_2.xml new file mode 100644 index 000000000..5bce78f35 --- /dev/null +++ b/test/layoutengine/standard-testcases/fox_external-document_2.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks fox:external-document.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="5in">
+ <fo:region-body reference-orientation="90"/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fox:external-document id="img1" src="../../resources/images/bgimg72dpi.png"
+ width="5in" height="5in" text-align="center" display-align="center"/>
+ <fo:page-sequence master-reference="normal" id="ps1">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>
+ Just a normal page-sequence in between...
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ <fox:external-document id="img2" src="../../resources/images/bgimg72dpi.png"
+ width="5in" height="5in" content-width="scale-to-fit"/>
+ <fox:external-document id="img3" src="../../resources/images/bgimg72dpi.png"
+ width="5in" height="5in" content-width="scale-down-to-fit" initial-page-number="5"/>
+ <fox:external-document id="img4" src="../../resources/images/big-image.png"
+ reference-orientation="90"/>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="5" xpath="count(/areaTree/pageSequence)"/>
+ <eval expected="6" xpath="count(//pageViewport)"/>
+
+ <eval expected="1" xpath="//pageViewport[@nr = '1']/@formatted-nr"/>
+ <eval expected="2" xpath="//pageViewport[@nr = '2']/@formatted-nr"/>
+ <eval expected="3" xpath="//pageViewport[@nr = '3']/@formatted-nr"/>
+ <eval expected="4" xpath="//pageViewport[@nr = '4']/@formatted-nr"/>
+ <true xpath="boolean(//pageViewport[@nr = '4']/@blank)"/> <!-- effect from force-page-count="auto" -->
+ <eval expected="5" xpath="//pageViewport[@nr = '5']/@formatted-nr"/>
+ <eval expected="6" xpath="//pageViewport[@nr = '6']/@formatted-nr"/>
+
+ <eval expected="0 0 360000 360000" xpath="//pageViewport[@nr = '1']/@bounds"/>
+ <eval expected="0 0 360000 360000" xpath="//pageViewport[@nr = '2']/@bounds"/>
+ <eval expected="0 0 360000 360000" xpath="//pageViewport[@nr = '3']/@bounds"/>
+ <eval expected="0 0 360000 360000" xpath="//pageViewport[@nr = '5']/@bounds"/>
+ <eval expected="0 0 843913 597171" xpath="//pageViewport[@nr = '6']/@bounds"/>
+
+ <eval expected="84012 84012 191975 191975" xpath="//viewport[@prod-id = 'img1']/@pos"/>
+ <eval expected="0 0 360000 360000" xpath="//viewport[@prod-id = 'img2']/@pos"/>
+ <eval expected="0 0 191975 191975" xpath="//viewport[@prod-id = 'img3']/@pos"/>
+ <eval expected="0 0 597171 843913" xpath="//viewport[@prod-id = 'img4']/@pos"/>
+
+ <eval expected="0 0 843913 597171" xpath="//pageViewport[@nr = '6']/page/regionViewport/@rect"/>
+ <eval expected="843913" xpath="//pageViewport[@nr = '6']/page/regionViewport/@ipd"/>
+ <eval expected="597171" xpath="//pageViewport[@nr = '6']/page/regionViewport/@bpd"/>
+ <eval expected="597171" xpath="//pageViewport[@nr = '6']/page/regionViewport/regionBody/@ipd"/>
+ <eval expected="843913" xpath="//pageViewport[@nr = '6']/page/regionViewport/regionBody/@bpd"/>
+ <eval expected="[0.0 -1.0 1.0 0.0 0.0 597171.0]" xpath="//pageViewport[@nr = '6']/page/regionViewport/regionBody/@ctm"/>
+
+ </checks>
+</testcase>
|