hexapdf 1.0.1 → 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: 61b0fb56c6522f2af82eb8ffb10570c45bb11460cf4c048c1bdfe8d9daf71afe
4
- data.tar.gz: 91cb053019c367825ac0799a84e4ddad837fe283a6ab2bc6df16ee9ed9f2456d
3
+ metadata.gz: 44eb3afec42bc0c5e0645c0ff6eb48f211083694866fb7c355a0b4ca3e3ad24a
4
+ data.tar.gz: 864792c029e975b0d8167d80326815ea6a7ebff90bc360c4d550c8a2d4a28003
5
5
  SHA512:
6
- metadata.gz: 9a71ee1e9307f0ef67c9dec108c7f68db45166a62f9b6ec60915ce2c089cf0e9ec5bfcd8d74e8b31b63238a09c820a0798689a84e5ea0b1577e2492e5a1d425e
7
- data.tar.gz: b20043cead03f7fc7fe527fdbcb3674ab2d1da06b546bac9c1549b6eb6d143232453132709d93ae008d78a83bff36cf85fd0dbc0938da848e7847a1830e6011e
6
+ metadata.gz: 19d415762cb49abfaf31df0d6dc8455c5a6a8b1eee40f18cc4d56b7a18e4d9e9aded8d5826a4f5a265bbc639d61f5a2fe081b1e672b626984d4be719115f4c9a
7
+ data.tar.gz: 6c8d31294566e9c408cb0fe8512ca4898d583edebe750191fda6d42fcb0a6cb44d83de90359abcac6cca1cf7b47f38e3515b5c94246456b485091d3d72c0be5c
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
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
+
1
15
  ## 1.0.1 - 2024-11-04
2
16
 
3
17
  ### Changed
@@ -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
@@ -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
 
@@ -37,6 +37,6 @@
37
37
  module HexaPDF
38
38
 
39
39
  # The version of HexaPDF.
40
- VERSION = '1.0.1'
40
+ VERSION = '1.0.2'
41
41
 
42
42
  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)
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.1
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-11-04 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