origami 2.0.0 → 2.0.1

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -0
  3. data/bin/gui/config.rb +2 -1
  4. data/bin/gui/file.rb +118 -240
  5. data/bin/gui/gtkhex.rb +5 -5
  6. data/bin/gui/hexview.rb +20 -16
  7. data/bin/gui/imgview.rb +1 -1
  8. data/bin/gui/menu.rb +138 -158
  9. data/bin/gui/properties.rb +46 -48
  10. data/bin/gui/signing.rb +183 -214
  11. data/bin/gui/textview.rb +1 -1
  12. data/bin/gui/treeview.rb +13 -7
  13. data/bin/gui/walker.rb +102 -71
  14. data/bin/gui/xrefs.rb +1 -1
  15. data/bin/pdf2ruby +3 -3
  16. data/bin/pdfcop +18 -11
  17. data/bin/pdfextract +14 -5
  18. data/bin/pdfmetadata +3 -3
  19. data/bin/shell/console.rb +8 -8
  20. data/bin/shell/hexdump.rb +4 -4
  21. data/examples/attachments/nested_document.rb +1 -1
  22. data/examples/javascript/hello_world.rb +3 -3
  23. data/lib/origami.rb +0 -1
  24. data/lib/origami/acroform.rb +3 -3
  25. data/lib/origami/array.rb +1 -3
  26. data/lib/origami/boolean.rb +1 -3
  27. data/lib/origami/catalog.rb +3 -9
  28. data/lib/origami/destinations.rb +2 -2
  29. data/lib/origami/dictionary.rb +15 -29
  30. data/lib/origami/encryption.rb +334 -692
  31. data/lib/origami/extensions/fdf.rb +3 -2
  32. data/lib/origami/extensions/ppklite.rb +5 -9
  33. data/lib/origami/filespec.rb +2 -2
  34. data/lib/origami/filters.rb +54 -36
  35. data/lib/origami/filters/ascii.rb +67 -49
  36. data/lib/origami/filters/ccitt.rb +4 -236
  37. data/lib/origami/filters/ccitt/tables.rb +267 -0
  38. data/lib/origami/filters/crypt.rb +1 -1
  39. data/lib/origami/filters/dct.rb +0 -1
  40. data/lib/origami/filters/flate.rb +3 -43
  41. data/lib/origami/filters/lzw.rb +62 -99
  42. data/lib/origami/filters/predictors.rb +135 -105
  43. data/lib/origami/filters/runlength.rb +34 -22
  44. data/lib/origami/graphics.rb +2 -2
  45. data/lib/origami/graphics/colors.rb +89 -63
  46. data/lib/origami/graphics/path.rb +14 -14
  47. data/lib/origami/graphics/patterns.rb +31 -33
  48. data/lib/origami/graphics/render.rb +0 -1
  49. data/lib/origami/graphics/state.rb +9 -9
  50. data/lib/origami/graphics/text.rb +17 -17
  51. data/lib/origami/graphics/xobject.rb +102 -92
  52. data/lib/origami/javascript.rb +91 -68
  53. data/lib/origami/linearization.rb +22 -20
  54. data/lib/origami/metadata.rb +1 -1
  55. data/lib/origami/name.rb +1 -3
  56. data/lib/origami/null.rb +1 -3
  57. data/lib/origami/numeric.rb +3 -13
  58. data/lib/origami/object.rb +100 -72
  59. data/lib/origami/page.rb +24 -28
  60. data/lib/origami/parser.rb +34 -51
  61. data/lib/origami/parsers/fdf.rb +2 -2
  62. data/lib/origami/parsers/pdf.rb +41 -18
  63. data/lib/origami/parsers/pdf/lazy.rb +83 -46
  64. data/lib/origami/parsers/pdf/linear.rb +19 -10
  65. data/lib/origami/parsers/ppklite.rb +1 -1
  66. data/lib/origami/pdf.rb +150 -206
  67. data/lib/origami/reference.rb +4 -6
  68. data/lib/origami/signature.rb +76 -48
  69. data/lib/origami/stream.rb +69 -63
  70. data/lib/origami/string.rb +2 -19
  71. data/lib/origami/trailer.rb +25 -22
  72. data/lib/origami/version.rb +1 -1
  73. data/lib/origami/xfa.rb +6 -4
  74. data/lib/origami/xreftable.rb +29 -29
  75. data/test/test_annotations.rb +16 -38
  76. data/test/test_pdf_attachment.rb +1 -1
  77. data/test/test_pdf_parse.rb +1 -1
  78. data/test/test_xrefs.rb +2 -2
  79. metadata +4 -4
  80. data/lib/origami/export.rb +0 -247
@@ -68,7 +68,7 @@ module PDFWalker
68
68
  end
69
69
 
70
70
  text.encode!("UTF-8", replace: '.')
71
- .gsub!("\x00", '.')
71
+ .tr!("\x00", '.')
72
72
 
73
73
  @pdfbuffer.set_text(text)
74
74
  @pdfbuffer.apply_tag("Default", @pdfbuffer.start_iter, @pdfbuffer.end_iter)
@@ -41,7 +41,7 @@ module PDFWalker
41
41
  end
42
42
 
43
43
  class PDFTree < TreeView
44
- include Popable
44
+ include PopupMenu
45
45
 
46
46
  OBJCOL = 0
47
47
  TEXTCOL = 1
@@ -73,7 +73,7 @@ module PDFWalker
73
73
  end
74
74
  }
75
75
 
76
- signal_connect('row-activated') { |tree, path, column|
76
+ signal_connect('row-activated') { |_tree, path,_column|
77
77
  if selection.selected
78
78
  obj = @treestore.get_value(selection.selected, OBJCOL)
79
79
 
@@ -87,7 +87,7 @@ module PDFWalker
87
87
  end
88
88
  }
89
89
 
90
- signal_connect('row-expanded') { |tree, iter, path|
90
+ signal_connect('row-expanded') { |_tree, iter, _path|
91
91
  obj = @treestore.get_value(iter, OBJCOL)
92
92
 
93
93
  if obj.is_a?(Origami::Stream) and iter.n_children == 1
@@ -114,7 +114,7 @@ module PDFWalker
114
114
  }
115
115
 
116
116
  add_events(Gdk::Event::BUTTON_PRESS_MASK)
117
- signal_connect('button_press_event') { |widget, event|
117
+ signal_connect('button_press_event') { |_widget, event|
118
118
  if event.button == 3 && parent.opened
119
119
  path = get_path(event.x,event.y).first
120
120
  set_cursor(path, nil, false)
@@ -209,6 +209,12 @@ module PDFWalker
209
209
  end
210
210
  end
211
211
 
212
+ def object_by_path(path)
213
+ iter = @treestore.get_iter(path)
214
+
215
+ @treestore.get_value(iter, OBJCOL)
216
+ end
217
+
212
218
  private
213
219
 
214
220
  def object_to_tree_pos(obj)
@@ -221,7 +227,7 @@ module PDFWalker
221
227
  object_path.push(root_obj)
222
228
  end
223
229
 
224
- @treestore.each do |model, path, iter|
230
+ @treestore.each do |_model, path, iter|
225
231
  current_obj = @treestore.get_value(iter, OBJCOL)
226
232
 
227
233
  # Load the intermediate nodes if necessary.
@@ -295,7 +301,7 @@ module PDFWalker
295
301
  name =
296
302
  case object
297
303
  when Origami::String
298
- '"' + object.to_utf8.gsub("\x00", ".") + '"'
304
+ '"' + object.to_utf8.tr("\x00", ".") + '"'
299
305
  when Origami::Number, Origami::Name
300
306
  object.value.to_s
301
307
  else
@@ -379,7 +385,7 @@ module PDFWalker
379
385
  @@appearance[:Body] = {Color: "purple", Weight: Pango::WEIGHT_BOLD, Style: Pango::STYLE_NORMAL}
380
386
  @@appearance[:XRefSection] = {Color: "purple", Weight: Pango::WEIGHT_BOLD, Style: Pango::STYLE_NORMAL}
381
387
  @@appearance[:XRefSubSection] = {Color: "brown", Weight: Pango::WEIGHT_BOLD, Style: Pango::STYLE_NORMAL}
382
- @@appearance[:XRef] = {Color: "gray20", Weight: Pango::WEIGHT_BOLD, Style: Pango::STYLE_NORMAL}
388
+ @@appearance[:XRef] = {Weight: Pango::WEIGHT_BOLD, Style: Pango::STYLE_NORMAL}
383
389
  @@appearance[:Trailer] = {Color: "purple", Weight: Pango::WEIGHT_BOLD, Style: Pango::STYLE_NORMAL}
384
390
  @@appearance[:StartXref] = {Weight: Pango::WEIGHT_BOLD, Style: Pango::STYLE_NORMAL}
385
391
  @@appearance[:String] = {Color: "red", Weight: Pango::WEIGHT_NORMAL, Style: Pango::STYLE_ITALIC}
@@ -74,38 +74,9 @@ module PDFWalker #:nodoc:all
74
74
  :type => :body
75
75
  }
76
76
 
77
- @explorer_history = Array.new
77
+ @explorer_history = []
78
78
 
79
- signal_connect('destroy') {
80
- @config.save
81
- Gtk.main_quit
82
- }
83
-
84
- add_events(Gdk::Event::KEY_RELEASE_MASK)
85
- signal_connect('key_release_event') { |w, event|
86
- if event.keyval == Gdk::Keyval::GDK_F1 then about
87
- elsif event.keyval == Gdk::Keyval::GDK_Escape and @opened and not @explorer_history.empty?
88
- @treeview.goto(@explorer_history.pop)
89
- end
90
- }
91
-
92
- create_menus
93
- create_treeview
94
- create_hexview
95
- create_objectview
96
- create_panels
97
- create_statusbar
98
-
99
- @vbox = VBox.new
100
- @vbox.pack_start(@menu, false, false)
101
- @vbox.pack_start(@hpaned)
102
- @vbox.pack_end(@statusbar, false, false)
103
-
104
- add @vbox
105
-
106
- set_default_size(self.screen.width * 0.5, self.screen.height * 0.5)
107
- #maximize
108
- show_all
79
+ init_interface
109
80
 
110
81
  open(target_file)
111
82
  end
@@ -155,50 +126,13 @@ module PDFWalker #:nodoc:all
155
126
  dialog.vbox.pack_start(button_byname)
156
127
  dialog.vbox.pack_end(button_regexp)
157
128
 
158
- dialog.signal_connect('response') do |dlg, response|
129
+ dialog.signal_connect('response') do |_, response|
159
130
  if response != Gtk::Dialog::RESPONSE_OK
160
131
  dialog.destroy
161
132
  next
162
133
  end
163
134
 
164
- search =
165
- {
166
- :expr => entry.text,
167
- :regexp => button_regexp.active?,
168
- :type => button_byname.active? ? :name : :body
169
- }
170
-
171
- if search == @last_search
172
- @last_search_result.push @last_search_result.shift
173
- results = @last_search_result
174
- else
175
- expr = search[:regexp] ? Regexp.new(search[:expr]) : search[:expr]
176
-
177
- results =
178
- if search[:type] == :body
179
- @opened.grep(expr)
180
- else
181
- @opened.ls(expr, follow_references: false)
182
- end
183
-
184
- @last_search = search
185
- end
186
-
187
- if results.empty?
188
- error("No result found.")
189
- else
190
- if results != @last_search_result
191
- # Reset the previous search highlighting.
192
- @last_search_result.each do |obj| @treeview.highlight(obj, nil) end
193
-
194
- # Highlight the new results.
195
- results.each do |obj| @treeview.highlight(obj, "lightpink") end
196
-
197
- @last_search_result = results
198
- end
199
-
200
- @treeview.goto(results.first, follow_references: false)
201
- end
135
+ search_document(entry.text, regexp: button_regexp.active?, type: (button_bydata.active? ? 'body' : 'name'))
202
136
  end
203
137
 
204
138
  dialog.show_all
@@ -244,12 +178,89 @@ module PDFWalker #:nodoc:all
244
178
  if obj.nil?
245
179
  error("Object #{no} not found.")
246
180
  else
247
- @treeview.goto(obj)
181
+ @treeview.goto(obj)
248
182
  end
249
183
  end
250
184
 
251
185
  private
252
186
 
187
+ def init_interface
188
+ signal_connect('destroy') {
189
+ @config.save
190
+ Gtk.main_quit
191
+ }
192
+
193
+ add_events(Gdk::Event::KEY_RELEASE_MASK)
194
+ signal_connect('key_release_event') { |_w, event|
195
+ if event.keyval == Gdk::Keyval::GDK_F1 then about
196
+ elsif event.keyval == Gdk::Keyval::GDK_Escape and @opened and not @explorer_history.empty?
197
+ @treeview.goto(@explorer_history.pop)
198
+ end
199
+ }
200
+
201
+ create_menus
202
+ create_treeview
203
+ create_hexview
204
+ create_objectview
205
+ create_panels
206
+ create_statusbar
207
+
208
+ @vbox = VBox.new
209
+ @vbox.pack_start(@menu, false, false)
210
+ @vbox.pack_start(@hpaned)
211
+ @vbox.pack_end(@statusbar, false, false)
212
+
213
+ add @vbox
214
+
215
+ set_default_size(self.screen.width * 0.5, self.screen.height * 0.5)
216
+ show_all
217
+ end
218
+
219
+ def search_document(expr, regexp: false, type: 'body')
220
+ search =
221
+ {
222
+ expr: expr,
223
+ regexp: regexp,
224
+ type: type,
225
+ }
226
+
227
+ if search == @last_search
228
+ @last_search_result.push @last_search_result.shift
229
+ results = @last_search_result
230
+ else
231
+ expr = Regexp.new(expr) if search[:regexp]
232
+
233
+ results =
234
+ if search[:type] == 'body'
235
+ @opened.grep(expr)
236
+ else
237
+ @opened.ls(expr, follow_references: false)
238
+ end
239
+
240
+ @last_search = search
241
+ end
242
+
243
+ goto_next_search_result(results)
244
+ end
245
+
246
+ def goto_next_search_result(results)
247
+ if results.empty?
248
+ error("No result found.")
249
+ else
250
+ if results != @last_search_result
251
+ # Reset the previous search highlighting.
252
+ @last_search_result.each do |obj| @treeview.highlight(obj, nil) end
253
+
254
+ # Highlight the new results.
255
+ results.each do |obj| @treeview.highlight(obj, "lightpink") end
256
+
257
+ @last_search_result = results
258
+ end
259
+
260
+ @treeview.goto(results.first, follow_references: false)
261
+ end
262
+ end
263
+
253
264
  def create_panels
254
265
  @hpaned = HPaned.new
255
266
 
@@ -270,6 +281,26 @@ module PDFWalker #:nodoc:all
270
281
  @main_context = @statusbar.get_context_id 'Main'
271
282
  @statusbar.push(@main_context, 'No file selected')
272
283
  end
284
+
285
+ def start_profiling
286
+ if @help_menu_profile.active?
287
+ require 'ruby-prof'
288
+ RubyProf.start
289
+ end
290
+
291
+ result = yield
292
+
293
+ if @help_menu_profile.active?
294
+ result = RubyProf.stop
295
+ multiprinter = RubyProf::MultiPrinter.new(result)
296
+
297
+ Dir.mkdir(@config.profile_output_dir) unless Dir.exist?(@config.profile_output_dir)
298
+
299
+ multiprinter.print(path: @config.profile_output_dir, profile: File.basename(filename))
300
+ end
301
+
302
+ result
303
+ end
273
304
  end
274
305
 
275
306
  end
@@ -55,7 +55,7 @@ module PDFWalker
55
55
  @list.set_value(iter, NAMECOL, str)
56
56
  }
57
57
 
58
- @view.signal_connect("row_activated") { |tree, path, _column|
58
+ @view.signal_connect("row_activated") { |_tree, _path, _column|
59
59
  if @view.selection.selected
60
60
  from = @list.get_value(@view.selection.selected, OBJCOL)
61
61
  @parent.treeview.goto(from)
@@ -185,7 +185,7 @@ def dictionaryToRuby(dict, inclevel, internalname, customtype = nil)
185
185
 
186
186
  if val.is_a?(Origami::Reference) and @var_hash[val] and @var_hash[val][0,3] == "obj"
187
187
  oldname = @var_hash[val]
188
- newname = (key.value.to_s.downcase.gsub(/[^[[:alnum:]]]/,'_') + "_" + @var_hash[val][4..-1]).gsub('.','_')
188
+ newname = (key.value.to_s.downcase.gsub(/[^[[:alnum:]]]/,'_') + "_" + @var_hash[val][4..-1]).tr('.', '_')
189
189
 
190
190
  if not @obj_route.include?(oldname)
191
191
  @var_hash[val] = newname
@@ -217,7 +217,7 @@ def dictionaryToHashMap(dict, inclevel, internalname)
217
217
 
218
218
  if val.is_a?(Origami::Reference) and @var_hash[val] and @var_hash[val][0,3] == "obj"
219
219
  oldname = @var_hash[val]
220
- newname = (key.value.to_s.downcase + "_" + @var_hash[val][4..-1]).gsub('.','_')
220
+ newname = (key.value.to_s.downcase + "_" + @var_hash[val][4..-1]).tr('.', '_')
221
221
 
222
222
  if not @obj_route.include?(oldname)
223
223
  @var_hash[val] = newname
@@ -237,7 +237,7 @@ def dictionaryToHashMap(dict, inclevel, internalname)
237
237
  end
238
238
 
239
239
  def streamToRuby(stm, internalname)
240
- dict = stm.dictionary.dup.delete_if{|k,v| k == :Length}
240
+ dict = stm.dictionary.dup.delete_if {|k, _| k == :Length}
241
241
 
242
242
  code = "Stream.new("
243
243
 
data/bin/pdfcop CHANGED
@@ -57,7 +57,7 @@ Options:
57
57
  USAGE
58
58
 
59
59
  def self.parse(args)
60
- options = {colors: true}
60
+ options = {colors: true, password: ''}
61
61
 
62
62
  opts = OptionParser.new do |opts|
63
63
  opts.banner = BANNER
@@ -70,8 +70,12 @@ USAGE
70
70
  options[:config_file] = cf
71
71
  end
72
72
 
73
- opts.on("-p", "--policy POLICY_NAME", "Specify applied policy. Predefined policies: 'none', 'standard', 'strong', 'paranoid'") do |p|
74
- options[:policy] = p
73
+ opts.on("-p", "--policy POLICY_NAME", "Specify applied policy. Predefined policies: 'none', 'standard', 'strong', 'paranoid'") do |policy|
74
+ options[:policy] = policy
75
+ end
76
+
77
+ opts.on("-P", "--password PASSWORD", "Password to use if the document is encrypted") do |passwd|
78
+ options[:password] = passwd
75
79
  end
76
80
 
77
81
  opts.on("-n", "--no-color", "Turn off colorized output") do
@@ -158,7 +162,7 @@ def analyze_xfa_forms(xfa)
158
162
  end
159
163
  end
160
164
 
161
- def analyze_annotation(annot, level = 0)
165
+ def analyze_annotation(annot, _level = 0)
162
166
  check_rights(:allowAnnotations)
163
167
 
164
168
  if annot.is_a?(Origami::Dictionary) and annot.has_key?(:Subtype)
@@ -193,7 +197,7 @@ def analyze_annotation(annot, level = 0)
193
197
  u3dstream = dd[:"3DD"]
194
198
  end
195
199
 
196
- if u3dstream and u3dstream.has_field?(:OnInstantiate)
200
+ if u3dstream and u3dstream.key?(:OnInstantiate)
197
201
  check_rights(:allowJS)
198
202
 
199
203
  if annot.has_key?(:"3DA") # is 3d view instantiated automatically?
@@ -262,11 +266,11 @@ def analyze_action(action, triggered_at_opening, level = 0)
262
266
  check_rights(:allowGoToAction)
263
267
  dest = action[:D].is_a?(Origami::Reference) ? action[:D].solve : action[:D]
264
268
  if dest.is_a?(Origami::Array) and dest.length > 0 and dest.first.is_a?(Origami::Reference)
265
- dest_page = dest.first.solve
266
- if dest_page.is_a?(Origami::Page)
267
- log(text_prefix + " Destination page found.")
268
- analyze_page(dest_page, level + 1)
269
- end
269
+ dest_page = dest.first.solve
270
+ if dest_page.is_a?(Origami::Page)
271
+ log(text_prefix + " Destination page found.")
272
+ analyze_page(dest_page, level + 1)
273
+ end
270
274
  end
271
275
 
272
276
  when :GoToE
@@ -329,7 +333,10 @@ begin
329
333
 
330
334
  @pdf = Origami::PDF.read(TARGET,
331
335
  verbosity: Origami::Parser::VERBOSE_QUIET,
332
- ignore_errors: SECURITY_POLICIES["POLICY_#{@options[:policy].upcase}"]['allowParserErrors']
336
+ ignore_errors: SECURITY_POLICIES["POLICY_#{@options[:policy].upcase}"]['allowParserErrors'],
337
+ decrypt: SECURITY_POLICIES["POLICY_#{@options[:policy].upcase}"]['allowEncryption'],
338
+ prompt_password: lambda { '' },
339
+ password: @options[:password]
333
340
  )
334
341
 
335
342
  log("> Inspecting document structure...", :yellow)
@@ -135,7 +135,13 @@ begin
135
135
 
136
136
  pdf.root_objects.find_all{|obj| obj.is_a?(Stream)}.each do |stream|
137
137
  stream_file = File.join(stream_dir, "stream_#{stream.reference.refno}.dmp")
138
- File.binwrite(stream_file, stream.data)
138
+ begin
139
+ File.binwrite(stream_file, stream.data)
140
+ rescue
141
+ STDERR.puts "Cannot decode stream #{stream.reference}: #{$!.message}"
142
+ next
143
+ end
144
+
139
145
  nstreams += 1
140
146
  end
141
147
 
@@ -161,7 +167,7 @@ begin
161
167
 
162
168
  # Also checking for presence of JavaScript in XML forms.
163
169
  if pdf.form? and pdf.Catalog.AcroForm.has_key?(:XFA)
164
- xfa = pdf.Catalog.AcroForm[:XFA].solve
170
+ xfa = pdf.Catalog.AcroForm.XFA
165
171
 
166
172
  case xfa
167
173
  when Array then
@@ -197,10 +203,11 @@ begin
197
203
  Dir::mkdir(attachments_dir) unless File.directory?(attachments_dir)
198
204
 
199
205
  pdf.each_attachment do |name, attachment|
206
+ name = name.to_utf8.tr("\/\x00", "_")
200
207
  attached_file = File.join(attachments_dir, "attached_#{File.basename(name)}")
201
- spec = attachment.solve
202
- if spec and spec.EF and f = spec.EF.F and f.is_a?(Stream)
203
- File.binwrite(attached_file, f.data)
208
+
209
+ if attachment and attachment.EF and attachment.EF.F.is_a?(Stream)
210
+ File.binwrite(attached_file, attachment.EF.F.data)
204
211
  nattach += 1
205
212
  end
206
213
  end
@@ -257,6 +264,7 @@ begin
257
264
  nimages += 1
258
265
  rescue
259
266
  STDERR.puts "Unable to decode image (stream #{stream.reference.refno}). #{$!.message}"
267
+ STDERR.puts $!.backtrace.join($/)
260
268
  end
261
269
  end
262
270
 
@@ -264,5 +272,6 @@ begin
264
272
  end
265
273
 
266
274
  rescue
275
+ STDERR.puts $!.backtrace.join($/)
267
276
  abort "#{$!.class}: #{$!.message}"
268
277
  end