hexapdf 0.12.3 → 0.14.3
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 +132 -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/dictionary.rb +12 -6
- data/lib/hexapdf/dictionary_fields.rb +2 -10
- 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 +16 -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 +107 -7
- data/lib/hexapdf/pdf_array.rb +15 -5
- data/lib/hexapdf/revisions.rb +29 -21
- data/lib/hexapdf/serializer.rb +37 -10
- data/lib/hexapdf/task/optimize.rb +6 -4
- data/lib/hexapdf/tokenizer.rb +22 -0
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +130 -27
- data/lib/hexapdf/type/acro_form/button_field.rb +5 -2
- 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 +6 -2
- data/lib/hexapdf/type/image.rb +2 -2
- data/lib/hexapdf/type/page.rb +21 -12
- data/lib/hexapdf/type/page_tree_node.rb +29 -5
- data/lib/hexapdf/type/resources.rb +5 -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 +2 -2
- 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 +10 -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_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 +9 -2
- data/test/hexapdf/test_document.rb +18 -10
- data/test/hexapdf/test_object.rb +71 -26
- data/test/hexapdf/test_parser.rb +205 -51
- data/test/hexapdf/test_pdf_array.rb +8 -1
- data/test/hexapdf/test_revisions.rb +35 -0
- data/test/hexapdf/test_serializer.rb +7 -0
- data/test/hexapdf/test_tokenizer.rb +28 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +288 -35
- data/test/hexapdf/type/acro_form/test_button_field.rb +15 -0
- 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 +8 -1
- data/test/hexapdf/type/test_page.rb +8 -1
- data/test/hexapdf/type/test_page_tree_node.rb +42 -0
- data/test/hexapdf/type/test_resources.rb +6 -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 -12
data/lib/hexapdf/version.rb
CHANGED
|
@@ -122,7 +122,7 @@ module CommonTokenizerTests
|
|
|
122
122
|
end
|
|
123
123
|
|
|
124
124
|
it "next_token: should not fail when reading super long numbers" do
|
|
125
|
-
create_tokenizer("1"
|
|
125
|
+
create_tokenizer("1" << "0" * 10_000)
|
|
126
126
|
assert_equal(10**10_000, @tokenizer.next_token)
|
|
127
127
|
end
|
|
128
128
|
|
|
@@ -162,7 +162,7 @@ module CommonTokenizerTests
|
|
|
162
162
|
end
|
|
163
163
|
|
|
164
164
|
it "returns the correct position on operations" do
|
|
165
|
-
create_tokenizer("hallo du"
|
|
165
|
+
create_tokenizer("hallo du" << " " * 50000 << "hallo du")
|
|
166
166
|
@tokenizer.next_token
|
|
167
167
|
assert_equal(5, @tokenizer.pos)
|
|
168
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,16 @@ 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(93) {|i| @subsetter.use_glyph(i) }
|
|
32
|
+
# glyph 0, 93 used glyph, 4 special glyphs
|
|
33
|
+
assert_equal(1 + 93 + 4, @subsetter.instance_variable_get(:@glyph_map).size)
|
|
34
|
+
1.upto(12) {|i| assert_equal(i, @subsetter.subset_glyph_id(i), "id=#{i}") }
|
|
35
|
+
13.upto(38) {|i| assert_equal(i + 1, @subsetter.subset_glyph_id(i), "id=#{i}") }
|
|
36
|
+
39.upto(88) {|i| assert_equal(i + 3, @subsetter.subset_glyph_id(i), "id=#{i}") }
|
|
37
|
+
89.upto(93) {|i| assert_equal(i + 4, @subsetter.subset_glyph_id(i), "id=#{i}") }
|
|
38
|
+
end
|
|
39
|
+
|
|
30
40
|
it "creates the subset font file" do
|
|
31
41
|
gid = @font[:cmap].preferred_table[0x41]
|
|
32
42
|
@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?)
|
|
@@ -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,9 +230,16 @@ 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
|
|
237
|
+
|
|
238
|
+
it "converts to a null value if an (invalid) empty array is given" do
|
|
239
|
+
doc = Minitest::Mock.new
|
|
240
|
+
doc.expect(:wrap, :data, [nil])
|
|
241
|
+
@field.convert([], doc)
|
|
242
|
+
doc.verify
|
|
243
|
+
end
|
|
237
244
|
end
|
|
238
245
|
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
|