origami 2.0.4 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +6 -5
- data/bin/pdf2pdfa +1 -1
- data/bin/pdfcop +5 -5
- data/bin/pdfsh +1 -1
- data/bin/shell/{.irbrc → irbrc} +0 -0
- data/lib/origami/array.rb +12 -3
- data/lib/origami/dictionary.rb +0 -2
- data/lib/origami/encryption.rb +77 -77
- data/lib/origami/filters.rb +0 -2
- data/lib/origami/filters/ccitt.rb +41 -33
- data/lib/origami/filters/lzw.rb +12 -13
- data/lib/origami/graphics/patterns.rb +3 -1
- data/lib/origami/graphics/render.rb +1 -0
- data/lib/origami/graphics/xobject.rb +2 -2
- data/lib/origami/signature.rb +68 -118
- data/lib/origami/stream.rb +0 -2
- data/lib/origami/version.rb +1 -1
- data/test/test_streams.rb +7 -5
- metadata +4 -21
- data/bin/gui/COPYING +0 -674
- data/bin/gui/about.rb +0 -36
- data/bin/gui/config.rb +0 -120
- data/bin/gui/file.rb +0 -314
- data/bin/gui/gtkhex.rb +0 -1326
- data/bin/gui/hexview.rb +0 -84
- data/bin/gui/imgview.rb +0 -69
- data/bin/gui/menu.rb +0 -382
- data/bin/gui/properties.rb +0 -126
- data/bin/gui/signing.rb +0 -558
- data/bin/gui/textview.rb +0 -88
- data/bin/gui/treeview.rb +0 -416
- data/bin/gui/walker.rb +0 -308
- data/bin/gui/xrefs.rb +0 -75
- data/bin/pdfwalker +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e32cc8725108ee820a1b79b293f0f22357816d7f
|
4
|
+
data.tar.gz: 329075b872aff363e86f995ef67bc2744f383539
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bba9faff66109e12ce8212f7c048e3c58e61ffce8012dbb797250545bced380a40ff02546a7bf15d73e0dba2f67782de12987816659656e4397a7411fd43662e
|
7
|
+
data.tar.gz: eea468b0212056341542b6bddae44e9194da88b0b546850b91e6ca5aa5a05161263bbbe303e2b67b12c72a4cdeb2a7a6c2af9aba3a0dce53784a8f30093bab53
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -28,10 +28,8 @@ As of version 2, the minimal version required to run Origami is Ruby 2.1.
|
|
28
28
|
|
29
29
|
Some optional features require additional gems:
|
30
30
|
|
31
|
-
* [Gtk2][ruby-gtk2] for running the GUI interface
|
32
31
|
* [therubyracer][the-ruby-racer] for JavaScript emulation of PDF scripts
|
33
32
|
|
34
|
-
[ruby-gtk2]: https://rubygems.org/gems/gtk2
|
35
33
|
[the-ruby-racer]: https://rubygems.org/gems/therubyracer
|
36
34
|
|
37
35
|
Quick start
|
@@ -102,12 +100,15 @@ Origami comes with a set of tools to manipulate PDF documents from the command l
|
|
102
100
|
* [pdfmetadata](bin/pdfmetadata): Displays the metadata contained in a document.
|
103
101
|
* [pdf2ruby](bin/pdf2ruby): Converts a PDF into an Origami script rebuilding an equivalent document (experimental).
|
104
102
|
* [pdfsh](bin/pdfsh): An IRB shell running inside the Origami namespace.
|
105
|
-
* [pdfwalker](bin/pdfwalker): A graphical interface to dig into the contents of a PDF document.
|
106
103
|
|
104
|
+
**Note**: Since version 2.1, [pdfwalker][pdfwalker-gem] has been moved to a [separate repository][pdfwalker-repo].
|
105
|
+
|
106
|
+
[pdfwalker-gem]: https://rubygems.org/gems/pdfwalker
|
107
|
+
[pdfwalker-repo]: https://github.com/gdelugre/pdfwalker
|
107
108
|
|
108
109
|
License
|
109
110
|
-------
|
110
111
|
|
111
|
-
Origami is distributed under the [LGPL](COPYING.LESSER) license
|
112
|
+
Origami is distributed under the [LGPL](COPYING.LESSER) license.
|
112
113
|
|
113
|
-
Copyright ©
|
114
|
+
Copyright © 2017 Guillaume Delugré.
|
data/bin/pdf2pdfa
CHANGED
data/bin/pdfcop
CHANGED
@@ -142,12 +142,12 @@ def reject(cause)
|
|
142
142
|
end
|
143
143
|
|
144
144
|
def quarantine(file, quarantine_folder)
|
145
|
-
digest = Digest::SHA256.file(
|
146
|
-
ext = File.extname(
|
147
|
-
dest_name = "#{File.basename(
|
148
|
-
dest_path = File.join(
|
145
|
+
digest = Digest::SHA256.file(file)
|
146
|
+
ext = File.extname(file)
|
147
|
+
dest_name = "#{File.basename(file, ext)}_#{digest}#{ext}"
|
148
|
+
dest_path = File.join(quarantine_folder, dest_name)
|
149
149
|
|
150
|
-
FileUtils.move(
|
150
|
+
FileUtils.move(file, dest_path)
|
151
151
|
end
|
152
152
|
|
153
153
|
def check_rights(*required_rights)
|
data/bin/pdfsh
CHANGED
data/bin/shell/{.irbrc → irbrc}
RENAMED
File without changes
|
data/lib/origami/array.rb
CHANGED
@@ -145,13 +145,22 @@ module Origami
|
|
145
145
|
end
|
146
146
|
alias push <<
|
147
147
|
|
148
|
-
def []=(key,val)
|
148
|
+
def []=(key, val)
|
149
149
|
key, val = key.to_o, val.to_o
|
150
150
|
super(key.to_o, val.to_o)
|
151
151
|
|
152
|
-
val.parent = self unless val.indirect?
|
152
|
+
val.parent = self unless val.indirect?
|
153
|
+
end
|
154
|
+
|
155
|
+
def concat(*arys)
|
156
|
+
arys.each do |ary|
|
157
|
+
ary.each do |e|
|
158
|
+
val = e.to_o
|
159
|
+
val.parent = self unless val.indirect?
|
153
160
|
|
154
|
-
|
161
|
+
self.push(val)
|
162
|
+
end
|
163
|
+
end
|
155
164
|
end
|
156
165
|
|
157
166
|
def copy
|
data/lib/origami/dictionary.rb
CHANGED
data/lib/origami/encryption.rb
CHANGED
@@ -58,48 +58,6 @@ module Origami
|
|
58
58
|
raise EncryptionNotSupportedError, "Unknown security handler : '#{handler.Filter}'"
|
59
59
|
end
|
60
60
|
|
61
|
-
crypt_filters = {
|
62
|
-
Identity: Encryption::Identity
|
63
|
-
}
|
64
|
-
|
65
|
-
case handler.V.to_i
|
66
|
-
when 1,2
|
67
|
-
crypt_filters = Hash.new(Encryption::RC4)
|
68
|
-
string_filter = stream_filter = nil
|
69
|
-
when 4,5
|
70
|
-
crypt_filters = {
|
71
|
-
Identity: Encryption::Identity
|
72
|
-
}
|
73
|
-
|
74
|
-
if handler[:CF].is_a?(Dictionary)
|
75
|
-
handler[:CF].each_pair do |name, cf|
|
76
|
-
next unless cf.is_a?(Dictionary)
|
77
|
-
|
78
|
-
crypt_filters[name.value] =
|
79
|
-
if cf[:CFM] == :V2 then Encryption::RC4
|
80
|
-
elsif cf[:CFM] == :AESV2 then Encryption::AES
|
81
|
-
elsif cf[:CFM] == :None then Encryption::Identity
|
82
|
-
elsif cf[:CFM] == :AESV3 and handler.V.to_i == 5 then Encryption::AES
|
83
|
-
else
|
84
|
-
raise EncryptionNotSupportedError, "Unsupported encryption version : #{handler.V}"
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
string_filter = handler.StrF.is_a?(Name) ? handler.StrF.value : :Identity
|
90
|
-
stream_filter = handler.StmF.is_a?(Name) ? handler.StmF.value : :Identity
|
91
|
-
|
92
|
-
unless crypt_filters.key?(string_filter)
|
93
|
-
raise EncryptionError, "Invalid StrF value in encryption dictionary"
|
94
|
-
end
|
95
|
-
|
96
|
-
unless crypt_filters.key?(stream_filter)
|
97
|
-
raise EncryptionError, "Invalid StmF value in encryption dictionary"
|
98
|
-
end
|
99
|
-
else
|
100
|
-
raise EncryptionNotSupportedError, "Unsupported encryption version : #{handler.V}"
|
101
|
-
end
|
102
|
-
|
103
61
|
doc_id = trailer_key(:ID)
|
104
62
|
unless doc_id.is_a?(Array)
|
105
63
|
raise EncryptionError, "Document ID was not found or is invalid" unless handler.V.to_i == 5
|
@@ -107,24 +65,11 @@ module Origami
|
|
107
65
|
doc_id = doc_id.first
|
108
66
|
end
|
109
67
|
|
110
|
-
|
111
|
-
encryption_key = handler.compute_user_encryption_key(passwd, doc_id)
|
112
|
-
elsif handler.is_owner_password?(passwd, doc_id)
|
113
|
-
if handler.V.to_i < 5
|
114
|
-
user_passwd = handler.retrieve_user_password(passwd)
|
115
|
-
encryption_key = handler.compute_user_encryption_key(user_passwd, doc_id)
|
116
|
-
else
|
117
|
-
encryption_key = handler.compute_owner_encryption_key(passwd)
|
118
|
-
end
|
119
|
-
else
|
120
|
-
raise EncryptionInvalidPasswordError
|
121
|
-
end
|
68
|
+
encryption_key = handler.derive_encryption_key(passwd, doc_id)
|
122
69
|
|
123
70
|
self.extend(Encryption::EncryptedDocument)
|
124
71
|
self.encryption_handler = handler
|
125
|
-
self.crypt_filters = crypt_filters
|
126
72
|
self.encryption_key = encryption_key
|
127
|
-
self.stm_filter, self.str_filter = stream_filter, string_filter
|
128
73
|
|
129
74
|
decrypt_objects
|
130
75
|
|
@@ -156,7 +101,7 @@ module Origami
|
|
156
101
|
}.update(options)
|
157
102
|
|
158
103
|
# Get the cryptographic parameters.
|
159
|
-
version, revision
|
104
|
+
version, revision = crypto_revision_from_options(params)
|
160
105
|
|
161
106
|
# Create the security handler.
|
162
107
|
handler, encryption_key = create_security_handler(version, revision, params)
|
@@ -165,8 +110,6 @@ module Origami
|
|
165
110
|
self.extend(Encryption::EncryptedDocument)
|
166
111
|
self.encryption_handler = handler
|
167
112
|
self.encryption_key = encryption_key
|
168
|
-
self.crypt_filters = crypt_filters
|
169
|
-
self.stm_filter = self.str_filter = :StdCF
|
170
113
|
|
171
114
|
self
|
172
115
|
end
|
@@ -228,23 +171,12 @@ module Origami
|
|
228
171
|
def crypto_revision_from_options(params)
|
229
172
|
case params[:cipher].upcase
|
230
173
|
when 'RC4'
|
231
|
-
|
232
|
-
version, revision = crypto_revision_from_rc4_key(params[:key_size])
|
233
|
-
crypt_filters = Hash.new(algorithm)
|
234
|
-
|
174
|
+
crypto_revision_from_rc4_key(params[:key_size])
|
235
175
|
when 'AES'
|
236
|
-
|
237
|
-
version, revision = crypto_revision_from_aes_key(params[:key_size], params[:hardened])
|
238
|
-
|
239
|
-
crypt_filters = {
|
240
|
-
Identity: Encryption::Identity,
|
241
|
-
StdCF: algorithm
|
242
|
-
}
|
176
|
+
crypto_revision_from_aes_key(params[:key_size], params[:hardened])
|
243
177
|
else
|
244
178
|
raise EncryptionNotSupportedError, "Cipher not supported : #{params[:cipher]}"
|
245
179
|
end
|
246
|
-
|
247
|
-
[ version, revision, crypt_filters ]
|
248
180
|
end
|
249
181
|
|
250
182
|
#
|
@@ -312,22 +244,20 @@ module Origami
|
|
312
244
|
module EncryptedDocument
|
313
245
|
attr_accessor :encryption_key
|
314
246
|
attr_accessor :encryption_handler
|
315
|
-
attr_accessor :str_filter, :stm_filter
|
316
|
-
attr_accessor :crypt_filters
|
317
247
|
|
318
248
|
# Get the encryption cipher from the crypt filter name.
|
319
249
|
def encryption_cipher(name)
|
320
|
-
@
|
250
|
+
@encryption_handler.encryption_cipher(name)
|
321
251
|
end
|
322
252
|
|
323
253
|
# Get the default string encryption cipher.
|
324
254
|
def string_encryption_cipher
|
325
|
-
|
255
|
+
@encryption_handler.string_encryption_cipher
|
326
256
|
end
|
327
257
|
|
328
258
|
# Get the default stream encryption cipher.
|
329
259
|
def stream_encryption_cipher
|
330
|
-
|
260
|
+
@encryption_handler.stream_encryption_cipher
|
331
261
|
end
|
332
262
|
|
333
263
|
private
|
@@ -736,6 +666,57 @@ module Origami
|
|
736
666
|
field :StmF, :Type => Name, :Default => :Identity, :Version => "1.5"
|
737
667
|
field :StrF, :Type => Name, :Default => :Identity, :Version => "1.5"
|
738
668
|
field :EFF, :Type => Name, :Version => "1.6"
|
669
|
+
|
670
|
+
#
|
671
|
+
# Returns the default string encryption cipher.
|
672
|
+
#
|
673
|
+
def string_encryption_cipher
|
674
|
+
encryption_cipher(self.StrF || :Identity)
|
675
|
+
end
|
676
|
+
|
677
|
+
#
|
678
|
+
# Returns the default stream encryption cipher.
|
679
|
+
#
|
680
|
+
def stream_encryption_cipher
|
681
|
+
encryption_cipher(self.StmF || :Identity)
|
682
|
+
end
|
683
|
+
|
684
|
+
#
|
685
|
+
# Returns the encryption cipher corresponding to a crypt filter name.
|
686
|
+
#
|
687
|
+
def encryption_cipher(name)
|
688
|
+
case self.V.to_i
|
689
|
+
when 1, 2
|
690
|
+
Encryption::RC4
|
691
|
+
when 4, 5
|
692
|
+
return Encryption::Identity if name == :Identity
|
693
|
+
raise EncryptionError, "Broken CF entry" unless self.CF.is_a?(Dictionary)
|
694
|
+
|
695
|
+
self.CF.select { |key, dict| key == name and dict.is_a?(Dictionary) }
|
696
|
+
.map { |_, dict| cipher_from_crypt_filter_method(dict[:CFM] || :None) }
|
697
|
+
.first
|
698
|
+
else
|
699
|
+
raise EncryptionNotSupportedError, "Unsupported encryption version: #{handler.V}"
|
700
|
+
end
|
701
|
+
end
|
702
|
+
|
703
|
+
private
|
704
|
+
|
705
|
+
#
|
706
|
+
# Converts a crypt filter method identifier to its cipher class.
|
707
|
+
#
|
708
|
+
def cipher_from_crypt_filter_method(name)
|
709
|
+
case name.to_sym
|
710
|
+
when :None then Encryption::Identity
|
711
|
+
when :V2 then Encryption::RC4
|
712
|
+
when :AESV2 then Encryption::AES
|
713
|
+
when :AESV3
|
714
|
+
raise EncryptionNotSupportedError, "AESV3 requires a version 5 handler" if self.V.to_i != 5
|
715
|
+
Encryption::AES
|
716
|
+
else
|
717
|
+
raise EncryptionNotSupportedError, "Unsupported crypt filter method: #{name}"
|
718
|
+
end
|
719
|
+
end
|
739
720
|
end
|
740
721
|
|
741
722
|
#
|
@@ -785,6 +766,25 @@ module Origami
|
|
785
766
|
end
|
786
767
|
end
|
787
768
|
|
769
|
+
#
|
770
|
+
# Checks the given password and derives the document encryption key.
|
771
|
+
# Raises EncryptionInvalidPasswordError on invalid password.
|
772
|
+
#
|
773
|
+
def derive_encryption_key(passwd, doc_id)
|
774
|
+
if is_user_password?(passwd, doc_id)
|
775
|
+
compute_user_encryption_key(passwd, doc_id)
|
776
|
+
elsif is_owner_password?(passwd, doc_id)
|
777
|
+
if self.V.to_i < 5
|
778
|
+
user_passwd = retrieve_user_password(passwd)
|
779
|
+
compute_user_encryption_key(user_passwd, doc_id)
|
780
|
+
else
|
781
|
+
compute_owner_encryption_key(passwd)
|
782
|
+
end
|
783
|
+
else
|
784
|
+
raise EncryptionInvalidPasswordError
|
785
|
+
end
|
786
|
+
end
|
787
|
+
|
788
788
|
#
|
789
789
|
# Computes the key that will be used to encrypt/decrypt the document contents with user password.
|
790
790
|
# Called at all revisions.
|
data/lib/origami/filters.rb
CHANGED
@@ -63,44 +63,50 @@ module Origami
|
|
63
63
|
# Encodes data using CCITT-facsimile compression method.
|
64
64
|
#
|
65
65
|
def encode(stream)
|
66
|
-
mode = @params.
|
66
|
+
mode = @params.key?(:K) ? @params.K.value : 0
|
67
|
+
colors = (@params.BlackIs1 == true) ? [0,1] : [1,0]
|
68
|
+
use_eob = (@params.EndOfBlock.nil? or @params.EndOfBlock == true)
|
69
|
+
use_eol = (@params.EndOfLine == true)
|
70
|
+
white, _black = colors
|
71
|
+
|
72
|
+
bitr = Utils::BitReader.new(stream)
|
73
|
+
bitw = Utils::BitWriter.new
|
67
74
|
|
68
75
|
unless mode.is_a?(::Integer) and mode <= 0
|
69
76
|
raise NotImplementedError.new("CCITT encoding scheme not supported", input_data: stream)
|
70
77
|
end
|
71
78
|
|
72
|
-
|
73
|
-
|
79
|
+
# Use a single row if no width has been set.
|
80
|
+
@params[:Columns] ||= stream.size * 8
|
81
|
+
columns = @params.Columns.value
|
82
|
+
|
83
|
+
unless columns.is_a?(::Integer) and columns >= 0
|
74
84
|
raise CCITTFaxFilterError.new("Invalid value for parameter `Columns'", input_data: stream)
|
75
85
|
end
|
76
86
|
|
77
|
-
if
|
78
|
-
|
79
|
-
|
87
|
+
if columns > 0
|
88
|
+
# Group 4 requires an imaginary white line
|
89
|
+
if mode < 0
|
90
|
+
prev_line = Utils::BitWriter.new
|
91
|
+
write_bit_range(prev_line, white, columns)
|
92
|
+
prev_line = Utils::BitReader.new(prev_line.final.to_s)
|
93
|
+
end
|
80
94
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
bitw = Utils::BitWriter.new
|
95
|
+
until bitr.eod?
|
96
|
+
# Emit line synchronization code.
|
97
|
+
bitw.write(*EOL) if use_eol
|
85
98
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
until bitr.eod?
|
94
|
-
case
|
95
|
-
when mode == 0
|
96
|
-
encode_one_dimensional_line(bitr, bitw, columns, colors)
|
97
|
-
when mode < 0
|
98
|
-
encode_two_dimensional_line(bitr, bitw, columns, colors, prev_line)
|
99
|
+
case
|
100
|
+
when mode == 0
|
101
|
+
encode_one_dimensional_line(bitr, bitw, columns, colors)
|
102
|
+
when mode < 0
|
103
|
+
encode_two_dimensional_line(bitr, bitw, columns, colors, prev_line)
|
104
|
+
end
|
99
105
|
end
|
100
106
|
end
|
101
107
|
|
102
|
-
# Emit return-to-control code
|
103
|
-
bitw.write(*RTC)
|
108
|
+
# Emit return-to-control code.
|
109
|
+
bitw.write(*RTC) if use_eob
|
104
110
|
|
105
111
|
bitw.final.to_s
|
106
112
|
end
|
@@ -109,14 +115,14 @@ module Origami
|
|
109
115
|
# Decodes data using CCITT-facsimile compression method.
|
110
116
|
#
|
111
117
|
def decode(stream)
|
112
|
-
mode = @params.
|
118
|
+
mode = @params.key?(:K) ? @params.K.value : 0
|
113
119
|
|
114
120
|
unless mode.is_a?(::Integer) and mode <= 0
|
115
121
|
raise NotImplementedError.new("CCITT encoding scheme not supported", input_data: stream)
|
116
122
|
end
|
117
123
|
|
118
124
|
columns = @params.has_key?(:Columns) ? @params.Columns.value : 1728
|
119
|
-
unless columns.is_a?(::Integer) and columns
|
125
|
+
unless columns.is_a?(::Integer) and columns >= 0
|
120
126
|
raise CCITTFaxFilterError.new("Invalid value for parameter `Columns'", input_data: stream)
|
121
127
|
end
|
122
128
|
|
@@ -130,18 +136,18 @@ module Origami
|
|
130
136
|
}
|
131
137
|
|
132
138
|
unless params[:has_eob?]
|
133
|
-
|
139
|
+
rows = @params.key?(:Rows) ? @params.Rows.value : 0
|
140
|
+
|
141
|
+
unless rows.is_a?(::Integer) and rows >= 0
|
134
142
|
raise CCITTFaxFilterError.new("Invalid value for parameter `Rows'", input_data: stream)
|
135
143
|
end
|
136
|
-
|
137
|
-
rows = @params.Rows.to_i
|
138
144
|
end
|
139
145
|
|
140
146
|
bitr = Utils::BitReader.new(stream)
|
141
147
|
bitw = Utils::BitWriter.new
|
142
148
|
|
143
149
|
# Group 4 requires an imaginary white line
|
144
|
-
if mode < 0
|
150
|
+
if columns > 0 and mode < 0
|
145
151
|
prev_line = Utils::BitWriter.new
|
146
152
|
write_bit_range(prev_line, white, columns)
|
147
153
|
prev_line = Utils::BitReader.new(prev_line.final.to_s)
|
@@ -159,6 +165,8 @@ module Origami
|
|
159
165
|
break
|
160
166
|
end
|
161
167
|
|
168
|
+
break if columns == 0
|
169
|
+
|
162
170
|
# checking for the presence of EOL
|
163
171
|
if bitr.peek(EOL[1]) != EOL[0]
|
164
172
|
raise InvalidCCITTFaxDataError.new(
|
@@ -184,7 +192,6 @@ module Origami
|
|
184
192
|
raise error
|
185
193
|
end
|
186
194
|
|
187
|
-
|
188
195
|
rows -= 1 unless params[:has_eob?]
|
189
196
|
end
|
190
197
|
|
@@ -194,11 +201,12 @@ module Origami
|
|
194
201
|
private
|
195
202
|
|
196
203
|
def encode_one_dimensional_line(input, output, columns, colors) #:nodoc:
|
197
|
-
output.write(*EOL)
|
198
204
|
scan_len = 0
|
199
205
|
white, _black = colors
|
200
206
|
current_color = white
|
201
207
|
|
208
|
+
return if columns == 0
|
209
|
+
|
202
210
|
# Process each bit in line.
|
203
211
|
begin
|
204
212
|
if input.read(1) == current_color
|