origami 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. data/COPYING.LESSER +165 -0
  2. data/README +77 -0
  3. data/VERSION +1 -0
  4. data/bin/config/pdfcop.conf.yml +237 -0
  5. data/bin/gui/about.rb +46 -0
  6. data/bin/gui/config.rb +132 -0
  7. data/bin/gui/file.rb +385 -0
  8. data/bin/gui/hexdump.rb +74 -0
  9. data/bin/gui/hexview.rb +91 -0
  10. data/bin/gui/imgview.rb +72 -0
  11. data/bin/gui/menu.rb +392 -0
  12. data/bin/gui/properties.rb +132 -0
  13. data/bin/gui/signing.rb +635 -0
  14. data/bin/gui/textview.rb +107 -0
  15. data/bin/gui/treeview.rb +409 -0
  16. data/bin/gui/walker.rb +282 -0
  17. data/bin/gui/xrefs.rb +79 -0
  18. data/bin/pdf2graph +121 -0
  19. data/bin/pdf2ruby +353 -0
  20. data/bin/pdfcocoon +104 -0
  21. data/bin/pdfcop +455 -0
  22. data/bin/pdfdecompress +104 -0
  23. data/bin/pdfdecrypt +95 -0
  24. data/bin/pdfencrypt +112 -0
  25. data/bin/pdfextract +221 -0
  26. data/bin/pdfmetadata +123 -0
  27. data/bin/pdfsh +13 -0
  28. data/bin/pdfwalker +7 -0
  29. data/bin/shell/.irbrc +104 -0
  30. data/bin/shell/console.rb +136 -0
  31. data/bin/shell/hexdump.rb +83 -0
  32. data/origami.rb +36 -0
  33. data/origami/3d.rb +239 -0
  34. data/origami/acroform.rb +321 -0
  35. data/origami/actions.rb +299 -0
  36. data/origami/adobe/fdf.rb +259 -0
  37. data/origami/adobe/ppklite.rb +489 -0
  38. data/origami/annotations.rb +775 -0
  39. data/origami/array.rb +187 -0
  40. data/origami/boolean.rb +101 -0
  41. data/origami/catalog.rb +486 -0
  42. data/origami/destinations.rb +213 -0
  43. data/origami/dictionary.rb +188 -0
  44. data/origami/docmdp.rb +96 -0
  45. data/origami/encryption.rb +1293 -0
  46. data/origami/export.rb +283 -0
  47. data/origami/file.rb +222 -0
  48. data/origami/filters.rb +250 -0
  49. data/origami/filters/ascii.rb +189 -0
  50. data/origami/filters/ccitt.rb +515 -0
  51. data/origami/filters/crypt.rb +47 -0
  52. data/origami/filters/dct.rb +61 -0
  53. data/origami/filters/flate.rb +112 -0
  54. data/origami/filters/jbig2.rb +63 -0
  55. data/origami/filters/jpx.rb +53 -0
  56. data/origami/filters/lzw.rb +195 -0
  57. data/origami/filters/predictors.rb +276 -0
  58. data/origami/filters/runlength.rb +117 -0
  59. data/origami/font.rb +209 -0
  60. data/origami/functions.rb +93 -0
  61. data/origami/graphics.rb +33 -0
  62. data/origami/graphics/colors.rb +191 -0
  63. data/origami/graphics/instruction.rb +126 -0
  64. data/origami/graphics/path.rb +154 -0
  65. data/origami/graphics/patterns.rb +180 -0
  66. data/origami/graphics/state.rb +164 -0
  67. data/origami/graphics/text.rb +224 -0
  68. data/origami/graphics/xobject.rb +493 -0
  69. data/origami/header.rb +90 -0
  70. data/origami/linearization.rb +318 -0
  71. data/origami/metadata.rb +114 -0
  72. data/origami/name.rb +170 -0
  73. data/origami/null.rb +75 -0
  74. data/origami/numeric.rb +188 -0
  75. data/origami/obfuscation.rb +233 -0
  76. data/origami/object.rb +527 -0
  77. data/origami/outline.rb +59 -0
  78. data/origami/page.rb +559 -0
  79. data/origami/parser.rb +268 -0
  80. data/origami/parsers/fdf.rb +45 -0
  81. data/origami/parsers/pdf.rb +27 -0
  82. data/origami/parsers/pdf/linear.rb +113 -0
  83. data/origami/parsers/ppklite.rb +86 -0
  84. data/origami/pdf.rb +1144 -0
  85. data/origami/reference.rb +113 -0
  86. data/origami/signature.rb +474 -0
  87. data/origami/stream.rb +575 -0
  88. data/origami/string.rb +416 -0
  89. data/origami/trailer.rb +173 -0
  90. data/origami/webcapture.rb +87 -0
  91. data/origami/xfa.rb +3027 -0
  92. data/origami/xreftable.rb +447 -0
  93. data/templates/patterns.rb +66 -0
  94. data/templates/widgets.rb +173 -0
  95. data/templates/xdp.rb +92 -0
  96. data/tests/dataset/test.dummycrt +28 -0
  97. data/tests/dataset/test.dummykey +27 -0
  98. data/tests/tc_actions.rb +32 -0
  99. data/tests/tc_annotations.rb +85 -0
  100. data/tests/tc_pages.rb +37 -0
  101. data/tests/tc_pdfattach.rb +24 -0
  102. data/tests/tc_pdfencrypt.rb +110 -0
  103. data/tests/tc_pdfnew.rb +32 -0
  104. data/tests/tc_pdfparse.rb +98 -0
  105. data/tests/tc_pdfsig.rb +37 -0
  106. data/tests/tc_streams.rb +129 -0
  107. data/tests/ts_pdf.rb +45 -0
  108. metadata +193 -0
@@ -0,0 +1,493 @@
1
+ =begin
2
+
3
+ = File
4
+ graphics/xobject.rb
5
+
6
+ = Info
7
+ This file is part of Origami, PDF manipulation framework for Ruby
8
+ Copyright (C) 2010 Guillaume Delugré <guillaume@security-labs.org>
9
+ All right reserved.
10
+
11
+ Origami is free software: you can redistribute it and/or modify
12
+ it under the terms of the GNU Lesser General Public License as published by
13
+ the Free Software Foundation, either version 3 of the License, or
14
+ (at your option) any later version.
15
+
16
+ Origami is distributed in the hope that it will be useful,
17
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ GNU Lesser General Public License for more details.
20
+
21
+ You should have received a copy of the GNU Lesser General Public License
22
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
23
+
24
+ =end
25
+
26
+ module Origami
27
+
28
+ #
29
+ # A class representing a Stream containing the contents of a Page.
30
+ #
31
+ class ContentStream < Stream
32
+
33
+ DEFAULT_SIZE = 12
34
+ DEFAULT_FONT = :F1
35
+ DEFAULT_LEADING = 20
36
+ DEFAULT_STROKE_COLOR = Graphics::Color::GrayScale.new(0.0)
37
+ DEFAULT_FILL_COLOR = Graphics::Color::GrayScale.new(1.0)
38
+ DEFAULT_LINECAP = Graphics::LineCapStyle::BUTT_CAP
39
+ DEFAULT_LINEJOIN = Graphics::LineJoinStyle::MITER_JOIN
40
+ DEFAULT_DASHPATTERN = Graphics::DashPattern.new([], 0)
41
+ DEFAULT_LINEWIDTH = 1.0
42
+
43
+ attr_reader :instructions
44
+
45
+ def initialize(rawdata = "", dictionary = {})
46
+
47
+ @instructions = nil
48
+ @gs = Graphics::State.new
49
+
50
+ super(rawdata, dictionary)
51
+ end
52
+
53
+ def pre_build #:nodoc:
54
+ load! if @instructions.nil?
55
+ if @gs.text_state.is_in_text_object?
56
+ @instructions << PDF::Instruction.new('ET').update_state(@gs)
57
+ end
58
+
59
+ @data = @instructions.join
60
+
61
+ super
62
+ end
63
+
64
+ def instructions
65
+ load! if @instructions.nil?
66
+
67
+ @instructions
68
+ end
69
+
70
+ #
71
+ # Draw a straight line from the point at coord _from_, to the point at coord _to_.
72
+ #
73
+ def draw_line(from, to, attr = {})
74
+ draw_polygon([from, to], attr)
75
+ end
76
+
77
+ #
78
+ # Draw a polygon from a array of coordinates.
79
+ #
80
+ def draw_polygon(coords = [], attr = {})
81
+ load! if @instructions.nil?
82
+
83
+ stroke_color = attr[:stroke_color] || DEFAULT_STROKE_COLOR
84
+ fill_color = attr[:fill_color] || DEFAULT_FILL_COLOR
85
+ line_cap = attr[:line_cap] || DEFAULT_LINECAP
86
+ line_join = attr[:line_join] || DEFAULT_LINEJOIN
87
+ line_width = attr[:line_width] || DEFAULT_LINEWIDTH
88
+ dash_pattern = attr[:dash] || DEFAULT_DASHPATTERN
89
+
90
+ stroke = attr[:stroke].nil? ? true : attr[:stroke]
91
+ fill = attr[:fill].nil? ? false : attr[:fill]
92
+
93
+ stroke = true if fill == false and stroke == false
94
+
95
+ set_fill_color(fill_color) if fill
96
+ set_stroke_color(stroke_color) if stroke
97
+ set_line_width(line_width)
98
+ set_line_cap(line_cap)
99
+ set_line_join(line_join)
100
+ set_dash_pattern(dash_pattern)
101
+
102
+ if @gs.text_state.is_in_text_object?
103
+ @instructions << PDF::Instruction.new('ET').update_state(@gs)
104
+ end
105
+
106
+ unless coords.size < 1
107
+ x,y = coords.slice!(0)
108
+ @instructions << PDF::Instruction.new('m',x,y).update_state(@gs)
109
+
110
+ coords.each do |px,py|
111
+ @instructions << PDF::Instruction.new('l',px,py).update_state(@gs)
112
+ end
113
+
114
+ @instructions << (i =
115
+ if stroke and not fill
116
+ PDF::Instruction.new('s')
117
+ elsif fill and not stroke
118
+ PDF::Instruction.new('f')
119
+ elsif fill and stroke
120
+ PDF::Instruction.new('b')
121
+ end
122
+ )
123
+
124
+ i.update_state(@gs)
125
+ end
126
+
127
+ self
128
+ end
129
+
130
+ #
131
+ # Draw a rectangle at position (_x_,_y_) with defined _width_ and _height_.
132
+ #
133
+ def draw_rectangle(x, y, width, height, attr = {})
134
+ load! if @instructions.nil?
135
+
136
+ stroke_color = attr[:stroke_color] || DEFAULT_STROKE_COLOR
137
+ fill_color = attr[:fill_color] || DEFAULT_FILL_COLOR
138
+ line_cap = attr[:line_cap] || DEFAULT_LINECAP
139
+ line_join = attr[:line_join] || DEFAULT_LINEJOIN
140
+ line_width = attr[:line_width] || DEFAULT_LINEWIDTH
141
+ dash_pattern = attr[:dash] || DEFAULT_DASHPATTERN
142
+
143
+ stroke = attr[:stroke].nil? ? true : attr[:stroke]
144
+ fill = attr[:fill].nil? ? false : attr[:fill]
145
+
146
+ stroke = true if fill == false and stroke == false
147
+
148
+ set_fill_color(fill_color) if fill
149
+ set_stroke_color(stroke_color) if stroke
150
+ set_line_width(line_width)
151
+ set_line_cap(line_cap)
152
+ set_line_join(line_join)
153
+ set_dash_pattern(dash_pattern)
154
+
155
+ if @gs.text_state.is_in_text_object?
156
+ @instructions << PDF::Instruction.new('ET').update_state(@gs)
157
+ end
158
+
159
+ @instructions << PDF::Instruction.new('re', x,y,width,height).update_state(@gs)
160
+
161
+ @instructions << (i =
162
+ if stroke and not fill
163
+ PDF::Instruction.new('S')
164
+ elsif fill and not stroke
165
+ PDF::Instruction.new('f')
166
+ elsif fill and stroke
167
+ PDF::Instruction.new('B')
168
+ end
169
+ )
170
+
171
+ i.update_state(@gs)
172
+
173
+ self
174
+ end
175
+
176
+ #
177
+ # Adds text to the content stream with custom formatting attributes.
178
+ # _text_:: Text to write.
179
+ # _attr_:: Formatting attributes.
180
+ #
181
+ def write(text, attr = {})
182
+ load! if @instructions.nil?
183
+
184
+ x,y = attr[:x], attr[:y]
185
+ font = attr[:font] || DEFAULT_FONT
186
+ size = attr[:size] || DEFAULT_SIZE
187
+ leading = attr[:leading] || DEFAULT_LEADING
188
+ color = attr[:color] || attr[:fill_color] || DEFAULT_STROKE_COLOR
189
+ stroke_color = attr[:stroke_color] || DEFAULT_STROKE_COLOR
190
+ line_width = attr[:line_width] || DEFAULT_LINEWIDTH
191
+ word_spacing = attr[:word_spacing]
192
+ char_spacing = attr[:char_spacing]
193
+ scale = attr[:scale]
194
+ rise = attr[:rise]
195
+ rendering = attr[:rendering]
196
+
197
+ @instructions << PDF::Instruction.new('ET').update_state(@gs) if (x or y) and @gs.text_state.is_in_text_object?
198
+
199
+ unless @gs.text_state.is_in_text_object?
200
+ @instructions << PDF::Instruction.new('BT').update_state(@gs)
201
+ end
202
+
203
+ set_text_font(font, size)
204
+ set_text_pos(x, y) if x or y
205
+ set_text_leading(leading) if leading
206
+ set_text_rendering(rendering) if rendering
207
+ set_text_rise(rise) if rise
208
+ set_text_scale(scale) if scale
209
+ set_text_word_spacing(word_spacing) if word_spacing
210
+ set_text_char_spacing(char_spacing) if char_spacing
211
+ set_fill_color(color)
212
+ set_stroke_color(stroke_color)
213
+ set_line_width(line_width)
214
+
215
+ write_text_block(text)
216
+
217
+ self
218
+ end
219
+
220
+ def paint_shading(shade)
221
+ load! if @instructions.nil?
222
+ @instructions << PDF::Instruction.new('sh', shade).update_state(@gs)
223
+
224
+ self
225
+ end
226
+
227
+ def set_text_font(fontname, size)
228
+ load! if @instructions.nil?
229
+ if fontname != @gs.text_state.font or size != @gs.text_state.font_size
230
+ @instructions << PDF::Instruction.new('Tf', fontname, size).update_state(@gs)
231
+ end
232
+
233
+ self
234
+ end
235
+
236
+ def set_text_pos(tx,ty)
237
+ load! if @instructions.nil?
238
+ @instructions << PDF::Instruction.new('Td', tx, ty).update_state(@gs)
239
+
240
+ self
241
+ end
242
+
243
+ def set_text_leading(leading)
244
+ load! if @instructions.nil?
245
+ if leading != @gs.text_state.leading
246
+ @instructions << PDF::Instruction.new('TL', leading).update_state(@gs)
247
+ end
248
+
249
+ self
250
+ end
251
+
252
+ def set_text_rendering(rendering)
253
+ load! if @instructions.nil?
254
+ if rendering != @gs.text_state.rendering_mode
255
+ @instructions << PDF::Instruction.new('Tr', rendering).update_state(@gs)
256
+ end
257
+
258
+ self
259
+ end
260
+
261
+ def set_text_rise(rise)
262
+ load! if @instructions.nil?
263
+ if rise != @gs.text_state.text_rise
264
+ @instructions << PDF::Instruction.new('Ts', rise).update_state(@gs)
265
+ end
266
+
267
+ self
268
+ end
269
+
270
+ def set_text_scale(scaling)
271
+ load! if @instructions.nil?
272
+ if scale != @gs.text_state.scaling
273
+ @instructions << PDF::Instruction.new('Tz', scaling).update_state(@gs)
274
+ end
275
+
276
+ self
277
+ end
278
+
279
+ def set_text_word_spacing(word_spacing)
280
+ load! if @instructions.nil?
281
+ if word_spacing != @gs.text_state.word_spacing
282
+ @instructions << PDF::Instruction.new('Tw', word_spacing).update_state(@gs)
283
+ end
284
+
285
+ self
286
+ end
287
+
288
+ def set_text_char_spacing(char_spacing)
289
+ load! if @instructions.nil?
290
+ if char_spacing != @gs.text_state.char_spacing
291
+ @instructions << PDF::Instruction.new('Tc', char_spacing).update_state(@gs)
292
+ end
293
+
294
+ self
295
+ end
296
+
297
+ def set_fill_color(color)
298
+ load! if @instructions.nil?
299
+
300
+ @instructions << ( i =
301
+ if (color.respond_to? :r and color.respond_to? :g and color.respond_to? :b) or (color.is_a?(::Array) and color.size == 3)
302
+ r = (color.respond_to?(:r) ? color.r : color[0]).to_f / 255
303
+ g = (color.respond_to?(:g) ? color.g : color[1]).to_f / 255
304
+ b = (color.respond_to?(:b) ? color.b : color[2]).to_f / 255
305
+ PDF::Instruction.new('rg', r, g, b) if @gs.nonstroking_color != [r,g,b]
306
+
307
+ elsif (color.respond_to? :c and color.respond_to? :m and color.respond_to? :y and color.respond_to? :k) or (color.is_a?(::Array) and color.size == 4)
308
+ c = (color.respond_to?(:c) ? color.c : color[0]).to_f
309
+ m = (color.respond_to?(:m) ? color.m : color[1]).to_f
310
+ y = (color.respond_to?(:y) ? color.y : color[2]).to_f
311
+ k = (color.respond_to?(:k) ? color.k : color[3]).to_f
312
+ PDF::Instruction.new('k', c, m, y, k) if @gs.nonstroking_color != [c,m,y,k]
313
+
314
+ elsif color.respond_to?:g or (0.0..1.0) === color
315
+ g = color.respond_to?(:g) ? color.g : color
316
+ PDF::Instruction.new('g', g) if @gs.nonstroking_color != [ g ]
317
+
318
+ else
319
+ raise TypeError, "Invalid color : #{color}"
320
+ end
321
+ )
322
+
323
+ i.update_state(@gs) if i
324
+ self
325
+ end
326
+
327
+ def set_stroke_color(color)
328
+ load! if @instructions.nil?
329
+
330
+ @instructions << ( i =
331
+ if (color.respond_to? :r and color.respond_to? :g and color.respond_to? :b) or (color.is_a?(::Array) and color.size == 3)
332
+ r = (color.respond_to?(:r) ? color.r : color[0]).to_f / 255
333
+ g = (color.respond_to?(:g) ? color.g : color[1]).to_f / 255
334
+ b = (color.respond_to?(:b) ? color.b : color[2]).to_f / 255
335
+ PDF::Instruction.new('RG', r, g, b) if @gs.stroking_color != [r,g,b]
336
+
337
+ elsif (color.respond_to? :c and color.respond_to? :m and color.respond_to? :y and color.respond_to? :k) or (color.is_a?(::Array) and color.size == 4)
338
+ c = (color.respond_to?(:c) ? color.c : color[0]).to_f
339
+ m = (color.respond_to?(:m) ? color.m : color[1]).to_f
340
+ y = (color.respond_to?(:y) ? color.y : color[2]).to_f
341
+ k = (color.respond_to?(:k) ? color.k : color[3]).to_f
342
+ PDF::Instruction.new('K', c, m, y, k) if @gs.stroking_color != [c,m,y,k]
343
+
344
+ elsif color.respond_to?:g or (0.0..1.0) === color
345
+ g = color.respond_to?(:g) ? color.g : color
346
+ PDF::Instruction.new('G', g) if @gs.stroking_color != [ g ]
347
+
348
+ else
349
+ raise TypeError, "Invalid color : #{color}"
350
+ end
351
+ )
352
+
353
+ i.update_state(@gs) if i
354
+ self
355
+ end
356
+
357
+ def set_dash_pattern(pattern)
358
+ load! if @instructions.nil?
359
+ unless @gs.dash_pattern.eql? pattern
360
+ @instructions << PDF::Instruction.new('d', pattern.array, pattern.phase).update_state(@gs)
361
+ end
362
+
363
+ self
364
+ end
365
+
366
+ def set_line_width(width)
367
+ load! if @instructions.nil?
368
+ if @gs.line_width != width
369
+ @instructions << PDF::Instruction.new('w', width).update_state(@gs)
370
+ end
371
+
372
+ self
373
+ end
374
+
375
+ def set_line_cap(cap)
376
+ load! if @instructions.nil?
377
+ if @gs.line_cap != cap
378
+ @instructions << PDF::Instruction.new('J', cap).update_state(@gs)
379
+ end
380
+
381
+ self
382
+ end
383
+
384
+ def set_line_join(join)
385
+ load! if @instructions.nil?
386
+ if @gs.line_join != join
387
+ @instructions << PDF::Instruction.new('j', join).update_state(@gs)
388
+ end
389
+
390
+ self
391
+ end
392
+
393
+ private
394
+
395
+ def load!
396
+ decode!
397
+
398
+ code = StringScanner.new self.data
399
+ @instructions = []
400
+ @instructions << PDF::Instruction.parse(code) until code.eos?
401
+
402
+ self
403
+ end
404
+
405
+ def write_text_block(text)
406
+
407
+ lines = text.split("\n").map!{|line| line.to_s}
408
+
409
+ @instructions << PDF::Instruction.new('Tj', lines.slice!(0)).update_state(@gs)
410
+ lines.each do |line|
411
+ @instructions << PDF::Instruction.new("'", line).update_state(@gs)
412
+ end
413
+
414
+ end
415
+
416
+ end #class ContentStream
417
+
418
+ module Graphics
419
+
420
+ module XObject
421
+ def self.included(receiver)
422
+ receiver.field :Type, :Type => Name, :Default => :XObject
423
+ end
424
+ end
425
+
426
+ class FormXObject < ContentStream
427
+ include XObject
428
+
429
+ field :Subtype, :Type => Name, :Default => :Form, :Required => true
430
+ field :FormType, :Type => Integer, :Default => 1
431
+ field :BBox, :Type => Array, :Required => true
432
+ field :Matrix, :Type => Array, :Default => [1, 0, 0, 1, 0, 0]
433
+ field :Resources, :Type => Dictionary, :Version => "1.2"
434
+ field :Group, :Type => Dictionary, :Version => "1.4"
435
+ field :Ref, :Type => Dictionary, :Version => "1.4"
436
+ field :Metadata, :Type => Stream, :Version => "1.4"
437
+ field :PieceInfo, :Type => Dictionary, :Version => "1.3"
438
+ field :LastModified, :Type => String, :Version => "1.3"
439
+ field :StructParent, :Type => Integer, :Version => "1.3"
440
+ field :StructParents, :Type => Integer, :Version => "1.3"
441
+ field :OPI, :Type => Dictionary, :Version => "1.2"
442
+ field :OC, :Type => Dictionary, :Version => "1.5"
443
+ field :Name, :Type => Name
444
+ field :Measure, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
445
+ field :PtData, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
446
+
447
+ def pre_build
448
+ self.Resources = Resources.new.pre_build unless has_field?(:Resources)
449
+
450
+ super
451
+ end
452
+
453
+ end
454
+
455
+ class ImageXObject < Stream
456
+ include XObject
457
+
458
+ field :Subtype, :Type => Name, :Default => :Image, :Required => true
459
+ field :Width, :Type => Integer, :Required => true
460
+ field :Height, :Type => Integer, :Required => true
461
+ field :ColorSpace, :Type => [ Name, Array ]
462
+ field :BitsPerComponent, :Type => Integer
463
+ field :Intent, :Type => Name, :Version => "1.1"
464
+ field :ImageMask, :Type => Boolean, :Default => false
465
+ field :Mask, :Type => [ Stream, Array ], :Version => "1.3"
466
+ field :Decode, :Type => Array
467
+ field :Interpolate, :Type => Boolean, :Default => false
468
+ field :Alternates, :Type => Array, :Version => "1.3"
469
+ field :SMask, :Type => Stream, :Version => "1.4"
470
+ field :SMaskInData, :Type => Integer, :Default => 0, :Version => "1.5"
471
+ field :Name, :Type => Name
472
+ field :StructParent, :Type => Integer, :Version => "1.3"
473
+ field :ID, :Type => String, :Version => "1.3"
474
+ field :OPI, :Type => Dictionary, :Version => "1.2"
475
+ field :Metadata, :Type => Stream, :Version => "1.4"
476
+ field :OC, :Type => Dictionary, :Version => "1.5"
477
+ field :Measure, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
478
+ field :PtData, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
479
+
480
+ end
481
+
482
+ class ReferenceDictionary < Dictionary
483
+ include StandardObject
484
+
485
+ field :F, :Type => Dictionary, :Required => true
486
+ field :Page, :Type => [Integer, String], :Required => true
487
+ field :ID, :Tyoe => Array
488
+ end
489
+
490
+ end
491
+
492
+ end
493
+