hexapdf 0.20.4 → 0.21.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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/README.md +5 -3
  4. data/Rakefile +10 -1
  5. data/examples/018-composer.rb +10 -10
  6. data/lib/hexapdf/cli/batch.rb +4 -6
  7. data/lib/hexapdf/cli/info.rb +5 -1
  8. data/lib/hexapdf/cli/inspect.rb +59 -0
  9. data/lib/hexapdf/cli/split.rb +1 -1
  10. data/lib/hexapdf/composer.rb +147 -53
  11. data/lib/hexapdf/configuration.rb +7 -3
  12. data/lib/hexapdf/content/canvas.rb +1 -1
  13. data/lib/hexapdf/content/color_space.rb +1 -1
  14. data/lib/hexapdf/content/operator.rb +7 -7
  15. data/lib/hexapdf/content/parser.rb +3 -3
  16. data/lib/hexapdf/content/processor.rb +9 -9
  17. data/lib/hexapdf/document/signatures.rb +5 -4
  18. data/lib/hexapdf/document.rb +7 -0
  19. data/lib/hexapdf/font/true_type/font.rb +7 -7
  20. data/lib/hexapdf/font/true_type/optimizer.rb +1 -1
  21. data/lib/hexapdf/font/true_type/subsetter.rb +1 -1
  22. data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +3 -3
  23. data/lib/hexapdf/font/true_type_wrapper.rb +9 -14
  24. data/lib/hexapdf/font/type1/font.rb +10 -12
  25. data/lib/hexapdf/font/type1_wrapper.rb +1 -2
  26. data/lib/hexapdf/layout/box.rb +12 -9
  27. data/lib/hexapdf/layout/image_box.rb +1 -1
  28. data/lib/hexapdf/layout/style.rb +28 -8
  29. data/lib/hexapdf/layout/text_fragment.rb +10 -9
  30. data/lib/hexapdf/parser.rb +5 -0
  31. data/lib/hexapdf/tokenizer.rb +3 -3
  32. data/lib/hexapdf/type/acro_form/appearance_generator.rb +6 -4
  33. data/lib/hexapdf/type/acro_form/choice_field.rb +2 -2
  34. data/lib/hexapdf/type/acro_form/field.rb +2 -2
  35. data/lib/hexapdf/type/font_type0.rb +1 -1
  36. data/lib/hexapdf/type/font_type3.rb +1 -1
  37. data/lib/hexapdf/type/resources.rb +4 -4
  38. data/lib/hexapdf/type/signature/adbe_pkcs7_detached.rb +1 -1
  39. data/lib/hexapdf/type/signature.rb +1 -1
  40. data/lib/hexapdf/type/trailer.rb +3 -3
  41. data/lib/hexapdf/version.rb +1 -1
  42. data/lib/hexapdf/xref_section.rb +1 -1
  43. data/test/hexapdf/common_tokenizer_tests.rb +5 -5
  44. data/test/hexapdf/content/test_graphics_state.rb +1 -0
  45. data/test/hexapdf/content/test_operator.rb +2 -2
  46. data/test/hexapdf/content/test_processor.rb +1 -1
  47. data/test/hexapdf/encryption/test_standard_security_handler.rb +23 -29
  48. data/test/hexapdf/filter/test_predictor.rb +16 -20
  49. data/test/hexapdf/font/test_type1_wrapper.rb +1 -1
  50. data/test/hexapdf/font/true_type/table/common.rb +1 -1
  51. data/test/hexapdf/font/true_type/table/test_cmap.rb +1 -1
  52. data/test/hexapdf/font/true_type/table/test_cmap_subtable.rb +1 -1
  53. data/test/hexapdf/image_loader/test_pdf.rb +6 -8
  54. data/test/hexapdf/image_loader/test_png.rb +2 -2
  55. data/test/hexapdf/layout/test_box.rb +11 -1
  56. data/test/hexapdf/layout/test_style.rb +23 -0
  57. data/test/hexapdf/layout/test_text_fragment.rb +21 -21
  58. data/test/hexapdf/test_composer.rb +115 -52
  59. data/test/hexapdf/test_dictionary.rb +2 -2
  60. data/test/hexapdf/test_document.rb +11 -9
  61. data/test/hexapdf/test_object.rb +1 -1
  62. data/test/hexapdf/test_parser.rb +13 -7
  63. data/test/hexapdf/test_serializer.rb +20 -22
  64. data/test/hexapdf/test_stream.rb +7 -9
  65. data/test/hexapdf/test_writer.rb +2 -2
  66. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +1 -2
  67. data/test/hexapdf/type/acro_form/test_choice_field.rb +1 -1
  68. data/test/hexapdf/type/signature/common.rb +1 -1
  69. data/test/hexapdf/type/test_font_type0.rb +1 -1
  70. data/test/hexapdf/type/test_font_type1.rb +7 -7
  71. data/test/hexapdf/type/test_image.rb +13 -17
  72. metadata +2 -2
@@ -476,16 +476,14 @@ describe HexaPDF::Document do
476
476
 
477
477
  describe "write" do
478
478
  it "writes the document to a file" do
479
- begin
480
- file = Tempfile.new('hexapdf-write')
481
- file.close
482
- @io_doc.write(file.path)
483
- HexaPDF::Document.open(file.path) do |doc|
484
- assert_equal(200, doc.object(2).value)
485
- end
486
- ensure
487
- file.unlink
479
+ file = Tempfile.new('hexapdf-write')
480
+ file.close
481
+ @io_doc.write(file.path)
482
+ HexaPDF::Document.open(file.path) do |doc|
483
+ assert_equal(200, doc.object(2).value)
488
484
  end
485
+ ensure
486
+ file.unlink
489
487
  end
490
488
 
491
489
  it "writes the document to an IO object" do
@@ -666,4 +664,8 @@ describe HexaPDF::Document do
666
664
  assert_raises(LocalJumpError) { @doc.cache(:a, :b) }
667
665
  end
668
666
  end
667
+
668
+ it "can be inspected and the output is not too large" do
669
+ assert_match(/HexaPDF::Document:\d+/, @doc.inspect)
670
+ end
669
671
  end
@@ -68,7 +68,7 @@ describe HexaPDF::Object do
68
68
 
69
69
  it "works for arrays" do
70
70
  obj = HexaPDF::PDFArray.new([:b, HexaPDF::Object.new(:a, oid: 3, document: @doc)],
71
- oid: 1, document: @doc)
71
+ oid: 1, document: @doc)
72
72
  assert_equal([:b, :a], HexaPDF::Object.make_direct(obj))
73
73
  end
74
74
  end
@@ -109,7 +109,7 @@ describe HexaPDF::Parser do
109
109
 
110
110
  it "treats indirect objects with invalid values as null objects" do
111
111
  create_parser("1 0 obj <</test ( /other (end)>> endobj")
112
- object, * = @parser.parse_indirect_object
112
+ object, * = @parser.parse_indirect_object
113
113
  assert_nil(object)
114
114
  end
115
115
 
@@ -475,33 +475,33 @@ describe HexaPDF::Parser do
475
475
  describe "invalid numbering of main xref section" do
476
476
  it "handles the xref if the numbering is off by N" do
477
477
  create_parser(" 1 0 obj 1 endobj\n" \
478
- "xref\n1 2\n0000000000 65535 f \n0000000001 00000 n \ntrailer\n<<>>\n")
478
+ "xref\n1 2\n0000000000 65535 f \n0000000001 00000 n \ntrailer\n<<>>\n")
479
479
  section, _trailer = @parser.parse_xref_section_and_trailer(17)
480
480
  assert_equal(HexaPDF::XRefSection.in_use_entry(1, 0, 1), section[1])
481
481
  end
482
482
 
483
483
  it "fails if the first entry is not the one for oid=0" do
484
484
  create_parser(" 1 0 obj 1 endobj\n" \
485
- "xref\n1 2\n0000000000 00005 f \n0000000001 00000 n \ntrailer\n<<>>\n")
485
+ "xref\n1 2\n0000000000 00005 f \n0000000001 00000 n \ntrailer\n<<>>\n")
486
486
  exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(17) }
487
487
  assert_match(/Main.*invalid numbering/i, exp.message)
488
488
 
489
489
  create_parser(" 1 0 obj 1 endobj\n" \
490
- "xref\n1 2\n0000000001 00000 n \n0000000001 00000 n \ntrailer\n<<>>\n")
490
+ "xref\n1 2\n0000000001 00000 n \n0000000001 00000 n \ntrailer\n<<>>\n")
491
491
  exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(17) }
492
492
  assert_match(/Main.*invalid numbering/i, exp.message)
493
493
  end
494
494
 
495
495
  it "fails if the tested entry position is invalid" do
496
496
  create_parser(" 1 0 obj 1 endobj\n" \
497
- "xref\n1 2\n0000000000 65535 f \n0000000005 00000 n \ntrailer\n<<>>\n")
497
+ "xref\n1 2\n0000000000 65535 f \n0000000005 00000 n \ntrailer\n<<>>\n")
498
498
  exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(17) }
499
499
  assert_match(/Main.*invalid numbering/i, exp.message)
500
500
  end
501
501
 
502
502
  it "fails if the tested entry position's oid doesn't match the corrected entry oid" do
503
503
  create_parser(" 2 0 obj 1 endobj\n" \
504
- "xref\n1 2\n0000000000 65535 f \n0000000001 00000 n \ntrailer\n<<>>\n")
504
+ "xref\n1 2\n0000000000 65535 f \n0000000001 00000 n \ntrailer\n<<>>\n")
505
505
  exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(17) }
506
506
  assert_match(/Main.*invalid numbering/i, exp.message)
507
507
  end
@@ -580,6 +580,12 @@ describe HexaPDF::Parser do
580
580
  @xref = HexaPDF::XRefSection.in_use_entry(1, 0, 100)
581
581
  end
582
582
 
583
+ it "can tell us if the cross-reference table was reconstructed" do
584
+ create_parser("1 0 obj\n5\nendobj\ntrailer\n<</Size 1>>")
585
+ @parser.load_object(@xref)
586
+ assert(@parser.reconstructed?)
587
+ end
588
+
583
589
  it "serially parses the contents" do
584
590
  create_parser("1 0 obj\n5\nendobj\n1 0 obj\n6\nendobj\ntrailer\n<</Size 1>>")
585
591
  assert_equal(6, @parser.load_object(@xref).value)
@@ -643,7 +649,7 @@ describe HexaPDF::Parser do
643
649
  end
644
650
 
645
651
  it "uses the first trailer in case of a linearized file" do
646
- create_parser("1 0 obj\n<</Linearized true>>\nendobj\ntrailer <</Size 1/Prev 342>>\ntrailer <</Size 2>>")
652
+ create_parser("1 0 obj\n<</Linearized true>>\nendobj\ntrailer <</Size 1/Prev 34>>\ntrailer <</Size 2>>")
647
653
  assert_equal({Size: 1}, @parser.reconstructed_revision.trailer.value)
648
654
  end
649
655
 
@@ -70,16 +70,16 @@ describe HexaPDF::Serializer do
70
70
 
71
71
  it "serializes symbols" do
72
72
  assert_serialized("/Name", :Name)
73
- assert_serialized("/A;Name_With-Various***Chars?", 'A;Name_With-Various***Chars?'.intern)
74
- assert_serialized("/1.2", '1.2'.intern)
75
- assert_serialized("/$$", '$$'.intern)
76
- assert_serialized("/@pattern", '@pattern'.intern)
77
- assert_serialized('/.notdef', '.notdef'.intern)
78
- assert_serialized('/lime#20Green', 'lime Green'.intern)
79
- assert_serialized('/paired#28#29parentheses', 'paired()parentheses'.intern)
80
- assert_serialized('/The_Key_of_F#23_Minor', 'The_Key_of_F#_Minor'.intern)
81
- assert_serialized('/ ', ''.intern)
82
- assert_serialized('/H#c3#b6#c3#9fgang', "Hößgang".intern)
73
+ assert_serialized("/A;Name_With-Various***Chars?", :'A;Name_With-Various***Chars?')
74
+ assert_serialized("/1.2", :'1.2')
75
+ assert_serialized("/$$", :$$)
76
+ assert_serialized("/@pattern", :@pattern)
77
+ assert_serialized('/.notdef', :'.notdef')
78
+ assert_serialized('/lime#20Green', :'lime Green')
79
+ assert_serialized('/paired#28#29parentheses', :'paired()parentheses')
80
+ assert_serialized('/The_Key_of_F#23_Minor', :'The_Key_of_F#_Minor')
81
+ assert_serialized('/ ', :"")
82
+ assert_serialized('/H#c3#b6#c3#9fgang', :Hößgang)
83
83
  assert_serialized('/H#e8lp', "H\xE8lp".force_encoding('BINARY').intern)
84
84
  end
85
85
 
@@ -101,18 +101,16 @@ describe HexaPDF::Serializer do
101
101
  end
102
102
 
103
103
  it "serializes time like objects" do
104
- begin
105
- tz = ENV['TZ']
106
- ENV['TZ'] = 'Europe/Vienna'
107
- assert_serialized("(D:20150416094100)", Time.new(2015, 04, 16, 9, 41, 0, 0))
108
- assert_serialized("(D:20150416094100+01'00')", Time.new(2015, 04, 16, 9, 41, 0, 3600))
109
- assert_serialized("(D:20150416094100-01'20')", Time.new(2015, 04, 16, 9, 41, 0, -4800))
110
- assert_serialized("(D:20150416000000+02'00')", Date.parse("2015-04-16 9:41:00 +02:00"))
111
- assert_serialized("(D:20150416094100+02'00')",
112
- Time.parse("2015-04-16 9:41:00 +02:00").to_datetime)
113
- ensure
114
- ENV['TZ'] = tz
115
- end
104
+ tz = ENV['TZ']
105
+ ENV['TZ'] = 'Europe/Vienna'
106
+ assert_serialized("(D:20150416094100)", Time.new(2015, 04, 16, 9, 41, 0, 0))
107
+ assert_serialized("(D:20150416094100+01'00')", Time.new(2015, 04, 16, 9, 41, 0, 3600))
108
+ assert_serialized("(D:20150416094100-01'20')", Time.new(2015, 04, 16, 9, 41, 0, -4800))
109
+ assert_serialized("(D:20150416000000+02'00')", Date.parse("2015-04-16 9:41:00 +02:00"))
110
+ assert_serialized("(D:20150416094100+02'00')",
111
+ Time.parse("2015-04-16 9:41:00 +02:00").to_datetime)
112
+ ensure
113
+ ENV['TZ'] = tz
116
114
  end
117
115
 
118
116
  it "serializes HexaPDF objects" do
@@ -47,15 +47,13 @@ describe HexaPDF::StreamData do
47
47
  end
48
48
 
49
49
  it "returns a fiber for a string representing a file name" do
50
- begin
51
- file = Tempfile.new('hexapdf-stream')
52
- file.write('source')
53
- file.close
54
- s = HexaPDF::StreamData.new(file.path)
55
- assert_equal('source', s.fiber.resume)
56
- ensure
57
- file.unlink
58
- end
50
+ file = Tempfile.new('hexapdf-stream')
51
+ file.write('source')
52
+ file.close
53
+ s = HexaPDF::StreamData.new(file.path)
54
+ assert_equal('source', s.fiber.resume)
55
+ ensure
56
+ file.unlink
59
57
  end
60
58
  end
61
59
 
@@ -40,7 +40,7 @@ describe HexaPDF::Writer do
40
40
  219
41
41
  %%EOF
42
42
  3 0 obj
43
- <</Producer(HexaPDF version 0.20.4)>>
43
+ <</Producer(HexaPDF version 0.21.0)>>
44
44
  endobj
45
45
  xref
46
46
  3 1
@@ -72,7 +72,7 @@ describe HexaPDF::Writer do
72
72
  141
73
73
  %%EOF
74
74
  6 0 obj
75
- <</Producer(HexaPDF version 0.20.4)>>
75
+ <</Producer(HexaPDF version 0.21.0)>>
76
76
  endobj
77
77
  2 0 obj
78
78
  <</Length 10>>stream
@@ -602,8 +602,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
602
602
  [:end_text],
603
603
  [:restore_graphics_state],
604
604
  [:restore_graphics_state],
605
- [:end_marked_content]],
606
- )
605
+ [:end_marked_content]])
607
606
  end
608
607
  end
609
608
 
@@ -120,7 +120,7 @@ describe HexaPDF::Type::AcroForm::ChoiceField do
120
120
 
121
121
  describe "option items" do
122
122
  before do
123
- @items = [["a", "Zx"], "\xFE\xFF".b << "Töne".encode('UTF-16BE').b, "H\xe4llo".b,]
123
+ @items = [["a", "Zx"], "\xFE\xFF".b << "Töne".encode('UTF-16BE').b, "H\xe4llo".b]
124
124
  end
125
125
 
126
126
  it "sets the option items" do
@@ -9,7 +9,7 @@ module TestHelper
9
9
  end
10
10
 
11
11
  def ca_certificate
12
- @ca_cert ||=
12
+ @ca_certificate ||=
13
13
  begin
14
14
  ca_name = OpenSSL::X509::Name.parse('/C=AT/O=HexaPDF/CN=HexaPDF Test Root CA')
15
15
 
@@ -112,7 +112,7 @@ describe HexaPDF::Type::FontType0 do
112
112
  end
113
113
 
114
114
  it "calls the configured proc if no mapping is available" do
115
- @font[:Encoding] = :"Identity-H"
115
+ @font[:Encoding] = :'Identity-H'
116
116
  @cid_font[:CIDSystemInfo][:Registry] = :Unknown
117
117
  assert_raises(HexaPDF::Error) { @font.to_utf8(32) }
118
118
  end
@@ -10,24 +10,24 @@ describe HexaPDF::Type::FontType1::StandardFonts do
10
10
  end
11
11
 
12
12
  it "checks whether a given name corresponds to a standard font via #standard_font?" do
13
- assert(@obj.standard_font?(:"Times-Roman"))
13
+ assert(@obj.standard_font?(:'Times-Roman'))
14
14
  assert(@obj.standard_font?(:TimesNewRoman))
15
15
  refute(@obj.standard_font?(:LibreSans))
16
16
  end
17
17
 
18
18
  it "returns the standard PDF name for an alias via #standard_name" do
19
- assert_equal(:"Times-Roman", @obj.standard_name(:TimesNewRoman))
19
+ assert_equal(:'Times-Roman', @obj.standard_name(:TimesNewRoman))
20
20
  end
21
21
 
22
22
  describe "font" do
23
23
  it "returns the Type1 font object for a given standard name" do
24
- font = @obj.font(:"Times-Roman")
24
+ font = @obj.font(:'Times-Roman')
25
25
  assert_equal("Times Roman", font.full_name)
26
26
  end
27
27
 
28
28
  it "caches the font for reuse" do
29
- font = @obj.font(:"Times-Roman")
30
- assert_same(font, @obj.font(:"Times-Roman"))
29
+ font = @obj.font(:'Times-Roman')
30
+ assert_same(font, @obj.font(:'Times-Roman'))
31
31
  end
32
32
 
33
33
  it "returns nil if the given name doesn't belong to a standard font" do
@@ -40,7 +40,7 @@ describe HexaPDF::Type::FontType1 do
40
40
  before do
41
41
  @doc = HexaPDF::Document.new
42
42
  @font = @doc.add({Type: :Font, Subtype: :Type1, Encoding: :WinAnsiEncoding,
43
- BaseFont: :"Times-Roman"})
43
+ BaseFont: :'Times-Roman'})
44
44
 
45
45
  font_file = @doc.add({}, stream: <<-EOF)
46
46
  /Encoding 256 array
@@ -95,7 +95,7 @@ describe HexaPDF::Type::FontType1 do
95
95
 
96
96
  describe "bounding_box" do
97
97
  it "returns the bounding box for a standard font" do
98
- font = HexaPDF::Type::FontType1::StandardFonts.font(:"Times-Roman")
98
+ font = HexaPDF::Type::FontType1::StandardFonts.font(:'Times-Roman')
99
99
  assert_equal(font.bounding_box, @font.bounding_box)
100
100
  end
101
101
 
@@ -165,26 +165,22 @@ describe HexaPDF::Type::Image do
165
165
  end
166
166
 
167
167
  it "writes JPEG images to a file with .jpg extension" do
168
- begin
169
- file = Tempfile.new(['hexapdf-image-write-test', '.jpg'])
170
- image = @doc.images.add(@jpg)
171
- image.write(file.path)
172
- assert_equal(File.binread(@jpg), File.binread(file.path))
173
- ensure
174
- file.unlink
175
- end
168
+ file = Tempfile.new(['hexapdf-image-write-test', '.jpg'])
169
+ image = @doc.images.add(@jpg)
170
+ image.write(file.path)
171
+ assert_equal(File.binread(@jpg), File.binread(file.path))
172
+ ensure
173
+ file.unlink
176
174
  end
177
175
 
178
176
  it "writes JPEG2000 images to a file with .jpx extension" do
179
- begin
180
- file = Tempfile.new(['hexapdf-image-write-test', '.jpx'])
181
- image = @doc.images.add(@jpg)
182
- image.set_filter(:JPXDecode) # fake it
183
- image.write(file.path)
184
- assert_equal(File.binread(@jpg), File.binread(file.path))
185
- ensure
186
- file.unlink
187
- end
177
+ file = Tempfile.new(['hexapdf-image-write-test', '.jpx'])
178
+ image = @doc.images.add(@jpg)
179
+ image.set_filter(:JPXDecode) # fake it
180
+ image.write(file.path)
181
+ assert_equal(File.binread(@jpg), File.binread(file.path))
182
+ ensure
183
+ file.unlink
188
184
  end
189
185
 
190
186
  Dir.glob(File.join(TEST_DATA_DIR, 'images', '*.png')).each do |png_file|
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.20.4
4
+ version: 0.21.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: 2022-01-26 00:00:00.000000000 Z
11
+ date: 2022-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cmdparse