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.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/bin/gui/config.rb +2 -1
- data/bin/gui/file.rb +118 -240
- data/bin/gui/gtkhex.rb +5 -5
- data/bin/gui/hexview.rb +20 -16
- data/bin/gui/imgview.rb +1 -1
- data/bin/gui/menu.rb +138 -158
- data/bin/gui/properties.rb +46 -48
- data/bin/gui/signing.rb +183 -214
- data/bin/gui/textview.rb +1 -1
- data/bin/gui/treeview.rb +13 -7
- data/bin/gui/walker.rb +102 -71
- data/bin/gui/xrefs.rb +1 -1
- data/bin/pdf2ruby +3 -3
- data/bin/pdfcop +18 -11
- data/bin/pdfextract +14 -5
- data/bin/pdfmetadata +3 -3
- data/bin/shell/console.rb +8 -8
- data/bin/shell/hexdump.rb +4 -4
- data/examples/attachments/nested_document.rb +1 -1
- data/examples/javascript/hello_world.rb +3 -3
- data/lib/origami.rb +0 -1
- data/lib/origami/acroform.rb +3 -3
- data/lib/origami/array.rb +1 -3
- data/lib/origami/boolean.rb +1 -3
- data/lib/origami/catalog.rb +3 -9
- data/lib/origami/destinations.rb +2 -2
- data/lib/origami/dictionary.rb +15 -29
- data/lib/origami/encryption.rb +334 -692
- data/lib/origami/extensions/fdf.rb +3 -2
- data/lib/origami/extensions/ppklite.rb +5 -9
- data/lib/origami/filespec.rb +2 -2
- data/lib/origami/filters.rb +54 -36
- data/lib/origami/filters/ascii.rb +67 -49
- data/lib/origami/filters/ccitt.rb +4 -236
- data/lib/origami/filters/ccitt/tables.rb +267 -0
- data/lib/origami/filters/crypt.rb +1 -1
- data/lib/origami/filters/dct.rb +0 -1
- data/lib/origami/filters/flate.rb +3 -43
- data/lib/origami/filters/lzw.rb +62 -99
- data/lib/origami/filters/predictors.rb +135 -105
- data/lib/origami/filters/runlength.rb +34 -22
- data/lib/origami/graphics.rb +2 -2
- data/lib/origami/graphics/colors.rb +89 -63
- data/lib/origami/graphics/path.rb +14 -14
- data/lib/origami/graphics/patterns.rb +31 -33
- data/lib/origami/graphics/render.rb +0 -1
- data/lib/origami/graphics/state.rb +9 -9
- data/lib/origami/graphics/text.rb +17 -17
- data/lib/origami/graphics/xobject.rb +102 -92
- data/lib/origami/javascript.rb +91 -68
- data/lib/origami/linearization.rb +22 -20
- data/lib/origami/metadata.rb +1 -1
- data/lib/origami/name.rb +1 -3
- data/lib/origami/null.rb +1 -3
- data/lib/origami/numeric.rb +3 -13
- data/lib/origami/object.rb +100 -72
- data/lib/origami/page.rb +24 -28
- data/lib/origami/parser.rb +34 -51
- data/lib/origami/parsers/fdf.rb +2 -2
- data/lib/origami/parsers/pdf.rb +41 -18
- data/lib/origami/parsers/pdf/lazy.rb +83 -46
- data/lib/origami/parsers/pdf/linear.rb +19 -10
- data/lib/origami/parsers/ppklite.rb +1 -1
- data/lib/origami/pdf.rb +150 -206
- data/lib/origami/reference.rb +4 -6
- data/lib/origami/signature.rb +76 -48
- data/lib/origami/stream.rb +69 -63
- data/lib/origami/string.rb +2 -19
- data/lib/origami/trailer.rb +25 -22
- data/lib/origami/version.rb +1 -1
- data/lib/origami/xfa.rb +6 -4
- data/lib/origami/xreftable.rb +29 -29
- data/test/test_annotations.rb +16 -38
- data/test/test_pdf_attachment.rb +1 -1
- data/test/test_pdf_parse.rb +1 -1
- data/test/test_xrefs.rb +2 -2
- metadata +4 -4
- data/lib/origami/export.rb +0 -247
data/bin/gui/textview.rb
CHANGED
data/bin/gui/treeview.rb
CHANGED
@@ -41,7 +41,7 @@ module PDFWalker
|
|
41
41
|
end
|
42
42
|
|
43
43
|
class PDFTree < TreeView
|
44
|
-
include
|
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') { |
|
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') { |
|
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') { |
|
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 |
|
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.
|
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] = {
|
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}
|
data/bin/gui/walker.rb
CHANGED
@@ -74,38 +74,9 @@ module PDFWalker #:nodoc:all
|
|
74
74
|
:type => :body
|
75
75
|
}
|
76
76
|
|
77
|
-
@explorer_history =
|
77
|
+
@explorer_history = []
|
78
78
|
|
79
|
-
|
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 |
|
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
|
-
|
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
|
-
|
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
|
data/bin/gui/xrefs.rb
CHANGED
@@ -55,7 +55,7 @@ module PDFWalker
|
|
55
55
|
@list.set_value(iter, NAMECOL, str)
|
56
56
|
}
|
57
57
|
|
58
|
-
@view.signal_connect("row_activated") { |
|
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)
|
data/bin/pdf2ruby
CHANGED
@@ -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]).
|
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]).
|
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,
|
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 |
|
74
|
-
options[:policy] =
|
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,
|
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.
|
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
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
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)
|
data/bin/pdfextract
CHANGED
@@ -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
|
-
|
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
|
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
|
-
|
202
|
-
if
|
203
|
-
File.binwrite(attached_file,
|
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
|