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
@@ -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