You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PageBoundaries.java 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.render.extensions.prepress;
  19. import java.awt.Dimension;
  20. import java.awt.Rectangle;
  21. import java.text.MessageFormat;
  22. import java.util.Map;
  23. import java.util.regex.Matcher;
  24. import java.util.regex.Pattern;
  25. import org.apache.xmlgraphics.util.QName;
  26. import org.apache.fop.fo.extensions.ExtensionElementMapping;
  27. import org.apache.fop.fo.properties.FixedLength;
  28. /**
  29. * This class is used to calculate the effective boundaries of a page including special-purpose
  30. * boxes used in prepress. These are specified using extension attributes:
  31. * bleedBox, trimBox and cropBox. The semantics are further described on the website.
  32. */
  33. public class PageBoundaries {
  34. /**
  35. * The extension attribute for calculating the PDF BleedBox area - specifies the bleed width.
  36. */
  37. public static final QName EXT_BLEED
  38. = new QName(ExtensionElementMapping.URI, null, "bleed");
  39. /**
  40. * The extension attribute for the PDF CropBox area.
  41. */
  42. public static final QName EXT_CROP_OFFSET
  43. = new QName(ExtensionElementMapping.URI, null, "crop-offset");
  44. /**
  45. * The extension attribute for the PDF CropBox area.
  46. */
  47. public static final QName EXT_CROP_BOX
  48. = new QName(ExtensionElementMapping.URI, null, "crop-box");
  49. private static final Pattern SIZE_UNIT_PATTERN
  50. = Pattern.compile("^(-?\\d*\\.?\\d*)(px|in|cm|mm|pt|pc|mpt)$");
  51. private Rectangle trimBox;
  52. private Rectangle bleedBox;
  53. private Rectangle mediaBox;
  54. private Rectangle cropBox;
  55. /**
  56. * Creates a new instance.
  57. * @param pageSize the page size (in mpt) defined by the simple-page-master.
  58. * @param bleed the bleed value (raw value as given in the property value)
  59. * @param cropOffset the crop-offset value (raw value as given in the property value)
  60. * @param cropBoxSelector the crop-box, valid values: (trim-box|bleed-box|media-box)
  61. */
  62. public PageBoundaries(Dimension pageSize, String bleed, String cropOffset,
  63. String cropBoxSelector) {
  64. calculate(pageSize, bleed, cropOffset, cropBoxSelector);
  65. }
  66. /**
  67. * Creates a new instance.
  68. * @param pageSize the page size (in mpt) defined by the simple-page-master.
  69. * @param foreignAttributes the foreign attributes for the page
  70. * (used to extract the extension attribute values)
  71. */
  72. public PageBoundaries(Dimension pageSize, Map foreignAttributes) {
  73. String bleed = (String)foreignAttributes.get(EXT_BLEED);
  74. String cropOffset = (String)foreignAttributes.get(EXT_CROP_OFFSET);
  75. String cropBoxSelector = (String)foreignAttributes.get(EXT_CROP_BOX);
  76. calculate(pageSize, bleed, cropOffset, cropBoxSelector);
  77. }
  78. private void calculate(Dimension pageSize, String bleed, String cropOffset,
  79. String cropBoxSelector) {
  80. this.trimBox = new Rectangle(pageSize);
  81. this.bleedBox = getBleedBoxRectangle(this.trimBox, bleed);
  82. Rectangle cropMarksBox = getCropMarksAreaRectangle(trimBox, cropOffset);
  83. //MediaBox includes all of the following three rectangles
  84. this.mediaBox = new Rectangle();
  85. this.mediaBox.add(this.trimBox);
  86. this.mediaBox.add(this.bleedBox);
  87. this.mediaBox.add(cropMarksBox);
  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. * Returns the trim box for the page. This is equal to the page size given in XSL-FO.
  105. * After production the printed media is trimmed to this rectangle.
  106. * @return the trim box
  107. */
  108. public Rectangle getTrimBox() {
  109. return this.trimBox;
  110. }
  111. /**
  112. * Returns the bleed box for the page.
  113. * @return the bleed box
  114. */
  115. public Rectangle getBleedBox() {
  116. return this.bleedBox;
  117. }
  118. /**
  119. * Returns the media box for the page.
  120. * @return the media box
  121. */
  122. public Rectangle getMediaBox() {
  123. return this.mediaBox;
  124. }
  125. /**
  126. * Returns the crop box for the page. The crop box is used by Adobe Acrobat to select which
  127. * parts of the document shall be displayed and it also defines the rectangle to which a
  128. * RIP will clip the document. For bitmap output, this defines the size of the size of
  129. * the bitmap.
  130. * @return the crop box
  131. */
  132. public Rectangle getCropBox() {
  133. return this.cropBox;
  134. }
  135. /**
  136. * The BleedBox is calculated by expanding the TrimBox by the bleed widths.
  137. *
  138. * @param trimBox the TrimBox rectangle
  139. * @param bleed the given bleed widths
  140. * @return the calculated BleedBox rectangle
  141. */
  142. public static Rectangle getBleedBoxRectangle(Rectangle trimBox, String bleed) {
  143. return getRectagleUsingOffset(trimBox, bleed);
  144. }
  145. /**
  146. * The MediaBox is calculated by expanding the TrimBox by the crop offsets.
  147. *
  148. * @param trimBox the TrimBox rectangle
  149. * @param cropOffsets the given crop offsets
  150. * @return the calculated MediaBox rectangle
  151. */
  152. public static Rectangle getCropMarksAreaRectangle(Rectangle trimBox, String cropOffsets) {
  153. return getRectagleUsingOffset(trimBox, cropOffsets);
  154. }
  155. private static Rectangle getRectagleUsingOffset(Rectangle originalRect, String offset) {
  156. if (offset == null || "".equals(offset) || originalRect == null) {
  157. return originalRect;
  158. }
  159. String[] bleeds = offset.split(" ");
  160. int[] coords = new int[4]; // top, right, bottom, left
  161. if (bleeds.length == 1) {
  162. coords[0] = getLengthIntValue(bleeds[0]);
  163. coords[1] = coords[0];
  164. coords[2] = coords[0];
  165. coords[3] = coords[0];
  166. } else if (bleeds.length == 2) {
  167. coords[0] = getLengthIntValue(bleeds[0]);
  168. coords[2] = coords[0];
  169. coords[1] = getLengthIntValue(bleeds[1]);
  170. coords[3] = coords[1];
  171. } else if (bleeds.length == 3) {
  172. coords[0] = getLengthIntValue(bleeds[0]);
  173. coords[1] = getLengthIntValue(bleeds[1]);
  174. coords[3] = coords[1];
  175. coords[2] = getLengthIntValue(bleeds[2]);
  176. } else if (bleeds.length == 4) {
  177. coords[0] = getLengthIntValue(bleeds[0]);
  178. coords[1] = getLengthIntValue(bleeds[1]);
  179. coords[2] = getLengthIntValue(bleeds[2]);
  180. coords[3] = getLengthIntValue(bleeds[3]);
  181. }
  182. return new Rectangle((int) (originalRect.getX() - coords[3]),
  183. (int) (originalRect.getY() - coords[0]),
  184. (int) (originalRect.getWidth() + coords[3] + coords[1]),
  185. (int) (originalRect.getHeight() + coords[0] + coords[2]));
  186. }
  187. private static int getLengthIntValue(final String length) {
  188. final String err = "Incorrect length value: {0}";
  189. Matcher m = SIZE_UNIT_PATTERN.matcher(length);
  190. if (m.find()) {
  191. return FixedLength.getInstance(Double.parseDouble(m.group(1)),
  192. m.group(2)).getLength().getValue();
  193. } else {
  194. throw new IllegalArgumentException(MessageFormat.format(err, new Object[]{length}));
  195. }
  196. }
  197. }