origami 1.2.7 → 2.0.0

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 (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -0
  3. data/README.md +112 -0
  4. data/bin/config/pdfcop.conf.yml +232 -233
  5. data/bin/gui/about.rb +27 -37
  6. data/bin/gui/config.rb +108 -117
  7. data/bin/gui/file.rb +416 -365
  8. data/bin/gui/gtkhex.rb +1138 -1153
  9. data/bin/gui/hexview.rb +55 -57
  10. data/bin/gui/imgview.rb +48 -51
  11. data/bin/gui/menu.rb +388 -386
  12. data/bin/gui/properties.rb +114 -130
  13. data/bin/gui/signing.rb +571 -617
  14. data/bin/gui/textview.rb +77 -95
  15. data/bin/gui/treeview.rb +382 -387
  16. data/bin/gui/walker.rb +227 -232
  17. data/bin/gui/xrefs.rb +56 -60
  18. data/bin/pdf2pdfa +53 -57
  19. data/bin/pdf2ruby +212 -228
  20. data/bin/pdfcop +338 -348
  21. data/bin/pdfdecompress +58 -65
  22. data/bin/pdfdecrypt +56 -60
  23. data/bin/pdfencrypt +75 -80
  24. data/bin/pdfexplode +185 -182
  25. data/bin/pdfextract +201 -218
  26. data/bin/pdfmetadata +83 -82
  27. data/bin/pdfsh +4 -5
  28. data/bin/pdfwalker +1 -2
  29. data/bin/shell/.irbrc +45 -82
  30. data/bin/shell/console.rb +105 -130
  31. data/bin/shell/hexdump.rb +40 -64
  32. data/examples/README.md +34 -0
  33. data/examples/attachments/attachment.rb +38 -0
  34. data/examples/attachments/nested_document.rb +51 -0
  35. data/examples/encryption/encryption.rb +28 -0
  36. data/{samples/actions/triggerevents/trigger.rb → examples/events/events.rb} +13 -16
  37. data/examples/flash/flash.rb +37 -0
  38. data/{samples → examples}/flash/helloworld.swf +0 -0
  39. data/examples/forms/javascript.rb +54 -0
  40. data/examples/forms/xfa.rb +115 -0
  41. data/examples/javascript/hello_world.rb +22 -0
  42. data/examples/javascript/js_emulation.rb +54 -0
  43. data/examples/loop/goto.rb +32 -0
  44. data/examples/loop/named.rb +33 -0
  45. data/examples/signature/signature.rb +65 -0
  46. data/examples/uri/javascript.rb +56 -0
  47. data/examples/uri/open-uri.rb +21 -0
  48. data/examples/uri/submitform.rb +47 -0
  49. data/lib/origami.rb +29 -42
  50. data/lib/origami/3d.rb +350 -225
  51. data/lib/origami/acroform.rb +262 -288
  52. data/lib/origami/actions.rb +268 -288
  53. data/lib/origami/annotations.rb +697 -722
  54. data/lib/origami/array.rb +258 -184
  55. data/lib/origami/boolean.rb +74 -84
  56. data/lib/origami/catalog.rb +397 -434
  57. data/lib/origami/collections.rb +144 -0
  58. data/lib/origami/destinations.rb +233 -194
  59. data/lib/origami/dictionary.rb +253 -232
  60. data/lib/origami/encryption.rb +1274 -1243
  61. data/lib/origami/export.rb +232 -268
  62. data/lib/origami/extensions/fdf.rb +307 -220
  63. data/lib/origami/extensions/ppklite.rb +368 -435
  64. data/lib/origami/filespec.rb +197 -0
  65. data/lib/origami/filters.rb +301 -295
  66. data/lib/origami/filters/ascii.rb +177 -180
  67. data/lib/origami/filters/ccitt.rb +528 -535
  68. data/lib/origami/filters/crypt.rb +26 -35
  69. data/lib/origami/filters/dct.rb +46 -52
  70. data/lib/origami/filters/flate.rb +95 -94
  71. data/lib/origami/filters/jbig2.rb +49 -55
  72. data/lib/origami/filters/jpx.rb +38 -44
  73. data/lib/origami/filters/lzw.rb +189 -183
  74. data/lib/origami/filters/predictors.rb +221 -235
  75. data/lib/origami/filters/runlength.rb +103 -104
  76. data/lib/origami/font.rb +173 -186
  77. data/lib/origami/functions.rb +67 -81
  78. data/lib/origami/graphics.rb +25 -21
  79. data/lib/origami/graphics/colors.rb +178 -187
  80. data/lib/origami/graphics/instruction.rb +79 -85
  81. data/lib/origami/graphics/path.rb +142 -148
  82. data/lib/origami/graphics/patterns.rb +160 -167
  83. data/lib/origami/graphics/render.rb +43 -50
  84. data/lib/origami/graphics/state.rb +138 -153
  85. data/lib/origami/graphics/text.rb +188 -205
  86. data/lib/origami/graphics/xobject.rb +819 -815
  87. data/lib/origami/header.rb +63 -78
  88. data/lib/origami/javascript.rb +596 -597
  89. data/lib/origami/linearization.rb +285 -290
  90. data/lib/origami/metadata.rb +139 -148
  91. data/lib/origami/name.rb +112 -148
  92. data/lib/origami/null.rb +53 -62
  93. data/lib/origami/numeric.rb +162 -175
  94. data/lib/origami/obfuscation.rb +186 -174
  95. data/lib/origami/object.rb +593 -573
  96. data/lib/origami/outline.rb +42 -47
  97. data/lib/origami/outputintents.rb +73 -82
  98. data/lib/origami/page.rb +703 -592
  99. data/lib/origami/parser.rb +238 -290
  100. data/lib/origami/parsers/fdf.rb +41 -33
  101. data/lib/origami/parsers/pdf.rb +75 -95
  102. data/lib/origami/parsers/pdf/lazy.rb +137 -0
  103. data/lib/origami/parsers/pdf/linear.rb +64 -66
  104. data/lib/origami/parsers/ppklite.rb +34 -70
  105. data/lib/origami/pdf.rb +1030 -1005
  106. data/lib/origami/reference.rb +102 -102
  107. data/lib/origami/signature.rb +591 -609
  108. data/lib/origami/stream.rb +668 -551
  109. data/lib/origami/string.rb +397 -373
  110. data/lib/origami/template/patterns.rb +56 -0
  111. data/lib/origami/template/widgets.rb +151 -0
  112. data/lib/origami/trailer.rb +144 -158
  113. data/lib/origami/tree.rb +62 -0
  114. data/lib/origami/version.rb +23 -0
  115. data/lib/origami/webcapture.rb +88 -79
  116. data/lib/origami/xfa.rb +2863 -2882
  117. data/lib/origami/xreftable.rb +472 -384
  118. data/test/dataset/calc.pdf +85 -0
  119. data/test/dataset/crypto.pdf +82 -0
  120. data/test/dataset/empty.pdf +49 -0
  121. data/test/test_actions.rb +27 -0
  122. data/test/test_annotations.rb +90 -0
  123. data/test/test_pages.rb +31 -0
  124. data/test/test_pdf.rb +16 -0
  125. data/test/test_pdf_attachment.rb +34 -0
  126. data/test/test_pdf_create.rb +24 -0
  127. data/test/test_pdf_encrypt.rb +95 -0
  128. data/test/test_pdf_parse.rb +96 -0
  129. data/test/test_pdf_sign.rb +58 -0
  130. data/test/test_streams.rb +182 -0
  131. data/test/test_xrefs.rb +67 -0
  132. metadata +88 -58
  133. data/README +0 -67
  134. data/bin/pdf2graph +0 -121
  135. data/bin/pdfcocoon +0 -104
  136. data/lib/origami/file.rb +0 -233
  137. data/samples/README.txt +0 -45
  138. data/samples/actions/launch/calc.rb +0 -87
  139. data/samples/actions/launch/winparams.rb +0 -22
  140. data/samples/actions/loop/loopgoto.rb +0 -24
  141. data/samples/actions/loop/loopnamed.rb +0 -21
  142. data/samples/actions/named/named.rb +0 -31
  143. data/samples/actions/samba/smbrelay.rb +0 -26
  144. data/samples/actions/webbug/submitform.js +0 -26
  145. data/samples/actions/webbug/webbug-browser.rb +0 -68
  146. data/samples/actions/webbug/webbug-js.rb +0 -67
  147. data/samples/actions/webbug/webbug-reader.rb +0 -90
  148. data/samples/attachments/attach.rb +0 -40
  149. data/samples/attachments/attached.txt +0 -1
  150. data/samples/crypto/crypto.rb +0 -28
  151. data/samples/digsig/signed.rb +0 -46
  152. data/samples/exploits/cve-2008-2992-utilprintf.rb +0 -87
  153. data/samples/exploits/cve-2009-0927-geticon.rb +0 -65
  154. data/samples/exploits/exploit_customdictopen.rb +0 -55
  155. data/samples/exploits/getannots.rb +0 -69
  156. data/samples/flash/flash.rb +0 -31
  157. data/samples/javascript/attached.txt +0 -1
  158. data/samples/javascript/js.rb +0 -52
  159. data/templates/patterns.rb +0 -66
  160. data/templates/widgets.rb +0 -173
  161. data/templates/xdp.rb +0 -92
  162. data/test/ts_pdf.rb +0 -50
@@ -1,101 +1,91 @@
1
1
  =begin
2
2
 
3
- = File
4
- boolean.rb
5
-
6
- = Info
7
- This file is part of Origami, PDF manipulation framework for Ruby
8
- Copyright (C) 2010 Guillaume DelugrÈ <guillaume AT security-labs DOT 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/>.
3
+ This file is part of Origami, PDF manipulation framework for Ruby
4
+ Copyright (C) 2016 Guillaume Delugré.
5
+
6
+ Origami is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ Origami is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
23
18
 
24
19
  =end
25
20
 
26
21
  module Origami
27
22
 
28
- class InvalidBooleanObjectError < InvalidObjectError #:nodoc:
29
- end
30
-
31
- #
32
- # Class representing a Boolean Object.
33
- # A Boolean Object can be *true* or *false*.
34
- #
35
- class Boolean
36
-
37
- include Origami::Object
38
-
39
- TOKENS = [ %w{ true false } ] #:nodoc:
40
-
41
- @@regexp = Regexp.new(WHITESPACES + "(#{TOKENS.first.join('|')})")
42
-
23
+ class InvalidBooleanObjectError < InvalidObjectError #:nodoc:
24
+ end
25
+
43
26
  #
44
- # Creates a new Boolean value.
45
- # _value_:: *true* or *false*.
27
+ # Class representing a Boolean Object.
28
+ # A Boolean Object can be *true* or *false*.
46
29
  #
47
- def initialize(value)
48
-
49
- unless value.is_a?(TrueClass) or value.is_a?(FalseClass)
50
- raise TypeError, "Expected type TrueClass or FalseClass, received #{value.class}."
51
- end
52
-
53
- super()
54
-
55
- @value = (value == nil || value == false) ? false : true
56
- end
57
-
58
- def to_s #:nodoc:
59
- super(@value.to_s)
60
- end
61
-
62
- def self.parse(stream, parser = nil) #:nodoc:
30
+ class Boolean
31
+ include Origami::Object
63
32
 
64
- offset = stream.pos
65
-
66
- if stream.scan(@@regexp).nil?
67
- raise InvalidBooleanObjectError
68
- end
33
+ TOKENS = %w{ true false } #:nodoc:
34
+ @@regexp = Regexp.new(WHITESPACES + "(?<value>#{Regexp.union(TOKENS)})")
69
35
 
70
- value = stream[2] == "true" ? true : false
71
-
72
- bool = Boolean.new(value)
73
- bool.file_offset = offset
36
+ #
37
+ # Creates a new Boolean value.
38
+ # _value_:: *true* or *false*.
39
+ #
40
+ def initialize(value)
41
+ unless value.is_a?(TrueClass) or value.is_a?(FalseClass)
42
+ raise TypeError, "Expected type TrueClass or FalseClass, received #{value.class}."
43
+ end
74
44
 
75
- bool
76
- end
77
-
78
- #
79
- # Converts self into a Ruby boolean, that is TrueClass or FalseClass instance.
80
- #
81
- def value
82
- @value
83
- end
45
+ super()
84
46
 
85
- def self.native_type ; Boolean end
86
-
87
- def false?
88
- @value == false
89
- end
90
-
91
- def true?
92
- @value == true
93
- end
47
+ @value = (value == true)
48
+ end
94
49
 
95
- def ==(bool)
96
- @value == bool
97
- end
50
+ def to_s #:nodoc:
51
+ super(@value.to_s)
52
+ end
53
+
54
+ def self.parse(stream, parser = nil) #:nodoc:
55
+ offset = stream.pos
56
+
57
+ if stream.scan(@@regexp).nil?
58
+ raise InvalidBooleanObjectError
59
+ end
60
+
61
+ value = (stream['value'] == "true")
62
+
63
+ bool = Boolean.new(value)
64
+ bool.file_offset = offset
98
65
 
99
- end
66
+ bool
67
+ end
68
+
69
+ #
70
+ # Converts self into a Ruby boolean, that is TrueClass or FalseClass instance.
71
+ #
72
+ def value
73
+ @value
74
+ end
75
+
76
+ def self.native_type ; Boolean end
77
+
78
+ def false?
79
+ @value == false
80
+ end
81
+
82
+ def true?
83
+ @value == true
84
+ end
85
+
86
+ def ==(bool)
87
+ @value == bool
88
+ end
89
+ end
100
90
 
101
91
  end
@@ -1,485 +1,448 @@
1
1
  =begin
2
2
 
3
- = File
4
- catalog.rb
5
-
6
- = Info
7
- This file is part of Origami, PDF manipulation framework for Ruby
8
- Copyright (C) 2010 Guillaume DelugrÈ <guillaume AT security-labs DOT 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/>.
3
+ This file is part of Origami, PDF manipulation framework for Ruby
4
+ Copyright (C) 2016 Guillaume Delugré.
5
+
6
+ Origami is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ Origami is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
23
18
 
24
19
  =end
25
20
 
26
21
  module Origami
27
22
 
28
- class PDF
23
+ class PDF
24
+ #
25
+ # Sets PDF extension level and version. Only supported values are "1.7" and 3.
26
+ #
27
+ def set_extension_level(version, level)
28
+ exts = (self.Catalog.Extensions ||= Extensions.new)
29
29
 
30
- #
31
- # Sets PDF extension level and version. Only supported values are "1.7" and 3.
32
- #
33
- def set_extension_level(version, level)
34
- exts = (self.Catalog.Extensions ||= Extensions.new)
30
+ exts[:ADBE] = DeveloperExtension.new
31
+ exts[:ADBE].BaseVersion = Name.new(version)
32
+ exts[:ADBE].ExtensionLevel = level
35
33
 
36
- exts[:ADBE] = DeveloperExtension.new
37
- exts[:ADBE].BaseVersion = Name.new(version)
38
- exts[:ADBE].ExtensionLevel = level
34
+ self
35
+ end
39
36
 
40
- self
41
- end
37
+ #
38
+ # Returns the current Catalog Dictionary.
39
+ #
40
+ def Catalog
41
+ cat = trailer_key(:Root)
42
+
43
+ case cat
44
+ when Catalog then
45
+ cat
46
+ when Dictionary then
47
+ cat.cast_to(Catalog)
48
+ else
49
+ raise InvalidPDFError, "Broken catalog"
50
+ end
51
+ end
42
52
 
43
- #
44
- # Returns the current Catalog Dictionary.
45
- #
46
- def Catalog
47
- cat = get_doc_attr(:Root)
48
-
49
- case cat
50
- when Catalog then
51
- cat
52
- when Dictionary then
53
- casted = Catalog.new(cat)
54
- casted.no, casted.generation = cat.no, cat.generation
55
- casted.set_indirect(true)
56
- casted.set_pdf(self)
57
-
58
- casted
59
- else
60
- raise InvalidPDFError, "Broken catalog"
61
- end
62
- end
63
-
64
- #
65
- # Sets the current Catalog Dictionary.
66
- #
67
- def Catalog=(cat)
68
- #unless cat.is_a?(Catalog)
69
- # raise TypeError, "Expected type Catalog, received #{cat.class}"
70
- #end
71
- cat = Catalog.new(cat) unless cat.is_a? Catalog
72
-
73
- if @revisions.last.trailer.Root
74
- delete_object(@revisions.last.trailer[:Root])
75
- end
76
-
77
- @revisions.last.trailer.Root = self << cat
53
+ #
54
+ # Sets the current Catalog Dictionary.
55
+ #
56
+ def Catalog=(cat)
57
+ cat = cat.cast_to(Catalog) unless cat.is_a? Catalog
58
+
59
+ delete_object(@revisions.last.trailer[:Root]) if @revisions.last.trailer[:Root]
60
+
61
+ @revisions.last.trailer.Root = self << cat
62
+ end
63
+
64
+ #
65
+ # Sets an action to run on document opening.
66
+ # _action_:: An Action Object.
67
+ #
68
+ def onDocumentOpen(action)
69
+ unless action.is_a?(Action) or action.is_a?(Destination) or action.is_a?(Reference)
70
+ raise TypeError, "An Action object must be passed."
71
+ end
72
+
73
+ unless self.Catalog
74
+ raise InvalidPDFError, "A catalog object must exist to add this action."
75
+ end
76
+
77
+ self.Catalog.OpenAction = action
78
+
79
+ self
80
+ end
81
+
82
+ #
83
+ # Sets an action to run on document closing.
84
+ # _action_:: A JavaScript Action Object.
85
+ #
86
+ def onDocumentClose(action)
87
+ unless action.is_a?(Action::JavaScript) or action.is_a?(Reference)
88
+ raise TypeError, "An Action::JavaScript object must be passed."
89
+ end
90
+
91
+ unless self.Catalog
92
+ raise InvalidPDFError, "A catalog object must exist to add this action."
93
+ end
94
+
95
+ self.Catalog.AA ||= CatalogAdditionalActions.new
96
+ self.Catalog.AA.WC = action
97
+
98
+ self
99
+ end
100
+
101
+ #
102
+ # Sets an action to run on document printing.
103
+ # _action_:: A JavaScript Action Object.
104
+ #
105
+ def onDocumentPrint(action)
106
+ unless action.is_a?(Action::JavaScript) or action.is_a?(Reference)
107
+ raise TypeError, "An Action::JavaScript object must be passed."
108
+ end
109
+
110
+ unless self.Catalog
111
+ raise InvalidPDFError, "A catalog object must exist to add this action."
112
+ end
113
+
114
+ self.Catalog.AA ||= CatalogAdditionalActions.new
115
+ self.Catalog.AA.WP = action
116
+
117
+ self
118
+ end
119
+
120
+ #
121
+ # Registers an object into a specific Names root dictionary.
122
+ # _root_:: The root dictionary (see Names::Root)
123
+ # _name_:: The value name.
124
+ # _value_:: The value to associate with this name.
125
+ #
126
+ def register(root, name, value)
127
+ self.Catalog.Names ||= Names.new
128
+
129
+ value.set_indirect(true) unless value.is_a?(Reference)
130
+
131
+ namesroot = self.Catalog.Names[root]
132
+ if namesroot.nil?
133
+ names = NameTreeNode.new(:Names => []).set_indirect(true)
134
+ self.Catalog.Names[root] = names
135
+ names.Names << name << value
136
+ else
137
+ namesroot.solve[:Names] << name << value
138
+ end
139
+ end
140
+
141
+ #
142
+ # Retrieve the corresponding value associated with _name_ in
143
+ # the specified _root_ name directory, or nil if the value does
144
+ # not exist.
145
+ #
146
+ def resolve_name(root, name)
147
+ namesroot = get_names_root(root)
148
+ return nil if namesroot.nil?
149
+
150
+ resolve_name_from_node(namesroot, name)
151
+ end
152
+
153
+ #
154
+ # Returns a Hash of all names under the specified _root_ name directory.
155
+ #
156
+ def names(root)
157
+ self.each_name(root).to_h
158
+ end
159
+
160
+ #
161
+ # Returns an Enumerator of all names under the specified _root_ name directory.
162
+ #
163
+ def each_name(root, &block)
164
+ return enum_for(__method__, root) unless block_given?
165
+
166
+ names_root = get_names_root(root)
167
+ return if names_root.nil?
168
+
169
+ names_from_node(names_root, &block)
170
+ self
171
+ end
172
+
173
+ private
174
+
175
+ def names_from_node(node, browsed_nodes: [], &block) #:nodoc:
176
+ return if browsed_nodes.any?{|browsed| browsed.equal?(node)}
177
+ raise InvalidNameTreeError, "node is not a dictionary" unless node.is_a?(Dictionary)
178
+
179
+ browsed_nodes.push(node)
180
+
181
+ if node.has_key?(:Names) # leaf node
182
+ names = node.Names
183
+ raise InvalidNameTreeError, "Names must be an Array" unless names.is_a?(Array)
184
+ raise InvalidNameTreeError, "Odd number of elements" if names.length.odd?
185
+
186
+ for i in 0...names.length/2
187
+ yield(names[i * 2].solve, names[i * 2 + 1].solve)
188
+ end
189
+
190
+ elsif node.has_key?(:Kids) # intermediate node
191
+ node.Kids.each do |kid|
192
+ names_from_node(kid.solve, browsed_nodes: browsed_nodes, &block)
193
+ end
194
+ end
195
+ end
196
+
197
+ def resolve_name_from_node(node, name, browsed_nodes: []) #:nodoc:
198
+ return if browsed_nodes.any?{|browsed| browsed.equal?(node)}
199
+ raise InvalidNameTreeError, "node is not a Dictionary" unless node.is_a?(Dictionary)
200
+
201
+ browsed_nodes.push(node)
202
+
203
+ if node.has_key?(:Names) # leaf node
204
+ limits = node.Limits
205
+ names = node.Names
206
+
207
+ raise InvalidNameTreeError, "Names must be an Array" unless names.is_a?(Array)
208
+ raise InvalidNameTreeError, "Odd number of elements" if names.length.odd?
209
+
210
+ if limits.is_a?(Array)
211
+ raise InvalidNameTreeError, "Invalid Limits array" unless limits.length == 2
212
+
213
+ min, max = limits[0].value, limits[1].value
214
+ if name.to_str >= min and name.to_str <= max
215
+ names = Hash[*names]
216
+ target = names[name]
217
+ return target && target.solve
218
+ end
219
+ else
220
+ names = Hash[*names]
221
+ target = names[name]
222
+ return target && target.solve
223
+ end
224
+
225
+ elsif node.has_key?(:Kids) # intermediate node
226
+ raise InvalidNameTreeError, "Kids must be an Array" unless node.Kids.is_a?(Array)
227
+
228
+ node.Kids.each do |kid|
229
+ kid = kid.solve
230
+ limits = kid.Limits
231
+ unless limits.is_a?(Array) and limits.length == 2
232
+ raise InvalidNameTreeError, "Invalid Limits array"
233
+ end
234
+
235
+ min, max = limits[0].value, limits[1].value
236
+
237
+ if name.to_str >= min and name.to_str <= max
238
+ return resolve_name_from_node(kid, name, browsed_nodes: browsed_nodes)
239
+ end
240
+ end
241
+ end
242
+ end
243
+
244
+ def get_names_root(root) #:nodoc:
245
+ namedirs = self.Catalog.Names
246
+ return nil if namedirs.nil? or namedirs[root].nil?
247
+
248
+ namedirs[root].solve
249
+ end
78
250
  end
79
-
80
- #
81
- # Sets an action to run on document opening.
82
- # _action_:: An Action Object.
83
- #
84
- def onDocumentOpen(action)
85
-
86
- unless action.is_a?(Action) or action.is_a?(Destination) or action.is_a?(Reference)
87
- raise TypeError, "An Action object must be passed."
88
- end
89
-
90
- unless self.Catalog
91
- raise InvalidPDFError, "A catalog object must exist to add this action."
92
- end
93
-
94
- self.Catalog.OpenAction = action
95
-
96
- self
251
+
252
+ module PageLayout #:nodoc:
253
+ SINGLE = :SinglePage
254
+ ONE_COLUMN = :OneColumn
255
+ TWO_COLUMN_LEFT = :TwoColumnLeft
256
+ TWO_COLUMN_RIGHT = :TwoColumnRight
257
+ TWO_PAGE_LEFT = :TwoPageLeft
258
+ TWO_PAGE_RIGHT = :TwoPageRight
97
259
  end
98
-
99
- #
100
- # Sets an action to run on document closing.
101
- # _action_:: A JavaScript Action Object.
102
- #
103
- def onDocumentClose(action)
104
-
105
- unless action.is_a?(Action::JavaScript) or action.is_a?(Reference)
106
- raise TypeError, "An Action::JavaScript object must be passed."
107
- end
108
-
109
- unless self.Catalog
110
- raise InvalidPDFError, "A catalog object must exist to add this action."
111
- end
112
-
113
- self.Catalog.AA ||= CatalogAdditionalActions.new
114
- self.Catalog.AA.WC = action
115
-
116
- self
260
+
261
+ module PageMode #:nodoc:
262
+ NONE = :UseNone
263
+ OUTLINES = :UseOutlines
264
+ THUMBS = :UseThumbs
265
+ FULLSCREEN = :FullScreen
266
+ OPTIONAL_CONTENT = :UseOC
267
+ ATTACHMENTS = :UseAttachments
117
268
  end
118
-
269
+
119
270
  #
120
- # Sets an action to run on document printing.
121
- # _action_:: A JavaScript Action Object.
271
+ # Class representing additional actions which can be associated with a Catalog.
122
272
  #
123
- def onDocumentPrint(action)
124
-
125
- unless action.is_a?(Action::JavaScript) or action.is_a?(Reference)
126
- raise TypeError, "An Action::JavaScript object must be passed."
127
- end
128
-
129
- unless self.Catalog
130
- raise InvalidPDFError, "A catalog object must exist to add this action."
131
- end
132
-
133
- self.Catalog.AA ||= CatalogAdditionalActions.new
134
- self.Catalog.AA.WP = action
135
-
273
+ class CatalogAdditionalActions < Dictionary
274
+ include StandardObject
275
+
276
+ field :WC, :Type => Action, :Version => "1.4"
277
+ field :WS, :Type => Action, :Version => "1.4"
278
+ field :DS, :Type => Action, :Version => "1.4"
279
+ field :WP, :Type => Action, :Version => "1.4"
280
+ field :DP, :Type => Action, :Version => "1.4"
136
281
  end
137
282
 
138
283
  #
139
- # Registers an object into a specific Names root dictionary.
140
- # _root_:: The root dictionary (see Names::Root)
141
- # _name_:: The value name.
142
- # _value_:: The value to associate with this name.
284
+ # Class representing the Names Dictionary of a PDF file.
143
285
  #
144
- def register(root, name, value)
145
- self.Catalog.Names ||= Names.new
146
-
147
- value.set_indirect(true) unless value.is_a? Reference
148
-
149
- namesroot = self.Catalog.Names[root]
150
- if namesroot.nil?
151
- names = NameTreeNode.new(:Names => []).set_indirect(true)
152
- self.Catalog.Names[root] = names
153
- names.Names << name << value
154
- else
155
- namesroot.solve[:Names] << name << value
156
- end
157
- end
158
-
159
- def each_name(root, &b)
160
- namesroot = get_names_root(root)
161
- return if namesroot.nil?
162
-
163
- each_name_from_node(namesroot, [], &b)
164
- self
286
+ class Names < Dictionary
287
+ include StandardObject
288
+
289
+ #
290
+ # Defines constants for Names tree root entries.
291
+ #
292
+ DESTINATIONS = :Dests
293
+ AP = :AP
294
+ JAVASCRIPT = :JavaScript
295
+ PAGES = :Pages
296
+ TEMPLATES = :Templates
297
+ IDS = :IDS
298
+ URLS = :URLS
299
+ EMBEDDED_FILES = :EmbeddedFiles
300
+ ALTERNATE_PRESENTATIONS = :AlternatePresentations
301
+ RENDITIONS = :Renditions
302
+ XFA_RESOURCES = :XFAResources
303
+
304
+ field DESTINATIONS, :Type => NameTreeNode.of([DestinationDictionary, Destination]), :Version => "1.2"
305
+ field AP, :Type => NameTreeNode.of(Annotation::AppearanceStream), :Version => "1.3"
306
+ field JAVASCRIPT, :Type => NameTreeNode.of(Action::JavaScript), :Version => "1.3"
307
+ field PAGES, :Type => NameTreeNode.of(Page), :Version => "1.3"
308
+ field TEMPLATES, :Type => NameTreeNode.of(Page), :Version => "1.3"
309
+ field IDS, :Type => NameTreeNode.of(WebCapture::ContentSet), :Version => "1.3"
310
+ field URLS, :Type => NameTreeNode.of(WebCapture::ContentSet), :Version => "1.3"
311
+ field EMBEDDED_FILES, :Type => NameTreeNode.of(FileSpec), :Version => "1.4"
312
+ field ALTERNATE_PRESENTATIONS, :Type => NameTreeNode, :Version => "1.4"
313
+ field RENDITIONS, :Type => NameTreeNode, :Version => "1.5"
314
+ field XFA_RESOURCES, :Type => NameTreeNode.of(XFAStream), :Version => "1.7", :ExtensionLevel => 3
165
315
  end
166
316
 
167
317
  #
168
- # Retrieve the corresponding value associated with _name_ in
169
- # the specified _root_ name directory, or nil if the value does
170
- # not exist.
318
+ # Class representing a leaf in a Name tree.
171
319
  #
172
- def resolve_name(root, name)
173
- namesroot = get_names_root(root)
174
- return nil if namesroot.nil?
175
-
176
- resolve_name_from_node(namesroot, name)
320
+ class NameLeaf < Array.of(String, Object)
321
+
322
+ #
323
+ # Creates a new leaf in a Name tree.
324
+ # _hash_:: A hash of couples, associating a Name with an Reference.
325
+ #
326
+ def initialize(hash = {})
327
+ super(hash.flat_map {|name, obj| [name.dup, obj]})
328
+ end
177
329
  end
178
330
 
179
331
  #
180
- # Returns a Hash of all names under specified _root_ name directory.
181
- # Returns nil if the directory does not exist.
332
+ # Class representing the ViewerPreferences Dictionary of a PDF.
333
+ # This dictionary modifies the way the UI looks when the file is opened in a viewer.
182
334
  #
183
- def ls_names(root)
184
- namesroot = get_names_root(root)
185
- return {} if namesroot.nil?
335
+ class ViewerPreferences < Dictionary
336
+ include StandardObject
186
337
 
187
- names = names_from_node(namesroot)
188
- if names.length % 2 != 0
189
- return InvalidNameTreeError, "Odd number of elements"
190
- end
191
-
192
- Hash[*names]
193
- end
194
-
195
- private
196
-
197
- def names_from_node(node, browsed_nodes = []) #:nodoc:
198
- children = []
199
-
200
- unless browsed_nodes.any? {|browsed| browsed.equal?(node)}
201
- browsed_nodes.push(node)
202
- if node.has_key?(:Names) # leaf node
203
- children.concat(node.Names)
204
- elsif node.has_key?(:Kids) # intermediate node
205
- node.Kids.each do |kid|
206
- children.concat(names_from_node(kid.solve, browsed_nodes))
207
- end
338
+ # Valid values for the Enforce field.
339
+ module Enforce
340
+ PRINT_SCALING = :PrintScaling
208
341
  end
209
- end
210
342
 
211
- children
343
+ field :HideToolbar, :Type => Boolean, :Default => false
344
+ field :HideMenubar, :Type => Boolean, :Default => false
345
+ field :HideWindowUI, :Type => Boolean, :Default => false
346
+ field :FitWindow, :Type => Boolean, :Default => false
347
+ field :CenterWindow, :Type => Boolean, :Default => false
348
+ field :DisplayDocTitle, :Type => Boolean, :Default => false, :Version => "1.4"
349
+ field :NonFullScreenPageMode, :Type => Name, :Default => :UseNone
350
+ field :Direction, :Type => Name, :Default => :L2R
351
+ field :ViewArea, :Type => Name, :Default => :CropBox, :Version => "1.4"
352
+ field :ViewClip, :Type => Name, :Default => :CropBox, :Version => "1.4"
353
+ field :PrintArea, :Type => Name, :Default => :CropBox, :Version => "1.4"
354
+ field :PrintClip, :Type => Name, :Default => :CropBox, :Version => "1.4"
355
+ field :PrintScaling, :Type => Name, :Default => :AppDefault, :Version => "1.6"
356
+ field :Duplex, :Type => Name, :Default => :Simplex, :Version => "1.7"
357
+ field :PickTrayByPDFSize, :Type => Boolean, :Version => "1.7"
358
+ field :PrintPageRange, :Type => Array.of(Integer), :Version => "1.7"
359
+ field :NumCopies, :Type => Integer, :Version => "1.7"
360
+ field :Enforce, :Type => Array.of(Name), :Version => "1.7", :ExtensionLevel => 3
212
361
  end
213
362
 
214
- def resolve_name_from_node(node, name, browsed_nodes = []) #:nodoc:
215
- unless browsed_nodes.any? {|browsed| browsed.equal?(node)}
216
- browsed_nodes.push(node)
363
+ class Requirement < Dictionary
364
+ include StandardObject
217
365
 
218
- if node.has_key?(:Names) # leaf node
219
- limits = node.Limits
366
+ class Handler < Dictionary
367
+ include StandardObject
220
368
 
221
- if limits
222
- min, max = limits[0].value, limits[1].value
223
- if (min..max) === name.to_str
224
- names = Hash[*node.Names]
225
- target = names[name]
226
- return target && target.solve
227
- end
228
- else
229
- names = Hash[*node.Names]
230
- target = names[name]
231
- return target && target.solve
232
- end
233
-
234
- elsif node.has_key?(:Kids) # intermediate node
235
- node.Kids.each do |kid|
236
- kid = kid.solve
237
- limits = kid.Limits
238
- min, max = limits[0].value, limits[1].value
239
-
240
- if (min..max) === name.to_str
241
- return resolve_name_from_node(kid, name, browsed_nodes)
369
+ module Type
370
+ JS = :JS
371
+ NOOP = :NoOp
242
372
  end
243
- end
244
- end
245
- end
246
- end
247
373
 
248
- def each_name_from_node(node, browsed_nodes = [], &b) #:nodoc:
249
- if node.has_key?(:Names) # leaf node
250
- names = Hash[*node.Names]
251
- names.each_pair do |name, value|
252
- b.call(name, value.solve)
253
- end
254
- elsif node.has_key?(:Kids) # intermediate node
255
- node.Kids.each do |kid|
256
- each_name_from_node(kid.solve, browsed_nodes, &b)
374
+ field :Type, :Type => Name, :Default => :ReqHandler
375
+ field :S, :Type => Name, :Default => Type::NOOP, :Required => true
376
+ field :Script, :Type => String
257
377
  end
258
- end
378
+
379
+ field :Type, :Type => Name, :Default => :Requirement
380
+ field :S, :Type => Name, :Default => :EnableJavaScripts, :Version => "1.7", :Required => true
381
+ field :RH, :Type => Array.of(Handler)
259
382
  end
260
383
 
261
- def get_names_root(root) #:nodoc:
262
- namedirs = self.Catalog.Names
263
- return nil if namedirs.nil? or namedirs[root].nil?
384
+ #
385
+ # Class representing a developer extension.
386
+ #
387
+ class DeveloperExtension < Dictionary
388
+ include StandardObject
264
389
 
265
- namedirs[root].solve
390
+ field :Type, :Type => Name, :Default => :DeveloperExtensions
391
+ field :BaseVersion, :Type => Name, :Required => true
392
+ field :ExtensionLevel, :Type => Integer, :Required => true
266
393
  end
267
- end
268
-
269
- module PageLayout #:nodoc:
270
- SINGLE = :SinglePage
271
- ONE_COLUMN = :OneColumn
272
- TWO_COLUMN_LEFT = :TwoColumnLeft
273
- TWO_COLUMN_RIGHT = :TwoColumnRight
274
- TWO_PAGE_LEFT = :TwoPageLeft
275
- TWO_PAGE_RIGHT = :TwoPageRight
276
- end
277
-
278
- module PageMode #:nodoc:
279
- NONE = :UseNone
280
- OUTLINES = :UseOutlines
281
- THUMBS = :UseThumbs
282
- FULLSCREEN = :FullScreen
283
- OPTIONAL_CONTENT = :UseOC
284
- ATTACHMENTS = :UseAttachments
285
- end
286
-
287
- #
288
- # Class representing additional actions which can be associated with a Catalog.
289
- #
290
- class CatalogAdditionalActions < Dictionary
291
- include StandardObject
292
-
293
- field :WC, :Type => Dictionary, :Version => "1.4"
294
- field :WS, :Type => Dictionary, :Version => "1.4"
295
- field :DS, :Type => Dictionary, :Version => "1.4"
296
- field :WP, :Type => Dictionary, :Version => "1.4"
297
- field :DP, :Type => Dictionary, :Version => "1.4"
298
- end
299
-
300
- class InvalidNameTreeError < Exception #:nodoc:
301
- end
302
-
303
- #
304
- # Class representing the Names Dictionary of a PDF file.
305
- #
306
- class Names < Dictionary
307
- include StandardObject
308
-
394
+
309
395
  #
310
- # Defines constants for Names tree root entries.
396
+ # Class representing an extension Dictionary.
311
397
  #
312
- module Root
313
- DESTS = :Dests
314
- AP = :AP
315
- JAVASCRIPT = :JavaScript
316
- PAGES = :Pages
317
- TEMPLATES = :Templates
318
- IDS = :IDS
319
- URLS = :URLS
320
- EMBEDDEDFILES = :EmbeddedFiles
321
- ALTERNATEPRESENTATIONS = :AlternatePresentations
322
- RENDITIONS = :Renditions
323
- XFARESOURCES = :XFAResources
398
+ class Extensions < Dictionary
399
+ include StandardObject
400
+
401
+ field :Type, :Type => Name, :Default => :Extensions
402
+ field :ADBE, :Type => DeveloperExtension
324
403
  end
325
404
 
326
- field Root::DESTS, :Type => Dictionary, :Version => "1.2"
327
- field Root::AP, :Type => Dictionary, :Version => "1.3"
328
- field Root::JAVASCRIPT, :Type => Dictionary, :Version => "1.3"
329
- field Root::PAGES, :Type => Dictionary, :Version => "1.3"
330
- field Root::TEMPLATES, :Type => Dictionary, :Version => "1.3"
331
- field Root::IDS, :Type => Dictionary, :Version => "1.3"
332
- field Root::URLS, :Type => Dictionary, :Version => "1.3"
333
- field Root::EMBEDDEDFILES, :Type => Dictionary, :Version => "1.4"
334
- field Root::ALTERNATEPRESENTATIONS, :Type => Dictionary, :Version => "1.4"
335
- field Root::RENDITIONS, :Type => Dictionary, :Version => "1.5"
336
- field Root::XFARESOURCES, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
337
- end
338
-
339
- #
340
- # Class representing a node in a Name tree.
341
- #
342
- class NameTreeNode < Dictionary
343
- include StandardObject
344
-
345
- field :Kids, :Type => Array
346
- field :Names, :Type => Array
347
- field :Limits, :Type => Array
348
- end
349
-
350
- #
351
- # Class representing a leaf in a Name tree.
352
- #
353
- class NameLeaf < Origami::Array
354
-
355
405
  #
356
- # Creates a new leaf in a Name tree.
357
- # _hash_:: A hash of couples, associating a Name with an Reference.
406
+ # Class representing the Catalog Dictionary of a PDF file.
358
407
  #
359
- def initialize(hash = {})
360
-
361
- names = []
362
- hash.each_pair do |k,v|
363
- names << k.to_o << v.to_o
364
- end
365
-
366
- super(names)
367
- end
368
- end
369
-
370
- #
371
- # Class representing the ViewerPreferences Dictionary of a PDF.
372
- # This dictionary modifies the way the UI looks when the file is opened in a viewer.
373
- #
374
- class ViewerPreferences < Dictionary
375
- include StandardObject
376
-
377
- field :HideToolbar, :Type => Boolean, :Default => false
378
- field :HideMenubar, :Type => Boolean, :Default => false
379
- field :HideWindowUI, :Type => Boolean, :Default => false
380
- field :FitWindow, :Type => Boolean, :Default => false
381
- field :CenterWindow, :Type => Boolean, :Default => false
382
- field :DisplayDocTitle, :Type => Boolean, :Default => false, :Version => "1.4"
383
- field :NonFullScreenPageMode, :Type => Name, :Default => :UseNone
384
- field :Direction, :Type => Name, :Default => :L2R
385
- field :ViewArea, :Type => Name, :Default => :CropBox, :Version => "1.4"
386
- field :ViewClip, :Type => Name, :Default => :CropBox, :Version => "1.4"
387
- field :PrintArea, :Type => Name, :Default => :CropBox, :Version => "1.4"
388
- field :PrintClip, :Type => Name, :Default => :CropBox, :Version => "1.4"
389
- field :PrintScaling, :Type => Name, :Default => :AppDefault, :Version => "1.6"
390
- field :Duplex, :Type => Name, :Default => :Simplex, :Version => "1.7"
391
- field :PickTrayByPDFSize, :Type => Boolean, :Version => "1.7"
392
- field :PrintPageRange, :Type => Array, :Version => "1.7"
393
- field :NumCopies, :Type => Integer, :Version => "1.7"
394
- field :Enforce, :Type => Array, :Version => "1.7", :ExtensionLevel => 3
395
-
396
- end
397
-
398
- class Requirement < Dictionary
399
- include StandardObject
400
-
401
- class Handler < Dictionary
402
- include StandardObject
403
-
404
- module Type
405
- JS = :JS
406
- NOOP = :NoOp
407
- end
408
-
409
- field :Type, :Type => Name, :Default => :ReqHandler
410
- field :S, :Type => Name, :Default => Type::NOOP, :Required => true
411
- field :Script, :Type => ByteString
408
+ class Catalog < Dictionary
409
+ include StandardObject
410
+
411
+ field :Type, :Type => Name, :Default => :Catalog, :Required => true
412
+ field :Version, :Type => Name, :Version => "1.4"
413
+ field :Pages, :Type => PageTreeNode, :Required => true
414
+ field :PageLabels, :Type => NumberTreeNode.of(PageLabel), :Version => "1.3"
415
+ field :Names, :Type => Names, :Version => "1.2"
416
+ field :Dests, :Type => Dictionary, :Version => "1.1"
417
+ field :ViewerPreferences, :Type => ViewerPreferences, :Version => "1.2"
418
+ field :PageLayout, :Type => Name, :Default => PageLayout::SINGLE
419
+ field :PageMode, :Type => Name, :Default => PageMode::NONE
420
+ field :Outlines, :Type => Outline
421
+ field :Threads, :Type => Array, :Version => "1.1"
422
+ field :OpenAction, :Type => [ Array, Dictionary ], :Version => "1.1"
423
+ field :AA, :Type => CatalogAdditionalActions, :Version => "1.4"
424
+ field :URI, :Type => Dictionary, :Version => "1.1"
425
+ field :AcroForm, :Type => InteractiveForm, :Version => "1.2"
426
+ field :Metadata, :Type => MetadataStream, :Version => "1.4"
427
+ field :StructTreeRoot, :Type => Dictionary, :Version => "1.3"
428
+ field :MarkInfo, :Type => Dictionary, :Version => "1.4"
429
+ field :Lang, :Type => String, :Version => "1.4"
430
+ field :SpiderInfo, :Type => WebCapture::SpiderInfo, :Version => "1.3"
431
+ field :OutputIntents, :Type => Array.of(OutputIntent), :Version => "1.4"
432
+ field :PieceInfo, :Type => Dictionary, :Version => "1.4"
433
+ field :OCProperties, :Type => Dictionary, :Version => "1.5"
434
+ field :Perms, :Type => Dictionary, :Version => "1.5"
435
+ field :Legal, :Type => Dictionary, :Version => "1.5"
436
+ field :Requirements, :Type => Array.of(Requirement), :Version => "1.7"
437
+ field :Collection, :Type => Collection, :Version => "1.7"
438
+ field :NeedsRendering, :Type => Boolean, :Version => "1.7", :Default => false
439
+ field :Extensions, :Type => Extensions, :Version => "1.7", :ExtensionLevel => 3
440
+
441
+ def initialize(hash = {}, parser = nil)
442
+ set_indirect(true)
443
+
444
+ super(hash, parser)
445
+ end
412
446
  end
413
447
 
414
- field :Type, :Type => Name, :Default => :Requirement
415
- field :S, :Type => Name, :Default => :EnableJavaScripts, :Version => "1.7", :Required => true
416
- field :RH, :Type => Array
417
- end
418
-
419
- #
420
- # Class representing an extension Dictionary.
421
- #
422
- class Extensions < Dictionary
423
- include StandardObject
424
-
425
- field :Type, :Type => Name, :Default => :Extensions
426
- end
427
-
428
- #
429
- # Class representing a developer extension.
430
- #
431
- class DeveloperExtension < Dictionary
432
- include StandardObject
433
-
434
- field :Type, :Type => Name, :Default => :DeveloperExtensions
435
- field :BaseVersion, :Type => Name, :Required => true
436
- field :ExtensionLevel, :Type => Integer, :Required => true
437
-
438
- end
439
-
440
- #
441
- # Class representing the Catalog Dictionary of a PDF file.
442
- #
443
- class Catalog < Dictionary
444
-
445
- include StandardObject
446
-
447
- field :Type, :Type => Name, :Default => :Catalog, :Required => true
448
- field :Version, :Type => Name, :Version => "1.4"
449
- field :Pages, :Type => Dictionary, :Required => true
450
- field :PageLabels, :Type => Dictionary, :Version => "1.3"
451
- field :Names, :Type => Dictionary, :Version => "1.2"
452
- field :Dests, :Type => Dictionary, :Version => "1.1"
453
- field :ViewerPreferences, :Type => ViewerPreferences, :Version => "1.2"
454
- field :PageLayout, :Type => Name, :Default => PageLayout::SINGLE
455
- field :PageMode, :Type => Name, :Default => PageMode::NONE
456
- field :Outlines, :Type => Dictionary
457
- field :Threads, :Type => Array, :Version => "1.1"
458
- field :OpenAction, :Type => [ Array, Dictionary ], :Version => "1.1"
459
- field :AA, :Type => Dictionary, :Version => "1.4"
460
- field :URI, :Type => Dictionary, :Version => "1.1"
461
- field :AcroForm, :Type => Dictionary, :Version => "1.2"
462
- field :Metadata, :Type => Stream, :Version => "1.4"
463
- field :StructTreeRoot, :Type => Dictionary, :Version => "1.3"
464
- field :MarkInfo, :Type => Dictionary, :Version => "1.4"
465
- field :Lang, :Type => String, :Version => "1.4"
466
- field :SpiderInfo, :Type => Dictionary, :Version => "1.3"
467
- field :OutputIntents, :Type => Array, :Version => "1.4"
468
- field :PieceInfo, :Type => Dictionary, :Version => "1.4"
469
- field :OCProperties, :Type => Dictionary, :Version => "1.5"
470
- field :Perms, :Type => Dictionary, :Version => "1.5"
471
- field :Legal, :Type => Dictionary, :Version => "1.5"
472
- field :Requirements, :Type => Array, :Version => "1.7"
473
- field :Collection, :Type => Dictionary, :Version => "1.7"
474
- field :NeedsRendering, :Type => Boolean, :Version => "1.7", :Default => false
475
- field :Extensions, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
476
-
477
- def initialize(hash = {})
478
- set_indirect(true)
479
-
480
- super(hash)
481
- end
482
-
483
- end
484
-
485
448
  end