origami 2.0.4 → 2.1.0
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/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
|