--- 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");
--- 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;
}
--- 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);
}
--- 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);
}
--- 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
--- 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);
--- 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}));
+ }
+ }
+
+}
--- 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);
+ }
+}
--- 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 {
--- 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");
--- 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)) {
--- 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 String
into
+ * an array of int
, splitting the base along the
+ * given separator pattern.
+ * Note: this method assumes the input is a string containing
+ * only decimal integers, signed or unsigned, that are parsable
+ * by java.lang.Integer.parseInt(String)
. If this
+ * is not the case, the resulting NumberFormatException
+ * will have to be handled by the caller.
+ *
+ * @param baseString the base string
+ * @param separatorPattern the pattern separating the integer values
+ * (if this is null
, the baseString is parsed as one
+ * integer value)
+ * @return an array of int
whose size is equal to the number
+ * values in the input string; null
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 String
into
+ * an array of double
, splitting the base along the
+ * given separator pattern.
+ * Note: this method assumes the input is a string containing
+ * only decimal doubles, signed or unsigned, that are parsable
+ * by java.lang.Double.parseDouble(String)
. If this
+ * is not the case, the resulting NumberFormatException
+ * will have to be handled by the caller.
+ *
+ * @param baseString the base string
+ * @param separatorPattern the pattern separating the integer values
+ * (if this is null
, the baseString is parsed as one
+ * double value)
+ * @return an array of double
whose size is equal to the number
+ * values in the input string; null
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;
+
+ }
+
+}
--- 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");
+
+}
--- 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);
+ }
+
+}
--- 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);
+ }
+
+}
--- 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;
}