hexapdf 0.32.0 → 0.32.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c74f0e152b612b628321f312413a0eb88e52a41a809f7b37e993acb9e0436b2
4
- data.tar.gz: 9dfe8cfaaa10527695b70d9019636ed9cc6059367125a45a3dacde7bc1d8c3bb
3
+ metadata.gz: f0907362b897b39d61fa8f859510ccfb31ebd5ad88b94a9e36ff62c62fae813a
4
+ data.tar.gz: 469465941253bad5e769fc0dd5a57e437c578fdecda52a0fea908370ef8b7169
5
5
  SHA512:
6
- metadata.gz: f9ee0dbe507a6d31cbf024d2f34e69c15e491bc05a64a0f4039f38bcf6d52192245d6c7d2ac4cb65ae1eb110f8af735afcf13366e15975d96bcc17dda8e2a618
7
- data.tar.gz: 846ce0611e5f125011aa0535d2de69c15c356f6164f9b09f0a314f9968e34e4addda5396c03d82cd70dffc96eaa0408e1a6dd7a6fc55ce59646be5c3f8b4f9a6
6
+ metadata.gz: 2fc77d33ddc2c6fb377a778b82f92f01fed93d9a4e494cd57c97dc0a00fce4baa7cab185776f8a3303507d68b544e4404ac2f33b72c3b1a7cc7c65ee838c7dd7
7
+ data.tar.gz: 21a08984f424b91594a2d8e6fc705a7f9458a990918ee77adef469e758ae2fee976ba905a7a4a696c5c2728831d02f8845dc9b5316c28de4ff5c7f062521a7e3
data/CHANGELOG.md CHANGED
@@ -1,3 +1,38 @@
1
+ ## 0.32.2 - 2023-05-06
2
+
3
+ ### Changed
4
+
5
+ * Cross-reference table reconstruction to be more relaxed concerning the
6
+ `endobj` keyword
7
+
8
+ ### Fixed
9
+
10
+ * [HexaPDF::Type::ObjectStream] to not compress any encryption dictionary
11
+ instead of only the current one
12
+
13
+
14
+ ## 0.32.1 - 2023-04-20
15
+
16
+ ### Added
17
+
18
+ * [HexaPDF::Type::FontType0#font_descriptor] and
19
+ [HexaPDF::Type::FontSimple#font_descriptor] for easy access to the font
20
+ descriptor
21
+
22
+ ### Changed
23
+
24
+ * [HexaPDF::Content::Canvas#color_from_specification] to allow strings and color
25
+ objects without a wrapping array
26
+
27
+ ### Fixed
28
+
29
+ * AES 128bit encryption to include unnecessary field in encryption dictionary to
30
+ work around buggy PDF libraries
31
+ * [HexaPDF::Layout::Style::LinkLayer] to correctly process the border color
32
+ * [HexaPDF::Type::AcroForm::AppearanceGenerator] to use fallback for font cap
33
+ height value when necessary
34
+
35
+
1
36
  ## 0.32.0 - 2023-03-08
2
37
 
3
38
  ### Added
data/lib/hexapdf/cli.rb CHANGED
@@ -62,6 +62,10 @@ module HexaPDF
62
62
  Application.new.parse(args)
63
63
  rescue StandardError => e
64
64
  $stderr.puts "Problem encountered: #{e.message}"
65
+ unless e.kind_of?(HexaPDF::Error)
66
+ $stderr.puts "--> The problem might indicate a faulty PDF or a bug in HexaPDF."
67
+ $stderr.puts "--> Please report this at https://github.com/gettalong/hexapdf/issues - thanks!"
68
+ end
65
69
  exit(1)
66
70
  end
67
71
 
@@ -2363,6 +2363,7 @@ module HexaPDF
2363
2363
  # This utility method is meant for use by higher-level methods that need to convert a color
2364
2364
  # specification into a color object for this Canvas object.
2365
2365
  def color_from_specification(spec)
2366
+ spec = Array(spec)
2366
2367
  if spec.length == 1 && spec[0].kind_of?(String)
2367
2368
  ColorSpace.device_color_from_specification(spec)
2368
2369
  elsif spec.length == 1 && spec[0].respond_to?(:color_space)
@@ -358,7 +358,7 @@ module HexaPDF
358
358
  raise(HexaPDF::UnsupportedEncryptionError,
359
359
  "Invalid key length #{key_length} specified")
360
360
  end
361
- dict[:Length] = key_length if dict[:V] == 2
361
+ dict[:Length] = key_length if dict[:V] == 4 || dict[:V] == 2
362
362
 
363
363
  if ![:aes, :arc4].include?(algorithm)
364
364
  raise(HexaPDF::UnsupportedEncryptionError,
@@ -508,6 +508,12 @@ module HexaPDF
508
508
  *matrix.evaluate(box.width, box.height), *matrix.evaluate(0, box.height)]
509
509
  x_minmax = quad_points.values_at(0, 2, 4, 6).minmax
510
510
  y_minmax = quad_points.values_at(1, 3, 5, 7).minmax
511
+ border_color = case @border_color
512
+ when [], nil
513
+ @border_color
514
+ else
515
+ canvas.color_from_specification(@border_color).components
516
+ end
511
517
  annot = {
512
518
  Subtype: :Link,
513
519
  Rect: [x_minmax[0], y_minmax[0], x_minmax[1], y_minmax[1]],
@@ -515,7 +521,7 @@ module HexaPDF
515
521
  Dest: @dest,
516
522
  A: @action,
517
523
  Border: @border,
518
- C: @border_color && canvas.color_from_specification(@border_color).components,
524
+ C: border_color,
519
525
  }
520
526
  (page[:Annots] ||= []) << page.document.add(annot)
521
527
  end
@@ -458,7 +458,7 @@ module HexaPDF
458
458
  linearized = obj.kind_of?(Hash) && obj.key?(:Linearized)
459
459
  @tokenizer.pos = pos
460
460
  end
461
- @tokenizer.scan_until(/(?:\n|\r\n?)endobj\b/)
461
+ @tokenizer.scan_until(/\bendobj\b/)
462
462
  end
463
463
  elsif token.kind_of?(Tokenizer::Token) && token == 'trailer'
464
464
  obj = @tokenizer.next_object rescue nil
@@ -395,7 +395,9 @@ module HexaPDF
395
395
 
396
396
  # Adobe seems to be vertically centering based on the cap height, if enough space is
397
397
  # available
398
- cap_height = style.font.wrapped_font.cap_height * style.font.scaling_factor / 1000.0 *
398
+ tmp_cap_height = style.font.wrapped_font.cap_height ||
399
+ style.font.pdf_object.font_descriptor&.[](:CapHeight)
400
+ cap_height = tmp_cap_height * style.font.scaling_factor / 1000.0 *
399
401
  style.font_size
400
402
  y = padding + (height - 2 * padding - cap_height) / 2.0
401
403
  y = padding - style.scaled_font_descender if y < 0
@@ -53,6 +53,13 @@ module HexaPDF
53
53
  define_field :FontDescriptor, type: :FontDescriptor, indirect: true
54
54
  define_field :Encoding, type: [Dictionary, Symbol]
55
55
 
56
+ # Returns the font descriptor. May be +nil+ for a standard 14 font.
57
+ #
58
+ # The font descriptor is required except for the standard 14 fonts in PDF version up to 1.7.
59
+ def font_descriptor
60
+ self[:FontDescriptor]
61
+ end
62
+
56
63
  # Returns the encoding object used for this font.
57
64
  #
58
65
  # Note that the encoding is cached internally when accessed the first time.
@@ -63,6 +63,11 @@ module HexaPDF
63
63
  end
64
64
  end
65
65
 
66
+ # Returns the font descriptor of the descendant font.
67
+ def font_descriptor
68
+ descendant_font[:FontDescriptor]
69
+ end
70
+
66
71
  # Returns the writing mode which is either :horizontal or :vertical.
67
72
  def writing_mode
68
73
  cmap.wmode == 0 ? :horizontal : :vertical
@@ -172,13 +172,16 @@ module HexaPDF
172
172
  serializer = Serializer.new
173
173
  obj_to_stm = {}
174
174
 
175
- encrypt_dict = document.trailer[:Encrypt]
175
+ is_encrypt_dict = document.revisions.each.with_object({}) do |rev, hash|
176
+ hash[rev.trailer[:Encrypt]] = true
177
+ end
176
178
  while index < objects.size / 2
177
179
  obj = revision.object(objects[index])
178
180
 
179
181
  # Due to a bug in Adobe Acrobat, the Catalog may not be in an object stream if the
180
182
  # document is encrypted
181
- if obj.nil? || obj.null? || obj.gen != 0 || obj.kind_of?(Stream) || obj == encrypt_dict ||
183
+ if obj.nil? || obj.null? || obj.gen != 0 || obj.kind_of?(Stream) ||
184
+ is_encrypt_dict[obj] ||
182
185
  obj.type == :Catalog ||
183
186
  obj.type == :Sig || obj.type == :DocTimeStamp ||
184
187
  (obj.respond_to?(:key?) && obj.key?(:ByteRange) && obj.key?(:Contents))
@@ -37,6 +37,6 @@
37
37
  module HexaPDF
38
38
 
39
39
  # The version of HexaPDF.
40
- VERSION = '0.32.0'
40
+ VERSION = '0.32.2'
41
41
 
42
42
  end
@@ -1280,4 +1280,30 @@ describe HexaPDF::Content::Canvas do
1280
1280
  end
1281
1281
  end
1282
1282
  end
1283
+
1284
+ describe "color_from_specification "do
1285
+ it "accepts a color string" do
1286
+ assert_equal([1, 0, 0], @canvas.color_from_specification("red").components)
1287
+ end
1288
+
1289
+ it "accepts a color string wrapped in an array" do
1290
+ assert_equal([1, 0, 0], @canvas.color_from_specification(["red"]).components)
1291
+ end
1292
+
1293
+ it "accepts a color object" do
1294
+ color = @canvas.color_from_specification("red")
1295
+ assert_equal(color, @canvas.color_from_specification(color))
1296
+ end
1297
+
1298
+ it "accepts a color object wrapped in an array" do
1299
+ color = @canvas.color_from_specification("red")
1300
+ assert_equal(color, @canvas.color_from_specification([color]))
1301
+ end
1302
+
1303
+ it "accepts an array with 1, 3, or 4 color values" do
1304
+ assert_equal([1], @canvas.color_from_specification([255]).components)
1305
+ assert_equal([1, 0, 0], @canvas.color_from_specification([255, 0, 0]).components)
1306
+ assert_equal([1, 0, 0, 0], @canvas.color_from_specification([100, 0, 0, 0]).components)
1307
+ end
1308
+ end
1283
1309
  end
@@ -141,6 +141,11 @@ describe HexaPDF::Encryption::SecurityHandler do
141
141
  @handler.set_up_encryption(key_length: key_length, algorithm: algorithm)
142
142
  assert(result == @handler.dict[:Length])
143
143
  end
144
+
145
+ # Work-around buggy software
146
+ @handler.set_up_encryption(key_length: 128, algorithm: :aes)
147
+ assert_equal(4, @handler.dict[:V])
148
+ assert_equal(128, @handler.dict[:Length])
144
149
  end
145
150
 
146
151
  it "calls the prepare_encryption method" do
@@ -573,10 +573,19 @@ describe HexaPDF::Layout::Style::LinkLayer do
573
573
  assert_equal([0, 0, 1], annot[:Border].value)
574
574
  end
575
575
 
576
- it "uses the specified border and border color" do
577
- annot = call_link(dest: true, border: [10, 10, 2], border_color: [255])
576
+ it "uses the specified border" do
577
+ annot = call_link(dest: true, border: [10, 10, 2])
578
578
  assert_equal([10, 10, 2], annot[:Border].value)
579
- assert_equal([1.0], annot[:C].value)
579
+ end
580
+
581
+ it "uses the specified border color" do
582
+ annot = call_link(dest: true, border_color: "red")
583
+ assert_equal([1.0, 0, 0], annot[:C].value)
584
+ end
585
+
586
+ it "works when the border color is transparent" do
587
+ annot = call_link(dest: true, border_color: [])
588
+ assert_equal([], annot[:C].value)
580
589
  end
581
590
 
582
591
  it "works for simple destinations" do
@@ -609,7 +609,7 @@ describe HexaPDF::Parser do
609
609
  end
610
610
 
611
611
  it "serially parses the contents" do
612
- create_parser("1 0 obj\n5\nendobj\n1 0 obj\n6\nendobj\ntrailer\n<</Size 1>>")
612
+ create_parser("1 0 obj\n5 endobj\n1 0 obj\n6\nendobj\ntrailer\n<</Size 1>>")
613
613
  assert_equal(6, @parser.load_object(@xref).value)
614
614
  end
615
615
 
@@ -40,7 +40,7 @@ describe HexaPDF::Writer do
40
40
  219
41
41
  %%EOF
42
42
  3 0 obj
43
- <</Producer(HexaPDF version 0.32.0)>>
43
+ <</Producer(HexaPDF version 0.32.2)>>
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.32.0)>>
75
+ <</Producer(HexaPDF version 0.32.2)>>
76
76
  endobj
77
77
  2 0 obj
78
78
  <</Length 10>>stream
@@ -214,7 +214,7 @@ describe HexaPDF::Writer do
214
214
  <</Type/Page/MediaBox[0 0 595 842]/Parent 2 0 R/Resources<<>>>>
215
215
  endobj
216
216
  5 0 obj
217
- <</Producer(HexaPDF version 0.32.0)>>
217
+ <</Producer(HexaPDF version 0.32.2)>>
218
218
  endobj
219
219
  4 0 obj
220
220
  <</Root 1 0 R/Info 5 0 R/Size 6/Type/XRef/W[1 1 2]/Index[0 6]/Filter/FlateDecode/DecodeParms<</Columns 4/Predictor 12>>/Length 33>>stream
@@ -519,6 +519,19 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
519
519
  range: 7)
520
520
  end
521
521
 
522
+ it "falls back to the cap height in the font descriptor for vertical alignment" do
523
+ font_metrics = @form.default_resources.font(:F1).font_wrapper.wrapped_font.metrics
524
+ cap_height = font_metrics.cap_height
525
+ font_metrics.cap_height = nil
526
+
527
+ @generator.create_appearances
528
+ assert_operators(@widget[:AP][:N].stream,
529
+ [:set_text_matrix, [1, 0, 0, 1, 2, 6.41]],
530
+ range: 7)
531
+ ensure
532
+ font_metrics.cap_height = cap_height
533
+ end
534
+
522
535
  it "vertically aligns to the font descender if the text is too high" do
523
536
  @widget[:Rect].height = 5
524
537
  @generator.create_appearances
@@ -13,15 +13,19 @@ describe HexaPDF::Type::FontSimple do
13
13
  <22> <0042>
14
14
  endbfchar
15
15
  EOF
16
- font_descriptor = @doc.add({Type: :FontDescriptor, FontName: :Embedded, Flags: 0b100,
17
- FontBBox: [0, 1, 2, 3], ItalicAngle: 0, Ascent: 900,
18
- MissingWidth: 500, Descent: -100, CapHeight: 800, StemV: 20})
16
+ @font_descriptor = @doc.add({Type: :FontDescriptor, FontName: :Embedded, Flags: 0b100,
17
+ FontBBox: [0, 1, 2, 3], ItalicAngle: 0, Ascent: 900,
18
+ MissingWidth: 500, Descent: -100, CapHeight: 800, StemV: 20})
19
19
  @font = @doc.add({Type: :Font, Encoding: :WinAnsiEncoding,
20
- BaseFont: :Embedded, FontDescriptor: font_descriptor, ToUnicode: cmap,
20
+ BaseFont: :Embedded, FontDescriptor: @font_descriptor, ToUnicode: cmap,
21
21
  FirstChar: 32, LastChar: 34, Widths: [600, 0, 700]},
22
22
  type: HexaPDF::Type::FontSimple)
23
23
  end
24
24
 
25
+ it "returns the font descriptor of the font" do
26
+ assert_same(@font_descriptor, @font.font_descriptor)
27
+ end
28
+
25
29
  describe "encoding" do
26
30
  it "fails if /Encoding is absent because encoding_from_font is not implemented" do
27
31
  @font.delete(:Encoding)
@@ -7,8 +7,8 @@ require 'hexapdf/type/font_type0'
7
7
  describe HexaPDF::Type::FontType0 do
8
8
  before do
9
9
  @doc = HexaPDF::Document.new
10
- fd = @doc.add({Type: :FontDescriptor, FontBBox: [0, 1, 2, 3]})
11
- @cid_font = @doc.wrap({Type: :Font, Subtype: :CIDFontType2, W: [633, [100]], FontDescriptor: fd,
10
+ @fd = @doc.add({Type: :FontDescriptor, FontBBox: [0, 1, 2, 3]})
11
+ @cid_font = @doc.wrap({Type: :Font, Subtype: :CIDFontType2, W: [633, [100]], FontDescriptor: @fd,
12
12
  CIDSystemInfo: {Registry: 'Adobe', Ordering: 'Japan1', Supplement: 1}})
13
13
  @font = @doc.wrap({Type: :Font, Subtype: :Type0, Encoding: :H, DescendantFonts: [@cid_font]})
14
14
  end
@@ -26,6 +26,10 @@ describe HexaPDF::Type::FontType0 do
26
26
  assert_equal(@cid_font.value, @font.descendant_font.value)
27
27
  end
28
28
 
29
+ it "returns the font descriptor of the descendant font" do
30
+ assert_same(@fd, @font.font_descriptor)
31
+ end
32
+
29
33
  it "uses the descendant font for getting the width of a code point" do
30
34
  assert_equal(100, @font.width(0x2121))
31
35
  end
@@ -23,9 +23,14 @@ describe HexaPDF::Type::ObjectStream do
23
23
  before do
24
24
  @doc = Object.new
25
25
  @doc.instance_variable_set(:@version, '1.5')
26
- def (@doc).trailer
27
- @trailer ||= {Encrypt: HexaPDF::Object.new({}, oid: 9)}
26
+ @doc.define_singleton_method(:revisions) do
27
+ rev1 = Object.new
28
+ def rev1.trailer; {Encrypt: HexaPDF::Object.new({}, oid: 10)}; end
29
+ rev2 = Object.new
30
+ def rev2.trailer; {Encrypt: HexaPDF::Object.new({}, oid: 9)}; end
31
+ @revisions ||= [rev1, rev2]
28
32
  end
33
+ @doc.define_singleton_method(:trailer) { revisions.last.trailer }
29
34
  @obj = HexaPDF::Type::ObjectStream.new({N: 2, First: 8}, oid: 1, document: @doc,
30
35
  stream: "1 0 5 2 5 [1 2]")
31
36
  end
@@ -96,8 +101,9 @@ describe HexaPDF::Type::ObjectStream do
96
101
  assert_equal("", @obj.stream)
97
102
  end
98
103
 
99
- it "doesn't allow the encryption dictionary to be compressed" do
104
+ it "doesn't allow an encryption dictionary to be compressed" do
100
105
  @obj.add_object(@doc.trailer[:Encrypt])
106
+ @obj.add_object(@doc.revisions[0].trailer[:Encrypt])
101
107
  @obj.write_objects(@revision)
102
108
  assert_equal(0, @obj.value[:N])
103
109
  assert_equal(0, @obj.value[:First])
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.32.0
4
+ version: 0.32.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Leitner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-08 00:00:00.000000000 Z
11
+ date: 2023-05-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cmdparse