hexapdf 0.17.1 → 0.17.2
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 +1024 -0
- data/LICENSE +29 -0
- data/README.md +129 -0
- data/Rakefile +109 -0
- data/agpl-3.0.txt +661 -0
- data/examples/001-hello_world.rb +16 -0
- data/examples/002-graphics.rb +275 -0
- data/examples/003-arcs.rb +50 -0
- data/examples/004-optimizing.rb +23 -0
- data/examples/005-merging.rb +27 -0
- data/examples/006-standard_pdf_fonts.rb +73 -0
- data/examples/007-truetype.rb +42 -0
- data/examples/008-show_char_bboxes.rb +55 -0
- data/examples/009-text_layouter_alignment.rb +47 -0
- data/examples/010-text_layouter_inline_boxes.rb +64 -0
- data/examples/011-text_layouter_line_wrapping.rb +57 -0
- data/examples/012-text_layouter_styling.rb +122 -0
- data/examples/013-text_layouter_shapes.rb +176 -0
- data/examples/014-text_in_polygon.rb +60 -0
- data/examples/015-boxes.rb +76 -0
- data/examples/016-frame_automatic_box_placement.rb +90 -0
- data/examples/017-frame_text_flow.rb +60 -0
- data/examples/018-composer.rb +44 -0
- data/examples/019-acro_form.rb +88 -0
- data/examples/emoji-smile.png +0 -0
- data/examples/emoji-wink.png +0 -0
- data/examples/machupicchu.jpg +0 -0
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +66 -0
- data/lib/hexapdf/content/graphic_object/geom2d.rb +13 -0
- data/lib/hexapdf/version.rb +1 -1
- data/test/data/aes-test-vectors/CBCGFSbox-128-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-128-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-192-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-192-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-256-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-256-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-128-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-128-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-192-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-192-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-256-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-256-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-128-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-128-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-192-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-192-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-256-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-256-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-128-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-128-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-192-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-192-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-256-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-256-encrypt.data.gz +0 -0
- data/test/data/fonts/Ubuntu-Title.ttf +0 -0
- data/test/data/images/cmyk.jpg +0 -0
- data/test/data/images/fillbytes.jpg +0 -0
- data/test/data/images/gray.jpg +0 -0
- data/test/data/images/greyscale-1bit.png +0 -0
- data/test/data/images/greyscale-2bit.png +0 -0
- data/test/data/images/greyscale-4bit.png +0 -0
- data/test/data/images/greyscale-8bit.png +0 -0
- data/test/data/images/greyscale-alpha-8bit.png +0 -0
- data/test/data/images/greyscale-trns-8bit.png +0 -0
- data/test/data/images/greyscale-with-gamma1.0.png +0 -0
- data/test/data/images/greyscale-with-gamma1.5.png +0 -0
- data/test/data/images/indexed-1bit.png +0 -0
- data/test/data/images/indexed-2bit.png +0 -0
- data/test/data/images/indexed-4bit.png +0 -0
- data/test/data/images/indexed-8bit.png +0 -0
- data/test/data/images/indexed-alpha-4bit.png +0 -0
- data/test/data/images/indexed-alpha-8bit.png +0 -0
- data/test/data/images/rgb.jpg +0 -0
- data/test/data/images/truecolour-8bit.png +0 -0
- data/test/data/images/truecolour-alpha-8bit.png +0 -0
- data/test/data/images/truecolour-gama-chrm-8bit.png +0 -0
- data/test/data/images/truecolour-srgb-8bit.png +0 -0
- data/test/data/images/ycck.jpg +0 -0
- data/test/data/minimal.pdf +44 -0
- data/test/data/standard-security-handler/README +9 -0
- data/test/data/standard-security-handler/bothpwd-aes-128bit-V4.pdf +44 -0
- data/test/data/standard-security-handler/bothpwd-aes-256bit-V5.pdf +0 -0
- data/test/data/standard-security-handler/bothpwd-arc4-128bit-V2.pdf +43 -0
- data/test/data/standard-security-handler/bothpwd-arc4-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/bothpwd-arc4-40bit-V1.pdf +0 -0
- data/test/data/standard-security-handler/nopwd-aes-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/nopwd-aes-256bit-V5.pdf +0 -0
- data/test/data/standard-security-handler/nopwd-arc4-128bit-V2.pdf +43 -0
- data/test/data/standard-security-handler/nopwd-arc4-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/nopwd-arc4-40bit-V1.pdf +43 -0
- data/test/data/standard-security-handler/ownerpwd-aes-128bit-V4.pdf +0 -0
- data/test/data/standard-security-handler/ownerpwd-aes-256bit-V5.pdf +43 -0
- data/test/data/standard-security-handler/ownerpwd-arc4-128bit-V2.pdf +43 -0
- data/test/data/standard-security-handler/ownerpwd-arc4-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/ownerpwd-arc4-40bit-V1.pdf +43 -0
- data/test/data/standard-security-handler/userpwd-aes-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/userpwd-aes-256bit-V5.pdf +43 -0
- data/test/data/standard-security-handler/userpwd-arc4-128bit-V2.pdf +0 -0
- data/test/data/standard-security-handler/userpwd-arc4-128bit-V4.pdf +0 -0
- data/test/data/standard-security-handler/userpwd-arc4-40bit-V1.pdf +43 -0
- data/test/hexapdf/common_tokenizer_tests.rb +236 -0
- data/test/hexapdf/content/common.rb +39 -0
- data/test/hexapdf/content/graphic_object/test_arc.rb +102 -0
- data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +90 -0
- data/test/hexapdf/content/graphic_object/test_geom2d.rb +79 -0
- data/test/hexapdf/content/graphic_object/test_solid_arc.rb +86 -0
- data/test/hexapdf/content/test_canvas.rb +1279 -0
- data/test/hexapdf/content/test_color_space.rb +176 -0
- data/test/hexapdf/content/test_graphics_state.rb +151 -0
- data/test/hexapdf/content/test_operator.rb +619 -0
- data/test/hexapdf/content/test_parser.rb +99 -0
- data/test/hexapdf/content/test_processor.rb +163 -0
- data/test/hexapdf/content/test_transformation_matrix.rb +64 -0
- data/test/hexapdf/document/test_files.rb +72 -0
- data/test/hexapdf/document/test_fonts.rb +60 -0
- data/test/hexapdf/document/test_images.rb +72 -0
- data/test/hexapdf/document/test_pages.rb +130 -0
- data/test/hexapdf/encryption/common.rb +87 -0
- data/test/hexapdf/encryption/test_aes.rb +129 -0
- data/test/hexapdf/encryption/test_arc4.rb +39 -0
- data/test/hexapdf/encryption/test_fast_aes.rb +17 -0
- data/test/hexapdf/encryption/test_fast_arc4.rb +12 -0
- data/test/hexapdf/encryption/test_identity.rb +21 -0
- data/test/hexapdf/encryption/test_ruby_aes.rb +23 -0
- data/test/hexapdf/encryption/test_ruby_arc4.rb +20 -0
- data/test/hexapdf/encryption/test_security_handler.rb +380 -0
- data/test/hexapdf/encryption/test_standard_security_handler.rb +322 -0
- data/test/hexapdf/filter/common.rb +53 -0
- data/test/hexapdf/filter/test_ascii85_decode.rb +59 -0
- data/test/hexapdf/filter/test_ascii_hex_decode.rb +38 -0
- data/test/hexapdf/filter/test_crypt.rb +21 -0
- data/test/hexapdf/filter/test_encryption.rb +24 -0
- data/test/hexapdf/filter/test_flate_decode.rb +44 -0
- data/test/hexapdf/filter/test_lzw_decode.rb +52 -0
- data/test/hexapdf/filter/test_predictor.rb +219 -0
- data/test/hexapdf/filter/test_run_length_decode.rb +32 -0
- data/test/hexapdf/font/cmap/test_parser.rb +102 -0
- data/test/hexapdf/font/cmap/test_writer.rb +66 -0
- data/test/hexapdf/font/encoding/test_base.rb +45 -0
- data/test/hexapdf/font/encoding/test_difference_encoding.rb +29 -0
- data/test/hexapdf/font/encoding/test_glyph_list.rb +59 -0
- data/test/hexapdf/font/encoding/test_zapf_dingbats_encoding.rb +16 -0
- data/test/hexapdf/font/test_cmap.rb +104 -0
- data/test/hexapdf/font/test_encoding.rb +27 -0
- data/test/hexapdf/font/test_invalid_glyph.rb +34 -0
- data/test/hexapdf/font/test_true_type_wrapper.rb +186 -0
- data/test/hexapdf/font/test_type1_wrapper.rb +107 -0
- data/test/hexapdf/font/true_type/common.rb +17 -0
- data/test/hexapdf/font/true_type/table/common.rb +27 -0
- data/test/hexapdf/font/true_type/table/test_cmap.rb +47 -0
- data/test/hexapdf/font/true_type/table/test_cmap_subtable.rb +141 -0
- data/test/hexapdf/font/true_type/table/test_directory.rb +30 -0
- data/test/hexapdf/font/true_type/table/test_glyf.rb +58 -0
- data/test/hexapdf/font/true_type/table/test_head.rb +56 -0
- data/test/hexapdf/font/true_type/table/test_hhea.rb +26 -0
- data/test/hexapdf/font/true_type/table/test_hmtx.rb +30 -0
- data/test/hexapdf/font/true_type/table/test_kern.rb +61 -0
- data/test/hexapdf/font/true_type/table/test_loca.rb +33 -0
- data/test/hexapdf/font/true_type/table/test_maxp.rb +50 -0
- data/test/hexapdf/font/true_type/table/test_name.rb +76 -0
- data/test/hexapdf/font/true_type/table/test_os2.rb +55 -0
- data/test/hexapdf/font/true_type/table/test_post.rb +78 -0
- data/test/hexapdf/font/true_type/test_builder.rb +42 -0
- data/test/hexapdf/font/true_type/test_font.rb +116 -0
- data/test/hexapdf/font/true_type/test_optimizer.rb +26 -0
- data/test/hexapdf/font/true_type/test_subsetter.rb +73 -0
- data/test/hexapdf/font/true_type/test_table.rb +48 -0
- data/test/hexapdf/font/type1/common.rb +6 -0
- data/test/hexapdf/font/type1/test_afm_parser.rb +65 -0
- data/test/hexapdf/font/type1/test_font.rb +104 -0
- data/test/hexapdf/font/type1/test_font_metrics.rb +22 -0
- data/test/hexapdf/font/type1/test_pfb_parser.rb +37 -0
- data/test/hexapdf/font_loader/test_from_configuration.rb +43 -0
- data/test/hexapdf/font_loader/test_from_file.rb +36 -0
- data/test/hexapdf/font_loader/test_standard14.rb +33 -0
- data/test/hexapdf/image_loader/test_jpeg.rb +93 -0
- data/test/hexapdf/image_loader/test_pdf.rb +47 -0
- data/test/hexapdf/image_loader/test_png.rb +259 -0
- data/test/hexapdf/layout/test_box.rb +154 -0
- data/test/hexapdf/layout/test_frame.rb +350 -0
- data/test/hexapdf/layout/test_image_box.rb +73 -0
- data/test/hexapdf/layout/test_inline_box.rb +71 -0
- data/test/hexapdf/layout/test_line.rb +206 -0
- data/test/hexapdf/layout/test_style.rb +790 -0
- data/test/hexapdf/layout/test_text_box.rb +140 -0
- data/test/hexapdf/layout/test_text_fragment.rb +375 -0
- data/test/hexapdf/layout/test_text_layouter.rb +758 -0
- data/test/hexapdf/layout/test_text_shaper.rb +62 -0
- data/test/hexapdf/layout/test_width_from_polygon.rb +109 -0
- data/test/hexapdf/task/test_dereference.rb +51 -0
- data/test/hexapdf/task/test_optimize.rb +162 -0
- data/test/hexapdf/test_composer.rb +258 -0
- data/test/hexapdf/test_configuration.rb +93 -0
- data/test/hexapdf/test_data_dir.rb +32 -0
- data/test/hexapdf/test_dictionary.rb +340 -0
- data/test/hexapdf/test_dictionary_fields.rb +269 -0
- data/test/hexapdf/test_document.rb +641 -0
- data/test/hexapdf/test_filter.rb +100 -0
- data/test/hexapdf/test_importer.rb +106 -0
- data/test/hexapdf/test_object.rb +258 -0
- data/test/hexapdf/test_parser.rb +645 -0
- data/test/hexapdf/test_pdf_array.rb +169 -0
- data/test/hexapdf/test_rectangle.rb +73 -0
- data/test/hexapdf/test_reference.rb +50 -0
- data/test/hexapdf/test_revision.rb +188 -0
- data/test/hexapdf/test_revisions.rb +196 -0
- data/test/hexapdf/test_serializer.rb +195 -0
- data/test/hexapdf/test_stream.rb +274 -0
- data/test/hexapdf/test_tokenizer.rb +80 -0
- data/test/hexapdf/test_type.rb +18 -0
- data/test/hexapdf/test_writer.rb +140 -0
- data/test/hexapdf/test_xref_section.rb +61 -0
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +795 -0
- data/test/hexapdf/type/acro_form/test_button_field.rb +308 -0
- data/test/hexapdf/type/acro_form/test_choice_field.rb +220 -0
- data/test/hexapdf/type/acro_form/test_field.rb +259 -0
- data/test/hexapdf/type/acro_form/test_form.rb +357 -0
- data/test/hexapdf/type/acro_form/test_signature_field.rb +38 -0
- data/test/hexapdf/type/acro_form/test_text_field.rb +201 -0
- data/test/hexapdf/type/acro_form/test_variable_text_field.rb +88 -0
- data/test/hexapdf/type/actions/test_launch.rb +24 -0
- data/test/hexapdf/type/actions/test_uri.rb +23 -0
- data/test/hexapdf/type/annotations/test_markup_annotation.rb +22 -0
- data/test/hexapdf/type/annotations/test_text.rb +34 -0
- data/test/hexapdf/type/annotations/test_widget.rb +225 -0
- data/test/hexapdf/type/test_annotation.rb +97 -0
- data/test/hexapdf/type/test_catalog.rb +48 -0
- data/test/hexapdf/type/test_cid_font.rb +61 -0
- data/test/hexapdf/type/test_file_specification.rb +141 -0
- data/test/hexapdf/type/test_font.rb +67 -0
- data/test/hexapdf/type/test_font_descriptor.rb +61 -0
- data/test/hexapdf/type/test_font_simple.rb +176 -0
- data/test/hexapdf/type/test_font_true_type.rb +31 -0
- data/test/hexapdf/type/test_font_type0.rb +120 -0
- data/test/hexapdf/type/test_font_type1.rb +142 -0
- data/test/hexapdf/type/test_font_type3.rb +26 -0
- data/test/hexapdf/type/test_form.rb +120 -0
- data/test/hexapdf/type/test_image.rb +261 -0
- data/test/hexapdf/type/test_info.rb +9 -0
- data/test/hexapdf/type/test_object_stream.rb +117 -0
- data/test/hexapdf/type/test_page.rb +598 -0
- data/test/hexapdf/type/test_page_tree_node.rb +315 -0
- data/test/hexapdf/type/test_resources.rb +209 -0
- data/test/hexapdf/type/test_trailer.rb +116 -0
- data/test/hexapdf/type/test_xref_stream.rb +143 -0
- data/test/hexapdf/utils/test_bit_field.rb +63 -0
- data/test/hexapdf/utils/test_bit_stream.rb +69 -0
- data/test/hexapdf/utils/test_graphics_helpers.rb +37 -0
- data/test/hexapdf/utils/test_lru_cache.rb +22 -0
- data/test/hexapdf/utils/test_object_hash.rb +120 -0
- data/test/hexapdf/utils/test_pdf_doc_encoding.rb +18 -0
- data/test/hexapdf/utils/test_sorted_tree_node.rb +239 -0
- data/test/test_helper.rb +58 -0
- metadata +263 -3
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'hexapdf/encryption/standard_security_handler'
|
|
5
|
+
require 'hexapdf/document'
|
|
6
|
+
require 'hexapdf/writer'
|
|
7
|
+
require 'stringio'
|
|
8
|
+
|
|
9
|
+
describe HexaPDF::Encryption::StandardEncryptionDictionary do
|
|
10
|
+
before do
|
|
11
|
+
@document = HexaPDF::Document.new
|
|
12
|
+
@dict = HexaPDF::Encryption::StandardEncryptionDictionary.new({}, document: @document)
|
|
13
|
+
@dict[:Filter] = :Standard
|
|
14
|
+
@dict[:V] = 1
|
|
15
|
+
@dict[:R] = 2
|
|
16
|
+
@dict[:U] = 'test' * 8
|
|
17
|
+
@dict[:O] = 'test' * 8
|
|
18
|
+
@dict[:P] = -5
|
|
19
|
+
@dict[:UE] = 'test' * 8
|
|
20
|
+
@dict[:OE] = 'test' * 8
|
|
21
|
+
@dict[:Perms] = 'test' * 8
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "validates the /R value" do
|
|
25
|
+
@dict[:R] = 2
|
|
26
|
+
assert(@dict.validate)
|
|
27
|
+
@dict[:R] = 5
|
|
28
|
+
refute(@dict.validate)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
[:U, :O].each do |field|
|
|
32
|
+
it "validates the length of /#{field} field for R <= 4" do
|
|
33
|
+
@dict[field] = 'test'
|
|
34
|
+
refute(@dict.validate)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
[:U, :O, :UE, :OE, :Perms].each do |field|
|
|
39
|
+
it "validates the length of /#{field} field for R=6" do
|
|
40
|
+
@dict[:R] = 6
|
|
41
|
+
@dict[field] = 'test'
|
|
42
|
+
refute(@dict.validate)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
[:UE, :OE, :Perms].each do |field|
|
|
47
|
+
it "validates the existence of the /#{field} field for R=6" do
|
|
48
|
+
@dict[:R] = 6
|
|
49
|
+
@dict.delete(field)
|
|
50
|
+
refute(@dict.validate)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe HexaPDF::Encryption::StandardSecurityHandler do
|
|
56
|
+
test_files = Dir[File.join(TEST_DATA_DIR, 'standard-security-handler', '*.pdf')].sort
|
|
57
|
+
user_password = 'uhexapdf'
|
|
58
|
+
owner_password = 'ohexapdf'
|
|
59
|
+
|
|
60
|
+
minimal_doc = HexaPDF::Document.new(io: StringIO.new(MINIMAL_PDF))
|
|
61
|
+
|
|
62
|
+
test_files.each do |file|
|
|
63
|
+
basename = File.basename(file)
|
|
64
|
+
it "can decrypt, encrypt and decrypt the encrypted file #{basename} with the user password" do
|
|
65
|
+
begin
|
|
66
|
+
doc = HexaPDF::Document.new(io: StringIO.new(File.binread(file)),
|
|
67
|
+
decryption_opts: {password: user_password})
|
|
68
|
+
assert_equal(minimal_doc.trailer[:Info][:ModDate], doc.trailer[:Info][:ModDate])
|
|
69
|
+
|
|
70
|
+
out = StringIO.new(''.b)
|
|
71
|
+
HexaPDF::Writer.new(doc, out).write
|
|
72
|
+
doc = HexaPDF::Document.new(io: out, decryption_opts: {password: user_password})
|
|
73
|
+
assert_equal(minimal_doc.trailer[:Info][:ModDate], doc.trailer[:Info][:ModDate])
|
|
74
|
+
rescue HexaPDF::EncryptionError => e
|
|
75
|
+
flunk("Error processing #{basename}: #{e}")
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
unless basename.start_with?("userpwd")
|
|
80
|
+
it "can decrypt the encrypted file #{basename} with the owner password" do
|
|
81
|
+
begin
|
|
82
|
+
doc = HexaPDF::Document.new(io: StringIO.new(File.binread(file)),
|
|
83
|
+
decryption_opts: {password: owner_password})
|
|
84
|
+
assert_equal(minimal_doc.trailer[:Info][:ModDate], doc.trailer[:Info][:ModDate])
|
|
85
|
+
rescue HexaPDF::EncryptionError => e
|
|
86
|
+
flunk("Error processing #{basename}: #{e}")
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
before do
|
|
93
|
+
@document = HexaPDF::Document.new
|
|
94
|
+
@handler = HexaPDF::Encryption::StandardSecurityHandler.new(@document)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "can encrypt and then decrypt with all encryption variations" do
|
|
98
|
+
{arc4: [40, 48, 128], aes: [128, 256]}.each do |algorithm, key_lengths|
|
|
99
|
+
key_lengths.each do |key_length|
|
|
100
|
+
begin
|
|
101
|
+
doc = HexaPDF::Document.new
|
|
102
|
+
doc.encrypt(algorithm: algorithm, key_length: key_length)
|
|
103
|
+
sio = StringIO.new
|
|
104
|
+
doc.write(sio)
|
|
105
|
+
doc = HexaPDF::Document.new(io: sio)
|
|
106
|
+
assert_kind_of(Time, doc.trailer.info[:ModDate], "alg: #{algorithm} #{key_length} bits")
|
|
107
|
+
rescue HexaPDF::Error => e
|
|
108
|
+
flunk("Error using variation: #{algorithm} #{key_length} bits\n" << e.message)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
describe "prepare_encryption" do
|
|
115
|
+
it "returns the encryption dictionary wrapped with a custom class" do
|
|
116
|
+
dict = @handler.set_up_encryption
|
|
117
|
+
assert_kind_of(HexaPDF::Encryption::StandardEncryptionDictionary, dict)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "sets the correct revision independent /Filter value" do
|
|
121
|
+
dict = @handler.set_up_encryption
|
|
122
|
+
assert_equal(:Standard, dict[:Filter])
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it "sets the correct revision independent /P value" do
|
|
126
|
+
dict = @handler.set_up_encryption
|
|
127
|
+
assert_equal(@handler.class::Permissions::ALL | \
|
|
128
|
+
@handler.class::Permissions::RESERVED - 2**32,
|
|
129
|
+
dict[:P])
|
|
130
|
+
dict = @handler.set_up_encryption(permissions: @handler.class::Permissions::COPY_CONTENT)
|
|
131
|
+
assert_equal(@handler.class::Permissions::COPY_CONTENT | \
|
|
132
|
+
@handler.class::Permissions::RESERVED - 2**32,
|
|
133
|
+
dict[:P])
|
|
134
|
+
dict = @handler.set_up_encryption(permissions: [:modify_content, :modify_annotation])
|
|
135
|
+
assert_equal(@handler.class::Permissions::MODIFY_CONTENT | \
|
|
136
|
+
@handler.class::Permissions::MODIFY_ANNOTATION | \
|
|
137
|
+
@handler.class::Permissions::RESERVED - 2**32,
|
|
138
|
+
dict[:P])
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it "sets the correct revision independent /EncryptMetadata value" do
|
|
142
|
+
dict = @handler.set_up_encryption
|
|
143
|
+
assert(dict[:EncryptMetadata])
|
|
144
|
+
dict = @handler.set_up_encryption(encrypt_metadata: false)
|
|
145
|
+
refute(dict[:EncryptMetadata])
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it "sets the correct encryption dictionary values for revision 2 and 3" do
|
|
149
|
+
arc4_assertions = lambda do |d|
|
|
150
|
+
assert_equal(32, d[:U].length)
|
|
151
|
+
assert_equal(32, d[:O].length)
|
|
152
|
+
refute(d.value.key?(:UE))
|
|
153
|
+
refute(d.value.key?(:OE))
|
|
154
|
+
refute(d.value.key?(:Perms))
|
|
155
|
+
end
|
|
156
|
+
dict = @handler.set_up_encryption(key_length: 40, algorithm: :arc4)
|
|
157
|
+
assert_equal(2, dict[:R])
|
|
158
|
+
arc4_assertions.call(dict)
|
|
159
|
+
|
|
160
|
+
dict = @handler.set_up_encryption(key_length: 128, algorithm: :arc4)
|
|
161
|
+
assert_equal(3, dict[:R])
|
|
162
|
+
arc4_assertions.call(dict)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it "sets the correct encryption dictionary values for revisions 4 and 6" do
|
|
166
|
+
crypt_filter = lambda do |d, r, alg, length|
|
|
167
|
+
assert_equal(r, d[:R])
|
|
168
|
+
assert_equal(alg == :AESV3 ? 48 : 32, d[:U].length)
|
|
169
|
+
assert_equal(alg == :AESV3 ? 48 : 32, d[:O].length)
|
|
170
|
+
assert_equal({CFM: alg, Length: length, AuthEvent: :DocOpen}, d[:CF][:StdCF])
|
|
171
|
+
assert_equal(:StdCF, d[:StrF])
|
|
172
|
+
assert_equal(:StdCF, d[:StmF])
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
dict = @handler.set_up_encryption(key_length: 128, algorithm: :arc4, force_v4: true)
|
|
176
|
+
refute(dict.value.key?(:UE))
|
|
177
|
+
refute(dict.value.key?(:OE))
|
|
178
|
+
refute(dict.value.key?(:Perms))
|
|
179
|
+
crypt_filter.call(dict, 4, :V2, 16)
|
|
180
|
+
|
|
181
|
+
dict = @handler.set_up_encryption(key_length: 128, algorithm: :aes)
|
|
182
|
+
refute(dict.value.key?(:UE))
|
|
183
|
+
refute(dict.value.key?(:OE))
|
|
184
|
+
refute(dict.value.key?(:Perms))
|
|
185
|
+
crypt_filter.call(dict, 4, :AESV2, 16)
|
|
186
|
+
|
|
187
|
+
dict = @handler.set_up_encryption(key_length: 256, algorithm: :aes)
|
|
188
|
+
assert_equal(32, dict[:UE].length)
|
|
189
|
+
assert_equal(32, dict[:OE].length)
|
|
190
|
+
assert_equal(16, dict[:Perms].length)
|
|
191
|
+
crypt_filter.call(dict, 6, :AESV3, 32)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it "uses the password keyword as fallback, the user password as owner password if necessary" do
|
|
195
|
+
dict1 = @handler.set_up_encryption(password: 'user', owner_password: 'owner')
|
|
196
|
+
dict2 = @handler.set_up_encryption(password: 'owner', user_password: 'user')
|
|
197
|
+
dict3 = @handler.set_up_encryption(user_password: 'user', owner_password: 'owner')
|
|
198
|
+
dict4 = @handler.set_up_encryption(user_password: 'test', owner_password: 'test')
|
|
199
|
+
dict5 = @handler.set_up_encryption(user_password: 'test')
|
|
200
|
+
|
|
201
|
+
assert_equal(dict1[:U], dict2[:U])
|
|
202
|
+
assert_equal(dict2[:U], dict3[:U])
|
|
203
|
+
assert_equal(dict1[:O], dict2[:O])
|
|
204
|
+
assert_equal(dict2[:O], dict3[:O])
|
|
205
|
+
assert_equal(dict4[:O], dict5[:O])
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
it "fails if the password contains invalid characters" do
|
|
209
|
+
assert_raises(HexaPDF::EncryptionError) { @handler.set_up_encryption(password: 'œ test') }
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
it "fails for unknown keywords" do
|
|
213
|
+
assert_raises(ArgumentError) { @handler.set_up_encryption(unknown: 'test') }
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
describe "prepare_decryption" do
|
|
218
|
+
it "fails if the /Filter value is incorrect" do
|
|
219
|
+
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
|
220
|
+
@handler.set_up_decryption({Filter: :NonStandard, V: 2})
|
|
221
|
+
end
|
|
222
|
+
assert_match(/Invalid \/Filter/i, exp.message)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
it "fails if the /R value is incorrect" do
|
|
226
|
+
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
|
227
|
+
@handler.set_up_decryption({Filter: :Standard, V: 2, R: 5})
|
|
228
|
+
end
|
|
229
|
+
assert_match(/Invalid \/R/i, exp.message)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
it "fails if the ID in the document's trailer is missing although it is needed" do
|
|
233
|
+
exp = assert_raises(HexaPDF::EncryptionError) do
|
|
234
|
+
@handler.set_up_decryption({Filter: :Standard, V: 2, R: 2})
|
|
235
|
+
end
|
|
236
|
+
assert_match(/Document ID/i, exp.message)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
it "fails if the supplied password is invalid" do
|
|
240
|
+
exp = assert_raises(HexaPDF::EncryptionError) do
|
|
241
|
+
@handler.set_up_decryption({Filter: :Standard, V: 2, R: 6, U: 'a' * 48, O: 'a' * 48,
|
|
242
|
+
UE: 'a' * 32, OE: 'a' * 32})
|
|
243
|
+
end
|
|
244
|
+
assert_match(/Invalid password/i, exp.message)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
describe "/Perms field checking" do
|
|
248
|
+
before do
|
|
249
|
+
@dict = @handler.set_up_encryption(key_length: 256, algorithm: :aes)
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
it "fails if the field cannot be decrypted" do
|
|
253
|
+
@dict[:Perms].succ!
|
|
254
|
+
exp = assert_raises(HexaPDF::EncryptionError) { @handler.set_up_decryption(@dict) }
|
|
255
|
+
assert_match(/cannot be decrypted/, exp.message)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
it "fails if the /P field doesn't match" do
|
|
259
|
+
@dict[:P] = 500
|
|
260
|
+
exp = assert_raises(HexaPDF::EncryptionError) { @handler.set_up_decryption(@dict) }
|
|
261
|
+
assert_match(/\/P/, exp.message)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
it "fails if the /EncryptMetadata field doesn't match" do
|
|
265
|
+
@dict[:EncryptMetadata] = false
|
|
266
|
+
exp = assert_raises(HexaPDF::EncryptionError) { @handler.set_up_decryption(@dict) }
|
|
267
|
+
assert_match(/\/EncryptMetadata/, exp.message)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
it "ignores the /Perms when requested" do
|
|
271
|
+
obj = HexaPDF::Object.new(nil, oid: 1)
|
|
272
|
+
obj.value = @handler.encrypt_string('test', obj)
|
|
273
|
+
|
|
274
|
+
@dict[:P] = 500
|
|
275
|
+
@handler.set_up_decryption(@dict, check_permissions: false)
|
|
276
|
+
assert_equal('test', @handler.decrypt(obj).value)
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
it "encryption key stays valid even if default dict values are set while setting up decryption" do
|
|
282
|
+
@document.encrypt(key_length: 128, algorithm: :aes)
|
|
283
|
+
assert(@document.security_handler.encryption_key_valid?)
|
|
284
|
+
|
|
285
|
+
@document.trailer[:Encrypt].delete(:EncryptMetadata)
|
|
286
|
+
handler = HexaPDF::Encryption::SecurityHandler.set_up_decryption(@document)
|
|
287
|
+
assert(handler.encryption_key_valid?)
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
it "returns an array of permission symbols" do
|
|
291
|
+
perms = @handler.class::Permissions::MODIFY_CONTENT | @handler.class::Permissions::COPY_CONTENT
|
|
292
|
+
@handler.set_up_encryption(permissions: perms)
|
|
293
|
+
assert_equal([:copy_content, :modify_content], @handler.permissions.sort)
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
describe "handling of metadata streams" do
|
|
297
|
+
before do
|
|
298
|
+
@doc = HexaPDF::Document.new
|
|
299
|
+
@output = StringIO.new(''.b)
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
it "doesn't decrypt or encrypt a metadata stream if /EncryptMetadata is false" do
|
|
303
|
+
@doc.encrypt(encrypt_metadata: false)
|
|
304
|
+
@doc.catalog[:Metadata] = @doc.wrap({Type: :Metadata, Subtype: :XML}, stream: "HELLODATA")
|
|
305
|
+
@doc.write(@output)
|
|
306
|
+
assert_match(/stream\nHELLODATA\nendstream/, @output.string)
|
|
307
|
+
|
|
308
|
+
doc = HexaPDF::Document.new(io: @output)
|
|
309
|
+
assert_equal('HELLODATA', doc.catalog[:Metadata].stream)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
it "doesn't modify decryption/encryption for metadata streams if /V is not 4 or 5" do
|
|
313
|
+
@doc.encrypt(encrypt_metadata: false, algorithm: :arc4)
|
|
314
|
+
@doc.catalog[:Metadata] = @doc.wrap({Type: :Metadata, Subtype: :XML}, stream: "HELLODATA")
|
|
315
|
+
@doc.write(@output)
|
|
316
|
+
refute_match(/stream\nHELLODATA\nendstream/, @output.string)
|
|
317
|
+
|
|
318
|
+
doc = HexaPDF::Document.new(io: @output)
|
|
319
|
+
assert_equal('HELLODATA', doc.catalog[:Metadata].stream)
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
|
|
5
|
+
# Provides common tests for all filter implementations.
|
|
6
|
+
#
|
|
7
|
+
# The filter object needs to be available in the @obj variable and the @all_test_cases variable
|
|
8
|
+
# needs to hold an array of test cases, i.e. [decoded, encoded] objects.
|
|
9
|
+
module CommonFilterTests
|
|
10
|
+
include TestHelper
|
|
11
|
+
|
|
12
|
+
TEST_BIG_STR = ''.b
|
|
13
|
+
TEST_BIG_STR << [rand(2**32)].pack('N') while TEST_BIG_STR.length < 2**16
|
|
14
|
+
TEST_BIG_STR.freeze
|
|
15
|
+
|
|
16
|
+
def test_decodes_correctly
|
|
17
|
+
@all_test_cases.each_with_index do |(result, str), index|
|
|
18
|
+
assert_equal(result, collector(@obj.decoder(feeder(str.dup))), "testcase #{index}")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_encodes_correctly
|
|
23
|
+
@all_test_cases.each_with_index do |(str, result), index|
|
|
24
|
+
assert_equal(result, collector(@obj.encoder(feeder(str.dup))), "testcase #{index}")
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_works_with_big_data
|
|
29
|
+
assert_equal(TEST_BIG_STR, collector(@obj.decoder(@obj.encoder(feeder(TEST_BIG_STR.dup)))))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_decoder_returns_strings_in_binary_encoding
|
|
33
|
+
assert_encodings(@obj.decoder(@obj.encoder(feeder('some test data', 1))), "decoder")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def test_encoder_returns_strings_in_binary_encoding
|
|
37
|
+
assert_encodings(@obj.encoder(feeder('some test data', 1)), "encoder")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def assert_encodings(source, type)
|
|
41
|
+
while source.alive? && (data = source.resume)
|
|
42
|
+
assert_equal(Encoding::BINARY, data.encoding, "encoding problem in #{type}")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def test_decoder_works_with_single_byte_input
|
|
47
|
+
assert_equal(@decoded, collector(@obj.decoder(feeder(@encoded.dup, 1))))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_encoder_works_with_single_byte_input
|
|
51
|
+
assert_equal(@encoded, collector(@obj.encoder(feeder(@decoded.dup, 1))))
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require_relative 'common'
|
|
4
|
+
require 'hexapdf/filter/ascii85_decode'
|
|
5
|
+
|
|
6
|
+
describe HexaPDF::Filter::ASCII85Decode do
|
|
7
|
+
include CommonFilterTests
|
|
8
|
+
|
|
9
|
+
before do
|
|
10
|
+
@obj = HexaPDF::Filter::ASCII85Decode
|
|
11
|
+
@all_test_cases ||= [['Nov shmoz ka pop.', ':2b:uF(fE/H6@!3+E27</c~>'],
|
|
12
|
+
['Nov shmoz ka pop.1', ':2b:uF(fE/H6@!3+E27</hm~>'],
|
|
13
|
+
['Nov shmoz ka pop.12', ':2b:uF(fE/H6@!3+E27</ho*~>'],
|
|
14
|
+
['Nov shmoz ka pop.123', ':2b:uF(fE/H6@!3+E27</ho+;~>'],
|
|
15
|
+
["\0\0\0\0Nov shmoz ka pop.", 'z:2b:uF(fE/H6@!3+E27</c~>'],
|
|
16
|
+
["Nov \x0\x0\x0\x0shmoz ka pop.", ':2b:uzF(fE/H6@!3+E27</c~>']]
|
|
17
|
+
@decoded = @all_test_cases[0][0]
|
|
18
|
+
@encoded = @all_test_cases[0][1]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe "decoder" do
|
|
22
|
+
it "works with single byte input on specially crated input" do
|
|
23
|
+
assert_equal("Nov \0\0\0", collector(@obj.decoder(feeder(':2b:u!!!!~>', 1))))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "ignores whitespace in the input" do
|
|
27
|
+
encoded = @encoded.dup.scan(/./).map {|a| "#{a} \r\t" }.join("\n")
|
|
28
|
+
assert_equal(@decoded, collector(@obj.decoder(feeder(encoded))))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "works without the EOD marker" do
|
|
32
|
+
assert_equal(@decoded, collector(@obj.decoder(feeder(@encoded.sub(/~>/, '')))))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "ignores data after the EOD marker" do
|
|
36
|
+
assert_equal(@decoded, collector(@obj.decoder(feeder(@encoded << "~>abcdefg"))))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "fails if the input contains invalid characters" do
|
|
40
|
+
assert_raises(HexaPDF::FilterError) { collector(@obj.decoder(feeder(':2bwx!'))) }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "fails if the input contains values outside the BASE85 range" do
|
|
44
|
+
assert_raises(HexaPDF::FilterError) { collector(@obj.decoder(feeder('uuuuu'))) }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "fails if the last rest contains a 'z' character" do
|
|
48
|
+
assert_raises(HexaPDF::FilterError) { collector(@obj.decoder(feeder('uuz'))) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "fails if the last rest contains a '~' character" do
|
|
52
|
+
assert_raises(HexaPDF::FilterError) { collector(@obj.decoder(feeder('uu~'))) }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "fails if the last rest contains values outside the BASE85 range" do
|
|
56
|
+
assert_raises(HexaPDF::FilterError) { collector(@obj.decoder(feeder('uuu>', 1))) }
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require_relative 'common'
|
|
4
|
+
require 'hexapdf/filter/ascii_hex_decode'
|
|
5
|
+
|
|
6
|
+
describe HexaPDF::Filter::ASCIIHexDecode do
|
|
7
|
+
include CommonFilterTests
|
|
8
|
+
|
|
9
|
+
before do
|
|
10
|
+
@obj = HexaPDF::Filter::ASCIIHexDecode
|
|
11
|
+
@all_test_cases = [['Nov shmoz ka pop.', '4e6f762073686d6f7a206b6120706f702e>']]
|
|
12
|
+
@decoded = @all_test_cases[0][0]
|
|
13
|
+
@encoded = @all_test_cases[0][1]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
describe "decoder" do
|
|
17
|
+
it "ignores whitespace in the input" do
|
|
18
|
+
with_whitespace = @encoded.scan(/./).map {|a| "#{a} \r\t" }.join("\n")
|
|
19
|
+
assert_equal(@decoded, collector(@obj.decoder(feeder(with_whitespace, 1))))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "works without the EOD marker" do
|
|
23
|
+
assert_equal(@decoded, collector(@obj.decoder(feeder(@encoded.chop, 5))))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "ignores data after the EOD marker" do
|
|
27
|
+
assert_equal(@decoded, collector(@obj.decoder(feeder(@encoded << '4e6f7gzz'))))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "assumes the missing char is '0' if the input length is odd" do
|
|
31
|
+
assert_equal(@decoded.chop << ' ', collector(@obj.decoder(feeder(@encoded.chop.chop))))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "fails on invalid characters" do
|
|
35
|
+
assert_raises(HexaPDF::FilterError) { @obj.decoder(feeder('f0f0z')).resume }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -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
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require_relative 'common'
|
|
4
|
+
require 'hexapdf/filter/encryption'
|
|
5
|
+
|
|
6
|
+
describe HexaPDF::Filter::Encryption do
|
|
7
|
+
before do
|
|
8
|
+
@obj = HexaPDF::Filter::Encryption
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "returns the correct decryption fiber" do
|
|
12
|
+
algorithm = Minitest::Mock.new
|
|
13
|
+
algorithm.expect(:decryption_fiber, :fiber, [:key, :source])
|
|
14
|
+
assert_equal(:fiber, @obj.decoder(:source, key: :key, algorithm: algorithm))
|
|
15
|
+
algorithm.verify
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "returns the correct encryption fiber" do
|
|
19
|
+
algorithm = Minitest::Mock.new
|
|
20
|
+
algorithm.expect(:encryption_fiber, :fiber, [:key, :source])
|
|
21
|
+
assert_equal(:fiber, @obj.encoder(:source, key: :key, algorithm: algorithm))
|
|
22
|
+
algorithm.verify
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require_relative 'common'
|
|
4
|
+
require 'hexapdf/filter/flate_decode'
|
|
5
|
+
|
|
6
|
+
describe HexaPDF::Filter::FlateDecode do
|
|
7
|
+
include CommonFilterTests
|
|
8
|
+
|
|
9
|
+
before do
|
|
10
|
+
@obj = HexaPDF::Filter::FlateDecode
|
|
11
|
+
@all_test_cases = [["abcdefg".force_encoding(Encoding::BINARY),
|
|
12
|
+
"x\xDAKLJNIMK\a\x00\n\xDB\x02\xBD".force_encoding(Encoding::BINARY)]]
|
|
13
|
+
@decoded = @all_test_cases[0][0]
|
|
14
|
+
@encoded = @all_test_cases[0][1]
|
|
15
|
+
@encoded_predictor = "x\xDAcJdbD@\x00\x05\x8F\x00v".force_encoding(Encoding::BINARY)
|
|
16
|
+
@predictor_opts = {Predictor: 12}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
describe "decoder" do
|
|
20
|
+
it "works for empty input" do
|
|
21
|
+
assert_equal('', collector(@obj.decoder(Fiber.new { "" })))
|
|
22
|
+
assert_equal('', collector(@obj.decoder(Fiber.new {})))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "applies the Predictor after decoding" do
|
|
26
|
+
assert_equal(@decoded, collector(@obj.decoder(feeder(@encoded_predictor), @predictor_opts)))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "fails on invalid input" do
|
|
30
|
+
assert_raises(HexaPDF::FilterError) do
|
|
31
|
+
collector(@obj.decoder(feeder(@encoded[0..-2], @encoded.length - 3)))
|
|
32
|
+
end
|
|
33
|
+
assert_raises(HexaPDF::FilterError) do
|
|
34
|
+
collector(@obj.decoder(feeder("some data")))
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe "encoder" do
|
|
40
|
+
it "applies the Predictor before encoding" do
|
|
41
|
+
assert_equal(@encoded_predictor, collector(@obj.encoder(feeder(@decoded), @predictor_opts)))
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require_relative 'common'
|
|
4
|
+
require 'hexapdf/filter/lzw_decode'
|
|
5
|
+
|
|
6
|
+
describe HexaPDF::Filter::LZWDecode do
|
|
7
|
+
include CommonFilterTests
|
|
8
|
+
|
|
9
|
+
before do
|
|
10
|
+
@obj = HexaPDF::Filter::LZWDecode
|
|
11
|
+
@all_test_cases ||= [["-----A---B".b, "\x80\x0b\x60\x50\x22\x0c\x0c\x85\x01".b],
|
|
12
|
+
['abcabcaaaabbbcdeffffffagggggg'.b,
|
|
13
|
+
"\x80\x18LF8\x14\x10\xC3\a1BLfC)\x9A\x1D\x0F0\x99\xE2Q8\b".b]]
|
|
14
|
+
@decoded = @all_test_cases[0][0]
|
|
15
|
+
@encoded = @all_test_cases[0][1]
|
|
16
|
+
@encoded_predictor = "\x80\x00\x85\xA0 \x04\x12\r\x05\n\x00\x9D\x90p\x10V\x02".b
|
|
17
|
+
@predictor_opts = {Predictor: 12}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe "decoder" do
|
|
21
|
+
it "applies the Predictor after decoding" do
|
|
22
|
+
assert_equal(@decoded, collector(@obj.decoder(feeder(@encoded_predictor), @predictor_opts)))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "fails if an unknown code is found after CLEAR_TABLE" do
|
|
26
|
+
assert_raises(HexaPDF::FilterError) { @obj.decoder(feeder("\xff\xff")).resume }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "fails if an unknown code is found elsewhere" do
|
|
30
|
+
assert_raises(HexaPDF::FilterError) { @obj.decoder(feeder("\x00\x7f\xff\xf0")).resume }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "fails if the code size would be more than 12bit" do
|
|
34
|
+
stream = HexaPDF::Utils::BitStreamWriter.new
|
|
35
|
+
result = stream.write(256, 9)
|
|
36
|
+
result << stream.write(65, 9)
|
|
37
|
+
258.upto(510) {|i| result << stream.write(i, 9) }
|
|
38
|
+
511.upto(1022) {|i| result << stream.write(i, 10) }
|
|
39
|
+
1023.upto(2046) {|i| result << stream.write(i, 11) }
|
|
40
|
+
2047.upto(4095) {|i| result << stream.write(i, 12) }
|
|
41
|
+
result << stream.write(96, 12)
|
|
42
|
+
result << stream.finalize
|
|
43
|
+
assert_raises(HexaPDF::FilterError) { @obj.decoder(feeder(result)).resume }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe "encoder" do
|
|
48
|
+
it "applies the Predictor before encoding" do
|
|
49
|
+
assert_equal(@encoded_predictor, collector(@obj.encoder(feeder(@decoded), @predictor_opts)))
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|