hexapdf 0.20.4 → 0.21.0

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