]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
bugzilla #54024: rewrote generation of /PageLabels dictionary
authorLuis Bernardo <lbernardo@apache.org>
Wed, 17 Oct 2012 23:36:43 +0000 (23:36 +0000)
committerLuis Bernardo <lbernardo@apache.org>
Wed, 17 Oct 2012 23:36:43 +0000 (23:36 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1399483 13f79535-47bb-0310-9956-ffa450edef68

findbugs-exclude.xml
src/java/org/apache/fop/pdf/PDFPageLabels.java
src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
test/java/org/apache/fop/pdf/PDFPageLabelsTestCase.java [new file with mode: 0644]

index 1b7bcac67e664cc1dfb7c8eebf435d4c52caea05..2bec97462965619d6e897e2cdaaf7a8e9401b0db 100644 (file)
       <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="&lt;init&gt;"/>
index 2c1a977b4499ecf49f639ad1e5bbf62ce1b9894c..e95c97a25a72cb2a0b285eaf0a972802367b618c 100644 (file)
 
 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
      */
@@ -45,4 +70,153 @@ public class PDFPageLabels extends PDFNumberTreeNode {
         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;
+    }
 }
index fba9d922f26c1b2219cc53dcc2be3fc0f32789c0..ef57c215257d0ffbc8ee913cae2d249c0ea2f92f 100644 (file)
@@ -308,12 +308,7 @@ class PDFRenderingUtil {
             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);
     }
 
     /**
diff --git a/test/java/org/apache/fop/pdf/PDFPageLabelsTestCase.java b/test/java/org/apache/fop/pdf/PDFPageLabelsTestCase.java
new file mode 100644 (file)
index 0000000..e982bf2
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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();
+    }
+
+}