hexapdf 0.28.0 → 0.31.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 +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
|