hexapdf 0.14.3 → 0.14.4

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
  SHA256:
3
- metadata.gz: c43d8e9e117db1717ddfee73a54e4384743b8aa35863ab5bd19ffe57b8ce5674
4
- data.tar.gz: 1020c8a3de8fcdf201500c1c0d22dfb99ed27daebac7baac92748f8127efc992
3
+ metadata.gz: 958692ab2c53f74fe599c0ba8c9c046aa41b38d2bf840a47dec8f0e258fd86e0
4
+ data.tar.gz: b41d46ccb39d36d351cc143ba0afc145e785307b2a7ae90bc0f32d1ab76949af
5
5
  SHA512:
6
- metadata.gz: e19eea4e88077afb7e8532fa6fe9ab2a03ffc5588749b72277462a971ebcec877ee72868d0ab698744117d46566be98e65c10225649d3bd1b4cd6e64e9625767
7
- data.tar.gz: 6626a9feba0af0b46f293c1069a0d53b458a0dc29d08b82253f14f9bb98a878b914042faccc433b73f2f0e35d4da47c58a1bdebd2f3dee2fefb24c076a4e6bb3
6
+ metadata.gz: c16e231aeeb12b55daf75a28a8e3918f6807127257655f42d1f3433c76cb25f0ea42daafc8dd20bd37099f9ef5f6f2a6a3e3b2468acf000c041452a32908e1ee
7
+ data.tar.gz: a9a1fff7c7ff699c2b48333d89fae1aa67cd7ce61003845f76e4855cfc2f2ad5969ae668359091cd3fa225ea9a8b99fb9883e28b07fe6fcd985c7f83b842969d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,28 @@
1
+ ## 0.14.4 - 2021-02-27
2
+
3
+ ### Added
4
+
5
+ * Support for the Crypt filters
6
+
7
+ ### Changed
8
+
9
+ * [HexaPDF::MalformedPDFError] to make the `pos` argument optional
10
+
11
+ ### Fixed
12
+
13
+ * Handling of invalid floating point numbers NaN, Inf and -Inf when serializing
14
+ * Processing of invalid PDF files containing NaN and Inf instead of numbers
15
+ * Bug in Type1 font AFM parser that occured if the file doesn't end with a new
16
+ line character
17
+ * Cross-reference table reconstruction to handle the case of an entry specifying
18
+ a non-existent indirect object
19
+ * Cross-reference table reconstruction to handle trailers specified by cross-
20
+ reference streams
21
+ * Cross-reference table reconstruction to use the set security handle for
22
+ decrypting indirect objects
23
+ * Parsing of cross-reference streams where data is missing
24
+
25
+
1
26
  ## 0.14.3 - 2021-02-16
2
27
 
3
28
  ### Fixed
@@ -393,7 +393,7 @@ module HexaPDF
393
393
  DCTDecode: 'HexaPDF::Filter::PassThrough',
394
394
  DCT: 'HexaPDF::Filter::PassThrough',
395
395
  JPXDecode: 'HexaPDF::Filter::PassThrough',
396
- Crypt: nil,
396
+ Crypt: 'HexaPDF::Filter::Crypt',
397
397
  Encryption: 'HexaPDF::Filter::Encryption',
398
398
  },
399
399
  'font.map' => {},
@@ -268,7 +268,7 @@ module HexaPDF
268
268
  str.replace(string_algorithm.decrypt(key, str))
269
269
  end
270
270
 
271
- if obj.kind_of?(HexaPDF::Stream)
271
+ if obj.kind_of?(HexaPDF::Stream) && obj.raw_stream.filter[0] != :Crypt
272
272
  unless string_algorithm == stream_algorithm
273
273
  key = object_key(obj.oid, obj.gen, stream_algorithm)
274
274
  end
@@ -300,7 +300,12 @@ module HexaPDF
300
300
  obj.raw_stream.key == key && obj.raw_stream.algorithm == stream_algorithm
301
301
  obj.raw_stream.undecrypted_fiber
302
302
  else
303
- stream_algorithm.encryption_fiber(key, result)
303
+ filter = obj[:Filter]
304
+ if filter == :Crypt || (filter.kind_of?(PDFArray) && filter[0] == :Crypt)
305
+ result
306
+ else
307
+ stream_algorithm.encryption_fiber(key, result)
308
+ end
304
309
  end
305
310
  end
306
311
 
data/lib/hexapdf/error.rb CHANGED
@@ -47,14 +47,14 @@ module HexaPDF
47
47
 
48
48
  # Creates a new malformed PDF error object for the given exception message.
49
49
  #
50
- # The byte position where the error occured has to be given via the +pos+ argument.
51
- def initialize(message, pos:)
50
+ # The byte position where the error occured can be given via the +pos+ argument.
51
+ def initialize(message, pos: nil)
52
52
  super(message)
53
53
  @pos = pos
54
54
  end
55
55
 
56
56
  def message # :nodoc:
57
- "PDF malformed around position #{pos}: #{super}"
57
+ "PDF malformed#{pos ? "around position #{pos}" : ''}: #{super}"
58
58
  end
59
59
 
60
60
  end
@@ -95,6 +95,7 @@ module HexaPDF
95
95
  autoload(:Predictor, 'hexapdf/filter/predictor')
96
96
 
97
97
  autoload(:Encryption, 'hexapdf/filter/encryption')
98
+ autoload(:Crypt, 'hexapdf/filter/crypt')
98
99
 
99
100
  autoload(:PassThrough, 'hexapdf/filter/pass_through')
100
101
 
@@ -0,0 +1,60 @@
1
+ # -*- encoding: utf-8; frozen_string_literal: true -*-
2
+ #
3
+ #--
4
+ # This file is part of HexaPDF.
5
+ #
6
+ # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
+ #
9
+ # HexaPDF is free software: you can redistribute it and/or modify it
10
+ # under the terms of the GNU Affero General Public License version 3 as
11
+ # published by the Free Software Foundation with the addition of the
12
+ # following permission added to Section 15 as permitted in Section 7(a):
13
+ # FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
14
+ # THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
15
+ # INFRINGEMENT OF THIRD PARTY RIGHTS.
16
+ #
17
+ # HexaPDF is distributed in the hope that it will be useful, but WITHOUT
18
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
20
+ # License for more details.
21
+ #
22
+ # You should have received a copy of the GNU Affero General Public License
23
+ # along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
24
+ #
25
+ # The interactive user interfaces in modified source and object code
26
+ # versions of HexaPDF must display Appropriate Legal Notices, as required
27
+ # under Section 5 of the GNU Affero General Public License version 3.
28
+ #
29
+ # In accordance with Section 7(b) of the GNU Affero General Public
30
+ # License, a covered work must retain the producer line in every PDF that
31
+ # is created or manipulated using HexaPDF.
32
+ #
33
+ # If the GNU Affero General Public License doesn't fit your need,
34
+ # commercial licenses are available at <https://gettalong.at/hexapdf/>.
35
+ #++
36
+
37
+ require 'hexapdf/error'
38
+
39
+ module HexaPDF
40
+ module Filter
41
+
42
+ # This filter module implements the Crypt filter. The only supported part is using the Identity
43
+ # filter.
44
+ module Crypt
45
+
46
+ # See HexaPDF::Filter
47
+ def self.decoder(source, options)
48
+ if !options || !options.key?(:Name) || options[:Name] == :Identity
49
+ source
50
+ else
51
+ raise FilterError, "Handling of Crypt filters besides Identity is not implemented"
52
+ end
53
+ end
54
+
55
+ singleton_class.send(:alias_method, :encoder, :decoder)
56
+
57
+ end
58
+
59
+ end
60
+ end
@@ -207,7 +207,8 @@ module HexaPDF
207
207
 
208
208
  # Returns the rest of the line, with whitespace stripped.
209
209
  def parse_string
210
- line = @line.strip!
210
+ @line.strip!
211
+ line = @line
211
212
  @line = ''
212
213
  line
213
214
  end
@@ -56,10 +56,12 @@ module HexaPDF
56
56
  # PDF references are resolved using the associated Document object.
57
57
  def initialize(io, document)
58
58
  @io = io
59
- @tokenizer = Tokenizer.new(io)
59
+ on_correctable_error = document.config['parser.on_correctable_error'].curry[document]
60
+ @tokenizer = Tokenizer.new(io, on_correctable_error: on_correctable_error)
60
61
  @document = document
61
62
  @object_stream_data = {}
62
63
  @reconstructed_revision = nil
64
+ @in_reconstruct_revision = false
63
65
  retrieve_pdf_header_offset_and_version
64
66
  end
65
67
 
@@ -94,7 +96,8 @@ module HexaPDF
94
96
 
95
97
  @document.wrap(obj, oid: oid, gen: gen, stream: stream)
96
98
  rescue HexaPDF::MalformedPDFError
97
- reconstructed_revision.object(xref_entry)
99
+ reconstructed_revision.object(xref_entry) ||
100
+ @document.wrap(nil, oid: xref_entry.oid, gen: xref_entry.gen)
98
101
  end
99
102
 
100
103
  # Parses the indirect object at the specified offset.
@@ -389,6 +392,9 @@ module HexaPDF
389
392
  # If the file contains multiple cross-reference sections, all objects will be put into a single
390
393
  # cross-reference table, later objects overwriting prior ones.
391
394
  def reconstruct_revision
395
+ return if @in_reconstruct_revision
396
+ @in_reconstruct_revision = true
397
+
392
398
  raise unless @document.config['parser.try_xref_reconstruction']
393
399
  msg = "#{$!} - trying cross-reference table reconstruction"
394
400
  @document.config['parser.on_correctable_error'].call(@document, msg, @tokenizer.pos)
@@ -424,16 +430,22 @@ module HexaPDF
424
430
  end
425
431
  end
426
432
 
427
- trailer&.delete(:Prev) # no need for this and may wreak havoc
428
433
  if !trailer || trailer.empty?
429
- raise_malformed("Could not reconstruct malformed PDF because trailer was not found", pos: 0)
434
+ _, trailer = load_revision(startxref_offset) rescue nil
435
+ unless trailer
436
+ @in_reconstruct_revision = false
437
+ raise_malformed("Could not reconstruct malformed PDF because trailer was not found", pos: 0)
438
+ end
430
439
  end
440
+ trailer&.delete(:Prev) # no need for this and may wreak havoc
431
441
 
432
442
  loader = lambda do |xref_entry|
433
443
  obj, oid, gen, stream = parse_indirect_object(xref_entry.pos)
434
- @document.wrap(obj, oid: oid, gen: gen, stream: stream)
444
+ obj = @document.wrap(obj, oid: oid, gen: gen, stream: stream)
445
+ @document.security_handler ? @document.security_handler.decrypt(obj) : obj
435
446
  end
436
447
 
448
+ @in_reconstruct_revision = false
437
449
  Revision.new(@document.wrap(trailer, type: :XXTrailer), xref_section: xref,
438
450
  loader: loader)
439
451
  end
@@ -191,7 +191,13 @@ module HexaPDF
191
191
  #
192
192
  # See: PDF1.7 s7.3.3
193
193
  def serialize_float(obj)
194
- -0.0001 < obj && obj < 0.0001 && obj != 0 ? sprintf("%.6f", obj) : obj.round(6).to_s
194
+ if -0.0001 < obj && obj < 0.0001 && obj != 0
195
+ sprintf("%.6f", obj)
196
+ elsif obj.finite?
197
+ obj.round(6).to_s
198
+ else
199
+ raise HexaPDF::Error, "Can't serialize special floating point number #{obj}"
200
+ end
195
201
  end
196
202
 
197
203
  # The regexp matches all characters that need to be escaped and the substs hash contains the
@@ -73,11 +73,15 @@ module HexaPDF
73
73
  # The IO object from the tokens are read.
74
74
  attr_reader :io
75
75
 
76
- # Creates a new tokenizer.
77
- def initialize(io)
76
+ # Creates a new tokenizer for the given IO stream.
77
+ #
78
+ # If +on_correctable_error+ is set to an object responding to +call(msg, pos)+, errors for
79
+ # correctable situations are only raised if the return value of calling the object is +true+.
80
+ def initialize(io, on_correctable_error: nil)
78
81
  @io = io
79
82
  @ss = StringScanner.new(''.b)
80
83
  @original_pos = -1
84
+ @on_correctable_error = on_correctable_error || proc { false }
81
85
  self.pos = 0
82
86
  end
83
87
 
@@ -180,7 +184,8 @@ module HexaPDF
180
184
  end
181
185
  else
182
186
  unless allow_keyword
183
- raise HexaPDF::MalformedPDFError.new("Invalid object, got token #{token}", pos: pos)
187
+ maybe_raise("Invalid object, got token #{token}", force: token !~ /^-?(nan|inf)$/i)
188
+ token = 0
184
189
  end
185
190
  end
186
191
  end
@@ -428,6 +433,20 @@ module HexaPDF
428
433
  true
429
434
  end
430
435
 
436
+ # Calls the @on_correctable_error callable object with the given message and the current
437
+ # position. If the returned value is +true+, raises a HexaPDF::MalformedPDFError. Otherwise the
438
+ # error is corrected (by the caller) and tokenization continues.
439
+ #
440
+ # If the option +force+ is used, the callable object is not called and the error is raised
441
+ # immediately.
442
+ def maybe_raise(msg, force: false)
443
+ if force || @on_correctable_error.call(msg, pos)
444
+ error = HexaPDF::MalformedPDFError.new(msg, pos: pos)
445
+ error.set_backtrace(caller(1))
446
+ raise error
447
+ end
448
+ end
449
+
431
450
  end
432
451
 
433
452
  end
@@ -135,6 +135,13 @@ module HexaPDF
135
135
  w1 = w[1]
136
136
  w2 = w[2]
137
137
 
138
+ needed_bytes = (w0 + w1 + w2) * index.each_slice(2).sum(&:last)
139
+
140
+ if needed_bytes > data.size
141
+ raise HexaPDF::MalformedPDFError, "Cross-reference stream is missing data " \
142
+ "(#{needed_bytes} bytes needed, got #{data.size})"
143
+ end
144
+
138
145
  index.each_slice(2) do |first_oid, number_of_entries|
139
146
  first_oid.upto(first_oid + number_of_entries - 1) do |oid|
140
147
  # Default for first field: type 1
@@ -37,6 +37,6 @@
37
37
  module HexaPDF
38
38
 
39
39
  # The version of HexaPDF.
40
- VERSION = '0.14.3'
40
+ VERSION = '0.14.4'
41
41
 
42
42
  end
@@ -297,6 +297,13 @@ describe HexaPDF::Encryption::SecurityHandler do
297
297
  assert_equal(@encrypted, @handler.decrypt(@obj)[:Key])
298
298
  end
299
299
 
300
+ it "defers handling encryption to a Crypt filter is specified" do
301
+ data = HexaPDF::StreamData.new(proc { 'mydata' }, filter: :Crypt)
302
+ obj = @document.wrap({}, oid: 1, stream: data)
303
+ @handler.decrypt(obj)
304
+ assert_equal('mydata', obj.stream)
305
+ end
306
+
300
307
  it "doesn't decrypt XRef streams" do
301
308
  @obj[:Type] = :XRef
302
309
  assert_equal(@encrypted, @handler.decrypt(@obj)[:Key])
@@ -343,6 +350,14 @@ describe HexaPDF::Encryption::SecurityHandler do
343
350
  assert_equal('string', @handler.encrypt_stream(@stream).resume)
344
351
  end
345
352
 
353
+ it "defers encrypting to a Crypt filter if specified" do
354
+ @stream.set_filter(:Crypt)
355
+ assert_equal('string', @handler.encrypt_stream(@stream).resume)
356
+
357
+ @stream.set_filter([:Crypt])
358
+ assert_equal('string', @handler.encrypt_stream(@stream).resume)
359
+ end
360
+
346
361
  it "doesn't encrypt the /Contents key of signature dictionaries" do
347
362
  @obj[:Type] = :Sig
348
363
  @obj[:Contents] = "test"
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/filter/crypt'
5
+
6
+ describe HexaPDF::Filter::Crypt do
7
+ before do
8
+ @obj = HexaPDF::Filter::Crypt
9
+ @source = Fiber.new { "hallo" }
10
+ end
11
+
12
+ it "works with the Identity filter" do
13
+ assert_equal(@source, @obj.decoder(@source, nil))
14
+ assert_equal(@source, @obj.encoder(@source, {})) # sic: 'encoder'
15
+ assert_equal(@source, @obj.decoder(@source, {Name: :Identity}))
16
+ end
17
+
18
+ it "fails if crypt filter name is not Identity" do
19
+ assert_raises(HexaPDF::FilterError) { @obj.decoder(@source, {Name: :Other}) }
20
+ end
21
+ end
@@ -39,6 +39,11 @@ describe HexaPDF::Font::Type1::AFMParser do
39
39
  end
40
40
  end
41
41
 
42
+ it "parses until EOF if no end token is found" do
43
+ io = StringIO.new("StartFontMetrics 4.1\nFontName Test")
44
+ assert_equal('Test', HexaPDF::Font::Type1::AFMParser.parse(io).font_name)
45
+ end
46
+
42
47
  it "extracts kerning and ligature information" do
43
48
  metrics = FONT_TIMES.metrics
44
49
  glyph = metrics.character_metrics[:f]
@@ -531,11 +531,25 @@ describe HexaPDF::Parser do
531
531
  assert_equal(6, @parser.load_object(@xref).value)
532
532
  end
533
533
 
534
+ it "uses a security handler for decrypting indirect objects if necessary" do
535
+ handler = Minitest::Mock.new
536
+ handler.expect(:decrypt, HexaPDF::Object.new(:result, oid: 1), [HexaPDF::Object])
537
+ @document.instance_variable_set(:@security_handler, handler)
538
+ create_parser("1 0 obj\n6\nendobj\ntrailer\n<</Size 1>>")
539
+ assert_equal(:result, @parser.load_object(@xref).value)
540
+ assert(handler.verify)
541
+ end
542
+
534
543
  it "ignores parts where the starting line is split across lines" do
535
544
  create_parser("1 0 obj\n5\nendobj\n1 0\nobj\n6\nendobj\ntrailer\n<</Size 1>>")
536
545
  assert_equal(5, @parser.load_object(@xref).value)
537
546
  end
538
547
 
548
+ it "handles the case when the specified object had an xref entry but is not found" do
549
+ create_parser("3 0 obj\n5\nendobj\ntrailer\n<</Size 1>>")
550
+ assert(@parser.load_object(@xref).null?)
551
+ end
552
+
539
553
  it "handles cases where the line contains an invalid string that exceeds the read buffer" do
540
554
  create_parser("(1" << "(abc" * 32188 << "\n1 0 obj\n6\nendobj\ntrailer\n<</Size 1>>")
541
555
  assert_equal(6, @parser.load_object(@xref).value)
@@ -568,6 +582,16 @@ describe HexaPDF::Parser do
568
582
  assert_equal({Size: 1}, @parser.reconstructed_revision.trailer.value)
569
583
  end
570
584
 
585
+ it "tries the trailer specified at the startxref position if no other is found" do
586
+ create_parser("1 0 obj\n5\nendobj\nquack xref trailer <</Size 1/Prev 5>>\nstartxref\n22\n%%EOF")
587
+ assert_equal({Size: 1}, @parser.reconstructed_revision.trailer.value)
588
+ end
589
+
590
+ it "fails if no trailer is found and the trailer specified at the startxref position is not valid" do
591
+ create_parser("1 0 obj\n5\nendobj\nquack trailer <</Size 1>>\nstartxref\n22\n%%EOF")
592
+ assert_raises(HexaPDF::MalformedPDFError) { @parser.reconstructed_revision.trailer }
593
+ end
594
+
571
595
  it "fails if no valid trailer is found" do
572
596
  create_parser("1 0 obj\n5\nendobj")
573
597
  assert_raises(HexaPDF::MalformedPDFError) { @parser.load_object(@xref) }
@@ -58,6 +58,9 @@ describe HexaPDF::Serializer do
58
58
  assert_serialized("0.000005", 0.000005)
59
59
  assert_serialized("-0.000005", -0.000005)
60
60
  assert_serialized("0.0", 0.0)
61
+ assert_raises(HexaPDF::Error) { @serializer.serialize(0.0 / 0) }
62
+ assert_raises(HexaPDF::Error) { @serializer.serialize(1.0 / 0) }
63
+ assert_raises(HexaPDF::Error) { @serializer.serialize(-1.0 / 0) }
61
64
  end
62
65
 
63
66
  it "serializes numerics" do
@@ -55,4 +55,26 @@ describe HexaPDF::Tokenizer do
55
55
  assert_equal(HexaPDF::Tokenizer::NO_MORE_TOKENS, @tokenizer.next_integer_or_keyword)
56
56
  end
57
57
 
58
+ describe "can correct some problems" do
59
+ describe "invalid inf/-inf/nan/-nan tokens" do
60
+ it "turns them into zeros" do
61
+ count = 0
62
+ on_correctable_error = lambda do |msg, pos|
63
+ count += 1
64
+ assert_match(/Invalid object, got token/, msg)
65
+ assert_equal(8, pos) if count == 2
66
+ false
67
+ end
68
+ tokenizer = HexaPDF::Tokenizer.new(StringIO.new("inf -Inf NaN -nan"),
69
+ on_correctable_error: on_correctable_error)
70
+ assert_equal([0, 0, 0, 0], 4.times.map { tokenizer.next_object })
71
+ assert_equal(4, count)
72
+ end
73
+
74
+ it "raises an error if configured so" do
75
+ tokenizer = HexaPDF::Tokenizer.new(StringIO.new("inf"), on_correctable_error: proc { true })
76
+ assert_raises(HexaPDF::MalformedPDFError) { tokenizer.next_object }
77
+ end
78
+ end
79
+ end
58
80
  end
@@ -40,7 +40,7 @@ describe HexaPDF::Writer do
40
40
  219
41
41
  %%EOF
42
42
  3 0 obj
43
- <</Producer(HexaPDF version 0.14.3)>>
43
+ <</Producer(HexaPDF version 0.14.4)>>
44
44
  endobj
45
45
  xref
46
46
  3 1
@@ -72,7 +72,7 @@ describe HexaPDF::Writer do
72
72
  141
73
73
  %%EOF
74
74
  6 0 obj
75
- <</Producer(HexaPDF version 0.14.3)>>
75
+ <</Producer(HexaPDF version 0.14.4)>>
76
76
  endobj
77
77
  2 0 obj
78
78
  <</Length 10>>stream
@@ -71,6 +71,13 @@ describe HexaPDF::Type::XRefStream do
71
71
  assert(section[10, 0].in_use?)
72
72
  assert_equal(250, section[10, 0].pos)
73
73
  end
74
+
75
+ it "fails if there is not enough data available" do
76
+ @obj[:Index] = [1, 2, 10, 4]
77
+ @obj[:W] = [1, 2, 1]
78
+ @obj.stream = "abcd"
79
+ assert_raises(HexaPDF::MalformedPDFError) { @obj.xref_section }
80
+ end
74
81
  end
75
82
 
76
83
  describe "trailer" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hexapdf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.3
4
+ version: 0.14.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Leitner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-16 00:00:00.000000000 Z
11
+ date: 2021-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cmdparse
@@ -267,6 +267,7 @@ files:
267
267
  - lib/hexapdf/filter.rb
268
268
  - lib/hexapdf/filter/ascii85_decode.rb
269
269
  - lib/hexapdf/filter/ascii_hex_decode.rb
270
+ - lib/hexapdf/filter/crypt.rb
270
271
  - lib/hexapdf/filter/encryption.rb
271
272
  - lib/hexapdf/filter/flate_decode.rb
272
273
  - lib/hexapdf/filter/lzw_decode.rb
@@ -507,6 +508,7 @@ files:
507
508
  - test/hexapdf/filter/common.rb
508
509
  - test/hexapdf/filter/test_ascii85_decode.rb
509
510
  - test/hexapdf/filter/test_ascii_hex_decode.rb
511
+ - test/hexapdf/filter/test_crypt.rb
510
512
  - test/hexapdf/filter/test_encryption.rb
511
513
  - test/hexapdf/filter/test_flate_decode.rb
512
514
  - test/hexapdf/filter/test_lzw_decode.rb