Index: src/java/org/apache/fop/area/AreaTreeParser.java
===================================================================
--- src/java/org/apache/fop/area/AreaTreeParser.java	(revision 830403)
+++ src/java/org/apache/fop/area/AreaTreeParser.java	(working copy)
@@ -20,6 +20,7 @@
 package org.apache.fop.area;
 
 import java.awt.Color;
+import java.awt.Rectangle;
 import java.awt.geom.Rectangle2D;
 import java.util.List;
 import java.util.Map;
@@ -36,17 +37,17 @@
 import javax.xml.transform.sax.SAXTransformerFactory;
 import javax.xml.transform.sax.TransformerHandler;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.util.*;
 import org.w3c.dom.DOMImplementation;
 import org.w3c.dom.Document;
-
 import org.xml.sax.Attributes;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.DefaultHandler;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
 import org.apache.xmlgraphics.image.loader.ImageInfo;
 import org.apache.xmlgraphics.image.loader.ImageManager;
 import org.apache.xmlgraphics.image.loader.ImageSessionContext;
@@ -74,11 +75,6 @@
 import org.apache.fop.fonts.Font;
 import org.apache.fop.fonts.FontInfo;
 import org.apache.fop.traits.BorderProps;
-import org.apache.fop.util.ColorUtil;
-import org.apache.fop.util.ContentHandlerFactory;
-import org.apache.fop.util.ContentHandlerFactoryRegistry;
-import org.apache.fop.util.DefaultErrorListener;
-import org.apache.fop.util.QName;
 
 /**
  * This is a parser for the area tree XML (intermediate format) which is used to reread an area
@@ -384,7 +380,7 @@
                 if (currentPageViewport != null) {
                     throw new IllegalStateException("currentPageViewport must be null");
                 }
-                Rectangle2D viewArea = parseRect(attributes.getValue("bounds"));
+                Rectangle viewArea = XMLUtil.getAttributeAsRectangle(attributes, "bounds");
                 int pageNumber = getAttributeAsInteger(attributes, "nr", -1);
                 String key = attributes.getValue("key");
                 String pageNumberString = attributes.getValue("formatted-nr");
Index: src/java/org/apache/fop/area/PageViewport.java
===================================================================
--- src/java/org/apache/fop/area/PageViewport.java	(revision 830403)
+++ src/java/org/apache/fop/area/PageViewport.java	(working copy)
@@ -20,7 +20,6 @@
 package org.apache.fop.area;
 
 import java.awt.Rectangle;
-import java.awt.geom.Rectangle2D;
 import java.io.ObjectOutputStream;
 import java.io.ObjectInputStream;
 import java.util.ArrayList;
@@ -49,7 +48,7 @@
 public class PageViewport extends AreaTreeObject implements Resolvable, Cloneable {
 
     private Page page;
-    private Rectangle2D viewArea;
+    private Rectangle viewArea;
     private String simplePageMasterName;
     
     /**
@@ -105,6 +104,7 @@
     public PageViewport(SimplePageMaster spm, int pageNumber, String pageStr, boolean blank) {
         this.simplePageMasterName = spm.getMasterName();
         this.extensionAttachments = spm.getExtensionAttachments();
+        setForeignAttributes(spm.getForeignAttributes());
         this.blank = blank;
         int pageWidth = spm.getPageWidth().getValue();
         int pageHeight = spm.getPageHeight().getValue();
@@ -123,10 +123,13 @@
         if (original.extensionAttachments != null) {
             this.extensionAttachments = new java.util.ArrayList(original.extensionAttachments);
         }
+        if (original.foreignAttributes != null) {
+            setForeignAttributes(original.foreignAttributes);
+        }
         this.pageNumber = original.pageNumber;
         this.pageNumberString = original.pageNumberString;
         this.page = (Page)original.page.clone();
-        this.viewArea = (Rectangle2D)original.viewArea.clone();
+        this.viewArea = new Rectangle(original.viewArea);
         this.simplePageMasterName = original.simplePageMasterName;
         this.blank = original.blank;
     }
@@ -139,7 +142,7 @@
      * @param simplePageMasterName name of the original simple-page-master that generated this page
      * @param blank true if this is a blank page
      */
-    public PageViewport(Rectangle2D viewArea, int pageNumber, String pageStr, 
+    public PageViewport(Rectangle viewArea, int pageNumber, String pageStr, 
             String simplePageMasterName, boolean blank) {
         this.viewArea = viewArea;
         this.pageNumber = pageNumber;
@@ -165,7 +168,7 @@
      * Get the view area rectangle of this viewport.
      * @return the rectangle for this viewport
      */
-    public Rectangle2D getViewArea() {
+    public Rectangle getViewArea() {
         return viewArea;
     }
 
Index: src/java/org/apache/fop/layoutmgr/Page.java
===================================================================
--- src/java/org/apache/fop/layoutmgr/Page.java	(revision 830403)
+++ src/java/org/apache/fop/layoutmgr/Page.java	(working copy)
@@ -19,7 +19,7 @@
 
 package org.apache.fop.layoutmgr;
 
-import java.awt.geom.Rectangle2D;
+import java.awt.Rectangle;
 
 import org.apache.fop.area.PageViewport;
 import org.apache.fop.fo.pagination.SimplePageMaster;
@@ -54,7 +54,7 @@
      * @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) {
+    public Page(Rectangle viewArea, int pageNumber, String pageNumberStr, boolean blank) {
         this.spm = null;
         this.pageViewport = new PageViewport(viewArea, pageNumber, pageNumberStr, null, blank);
     }
Index: src/java/org/apache/fop/pdf/PDFFactory.java
===================================================================
--- src/java/org/apache/fop/pdf/PDFFactory.java	(revision 830403)
+++ src/java/org/apache/fop/pdf/PDFFactory.java	(working copy)
@@ -171,22 +171,19 @@
      * PDFDocument later using addObject().
      *
      * @param resources resources object to use
-     * @param pageWidth width of the page in points
-     * @param pageHeight height of the page in points
      * @param pageIndex index of the page (zero-based)
+     * @param mediaBox the MediaBox area
+     * @param cropBox the CropBox area
+     * @param bleedBox the BleedBox area
+     * @param trimBox the TrimBox area
      *
      * @return the created /Page object
      */
-    public PDFPage makePage(PDFResources resources,
-                            int pageWidth, int pageHeight, int pageIndex) {
+    public PDFPage makePage(PDFResources resources, int pageIndex,
+                            Rectangle2D mediaBox, Rectangle2D cropBox,
+                            Rectangle2D bleedBox, Rectangle2D trimBox) {
+        PDFPage page = new PDFPage(resources, pageIndex, mediaBox, cropBox, bleedBox, trimBox);
 
-        /*
-         * create a PDFPage with the next object number, the given
-         * resources, contents and dimensions
-         */
-        PDFPage page = new PDFPage(resources,
-                                   pageWidth, pageHeight, pageIndex);
-
         getDocument().assignObjectNumber(page);
         getDocument().getPages().addPage(page);
         return page;
@@ -200,10 +197,28 @@
      * @param resources resources object to use
      * @param pageWidth width of the page in points
      * @param pageHeight height of the page in points
+     * @param pageIndex index of the page (zero-based)
      *
      * @return the created /Page object
      */
     public PDFPage makePage(PDFResources resources,
+                            int pageWidth, int pageHeight, int pageIndex) {
+        Rectangle2D mediaBox = new Rectangle2D.Double(0, 0, pageWidth, pageHeight);
+        return makePage(resources, pageIndex, mediaBox, mediaBox, mediaBox, mediaBox);
+    }
+
+    /**
+     * Make a /Page object. The page is assigned an object number immediately
+     * so references can already be made. The page must be added to the
+     * PDFDocument later using addObject().
+     *
+     * @param resources resources object to use
+     * @param pageWidth width of the page in points
+     * @param pageHeight height of the page in points
+     *
+     * @return the created /Page object
+     */
+    public PDFPage makePage(PDFResources resources,
                             int pageWidth, int pageHeight) {
         return makePage(resources, pageWidth, pageHeight, -1);
     }
Index: src/java/org/apache/fop/pdf/PDFPage.java
===================================================================
--- src/java/org/apache/fop/pdf/PDFPage.java	(revision 830403)
+++ src/java/org/apache/fop/pdf/PDFPage.java	(working copy)
@@ -38,42 +38,42 @@
      * Create a /Page object
      *
      * @param resources the /Resources object
-     * @param contents the content stream
-     * @param pageWidth the page's width in points
-     * @param pageHeight the page's height in points
      * @param pageIndex the page's zero-based index (or -1 if the page number is auto-determined)
+     * @param mediaBox the MediaBox
+     * @param cropBox the CropBox. If null, mediaBox is used.
+     * @param bleedBox the BleedBox. If null, cropBox is used.
+     * @param trimBox the TrimBox. If null, bleedBox is used.
      */
-    public PDFPage(PDFResources resources, PDFStream contents,
-                   int pageWidth, int pageHeight, int pageIndex) {
-
+    public PDFPage(PDFResources resources, int pageIndex,
+                   Rectangle2D mediaBox, Rectangle2D cropBox,
+                   Rectangle2D bleedBox, Rectangle2D trimBox) {
         /* generic creation of object */
         super(resources);
 
         put("Type", new PDFName("Page"));
         /* set fields using parameters */
-        setContents(contents);
-        setSimplePageSize(pageWidth, pageHeight);
+        setSimplePageSize(mediaBox, cropBox, bleedBox, trimBox);
         this.pageIndex = pageIndex;
     }
 
-    /**
-     * Create a /Page object
-     *
-     * @param resources the /Resources object
-     * @param pageWidth the page's width in points
-     * @param pageHeight the page's height in points
-     * @param pageIndex the page's zero-based index (or -1 if the page number is auto-determined)
-     */
-    public PDFPage(PDFResources resources,
-                   int pageWidth, int pageHeight, int pageIndex) {
-        this(resources, null, pageWidth, pageHeight, pageIndex);
-    }
+    private void setSimplePageSize(Rectangle2D mediaBox, Rectangle2D cropBox,
+                                   Rectangle2D bleedBox, Rectangle2D trimBox) {
+        setMediaBox(mediaBox);
 
-    private void setSimplePageSize(int width, int height) {
-        Rectangle2D box = new Rectangle2D.Double(0, 0, width, height);
-        setMediaBox(box);
-        setBleedBox(box); //Recommended by PDF/X
-        setTrimBox(box); //Needed for PDF/X
+        if (cropBox == null) {
+            cropBox = mediaBox;
+        }
+        setCropBox(cropBox);
+  
+        if (bleedBox == null) {
+            bleedBox = cropBox;
+        }
+        setBleedBox(bleedBox); //Recommended by PDF/X
+
+        if (trimBox == null) {
+            trimBox = bleedBox;
+        }
+        setTrimBox(trimBox); //Needed for PDF/X
     }
     
     private PDFArray toPDFArray(Rectangle2D box) {
@@ -88,6 +88,14 @@
     public void setMediaBox(Rectangle2D box) {
         put("MediaBox", toPDFArray(box));
     }
+
+    /**
+     * Sets the "CropBox" entry
+     * @param box the bleed rectangle
+     */
+    public void setCropBox(Rectangle2D box) {
+        put("CropBox", toPDFArray(box));
+    }  
     
     /**
      * Sets the "TrimBox" entry
Index: src/java/org/apache/fop/render/awt/AWTRenderer.java
===================================================================
--- src/java/org/apache/fop/render/awt/AWTRenderer.java	(revision 830403)
+++ src/java/org/apache/fop/render/awt/AWTRenderer.java	(working copy)
@@ -30,6 +30,7 @@
 import java.awt.Color;
 import java.awt.Dimension;
 import java.awt.geom.Rectangle2D;
+import java.awt.geom.Point2D;
 import java.awt.print.PageFormat;
 import java.awt.print.Pageable;
 import java.awt.print.Paper;
@@ -46,6 +47,7 @@
 import org.apache.fop.render.awt.viewer.Renderable;
 import org.apache.fop.render.awt.viewer.StatusListener;
 import org.apache.fop.render.java2d.Java2DRenderer;
+import org.apache.fop.render.extensions.prepress.PageScale;
 
 /**
  * The AWTRender outputs the pages generated by the layout engine to a Swing
@@ -155,6 +157,15 @@
         double scaleY = scaleFactor
                 * (25.4 / FopFactoryConfigurator.DEFAULT_TARGET_RESOLUTION)
                 / userAgent.getTargetPixelUnitToMillimeter();
+        if (getPageViewport(pageNum).getForeignAttributes() != null) {
+            String scale = (String) getPageViewport(pageNum).getForeignAttributes().get(
+                    PageScale.EXT_PAGE_SCALE);
+            Point2D scales = PageScale.getScale(scale);
+            if (scales != null) {
+                scaleX *= scales.getX();
+                scaleY *= scales.getY();
+            }
+        }
         int bitmapWidth = (int) ((pageWidth * scaleX) + 0.5);
         int bitmapHeight = (int) ((pageHeight * scaleY) + 0.5);
         return new Dimension(bitmapWidth, bitmapHeight);
Index: src/java/org/apache/fop/render/extensions/prepress/PageBoundaries.java
===================================================================
--- src/java/org/apache/fop/render/extensions/prepress/PageBoundaries.java	(revision 0)
+++ src/java/org/apache/fop/render/extensions/prepress/PageBoundaries.java	(revision 0)
@@ -0,0 +1,216 @@
+package org.apache.fop.render.extensions.prepress;
+
+import java.awt.*;
+import java.text.MessageFormat;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.fop.util.QName;
+
+import org.apache.fop.fo.extensions.ExtensionElementMapping;
+import org.apache.fop.fo.properties.FixedLength;
+
+
+/**
+ * This class is used to calculate the effective boundaries of a page including special-purpose
+ * boxes used in prepress. These are specified using extension attributes:
+ * bleedBox, trimBox and cropBox. The semantics are further described on the website.
+ */
+public class PageBoundaries {
+
+    /**
+     * The extension attribute for calculating the PDF BleedBox area - specifies the bleed width.
+     */
+    public static final QName EXT_BLEED
+            = new QName(ExtensionElementMapping.URI, null, "bleed");
+
+    /**
+     * The extension attribute for the PDF CropBox area.
+     */
+    public static final QName EXT_CROP_OFFSET
+            = new QName(ExtensionElementMapping.URI, null, "crop-offset");
+
+    /**
+     * The extension attribute for the PDF CropBox area.
+     */
+    public static final QName EXT_CROP_BOX
+            = new QName(ExtensionElementMapping.URI, null, "crop-box");
+
+
+    private static final Pattern SIZE_UNIT_PATTERN
+            = Pattern.compile("^(-?\\d*\\.?\\d*)(px|in|cm|mm|pt|pc|mpt)$");
+
+    private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s+");
+
+    private Rectangle trimBox;
+    private Rectangle bleedBox;
+    private Rectangle mediaBox;
+    private Rectangle cropBox;
+
+    /**
+     * Creates a new instance.
+     * @param pageSize the page size (in mpt) defined by the simple-page-master.
+     * @param bleed the bleed value (raw value as given in the property value)
+     * @param cropOffset the crop-offset value (raw value as given in the property value)
+     * @param cropBoxSelector the crop-box, valid values: (trim-box|bleed-box|media-box)
+     */
+    public PageBoundaries(Dimension pageSize, String bleed, String cropOffset,
+            String cropBoxSelector) {
+        calculate(pageSize, bleed, cropOffset, cropBoxSelector);
+    }
+
+    /**
+     * Creates a new instance.
+     * @param pageSize the page size (in mpt) defined by the simple-page-master.
+     * @param foreignAttributes the foreign attributes for the page
+     *                  (used to extract the extension attribute values)
+     */
+    public PageBoundaries(Dimension pageSize, Map foreignAttributes) {
+        String bleed = (String)foreignAttributes.get(EXT_BLEED);
+        String cropOffset = (String)foreignAttributes.get(EXT_CROP_OFFSET);
+        String cropBoxSelector = (String)foreignAttributes.get(EXT_CROP_BOX);
+        calculate(pageSize, bleed, cropOffset, cropBoxSelector);
+    }
+
+    private void calculate(Dimension pageSize, String bleed, String cropOffset,
+            String cropBoxSelector) {
+        this.trimBox = new Rectangle(pageSize);
+        this.bleedBox = getBleedBoxRectangle(this.trimBox, bleed);
+        Rectangle cropMarksBox = getCropMarksAreaRectangle(trimBox, cropOffset);
+
+        //MediaBox includes all of the following three rectangles
+        this.mediaBox = new Rectangle();
+        this.mediaBox.add(this.trimBox);
+        this.mediaBox.add(this.bleedBox);
+        this.mediaBox.add(cropMarksBox);
+
+        if ("trim-box".equals(cropBoxSelector)) {
+            this.cropBox = this.trimBox;
+        } else if ("bleed-box".equals(cropBoxSelector)) {
+            this.cropBox = this.bleedBox;
+        } else if ("media-box".equals(cropBoxSelector)
+                || cropBoxSelector == null
+                || "".equals(cropBoxSelector)) {
+            this.cropBox = this.mediaBox;
+        } else {
+            final String err = "The crop-box has invalid value: {0}, "
+                + "possible values of crop-box: (trim-box|bleed-box|media-box)";
+            throw new IllegalArgumentException(MessageFormat.format(err,
+                    new Object[]{cropBoxSelector}));
+        }
+    }
+
+    /**
+     * Returns the trim box for the page. This is equal to the page size given in XSL-FO.
+     * After production the printed media is trimmed to this rectangle.
+     * @return the trim box
+     */
+    public Rectangle getTrimBox() {
+        return this.trimBox;
+    }
+
+    /**
+     * Returns the bleed box for the page.
+     * @return the bleed box
+     */
+    public Rectangle getBleedBox() {
+        return this.bleedBox;
+    }
+
+    /**
+     * Returns the media box for the page.
+     * @return the media box
+     */
+    public Rectangle getMediaBox() {
+        return this.mediaBox;
+    }
+
+    /**
+     * Returns the crop box for the page. The crop box is used by Adobe Acrobat to select which
+     * parts of the document shall be displayed and it also defines the rectangle to which a
+     * RIP will clip the document. For bitmap output, this defines the size of the bitmap.
+     * @return the crop box
+     */
+    public Rectangle getCropBox() {
+        return this.cropBox;
+    }
+
+    /**
+     * The BleedBox is calculated by expanding the TrimBox by the bleed widths.
+     *
+     * @param trimBox the TrimBox rectangle
+     * @param bleed   the given bleed widths
+     * @return the calculated BleedBox rectangle
+     */
+    private static Rectangle getBleedBoxRectangle(Rectangle trimBox, String bleed) {
+        return getRectangleUsingOffset(trimBox, bleed);
+    }
+
+    /**
+     * The MediaBox is calculated by expanding the TrimBox by the crop offsets.
+     *
+     * @param trimBox     the TrimBox rectangle
+     * @param cropOffsets the given crop offsets
+     * @return the calculated MediaBox rectangle
+     */
+    private static Rectangle getCropMarksAreaRectangle(Rectangle trimBox, String cropOffsets) {
+        return getRectangleUsingOffset(trimBox, cropOffsets);
+    }
+
+    private static Rectangle getRectangleUsingOffset(Rectangle originalRect, String offset) {
+        if (offset == null || "".equals(offset) || originalRect == null) {
+            return originalRect;
+        }
+
+        String[] offsets = WHITESPACE_PATTERN.split(offset);
+        int[] coords = new int[4]; // top, right, bottom, left
+        switch (offsets.length) {
+        case 1:
+            coords[0] = getLengthIntValue(offsets[0]);
+            coords[1] = coords[0];
+            coords[2] = coords[0];
+            coords[3] = coords[0];
+            break;
+        case 2:
+            coords[0] = getLengthIntValue(offsets[0]);
+            coords[1] = getLengthIntValue(offsets[1]);
+            coords[2] = coords[0];
+            coords[3] = coords[1];
+            break;
+        case 3:
+            coords[0] = getLengthIntValue(offsets[0]);
+            coords[1] = getLengthIntValue(offsets[1]);
+            coords[2] = getLengthIntValue(offsets[2]);
+            coords[3] = coords[1];
+            break;
+        case 4:
+            coords[0] = getLengthIntValue(offsets[0]);
+            coords[1] = getLengthIntValue(offsets[1]);
+            coords[2] = getLengthIntValue(offsets[2]);
+            coords[3] = getLengthIntValue(offsets[3]);
+            break;
+        default:
+            // TODO throw appropriate exception that can be caught by the event
+            // notification mechanism
+            throw new IllegalArgumentException("Too many arguments");
+        }
+        return new Rectangle(originalRect.x - coords[3],
+                originalRect.y - coords[2],
+                originalRect.width + coords[3] + coords[1],
+                originalRect.height + coords[0] + coords[2]);
+    }
+
+    private static int getLengthIntValue(final String length) {
+        final String err = "Incorrect length value: {0}";
+        Matcher m = SIZE_UNIT_PATTERN.matcher(length);
+
+        if (m.find()) {
+            return FixedLength.getInstance(Double.parseDouble(m.group(1)),
+                    m.group(2)).getLength().getValue();
+        } else {
+            throw new IllegalArgumentException(MessageFormat.format(err, new Object[]{length}));
+        }
+    }
+
+}
Index: src/java/org/apache/fop/render/extensions/prepress/PageScale.java
===================================================================
--- src/java/org/apache/fop/render/extensions/prepress/PageScale.java	(revision 0)
+++ src/java/org/apache/fop/render/extensions/prepress/PageScale.java	(revision 0)
@@ -0,0 +1,93 @@
+/*
+ * 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: PageScaleAttributes.java 800142 2009-08-02 19:41:37Z jeremias $ */
+
+package org.apache.fop.render.extensions.prepress;
+
+import java.awt.geom.Point2D;
+import java.text.MessageFormat;
+import java.util.regex.Pattern;
+
+import org.apache.fop.util.QName;
+
+import org.apache.fop.fo.extensions.ExtensionElementMapping;
+
+
+/**
+ * This class provides utility methods to parse the 'fox:scale' extension attribute.
+ */
+public final class PageScale {
+
+    /**
+     * The extension 'scale' attribute for the simple-page-master element.
+     */
+    public static final QName EXT_PAGE_SCALE
+            = new QName(ExtensionElementMapping.URI, null, "scale");
+
+    private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s+");
+
+    /**
+     * Utility classes should not have a public or default constructor
+     */
+    private PageScale() {
+    }
+
+    /**
+     * Compute scale parameters from given fox:scale attribute which has the format: scaleX [scaleY]
+     * If scaleY is not defined, it equals scaleX.
+     * @param scale scale attribute, input format: scaleX [scaleY]
+     * @return the pair of (sx, sy) values
+     */
+    public static Point2D getScale(String scale) {
+        // TODO throw appropriate exceptions that can be caught by the event
+        // notification mechanism
+        final String err = "Extension 'scale' attribute has incorrect value(s): {0}";
+
+        if (scale == null || scale.equals("")) {
+            return null;
+        }
+
+        String[] scales = WHITESPACE_PATTERN.split(scale);
+        double scaleX;
+        try {
+            scaleX = Double.parseDouble(scales[0]);
+        } catch (NumberFormatException nfe) {
+            throw new IllegalArgumentException(MessageFormat.format(err, new Object[]{scale}));
+        }
+        double scaleY;
+        switch (scales.length) {
+        case 1:
+            scaleY = scaleX;
+            break;
+        case 2:
+            try {
+                scaleY = Double.parseDouble(scales[1]);
+            } catch (NumberFormatException nfe) {
+                throw new IllegalArgumentException(MessageFormat.format(err, new Object[]{scale}));
+            }
+            break;
+        default:
+            throw new IllegalArgumentException("Too many arguments");
+        }
+        if (scaleX <= 0 || scaleY <= 0) {
+            throw new IllegalArgumentException(MessageFormat.format(err, new Object[]{scale}));
+        }
+
+        return new Point2D.Double(scaleX, scaleY);
+    }
+}
Index: src/java/org/apache/fop/render/java2d/Java2DRenderer.java
===================================================================
--- src/java/org/apache/fop/render/java2d/Java2DRenderer.java	(revision 830403)
+++ src/java/org/apache/fop/render/java2d/Java2DRenderer.java	(working copy)
@@ -20,11 +20,7 @@
 package org.apache.fop.render.java2d;
 
 // Java
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.RenderingHints;
+import java.awt.*;
 import java.awt.font.GlyphVector;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.GeneralPath;
@@ -73,6 +69,8 @@
 import org.apache.fop.render.AbstractPathOrientedRenderer;
 import org.apache.fop.render.Graphics2DAdapter;
 import org.apache.fop.render.RendererContext;
+import org.apache.fop.render.extensions.prepress.PageBoundaries;
+import org.apache.fop.render.extensions.prepress.PageScale;
 import org.apache.fop.render.pdf.CTMHelper;
 import org.apache.fop.util.CharUtilities;
 
@@ -278,7 +276,10 @@
 
         this.currentPageViewport = pageViewport;
         try {
-            Rectangle2D bounds = pageViewport.getViewArea();
+            PageBoundaries boundaries = new PageBoundaries(
+                    pageViewport.getViewArea().getSize(), pageViewport.getForeignAttributes());
+            Rectangle bounds = boundaries.getCropBox();
+            Rectangle bleedBox = boundaries.getBleedBox();
             pageWidth = (int) Math.round(bounds.getWidth() / 1000f);
             pageHeight = (int) Math.round(bounds.getHeight() / 1000f);
 
@@ -287,11 +288,22 @@
                             + " (pageWidth " + pageWidth + ", pageHeight "
                             + pageHeight + ")");
 
-            double scaleX = scaleFactor 
-                * (25.4 / FopFactoryConfigurator.DEFAULT_TARGET_RESOLUTION) 
+            // set scale factor
+            double scaleX = scaleFactor;
+            double scaleY = scaleFactor;
+            String scale = (String) currentPageViewport.getForeignAttributes().get(
+                    PageScale.EXT_PAGE_SCALE);
+            Point2D scales = PageScale.getScale(scale);
+            if (scales != null) {
+                scaleX *= scales.getX();
+                scaleY *= scales.getY();
+            }
+          
+            scaleX = scaleX
+                * (25.4f / FopFactoryConfigurator.DEFAULT_TARGET_RESOLUTION)
                 / userAgent.getTargetPixelUnitToMillimeter();
-            double scaleY = scaleFactor
-                * (25.4 / FopFactoryConfigurator.DEFAULT_TARGET_RESOLUTION)
+            scaleY = scaleY
+                * (25.4f / FopFactoryConfigurator.DEFAULT_TARGET_RESOLUTION)
                 / userAgent.getTargetPixelUnitToMillimeter();
             int bitmapWidth = (int) ((pageWidth * scaleX) + 0.5);
             int bitmapHeight = (int) ((pageHeight * scaleY) + 0.5);
@@ -318,19 +330,26 @@
             // transform page based on scale factor supplied
             AffineTransform at = graphics.getTransform();
             at.scale(scaleX, scaleY);
+            at.translate(bounds.getMinX() / -1000f, bounds.getMinY() / -1000f);
             graphics.setTransform(at);
 
             // draw page frame
             if (!transparentPageBackground) {
                 graphics.setColor(Color.white);
-                graphics.fillRect(0, 0, pageWidth, pageHeight);
+                graphics.fillRect(
+                        (int)Math.round(bleedBox.getMinX() / 1000f),
+                        (int)Math.round(bleedBox.getMinY() / 1000f),
+                        (int)Math.round(bleedBox.getWidth() / 1000f),
+                        (int)Math.round(bleedBox.getHeight() / 1000f));
             }
+            /* why did we have this???
             graphics.setColor(Color.black);
             graphics.drawRect(-1, -1, pageWidth + 2, pageHeight + 2);
             graphics.drawLine(pageWidth + 2, 0, pageWidth + 2, pageHeight + 2);
             graphics.drawLine(pageWidth + 3, 1, pageWidth + 3, pageHeight + 3);
             graphics.drawLine(0, pageHeight + 2, pageWidth + 2, pageHeight + 2);
             graphics.drawLine(1, pageHeight + 3, pageWidth + 3, pageHeight + 3);
+            */
 
             state = new Java2DGraphicsState(graphics, this.fontInfo, at);
             try {
Index: src/java/org/apache/fop/render/pdf/PDFRenderer.java
===================================================================
--- src/java/org/apache/fop/render/pdf/PDFRenderer.java	(revision 830403)
+++ src/java/org/apache/fop/render/pdf/PDFRenderer.java	(working copy)
@@ -27,6 +27,7 @@
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
+import java.awt.geom.Rectangle2D.Double;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
@@ -41,6 +42,8 @@
 
 import org.apache.commons.io.IOUtils;
 
+import org.apache.fop.render.extensions.prepress.PageBoundaries;
+import org.apache.fop.render.extensions.prepress.PageScale;
 import org.apache.xmlgraphics.image.loader.ImageException;
 import org.apache.xmlgraphics.image.loader.ImageInfo;
 import org.apache.xmlgraphics.image.loader.ImageManager;
@@ -739,13 +742,38 @@
     private void setupPage(PageViewport page) {
         this.pdfResources = this.pdfDoc.getResources();
 
-        Rectangle2D bounds = page.getViewArea();
-        double w = bounds.getWidth();
-        double h = bounds.getHeight();
-        currentPage = this.pdfDoc.getFactory().makePage(
-            this.pdfResources,
-            (int) Math.round(w / 1000), (int) Math.round(h / 1000),
-            page.getPageIndex());
+        PageBoundaries boundaries = new PageBoundaries(page.getViewArea().getSize(), page.getForeignAttributes());
+
+        Rectangle trimBox = boundaries.getTrimBox();
+        Rectangle bleedBox = boundaries.getBleedBox();
+        Rectangle mediaBox = boundaries.getMediaBox();
+        Rectangle cropBox = boundaries.getCropBox();
+
+        // set scale attributes
+        double scaleX = 1;
+        double scaleY = 1;
+        String scale = (String) page.getForeignAttributes().get(
+                PageScale.EXT_PAGE_SCALE);
+        Point2D scales = PageScale.getScale(scale);
+        if (scales != null) {
+            scaleX = scales.getX();
+            scaleY = scales.getY();
+        }
+
+//        double w = bounds.getWidth();
+//        double h = bounds.getHeight();
+        this.currentPage = this.pdfDoc.getFactory().makePage(
+                this.pdfResources,
+                page.getPageIndex(),
+                toPointAndScale(mediaBox, scaleX, scaleY),
+                toPointAndScale(cropBox, scaleX, scaleY),
+                toPointAndScale(bleedBox, scaleX, scaleY),
+                toPointAndScale(trimBox, scaleX, scaleY));
+
+//        currentPage = this.pdfDoc.getFactory().makePage(
+//            this.pdfResources,
+//            (int) Math.round(w / 1000), (int) Math.round(h / 1000),
+//            page.getPageIndex());
         pageReferences.put(page.getKey(), currentPage.referencePDF());
         pvReferences.put(page.getKey(), page);
         
@@ -763,6 +791,13 @@
         //expressed in a more space-efficient way
         nums.put(page.getPageIndex(), dict);
     }
+
+    private Double toPointAndScale(Rectangle box, double scaleX, double scaleY) {
+        return new Rectangle2D.Double(box.getX() * scaleX / 1000,
+                box.getY() * scaleY / 1000,
+                box.getWidth() * scaleX / 1000,
+                box.getHeight() * scaleY / 1000);
+    }
     
     /**
      * This method creates a pdf stream for the current page
@@ -781,9 +816,18 @@
         }
         currentPageRef = currentPage.referencePDF();
 
-        Rectangle2D bounds = page.getViewArea();
-        double h = bounds.getHeight();
-        pageHeight = (int) h;
+        Rectangle bounds = page.getViewArea();
+        // set scale attributes
+        double scaleX = 1;
+        double scaleY = 1;
+        String scale = (String) page.getForeignAttributes().get(
+                PageScale.EXT_PAGE_SCALE);
+        Point2D scales = PageScale.getScale(scale);
+        if (scales != null) {
+            scaleX = scales.getX();
+            scaleY = scales.getY();
+        }
+        pageHeight = bounds.height;
 
         currentStream = this.pdfDoc.getFactory()
             .makeStream(PDFFilterList.CONTENT_FILTER, false);
@@ -791,7 +835,8 @@
         currentState = new PDFState();
         // Transform the PDF's default coordinate system (0,0 at lower left) to the PDFRenderer's
         AffineTransform basicPageTransform = new AffineTransform(1, 0, 0, -1, 0,
-                pageHeight / 1000f);
+                (scaleY * pageHeight) / 1000f);
+        basicPageTransform.scale(scaleX, scaleY);
         currentState.concatenate(basicPageTransform);
         currentStream.add(CTMHelper.toPDFString(basicPageTransform, false) + " cm\n");
         
Index: src/java/org/apache/fop/render/ps/PSRenderer.java
===================================================================
--- src/java/org/apache/fop/render/ps/PSRenderer.java	(revision 830403)
+++ src/java/org/apache/fop/render/ps/PSRenderer.java	(working copy)
@@ -1128,8 +1128,8 @@
                 {page.getPageNumberString(),
                  new Integer(this.currentPageNumber)});
 
-        double pageWidth = Math.round(page.getViewArea().getWidth()) / 1000f;
-        double pageHeight = Math.round(page.getViewArea().getHeight()) / 1000f;
+        double pageWidth = page.getViewArea().width / 1000f;
+        double pageHeight = page.getViewArea().height / 1000f;
         boolean rotate = false;
         List pageSizes = new java.util.ArrayList();
         if (this.autoRotateLandscape && (pageHeight < pageWidth)) {
Index: src/java/org/apache/fop/util/ConversionUtils.java
===================================================================
--- src/java/org/apache/fop/util/ConversionUtils.java	(revision 0)
+++ src/java/org/apache/fop/util/ConversionUtils.java	(revision 0)
@@ -0,0 +1,112 @@
+/*
+ * 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: ConversionUtils.java 746664 2009-02-22 12:40:44Z jeremias $ */
+
+package org.apache.fop.util;
+
+/**
+ * This class contains utility methods for conversions, like
+ * a java.lang.String to an array of int or double.
+ */
+public final class ConversionUtils {
+
+    /**
+     * Converts the given base <code>String</code> into
+     * an array of <code>int</code>, splitting the base along the
+     * given separator pattern.
+     * <em>Note: this method assumes the input is a string containing
+     * only decimal integers, signed or unsigned, that are parsable
+     * by <code>java.lang.Integer.parseInt(String)</code>. If this
+     * is not the case, the resulting <code>NumberFormatException</code>
+     * will have to be handled by the caller.</em>
+     *
+     * @param baseString    the base string
+     * @param separatorPattern  the pattern separating the integer values
+     *        (if this is <code>null</code>, the baseString is parsed as one
+     *         integer value)
+     * @return  an array of <code>int</code> whose size is equal to the number
+     *          values in the input string; <code>null</code> if this number
+     *          is equal to zero.
+     */
+    public static int[] toIntArray(String baseString, String separatorPattern) {
+
+        if (baseString == null || "".equals(baseString)) {
+            return null;
+        }
+
+        if (separatorPattern == null || "".equals(separatorPattern)) {
+            return new int[] {Integer.parseInt(baseString)};
+        }
+
+        String[] values = baseString.split(separatorPattern);
+        int numValues = values.length;
+        if (numValues == 0) {
+            return null;
+        }
+
+        int[] returnArray = new int[numValues];
+        for (int i = 0; i < numValues; ++i) {
+            returnArray[i] = Integer.parseInt(values[i]);
+        }
+        return returnArray;
+
+    }
+
+    /**
+     * Converts the given base <code>String</code> into
+     * an array of <code>double</code>, splitting the base along the
+     * given separator pattern.
+     * <em>Note: this method assumes the input is a string containing
+     * only decimal doubles, signed or unsigned, that are parsable
+     * by <code>java.lang.Double.parseDouble(String)</code>. If this
+     * is not the case, the resulting <code>NumberFormatException</code>
+     * will have to be handled by the caller.</em>
+     *
+     * @param baseString    the base string
+     * @param separatorPattern  the pattern separating the integer values
+     *        (if this is <code>null</code>, the baseString is parsed as one
+     *         double value)
+     * @return  an array of <code>double</code> whose size is equal to the number
+     *          values in the input string; <code>null</code> if this number
+     *          is equal to zero.
+     */
+    public static double[] toDoubleArray(String baseString, String separatorPattern) {
+
+        if (baseString == null || "".equals(baseString)) {
+            return null;
+        }
+
+        if (separatorPattern == null || "".equals(separatorPattern)) {
+            return new double[] {Double.parseDouble(baseString)};
+        }
+
+        String[] values = baseString.split(separatorPattern);
+        int numValues = values.length;
+        if (numValues == 0) {
+            return null;
+        }
+
+        double[] returnArray = new double[numValues];
+        for (int i = 0; i < numValues; ++i) {
+            returnArray[i] = Double.parseDouble(values[i]);
+        }
+        return returnArray;
+
+    }
+
+}
Index: src/java/org/apache/fop/util/XMLConstants.java
===================================================================
--- src/java/org/apache/fop/util/XMLConstants.java	(revision 0)
+++ src/java/org/apache/fop/util/XMLConstants.java	(revision 0)
@@ -0,0 +1,52 @@
+/*
+ * 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: XMLConstants.java 746664 2009-02-22 12:40:44Z jeremias $ */
+
+package org.apache.fop.util;
+
+
+/**
+ * A collection of constants for XML handling.
+ */
+public interface XMLConstants {
+
+    /** "CDATA" constant */
+    String CDATA = "CDATA";
+
+    /** XML namespace prefix */
+    String XML_PREFIX = "xml";
+    /** XML namespace URI */
+    String XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace";
+    /** xml:space attribute */
+    org.apache.xmlgraphics.util.QName XML_SPACE = new org.apache.xmlgraphics.util.QName(
+            XML_NAMESPACE, XML_PREFIX, "space");
+
+    /** XMLNS namespace prefix */
+    String XMLNS_PREFIX = "xmlns";
+    /** XMLNS namespace URI */
+    String XMLNS_NAMESPACE_URI = "http://www.w3.org/2000/xmlns/";
+
+    /** Namespace prefix for XLink */
+    String XLINK_PREFIX = "xlink";
+    /** XML namespace for XLink */
+    String XLINK_NAMESPACE = "http://www.w3.org/1999/xlink";
+    /** xlink:href attribute */
+    org.apache.xmlgraphics.util.QName XLINK_HREF = new org.apache.xmlgraphics.util.QName(
+            XLINK_NAMESPACE, XLINK_PREFIX, "href");
+
+}
Index: src/java/org/apache/fop/util/XMLUtil.java
===================================================================
--- src/java/org/apache/fop/util/XMLUtil.java	(revision 0)
+++ src/java/org/apache/fop/util/XMLUtil.java	(revision 0)
@@ -0,0 +1,173 @@
+/*
+ * 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: XMLUtil.java 820672 2009-10-01 14:48:27Z jeremias $ */
+
+package org.apache.fop.util;
+
+import java.awt.Rectangle;
+import java.awt.geom.Rectangle2D;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * A collection of utility method for XML handling.
+ */
+public class XMLUtil implements XMLConstants {
+
+    /**
+     * Returns an attribute value as a boolean value.
+     * @param attributes the Attributes object
+     * @param name the name of the attribute
+     * @param defaultValue the default value if the attribute is not specified
+     * @return the attribute value as a boolean
+     */
+    public static boolean getAttributeAsBoolean(Attributes attributes, String name,
+            boolean defaultValue) {
+        String s = attributes.getValue(name);
+        if (s == null) {
+            return defaultValue;
+        } else {
+            return Boolean.valueOf(s).booleanValue();
+        }
+    }
+
+    /**
+     * Returns an attribute value as a int value.
+     * @param attributes the Attributes object
+     * @param name the name of the attribute
+     * @param defaultValue the default value if the attribute is not specified
+     * @return the attribute value as an int
+     */
+    public static int getAttributeAsInt(Attributes attributes, String name,
+            int defaultValue) {
+        String s = attributes.getValue(name);
+        if (s == null) {
+            return defaultValue;
+        } else {
+            return Integer.parseInt(s);
+        }
+    }
+
+    /**
+     * Returns an attribute value as a int value.
+     * @param attributes the Attributes object
+     * @param name the name of the attribute
+     * @return the attribute value as an int
+     * @throws SAXException if the attribute is missing
+     */
+    public static int getAttributeAsInt(Attributes attributes, String name) throws SAXException {
+        String s = attributes.getValue(name);
+        if (s == null) {
+            throw new SAXException("Attribute '" + name + "' is missing");
+        } else {
+            return Integer.parseInt(s);
+        }
+    }
+
+    /**
+     * Returns an attribute value as a Integer value.
+     * @param attributes the Attributes object
+     * @param name the name of the attribute
+     * @return the attribute value as an Integer or null if the attribute is missing
+     */
+    public static Integer getAttributeAsInteger(Attributes attributes, String name) {
+        String s = attributes.getValue(name);
+        if (s == null) {
+            return null;
+        } else {
+            return new Integer(s);
+        }
+    }
+
+    /**
+     * Returns an attribute value as a Rectangle2D value. The string value is expected as 4
+     * double-precision numbers separated by whitespace.
+     * @param attributes the Attributes object
+     * @param name the name of the attribute
+     * @return the attribute value as an Rectangle2D
+     */
+    public static Rectangle2D getAttributeAsRectangle2D(Attributes attributes, String name) {
+        String s = attributes.getValue(name).trim();
+        double[] values = ConversionUtils.toDoubleArray(s, "\\s");
+        if (values.length != 4) {
+            throw new IllegalArgumentException("Rectangle must consist of 4 double values!");
+        }
+        return new Rectangle2D.Double(values[0], values[1], values[2], values[3]);
+    }
+
+    /**
+     * Returns an attribute value as a Rectangle value. The string value is expected as 4
+     * integer numbers separated by whitespace.
+     * @param attributes the Attributes object
+     * @param name the name of the attribute
+     * @return the attribute value as an Rectangle
+     */
+    public static Rectangle getAttributeAsRectangle(Attributes attributes, String name) {
+        String s = attributes.getValue(name);
+        if (s == null) {
+            return null;
+        }
+        int[] values = ConversionUtils.toIntArray(s.trim(), "\\s");
+        if (values.length != 4) {
+            throw new IllegalArgumentException("Rectangle must consist of 4 int values!");
+        }
+        return new Rectangle(values[0], values[1], values[2], values[3]);
+    }
+
+    /**
+     * Returns an attribute value as a integer array. The string value is expected as 4
+     * integer numbers separated by whitespace.
+     * @param attributes the Attributes object
+     * @param name the name of the attribute
+     * @return the attribute value as an int array
+     */
+    public static int[] getAttributeAsIntArray(Attributes attributes, String name) {
+        String s = attributes.getValue(name);
+        if (s == null) {
+            return null;
+        } else {
+            return ConversionUtils.toIntArray(s.trim(), "\\s");
+        }
+    }
+
+    /**
+     * Adds an attribute to a given {@link AttributesImpl} instance.
+     * @param atts the attributes collection
+     * @param attribute the attribute to add
+     * @param value the attribute's CDATA value
+     */
+    public static void addAttribute(AttributesImpl atts,
+            org.apache.xmlgraphics.util.QName attribute, String value) {
+        atts.addAttribute(attribute.getNamespaceURI(),
+                attribute.getLocalName(), attribute.getQName(), XMLUtil.CDATA, value);
+    }
+
+    /**
+     * Adds an attribute to a given {@link AttributesImpl} instance. The attribute will be
+     * added in the default namespace.
+     * @param atts the attributes collection
+     * @param localName the local name of the attribute
+     * @param value the attribute's CDATA value
+     */
+    public static void addAttribute(AttributesImpl atts, String localName, String value) {
+        atts.addAttribute("", localName, localName, XMLUtil.CDATA, value);
+    }
+
+}
Index: test/java/org/apache/fop/render/extensions/prepress/PageScaleTest.java
===================================================================
--- test/java/org/apache/fop/render/extensions/prepress/PageScaleTest.java	(revision 0)
+++ test/java/org/apache/fop/render/extensions/prepress/PageScaleTest.java	(revision 0)
@@ -0,0 +1,79 @@
+/*
+ * 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: PageScaleTest.java 803440 2009-08-12 10:46:39Z vhennebert $ */
+
+package org.apache.fop.render.extensions.prepress;
+
+import java.awt.geom.Point2D;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for the fox:scale extension property.
+ */
+public class PageScaleTest extends TestCase {
+
+    /**
+     * Default constructor.
+     */
+    public PageScaleTest() {
+        super();
+    }
+
+    /**
+     * Creates a test case with the given name.
+     *
+     * @param name name for the test case
+     */
+    public PageScaleTest(String name) {
+        super(name);
+    }
+
+    /** 1 value is used for both x and y. */
+    public void testScale1() {
+        Point2D res = PageScale.getScale(".5");
+        assertEquals(0.5, res.getX(), 0.0);
+        assertEquals(0.5, res.getY(), 0.0);
+    }
+
+    /** Two values, used resp. for x and y. */
+    public void testScale2() {
+        Point2D res = PageScale.getScale("1. \t \n 1.2");
+        assertEquals(1.0, res.getX(), 0.0);
+        assertEquals(1.2, res.getY(), 0.0);
+    }
+
+    /** Scale must not contain units. */
+    public void testScaleFail() {
+        try {
+            PageScale.getScale("0.5mm 0.5cm");
+            fail("Expected IllegalArgumentException. Scale shouldn't contain units");
+        } catch (IllegalArgumentException iae) {
+            // Good!
+        }
+    }
+
+    /** @{code null} is returned when scale is unspecified. */
+    public void testScaleNull() {
+        Point2D res = PageScale.getScale(null);
+        assertNull("Result should be null", res);
+        res = PageScale.getScale("");
+        assertNull("Result should be null", res);
+    }
+
+}
Index: test/java/org/apache/fop/StandardTestSuite.java
===================================================================
--- test/java/org/apache/fop/StandardTestSuite.java	(revision 830403)
+++ test/java/org/apache/fop/StandardTestSuite.java	(working copy)
@@ -28,6 +28,8 @@
 import org.apache.fop.render.pdf.PDFEncodingTestCase;
 import org.apache.fop.render.pdf.PDFsRGBSettingsTestCase;
 import org.apache.fop.render.rtf.RichTextFormatTestSuite;
+import org.apache.fop.render.extensions.prepress.PageBoundariesTest;
+import org.apache.fop.render.extensions.prepress.PageScaleTest;
 
 /**
  * Test suite for basic functionality of FOP.
@@ -50,6 +52,8 @@
         suite.addTest(new TestSuite(PDFsRGBSettingsTestCase.class));
         suite.addTest(new TestSuite(TrueTypeAnsiTestCase.class));
         suite.addTest(RichTextFormatTestSuite.suite());
+        suite.addTest(new TestSuite(PageBoundariesTest.class));
+        suite.addTest(new TestSuite(PageScaleTest.class));
         //$JUnit-END$
         return suite;
     }
