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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 67f206ada90cab96c6551107f6e1b7c882608a9c
4
- data.tar.gz: c64e6272dd9512390470c9e5c56454f569162913
3
+ metadata.gz: e32cc8725108ee820a1b79b293f0f22357816d7f
4
+ data.tar.gz: 329075b872aff363e86f995ef67bc2744f383539
5
5
  SHA512:
6
- metadata.gz: a5335917cb27a2fba1c7e36cd805b924d225d436b62be76411d43c51102c651eb5cbdc931cf12bfd4de206e93a421afcf01edae11b20816a4d6815d06b7aba3f
7
- data.tar.gz: aa3eaf290b1c5ece1b06630dcb1440e81403865d753355dbccac57ba7f395486eb2bd3b14ba0a96baa8b15c2d4666072d37dead6a54de7c44a40d212ebb67bb8
6
+ metadata.gz: bba9faff66109e12ce8212f7c048e3c58e61ffce8012dbb797250545bced380a40ff02546a7bf15d73e0dba2f67782de12987816659656e4397a7411fd43662e
7
+ data.tar.gz: eea468b0212056341542b6bddae44e9194da88b0b546850b91e6ca5aa5a05161263bbbe303e2b67b12c72a4cdeb2a7a6c2af9aba3a0dce53784a8f30093bab53
@@ -1,3 +1,7 @@
1
+ 2.1.0
2
+ -----
3
+ * Moved pdfwalker to a separate gem
4
+
1
5
  2.0.0
2
6
  -----
3
7
  * Code reindented to 4 spaces.
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, except for the graphical interface which is distributed under the [GPL](bin/gui/COPYING) license.
112
+ Origami is distributed under the [LGPL](COPYING.LESSER) license.
112
113
 
113
- Copyright © 2016 Guillaume Delugré.
114
+ Copyright © 2017 Guillaume Delugré.
@@ -83,5 +83,5 @@ begin
83
83
  PDF.read(target, params).save(@options[:output], intent: 'PDF/A', noindent: true)
84
84
 
85
85
  rescue
86
- abort"#{$!.class}: #{$!.message}"
86
+ abort "#{$!.class}: #{$!.message}"
87
87
  end
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(TARGET)
146
- ext = File.extname(TARGET)
147
- dest_name = "#{File.basename(TARGET, ext)}_#{digest}#{ext}"
148
- dest_path = File.join(@options[:move_dir], dest_name)
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(TARGET, dest_path)
150
+ FileUtils.move(file, dest_path)
151
151
  end
152
152
 
153
153
  def check_rights(*required_rights)
data/bin/pdfsh CHANGED
@@ -7,6 +7,6 @@ rescue LoadError
7
7
  end
8
8
 
9
9
  $:.unshift File.join(__dir__, 'shell')
10
- ENV["IRBRC"] = File.join(__dir__, "shell", ".irbrc")
10
+ ENV["IRBRC"] = File.join(__dir__, "shell", "irbrc")
11
11
 
12
12
  IRB.start
File without changes
@@ -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? or val.parent.equal?(self)
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
- val
161
+ self.push(val)
162
+ end
163
+ end
155
164
  end
156
165
 
157
166
  def copy
@@ -159,8 +159,6 @@ module Origami
159
159
 
160
160
  key.parent = self
161
161
  val.parent = self unless val.indirect? or val.parent.equal?(self)
162
-
163
- val
164
162
  end
165
163
 
166
164
  def [](key)
@@ -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
- if handler.is_user_password?(passwd, doc_id)
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, crypt_filters = crypto_revision_from_options(params)
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
- algorithm = Encryption::RC4
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
- algorithm = Encryption::AES
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
- @crypt_filters[name]
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
- encryption_cipher @str_filter
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
- encryption_cipher @stm_filter
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.
@@ -214,8 +214,6 @@ module Origami
214
214
  pbyte = bits >> 3
215
215
  pbit = bits - (pbyte << 3)
216
216
  @ptr_byte, @ptr_bit = pbyte, pbit
217
-
218
- bits
219
217
  end
220
218
 
221
219
  #
@@ -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.has_key?(:K) ? @params.K.value : 0
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
- columns = @params.has_key?(:Columns) ? @params.Columns.value : (stream.size << 3)
73
- unless columns.is_a?(::Integer) and columns > 0 #and columns % 8 == 0
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 stream.size % (columns >> 3) != 0
78
- raise CCITTFaxFilterError.new("Data size is not a multiple of image width", input_data: stream)
79
- end
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
- colors = (@params.BlackIs1 == true) ? [0,1] : [1,0]
82
- white, _black = colors
83
- bitr = Utils::BitReader.new(stream)
84
- bitw = Utils::BitWriter.new
95
+ until bitr.eod?
96
+ # Emit line synchronization code.
97
+ bitw.write(*EOL) if use_eol
85
98
 
86
- # Group 4 requires an imaginary white line
87
- if mode < 0
88
- prev_line = Utils::BitWriter.new
89
- write_bit_range(prev_line, white, columns)
90
- prev_line = Utils::BitReader.new(prev_line.final.to_s)
91
- end
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.has_key?(:K) ? @params.K.value : 0
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 > 0 #and columns % 8 == 0
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
- unless @params.has_key?(:Rows) and @params.Rows.is_a?(::Integer) and @params.Rows.value > 0
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