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.

PDFPageLabels.java 7.2KB

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