hexapdf 0.46.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +83 -16
- data/lib/hexapdf/composer.rb +7 -0
- data/lib/hexapdf/configuration.rb +13 -0
- data/lib/hexapdf/content/parser.rb +3 -1
- data/lib/hexapdf/digital_signature/cms_handler.rb +13 -0
- data/lib/hexapdf/digital_signature/signature.rb +1 -1
- data/lib/hexapdf/digital_signature/signing/default_handler.rb +1 -0
- data/lib/hexapdf/document.rb +14 -3
- data/lib/hexapdf/encryption/standard_security_handler.rb +32 -26
- data/lib/hexapdf/font/cmap/writer.rb +58 -4
- data/lib/hexapdf/font/cmap.rb +7 -0
- data/lib/hexapdf/font/true_type_wrapper.rb +41 -16
- data/lib/hexapdf/importer.rb +1 -1
- data/lib/hexapdf/layout/table_box.rb +57 -10
- data/lib/hexapdf/layout/text_fragment.rb +2 -1
- data/lib/hexapdf/object.rb +1 -1
- data/lib/hexapdf/parser.rb +1 -1
- data/lib/hexapdf/reference.rb +1 -1
- data/lib/hexapdf/task/merge_acro_form.rb +164 -0
- data/lib/hexapdf/task/optimize.rb +4 -4
- data/lib/hexapdf/task.rb +1 -0
- data/lib/hexapdf/tokenizer.rb +2 -0
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +8 -4
- data/lib/hexapdf/type/acro_form/form.rb +14 -24
- data/lib/hexapdf/type/acro_form/signature_field.rb +18 -7
- data/lib/hexapdf/type/acro_form/variable_text_field.rb +12 -4
- data/lib/hexapdf/type/actions/go_to.rb +1 -0
- data/lib/hexapdf/type/actions/go_to_r.rb +1 -0
- data/lib/hexapdf/type/actions/launch.rb +5 -1
- data/lib/hexapdf/type/annotation.rb +6 -1
- data/lib/hexapdf/type/annotations/markup_annotation.rb +14 -1
- data/lib/hexapdf/type/annotations/widget.rb +4 -2
- data/lib/hexapdf/type/catalog.rb +3 -0
- data/lib/hexapdf/type/cid_font.rb +4 -1
- data/lib/hexapdf/type/file_specification.rb +17 -14
- data/lib/hexapdf/type/font_descriptor.rb +4 -3
- data/lib/hexapdf/type/font_simple.rb +3 -1
- data/lib/hexapdf/type/font_true_type.rb +2 -0
- data/lib/hexapdf/type/font_type0.rb +1 -1
- data/lib/hexapdf/type/font_type1.rb +7 -0
- data/lib/hexapdf/type/font_type3.rb +0 -1
- data/lib/hexapdf/type/form.rb +5 -2
- data/lib/hexapdf/type/graphics_state_parameter.rb +7 -4
- data/lib/hexapdf/type/image.rb +8 -4
- data/lib/hexapdf/type/info.rb +2 -2
- data/lib/hexapdf/type/mark_information.rb +2 -2
- data/lib/hexapdf/type/optional_content_configuration.rb +1 -1
- data/lib/hexapdf/type/optional_content_membership.rb +1 -1
- data/lib/hexapdf/type/page.rb +5 -3
- data/lib/hexapdf/type/resources.rb +6 -6
- data/lib/hexapdf/type/viewer_preferences.rb +4 -3
- data/lib/hexapdf/version.rb +1 -1
- data/lib/hexapdf/writer.rb +1 -0
- data/test/data/standard-security-handler/bothpwd-aes-256bit-V5-R5.pdf +43 -0
- data/test/data/standard-security-handler/nopwd-aes-256bit-V5-R5.pdf +44 -0
- data/test/data/standard-security-handler/ownerpwd-aes-256bit-V5-R5.pdf +43 -0
- data/test/data/standard-security-handler/userpwd-aes-256bit-V5-R5.pdf +0 -0
- data/test/hexapdf/common_tokenizer_tests.rb +5 -0
- data/test/hexapdf/digital_signature/signing/test_default_handler.rb +6 -0
- data/test/hexapdf/digital_signature/test_cms_handler.rb +12 -7
- data/test/hexapdf/digital_signature/test_signature.rb +7 -0
- data/test/hexapdf/digital_signature/test_signatures.rb +12 -7
- data/test/hexapdf/encryption/test_standard_security_handler.rb +5 -2
- data/test/hexapdf/font/cmap/test_writer.rb +73 -16
- data/test/hexapdf/font/test_true_type_wrapper.rb +17 -3
- data/test/hexapdf/layout/test_list_box.rb +7 -7
- data/test/hexapdf/layout/test_table_box.rb +52 -0
- data/test/hexapdf/layout/test_text_fragment.rb +3 -3
- data/test/hexapdf/layout/test_text_layouter.rb +4 -2
- data/test/hexapdf/task/test_merge_acro_form.rb +104 -0
- data/test/hexapdf/task/test_optimize.rb +2 -0
- data/test/hexapdf/test_composer.rb +8 -0
- data/test/hexapdf/test_document.rb +12 -3
- data/test/hexapdf/test_importer.rb +7 -0
- data/test/hexapdf/test_parser.rb +7 -0
- data/test/hexapdf/test_writer.rb +19 -5
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +40 -23
- data/test/hexapdf/type/acro_form/test_form.rb +7 -8
- data/test/hexapdf/type/acro_form/test_signature_field.rb +3 -1
- data/test/hexapdf/type/acro_form/test_variable_text_field.rb +14 -1
- data/test/hexapdf/type/actions/test_launch.rb +6 -2
- data/test/hexapdf/type/annotations/test_widget.rb +4 -0
- data/test/hexapdf/type/test_font_type1.rb +5 -0
- data/test/hexapdf/type/test_form.rb +1 -1
- data/test/hexapdf/type/test_page.rb +7 -1
- metadata +8 -2
@@ -0,0 +1,43 @@
|
|
1
|
+
%PDF-1.7
|
2
|
+
%����
|
3
|
+
1 0 obj
|
4
|
+
<< /Extensions << /ADBE << /BaseVersion /1.7 /ExtensionLevel 3 >> >> /Pages 3 0 R /Type /Catalog >>
|
5
|
+
endobj
|
6
|
+
2 0 obj
|
7
|
+
<< /ModDate <aa1d1637459ea17c7fc9709f0c0c7b64761ff74e905b3765f33425776aa3010163cd5e898cfa7c5fc16159359375c323> >>
|
8
|
+
endobj
|
9
|
+
3 0 obj
|
10
|
+
<< /Count 1 /Kids [ 4 0 R ] /MediaBox [ 0 0 612 446 ] /Type /Pages >>
|
11
|
+
endobj
|
12
|
+
4 0 obj
|
13
|
+
<< /Contents 5 0 R /Parent 3 0 R /Resources 6 0 R /Type /Page >>
|
14
|
+
endobj
|
15
|
+
5 0 obj
|
16
|
+
<< /Length 80 /Filter /FlateDecode >>
|
17
|
+
stream
|
18
|
+
J<~S�)��9�M�$S�z��g�j���r�.R�"PO���_���X���^�GP�&z�g�*$�2SΈ�z�Kl��5ĩendstream
|
19
|
+
endobj
|
20
|
+
6 0 obj
|
21
|
+
<< /Font << /F1 7 0 R >> /ProcSet [ /PDF /Text ] >>
|
22
|
+
endobj
|
23
|
+
7 0 obj
|
24
|
+
<< /BaseFont /Helvetica /Name /F1 /Subtype /Type1 /Type /Font >>
|
25
|
+
endobj
|
26
|
+
8 0 obj
|
27
|
+
<< /CF << /StdCF << /AuthEvent /DocOpen /CFM /AESV3 /Length 32 >> >> /Filter /Standard /Length 256 /O <c60934c0925e8d3ceebd0609102d9407dcf22d7fb3d87d030fce633af6d2ed99c1c3382bee5e8afc0d55ff8bca441d48> /OE <3fa2e3ecf34ddcb38b44af372a43268dda32111f58dc79da74d960b8fa206ead> /P -4 /Perms <b6c6ab65529f0a3322f03909e8a5547a> /R 5 /StmF /StdCF /StrF /StdCF /U <18fbd94777c28531c495a3116d273de9f8f3ed338b31c07687ca0ba06812842843765a66484d098194bcef0f9ecaaace> /UE <496a81bbb3207dfd12ddca4c16b60a411c6a18e638205824295e09afde826b4a> /V 5 >>
|
28
|
+
endobj
|
29
|
+
xref
|
30
|
+
0 9
|
31
|
+
0000000000 65535 f
|
32
|
+
0000000015 00000 n
|
33
|
+
0000000130 00000 n
|
34
|
+
0000000259 00000 n
|
35
|
+
0000000344 00000 n
|
36
|
+
0000000424 00000 n
|
37
|
+
0000000574 00000 n
|
38
|
+
0000000641 00000 n
|
39
|
+
0000000721 00000 n
|
40
|
+
trailer << /Info 2 0 R /Root 1 0 R /Size 9 /ID [<6790ffa610024e78369114311fc0df96><ed6c0810cb0d19599ac62042a0487749>] /Encrypt 8 0 R >>
|
41
|
+
startxref
|
42
|
+
1268
|
43
|
+
%%EOF
|
@@ -0,0 +1,44 @@
|
|
1
|
+
%PDF-1.7
|
2
|
+
%����
|
3
|
+
1 0 obj
|
4
|
+
<< /Extensions << /ADBE << /BaseVersion /1.7 /ExtensionLevel 3 >> >> /Pages 3 0 R /Type /Catalog >>
|
5
|
+
endobj
|
6
|
+
2 0 obj
|
7
|
+
<< /ModDate <0a09febf75d913e5eff6197f52f28b31321a43f49e0735a25a93dd77b1a2701e73ae05be0803747ca030ee4917544cbb> >>
|
8
|
+
endobj
|
9
|
+
3 0 obj
|
10
|
+
<< /Count 1 /Kids [ 4 0 R ] /MediaBox [ 0 0 612 446 ] /Type /Pages >>
|
11
|
+
endobj
|
12
|
+
4 0 obj
|
13
|
+
<< /Contents 5 0 R /Parent 3 0 R /Resources 6 0 R /Type /Page >>
|
14
|
+
endobj
|
15
|
+
5 0 obj
|
16
|
+
<< /Length 80 /Filter /FlateDecode >>
|
17
|
+
stream
|
18
|
+
\��sy%1����a��
|
19
|
+
�/���F�.�L:pb���C�`��D�5 �� �کU"�ʇ�3�Fy/�9�<V��ڦ;�?5�S�8endstream
|
20
|
+
endobj
|
21
|
+
6 0 obj
|
22
|
+
<< /Font << /F1 7 0 R >> /ProcSet [ /PDF /Text ] >>
|
23
|
+
endobj
|
24
|
+
7 0 obj
|
25
|
+
<< /BaseFont /Helvetica /Name /F1 /Subtype /Type1 /Type /Font >>
|
26
|
+
endobj
|
27
|
+
8 0 obj
|
28
|
+
<< /CF << /StdCF << /AuthEvent /DocOpen /CFM /AESV3 /Length 32 >> >> /Filter /Standard /Length 256 /O <8034f99b1eff9e91054d7ee490155e22f65170f607d6a614236b089e602517d9a22abe7c1ea53f52dbc9ae8d9701a065> /OE <bdc5c4b46519af7b4041b7341f70e4d8b1c6087dc12d3f16f060313f18e73386> /P -4 /Perms <64028d6bceb0d927477ecf45e6be7f3f> /R 5 /StmF /StdCF /StrF /StdCF /U <5db7b7b5bd8bbe9fac28477756a930cb079e1dcbdcd3b50c1817d3de4ce5184b5189848832df0043f8e1fe19ff696a36> /UE <dc3fed7b473148238ba0689409edd3d80c91bd26802003c152594aa27a50a81e> /V 5 >>
|
29
|
+
endobj
|
30
|
+
xref
|
31
|
+
0 9
|
32
|
+
0000000000 65535 f
|
33
|
+
0000000015 00000 n
|
34
|
+
0000000130 00000 n
|
35
|
+
0000000259 00000 n
|
36
|
+
0000000344 00000 n
|
37
|
+
0000000424 00000 n
|
38
|
+
0000000574 00000 n
|
39
|
+
0000000641 00000 n
|
40
|
+
0000000721 00000 n
|
41
|
+
trailer << /Info 2 0 R /Root 1 0 R /Size 9 /ID [<6790ffa610024e78369114311fc0df96><da8e0d03302398724f68ef24a831285e>] /Encrypt 8 0 R >>
|
42
|
+
startxref
|
43
|
+
1268
|
44
|
+
%%EOF
|
@@ -0,0 +1,43 @@
|
|
1
|
+
%PDF-1.7
|
2
|
+
%����
|
3
|
+
1 0 obj
|
4
|
+
<< /Extensions << /ADBE << /BaseVersion /1.7 /ExtensionLevel 3 >> >> /Pages 3 0 R /Type /Catalog >>
|
5
|
+
endobj
|
6
|
+
2 0 obj
|
7
|
+
<< /ModDate <d2aa32b8d87666bd29df499a424d2e6d23c0b475ebb67598733b5f06429470a37062d3d6a32fc9603301c5eb99a20605> >>
|
8
|
+
endobj
|
9
|
+
3 0 obj
|
10
|
+
<< /Count 1 /Kids [ 4 0 R ] /MediaBox [ 0 0 612 446 ] /Type /Pages >>
|
11
|
+
endobj
|
12
|
+
4 0 obj
|
13
|
+
<< /Contents 5 0 R /Parent 3 0 R /Resources 6 0 R /Type /Page >>
|
14
|
+
endobj
|
15
|
+
5 0 obj
|
16
|
+
<< /Length 80 /Filter /FlateDecode >>
|
17
|
+
stream
|
18
|
+
j��D3�(���"�ڛ�������xvY$B\4^Y����$��
|
19
|
+
endobj
|
20
|
+
6 0 obj
|
21
|
+
<< /Font << /F1 7 0 R >> /ProcSet [ /PDF /Text ] >>
|
22
|
+
endobj
|
23
|
+
7 0 obj
|
24
|
+
<< /BaseFont /Helvetica /Name /F1 /Subtype /Type1 /Type /Font >>
|
25
|
+
endobj
|
26
|
+
8 0 obj
|
27
|
+
<< /CF << /StdCF << /AuthEvent /DocOpen /CFM /AESV3 /Length 32 >> >> /Filter /Standard /Length 256 /O <95d20f6277a6955bf4bda243c2144e94889bd5fa4225a4cf4e0d496fa1ffa1d991e1a37d46e4afe9e2dfc207eba9ec53> /OE <432a7d086b4338f1020bbc5847e6dd4f49ce586ab2c5de0f5301450e45e3bb3e> /P -4 /Perms <7df865105074d365f2ce50407e8b6dc8> /R 5 /StmF /StdCF /StrF /StdCF /U <930a631e8b2b95ff6b024e9bde92cb73c5c43f0106ec4fb1c336e49608c0740d87395c6ea79b99ee07eeae5ffbacd031> /UE <3f7eb6b9a897049bfca85a5ae71470eca3dfedbe9101e8532b217e4d95bcf51a> /V 5 >>
|
28
|
+
endobj
|
29
|
+
xref
|
30
|
+
0 9
|
31
|
+
0000000000 65535 f
|
32
|
+
0000000015 00000 n
|
33
|
+
0000000130 00000 n
|
34
|
+
0000000259 00000 n
|
35
|
+
0000000344 00000 n
|
36
|
+
0000000424 00000 n
|
37
|
+
0000000574 00000 n
|
38
|
+
0000000641 00000 n
|
39
|
+
0000000721 00000 n
|
40
|
+
trailer << /Info 2 0 R /Root 1 0 R /Size 9 /ID [<6790ffa610024e78369114311fc0df96><dd2e6bd65a9735c0bef37d88d5291ff1>] /Encrypt 8 0 R >>
|
41
|
+
startxref
|
42
|
+
1268
|
43
|
+
%%EOF
|
Binary file
|
@@ -104,6 +104,11 @@ module CommonTokenizerTests
|
|
104
104
|
assert_raises(HexaPDF::MalformedPDFError) { @tokenizer.next_token }
|
105
105
|
end
|
106
106
|
|
107
|
+
it "next_token: fails on a closing parenthesis that is not part of a literal string" do
|
108
|
+
create_tokenizer(" )")
|
109
|
+
assert_raises(HexaPDF::MalformedPDFError) { @tokenizer.next_token }
|
110
|
+
end
|
111
|
+
|
107
112
|
it "next_token: fails on a missing greater than sign in a hex string" do
|
108
113
|
create_tokenizer("<ABCD")
|
109
114
|
assert_raises(HexaPDF::MalformedPDFError) { @tokenizer.next_token }
|
@@ -157,6 +157,12 @@ describe HexaPDF::DigitalSignature::Signing::DefaultHandler do
|
|
157
157
|
assert_same(@obj, @doc.catalog[:Perms][:DocMDP])
|
158
158
|
end
|
159
159
|
|
160
|
+
it "updates the document version if :pades signing is used" do
|
161
|
+
@handler.signature_type = :pades
|
162
|
+
@handler.finalize_objects(@field, @obj)
|
163
|
+
assert_equal('2.0', @doc.version)
|
164
|
+
end
|
165
|
+
|
160
166
|
it "fails if DocMDP should be set but there is already a signature" do
|
161
167
|
@handler.doc_mdp_permissions = :no_changes
|
162
168
|
2.times do
|
@@ -62,7 +62,7 @@ describe HexaPDF::DigitalSignature::CMSHandler do
|
|
62
62
|
@dict.contents = @pkcs7.to_der
|
63
63
|
@handler = HexaPDF::DigitalSignature::CMSHandler.new(@dict)
|
64
64
|
result = @handler.verify(@store)
|
65
|
-
assert_equal(
|
65
|
+
assert_equal(3, result.messages.size)
|
66
66
|
assert_equal(:error, result.messages.first.type)
|
67
67
|
assert_match(/Exactly one signer needed/, result.messages.first.content)
|
68
68
|
end
|
@@ -100,13 +100,13 @@ describe HexaPDF::DigitalSignature::CMSHandler do
|
|
100
100
|
|
101
101
|
it "verifies the signature itself" do
|
102
102
|
result = @handler.verify(@store)
|
103
|
-
assert_equal(:info, result.messages.
|
104
|
-
assert_match(/Signature valid/, result.messages.
|
103
|
+
assert_equal(:info, result.messages[-2].type)
|
104
|
+
assert_match(/Signature valid/, result.messages[-2].content)
|
105
105
|
|
106
106
|
@dict.signed_data = 'other data'
|
107
107
|
result = @handler.verify(@store)
|
108
|
-
assert_equal(:error, result.messages.
|
109
|
-
assert_match(/Signature verification failed/, result.messages.
|
108
|
+
assert_equal(:error, result.messages[-2].type)
|
109
|
+
assert_match(/Signature verification failed/, result.messages[-2].content)
|
110
110
|
end
|
111
111
|
|
112
112
|
it "verifies a timestamp signature" do
|
@@ -125,8 +125,13 @@ describe HexaPDF::DigitalSignature::CMSHandler do
|
|
125
125
|
@handler = HexaPDF::DigitalSignature::CMSHandler.new(@dict)
|
126
126
|
|
127
127
|
result = @handler.verify(@store)
|
128
|
-
assert_equal(:info, result.messages.
|
129
|
-
assert_match(/Signature valid/, result.messages.
|
128
|
+
assert_equal(:info, result.messages[-2].type)
|
129
|
+
assert_match(/Signature valid/, result.messages[-2].content)
|
130
|
+
end
|
131
|
+
|
132
|
+
it "provides information on the certificate chain" do
|
133
|
+
result = @handler.verify(@store)
|
134
|
+
assert_match(/RSA signer -> HexaPDF Test Root CA/, result.messages.last.content)
|
130
135
|
end
|
131
136
|
end
|
132
137
|
|
@@ -111,6 +111,13 @@ describe HexaPDF::DigitalSignature::Signature do
|
|
111
111
|
assert_equal((MINIMAL_PDF[0, 400] << MINIMAL_PDF[500, 333]).b, @sig.signed_data)
|
112
112
|
end
|
113
113
|
|
114
|
+
it "works for invalid offsets" do
|
115
|
+
doc = HexaPDF::Document.new(io: StringIO.new(MINIMAL_PDF))
|
116
|
+
@sig.document = doc
|
117
|
+
@sig[:ByteRange] = [0, 400, 9000, 333]
|
118
|
+
assert_equal(MINIMAL_PDF[0, 400], @sig.signed_data)
|
119
|
+
end
|
120
|
+
|
114
121
|
it "fails if the document isn't associated with an existing PDF file" do
|
115
122
|
assert_raises(HexaPDF::Error) { @sig.signed_data }
|
116
123
|
end
|
@@ -16,13 +16,13 @@ describe HexaPDF::DigitalSignature::Signatures do
|
|
16
16
|
|
17
17
|
it "iterates over all signature dictionaries" do
|
18
18
|
assert_equal([], @doc.signatures.to_a)
|
19
|
-
@sig1.field_value = :sig1
|
20
|
-
@sig2.field_value = :sig2
|
21
|
-
assert_equal([:sig1, :sig2], @doc.signatures.to_a)
|
19
|
+
@sig1.field_value = {k: :sig1}
|
20
|
+
@sig2.field_value = {k: :sig2}
|
21
|
+
assert_equal([{k: :sig1}, {k: :sig2}], @doc.signatures.to_a)
|
22
22
|
end
|
23
23
|
|
24
24
|
it "returns the number of signature dictionaries" do
|
25
|
-
@sig1.field_value = :sig1
|
25
|
+
@sig1.field_value = {k: :sig1}
|
26
26
|
assert_equal(1, @doc.signatures.count)
|
27
27
|
end
|
28
28
|
|
@@ -70,7 +70,8 @@ describe HexaPDF::DigitalSignature::Signatures do
|
|
70
70
|
end
|
71
71
|
@doc.signatures.add(@io, @handler, write_options: {update_fields: false})
|
72
72
|
sig = @doc.signatures.first
|
73
|
-
assert_equal([0, 925, 925 + sig[:Contents].size * 2 + 2,
|
73
|
+
assert_equal([0, 925, 925 + sig[:Contents].size * 2 + 2, 2455 + HexaPDF::VERSION.length],
|
74
|
+
sig[:ByteRange].value)
|
74
75
|
assert_equal(:sig, sig[:key])
|
75
76
|
assert_equal(:sig_field, @doc.acro_form.each_field.first[:key])
|
76
77
|
assert(sig.key?(:Contents))
|
@@ -132,14 +133,18 @@ describe HexaPDF::DigitalSignature::Signatures do
|
|
132
133
|
it "handles different xref section types correctly when determing the offsets" do
|
133
134
|
@doc.delete(7)
|
134
135
|
sig = @doc.signatures.add(@io, @handler, write_options: {update_fields: false})
|
135
|
-
|
136
|
+
l1 = 1030 + HexaPDF::VERSION.length
|
137
|
+
assert_equal([0, l1, l1 + sig[:Contents].size * 2 + 2, 2437 + HexaPDF::VERSION.length],
|
138
|
+
sig[:ByteRange].value)
|
136
139
|
end
|
137
140
|
|
138
141
|
it "works if the signature object is the last object of the xref section" do
|
139
142
|
field = @doc.acro_form(create: true).create_signature_field('Signature2')
|
140
143
|
field.create_widget(@doc.pages[0], Rect: [0, 0, 0, 0])
|
141
144
|
sig = @doc.signatures.add(@io, @handler, signature: field, write_options: {update_fields: false})
|
142
|
-
|
145
|
+
l1 = 3097 + HexaPDF::VERSION.length
|
146
|
+
assert_equal([0, l1, l1 + sig[:Contents].size * 2 + 2, 374 + HexaPDF::VERSION.length],
|
147
|
+
sig[:ByteRange].value)
|
143
148
|
end
|
144
149
|
|
145
150
|
it "allows writing to a file in addition to writing to an IO" do
|
@@ -228,11 +228,14 @@ describe HexaPDF::Encryption::StandardSecurityHandler do
|
|
228
228
|
end
|
229
229
|
|
230
230
|
it "fails if the /R value is incorrect" do
|
231
|
+
HexaPDF::Encryption::StandardEncryptionDictionary.field(:R).allowed_values << 7
|
231
232
|
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
232
|
-
@handler.set_up_decryption({Filter: :Standard, V: 2, R:
|
233
|
+
@handler.set_up_decryption({Filter: :Standard, V: 2, R: 7, O: 't' * 32, U: 't' * 32, P: 0,
|
233
234
|
Length: 128})
|
234
235
|
end
|
235
|
-
assert_match(/Invalid \/R value
|
236
|
+
assert_match(/Invalid \/R value 7/i, exp.message)
|
237
|
+
ensure
|
238
|
+
HexaPDF::Encryption::StandardEncryptionDictionary.field(:R).allowed_values.pop
|
236
239
|
end
|
237
240
|
|
238
241
|
it "fails if the supplied password is invalid" do
|
@@ -5,7 +5,7 @@ require 'hexapdf/font/cmap/writer'
|
|
5
5
|
|
6
6
|
describe HexaPDF::Font::CMap::Writer do
|
7
7
|
before do
|
8
|
-
@
|
8
|
+
@to_unicode_cmap_data = <<~EOF
|
9
9
|
/CIDInit /ProcSet findresource begin
|
10
10
|
12 dict begin
|
11
11
|
begincmap
|
@@ -32,35 +32,92 @@ describe HexaPDF::Font::CMap::Writer do
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
EOF
|
35
|
-
@
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
35
|
+
@cid_cmap_data = <<~EOF
|
36
|
+
%!PS-Adobe-3.0 Resource-CMap
|
37
|
+
%%DocumentNeededResources: ProcSet (CIDInit)
|
38
|
+
%%IncludeResource: ProcSet (CIDInit)
|
39
|
+
%%BeginResource: CMap (Custom)
|
40
|
+
%%Title: (Custom Adobe Identity 0)
|
41
|
+
%%Version: 1
|
42
|
+
/CIDInit /ProcSet findresource begin
|
43
|
+
12 dict begin
|
44
|
+
begincmap
|
45
|
+
/CIDSystemInfo 3 dict dup begin
|
46
|
+
/Registry (Adobe) def
|
47
|
+
/Ordering (Identity) def
|
48
|
+
/Supplement 0 def
|
49
|
+
end def
|
50
|
+
/CMapName /Custom def
|
51
|
+
/CMapType 1 def
|
52
|
+
/CMapVersion 1 def
|
53
|
+
/WMode 0 def
|
54
|
+
1 begincodespacerange
|
55
|
+
<0000> <FFFF>
|
56
|
+
endcodespacerange
|
57
|
+
1 begincidchar
|
58
|
+
<0060> 144
|
59
|
+
endcidchar
|
60
|
+
1 begincidrange
|
61
|
+
<0000><005E> 32
|
62
|
+
endcidrange
|
63
|
+
endcmap
|
64
|
+
CMapName currentdict /CMap defineresource pop
|
65
|
+
end
|
66
|
+
end
|
67
|
+
%%EndResource
|
68
|
+
%%EOF
|
69
|
+
EOF
|
70
|
+
|
71
|
+
@to_unicode_mapping = []
|
72
|
+
@cid_mapping = []
|
73
|
+
0x00.upto(0x5e) do |i|
|
74
|
+
@to_unicode_mapping << [i, 0x20 + i]
|
75
|
+
@cid_mapping << [i, 0x20 + i]
|
76
|
+
end
|
77
|
+
@to_unicode_mapping << [0x60, 0x90]
|
78
|
+
@cid_mapping << [0x60, 0x90]
|
79
|
+
0x1379.upto(0x137B) do |i|
|
80
|
+
@to_unicode_mapping << [i, 0x90FE + i - 0x1379]
|
81
|
+
end
|
82
|
+
@to_unicode_mapping << [0x3A51, 0x2003E]
|
40
83
|
end
|
41
84
|
|
42
85
|
describe "create_to_unicode_cmap" do
|
43
86
|
it "creates a correct CMap file" do
|
44
|
-
assert_equal(@
|
87
|
+
assert_equal(@to_unicode_cmap_data,
|
88
|
+
HexaPDF::Font::CMap.create_to_unicode_cmap(@to_unicode_mapping))
|
45
89
|
end
|
46
90
|
|
47
91
|
it "works if the last item is a range" do
|
48
|
-
@
|
49
|
-
@
|
50
|
-
@
|
51
|
-
assert_equal(@
|
92
|
+
@to_unicode_mapping.pop
|
93
|
+
@to_unicode_cmap_data.sub!(/2 beginbfchar/, '1 beginbfchar')
|
94
|
+
@to_unicode_cmap_data.sub!(/<3A51><d840dc3e>\n/, '')
|
95
|
+
assert_equal(@to_unicode_cmap_data,
|
96
|
+
HexaPDF::Font::CMap.create_to_unicode_cmap(@to_unicode_mapping))
|
52
97
|
end
|
53
98
|
|
54
99
|
it "works with only ranges" do
|
55
|
-
@
|
56
|
-
@
|
57
|
-
@
|
58
|
-
assert_equal(@
|
100
|
+
@to_unicode_mapping.delete_at(-1)
|
101
|
+
@to_unicode_mapping.delete_at(0x5f)
|
102
|
+
@to_unicode_cmap_data.sub!(/\n2 beginbfchar.*endbfchar/m, '')
|
103
|
+
assert_equal(@to_unicode_cmap_data,
|
104
|
+
HexaPDF::Font::CMap.create_to_unicode_cmap(@to_unicode_mapping))
|
59
105
|
end
|
60
106
|
|
61
107
|
it "returns an empty CMap if the mapping is empty" do
|
62
|
-
assert_equal(@
|
108
|
+
assert_equal(@to_unicode_cmap_data.sub(/\d+ beginbfchar.*endbfrange/m, ''),
|
63
109
|
HexaPDF::Font::CMap.create_to_unicode_cmap([]))
|
64
110
|
end
|
65
111
|
end
|
112
|
+
|
113
|
+
describe "create_cid_cmap" do
|
114
|
+
it "creates a correct CMap file" do
|
115
|
+
assert_equal(@cid_cmap_data, HexaPDF::Font::CMap.create_cid_cmap(@cid_mapping))
|
116
|
+
end
|
117
|
+
|
118
|
+
it "returns an empty CMap if the mapping is empty" do
|
119
|
+
assert_equal(@cid_cmap_data.sub(/\d+ begincidchar.*endcidrange/m, ''),
|
120
|
+
HexaPDF::Font::CMap.create_cid_cmap([]))
|
121
|
+
end
|
122
|
+
end
|
66
123
|
end
|
@@ -71,6 +71,12 @@ describe HexaPDF::Font::TrueTypeWrapper do
|
|
71
71
|
glyph.inspect)
|
72
72
|
end
|
73
73
|
|
74
|
+
it "caches glyphs based on the id and string" do
|
75
|
+
glyph = @font_wrapper.glyph(17)
|
76
|
+
assert_same(glyph, @font_wrapper.glyph(17))
|
77
|
+
refute_same(glyph, @font_wrapper.glyph(17, "1"))
|
78
|
+
end
|
79
|
+
|
74
80
|
it "invokes font.on_missing_glyph for missing glyphs" do
|
75
81
|
glyph = @font_wrapper.glyph(9999)
|
76
82
|
assert_kind_of(HexaPDF::Font::InvalidGlyph, glyph)
|
@@ -99,14 +105,18 @@ describe HexaPDF::Font::TrueTypeWrapper do
|
|
99
105
|
assert_equal([1].pack('n'), code)
|
100
106
|
code = @font_wrapper.encode(@font_wrapper.glyph(10))
|
101
107
|
assert_equal([2].pack('n'), code)
|
108
|
+
code = @font_wrapper.encode(@font_wrapper.glyph(10, "o"))
|
109
|
+
assert_equal([3].pack('n'), code)
|
102
110
|
end
|
103
111
|
|
104
112
|
it "returns the encoded glyph ID for fonts that are not subset" do
|
105
113
|
@font_wrapper = HexaPDF::Font::TrueTypeWrapper.new(@doc, @font, subset: false)
|
106
114
|
code = @font_wrapper.encode(@font_wrapper.glyph(3))
|
107
|
-
assert_equal([
|
115
|
+
assert_equal([1].pack('n'), code)
|
108
116
|
code = @font_wrapper.encode(@font_wrapper.glyph(10))
|
109
|
-
assert_equal([
|
117
|
+
assert_equal([2].pack('n'), code)
|
118
|
+
code = @font_wrapper.encode(@font_wrapper.glyph(10, "o"))
|
119
|
+
assert_equal([3].pack('n'), code)
|
110
120
|
end
|
111
121
|
|
112
122
|
it "raises an error if an InvalidGlyph is encoded" do
|
@@ -180,14 +190,18 @@ describe HexaPDF::Font::TrueTypeWrapper do
|
|
180
190
|
it "with fonts that are not subset (only differences to other case)" do
|
181
191
|
@font_wrapper = HexaPDF::Font::TrueTypeWrapper.new(@doc, @font, subset: false)
|
182
192
|
@font_wrapper.encode(@font_wrapper.glyph(3))
|
193
|
+
@font_wrapper.encode(@font_wrapper.glyph(3, "-"))
|
183
194
|
glyph = @font_wrapper.decode_utf8('H').first
|
184
195
|
@font_wrapper.encode(glyph)
|
185
196
|
@doc.dispatch_message(:complete_objects)
|
186
197
|
|
187
198
|
dict = @font_wrapper.pdf_object
|
188
199
|
|
189
|
-
assert_equal(HexaPDF::Font::CMap.create_to_unicode_cmap([[
|
200
|
+
assert_equal(HexaPDF::Font::CMap.create_to_unicode_cmap([[1, ' '.ord], [2, '-'.ord],
|
201
|
+
[3, 'H'.ord]]),
|
190
202
|
dict[:ToUnicode].stream)
|
203
|
+
assert_equal(HexaPDF::Font::CMap.create_cid_cmap([[1, 3], [2, 3], [3, glyph.id]]),
|
204
|
+
dict[:Encoding].stream)
|
191
205
|
assert_equal([glyph.id, [glyph.width]], dict[:DescendantFonts][0][:W].value)
|
192
206
|
end
|
193
207
|
end
|
@@ -185,7 +185,7 @@ describe HexaPDF::Layout::ListBox do
|
|
185
185
|
[:set_font_and_size, [:F1, 11]],
|
186
186
|
[:set_device_gray_non_stroking_color, [0.5]],
|
187
187
|
[:begin_text],
|
188
|
-
[:
|
188
|
+
[:move_text, [1.15, 92.487]],
|
189
189
|
[:show_text, ["\x95".b]],
|
190
190
|
[:end_text],
|
191
191
|
[:restore_graphics_state],
|
@@ -197,7 +197,7 @@ describe HexaPDF::Layout::ListBox do
|
|
197
197
|
[:set_font_and_size, [:F1, 11]],
|
198
198
|
[:set_device_gray_non_stroking_color, [0.5]],
|
199
199
|
[:begin_text],
|
200
|
-
[:
|
200
|
+
[:move_text, [1.15, 82.487]],
|
201
201
|
[:show_text, ["\x95".b]],
|
202
202
|
[:end_text],
|
203
203
|
[:restore_graphics_state],
|
@@ -219,7 +219,7 @@ describe HexaPDF::Layout::ListBox do
|
|
219
219
|
[:set_text_rise, [-6.111111]],
|
220
220
|
[:set_device_gray_non_stroking_color, [0.5]],
|
221
221
|
[:begin_text],
|
222
|
-
[:
|
222
|
+
[:move_text, [0.1985, 100]],
|
223
223
|
[:show_text, ["m".b]],
|
224
224
|
[:end_text],
|
225
225
|
[:restore_graphics_state],
|
@@ -241,7 +241,7 @@ describe HexaPDF::Layout::ListBox do
|
|
241
241
|
[:set_text_rise, [-6.111111]],
|
242
242
|
[:set_device_gray_non_stroking_color, [0.5]],
|
243
243
|
[:begin_text],
|
244
|
-
[:
|
244
|
+
[:move_text, [0.8145, 100]],
|
245
245
|
[:show_text, ["n".b]],
|
246
246
|
[:end_text],
|
247
247
|
[:restore_graphics_state],
|
@@ -263,7 +263,7 @@ describe HexaPDF::Layout::ListBox do
|
|
263
263
|
[:set_font_and_size, [:F1, 11]],
|
264
264
|
[:set_device_gray_non_stroking_color, [0.5]],
|
265
265
|
[:begin_text],
|
266
|
-
[:
|
266
|
+
[:move_text, [6.75, 92.487]],
|
267
267
|
[:show_text, ["1.".b]],
|
268
268
|
[:end_text],
|
269
269
|
[:restore_graphics_state],
|
@@ -275,7 +275,7 @@ describe HexaPDF::Layout::ListBox do
|
|
275
275
|
[:set_font_and_size, [:F1, 11]],
|
276
276
|
[:set_device_gray_non_stroking_color, [0.5]],
|
277
277
|
[:begin_text],
|
278
|
-
[:
|
278
|
+
[:move_text, [6.75, 82.487]],
|
279
279
|
[:show_text, ["2.".b]],
|
280
280
|
[:end_text],
|
281
281
|
[:restore_graphics_state],
|
@@ -314,7 +314,7 @@ describe HexaPDF::Layout::ListBox do
|
|
314
314
|
[:save_graphics_state],
|
315
315
|
[:set_font_and_size, [:F1, 10]],
|
316
316
|
[:begin_text],
|
317
|
-
[:
|
317
|
+
[:move_text, [1.5, 93.17]],
|
318
318
|
[:show_text, ["\x95".b]],
|
319
319
|
[:end_text],
|
320
320
|
[:restore_graphics_state],
|
@@ -511,6 +511,58 @@ describe HexaPDF::Layout::TableBox do
|
|
511
511
|
[0, 66, 39.75, 22], [39.75, 66, 39.75, 22], [79.5, 44, 39.75, 44], [119.25, 66, 39.75, 22]])
|
512
512
|
end
|
513
513
|
|
514
|
+
describe "row spans" do
|
515
|
+
# ----------
|
516
|
+
# | a | b |
|
517
|
+
# | a | |
|
518
|
+
# | a |----|
|
519
|
+
# | a | c |
|
520
|
+
# | a | |
|
521
|
+
# ----------
|
522
|
+
it "works if content of a row span cell is larger than the rows" do
|
523
|
+
cells = [[{row_span: 2, content: @fixed_size_boxes[0..2]}, @fixed_size_boxes[3]],
|
524
|
+
[@fixed_size_boxes[4]]]
|
525
|
+
check_box(create_box(cells: cells, cell_style: {padding: 0, border: {width: 0}}),
|
526
|
+
:success, 160, 30,
|
527
|
+
[[0, 0, 80, 30], [80, 0, 80, 15], [0, 0, 80, 30], [80, 15, 80, 15]])
|
528
|
+
end
|
529
|
+
|
530
|
+
# ----------
|
531
|
+
# | a | b |
|
532
|
+
# | |----|
|
533
|
+
# | | c |
|
534
|
+
# ----------
|
535
|
+
it "works if content of a row span cell is smaller than the rows" do
|
536
|
+
cells = [[{row_span: 2, content: @fixed_size_boxes[0]}, @fixed_size_boxes[3]],
|
537
|
+
[@fixed_size_boxes[4]]]
|
538
|
+
check_box(create_box(cells: cells, cell_style: {padding: 0, border: {width: 0}}),
|
539
|
+
:success, 160, 20,
|
540
|
+
[[0, 0, 80, 20], [80, 0, 80, 10], [0, 0, 80, 20], [80, 10, 80, 10]])
|
541
|
+
end
|
542
|
+
|
543
|
+
# -----------------
|
544
|
+
# | a | b | c | d |
|
545
|
+
# | a | b |---| d |
|
546
|
+
# | a | b | e | |
|
547
|
+
# | a | | e | |
|
548
|
+
# --------| e | |
|
549
|
+
# | f | g | e | |
|
550
|
+
# ----------------
|
551
|
+
it "works if multiple, possibly overlapping row spans are involved" do
|
552
|
+
cells = [[{row_span: 2, content: @fixed_size_boxes[0..2]},
|
553
|
+
{row_span: 2, content: @fixed_size_boxes[3..4]},
|
554
|
+
@fixed_size_boxes[5],
|
555
|
+
{row_span: 3, content: @fixed_size_boxes[6..7]}],
|
556
|
+
[{row_span: 2, content: @fixed_size_boxes[8, 3]}],
|
557
|
+
[@fixed_size_boxes[11], @fixed_size_boxes[12]]]
|
558
|
+
check_box(create_box(cells: cells, cell_style: {padding: 0, border: {width: 0}}),
|
559
|
+
:success, 160, 40,
|
560
|
+
[[0, 0, 40, 30], [40, 0, 40, 30], [80, 0, 40, 10], [120, 0, 40, 40],
|
561
|
+
[0, 0, 40, 30], [40, 0, 40, 30], [80, 10, 40, 30], [120, 0, 40, 40],
|
562
|
+
[0, 30, 40, 10], [40, 30, 40, 10], [80, 10, 40, 30], [120, 0, 40, 40]])
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
514
566
|
it "fits a table with header rows" do
|
515
567
|
result = [[0, 0, 80, 10], [80, 0, 80, 10], [0, 10, 80, 10], [80, 10, 80, 10]]
|
516
568
|
header = lambda {|_| [@fixed_size_boxes[10, 2], @fixed_size_boxes[12, 2]] }
|
@@ -139,7 +139,7 @@ describe HexaPDF::Layout::TextFragment do
|
|
139
139
|
[:set_text_rise, [2]],
|
140
140
|
*middle,
|
141
141
|
[:begin_text],
|
142
|
-
[:
|
142
|
+
[:move_text, [10, 15]],
|
143
143
|
[:show_text, ['!']],
|
144
144
|
*back,
|
145
145
|
].compact
|
@@ -156,7 +156,7 @@ describe HexaPDF::Layout::TextFragment do
|
|
156
156
|
@canvas = @doc.pages.add.canvas
|
157
157
|
@fragment.draw(@canvas, 10, 15, ignore_text_properties: true)
|
158
158
|
assert_operators(@canvas.contents, [[:begin_text],
|
159
|
-
[:
|
159
|
+
[:move_text, [10, 15]]])
|
160
160
|
end
|
161
161
|
|
162
162
|
describe "uses an appropriate text position setter" do
|
@@ -188,7 +188,7 @@ describe HexaPDF::Layout::TextFragment do
|
|
188
188
|
it "horizontal and vertical movement" do
|
189
189
|
@fragment.draw(@canvas, 10, 10, ignore_text_properties: true)
|
190
190
|
assert_operators(@canvas.contents, [[:begin_text],
|
191
|
-
[:
|
191
|
+
[:move_text, [10, 10]]])
|
192
192
|
end
|
193
193
|
end
|
194
194
|
|
@@ -743,10 +743,12 @@ describe HexaPDF::Layout::TextLayouter do
|
|
743
743
|
result = processor.recorded_ops
|
744
744
|
leading = (result.select {|name, _| name == :set_leading } || [0]).map(&:last).flatten.first
|
745
745
|
pos = [0, 0]
|
746
|
-
result.select!
|
747
|
-
|
746
|
+
result.select! do |name, _|
|
747
|
+
name == :set_text_matrix || name == :move_text || name == :move_text_next_line
|
748
|
+
end.map! do |name, ops|
|
748
749
|
case name
|
749
750
|
when :set_text_matrix then pos = ops[-2, 2]
|
751
|
+
when :move_text then pos = ops
|
750
752
|
when :move_text_next_line then pos[1] -= leading
|
751
753
|
end
|
752
754
|
pos.dup
|