hexapdf 0.47.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +50 -16
- 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 +1 -1
- 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/version.rb +1 -1
- 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 +7 -0
- data/test/hexapdf/test_writer.rb +8 -3
- 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
- metadata +4 -2
@@ -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
@@ -152,6 +152,13 @@ describe HexaPDF::Parser do
|
|
152
152
|
assert_equal('12', collector(stream.fiber))
|
153
153
|
end
|
154
154
|
|
155
|
+
it "recovers from a non-existing indirect reference to a stream length value" do
|
156
|
+
create_parser("1 0 obj<</Length 2 0 R>> stream\n12(ab\nendstream endobj")
|
157
|
+
obj, _, _, stream = @parser.parse_indirect_object
|
158
|
+
assert_equal(5, obj[:Length])
|
159
|
+
assert_equal('12(ab', collector(stream.fiber))
|
160
|
+
end
|
161
|
+
|
155
162
|
it "works even if the keyword endobj is missing or mangled" do
|
156
163
|
create_parser("1 0 obj<</Length 4>>5")
|
157
164
|
object, * = @parser.parse_indirect_object
|
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\xDAcbdlc``b`\xB0\x04\x93\x93\x19\x18\x00\f\x0F\x01["
|
57
|
+
when 6 then "x\xDAcbdlg``b`\xB0\x04\x93\x93\x18\x18\x00\f\e\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
|
@@ -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
|
+
#{442 + HexaPDF::VERSION.length}
|
89
94
|
%%EOF
|
90
95
|
EOF
|
91
96
|
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
|
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.0
|
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-10-26 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
|