Browse Source

bugzilla #54024: rewrote generation of /PageLabels dictionary

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1399483 13f79535-47bb-0310-9956-ffa450edef68
tags/fop-2_0
Luis Bernardo 11 years ago
parent
commit
9772267c98

+ 9
- 0
findbugs-exclude.xml View File

@@ -1078,6 +1078,15 @@
<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;"/>

+ 174
- 0
src/java/org/apache/fop/pdf/PDFPageLabels.java View File

@@ -19,11 +19,36 @@

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;
}
}

+ 1
- 6
src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java View 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);
}

/**

+ 93
- 0
test/java/org/apache/fop/pdf/PDFPageLabelsTestCase.java View File

@@ -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();
}

}

Loading…
Cancel
Save