hexapdf 0.47.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +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
|