Line 0
Link Here
|
|
|
1 |
package org.apache.fop.render.extensions.prepress; |
2 |
|
3 |
import java.awt.*; |
4 |
import java.text.MessageFormat; |
5 |
import java.util.Map; |
6 |
import java.util.regex.Matcher; |
7 |
import java.util.regex.Pattern; |
8 |
|
9 |
import org.apache.fop.util.QName; |
10 |
|
11 |
import org.apache.fop.fo.extensions.ExtensionElementMapping; |
12 |
import org.apache.fop.fo.properties.FixedLength; |
13 |
|
14 |
|
15 |
/** |
16 |
* This class is used to calculate the effective boundaries of a page including special-purpose |
17 |
* boxes used in prepress. These are specified using extension attributes: |
18 |
* bleedBox, trimBox and cropBox. The semantics are further described on the website. |
19 |
*/ |
20 |
public class PageBoundaries { |
21 |
|
22 |
/** |
23 |
* The extension attribute for calculating the PDF BleedBox area - specifies the bleed width. |
24 |
*/ |
25 |
public static final QName EXT_BLEED |
26 |
= new QName(ExtensionElementMapping.URI, null, "bleed"); |
27 |
|
28 |
/** |
29 |
* The extension attribute for the PDF CropBox area. |
30 |
*/ |
31 |
public static final QName EXT_CROP_OFFSET |
32 |
= new QName(ExtensionElementMapping.URI, null, "crop-offset"); |
33 |
|
34 |
/** |
35 |
* The extension attribute for the PDF CropBox area. |
36 |
*/ |
37 |
public static final QName EXT_CROP_BOX |
38 |
= new QName(ExtensionElementMapping.URI, null, "crop-box"); |
39 |
|
40 |
|
41 |
private static final Pattern SIZE_UNIT_PATTERN |
42 |
= Pattern.compile("^(-?\\d*\\.?\\d*)(px|in|cm|mm|pt|pc|mpt)$"); |
43 |
|
44 |
private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s+"); |
45 |
|
46 |
private Rectangle trimBox; |
47 |
private Rectangle bleedBox; |
48 |
private Rectangle mediaBox; |
49 |
private Rectangle cropBox; |
50 |
|
51 |
/** |
52 |
* Creates a new instance. |
53 |
* @param pageSize the page size (in mpt) defined by the simple-page-master. |
54 |
* @param bleed the bleed value (raw value as given in the property value) |
55 |
* @param cropOffset the crop-offset value (raw value as given in the property value) |
56 |
* @param cropBoxSelector the crop-box, valid values: (trim-box|bleed-box|media-box) |
57 |
*/ |
58 |
public PageBoundaries(Dimension pageSize, String bleed, String cropOffset, |
59 |
String cropBoxSelector) { |
60 |
calculate(pageSize, bleed, cropOffset, cropBoxSelector); |
61 |
} |
62 |
|
63 |
/** |
64 |
* Creates a new instance. |
65 |
* @param pageSize the page size (in mpt) defined by the simple-page-master. |
66 |
* @param foreignAttributes the foreign attributes for the page |
67 |
* (used to extract the extension attribute values) |
68 |
*/ |
69 |
public PageBoundaries(Dimension pageSize, Map foreignAttributes) { |
70 |
String bleed = (String)foreignAttributes.get(EXT_BLEED); |
71 |
String cropOffset = (String)foreignAttributes.get(EXT_CROP_OFFSET); |
72 |
String cropBoxSelector = (String)foreignAttributes.get(EXT_CROP_BOX); |
73 |
calculate(pageSize, bleed, cropOffset, cropBoxSelector); |
74 |
} |
75 |
|
76 |
private void calculate(Dimension pageSize, String bleed, String cropOffset, |
77 |
String cropBoxSelector) { |
78 |
this.trimBox = new Rectangle(pageSize); |
79 |
this.bleedBox = getBleedBoxRectangle(this.trimBox, bleed); |
80 |
Rectangle cropMarksBox = getCropMarksAreaRectangle(trimBox, cropOffset); |
81 |
|
82 |
//MediaBox includes all of the following three rectangles |
83 |
this.mediaBox = new Rectangle(); |
84 |
this.mediaBox.add(this.trimBox); |
85 |
this.mediaBox.add(this.bleedBox); |
86 |
this.mediaBox.add(cropMarksBox); |
87 |
|
88 |
if ("trim-box".equals(cropBoxSelector)) { |
89 |
this.cropBox = this.trimBox; |
90 |
} else if ("bleed-box".equals(cropBoxSelector)) { |
91 |
this.cropBox = this.bleedBox; |
92 |
} else if ("media-box".equals(cropBoxSelector) |
93 |
|| cropBoxSelector == null |
94 |
|| "".equals(cropBoxSelector)) { |
95 |
this.cropBox = this.mediaBox; |
96 |
} else { |
97 |
final String err = "The crop-box has invalid value: {0}, " |
98 |
+ "possible values of crop-box: (trim-box|bleed-box|media-box)"; |
99 |
throw new IllegalArgumentException(MessageFormat.format(err, |
100 |
new Object[]{cropBoxSelector})); |
101 |
} |
102 |
} |
103 |
|
104 |
/** |
105 |
* Returns the trim box for the page. This is equal to the page size given in XSL-FO. |
106 |
* After production the printed media is trimmed to this rectangle. |
107 |
* @return the trim box |
108 |
*/ |
109 |
public Rectangle getTrimBox() { |
110 |
return this.trimBox; |
111 |
} |
112 |
|
113 |
/** |
114 |
* Returns the bleed box for the page. |
115 |
* @return the bleed box |
116 |
*/ |
117 |
public Rectangle getBleedBox() { |
118 |
return this.bleedBox; |
119 |
} |
120 |
|
121 |
/** |
122 |
* Returns the media box for the page. |
123 |
* @return the media box |
124 |
*/ |
125 |
public Rectangle getMediaBox() { |
126 |
return this.mediaBox; |
127 |
} |
128 |
|
129 |
/** |
130 |
* Returns the crop box for the page. The crop box is used by Adobe Acrobat to select which |
131 |
* parts of the document shall be displayed and it also defines the rectangle to which a |
132 |
* RIP will clip the document. For bitmap output, this defines the size of the bitmap. |
133 |
* @return the crop box |
134 |
*/ |
135 |
public Rectangle getCropBox() { |
136 |
return this.cropBox; |
137 |
} |
138 |
|
139 |
/** |
140 |
* The BleedBox is calculated by expanding the TrimBox by the bleed widths. |
141 |
* |
142 |
* @param trimBox the TrimBox rectangle |
143 |
* @param bleed the given bleed widths |
144 |
* @return the calculated BleedBox rectangle |
145 |
*/ |
146 |
private static Rectangle getBleedBoxRectangle(Rectangle trimBox, String bleed) { |
147 |
return getRectangleUsingOffset(trimBox, bleed); |
148 |
} |
149 |
|
150 |
/** |
151 |
* The MediaBox is calculated by expanding the TrimBox by the crop offsets. |
152 |
* |
153 |
* @param trimBox the TrimBox rectangle |
154 |
* @param cropOffsets the given crop offsets |
155 |
* @return the calculated MediaBox rectangle |
156 |
*/ |
157 |
private static Rectangle getCropMarksAreaRectangle(Rectangle trimBox, String cropOffsets) { |
158 |
return getRectangleUsingOffset(trimBox, cropOffsets); |
159 |
} |
160 |
|
161 |
private static Rectangle getRectangleUsingOffset(Rectangle originalRect, String offset) { |
162 |
if (offset == null || "".equals(offset) || originalRect == null) { |
163 |
return originalRect; |
164 |
} |
165 |
|
166 |
String[] offsets = WHITESPACE_PATTERN.split(offset); |
167 |
int[] coords = new int[4]; // top, right, bottom, left |
168 |
switch (offsets.length) { |
169 |
case 1: |
170 |
coords[0] = getLengthIntValue(offsets[0]); |
171 |
coords[1] = coords[0]; |
172 |
coords[2] = coords[0]; |
173 |
coords[3] = coords[0]; |
174 |
break; |
175 |
case 2: |
176 |
coords[0] = getLengthIntValue(offsets[0]); |
177 |
coords[1] = getLengthIntValue(offsets[1]); |
178 |
coords[2] = coords[0]; |
179 |
coords[3] = coords[1]; |
180 |
break; |
181 |
case 3: |
182 |
coords[0] = getLengthIntValue(offsets[0]); |
183 |
coords[1] = getLengthIntValue(offsets[1]); |
184 |
coords[2] = getLengthIntValue(offsets[2]); |
185 |
coords[3] = coords[1]; |
186 |
break; |
187 |
case 4: |
188 |
coords[0] = getLengthIntValue(offsets[0]); |
189 |
coords[1] = getLengthIntValue(offsets[1]); |
190 |
coords[2] = getLengthIntValue(offsets[2]); |
191 |
coords[3] = getLengthIntValue(offsets[3]); |
192 |
break; |
193 |
default: |
194 |
// TODO throw appropriate exception that can be caught by the event |
195 |
// notification mechanism |
196 |
throw new IllegalArgumentException("Too many arguments"); |
197 |
} |
198 |
return new Rectangle(originalRect.x - coords[3], |
199 |
originalRect.y - coords[2], |
200 |
originalRect.width + coords[3] + coords[1], |
201 |
originalRect.height + coords[0] + coords[2]); |
202 |
} |
203 |
|
204 |
private static int getLengthIntValue(final String length) { |
205 |
final String err = "Incorrect length value: {0}"; |
206 |
Matcher m = SIZE_UNIT_PATTERN.matcher(length); |
207 |
|
208 |
if (m.find()) { |
209 |
return FixedLength.getInstance(Double.parseDouble(m.group(1)), |
210 |
m.group(2)).getLength().getValue(); |
211 |
} else { |
212 |
throw new IllegalArgumentException(MessageFormat.format(err, new Object[]{length})); |
213 |
} |
214 |
} |
215 |
|
216 |
} |