]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Added new extension element: fox:external-document. It allows to add whole documents...
authorJeremias Maerki <jeremias@apache.org>
Thu, 27 Dec 2007 10:34:15 +0000 (10:34 +0000)
committerJeremias Maerki <jeremias@apache.org>
Thu, 27 Dec 2007 10:34:15 +0000 (10:34 +0000)
Some preparations for page-position="only" but the implementation is incomplete and "only" has no effect, yet. (Just uploaded some stuff I once started)
Some javadoc cleanups.

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@607032 13f79535-47bb-0310-9956-ffa450edef68

35 files changed:
src/documentation/content/xdocs/trunk/extensions.xml
src/java/org/apache/fop/apps/FormattingResults.java
src/java/org/apache/fop/area/AreaTreeHandler.java
src/java/org/apache/fop/area/RenderPagesModel.java
src/java/org/apache/fop/fo/FOEventHandler.java
src/java/org/apache/fop/fo/GraphicsProperties.java [new file with mode: 0644]
src/java/org/apache/fop/fo/PropertyList.java
src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java
src/java/org/apache/fop/fo/extensions/ExternalDocument.java [new file with mode: 0644]
src/java/org/apache/fop/fo/flow/AbstractGraphics.java
src/java/org/apache/fop/fo/pagination/AbstractPageSequence.java [new file with mode: 0644]
src/java/org/apache/fop/fo/pagination/ConditionalPageMasterReference.java
src/java/org/apache/fop/fo/pagination/PageSequence.java
src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java
src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java
src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java
src/java/org/apache/fop/fo/pagination/Root.java
src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java
src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java
src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java
src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/LayoutManagerMaker.java
src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java
src/java/org/apache/fop/layoutmgr/Page.java
src/java/org/apache/fop/layoutmgr/PageProvider.java
src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java
src/java/org/apache/fop/layoutmgr/TopLevelLayoutManager.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java
src/java/org/apache/fop/layoutmgr/inline/ImageLayout.java [new file with mode: 0644]
src/java/org/apache/fop/render/rtf/RTFHandler.java
src/java/org/apache/fop/render/xml/XMLRenderer.java
status.xml
test/layoutengine/standard-testcases/fox_external-document_1.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/fox_external-document_2.xml [new file with mode: 0644]

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