hexapdf 0.47.0 → 1.0.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 +65 -16
- data/lib/hexapdf/cli.rb +14 -1
- data/lib/hexapdf/composer.rb +7 -0
- data/lib/hexapdf/configuration.rb +2 -0
- data/lib/hexapdf/content/parser.rb +3 -1
- data/lib/hexapdf/digital_signature/cms_handler.rb +13 -0
- data/lib/hexapdf/digital_signature/signature.rb +1 -1
- data/lib/hexapdf/digital_signature/signing/default_handler.rb +1 -0
- data/lib/hexapdf/document.rb +14 -3
- data/lib/hexapdf/font/cmap/writer.rb +58 -4
- data/lib/hexapdf/font/cmap.rb +7 -0
- data/lib/hexapdf/font/true_type_wrapper.rb +41 -16
- data/lib/hexapdf/layout/text_fragment.rb +2 -1
- data/lib/hexapdf/object.rb +1 -1
- data/lib/hexapdf/parser.rb +6 -2
- data/lib/hexapdf/reference.rb +1 -1
- data/lib/hexapdf/task/merge_acro_form.rb +164 -0
- data/lib/hexapdf/task.rb +1 -0
- data/lib/hexapdf/tokenizer.rb +2 -0
- data/lib/hexapdf/type/acro_form/form.rb +14 -27
- data/lib/hexapdf/type/acro_form/signature_field.rb +16 -6
- data/lib/hexapdf/type/acro_form/variable_text_field.rb +1 -1
- data/lib/hexapdf/type/actions/go_to.rb +1 -0
- data/lib/hexapdf/type/actions/go_to_r.rb +1 -0
- data/lib/hexapdf/type/actions/launch.rb +5 -1
- data/lib/hexapdf/type/annotation.rb +6 -1
- data/lib/hexapdf/type/annotations/markup_annotation.rb +14 -1
- data/lib/hexapdf/type/catalog.rb +3 -0
- data/lib/hexapdf/type/cid_font.rb +4 -1
- data/lib/hexapdf/type/file_specification.rb +17 -14
- data/lib/hexapdf/type/font_descriptor.rb +4 -3
- data/lib/hexapdf/type/font_simple.rb +3 -1
- data/lib/hexapdf/type/font_true_type.rb +2 -0
- data/lib/hexapdf/type/font_type0.rb +1 -1
- data/lib/hexapdf/type/font_type1.rb +7 -0
- data/lib/hexapdf/type/font_type3.rb +0 -1
- data/lib/hexapdf/type/form.rb +5 -2
- data/lib/hexapdf/type/graphics_state_parameter.rb +7 -4
- data/lib/hexapdf/type/image.rb +8 -4
- data/lib/hexapdf/type/info.rb +2 -2
- data/lib/hexapdf/type/mark_information.rb +2 -2
- data/lib/hexapdf/type/optional_content_configuration.rb +1 -1
- data/lib/hexapdf/type/optional_content_membership.rb +1 -1
- data/lib/hexapdf/type/page.rb +5 -3
- data/lib/hexapdf/type/resources.rb +6 -6
- data/lib/hexapdf/type/viewer_preferences.rb +4 -3
- data/lib/hexapdf/utils/sorted_tree_node.rb +12 -2
- data/lib/hexapdf/version.rb +1 -1
- data/lib/hexapdf/writer.rb +1 -0
- data/lib/hexapdf/xref_section.rb +20 -4
- data/test/hexapdf/common_tokenizer_tests.rb +5 -0
- data/test/hexapdf/digital_signature/signing/test_default_handler.rb +6 -0
- data/test/hexapdf/digital_signature/test_cms_handler.rb +12 -7
- data/test/hexapdf/digital_signature/test_signature.rb +7 -0
- data/test/hexapdf/digital_signature/test_signatures.rb +8 -3
- data/test/hexapdf/font/cmap/test_writer.rb +73 -16
- data/test/hexapdf/font/test_true_type_wrapper.rb +17 -3
- data/test/hexapdf/layout/test_list_box.rb +7 -7
- data/test/hexapdf/layout/test_text_fragment.rb +3 -3
- data/test/hexapdf/layout/test_text_layouter.rb +4 -2
- data/test/hexapdf/task/test_merge_acro_form.rb +104 -0
- data/test/hexapdf/test_composer.rb +8 -0
- data/test/hexapdf/test_document.rb +9 -0
- data/test/hexapdf/test_parser.rb +23 -6
- data/test/hexapdf/test_writer.rb +10 -5
- data/test/hexapdf/test_xref_section.rb +15 -0
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +18 -18
- data/test/hexapdf/type/acro_form/test_form.rb +7 -3
- data/test/hexapdf/type/actions/test_launch.rb +6 -2
- data/test/hexapdf/type/test_font_type1.rb +5 -0
- data/test/hexapdf/type/test_form.rb +1 -1
- data/test/hexapdf/type/test_page.rb +7 -1
- data/test/hexapdf/utils/test_sorted_tree_node.rb +7 -6
- metadata +4 -2
@@ -0,0 +1,104 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'hexapdf/document'
|
5
|
+
require 'hexapdf/task/merge_acro_form'
|
6
|
+
|
7
|
+
describe HexaPDF::Task::MergeAcroForm do
|
8
|
+
before do
|
9
|
+
@doc = HexaPDF::Document.new
|
10
|
+
@doc.pages.add
|
11
|
+
@doc.pages.add
|
12
|
+
form = @doc.acro_form(create: true)
|
13
|
+
field = form.create_text_field("Text")
|
14
|
+
field.create_widget(@doc.pages[0], Rect: [0, 0, 0, 0])
|
15
|
+
field.create_widget(@doc.pages[1], Rect: [0, 0, 0, 0])
|
16
|
+
|
17
|
+
form.create_text_field("Calc.Field a")
|
18
|
+
form.create_text_field("Calc.Field b")
|
19
|
+
field = form.create_text_field('Other.Calculation 1')
|
20
|
+
field.set_calculate_action(:sum, fields: ["Calc.Field a", "Calc.Field b"])
|
21
|
+
field.create_widget(@doc.pages[1])
|
22
|
+
field = form.create_text_field('Other.Calculation 2')
|
23
|
+
field.set_calculate_action(:sfn, fields: "Calc.Field\\ a + Calc.Field\\ b")
|
24
|
+
field.create_widget(@doc.pages[1])
|
25
|
+
|
26
|
+
@root_fields = @doc.acro_form.root_fields
|
27
|
+
|
28
|
+
@doc.dispatch_message(:complete_objects)
|
29
|
+
@doc.validate
|
30
|
+
@doc1 = @doc.duplicate
|
31
|
+
@pages = []
|
32
|
+
@pages << @doc.pages.add(@doc.import(@doc1.pages[0]))
|
33
|
+
@pages << @doc.pages.add(@doc.import(@doc1.pages[1]))
|
34
|
+
end
|
35
|
+
|
36
|
+
it "selects a unique name for the root field" do
|
37
|
+
@doc.task(:merge_acro_form, source: @doc1, pages: @pages)
|
38
|
+
assert_equal('merged_1', @root_fields[3][:T])
|
39
|
+
|
40
|
+
@root_fields << @doc.wrap({T: 'merged_23'})
|
41
|
+
@doc.task(:merge_acro_form, source: @doc1, pages: @pages)
|
42
|
+
assert_equal('merged_24', @root_fields[5][:T])
|
43
|
+
end
|
44
|
+
|
45
|
+
it "merges the /DR entry of the main AcroForm dictionary" do
|
46
|
+
@doc.task(:merge_acro_form, source: @doc1, pages: @pages)
|
47
|
+
assert(@doc.acro_form.default_resources[:Font].key?(:F2))
|
48
|
+
end
|
49
|
+
|
50
|
+
it "updates the /SigFlags if necessary" do
|
51
|
+
@doc.task(:merge_acro_form, source: @doc1, pages: [@pages[0]])
|
52
|
+
refute(@doc.acro_form.signature_flag?(:signatures_exist))
|
53
|
+
|
54
|
+
@pages[0][:Annots][0].form_field[:FT] = :Sig
|
55
|
+
@doc.task(:merge_acro_form, source: @doc1, pages: [@pages[0]])
|
56
|
+
refute(@doc.acro_form.signature_flag?(:signatures_exist))
|
57
|
+
|
58
|
+
@doc1.acro_form.signature_flag(:signatures_exist)
|
59
|
+
@doc.task(:merge_acro_form, source: @doc1, pages: [@pages[0]])
|
60
|
+
assert(@doc.acro_form.signature_flag?(:signatures_exist))
|
61
|
+
end
|
62
|
+
|
63
|
+
it "applies the /DA and /Q entries of the source AcroForm to the created root field" do
|
64
|
+
@doc1.acro_form.set_default_appearance_string
|
65
|
+
@doc1.acro_form[:Q] = @doc1.add(5)
|
66
|
+
@doc.task(:merge_acro_form, source: @doc1, pages: [@pages[0]])
|
67
|
+
assert_equal('0.0 g /F2 0 Tf', @root_fields[3][:DA])
|
68
|
+
assert_equal(5, @root_fields[3][:Q])
|
69
|
+
end
|
70
|
+
|
71
|
+
it "merges only the fields references in the given pages" do
|
72
|
+
@doc.task(:merge_acro_form, source: @doc1, pages: [@pages[0]])
|
73
|
+
assert_equal('merged_1', @root_fields[3][:T])
|
74
|
+
assert_equal(1, @root_fields[3][:Kids].size)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "only merges fields that have at least one widget" do
|
78
|
+
@doc.task(:merge_acro_form, source: @doc1, pages: @pages)
|
79
|
+
assert_equal(2, @root_fields[3][:Kids].size)
|
80
|
+
assert_nil(@doc.acro_form.field_by_name('merged_1.Calc'))
|
81
|
+
end
|
82
|
+
|
83
|
+
it "updates the /DA entries of widgets and fields" do
|
84
|
+
@pages[0][:Annots][0][:DA] = '/F1 10 Tf'
|
85
|
+
@doc.task(:merge_acro_form, source: @doc1, pages: @pages)
|
86
|
+
field = @doc.acro_form.field_by_name('merged_1.Text')
|
87
|
+
assert_equal('0.0 g /F2 0 Tf', field[:DA])
|
88
|
+
assert_equal('/F2 10 Tf', field.each_widget.to_a[0][:DA])
|
89
|
+
end
|
90
|
+
|
91
|
+
it "doesn't update the calculation actions if no field with one is merged" do
|
92
|
+
@doc.task(:merge_acro_form, source: @doc1, pages: [@pages[0]])
|
93
|
+
assert_equal(2, @doc.acro_form[:CO].size)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "updates the field names in known calculation actions" do
|
97
|
+
@doc.task(:merge_acro_form, source: @doc1, pages: @pages)
|
98
|
+
assert_equal(4, @doc.acro_form[:CO].size)
|
99
|
+
js = @doc.acro_form.field_by_name('merged_1.Other.Calculation 1')[:AA][:C][:JS]
|
100
|
+
assert_match(/merged_1.Calc.Field a/, js)
|
101
|
+
js = @doc.acro_form.field_by_name('merged_1.Other.Calculation 2')[:AA][:C][:JS]
|
102
|
+
assert_match(/merged_1.Calc.Field a/, js)
|
103
|
+
end
|
104
|
+
end
|
@@ -63,6 +63,14 @@ describe HexaPDF::Composer do
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
+
it "writes the document to a string" do
|
67
|
+
pdf = HexaPDF::Composer.new
|
68
|
+
pdf.new_page
|
69
|
+
str = pdf.write_to_string
|
70
|
+
doc = HexaPDF::Document.new(io: StringIO.new(str))
|
71
|
+
assert_equal(2, doc.pages.count)
|
72
|
+
end
|
73
|
+
|
66
74
|
describe "new_page" do
|
67
75
|
it "creates a new page" do
|
68
76
|
c = HexaPDF::Composer.new(page_size: [0, 0, 50, 100], margin: 10)
|
@@ -595,4 +595,13 @@ describe HexaPDF::Document do
|
|
595
595
|
refute(dupped.encrypted?)
|
596
596
|
end
|
597
597
|
end
|
598
|
+
|
599
|
+
it "writes the document to a string" do
|
600
|
+
doc = HexaPDF::Document.new
|
601
|
+
doc.trailer.info[:test] = :test
|
602
|
+
str = doc.write_to_string(update_fields: false)
|
603
|
+
assert_equal(Encoding::ASCII_8BIT, str.encoding)
|
604
|
+
doc = HexaPDF::Document.new(io: StringIO.new(str))
|
605
|
+
assert_equal(:test, doc.trailer.info[:test])
|
606
|
+
end
|
598
607
|
end
|
data/test/hexapdf/test_parser.rb
CHANGED
@@ -33,18 +33,23 @@ describe HexaPDF::Parser do
|
|
33
33
|
endstream
|
34
34
|
endobj
|
35
35
|
|
36
|
+
5 0 obj
|
37
|
+
1 0 R
|
38
|
+
endobj
|
39
|
+
|
36
40
|
xref
|
37
41
|
0 4
|
38
42
|
0000000000 65535 f
|
39
43
|
0000000010 00000 n
|
40
44
|
0000000029 00000 n
|
41
45
|
0000000000 65535 f
|
42
|
-
3
|
46
|
+
3 2
|
43
47
|
0000000556 00000 n
|
48
|
+
0000000308 00000 n
|
44
49
|
trailer
|
45
50
|
<< /Test (now) >>
|
46
51
|
startxref
|
47
|
-
|
52
|
+
330
|
48
53
|
%%EOF
|
49
54
|
EOF
|
50
55
|
end
|
@@ -152,6 +157,13 @@ describe HexaPDF::Parser do
|
|
152
157
|
assert_equal('12', collector(stream.fiber))
|
153
158
|
end
|
154
159
|
|
160
|
+
it "recovers from a non-existing indirect reference to a stream length value" do
|
161
|
+
create_parser("1 0 obj<</Length 2 0 R>> stream\n12(ab\nendstream endobj")
|
162
|
+
obj, _, _, stream = @parser.parse_indirect_object
|
163
|
+
assert_equal(5, obj[:Length])
|
164
|
+
assert_equal('12(ab', collector(stream.fiber))
|
165
|
+
end
|
166
|
+
|
155
167
|
it "works even if the keyword endobj is missing or mangled" do
|
156
168
|
create_parser("1 0 obj<</Length 4>>5")
|
157
169
|
object, * = @parser.parse_indirect_object
|
@@ -298,6 +310,11 @@ describe HexaPDF::Parser do
|
|
298
310
|
assert_equal(0, obj.gen)
|
299
311
|
end
|
300
312
|
|
313
|
+
it "handles the case of the value of an indirect object being an indirect reference" do
|
314
|
+
obj = @parser.load_object(HexaPDF::XRefSection.in_use_entry(5, 0, 308))
|
315
|
+
assert_equal(1, obj.oid)
|
316
|
+
end
|
317
|
+
|
301
318
|
describe "with strict parsing" do
|
302
319
|
it "raises an error if an indirect object has an offset of 0" do
|
303
320
|
@document.config['parser.on_correctable_error'] = proc { true }
|
@@ -336,13 +353,13 @@ describe HexaPDF::Parser do
|
|
336
353
|
|
337
354
|
describe "startxref_offset" do
|
338
355
|
it "caches the offset value" do
|
339
|
-
assert_equal(
|
340
|
-
@parser.instance_eval { @io }.string.sub!(/
|
341
|
-
assert_equal(
|
356
|
+
assert_equal(330, @parser.startxref_offset)
|
357
|
+
@parser.instance_eval { @io }.string.sub!(/330\n/, "309\n")
|
358
|
+
assert_equal(330, @parser.startxref_offset)
|
342
359
|
end
|
343
360
|
|
344
361
|
it "returns the correct offset" do
|
345
|
-
assert_equal(
|
362
|
+
assert_equal(330, @parser.startxref_offset)
|
346
363
|
end
|
347
364
|
|
348
365
|
it "ignores garbage at the end of the file" do
|
data/test/hexapdf/test_writer.rb
CHANGED
@@ -48,10 +48,15 @@ describe HexaPDF::Writer do
|
|
48
48
|
trailer
|
49
49
|
<</Size 4/Root<</Type/Catalog>>/Info 3 0 R/Prev 219>>
|
50
50
|
startxref
|
51
|
-
|
51
|
+
#{343 + HexaPDF::VERSION.length}
|
52
52
|
%%EOF
|
53
53
|
EOF
|
54
54
|
|
55
|
+
xref_stream = case HexaPDF::VERSION.length
|
56
|
+
when 5 then "x\xDAcbdlg``b`\xB0\x04\x93\x93\x19\x18\x00\f\x1E\x01\\"
|
57
|
+
when 6 then "x\xDAcbd\xEC```b`\xB0\x04\x93\x93\x18\x18\x00\f*\x01\\"
|
58
|
+
else fail
|
59
|
+
end
|
55
60
|
@compressed_input_io = StringIO.new(<<~EOF.force_encoding(Encoding::BINARY))
|
56
61
|
%PDF-1.7
|
57
62
|
%\xCF\xEC\xFF\xE8\xD7\xCB\xCD
|
@@ -64,8 +69,8 @@ describe HexaPDF::Writer do
|
|
64
69
|
20
|
65
70
|
endobj
|
66
71
|
3 0 obj
|
67
|
-
<</Size 6/Type/XRef/W[1 1 2]/Index[0
|
68
|
-
x\xDAcb`\xF8\xFF\x9F\x89\x89\x95\x91\x91\xE9\x7F\x19\x03\x03\x13\x83\x10\
|
72
|
+
<</Size 6/Type/XRef/W[1 1 2]/Index[0 6]/Filter/FlateDecode/DecodeParms<</Columns 4/Predictor 12>>/Length 36>>stream
|
73
|
+
x\xDAcb`\xF8\xFF\x9F\x89\x89\x95\x91\x91\xE9\x7F\x19\x03\x03\x13\x83\x10\x90\xF8_\f\x14c\x14bd\x04\x00lk\a
|
69
74
|
endstream
|
70
75
|
endobj
|
71
76
|
startxref
|
@@ -81,11 +86,11 @@ describe HexaPDF::Writer do
|
|
81
86
|
endobj
|
82
87
|
4 0 obj
|
83
88
|
<</Size 7/Root<</Type/Catalog>>/Info 6 0 R/Prev 141/Type/XRef/W[1 2 2]/Index[2 1 4 1 6 1]/Filter/FlateDecode/DecodeParms<</Columns 5/Predictor 12>>/Length 22>>stream
|
84
|
-
|
89
|
+
#{xref_stream}
|
85
90
|
endstream
|
86
91
|
endobj
|
87
92
|
startxref
|
88
|
-
|
93
|
+
#{443 + HexaPDF::VERSION.length}
|
89
94
|
%%EOF
|
90
95
|
EOF
|
91
96
|
end
|
@@ -57,5 +57,20 @@ describe HexaPDF::XRefSection do
|
|
57
57
|
@xref_section.add_in_use_entry(20, 0, 0)
|
58
58
|
assert_subsections([[1, 2], [10, 11], [20]])
|
59
59
|
end
|
60
|
+
|
61
|
+
it "yields a single subsection if the section was marked as the initial one" do
|
62
|
+
@xref_section.mark_as_initial_section!
|
63
|
+
@xref_section.add_in_use_entry(6, 0, 0)
|
64
|
+
@xref_section.add_in_use_entry(7, 0, 0)
|
65
|
+
@xref_section.add_in_use_entry(9, 0, 0)
|
66
|
+
@xref_section.add_in_use_entry(1, 0, 0)
|
67
|
+
@xref_section.add_in_use_entry(2, 0, 0)
|
68
|
+
result = @xref_section.each_subsection.map {|s| s.map {|e| [e.oid, e.type] }}
|
69
|
+
assert_equal([[[1, :in_use], [2, :in_use],
|
70
|
+
[3, :free], [4, :free], [5, :free],
|
71
|
+
[6, :in_use], [7, :in_use],
|
72
|
+
[8, :free],
|
73
|
+
[9, :in_use]]], result)
|
74
|
+
end
|
60
75
|
end
|
61
76
|
end
|
@@ -549,7 +549,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
549
549
|
@field.text_alignment(:left)
|
550
550
|
@generator.create_appearances
|
551
551
|
assert_operators(@widget[:AP][:N].stream,
|
552
|
-
[:
|
552
|
+
[:move_text, [2, 6.41]],
|
553
553
|
range: 7)
|
554
554
|
end
|
555
555
|
|
@@ -557,7 +557,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
557
557
|
@field.text_alignment(:right)
|
558
558
|
@generator.create_appearances
|
559
559
|
assert_operators(@widget[:AP][:N].stream,
|
560
|
-
[:
|
560
|
+
[:move_text, [78.55, 6.41]],
|
561
561
|
range: 7)
|
562
562
|
end
|
563
563
|
|
@@ -565,7 +565,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
565
565
|
@field.text_alignment(:center)
|
566
566
|
@generator.create_appearances
|
567
567
|
assert_operators(@widget[:AP][:N].stream,
|
568
|
-
[:
|
568
|
+
[:move_text, [40.275, 6.41]],
|
569
569
|
range: 7)
|
570
570
|
end
|
571
571
|
|
@@ -576,7 +576,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
576
576
|
|
577
577
|
@generator.create_appearances
|
578
578
|
assert_operators(@widget[:AP][:N].stream,
|
579
|
-
[:
|
579
|
+
[:move_text, [2, 6.41]],
|
580
580
|
range: 7)
|
581
581
|
ensure
|
582
582
|
font_metrics.cap_height = cap_height
|
@@ -586,7 +586,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
586
586
|
@widget[:Rect].height = 5
|
587
587
|
@generator.create_appearances
|
588
588
|
assert_operators(@widget[:AP][:N].stream,
|
589
|
-
[:
|
589
|
+
[:move_text, [2, 3.07]],
|
590
590
|
range: 7)
|
591
591
|
end
|
592
592
|
end
|
@@ -614,7 +614,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
614
614
|
[:set_font_and_size, [:F1, 10]],
|
615
615
|
[:set_device_rgb_non_stroking_color, [1.0, 0.0, 0.0]],
|
616
616
|
[:begin_text],
|
617
|
-
[:
|
617
|
+
[:move_text, [2, 2.035]],
|
618
618
|
[:show_text, ["Te "]],
|
619
619
|
[:set_font_and_size, [:F2, 10]],
|
620
620
|
[:move_text, [14.45, 0]],
|
@@ -645,7 +645,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
645
645
|
@field.text_alignment(:left)
|
646
646
|
@generator.create_appearances
|
647
647
|
assert_operators(@widget[:AP][:N].stream,
|
648
|
-
[:
|
648
|
+
[:move_text, [2, 16.195]],
|
649
649
|
range: 9)
|
650
650
|
end
|
651
651
|
|
@@ -653,7 +653,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
653
653
|
@field.text_alignment(:right)
|
654
654
|
@generator.create_appearances
|
655
655
|
assert_operators(@widget[:AP][:N].stream,
|
656
|
-
[:
|
656
|
+
[:move_text, [78.55, 16.195]],
|
657
657
|
range: 9)
|
658
658
|
end
|
659
659
|
|
@@ -661,7 +661,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
661
661
|
@field.text_alignment(:center)
|
662
662
|
@generator.create_appearances
|
663
663
|
assert_operators(@widget[:AP][:N].stream,
|
664
|
-
[:
|
664
|
+
[:move_text, [40.275, 16.195]],
|
665
665
|
range: 9)
|
666
666
|
end
|
667
667
|
end
|
@@ -681,7 +681,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
681
681
|
[:set_font_and_size, [:F1, 10]],
|
682
682
|
[:set_device_rgb_non_stroking_color, [1.0, 0.0, 0.0]],
|
683
683
|
[:begin_text],
|
684
|
-
[:
|
684
|
+
[:move_text, [2, 16.195]],
|
685
685
|
[:show_text, ['Test']],
|
686
686
|
[:move_text_next_line],
|
687
687
|
[:show_text, ['Value']],
|
@@ -703,7 +703,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
703
703
|
[:set_leading, [9.25]],
|
704
704
|
[:set_font_and_size, [:F1, 8]],
|
705
705
|
[:begin_text],
|
706
|
-
[:
|
706
|
+
[:move_text, [2, 18.556]],
|
707
707
|
[:show_text, ['Test']],
|
708
708
|
[:move_text_next_line],
|
709
709
|
[:show_text, ['Test']],
|
@@ -734,7 +734,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
734
734
|
@field.text_alignment(:left)
|
735
735
|
@generator.create_appearances
|
736
736
|
assert_operators(@widget[:AP][:N].stream,
|
737
|
-
[:
|
737
|
+
[:move_text, [2.945, 6.41]],
|
738
738
|
range: 7)
|
739
739
|
end
|
740
740
|
|
@@ -742,7 +742,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
742
742
|
@field.text_alignment(:right)
|
743
743
|
@generator.create_appearances
|
744
744
|
assert_operators(@widget[:AP][:N].stream,
|
745
|
-
[:
|
745
|
+
[:move_text, [62.945, 6.41]],
|
746
746
|
range: 7)
|
747
747
|
end
|
748
748
|
|
@@ -750,7 +750,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
750
750
|
@field.text_alignment(:center)
|
751
751
|
@generator.create_appearances
|
752
752
|
assert_operators(@widget[:AP][:N].stream,
|
753
|
-
[:
|
753
|
+
[:move_text, [32.945, 6.41]],
|
754
754
|
range: 7)
|
755
755
|
end
|
756
756
|
|
@@ -759,7 +759,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
759
759
|
@field.text_alignment(:center)
|
760
760
|
@generator.create_appearances
|
761
761
|
assert_operators(@widget[:AP][:N].stream,
|
762
|
-
[:
|
762
|
+
[:move_text, [22.39, 6.41]],
|
763
763
|
range: 7)
|
764
764
|
end
|
765
765
|
end
|
@@ -777,7 +777,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
777
777
|
[:set_font_and_size, [:F1, 10]],
|
778
778
|
[:set_device_rgb_non_stroking_color, [1.0, 0.0, 0.0]],
|
779
779
|
[:begin_text],
|
780
|
-
[:
|
780
|
+
[:move_text, [2.945, 6.41]],
|
781
781
|
[:show_text_with_positioning, [['T', -416.5, 'e', -472, 'x', -611, 't']]],
|
782
782
|
[:end_text],
|
783
783
|
[:restore_graphics_state],
|
@@ -789,7 +789,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
789
789
|
@generator.create_appearances
|
790
790
|
assert_operators(@widget[:AP][:N].stream,
|
791
791
|
[[:begin_text],
|
792
|
-
[:
|
792
|
+
[:move_text, [2, 6.41]],
|
793
793
|
[:end_text]], range: 6..8)
|
794
794
|
end
|
795
795
|
|
@@ -870,7 +870,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
870
870
|
[:set_font_and_size, [:F1, 12]],
|
871
871
|
[:set_device_rgb_non_stroking_color, [1.0, 0.0, 0.0]],
|
872
872
|
[:begin_text],
|
873
|
-
[:
|
873
|
+
[:move_text, [2, 23.609]],
|
874
874
|
[:show_text, ["a"]],
|
875
875
|
[:move_text_next_line],
|
876
876
|
[:show_text, ["b"]],
|
@@ -159,11 +159,16 @@ describe HexaPDF::Type::AcroForm::Form do
|
|
159
159
|
end
|
160
160
|
|
161
161
|
def applies_variable_text_properties(method, **args)
|
162
|
-
field = @acro_form.send(method, "field", **args
|
162
|
+
field = @acro_form.send(method, "field", **args)
|
163
163
|
font_name, font_size, font_color = field.parse_default_appearance_string
|
164
|
-
assert_equal(:'
|
164
|
+
assert_equal(:'Helvetica', @acro_form.default_resources.font(font_name)[:BaseFont])
|
165
165
|
assert_equal(0, font_size)
|
166
166
|
assert_equal(HexaPDF::Content::ColorSpace::DeviceGray.new.color(0), font_color)
|
167
|
+
assert_equal(0, field.value[:Q])
|
168
|
+
|
169
|
+
field = @acro_form.send(method, "field", **args, font: 'Times')
|
170
|
+
font_name, font_size, font_color = field.parse_default_appearance_string
|
171
|
+
assert_equal(:'Times-Roman', @acro_form.default_resources.font(font_name)[:BaseFont])
|
167
172
|
|
168
173
|
field = @acro_form.send(method, "field", **args, font_options: {variant: :bold})
|
169
174
|
font_name, = field.parse_default_appearance_string
|
@@ -171,7 +176,6 @@ describe HexaPDF::Type::AcroForm::Form do
|
|
171
176
|
|
172
177
|
field = @acro_form.send(method, "field", **args, font_size: 10)
|
173
178
|
font_name, font_size = field.parse_default_appearance_string
|
174
|
-
assert_equal(:Helvetica, @acro_form.default_resources.font(font_name)[:BaseFont])
|
175
179
|
assert_equal(10, font_size)
|
176
180
|
|
177
181
|
field = @acro_form.send(method, "field", **args, font_color: "red")
|
@@ -14,10 +14,14 @@ describe HexaPDF::Type::Actions::Launch do
|
|
14
14
|
it "needs a launch target" do
|
15
15
|
refute(@action.validate)
|
16
16
|
|
17
|
-
@action.value = {F:
|
17
|
+
@action.value = {Win: {F: "test.exe"}}
|
18
|
+
assert(@action.validate)
|
19
|
+
@action.value = {Mac: 'test'}
|
20
|
+
assert(@action.validate)
|
21
|
+
@action.value = {Unix: 'test'}
|
18
22
|
assert(@action.validate)
|
19
23
|
|
20
|
-
@action.value = {
|
24
|
+
@action.value = {F: {}}
|
21
25
|
assert(@action.validate)
|
22
26
|
end
|
23
27
|
end
|
@@ -138,5 +138,10 @@ describe HexaPDF::Type::FontType1 do
|
|
138
138
|
@embedded_font.delete(:FontDescriptor)
|
139
139
|
refute(@embedded_font.validate)
|
140
140
|
end
|
141
|
+
|
142
|
+
it "ensures a correct Symbol value for the /Encoding key" do
|
143
|
+
@font[:Encoding] = :Other
|
144
|
+
refute(@font.validate)
|
145
|
+
end
|
141
146
|
end
|
142
147
|
end
|
@@ -57,7 +57,7 @@ describe HexaPDF::Type::Form do
|
|
57
57
|
it "creates the resource dictionary if it is not found" do
|
58
58
|
resources = @form.resources
|
59
59
|
assert_equal(:XXResources, resources.type)
|
60
|
-
assert_equal({
|
60
|
+
assert_equal({}, resources.value)
|
61
61
|
end
|
62
62
|
|
63
63
|
it "returns the already used resource dictionary" do
|
@@ -358,6 +358,12 @@ describe HexaPDF::Type::Page do
|
|
358
358
|
page[:Contents] = [@doc.wrap({}, stream: 'q 10'), @doc.wrap({}, stream: 'w Q')]
|
359
359
|
assert_equal('q 10 w Q', page.contents)
|
360
360
|
end
|
361
|
+
|
362
|
+
it "handles null objects in the /Contents array" do
|
363
|
+
page = @doc.pages.add
|
364
|
+
page[:Contents] = [@doc.wrap({}, stream: 'q 10'), nil]
|
365
|
+
assert_equal('q 10 ', page.contents)
|
366
|
+
end
|
361
367
|
end
|
362
368
|
|
363
369
|
describe "contents=" do
|
@@ -389,7 +395,7 @@ describe HexaPDF::Type::Page do
|
|
389
395
|
page = @doc.add({Type: :Page, Parent: @doc.pages.root})
|
390
396
|
resources = page.resources
|
391
397
|
assert_equal(:XXResources, resources.type)
|
392
|
-
assert_equal({
|
398
|
+
assert_equal({}, resources.value)
|
393
399
|
end
|
394
400
|
|
395
401
|
it "returns the already used resource dictionary" do
|
@@ -12,10 +12,12 @@ describe HexaPDF::Utils::SortedTreeNode do
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def add_multilevel_entries
|
15
|
-
|
15
|
+
item = @doc.add(1)
|
16
|
+
@item_ref = HexaPDF::Reference.new(item.oid, item.gen)
|
17
|
+
@kid11 = @doc.add({Limits: ['c', 'f'], Names: ['c', @item_ref, 'f', 1]}, type: HexaPDF::NameTreeNode)
|
16
18
|
@kid12 = @doc.add({Limits: ['i', 'm'], Names: ['i', 1, 'm', 1]}, type: HexaPDF::NameTreeNode)
|
17
19
|
ref = HexaPDF::Reference.new(@kid11.oid, @kid11.gen)
|
18
|
-
@kid1 = @doc.add({Limits: ['c', 'm'], Kids: [ref, @kid12]}
|
20
|
+
@kid1 = @doc.add({Limits: ['c', 'm'], Kids: [ref, @kid12]})
|
19
21
|
@kid21 = @doc.add({Limits: ['o', 'q'], Names: ['o', 1, 'q', 1]}, type: HexaPDF::NameTreeNode)
|
20
22
|
@kid221 = @doc.add({Limits: ['s', 'u'], Names: ['s', 1, 'u', 1]}, type: HexaPDF::NameTreeNode)
|
21
23
|
@kid22 = @doc.add({Limits: ['s', 'u'], Kids: [@kid221]}, type: HexaPDF::NameTreeNode)
|
@@ -75,7 +77,7 @@ describe HexaPDF::Utils::SortedTreeNode do
|
|
75
77
|
@root.add_entry('v', 1)
|
76
78
|
assert_equal(['a', 'm'], @kid1[:Limits].value)
|
77
79
|
assert_equal(['a', 'f'], @kid11[:Limits].value)
|
78
|
-
assert_equal(['a', 1, 'c',
|
80
|
+
assert_equal(['a', 1, 'c', @item_ref, 'e', 1, 'f', 1], @kid11[:Names].value)
|
79
81
|
assert_equal(['g', 'm'], @kid12[:Limits].value)
|
80
82
|
assert_equal(['g', 1, 'i', 1, 'j', 1, 'm', 1], @kid12[:Names].value)
|
81
83
|
assert_equal(['n', 'v'], @kid2[:Limits].value)
|
@@ -203,13 +205,12 @@ describe HexaPDF::Utils::SortedTreeNode do
|
|
203
205
|
end
|
204
206
|
|
205
207
|
it "checks that all kid objects are indirect objects" do
|
206
|
-
@root[:Kids][0] = ref = HexaPDF::Reference.new(@kid1.oid, @kid1.gen)
|
207
208
|
assert(@root.validate)
|
208
209
|
|
209
|
-
@root[:Kids][0] =
|
210
|
+
@root[:Kids][0] = @kid1
|
210
211
|
@kid1.oid = 0
|
211
212
|
assert(@root.validate do |message, c|
|
212
|
-
assert_match(/must be
|
213
|
+
assert_match(/children.*must be indirect/i, message)
|
213
214
|
assert(c)
|
214
215
|
end)
|
215
216
|
assert(@kid1.indirect?)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hexapdf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Leitner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-11-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cmdparse
|
@@ -469,6 +469,7 @@ files:
|
|
469
469
|
- lib/hexapdf/stream.rb
|
470
470
|
- lib/hexapdf/task.rb
|
471
471
|
- lib/hexapdf/task/dereference.rb
|
472
|
+
- lib/hexapdf/task/merge_acro_form.rb
|
472
473
|
- lib/hexapdf/task/optimize.rb
|
473
474
|
- lib/hexapdf/task/pdfa.rb
|
474
475
|
- lib/hexapdf/test_utils.rb
|
@@ -728,6 +729,7 @@ files:
|
|
728
729
|
- test/hexapdf/layout/test_text_shaper.rb
|
729
730
|
- test/hexapdf/layout/test_width_from_polygon.rb
|
730
731
|
- test/hexapdf/task/test_dereference.rb
|
732
|
+
- test/hexapdf/task/test_merge_acro_form.rb
|
731
733
|
- test/hexapdf/task/test_optimize.rb
|
732
734
|
- test/hexapdf/task/test_pdfa.rb
|
733
735
|
- test/hexapdf/test_composer.rb
|