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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -16
  3. data/lib/hexapdf/composer.rb +7 -0
  4. data/lib/hexapdf/configuration.rb +2 -0
  5. data/lib/hexapdf/content/parser.rb +3 -1
  6. data/lib/hexapdf/digital_signature/cms_handler.rb +13 -0
  7. data/lib/hexapdf/digital_signature/signature.rb +1 -1
  8. data/lib/hexapdf/digital_signature/signing/default_handler.rb +1 -0
  9. data/lib/hexapdf/document.rb +14 -3
  10. data/lib/hexapdf/font/cmap/writer.rb +58 -4
  11. data/lib/hexapdf/font/cmap.rb +7 -0
  12. data/lib/hexapdf/font/true_type_wrapper.rb +41 -16
  13. data/lib/hexapdf/layout/text_fragment.rb +2 -1
  14. data/lib/hexapdf/object.rb +1 -1
  15. data/lib/hexapdf/parser.rb +1 -1
  16. data/lib/hexapdf/reference.rb +1 -1
  17. data/lib/hexapdf/task/merge_acro_form.rb +164 -0
  18. data/lib/hexapdf/task.rb +1 -0
  19. data/lib/hexapdf/tokenizer.rb +2 -0
  20. data/lib/hexapdf/type/acro_form/form.rb +14 -27
  21. data/lib/hexapdf/type/acro_form/signature_field.rb +16 -6
  22. data/lib/hexapdf/type/acro_form/variable_text_field.rb +1 -1
  23. data/lib/hexapdf/type/actions/go_to.rb +1 -0
  24. data/lib/hexapdf/type/actions/go_to_r.rb +1 -0
  25. data/lib/hexapdf/type/actions/launch.rb +5 -1
  26. data/lib/hexapdf/type/annotation.rb +6 -1
  27. data/lib/hexapdf/type/annotations/markup_annotation.rb +14 -1
  28. data/lib/hexapdf/type/catalog.rb +3 -0
  29. data/lib/hexapdf/type/cid_font.rb +4 -1
  30. data/lib/hexapdf/type/file_specification.rb +17 -14
  31. data/lib/hexapdf/type/font_descriptor.rb +4 -3
  32. data/lib/hexapdf/type/font_simple.rb +3 -1
  33. data/lib/hexapdf/type/font_true_type.rb +2 -0
  34. data/lib/hexapdf/type/font_type0.rb +1 -1
  35. data/lib/hexapdf/type/font_type1.rb +7 -0
  36. data/lib/hexapdf/type/font_type3.rb +0 -1
  37. data/lib/hexapdf/type/form.rb +5 -2
  38. data/lib/hexapdf/type/graphics_state_parameter.rb +7 -4
  39. data/lib/hexapdf/type/image.rb +8 -4
  40. data/lib/hexapdf/type/info.rb +2 -2
  41. data/lib/hexapdf/type/mark_information.rb +2 -2
  42. data/lib/hexapdf/type/optional_content_configuration.rb +1 -1
  43. data/lib/hexapdf/type/optional_content_membership.rb +1 -1
  44. data/lib/hexapdf/type/page.rb +5 -3
  45. data/lib/hexapdf/type/resources.rb +6 -6
  46. data/lib/hexapdf/type/viewer_preferences.rb +4 -3
  47. data/lib/hexapdf/version.rb +1 -1
  48. data/test/hexapdf/common_tokenizer_tests.rb +5 -0
  49. data/test/hexapdf/digital_signature/signing/test_default_handler.rb +6 -0
  50. data/test/hexapdf/digital_signature/test_cms_handler.rb +12 -7
  51. data/test/hexapdf/digital_signature/test_signature.rb +7 -0
  52. data/test/hexapdf/digital_signature/test_signatures.rb +8 -3
  53. data/test/hexapdf/font/cmap/test_writer.rb +73 -16
  54. data/test/hexapdf/font/test_true_type_wrapper.rb +17 -3
  55. data/test/hexapdf/layout/test_list_box.rb +7 -7
  56. data/test/hexapdf/layout/test_text_fragment.rb +3 -3
  57. data/test/hexapdf/layout/test_text_layouter.rb +4 -2
  58. data/test/hexapdf/task/test_merge_acro_form.rb +104 -0
  59. data/test/hexapdf/test_composer.rb +8 -0
  60. data/test/hexapdf/test_document.rb +9 -0
  61. data/test/hexapdf/test_parser.rb +7 -0
  62. data/test/hexapdf/test_writer.rb +8 -3
  63. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +18 -18
  64. data/test/hexapdf/type/acro_form/test_form.rb +7 -3
  65. data/test/hexapdf/type/actions/test_launch.rb +6 -2
  66. data/test/hexapdf/type/test_font_type1.rb +5 -0
  67. data/test/hexapdf/type/test_form.rb +1 -1
  68. data/test/hexapdf/type/test_page.rb +7 -1
  69. 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
@@ -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
@@ -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
- 349
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
- x\xDAcbdlg``b`\xB0\x04\x93\x93\x18\x18\x00\f\e\x01[
89
+ #{xref_stream}
85
90
  endstream
86
91
  endobj
87
92
  startxref
88
- 448
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
- [:set_text_matrix, [1, 0, 0, 1, 2, 6.41]],
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
- [:set_text_matrix, [1, 0, 0, 1, 78.55, 6.41]],
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
- [:set_text_matrix, [1, 0, 0, 1, 40.275, 6.41]],
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
- [:set_text_matrix, [1, 0, 0, 1, 2, 6.41]],
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
- [:set_text_matrix, [1, 0, 0, 1, 2, 3.07]],
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
- [:set_text_matrix, [1, 0, 0, 1, 2, 2.035]],
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
- [:set_text_matrix, [1, 0, 0, 1, 2, 16.195]],
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
- [:set_text_matrix, [1, 0, 0, 1, 78.55, 16.195]],
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
- [:set_text_matrix, [1, 0, 0, 1, 40.275, 16.195]],
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
- [:set_text_matrix, [1, 0, 0, 1, 2, 16.195]],
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
- [:set_text_matrix, [1, 0, 0, 1, 2, 18.556]],
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
- [:set_text_matrix, [1, 0, 0, 1, 2.945, 6.41]],
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
- [:set_text_matrix, [1, 0, 0, 1, 62.945, 6.41]],
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
- [:set_text_matrix, [1, 0, 0, 1, 32.945, 6.41]],
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
- [:set_text_matrix, [1, 0, 0, 1, 22.39, 6.41]],
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
- [:set_text_matrix, [1, 0, 0, 1, 2.945, 6.41]],
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
- [:set_text_matrix, [1, 0, 0, 1, 2, 6.41]],
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
- [:set_text_matrix, [1, 0, 0, 1, 2, 23.609]],
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, font: 'Times')
162
+ field = @acro_form.send(method, "field", **args)
163
163
  font_name, font_size, font_color = field.parse_default_appearance_string
164
- assert_equal(:'Times-Roman', @acro_form.default_resources.font(font_name)[:BaseFont])
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 = {Win: {F: "test.exe"}}
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({ProcSet: [:PDF, :Text, :ImageB, :ImageC, :ImageI]}, resources.value)
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({ProcSet: [:PDF, :Text, :ImageB, :ImageC, :ImageI]}, resources.value)
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.47.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-09-07 00:00:00.000000000 Z
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