ttfunk 1.7.0 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
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