<Method name="getExternalAction"/>
<Bug pattern="DM_CONVERT_CASE"/>
</Match>
+ <Match>
+ <Class name="org.apache.fop.pdf.PDFPageLabels"/>
+ <Or>
+ <Method name="addPageLabel"/>
+ <Method name="alphabeticToArabic"/>
+ <Method name="romanToArabic"/>
+ </Or>
+ <Bug pattern="DM_CONVERT_CASE"/>
+ </Match>
<Match>
<Class name="org.apache.fop.render.bitmap.MultiFileRenderingUtil"/>
<Method name="<init>"/>
package org.apache.fop.pdf;
+import java.util.regex.Pattern;
+
/**
* Class representing a PDF /PageLabels dictionary.
*/
public class PDFPageLabels extends PDFNumberTreeNode {
+ // TODO: maybe merge these constants with similar ones in PageNumberGenerator
+ private static final int DECIMAL = 1; // '0*1'
+ private static final int LOWER_ALPHA = 2; // 'a'
+ private static final int UPPER_ALPHA = 3; // 'A'
+ private static final int LOWER_ROMAN = 4; // 'i'
+ private static final int UPPER_ROMAN = 5; // 'I'
+ private static final int PREFIX = 6;
+
+ private static final PDFName S_D = new PDFName("D");
+ private static final PDFName S_UR = new PDFName("R");
+ private static final PDFName S_LR = new PDFName("r");
+ private static final PDFName S_UA = new PDFName("A");
+ private static final PDFName S_LA = new PDFName("a");
+
+ private static final Pattern MATCH_DECIMAL = Pattern.compile("\\d+");
+ private static final Pattern MATCH_ROMAN = Pattern.compile(
+ "^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$", Pattern.CASE_INSENSITIVE);
+ private static final Pattern MATCH_LETTER = Pattern.compile("^[a-zA-Z]$");
+
+ private int lastPageLabelType;
+ private int lastPageNumber;
+ private String lastZeroPaddingPrefix = "";
+
/**
* Create the /PageLabels dictionary
*/
return nums;
}
+ /**
+ * Adds a new entry, if necessary, to the /PageLabels dictionary.
+ * @param index the page index (0 for page 1)
+ * @param pageLabel the page number as a string
+ */
+ public void addPageLabel(int index, String pageLabel) {
+ boolean addNewPageLabel = false;
+ String padding = "000000";
+ int currentPageNumber = 0;
+ int currentPageLabelType = 0;
+ String currentZeroPaddingPrefix = "";
+ if (MATCH_DECIMAL.matcher(pageLabel).matches()) {
+ // since an integer is the most common case we start with that
+ currentPageLabelType = DECIMAL;
+ currentPageNumber = Integer.parseInt(pageLabel);
+ int zeroPadding = 0;
+ if (pageLabel.startsWith("0")) {
+ while (pageLabel.charAt(zeroPadding) == '0') {
+ zeroPadding++;
+ }
+ currentZeroPaddingPrefix = padding.substring(0, zeroPadding);
+ if (currentZeroPaddingPrefix.length() != lastZeroPaddingPrefix.length()) {
+ addNewPageLabel = true;
+ }
+ } else {
+ if (lastZeroPaddingPrefix.length() != 0) {
+ addNewPageLabel = true;
+ }
+ }
+ } else if (MATCH_ROMAN.matcher(pageLabel).matches()) {
+ if (pageLabel.toLowerCase().equals(pageLabel)) {
+ currentPageLabelType = LOWER_ROMAN;
+ } else {
+ currentPageLabelType = UPPER_ROMAN;
+ }
+ currentPageNumber = romanToArabic(pageLabel);
+ } else if (MATCH_LETTER.matcher(pageLabel).matches()) {
+ if (pageLabel.toLowerCase().equals(pageLabel)) {
+ currentPageLabelType = LOWER_ALPHA;
+ } else {
+ currentPageLabelType = UPPER_ALPHA;
+ }
+ currentPageNumber = alphabeticToArabic(pageLabel);
+ } else {
+ // alphabetic numbering in XSL_FO and labelling in PDF are different after AA (AB versus BB)
+ // we will use the /P (prefix) label in that case
+ currentPageLabelType = PREFIX;
+ addNewPageLabel = true;
+ }
+ if (lastPageLabelType != currentPageLabelType) {
+ addNewPageLabel = true;
+ }
+ if (lastPageNumber != currentPageNumber - 1) {
+ addNewPageLabel = true;
+ }
+ if (addNewPageLabel) {
+ PDFNumsArray nums = getNums();
+ PDFDictionary dict = new PDFDictionary(nums);
+ PDFName pdfName = null;
+ switch (currentPageLabelType) {
+ case PREFIX:
+ dict.put("P", pageLabel);
+ break;
+ default:
+ switch (currentPageLabelType) {
+ case DECIMAL:
+ pdfName = S_D;
+ if (currentZeroPaddingPrefix.length() != 0) {
+ dict.put("P", currentZeroPaddingPrefix);
+ }
+ break;
+ case LOWER_ROMAN:
+ pdfName = S_LR;
+ break;
+ case UPPER_ROMAN:
+ pdfName = S_UR;
+ break;
+ case LOWER_ALPHA:
+ pdfName = S_LA;
+ break;
+ case UPPER_ALPHA:
+ pdfName = S_UA;
+ break;
+ default:
+ }
+ dict.put("S", pdfName);
+ if (currentPageNumber != 1) {
+ dict.put("St", currentPageNumber);
+ }
+ }
+ nums.put(index, dict);
+ }
+ lastPageLabelType = currentPageLabelType;
+ lastPageNumber = currentPageNumber;
+ lastZeroPaddingPrefix = currentZeroPaddingPrefix;
+ }
+
+ private int romanToArabic(String roman) {
+ int arabic = 0;
+ int previousValue = 0;
+ int newValue = 0;
+ String upperRoman = roman.toUpperCase();
+ for (int i = 0; i < upperRoman.length(); i++) {
+ char romanDigit = upperRoman.charAt(i);
+ switch (romanDigit) {
+ case 'I':
+ newValue = 1;
+ break;
+ case 'V':
+ newValue = 5;
+ break;
+ case 'X':
+ newValue = 10;
+ break;
+ case 'L':
+ newValue = 50;
+ break;
+ case 'C':
+ newValue = 100;
+ break;
+ case 'D':
+ newValue = 500;
+ break;
+ case 'M':
+ newValue = 1000;
+ break;
+ default:
+ }
+ if (previousValue < newValue) {
+ arabic -= previousValue;
+ } else {
+ arabic += previousValue;
+ }
+ previousValue = newValue;
+ }
+ arabic += previousValue;
+ return arabic;
+ }
+
+ private int alphabeticToArabic(String alpha) {
+ int arabic = 0;
+ if (alpha.length() > 1) {
+ // this should never happen
+ return arabic;
+ }
+ String lowerAlpha = alpha.toLowerCase();
+ arabic = (lowerAlpha.charAt(0) - 'a' + 1);
+ return arabic;
+ }
}
pageLabels = this.pdfDoc.getFactory().makePageLabels();
this.pdfDoc.getRoot().setPageLabels(pageLabels);
}
- PDFNumsArray nums = pageLabels.getNums();
- PDFDictionary dict = new PDFDictionary(nums);
- dict.put("P", pageNumber);
- //TODO If the sequence of generated page numbers were inspected, this could be
- //expressed in a more space-efficient way
- nums.put(pageIndex, dict);
+ pageLabels.addPageLabel(pageIndex, pageNumber);
}
/**
--- /dev/null
+/*
+ * 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$ */
+
+package org.apache.fop.pdf;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+public class PDFPageLabelsTestCase {
+
+ @Test
+ public void testAddPageLabel() throws IOException {
+ PDFDocument pdfDoc = mock(PDFDocument.class);
+ PDFPageLabels pageLabels = new PDFPageLabels();
+ pageLabels.setDocument(pdfDoc);
+ int index = 0;
+ StringBuilder expected = new StringBuilder();
+ expected.append("[");
+ expected.append(index + " << /S /r >>\n");
+ pageLabels.addPageLabel(index++, "i");
+ pageLabels.addPageLabel(index++, "ii");
+ pageLabels.addPageLabel(index++, "iii");
+ expected.append(" " + index + " << /S /D >>\n");
+ pageLabels.addPageLabel(index++, "1");
+ pageLabels.addPageLabel(index++, "2");
+ pageLabels.addPageLabel(index++, "3");
+ pageLabels.addPageLabel(index++, "4");
+ pageLabels.addPageLabel(index++, "5");
+ pageLabels.addPageLabel(index++, "6");
+ pageLabels.addPageLabel(index++, "7");
+ pageLabels.addPageLabel(index++, "8");
+ pageLabels.addPageLabel(index++, "9");
+ pageLabels.addPageLabel(index++, "10");
+ expected.append(" " + index + " << /S /A >>\n");
+ pageLabels.addPageLabel(index++, "A");
+ pageLabels.addPageLabel(index++, "B");
+ expected.append(" " + index + " << /S /R /St 100 >>\n");
+ pageLabels.addPageLabel(index++, "C");
+ expected.append(" " + index + " << /S /R /St 500 >>\n");
+ pageLabels.addPageLabel(index++, "D");
+ expected.append(" " + index + " << /S /A /St 5 >>\n");
+ pageLabels.addPageLabel(index++, "E");
+ pageLabels.addPageLabel(index++, "F");
+ pageLabels.addPageLabel(index++, "G");
+ expected.append(" " + index + " << /P (aa) >>\n");
+ pageLabels.addPageLabel(index++, "aa");
+ expected.append(" " + index + " << /P (ab) >>\n");
+ pageLabels.addPageLabel(index++, "ab");
+ expected.append(" " + index + " << /P (ac) >>\n");
+ pageLabels.addPageLabel(index++, "ac");
+ expected.append(" " + index + " << /S /a >>\n");
+ pageLabels.addPageLabel(index++, "a");
+ pageLabels.addPageLabel(index++, "b");
+ expected.append(" " + index + " << /S /R /St 2 >>\n");
+ pageLabels.addPageLabel(index++, "II");
+ expected.append(" " + index + " << /S /R /St 12 >>\n");
+ pageLabels.addPageLabel(index++, "XII");
+ expected.append(" " + index + " <<\n /P (00)\n /S /D\n /St 9\n>>\n");
+ pageLabels.addPageLabel(index++, "009");
+ expected.append(" " + index + " <<\n /P (0)\n /S /D\n /St 10\n>>\n");
+ pageLabels.addPageLabel(index++, "010");
+ pageLabels.addPageLabel(index++, "011");
+ expected.append("]");
+
+ PDFNumsArray nums = pageLabels.getNums();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ nums.output(baos);
+ assertEquals(expected.toString(), baos.toString());
+ baos.close();
+ }
+
+}