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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +74 -0
- data/README.md +17 -15
- data/lib/ttfunk/aggregate.rb +5 -0
- data/lib/ttfunk/bin_utils.rb +27 -8
- data/lib/ttfunk/bit_field.rb +25 -2
- data/lib/ttfunk/collection.rb +27 -3
- data/lib/ttfunk/directory.rb +7 -1
- data/lib/ttfunk/encoded_string.rb +58 -4
- data/lib/ttfunk/max.rb +14 -0
- data/lib/ttfunk/min.rb +14 -0
- data/lib/ttfunk/one_based_array.rb +20 -0
- data/lib/ttfunk/otf_encoder.rb +5 -14
- data/lib/ttfunk/placeholder.rb +15 -1
- data/lib/ttfunk/reader.rb +6 -4
- data/lib/ttfunk/resource_file.rb +29 -5
- data/lib/ttfunk/sci_form.rb +20 -3
- data/lib/ttfunk/sub_table.rb +29 -4
- data/lib/ttfunk/subset/base.rb +48 -0
- data/lib/ttfunk/subset/code_page.rb +49 -2
- data/lib/ttfunk/subset/mac_roman.rb +2 -0
- data/lib/ttfunk/subset/unicode.rb +32 -0
- data/lib/ttfunk/subset/unicode_8bit.rb +32 -0
- data/lib/ttfunk/subset/windows_1252.rb +2 -0
- data/lib/ttfunk/subset.rb +8 -0
- data/lib/ttfunk/subset_collection.rb +39 -14
- data/lib/ttfunk/sum.rb +13 -0
- data/lib/ttfunk/table/cff/charset.rb +96 -18
- data/lib/ttfunk/table/cff/charsets/expert.rb +3 -2
- data/lib/ttfunk/table/cff/charsets/expert_subset.rb +3 -2
- data/lib/ttfunk/table/cff/charsets/iso_adobe.rb +3 -2
- data/lib/ttfunk/table/cff/charsets/standard_strings.rb +3 -2
- data/lib/ttfunk/table/cff/charsets.rb +1 -0
- data/lib/ttfunk/table/cff/charstring.rb +33 -12
- data/lib/ttfunk/table/cff/charstrings_index.rb +17 -11
- data/lib/ttfunk/table/cff/dict.rb +53 -23
- data/lib/ttfunk/table/cff/encoding.rb +82 -24
- data/lib/ttfunk/table/cff/encodings/expert.rb +3 -2
- data/lib/ttfunk/table/cff/encodings/standard.rb +3 -2
- data/lib/ttfunk/table/cff/encodings.rb +1 -0
- data/lib/ttfunk/table/cff/fd_selector.rb +61 -21
- data/lib/ttfunk/table/cff/font_dict.rb +30 -18
- data/lib/ttfunk/table/cff/font_index.rb +22 -10
- data/lib/ttfunk/table/cff/header.rb +16 -3
- data/lib/ttfunk/table/cff/index.rb +97 -65
- data/lib/ttfunk/table/cff/one_based_index.rb +11 -1
- data/lib/ttfunk/table/cff/path.rb +43 -4
- data/lib/ttfunk/table/cff/private_dict.rb +31 -11
- data/lib/ttfunk/table/cff/subr_index.rb +7 -2
- data/lib/ttfunk/table/cff/top_dict.rb +82 -59
- data/lib/ttfunk/table/cff/top_index.rb +10 -6
- data/lib/ttfunk/table/cff.rb +41 -21
- data/lib/ttfunk/table/cmap/format00.rb +27 -6
- data/lib/ttfunk/table/cmap/format04.rb +34 -14
- data/lib/ttfunk/table/cmap/format06.rb +28 -1
- data/lib/ttfunk/table/cmap/format10.rb +29 -2
- data/lib/ttfunk/table/cmap/format12.rb +29 -2
- data/lib/ttfunk/table/cmap/subtable.rb +50 -6
- data/lib/ttfunk/table/cmap.rb +21 -0
- data/lib/ttfunk/table/dsig.rb +47 -6
- data/lib/ttfunk/table/glyf/compound.rb +73 -6
- data/lib/ttfunk/table/glyf/path_based.rb +40 -3
- data/lib/ttfunk/table/glyf/simple.rb +50 -5
- data/lib/ttfunk/table/glyf.rb +15 -7
- data/lib/ttfunk/table/head.rb +84 -6
- data/lib/ttfunk/table/hhea.rb +71 -10
- data/lib/ttfunk/table/hmtx.rb +32 -5
- data/lib/ttfunk/table/kern/format0.rb +25 -7
- data/lib/ttfunk/table/kern.rb +16 -4
- data/lib/ttfunk/table/loca.rb +21 -8
- data/lib/ttfunk/table/maxp.rb +195 -10
- data/lib/ttfunk/table/name.rb +126 -9
- data/lib/ttfunk/table/os2.rb +150 -26
- data/lib/ttfunk/table/post/format10.rb +7 -0
- data/lib/ttfunk/table/post/format20.rb +9 -0
- data/lib/ttfunk/table/post/format30.rb +6 -0
- data/lib/ttfunk/table/post/format40.rb +5 -0
- data/lib/ttfunk/table/post.rb +63 -7
- data/lib/ttfunk/table/sbix.rb +50 -14
- data/lib/ttfunk/table/simple.rb +5 -0
- data/lib/ttfunk/table/vorg.rb +31 -3
- data/lib/ttfunk/table.rb +20 -1
- data/lib/ttfunk/ttf_encoder.rb +39 -41
- data/lib/ttfunk.rb +154 -1
- data.tar.gz.sig +0 -0
- metadata +50 -28
- 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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
27
|
+
# Maximum number of operands allowed per operator.
|
22
28
|
MAX_OPERANDS = 48
|
23
29
|
|
24
|
-
#
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
221
|
-
|
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
|
-
|
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
|
-
@
|
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
|
-
(
|
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
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
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
|
-
@
|
158
|
+
@supplemental
|
99
159
|
end
|
100
160
|
|
101
161
|
private
|
102
162
|
|
103
|
-
def encode_supplemental(
|
163
|
+
def encode_supplemental(charmap)
|
104
164
|
new_entries =
|
105
|
-
|
106
|
-
|
107
|
-
|
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
|
-
@
|
211
|
+
@items_count = entry_count
|
154
212
|
@entries = OneBasedArray.new(read(length, 'C*'))
|
155
213
|
|
156
214
|
when :range_format
|
157
215
|
@entries = []
|
158
|
-
@
|
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
|
-
@
|
221
|
+
@items_count += num_left + 1
|
164
222
|
end
|
165
223
|
|
166
224
|
when :supplemental
|
167
225
|
@entries = {}
|
168
|
-
@
|
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,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
|
-
|
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
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
84
|
+
items_count.times { |i| yield(self[i]) }
|
52
85
|
end
|
53
86
|
|
54
|
-
#
|
55
|
-
|
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
|
-
|
59
|
-
|
60
|
-
|
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.
|
151
|
+
@n_glyphs = top_dict.charstrings_index.items_count
|
112
152
|
data = io.read(n_glyphs)
|
113
153
|
@length += data.bytesize
|
114
|
-
@
|
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
|
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
|
-
|
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
|
-
@
|
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
|
-
|
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
|
-
|
34
|
-
|
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
|
-
|
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
|
-
|
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
|