ttfunk 1.6.2.1 → 1.7.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +2 -1
- data.tar.gz.sig +0 -0
- data/CHANGELOG.md +15 -1
- data/lib/ttfunk.rb +3 -2
- data/lib/ttfunk/bit_field.rb +1 -1
- data/lib/ttfunk/collection.rb +8 -2
- data/lib/ttfunk/encoded_string.rb +3 -3
- data/lib/ttfunk/max.rb +1 -0
- data/lib/ttfunk/min.rb +1 -0
- data/lib/ttfunk/one_based_array.rb +1 -1
- data/lib/ttfunk/otf_encoder.rb +1 -1
- data/lib/ttfunk/reader.rb +3 -3
- data/lib/ttfunk/sci_form.rb +1 -1
- data/lib/ttfunk/subset.rb +3 -3
- data/lib/ttfunk/subset/base.rb +24 -21
- data/lib/ttfunk/subset/code_page.rb +16 -19
- data/lib/ttfunk/subset/unicode.rb +8 -6
- data/lib/ttfunk/subset/unicode_8bit.rb +14 -12
- data/lib/ttfunk/sum.rb +1 -0
- data/lib/ttfunk/table/cff.rb +17 -17
- data/lib/ttfunk/table/cff/charset.rb +31 -30
- data/lib/ttfunk/table/cff/charsets.rb +3 -3
- data/lib/ttfunk/table/cff/charstring.rb +54 -55
- data/lib/ttfunk/table/cff/dict.rb +6 -4
- data/lib/ttfunk/table/cff/encoding.rb +20 -21
- data/lib/ttfunk/table/cff/encodings.rb +1 -1
- data/lib/ttfunk/table/cff/fd_selector.rb +14 -11
- data/lib/ttfunk/table/cff/font_index.rb +7 -6
- data/lib/ttfunk/table/cff/index.rb +6 -5
- data/lib/ttfunk/table/cff/one_based_index.rb +7 -3
- data/lib/ttfunk/table/cff/path.rb +5 -3
- data/lib/ttfunk/table/cff/top_dict.rb +7 -6
- data/lib/ttfunk/table/cff/top_index.rb +5 -4
- data/lib/ttfunk/table/cmap.rb +8 -6
- data/lib/ttfunk/table/cmap/format00.rb +7 -6
- data/lib/ttfunk/table/cmap/format04.rb +16 -15
- data/lib/ttfunk/table/cmap/format06.rb +6 -5
- data/lib/ttfunk/table/cmap/format10.rb +6 -5
- data/lib/ttfunk/table/cmap/format12.rb +13 -12
- data/lib/ttfunk/table/cmap/subtable.rb +4 -4
- data/lib/ttfunk/table/dsig.rb +11 -9
- data/lib/ttfunk/table/glyf.rb +3 -3
- data/lib/ttfunk/table/glyf/compound.rb +8 -8
- data/lib/ttfunk/table/hmtx.rb +6 -5
- data/lib/ttfunk/table/kern.rb +4 -4
- data/lib/ttfunk/table/kern/format0.rb +1 -1
- data/lib/ttfunk/table/loca.rb +7 -6
- data/lib/ttfunk/table/name.rb +6 -5
- data/lib/ttfunk/table/os2.rb +261 -151
- data/lib/ttfunk/table/post.rb +1 -1
- data/lib/ttfunk/table/sbix.rb +15 -13
- data/lib/ttfunk/table/vorg.rb +1 -1
- data/lib/ttfunk/ttf_encoder.rb +4 -4
- metadata +29 -99
- metadata.gz.sig +0 -0
@@ -4,9 +4,9 @@ module TTFunk
|
|
4
4
|
class Table
|
5
5
|
class Cff < TTFunk::Table
|
6
6
|
module Charsets
|
7
|
-
autoload :EXPERT,
|
8
|
-
autoload :EXPERT_SUBSET,
|
9
|
-
autoload :ISO_ADOBE,
|
7
|
+
autoload :EXPERT, 'ttfunk/table/cff/charsets/expert'
|
8
|
+
autoload :EXPERT_SUBSET, 'ttfunk/table/cff/charsets/expert_subset'
|
9
|
+
autoload :ISO_ADOBE, 'ttfunk/table/cff/charsets/iso_adobe'
|
10
10
|
autoload :STANDARD_STRINGS, 'ttfunk/table/cff/charsets/standard_strings'
|
11
11
|
end
|
12
12
|
end
|
@@ -46,11 +46,9 @@ module TTFunk
|
|
46
46
|
@font_dict = font_dict
|
47
47
|
@raw = raw
|
48
48
|
|
49
|
-
default_width_x = (@font_dict || @top_dict)
|
50
|
-
.private_dict.default_width_x
|
49
|
+
default_width_x = (@font_dict || @top_dict).private_dict.default_width_x
|
51
50
|
|
52
|
-
@nominal_width_x = (@font_dict || @top_dict)
|
53
|
-
.private_dict.nominal_width_x
|
51
|
+
@nominal_width_x = (@font_dict || @top_dict).private_dict.nominal_width_x
|
54
52
|
|
55
53
|
@subrs = (@font_dict || @top_dict).private_dict.subr_index
|
56
54
|
@gsubrs = @top_dict.cff.global_subr_index
|
@@ -77,10 +75,11 @@ module TTFunk
|
|
77
75
|
end
|
78
76
|
|
79
77
|
def glyph
|
80
|
-
@glyph ||=
|
81
|
-
|
82
|
-
|
83
|
-
|
78
|
+
@glyph ||=
|
79
|
+
begin
|
80
|
+
horizontal_metrics = @top_dict.file.horizontal_metrics.for(glyph_id)
|
81
|
+
Glyf::PathBased.new(path, horizontal_metrics)
|
82
|
+
end
|
84
83
|
end
|
85
84
|
|
86
85
|
def render(x: 0, y: 0, font_size: 72)
|
@@ -109,7 +108,7 @@ module TTFunk
|
|
109
108
|
if code >= 32 && code <= 246
|
110
109
|
@stack << code - 139
|
111
110
|
elsif (m = CODE_MAP[code])
|
112
|
-
|
111
|
+
__send__(m)
|
113
112
|
elsif code >= 247 && code <= 250
|
114
113
|
b0 = code
|
115
114
|
b1 = @data[@index]
|
@@ -229,73 +228,73 @@ module TTFunk
|
|
229
228
|
def flex_select
|
230
229
|
flex_code = @data[@index]
|
231
230
|
@index += 1
|
232
|
-
|
231
|
+
__send__(FLEX_CODE_MAP[flex_code])
|
233
232
|
end
|
234
233
|
|
235
234
|
def flex
|
236
|
-
c1x = @x
|
237
|
-
c1y = @y
|
238
|
-
c2x = c1x + @stack.shift
|
239
|
-
c2y = c1y + @stack.shift
|
240
|
-
jpx = c2x + @stack.shift
|
241
|
-
jpy = c2y + @stack.shift
|
242
|
-
c3x = jpx + @stack.shift
|
243
|
-
c3y = jpy + @stack.shift
|
244
|
-
c4x = c3x + @stack.shift
|
245
|
-
c4y = c3y + @stack.shift
|
246
|
-
@x
|
247
|
-
@y
|
248
|
-
@stack.shift
|
235
|
+
c1x = @x + @stack.shift # dx1
|
236
|
+
c1y = @y + @stack.shift # dy1
|
237
|
+
c2x = c1x + @stack.shift # dx2
|
238
|
+
c2y = c1y + @stack.shift # dy2
|
239
|
+
jpx = c2x + @stack.shift # dx3
|
240
|
+
jpy = c2y + @stack.shift # dy3
|
241
|
+
c3x = jpx + @stack.shift # dx4
|
242
|
+
c3y = jpy + @stack.shift # dy4
|
243
|
+
c4x = c3x + @stack.shift # dx5
|
244
|
+
c4y = c3y + @stack.shift # dy5
|
245
|
+
@x = c4x + @stack.shift # dx6
|
246
|
+
@y = c4y + @stack.shift # dy6
|
247
|
+
@stack.shift # flex depth
|
249
248
|
|
250
249
|
@path.curve_to(c1x, c1y, c2x, c2y, jpx, jpy)
|
251
250
|
@path.curve_to(c3x, c3y, c4x, c4y, @x, @y)
|
252
251
|
end
|
253
252
|
|
254
253
|
def hflex
|
255
|
-
c1x = @x + @stack.shift
|
256
|
-
c1y = @y
|
257
|
-
c2x = c1x + @stack.shift
|
258
|
-
c2y = c1y + @stack.shift
|
259
|
-
jpx = c2x + @stack.shift
|
260
|
-
jpy = c2y
|
261
|
-
c3x = jpx + stack.shift
|
262
|
-
c3y = c2y
|
263
|
-
c4x = c3x + stack.shift
|
264
|
-
c4y = @y
|
265
|
-
@x
|
254
|
+
c1x = @x + @stack.shift # dx1
|
255
|
+
c1y = @y # dy1
|
256
|
+
c2x = c1x + @stack.shift # dx2
|
257
|
+
c2y = c1y + @stack.shift # dy2
|
258
|
+
jpx = c2x + @stack.shift # dx3
|
259
|
+
jpy = c2y # dy3
|
260
|
+
c3x = jpx + stack.shift # dx4
|
261
|
+
c3y = c2y # dy4
|
262
|
+
c4x = c3x + stack.shift # dx5
|
263
|
+
c4y = @y # dy5
|
264
|
+
@x = c4x + stack.shift # dx6
|
266
265
|
|
267
266
|
@path.curve_to(c1x, c1y, c2x, c2y, jpx, jpy)
|
268
267
|
@path.curve_to(c3x, c3y, c4x, c4y, @x, @y)
|
269
268
|
end
|
270
269
|
|
271
270
|
def hflex1
|
272
|
-
c1x = @x
|
273
|
-
c1y = @y
|
274
|
-
c2x = c1x + @stack.shift
|
275
|
-
c2y = c1y + @stack.shift
|
276
|
-
jpx = c2x + @stack.shift
|
277
|
-
jpy = c2y
|
278
|
-
c3x = jpx + @stack.shift
|
279
|
-
c3y = c2y
|
280
|
-
c4x = c3x + @stack.shift
|
281
|
-
c4y = c3y + @stack.shift
|
282
|
-
@x
|
271
|
+
c1x = @x + @stack.shift # dx1
|
272
|
+
c1y = @y + @stack.shift # dy1
|
273
|
+
c2x = c1x + @stack.shift # dx2
|
274
|
+
c2y = c1y + @stack.shift # dy2
|
275
|
+
jpx = c2x + @stack.shift # dx3
|
276
|
+
jpy = c2y # dy3
|
277
|
+
c3x = jpx + @stack.shift # dx4
|
278
|
+
c3y = c2y # dy4
|
279
|
+
c4x = c3x + @stack.shift # dx5
|
280
|
+
c4y = c3y + @stack.shift # dy5
|
281
|
+
@x = c4x + @stack.shift # dx6
|
283
282
|
|
284
283
|
@path.curve_to(c1x, c1y, c2x, c2y, jpx, jpy)
|
285
284
|
@path.curve_to(c3x, c3y, c4x, c4y, @x, @y)
|
286
285
|
end
|
287
286
|
|
288
287
|
def flex1
|
289
|
-
c1x = @x
|
290
|
-
c1y = @y
|
291
|
-
c2x = c1x + @stack.shift
|
292
|
-
c2y = c1y + @stack.shift
|
293
|
-
jpx = c2x + @stack.shift
|
294
|
-
jpy = c2y + @stack.shift
|
295
|
-
c3x = jpx + @stack.shift
|
296
|
-
c3y = jpy + @stack.shift
|
297
|
-
c4x = c3x + @stack.shift
|
298
|
-
c4y = c3y + @stack.shift
|
288
|
+
c1x = @x + @stack.shift # dx1
|
289
|
+
c1y = @y + @stack.shift # dy1
|
290
|
+
c2x = c1x + @stack.shift # dx2
|
291
|
+
c2y = c1y + @stack.shift # dy2
|
292
|
+
jpx = c2x + @stack.shift # dx3
|
293
|
+
jpy = c2y + @stack.shift # dy3
|
294
|
+
c3x = jpx + @stack.shift # dx4
|
295
|
+
c3y = jpy + @stack.shift # dy4
|
296
|
+
c4x = c3x + @stack.shift # dx5
|
297
|
+
c4y = c3y + @stack.shift # dy5
|
299
298
|
|
300
299
|
if (c4x - @x).abs > (c4y - @y).abs
|
301
300
|
@x = c4x + @stack.shift
|
@@ -7,6 +7,7 @@ module TTFunk
|
|
7
7
|
class Cff < TTFunk::Table
|
8
8
|
class Dict < TTFunk::SubTable
|
9
9
|
class InvalidOperandError < StandardError; end
|
10
|
+
|
10
11
|
class TooManyOperandsError < StandardError; end
|
11
12
|
|
12
13
|
# for regular single-byte operators
|
@@ -103,9 +104,9 @@ module TTFunk
|
|
103
104
|
end
|
104
105
|
|
105
106
|
def encode_exponent(exp)
|
106
|
-
return [] if exp
|
107
|
+
return [] if exp.zero?
|
107
108
|
|
108
|
-
[exp
|
109
|
+
[exp.positive? ? 0xB : 0xC, *encode_significand(exp.abs)]
|
109
110
|
end
|
110
111
|
|
111
112
|
def encode_significand(sig)
|
@@ -154,11 +155,12 @@ module TTFunk
|
|
154
155
|
operands << decode_operand(b_zero)
|
155
156
|
|
156
157
|
if operands.size > MAX_OPERANDS
|
157
|
-
raise TooManyOperandsError,
|
158
|
+
raise TooManyOperandsError,
|
159
|
+
'found one too many operands at '\
|
158
160
|
"position #{io.pos} in dict at position #{table_offset}"
|
159
161
|
end
|
160
162
|
else
|
161
|
-
raise "dict byte value #{b_zero} is reserved"
|
163
|
+
raise Error, "dict byte value #{b_zero} is reserved"
|
162
164
|
end
|
163
165
|
end
|
164
166
|
end
|
@@ -43,7 +43,7 @@ module TTFunk
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def [](glyph_id)
|
46
|
-
return 0 if glyph_id
|
46
|
+
return 0 if glyph_id.zero?
|
47
47
|
return code_for(glyph_id) if offset
|
48
48
|
|
49
49
|
self.class.codes_for_encoding_id(offset_or_id)[glyph_id]
|
@@ -68,9 +68,10 @@ module TTFunk
|
|
68
68
|
return '' unless offset
|
69
69
|
return encode_supplemental(new_to_old, old_to_new) if supplemental?
|
70
70
|
|
71
|
-
codes =
|
72
|
-
|
73
|
-
|
71
|
+
codes =
|
72
|
+
new_to_old.keys.sort.map do |new_gid|
|
73
|
+
code_for(new_to_old[new_gid])
|
74
|
+
end
|
74
75
|
|
75
76
|
ranges = TTFunk::BinUtils.rangify(codes)
|
76
77
|
|
@@ -100,11 +101,12 @@ module TTFunk
|
|
100
101
|
private
|
101
102
|
|
102
103
|
def encode_supplemental(_new_to_old, old_to_new)
|
103
|
-
new_entries =
|
104
|
-
|
105
|
-
|
104
|
+
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
|
106
109
|
end
|
107
|
-
end
|
108
110
|
|
109
111
|
result = [format_int(:supplemental), new_entries.size].pack('CC')
|
110
112
|
fmt = element_format(:supplemental)
|
@@ -117,14 +119,14 @@ module TTFunk
|
|
117
119
|
end
|
118
120
|
|
119
121
|
def code_for(glyph_id)
|
120
|
-
return 0 if glyph_id
|
122
|
+
return 0 if glyph_id.zero?
|
121
123
|
|
122
124
|
# rather than validating the glyph as part of one of the predefined
|
123
125
|
# encodings, just pass it through
|
124
126
|
return glyph_id unless offset
|
125
127
|
|
126
128
|
case format_sym
|
127
|
-
when :array_format
|
129
|
+
when :array_format, :supplemental
|
128
130
|
@entries[glyph_id]
|
129
131
|
|
130
132
|
when :range_format
|
@@ -139,9 +141,6 @@ module TTFunk
|
|
139
141
|
end
|
140
142
|
|
141
143
|
0
|
142
|
-
|
143
|
-
when :supplemental
|
144
|
-
@entries[glyph_id]
|
145
144
|
end
|
146
145
|
end
|
147
146
|
|
@@ -176,11 +175,11 @@ module TTFunk
|
|
176
175
|
end
|
177
176
|
|
178
177
|
def element_format(fmt = format_sym)
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
178
|
+
{
|
179
|
+
array_format: 'C',
|
180
|
+
range_format: 'CC',
|
181
|
+
supplemental: 'Cn'
|
182
|
+
}[fmt]
|
184
183
|
end
|
185
184
|
|
186
185
|
# @TODO: handle supplemental encoding (necessary?)
|
@@ -190,7 +189,7 @@ module TTFunk
|
|
190
189
|
when :range_format then 2
|
191
190
|
when :supplemental then 3
|
192
191
|
else
|
193
|
-
raise "'#{fmt}' is an unsupported encoding format"
|
192
|
+
raise Error, "'#{fmt}' is an unsupported encoding format"
|
194
193
|
end
|
195
194
|
end
|
196
195
|
|
@@ -201,7 +200,7 @@ module TTFunk
|
|
201
200
|
when 0 then :array_format
|
202
201
|
when 1 then :range_format
|
203
202
|
else
|
204
|
-
raise "unsupported charset format '#{fmt}'"
|
203
|
+
raise Error, "unsupported charset format '#{fmt}'"
|
205
204
|
end
|
206
205
|
end
|
207
206
|
|
@@ -211,7 +210,7 @@ module TTFunk
|
|
211
210
|
when :range_format then 1
|
212
211
|
when :supplemental then 129
|
213
212
|
else
|
214
|
-
raise "unsupported charset format '#{sym}'"
|
213
|
+
raise Error, "unsupported charset format '#{sym}'"
|
215
214
|
end
|
216
215
|
end
|
217
216
|
end
|
@@ -29,7 +29,8 @@ module TTFunk
|
|
29
29
|
return entry
|
30
30
|
end
|
31
31
|
|
32
|
-
range, entry =
|
32
|
+
range, entry =
|
33
|
+
entries.bsearch do |rng, _|
|
33
34
|
if rng.cover?(glyph_id)
|
34
35
|
0
|
35
36
|
elsif glyph_id < rng.first
|
@@ -53,9 +54,10 @@ module TTFunk
|
|
53
54
|
# mapping is new -> old glyph ids
|
54
55
|
def encode(mapping)
|
55
56
|
# get list of [new_gid, fd_index] pairs
|
56
|
-
new_indices =
|
57
|
-
|
58
|
-
|
57
|
+
new_indices =
|
58
|
+
mapping.keys.sort.map do |new_gid|
|
59
|
+
[new_gid, self[mapping[new_gid]]]
|
60
|
+
end
|
59
61
|
|
60
62
|
ranges = rangify_gids(new_indices)
|
61
63
|
total_range_size = ranges.size * RANGE_ENTRY_SIZE
|
@@ -119,11 +121,12 @@ module TTFunk
|
|
119
121
|
|
120
122
|
ranges = Array.new(num_ranges) { read(RANGE_ENTRY_SIZE, 'nC') }
|
121
123
|
|
122
|
-
@entries =
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
124
|
+
@entries =
|
125
|
+
ranges.each_cons(2).map do |first, second|
|
126
|
+
first_gid, fd_index = first
|
127
|
+
second_gid, = second
|
128
|
+
[(first_gid...second_gid), fd_index]
|
129
|
+
end
|
127
130
|
|
128
131
|
# read the sentinel GID, otherwise known as the number of glyphs
|
129
132
|
# in the font
|
@@ -132,7 +135,7 @@ module TTFunk
|
|
132
135
|
last_start_gid, last_fd_index = ranges.last
|
133
136
|
@entries << [(last_start_gid...(n_glyphs + 1)), last_fd_index]
|
134
137
|
|
135
|
-
@count = entries.
|
138
|
+
@count = entries.reduce(0) { |sum, entry| sum + entry.first.size }
|
136
139
|
end
|
137
140
|
end
|
138
141
|
|
@@ -141,7 +144,7 @@ module TTFunk
|
|
141
144
|
when ARRAY_FORMAT then :array_format
|
142
145
|
when RANGE_FORMAT then :range_format
|
143
146
|
else
|
144
|
-
raise "unsupported fd select format '#{@format}'"
|
147
|
+
raise Error, "unsupported fd select format '#{@format}'"
|
145
148
|
end
|
146
149
|
end
|
147
150
|
end
|
@@ -12,12 +12,13 @@ module TTFunk
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def [](index)
|
15
|
-
entry_cache[index] ||=
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
entry_cache[index] ||=
|
16
|
+
begin
|
17
|
+
start, finish = absolute_offsets_for(index)
|
18
|
+
TTFunk::Table::Cff::FontDict.new(
|
19
|
+
top_dict, file, start, (finish - start) + 1
|
20
|
+
)
|
21
|
+
end
|
21
22
|
end
|
22
23
|
|
23
24
|
def finalize(new_cff_data, mapping)
|
@@ -30,10 +30,11 @@ module TTFunk
|
|
30
30
|
def encode
|
31
31
|
result = EncodedString.new
|
32
32
|
|
33
|
-
entries =
|
34
|
-
|
35
|
-
|
36
|
-
|
33
|
+
entries =
|
34
|
+
each_with_object([]).with_index do |(entry, ret), index|
|
35
|
+
new_entry = block_given? ? yield(entry, index) : entry
|
36
|
+
ret << new_entry if new_entry
|
37
|
+
end
|
37
38
|
|
38
39
|
# "An empty INDEX is represented by a count field with a 0 value and
|
39
40
|
# no additional fields. Thus, the total size of an empty INDEX is 2
|
@@ -89,7 +90,7 @@ module TTFunk
|
|
89
90
|
def parse!
|
90
91
|
@count = read(2, 'n').first
|
91
92
|
|
92
|
-
if count
|
93
|
+
if count.zero?
|
93
94
|
@length = 2
|
94
95
|
@data = []
|
95
96
|
return
|
@@ -8,8 +8,12 @@ module TTFunk
|
|
8
8
|
class OneBasedIndex
|
9
9
|
extend Forwardable
|
10
10
|
|
11
|
-
def_delegators :base_index,
|
12
|
-
:
|
11
|
+
def_delegators :base_index,
|
12
|
+
:each,
|
13
|
+
:table_offset,
|
14
|
+
:count,
|
15
|
+
:length,
|
16
|
+
:encode
|
13
17
|
|
14
18
|
attr_reader :base_index
|
15
19
|
|
@@ -18,7 +22,7 @@ module TTFunk
|
|
18
22
|
end
|
19
23
|
|
20
24
|
def [](idx)
|
21
|
-
if idx
|
25
|
+
if idx.zero?
|
22
26
|
raise IndexError,
|
23
27
|
"index #{idx} was outside the bounds of the index"
|
24
28
|
end
|