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 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