origami 1.2.5 → 1.2.6

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 (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