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
@@ -5,43 +5,74 @@ require 'bigdecimal'
5
5
  module TTFunk
6
6
  class Table
7
7
  class Cff < TTFunk::Table
8
+ # CFF Dict.
8
9
  class Dict < TTFunk::SubTable
10
+ # Indicates malformed operand.
9
11
  class InvalidOperandError < StandardError; end
10
12
 
13
+ # Indicates too many operands.
11
14
  class TooManyOperandsError < StandardError; end
12
15
 
13
- # for regular single-byte operators
16
+ # Single-byte operators.
14
17
  OPERATOR_BZERO = (0..21).freeze
18
+ # Bytes indicating an operand.
15
19
  OPERAND_BZERO = [28..30, 32..254].freeze
16
20
 
17
- # for operators that are two bytes wide
21
+ # Two-byte operator
18
22
  WIDE_OPERATOR_BZERO = 12
23
+ # Two-byte operator adjustment. Used for encoding and decoding of wide
24
+ # operators.
19
25
  WIDE_OPERATOR_ADJUSTMENT = 1200
20
26
 
21
- # maximum number of operands allowed per operator
27
+ # Maximum number of operands allowed per operator.
22
28
  MAX_OPERANDS = 48
23
29
 
24
- # used to validate operands expressed in scientific notation
30
+ # Scientific notation operand significand validation regular
31
+ # experession.
25
32
  VALID_SCI_SIGNIFICAND_RE = /\A-?(\.\d+|\d+|\d+\.\d+)\z/.freeze
33
+
34
+ # Scientific notation operand exponent validation regular experession.
26
35
  VALID_SCI_EXPONENT_RE = /\A-?\d+\z/.freeze
27
36
 
28
37
  include Enumerable
29
38
 
39
+ # Get dict value by operator.
40
+ #
41
+ # @param operator [Integer]
42
+ # @return [Array<Integer, TTFunk::SciForm>]
30
43
  def [](operator)
31
44
  @dict[operator]
32
45
  end
33
46
 
47
+ # Add dict entry.
48
+ #
49
+ # @param operator [Integer] Entry operator. Must be in range 0..255. Wide operators must be in range 1200..1455.
50
+ # @param operands [Array<Integer, TTFunk::SciForm>]
51
+ def []=(operator, *operands)
52
+ @dict[operator] = Array(*operands)
53
+ end
54
+
55
+ # Iterate over dict entries.
56
+ #
57
+ # @yieldparam key [Integer]
58
+ # @yieldparam value [Array<Integer, TTFunk::SciForm>]
59
+ # @return [void]
34
60
  def each(&block)
35
61
  @dict.each(&block)
36
62
  end
37
63
 
38
64
  alias each_pair each
39
65
 
66
+ # Encode dict.
67
+ #
68
+ # @return [String]
40
69
  def encode
41
- map do |(operator, operands)|
42
- operands.map { |operand| encode_operand(operand) }.join +
43
- encode_operator(operator)
44
- end.join
70
+ sort_by(&:first)
71
+ .map { |(operator, operands)|
72
+ operands.map { |operand| encode_operand(operand) }.join +
73
+ encode_operator(operator)
74
+ }
75
+ .join
45
76
  end
46
77
 
47
78
  private
@@ -50,7 +81,7 @@ module TTFunk
50
81
  if operator >= WIDE_OPERATOR_ADJUSTMENT
51
82
  [
52
83
  WIDE_OPERATOR_BZERO,
53
- operator - WIDE_OPERATOR_ADJUSTMENT
84
+ operator - WIDE_OPERATOR_ADJUSTMENT,
54
85
  ].pack('C*')
55
86
  else
56
87
  [operator].pack('C')
@@ -113,7 +144,7 @@ module TTFunk
113
144
  sig.to_s.each_char.with_object([]) do |char, ret|
114
145
  case char
115
146
  when '0'..'9'
116
- ret << char.to_i
147
+ ret << Integer(char)
117
148
  when '.'
118
149
  ret << 0xA
119
150
  when '-'
@@ -130,7 +161,7 @@ module TTFunk
130
161
  nibbles.each_slice(2).each do |(high_nb, low_nb)|
131
162
  # low_nb can be nil if nibbles contains an odd number of elements
132
163
  low_nb ||= 0xF
133
- bytes << (high_nb << 4 | low_nb)
164
+ bytes << ((high_nb << 4) | low_nb)
134
165
  end
135
166
 
136
167
  bytes << 0xFF if nibbles.size.even?
@@ -156,8 +187,7 @@ module TTFunk
156
187
 
157
188
  if operands.size > MAX_OPERANDS
158
189
  raise TooManyOperandsError,
159
- 'found one too many operands at '\
160
- "position #{io.pos} in dict at position #{table_offset}"
190
+ "found one too many operands at position #{io.pos} in dict at position #{table_offset}"
161
191
  end
162
192
  else
163
193
  raise Error, "dict byte value #{b_zero} is reserved"
@@ -210,15 +240,17 @@ module TTFunk
210
240
 
211
241
  validate_sci!(significand, exponent)
212
242
 
213
- SciForm.new(significand.to_f, exponent.to_i)
243
+ exponent = 0 if exponent.empty?
244
+
245
+ SciForm.new(Float(significand), Integer(exponent))
214
246
  end
215
247
 
216
248
  def validate_sci!(significand, exponent)
217
249
  unless valid_significand?(significand) && valid_exponent?(exponent)
218
250
  raise InvalidOperandError,
219
- 'invalid scientific notation operand with significand '\
220
- "'#{significand}' and exponent '#{exponent}' ending at "\
221
- "position #{io.pos} in dict at position #{table_offset}"
251
+ 'invalid scientific notation operand with significand ' \
252
+ "'#{significand}' and exponent '#{exponent}' ending at " \
253
+ "position #{io.pos} in dict at position #{table_offset}"
222
254
  end
223
255
  end
224
256
 
@@ -242,24 +274,22 @@ module TTFunk
242
274
  when 247..250
243
275
  # 2 bytes
244
276
  b_one = read(1, 'C').first
245
- (b_zero - 247) * 256 + b_one + 108
277
+ ((b_zero - 247) * 256) + b_one + 108
246
278
 
247
279
  when 251..254
248
280
  # 2 bytes
249
281
  b_one = read(1, 'C').first
250
- -(b_zero - 251) * 256 - b_one - 108
282
+ (-(b_zero - 251) * 256) - b_one - 108
251
283
 
252
284
  when 28
253
285
  # 2 bytes in number (3 total)
254
286
  b_one, b_two = read(2, 'C*')
255
- BinUtils.twos_comp_to_int(b_one << 8 | b_two, bit_width: 16)
287
+ BinUtils.twos_comp_to_int((b_one << 8) | b_two, bit_width: 16)
256
288
 
257
289
  when 29
258
290
  # 4 bytes in number (5 total)
259
291
  b_one, b_two, b_three, b_four = read(4, 'C*')
260
- BinUtils.twos_comp_to_int(
261
- b_one << 24 | b_two << 16 | b_three << 8 | b_four, bit_width: 32
262
- )
292
+ BinUtils.twos_comp_to_int((b_one << 24) | (b_two << 16) | (b_three << 8) | b_four, bit_width: 32)
263
293
  end
264
294
  end
265
295
  end
@@ -3,15 +3,24 @@
3
3
  module TTFunk
4
4
  class Table
5
5
  class Cff < TTFunk::Table
6
+ # CFF Encoding.
6
7
  class Encoding < TTFunk::SubTable
7
8
  include Enumerable
8
9
 
10
+ # Predefined Standard Encoding ID.
9
11
  STANDARD_ENCODING_ID = 0
12
+
13
+ # Predefined Expert Encoding ID.
10
14
  EXPERT_ENCODING_ID = 1
11
15
 
16
+ # Default encoding ID.
12
17
  DEFAULT_ENCODING_ID = STANDARD_ENCODING_ID
13
18
 
14
19
  class << self
20
+ # Get predefined encoding by ID.
21
+ #
22
+ # @param encoding_id [Integer]
23
+ # @return [TTFunk::OneBasedArray<Integer>]
15
24
  def codes_for_encoding_id(encoding_id)
16
25
  case encoding_id
17
26
  when STANDARD_ENCODING_ID
@@ -22,26 +31,62 @@ module TTFunk
22
31
  end
23
32
  end
24
33
 
25
- attr_reader :top_dict, :format, :count, :offset_or_id
26
-
34
+ # Top dict.
35
+ # @return [TTFunk::Table::Cff::TopDict]
36
+ attr_reader :top_dict
37
+
38
+ # Encodign format.
39
+ # @return [Integer]
40
+ attr_reader :format
41
+
42
+ # Number of encoded items.
43
+ # @return [Integer]
44
+ attr_reader :items_count
45
+
46
+ # Offset or encoding ID.
47
+ # @return [Integer]
48
+ attr_reader :offset_or_id
49
+
50
+ # @overload initialize(top_dict, file, offset = nil, length = nil)
51
+ # @param top_dict [TTFunk::Table:Cff::TopDict]
52
+ # @param file [TTFunk::File]
53
+ # @param offset [Integer]
54
+ # @param length [Integer]
55
+ # @overload initialize(top_dict, file, charset_id)
56
+ # @param top_dict [TTFunk::Table:Cff::TopDict]
57
+ # @param file [TTFunk::File]
58
+ # @param encoding_id [Integer] 0, 1, or 2
27
59
  def initialize(top_dict, file, offset_or_id = nil, length = nil)
28
60
  @top_dict = top_dict
29
61
  @offset_or_id = offset_or_id || DEFAULT_ENCODING_ID
30
62
 
31
63
  if offset
32
64
  super(file, offset, length)
65
+ @supplemental = format >> 7 == 1
33
66
  else
34
- @count = self.class.codes_for_encoding_id(offset_or_id).size
67
+ @items_count = self.class.codes_for_encoding_id(offset_or_id).size
68
+ @supplemental = false
35
69
  end
36
70
  end
37
71
 
72
+ # Iterate over character codes.
73
+ #
74
+ # @overload each()
75
+ # @yieldparam code [Integer]
76
+ # @return [void]
77
+ # @overload each()
78
+ # @return [Enumerator]
38
79
  def each
39
80
  return to_enum(__method__) unless block_given?
40
81
 
41
82
  # +1 adjusts for the implicit .notdef glyph
42
- (count + 1).times { |i| yield self[i] }
83
+ (items_count + 1).times { |i| yield(self[i]) }
43
84
  end
44
85
 
86
+ # Get character code for glyph index.
87
+ #
88
+ # @param glyph_id [Integer]
89
+ # @return [Integer, nil]
45
90
  def [](glyph_id)
46
91
  return 0 if glyph_id.zero?
47
92
  return code_for(glyph_id) if offset
@@ -49,6 +94,9 @@ module TTFunk
49
94
  self.class.codes_for_encoding_id(offset_or_id)[glyph_id]
50
95
  end
51
96
 
97
+ # Encoding offset in the file.
98
+ #
99
+ # @return [Integer, nil]
52
100
  def offset
53
101
  # Numbers from 0..1 mean encoding IDs instead of offsets. IDs are
54
102
  # pre-defined, generic encodings that define the characters present
@@ -62,16 +110,25 @@ module TTFunk
62
110
  end
63
111
  end
64
112
 
65
- def encode(new_to_old, old_to_new)
66
- # no offset means no encoding was specified (i.e. we're supposed to
67
- # use a predefined encoding) so there's nothing to encode
68
- return '' unless offset
69
- return encode_supplemental(new_to_old, old_to_new) if supplemental?
113
+ # Encode encoding.
114
+ #
115
+ # @param charmap [Hash{Integer => Hash}] keys are the charac codes,
116
+ # values are hashes:
117
+ # * `:old` (<tt>Integer</tt>) - glyph ID in the original font.
118
+ # * `:new` (<tt>Integer</tt>) - glyph ID in the subset font.
119
+ # @return [String]
120
+ def encode(charmap)
121
+ # Any subset encoding is all but guaranteed to be different from the
122
+ # standard encoding so we don't even attempt to see if it matches. We
123
+ # assume it's different and just encode it anew.
124
+
125
+ return encode_supplemental(charmap) if supplemental?
70
126
 
71
127
  codes =
72
- new_to_old.keys.sort.map do |new_gid|
73
- code_for(new_to_old[new_gid])
74
- end
128
+ charmap
129
+ .reject { |_code, mapping| mapping[:new].zero? }
130
+ .sort_by { |_code, mapping| mapping[:new] }
131
+ .map { |(code, _m)| code }
75
132
 
76
133
  ranges = TTFunk::BinUtils.rangify(codes)
77
134
 
@@ -93,20 +150,21 @@ module TTFunk
93
150
  end
94
151
  end
95
152
 
153
+ # Is this a supplemental encoding?
154
+ #
155
+ # @return [Boolean]
96
156
  def supplemental?
97
157
  # high-order bit set to 1 indicates supplemental encoding
98
- @format >> 7 == 1
158
+ @supplemental
99
159
  end
100
160
 
101
161
  private
102
162
 
103
- def encode_supplemental(_new_to_old, old_to_new)
163
+ def encode_supplemental(charmap)
104
164
  new_entries =
105
- @entries.each_with_object({}) do |(code, old_gid), ret|
106
- if (new_gid = old_to_new[old_gid])
107
- ret[code] = new_gid
108
- end
109
- end
165
+ charmap
166
+ .reject { |_code, mapping| mapping[:new].zero? }
167
+ .transform_values { |mapping| mapping[:new] }
110
168
 
111
169
  result = [format_int(:supplemental), new_entries.size].pack('CC')
112
170
  fmt = element_format(:supplemental)
@@ -150,22 +208,22 @@ module TTFunk
150
208
 
151
209
  case format_sym
152
210
  when :array_format
153
- @count = entry_count
211
+ @items_count = entry_count
154
212
  @entries = OneBasedArray.new(read(length, 'C*'))
155
213
 
156
214
  when :range_format
157
215
  @entries = []
158
- @count = 0
216
+ @items_count = 0
159
217
 
160
218
  entry_count.times do
161
219
  code, num_left = read(element_width, element_format)
162
220
  @entries << (code..(code + num_left))
163
- @count += num_left + 1
221
+ @items_count += num_left + 1
164
222
  end
165
223
 
166
224
  when :supplemental
167
225
  @entries = {}
168
- @count = entry_count
226
+ @items_count = entry_count
169
227
 
170
228
  entry_count.times do
171
229
  code, glyph = read(element_width, element_format)
@@ -178,7 +236,7 @@ module TTFunk
178
236
  {
179
237
  array_format: 'C',
180
238
  range_format: 'CC',
181
- supplemental: 'Cn'
239
+ supplemental: 'Cn',
182
240
  }[fmt]
183
241
  end
184
242
 
@@ -4,6 +4,7 @@ module TTFunk
4
4
  class Table
5
5
  class Cff < TTFunk::Table
6
6
  module Encodings
7
+ # CFF predefined Expert encoding.
7
8
  EXPERT = OneBasedArray.new(
8
9
  [
9
10
  *[0] * 31,
@@ -197,8 +198,8 @@ module TTFunk
197
198
  375,
198
199
  376,
199
200
  377,
200
- 378
201
- ]
201
+ 378,
202
+ ],
202
203
  ).freeze
203
204
  end
204
205
  end
@@ -4,6 +4,7 @@ module TTFunk
4
4
  class Table
5
5
  class Cff < TTFunk::Table
6
6
  module Encodings
7
+ # CFF predefined Standard encoding.
7
8
  STANDARD = OneBasedArray.new(
8
9
  [
9
10
  *[0] * 31,
@@ -172,8 +173,8 @@ module TTFunk
172
173
  147,
173
174
  148,
174
175
  149,
175
- *[0] * 4
176
- ]
176
+ *[0] * 4,
177
+ ],
177
178
  ).freeze
178
179
  end
179
180
  end
@@ -3,6 +3,7 @@
3
3
  module TTFunk
4
4
  class Table
5
5
  class Cff < TTFunk::Table
6
+ # Predefined encodings.
6
7
  module Encodings
7
8
  autoload :EXPERT, 'ttfunk/table/cff/encodings/expert'
8
9
  autoload :STANDARD, 'ttfunk/table/cff/encodings/standard'
@@ -3,22 +3,51 @@
3
3
  module TTFunk
4
4
  class Table
5
5
  class Cff < TTFunk::Table
6
+ # CFF FDSelect.
6
7
  class FdSelector < TTFunk::SubTable
7
8
  include Enumerable
8
9
 
10
+ # Array format.
9
11
  ARRAY_FORMAT = 0
12
+
13
+ # Range format.
10
14
  RANGE_FORMAT = 3
11
15
 
16
+ # Range entry size.
12
17
  RANGE_ENTRY_SIZE = 3
18
+
19
+ # Array entry size.
13
20
  ARRAY_ENTRY_SIZE = 1
14
21
 
15
- attr_reader :top_dict, :count, :entries, :n_glyphs
22
+ # Top dict.
23
+ # @return [TTFunk::Table::Cff::TopDict]
24
+ attr_reader :top_dict
25
+
26
+ # Number of encoded items.
27
+ # @return [Integer]
28
+ attr_reader :items_count
29
+
30
+ # Number of entries.
31
+ # @return [Array<Integer>] if format is array.
32
+ # @return [Array<Array(Range, Integer)>] if format is range.
33
+ attr_reader :entries
34
+
35
+ # Number of glyphs.
36
+ # @return [Integer]
37
+ attr_reader :n_glyphs
16
38
 
39
+ # @param top_dict [TTFunk::Table:Cff::TopDict]
40
+ # @param file [TTFunk::File]
41
+ # @param offset [Integer]
42
+ # @param length [Integer]
17
43
  def initialize(top_dict, file, offset, length = nil)
18
44
  @top_dict = top_dict
19
45
  super(file, offset, length)
20
46
  end
21
47
 
48
+ # Get font dict index for glyph ID.
49
+ #
50
+ # @return [Integer]
22
51
  def [](glyph_id)
23
52
  case format_sym
24
53
  when :array_format
@@ -30,34 +59,45 @@ module TTFunk
30
59
  end
31
60
 
32
61
  range, entry =
33
- entries.bsearch do |rng, _|
34
- if rng.cover?(glyph_id)
35
- 0
36
- elsif glyph_id < rng.first
37
- -1
38
- else
39
- 1
40
- end
41
- end
62
+ entries.bsearch { |rng, _|
63
+ if rng.cover?(glyph_id)
64
+ 0
65
+ elsif glyph_id < rng.first
66
+ -1
67
+ else
68
+ 1
69
+ end
70
+ }
42
71
 
43
72
  range.each { |i| range_cache[i] = entry }
44
73
  entry
45
74
  end
46
75
  end
47
76
 
77
+ # Iterate over font dicts for each glyph ID.
78
+ #
79
+ # @yieldparam [Integer] font dict index.
80
+ # @return [void]
48
81
  def each
49
82
  return to_enum(__method__) unless block_given?
50
83
 
51
- count.times { |i| yield self[i] }
84
+ items_count.times { |i| yield(self[i]) }
52
85
  end
53
86
 
54
- # mapping is new -> old glyph ids
55
- def encode(mapping)
87
+ # Encode Font dict selector.
88
+ #
89
+ # @param charmap [Hash{Integer => Hash}] keys are the charac codes,
90
+ # values are hashes:
91
+ # * `:old` (<tt>Integer</tt>) - glyph ID in the original font.
92
+ # * `:new` (<tt>Integer</tt>) - glyph ID in the subset font.
93
+ # @return [String]
94
+ def encode(charmap)
56
95
  # get list of [new_gid, fd_index] pairs
57
96
  new_indices =
58
- mapping.keys.sort.map do |new_gid|
59
- [new_gid, self[mapping[new_gid]]]
60
- end
97
+ charmap
98
+ .reject { |code, mapping| mapping[:new].zero? && !code.zero? }
99
+ .sort_by { |_code, mapping| mapping[:new] }
100
+ .map { |(_code, mapping)| [mapping[:new], self[mapping[:old]]] }
61
101
 
62
102
  ranges = rangify_gids(new_indices)
63
103
  total_range_size = ranges.size * RANGE_ENTRY_SIZE
@@ -108,10 +148,10 @@ module TTFunk
108
148
 
109
149
  case format_sym
110
150
  when :array_format
111
- @n_glyphs = top_dict.charstrings_index.count
151
+ @n_glyphs = top_dict.charstrings_index.items_count
112
152
  data = io.read(n_glyphs)
113
153
  @length += data.bytesize
114
- @count = data.bytesize
154
+ @items_count = data.bytesize
115
155
  @entries = data.bytes
116
156
 
117
157
  when :range_format
@@ -122,11 +162,11 @@ module TTFunk
122
162
  ranges = Array.new(num_ranges) { read(RANGE_ENTRY_SIZE, 'nC') }
123
163
 
124
164
  @entries =
125
- ranges.each_cons(2).map do |first, second|
165
+ ranges.each_cons(2).map { |first, second|
126
166
  first_gid, fd_index = first
127
167
  second_gid, = second
128
168
  [(first_gid...second_gid), fd_index]
129
- end
169
+ }
130
170
 
131
171
  # read the sentinel GID, otherwise known as the number of glyphs
132
172
  # in the font
@@ -135,7 +175,7 @@ module TTFunk
135
175
  last_start_gid, last_fd_index = ranges.last
136
176
  @entries << [(last_start_gid...(n_glyphs + 1)), last_fd_index]
137
177
 
138
- @count = entries.reduce(0) { |sum, entry| sum + entry.first.size }
178
+ @items_count = entries.reduce(0) { |sum, entry| sum + entry.first.size }
139
179
  end
140
180
  end
141
181
 
@@ -3,19 +3,34 @@
3
3
  module TTFunk
4
4
  class Table
5
5
  class Cff < TTFunk::Table
6
+ # CFF Font dict.
6
7
  class FontDict < TTFunk::Table::Cff::Dict
8
+ # Length of placeholders.
7
9
  PLACEHOLDER_LENGTH = 5
10
+
11
+ # Operators we care about in this dict.
8
12
  OPERATORS = { private: 18 }.freeze
13
+
14
+ # Inverse operator mapping.
9
15
  OPERATOR_CODES = OPERATORS.invert
10
16
 
17
+ # Top dict.
18
+ # @return [TTFunk::Table::Cff::TopDict]
11
19
  attr_reader :top_dict
12
20
 
21
+ # @param top_dict [TTFunk::Table:Cff::TopDict]
22
+ # @param file [TTFunk::File]
23
+ # @param offset [Integer]
24
+ # @param length [Integer]
13
25
  def initialize(top_dict, file, offset, length = nil)
14
26
  @top_dict = top_dict
15
27
  super(file, offset, length)
16
28
  end
17
29
 
18
- def encode(_mapping)
30
+ # Encode dict.
31
+ #
32
+ # @return [TTFunk::EncodedString]
33
+ def encode
19
34
  EncodedString.new do |result|
20
35
  each do |operator, operands|
21
36
  case OPERATOR_CODES[operator]
@@ -30,23 +45,25 @@ module TTFunk
30
45
  end
31
46
  end
32
47
 
33
- def finalize(new_cff_data, mapping)
34
- encoded_private_dict = private_dict.encode(mapping)
48
+ # Finalize dict.
49
+ #
50
+ # @param new_cff_data [TTFunk::EncodedString]
51
+ # @return [void]
52
+ def finalize(new_cff_data)
53
+ encoded_private_dict = private_dict.encode
35
54
  encoded_offset = encode_integer32(new_cff_data.length)
36
55
  encoded_length = encode_integer32(encoded_private_dict.length)
37
56
 
38
- new_cff_data.resolve_placeholder(
39
- :"private_length_#{@table_offset}", encoded_length
40
- )
41
-
42
- new_cff_data.resolve_placeholder(
43
- :"private_offset_#{@table_offset}", encoded_offset
44
- )
57
+ new_cff_data.resolve_placeholder(:"private_length_#{@table_offset}", encoded_length)
58
+ new_cff_data.resolve_placeholder(:"private_offset_#{@table_offset}", encoded_offset)
45
59
 
46
60
  private_dict.finalize(encoded_private_dict)
47
61
  new_cff_data << encoded_private_dict
48
62
  end
49
63
 
64
+ # Private dict.
65
+ #
66
+ # @return [TTFunk::Table::Cff::PrivateDict, nil]
50
67
  def private_dict
51
68
  @private_dict ||=
52
69
  if (info = self[OPERATORS[:private]])
@@ -55,7 +72,7 @@ module TTFunk
55
72
  PrivateDict.new(
56
73
  file,
57
74
  top_dict.cff_offset + private_dict_offset,
58
- private_dict_length
75
+ private_dict_length,
59
76
  )
60
77
  end
61
78
  end
@@ -64,13 +81,8 @@ module TTFunk
64
81
 
65
82
  def encode_private
66
83
  EncodedString.new do |result|
67
- result << Placeholder.new(
68
- :"private_length_#{@table_offset}", length: PLACEHOLDER_LENGTH
69
- )
70
-
71
- result << Placeholder.new(
72
- :"private_offset_#{@table_offset}", length: PLACEHOLDER_LENGTH
73
- )
84
+ result << Placeholder.new(:"private_length_#{@table_offset}", length: PLACEHOLDER_LENGTH)
85
+ result << Placeholder.new(:"private_offset_#{@table_offset}", length: PLACEHOLDER_LENGTH)
74
86
  end
75
87
  end
76
88
  end