
org.apache.fop.render.extensions.prepress.PageBoundaries Maven / Gradle / Ivy
/*
* 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: PageBoundaries.java 802752 2009-08-10 11:16:45Z vhennebert $ */
package org.apache.fop.render.extensions.prepress;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.text.MessageFormat;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.xmlgraphics.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[0],
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}));
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy