You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by le...@apache.org on 2013/11/17 16:27:34 UTC
svn commit: r1542748 [5/5] - in /pdfbox/branches/1.8: ./
examples/src/main/java/org/apache/pdfbox/examples/fdf/
examples/src/main/java/org/apache/pdfbox/examples/signature/
fontbox/src/main/java/org/apache/fontbox/cff/
fontbox/src/main/java/org/apache/...
Modified: pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/util/PDFMergerUtility.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/util/PDFMergerUtility.java?rev=1542748&r1=1542747&r2=1542748&view=diff
==============================================================================
--- pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/util/PDFMergerUtility.java (original)
+++ pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/util/PDFMergerUtility.java Sun Nov 17 15:27:33 2013
@@ -22,15 +22,18 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSInteger;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSNumber;
import org.apache.pdfbox.cos.COSStream;
+import org.apache.pdfbox.cos.COSString;
import org.apache.pdfbox.exceptions.COSVisitorException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
@@ -38,7 +41,11 @@ import org.apache.pdfbox.pdmodel.PDDocum
import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.COSArrayList;
+import org.apache.pdfbox.pdmodel.common.PDNumberTreeNode;
import org.apache.pdfbox.pdmodel.common.PDStream;
+import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDMarkInfo;
+import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureTreeRoot;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
@@ -46,14 +53,14 @@ import org.apache.pdfbox.pdmodel.interac
import org.apache.pdfbox.pdmodel.interactive.form.PDFieldFactory;
/**
- * This class will take a list of pdf documents and merge them, saving the result
- * in a new document.
- *
+ * This class will take a list of pdf documents and merge them, saving the result in a new document.
+ *
* @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
- * @version $Revision: 1.3 $
+ *
*/
public class PDFMergerUtility
{
+ private static final String STRUCTURETYPE_DOCUMENT = "Document";
private List<InputStream> sources;
private String destinationFileName;
@@ -70,6 +77,7 @@ public class PDFMergerUtility
/**
* Get the name of the destination file.
+ *
* @return Returns the destination.
*/
public String getDestinationFileName()
@@ -79,8 +87,8 @@ public class PDFMergerUtility
/**
* Set the name of the destination file.
- * @param destination
- * The destination to set.
+ *
+ * @param destination The destination to set.
*/
public void setDestinationFileName(String destination)
{
@@ -89,6 +97,7 @@ public class PDFMergerUtility
/**
* Get the destination OutputStream.
+ *
* @return Returns the destination OutputStream.
*/
public OutputStream getDestinationStream()
@@ -98,6 +107,7 @@ public class PDFMergerUtility
/**
* Set the destination OutputStream.
+ *
* @param destStream The destination to set.
*/
public void setDestinationStream(OutputStream destStream)
@@ -107,7 +117,7 @@ public class PDFMergerUtility
/**
* Add a source file to the list of files to merge.
- *
+ *
* @param source Full path and file name of source document.
*/
public void addSource(String source)
@@ -116,7 +126,7 @@ public class PDFMergerUtility
{
sources.add(new FileInputStream(new File(source)));
}
- catch(Exception e)
+ catch (Exception e)
{
throw new RuntimeException(e);
}
@@ -124,7 +134,7 @@ public class PDFMergerUtility
/**
* Add a source file to the list of files to merge.
- *
+ *
* @param source File representing source document
*/
public void addSource(File source)
@@ -133,7 +143,7 @@ public class PDFMergerUtility
{
sources.add(new FileInputStream(source));
}
- catch(Exception e)
+ catch (Exception e)
{
throw new RuntimeException(e);
}
@@ -141,7 +151,7 @@ public class PDFMergerUtility
/**
* Add a source to the list of documents to merge.
- *
+ *
* @param source InputStream representing source document
*/
public void addSource(InputStream source)
@@ -151,17 +161,17 @@ public class PDFMergerUtility
/**
* Add a list of sources to the list of documents to merge.
- *
+ *
* @param sourcesList List of InputStream objects representing source documents
*/
public void addSources(List<InputStream> sourcesList)
{
- this.sources.addAll(sourcesList);
+ sources.addAll(sourcesList);
}
/**
* Merge the list of source documents, saving the result in the destination file.
- *
+ *
* @throws IOException If there is an error saving the document.
* @throws COSVisitorException If an error occurs while saving the destination file.
*/
@@ -187,7 +197,7 @@ public class PDFMergerUtility
tobeclosed.add(source);
appendDocument(destination, source);
}
- if(destinationStream == null)
+ if (destinationStream == null)
{
destination.save(destinationFileName);
}
@@ -210,52 +220,51 @@ public class PDFMergerUtility
}
}
-
/**
* append all pages from source to destination.
- *
+ *
* @param destination the document to receive the pages
* @param source the document originating the new pages
- *
+ *
* @throws IOException If there is an error accessing data from either document.
*/
public void appendDocument(PDDocument destination, PDDocument source) throws IOException
{
- if( destination.isEncrypted() )
+ if (destination.isEncrypted())
{
- throw new IOException( "Error: destination PDF is encrypted, can't append encrypted PDF documents." );
+ throw new IOException("Error: destination PDF is encrypted, can't append encrypted PDF documents.");
}
- if( source.isEncrypted() )
+ if (source.isEncrypted())
{
- throw new IOException( "Error: source PDF is encrypted, can't append encrypted PDF documents." );
+ throw new IOException("Error: source PDF is encrypted, can't append encrypted PDF documents.");
}
PDDocumentInformation destInfo = destination.getDocumentInformation();
PDDocumentInformation srcInfo = source.getDocumentInformation();
- destInfo.getDictionary().mergeInto( srcInfo.getDictionary() );
+ destInfo.getDictionary().mergeInto(srcInfo.getDictionary());
PDDocumentCatalog destCatalog = destination.getDocumentCatalog();
PDDocumentCatalog srcCatalog = source.getDocumentCatalog();
// use the highest version number for the resulting pdf
- float destVersion = destination.getDocument().getVersion();
- float srcVersion = source.getDocument().getVersion();
+ float destVersion = destination.getDocument().getVersion();
+ float srcVersion = source.getDocument().getVersion();
if (destVersion < srcVersion)
{
destination.getDocument().setVersion(srcVersion);
}
-
- if( destCatalog.getOpenAction() == null )
+
+ if (destCatalog.getOpenAction() == null)
{
- destCatalog.setOpenAction( srcCatalog.getOpenAction() );
+ destCatalog.setOpenAction(srcCatalog.getOpenAction());
}
- // maybe there are some shared resources for all pages
- COSDictionary srcPages = (COSDictionary)srcCatalog.getCOSDictionary().getDictionaryObject( COSName.PAGES );
- COSDictionary srcResources = (COSDictionary)srcPages.getDictionaryObject( COSName.RESOURCES );
- COSDictionary destPages = (COSDictionary)destCatalog.getCOSDictionary().getDictionaryObject( COSName.PAGES );
- COSDictionary destResources = (COSDictionary)destPages.getDictionaryObject( COSName.RESOURCES );
- if (srcResources != null)
+ // maybe there are some shared resources for all pages
+ COSDictionary srcPages = (COSDictionary) srcCatalog.getCOSDictionary().getDictionaryObject(COSName.PAGES);
+ COSDictionary srcResources = (COSDictionary) srcPages.getDictionaryObject(COSName.RESOURCES);
+ COSDictionary destPages = (COSDictionary) destCatalog.getCOSDictionary().getDictionaryObject(COSName.PAGES);
+ COSDictionary destResources = (COSDictionary) destPages.getDictionaryObject(COSName.RESOURCES);
+ if (srcResources != null)
{
if (destResources != null)
{
@@ -266,56 +275,54 @@ public class PDFMergerUtility
destPages.setItem(COSName.RESOURCES, srcResources);
}
}
-
+
PDFCloneUtility cloner = new PDFCloneUtility(destination);
try
{
PDAcroForm destAcroForm = destCatalog.getAcroForm();
PDAcroForm srcAcroForm = srcCatalog.getAcroForm();
- if( destAcroForm == null )
+ if (destAcroForm == null)
{
- cloner.cloneForNewDocument( srcAcroForm );
- destCatalog.setAcroForm( srcAcroForm );
+ cloner.cloneForNewDocument(srcAcroForm);
+ destCatalog.setAcroForm(srcAcroForm);
}
else
{
- if( srcAcroForm != null )
+ if (srcAcroForm != null)
{
mergeAcroForm(cloner, destAcroForm, srcAcroForm);
}
}
}
- catch(Exception e)
+ catch (Exception e)
{
// if we are not ignoring exceptions, we'll re-throw this
- if(!ignoreAcroFormErrors)
+ if (!ignoreAcroFormErrors)
{
- throw (IOException)e;
+ throw (IOException) e;
}
}
- COSArray destThreads = (COSArray)destCatalog.getCOSDictionary().getDictionaryObject(
- COSName.THREADS);
- COSArray srcThreads = (COSArray)cloner.cloneForNewDocument(
- destCatalog.getCOSDictionary().getDictionaryObject( COSName.THREADS ));
- if( destThreads == null )
+ COSArray destThreads = (COSArray) destCatalog.getCOSDictionary().getDictionaryObject(COSName.THREADS);
+ COSArray srcThreads = (COSArray) cloner.cloneForNewDocument(destCatalog.getCOSDictionary().getDictionaryObject(
+ COSName.THREADS));
+ if (destThreads == null)
{
- destCatalog.getCOSDictionary().setItem( COSName.THREADS, srcThreads );
+ destCatalog.getCOSDictionary().setItem(COSName.THREADS, srcThreads);
}
else
{
- destThreads.addAll( srcThreads );
+ destThreads.addAll(srcThreads);
}
PDDocumentNameDictionary destNames = destCatalog.getNames();
PDDocumentNameDictionary srcNames = srcCatalog.getNames();
- if( srcNames != null )
+ if (srcNames != null)
{
- if( destNames == null )
+ if (destNames == null)
{
- destCatalog.getCOSDictionary().setItem( COSName.NAMES,
- cloner.cloneForNewDocument( srcNames ) );
+ destCatalog.getCOSDictionary().setItem(COSName.NAMES, cloner.cloneForNewDocument(srcNames));
}
else
{
@@ -326,142 +333,336 @@ public class PDFMergerUtility
PDDocumentOutline destOutline = destCatalog.getDocumentOutline();
PDDocumentOutline srcOutline = srcCatalog.getDocumentOutline();
- if( srcOutline != null )
+ if (srcOutline != null)
{
- if( destOutline == null )
+ if (destOutline == null)
{
- PDDocumentOutline cloned =
- new PDDocumentOutline( (COSDictionary)cloner.cloneForNewDocument( srcOutline ) );
- destCatalog.setDocumentOutline( cloned );
+ PDDocumentOutline cloned = new PDDocumentOutline((COSDictionary) cloner.cloneForNewDocument(srcOutline));
+ destCatalog.setDocumentOutline(cloned);
}
else
{
PDOutlineItem first = srcOutline.getFirstChild();
- if(first != null)
+ if (first != null)
{
- PDOutlineItem clonedFirst = new PDOutlineItem(
- (COSDictionary)cloner.cloneForNewDocument( first ));
- destOutline.appendChild( clonedFirst );
+ PDOutlineItem clonedFirst = new PDOutlineItem((COSDictionary) cloner.cloneForNewDocument(first));
+ destOutline.appendChild(clonedFirst);
}
}
}
String destPageMode = destCatalog.getPageMode();
String srcPageMode = srcCatalog.getPageMode();
- if( destPageMode == null )
+ if (destPageMode == null)
{
- destCatalog.setPageMode( srcPageMode );
+ destCatalog.setPageMode(srcPageMode);
}
- COSDictionary destLabels = (COSDictionary)destCatalog.getCOSDictionary().getDictionaryObject(
+ COSDictionary destLabels = (COSDictionary) destCatalog.getCOSDictionary().getDictionaryObject(
COSName.PAGE_LABELS);
- COSDictionary srcLabels = (COSDictionary)srcCatalog.getCOSDictionary().getDictionaryObject(
- COSName.PAGE_LABELS);
- if( srcLabels != null )
+ COSDictionary srcLabels = (COSDictionary) srcCatalog.getCOSDictionary()
+ .getDictionaryObject(COSName.PAGE_LABELS);
+ if (srcLabels != null)
{
int destPageCount = destination.getNumberOfPages();
COSArray destNums = null;
- if( destLabels == null )
+ if (destLabels == null)
{
destLabels = new COSDictionary();
destNums = new COSArray();
- destLabels.setItem( COSName.NUMS, destNums );
- destCatalog.getCOSDictionary().setItem( COSName.PAGE_LABELS, destLabels );
+ destLabels.setItem(COSName.NUMS, destNums);
+ destCatalog.getCOSDictionary().setItem(COSName.PAGE_LABELS, destLabels);
}
else
{
- destNums = (COSArray)destLabels.getDictionaryObject( COSName.NUMS );
+ destNums = (COSArray) destLabels.getDictionaryObject(COSName.NUMS);
}
- COSArray srcNums = (COSArray)srcLabels.getDictionaryObject( COSName.NUMS );
+ COSArray srcNums = (COSArray) srcLabels.getDictionaryObject(COSName.NUMS);
if (srcNums != null)
{
- for( int i=0; i<srcNums.size(); i+=2 )
+ for (int i = 0; i < srcNums.size(); i += 2)
{
- COSNumber labelIndex = (COSNumber)srcNums.getObject( i );
+ COSNumber labelIndex = (COSNumber) srcNums.getObject(i);
long labelIndexValue = labelIndex.intValue();
- destNums.add( COSInteger.get( labelIndexValue + destPageCount ) );
- destNums.add( cloner.cloneForNewDocument( srcNums.getObject( i+1 ) ) );
+ destNums.add(COSInteger.get(labelIndexValue + destPageCount));
+ destNums.add(cloner.cloneForNewDocument(srcNums.getObject(i + 1)));
}
}
}
- COSStream destMetadata = (COSStream)destCatalog.getCOSDictionary().getDictionaryObject( COSName.METADATA );
- COSStream srcMetadata = (COSStream)srcCatalog.getCOSDictionary().getDictionaryObject( COSName.METADATA );
- if( destMetadata == null && srcMetadata != null )
+ COSStream destMetadata = (COSStream) destCatalog.getCOSDictionary().getDictionaryObject(COSName.METADATA);
+ COSStream srcMetadata = (COSStream) srcCatalog.getCOSDictionary().getDictionaryObject(COSName.METADATA);
+ if (destMetadata == null && srcMetadata != null)
{
- PDStream newStream = new PDStream( destination, srcMetadata.getUnfilteredStream(), false );
- newStream.getStream().mergeInto( srcMetadata );
+ PDStream newStream = new PDStream(destination, srcMetadata.getUnfilteredStream(), false);
+ newStream.getStream().mergeInto(srcMetadata);
newStream.addCompression();
- destCatalog.getCOSDictionary().setItem( COSName.METADATA, newStream );
+ destCatalog.getCOSDictionary().setItem(COSName.METADATA, newStream);
+ }
+
+ // merge logical structure hierarchy if logical structure information is available in both source pdf and
+ // destination pdf
+ boolean mergeStructTree = false;
+ int destParentTreeNextKey = -1;
+ COSDictionary destParentTreeDict = null;
+ COSDictionary srcParentTreeDict = null;
+ COSArray destNumbersArray = null;
+ COSArray srcNumbersArray = null;
+ PDMarkInfo destMark = destCatalog.getMarkInfo();
+ PDStructureTreeRoot destStructTree = destCatalog.getStructureTreeRoot();
+ PDMarkInfo srcMark = srcCatalog.getMarkInfo();
+ PDStructureTreeRoot srcStructTree = srcCatalog.getStructureTreeRoot();
+ if (destStructTree != null)
+ {
+ PDNumberTreeNode destParentTree = destStructTree.getParentTree();
+ destParentTreeNextKey = destStructTree.getParentTreeNextKey();
+ if (destParentTree != null)
+ {
+ destParentTreeDict = destParentTree.getCOSDictionary();
+ destNumbersArray = (COSArray) destParentTreeDict.getDictionaryObject(COSName.NUMS);
+ if (destNumbersArray != null)
+ {
+ if (destParentTreeNextKey < 0)
+ {
+ destParentTreeNextKey = destNumbersArray.size() / 2;
+ }
+ if (destParentTreeNextKey > 0)
+ {
+ if (srcStructTree != null)
+ {
+ PDNumberTreeNode srcParentTree = srcStructTree.getParentTree();
+ if (srcParentTree != null)
+ {
+ srcParentTreeDict = srcParentTree.getCOSDictionary();
+ srcNumbersArray = (COSArray) srcParentTreeDict.getDictionaryObject(COSName.NUMS);
+ if (srcNumbersArray != null)
+ {
+ mergeStructTree = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (destMark != null && destMark.isMarked() && !mergeStructTree)
+ {
+ destMark.setMarked(false);
+ }
+ if (!mergeStructTree)
+ {
+ destCatalog.setStructureTreeRoot(null);
+ }
}
- //finally append the pages
+ // finally append the pages
List<PDPage> pages = srcCatalog.getAllPages();
Iterator<PDPage> pageIter = pages.iterator();
- while( pageIter.hasNext() )
+ HashMap<COSDictionary, COSDictionary> objMapping = new HashMap<COSDictionary, COSDictionary>();
+ while (pageIter.hasNext())
{
PDPage page = pageIter.next();
- PDPage newPage =
- new PDPage( (COSDictionary)cloner.cloneForNewDocument( page.getCOSDictionary() ) );
- newPage.setCropBox( page.findCropBox() );
- newPage.setMediaBox( page.findMediaBox() );
- newPage.setRotation( page.findRotation() );
- destination.addPage( newPage );
+ PDPage newPage = new PDPage((COSDictionary) cloner.cloneForNewDocument(page.getCOSDictionary()));
+ newPage.setCropBox(page.findCropBox());
+ newPage.setMediaBox(page.findMediaBox());
+ newPage.setRotation(page.findRotation());
+ if (mergeStructTree)
+ {
+ updateStructParentEntries(newPage, destParentTreeNextKey);
+ objMapping.put(page.getCOSDictionary(), newPage.getCOSDictionary());
+ List<PDAnnotation> oldAnnots = page.getAnnotations();
+ List<PDAnnotation> newAnnots = newPage.getAnnotations();
+ for (int i = 0; i < oldAnnots.size(); i++)
+ {
+ objMapping.put(oldAnnots.get(i).getDictionary(), newAnnots.get(i).getDictionary());
+ }
+ // TODO update mapping for XObjects
+ }
+ destination.addPage(newPage);
}
- }
+ if (mergeStructTree)
+ {
+ updatePageReferences(srcNumbersArray, objMapping);
+ for (int i = 0; i < srcNumbersArray.size() / 2; i++)
+ {
+ destNumbersArray.add(COSInteger.get(destParentTreeNextKey + i));
+ destNumbersArray.add(srcNumbersArray.getObject(i * 2 + 1));
+ }
+ destParentTreeNextKey += srcNumbersArray.size() / 2;
+ destParentTreeDict.setItem(COSName.NUMS, destNumbersArray);
+ PDNumberTreeNode newParentTreeNode = new PDNumberTreeNode(destParentTreeDict, COSBase.class);
+ destStructTree.setParentTree(newParentTreeNode);
+ destStructTree.setParentTreeNextKey(destParentTreeNextKey);
+ COSDictionary kDictLevel0 = new COSDictionary();
+ COSArray newKArray = new COSArray();
+ COSArray destKArray = destStructTree.getKArray();
+ COSArray srcKArray = srcStructTree.getKArray();
+ if (destKArray != null && srcKArray != null)
+ {
+ updateParentEntry(destKArray, kDictLevel0);
+ newKArray.addAll(destKArray);
+ if (mergeStructTree)
+ {
+ updateParentEntry(srcKArray, kDictLevel0);
+ }
+ newKArray.addAll(srcKArray);
+ }
+ kDictLevel0.setItem(COSName.K, newKArray);
+ kDictLevel0.setItem(COSName.P, destStructTree);
+ kDictLevel0.setItem(COSName.S, new COSString(STRUCTURETYPE_DOCUMENT));
+ destStructTree.setK(kDictLevel0);
+ }
+ }
private int nextFieldNum = 1;
/**
- * Merge the contents of the source form into the destination form
- * for the destination file.
- *
+ * Merge the contents of the source form into the destination form for the destination file.
+ *
* @param cloner the object cloner for the destination document
* @param destAcroForm the destination form
* @param srcAcroForm the source form
* @throws IOException If an error occurs while adding the field.
*/
private void mergeAcroForm(PDFCloneUtility cloner, PDAcroForm destAcroForm, PDAcroForm srcAcroForm)
- throws IOException
+ throws IOException
{
List destFields = destAcroForm.getFields();
List srcFields = srcAcroForm.getFields();
- if( srcFields != null )
+ if (srcFields != null)
{
- if( destFields == null )
+ if (destFields == null)
{
destFields = new COSArrayList();
- destAcroForm.setFields( destFields );
+ destAcroForm.setFields(destFields);
}
Iterator srcFieldsIterator = srcFields.iterator();
while (srcFieldsIterator.hasNext())
{
- PDField srcField = (PDField)srcFieldsIterator.next();
- PDField destField =
- PDFieldFactory.createField(
- destAcroForm,
- (COSDictionary)cloner.cloneForNewDocument(srcField.getDictionary() ));
+ PDField srcField = (PDField) srcFieldsIterator.next();
+ PDField destField = PDFieldFactory.createField(destAcroForm,
+ (COSDictionary) cloner.cloneForNewDocument(srcField.getDictionary()));
// if the form already has a field with this name then we need to rename this field
// to prevent merge conflicts.
- if ( destAcroForm.getField(destField.getFullyQualifiedName()) != null )
+ if (destAcroForm.getField(destField.getFullyQualifiedName()) != null)
{
- destField.setPartialName("dummyFieldName"+(nextFieldNum++));
+ destField.setPartialName("dummyFieldName" + (nextFieldNum++));
}
destFields.add(destField);
}
}
}
+ /**
+ * Indicates if acroform errors are ignored or not.
+ *
+ * @return true if acroform errors are ignored
+ */
public boolean isIgnoreAcroFormErrors()
{
return ignoreAcroFormErrors;
}
- public void setIgnoreAcroFormErrors(boolean ignoreAcroFormErrors)
+ /**
+ * Set to true to ignore acroform errors.
+ *
+ * @param ignoreAcroFormErrorsValue true if acroform errors should be ignored
+ */
+ public void setIgnoreAcroFormErrors(boolean ignoreAcroFormErrorsValue)
{
- this.ignoreAcroFormErrors = ignoreAcroFormErrors;
+ ignoreAcroFormErrors = ignoreAcroFormErrorsValue;
}
+ /**
+ * Update the Pg and Obj references to the new (merged) page.
+ *
+ * @param parentTreeEntry
+ * @param objMapping mapping between old and new references
+ */
+ private void updatePageReferences(COSDictionary parentTreeEntry, HashMap<COSDictionary, COSDictionary> objMapping)
+ {
+ COSBase page = parentTreeEntry.getDictionaryObject(COSName.PG);
+ if (page instanceof COSDictionary)
+ {
+ if (objMapping.containsKey(page))
+ {
+ parentTreeEntry.setItem(COSName.PG, objMapping.get(page));
+ }
+ }
+ COSBase obj = parentTreeEntry.getDictionaryObject(COSName.OBJ);
+ if (obj instanceof COSDictionary)
+ {
+ if (objMapping.containsKey(obj))
+ {
+ parentTreeEntry.setItem(COSName.OBJ, objMapping.get(obj));
+ }
+ }
+ COSBase kSubEntry = parentTreeEntry.getDictionaryObject(COSName.K);
+ if (kSubEntry instanceof COSArray)
+ {
+ updatePageReferences((COSArray) kSubEntry, objMapping);
+ }
+ else if (kSubEntry instanceof COSDictionary)
+ {
+ updatePageReferences((COSDictionary) kSubEntry, objMapping);
+ }
+ }
+
+ private void updatePageReferences(COSArray parentTreeEntry, HashMap<COSDictionary, COSDictionary> objMapping)
+ {
+ for (int i = 0; i < parentTreeEntry.size(); i++)
+ {
+ COSBase subEntry = parentTreeEntry.getObject(i);
+ if (subEntry instanceof COSArray)
+ {
+ updatePageReferences((COSArray) subEntry, objMapping);
+ }
+ else if (subEntry instanceof COSDictionary)
+ {
+ updatePageReferences((COSDictionary) subEntry, objMapping);
+ }
+ }
+ }
+
+ /**
+ * Update the P reference to the new parent dictionary.
+ *
+ * @param kArray the kids array
+ * @param newParent the new parent
+ */
+ private void updateParentEntry(COSArray kArray, COSDictionary newParent)
+ {
+ for (int i = 0; i < kArray.size(); i++)
+ {
+ COSBase subEntry = kArray.getObject(i);
+ if (subEntry instanceof COSDictionary)
+ {
+ COSDictionary dictEntry = (COSDictionary) subEntry;
+ if (dictEntry.getDictionaryObject(COSName.P) != null)
+ {
+ dictEntry.setItem(COSName.P, newParent);
+ }
+ }
+ }
+ }
+
+ /**
+ * Update the StructParents and StructParent values in a PDPage.
+ *
+ * @param page the new page
+ * @param structParentOffset the offset which should be applied
+ */
+ private void updateStructParentEntries(PDPage page, int structParentOffset) throws IOException
+ {
+ page.setStructParents(page.getStructParents() + structParentOffset);
+ List<PDAnnotation> annots = page.getAnnotations();
+ List<PDAnnotation> newannots = new ArrayList<PDAnnotation>();
+ for (PDAnnotation annot : annots)
+ {
+ annot.setStructParent(annot.getStructParent() + structParentOffset);
+ newannots.add(annot);
+ }
+ page.setAnnotations(newannots);
+ }
}
Modified: pdfbox/branches/1.8/pdfbox/src/test/java/org/apache/pdfbox/util/TestDateUtil.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/1.8/pdfbox/src/test/java/org/apache/pdfbox/util/TestDateUtil.java?rev=1542748&r1=1542747&r2=1542748&view=diff
==============================================================================
--- pdfbox/branches/1.8/pdfbox/src/test/java/org/apache/pdfbox/util/TestDateUtil.java (original)
+++ pdfbox/branches/1.8/pdfbox/src/test/java/org/apache/pdfbox/util/TestDateUtil.java Sun Nov 17 15:27:33 2013
@@ -17,22 +17,32 @@
package org.apache.pdfbox.util;
import java.io.IOException;
+import java.text.ParsePosition;
import java.util.Calendar;
import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.SimpleTimeZone;
import java.util.TimeZone;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
+import org.apache.pdfbox.cos.COSString;
+
/**
* Test the date conversion utility.
*
* @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
- * @version $Revision: 1.2 $
+ * @author <a href="mailto:zweibieren@ahoo.com">Fred Hansen</a>
+ *
*/
public class TestDateUtil extends TestCase
{
+ private static final int MINS = 60*1000, HRS = 60*MINS;
+ // expect parse fail
+ private static final int BAD = -666;
+
/**
* Test class constructor.
*
@@ -45,29 +55,68 @@ public class TestDateUtil extends TestCa
super( name );
}
+
+ ////////////////////////////////////////////////////
+ // Test body follows
+
+
/**
* Test common date formats.
*
* @throws Exception when there is an exception
*/
- public void testExtract()
- throws Exception
+ public void testExtract() throws Exception
{
TimeZone timezone = TimeZone.getDefault();
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
- try {
- assertEquals( DateConverter.toCalendar( "D:05/12/2005" ), new GregorianCalendar( 2005, 4, 12 ) );
- assertEquals( DateConverter.toCalendar( "5/12/2005 15:57:16" ), new GregorianCalendar( 2005, 4,12,15,57,16 ) );
- } finally {
+ try
+ {
+ assertCalendarEquals( new GregorianCalendar( 2005, 4, 12 ),
+ DateConverter.toCalendar( "D:05/12/2005" ) );
+ assertCalendarEquals( new GregorianCalendar( 2005, 4,12,15,57,16 ),
+ DateConverter.toCalendar( "5/12/2005 15:57:16" ) );
+ }
+ catch (IOException ex)
+ {
+ ex.printStackTrace();
+ }
+ finally
+ {
TimeZone.setDefault(timezone);
}
+ // check that new toCalendar gives NullPointer for a null arg
+ try
+ {
+ DateConverter.toCalendar(null, null);
+ assertNotNull(null); // failed to have expected exception
+ }
+ catch (NullPointerException ex)
+ {
+ // expected outcome
+ }
}
-
+
+ /**
+ * Calendar.equals test case.
+ *
+ * @param expect the expected calendar value
+ * @param was the calendar value to be checked
+ */
+ public void assertCalendarEquals(Calendar expect, Calendar was)
+ {
+ assertEquals( expect.getTimeInMillis(), was.getTimeInMillis() );
+ assertEquals( expect.getTimeZone().getRawOffset(),
+ was.getTimeZone().getRawOffset() );
+ }
+
/**
* Test case for
- * <a href="https://issues.apache.org/jira/browse/PDFBOX-598">PDFBOX-598</a>
+ * <a href="https://issues.apache.org/jira/browse/PDFBOX-598">PDFBOX-598</a>.
+ *
+ * @throws IOException if something went wrong.
*/
- public void testDateConversion() throws Exception {
+ public void testDateConversion() throws IOException
+ {
Calendar c = DateConverter.toCalendar("D:20050526205258+01'00'");
assertEquals(2005, c.get(Calendar.YEAR));
assertEquals(05-1, c.get(Calendar.MONTH));
@@ -78,42 +127,323 @@ public class TestDateUtil extends TestCa
assertEquals(0, c.get(Calendar.MILLISECOND));
}
- public void testDateConverter() throws Exception {
- TimeZone timezone = TimeZone.getDefault();
- TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
- try {
- assertDate("2010-01-01T00:00:00+00:00", "D:2010");
- assertDate("2010-01-01T00:00:00+00:00", "2010");
- assertDate("2010-04-23T00:00:00+00:00", "D:20100423");
- assertDate("2010-04-23T00:00:00+00:00", "20100423");
-
- // assertDate("2007-04-30T19:36:47+????", "20070430193647+713'00'");
- // assertDate("2007-08-21T10:35:22+00:00", "Tue Aug 21 10:35:22 2007");
- assertDate("2008-11-04T00:00:00+00:00", "Tuesday, November 04, 2008");
- // assertDate("2007-12-17T02:02:03+00:00", "200712172:2:3");
- // assertDate("????", "Unknown");
- // assertDate("2009-03-19T20:01:22+00:00", "20090319 200122");
- // assertDate("2008-05-12T09:47:00+00:00", "9:47 5/12/2008");
-
- // assertDate("2009-04-01T00:00:00+02:00", "20090401+0200");
- assertDate("2008-01-11T00:00:00+00:00", "Friday, January 11, 2008");
- // assertDate("2009-04-01T00:00:00+04:00", "20090401+04'00'");
- // assertDate("2009-04-01T00:00:00+09:00", "20090401+09'00'");
- // assertDate("2009-04-01T00:00:00-02:00", "20090401-02'00'");
- // assertDate("2009-04-01T06:01:01+00:00", "20090401 01:01:01 -0500");
- // assertDate("2000-05-26T11:25:10+00:00", "26 May 2000 11:25:10");
- // assertDate("2000-05-26T11:25:00+00:00", "26 May 2000 11:25");
- } finally {
- TimeZone.setDefault(timezone);
+ /**
+ * Check toCalendar.
+ * @param yr expected year value
+ * If an IOException is the expected result, yr should be null
+ * @param mon expected month value
+ * @param day expected dayofmonth value
+ * @param hr expected hour value
+ * @param min expected minute value
+ * @param sec expected second value
+ * @param tz represents expected timezone offset
+ * @param orig A date to be parsed.
+ * @throws Exception If an unexpected error occurs.
+ */
+ private static void checkParse(int yr, int mon, int day,
+ int hr, int min, int sec, int offset,
+ String orig) throws Exception
+ {
+ String pdfDate = String.format("D:%04d%02d%02d%02d%02d%02d%+03d'00'",
+ yr,mon,day,hr,min,sec,offset);
+ String iso8601Date = String.format("%04d-%02d-%02d"
+ + "T%02d:%02d:%02d%+03d:00",
+ yr,mon,day,hr,min,sec,offset);
+ Calendar cal = null;
+ try
+ {
+ cal = DateConverter.toCalendar(orig);
+ }
+ catch (IOException ex)
+ {
+ assertEquals(yr, BAD);
}
+ if (cal != null)
+ {
+ assertEquals(iso8601Date, DateConverter.toISO8601(cal));
+ assertEquals(pdfDate, DateConverter.toString(cal));
+ }
+ // new toCalendar()
+ cal = DateConverter.toCalendar(orig, null);
+ if (yr == BAD)
+ {
+ assertEquals(cal.get(Calendar.YEAR), DateConverter.INVALID_YEAR);
+ }
+ else
+ {
+ assertEquals(pdfDate, DateConverter.toString(cal));
+ }
+ }
+
+ /**
+ * Test dates in various formats.
+ * Years differ to make it easier to find failures.
+ * @throws Exception none expected
+ */
+ public void testDateConverter() throws Exception
+ {
+ int year = Calendar.getInstance().get(Calendar.YEAR);
+ checkParse(2010, 4,23, 0, 0, 0, 0, "D:20100423");
+ checkParse(2011, 4,23, 0, 0, 0, 0, "20110423");
+ checkParse(2012, 1, 1, 0, 0, 0, 0, "D:2012");
+ checkParse(2013, 1, 1, 0, 0, 0, 0, "2013");
+
+ // PDFBOX-1219
+ checkParse(2001, 1,31,10,33, 0, +1, "2001-01-31T10:33+01:00 ");
+ // PDFBOX-465
+ checkParse(2002, 5,12, 9,47, 0, 0, "9:47 5/12/2002");
+ // PDFBOX-465
+ checkParse(2003,12,17, 2, 2, 3, 0, "200312172:2:3");
+ // PDFBOX-465
+ checkParse(2009, 3,19,20, 1,22, 0, " 20090319 200122");
+
+ checkParse(2014, 4, 1, 0, 0, 0, +2, "20140401+0200");
+ // "EEEE, MMM dd, yy",
+ checkParse(2115, 1,11, 0, 0, 0, 0, "Friday, January 11, 2115");
+ // "EEEE, MMM dd, yy",
+ checkParse(1915, 1,11, 0, 0, 0, 0, "Monday, Jan 11, 1915");
+ // "EEEE, MMM dd, yy",
+ checkParse(2215, 1,11, 0, 0, 0, 0, "Wed, January 11, 2215");
+ // "EEEE, MMM dd, yy",
+ checkParse(2015, 1,11, 0, 0, 0, 0, " Sun, January 11, 2015 ");
+ checkParse(2016, 4, 1, 0, 0, 0, +4, "20160401+04'00'");
+ checkParse(2017, 4, 1, 0, 0, 0, +9, "20170401+09'00'");
+ checkParse(2018, 4, 1, 0, 0, 0, -2, "20180401-02'00'");
+ checkParse(2019, 4, 1, 6, 1, 1, -11, "20190401 6:1:1 -1100");
+ checkParse(2020, 5,26,11,25,10, 0, "26 May 2020 11:25:10");
+ checkParse(2021, 5,26,11,23, 0, 0, "26 May 2021 11:23");
+
+ // try dates invalid due to out of limit values
+ checkParse(BAD, 0, 0, 0, 0, 0, 0, "Tuesday, May 32 2000 11:27 UCT");
+ checkParse(BAD, 0, 0, 0, 0, 0, 0, "32 May 2000 11:25");
+ checkParse(BAD, 0, 0, 0, 0, 0, 0, "Tuesday, May 32 2000 11:25");
+ checkParse(BAD, 0, 0, 0, 0, 0, 0, "19921301 11:25");
+ checkParse(BAD, 0, 0, 0, 0, 0, 0, "19921232 11:25");
+ checkParse(BAD, 0, 0, 0, 0, 0, 0, "19921001 11:60");
+ checkParse(BAD, 0, 0, 0, 0, 0, 0, "19920401 24:25");
+
+ checkParse(BAD, 0, 0, 0, 0, 0, 0,
+ "20070430193647+713'00' illegal tz hr"); // PDFBOX-465
+ checkParse(BAD, 0, 0, 0, 0, 0, 0, "nodigits");
+ checkParse(BAD, 0, 0, 0, 0, 0, 0, "Unknown"); // PDFBOX-465
+ checkParse(BAD, 0, 0, 0, 0, 0, 0, "333three digit year");
+
+ checkParse(2000, 2,29, 0, 0, 0, 0, "2000 Feb 29"); // valid date
+ checkParse(2000, 2,29, 0, 0, 0,+11, " 2000 Feb 29 GMT + 11:00"); // valid date
+ checkParse(BAD, 0, 0, 0, 0, 0, 0, "2100 Feb 29 GMT+11"); // invalid date
+ checkParse(2012, 2,29, 0, 0, 0,+11, "2012 Feb 29 GMT+11"); // valid date
+ checkParse(BAD, 0, 0, 0, 0, 0, 0, "2012 Feb 30 GMT+11"); // invalid date
+
+ checkParse(1970,12,23, 0, 8, 0, 0, "1970 12 23:08"); // test ambiguous date
+
+ // cannot have P for PM
+ // cannot have Sat. instead of Sat
+ // EST works, but EDT does not; EST is a special kludge in Java
+
+ // test cases for all entries on old formats list
+ // "E, dd MMM yyyy hh:mm:ss a"
+ checkParse(1971, 7, 6, 17, 22, 1, 0, "Tuesday, 6 Jul 1971 5:22:1 PM");
+ // "EE, MMM dd, yyyy hh:mm:ss a"
+ checkParse(1972, 7, 6, 17, 22, 1, 0, "Thu, July 6, 1972 5:22:1 pm");
+ // "MM/dd/yyyy hh:mm:ss"
+ checkParse(1973, 7, 6, 17, 22, 1, 0, "7/6/1973 17:22:1");
+ // "MM/dd/yyyy"
+ checkParse(1974, 7, 6, 0, 0, 0, 0, "7/6/1974");
+ // "yyyy-MM-dd'T'HH:mm:ss'Z'"
+ checkParse(1975, 7, 6, 17, 22, 1, -10, "1975-7-6T17:22:1-1000");
+ // "yyyy-MM-dd'T'HH:mm:ssz"
+ checkParse(1976, 7, 6, 17, 22, 1, -4, "1976-7-6T17:22:1GMT-4");
+ // "yyyy-MM-dd'T'HH:mm:ssz"
+ checkParse(BAD, 7, 6, 17, 22, 1, -4, "2076-7-6T17:22:1EDT"); // "EDT" is not a known tz ID
+ // "yyyy-MM-dd'T'HH:mm:ssz"
+ // ATTENTION ****************************************
+ // changed from "EST" to "GMT-5", as this doesn't work on java5
+ // ATTENTION ****************************************
+ checkParse(1960, 7, 6, 17, 22, 1, -5, "1960-7-6T17:22:1GMT-5"); // "EST" does not have a DST rule
+ // "EEEE, MMM dd, yyyy"
+ checkParse(1977, 7, 6, 0, 0, 0, 0, "Wednesday, Jul 6, 1977");
+ // "EEEE MMM dd, yyyy HH:mm:ss"
+ checkParse(1978, 7, 6, 17, 22, 1, 0, "Thu Jul 6, 1978 17:22:1");
+ // "EEEE MMM dd HH:mm:ss z yyyy"
+ checkParse(1979, 7, 6, 17, 22, 1, +8, "Friday July 6 17:22:1 GMT+08:00 1979");
+ // "EEEE, MMM dd, yyyy 'at' hh:mma"
+ checkParse(1980, 7, 6, 16, 23, 0, 0, "Sun, Jul 6, 1980 at 4:23pm");
+ // "EEEEEEEEEE, MMMMMMMMMMMM dd, yyyy"
+ checkParse(1981, 7, 6, 0, 0, 0, 0, "Monday, July 6, 1981");
+ // "dd MMM yyyy hh:mm:ss"
+ checkParse(1982, 7, 6, 17, 22, 1, 0, "6 Jul 1982 17:22:1");
+ // "M/dd/yyyy hh:mm:ss"
+ checkParse(1983, 7, 6, 17, 22, 1, 0, "7/6/1983 17:22:1");
+ // "MM/d/yyyy hh:mm:ss"
+ checkParse(1984, 7, 6, 17, 22, 1, 0, "7/6/1984 17:22:01");
+ // "M/dd/yyyy"
+ checkParse(1985, 7, 6, 0, 0, 0, 0, "7/6/1985");
+ // "MM/d/yyyy"
+ checkParse(1986, 7, 6, 0, 0, 0, 0, "07/06/1986");
+ // "M/d/yyyy hh:mm:ss"
+ checkParse(1987, 7, 6, 17, 22, 1, 0, "7/6/1987 17:22:1");
+ // "M/d/yyyy"
+ checkParse(1988, 7, 6, 0, 0, 0, 0, "7/6/1988");
+
+ // test ends of range of two digit years
+ checkParse(year-79, 1, 1, 0, 0, 0, 0, "1/1/" + ((year-79)%100)
+ + " 00:00:00"); // "M/d/yy hh:mm:ss"
+ // "M/d/yy"
+ checkParse(year+19, 1, 1, 0, 0, 0, 0, "1/1/" + ((year+19)%100));
+
+ // "yyyyMMdd hh:mm:ss Z"
+ checkParse(1991, 7, 6, 17, 7, 1, +6, "19910706 17:7:1 Z+0600");
+ // "yyyyMMdd hh:mm:ss"
+ checkParse(1992, 7, 6, 17, 7, 1, 0, "19920706 17:07:01");
+ // "yyyyMMdd'+00''00'''"
+ checkParse(1993, 7, 6, 0, 0, 0, 0, "19930706+00'00'");
+ // "yyyyMMdd'+01''00'''"
+ checkParse(1994, 7, 6, 0, 0, 0, 1, "19940706+01'00'");
+ // "yyyyMMdd'+02''00'''"
+ checkParse(1995, 7, 6, 0, 0, 0, 2, "19950706+02'00'");
+ // "yyyyMMdd'+03''00'''"
+ checkParse(1996, 7, 6, 0, 0, 0, 3, "19960706+03'00'");
+ // . . .
+ // "yyyyMMdd'-10''00'''"
+ checkParse(1997, 7, 6, 0, 0, 0, -10, "19970706-10'00'");
+ // "yyyyMMdd'-11''00'''"
+ checkParse(1998, 7, 6, 0, 0, 0, -11, "19980706-11'00'");
+ // "yyyyMMdd"
+ checkParse(1999, 7, 6, 0, 0, 0, 0, "19990706");
+ // ambiguous big-endian date
+ checkParse(2073,12,25, 0, 8, 0, 0, "2073 12 25:08");
+
}
- private void assertDate(String expected, String date) throws Exception {
- Calendar calendar = DateConverter.toCalendar(date);
- assertEquals(expected, DateConverter.toISO8601(calendar));
+ private static void checkToString(int yr, int mon, int day,
+ int hr, int min, int sec, TimeZone tz, int off) throws Exception
+ {
+ // construct a GregoreanCalendar from args
+ GregorianCalendar cal = new GregorianCalendar(tz, Locale.ENGLISH);
+ cal.set(yr, mon-1, day, hr, min, sec);
+ // create expected strings
+ String pdfDate = String.format("D:%04d%02d%02d%02d%02d%02d%+03d'00'",
+ yr,mon,day,hr,min,sec,off);
+ String iso8601Date = String.format("%04d-%02d-%02d"
+ + "T%02d:%02d:%02d%+03d:00",
+ yr,mon,day,hr,min,sec,off);
+ // compare outputs from toString and toISO8601 with expected values
+ assertEquals(pdfDate, DateConverter.toString(cal));
+ assertEquals(iso8601Date, DateConverter.toISO8601(cal));
+ }
+
+ /**
+ * Test toString() and toISO8601() for various dates.
+ *
+ * @throws Exception if something went wrong.
+ */
+ public void testToString() throws Exception
+ { // std DST
+ TimeZone tzPgh = TimeZone.getTimeZone("America/New_York"); // -5 -4
+ TimeZone tzBerlin = TimeZone.getTimeZone("Europe/Berlin"); // +1 +2
+ TimeZone tzMaputo = TimeZone.getTimeZone("Africa/Maputo"); // +2 +2
+ TimeZone tzAruba = TimeZone.getTimeZone("America/Aruba"); // -4 -4
+ TimeZone tzJamaica = TimeZone.getTimeZone("America/Jamaica");// -5 -5
+ TimeZone tzMcMurdo = TimeZone.getTimeZone("Antartica/McMurdo");// +12 +13
+
+ assertNull(DateConverter.toCalendar((COSString) null));
+ assertNull(DateConverter.toCalendar((String) null));
+
+ checkToString(2013, 8, 28, 3, 14, 15, tzPgh, -4);
+ checkToString(2014, 2, 28, 3, 14, 15, tzPgh, -5);
+ checkToString(2015, 8, 28, 3, 14, 15, tzBerlin, +2);
+ checkToString(2016, 2, 28, 3, 14, 15, tzBerlin, +1);
+ checkToString(2017, 8, 28, 3, 14, 15, tzAruba, -4);
+ checkToString(2018, 1, 1, 1, 14, 15, tzJamaica, -5);
+ checkToString(2019, 12, 31, 12, 59, 59, tzJamaica, -5);
+ checkToString(2020, 2, 29, 0, 0, 0, tzMaputo, +2);
+ // McMurdo has a daylightsavings rule, but it seems never to apply
+ checkToString(1981, 1, 1, 1, 14, 15, tzMcMurdo, +0);
+ checkToString(1982, 2, 1, 1, 14, 15, tzMcMurdo, +0);
+ checkToString(1983, 3, 1, 1, 14, 15, tzMcMurdo, +0);
+ checkToString(1984, 4, 1, 1, 14, 15, tzMcMurdo, +0);
+ checkToString(1985, 5, 1, 1, 14, 15, tzMcMurdo, +0);
+ checkToString(1986, 6, 1, 1, 14, 15, tzMcMurdo, +0);
+ checkToString(1987, 7, 1, 1, 14, 15, tzMcMurdo, +0);
+ checkToString(1988, 8, 1, 1, 14, 15, tzMcMurdo, +0);
+ checkToString(1989, 9, 1, 1, 14, 15, tzMcMurdo, +0);
+ checkToString(1990, 10, 1, 1, 14, 15, tzMcMurdo, +0);
+ checkToString(1991, 11, 1, 1, 14, 15, tzMcMurdo, +0);
+ checkToString(1992, 12, 1, 1, 14, 15, tzMcMurdo, +0);
+ }
+
+ private static void checkParseTZ(int expect, String src)
+ {
+ GregorianCalendar dest = DateConverter.newGreg();
+ DateConverter.parseTZoffset(src, dest, new ParsePosition(0));
+ assertEquals(expect, dest.get(Calendar.ZONE_OFFSET));
}
/**
+ * Timezone testcase.
+ */
+ public void testParseTZ()
+ {
+ checkParseTZ(0*HRS+0*MINS, "+00:00");
+ checkParseTZ(0*HRS+0*MINS, "-0000");
+ checkParseTZ(1*HRS+0*MINS, "+1:00");
+ checkParseTZ(-(1*HRS+0*MINS), "-1:00");
+ checkParseTZ(-(1*HRS+30*MINS), "-0130");
+ checkParseTZ(11*HRS+59*MINS, "1159");
+ checkParseTZ(-(11*HRS+30*MINS), "1230");
+ checkParseTZ(11*HRS+30*MINS, "-12:30");
+ checkParseTZ(0*HRS+0*MINS, "Z");
+ checkParseTZ(-(8*HRS+0*MINS), "PST");
+ checkParseTZ(0*HRS+0*MINS, "EDT"); // EDT does not parse
+ checkParseTZ(-(3*HRS+0*MINS), "GMT-0300");
+ checkParseTZ(+(11*HRS+0*MINS), "GMT+11:00");
+ checkParseTZ(-(6*HRS+0*MINS), "America/Chicago");
+ // ATTENTION ****************************************
+ // changed from "Europe/Moscow" to "Europe/Berlin", as this doesn't work on java5
+ // ATTENTION ****************************************
+ checkParseTZ(+(1*HRS+0*MINS), "Europe/Berlin");
+ checkParseTZ((5*HRS+0*MINS), "0500");
+ checkParseTZ((5*HRS+0*MINS), "+0500");
+ checkParseTZ((11*HRS+0*MINS), "+11'00'");
+ checkParseTZ(0, "Z");
+ }
+
+ private static void checkFormatOffset(double off, String expect)
+ {
+ TimeZone tz = new SimpleTimeZone((int)(off*60*60*1000), "junkID");
+ String got = DateConverter.formatTZoffset(tz.getRawOffset(), ":");
+ assertEquals(expect, got);
+ }
+
+ /**
+ * Timezone offset testcase.
+ *
+ * @throws Exception
+ */
+ public void testFormatTZoffset()
+ {
+ checkFormatOffset(-12.1, "+11:54");
+ checkFormatOffset(12.1, "-11:54");
+ checkFormatOffset(0, "+00:00");
+ checkFormatOffset(-1, "-01:00");
+ checkFormatOffset(.5, "+00:30");
+ checkFormatOffset(-0.5, "-00:30");
+ checkFormatOffset(.1, "+00:06");
+ checkFormatOffset(-0.1, "-00:06");
+ checkFormatOffset(-12, "+00:00");
+ checkFormatOffset(12, "+00:00");
+ checkFormatOffset(-11.5, "-11:30");
+ checkFormatOffset(11.5, "+11:30");
+ checkFormatOffset(11.9, "+11:54");
+ checkFormatOffset(11.1, "+11:06");
+ checkFormatOffset(-11.9, "-11:54");
+ checkFormatOffset(-11.1, "-11:06");
+ }
+
+ // testbody precedes
+ ////////////////////////////////////////////////////
+
+ /**
* Set the tests in the suite for this test class.
*
* @return the Suite.
@@ -130,7 +460,10 @@ public class TestDateUtil extends TestCa
*/
public static void main( String[] args )
{
- String[] arg = {TestDateUtil.class.getName() };
+ String[] arg =
+ {
+ TestDateUtil.class.getName()
+ };
junit.textui.TestRunner.main( arg );
}
}
Modified: pdfbox/branches/1.8/preflight/src/main/java/org/apache/pdfbox/preflight/process/FileSpecificationValidationProcess.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/1.8/preflight/src/main/java/org/apache/pdfbox/preflight/process/FileSpecificationValidationProcess.java?rev=1542748&r1=1542747&r2=1542748&view=diff
==============================================================================
--- pdfbox/branches/1.8/preflight/src/main/java/org/apache/pdfbox/preflight/process/FileSpecificationValidationProcess.java (original)
+++ pdfbox/branches/1.8/preflight/src/main/java/org/apache/pdfbox/preflight/process/FileSpecificationValidationProcess.java Sun Nov 17 15:27:33 2013
@@ -58,7 +58,7 @@ public class FileSpecificationValidation
{
COSDictionary dic = (COSDictionary) cBase;
String type = dic.getNameAsString(COSName.TYPE);
- if (FILE_SPECIFICATION_VALUE_TYPE.equals(type))
+ if (FILE_SPECIFICATION_VALUE_TYPE.equals(type) || COSName.F.getName().equals(type))
{
// ---- It is a file specification
validateFileSpecification(ctx, dic);