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>
\r
import java.util.List;\r
\r
-import org.apache.fop.fo.pagination.PageSequence;\r
+import org.apache.fop.fo.pagination.AbstractPageSequence;\r
\r
/**\r
* Class for reporting back formatting results to the calling application.\r
* Reports the result of one page sequence rendering\r
* back into this object.\r
*\r
- * @param pageSequence the PageSequence which just completed rendering\r
+ * @param pageSequence the page sequence which just completed rendering\r
* @param pageCount the number of pages rendered for that PageSequence\r
*/\r
- public void haveFormattedPageSequence(PageSequence pageSequence, int pageCount) {\r
+ public void haveFormattedPageSequence(AbstractPageSequence pageSequence, int pageCount) {\r
this.pageCount += pageCount;\r
if (this.pageSequences == null) {\r
this.pageSequences = new java.util.ArrayList();\r
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.
// 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;
}
}
+ /**
+ * @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()) {
}
} 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;
}
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;
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) {
+ }
+
}
--- /dev/null
+/*
+ * 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();
+
+}
package org.apache.fop.fo;
// Java
+import java.text.MessageFormat;
+
import org.xml.sax.Attributes;
import org.apache.commons.logging.Log;
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;
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());
}
}
}
}
+ static class ExternalDocumentMaker extends ElementMapping.Maker {
+ public FONode make(FONode parent) {
+ return new ExternalDocument(parent);
+ }
+ }
+
/** {@inheritDoc} */
public String getStandardPrefix() {
return "fox";
--- /dev/null
+/*
+ * 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";
+ }
+
+}
+
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;
* 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.
width = pList.get(PR_WIDTH).getLength();
}
- /**
- * 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.
*/
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;
}
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
*/
--- /dev/null
+/*
+ * 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();
+ }
+
+}
* @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) {
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
/** 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,
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");
*/
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"
}
}
- this.pageNumberGenerator = new PageNumberGenerator(
- format, groupingSeparator, groupingSize, letterValue);
-
getFOEventHandler().startPageSequence(this);
}
+ "\" 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 \""
}
}
- /**
- * 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.
// */
// 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) {
* 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
public SimplePageMaster getNextSimplePageMaster(int page,
boolean isFirstPage,
boolean isLastPage,
+ boolean isOnlyPage,
boolean isBlank) throws FOPException {
if (pageSequenceMaster == null) {
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);
}
/**
}
}
- /**
- * 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;
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;
}
}
+ /** @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
public SimplePageMaster getNextSimplePageMaster(boolean isOddPage,
boolean isFirstPage,
boolean isLastPage,
+ boolean isOnlyPage,
boolean isBlankPage)
throws FOPException {
if (currentSubSequence == null) {
}
}
String pageMasterName = currentSubSequence
- .getNextPageMasterName(isOddPage, isFirstPage, isLastPage, isBlankPage);
+ .getNextPageMasterName(isOddPage, isFirstPage, isLastPage, isOnlyPage, isBlankPage);
boolean canRecover = true;
while (pageMasterName == null) {
SubSequenceSpecifier nextSubSequence = getNextSubSequence();
currentSubSequence = nextSubSequence;
}
pageMasterName = currentSubSequence
- .getNextPageMasterName(isOddPage, isFirstPage, isLastPage, isBlankPage);
+ .getNextPageMasterName(isOddPage, isFirstPage, isLastPage, isOnlyPage, isBlankPage);
}
SimplePageMaster pageMaster = this.layoutMasterSet
.getSimplePageMaster(pageMasterName);
private List conditionalPageMasterRefs;
private boolean hasPagePositionLast = false;
+ private boolean hasPagePositionOnly = false;
/**
* @see org.apache.fop.fo.FONode#FONode(FONode)
public String getNextPageMasterName(boolean isOddPage,
boolean isFirstPage,
boolean isLastPage,
+ boolean isOnlyPage,
boolean isBlankPage) {
if (getMaximumRepeats() != INFINITE) {
if (numberConsumed < getMaximumRepeats()) {
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();
}
}
if (cpmr.getPagePosition() == EN_LAST) {
this.hasPagePositionLast = true;
}
+ if (cpmr.getPagePosition() == EN_ONLY) {
+ this.hasPagePositionOnly = true;
+ }
}
/** {@inheritDoc} */
}
/** {@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";
}
public String getNextPageMasterName(boolean isOddPage,
boolean isFirstPage,
boolean isLastPage,
+ boolean isOnlyPage,
boolean isEmptyPage) {
if (getMaximumRepeats() != INFINITE) {
if (numberConsumed < getMaximumRepeats()) {
return false;
}
+ /** {@inheritDoc} */
+ public boolean hasPagePositionOnly() {
+ return false;
+ }
+
/** {@inheritDoc} */
public String getLocalName() {
return "repeatable-page-master-reference";
return FO_REPEATABLE_PAGE_MASTER_REFERENCE;
}
+
}
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;
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))");
}
}
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;
}
}
public String getNextPageMasterName(boolean isOddPage,
boolean isFirstPage,
boolean isLastPage,
+ boolean isOnlyPage,
boolean isEmptyPage) {
if (this.state == FIRST) {
this.state = DONE;
return false;
}
+ /** {@inheritDoc} */
+ public boolean hasPagePositionOnly() {
+ return false;
+ }
+
/** {@inheritDoc} */
public String getLocalName() {
return "single-page-master-reference";
public int getNameId() {
return FO_SINGLE_PAGE_MASTER_REFERENCE;
}
+
}
* @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
String getNextPageMasterName(boolean isOddPage,
boolean isFirstPage,
boolean isLastPage,
+ boolean isOnlyPage,
boolean isBlankPage)
throws FOPException;
/** @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();
+
}
return newLMs;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public PageSequenceLayoutManager getPSLM() {
return parentLM.getPSLM();
}
--- /dev/null
+/*
+ * 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();
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
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;
public PageSequenceLayoutManager makePageSequenceLayoutManager(
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
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;
}
}
+ public ExternalDocumentLayoutManager makeExternalDocumentLayoutManager(
+ AreaTreeHandler ath, ExternalDocument ed) {
+ return new ExternalDocumentLayoutManager(ath, ed);
+ }
+
}
package org.apache.fop.layoutmgr;
+import java.awt.geom.Rectangle2D;
+
import org.apache.fop.area.PageViewport;
import org.apache.fop.fo.pagination.SimplePageMaster;
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;
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())) {
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
*
* @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;
* @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
finishPage();
}
- /**
- * Finished the page-sequence and notifies everyone about it.
- */
+ /** {@inheritDoc} */
public void finishPageSequence() {
if (pageSeq.hasId()) {
idTracker.signalIDProcessed(pageSeq.getId());
(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) {
}
}
- /**
- * 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;
}
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();
- }
- }
}
--- /dev/null
+/*
+ * 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
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;
* @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());
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
return vp;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public LinkedList getNextKnuthElements(LayoutContext context,
int alignment) {
Viewport areaCurrent = getInlineArea();
return super.getNextKnuthElements(context, alignment);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected AlignmentContext makeAlignmentContext(LayoutContext context) {
return new AlignmentContext(
get(context).getAllocBPD()
--- /dev/null
+/*
+ * 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
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);
}
}
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");
}
<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>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<!--\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+-->\r
+<!-- $Id$ -->\r
+<testcase>\r
+ <info>\r
+ <p>\r
+ This test checks the basics of fox:external-document.\r
+ </p>\r
+ </info>\r
+ <fo>\r
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"\r
+ xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">\r
+ <fo:layout-master-set>\r
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="5in">\r
+ <fo:region-body/>\r
+ </fo:simple-page-master>\r
+ </fo:layout-master-set>\r
+ <fox:external-document id="img" src="../../resources/images/bgimg72dpi.png"/>\r
+ </fo:root>\r
+ </fo>\r
+ <checks>\r
+ <eval expected="1" xpath="count(/areaTree/pageSequence)"/>\r
+ <eval expected="1" xpath="count(//pageViewport)"/>\r
+ <eval expected="1" xpath="/areaTree/pageSequence/pageViewport/@formatted-nr"/>\r
+ <eval expected="1" xpath="/areaTree/pageSequence/pageViewport/@nr"/>\r
+ <eval expected="0 0 191975 191975" xpath="/areaTree/pageSequence/pageViewport/@bounds"/>\r
+ \r
+ <eval expected="191975" xpath="//regionViewport/@ipd"/>\r
+ <eval expected="191975" xpath="//regionViewport/@bpd"/>\r
+ <eval expected="191975" xpath="//lineArea/@ipd"/>\r
+ <eval expected="191975" xpath="//lineArea/@bpd"/>\r
+ <eval expected="191975" xpath="//viewport/@ipd"/>\r
+ <eval expected="191975" xpath="//viewport/@bpd"/>\r
+ <eval expected="0 0 191975 191975" xpath="//viewport/@pos"/>\r
+ <eval expected="img" xpath="//viewport/@prod-id"/>\r
+ <eval expected="img" xpath="//image/@prod-id"/>\r
+ </checks>\r
+</testcase>\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<!--\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+-->\r
+<!-- $Id$ -->\r
+<testcase>\r
+ <info>\r
+ <p>\r
+ This test checks fox:external-document.\r
+ </p>\r
+ </info>\r
+ <fo>\r
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"\r
+ xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">\r
+ <fo:layout-master-set>\r
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="5in">\r
+ <fo:region-body reference-orientation="90"/>\r
+ </fo:simple-page-master>\r
+ </fo:layout-master-set>\r
+ <fox:external-document id="img1" src="../../resources/images/bgimg72dpi.png"\r
+ width="5in" height="5in" text-align="center" display-align="center"/>\r
+ <fo:page-sequence master-reference="normal" id="ps1">\r
+ <fo:flow flow-name="xsl-region-body">\r
+ <fo:block>\r
+ Just a normal page-sequence in between...\r
+ </fo:block>\r
+ </fo:flow>\r
+ </fo:page-sequence>\r
+ <fox:external-document id="img2" src="../../resources/images/bgimg72dpi.png"\r
+ width="5in" height="5in" content-width="scale-to-fit"/>\r
+ <fox:external-document id="img3" src="../../resources/images/bgimg72dpi.png"\r
+ width="5in" height="5in" content-width="scale-down-to-fit" initial-page-number="5"/>\r
+ <fox:external-document id="img4" src="../../resources/images/big-image.png"\r
+ reference-orientation="90"/>\r
+ </fo:root>\r
+ </fo>\r
+ <checks>\r
+ <eval expected="5" xpath="count(/areaTree/pageSequence)"/>\r
+ <eval expected="6" xpath="count(//pageViewport)"/>\r
+ \r
+ <eval expected="1" xpath="//pageViewport[@nr = '1']/@formatted-nr"/>\r
+ <eval expected="2" xpath="//pageViewport[@nr = '2']/@formatted-nr"/>\r
+ <eval expected="3" xpath="//pageViewport[@nr = '3']/@formatted-nr"/>\r
+ <eval expected="4" xpath="//pageViewport[@nr = '4']/@formatted-nr"/>\r
+ <true xpath="boolean(//pageViewport[@nr = '4']/@blank)"/> <!-- effect from force-page-count="auto" -->\r
+ <eval expected="5" xpath="//pageViewport[@nr = '5']/@formatted-nr"/>\r
+ <eval expected="6" xpath="//pageViewport[@nr = '6']/@formatted-nr"/>\r
+ \r
+ <eval expected="0 0 360000 360000" xpath="//pageViewport[@nr = '1']/@bounds"/>\r
+ <eval expected="0 0 360000 360000" xpath="//pageViewport[@nr = '2']/@bounds"/>\r
+ <eval expected="0 0 360000 360000" xpath="//pageViewport[@nr = '3']/@bounds"/>\r
+ <eval expected="0 0 360000 360000" xpath="//pageViewport[@nr = '5']/@bounds"/>\r
+ <eval expected="0 0 843913 597171" xpath="//pageViewport[@nr = '6']/@bounds"/>\r
+ \r
+ <eval expected="84012 84012 191975 191975" xpath="//viewport[@prod-id = 'img1']/@pos"/>\r
+ <eval expected="0 0 360000 360000" xpath="//viewport[@prod-id = 'img2']/@pos"/>\r
+ <eval expected="0 0 191975 191975" xpath="//viewport[@prod-id = 'img3']/@pos"/>\r
+ <eval expected="0 0 597171 843913" xpath="//viewport[@prod-id = 'img4']/@pos"/>\r
+\r
+ <eval expected="0 0 843913 597171" xpath="//pageViewport[@nr = '6']/page/regionViewport/@rect"/>\r
+ <eval expected="843913" xpath="//pageViewport[@nr = '6']/page/regionViewport/@ipd"/>\r
+ <eval expected="597171" xpath="//pageViewport[@nr = '6']/page/regionViewport/@bpd"/>\r
+ <eval expected="597171" xpath="//pageViewport[@nr = '6']/page/regionViewport/regionBody/@ipd"/>\r
+ <eval expected="843913" xpath="//pageViewport[@nr = '6']/page/regionViewport/regionBody/@bpd"/>\r
+ <eval expected="[0.0 -1.0 1.0 0.0 0.0 597171.0]" xpath="//pageViewport[@nr = '6']/page/regionViewport/regionBody/@ctm"/>\r
+ \r
+ </checks>\r
+</testcase>\r