hexapdf 0.12.1 → 0.14.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +130 -0
- data/examples/019-acro_form.rb +41 -4
- data/lib/hexapdf/cli/command.rb +4 -2
- data/lib/hexapdf/cli/image2pdf.rb +2 -1
- data/lib/hexapdf/cli/info.rb +51 -2
- data/lib/hexapdf/cli/inspect.rb +30 -8
- data/lib/hexapdf/cli/merge.rb +1 -1
- data/lib/hexapdf/cli/split.rb +74 -14
- data/lib/hexapdf/configuration.rb +15 -0
- data/lib/hexapdf/content/graphic_object/arc.rb +3 -3
- data/lib/hexapdf/content/parser.rb +1 -1
- data/lib/hexapdf/dictionary.rb +9 -6
- data/lib/hexapdf/dictionary_fields.rb +1 -9
- data/lib/hexapdf/document.rb +41 -16
- data/lib/hexapdf/document/files.rb +0 -1
- data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
- data/lib/hexapdf/encryption/security_handler.rb +1 -0
- data/lib/hexapdf/encryption/standard_security_handler.rb +1 -0
- data/lib/hexapdf/font/cmap.rb +1 -4
- data/lib/hexapdf/font/true_type/subsetter.rb +12 -3
- data/lib/hexapdf/font/true_type/table/head.rb +1 -0
- data/lib/hexapdf/font/true_type/table/os2.rb +2 -0
- data/lib/hexapdf/font/true_type/table/post.rb +15 -10
- data/lib/hexapdf/font_loader/from_configuration.rb +2 -2
- data/lib/hexapdf/font_loader/from_file.rb +18 -8
- data/lib/hexapdf/image_loader/png.rb +3 -2
- data/lib/hexapdf/importer.rb +3 -2
- data/lib/hexapdf/layout/line.rb +1 -1
- data/lib/hexapdf/layout/style.rb +23 -23
- data/lib/hexapdf/layout/text_layouter.rb +2 -2
- data/lib/hexapdf/layout/text_shaper.rb +3 -2
- data/lib/hexapdf/object.rb +52 -25
- data/lib/hexapdf/parser.rb +96 -4
- data/lib/hexapdf/pdf_array.rb +12 -5
- data/lib/hexapdf/revisions.rb +29 -21
- data/lib/hexapdf/serializer.rb +34 -8
- data/lib/hexapdf/task/optimize.rb +6 -4
- data/lib/hexapdf/tokenizer.rb +4 -3
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +132 -28
- data/lib/hexapdf/type/acro_form/button_field.rb +21 -13
- data/lib/hexapdf/type/acro_form/choice_field.rb +68 -14
- data/lib/hexapdf/type/acro_form/field.rb +35 -5
- data/lib/hexapdf/type/acro_form/form.rb +139 -14
- data/lib/hexapdf/type/acro_form/text_field.rb +70 -4
- data/lib/hexapdf/type/actions/uri.rb +3 -2
- data/lib/hexapdf/type/annotations/widget.rb +3 -4
- data/lib/hexapdf/type/catalog.rb +2 -2
- data/lib/hexapdf/type/cid_font.rb +1 -1
- data/lib/hexapdf/type/file_specification.rb +1 -1
- data/lib/hexapdf/type/font.rb +1 -1
- data/lib/hexapdf/type/font_simple.rb +4 -2
- data/lib/hexapdf/type/font_true_type.rb +6 -2
- data/lib/hexapdf/type/font_type0.rb +4 -4
- data/lib/hexapdf/type/form.rb +15 -2
- data/lib/hexapdf/type/image.rb +2 -2
- data/lib/hexapdf/type/page.rb +37 -13
- data/lib/hexapdf/type/page_tree_node.rb +29 -5
- data/lib/hexapdf/type/resources.rb +1 -0
- data/lib/hexapdf/type/trailer.rb +2 -3
- data/lib/hexapdf/utils/object_hash.rb +0 -1
- data/lib/hexapdf/utils/sorted_tree_node.rb +18 -15
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/common_tokenizer_tests.rb +6 -1
- data/test/hexapdf/content/graphic_object/test_arc.rb +4 -4
- data/test/hexapdf/content/test_canvas.rb +3 -3
- data/test/hexapdf/content/test_color_space.rb +1 -1
- data/test/hexapdf/encryption/test_aes.rb +4 -4
- data/test/hexapdf/encryption/test_standard_security_handler.rb +11 -11
- data/test/hexapdf/filter/test_ascii85_decode.rb +1 -1
- data/test/hexapdf/filter/test_ascii_hex_decode.rb +1 -1
- data/test/hexapdf/font/true_type/table/test_post.rb +1 -1
- data/test/hexapdf/font/true_type/test_subsetter.rb +5 -0
- data/test/hexapdf/font_loader/test_from_configuration.rb +7 -3
- data/test/hexapdf/font_loader/test_from_file.rb +7 -0
- data/test/hexapdf/layout/test_style.rb +1 -1
- data/test/hexapdf/layout/test_text_layouter.rb +12 -5
- data/test/hexapdf/test_configuration.rb +2 -2
- data/test/hexapdf/test_dictionary.rb +8 -1
- data/test/hexapdf/test_dictionary_fields.rb +2 -2
- data/test/hexapdf/test_document.rb +18 -10
- data/test/hexapdf/test_object.rb +71 -26
- data/test/hexapdf/test_parser.rb +171 -53
- data/test/hexapdf/test_pdf_array.rb +8 -1
- data/test/hexapdf/test_revisions.rb +35 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +296 -38
- data/test/hexapdf/type/acro_form/test_button_field.rb +22 -2
- data/test/hexapdf/type/acro_form/test_choice_field.rb +92 -9
- data/test/hexapdf/type/acro_form/test_field.rb +39 -0
- data/test/hexapdf/type/acro_form/test_form.rb +87 -15
- data/test/hexapdf/type/acro_form/test_text_field.rb +77 -1
- data/test/hexapdf/type/test_font_simple.rb +2 -1
- data/test/hexapdf/type/test_font_true_type.rb +6 -0
- data/test/hexapdf/type/test_form.rb +26 -1
- data/test/hexapdf/type/test_page.rb +45 -7
- data/test/hexapdf/type/test_page_tree_node.rb +42 -0
- data/test/hexapdf/utils/test_bit_field.rb +2 -0
- data/test/hexapdf/utils/test_object_hash.rb +5 -0
- data/test/hexapdf/utils/test_sorted_tree_node.rb +10 -9
- data/test/test_helper.rb +2 -0
- metadata +6 -11
data/lib/hexapdf/version.rb
CHANGED
@@ -121,6 +121,11 @@ module CommonTokenizerTests
|
|
121
121
|
assert(token.kind_of?(HexaPDF::Tokenizer::Token))
|
122
122
|
end
|
123
123
|
|
124
|
+
it "next_token: should not fail when reading super long numbers" do
|
125
|
+
create_tokenizer("1" << "0" * 10_000)
|
126
|
+
assert_equal(10**10_000, @tokenizer.next_token)
|
127
|
+
end
|
128
|
+
|
124
129
|
it "next_object: works for all PDF object types, including array and dictionary" do
|
125
130
|
create_tokenizer(<<-EOF.chomp.gsub(/^ {8}/, ''))
|
126
131
|
true false null 123 34.5 (string) <4E6F76> /Name
|
@@ -157,7 +162,7 @@ module CommonTokenizerTests
|
|
157
162
|
end
|
158
163
|
|
159
164
|
it "returns the correct position on operations" do
|
160
|
-
create_tokenizer("hallo du"
|
165
|
+
create_tokenizer("hallo du" << " " * 50000 << "hallo du")
|
161
166
|
@tokenizer.next_token
|
162
167
|
assert_equal(5, @tokenizer.pos)
|
163
168
|
|
@@ -68,14 +68,14 @@ describe HexaPDF::Content::GraphicObject::Arc do
|
|
68
68
|
arc.max_curves = 4
|
69
69
|
curves = arc.curves
|
70
70
|
assert_equal(2, curves.size)
|
71
|
-
assert_curve_values([0, 1, p1: [1, 0.548584], p2: [0.548584, 1]], curves[0])
|
72
|
-
assert_curve_values([-1, 0, p1: [-0.548584, 1], p2: [-1, 0.548584]], curves[1])
|
71
|
+
assert_curve_values([0, 1, {p1: [1, 0.548584], p2: [0.548584, 1]}], curves[0])
|
72
|
+
assert_curve_values([-1, 0, {p1: [-0.548584, 1], p2: [-1, 0.548584]}], curves[1])
|
73
73
|
|
74
74
|
arc.configure(clockwise: true)
|
75
75
|
curves = arc.curves
|
76
76
|
assert_equal(2, curves.size)
|
77
|
-
assert_curve_values([0, -1, p1: [1, -0.548584], p2: [0.548584, -1]], curves[0])
|
78
|
-
assert_curve_values([-1, 0, p1: [-0.548584, -1], p2: [-1, -0.548584]], curves[1])
|
77
|
+
assert_curve_values([0, -1, {p1: [1, -0.548584], p2: [0.548584, -1]}], curves[0])
|
78
|
+
assert_curve_values([-1, 0, {p1: [-0.548584, -1], p2: [-1, -0.548584]}], curves[1])
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
@@ -531,7 +531,7 @@ describe HexaPDF::Content::Canvas do
|
|
531
531
|
end
|
532
532
|
|
533
533
|
it "invokes the polygon method when radius != 0" do
|
534
|
-
args = [0, 0, 10, 0, 10, 10, 0, 10, radius: 5]
|
534
|
+
args = [0, 0, 10, 0, 10, 10, 0, 10, {radius: 5}]
|
535
535
|
assert_method_invoked(@canvas, :polygon, args) do
|
536
536
|
@canvas.rectangle(0, 0, 10, 10, radius: 5)
|
537
537
|
end
|
@@ -631,7 +631,7 @@ describe HexaPDF::Content::Canvas do
|
|
631
631
|
|
632
632
|
describe "circle" do
|
633
633
|
it "uses arc for the hard work" do
|
634
|
-
assert_method_invoked(@canvas, :arc, [5, 6, a: 7]) do
|
634
|
+
assert_method_invoked(@canvas, :arc, [5, 6, {a: 7}]) do
|
635
635
|
@canvas.graphics_object = :path
|
636
636
|
@canvas.circle(5, 6, 7)
|
637
637
|
end
|
@@ -651,7 +651,7 @@ describe HexaPDF::Content::Canvas do
|
|
651
651
|
|
652
652
|
describe "ellipse" do
|
653
653
|
it "uses arc for the hard work" do
|
654
|
-
assert_method_invoked(@canvas, :ellipse, [5, 6, a: 7, b: 5, inclination: 10]) do
|
654
|
+
assert_method_invoked(@canvas, :ellipse, [5, 6, {a: 7, b: 5, inclination: 10}]) do
|
655
655
|
@canvas.ellipse(5, 6, a: 7, b: 5, inclination: 10)
|
656
656
|
end
|
657
657
|
end
|
@@ -155,7 +155,7 @@ describe HexaPDF::Content::ColorSpace::DeviceGray do
|
|
155
155
|
|
156
156
|
before do
|
157
157
|
@color_space = HexaPDF::Content::ColorSpace::DeviceGray.new
|
158
|
-
@color_space_family = @color_space_definition =
|
158
|
+
@color_space_family = @color_space_definition = :DeviceGray
|
159
159
|
@color = @color_space.default_color
|
160
160
|
@other_color = @color_space.color(128)
|
161
161
|
@colors = [128]
|
@@ -101,13 +101,13 @@ describe HexaPDF::Encryption::AES do
|
|
101
101
|
result = TestHelper.collector(@algorithm_class.decryption_fiber('some' * 4, f))
|
102
102
|
assert_equal('a' * 16, result)
|
103
103
|
|
104
|
-
f = Fiber.new { 'a' * 31
|
104
|
+
f = Fiber.new { 'a' * 31 << "\x00" }
|
105
105
|
result = TestHelper.collector(@algorithm_class.decryption_fiber('some' * 4, f))
|
106
|
-
assert_equal('a' * 15
|
106
|
+
assert_equal('a' * 15 << "\x00", result)
|
107
107
|
|
108
|
-
f = Fiber.new { 'a' * 29
|
108
|
+
f = Fiber.new { 'a' * 29 << "\x00\x01\x03" }
|
109
109
|
result = TestHelper.collector(@algorithm_class.decryption_fiber('some' * 4, f))
|
110
|
-
assert_equal('a' * 13
|
110
|
+
assert_equal('a' * 13 << "\x00\x01\x03", result)
|
111
111
|
end
|
112
112
|
|
113
113
|
it "fails on decryption if not enough bytes are provided" do
|
@@ -53,24 +53,24 @@ describe HexaPDF::Encryption::StandardEncryptionDictionary do
|
|
53
53
|
end
|
54
54
|
|
55
55
|
describe HexaPDF::Encryption::StandardSecurityHandler do
|
56
|
-
|
57
|
-
|
58
|
-
|
56
|
+
test_files = Dir[File.join(TEST_DATA_DIR, 'standard-security-handler', '*.pdf')].sort
|
57
|
+
user_password = 'uhexapdf'
|
58
|
+
owner_password = 'ohexapdf'
|
59
59
|
|
60
|
-
|
60
|
+
minimal_doc = HexaPDF::Document.new(io: StringIO.new(MINIMAL_PDF))
|
61
61
|
|
62
|
-
|
62
|
+
test_files.each do |file|
|
63
63
|
basename = File.basename(file)
|
64
64
|
it "can decrypt, encrypt and decrypt the encrypted file #{basename} with the user password" do
|
65
65
|
begin
|
66
66
|
doc = HexaPDF::Document.new(io: StringIO.new(File.binread(file)),
|
67
|
-
decryption_opts: {password:
|
68
|
-
assert_equal(
|
67
|
+
decryption_opts: {password: user_password})
|
68
|
+
assert_equal(minimal_doc.trailer[:Info][:ModDate], doc.trailer[:Info][:ModDate])
|
69
69
|
|
70
70
|
out = StringIO.new(''.b)
|
71
71
|
HexaPDF::Writer.new(doc, out).write
|
72
|
-
doc = HexaPDF::Document.new(io: out, decryption_opts: {password:
|
73
|
-
assert_equal(
|
72
|
+
doc = HexaPDF::Document.new(io: out, decryption_opts: {password: user_password})
|
73
|
+
assert_equal(minimal_doc.trailer[:Info][:ModDate], doc.trailer[:Info][:ModDate])
|
74
74
|
rescue HexaPDF::EncryptionError => e
|
75
75
|
flunk("Error processing #{basename}: #{e}")
|
76
76
|
end
|
@@ -80,8 +80,8 @@ describe HexaPDF::Encryption::StandardSecurityHandler do
|
|
80
80
|
it "can decrypt the encrypted file #{basename} with the owner password" do
|
81
81
|
begin
|
82
82
|
doc = HexaPDF::Document.new(io: StringIO.new(File.binread(file)),
|
83
|
-
decryption_opts: {password:
|
84
|
-
assert_equal(
|
83
|
+
decryption_opts: {password: owner_password})
|
84
|
+
assert_equal(minimal_doc.trailer[:Info][:ModDate], doc.trailer[:Info][:ModDate])
|
85
85
|
rescue HexaPDF::EncryptionError => e
|
86
86
|
flunk("Error processing #{basename}: #{e}")
|
87
87
|
end
|
@@ -33,7 +33,7 @@ describe HexaPDF::Filter::ASCII85Decode do
|
|
33
33
|
end
|
34
34
|
|
35
35
|
it "ignores data after the EOD marker" do
|
36
|
-
assert_equal(@decoded, collector(@obj.decoder(feeder(@encoded
|
36
|
+
assert_equal(@decoded, collector(@obj.decoder(feeder(@encoded << "~>abcdefg"))))
|
37
37
|
end
|
38
38
|
|
39
39
|
it "fails if the input contains invalid characters" do
|
@@ -24,7 +24,7 @@ describe HexaPDF::Filter::ASCIIHexDecode do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
it "ignores data after the EOD marker" do
|
27
|
-
assert_equal(@decoded, collector(@obj.decoder(feeder(@encoded
|
27
|
+
assert_equal(@decoded, collector(@obj.decoder(feeder(@encoded << '4e6f7gzz'))))
|
28
28
|
end
|
29
29
|
|
30
30
|
it "assumes the missing char is '0' if the input length is odd" do
|
@@ -72,7 +72,7 @@ describe HexaPDF::Font::TrueType::Table::Post do
|
|
72
72
|
assert_equal('.notdef', table[0])
|
73
73
|
|
74
74
|
@font.config['font.true_type.unknown_format'] = :raise
|
75
|
-
assert_raises(HexaPDF::Error) { create_table(:Post) }
|
75
|
+
assert_raises(HexaPDF::Error) { create_table(:Post)[0] }
|
76
76
|
end
|
77
77
|
end
|
78
78
|
end
|
@@ -27,6 +27,11 @@ describe HexaPDF::Font::TrueType::Subsetter do
|
|
27
27
|
assert_equal(value, @subsetter.subset_glyph_id(5))
|
28
28
|
end
|
29
29
|
|
30
|
+
it "doesn't use certain subset glyph IDs for performance reasons" do
|
31
|
+
1.upto(13) {|i| @subsetter.use_glyph(i) }
|
32
|
+
assert_equal(14, @subsetter.subset_glyph_id(13))
|
33
|
+
end
|
34
|
+
|
30
35
|
it "creates the subset font file" do
|
31
36
|
gid = @font[:cmap].preferred_table[0x41]
|
32
37
|
@subsetter.use_glyph(gid)
|
@@ -8,13 +8,17 @@ describe HexaPDF::FontLoader::FromConfiguration do
|
|
8
8
|
before do
|
9
9
|
@doc = HexaPDF::Document.new
|
10
10
|
font_file = File.join(TEST_DATA_DIR, "fonts", "Ubuntu-Title.ttf")
|
11
|
-
@
|
11
|
+
@font_obj = HexaPDF::Font::TrueType::Font.new(File.open(font_file, 'rb'))
|
12
|
+
@doc.config['font.map'] = {'font' => {none: font_file}, 'font1' => {none: @font_obj}}
|
12
13
|
@klass = HexaPDF::FontLoader::FromConfiguration
|
13
14
|
end
|
14
15
|
|
15
16
|
it "loads the configured font" do
|
16
17
|
wrapper = @klass.call(@doc, "font")
|
17
18
|
assert_equal("Ubuntu-Title", wrapper.wrapped_font.font_name)
|
19
|
+
wrapper = @klass.call(@doc, "font1")
|
20
|
+
assert_equal("Ubuntu-Title", wrapper.wrapped_font.font_name)
|
21
|
+
assert_same(@font_obj, wrapper.wrapped_font)
|
18
22
|
end
|
19
23
|
|
20
24
|
it "passes the subset value to the wrapper" do
|
@@ -24,7 +28,7 @@ describe HexaPDF::FontLoader::FromConfiguration do
|
|
24
28
|
refute(wrapper.subset?)
|
25
29
|
end
|
26
30
|
|
27
|
-
it "fails if the font
|
31
|
+
it "fails if the provided font is invalid" do
|
28
32
|
@doc.config['font.map']['font'][:none] << "unknown"
|
29
33
|
assert_raises(HexaPDF::Error) { @klass.call(@doc, "font") }
|
30
34
|
end
|
@@ -34,6 +38,6 @@ describe HexaPDF::FontLoader::FromConfiguration do
|
|
34
38
|
end
|
35
39
|
|
36
40
|
it "returns a hash with all configured fonts" do
|
37
|
-
assert_equal({'font' => [:none]}, @klass.available_fonts(@doc))
|
41
|
+
assert_equal({'font' => [:none], 'font1' => [:none]}, @klass.available_fonts(@doc))
|
38
42
|
end
|
39
43
|
end
|
@@ -16,6 +16,13 @@ describe HexaPDF::FontLoader::FromFile do
|
|
16
16
|
assert_equal("Ubuntu-Title", wrapper.wrapped_font.font_name)
|
17
17
|
end
|
18
18
|
|
19
|
+
it "loads the specified font object" do
|
20
|
+
font = HexaPDF::Font::TrueType::Font.new(File.open(@font_file, 'rb'))
|
21
|
+
wrapper = @klass.call(@doc, font)
|
22
|
+
assert_equal("Ubuntu-Title", wrapper.wrapped_font.font_name)
|
23
|
+
assert_same(font, wrapper.wrapped_font)
|
24
|
+
end
|
25
|
+
|
19
26
|
it "passes the subset value to the wrapper" do
|
20
27
|
wrapper = @klass.call(@doc, @font_file)
|
21
28
|
assert(wrapper.subset?)
|
@@ -537,7 +537,7 @@ describe HexaPDF::Layout::Style::LinkLayer do
|
|
537
537
|
end
|
538
538
|
|
539
539
|
it "does nothing if the context is not a page object" do
|
540
|
-
@canvas = HexaPDF::Document.new.add({Type: :XObject, Subtype: :Form}).canvas
|
540
|
+
@canvas = HexaPDF::Document.new.add({Type: :XObject, Subtype: :Form, BBox: [0, 0, 1, 1]}).canvas
|
541
541
|
assert_nil(call_link(dest: true))
|
542
542
|
end
|
543
543
|
|
@@ -591,25 +591,33 @@ describe HexaPDF::Layout::TextLayouter do
|
|
591
591
|
|
592
592
|
describe "horizontal alignment" do
|
593
593
|
before do
|
594
|
-
@items = boxes(*[[20, 20]] * 4)
|
594
|
+
@items = boxes(*[[20, 20]] * 4) + [glue(10), penalty(-5000, boxes(0).first.item)]
|
595
595
|
end
|
596
596
|
|
597
597
|
it "aligns the contents to the left" do
|
598
598
|
@style.align = :left
|
599
599
|
result = @layouter.fit(@items, 100, 100)
|
600
600
|
assert_equal(0, result.lines[0].x_offset)
|
601
|
+
assert_equal(80, result.lines[0].width)
|
602
|
+
result = @layouter.fit(@items, proc { 100 }, 100)
|
603
|
+
assert_equal(0, result.lines[0].x_offset)
|
604
|
+
assert_equal(80, result.lines[0].width)
|
601
605
|
end
|
602
606
|
|
603
607
|
it "aligns the contents to the center" do
|
604
608
|
@style.align = :center
|
605
609
|
result = @layouter.fit(@items, 100, 100)
|
606
610
|
assert_equal(10, result.lines[0].x_offset)
|
611
|
+
result = @layouter.fit(@items, proc { 100 }, 100)
|
612
|
+
assert_equal(10, result.lines[0].x_offset)
|
607
613
|
end
|
608
614
|
|
609
615
|
it "aligns the contents to the right" do
|
610
616
|
@style.align = :right
|
611
617
|
result = @layouter.fit(@items, 100, 100)
|
612
618
|
assert_equal(20, result.lines[0].x_offset)
|
619
|
+
result = @layouter.fit(@items, proc { 100 }, 100)
|
620
|
+
assert_equal(20, result.lines[0].x_offset)
|
613
621
|
end
|
614
622
|
end
|
615
623
|
|
@@ -674,10 +682,9 @@ describe HexaPDF::Layout::TextLayouter do
|
|
674
682
|
pos = [0, 0]
|
675
683
|
result.select! {|name, _| name == :set_text_matrix || name == :move_text_next_line }.
|
676
684
|
map! do |name, ops|
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
pos[1] -= leading
|
685
|
+
case name
|
686
|
+
when :set_text_matrix then pos = ops[-2, 2]
|
687
|
+
when :move_text_next_line then pos[1] -= leading
|
681
688
|
end
|
682
689
|
pos.dup
|
683
690
|
end
|
@@ -66,8 +66,8 @@ describe HexaPDF::Configuration do
|
|
66
66
|
assert_equal(HexaPDF, @config.constantize('test', 1))
|
67
67
|
end
|
68
68
|
|
69
|
-
def assert_constantize_error # :nodoc:
|
70
|
-
exp = assert_raises(HexaPDF::Error)
|
69
|
+
def assert_constantize_error(&block) # :nodoc:
|
70
|
+
exp = assert_raises(HexaPDF::Error, &block)
|
71
71
|
assert_match(/Error getting constant for configuration option/, exp.message)
|
72
72
|
end
|
73
73
|
|
@@ -14,7 +14,9 @@ describe HexaPDF::Dictionary do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def add(obj)
|
17
|
-
HexaPDF::Object
|
17
|
+
klass = HexaPDF::Object
|
18
|
+
klass = HexaPDF::Dictionary if obj.kind_of?(HexaPDF::Dictionary) || obj.kind_of?(Hash)
|
19
|
+
klass.new(obj, oid: 1)
|
18
20
|
end
|
19
21
|
|
20
22
|
def delete(_obj)
|
@@ -281,6 +283,11 @@ describe HexaPDF::Dictionary do
|
|
281
283
|
@obj[:TestClass][:Nested][:Nested][:TestClass][:Inherited] = :symbol
|
282
284
|
assert(@obj.validate)
|
283
285
|
end
|
286
|
+
|
287
|
+
it "makes sure validation works in special case where the dictionary is modified" do
|
288
|
+
@dict[:Array] = 5
|
289
|
+
refute(@dict.validate {|_, _, object| object[:Boolean] })
|
290
|
+
end
|
284
291
|
end
|
285
292
|
|
286
293
|
describe "delete" do
|
@@ -222,7 +222,7 @@ describe HexaPDF::DictionaryFields do
|
|
222
222
|
|
223
223
|
it "allows conversion to a Rectangle from an Array" do
|
224
224
|
doc = Minitest::Mock.new
|
225
|
-
doc.expect(:wrap, :data, [[0, 1, 2, 3], type: HexaPDF::Rectangle])
|
225
|
+
doc.expect(:wrap, :data, [[0, 1, 2, 3], {type: HexaPDF::Rectangle}])
|
226
226
|
@field.convert([0, 1, 2, 3], doc)
|
227
227
|
doc.verify
|
228
228
|
end
|
@@ -230,7 +230,7 @@ describe HexaPDF::DictionaryFields do
|
|
230
230
|
it "allows conversion to a Rectangle from a HexaPDF::PDFArray" do
|
231
231
|
data = HexaPDF::PDFArray.new([0, 1, 2, 3])
|
232
232
|
doc = Minitest::Mock.new
|
233
|
-
doc.expect(:wrap, :data, [data, type: HexaPDF::Rectangle])
|
233
|
+
doc.expect(:wrap, :data, [data, {type: HexaPDF::Rectangle}])
|
234
234
|
@field.convert(data, doc)
|
235
235
|
doc.verify
|
236
236
|
end
|
@@ -441,21 +441,21 @@ describe HexaPDF::Document do
|
|
441
441
|
|
442
442
|
describe "validate" do
|
443
443
|
before do
|
444
|
-
@doc.
|
444
|
+
@doc.validate # to create a valid document
|
445
445
|
end
|
446
446
|
|
447
447
|
it "validates indirect objects" do
|
448
|
-
obj = @doc.add({Type: :
|
448
|
+
obj = @doc.add({Type: :Page, MediaBox: [1, 1, 1, 1], Parent: @doc.pages.root})
|
449
449
|
refute(@doc.validate(auto_correct: false))
|
450
450
|
|
451
451
|
called = false
|
452
|
-
assert(@doc.validate {|o| assert_same(obj, o); called = true })
|
452
|
+
assert(@doc.validate {|_, _, o| assert_same(obj, o); called = true })
|
453
453
|
assert(called)
|
454
454
|
end
|
455
455
|
|
456
456
|
it "validates the trailer object" do
|
457
457
|
@doc.trailer[:ID] = :Symbol
|
458
|
-
refute(@doc.validate {|obj| assert_same(@doc.trailer, obj) })
|
458
|
+
refute(@doc.validate {|_, _, obj| assert_same(@doc.trailer, obj) })
|
459
459
|
end
|
460
460
|
|
461
461
|
it "validates only loaded objects" do
|
@@ -609,16 +609,24 @@ describe HexaPDF::Document do
|
|
609
609
|
|
610
610
|
describe "caching interface" do
|
611
611
|
it "allows setting and retrieving values" do
|
612
|
-
assert_equal(:test, @doc.cache(:a, :b, :test))
|
613
|
-
assert_equal(:test, @doc.cache(:a, :b
|
614
|
-
assert_equal(:
|
612
|
+
assert_equal(:test, @doc.cache(:a, :b, :test) { :notused })
|
613
|
+
assert_equal(:test, @doc.cache(:a, :b) { :other })
|
614
|
+
assert_equal(:test, @doc.cache(:a, :b))
|
615
|
+
assert_nil(@doc.cache(:a, :c, nil))
|
616
|
+
assert_nil(@doc.cache(:a, :c) { :other })
|
617
|
+
assert_nil(@doc.cache(:a, :c))
|
615
618
|
assert(@doc.cached?(:a, :b))
|
616
619
|
assert(@doc.cached?(:a, :c))
|
617
620
|
end
|
618
621
|
|
622
|
+
it "allows updating a value" do
|
623
|
+
@doc.cache(:a, :b) { :test }
|
624
|
+
assert_equal(:new, @doc.cache(:a, :b, update: true) { :new })
|
625
|
+
end
|
626
|
+
|
619
627
|
it "allows clearing cached values" do
|
620
|
-
@doc.cache(:a, :b
|
621
|
-
@doc.cache(:b, :c
|
628
|
+
@doc.cache(:a, :b) { :c }
|
629
|
+
@doc.cache(:b, :c) { :d }
|
622
630
|
@doc.clear_cache(:a)
|
623
631
|
refute(@doc.cached?(:a, :b))
|
624
632
|
assert(@doc.cached?(:b, :c))
|
@@ -626,7 +634,7 @@ describe HexaPDF::Document do
|
|
626
634
|
refute(@doc.cached?(:a, :c))
|
627
635
|
end
|
628
636
|
|
629
|
-
it "fails if no cached value exists and
|
637
|
+
it "fails if no cached value exists and no block is given" do
|
630
638
|
assert_raises(LocalJumpError) { @doc.cache(:a, :b) }
|
631
639
|
end
|
632
640
|
end
|
data/test/hexapdf/test_object.rb
CHANGED
@@ -3,18 +3,10 @@
|
|
3
3
|
require 'test_helper'
|
4
4
|
require 'hexapdf/object'
|
5
5
|
require 'hexapdf/reference'
|
6
|
+
require 'hexapdf/document'
|
6
7
|
|
7
8
|
describe HexaPDF::Object do
|
8
9
|
describe "class.deep_copy" do
|
9
|
-
it "handles not-duplicatable classes" do
|
10
|
-
assert_equal(5, HexaPDF::Object.deep_copy(5))
|
11
|
-
assert_equal(5.5, HexaPDF::Object.deep_copy(5.5))
|
12
|
-
assert_nil(HexaPDF::Object.deep_copy(nil))
|
13
|
-
assert_equal(true, HexaPDF::Object.deep_copy(true))
|
14
|
-
assert_equal(false, HexaPDF::Object.deep_copy(false))
|
15
|
-
assert_equal(:Name, HexaPDF::Object.deep_copy(:Name))
|
16
|
-
end
|
17
|
-
|
18
10
|
it "handles general, duplicatable classes" do
|
19
11
|
x = "test"
|
20
12
|
assert_equal("test", HexaPDF::Object.deep_copy(x))
|
@@ -103,30 +95,57 @@ describe HexaPDF::Object do
|
|
103
95
|
end
|
104
96
|
|
105
97
|
describe "validate" do
|
106
|
-
|
107
|
-
obj = HexaPDF::Object.new(5)
|
108
|
-
|
109
|
-
|
110
|
-
|
98
|
+
before do
|
99
|
+
@obj = HexaPDF::Object.new(5)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "invokes perform_validation correctly" do
|
103
|
+
invoked = false
|
104
|
+
@obj.define_singleton_method(:perform_validation) { invoked = true }
|
105
|
+
assert(@obj.validate)
|
106
|
+
assert(invoked)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "yields all arguments yieled by perform_validation" do
|
110
|
+
invoked = []
|
111
|
+
@obj.define_singleton_method(:perform_validation) do |&block|
|
112
|
+
block.call("error", true, :object)
|
113
|
+
end
|
114
|
+
assert(@obj.validate {|*a| invoked << a })
|
115
|
+
assert_equal([["error", true, :object]], invoked)
|
116
|
+
end
|
117
|
+
|
118
|
+
it "provides self as third argument if none is yielded by perform_validation" do
|
119
|
+
invoked = []
|
120
|
+
@obj.define_singleton_method(:perform_validation) do |&block|
|
111
121
|
block.call("error", true)
|
112
122
|
end
|
113
|
-
assert(obj.validate {|*a| invoked
|
114
|
-
assert_equal([
|
115
|
-
|
123
|
+
assert(@obj.validate {|*a| invoked << a })
|
124
|
+
assert_equal([["error", true, @obj]], invoked)
|
125
|
+
end
|
116
126
|
|
117
|
-
|
127
|
+
it "yields all problems when auto_correct is true" do
|
128
|
+
invoked = []
|
129
|
+
@obj.define_singleton_method(:perform_validation) do |&block|
|
130
|
+
invoked << :before
|
131
|
+
block.call("error", false)
|
132
|
+
invoked << :after
|
133
|
+
block.call("error2", true)
|
134
|
+
invoked << :last
|
135
|
+
end
|
136
|
+
refute(@obj.validate)
|
137
|
+
assert_equal([:before, :after, :last], invoked)
|
118
138
|
end
|
119
139
|
|
120
|
-
it "stops
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
invoked[:before] = true
|
140
|
+
it "stops at the first uncorrectable problem if auto_correct is false" do
|
141
|
+
invoked = []
|
142
|
+
@obj.define_singleton_method(:perform_validation) do |&block|
|
143
|
+
invoked << :before
|
125
144
|
block.call("error", false)
|
126
|
-
invoked
|
145
|
+
invoked << :after
|
127
146
|
end
|
128
|
-
refute(obj.validate
|
129
|
-
|
147
|
+
refute(@obj.validate(auto_correct: false))
|
148
|
+
assert_equal([:before], invoked)
|
130
149
|
end
|
131
150
|
end
|
132
151
|
|
@@ -181,6 +200,32 @@ describe HexaPDF::Object do
|
|
181
200
|
end
|
182
201
|
end
|
183
202
|
|
203
|
+
describe "caching" do
|
204
|
+
before do
|
205
|
+
@obj = HexaPDF::Object.new({}, document: HexaPDF::Document.new)
|
206
|
+
end
|
207
|
+
|
208
|
+
it "can set and return a cached value" do
|
209
|
+
assert_equal(:value, @obj.cache(:data, :value))
|
210
|
+
assert_equal(:value, @obj.cache(:data, :other))
|
211
|
+
assert_equal(:value, @obj.cache(:block) { :value })
|
212
|
+
assert_equal(:other, @obj.cache(:data, :other, update: true))
|
213
|
+
end
|
214
|
+
|
215
|
+
it "can check for the existence of a cached value" do
|
216
|
+
refute(@obj.cached?(:data))
|
217
|
+
@obj.cache(:data, :value)
|
218
|
+
assert(@obj.cached?(:data))
|
219
|
+
end
|
220
|
+
|
221
|
+
it "can clear all cached values" do
|
222
|
+
@obj.cache(:data, :value)
|
223
|
+
assert(@obj.cached?(:data))
|
224
|
+
@obj.clear_cache
|
225
|
+
refute(@obj.cached?(:data))
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
184
229
|
describe "validation" do
|
185
230
|
before do
|
186
231
|
@doc = Object.new
|