origami 1.0.2
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.
- data/COPYING.LESSER +165 -0
- data/README +77 -0
- data/VERSION +1 -0
- data/bin/config/pdfcop.conf.yml +237 -0
- data/bin/gui/about.rb +46 -0
- data/bin/gui/config.rb +132 -0
- data/bin/gui/file.rb +385 -0
- data/bin/gui/hexdump.rb +74 -0
- data/bin/gui/hexview.rb +91 -0
- data/bin/gui/imgview.rb +72 -0
- data/bin/gui/menu.rb +392 -0
- data/bin/gui/properties.rb +132 -0
- data/bin/gui/signing.rb +635 -0
- data/bin/gui/textview.rb +107 -0
- data/bin/gui/treeview.rb +409 -0
- data/bin/gui/walker.rb +282 -0
- data/bin/gui/xrefs.rb +79 -0
- data/bin/pdf2graph +121 -0
- data/bin/pdf2ruby +353 -0
- data/bin/pdfcocoon +104 -0
- data/bin/pdfcop +455 -0
- data/bin/pdfdecompress +104 -0
- data/bin/pdfdecrypt +95 -0
- data/bin/pdfencrypt +112 -0
- data/bin/pdfextract +221 -0
- data/bin/pdfmetadata +123 -0
- data/bin/pdfsh +13 -0
- data/bin/pdfwalker +7 -0
- data/bin/shell/.irbrc +104 -0
- data/bin/shell/console.rb +136 -0
- data/bin/shell/hexdump.rb +83 -0
- data/origami.rb +36 -0
- data/origami/3d.rb +239 -0
- data/origami/acroform.rb +321 -0
- data/origami/actions.rb +299 -0
- data/origami/adobe/fdf.rb +259 -0
- data/origami/adobe/ppklite.rb +489 -0
- data/origami/annotations.rb +775 -0
- data/origami/array.rb +187 -0
- data/origami/boolean.rb +101 -0
- data/origami/catalog.rb +486 -0
- data/origami/destinations.rb +213 -0
- data/origami/dictionary.rb +188 -0
- data/origami/docmdp.rb +96 -0
- data/origami/encryption.rb +1293 -0
- data/origami/export.rb +283 -0
- data/origami/file.rb +222 -0
- data/origami/filters.rb +250 -0
- data/origami/filters/ascii.rb +189 -0
- data/origami/filters/ccitt.rb +515 -0
- data/origami/filters/crypt.rb +47 -0
- data/origami/filters/dct.rb +61 -0
- data/origami/filters/flate.rb +112 -0
- data/origami/filters/jbig2.rb +63 -0
- data/origami/filters/jpx.rb +53 -0
- data/origami/filters/lzw.rb +195 -0
- data/origami/filters/predictors.rb +276 -0
- data/origami/filters/runlength.rb +117 -0
- data/origami/font.rb +209 -0
- data/origami/functions.rb +93 -0
- data/origami/graphics.rb +33 -0
- data/origami/graphics/colors.rb +191 -0
- data/origami/graphics/instruction.rb +126 -0
- data/origami/graphics/path.rb +154 -0
- data/origami/graphics/patterns.rb +180 -0
- data/origami/graphics/state.rb +164 -0
- data/origami/graphics/text.rb +224 -0
- data/origami/graphics/xobject.rb +493 -0
- data/origami/header.rb +90 -0
- data/origami/linearization.rb +318 -0
- data/origami/metadata.rb +114 -0
- data/origami/name.rb +170 -0
- data/origami/null.rb +75 -0
- data/origami/numeric.rb +188 -0
- data/origami/obfuscation.rb +233 -0
- data/origami/object.rb +527 -0
- data/origami/outline.rb +59 -0
- data/origami/page.rb +559 -0
- data/origami/parser.rb +268 -0
- data/origami/parsers/fdf.rb +45 -0
- data/origami/parsers/pdf.rb +27 -0
- data/origami/parsers/pdf/linear.rb +113 -0
- data/origami/parsers/ppklite.rb +86 -0
- data/origami/pdf.rb +1144 -0
- data/origami/reference.rb +113 -0
- data/origami/signature.rb +474 -0
- data/origami/stream.rb +575 -0
- data/origami/string.rb +416 -0
- data/origami/trailer.rb +173 -0
- data/origami/webcapture.rb +87 -0
- data/origami/xfa.rb +3027 -0
- data/origami/xreftable.rb +447 -0
- data/templates/patterns.rb +66 -0
- data/templates/widgets.rb +173 -0
- data/templates/xdp.rb +92 -0
- data/tests/dataset/test.dummycrt +28 -0
- data/tests/dataset/test.dummykey +27 -0
- data/tests/tc_actions.rb +32 -0
- data/tests/tc_annotations.rb +85 -0
- data/tests/tc_pages.rb +37 -0
- data/tests/tc_pdfattach.rb +24 -0
- data/tests/tc_pdfencrypt.rb +110 -0
- data/tests/tc_pdfnew.rb +32 -0
- data/tests/tc_pdfparse.rb +98 -0
- data/tests/tc_pdfsig.rb +37 -0
- data/tests/tc_streams.rb +129 -0
- data/tests/ts_pdf.rb +45 -0
- metadata +193 -0
data/origami/parser.rb
ADDED
@@ -0,0 +1,268 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
= File
|
4
|
+
parser.rb
|
5
|
+
|
6
|
+
= Info
|
7
|
+
Origami is free software: you can redistribute it and/or modify
|
8
|
+
it under the terms of the GNU Lesser General Public License as published by
|
9
|
+
the Free Software Foundation, either version 3 of the License, or
|
10
|
+
(at your option) any later version.
|
11
|
+
|
12
|
+
Origami is distributed in the hope that it will be useful,
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
GNU Lesser General Public License for more details.
|
16
|
+
|
17
|
+
You should have received a copy of the GNU Lesser General Public License
|
18
|
+
along with Origami. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
=end
|
21
|
+
|
22
|
+
require 'strscan'
|
23
|
+
|
24
|
+
module Origami
|
25
|
+
|
26
|
+
if RUBY_PLATFORM =~ /win32/ or RUBY_PLATFORM =~ /mingw32/
|
27
|
+
require "Win32API"
|
28
|
+
|
29
|
+
getStdHandle = Win32API.new("kernel32", "GetStdHandle", ['L'], 'L')
|
30
|
+
@@setConsoleTextAttribute = Win32API.new("kernel32", "SetConsoleTextAttribute", ['L', 'N'], 'I')
|
31
|
+
|
32
|
+
@@hOut = getStdHandle.call(-11)
|
33
|
+
end
|
34
|
+
|
35
|
+
module Colors #:nodoc;
|
36
|
+
if RUBY_PLATFORM =~ /win32/ or RUBY_PLATFORM =~ /mingw32/
|
37
|
+
BLACK = 0
|
38
|
+
BLUE = 1
|
39
|
+
GREEN = 2
|
40
|
+
CYAN = 3
|
41
|
+
RED = 4
|
42
|
+
MAGENTA = 5
|
43
|
+
YELLOW = 6
|
44
|
+
GREY = 7
|
45
|
+
WHITE = 8
|
46
|
+
else
|
47
|
+
GREY = '0;0'
|
48
|
+
BLACK = '0;30'
|
49
|
+
RED = '0;31'
|
50
|
+
GREEN = '0;32'
|
51
|
+
YELLOW = '0;33'
|
52
|
+
BLUE = '0;34'
|
53
|
+
MAGENTA = '0;35'
|
54
|
+
CYAN = '0;36'
|
55
|
+
WHITE = '0;37'
|
56
|
+
BRIGHT_GREY = '1;30'
|
57
|
+
BRIGHT_RED = '1;31'
|
58
|
+
BRIGHT_GREEN = '1;32'
|
59
|
+
BRIGHT_YELLOW = '1;33'
|
60
|
+
BRIGHT_BLUE = '1;34'
|
61
|
+
BRIGHT_MAGENTA = '1;35'
|
62
|
+
BRIGHT_CYAN = '1;36'
|
63
|
+
BRIGHT_WHITE = '1;37'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def set_fg_color(color, bright = false, fd = STDOUT) #:nodoc:
|
68
|
+
if RUBY_PLATFORM =~ /win32/ or RUBY_PLATFORM =~ /mingw32/
|
69
|
+
if bright then color |= Colors::WHITE end
|
70
|
+
@@setConsoleTextAttribute.call(@@hOut, color)
|
71
|
+
yield
|
72
|
+
@@setConsoleTextAttribute.call(@@hOut, Colors::GREY)
|
73
|
+
else
|
74
|
+
col, nocol = [color, Colors::GREY].map! { |key| "\033[#{key}m" }
|
75
|
+
fd << col
|
76
|
+
yield
|
77
|
+
fd << nocol
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
unless RUBY_PLATFORM =~ /win32/ or RUBY_PLATFORM =~ /mingw32/
|
82
|
+
def colorize(text, color, bright = false)
|
83
|
+
col, nocol = [color, Colors::GREY].map! { |key| "\033[#{key}m" }
|
84
|
+
"#{col}#{text}#{nocol}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def colorprint(text, color, bright = false, fd = STDOUT) #:nodoc:
|
89
|
+
set_fg_color(color, bright, fd) {
|
90
|
+
fd << text
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
EOL = "\r\n" #:nodoc:
|
95
|
+
DEFINED_TOKENS = "[<\\[(%\\/)\\]>]" #:nodoc:
|
96
|
+
WHITESPACES = "([ \\f\\t\\r\\n\\0]|%[^\\n]*\\n)*" #:nodoc:
|
97
|
+
WHITECHARS = "[ \\f\\t\\r\\n\\0]*" #:nodoc:
|
98
|
+
WHITECHARS_NORET = "[ \\f\\t\\0]*" #:nodoc:
|
99
|
+
|
100
|
+
REGEXP_WHITESPACES = Regexp.new(WHITESPACES) #:nodoc:
|
101
|
+
|
102
|
+
class Parser #:nodoc:
|
103
|
+
|
104
|
+
class ParsingError < Exception #:nodoc:
|
105
|
+
end
|
106
|
+
|
107
|
+
#
|
108
|
+
# Do not output debug information.
|
109
|
+
#
|
110
|
+
VERBOSE_QUIET = 0
|
111
|
+
|
112
|
+
#
|
113
|
+
# Output some useful information.
|
114
|
+
#
|
115
|
+
VERBOSE_INFO = 1
|
116
|
+
|
117
|
+
#
|
118
|
+
# Output debug information.
|
119
|
+
#
|
120
|
+
VERBOSE_DEBUG = 2
|
121
|
+
|
122
|
+
#
|
123
|
+
# Output every objects read
|
124
|
+
#
|
125
|
+
VERBOSE_INSANE = 3
|
126
|
+
|
127
|
+
attr_accessor :options
|
128
|
+
|
129
|
+
def initialize(options = {}) #:nodoc:
|
130
|
+
|
131
|
+
#Default options values
|
132
|
+
@options =
|
133
|
+
{
|
134
|
+
:verbosity => VERBOSE_INFO, # Verbose level.
|
135
|
+
:ignore_errors => true, # Try to keep on parsing when errors occur.
|
136
|
+
:callback => Proc.new {}, # Callback procedure whenever a structure is read.
|
137
|
+
:password => '', # Default password being tried when opening a protected document.
|
138
|
+
:prompt_password => Proc.new { print "Password: "; gets.chomp }, # Callback procedure to prompt password when document is encrypted.
|
139
|
+
:force => false # Force PDF header detection
|
140
|
+
}
|
141
|
+
|
142
|
+
@options.update(options)
|
143
|
+
end
|
144
|
+
|
145
|
+
def parse(stream)
|
146
|
+
data =
|
147
|
+
if stream.respond_to? :read
|
148
|
+
if ''.respond_to? :force_encoding
|
149
|
+
StringScanner.new(stream.read.force_encoding('binary')) # 1.9 compat
|
150
|
+
else
|
151
|
+
StringScanner.new(stream.read)
|
152
|
+
end
|
153
|
+
elsif stream.is_a? ::String
|
154
|
+
if ''.respond_to? :force_encoding
|
155
|
+
StringScanner.new(File.open(stream, "r", :encoding => 'binary').binmode.read)
|
156
|
+
else
|
157
|
+
StringScanner.new(File.open(stream, "r").binmode.read)
|
158
|
+
end
|
159
|
+
elsif stream.is_a? StringScanner
|
160
|
+
stream
|
161
|
+
else
|
162
|
+
raise TypeError
|
163
|
+
end
|
164
|
+
|
165
|
+
@data = data
|
166
|
+
@data.pos = 0
|
167
|
+
end
|
168
|
+
|
169
|
+
def parse_objects(file) #:nodoc:
|
170
|
+
begin
|
171
|
+
loop do
|
172
|
+
obj = Object.parse(@data)
|
173
|
+
return if obj.nil?
|
174
|
+
|
175
|
+
trace "Read #{obj.type} object#{if obj.type != obj.real_type then " (" + obj.real_type.to_s.split('::').last + ")" end}, #{obj.reference}"
|
176
|
+
|
177
|
+
file << obj
|
178
|
+
|
179
|
+
@options[:callback].call(obj)
|
180
|
+
end
|
181
|
+
|
182
|
+
rescue UnterminatedObjectError => e
|
183
|
+
error e.message
|
184
|
+
file << e.obj
|
185
|
+
|
186
|
+
@options[:callback].call(e.obj)
|
187
|
+
|
188
|
+
Object.skip_until_next_obj(@data)
|
189
|
+
retry
|
190
|
+
|
191
|
+
rescue Exception => e
|
192
|
+
error "Breaking on: #{(@data.peek(10) + "...").inspect} at offset 0x#{@data.pos.to_s(16)}"
|
193
|
+
error "Last exception: [#{e.class}] #{e.message}"
|
194
|
+
debug "-> Stopped reading body : #{file.revisions.last.body.size} indirect objects have been parsed" if file.is_a?(PDF)
|
195
|
+
abort("Manually fix the file or set :ignore_errors parameter.") if not @options[:ignore_errors]
|
196
|
+
|
197
|
+
debug 'Skipping this indirect object.'
|
198
|
+
raise(e) if not Object.skip_until_next_obj(@data)
|
199
|
+
|
200
|
+
retry
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def parse_xreftable(file) #:nodoc:
|
205
|
+
begin
|
206
|
+
info "...Parsing xref table..."
|
207
|
+
file.revisions.last.xreftable = XRef::Section.parse(@data)
|
208
|
+
@options[:callback].call(file.revisions.last.xreftable)
|
209
|
+
rescue Exception => e
|
210
|
+
debug "Exception caught while parsing xref table : " + e.message
|
211
|
+
warn "Unable to parse xref table! Xrefs might be stored into an XRef stream."
|
212
|
+
|
213
|
+
@data.pos -= 'trailer'.length unless @data.skip_until(/trailer/).nil?
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def parse_trailer(file) #:nodoc:
|
218
|
+
begin
|
219
|
+
info "...Parsing trailer..."
|
220
|
+
trailer = Trailer.parse(@data)
|
221
|
+
|
222
|
+
if file.is_a?(PDF)
|
223
|
+
xrefstm = file.get_object_by_offset(trailer.startxref) ||
|
224
|
+
(file.get_object_by_offset(trailer.XRefStm) if trailer.has_field? :XRefStm)
|
225
|
+
end
|
226
|
+
|
227
|
+
if not xrefstm.nil?
|
228
|
+
debug "Found a XRefStream for this revision at #{xrefstm.reference}"
|
229
|
+
file.revisions.last.xrefstm = xrefstm
|
230
|
+
end
|
231
|
+
|
232
|
+
file.revisions.last.trailer = trailer
|
233
|
+
@options[:callback].call(file.revisions.last.trailer)
|
234
|
+
|
235
|
+
rescue Exception => e
|
236
|
+
debug "Exception caught while parsing trailer : " + e.message
|
237
|
+
warn "Unable to parse trailer!"
|
238
|
+
|
239
|
+
abort("Manually fix the file or set :ignore_errors parameter.") if not @options[:ignore_errors]
|
240
|
+
|
241
|
+
raise
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
private
|
246
|
+
|
247
|
+
def error(str = "") #:nodoc:
|
248
|
+
colorprint("[error] #{str}\n", Colors::RED, false, STDERR)
|
249
|
+
end
|
250
|
+
|
251
|
+
def warn(str = "") #:nodoc:
|
252
|
+
colorprint("[info ] Warning: #{str}\n", Colors::YELLOW, false, STDERR) if @options[:verbosity] >= VERBOSE_INFO
|
253
|
+
end
|
254
|
+
|
255
|
+
def info(str = "") #:nodoc:
|
256
|
+
(colorprint("[info ] ", Colors::GREEN, false, STDERR); STDERR << "#{str}\n") if @options[:verbosity] >= VERBOSE_INFO
|
257
|
+
end
|
258
|
+
|
259
|
+
def debug(str = "") #:nodoc:
|
260
|
+
(colorprint("[debug] ", Colors::MAGENTA, false, STDERR); STDERR << "#{str}\n") if @options[:verbosity] >= VERBOSE_DEBUG
|
261
|
+
end
|
262
|
+
|
263
|
+
def trace(str = "") #:nodoc:
|
264
|
+
(colorprint("[trace] ", Colors::CYAN, false, STDERR); STDERR << "#{str}\n") if @options[:verbosity] >= VERBOSE_INSANE
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
= File
|
4
|
+
parsers/fdf.rb
|
5
|
+
|
6
|
+
= Info
|
7
|
+
Origami is free software: you can redistribute it and/or modify
|
8
|
+
it under the terms of the GNU Lesser General Public License as published by
|
9
|
+
the Free Software Foundation, either version 3 of the License, or
|
10
|
+
(at your option) any later version.
|
11
|
+
|
12
|
+
Origami is distributed in the hope that it will be useful,
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
GNU Lesser General Public License for more details.
|
16
|
+
|
17
|
+
You should have received a copy of the GNU Lesser General Public License
|
18
|
+
along with Origami. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
=end
|
21
|
+
|
22
|
+
require 'origami/parser'
|
23
|
+
require 'origami/adobe/fdf'
|
24
|
+
|
25
|
+
module Origami
|
26
|
+
|
27
|
+
class Adobe::FDF
|
28
|
+
class Parser < Origami::Parser
|
29
|
+
def parse(stream) #:nodoc:
|
30
|
+
super
|
31
|
+
|
32
|
+
fdf = Adobe::FDF.new
|
33
|
+
fdf.header = Adobe::FDF::Header.parse(stream)
|
34
|
+
@options[:callback].call(fdf.header)
|
35
|
+
|
36
|
+
parse_objects(fdf)
|
37
|
+
parse_xreftable(fdf)
|
38
|
+
parse_trailer(fdf)
|
39
|
+
|
40
|
+
addrbk
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
= File
|
4
|
+
parsers/pdf.rb
|
5
|
+
|
6
|
+
= Info
|
7
|
+
This file is part of Origami, PDF manipulation framework for Ruby
|
8
|
+
Copyright (C) 2010 Guillaume Delugré <guillaume@security-labs.org>
|
9
|
+
All right reserved.
|
10
|
+
|
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
|
+
require 'origami/parsers/pdf/linear'
|
27
|
+
|
@@ -0,0 +1,113 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
= File
|
4
|
+
parsers/linear.rb
|
5
|
+
|
6
|
+
= Info
|
7
|
+
Origami is free software: you can redistribute it and/or modify
|
8
|
+
it under the terms of the GNU Lesser General Public License as published by
|
9
|
+
the Free Software Foundation, either version 3 of the License, or
|
10
|
+
(at your option) any later version.
|
11
|
+
|
12
|
+
Origami is distributed in the hope that it will be useful,
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
GNU Lesser General Public License for more details.
|
16
|
+
|
17
|
+
You should have received a copy of the GNU Lesser General Public License
|
18
|
+
along with Origami. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
=end
|
21
|
+
|
22
|
+
|
23
|
+
require 'origami/parser'
|
24
|
+
require 'origami/pdf'
|
25
|
+
|
26
|
+
module Origami
|
27
|
+
|
28
|
+
class PDF
|
29
|
+
|
30
|
+
#
|
31
|
+
# Create a new PDF linear Parser.
|
32
|
+
#
|
33
|
+
class LinearParser < Origami::Parser
|
34
|
+
def parse(stream)
|
35
|
+
super
|
36
|
+
|
37
|
+
if @options[:force] == true
|
38
|
+
@data.skip_until(/%PDF-/).nil?
|
39
|
+
@data.pos = @data.pos - 5
|
40
|
+
end
|
41
|
+
|
42
|
+
pdf = PDF.new(false)
|
43
|
+
|
44
|
+
info "...Reading header..."
|
45
|
+
begin
|
46
|
+
pdf.header = PDF::Header.parse(@data)
|
47
|
+
@options[:callback].call(pdf.header)
|
48
|
+
rescue InvalidHeaderError => e
|
49
|
+
if @options[:ignore_errors] == true
|
50
|
+
warn "PDF header is invalid, ignoring..."
|
51
|
+
else
|
52
|
+
raise e
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# Parse each revision
|
58
|
+
#
|
59
|
+
revision = 0
|
60
|
+
until @data.eos? do
|
61
|
+
|
62
|
+
begin
|
63
|
+
|
64
|
+
pdf.add_new_revision unless revision.zero?
|
65
|
+
revision = revision.succ
|
66
|
+
|
67
|
+
info "...Parsing revision #{pdf.revisions.size}..."
|
68
|
+
parse_objects(pdf)
|
69
|
+
parse_xreftable(pdf)
|
70
|
+
parse_trailer(pdf)
|
71
|
+
|
72
|
+
rescue SystemExit
|
73
|
+
raise
|
74
|
+
rescue Exception => e
|
75
|
+
error "Cannot read : " + (@data.peek(10) + "...").inspect
|
76
|
+
error "Stopped on exception : " + e.message
|
77
|
+
|
78
|
+
break
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
warn "This file has been linearized." if pdf.is_linearized?
|
84
|
+
|
85
|
+
#
|
86
|
+
# Decrypt encrypted file contents
|
87
|
+
#
|
88
|
+
if pdf.is_encrypted?
|
89
|
+
warn "This document contains encrypted data!"
|
90
|
+
|
91
|
+
passwd = @options[:password]
|
92
|
+
begin
|
93
|
+
pdf.decrypt(passwd)
|
94
|
+
rescue EncryptionInvalidPasswordError
|
95
|
+
if passwd.empty?
|
96
|
+
passwd = @options[:prompt_password].call
|
97
|
+
retry unless passwd.empty?
|
98
|
+
end
|
99
|
+
|
100
|
+
raise EncryptionInvalidPasswordError
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
if pdf.is_signed?
|
105
|
+
warn "This document has been signed!"
|
106
|
+
end
|
107
|
+
|
108
|
+
pdf
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|