origami 1.2.5 → 1.2.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/bin/gui/config.rb +0 -4
  3. data/bin/gui/imgview.rb +2 -2
  4. data/bin/gui/menu.rb +11 -3
  5. data/bin/gui/treeview.rb +9 -3
  6. data/bin/pdfexplode +220 -0
  7. data/bin/pdfextract +3 -0
  8. data/lib/origami/acroform.rb +2 -2
  9. data/lib/origami/actions.rb +62 -35
  10. data/lib/origami/annotations.rb +3 -2
  11. data/lib/origami/array.rb +27 -4
  12. data/lib/origami/boolean.rb +2 -2
  13. data/lib/origami/catalog.rb +45 -45
  14. data/lib/origami/dictionary.rb +87 -14
  15. data/lib/origami/encryption.rb +46 -24
  16. data/lib/origami/file.rb +1 -2
  17. data/lib/origami/filters/ccitt.rb +118 -66
  18. data/lib/origami/filters/flate.rb +5 -1
  19. data/lib/origami/filters.rb +84 -2
  20. data/lib/origami/font.rb +71 -71
  21. data/lib/origami/graphics/patterns.rb +2 -1
  22. data/lib/origami/graphics/xobject.rb +123 -1
  23. data/lib/origami/javascript.rb +2 -1
  24. data/lib/origami/name.rb +2 -2
  25. data/lib/origami/null.rb +2 -2
  26. data/lib/origami/numeric.rb +11 -3
  27. data/lib/origami/object.rb +37 -16
  28. data/lib/origami/page.rb +135 -71
  29. data/lib/origami/parser.rb +11 -4
  30. data/lib/origami/parsers/pdf/linear.rb +1 -0
  31. data/lib/origami/parsers/pdf.rb +10 -0
  32. data/lib/origami/pdf.rb +10 -70
  33. data/lib/origami/reference.rb +4 -5
  34. data/lib/origami/signature.rb +22 -8
  35. data/lib/origami/stream.rb +41 -20
  36. data/lib/origami/string.rb +15 -6
  37. data/lib/origami/trailer.rb +9 -5
  38. data/lib/origami.rb +19 -0
  39. data/samples/actions/loop/loopgoto.rb +1 -1
  40. data/samples/actions/loop/loopnamed.rb +2 -2
  41. data/samples/actions/named/named.rb +1 -1
  42. data/samples/actions/samba/smbrelay.rb +1 -1
  43. data/samples/actions/triggerevents/trigger.rb +13 -13
  44. data/samples/actions/webbug/webbug-browser.rb +1 -1
  45. data/samples/actions/webbug/webbug-js.rb +1 -1
  46. data/samples/actions/webbug/webbug-reader.rb +1 -1
  47. data/samples/attachments/attach.rb +2 -2
  48. data/samples/exploits/cve-2008-2992-utilprintf.rb +1 -1
  49. data/samples/exploits/cve-2009-0927-geticon.rb +1 -1
  50. data/samples/exploits/exploit_customdictopen.rb +2 -2
  51. data/samples/exploits/getannots.rb +1 -1
  52. data/samples/javascript/js.rb +2 -2
  53. data/test/ts_pdf.rb +23 -23
  54. metadata +71 -86
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 400584bfa83ed0b47e3501f85f6ea19733aa365a
4
+ data.tar.gz: ebe216889afe825f5ea850ac3a330b7b462afb0a
5
+ SHA512:
6
+ metadata.gz: a5383fd775e5eb11a8a2a1212b4e8523f9751974fae787fd72074206e435b29b6e5d915c05154e25c12617733b317f20e5a3cd3c7f099a275ec995ce55308bb8
7
+ data.tar.gz: 5dd566b2f6cedca2b1be9b124d809efb46cc8057fc4f6ffbd5a7f22a56c165da1ff0fd1e1ea0e8be8695d98ef8363adbefafeb77fcab256ddea3dad38cbe674a
data/bin/gui/config.rb CHANGED
@@ -96,10 +96,8 @@ module PDFWalker
96
96
  end
97
97
 
98
98
  def set_verbosity(level)
99
-
100
99
  @conf["Debug"]['Verbosity'] = level
101
100
  save
102
-
103
101
  end
104
102
 
105
103
  def verbosity
@@ -113,11 +111,9 @@ module PDFWalker
113
111
  private
114
112
 
115
113
  def set_missing_values
116
-
117
114
  @conf ||= {}
118
115
 
119
116
  DEFAULT_CONFIG.each_key do |cat|
120
-
121
117
  @conf[cat] = {} unless @conf.include?(cat)
122
118
 
123
119
  DEFAULT_CONFIG[cat].each_pair do |key, value|
data/bin/gui/imgview.rb CHANGED
@@ -41,11 +41,11 @@ module PDFWalker
41
41
  }
42
42
  end
43
43
 
44
- def show_raw_img(data, w, h, bpp, bpr)
44
+ def show_raw_img(data, w, h, bpc, bpr)
45
45
  set_default_size w,h
46
46
 
47
47
  pixbuf = Gdk::Pixbuf.new data,
48
- Gdk::Pixbuf::ColorSpace::RGB, false, bpp,
48
+ Gdk::Pixbuf::ColorSpace::RGB, false, bpc,
49
49
  w, h,
50
50
  bpr
51
51
 
data/bin/gui/menu.rb CHANGED
@@ -153,8 +153,16 @@ module PDFWalker
153
153
  :Callback => lambda { |widget, viewer, path|
154
154
  stm = viewer.model.get_value(viewer.model.get_iter(path), viewer.class::OBJCOL)
155
155
  w,h = stm.Width, stm.Height
156
+ colors =
157
+ case stm.ColorSpace
158
+ when :DeviceGray.to_o then 1
159
+ when :DeviceRGB.to_o then 3
160
+ when :DeviceCMYK.to_o then 4
161
+ else
162
+ 1
163
+ end
156
164
  bpc = stm.BitsPerComponent || 8
157
- bpr = (bpc >> 3) * 3 * w
165
+ bpr = (w * colors * bpc + 7) >> 3
158
166
  data = stm.data
159
167
 
160
168
  begin
@@ -162,7 +170,7 @@ module PDFWalker
162
170
  if stm.Filter == :DCTDecode or (stm.Filter.is_a?(Array) and stm.Filter[0] == :DCTDecode)
163
171
  imgview.show_compressed_img data
164
172
  else
165
- imgview.show_raw_img data, w, h, bpp, bpr
173
+ imgview.show_raw_img data, w, h, bpc, bpr
166
174
  end
167
175
  rescue Exception => e
168
176
  viewer.parent.error("#{e.class}: #{e.message}")
@@ -179,7 +187,7 @@ module PDFWalker
179
187
  if obj.is_a?(Graphics::ImageXObject)
180
188
  :Image
181
189
  else
182
- obj.real_type.to_s.split("::").last.to_sym
190
+ obj.native_type.to_s.split("::").last.to_sym
183
191
  end
184
192
  else case obj
185
193
  when Origami::PDF
data/bin/gui/treeview.rb CHANGED
@@ -107,7 +107,7 @@ module PDFWalker
107
107
  expand_row(path, false)
108
108
  end
109
109
 
110
- goto(obj.solve) if obj.is_a?(Origami::Reference)
110
+ goto(obj) if obj.is_a?(Origami::Reference)
111
111
  end
112
112
  }
113
113
 
@@ -136,7 +136,13 @@ module PDFWalker
136
136
  if obj.is_a?(Name) and obj.parent.is_a?(Dictionary) and obj.parent.has_key?(obj)
137
137
  obj = obj.parent[obj]
138
138
  elsif obj.is_a?(Reference)
139
- obj = obj.solve
139
+ obj =
140
+ begin
141
+ obj.solve
142
+ rescue InvalidReferenceError
143
+ @parent.error("Object not found : #{obj}")
144
+ return
145
+ end
140
146
  end
141
147
 
142
148
  @treestore.each { |model, path, iter|
@@ -279,7 +285,7 @@ module PDFWalker
279
285
  obj = @treestore.append(container)
280
286
  @treestore.set_value(obj, OBJCOL, object)
281
287
 
282
- type = object.real_type.to_s.split('::').last.to_sym
288
+ type = object.native_type.to_s.split('::').last.to_sym
283
289
 
284
290
  if name.nil?
285
291
  name =
data/bin/pdfexplode ADDED
@@ -0,0 +1,220 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ =begin
4
+
5
+ = Author:
6
+ Guillaume Delugré <guillaume/at/security-labs.org>
7
+
8
+ = Info:
9
+ Explodes a PDF into separate documents.
10
+ = License:
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
+ begin
27
+ require 'origami'
28
+ rescue LoadError
29
+ ORIGAMIDIR = "#{File.dirname(__FILE__)}/../lib"
30
+ $: << ORIGAMIDIR
31
+ require 'origami'
32
+ end
33
+ include Origami
34
+
35
+ require 'optparse'
36
+ require 'rexml/document'
37
+
38
+ class OptParser
39
+ BANNER = <<USAGE
40
+ Usage: #{$0} <PDF-file> [-r <range>] [-t pages|rsrc] [-d <output-directory>]
41
+ Explodes a document into separate documents.
42
+ Bug reports or feature requests at: http://origami-pdf.googlecode.com/
43
+
44
+ Options:
45
+ USAGE
46
+
47
+ def self.parser(options)
48
+ OptionParser.new do |opts|
49
+ opts.banner = BANNER
50
+
51
+ opts.on("-d", "--output-dir DIR", "Output directory.") do |d|
52
+ options[:output_dir] = d
53
+ end
54
+
55
+ opts.on("-r", "--range PAGES", "Page range (e.g: 2-, 1-3, 5). Default to '-'.") do |r|
56
+ range =
57
+ if r.index('-').nil?
58
+ page = r.to_i
59
+ Range.new(page-1, page-1)
60
+ else
61
+ from, to = r.split('-').map{|bound| bound.to_i}
62
+ from ||= 1
63
+ to ||= 0
64
+ Range.new(from-1, to-1)
65
+ end
66
+ options[:page_range] = range
67
+ end
68
+
69
+ opts.on("-t", "--type TYPE", "Split by type. Can be 'pages' or 'rsrc'. Default to 'pages'.") do |t|
70
+ options[:split_by] = t
71
+ end
72
+
73
+ opts.on_tail("-h", "--help", "Show this message.") do
74
+ puts opts
75
+ exit
76
+ end
77
+ end
78
+ end
79
+
80
+ def self.parse(args)
81
+ options =
82
+ {
83
+ :page_range => (0..-1),
84
+ :split_by => 'pages'
85
+ }
86
+
87
+ self.parser(options).parse!(args)
88
+
89
+ options
90
+ end
91
+ end
92
+
93
+ begin
94
+ @options = OptParser.parse(ARGV)
95
+
96
+ if ARGV.empty?
97
+ STDERR.puts "Error: No filename was specified. #{$0} --help for details."
98
+ exit 1
99
+ else
100
+ target = ARGV.shift
101
+ end
102
+
103
+ if @options[:output_dir].nil?
104
+ @options[:output_dir] = "#{File.join(File.dirname(target), File.basename(target,'.pdf'))}.explode"
105
+ end
106
+
107
+ Origami::OPTIONS[:ignore_bad_references] = true
108
+ OUTPUT_DIR = @options[:output_dir]
109
+ Dir::mkdir(OUTPUT_DIR) unless File.directory?(OUTPUT_DIR)
110
+
111
+ def split_by_rsrc(n, page, type)
112
+ all_rsrc = page.resources
113
+ type_rsrc = page.ls_resources(type)
114
+ other_rsrc = all_rsrc.keys - type_rsrc.keys
115
+
116
+ unless type_rsrc.empty?
117
+ # Keep only specified resource type.
118
+ output_file = File.join(OUTPUT_DIR, "page_#{n}_keeponly_#{type}.pdf")
119
+ PDF.write(output_file) do |pdf|
120
+ reduced = page.copy
121
+ # New resource dictionary with only matching resources.
122
+ reduced.Resources = Resources.new(type => type_rsrc)
123
+ # Remove mention of other resources.
124
+ reduced.Contents.data = reduced.Contents.data.lines.to_a.
125
+ delete_if {|line| other_rsrc.any?{|rsrc| line =~ /#{rsrc}/}}.join
126
+
127
+ STDERR.puts "Creating #{output_file}..."
128
+ pdf.append_page(reduced)
129
+ end
130
+
131
+ # Remove all specified resource type.
132
+ output_file = File.join(OUTPUT_DIR, "page_#{n}_excluded_#{type}.pdf")
133
+ PDF.write(output_file) do |pdf|
134
+ reduced = page.copy
135
+ # New resource dictionary with no resource of specified type.
136
+ reduced.Resources = reduced.Resources.copy
137
+ reduced.Resources.delete(type)
138
+ # Remove mention this resource type.
139
+ reduced.Contents.data = reduced.Contents.data.lines.to_a.
140
+ delete_if {|line| type_rsrc.keys.any?{|rsrc| line =~ /#{rsrc}/}}.join
141
+
142
+ STDERR.puts "Creating #{output_file}..."
143
+ pdf.append_page(reduced)
144
+ end
145
+
146
+ # Now treating each resource object separately.
147
+ type_rsrc.each_pair do |name, rsrc|
148
+ anyother_rsrc = all_rsrc.keys - [ name ]
149
+ # Keey only specified resource object.
150
+ output_file = File.join(OUTPUT_DIR, "page_#{n}_keeponly_#{type}_#{name}.pdf")
151
+ PDF.write(output_file) do |pdf|
152
+ reduced = page.copy
153
+ # New resource dictionary with only specified resource object.
154
+ reduced.Resources = Resources.new(type => {name => rsrc})
155
+ # Remove mention of all other resources.
156
+ reduced.Contents.data = reduced.Contents.data.lines.to_a.
157
+ delete_if {|line| anyother_rsrc.any?{|rsrc| line =~ /#{rsrc}/}}.join
158
+
159
+ STDERR.puts "Creating #{output_file}..."
160
+ pdf.append_page(reduced)
161
+ end
162
+
163
+ # Remove only specified resource object.
164
+ output_file = File.join(OUTPUT_DIR, "page_#{n}_excluded_#{type}_#{name}.pdf")
165
+ PDF.write(output_file) do |pdf|
166
+ reduced = page.copy
167
+ # New resource dictionary with only specified resource object.
168
+ reduced.Resources = reduced.Resources.copy
169
+ reduced.Resources[type] = reduced.Resources.send(type).copy
170
+ reduced.Resources[type].delete(name)
171
+ # Remove mention of this resource only.
172
+ reduced.Contents.data = reduced.Contents.data.lines.to_a.
173
+ delete_if {|line| line =~ /#{name}/}.join
174
+
175
+ STDERR.puts "Creating #{output_file}..."
176
+ pdf.append_page(reduced)
177
+ end
178
+ end
179
+ end
180
+ end
181
+
182
+ params =
183
+ {
184
+ :verbosity => Parser::VERBOSE_QUIET,
185
+ }
186
+ pdf = PDF.read(target, params)
187
+
188
+ i = @options[:page_range].first + 1
189
+ pdf.pages[@options[:page_range]].each do |page|
190
+ case @options[:split_by]
191
+ when 'pages'
192
+ output_file = File.join(OUTPUT_DIR, "page_#{i}.pdf")
193
+ PDF.write(output_file) do |pdf|
194
+ STDERR.puts "Creating #{output_file}..."
195
+ pdf.append_page(page)
196
+ end
197
+
198
+ when 'rsrc'
199
+ [ Resources::EXTGSTATE,
200
+ Resources::COLORSPACE,
201
+ Resources::PATTERN,
202
+ Resources::SHADING,
203
+ Resources::XOBJECT,
204
+ Resources::FONT,
205
+ Resources::PROPERTIES
206
+ ].each { |type| split_by_rsrc(i, page, type) }
207
+
208
+ else
209
+ raise ArgumentError, "Unknown split option: #{@options[:split_by]}"
210
+ end
211
+
212
+ i += 1
213
+ end
214
+
215
+ rescue SystemExit
216
+ rescue Exception => e
217
+ STDERR.puts "#{e.class}: #{e.message} #{e.backtrace}"
218
+ exit 1
219
+ end
220
+
data/bin/pdfextract CHANGED
@@ -120,6 +120,9 @@ begin
120
120
  @options[:output_dir] = "#{File.basename(target, '.pdf')}.dump"
121
121
  end
122
122
 
123
+ # Force data extraction, even for invalid FlateDecode streams.
124
+ Origami::OPTIONS[:ignore_zlib_errors] = true
125
+
123
126
  OUTPUT_DIR = @options[:output_dir]
124
127
  Dir::mkdir(OUTPUT_DIR) unless File.directory?(OUTPUT_DIR)
125
128
 
@@ -147,7 +147,7 @@ module Origami
147
147
 
148
148
  def self.included(receiver) #:nodoc:
149
149
 
150
- receiver.field :FT, :Type => Name, :Default => Type::TEXT, :Required => true
150
+ receiver.field :FT, :Type => Name, :Required => true
151
151
  receiver.field :Parent, :Type => Dictionary
152
152
  receiver.field :Kids, :Type => Array
153
153
  receiver.field :T, :Type => String
@@ -254,7 +254,7 @@ module Origami
254
254
  end
255
255
 
256
256
  field :Type, :Type => Name, :Default => :SigFieldLock
257
- field :Action, :Type => Name, :Default => Actions::ALL, :Required => true
257
+ field :Action, :Type => Name, :Required => true
258
258
  field :Fields, :Type => Array
259
259
 
260
260
  def pre_build
@@ -65,18 +65,20 @@ module Origami
65
65
  # Creates a new GoTo Action.
66
66
  # _hash_:: A hash of options to set for this jump.
67
67
  #
68
- def initialize(hash = {})
69
-
68
+ def self.[](hash = {})
70
69
  if hash.is_a? Destination
71
- super(:S => :GoTo, :D => hash)
70
+ self.new(:S => :GoTo, :D => hash)
72
71
  else
73
- super(hash)
72
+ self.new(hash)
74
73
  end
75
-
76
74
  end
77
75
 
78
76
  end
79
77
 
78
+ def self.GoTo(hash = {})
79
+ Action::GoTo[hash]
80
+ end
81
+
80
82
  #
81
83
  # Class representing an action launching an URL.
82
84
  #
@@ -91,12 +93,16 @@ module Origami
91
93
  # _uri_:: The URI to launch.
92
94
  # _ismap_::
93
95
  #
94
- def initialize(uri, ismap = false)
95
- super(:URI => uri, :IsMap => ismap)
96
+ def self.[](uri, ismap = false)
97
+ self.new(:URI => uri, :IsMap => ismap)
96
98
  end
97
99
 
98
100
  end
99
101
 
102
+ def self.URI(uri, ismap = false)
103
+ Action::URI[uri, ismap]
104
+ end
105
+
100
106
  #
101
107
  # Class representing a JavaScript Action.
102
108
  #
@@ -109,11 +115,15 @@ module Origami
109
115
  # Creates a new JavaScript Action.
110
116
  # _script_:: The script to be executed.
111
117
  #
112
- def initialize(script)
113
- super(:JS => script)
118
+ def self.[](script)
119
+ self.new(:JS => script)
114
120
  end
115
121
  end
116
122
 
123
+ def self.JavaScript(script)
124
+ Action::JavaScript[script]
125
+ end
126
+
117
127
  #
118
128
  # Class representing an Action which run a command on the current system.
119
129
  #
@@ -147,19 +157,22 @@ module Origami
147
157
  #
148
158
  class Named < Action
149
159
 
150
- NEXTPAGE = :NextPage
151
- PREVPAGE = :PrevPage
152
- FIRSTPAGE = :FirstPage
153
- LASTPAGE = :LastPage
154
- PRINT = :Print
155
-
156
160
  field :S, :Type => Name, :Default => :Named, :Required => true
157
161
  field :N, :Type => Name, :Required => true
158
162
 
159
- def initialize(type)
160
- super(:N => type)
163
+ def self.[](type)
164
+ self.new(:N => type)
161
165
  end
162
166
 
167
+ NEXTPAGE = self[:NextPage]
168
+ PREVPAGE = self[:PrevPage]
169
+ FIRSTPAGE = self[:FirstPage]
170
+ LASTPAGE = self[:LastPage]
171
+ PRINT = self[:Print]
172
+ end
173
+
174
+ def self.Named(type)
175
+ Action::Named[type]
163
176
  end
164
177
 
165
178
  #
@@ -178,11 +191,15 @@ module Origami
178
191
  # _dest_:: A Destination in the file.
179
192
  # _newwindow_:: Specifies whether the file has to be opened in a new window.
180
193
  #
181
- def initialize(file, dest = Destination::GlobalFit.new(0), newwindow = false)
182
- super(:F => file, :D => dest, :NewWindow => newwindow)
194
+ def self.[](file, dest = Destination::GlobalFit.new(0), newwindow = false)
195
+ self.new(:F => file, :D => dest, :NewWindow => newwindow)
183
196
  end
184
197
 
185
198
  end
199
+
200
+ def self.GoToR(file, dest = Destination::GlobalFit.new(0), newwindow = false)
201
+ Action::GoToR[file, dest, newwindow]
202
+ end
186
203
 
187
204
  #
188
205
  # Class representing a GoTo Action to an embedded pdf file.
@@ -215,11 +232,15 @@ module Origami
215
232
 
216
233
  end
217
234
 
218
- def initialize(filename, dest, newwindow = false)
219
- super(:T => EmbeddedTarget.new(:R => :C, :N => filename), :D => dest, :NewWindow => newwindow)
235
+ def self.[](filename, dest, newwindow = false)
236
+ self.new(:T => EmbeddedTarget.new(:R => :C, :N => filename), :D => dest, :NewWindow => newwindow)
220
237
  end
221
238
 
222
239
  end
240
+
241
+ def self.GoToE(filename, dest, newwindow = false)
242
+ Action::GoToE[filename, dest, newwindow]
243
+ end
223
244
 
224
245
  #
225
246
  # (PDF 1.2) Send data to a uniform resource locator. p703
@@ -247,31 +268,33 @@ module Origami
247
268
  field :Fields, :Type => Array
248
269
  field :Flags, :Type => Integer, :Default => 0
249
270
 
250
- def initialize(url, fields = [], flags = 0)
251
- if not url.is_a? FileSpec
252
- url = FileSpec.new(:FS => :URL, :F => url)
253
- end
254
-
255
- super(:F => url, :Fields => fields, :Flags => flags)
271
+ def self.[](url, fields = [], flags = 0)
272
+ url = FileSpec.new(:FS => :URL, :F => url) unless url.is_a? FileSpec
273
+ self.new(:F => url, :Fields => fields, :Flags => flags)
256
274
  end
257
275
 
258
276
  end
259
277
 
278
+ def self.SubmitForm(url, fields = [], flags = 0)
279
+ Action::SubmitForm[url, fields, flags]
280
+ end
281
+
260
282
  class ImportData < Action
261
283
 
262
284
  field :S, :Type => Name, :Default => :ImportData, :Required => true
263
285
  field :F, :Type => Dictionary, :Required => true
264
286
 
265
- def initialize(file)
266
- if not file.is_a? FileSpec
267
- file = FileSpec.new(:FS => :File, :F => file)
268
- end
269
-
270
- super(:F => file)
287
+ def self.[](file)
288
+ file = FileSpec.new(:FS => :File, :F => file) unless file.is_a? FileSpec
289
+ self.new(:F => file)
271
290
  end
272
291
 
273
292
  end
274
293
 
294
+ def self.ImportData(file)
295
+ Action::ImportData[file]
296
+ end
297
+
275
298
  class RichMediaExecute < Action
276
299
 
277
300
  field :S, :Type => Name, :Default => :RichMediaExecute, :Version => "1.7", :ExtensionLevel => 3, :Required => true
@@ -287,11 +310,15 @@ module Origami
287
310
  field :A, :Type => Object, :Version => "1.7", :ExtensionLevel => 3
288
311
  end
289
312
 
290
- def initialize(annotation, command, *params)
291
- super(:TA => annotation, :CMD => Command.new(:C => command, :A => params))
313
+ def self.[](annotation, command, *params)
314
+ self.new(:TA => annotation, :CMD => Command.new(:C => command, :A => params))
292
315
  end
293
316
 
294
317
  end
318
+
319
+ def self.RichMediaExecute(annotation, command, *params)
320
+ Action::RichMediaExecute[annotation, command, *params]
321
+ end
295
322
 
296
323
  end
297
324
 
@@ -34,7 +34,7 @@ module Origami
34
34
  include StandardObject
35
35
 
36
36
  field :Type, :Type => Name, :Default => :Annot
37
- field :Subtype, :Type => Name, :Default => :Text, :Required => true
37
+ field :Subtype, :Type => Name, :Required => true
38
38
  field :Rect, :Type => Array, :Default => [ 0 , 0 , 0 , 0 ], :Required => true
39
39
  field :Contents, :Type => String
40
40
  field :P, :Type => Dictionary, :Version => "1.3"
@@ -257,7 +257,7 @@ module Origami
257
257
 
258
258
  include Markup
259
259
 
260
- field :Subtype, :Type => Name, :Default => :Square, :Required => true
260
+ field :Subtype, :Type => Name, :Required => true
261
261
  field :BS, :Type => Dictionary
262
262
  field :IC, :Type => Array
263
263
  field :BE, :Type => Dictionary, :Version => "1.5"
@@ -266,6 +266,7 @@ module Origami
266
266
  end
267
267
 
268
268
  class Square < Shape
269
+ field :Subtype, :Type => Name, :Default => :Square, :Required => true
269
270
  end
270
271
 
271
272
  class Circle < Shape
data/lib/origami/array.rb CHANGED
@@ -85,7 +85,7 @@ module Origami
85
85
  super
86
86
  end
87
87
 
88
- def self.parse(stream) #:nodoc:
88
+ def self.parse(stream, parser = nil) #:nodoc:
89
89
  data = []
90
90
  offset = stream.pos
91
91
 
@@ -100,7 +100,7 @@ module Origami
100
100
  raise InvalidArrayObjectError, "Bad embedded object format"
101
101
  end
102
102
 
103
- value = type.parse(stream)
103
+ value = type.parse(stream, parser)
104
104
  data << value
105
105
  end
106
106
 
@@ -155,7 +155,20 @@ module Origami
155
155
 
156
156
  alias value to_a
157
157
 
158
- def real_type ; Origami::Array end
158
+ def copy
159
+ copy = self.class.new
160
+ self.each do |obj|
161
+ copy << obj.copy
162
+ end
163
+
164
+ copy.parent = @parent
165
+ copy.no, copy.generation = @no, @generation
166
+ copy.set_indirect(true) if is_indirect?
167
+ copy.set_pdf(@pdf) if is_indirect?
168
+ copy
169
+ end
170
+
171
+ def self.native_type ; Origami::Array end
159
172
 
160
173
  end
161
174
 
@@ -167,7 +180,17 @@ module Origami
167
180
  class << self
168
181
 
169
182
  def [](coords)
170
- corners = coords.values_at(:llx, :lly, :urx, :ury)
183
+ corners =
184
+ if [ :llx, :lly, :urx, :ury ].all? {|p| coords.include?(p)}
185
+ coords.values_at(:llx, :lly, :urx, :ury)
186
+ elsif [ :width, :height ].all? {|p| coords.include?(p)}
187
+ width, height = coords.values_at(:width, :height)
188
+ x = coords.values_at(:x).first || 0
189
+ y = coords.values_at(:y).first || 0
190
+ [ x, y, x+width, y+height ]
191
+ else
192
+ raise ArgumentError, "Bad arguments for #{self.class}: #{coords.inspect}"
193
+ end
171
194
 
172
195
  unless corners.all? { |corner| corner.is_a?(Numeric) }
173
196
  raise TypeError, "All coords must be numbers"
@@ -59,7 +59,7 @@ module Origami
59
59
  super(@value.to_s)
60
60
  end
61
61
 
62
- def self.parse(stream) #:nodoc:
62
+ def self.parse(stream, parser = nil) #:nodoc:
63
63
 
64
64
  offset = stream.pos
65
65
 
@@ -82,7 +82,7 @@ module Origami
82
82
  @value
83
83
  end
84
84
 
85
- def real_type ; Boolean end
85
+ def self.native_type ; Boolean end
86
86
 
87
87
  def false?
88
88
  @value == false