@@ -17,11 +17,13 @@ End Sub
1717Public Sub CombinePDFs (ByRef sourceFiles() As String , ByRef outFile As String )
1818 Dim combinedPdfDoc As pdfDocument : Set combinedPdfDoc = New pdfDocument
1919 ' initialize with some basic structures
20- combinedPdfDoc.AddInfo
21- combinedPdfDoc.AddPages
22- combinedPdfDoc.AddOutlines
23- With combinedPdfDoc.rootCatalog.asDictionary()
24- Set .Item("/PageMode" ) = pdfValue.NewNameValue("/UseOutlines" ) ' default is "/UseNone"
20+ With combinedPdfDoc
21+ .AddInfo
22+ .AddPages
23+ .AddOutlines
24+ With .rootCatalog.asDictionary()
25+ Set .item("/PageMode" ) = pdfValue.NewNameValue("/UseOutlines" ) ' default is "/UseNone"
26+ End With
2527 End With
2628
2729 Dim offset As Long
@@ -42,16 +44,53 @@ Public Sub CombinePDFs(ByRef sourceFiles() As String, ByRef outFile As String)
4244 pdfDoc.parsePdf
4345
4446 ' adjust obj id's so no conflict with previously stored ones
45- pdfDoc.renumberIds baseId
47+ pdfDoc.renumberIds baseId + 1 ' +1 as baseId used for our /OutlineItem
4648
49+ ' add the first page of this document as a Named Destination to our combined pdf
50+ ' Note: we assume current pageCount is how many existing pages there are, with
51+ ' the next page being 1st of just loaded document, then subtract 1 as page# begins with 0
52+ Dim docDestinationName As pdfValue
53+ Set docDestinationName = pdfValue.NewNameValue("/" & pdfDoc.Title & ".1" , utf8:=True )
54+ Dim docDestination As pdfValue
55+ Set docDestination = combinedPdfDoc.NewDestination(combinedPdfDoc.pageCount, PDF_FIT.PDF_FIT)
56+ combinedPdfDoc.AddNamedDestinations docDestinationName, docDestination
57+
58+ ' copy over any pre-existing Named Destinations
59+ ' Note: these are in a <<dictionary>> in /Root/Pages so not automatically included
60+ If pdfDoc.Dests.valueType = PDF_ValueType.PDF_Dictionary Then
61+ Dim dict As Dictionary : Set dict = pdfDoc.Dests.asDictionary()
62+ If Not dict Is Nothing Then
63+ Dim v As Variant
64+ For Each v In dict.Keys
65+ ' we treat name as PDF_String instead of PDF_Name to avoid potential double escaping
66+ ' and because while PDF_Name recommended, not required so could just be a PDF_String
67+ Dim name As pdfValue : Set name = pdfValue.NewValue(v)
68+ combinedPdfDoc.AddNamedDestinations name, dict(v)
69+ Next v
70+ Set v = Nothing
71+ End If
72+ Set dict = Nothing
73+ End If
74+
4775 ' we inject a new top level /Pages object which we add all the document /Pages to
4876 ' so we need to add this /Pages to our top level /Pages and add a /Parent indirect reference
49- combinedPdfDoc.AddPages pdfDoc.pages
77+ combinedPdfDoc.AddPages pdfDoc.Pages
5078
5179 ' we need to copy/merge some optional fields such as /Outline for bookmarks
52- If pdfDoc.rootCatalog.hasKey("/Outlines" ) Then
80+ If False And pdfDoc.rootCatalog.hasKey("/Outlines" ) Then
81+ ' Note: these are objs in pdfDoc so we need to remove if we add equivalent ones to combindedPdfDoc to avoid duplicates
5382 ' hack for now, just copy over
54- combinedPdfDoc.rootCatalog.asDictionary().Add "/Outlines" , pdfDoc.rootCatalog.asDictionary().Item("/Outlines" )
83+ 'combinedPdfDoc.rootCatalog.asDictionary().Add "/Outlines", pdfDoc.rootCatalog.asDictionary().item("/Outlines")
84+ ' TODO
85+ Else
86+ ' defaults should include /First /Last along with /Count, /Title /Prev /Next and optionally /Dest
87+ Dim defaults As Dictionary : Set defaults = New Dictionary
88+ Set defaults("/Title" ) = pdfValue.NewValue(pdfDoc.Title)
89+ Set defaults("/Dest" ) = docDestinationName
90+ Dim outlineItem As pdfValue
91+ Set outlineItem = combinedPdfDoc.NewOutlineItem(combinedPdfDoc.Outlines, defaults)
92+ combinedPdfDoc.AddOutlines outlineItem
93+ ' don't save here as we may need to adjust /Prev & /Next values
5594 End If
5695
5796 ' remove /Root object and ensure only left with 1 /Root
@@ -67,13 +106,28 @@ Public Sub CombinePDFs(ByRef sourceFiles() As String, ByRef outFile As String)
67106
68107 ' determine highest id used, 1st obj in next file will start at this + 1
69108 ' Note: we need to use pdfDoc.xrefTable's size and not combinedPdfDoc.xrefTable as we are reserving full count from just loaded pdf document
70- baseId = baseId + pdfDoc.xrefTable.Count - 1 ' highest id possible so far
109+ baseId = baseId + pdfDoc.xrefTable.count - 1 ' highest id possible so far
71110 combinedPdfDoc.nextObjId = baseId + 1
72111 DoEvents
73112 Next ndx
74113
114+ combinedPdfDoc.SavePdfObject outputFileNum, combinedPdfDoc.Outlines, offset
115+ If combinedPdfDoc.Outlines.hasKey("/First" ) Then
116+ Dim outlineObj As pdfValue
117+ Set outlineObj = combinedPdfDoc.Outlines.asDictionary().item("/First" )
118+ Set outlineObj = combinedPdfDoc.getObject(outlineObj.value, outlineObj.generation)
119+ Do While Not outlineObj Is Nothing
120+ combinedPdfDoc.SavePdfObject outputFileNum, outlineObj, offset
121+ If outlineObj.hasKey("/Next" ) Then
122+ Set outlineObj = outlineObj.asDictionary().item("/Next" )
123+ Set outlineObj = combinedPdfDoc.getObject(outlineObj.value, outlineObj.generation)
124+ Else
125+ Set outlineObj = Nothing
126+ End If
127+ Loop
128+ End If
75129 ' save updated /Pages object (but not nested objects as already saved)
76- combinedPdfDoc.SavePdfObject outputFileNum, combinedPdfDoc.pages , offset
130+ combinedPdfDoc.SavePdfObject outputFileNum, combinedPdfDoc.Pages , offset
77131 combinedPdfDoc.SavePdfObject outputFileNum, combinedPdfDoc.rootCatalog, offset
78132
79133 ' writes out trailer and cross reference table
@@ -101,9 +155,9 @@ Function PickFiles(Optional ByVal AllowMultiSelect As Boolean = True) As String(
101155 If .Show = -1 Then
102156 'Step through each string in the FileDialogSelectedItems collection.
103157 Dim files() As String
104- ReDim files(0 To .SelectedItems.Count - 1 )
158+ ReDim files(0 To .SelectedItems.count - 1 )
105159 Dim ndx As Long
106- For ndx = 1 To .SelectedItems.Count
160+ For ndx = 1 To .SelectedItems.count
107161 files(ndx - 1 ) = .SelectedItems(ndx)
108162 Next ndx
109163 PickFiles = files
0 commit comments