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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ce1a44a58ce5b5e21a3279fda0f8195b10820eb7
4
- data.tar.gz: d47ccde08ad05ece6b5008fcbbf19de9916709d9
3
+ metadata.gz: 5d1ff84fe2a78a359e39368afcefca18d5538136
4
+ data.tar.gz: 4b471c0175c77ec5189c2579496e552a3c416f30
5
5
  SHA512:
6
- metadata.gz: 064c8a4052fb0a0b471bccb62351be31f00bd6362ed615affb31b0230c0d05b3c35819d7c69576b4c5ccfb203d3eab4187f4aad4b490003a88193c22f7ba377d
7
- data.tar.gz: 8e6ee0137acfd94f3bb569f0dc6614c59e5aba05ef80a815f241a3b0309b5f8d200a7ffbe40955ac805ad4c7143c1f6709d4f014e3e8ad3afbd60fb6ab51526e
6
+ metadata.gz: 4f5d904ad0125e4f78efc68d0d0fd1115d19d9fd3221162c2d95e27084bebc88bd221167ffe10f9396cf7abf67cf66d1983e8be625bd089ba0d5b9093220dc44
7
+ data.tar.gz: 22a28b2c789e1614d757b8c1eb7fd39a10bb0f69ff3fba10aad1103dcbb25aebe2e5c1bab1b986e271e4041add4e653077a1801e2146623bf2488f1fba3c6275
data/README.md CHANGED
@@ -45,6 +45,7 @@ Then import Origami with:
45
45
 
46
46
  ```ruby
47
47
  require 'origami'
48
+ include Origami
48
49
  ```
49
50
 
50
51
  To process a PDF document, you can use the ``PDF.read`` method:
@@ -46,8 +46,9 @@ module PDFWalker
46
46
 
47
47
  def initialize(configfile = DEFAULT_CONFIG_FILE)
48
48
  begin
49
- @conf = YAML.load(File.open(configfile)) or DEFAULT_CONFIG
49
+ @conf = YAML.load(File.open(configfile))
50
50
  rescue
51
+ @conf = DEFAULT_CONFIG
51
52
  ensure
52
53
  @filename = configfile
53
54
  set_missing_values
@@ -37,7 +37,7 @@ module PDFWalker
37
37
 
38
38
  # disable all menus.
39
39
  [
40
- @file_menu_close, @file_menu_saveas, @file_menu_serialize, @file_menu_refresh,
40
+ @file_menu_close, @file_menu_saveas, @file_menu_refresh,
41
41
  @document_menu_search,
42
42
  @document_menu_gotocatalog, @document_menu_gotodocinfo, @document_menu_gotometadata,
43
43
  @document_menu_gotopage, @document_menu_gotofield, @document_menu_gotorev, @document_menu_gotoobj,
@@ -78,174 +78,18 @@ module PDFWalker
78
78
  dialog.destroy
79
79
 
80
80
  begin
81
- if @help_menu_profile.active?
82
- require 'ruby-prof'
83
- RubyProf.start
81
+ document = start_profiling do
82
+ parse_file(filename)
84
83
  end
85
84
 
86
- target = parse_file(filename)
87
-
88
- if @help_menu_profile.active?
89
- result = RubyProf.stop
90
- multiprinter = RubyProf::MultiPrinter.new(result)
91
-
92
- Dir.mkdir(@config.profile_output_dir) unless Dir.exists?(@config.profile_output_dir)
93
-
94
- multiprinter.print(path: @config.profile_output_dir, profile: File.basename(filename))
95
- end
96
-
97
- if target
98
- close if @opened
99
- @opened = target
100
- @filename = filename
101
-
102
- @config.last_opened_file(filename)
103
- @config.save
104
- update_recent_menu
105
-
106
- @last_search_result = []
107
- @last_search =
108
- {
109
- :expr => "",
110
- :regexp => false,
111
- :type => :body
112
- }
113
-
114
- self.reload
115
-
116
- # Enable basic file menus.
117
- [
118
- @file_menu_close, @file_menu_refresh,
119
- ].each do |menu|
120
- menu.sensitive = true
121
- end
122
-
123
- @explorer_history.clear
124
-
125
- @statusbar.push(@main_context, "Viewing #{filename}")
126
-
127
- if @opened.is_a?(Origami::PDF)
128
-
129
- # Enable save and document menu.
130
- [
131
- @file_menu_saveas, @file_menu_serialize,
132
- @document_menu_search,
133
- @document_menu_gotocatalog, @document_menu_gotopage, @document_menu_gotorev, @document_menu_gotoobj,
134
- @document_menu_properties, @document_menu_sign, @document_menu_ur
135
- ].each do |menu|
136
- menu.sensitive = true
137
- end
138
-
139
- @document_menu_gotodocinfo.sensitive = true if @opened.document_info?
140
- @document_menu_gotometadata.sensitive = true if @opened.metadata?
141
- @document_menu_gotofield.sensitive = true if @opened.form?
142
-
143
- page_menu = Menu.new
144
- @document_menu_gotopage.remove_submenu
145
- @opened.each_page.with_index(1) do |page, index|
146
- page_menu.append(item = MenuItem.new(index.to_s).show)
147
- item.signal_connect("activate") do @treeview.goto(page) end
148
- end
149
- @document_menu_gotopage.set_submenu(page_menu)
150
-
151
- field_menu = Menu.new
152
- @document_menu_gotofield.remove_submenu
153
- @opened.each_field do |field|
154
- field_name = field.T || "<unnamed field>"
155
- field_menu.append(item = MenuItem.new(field_name).show)
156
- item.signal_connect("activate") do @treeview.goto(field) end
157
- end
158
- @document_menu_gotofield.set_submenu(field_menu)
159
-
160
- rev_menu = Menu.new
161
- @document_menu_gotorev.remove_submenu
162
- rev_index = 1
163
- @opened.revisions.each do |rev|
164
- rev_menu.append(item = MenuItem.new(rev_index.to_s).show)
165
- item.signal_connect("activate") do @treeview.goto(rev) end
166
- rev_index = rev_index + 1
167
- end
168
- @document_menu_gotorev.set_submenu(rev_menu)
169
-
170
- goto_catalog
171
- end
172
- end
85
+ set_active_document(filename, document)
173
86
 
174
87
  rescue
175
88
  error("Error while parsing file.\n#{$!} (#{$!.class})\n" + $!.backtrace.join("\n"))
89
+ ensure
90
+ close_progressbar
91
+ self.activate_focus
176
92
  end
177
-
178
- close_progressbar
179
- self.activate_focus
180
- end
181
-
182
- def deserialize
183
- dialog = Gtk::FileChooserDialog.new("Open dump file",
184
- self,
185
- FileChooser::ACTION_OPEN,
186
- nil,
187
- [Stock::CANCEL, Dialog::RESPONSE_CANCEL],
188
- [Stock::OPEN, Dialog::RESPONSE_ACCEPT]
189
- )
190
-
191
- dialog.current_folder = File.join(Dir.pwd, "dumps")
192
- dialog.filter = FileFilter.new.add_pattern("*.gz")
193
-
194
- if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
195
- close if @opened
196
- filename = dialog.filename
197
-
198
- begin
199
- @opened = Origami::PDF.deserialize(filename)
200
- self.reload
201
-
202
- [
203
- @file_menu_close, @file_menu_saveas, @file_menu_serialize, @file_menu_refresh,
204
- @document_menu_search,
205
- @document_menu_gotocatalog, @document_menu_gotopage, @document_menu_gotorev, @document_menu_gotoobj,
206
- @document_menu_properties, @document_menu_sign, @document_menu_ur
207
- ].each do |menu|
208
- menu.sensitive = true
209
- end
210
-
211
- @document_menu_gotodocinfo.sensitive = true if @opened.document_info?
212
- @document_menu_gotometadata.sensitive = true if @opened.metadata?
213
- @document_menu_gotofield.sensitive = true if @opened.form?
214
-
215
- @explorer_history.clear
216
-
217
- @statusbar.push(@main_context, "Viewing dump of #{filename}")
218
- rescue
219
- error("This file cannot be loaded.\n#{$!} (#{$!.class})")
220
- end
221
- end
222
-
223
- dialog.destroy
224
- end
225
-
226
- def serialize
227
- dialog = Gtk::FileChooserDialog.new("Save dump file",
228
- self,
229
- Gtk::FileChooser::ACTION_SAVE,
230
- nil,
231
- [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
232
- [Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT]
233
- )
234
-
235
- dialog.do_overwrite_confirmation = true
236
- dialog.current_folder = File.join(Dir.pwd, "dumps")
237
- dialog.current_name = "#{File.basename(@filename)}.dmp.gz"
238
- dialog.filter = FileFilter.new.add_pattern("*.gz")
239
-
240
- if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
241
- begin
242
- @opened.serialize(dialog.filename)
243
- rescue
244
- error("Error: #{$!.message}")
245
- end
246
- end
247
-
248
- dialog.destroy
249
93
  end
250
94
 
251
95
  def save_data(caption, data, filename = "")
@@ -297,97 +141,95 @@ module PDFWalker
297
141
  dialog.destroy
298
142
  end
299
143
 
300
- def save_dot
301
- dialog = Gtk::FileChooserDialog.new("Save dot file",
302
- self,
303
- Gtk::FileChooser::ACTION_SAVE,
304
- nil,
305
- [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
306
- [Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT]
307
- )
144
+ private
308
145
 
309
- dialog.filter = FileFilter.new.add_pattern("*.dot")
146
+ def set_active_document(filename, document)
147
+ close if @opened
148
+ @opened = document
149
+ @filename = filename
150
+
151
+ @config.last_opened_file(filename)
152
+ @config.save
153
+ update_recent_menu
154
+
155
+ @last_search_result = []
156
+ @last_search =
157
+ {
158
+ :expr => "",
159
+ :regexp => false,
160
+ :type => :body
161
+ }
310
162
 
311
- folder = File.dirname(@filename)
312
- dialog.set_current_folder(folder)
163
+ self.reload
313
164
 
314
- if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
315
- begin
316
- @opened.export_to_graph(dialog.filename)
317
- rescue
318
- error("Error: #{$!.message}")
319
- end
165
+ # Enable basic file menus.
166
+ [
167
+ @file_menu_close, @file_menu_refresh,
168
+ ].each do |menu|
169
+ menu.sensitive = true
320
170
  end
321
171
 
322
- dialog.destroy
172
+ @explorer_history.clear
173
+
174
+ @statusbar.push(@main_context, "Viewing #{filename}")
175
+
176
+ setup_pdf_interface if @opened.is_a?(Origami::PDF)
323
177
  end
324
178
 
325
- def save_graphml
326
- dialog = Gtk::FileChooserDialog.new("Save GraphML file",
327
- self,
328
- Gtk::FileChooser::ACTION_SAVE,
329
- nil,
330
- [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
331
- [Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT]
332
- )
179
+ def setup_pdf_interface
180
+ # Enable save and document menu.
181
+ [
182
+ @file_menu_saveas,
183
+ @document_menu_search,
184
+ @document_menu_gotocatalog, @document_menu_gotopage, @document_menu_gotorev, @document_menu_gotoobj,
185
+ @document_menu_properties, @document_menu_sign, @document_menu_ur
186
+ ].each do |menu|
187
+ menu.sensitive = true
188
+ end
333
189
 
334
- dialog.filter = FileFilter.new.add_pattern("*.graphml")
190
+ @document_menu_gotodocinfo.sensitive = true if @opened.document_info?
191
+ @document_menu_gotometadata.sensitive = true if @opened.metadata?
192
+ @document_menu_gotofield.sensitive = true if @opened.form?
335
193
 
336
- folder = File.dirname(@filename)
337
- dialog.set_current_folder(folder)
194
+ setup_page_menu
195
+ setup_field_menu
196
+ setup_revision_menu
338
197
 
339
- if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
340
- begin
341
- @opened.export_to_graphml(dialog.filename)
342
- rescue
343
- error("Error: #{$!.message}")
344
- end
198
+ goto_catalog
199
+ end
200
+
201
+ def setup_page_menu
202
+ page_menu = Menu.new
203
+ @document_menu_gotopage.remove_submenu
204
+ @opened.each_page.with_index(1) do |page, index|
205
+ page_menu.append(item = MenuItem.new(index.to_s).show)
206
+ item.signal_connect("activate") { @treeview.goto(page) }
345
207
  end
208
+ @document_menu_gotopage.set_submenu(page_menu)
209
+ end
346
210
 
347
- dialog.destroy
211
+ def setup_field_menu
212
+ field_menu = Menu.new
213
+ @document_menu_gotofield.remove_submenu
214
+ @opened.each_field do |field|
215
+ field_name = field.T || "<unnamed field>"
216
+ field_menu.append(item = MenuItem.new(field_name).show)
217
+ item.signal_connect("activate") { @treeview.goto(field) }
218
+ end
219
+ @document_menu_gotofield.set_submenu(field_menu)
348
220
  end
349
221
 
350
- private
222
+ def setup_revision_menu
223
+ rev_menu = Menu.new
224
+ @document_menu_gotorev.remove_submenu
225
+ @opened.revisions.each.with_index(1) do |rev, index|
226
+ rev_menu.append(item = MenuItem.new(index.to_s).show)
227
+ item.signal_connect("activate") { @treeview.goto(rev) }
228
+ end
229
+ @document_menu_gotorev.set_submenu(rev_menu)
230
+ end
351
231
 
352
232
  def parse_file(path)
353
- update_bar = lambda { |obj|
354
- @progressbar.pulse if @progressbar
355
- Gtk.main_iteration while Gtk.events_pending?
356
- }
357
-
358
- prompt_passwd = lambda {
359
- passwd = ""
360
-
361
- dialog = Gtk::Dialog.new(
362
- "This document is encrypted",
363
- nil,
364
- Gtk::Dialog::MODAL,
365
- [ Gtk::Stock::OK, Gtk::Dialog::RESPONSE_OK ],
366
- [ Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL ]
367
- )
368
-
369
- dialog.set_default_response(Gtk::Dialog::RESPONSE_OK)
370
-
371
- label = Gtk::Label.new("Please enter password:")
372
- entry = Gtk::Entry.new
373
- entry.signal_connect('activate') {
374
- dialog.response(Gtk::Dialog::RESPONSE_OK)
375
- }
376
-
377
- dialog.vbox.add(label)
378
- dialog.vbox.add(entry)
379
- dialog.show_all
380
-
381
- dialog.run do |response|
382
- if response == Gtk::Dialog::RESPONSE_OK
383
- passwd = entry.text
384
- end
385
- end
386
-
387
- dialog.destroy
388
- return passwd
389
- }
390
-
391
233
  #
392
234
  # Try to detect the file type of the document.
393
235
  # Fallback to PDF if none is found.
@@ -403,12 +245,48 @@ module PDFWalker
403
245
  file_type.read(path,
404
246
  verbosity: Origami::Parser::VERBOSE_TRACE,
405
247
  ignore_errors: false,
406
- callback: update_bar,
407
- prompt_password: prompt_passwd,
248
+ callback: method(:update_progressbar),
249
+ prompt_password: method(:prompt_password),
408
250
  force: force_mode
409
251
  )
410
252
  end
411
253
 
254
+ def update_progressbar(_obj)
255
+ @progressbar.pulse if @progressbar
256
+ Gtk.main_iteration while Gtk.events_pending?
257
+ end
258
+
259
+ def prompt_password
260
+ passwd = ""
261
+
262
+ dialog = Gtk::Dialog.new(
263
+ "This document is encrypted",
264
+ nil,
265
+ Gtk::Dialog::MODAL,
266
+ [ Gtk::Stock::OK, Gtk::Dialog::RESPONSE_OK ],
267
+ [ Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL ]
268
+ )
269
+
270
+ dialog.set_default_response(Gtk::Dialog::RESPONSE_OK)
271
+
272
+ label = Gtk::Label.new("Please enter password:")
273
+ entry = Gtk::Entry.new
274
+ entry.signal_connect('activate') {
275
+ dialog.response(Gtk::Dialog::RESPONSE_OK)
276
+ }
277
+
278
+ dialog.vbox.add(label)
279
+ dialog.vbox.add(entry)
280
+ dialog.show_all
281
+
282
+ dialog.run do |response|
283
+ passwd = entry.text if response == Gtk::Dialog::RESPONSE_OK
284
+ end
285
+
286
+ dialog.destroy
287
+ passwd
288
+ end
289
+
412
290
  def detect_file_type(path)
413
291
  supported_types = [ Origami::PDF, Origami::FDF, Origami::PPKLite ]
414
292
 
@@ -169,7 +169,7 @@ module Gtk
169
169
  @xdisp_gc.set_exposures(true)
170
170
  end
171
171
 
172
- @xdisp.signal_connect 'expose_event' do |xdisp, event|
172
+ @xdisp.signal_connect 'expose_event' do |_xdisp, event|
173
173
  imin = (event.area.y / @char_height).to_i
174
174
  imax = ((event.area.y + event.area.height) / @char_height).to_i
175
175
  imax += 1 if (event.area.y + event.area.height).to_i % @char_height != 0
@@ -179,7 +179,7 @@ module Gtk
179
179
  render_hex_lines(imin, imax)
180
180
  end
181
181
 
182
- @xdisp.signal_connect 'scroll_event' do |xdisp, event|
182
+ @xdisp.signal_connect 'scroll_event' do |_xdisp, event|
183
183
  @scrollbar.event(event)
184
184
  end
185
185
 
@@ -245,7 +245,7 @@ module Gtk
245
245
  @adisp_gc.set_exposures(true)
246
246
  end
247
247
 
248
- @adisp.signal_connect 'expose_event' do |adisp, event|
248
+ @adisp.signal_connect 'expose_event' do |_adisp, event|
249
249
  imin = (event.area.y / @char_height).to_i
250
250
  imax = ((event.area.y + event.area.height) / @char_height).to_i
251
251
  imax += 1 if (event.area.y + event.area.height).to_i % @char_height != 0
@@ -254,7 +254,7 @@ module Gtk
254
254
  render_ascii_lines(imin, imax)
255
255
  end
256
256
 
257
- @adisp.signal_connect 'scroll_event' do |adisp, event|
257
+ @adisp.signal_connect 'scroll_event' do |_adisp, event|
258
258
  @scrollbar.event(event)
259
259
  end
260
260
 
@@ -910,7 +910,7 @@ module Gtk
910
910
  @olayout = @offsets.create_pango_layout('')
911
911
 
912
912
  @offsets.events = Gdk::Event::EXPOSURE_MASK
913
- @offsets.signal_connect 'expose_event' do |offsets, event|
913
+ @offsets.signal_connect 'expose_event' do |_offsets, event|
914
914
  imin = (event.area.y / @char_height).to_i
915
915
  imax = ((event.area.y + event.area.height) / @char_height).to_i
916
916
  imax += 1 if (event.area.y + event.area.height).to_i % @char_height != 0