hexapdf 1.0.0 → 1.0.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2889ba1d03e2c351efd694b1583063023fff97c0da636ff5103f88538255735c
4
- data.tar.gz: 6fb4727db05900e8fccba2ad4e093d1092e17305e5b5616ded97a76cf835673c
3
+ metadata.gz: 44eb3afec42bc0c5e0645c0ff6eb48f211083694866fb7c355a0b4ca3e3ad24a
4
+ data.tar.gz: 864792c029e975b0d8167d80326815ea6a7ebff90bc360c4d550c8a2d4a28003
5
5
  SHA512:
6
- metadata.gz: 00be8ed2c306a88e5bfc0eada97a7e6bf802ec269e832bb21b3521d4077b18ecad11946ddc6f8a6d575820e66339059e59ba2c4cdd2b74d6c7d6defd0f2f5256
7
- data.tar.gz: 94c6a8178ead2a986921b72b07ef5dc388a5fa6a67945573eec921db30c9940d241d3f47591c8fbbf9bdcf313df0dda536f0fc78e0e946e27dfa3bc13dad9a28
6
+ metadata.gz: 19d415762cb49abfaf31df0d6dc8455c5a6a8b1eee40f18cc4d56b7a18e4d9e9aded8d5826a4f5a265bbc639d61f5a2fe081b1e672b626984d4be719115f4c9a
7
+ data.tar.gz: 6c8d31294566e9c408cb0fe8512ca4898d583edebe750191fda6d42fcb0a6cb44d83de90359abcac6cca1cf7b47f38e3515b5c94246456b485091d3d72c0be5c
data/CHANGELOG.md CHANGED
@@ -1,3 +1,32 @@
1
+ ## 1.0.1 - 2024-11-05
2
+
3
+ ### Added
4
+
5
+ * [HexaPDF::Type::CMap] for representing CMap streams
6
+
7
+ ### Fixed
8
+
9
+ * Checksum calculation for TrueType tables
10
+ * Automatic wrapping of dictionary entry /CIDToGIDMap for CID fonts
11
+ * Performance regression when encoding char codes for TrueType fonts
12
+ * PDF/A validation regression for PDFs using TrueType fonts
13
+
14
+
15
+ ## 1.0.1 - 2024-11-04
16
+
17
+ ### Changed
18
+
19
+ * Informational output on errors when running CLI commands to provide more
20
+ details
21
+
22
+ ### Fixed
23
+
24
+ * Parsing of indirect objects the value of which is an indirect reference
25
+ * Writing of the initial cross-reference section to ensure a single subsection
26
+ * [HexaPDF::Utils::SortedTreeNode] to wrap all /Kids entries with the correct
27
+ type class
28
+
29
+
1
30
  ## 1.0.0 - 2024-10-26
2
31
 
3
32
  ### Added
data/lib/hexapdf/cli.rb CHANGED
@@ -64,8 +64,21 @@ module HexaPDF
64
64
  rescue StandardError => e
65
65
  $stderr.puts "Problem encountered: #{e.message}"
66
66
  unless e.kind_of?(HexaPDF::Error)
67
+ $stderr.puts "Backtrace (last 10 lines):"
68
+ $stderr.puts e.backtrace[0, 10]
69
+ $stderr.puts
67
70
  $stderr.puts "--> The problem might indicate a faulty PDF or a bug in HexaPDF."
68
- $stderr.puts "--> Please report this at https://github.com/gettalong/hexapdf/issues - thanks!"
71
+ $stderr.puts "--> Please report this at"
72
+ $stderr.puts "-->"
73
+ $stderr.puts "--> https://github.com/gettalong/hexapdf/issues"
74
+ $stderr.puts "-->"
75
+ $stderr.puts "--> and include the information above as well as the output of running"
76
+ $stderr.puts "--> the following command on the input PDF:"
77
+ $stderr.puts "-->"
78
+ $stderr.puts "--> hexapdf info --check INPUT.PDF"
79
+ $stderr.puts "-->"
80
+ $stderr.puts "--> If possible, please also provide the input PDF."
81
+ $stderr.puts "--> Thanks!"
69
82
  end
70
83
  exit(1)
71
84
  end
@@ -722,6 +722,7 @@ module HexaPDF
722
722
  OutputIntent: 'HexaPDF::Type::OutputIntent',
723
723
  XXDestOutputProfileRef: 'HexaPDF::Type::OutputIntent::DestOutputProfileRef',
724
724
  ExData: 'HexaPDF::Type::Annotations::MarkupAnnotation::ExData',
725
+ CMap: 'HexaPDF::Type::CMap',
725
726
  },
726
727
  'object.subtype_map' => {
727
728
  nil => {
@@ -63,16 +63,6 @@ module HexaPDF
63
63
  def use_glyph(glyph_id)
64
64
  return @glyph_map[glyph_id] if @glyph_map.key?(glyph_id)
65
65
  @last_id += 1
66
- # Handle codes for ASCII characters \r (13), (, ) (40, 41) and \ (92) specially so that
67
- # they never appear in the output (PDF serialization would need to escape them)
68
- if @last_id == 13 || @last_id == 40 || @last_id == 92
69
- @glyph_map[:"s#{@last_id}"] = @last_id
70
- if @last_id == 40
71
- @last_id += 1
72
- @glyph_map[:"s#{@last_id}"] = @last_id
73
- end
74
- @last_id += 1
75
- end
76
66
  @glyph_map[glyph_id] = @last_id
77
67
  end
78
68
 
@@ -117,7 +107,7 @@ module HexaPDF
117
107
  locations = []
118
108
 
119
109
  @glyph_map.each_key do |old_gid|
120
- glyph = orig_glyf[old_gid.kind_of?(Symbol) ? 0 : old_gid]
110
+ glyph = orig_glyf[old_gid]
121
111
  locations << table.size
122
112
  data = glyph.raw_data
123
113
  if glyph.compound?
@@ -176,10 +166,7 @@ module HexaPDF
176
166
  # Adds the components of compound glyphs to the subset.
177
167
  def add_glyph_components
178
168
  glyf = @font[:glyf]
179
- @glyph_map.keys.each do |gid|
180
- next if gid.kind_of?(Symbol)
181
- glyf[gid].components&.each {|cgid| use_glyph(cgid) }
182
- end
169
+ @glyph_map.keys.each {|gid| glyf[gid].components&.each {|cgid| use_glyph(cgid) } }
183
170
  end
184
171
 
185
172
  end
@@ -63,7 +63,12 @@ module HexaPDF
63
63
 
64
64
  # Calculates the checksum for the given data.
65
65
  def self.calculate_checksum(data)
66
- data.unpack('N*').inject(0) {|sum, long| sum + long } % 2**32
66
+ checksum = 0
67
+ if (remainder_length = data.length % 4) != 0
68
+ checksum = (data[-remainder_length, remainder_length] << "\0" * (4 - remainder_length)).
69
+ unpack1('N')
70
+ end
71
+ checksum + data.unpack('N*').inject(0) {|sum, long| sum + long } % 2**32
67
72
  end
68
73
 
69
74
  # The TrueType font object associated with this table.
@@ -239,6 +239,11 @@ module HexaPDF
239
239
  raise HexaPDF::MissingGlyphError.new(glyph) if glyph.kind_of?(InvalidGlyph)
240
240
  @subsetter.use_glyph(glyph.id) if @subsetter
241
241
  @last_char_code += 1
242
+ # Handle codes for ASCII characters \r (13), (, ) (40, 41) and \ (92) specially so that
243
+ # they never appear in the output (PDF serialization would need to escape them)
244
+ if @last_char_code == 13 || @last_char_code == 40 || @last_char_code == 92
245
+ @last_char_code += (@last_char_code == 40 ? 2 : 1)
246
+ end
242
247
  [[@last_char_code].pack('n'), @last_char_code]
243
248
  end)[0]
244
249
  end
@@ -376,7 +381,11 @@ module HexaPDF
376
381
  dict[:Encoding] = :'Identity-H'
377
382
  else
378
383
  stream = HexaPDF::StreamData.new { HexaPDF::Font::CMap.create_cid_cmap(mapping) }
379
- stream_obj = document.add({}, stream: stream)
384
+ stream_obj = document.add({Type: :CMap,
385
+ CMapName: :Custom,
386
+ CIDSystemInfo: {Registry: "Adobe", Ordering: "Identity",
387
+ Supplement: 0},
388
+ }, stream: stream)
380
389
  stream_obj.set_filter(:FlateDecode)
381
390
  dict[:Encoding] = stream_obj
382
391
  end
@@ -116,7 +116,11 @@ module HexaPDF
116
116
  "the values (#{xref_entry.oid},#{xref_entry.gen}) from the xref")
117
117
  end
118
118
 
119
- @document.wrap(obj, oid: oid, gen: gen, stream: stream)
119
+ if obj.kind_of?(Reference)
120
+ @document.deref(obj)
121
+ else
122
+ @document.wrap(obj, oid: oid, gen: gen, stream: stream)
123
+ end
120
124
  rescue HexaPDF::MalformedPDFError
121
125
  reconstructed_revision.object(xref_entry) ||
122
126
  @document.wrap(nil, oid: xref_entry.oid, gen: xref_entry.gen)
@@ -70,7 +70,7 @@ module HexaPDF
70
70
  define_field :W, type: PDFArray
71
71
  define_field :DW2, type: PDFArray, default: [880, -1100]
72
72
  define_field :W2, type: PDFArray
73
- define_field :CIDToGIDMap, type: [Symbol, Stream]
73
+ define_field :CIDToGIDMap, type: [Stream, Symbol]
74
74
 
75
75
  # Returns the unscaled width of the given CID in glyph units, or 0 if the width for the CID is
76
76
  # missing.
@@ -0,0 +1,58 @@
1
+ # -*- encoding: utf-8; frozen_string_literal: true -*-
2
+ #
3
+ #--
4
+ # This file is part of HexaPDF.
5
+ #
6
+ # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
+ # Copyright (C) 2014-2024 Thomas Leitner
8
+ #
9
+ # HexaPDF is free software: you can redistribute it and/or modify it
10
+ # under the terms of the GNU Affero General Public License version 3 as
11
+ # published by the Free Software Foundation with the addition of the
12
+ # following permission added to Section 15 as permitted in Section 7(a):
13
+ # FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
14
+ # THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
15
+ # INFRINGEMENT OF THIRD PARTY RIGHTS.
16
+ #
17
+ # HexaPDF is distributed in the hope that it will be useful, but WITHOUT
18
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
20
+ # License for more details.
21
+ #
22
+ # You should have received a copy of the GNU Affero General Public License
23
+ # along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
24
+ #
25
+ # The interactive user interfaces in modified source and object code
26
+ # versions of HexaPDF must display Appropriate Legal Notices, as required
27
+ # under Section 5 of the GNU Affero General Public License version 3.
28
+ #
29
+ # In accordance with Section 7(b) of the GNU Affero General Public
30
+ # License, a covered work must retain the producer line in every PDF that
31
+ # is created or manipulated using HexaPDF.
32
+ #
33
+ # If the GNU Affero General Public License doesn't fit your need,
34
+ # commercial licenses are available at <https://gettalong.at/hexapdf/>.
35
+ #++
36
+
37
+ require 'hexapdf/stream'
38
+
39
+ module HexaPDF
40
+ module Type
41
+
42
+ # Represents an embedded CMap file.
43
+ #
44
+ # See: PDF2.0 s9.7.5.3
45
+ class CMap < Stream
46
+
47
+ define_type :CMap
48
+
49
+ define_field :Type, type: Symbol, required: true, default: type
50
+ define_field :CMapName, type: Symbol, required: true
51
+ define_field :CIDSystemInfo, type: :XXCIDSystemInfo, required: true
52
+ define_field :WMode, type: Integer
53
+ define_field :UseCMap, type: [Stream, Symbol]
54
+
55
+ end
56
+
57
+ end
58
+ end
data/lib/hexapdf/type.rb CHANGED
@@ -82,6 +82,7 @@ module HexaPDF
82
82
  autoload(:OptionalContentConfiguration, 'hexapdf/type/optional_content_configuration')
83
83
  autoload(:Metadata, 'hexapdf/type/metadata')
84
84
  autoload(:OutputIntent, 'hexapdf/type/output_intent')
85
+ autoload(:CMap, 'hexapdf/type/cmap')
85
86
 
86
87
  end
87
88
 
@@ -174,6 +174,7 @@ module HexaPDF
174
174
  elsif node.key?(:Kids)
175
175
  index = find_in_intermediate_node(node[:Kids], key)
176
176
  node = node[:Kids][index]
177
+ node = document.wrap(node, type: self.class) if node
177
178
  break unless node && key >= node[:Limits][0] && key <= node[:Limits][1]
178
179
  else
179
180
  break
@@ -194,7 +195,7 @@ module HexaPDF
194
195
  container_name = leaf_node_container_name
195
196
  stack = [self]
196
197
  until stack.empty?
197
- node = stack.pop
198
+ node = document.wrap(stack.pop, type: self.class)
198
199
  if node.key?(container_name)
199
200
  data = node[container_name]
200
201
  index = 0
@@ -217,7 +218,7 @@ module HexaPDF
217
218
  def path_to_key(node, key, stack)
218
219
  return unless node.key?(:Kids)
219
220
  index = find_in_intermediate_node(node[:Kids], key)
220
- stack << node[:Kids][index]
221
+ stack << document.wrap(node[:Kids][index], type: self.class)
221
222
  path_to_key(stack.last, key, stack)
222
223
  end
223
224
 
@@ -307,6 +308,15 @@ module HexaPDF
307
308
  super
308
309
  container_name = leaf_node_container_name
309
310
 
311
+ if key?(:Kids)
312
+ self[:Kids].each do |kid|
313
+ unless kid.indirect?
314
+ yield("Children of sorted tree nodes must be indirect", true)
315
+ document.add(kid)
316
+ end
317
+ end
318
+ end
319
+
310
320
  # All keys of the container must be lexically ordered strings and the container must be
311
321
  # correctly formatted
312
322
  if key?(container_name)
@@ -37,6 +37,6 @@
37
37
  module HexaPDF
38
38
 
39
39
  # The version of HexaPDF.
40
- VERSION = '1.0.0'
40
+ VERSION = '1.0.2'
41
41
 
42
42
  end
@@ -149,6 +149,7 @@ module HexaPDF
149
149
  obj_to_stm = object_streams.each_with_object({}) {|stm, m| m.update(stm.write_objects(rev)) }
150
150
 
151
151
  xref_section = XRefSection.new
152
+ xref_section.mark_as_initial_section! unless previous_xref_pos
152
153
  xref_section.add_free_entry(0, 65535) if previous_xref_pos.nil?
153
154
  rev.each do |obj|
154
155
  if obj.null?
@@ -111,6 +111,13 @@ module HexaPDF
111
111
  # used.
112
112
  private :'[]='
113
113
 
114
+ # Marks this XRefSection object as being the first cross-reference section in a PDF file.
115
+ #
116
+ # This has the consequence that only a single sub-section is created.
117
+ def mark_as_initial_section!
118
+ @initial_section = true
119
+ end
120
+
114
121
  # Adds an in-use entry to the cross-reference section.
115
122
  #
116
123
  # See: ::in_use_entry
@@ -147,15 +154,24 @@ module HexaPDF
147
154
  # If this section contains no objects, a single empty array is yielded (corresponding to a
148
155
  # subsection with zero elements).
149
156
  #
150
- # The subsections are dynamically generated based on the object numbers in this section.
157
+ # The subsections are dynamically generated based on the object numbers in this section. In case
158
+ # the section was marked as the initial section (see #mark_as_initial_section!) only a single
159
+ # subsection is yielded.
151
160
  def each_subsection
152
161
  return to_enum(__method__) unless block_given?
153
162
 
154
163
  temp = []
155
164
  oids.sort.each do |oid|
156
- if !temp.empty? && temp[-1].oid + 1 != oid
157
- yield(temp)
158
- temp = []
165
+ expected_next_oid = !temp.empty? && temp[-1].oid + 1
166
+ if expected_next_oid && expected_next_oid != oid
167
+ if @initial_section
168
+ expected_next_oid.upto(oid - 1) do |free_oid|
169
+ temp << self.class.free_entry(free_oid, 0)
170
+ end
171
+ else
172
+ yield(temp)
173
+ temp = []
174
+ end
159
175
  end
160
176
  temp << self[oid]
161
177
  end
@@ -119,6 +119,11 @@ describe HexaPDF::Font::TrueTypeWrapper do
119
119
  assert_equal([3].pack('n'), code)
120
120
  end
121
121
 
122
+ it "doesn't use char codes 13, 40, 41 and 92 because they would need to be escaped" do
123
+ codes = 1.upto(93).map {|i| @font_wrapper.encode(@font_wrapper.glyph(i)) }.join
124
+ assert_equal([1..12, 14..39, 42..91, 93..97].flat_map(&:to_a).pack('n*'), codes)
125
+ end
126
+
122
127
  it "raises an error if an InvalidGlyph is encoded" do
123
128
  exp = assert_raises(HexaPDF::MissingGlyphError) do
124
129
  @font_wrapper.encode(@font_wrapper.decode_utf8("ö").first)
@@ -27,16 +27,6 @@ describe HexaPDF::Font::TrueType::Subsetter do
27
27
  assert_equal(value, @subsetter.subset_glyph_id(5))
28
28
  end
29
29
 
30
- it "doesn't use certain subset glyph IDs for performance reasons" do
31
- 1.upto(93) {|i| @subsetter.use_glyph(i) }
32
- # glyph 0, 93 used glyph, 4 special glyphs
33
- assert_equal(1 + 93 + 4, @subsetter.instance_variable_get(:@glyph_map).size)
34
- 1.upto(12) {|i| assert_equal(i, @subsetter.subset_glyph_id(i), "id=#{i}") }
35
- 13.upto(38) {|i| assert_equal(i + 1, @subsetter.subset_glyph_id(i), "id=#{i}") }
36
- 39.upto(88) {|i| assert_equal(i + 3, @subsetter.subset_glyph_id(i), "id=#{i}") }
37
- 89.upto(93) {|i| assert_equal(i + 4, @subsetter.subset_glyph_id(i), "id=#{i}") }
38
- end
39
-
40
30
  it "creates the subset font file" do
41
31
  gid = @font[:cmap].preferred_table[0x41]
42
32
  @subsetter.use_glyph(gid)
@@ -13,6 +13,18 @@ describe HexaPDF::Font::TrueType::Table do
13
13
  @entry = HexaPDF::Font::TrueType::Table::Directory::Entry.new('tagg', 0, 0, @file.io.string.length)
14
14
  end
15
15
 
16
+ describe "self.calculate_checksum" do
17
+ it "works for data with a length divisible by four" do
18
+ klass = HexaPDF::Font::TrueType::Table
19
+ assert_equal(256, klass.calculate_checksum("\x00\x00\x00\x01\x00\x00\x00\xFF"))
20
+ end
21
+
22
+ it "works for data with a length not divisible by four" do
23
+ klass = HexaPDF::Font::TrueType::Table
24
+ assert_equal(512, klass.calculate_checksum("\x00\x00\x00\x01\x00\x00\x00\xFF\x00\x00\x01"))
25
+ end
26
+ end
27
+
16
28
  describe "initialize" do
17
29
  it "reads the data from the associated file" do
18
30
  table = TrueTypeTestTable.new(@file, @entry)
@@ -33,18 +33,23 @@ describe HexaPDF::Parser do
33
33
  endstream
34
34
  endobj
35
35
 
36
+ 5 0 obj
37
+ 1 0 R
38
+ endobj
39
+
36
40
  xref
37
41
  0 4
38
42
  0000000000 65535 f
39
43
  0000000010 00000 n
40
44
  0000000029 00000 n
41
45
  0000000000 65535 f
42
- 3 1
46
+ 3 2
43
47
  0000000556 00000 n
48
+ 0000000308 00000 n
44
49
  trailer
45
50
  << /Test (now) >>
46
51
  startxref
47
- 308
52
+ 330
48
53
  %%EOF
49
54
  EOF
50
55
  end
@@ -305,6 +310,11 @@ describe HexaPDF::Parser do
305
310
  assert_equal(0, obj.gen)
306
311
  end
307
312
 
313
+ it "handles the case of the value of an indirect object being an indirect reference" do
314
+ obj = @parser.load_object(HexaPDF::XRefSection.in_use_entry(5, 0, 308))
315
+ assert_equal(1, obj.oid)
316
+ end
317
+
308
318
  describe "with strict parsing" do
309
319
  it "raises an error if an indirect object has an offset of 0" do
310
320
  @document.config['parser.on_correctable_error'] = proc { true }
@@ -343,13 +353,13 @@ describe HexaPDF::Parser do
343
353
 
344
354
  describe "startxref_offset" do
345
355
  it "caches the offset value" do
346
- assert_equal(308, @parser.startxref_offset)
347
- @parser.instance_eval { @io }.string.sub!(/308\n/, "309\n")
348
- assert_equal(308, @parser.startxref_offset)
356
+ assert_equal(330, @parser.startxref_offset)
357
+ @parser.instance_eval { @io }.string.sub!(/330\n/, "309\n")
358
+ assert_equal(330, @parser.startxref_offset)
349
359
  end
350
360
 
351
361
  it "returns the correct offset" do
352
- assert_equal(308, @parser.startxref_offset)
362
+ assert_equal(330, @parser.startxref_offset)
353
363
  end
354
364
 
355
365
  it "ignores garbage at the end of the file" do
@@ -53,8 +53,8 @@ describe HexaPDF::Writer do
53
53
  EOF
54
54
 
55
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["
56
+ when 5 then "x\xDAcbdlg``b`\xB0\x04\x93\x93\x19\x18\x00\f\x1E\x01\\"
57
+ when 6 then "x\xDAcbd\xEC```b`\xB0\x04\x93\x93\x18\x18\x00\f*\x01\\"
58
58
  else fail
59
59
  end
60
60
  @compressed_input_io = StringIO.new(<<~EOF.force_encoding(Encoding::BINARY))
@@ -69,8 +69,8 @@ describe HexaPDF::Writer do
69
69
  20
70
70
  endobj
71
71
  3 0 obj
72
- <</Size 6/Type/XRef/W[1 1 2]/Index[0 4 5 1]/Filter/FlateDecode/DecodeParms<</Columns 4/Predictor 12>>/Length 31>>stream
73
- x\xDAcb`\xF8\xFF\x9F\x89\x89\x95\x91\x91\xE9\x7F\x19\x03\x03\x13\x83\x10\x88he`\x00\x00B4\x04\x1E
72
+ <</Size 6/Type/XRef/W[1 1 2]/Index[0 6]/Filter/FlateDecode/DecodeParms<</Columns 4/Predictor 12>>/Length 36>>stream
73
+ x\xDAcb`\xF8\xFF\x9F\x89\x89\x95\x91\x91\xE9\x7F\x19\x03\x03\x13\x83\x10\x90\xF8_\f\x14c\x14bd\x04\x00lk\a
74
74
  endstream
75
75
  endobj
76
76
  startxref
@@ -90,7 +90,7 @@ describe HexaPDF::Writer do
90
90
  endstream
91
91
  endobj
92
92
  startxref
93
- #{442 + HexaPDF::VERSION.length}
93
+ #{443 + HexaPDF::VERSION.length}
94
94
  %%EOF
95
95
  EOF
96
96
  end
@@ -57,5 +57,20 @@ describe HexaPDF::XRefSection do
57
57
  @xref_section.add_in_use_entry(20, 0, 0)
58
58
  assert_subsections([[1, 2], [10, 11], [20]])
59
59
  end
60
+
61
+ it "yields a single subsection if the section was marked as the initial one" do
62
+ @xref_section.mark_as_initial_section!
63
+ @xref_section.add_in_use_entry(6, 0, 0)
64
+ @xref_section.add_in_use_entry(7, 0, 0)
65
+ @xref_section.add_in_use_entry(9, 0, 0)
66
+ @xref_section.add_in_use_entry(1, 0, 0)
67
+ @xref_section.add_in_use_entry(2, 0, 0)
68
+ result = @xref_section.each_subsection.map {|s| s.map {|e| [e.oid, e.type] }}
69
+ assert_equal([[[1, :in_use], [2, :in_use],
70
+ [3, :free], [4, :free], [5, :free],
71
+ [6, :in_use], [7, :in_use],
72
+ [8, :free],
73
+ [9, :in_use]]], result)
74
+ end
60
75
  end
61
76
  end
@@ -12,10 +12,12 @@ describe HexaPDF::Utils::SortedTreeNode do
12
12
  end
13
13
 
14
14
  def add_multilevel_entries
15
- @kid11 = @doc.add({Limits: ['c', 'f'], Names: ['c', 1, 'f', 1]}, type: HexaPDF::NameTreeNode)
15
+ item = @doc.add(1)
16
+ @item_ref = HexaPDF::Reference.new(item.oid, item.gen)
17
+ @kid11 = @doc.add({Limits: ['c', 'f'], Names: ['c', @item_ref, 'f', 1]}, type: HexaPDF::NameTreeNode)
16
18
  @kid12 = @doc.add({Limits: ['i', 'm'], Names: ['i', 1, 'm', 1]}, type: HexaPDF::NameTreeNode)
17
19
  ref = HexaPDF::Reference.new(@kid11.oid, @kid11.gen)
18
- @kid1 = @doc.add({Limits: ['c', 'm'], Kids: [ref, @kid12]}, type: HexaPDF::NameTreeNode)
20
+ @kid1 = @doc.add({Limits: ['c', 'm'], Kids: [ref, @kid12]})
19
21
  @kid21 = @doc.add({Limits: ['o', 'q'], Names: ['o', 1, 'q', 1]}, type: HexaPDF::NameTreeNode)
20
22
  @kid221 = @doc.add({Limits: ['s', 'u'], Names: ['s', 1, 'u', 1]}, type: HexaPDF::NameTreeNode)
21
23
  @kid22 = @doc.add({Limits: ['s', 'u'], Kids: [@kid221]}, type: HexaPDF::NameTreeNode)
@@ -75,7 +77,7 @@ describe HexaPDF::Utils::SortedTreeNode do
75
77
  @root.add_entry('v', 1)
76
78
  assert_equal(['a', 'm'], @kid1[:Limits].value)
77
79
  assert_equal(['a', 'f'], @kid11[:Limits].value)
78
- assert_equal(['a', 1, 'c', 1, 'e', 1, 'f', 1], @kid11[:Names].value)
80
+ assert_equal(['a', 1, 'c', @item_ref, 'e', 1, 'f', 1], @kid11[:Names].value)
79
81
  assert_equal(['g', 'm'], @kid12[:Limits].value)
80
82
  assert_equal(['g', 1, 'i', 1, 'j', 1, 'm', 1], @kid12[:Names].value)
81
83
  assert_equal(['n', 'v'], @kid2[:Limits].value)
@@ -203,13 +205,12 @@ describe HexaPDF::Utils::SortedTreeNode do
203
205
  end
204
206
 
205
207
  it "checks that all kid objects are indirect objects" do
206
- @root[:Kids][0] = ref = HexaPDF::Reference.new(@kid1.oid, @kid1.gen)
207
208
  assert(@root.validate)
208
209
 
209
- @root[:Kids][0] = ref
210
+ @root[:Kids][0] = @kid1
210
211
  @kid1.oid = 0
211
212
  assert(@root.validate do |message, c|
212
- assert_match(/must be an indirect object/, message)
213
+ assert_match(/children.*must be indirect/i, message)
213
214
  assert(c)
214
215
  end)
215
216
  assert(@kid1.indirect?)
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: 1.0.0
4
+ version: 1.0.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: 2024-10-26 00:00:00.000000000 Z
11
+ date: 2024-11-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cmdparse
@@ -500,6 +500,7 @@ files:
500
500
  - lib/hexapdf/type/annotations/widget.rb
501
501
  - lib/hexapdf/type/catalog.rb
502
502
  - lib/hexapdf/type/cid_font.rb
503
+ - lib/hexapdf/type/cmap.rb
503
504
  - lib/hexapdf/type/embedded_file.rb
504
505
  - lib/hexapdf/type/file_specification.rb
505
506
  - lib/hexapdf/type/font.rb