hexapdf 0.47.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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