hexapdf 0.28.0 → 0.31.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 +86 -10
- data/examples/024-digital-signatures.rb +23 -0
- data/lib/hexapdf/cli/command.rb +16 -1
- data/lib/hexapdf/cli/info.rb +9 -1
- data/lib/hexapdf/cli/inspect.rb +2 -2
- data/lib/hexapdf/composer.rb +76 -28
- data/lib/hexapdf/configuration.rb +29 -16
- data/lib/hexapdf/dictionary_fields.rb +13 -4
- data/lib/hexapdf/digital_signature/cms_handler.rb +137 -0
- data/lib/hexapdf/digital_signature/handler.rb +138 -0
- data/lib/hexapdf/digital_signature/pkcs1_handler.rb +96 -0
- data/lib/hexapdf/{type → digital_signature}/signature.rb +3 -8
- data/lib/hexapdf/digital_signature/signatures.rb +210 -0
- data/lib/hexapdf/digital_signature/signing/default_handler.rb +317 -0
- data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +308 -0
- data/lib/hexapdf/digital_signature/signing/timestamp_handler.rb +148 -0
- data/lib/hexapdf/digital_signature/signing.rb +101 -0
- data/lib/hexapdf/{type/signature → digital_signature}/verification_result.rb +37 -41
- data/lib/hexapdf/digital_signature.rb +56 -0
- data/lib/hexapdf/document/pages.rb +31 -18
- data/lib/hexapdf/document.rb +29 -15
- data/lib/hexapdf/encryption/standard_security_handler.rb +4 -3
- data/lib/hexapdf/filter/flate_decode.rb +20 -8
- data/lib/hexapdf/layout/page_style.rb +144 -0
- data/lib/hexapdf/layout.rb +1 -0
- data/lib/hexapdf/task/optimize.rb +8 -6
- data/lib/hexapdf/type/font_simple.rb +14 -2
- data/lib/hexapdf/type/object_stream.rb +7 -2
- data/lib/hexapdf/type/outline.rb +1 -1
- data/lib/hexapdf/type/outline_item.rb +1 -1
- data/lib/hexapdf/type/page.rb +29 -8
- data/lib/hexapdf/type/xref_stream.rb +11 -4
- data/lib/hexapdf/type.rb +0 -1
- data/lib/hexapdf/version.rb +1 -1
- data/lib/hexapdf/writer.rb +1 -1
- data/test/hexapdf/{type/signature → digital_signature}/common.rb +31 -3
- data/test/hexapdf/digital_signature/signing/test_default_handler.rb +162 -0
- data/test/hexapdf/digital_signature/signing/test_signed_data_creator.rb +225 -0
- data/test/hexapdf/digital_signature/signing/test_timestamp_handler.rb +88 -0
- data/test/hexapdf/{type/signature/test_adbe_pkcs7_detached.rb → digital_signature/test_cms_handler.rb} +7 -7
- data/test/hexapdf/{type/signature → digital_signature}/test_handler.rb +4 -4
- data/test/hexapdf/{type/signature/test_adbe_x509_rsa_sha1.rb → digital_signature/test_pkcs1_handler.rb} +3 -3
- data/test/hexapdf/{type → digital_signature}/test_signature.rb +7 -7
- data/test/hexapdf/digital_signature/test_signatures.rb +137 -0
- data/test/hexapdf/digital_signature/test_signing.rb +53 -0
- data/test/hexapdf/{type/signature → digital_signature}/test_verification_result.rb +7 -7
- data/test/hexapdf/document/test_pages.rb +25 -0
- data/test/hexapdf/encryption/test_standard_security_handler.rb +2 -2
- data/test/hexapdf/filter/test_flate_decode.rb +19 -5
- data/test/hexapdf/layout/test_page_style.rb +70 -0
- data/test/hexapdf/task/test_optimize.rb +11 -9
- data/test/hexapdf/test_composer.rb +35 -10
- data/test/hexapdf/test_dictionary_fields.rb +9 -3
- data/test/hexapdf/test_document.rb +1 -1
- data/test/hexapdf/test_writer.rb +8 -8
- data/test/hexapdf/type/test_font_simple.rb +18 -6
- data/test/hexapdf/type/test_object_stream.rb +16 -7
- data/test/hexapdf/type/test_outline.rb +3 -1
- data/test/hexapdf/type/test_outline_item.rb +3 -1
- data/test/hexapdf/type/test_page.rb +42 -11
- data/test/hexapdf/type/test_xref_stream.rb +6 -1
- metadata +27 -15
- data/lib/hexapdf/document/signatures.rb +0 -546
- data/lib/hexapdf/type/signature/adbe_pkcs7_detached.rb +0 -135
- data/lib/hexapdf/type/signature/adbe_x509_rsa_sha1.rb +0 -95
- data/lib/hexapdf/type/signature/handler.rb +0 -140
- data/test/hexapdf/document/test_signatures.rb +0 -352
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
2
|
|
|
3
3
|
require 'test_helper'
|
|
4
|
-
require 'hexapdf/
|
|
4
|
+
require 'hexapdf/digital_signature'
|
|
5
5
|
|
|
6
|
-
describe HexaPDF::
|
|
6
|
+
describe HexaPDF::DigitalSignature::VerificationResult do
|
|
7
7
|
describe "Message" do
|
|
8
8
|
it "accepts a type and a content argument on creation" do
|
|
9
|
-
m = HexaPDF::
|
|
9
|
+
m = HexaPDF::DigitalSignature::VerificationResult::Message.new(:type, 'content')
|
|
10
10
|
assert_equal(:type, m.type)
|
|
11
11
|
assert_equal('content', m.content)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
it "allows sorting by type" do
|
|
15
|
-
info = HexaPDF::
|
|
16
|
-
warning = HexaPDF::
|
|
17
|
-
error = HexaPDF::
|
|
15
|
+
info = HexaPDF::DigitalSignature::VerificationResult::Message.new(:info, 'c')
|
|
16
|
+
warning = HexaPDF::DigitalSignature::VerificationResult::Message.new(:warning, 'c')
|
|
17
|
+
error = HexaPDF::DigitalSignature::VerificationResult::Message.new(:error, 'c')
|
|
18
18
|
assert_equal([error, warning, info], [info, error, warning].sort)
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
before do
|
|
23
|
-
@result = HexaPDF::
|
|
23
|
+
@result = HexaPDF::DigitalSignature::VerificationResult.new
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
it "can add new messages" do
|
|
@@ -14,6 +14,31 @@ describe HexaPDF::Document::Pages do
|
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
+
describe "create" do
|
|
18
|
+
it "uses the defaults from the configuration for missing arguments" do
|
|
19
|
+
page = @doc.pages.create
|
|
20
|
+
assert_equal([0, 0, 595, 842], page.box(:media).value)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "allows specifying a reference to a predefined page size" do
|
|
24
|
+
page = @doc.pages.create(media_box: :A3)
|
|
25
|
+
assert_equal([0, 0, 842, 1191], page.box(:media).value)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "allows specifying the orientation for a predefined page size" do
|
|
29
|
+
page = @doc.pages.create(media_box: :A4, orientation: :landscape)
|
|
30
|
+
assert_equal([0, 0, 842, 595], page.box(:media).value)
|
|
31
|
+
|
|
32
|
+
page = @doc.pages.create(orientation: :landscape)
|
|
33
|
+
assert_equal([0, 0, 842, 595], page.box(:media).value)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "allows using a media box array" do
|
|
37
|
+
page = @doc.pages.create(media_box: [0, 0, 12, 24], orientation: :landscape)
|
|
38
|
+
assert_equal([0, 0, 12, 24], page.box(:media).value)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
17
42
|
describe "add" do
|
|
18
43
|
it "adds a new empty page when no page is given" do
|
|
19
44
|
page = @doc.pages.add
|
|
@@ -213,14 +213,14 @@ describe HexaPDF::Encryption::StandardSecurityHandler do
|
|
|
213
213
|
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
|
214
214
|
@handler.set_up_decryption({Filter: :NonStandard, V: 2})
|
|
215
215
|
end
|
|
216
|
-
assert_match(/Invalid \/Filter/i, exp.message)
|
|
216
|
+
assert_match(/Invalid \/Filter value NonStandard/i, exp.message)
|
|
217
217
|
end
|
|
218
218
|
|
|
219
219
|
it "fails if the /R value is incorrect" do
|
|
220
220
|
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
|
221
221
|
@handler.set_up_decryption({Filter: :Standard, V: 2, R: 5})
|
|
222
222
|
end
|
|
223
|
-
assert_match(/Invalid \/R/i, exp.message)
|
|
223
|
+
assert_match(/Invalid \/R value 5/i, exp.message)
|
|
224
224
|
end
|
|
225
225
|
|
|
226
226
|
it "fails if the supplied password is invalid" do
|
|
@@ -26,12 +26,26 @@ describe HexaPDF::Filter::FlateDecode do
|
|
|
26
26
|
assert_equal(@decoded, collector(@obj.decoder(feeder(@encoded_predictor), @predictor_opts)))
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
describe "invalid input is handled as good as possible" do
|
|
30
|
+
def strict_mode
|
|
31
|
+
HexaPDF::GlobalConfiguration['filter.flate.on_error'] = proc { true }
|
|
32
|
+
yield
|
|
33
|
+
ensure
|
|
34
|
+
HexaPDF::GlobalConfiguration['filter.flate.on_error'] = proc { false }
|
|
32
35
|
end
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
|
|
37
|
+
it "handles completely invalid data" do
|
|
38
|
+
assert_equal('', collector(@obj.decoder(feeder("some data"))))
|
|
39
|
+
assert_raises(HexaPDF::FilterError) do
|
|
40
|
+
strict_mode { collector(@obj.decoder(feeder("some data"))) }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "handles missing data" do
|
|
45
|
+
assert_equal('abcdefg', collector(@obj.decoder(feeder(@encoded[0..-2]))))
|
|
46
|
+
assert_raises(HexaPDF::FilterError) do
|
|
47
|
+
strict_mode { collector(@obj.decoder(feeder(@encoded[0..-2]))) }
|
|
48
|
+
end
|
|
35
49
|
end
|
|
36
50
|
end
|
|
37
51
|
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'hexapdf/layout/page_style'
|
|
5
|
+
require 'hexapdf/document'
|
|
6
|
+
|
|
7
|
+
describe HexaPDF::Layout::PageStyle do
|
|
8
|
+
it "allows assigning the page size, orientation and template on initialization" do
|
|
9
|
+
block = lambda {}
|
|
10
|
+
style = HexaPDF::Layout::PageStyle.new(page_size: :A3, orientation: :landscape, &block)
|
|
11
|
+
assert_equal(:A3, style.page_size)
|
|
12
|
+
assert_equal(:landscape, style.orientation)
|
|
13
|
+
assert_same(block, style.template)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "uses defaults for all values" do
|
|
17
|
+
style = HexaPDF::Layout::PageStyle.new
|
|
18
|
+
assert_equal(:A4, style.page_size)
|
|
19
|
+
assert_equal(:portrait, style.orientation)
|
|
20
|
+
assert_nil(style.template)
|
|
21
|
+
assert_nil(style.frame)
|
|
22
|
+
assert_nil(style.next_style)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe "create_page" do
|
|
26
|
+
before do
|
|
27
|
+
@doc = HexaPDF::Document.new
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "creates a new page object" do
|
|
31
|
+
style = HexaPDF::Layout::PageStyle.new do |canvas, istyle|
|
|
32
|
+
canvas.rectangle(0, 0, 10, 10).stroke
|
|
33
|
+
istyle.frame = :frame
|
|
34
|
+
istyle.next_style = :other
|
|
35
|
+
end
|
|
36
|
+
page = style.create_page(@doc)
|
|
37
|
+
assert_equal([0, 0, 595, 842], page.box(:media))
|
|
38
|
+
assert_equal("0 0 10 10 re\nS\n", page.contents)
|
|
39
|
+
assert_equal(:frame, style.frame)
|
|
40
|
+
assert_equal(:other, style.next_style)
|
|
41
|
+
assert_equal(0, @doc.pages.count)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "works when no template is set" do
|
|
45
|
+
style = HexaPDF::Layout::PageStyle.new
|
|
46
|
+
page = style.create_page(@doc)
|
|
47
|
+
assert_equal("", page.contents)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "creates a default frame if none is set beforehand or during template execution" do
|
|
51
|
+
style = HexaPDF::Layout::PageStyle.new
|
|
52
|
+
style.create_page(@doc)
|
|
53
|
+
assert_kind_of(HexaPDF::Layout::Frame, style.frame)
|
|
54
|
+
assert_equal(36, style.frame.left)
|
|
55
|
+
assert_equal(36, style.frame.bottom)
|
|
56
|
+
assert_equal(523, style.frame.width)
|
|
57
|
+
assert_equal(770, style.frame.height)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "creates new frame objects given a page and a margin specification" do
|
|
62
|
+
doc = HexaPDF::Document.new
|
|
63
|
+
style = HexaPDF::Layout::PageStyle.new
|
|
64
|
+
frame = style.create_frame(style.create_page(doc), [15, 10])
|
|
65
|
+
assert_equal(10, frame.left)
|
|
66
|
+
assert_equal(15, frame.bottom)
|
|
67
|
+
assert_equal(575, frame.width)
|
|
68
|
+
assert_equal(812, frame.height)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -81,8 +81,10 @@ describe HexaPDF::Task::Optimize do
|
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
it "compacts and deletes xref streams" do
|
|
84
|
-
@doc.revisions.all[0].add(@doc.wrap({
|
|
85
|
-
|
|
84
|
+
@doc.revisions.all[0].add(@doc.wrap({}, type: HexaPDF::Type::XRefStream,
|
|
85
|
+
oid: @doc.revisions.next_oid))
|
|
86
|
+
@doc.revisions.all[1].add(@doc.wrap({}, type: HexaPDF::Type::XRefStream,
|
|
87
|
+
oid: @doc.revisions.next_oid))
|
|
86
88
|
@doc.task(:optimize, compact: true, xref_streams: :delete)
|
|
87
89
|
assert_no_xrefstms
|
|
88
90
|
assert_default_deleted
|
|
@@ -92,8 +94,8 @@ describe HexaPDF::Task::Optimize do
|
|
|
92
94
|
describe "object_streams" do
|
|
93
95
|
def reload_document_with_objstm_from_io
|
|
94
96
|
io = StringIO.new
|
|
95
|
-
objstm = @doc.add({
|
|
96
|
-
@doc.add({
|
|
97
|
+
objstm = @doc.add({}, type: HexaPDF::Type::ObjectStream)
|
|
98
|
+
@doc.add({}, type: HexaPDF::Type::XRefStream)
|
|
97
99
|
objstm.add_object(@doc.add({Type: :Test}))
|
|
98
100
|
@doc.write(io)
|
|
99
101
|
io.rewind
|
|
@@ -102,7 +104,7 @@ describe HexaPDF::Task::Optimize do
|
|
|
102
104
|
|
|
103
105
|
it "generates object streams" do
|
|
104
106
|
210.times { @doc.add(5) }
|
|
105
|
-
objstm = @doc.add({
|
|
107
|
+
objstm = @doc.add({}, type: HexaPDF::Type::ObjectStream)
|
|
106
108
|
reload_document_with_objstm_from_io
|
|
107
109
|
@doc.task(:optimize, object_streams: :generate)
|
|
108
110
|
assert_objstms_generated
|
|
@@ -122,8 +124,8 @@ describe HexaPDF::Task::Optimize do
|
|
|
122
124
|
end
|
|
123
125
|
|
|
124
126
|
it "deletes object and generates xref streams" do
|
|
125
|
-
@doc.add({
|
|
126
|
-
xref = @doc.add({
|
|
127
|
+
@doc.add({}, type: HexaPDF::Type::ObjectStream)
|
|
128
|
+
xref = @doc.add({}, type: HexaPDF::Type::XRefStream)
|
|
127
129
|
@doc.task(:optimize, object_streams: :delete, xref_streams: :generate)
|
|
128
130
|
assert_no_objstms
|
|
129
131
|
assert_xrefstms_generated
|
|
@@ -140,13 +142,13 @@ describe HexaPDF::Task::Optimize do
|
|
|
140
142
|
end
|
|
141
143
|
|
|
142
144
|
it "reuses an xref stream in generatation mode" do
|
|
143
|
-
@doc.add({
|
|
145
|
+
@doc.add({}, type: HexaPDF::Type::XRefStream)
|
|
144
146
|
@doc.task(:optimize, xref_streams: :generate)
|
|
145
147
|
assert_xrefstms_generated
|
|
146
148
|
end
|
|
147
149
|
|
|
148
150
|
it "deletes xref streams" do
|
|
149
|
-
@doc.add({
|
|
151
|
+
@doc.add({}, type: HexaPDF::Type::XRefStream)
|
|
150
152
|
@doc.task(:optimize, xref_streams: :delete)
|
|
151
153
|
assert_no_xrefstms
|
|
152
154
|
assert_default_deleted
|
|
@@ -39,6 +39,14 @@ describe HexaPDF::Composer do
|
|
|
39
39
|
assert_equal(682, composer.frame.height)
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
it "allows skipping the initial page creation" do
|
|
43
|
+
composer = HexaPDF::Composer.new(skip_page_creation: true)
|
|
44
|
+
assert_nil(composer.page)
|
|
45
|
+
assert_nil(composer.canvas)
|
|
46
|
+
assert_nil(composer.frame)
|
|
47
|
+
assert_nil(composer.page_style(:default))
|
|
48
|
+
end
|
|
49
|
+
|
|
42
50
|
it "yields itself" do
|
|
43
51
|
yielded = nil
|
|
44
52
|
composer = HexaPDF::Composer.new {|c| yielded = c }
|
|
@@ -56,7 +64,7 @@ describe HexaPDF::Composer do
|
|
|
56
64
|
end
|
|
57
65
|
|
|
58
66
|
describe "new_page" do
|
|
59
|
-
it "creates a new page
|
|
67
|
+
it "creates a new page" do
|
|
60
68
|
c = HexaPDF::Composer.new(page_size: [0, 0, 50, 100], margin: 10)
|
|
61
69
|
c.new_page
|
|
62
70
|
assert_equal([0, 0, 50, 100], c.page.box.value)
|
|
@@ -64,16 +72,33 @@ describe HexaPDF::Composer do
|
|
|
64
72
|
assert_equal(10, c.frame.bottom)
|
|
65
73
|
end
|
|
66
74
|
|
|
67
|
-
it "uses the
|
|
68
|
-
@composer.
|
|
69
|
-
|
|
70
|
-
assert_equal(
|
|
71
|
-
|
|
75
|
+
it "uses the named page style for the new page" do
|
|
76
|
+
@composer.page_style(:other, page_size: [0, 0, 100, 100])
|
|
77
|
+
@composer.new_page(:other)
|
|
78
|
+
assert_equal([0, 0, 100, 100], @composer.page.box.value)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "sets the next page's style to the next_style value of the used page style" do
|
|
82
|
+
@composer.page_style(:one, page_size: [0, 0, 1, 1]).next_style = :two
|
|
83
|
+
@composer.page_style(:two, page_size: [0, 0, 2, 2]).next_style = :one
|
|
84
|
+
@composer.new_page(:one)
|
|
85
|
+
assert_equal([0, 0, 1, 1], @composer.page.box.value)
|
|
86
|
+
@composer.new_page
|
|
87
|
+
assert_equal([0, 0, 2, 2], @composer.page.box.value)
|
|
88
|
+
@composer.new_page
|
|
89
|
+
assert_equal([0, 0, 1, 1], @composer.page.box.value)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "uses the current page style for new pages if no next_style value is set" do
|
|
93
|
+
@composer.page_style(:one, page_size: [0, 0, 1, 1])
|
|
94
|
+
@composer.new_page(:one)
|
|
95
|
+
assert_equal([0, 0, 1, 1], @composer.page.box.value)
|
|
72
96
|
@composer.new_page
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
97
|
+
assert_equal([0, 0, 1, 1], @composer.page.box.value)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "fails if the specified page style has not been defined" do
|
|
101
|
+
assert_raises(ArgumentError) { @composer.new_page(:unknown) }
|
|
77
102
|
end
|
|
78
103
|
end
|
|
79
104
|
|
|
@@ -175,7 +175,6 @@ describe HexaPDF::DictionaryFields do
|
|
|
175
175
|
|
|
176
176
|
it "allows conversion to a Time object from a binary string" do
|
|
177
177
|
refute(@field.convert('test'.b, self))
|
|
178
|
-
refute(@field.convert('D:01211016165909+00\'64'.b, self))
|
|
179
178
|
|
|
180
179
|
[
|
|
181
180
|
["D:1998", [1998, 01, 01, 00, 00, 00, "-00:00"]],
|
|
@@ -189,8 +188,15 @@ describe HexaPDF::DictionaryFields do
|
|
|
189
188
|
["D:19981223-08'00'", [1998, 12, 23, 00, 00, 00, "-08:00"]],
|
|
190
189
|
["D:199812-08'00'", [1998, 12, 01, 00, 00, 00, "-08:00"]],
|
|
191
190
|
["D:1998-08'00'", [1998, 01, 01, 00, 00, 00, "-08:00"]],
|
|
192
|
-
["D:19981223195210-08", [1998, 12, 23, 19, 52, 10, "-08:00"]], #
|
|
193
|
-
["D:19981223195210-08'00", [1998, 12, 23, 19, 52, 10, "-08:00"]], #
|
|
191
|
+
["D:19981223195210-08", [1998, 12, 23, 19, 52, 10, "-08:00"]], # missing '
|
|
192
|
+
["D:19981223195210-08'00", [1998, 12, 23, 19, 52, 10, "-08:00"]], # missing '
|
|
193
|
+
["D:19981223195210-54'00", [1998, 12, 23, 19, 52, 10, "-23:59:59"]], # TZ hour too large
|
|
194
|
+
["D:19981223195210+10'65", [1998, 12, 23, 19, 52, 10, "+11:05"]], # TZ min too large
|
|
195
|
+
["D:19982423195210-08'00'", [1998, 12, 23, 19, 52, 10, "-08:00"]], # months too large
|
|
196
|
+
["D:19981273195210-08'00'", [1998, 12, 31, 19, 52, 10, "-08:00"]], # day too large
|
|
197
|
+
["D:19981223275210-08'00'", [1998, 12, 23, 23, 52, 10, "-08:00"]], # hour too large
|
|
198
|
+
["D:19981223197710-08'00'", [1998, 12, 23, 19, 59, 10, "-08:00"]], # minute too large
|
|
199
|
+
["D:19981223195280-08'00'", [1998, 12, 23, 19, 52, 59, "-08:00"]], # seconds too large
|
|
194
200
|
].each do |str, data|
|
|
195
201
|
obj = @field.convert(str, self)
|
|
196
202
|
assert_equal(Time.new(*data), obj, "date str used: #{str}")
|
|
@@ -504,7 +504,7 @@ describe HexaPDF::Document do
|
|
|
504
504
|
|
|
505
505
|
it "allows to conveniently sign a document" do
|
|
506
506
|
mock = Minitest::Mock.new
|
|
507
|
-
mock.expect(:
|
|
507
|
+
mock.expect(:signing_handler, :handler, name: :handler, opt: :key)
|
|
508
508
|
mock.expect(:add, :added, [:io, :handler], signature: :sig, write_options: :write_options)
|
|
509
509
|
@doc.instance_variable_set(:@signatures, mock)
|
|
510
510
|
result = @doc.sign(:io, handler: :handler, write_options: :write_options, signature: :sig, opt: :key)
|
data/test/hexapdf/test_writer.rb
CHANGED
|
@@ -40,7 +40,7 @@ describe HexaPDF::Writer do
|
|
|
40
40
|
219
|
|
41
41
|
%%EOF
|
|
42
42
|
3 0 obj
|
|
43
|
-
<</Producer(HexaPDF version 0.
|
|
43
|
+
<</Producer(HexaPDF version 0.31.0)>>
|
|
44
44
|
endobj
|
|
45
45
|
xref
|
|
46
46
|
3 1
|
|
@@ -72,7 +72,7 @@ describe HexaPDF::Writer do
|
|
|
72
72
|
141
|
|
73
73
|
%%EOF
|
|
74
74
|
6 0 obj
|
|
75
|
-
<</Producer(HexaPDF version 0.
|
|
75
|
+
<</Producer(HexaPDF version 0.31.0)>>
|
|
76
76
|
endobj
|
|
77
77
|
2 0 obj
|
|
78
78
|
<</Length 10>>stream
|
|
@@ -164,14 +164,14 @@ describe HexaPDF::Writer do
|
|
|
164
164
|
|
|
165
165
|
document = HexaPDF::Document.new(io: io)
|
|
166
166
|
assert_equal(3, document.revisions.count)
|
|
167
|
-
assert_equal(1, document.revisions.all[0].object(
|
|
168
|
-
assert_equal(2, document.revisions.all[1].object(
|
|
169
|
-
assert_equal(3, document.revisions.all[2].object(
|
|
167
|
+
assert_equal(1, document.revisions.all[0].object(3)[:Kids].length)
|
|
168
|
+
assert_equal(2, document.revisions.all[1].object(3)[:Kids].length)
|
|
169
|
+
assert_equal(3, document.revisions.all[2].object(3)[:Kids].length)
|
|
170
170
|
end
|
|
171
171
|
|
|
172
172
|
it "creates an xref stream if no xref stream is in a revision but object streams are" do
|
|
173
173
|
document = HexaPDF::Document.new
|
|
174
|
-
document.add({
|
|
174
|
+
document.add({}, type: HexaPDF::Type::ObjectStream)
|
|
175
175
|
HexaPDF::Writer.new(document, StringIO.new).write
|
|
176
176
|
assert_equal(:XRef, document.object(4).type)
|
|
177
177
|
end
|
|
@@ -184,7 +184,7 @@ describe HexaPDF::Writer do
|
|
|
184
184
|
|
|
185
185
|
document = HexaPDF::Document.new(io: io)
|
|
186
186
|
document.pages.add
|
|
187
|
-
document.add({
|
|
187
|
+
document.add({}, type: HexaPDF::Type::ObjectStream)
|
|
188
188
|
io2 = StringIO.new
|
|
189
189
|
HexaPDF::Writer.new(document, io2).write_incremental
|
|
190
190
|
|
|
@@ -214,7 +214,7 @@ describe HexaPDF::Writer do
|
|
|
214
214
|
<</Type/Page/MediaBox[0 0 595 842]/Parent 2 0 R/Resources<<>>>>
|
|
215
215
|
endobj
|
|
216
216
|
5 0 obj
|
|
217
|
-
<</Producer(HexaPDF version 0.
|
|
217
|
+
<</Producer(HexaPDF version 0.31.0)>>
|
|
218
218
|
endobj
|
|
219
219
|
4 0 obj
|
|
220
220
|
<</Root 1 0 R/Info 5 0 R/Size 6/Type/XRef/W[1 1 2]/Index[0 6]/Filter/FlateDecode/DecodeParms<</Columns 4/Predictor 12>>/Length 33>>stream
|
|
@@ -15,7 +15,7 @@ describe HexaPDF::Type::FontSimple do
|
|
|
15
15
|
EOF
|
|
16
16
|
font_descriptor = @doc.add({Type: :FontDescriptor, FontName: :Embedded, Flags: 0b100,
|
|
17
17
|
FontBBox: [0, 1, 2, 3], ItalicAngle: 0, Ascent: 900,
|
|
18
|
-
Descent: -100, CapHeight: 800, StemV: 20})
|
|
18
|
+
MissingWidth: 500, Descent: -100, CapHeight: 800, StemV: 20})
|
|
19
19
|
@font = @doc.add({Type: :Font, Encoding: :WinAnsiEncoding,
|
|
20
20
|
BaseFont: :Embedded, FontDescriptor: font_descriptor, ToUnicode: cmap,
|
|
21
21
|
FirstChar: 32, LastChar: 34, Widths: [600, 0, 700]},
|
|
@@ -130,9 +130,7 @@ describe HexaPDF::Type::FontSimple do
|
|
|
130
130
|
end
|
|
131
131
|
|
|
132
132
|
it "returns the /MissingWidth of a /FontDescriptor if available and the width was not found" do
|
|
133
|
-
assert_equal(
|
|
134
|
-
@font[:FontDescriptor][:MissingWidth] = 99
|
|
135
|
-
assert_equal(99, @font.width(0))
|
|
133
|
+
assert_equal(500, @font.width(0))
|
|
136
134
|
end
|
|
137
135
|
|
|
138
136
|
it "returns 0 for a missing code point when FontDescriptor is not available" do
|
|
@@ -169,8 +167,22 @@ describe HexaPDF::Type::FontSimple do
|
|
|
169
167
|
end
|
|
170
168
|
|
|
171
169
|
it "validates the lengths of the /Widths field" do
|
|
172
|
-
@font[:Widths] = [65]
|
|
173
|
-
|
|
170
|
+
@font[:Widths] = [65, 65]
|
|
171
|
+
assert(@font.validate)
|
|
172
|
+
assert_equal([65, 65, 65], @font[:Widths])
|
|
173
|
+
|
|
174
|
+
@font[:Widths] = [65, 70]
|
|
175
|
+
assert(@font.validate)
|
|
176
|
+
assert_equal([65, 70, 500], @font[:Widths])
|
|
177
|
+
|
|
178
|
+
@font[:Widths] = [65, 70]
|
|
179
|
+
@font.delete(:FontDescriptor)
|
|
180
|
+
assert(@font.validate)
|
|
181
|
+
assert_equal([65, 70, 0], @font[:Widths])
|
|
182
|
+
|
|
183
|
+
@font[:Widths] = [65, 65, 70, 90, 100]
|
|
184
|
+
assert(@font.validate)
|
|
185
|
+
assert_equal([65, 65, 70], @font[:Widths])
|
|
174
186
|
end
|
|
175
187
|
end
|
|
176
188
|
end
|
|
@@ -123,12 +123,21 @@ describe HexaPDF::Type::ObjectStream do
|
|
|
123
123
|
end
|
|
124
124
|
end
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
126
|
+
describe "perform_validation" do
|
|
127
|
+
it "fails validation if gen != 0" do
|
|
128
|
+
assert(@obj.validate(auto_correct: false))
|
|
129
|
+
@obj.gen = 1
|
|
130
|
+
refute(@obj.validate(auto_correct: false) do |msg, correctable|
|
|
131
|
+
assert_match(/invalid generation/, msg)
|
|
132
|
+
refute(correctable)
|
|
133
|
+
end)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it "sets the /N and /First entries to dummy values so that validation works" do
|
|
137
|
+
@obj = HexaPDF::Type::ObjectStream.new({}, oid: 1, document: @doc)
|
|
138
|
+
assert(@obj.validate(auto_correct: false))
|
|
139
|
+
assert_equal(0, @obj[:N])
|
|
140
|
+
assert_equal(0, @obj[:First])
|
|
141
|
+
end
|
|
133
142
|
end
|
|
134
143
|
end
|
|
@@ -30,11 +30,12 @@ describe HexaPDF::Type::Outline do
|
|
|
30
30
|
|
|
31
31
|
describe "perform_validation" do
|
|
32
32
|
before do
|
|
33
|
-
5.times { @outline.add_item("Test1") }
|
|
33
|
+
@outline_items = 5.times.map { @outline.add_item("Test1") }
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
it "fixes a missing /First entry" do
|
|
37
37
|
@outline.delete(:First)
|
|
38
|
+
@outline_items[0][:Prev] = HexaPDF::Reference.new(100)
|
|
38
39
|
called = false
|
|
39
40
|
@outline.validate do |msg, correctable, _|
|
|
40
41
|
called = true
|
|
@@ -46,6 +47,7 @@ describe HexaPDF::Type::Outline do
|
|
|
46
47
|
|
|
47
48
|
it "fixes a missing /Last entry" do
|
|
48
49
|
@outline.delete(:Last)
|
|
50
|
+
@outline_items[4][:Next] = HexaPDF::Reference.new(100)
|
|
49
51
|
called = false
|
|
50
52
|
@outline.validate do |msg, correctable, _|
|
|
51
53
|
called = true
|
|
@@ -276,12 +276,13 @@ describe HexaPDF::Type::OutlineItem do
|
|
|
276
276
|
|
|
277
277
|
describe "perform_validation" do
|
|
278
278
|
before do
|
|
279
|
-
5.times { @item.add_item("Test1") }
|
|
279
|
+
@outline_items = 5.times.map { @item.add_item("Test1") }
|
|
280
280
|
@item[:Parent] = @doc.add({})
|
|
281
281
|
end
|
|
282
282
|
|
|
283
283
|
it "fixes a missing /First entry" do
|
|
284
284
|
@item.delete(:First)
|
|
285
|
+
@outline_items[0][:Prev] = HexaPDF::Reference.new(100)
|
|
285
286
|
called = false
|
|
286
287
|
@item.validate do |msg, correctable, _|
|
|
287
288
|
called = true
|
|
@@ -293,6 +294,7 @@ describe HexaPDF::Type::OutlineItem do
|
|
|
293
294
|
|
|
294
295
|
it "fixes a missing /Last entry" do
|
|
295
296
|
@item.delete(:Last)
|
|
297
|
+
@outline_items[4][:Next] = HexaPDF::Reference.new(100)
|
|
296
298
|
called = false
|
|
297
299
|
@item.validate do |msg, correctable, _|
|
|
298
300
|
called = true
|
|
@@ -19,9 +19,21 @@ describe HexaPDF::Type::Page do
|
|
|
19
19
|
assert_equal([0, 0, 842, 595], HexaPDF::Type::Page.media_box(:A4, orientation: :landscape))
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
it "works with a paper size array" do
|
|
23
|
+
assert_equal([0, 0, 842, 595], HexaPDF::Type::Page.media_box([0, 0, 842, 595]))
|
|
24
|
+
end
|
|
25
|
+
|
|
22
26
|
it "fails if the paper size is unknown" do
|
|
23
27
|
assert_raises(HexaPDF::Error) { HexaPDF::Type::Page.media_box(:Unknown) }
|
|
24
28
|
end
|
|
29
|
+
|
|
30
|
+
it "fails if the array doesn't contain four numbers" do
|
|
31
|
+
assert_raises(HexaPDF::Error) { HexaPDF::Type::Page.media_box([0, 1, 2]) }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "fails if the array doesn't contain only numbers" do
|
|
35
|
+
assert_raises(HexaPDF::Error) { HexaPDF::Type::Page.media_box([0, 1, 2, 'a']) }
|
|
36
|
+
end
|
|
25
37
|
end
|
|
26
38
|
|
|
27
39
|
# Asserts that the page's contents contains the operators.
|
|
@@ -70,22 +82,41 @@ describe HexaPDF::Type::Page do
|
|
|
70
82
|
end
|
|
71
83
|
end
|
|
72
84
|
|
|
73
|
-
describe "
|
|
85
|
+
describe "perform_validation" do
|
|
74
86
|
it "only does validation if the page is in the document's page tree" do
|
|
75
87
|
page = @doc.add({Type: :Page})
|
|
88
|
+
assert(page.validate(auto_correct: false))
|
|
89
|
+
page[:Parent] = @doc.add({Type: :Pages, Kids: [page], Count: 1})
|
|
90
|
+
assert(page.validate(auto_correct: false))
|
|
91
|
+
@doc.pages.add(page)
|
|
92
|
+
refute(page.validate(auto_correct: false))
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "validates that the required inheritable field /Resources is set" do
|
|
96
|
+
page = @doc.pages.add
|
|
97
|
+
page.delete(:Resources)
|
|
98
|
+
refute(page.validate(auto_correct: false))
|
|
76
99
|
assert(page.validate)
|
|
77
|
-
|
|
78
|
-
assert(page.validate)
|
|
79
|
-
page[:Parent] = @doc.catalog.pages
|
|
80
|
-
refute(page.validate)
|
|
100
|
+
assert_kind_of(HexaPDF::Dictionary, page[:Resources])
|
|
81
101
|
end
|
|
82
102
|
|
|
83
|
-
it "
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
103
|
+
it "validates that the required inheritable field /MediaBox is set" do
|
|
104
|
+
page1 = @doc.pages.add(:Letter)
|
|
105
|
+
page2 = @doc.pages.add(:Letter)
|
|
106
|
+
page3 = @doc.pages.add(:Letter)
|
|
107
|
+
|
|
108
|
+
[page1, page2, page3].each do |page|
|
|
109
|
+
page.delete(:MediaBox)
|
|
110
|
+
refute(page.validate(auto_correct: false))
|
|
111
|
+
assert(page.validate)
|
|
112
|
+
assert_equal([0, 0, 612, 792], page[:MediaBox])
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
page2.delete(:MediaBox)
|
|
116
|
+
page1[:MediaBox] = [0, 0, 1, 1]
|
|
117
|
+
refute(page2.validate(auto_correct: false))
|
|
118
|
+
assert(page2.validate)
|
|
119
|
+
assert_equal([0, 0, 595, 842], page2[:MediaBox])
|
|
89
120
|
end
|
|
90
121
|
end
|
|
91
122
|
|
|
@@ -6,9 +6,10 @@ require 'hexapdf/type/xref_stream'
|
|
|
6
6
|
describe HexaPDF::Type::XRefStream do
|
|
7
7
|
before do
|
|
8
8
|
@doc = Object.new
|
|
9
|
+
@doc.instance_variable_set(:@version, '1.5')
|
|
9
10
|
def (@doc).deref(obj); obj; end
|
|
10
11
|
def (@doc).wrap(obj, **); obj; end
|
|
11
|
-
@obj = HexaPDF::Type::XRefStream.new({}, oid: 1, document: @doc)
|
|
12
|
+
@obj = HexaPDF::Type::XRefStream.new({}, oid: 1, document: @doc, stream: '')
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
describe "xref_section" do
|
|
@@ -141,4 +142,8 @@ describe HexaPDF::Type::XRefStream do
|
|
|
141
142
|
assert_raises(HexaPDF::Error) { @obj.update_with_xref_section_and_trailer(@section, {}) }
|
|
142
143
|
end
|
|
143
144
|
end
|
|
145
|
+
|
|
146
|
+
it "sets /Size and /W to dummy values to make validation work" do
|
|
147
|
+
assert(@obj.validate(auto_correct: false))
|
|
148
|
+
end
|
|
144
149
|
end
|