Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

PDFPageLabels.java 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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.pdf;
  19. import java.util.regex.Pattern;
  20. /**
  21. * Class representing a PDF /PageLabels dictionary.
  22. */
  23. public class PDFPageLabels extends PDFNumberTreeNode {
  24. // TODO: maybe merge these constants with similar ones in PageNumberGenerator
  25. private static final int DECIMAL = 1; // '0*1'
  26. private static final int LOWER_ALPHA = 2; // 'a'
  27. private static final int UPPER_ALPHA = 3; // 'A'
  28. private static final int LOWER_ROMAN = 4; // 'i'
  29. private static final int UPPER_ROMAN = 5; // 'I'
  30. private static final int PREFIX = 6;
  31. private static final PDFName S_D = new PDFName("D");
  32. private static final PDFName S_UR = new PDFName("R");
  33. private static final PDFName S_LR = new PDFName("r");
  34. private static final PDFName S_UA = new PDFName("A");
  35. private static final PDFName S_LA = new PDFName("a");
  36. private static final Pattern MATCH_DECIMAL = Pattern.compile("\\d+");
  37. private static final Pattern MATCH_ROMAN = Pattern.compile(
  38. "^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$", Pattern.CASE_INSENSITIVE);
  39. private static final Pattern MATCH_LETTER = Pattern.compile("^[a-zA-Z]$");
  40. private int lastPageLabelType;
  41. private int lastPageNumber;
  42. private String lastZeroPaddingPrefix = "";
  43. /**
  44. * Create the /PageLabels dictionary
  45. */
  46. public PDFPageLabels() {
  47. super();
  48. }
  49. /**
  50. * Returns the Nums object
  51. * @return the Nums object (an empty PDFNumsArray for the "/Nums" entry is created
  52. * if it doesn't exist)
  53. */
  54. public PDFNumsArray getNums() {
  55. PDFNumsArray nums = super.getNums();
  56. if (nums == null) {
  57. nums = new PDFNumsArray(this);
  58. setNums(nums);
  59. }
  60. return nums;
  61. }
  62. /**
  63. * Adds a new entry, if necessary, to the /PageLabels dictionary.
  64. * @param index the page index (0 for page 1)
  65. * @param pageLabel the page number as a string
  66. */
  67. public void addPageLabel(int index, String pageLabel) {
  68. boolean addNewPageLabel = false;
  69. String padding = "000000";
  70. int currentPageNumber = 0;
  71. int currentPageLabelType = 0;
  72. String currentZeroPaddingPrefix = "";
  73. if (MATCH_DECIMAL.matcher(pageLabel).matches()) {
  74. // since an integer is the most common case we start with that
  75. currentPageLabelType = DECIMAL;
  76. currentPageNumber = Integer.parseInt(pageLabel);
  77. int zeroPadding = 0;
  78. if (pageLabel.startsWith("0")) {
  79. while (pageLabel.charAt(zeroPadding) == '0') {
  80. zeroPadding++;
  81. }
  82. currentZeroPaddingPrefix = padding.substring(0, zeroPadding);
  83. if (currentZeroPaddingPrefix.length() != lastZeroPaddingPrefix.length()) {
  84. addNewPageLabel = true;
  85. }
  86. } else {
  87. if (lastZeroPaddingPrefix.length() != 0) {
  88. addNewPageLabel = true;
  89. }
  90. }
  91. } else if (MATCH_ROMAN.matcher(pageLabel).matches()) {
  92. if (pageLabel.toLowerCase().equals(pageLabel)) {
  93. currentPageLabelType = LOWER_ROMAN;
  94. } else {
  95. currentPageLabelType = UPPER_ROMAN;
  96. }
  97. currentPageNumber = romanToArabic(pageLabel);
  98. } else if (MATCH_LETTER.matcher(pageLabel).matches()) {
  99. if (pageLabel.toLowerCase().equals(pageLabel)) {
  100. currentPageLabelType = LOWER_ALPHA;
  101. } else {
  102. currentPageLabelType = UPPER_ALPHA;
  103. }
  104. currentPageNumber = alphabeticToArabic(pageLabel);
  105. } else {
  106. // alphabetic numbering in XSL_FO and labelling in PDF are different after AA (AB versus BB)
  107. // we will use the /P (prefix) label in that case
  108. currentPageLabelType = PREFIX;
  109. addNewPageLabel = true;
  110. }
  111. if (lastPageLabelType != currentPageLabelType) {
  112. addNewPageLabel = true;
  113. }
  114. if (lastPageNumber != currentPageNumber - 1) {
  115. addNewPageLabel = true;
  116. }
  117. if (addNewPageLabel) {
  118. PDFNumsArray nums = getNums();
  119. PDFDictionary dict = new PDFDictionary(nums);
  120. PDFName pdfName = null;
  121. switch (currentPageLabelType) {
  122. case PREFIX:
  123. dict.put("P", pageLabel);
  124. break;
  125. default:
  126. switch (currentPageLabelType) {
  127. case DECIMAL:
  128. pdfName = S_D;
  129. if (currentZeroPaddingPrefix.length() != 0) {
  130. dict.put("P", currentZeroPaddingPrefix);
  131. }
  132. break;
  133. case LOWER_ROMAN:
  134. pdfName = S_LR;
  135. break;
  136. case UPPER_ROMAN:
  137. pdfName = S_UR;
  138. break;
  139. case LOWER_ALPHA:
  140. pdfName = S_LA;
  141. break;
  142. case UPPER_ALPHA:
  143. pdfName = S_UA;
  144. break;
  145. default:
  146. }
  147. dict.put("S", pdfName);
  148. if (currentPageNumber != 1) {
  149. dict.put("St", currentPageNumber);
  150. }
  151. }
  152. nums.put(index, dict);
  153. }
  154. lastPageLabelType = currentPageLabelType;
  155. lastPageNumber = currentPageNumber;
  156. lastZeroPaddingPrefix = currentZeroPaddingPrefix;
  157. }
  158. private int romanToArabic(String roman) {
  159. int arabic = 0;
  160. int previousValue = 0;
  161. int newValue = 0;
  162. String upperRoman = roman.toUpperCase();
  163. for (int i = 0; i < upperRoman.length(); i++) {
  164. char romanDigit = upperRoman.charAt(i);
  165. switch (romanDigit) {
  166. case 'I':
  167. newValue = 1;
  168. break;
  169. case 'V':
  170. newValue = 5;
  171. break;
  172. case 'X':
  173. newValue = 10;
  174. break;
  175. case 'L':
  176. newValue = 50;
  177. break;
  178. case 'C':
  179. newValue = 100;
  180. break;
  181. case 'D':
  182. newValue = 500;
  183. break;
  184. case 'M':
  185. newValue = 1000;
  186. break;
  187. default:
  188. }
  189. if (previousValue < newValue) {
  190. arabic -= previousValue;
  191. } else {
  192. arabic += previousValue;
  193. }
  194. previousValue = newValue;
  195. }
  196. arabic += previousValue;
  197. return arabic;
  198. }
  199. private int alphabeticToArabic(String alpha) {
  200. int arabic = 0;
  201. if (alpha.length() > 1) {
  202. // this should never happen
  203. return arabic;
  204. }
  205. String lowerAlpha = alpha.toLowerCase();
  206. arabic = (lowerAlpha.charAt(0) - 'a' + 1);
  207. return arabic;
  208. }
  209. }