@@ -512,6 +512,7 @@ private void legacyMergeDocuments(StreamCacheCreateFunction streamCacheCreateFun
512512 */
513513 public void appendDocument (PDDocument destination , PDDocument source ) throws IOException
514514 {
515+ PDFCloneUtility cloner = new PDFCloneUtility (destination );
515516 if (source .getDocument ().isClosed ())
516517 {
517518 throw new IOException ("Error: source PDF is closed." );
@@ -529,7 +530,7 @@ public void appendDocument(PDDocument destination, PDDocument source) throws IOE
529530
530531 PDDocumentInformation destInfo = destination .getDocumentInformation ();
531532 PDDocumentInformation srcInfo = source .getDocumentInformation ();
532- mergeInto (srcInfo .getCOSObject (), destInfo .getCOSObject (), Collections .emptySet ());
533+ mergeInto (srcInfo .getCOSObject (), destInfo .getCOSObject (), cloner , Collections .emptySet ());
533534
534535 // use the highest version number for the resulting pdf
535536 float destVersion = destination .getVersion ();
@@ -541,8 +542,6 @@ public void appendDocument(PDDocument destination, PDDocument source) throws IOE
541542 }
542543
543544 PDDocumentCatalog destCatalog = destination .getDocumentCatalog ();
544- PDFCloneUtility cloner = new PDFCloneUtility (destination );
545-
546545 mergeAcroForm (cloner , destCatalog , srcCatalog );
547546
548547 COSArray destThreads = destCatalog .getCOSObject ().getCOSArray (COSName .THREADS );
@@ -688,7 +687,7 @@ public void appendDocument(PDDocument destination, PDDocument source) throws IOE
688687 try
689688 {
690689 PDStream newStream = new PDStream (destination , srcMetadata .createInputStream (), (COSName ) null );
691- mergeInto (srcMetadata , newStream .getCOSObject (),
690+ mergeInto (srcMetadata , newStream .getCOSObject (), cloner ,
692691 new HashSet <>(Arrays .asList (COSName .FILTER , COSName .LENGTH )));
693692 destCatalog .getCOSObject ().setItem (COSName .METADATA , newStream );
694693 }
@@ -710,7 +709,7 @@ else if (destOCP != null && srcOCP != null)
710709 cloner .cloneMerge (srcOCP , destOCP );
711710 }
712711
713- mergeOutputIntents (cloner , srcCatalog , destCatalog );
712+ mergeOutputIntents (srcCatalog , destCatalog , cloner );
714713
715714 // merge logical structure hierarchy
716715 boolean mergeStructTree = false ;
@@ -770,7 +769,6 @@ else if (destOCP != null && srcOCP != null)
770769 }
771770
772771 Map <COSDictionary , COSDictionary > objMapping = new HashMap <>();
773- int pageIndex = 0 ;
774772 PDPageTree destinationPageTree = destination .getPages (); // cache PageTree
775773 for (PDPage page : srcCatalog .getPages ())
776774 {
@@ -813,7 +811,6 @@ else if (destOCP != null && srcOCP != null)
813811 // TODO update mapping for XObjects
814812 }
815813 destinationPageTree .add (newPage );
816- ++pageIndex ;
817814 }
818815 mergeOpenAction (srcCatalog , destCatalog , cloner );
819816 if (mergeStructTree )
@@ -832,19 +829,19 @@ else if (destOCP != null && srcOCP != null)
832829
833830 // Note that all elements are stored flatly. This could become a problem for large files
834831 // when these are opened in a viewer that uses the tagging information.
835- // If this happens, then PDNumberTreeNode should be improved with a convenience method that
832+ // If this happens, then PDNumberTreeNode should be improved with a convenience method that
836833 // stores the map into a B+Tree, see https://en.wikipedia.org/wiki/B+_tree
837834 newParentTreeNode .setNumbers (destNumberTreeAsMap );
838835
839836 destStructTree .setParentTree (newParentTreeNode );
840837 destStructTree .setParentTreeNextKey (destParentTreeNextKey );
841838
842839 mergeKEntries (cloner , srcStructTree , destStructTree );
843- mergeRoleMap (srcStructTree , destStructTree );
840+ mergeRoleMap (srcStructTree , destStructTree , cloner );
844841 mergeIDTree (cloner , srcStructTree , destStructTree );
845842 mergeMarkInfo (destCatalog , srcCatalog );
846843 mergeLanguage (destCatalog , srcCatalog );
847- mergeViewerPreferences (destCatalog , srcCatalog );
844+ mergeViewerPreferences (destCatalog , srcCatalog , cloner );
848845 }
849846 }
850847
@@ -893,7 +890,8 @@ else if (clonedOpenActionBase instanceof COSArray)
893890 }
894891 }
895892
896- private void mergeViewerPreferences (PDDocumentCatalog destCatalog , PDDocumentCatalog srcCatalog )
893+ private void mergeViewerPreferences (PDDocumentCatalog destCatalog , PDDocumentCatalog srcCatalog ,
894+ PDFCloneUtility cloner ) throws IOException
897895 {
898896 PDViewerPreferences srcViewerPreferences = srcCatalog .getViewerPreferences ();
899897 if (srcViewerPreferences == null )
@@ -906,7 +904,7 @@ private void mergeViewerPreferences(PDDocumentCatalog destCatalog, PDDocumentCat
906904 destViewerPreferences = new PDViewerPreferences ();
907905 destCatalog .setViewerPreferences (destViewerPreferences );
908906 }
909- mergeInto (srcViewerPreferences .getCOSObject (), destViewerPreferences .getCOSObject (),
907+ mergeInto (srcViewerPreferences .getCOSObject (), destViewerPreferences .getCOSObject (), cloner ,
910908 Collections .emptySet ());
911909
912910 // check the booleans - set to true if one is set and true
@@ -1106,7 +1104,7 @@ private void mergeIDTree(PDFCloneUtility cloner,
11061104 {
11071105 if (destNames .containsKey (entry .getKey ()))
11081106 {
1109- LOG .warn ("key {} already exists in destination IDTree" , entry .getKey ());
1107+ LOG .warn ("key '{}' already exists in destination IDTree" , entry .getKey ());
11101108 }
11111109 else
11121110 {
@@ -1183,7 +1181,8 @@ static Map<Integer, COSObjectable> getNumberTreeAsMap(PDNumberTreeNode tree)
11831181 return numbers ;
11841182 }
11851183
1186- private void mergeRoleMap (PDStructureTreeRoot srcStructTree , PDStructureTreeRoot destStructTree )
1184+ private void mergeRoleMap (PDStructureTreeRoot srcStructTree , PDStructureTreeRoot destStructTree ,
1185+ PDFCloneUtility cloner ) throws IOException
11871186 {
11881187 COSDictionary srcDict = srcStructTree .getCOSObject ().getCOSDictionary (COSName .ROLE_MAP );
11891188 if (srcDict == null )
@@ -1193,7 +1192,7 @@ private void mergeRoleMap(PDStructureTreeRoot srcStructTree, PDStructureTreeRoot
11931192 COSDictionary destDict = destStructTree .getCOSObject ().getCOSDictionary (COSName .ROLE_MAP );
11941193 if (destDict == null )
11951194 {
1196- destStructTree .getCOSObject ().setItem (COSName .ROLE_MAP , srcDict ); // clone not needed
1195+ destStructTree .getCOSObject ().setItem (COSName .ROLE_MAP , cloner . cloneForNewDocument ( srcDict ));
11971196 return ;
11981197 }
11991198 for (Map .Entry <COSName , COSBase > entry : srcDict .entrySet ())
@@ -1206,11 +1205,11 @@ private void mergeRoleMap(PDStructureTreeRoot srcStructTree, PDStructureTreeRoot
12061205 }
12071206 if (destDict .containsKey (entry .getKey ()))
12081207 {
1209- LOG .warn ("key {} already exists in destination RoleMap" , entry .getKey ());
1208+ LOG .warn ("key '{}' already exists in destination RoleMap" , entry .getKey (). getName ());
12101209 }
12111210 else
12121211 {
1213- destDict .setItem (entry .getKey (), entry .getValue ());
1212+ destDict .setItem (entry .getKey (), cloner . cloneForNewDocument ( entry .getValue () ));
12141213 }
12151214 }
12161215 }
@@ -1332,11 +1331,9 @@ private void acroFormLegacyMode(PDFCloneUtility cloner, PDAcroForm destAcroForm,
13321331 }
13331332 }
13341333
1335-
13361334 // copy outputIntents to destination, but avoid duplicate OutputConditionIdentifier,
13371335 // except when it is missing or is named "Custom".
1338- private void mergeOutputIntents (PDFCloneUtility cloner ,
1339- PDDocumentCatalog srcCatalog , PDDocumentCatalog destCatalog ) throws IOException
1336+ private void mergeOutputIntents (PDDocumentCatalog srcCatalog , PDDocumentCatalog destCatalog , PDFCloneUtility cloner ) throws IOException
13401337 {
13411338 List <PDOutputIntent > srcOutputIntents = srcCatalog .getOutputIntents ();
13421339 List <PDOutputIntent > dstOutputIntents = destCatalog .getOutputIntents ();
@@ -1541,13 +1538,13 @@ private boolean isDynamicXfa(PDAcroForm acroForm)
15411538 * @param dst The destination dictionary to merge the keys/values into.
15421539 * @param exclude Names of keys that shall be skipped.
15431540 */
1544- private void mergeInto (COSDictionary src , COSDictionary dst , Set <COSName > exclude )
1541+ private void mergeInto (COSDictionary src , COSDictionary dst , PDFCloneUtility cloner , Set <COSName > exclude ) throws IOException
15451542 {
15461543 for (Map .Entry <COSName , COSBase > entry : src .entrySet ())
15471544 {
15481545 if (!exclude .contains (entry .getKey ()) && !dst .containsKey (entry .getKey ()))
15491546 {
1550- dst .setItem (entry .getKey (), entry .getValue ());
1547+ dst .setItem (entry .getKey (), cloner . cloneForNewDocument ( entry .getValue () ));
15511548 }
15521549 }
15531550 }
0 commit comments