hexapdf 0.45.0 → 0.47.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 +4 -4
- data/CHANGELOG.md +120 -47
- data/examples/019-acro_form.rb +5 -0
- data/lib/hexapdf/cli/inspect.rb +5 -0
- data/lib/hexapdf/composer.rb +1 -1
- data/lib/hexapdf/configuration.rb +19 -0
- data/lib/hexapdf/digital_signature/cms_handler.rb +31 -3
- data/lib/hexapdf/digital_signature/signing/default_handler.rb +9 -1
- data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +5 -1
- data/lib/hexapdf/document/layout.rb +48 -27
- data/lib/hexapdf/document.rb +24 -2
- data/lib/hexapdf/encryption/standard_security_handler.rb +32 -26
- data/lib/hexapdf/importer.rb +15 -5
- data/lib/hexapdf/layout/box.rb +25 -28
- data/lib/hexapdf/layout/frame.rb +1 -1
- data/lib/hexapdf/layout/inline_box.rb +17 -23
- data/lib/hexapdf/layout/list_box.rb +24 -29
- data/lib/hexapdf/layout/page_style.rb +23 -16
- data/lib/hexapdf/layout/style.rb +2 -2
- data/lib/hexapdf/layout/table_box.rb +57 -10
- data/lib/hexapdf/layout/text_box.rb +2 -6
- data/lib/hexapdf/parser.rb +5 -1
- data/lib/hexapdf/revisions.rb +1 -1
- data/lib/hexapdf/stream.rb +3 -3
- data/lib/hexapdf/task/optimize.rb +4 -4
- data/lib/hexapdf/tokenizer.rb +3 -2
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +8 -4
- data/lib/hexapdf/type/acro_form/button_field.rb +2 -0
- data/lib/hexapdf/type/acro_form/choice_field.rb +2 -0
- data/lib/hexapdf/type/acro_form/field.rb +8 -0
- data/lib/hexapdf/type/acro_form/form.rb +10 -6
- data/lib/hexapdf/type/acro_form/signature_field.rb +2 -1
- data/lib/hexapdf/type/acro_form/text_field.rb +2 -0
- data/lib/hexapdf/type/acro_form/variable_text_field.rb +11 -3
- data/lib/hexapdf/type/annotations/widget.rb +4 -2
- 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/digital_signature/common.rb +66 -84
- data/test/hexapdf/digital_signature/signing/test_default_handler.rb +7 -0
- data/test/hexapdf/digital_signature/signing/test_signed_data_creator.rb +9 -0
- data/test/hexapdf/digital_signature/test_cms_handler.rb +41 -1
- data/test/hexapdf/digital_signature/test_handler.rb +2 -1
- data/test/hexapdf/digital_signature/test_signatures.rb +4 -4
- data/test/hexapdf/document/test_layout.rb +28 -5
- data/test/hexapdf/encryption/test_standard_security_handler.rb +5 -2
- data/test/hexapdf/layout/test_box.rb +12 -5
- data/test/hexapdf/layout/test_frame.rb +12 -2
- data/test/hexapdf/layout/test_inline_box.rb +17 -28
- data/test/hexapdf/layout/test_list_box.rb +5 -5
- data/test/hexapdf/layout/test_page_style.rb +7 -2
- data/test/hexapdf/layout/test_table_box.rb +52 -0
- data/test/hexapdf/layout/test_text_box.rb +3 -9
- data/test/hexapdf/layout/test_text_layouter.rb +0 -3
- data/test/hexapdf/task/test_optimize.rb +2 -0
- data/test/hexapdf/test_document.rb +30 -3
- data/test/hexapdf/test_importer.rb +24 -0
- data/test/hexapdf/test_revisions.rb +54 -41
- data/test/hexapdf/test_writer.rb +11 -2
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +22 -5
- data/test/hexapdf/type/acro_form/test_form.rb +9 -5
- 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/annotations/test_widget.rb +4 -0
- metadata +6 -2
|
@@ -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
|
|
@@ -12,26 +12,10 @@ module HexaPDF
|
|
|
12
12
|
def ca_certificate
|
|
13
13
|
@ca_certificate ||=
|
|
14
14
|
begin
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
ca_cert.version = 2
|
|
20
|
-
ca_cert.not_before = Time.now - 86400
|
|
21
|
-
ca_cert.not_after = Time.now + 86400
|
|
22
|
-
ca_cert.public_key = ca_key.public_key
|
|
23
|
-
ca_cert.subject = ca_name
|
|
24
|
-
ca_cert.issuer = ca_name
|
|
25
|
-
|
|
26
|
-
extension_factory = OpenSSL::X509::ExtensionFactory.new
|
|
27
|
-
extension_factory.subject_certificate = ca_cert
|
|
28
|
-
extension_factory.issuer_certificate = ca_cert
|
|
29
|
-
ca_cert.add_extension(extension_factory.create_extension('subjectKeyIdentifier', 'hash'))
|
|
30
|
-
ca_cert.add_extension(extension_factory.create_extension('basicConstraints', 'CA:TRUE', true))
|
|
31
|
-
ca_cert.add_extension(extension_factory.create_extension('keyUsage', 'cRLSign,keyCertSign', true))
|
|
32
|
-
ca_cert.sign(ca_key, OpenSSL::Digest.new('SHA1'))
|
|
33
|
-
|
|
34
|
-
ca_cert
|
|
15
|
+
cert = create_cert(name: '/C=AT/O=HexaPDF/CN=HexaPDF Test Root CA', serial: 0,
|
|
16
|
+
public_key: ca_key.public_key)
|
|
17
|
+
add_extensions(cert, cert, ca_key, is_ca: true, key_usage: 'cRLSign,keyCertSign')
|
|
18
|
+
cert
|
|
35
19
|
end
|
|
36
20
|
end
|
|
37
21
|
|
|
@@ -39,88 +23,86 @@ module HexaPDF
|
|
|
39
23
|
@signer_key ||= OpenSSL::PKey::RSA.new(2048)
|
|
40
24
|
end
|
|
41
25
|
|
|
42
|
-
def dsa_signer_key
|
|
43
|
-
@dsa_signer_key ||= OpenSSL::PKey::DSA.new(2048)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
26
|
def signer_certificate
|
|
47
27
|
@signer_certificate ||=
|
|
48
28
|
begin
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
signer_cert.add_extension(extension_factory.create_extension('subjectKeyIdentifier', 'hash'))
|
|
64
|
-
signer_cert.add_extension(extension_factory.create_extension('basicConstraints', 'CA:FALSE'))
|
|
65
|
-
signer_cert.add_extension(extension_factory.create_extension('keyUsage', 'digitalSignature'))
|
|
66
|
-
signer_cert.sign(ca_key, OpenSSL::Digest.new('SHA1'))
|
|
67
|
-
|
|
68
|
-
signer_cert
|
|
29
|
+
cert = create_cert(name: '/CN=RSA signer/DC=gettalong', serial: 2,
|
|
30
|
+
public_key: signer_key.public_key, issuer: ca_certificate)
|
|
31
|
+
add_extensions(cert, ca_certificate, ca_key, key_usage: 'digitalSignature')
|
|
32
|
+
cert
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def non_repudiation_signer_certificate
|
|
37
|
+
@non_repudiation_signer_certificate ||=
|
|
38
|
+
begin
|
|
39
|
+
cert = create_cert(name: '/CN=Non repudiation signer/DC=gettalong', serial: 2,
|
|
40
|
+
public_key: signer_key.public_key, issuer: ca_certificate)
|
|
41
|
+
add_extensions(cert, ca_certificate, ca_key, key_usage: 'nonRepudiation')
|
|
42
|
+
cert
|
|
69
43
|
end
|
|
70
44
|
end
|
|
71
45
|
|
|
46
|
+
def dsa_signer_key
|
|
47
|
+
@dsa_signer_key ||= OpenSSL::PKey::DSA.new(2048)
|
|
48
|
+
end
|
|
49
|
+
|
|
72
50
|
def dsa_signer_certificate
|
|
73
51
|
@dsa_signer_certificate ||=
|
|
74
52
|
begin
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
signer_cert.not_after = Time.now + 86400
|
|
80
|
-
signer_cert.public_key = dsa_signer_key.public_key
|
|
81
|
-
signer_cert.subject = OpenSSL::X509::Name.parse('/CN=DSA signer/DC=gettalong')
|
|
82
|
-
signer_cert.issuer = ca_certificate.subject
|
|
83
|
-
|
|
84
|
-
extension_factory = OpenSSL::X509::ExtensionFactory.new
|
|
85
|
-
extension_factory.subject_certificate = signer_cert
|
|
86
|
-
extension_factory.issuer_certificate = ca_certificate
|
|
87
|
-
signer_cert.add_extension(extension_factory.create_extension('subjectKeyIdentifier', 'hash'))
|
|
88
|
-
signer_cert.add_extension(extension_factory.create_extension('basicConstraints', 'CA:FALSE'))
|
|
89
|
-
signer_cert.add_extension(extension_factory.create_extension('keyUsage', 'digitalSignature'))
|
|
90
|
-
signer_cert.sign(ca_key, OpenSSL::Digest.new('SHA1'))
|
|
91
|
-
|
|
92
|
-
signer_cert
|
|
53
|
+
cert = create_cert(name: '/CN=DSA signer/DC=gettalong', serial: 3,
|
|
54
|
+
public_key: dsa_signer_key.public_key, issuer: ca_certificate)
|
|
55
|
+
add_extensions(cert, ca_certificate, ca_key, key_usage: 'digitalSignature')
|
|
56
|
+
cert
|
|
93
57
|
end
|
|
94
58
|
end
|
|
95
59
|
|
|
96
60
|
def timestamp_certificate
|
|
97
61
|
@timestamp_certificate ||=
|
|
98
62
|
begin
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
signer_cert.not_before = Time.now - 86400
|
|
105
|
-
signer_cert.not_after = Time.now + 86400
|
|
106
|
-
signer_cert.public_key = signer_key.public_key
|
|
107
|
-
signer_cert.subject = name
|
|
108
|
-
signer_cert.issuer = ca_certificate.subject
|
|
109
|
-
|
|
110
|
-
extension_factory = OpenSSL::X509::ExtensionFactory.new
|
|
111
|
-
extension_factory.subject_certificate = signer_cert
|
|
112
|
-
extension_factory.issuer_certificate = ca_certificate
|
|
113
|
-
signer_cert.add_extension(extension_factory.create_extension('subjectKeyIdentifier', 'hash'))
|
|
114
|
-
signer_cert.add_extension(extension_factory.create_extension('basicConstraints', 'CA:FALSE'))
|
|
115
|
-
signer_cert.add_extension(extension_factory.create_extension('keyUsage', 'digitalSignature'))
|
|
116
|
-
signer_cert.add_extension(extension_factory.create_extension('extendedKeyUsage',
|
|
117
|
-
'timeStamping', true))
|
|
118
|
-
signer_cert.sign(ca_key, OpenSSL::Digest.new('SHA1'))
|
|
119
|
-
|
|
120
|
-
signer_cert
|
|
63
|
+
cert = create_cert(name: '/CN=timestamp/DC=gettalong', serial: 3,
|
|
64
|
+
public_key: signer_key.public_key, issuer: ca_certificate)
|
|
65
|
+
add_extensions(cert, ca_certificate, ca_key, key_usage: 'digitalSignature',
|
|
66
|
+
extended_key_usage: 'timeStamping')
|
|
67
|
+
cert
|
|
121
68
|
end
|
|
122
69
|
end
|
|
123
70
|
|
|
71
|
+
def create_cert(name:, serial:, public_key:, issuer: nil)
|
|
72
|
+
name = OpenSSL::X509::Name.parse(name)
|
|
73
|
+
cert = OpenSSL::X509::Certificate.new
|
|
74
|
+
cert.serial = serial
|
|
75
|
+
cert.version = 2
|
|
76
|
+
cert.not_before = Time.now - 86400
|
|
77
|
+
cert.not_after = Time.now + 86400
|
|
78
|
+
cert.public_key = public_key
|
|
79
|
+
cert.subject = name
|
|
80
|
+
cert.issuer = (issuer ? issuer.subject : name)
|
|
81
|
+
cert
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def add_extensions(subject_cert, issuer_cert, signing_key, is_ca: false, key_usage: nil,
|
|
85
|
+
extended_key_usage: nil)
|
|
86
|
+
extension_factory = OpenSSL::X509::ExtensionFactory.new
|
|
87
|
+
extension_factory.subject_certificate = subject_cert
|
|
88
|
+
extension_factory.issuer_certificate = issuer_cert
|
|
89
|
+
subject_cert.add_extension(extension_factory.create_extension('subjectKeyIdentifier', 'hash'))
|
|
90
|
+
if is_ca
|
|
91
|
+
subject_cert.add_extension(extension_factory.create_extension('basicConstraints', 'CA:TRUE', true))
|
|
92
|
+
else
|
|
93
|
+
subject_cert.add_extension(extension_factory.create_extension('basicConstraints', 'CA:FALSE'))
|
|
94
|
+
end
|
|
95
|
+
if key_usage
|
|
96
|
+
subject_cert.add_extension(extension_factory.create_extension('keyUsage', key_usage, true))
|
|
97
|
+
end
|
|
98
|
+
if extended_key_usage
|
|
99
|
+
subject_cert.add_extension(extension_factory.create_extension('extendedKeyUsage',
|
|
100
|
+
extended_key_usage, true))
|
|
101
|
+
end
|
|
102
|
+
subject_cert.sign(signing_key, OpenSSL::Digest.new('SHA1'))
|
|
103
|
+
end
|
|
104
|
+
private :add_extensions
|
|
105
|
+
|
|
124
106
|
def start_tsa_server
|
|
125
107
|
return if defined?(@tsa_server)
|
|
126
108
|
require 'webrick'
|
|
@@ -133,6 +133,13 @@ describe HexaPDF::DigitalSignature::Signing::DefaultHandler do
|
|
|
133
133
|
assert_equal(['Reason', 'Location', 'Contact'], @obj.value.values_at(:Reason, :Location, :ContactInfo))
|
|
134
134
|
end
|
|
135
135
|
|
|
136
|
+
it "sets the signing time" do
|
|
137
|
+
time = Time.now
|
|
138
|
+
@handler.signing_time = time
|
|
139
|
+
@handler.finalize_objects(@field, @obj)
|
|
140
|
+
assert_equal(time, @obj[:M])
|
|
141
|
+
end
|
|
142
|
+
|
|
136
143
|
it "fills the build properties dictionary with appropriate application information" do
|
|
137
144
|
@handler.finalize_objects(@field, @obj)
|
|
138
145
|
assert_equal(:HexaPDF, @obj[:Prop_Build][:App][:Name])
|
|
@@ -214,6 +214,15 @@ describe HexaPDF::DigitalSignature::Signing::SignedDataCreator do
|
|
|
214
214
|
assert_equal(Time.now.utc, attr.value[1].value[0].value)
|
|
215
215
|
end
|
|
216
216
|
end
|
|
217
|
+
|
|
218
|
+
it "can use a user-defined time as signing time" do
|
|
219
|
+
current_time = Time.now
|
|
220
|
+
@signed_data.signing_time = current_time
|
|
221
|
+
asn1 = OpenSSL::ASN1.decode(@signed_data.create("data"))
|
|
222
|
+
attr = asn1.value[1].value[0].value[4].value[0].value[3].value.
|
|
223
|
+
find {|obj| obj.value[0].value == 'signingTime' }
|
|
224
|
+
assert_equal(current_time.floor.utc, attr.value[1].value[0].value)
|
|
225
|
+
end
|
|
217
226
|
end
|
|
218
227
|
|
|
219
228
|
describe "pades signature" do
|
|
@@ -22,7 +22,7 @@ describe HexaPDF::DigitalSignature::CMSHandler do
|
|
|
22
22
|
assert_equal("RSA signer", @handler.signer_name)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
it "returns the signing time" do
|
|
25
|
+
it "returns the signing time from the signed attributes" do
|
|
26
26
|
assert_equal(@pkcs7.signers.first.signed_time, @handler.signing_time)
|
|
27
27
|
end
|
|
28
28
|
|
|
@@ -86,6 +86,18 @@ describe HexaPDF::DigitalSignature::CMSHandler do
|
|
|
86
86
|
assert_match(/key usage is missing 'Digital Signature'/, result.messages.first.content)
|
|
87
87
|
end
|
|
88
88
|
|
|
89
|
+
it "provides info for a non-repudiation signature" do
|
|
90
|
+
@pkcs7 = OpenSSL::PKCS7.sign(CERTIFICATES.non_repudiation_signer_certificate,
|
|
91
|
+
CERTIFICATES.signer_key,
|
|
92
|
+
@data, [CERTIFICATES.ca_certificate],
|
|
93
|
+
OpenSSL::PKCS7::DETACHED)
|
|
94
|
+
@dict.contents = @pkcs7.to_der
|
|
95
|
+
@handler = HexaPDF::DigitalSignature::CMSHandler.new(@dict)
|
|
96
|
+
result = @handler.verify(@store)
|
|
97
|
+
assert_equal(:info, result.messages.first.type)
|
|
98
|
+
assert_match(/Certificate used for non-repudiation/, result.messages.first.content)
|
|
99
|
+
end
|
|
100
|
+
|
|
89
101
|
it "verifies the signature itself" do
|
|
90
102
|
result = @handler.verify(@store)
|
|
91
103
|
assert_equal(:info, result.messages.last.type)
|
|
@@ -117,4 +129,32 @@ describe HexaPDF::DigitalSignature::CMSHandler do
|
|
|
117
129
|
assert_match(/Signature valid/, result.messages.last.content)
|
|
118
130
|
end
|
|
119
131
|
end
|
|
132
|
+
|
|
133
|
+
describe "with embedded TSA signature" do
|
|
134
|
+
before do
|
|
135
|
+
CERTIFICATES.start_tsa_server
|
|
136
|
+
tsh = HexaPDF::DigitalSignature::Signing::TimestampHandler.new(
|
|
137
|
+
signature_size: 10_000, tsa_url: 'http://127.0.0.1:34567'
|
|
138
|
+
)
|
|
139
|
+
cms = HexaPDF::DigitalSignature::Signing::SignedDataCreator.create(
|
|
140
|
+
@data, type: :pades, certificate: CERTIFICATES.signer_certificate,
|
|
141
|
+
key: CERTIFICATES.signer_key, timestamp_handler: tsh,
|
|
142
|
+
certificates: [CERTIFICATES.ca_certificate]
|
|
143
|
+
)
|
|
144
|
+
@dict.contents = cms.to_der
|
|
145
|
+
@dict.signed_data = @data
|
|
146
|
+
@handler = HexaPDF::DigitalSignature::CMSHandler.new(@dict)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it "returns the signing time from the TSA signature" do
|
|
150
|
+
assert_equal(@handler.embedded_tsa_signature.signers.first.signed_time, @handler.signing_time)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it "provides informational output if the time is from a TSA signature" do
|
|
154
|
+
store = OpenSSL::X509::Store.new
|
|
155
|
+
result = @handler.verify(store)
|
|
156
|
+
assert_equal(:info, result.messages.first.type)
|
|
157
|
+
assert_match(/Signing time.*timestamp authority/, result.messages.first.content)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
120
160
|
end
|
|
@@ -5,6 +5,7 @@ require 'hexapdf/digital_signature'
|
|
|
5
5
|
require 'hexapdf/document'
|
|
6
6
|
require 'time'
|
|
7
7
|
require 'ostruct'
|
|
8
|
+
require 'openssl'
|
|
8
9
|
|
|
9
10
|
describe HexaPDF::DigitalSignature::Handler do
|
|
10
11
|
before do
|
|
@@ -36,7 +37,7 @@ describe HexaPDF::DigitalSignature::Handler do
|
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
it "can allow self-signed certificates" do
|
|
39
|
-
[OpenSSL::X509::
|
|
40
|
+
[OpenSSL::X509::V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT,
|
|
40
41
|
OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN].each do |error|
|
|
41
42
|
[true, false].each do |allow_self_signed|
|
|
42
43
|
@result.messages.clear
|
|
@@ -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
|
|
|
@@ -35,11 +35,11 @@ describe HexaPDF::Document::Layout::ChildrenCollector do
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
it "allows appending boxes created by the Layout class" do
|
|
38
|
-
@collector.lorem_ipsum
|
|
39
|
-
@collector.lorem_ipsum_box
|
|
40
|
-
@collector.column
|
|
41
|
-
@collector.column_box
|
|
42
|
-
assert_equal(
|
|
38
|
+
box1 = @collector.lorem_ipsum
|
|
39
|
+
box2 = @collector.lorem_ipsum_box
|
|
40
|
+
box3 = @collector.column
|
|
41
|
+
box4 = @collector.column_box
|
|
42
|
+
assert_equal([box1, box2, box3, box4], @collector.children)
|
|
43
43
|
assert_kind_of(HexaPDF::Layout::TextBox, @collector.children[0])
|
|
44
44
|
assert_kind_of(HexaPDF::Layout::TextBox, @collector.children[1])
|
|
45
45
|
assert_kind_of(HexaPDF::Layout::ColumnBox, @collector.children[2])
|
|
@@ -95,6 +95,11 @@ describe HexaPDF::Document::Layout::CellArgumentCollector do
|
|
|
95
95
|
@args[-3..-1, -5..-2] = {key: :value}
|
|
96
96
|
check_argument_info(@args.argument_infos.first, 17..19, 5..8, {key: :value})
|
|
97
97
|
end
|
|
98
|
+
|
|
99
|
+
it "allows using stepped ranges" do
|
|
100
|
+
@args[(0..-1).step(2)] = {key: :value}
|
|
101
|
+
check_argument_info(@args.argument_infos.first, (0..19).step(2), 0..9, {key: :value})
|
|
102
|
+
end
|
|
98
103
|
end
|
|
99
104
|
|
|
100
105
|
describe "retrieve_arguments_for" do
|
|
@@ -157,6 +162,24 @@ describe HexaPDF::Document::Layout do
|
|
|
157
162
|
end
|
|
158
163
|
end
|
|
159
164
|
|
|
165
|
+
describe "private retrieve_style" do
|
|
166
|
+
it "resolves a font name to a font wrapper" do
|
|
167
|
+
style = @layout.send(:retrieve_style, {font: 'Helvetica'})
|
|
168
|
+
assert_kind_of(HexaPDF::Font::Type1Wrapper, style.font)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it "sets the :base style's font if no font is set" do
|
|
172
|
+
@layout.style(:base, font: 'Helvetica')
|
|
173
|
+
style = @layout.send(:retrieve_style, {})
|
|
174
|
+
assert_equal('Helvetica', style.font.wrapped_font.font_name)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it "sets the font specified in the config option font.default as fallback" do
|
|
178
|
+
style = @layout.send(:retrieve_style, {})
|
|
179
|
+
assert_equal('Times-Roman', style.font.wrapped_font.font_name)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
160
183
|
describe "inline_box" do
|
|
161
184
|
it "takes a box as argument" do
|
|
162
185
|
box = HexaPDF::Layout::Box.create(width: 10, height: 10)
|
|
@@ -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
|
|
@@ -148,29 +148,36 @@ describe HexaPDF::Layout::Box do
|
|
|
148
148
|
assert_equal(49.9999996, box.height)
|
|
149
149
|
end
|
|
150
150
|
|
|
151
|
-
it "
|
|
151
|
+
it "works for boxes with no space for the content" do
|
|
152
|
+
box = create_box(height: 1, style: {border: {width: [1, 0, 0]}})
|
|
153
|
+
assert(box.fit(100, 100, @frame).success?)
|
|
154
|
+
assert_equal(1, box.height)
|
|
155
|
+
assert_equal(100, box.width)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
it "fails if position != :flow and its width is greater than the available width" do
|
|
152
159
|
box = create_box(width: 101)
|
|
153
160
|
assert(box.fit(100, 100, @frame).failure?)
|
|
154
161
|
end
|
|
155
162
|
|
|
156
|
-
it "fails if
|
|
163
|
+
it "fails if position != :flow and its width is greater than the available width" do
|
|
157
164
|
box = create_box(height: 101)
|
|
158
165
|
assert(box.fit(100, 100, @frame).failure?)
|
|
159
166
|
end
|
|
160
167
|
|
|
161
|
-
it "fails if
|
|
168
|
+
it "fails if position != :flow and the reserved width is greater than the width" do
|
|
162
169
|
box = create_box(height: 100)
|
|
163
170
|
box.style.padding = [0, 100]
|
|
164
171
|
assert(box.fit(150, 150, @frame).failure?)
|
|
165
172
|
end
|
|
166
173
|
|
|
167
|
-
it "fails if
|
|
174
|
+
it "fails if position != :flow and the reserved height is greater than the height" do
|
|
168
175
|
box = create_box(width: 100)
|
|
169
176
|
box.style.padding = [100, 0]
|
|
170
177
|
assert(box.fit(150, 150, @frame).failure?)
|
|
171
178
|
end
|
|
172
179
|
|
|
173
|
-
it "can use the #
|
|
180
|
+
it "can use the #update_content_width/#update_content_height helper methods" do
|
|
174
181
|
box = create_box
|
|
175
182
|
box.define_singleton_method(:fit_content) do |_aw, _ah, _frame|
|
|
176
183
|
update_content_width { 10 }
|
|
@@ -316,10 +316,20 @@ describe HexaPDF::Layout::Frame do
|
|
|
316
316
|
|
|
317
317
|
describe "flowing boxes" do
|
|
318
318
|
it "flows inside the frame's outline" do
|
|
319
|
+
remove_area(:left)
|
|
319
320
|
check_box({width: 10, height: 20, margin: 10, position: :flow},
|
|
320
|
-
[
|
|
321
|
+
[10, 90],
|
|
321
322
|
[10, 80, 110, 110],
|
|
322
|
-
[[[
|
|
323
|
+
[[[20, 10], [110, 10], [110, 80], [20, 80]]])
|
|
324
|
+
assert_equal(10, @box.fit_result.x)
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
it "doesn't overwrite fit_result.x" do
|
|
328
|
+
box = HexaPDF::Layout::Box.create(position: :flow) {}
|
|
329
|
+
box.define_singleton_method(:supports_position_flow?) { true }
|
|
330
|
+
box.define_singleton_method(:fit_content) {|*args| fit_result.x = 30; super(*args) }
|
|
331
|
+
fit_result = @frame.fit(box)
|
|
332
|
+
assert_equal(30, fit_result.x)
|
|
323
333
|
end
|
|
324
334
|
|
|
325
335
|
it "uses position=default if the box indicates it doesn't support flowing contents" do
|
|
@@ -23,47 +23,36 @@ describe HexaPDF::Layout::InlineBox do
|
|
|
23
23
|
ibox = inline_box(box, valign: :top)
|
|
24
24
|
assert_equal(:top, ibox.valign)
|
|
25
25
|
end
|
|
26
|
-
|
|
27
|
-
it "fails if the wrapped box has not width set" do
|
|
28
|
-
box = HexaPDF::Document.new.layout.text("test is not going good")
|
|
29
|
-
assert_raises(HexaPDF::Error) { inline_box(box) }
|
|
30
|
-
end
|
|
31
26
|
end
|
|
32
27
|
|
|
33
28
|
describe "fit_wrapped_box" do
|
|
34
|
-
it "automatically fits the provided box
|
|
35
|
-
ibox = inline_box(HexaPDF::Document.new.layout.text("test is going good",
|
|
29
|
+
it "automatically fits the provided box when given a frame" do
|
|
30
|
+
ibox = inline_box(HexaPDF::Document.new.layout.text("test is going good", margin: [5, 10]))
|
|
36
31
|
ibox.fit_wrapped_box(HexaPDF::Layout::Frame.new(0, 0, 50, 50))
|
|
37
|
-
assert_equal(
|
|
38
|
-
assert_equal(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
assert_equal(20, ibox.width)
|
|
45
|
-
assert_equal(45, ibox.height)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
it "fails if the wrapped box could not be fit" do
|
|
49
|
-
box = HexaPDF::Document.new.layout.text("test is not going good", width: 1)
|
|
50
|
-
assert_raises(HexaPDF::Error) { inline_box(box).fit_wrapped_box(nil) }
|
|
32
|
+
assert_equal(90.84, ibox.width)
|
|
33
|
+
assert_equal(19, ibox.height)
|
|
34
|
+
fit_result = ibox.instance_variable_get(:@fit_result)
|
|
35
|
+
assert_equal(10, fit_result.x)
|
|
36
|
+
assert_equal(5, fit_result.y)
|
|
37
|
+
assert_equal(70.84 + 2 * 10, fit_result.mask.width)
|
|
38
|
+
assert_equal(9 + 2 * 5, fit_result.mask.height)
|
|
51
39
|
end
|
|
52
40
|
|
|
53
|
-
it "
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
41
|
+
it "automatically fits the provided box without a given frame" do
|
|
42
|
+
ibox = inline_box(HexaPDF::Document.new.layout.text("test is going good"))
|
|
43
|
+
ibox.fit_wrapped_box
|
|
44
|
+
assert_equal(70.84, ibox.width)
|
|
45
|
+
assert_equal(9, ibox.height)
|
|
57
46
|
end
|
|
58
47
|
end
|
|
59
48
|
|
|
60
49
|
it "draws the wrapped box at the correct position" do
|
|
61
50
|
doc = HexaPDF::Document.new
|
|
62
51
|
canvas = doc.pages.add.canvas
|
|
63
|
-
box =
|
|
64
|
-
box.fit_wrapped_box
|
|
52
|
+
box = HexaPDF::Layout::InlineBox.create(width: 10, margin: [15, 10]) {}
|
|
53
|
+
box.fit_wrapped_box
|
|
65
54
|
box.draw(canvas, 100, 200)
|
|
66
|
-
assert_equal("q\n1 0 0 1 110
|
|
55
|
+
assert_equal("q\n1 0 0 1 110 215 cm\nQ\n", canvas.contents)
|
|
67
56
|
end
|
|
68
57
|
|
|
69
58
|
it "returns true if the inline box is empty with no drawing operations" do
|
|
@@ -65,13 +65,13 @@ describe HexaPDF::Layout::ListBox do
|
|
|
65
65
|
describe "fit" do
|
|
66
66
|
[:default, :flow].each do |position|
|
|
67
67
|
it "respects the set initial width, position #{position}" do
|
|
68
|
-
box = create_box(children: @text_boxes[0, 2], width:
|
|
69
|
-
check_box(box,
|
|
68
|
+
box = create_box(children: @text_boxes[0, 2], width: 55, style: {position: position})
|
|
69
|
+
check_box(box, 55, 80)
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
it "respects the set initial height, position #{position}" do
|
|
73
|
-
box = create_box(children: @text_boxes[0, 2], height:
|
|
74
|
-
check_box(box, 100,
|
|
73
|
+
box = create_box(children: @text_boxes[0, 2], height: 55, style: {position: position})
|
|
74
|
+
check_box(box, 100, 55)
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
it "respects the set initial height even when it doesn't fit completely" do
|
|
@@ -123,7 +123,7 @@ describe HexaPDF::Layout::ListBox do
|
|
|
123
123
|
|
|
124
124
|
it "fails if not even a part of the first list item fits" do
|
|
125
125
|
box = create_box(children: @text_boxes[0, 2], height: 5)
|
|
126
|
-
check_box(box, 100,
|
|
126
|
+
check_box(box, 100, 5, status: :failure)
|
|
127
127
|
end
|
|
128
128
|
|
|
129
129
|
it "fails for unknown marker types" do
|
|
@@ -44,8 +44,13 @@ describe HexaPDF::Layout::PageStyle do
|
|
|
44
44
|
|
|
45
45
|
it "works when no template is set" do
|
|
46
46
|
style = HexaPDF::Layout::PageStyle.new
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
page1 = style.create_page(@doc)
|
|
48
|
+
frame1 = style.frame
|
|
49
|
+
assert_equal("", page1.contents)
|
|
50
|
+
assert_equal(523.275591, style.frame.width)
|
|
51
|
+
|
|
52
|
+
page2 = style.create_page(@doc)
|
|
53
|
+
refute_same(frame1, style.frame)
|
|
49
54
|
end
|
|
50
55
|
|
|
51
56
|
it "creates a default frame if none is set beforehand or during template execution" do
|