ttfunk 1.7.0 → 1.8.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 (88) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +74 -0
  4. data/README.md +17 -15
  5. data/lib/ttfunk/aggregate.rb +5 -0
  6. data/lib/ttfunk/bin_utils.rb +27 -8
  7. data/lib/ttfunk/bit_field.rb +25 -2
  8. data/lib/ttfunk/collection.rb +27 -3
  9. data/lib/ttfunk/directory.rb +7 -1
  10. data/lib/ttfunk/encoded_string.rb +58 -4
  11. data/lib/ttfunk/max.rb +14 -0
  12. data/lib/ttfunk/min.rb +14 -0
  13. data/lib/ttfunk/one_based_array.rb +20 -0
  14. data/lib/ttfunk/otf_encoder.rb +5 -14
  15. data/lib/ttfunk/placeholder.rb +15 -1
  16. data/lib/ttfunk/reader.rb +6 -4
  17. data/lib/ttfunk/resource_file.rb +29 -5
  18. data/lib/ttfunk/sci_form.rb +20 -3
  19. data/lib/ttfunk/sub_table.rb +29 -4
  20. data/lib/ttfunk/subset/base.rb +48 -0
  21. data/lib/ttfunk/subset/code_page.rb +49 -2
  22. data/lib/ttfunk/subset/mac_roman.rb +2 -0
  23. data/lib/ttfunk/subset/unicode.rb +32 -0
  24. data/lib/ttfunk/subset/unicode_8bit.rb +32 -0
  25. data/lib/ttfunk/subset/windows_1252.rb +2 -0
  26. data/lib/ttfunk/subset.rb +8 -0
  27. data/lib/ttfunk/subset_collection.rb +39 -14
  28. data/lib/ttfunk/sum.rb +13 -0
  29. data/lib/ttfunk/table/cff/charset.rb +96 -18
  30. data/lib/ttfunk/table/cff/charsets/expert.rb +3 -2
  31. data/lib/ttfunk/table/cff/charsets/expert_subset.rb +3 -2
  32. data/lib/ttfunk/table/cff/charsets/iso_adobe.rb +3 -2
  33. data/lib/ttfunk/table/cff/charsets/standard_strings.rb +3 -2
  34. data/lib/ttfunk/table/cff/charsets.rb +1 -0
  35. data/lib/ttfunk/table/cff/charstring.rb +33 -12
  36. data/lib/ttfunk/table/cff/charstrings_index.rb +17 -11
  37. data/lib/ttfunk/table/cff/dict.rb +53 -23
  38. data/lib/ttfunk/table/cff/encoding.rb +82 -24
  39. data/lib/ttfunk/table/cff/encodings/expert.rb +3 -2
  40. data/lib/ttfunk/table/cff/encodings/standard.rb +3 -2
  41. data/lib/ttfunk/table/cff/encodings.rb +1 -0
  42. data/lib/ttfunk/table/cff/fd_selector.rb +61 -21
  43. data/lib/ttfunk/table/cff/font_dict.rb +30 -18
  44. data/lib/ttfunk/table/cff/font_index.rb +22 -10
  45. data/lib/ttfunk/table/cff/header.rb +16 -3
  46. data/lib/ttfunk/table/cff/index.rb +97 -65
  47. data/lib/ttfunk/table/cff/one_based_index.rb +11 -1
  48. data/lib/ttfunk/table/cff/path.rb +43 -4
  49. data/lib/ttfunk/table/cff/private_dict.rb +31 -11
  50. data/lib/ttfunk/table/cff/subr_index.rb +7 -2
  51. data/lib/ttfunk/table/cff/top_dict.rb +82 -59
  52. data/lib/ttfunk/table/cff/top_index.rb +10 -6
  53. data/lib/ttfunk/table/cff.rb +41 -21
  54. data/lib/ttfunk/table/cmap/format00.rb +27 -6
  55. data/lib/ttfunk/table/cmap/format04.rb +34 -14
  56. data/lib/ttfunk/table/cmap/format06.rb +28 -1
  57. data/lib/ttfunk/table/cmap/format10.rb +29 -2
  58. data/lib/ttfunk/table/cmap/format12.rb +29 -2
  59. data/lib/ttfunk/table/cmap/subtable.rb +50 -6
  60. data/lib/ttfunk/table/cmap.rb +21 -0
  61. data/lib/ttfunk/table/dsig.rb +47 -6
  62. data/lib/ttfunk/table/glyf/compound.rb +73 -6
  63. data/lib/ttfunk/table/glyf/path_based.rb +40 -3
  64. data/lib/ttfunk/table/glyf/simple.rb +50 -5
  65. data/lib/ttfunk/table/glyf.rb +15 -7
  66. data/lib/ttfunk/table/head.rb +84 -6
  67. data/lib/ttfunk/table/hhea.rb +71 -10
  68. data/lib/ttfunk/table/hmtx.rb +32 -5
  69. data/lib/ttfunk/table/kern/format0.rb +25 -7
  70. data/lib/ttfunk/table/kern.rb +16 -4
  71. data/lib/ttfunk/table/loca.rb +21 -8
  72. data/lib/ttfunk/table/maxp.rb +195 -10
  73. data/lib/ttfunk/table/name.rb +126 -9
  74. data/lib/ttfunk/table/os2.rb +150 -26
  75. data/lib/ttfunk/table/post/format10.rb +7 -0
  76. data/lib/ttfunk/table/post/format20.rb +9 -0
  77. data/lib/ttfunk/table/post/format30.rb +6 -0
  78. data/lib/ttfunk/table/post/format40.rb +5 -0
  79. data/lib/ttfunk/table/post.rb +63 -7
  80. data/lib/ttfunk/table/sbix.rb +50 -14
  81. data/lib/ttfunk/table/simple.rb +5 -0
  82. data/lib/ttfunk/table/vorg.rb +31 -3
  83. data/lib/ttfunk/table.rb +20 -1
  84. data/lib/ttfunk/ttf_encoder.rb +39 -41
  85. data/lib/ttfunk.rb +154 -1
  86. data.tar.gz.sig +0 -0
  87. metadata +50 -28
  88. metadata.gz.sig +0 -0
@@ -3,31 +3,41 @@
3
3
  module TTFunk
4
4
  class Table
5
5
  class Cff < TTFunk::Table
6
+ # CFF top dict.
6
7
  class TopDict < TTFunk::Table::Cff::Dict
8
+ # Default charstring type.
7
9
  DEFAULT_CHARSTRING_TYPE = 2
10
+
11
+ # Length of placeholders for pointer operators.
8
12
  POINTER_PLACEHOLDER_LENGTH = 5
13
+
14
+ # Length of placeholders for other operators.
9
15
  PLACEHOLDER_LENGTH = 5
10
16
 
11
- # operators whose values are offsets that point to other parts
12
- # of the file
17
+ # Operators whose values are offsets that point to other parts
18
+ # of the file.
13
19
  POINTER_OPERATORS = {
14
20
  charset: 15,
15
21
  encoding: 16,
16
22
  charstrings_index: 17,
17
23
  private: 18,
18
24
  font_index: 1236,
19
- font_dict_selector: 1237
25
+ font_dict_selector: 1237,
20
26
  }.freeze
21
27
 
22
- # all the operators we currently care about
28
+ # All the operators we currently care about.
23
29
  OPERATORS = {
24
30
  **POINTER_OPERATORS,
25
31
  ros: 1230,
26
- charstring_type: 1206
32
+ charstring_type: 1206,
27
33
  }.freeze
28
34
 
35
+ # Inverse operator mapping.
29
36
  OPERATOR_CODES = OPERATORS.invert
30
37
 
38
+ # Encode dict.
39
+ #
40
+ # @return [TTFunk::EncodedString]
31
41
  def encode(*)
32
42
  EncodedString.new do |result|
33
43
  each_with_index do |(operator, operands), _idx|
@@ -36,7 +46,7 @@ module TTFunk
36
46
  elsif pointer_operator?(operator)
37
47
  result << Placeholder.new(
38
48
  OPERATOR_CODES[operator],
39
- length: POINTER_PLACEHOLDER_LENGTH
49
+ length: POINTER_PLACEHOLDER_LENGTH,
40
50
  )
41
51
  else
42
52
  operands.each { |operand| result << encode_operand(operand) }
@@ -47,75 +57,69 @@ module TTFunk
47
57
  end
48
58
  end
49
59
 
50
- def finalize(new_cff_data, new_to_old, old_to_new)
60
+ # Finalize the table.
61
+ #
62
+ # @param new_cff_data [TTFunk::EncodedString]
63
+ # @param charmap [Hash{Integer => Hash}] keys are the charac codes,
64
+ # values are hashes:
65
+ # * `:old` (<tt>Integer</tt>) - glyph ID in the original font.
66
+ # * `:new` (<tt>Integer</tt>) - glyph ID in the subset font.
67
+ # @return [void]
68
+ def finalize(new_cff_data, charmap)
51
69
  if charset
52
- finalize_subtable(
53
- new_cff_data, :charset, charset.encode(new_to_old)
54
- )
70
+ finalize_subtable(new_cff_data, :charset, charset.encode(charmap))
55
71
  end
56
72
 
57
73
  if encoding
58
- finalize_subtable(
59
- new_cff_data, :encoding, encoding.encode(new_to_old, old_to_new)
60
- )
74
+ finalize_subtable(new_cff_data, :encoding, encoding.encode(charmap))
61
75
  end
62
76
 
63
77
  if charstrings_index
64
- finalize_subtable(
65
- new_cff_data,
66
- :charstrings_index,
67
- charstrings_index.encode(new_to_old, &:encode)
68
- )
78
+ finalize_subtable(new_cff_data, :charstrings_index, charstrings_index.encode(charmap))
69
79
  end
70
80
 
71
81
  if font_index
72
- finalize_subtable(
73
- new_cff_data,
74
- :font_index,
75
- font_index.encode do |font_dict|
76
- font_dict.encode(new_to_old)
77
- end
78
- )
82
+ finalize_subtable(new_cff_data, :font_index, font_index.encode)
79
83
 
80
- font_index.finalize(new_cff_data, new_to_old)
84
+ font_index.finalize(new_cff_data)
81
85
  end
82
86
 
83
87
  if font_dict_selector
84
- finalize_subtable(
85
- new_cff_data,
86
- :font_dict_selector,
87
- font_dict_selector.encode(new_to_old)
88
- )
88
+ finalize_subtable(new_cff_data, :font_dict_selector, font_dict_selector.encode(charmap))
89
89
  end
90
90
 
91
91
  if private_dict
92
- encoded_private_dict = private_dict.encode(new_to_old)
92
+ encoded_private_dict = private_dict.encode
93
93
  encoded_offset = encode_integer32(new_cff_data.length)
94
94
  encoded_length = encode_integer32(encoded_private_dict.length)
95
95
 
96
- new_cff_data.resolve_placeholder(
97
- :"private_length_#{@table_offset}", encoded_length
98
- )
99
-
100
- new_cff_data.resolve_placeholder(
101
- :"private_offset_#{@table_offset}", encoded_offset
102
- )
96
+ new_cff_data.resolve_placeholder(:"private_length_#{@table_offset}", encoded_length)
97
+ new_cff_data.resolve_placeholder(:"private_offset_#{@table_offset}", encoded_offset)
103
98
 
104
99
  private_dict.finalize(encoded_private_dict)
105
100
  new_cff_data << encoded_private_dict
106
101
  end
107
102
  end
108
103
 
104
+ # Registry Ordering Supplement.
105
+ #
106
+ # @return [Array(Integer, Integer, Integer), nil]
109
107
  def ros
110
108
  self[OPERATORS[:ros]]
111
109
  end
112
110
 
111
+ # Is Registry Ordering Supplement present in this dict?
112
+ #
113
+ # @return [Boolean]
113
114
  def ros?
114
115
  !ros.nil?
115
116
  end
116
117
 
117
118
  alias is_cid_font? ros?
118
119
 
120
+ # Charset specified in this dict.
121
+ #
122
+ # @return [TTFunk::Table::Cff::Charset, nil]
119
123
  def charset
120
124
  @charset ||=
121
125
  if (charset_offset_or_id = self[OPERATORS[:charset]])
@@ -127,38 +131,47 @@ module TTFunk
127
131
  end
128
132
  end
129
133
 
134
+ # Encoding specified in this dict.
135
+ #
136
+ # @return [TTFunk::Table::Cff::Encoding, nil]
130
137
  def encoding
138
+ # PostScript type 1 fonts, i.e. CID fonts, i.e. some fonts that use
139
+ # the CFF table, don't specify an encoding, so this can be nil
131
140
  @encoding ||=
132
- begin
133
- # PostScript type 1 fonts, i.e. CID fonts, i.e. some fonts that use
134
- # the CFF table, don't specify an encoding, so this can be nil
135
- if (encoding_offset_or_id = self[OPERATORS[:encoding]])
136
- Encoding.new(self, file, encoding_offset_or_id.first)
137
- end
141
+ if (encoding_offset_or_id = self[OPERATORS[:encoding]])
142
+ Encoding.new(self, file, encoding_offset_or_id.first)
138
143
  end
139
144
  end
140
145
 
141
- # https://www.microsoft.com/typography/otspec/cff.htm
146
+ # Charstrings index specified in this dict.
147
+ #
148
+ # > OpenType fonts with TrueType outlines use a glyph index to specify
149
+ # and access glyphs within a font; e.g., to index within the `loca`
150
+ # table and thereby access glyph data in the `glyf` table. This
151
+ # concept is retained in OpenType CFF fonts, except that glyph data is
152
+ # accessed through the CharStrings INDEX of the CFF table.
153
+ #
154
+ # > --- [CFF — Compact Font Format Table](https://www.microsoft.com/typography/otspec/cff.htm)
142
155
  #
143
- # "OpenType fonts with TrueType outlines use a glyph index to specify
144
- # and access glyphs within a font; e.g., to index within the 'loca'
145
- # table and thereby access glyph data in the 'glyf' table. This concept
146
- # is retained in OpenType CFF fonts, except that glyph data is accessed
147
- # through the CharStrings INDEX of the CFF table."
156
+ # @return [TTFunk::Table::Cff::CharstringsIndex, nil]
148
157
  def charstrings_index
149
158
  @charstrings_index ||=
150
159
  if (charstrings_offset = self[OPERATORS[:charstrings_index]])
151
- CharstringsIndex.new(
152
- self, file, cff_offset + charstrings_offset.first
153
- )
160
+ CharstringsIndex.new(self, file, cff_offset + charstrings_offset.first)
154
161
  end
155
162
  end
156
163
 
164
+ # Charstring type specified in this dict.
165
+ #
166
+ # @return [Integer]
157
167
  def charstring_type
158
168
  @charstring_type =
159
169
  self[OPERATORS[:charstring_type]] || DEFAULT_CHARSTRING_TYPE
160
170
  end
161
171
 
172
+ # Font index specified in this dict.
173
+ #
174
+ # @return [TTFunk::Table::Cff::FontIndex, nil]
162
175
  def font_index
163
176
  @font_index ||=
164
177
  if (font_index_offset = self[OPERATORS[:font_index]])
@@ -166,6 +179,9 @@ module TTFunk
166
179
  end
167
180
  end
168
181
 
182
+ # Font dict selector specified in this dict.
183
+ #
184
+ # @return [TTFunk::Table::Cff::FdSelector, nil]
169
185
  def font_dict_selector
170
186
  @font_dict_selector ||=
171
187
  if (fd_select_offset = self[OPERATORS[:font_dict_selector]])
@@ -173,21 +189,28 @@ module TTFunk
173
189
  end
174
190
  end
175
191
 
192
+ # Private dict specified in this dict.
193
+ #
194
+ # @return [TTFunk::Table::Cff::PrivateDict, nil]
176
195
  def private_dict
177
196
  @private_dict ||=
178
197
  if (info = self[OPERATORS[:private]])
179
198
  private_dict_length, private_dict_offset = info
180
199
 
181
- PrivateDict.new(
182
- file, cff_offset + private_dict_offset, private_dict_length
183
- )
200
+ PrivateDict.new(file, cff_offset + private_dict_offset, private_dict_length)
184
201
  end
185
202
  end
186
203
 
204
+ # CFF table in this file.
205
+ #
206
+ # @return [TTFunk::Table::Cff]
187
207
  def cff
188
208
  file.cff
189
209
  end
190
210
 
211
+ # Ofsset of CFF table in the file.
212
+ #
213
+ # @return [Integer]
191
214
  def cff_offset
192
215
  cff.offset
193
216
  end
@@ -198,12 +221,12 @@ module TTFunk
198
221
  EncodedString.new do |result|
199
222
  result << Placeholder.new(
200
223
  :"private_length_#{@table_offset}",
201
- length: PLACEHOLDER_LENGTH
224
+ length: PLACEHOLDER_LENGTH,
202
225
  )
203
226
 
204
227
  result << Placeholder.new(
205
228
  :"private_offset_#{@table_offset}",
206
- length: PLACEHOLDER_LENGTH
229
+ length: PLACEHOLDER_LENGTH,
207
230
  )
208
231
  end
209
232
  end
@@ -3,13 +3,17 @@
3
3
  module TTFunk
4
4
  class Table
5
5
  class Cff < TTFunk::Table
6
+ # CFF Index conatining Top dict.
6
7
  class TopIndex < TTFunk::Table::Cff::Index
7
- def [](index)
8
- entry_cache[index] ||=
9
- begin
10
- start, finish = absolute_offsets_for(index)
11
- TTFunk::Table::Cff::TopDict.new(file, start, (finish - start) + 1)
12
- end
8
+ private
9
+
10
+ def decode_item(_index, offset, length)
11
+ TTFunk::Table::Cff::TopDict.new(file, offset, length)
12
+ end
13
+
14
+ def encode_items(*)
15
+ # Re-encode the top dict
16
+ map(&:encode)
13
17
  end
14
18
  end
15
19
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module TTFunk
4
4
  class Table
5
+ # Compact Font Format (`CFF `) table
5
6
  class Cff < TTFunk::Table
6
7
  autoload :Charset, 'ttfunk/table/cff/charset'
7
8
  autoload :Charsets, 'ttfunk/table/cff/charsets'
@@ -22,27 +23,55 @@ module TTFunk
22
23
  autoload :TopDict, 'ttfunk/table/cff/top_dict'
23
24
  autoload :TopIndex, 'ttfunk/table/cff/top_index'
24
25
 
25
- TAG = 'CFF ' # the extra space is important
26
+ # Table tag. The extra space is important.
27
+ TAG = 'CFF '
26
28
 
27
- attr_reader :header, :name_index, :top_index, :string_index
29
+ # Table header.
30
+ # @return [TTFunk::Table::Cff::Header]
31
+ attr_reader :header
32
+
33
+ # Name index.
34
+ # @return [TTFunk::Table::Cff::Index]
35
+ attr_reader :name_index
36
+
37
+ # Top dict index.
38
+ # @return [TTFunk::Table::Cff::TopIndex]
39
+ attr_reader :top_index
40
+
41
+ # Strings index.
42
+ # @return [TTFunk::Table::Cff::OneBasedIndex]
43
+ attr_reader :string_index
44
+
45
+ # Global subroutine index.
46
+ # @return [TTFunk::Table::Cff::SubrIndex]
28
47
  attr_reader :global_subr_index
29
48
 
49
+ # Table tag.
50
+ # @return [String]
30
51
  def tag
31
52
  TAG
32
53
  end
33
54
 
34
- def encode(new_to_old, old_to_new)
55
+ # Encode table.
56
+ #
57
+ # @param subset [TTFunk::Subset::MacRoman, TTFunk::Subset::Windows1252,
58
+ # TTFunk::Subset::Unicode, TTFunk::Subset::Unicode8Bit]
59
+ # @return [TTFunk::EncodedString]
60
+ def encode(subset)
61
+ # Make sure TopDict has an entry for encoding so it could be properly replaced
62
+ top_index[0][TopDict::OPERATORS[:encoding]] = 0
63
+
35
64
  EncodedString.new do |result|
36
- sub_tables = [
65
+ result.concat(
37
66
  header.encode,
38
67
  name_index.encode,
39
- top_index.encode(&:encode),
68
+ top_index.encode,
40
69
  string_index.encode,
41
- global_subr_index.encode
42
- ]
70
+ global_subr_index.encode,
71
+ )
43
72
 
44
- sub_tables.each { |tb| result << tb }
45
- top_index[0].finalize(result, new_to_old, old_to_new)
73
+ charmap = subset.new_cmap_table[:charmap]
74
+ top_index[0].finalize(result, charmap)
46
75
  end
47
76
  end
48
77
 
@@ -51,18 +80,9 @@ module TTFunk
51
80
  def parse!
52
81
  @header = Header.new(file, offset)
53
82
  @name_index = Index.new(file, @header.table_offset + @header.length)
54
-
55
- @top_index = TopIndex.new(
56
- file, @name_index.table_offset + @name_index.length
57
- )
58
-
59
- @string_index = OneBasedIndex.new(
60
- file, @top_index.table_offset + @top_index.length
61
- )
62
-
63
- @global_subr_index = SubrIndex.new(
64
- file, @string_index.table_offset + @string_index.length
65
- )
83
+ @top_index = TopIndex.new(file, @name_index.table_offset + @name_index.length)
84
+ @string_index = OneBasedIndex.new(file, @top_index.table_offset + @top_index.length)
85
+ @global_subr_index = SubrIndex.new(file, @string_index.table_offset + @string_index.length)
66
86
  end
67
87
  end
68
88
  end
@@ -3,16 +3,30 @@
3
3
  module TTFunk
4
4
  class Table
5
5
  class Cmap
6
+ # Format 0: Byte encoding table.
7
+ #
8
+ # This module conditionally extends {TTFunk::Table::Cmap::Subtable}.
6
9
  module Format00
10
+ # Language.
11
+ # @return [Integer]
7
12
  attr_reader :language
13
+
14
+ # Code map.
15
+ # @return [Array<Integer>]
8
16
  attr_reader :code_map
9
17
 
10
- # Expects a hash mapping character codes to glyph ids (where the
11
- # glyph ids are from the original font). Returns a hash including
12
- # a new map (:charmap) that maps the characters in charmap to a
13
- # another hash containing both the old (:old) and new (:new) glyph
14
- # ids. The returned hash also includes a :subtable key, which contains
15
- # the encoded subtable for the given charmap.
18
+ # Encode the encoding record to format 0.
19
+ #
20
+ # @param charmap [Hash{Integer => Integer}] a hash mapping character
21
+ # codes to glyph ids (where the glyph ids are from the original font).
22
+ # @return [Hash]
23
+ # * `:charmap` (<tt>Hash{Integer => Hash}</tt>) keys are the characrers in
24
+ # `charset`, values are hashes:
25
+ # * `:old` (<tt>Integer</tt>) - glyph ID in the original font.
26
+ # * `:new` (<tt>Integer</tt>) - glyph ID in the subset font.
27
+ # that maps the characters in charmap to a
28
+ # * `:subtable` (<tt>String</tt>) - serialized encoding record.
29
+ # * `:max_glyph_id` (<tt>Integer</tt>) - maximum glyph ID in the new font.
16
30
  def self.encode(charmap)
17
31
  next_id = 0
18
32
  glyph_indexes = Array.new(256, 0)
@@ -32,10 +46,17 @@ module TTFunk
32
46
  { charmap: new_map, subtable: subtable, max_glyph_id: next_id + 1 }
33
47
  end
34
48
 
49
+ # Get glyph ID for character code.
50
+ #
51
+ # @param code [Integer] character code.
52
+ # @return [Integer] glyph ID.
35
53
  def [](code)
36
54
  @code_map[code] || 0
37
55
  end
38
56
 
57
+ # Is this encoding record format supported?
58
+ #
59
+ # @return [true]
39
60
  def supported?
40
61
  true
41
62
  end
@@ -3,16 +3,30 @@
3
3
  module TTFunk
4
4
  class Table
5
5
  class Cmap
6
+ # Format 4: Segment mapping to delta values.
7
+ #
8
+ # This module conditionally extends {TTFunk::Table::Cmap::Subtable}.
6
9
  module Format04
10
+ # Language.
11
+ # @return [Integer]
7
12
  attr_reader :language
13
+
14
+ # Code map.
15
+ # @return [Hash{Integer => Integer}]
8
16
  attr_reader :code_map
9
17
 
10
- # Expects a hash mapping character codes to glyph ids (where the
11
- # glyph ids are from the original font). Returns a hash including
12
- # a new map (:charmap) that maps the characters in charmap to a
13
- # another hash containing both the old (:old) and new (:new) glyph
14
- # ids. The returned hash also includes a :subtable key, which contains
15
- # the encoded subtable for the given charmap.
18
+ # Encode the encoding record to format 4.
19
+ #
20
+ # @param charmap [Hash{Integer => Integer}] a hash mapping character
21
+ # codes to glyph IDs from the original font.
22
+ # @return [Hash]
23
+ # * `:charmap` (<tt>Hash{Integer => Hash}</tt>) keys are the characrers in
24
+ # `charset`, values are hashes:
25
+ # * `:old` (<tt>Integer</tt>) - glyph ID in the original font.
26
+ # * `:new` (<tt>Integer</tt>) - glyph ID in the subset font.
27
+ # that maps the characters in charmap to a
28
+ # * `:subtable` (<tt>String</tt>) - serialized encoding record.
29
+ # * `:max_glyph_id` (<tt>Integer</tt>) - maximum glyph ID in the new font.
16
30
  def self.encode(charmap)
17
31
  end_codes = []
18
32
  start_codes = []
@@ -63,10 +77,10 @@ module TTFunk
63
77
 
64
78
  if a - start_glyph_id >= 0x8000
65
79
  deltas << 0
66
- range_offsets << 2 * (glyph_indices.length + segcount - segment)
80
+ range_offsets << (2 * (glyph_indices.length + segcount - segment))
67
81
  a.upto(b) { |code| glyph_indices << new_map[code][:new] }
68
82
  else
69
- deltas << -a + start_glyph_id
83
+ deltas << (-a + start_glyph_id)
70
84
  range_offsets << 0
71
85
  end
72
86
 
@@ -75,14 +89,14 @@ module TTFunk
75
89
 
76
90
  # format, length, language
77
91
  subtable = [
78
- 4, 16 + 8 * segcount + 2 * glyph_indices.length, 0
92
+ 4, 16 + (8 * segcount) + (2 * glyph_indices.length), 0,
79
93
  ].pack('nnn')
80
94
 
81
- search_range = 2 * 2**(Math.log(segcount) / Math.log(2)).to_i
82
- entry_selector = (Math.log(search_range / 2) / Math.log(2)).to_i
95
+ search_range = 2 * (2**Integer(Math.log(segcount) / Math.log(2)))
96
+ entry_selector = Integer(Math.log(search_range / 2) / Math.log(2))
83
97
  range_shift = (2 * segcount) - search_range
84
98
  subtable << [
85
- segcount * 2, search_range, entry_selector, range_shift
99
+ segcount * 2, search_range, entry_selector, range_shift,
86
100
  ].pack('nnnn')
87
101
 
88
102
  subtable << end_codes.pack('n*') << "\0\0" << start_codes.pack('n*')
@@ -92,10 +106,17 @@ module TTFunk
92
106
  { charmap: new_map, subtable: subtable, max_glyph_id: next_id + 1 }
93
107
  end
94
108
 
109
+ # Get glyph ID for character code.
110
+ #
111
+ # @param code [Integer] character code.
112
+ # @return [Integer] glyph ID.
95
113
  def [](code)
96
114
  @code_map[code] || 0
97
115
  end
98
116
 
117
+ # Is this encoding record format supported?
118
+ #
119
+ # @return [true]
99
120
  def supported?
100
121
  true
101
122
  end
@@ -123,8 +144,7 @@ module TTFunk
123
144
  if (id_range_offset[i]).zero?
124
145
  glyph_id = code + id_delta[i]
125
146
  else
126
- index = id_range_offset[i] / 2 +
127
- (code - start_code[i]) - (segcount - i)
147
+ index = (id_range_offset[i] / 2) + (code - start_code[i]) - (segcount - i)
128
148
  # Because some TTF fonts are broken
129
149
  glyph_id = glyph_ids[index] || 0
130
150
  glyph_id += id_delta[i] if glyph_id != 0
@@ -3,10 +3,30 @@
3
3
  module TTFunk
4
4
  class Table
5
5
  class Cmap
6
+ # Format 6: Trimmed table mapping.
7
+ #
8
+ # This module conditionally extends {TTFunk::Table::Cmap::Subtable}.
6
9
  module Format06
10
+ # Language.
11
+ # @return [Integer]
7
12
  attr_reader :language
13
+
14
+ # Code map.
15
+ # @return [Hash{Integer => Integer}]
8
16
  attr_reader :code_map
9
17
 
18
+ # Encode the encoding record to format 6.
19
+ #
20
+ # @param charmap [Hash{Integer => Integer}] a hash mapping character
21
+ # codes to glyph IDs from the original font.
22
+ # @return [Hash]
23
+ # * `:charmap` (<tt>Hash{Integer => Hash}</tt>) keys are the characrers in
24
+ # `charset`, values are hashes:
25
+ # * `:old` (<tt>Integer</tt>) - glyph ID in the original font.
26
+ # * `:new` (<tt>Integer</tt>) - glyph ID in the subset font.
27
+ # that maps the characters in charmap to a
28
+ # * `:subtable` (<tt>String</tt>) - serialized encoding record.
29
+ # * `:max_glyph_id` (<tt>Integer</tt>) - maximum glyph ID in the new font.
10
30
  def self.encode(charmap)
11
31
  next_id = 0
12
32
  glyph_map = { 0 => 0 }
@@ -25,16 +45,23 @@ module TTFunk
25
45
  end
26
46
 
27
47
  subtable = [
28
- 6, 10 + entry_count * 2, 0, low_char, entry_count, *glyph_indexes
48
+ 6, 10 + (entry_count * 2), 0, low_char, entry_count, *glyph_indexes,
29
49
  ].pack('n*')
30
50
 
31
51
  { charmap: new_map, subtable: subtable, max_glyph_id: next_id + 1 }
32
52
  end
33
53
 
54
+ # Get glyph ID for character code.
55
+ #
56
+ # @param code [Integer] character code.
57
+ # @return [Integer] glyph ID.
34
58
  def [](code)
35
59
  @code_map[code] || 0
36
60
  end
37
61
 
62
+ # Is this encoding record format supported?
63
+ #
64
+ # @return [true]
38
65
  def supported?
39
66
  true
40
67
  end
@@ -3,10 +3,30 @@
3
3
  module TTFunk
4
4
  class Table
5
5
  class Cmap
6
+ # Format 10: Trimmed array.
7
+ #
8
+ # This module conditionally extends {TTFunk::Table::Cmap::Subtable}.
6
9
  module Format10
10
+ # Language.
11
+ # @return [Integer]
7
12
  attr_reader :language
13
+
14
+ # Code map.
15
+ # @return [Hash{Integer => Integer}]
8
16
  attr_reader :code_map
9
17
 
18
+ # Encode the encoding record to format 10.
19
+ #
20
+ # @param charmap [Hash{Integer => Integer}] a hash mapping character
21
+ # codes to glyph IDs from the original font.
22
+ # @return [Hash]
23
+ # * `:charmap` (<tt>Hash{Integer => Hash}</tt>) keys are the characrers in
24
+ # `charset`, values are hashes:
25
+ # * `:old` (<tt>Integer</tt>) - glyph ID in the original font.
26
+ # * `:new` (<tt>Integer</tt>) - glyph ID in the subset font.
27
+ # that maps the characters in charmap to a
28
+ # * `:subtable` (<tt>String</tt>) - serialized encoding record.
29
+ # * `:max_glyph_id` (<tt>Integer</tt>) - maximum glyph ID in the new font.
10
30
  def self.encode(charmap)
11
31
  next_id = 0
12
32
  glyph_map = { 0 => 0 }
@@ -25,17 +45,24 @@ module TTFunk
25
45
  end
26
46
 
27
47
  subtable = [
28
- 10, 0, 20 + entry_count * 4, 0, low_char, entry_count,
29
- *glyph_indexes
48
+ 10, 0, 20 + (entry_count * 4), 0, low_char, entry_count,
49
+ *glyph_indexes,
30
50
  ].pack('nnN*')
31
51
 
32
52
  { charmap: new_map, subtable: subtable, max_glyph_id: next_id + 1 }
33
53
  end
34
54
 
55
+ # Get glyph ID for character code.
56
+ #
57
+ # @param code [Integer] character code.
58
+ # @return [Integer] glyph ID.
35
59
  def [](code)
36
60
  @code_map[code] || 0
37
61
  end
38
62
 
63
+ # Is this encoding record format supported?
64
+ #
65
+ # @return [true]
39
66
  def supported?
40
67
  true
41
68
  end