origami 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
+