55Attribute VB_Name = "pdfValue"
66Attribute VB_GlobalNameSpace = False
77Attribute VB_Creatable = False
8- Attribute VB_PredeclaredId = False
8+ Attribute VB_PredeclaredId = True
99Attribute VB_Exposed = True
1010' stores a PDF value type
1111Option Explicit
1212
13- Public id As Long
14- Public generation As Long
13+ Private m_id As Long
14+ Private m_generation As Long
1515Public valueType As PDF_ValueType
16- Public Value As Variant
16+ Public value As Variant
1717
18+ ' to help maintain id across indirect references, PDF_Object provides a PDF_Reference object to be used by others
19+ Private m_referenceObj As pdfValue
1820
21+
22+ ' keep our referencObj in sync
23+ Public Property Let id(ByVal newId As Long )
24+ m_id = newId
25+ If valueType = PDF_ValueType.PDF_Object Then
26+ If Not referenceObj Is Nothing Then
27+ referenceObj.value = m_id
28+ End If
29+ End If
30+ End Property
31+ Public Property Get id() As Long
32+ id = m_id
33+ End Property
34+
35+ ' keep our referencObj in sync
36+ Public Property Let generation(ByVal newGeneration As Long )
37+ m_generation = newGeneration
38+ If valueType = PDF_ValueType.PDF_Object Then
39+ If Not m_referenceObj Is Nothing Then
40+ m_referenceObj.generation = m_generation
41+ End If
42+ End If
43+ End Property
44+ Public Property Get generation() As Long
45+ generation = m_generation
46+ End Property
47+
48+
49+ ' returns an indirect reference object to self
50+ Friend Property Set referenceObj(ByRef refObj As pdfValue )
51+ Set m_referenceObj = refObj
52+ End Property
53+ Public Property Get referenceObj() As pdfValue
54+ ' create if not already exists
55+ If m_referenceObj Is Nothing Then
56+ Set m_referenceObj = pdfValue.NewValue(Me, "/Reference" )
57+ End If
58+
59+ ' always return same object so all references updated if id changes
60+ Set referenceObj = m_referenceObj
61+ End Property
62+
63+
64+ #If False Then
1965' is this merely an object containing a pdf dictionary? i.e. is Value a pdfValue of PDF_Dictionary
2066Function isDictionaryObject () As Boolean
21- If (valueType = PDF_ValueType.PDF_Object) And (Not Value Is Nothing ) Then
22- Dim pdfDict As pdfValue : Set pdfDict = Value
67+ If (valueType = PDF_ValueType.PDF_Object) And (Not value Is Nothing ) Then
68+ Dim pdfDict As pdfValue : Set pdfDict = value
2369 isDictionaryObject = (pdfDict.valueType = PDF_ValueType.PDF_Dictionary)
2470 End If
2571End Function
72+ #End If
2673
2774' returns vba Dictionary instance contained in pdf dictionary
28- ' Warning: returns Nothing if object lacks dictionary to return
75+ ' Warning: returns Nothing if object lacks dictionary to return, hasKey()
2976Function asDictionary () As Dictionary
3077 On Error GoTo errHandler
3178 If ((valueType = PDF_ValueType.PDF_Object) Or (valueType = PDF_ValueType.PDF_Dictionary) Or (valueType = PDF_ValueType.PDF_Trailer)) _
32- And (Not IsEmpty(Value )) Then
79+ And (Not IsEmpty(value )) Then
3380 Dim pdfDict As pdfValue
3481 If (valueType = PDF_ValueType.PDF_Dictionary) Then
3582 Set pdfDict = Me
3683 Else
37- Set pdfDict = Value
84+ Set pdfDict = value
3885 End If
39- Set asDictionary = pdfDict.Value
86+ Set asDictionary = pdfDict.value
4087 End If
4188 Exit Function
4289errHandler:
@@ -45,6 +92,20 @@ errHandler:
4592 Resume
4693End Function
4794
95+ ' returns True if Dictionary object contains key
96+ Function hasKey (ByRef key As Variant ) As Boolean
97+ On Error GoTo errHandler
98+ Dim dict As Dictionary
99+ Set dict = asDictionary()
100+ If Not dict Is Nothing Then hasKey = dict.Exists(key)
101+ 'Else return False
102+ Exit Function
103+ errHandler:
104+ Debug.Print Err.Description & " (" & Err.Number & ")"
105+ Stop
106+ Resume
107+ End Function
108+
48109
49110' Helper function to convert pdf string to byte array (0..N-1)
50111' escaping values as needed
@@ -111,41 +172,41 @@ End Function
111172
112173
113174' convert pdf Value (Me) into a Byte() array as stored in pdf file
114- Function serialize (Optional ByVal baseId As Long = 0 ) As Byte ()
175+ Public Function serialize (Optional ByVal baseId As Long = 0 ) As Byte ()
115176 On Error GoTo errHandler
116- Dim Value As pdfValue : Set Value = Me
177+ Dim value As pdfValue : Set value = Me
117178 Dim objStr As String : objStr = vbNullString
118179 Dim objBytes() As Byte
119180 Dim IsBytes As Boolean ' for most we convert at end, but stream we leave as Byte()
120181 Dim v As Variant
121182 Dim pv As pdfValue
122183 Dim firstPass As Boolean
123184
124- Select Case Value .valueType
185+ Select Case value .valueType
125186 Case PDF_ValueType.PDF_Null
126187 objStr = "null"
127188 Case PDF_ValueType.PDF_Name
128- objStr = Value.Value
189+ objStr = value.value
129190 Case PDF_ValueType.PDF_Boolean
130- If Value.Value Then
191+ If value.value Then
131192 objStr = "true"
132193 Else
133194 objStr = "false"
134195 End If
135196 Case PDF_ValueType.PDF_Integer
136- objStr = Format(CLng(Value.Value ), "0" )
197+ objStr = Format(CLng(value.value ), "0" )
137198 Case PDF_ValueType.PDF_Real
138- objStr = CDbl(Value.Value ) ' dont' format as we want all current digits stored, it won't add extra 0s anyway
199+ objStr = CDbl(value.value ) ' dont' format as we want all current digits stored, it won't add extra 0s anyway
139200 ' ensure has .0 if whole number
140201 If InStr(1 , objStr, "." , vbBinaryCompare) < 1 Then objStr = objStr & ".0"
141202 Case PDF_ValueType.PDF_String
142203 ' convert to bytes and escape values as needed
143204 IsBytes = True
144- objBytes = pdfStringToBytes(Value.Value )
205+ objBytes = pdfStringToBytes(value.value )
145206 Case PDF_ValueType.PDF_Array
146207 objStr = "[ "
147208 firstPass = True
148- For Each v In Value.Value
209+ For Each v In value.value
149210 Set pv = v
150211 If Not firstPass Then objStr = objStr & " "
151212 firstPass = False
@@ -155,44 +216,50 @@ Function serialize(Optional ByVal baseId As Long = 0) As Byte()
155216 Case PDF_ValueType.PDF_Dictionary
156217 objStr = "<<" & vbLf
157218 Dim dict As Dictionary
158- Set dict = Value.Value
219+ Set dict = value.value
159220 firstPass = True
160221 For Each v In dict.Keys
161- Set pv = dict.Item(v)
162222 If Not firstPass Then objStr = objStr & vbLf
163223 firstPass = False
164- objStr = objStr & CStr(v) & " "
224+ Dim key As String
225+ If TypeName(v) = "String" Then
226+ key = CStr(v)
227+ Else ' assume pdfValue with valueType=PDF_Name
228+ key = CStr(v.value)
229+ End If
230+ Set pv = dict.Item(key)
231+ objStr = objStr & key & " "
165232 objStr = objStr & BytesToString(pv.serialize(baseId))
166233 Next v
167234 If Right(objStr, 1 ) <> vbLf Then objStr = objStr & vbLf
168235 objStr = objStr & ">>" & vbLf
169236 Case PDF_ValueType.PDF_Stream ' actual stream object with dictionary and data
170237 Dim stream As pdfStream
171- Set stream = Value.Value
238+ Set stream = value.value
172239 IsBytes = True
173240 objBytes = stream.stream_meta.serialize(baseId)
174241 CopyBytes stream.stream_data.serialize(baseId), objBytes, 0 , UBound(objBytes) + 1
175242 Case PDF_ValueType.PDF_StreamData ' represents only stream ... endstream portion
176243 IsBytes = True
177244 objBytes = StringToBytes("stream" & vbLf)
178- CopyBytes Value.Value , objBytes, 0 , UBound(objBytes) + 1
245+ CopyBytes value.value , objBytes, 0 , UBound(objBytes) + 1
179246 CopyBytes StringToBytes(vbLf & "endstream" & vbLf), objBytes, 0 , UBound(objBytes) + 1
180247
181248 ' to simplify processing, not one of 9 basic types either
182249 Case PDF_ValueType.PDF_Object ' id generation obj << dictionary >> endobj
183250 IsBytes = True
184- objBytes = StringToBytes(baseId + Value .id & " " & Value .generation & " obj" & vbLf)
185- Dim pdfObj As pdfValue : Set pdfObj = Value.Value
251+ objBytes = StringToBytes(baseId + value .id & " " & value .generation & " obj" & vbLf)
252+ Dim pdfObj As pdfValue : Set pdfObj = value.value
186253 CopyBytes pdfObj.serialize(baseId), objBytes, 0 , UBound(objBytes) + 1
187254 If objBytes(UBound(objBytes)) <> 10 Then CopyBytes StringToBytes(vbLf), objBytes, 0 , UBound(objBytes) + 1
188255 CopyBytes StringToBytes("endobj" & vbLf), objBytes, 0 , UBound(objBytes) + 1
189256 Case PDF_ValueType.PDF_Reference ' indirect object
190257 ' Note: if indirect reference to /Parent and that obj is not in current set, will have wrong id, offset correct id by -baseId in Reference object prior to saving
191- objStr = baseId + Value.Value & " " & Value .generation & " R"
258+ objStr = baseId + value.value & " " & value .generation & " R"
192259 Case PDF_ValueType.PDF_Comment
193- objStr = Value.Value & vbLf
260+ objStr = value.value & vbLf
194261 Case PDF_ValueType.PDF_Trailer
195- Dim pdfTrailer As pdfValue : Set pdfTrailer = Value.Value
262+ Dim pdfTrailer As pdfValue : Set pdfTrailer = value.value
196263 objStr = "trailer" & vbLf
197264 objStr = objStr & BytesToString(pdfTrailer.serialize(baseId))
198265 Case Else
@@ -212,3 +279,87 @@ errHandler:
212279 Resume
213280End Function
214281
282+
283+
284+ ' construction methods, these return new instances of pdfValue
285+ ' can be used by predeclared object, e.g. pdfValue.NewValueObj("/MyName", "/Name")
286+
287+ ' returns a name as a pdfValue /Name obj
288+ Function NewNameValue (ByVal name As String ) As pdfValue
289+ Set NewNameValue = NewValue(name, "/Name" )
290+ End Function
291+
292+
293+ ' returns value as a pdfValue obj
294+ ' Note: if value is String then valueType can be used if want a PDF_Name or PDF_Trailer object instead of PDF_String
295+ ' a Dictionary returns a PDF_Dictionary and a Collection returns as PDF_Array
296+ Function NewValue (ByRef value As Variant , Optional ByRef valueType As String = vbNullString, Optional ByVal id As Long = 0 ) As pdfValue
297+ Dim obj As pdfValue
298+ Set obj = New pdfValue
299+
300+ ' initialize indirect reference object instance to be shared
301+ If id <> 0 Then
302+ obj.id = id
303+ Set obj.referenceObj = NewValue(obj, "/Reference" )
304+ End If
305+
306+ ' set obj's value and type information
307+ Select Case VarType(value)
308+ Case vbNull
309+ ' always return same instance for Null
310+ Static NullValue As pdfValue
311+ If NullValue Is Nothing Then
312+ obj.valueType = PDF_ValueType.PDF_Null
313+ Set NullValue = obj
314+ Else
315+ Set obj = NullValue
316+ End If
317+ Case vbLong, vbInteger
318+ obj.valueType = PDF_ValueType.PDF_Integer
319+ obj.value = CLng(value)
320+ Case vbSingle, vbDouble
321+ obj.valueType = PDF_ValueType.PDF_Real
322+ obj.value = CDbl(value)
323+ Case vbBoolean
324+ obj.valueType = PDF_ValueType.PDF_Boolean
325+ obj.value = CBool(value)
326+ Case vbString
327+ Select Case valueType
328+ Case "/Name"
329+ obj.valueType = PDF_ValueType.PDF_Name
330+ ' ensure /Name object begin with a /
331+ If Left(value, 1 ) <> "/" Then value = "/" & value
332+ obj.value = value
333+ Case Else
334+ obj.valueType = PDF_ValueType.PDF_String
335+ obj.value = CStr(value)
336+ End Select
337+ Case vbObject
338+ Select Case TypeName(value)
339+ Case "Dictionary"
340+ obj.valueType = PDF_ValueType.PDF_Dictionary
341+ Set obj.value = value
342+ Case "Collection"
343+ obj.valueType = PDF_ValueType.PDF_Array
344+ Set obj.value = value
345+ Case "pdfValue"
346+ Select Case valueType
347+ Case "/Trailer"
348+ obj.valueType = PDF_ValueType.PDF_Trailer
349+ Set obj.value = value
350+ Case "/Reference"
351+ obj.valueType = PDF_ValueType.PDF_Reference
352+ obj.value = value.id
353+ obj.generation = value.generation
354+ Case Else ' default to "/Object"
355+ obj.valueType = PDF_ValueType.PDF_Object
356+ Set obj.value = value
357+ End Select
358+ Case Else
359+ Stop ' ???
360+ End Select
361+ End Select
362+
363+ Set NewValue = obj
364+ End Function
365+
0 commit comments