return this.map.size();
}
+ /**
+ * Determines whether a value object should be converted to an indirect reference for inclusion in a Number Tree
+ * array according to the PDF spec.
+ * PDF1.0 - 1.2 - Spec is silent on this subject (as Number Trees don't exist).
+ * PDF1.3 & 1.4 - Values must be indirect object refs.
+ * PDF1.5 - Recommended: stream, dictionary, array, and string values be indirect object refs.
+ * PDF1.6 - 2.0 - Stream values must be indirect object refs.
+ * Recommended: dictionary, array, and string values be indirect object refs.
+ * Method signals for values that must be, and those recommended to be indirect object refs.
+ * @param obj The object to be considered.
+ * @return True iff the object should be converted.
+ */
+ private boolean shouldConvertToRef(PDFObject obj) {
+ boolean retval = false;
+ if (getDocument() != null && getDocument().getPDFVersion() != null) {
+ switch (getDocument().getPDFVersion()) {
+ case V1_0: // fall-through
+ case V1_1: // fall-through
+ case V1_2:
+ log.error("Number Tree used in PDF version " + getDocument().getPDFVersion());
+ break;
+ case V1_3: // fall-through
+ case V1_4:
+ retval = true;
+ break;
+ case V1_5: // fall-through
+ case V1_6: // fall-through
+ case V1_7: // fall-through
+ case V2_0:
+ if (obj instanceof PDFStream
+ || obj instanceof PDFDictionary
+ || obj instanceof PDFArray
+ || obj instanceof PDFText) {
+ retval = true;
+ }
+ break;
+ default:
+ log.error("Unrecognised PDF version " + getDocument().getPDFVersion());
+ break;
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * This method provides conformance with the different PDF specs which require or recommend different types be used
+ * for Number Tree array values. Method indirects objects where indicated.
+ * @param obj The object to be considered for indirection.
+ * @return Either the object or a reference to the object.
+ */
+ private Object indirectIfReq(Object obj) {
+ PDFDocument doc = getDocument();
+ Object retval = obj;
+ if (obj instanceof PDFObject) {
+ PDFObject pdfObj = (PDFObject) obj;
+ PDFObject parent = pdfObj.getParent();
+ if (shouldConvertToRef(pdfObj)) {
+ if (!pdfObj.hasObjectNumber()) { // Needs registering with the doc.
+ pdfObj.setParent(null); // Can't register if it has a parent.
+ pdfObj = doc.registerObject(pdfObj);
+ if (parent != null) {
+ pdfObj.setParent(parent); // Reinstate original parent.
+ }
+ }
+ retval = pdfObj.makeReference();
+ }
+ }
+ return retval;
+ }
+
/**
* Sets an entry.
* @param key the key of the value to set
* @param obj the new value
*/
public void put(Integer key, Object obj) {
- this.map.put(key, obj);
+ this.map.put(key, indirectIfReq(obj));
}
/**
* Receive notification of the end of the current page.
*/
void endPage() {
- // TODO
- // Values in a number tree must be indirect references to the PDF
- // objects associated to the keys. To enforce that the array is
- // registered to the PDF document. Unfortunately that can't be done
- // earlier since a call to PDFContentGenerator.flushPDFDoc can be made
- // before the array is complete, which would result in only part of it
- // being output to the PDF.
- // This should really be handled by PDFNumsArray
pdfDoc.registerObject(pageParentTreeArray);
parentTree.addToNums(currentPage.getStructParents(), pageParentTreeArray);
}
PDFDocumentTestCase.class,
PDFNullTestCase.class,
PDFNumsArrayTestCase.class,
+ PDFNumsArrayPDFV17TestCase.class,
PDFRectangleTestCase.class,
PDFReferenceTestCase.class,
PDFResourcesTestCase.class,
import org.junit.Test;
/**
- * Test case for {@link PDFNumsArray}.
+ * Test case for {@link PDFNumsArray}. Uses default PDF doc (version 1.4) - requires text values and number values to be
+ * included as refs.
*/
public class PDFNumsArrayTestCase extends PDFObjectTestCase {
+ private static final String TEST_NAME = "Test name";
+ private static final int TEST_NUMBER = 10;
private PDFNumsArray numsArray;
- private String expectedString = "[0 /Test#20name 1 10]";
+ private String expectedString = "[0 1 0 R 1 2 0 R]";
+ private String expectedStringName = "/Test#20name";
+ private String expectedStringNumber = Integer.valueOf(TEST_NUMBER).toString();
@Before
public void setUp() {
numsArray = new PDFNumsArray(parent);
- numsArray.put(0, new PDFName("Test name"));
+ numsArray.setDocument(doc);
+ numsArray.put(0, new PDFName(TEST_NAME));
PDFNumber num = new PDFNumber();
- num.setNumber(10);
+ num.setNumber(TEST_NUMBER);
numsArray.put(1, num);
- numsArray.setDocument(doc);
pdfObjectUnderTest = numsArray;
}
@Test
public void testOutput() throws IOException {
testOutputStreams(expectedString, numsArray);
+ testOutputStreams(expectedStringName, doc.objects.get(1));
+ testOutputStreams(expectedStringNumber, doc.objects.get(2));
}
}
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
/**
* Tests that the nums array in the ParentTree dictionary is correctly being split into
*/
@Test
public void testOutOfOrderSplit() throws Exception {
- PDFStructElem structElem = mock(PDFStructElem.class);
+ PDFStructElem structElem = spy(PDFStructElem.class);
for (int num = 50; num < 53; num++) {
parentTree.addToNums(num, structElem);
}
* @throws Exception
*/
private int getArrayNumber(int elementNumber) throws Exception {
- PDFStructElem structElem = mock(PDFStructElem.class);
+ PDFStructElem structElem = spy(PDFStructElem.class);
for (int structParent = 0; structParent < elementNumber; structParent++) {
parentTree.addToNums(structParent, structElem);
}