ttfunk 1.5.1 → 1.6.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 (90) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CHANGELOG.md +60 -0
  5. data/README.md +2 -1
  6. data/lib/ttfunk.rb +45 -0
  7. data/lib/ttfunk/aggregate.rb +15 -0
  8. data/lib/ttfunk/bin_utils.rb +47 -0
  9. data/lib/ttfunk/bit_field.rb +31 -0
  10. data/lib/ttfunk/collection.rb +3 -1
  11. data/lib/ttfunk/directory.rb +6 -0
  12. data/lib/ttfunk/encoded_string.rb +97 -0
  13. data/lib/ttfunk/max.rb +25 -0
  14. data/lib/ttfunk/min.rb +25 -0
  15. data/lib/ttfunk/one_based_array.rb +36 -0
  16. data/lib/ttfunk/otf_encoder.rb +61 -0
  17. data/lib/ttfunk/placeholder.rb +13 -0
  18. data/lib/ttfunk/reader.rb +34 -32
  19. data/lib/ttfunk/resource_file.rb +7 -5
  20. data/lib/ttfunk/sci_form.rb +29 -0
  21. data/lib/ttfunk/sub_table.rb +38 -0
  22. data/lib/ttfunk/subset.rb +2 -0
  23. data/lib/ttfunk/subset/base.rb +61 -120
  24. data/lib/ttfunk/subset/code_page.rb +89 -0
  25. data/lib/ttfunk/subset/mac_roman.rb +5 -42
  26. data/lib/ttfunk/subset/unicode.rb +12 -6
  27. data/lib/ttfunk/subset/unicode_8bit.rb +14 -12
  28. data/lib/ttfunk/subset/windows_1252.rb +5 -47
  29. data/lib/ttfunk/subset_collection.rb +4 -0
  30. data/lib/ttfunk/sum.rb +20 -0
  31. data/lib/ttfunk/table.rb +4 -0
  32. data/lib/ttfunk/table/cff.rb +69 -0
  33. data/lib/ttfunk/table/cff/charset.rb +212 -0
  34. data/lib/ttfunk/table/cff/charsets.rb +14 -0
  35. data/lib/ttfunk/table/cff/charsets/expert.rb +189 -0
  36. data/lib/ttfunk/table/cff/charsets/expert_subset.rb +119 -0
  37. data/lib/ttfunk/table/cff/charsets/iso_adobe.rb +241 -0
  38. data/lib/ttfunk/table/cff/charsets/standard_strings.rb +404 -0
  39. data/lib/ttfunk/table/cff/charstring.rb +487 -0
  40. data/lib/ttfunk/table/cff/charstrings_index.rb +39 -0
  41. data/lib/ttfunk/table/cff/dict.rb +266 -0
  42. data/lib/ttfunk/table/cff/encoding.rb +220 -0
  43. data/lib/ttfunk/table/cff/encodings.rb +12 -0
  44. data/lib/ttfunk/table/cff/encodings/expert.rb +206 -0
  45. data/lib/ttfunk/table/cff/encodings/standard.rb +181 -0
  46. data/lib/ttfunk/table/cff/fd_selector.rb +150 -0
  47. data/lib/ttfunk/table/cff/font_dict.rb +79 -0
  48. data/lib/ttfunk/table/cff/font_index.rb +29 -0
  49. data/lib/ttfunk/table/cff/header.rb +33 -0
  50. data/lib/ttfunk/table/cff/index.rb +125 -0
  51. data/lib/ttfunk/table/cff/one_based_index.rb +31 -0
  52. data/lib/ttfunk/table/cff/path.rb +66 -0
  53. data/lib/ttfunk/table/cff/private_dict.rb +84 -0
  54. data/lib/ttfunk/table/cff/subr_index.rb +19 -0
  55. data/lib/ttfunk/table/cff/top_dict.rb +230 -0
  56. data/lib/ttfunk/table/cff/top_index.rb +16 -0
  57. data/lib/ttfunk/table/cmap.rb +4 -4
  58. data/lib/ttfunk/table/cmap/format00.rb +1 -2
  59. data/lib/ttfunk/table/cmap/format04.rb +11 -3
  60. data/lib/ttfunk/table/cmap/format06.rb +2 -0
  61. data/lib/ttfunk/table/cmap/format10.rb +2 -0
  62. data/lib/ttfunk/table/cmap/format12.rb +2 -0
  63. data/lib/ttfunk/table/cmap/subtable.rb +12 -8
  64. data/lib/ttfunk/table/dsig.rb +50 -0
  65. data/lib/ttfunk/table/glyf.rb +11 -9
  66. data/lib/ttfunk/table/glyf/compound.rb +14 -7
  67. data/lib/ttfunk/table/glyf/path_based.rb +47 -0
  68. data/lib/ttfunk/table/glyf/simple.rb +21 -15
  69. data/lib/ttfunk/table/head.rb +43 -5
  70. data/lib/ttfunk/table/hhea.rb +47 -4
  71. data/lib/ttfunk/table/hmtx.rb +11 -4
  72. data/lib/ttfunk/table/kern.rb +3 -0
  73. data/lib/ttfunk/table/kern/format0.rb +3 -0
  74. data/lib/ttfunk/table/loca.rb +2 -0
  75. data/lib/ttfunk/table/maxp.rb +144 -10
  76. data/lib/ttfunk/table/name.rb +75 -37
  77. data/lib/ttfunk/table/os2.rb +327 -4
  78. data/lib/ttfunk/table/post.rb +8 -1
  79. data/lib/ttfunk/table/post/format10.rb +2 -0
  80. data/lib/ttfunk/table/post/format20.rb +5 -1
  81. data/lib/ttfunk/table/post/format30.rb +2 -0
  82. data/lib/ttfunk/table/post/format40.rb +2 -0
  83. data/lib/ttfunk/table/sbix.rb +2 -0
  84. data/lib/ttfunk/table/simple.rb +2 -0
  85. data/lib/ttfunk/table/vorg.rb +54 -0
  86. data/lib/ttfunk/ttf_encoder.rb +220 -0
  87. metadata +88 -20
  88. metadata.gz.sig +0 -0
  89. data/lib/ttfunk/encoding/mac_roman.rb +0 -100
  90. data/lib/ttfunk/encoding/windows_1252.rb +0 -76
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TTFunk
4
+ class Table
5
+ class Cff < TTFunk::Table
6
+ module Encodings
7
+ autoload :EXPERT, 'ttfunk/table/cff/encodings/expert'
8
+ autoload :STANDARD, 'ttfunk/table/cff/encodings/standard'
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TTFunk
4
+ class Table
5
+ class Cff < TTFunk::Table
6
+ module Encodings
7
+ EXPERT = OneBasedArray.new(
8
+ [
9
+ *[0] * 31,
10
+ 1,
11
+ 229,
12
+ 230,
13
+ 0,
14
+ 231,
15
+ 232,
16
+ 233,
17
+ 234,
18
+ 235,
19
+ 236,
20
+ 237,
21
+ 238,
22
+ 13,
23
+ 14,
24
+ 15,
25
+ 99,
26
+ 239,
27
+ 240,
28
+ 241,
29
+ 242,
30
+ 243,
31
+ 244,
32
+ 245,
33
+ 246,
34
+ 247,
35
+ 248,
36
+ 27,
37
+ 28,
38
+ 249,
39
+ 250,
40
+ 251,
41
+ 252,
42
+ 0,
43
+ 253,
44
+ 254,
45
+ 255,
46
+ 256,
47
+ 257,
48
+ 0,
49
+ 0,
50
+ 0,
51
+ 258,
52
+ 0,
53
+ 0,
54
+ 259,
55
+ 260,
56
+ 261,
57
+ 262,
58
+ 0,
59
+ 0,
60
+ 263,
61
+ 264,
62
+ 265,
63
+ 0,
64
+ 266,
65
+ 109,
66
+ 110,
67
+ 267,
68
+ 268,
69
+ 269,
70
+ 0,
71
+ 270,
72
+ 271,
73
+ 272,
74
+ 273,
75
+ 274,
76
+ 275,
77
+ 276,
78
+ 277,
79
+ 278,
80
+ 279,
81
+ 280,
82
+ 281,
83
+ 282,
84
+ 283,
85
+ 284,
86
+ 285,
87
+ 286,
88
+ 287,
89
+ 288,
90
+ 289,
91
+ 290,
92
+ 291,
93
+ 292,
94
+ 293,
95
+ 294,
96
+ 295,
97
+ 296,
98
+ 297,
99
+ 298,
100
+ 299,
101
+ 300,
102
+ 301,
103
+ 302,
104
+ 303,
105
+ *[0] * 34,
106
+ 304,
107
+ 305,
108
+ 306,
109
+ 0,
110
+ 0,
111
+ 307,
112
+ 308,
113
+ 309,
114
+ 310,
115
+ 311,
116
+ 0,
117
+ 312,
118
+ 0,
119
+ 0,
120
+ 313,
121
+ 0,
122
+ 0,
123
+ 314,
124
+ 315,
125
+ 0,
126
+ 0,
127
+ 316,
128
+ 317,
129
+ 318,
130
+ 0,
131
+ 0,
132
+ 0,
133
+ 158,
134
+ 155,
135
+ 163,
136
+ 319,
137
+ 320,
138
+ 321,
139
+ 322,
140
+ 323,
141
+ 324,
142
+ 325,
143
+ 0,
144
+ 0,
145
+ 326,
146
+ 150,
147
+ 164,
148
+ 169,
149
+ 327,
150
+ 328,
151
+ 329,
152
+ 330,
153
+ 331,
154
+ 332,
155
+ 333,
156
+ 334,
157
+ 335,
158
+ 336,
159
+ 337,
160
+ 338,
161
+ 339,
162
+ 340,
163
+ 341,
164
+ 342,
165
+ 343,
166
+ 344,
167
+ 345,
168
+ 346,
169
+ 347,
170
+ 348,
171
+ 349,
172
+ 350,
173
+ 351,
174
+ 352,
175
+ 353,
176
+ 354,
177
+ 355,
178
+ 356,
179
+ 357,
180
+ 358,
181
+ 359,
182
+ 360,
183
+ 361,
184
+ 362,
185
+ 363,
186
+ 364,
187
+ 365,
188
+ 366,
189
+ 367,
190
+ 368,
191
+ 369,
192
+ 370,
193
+ 371,
194
+ 372,
195
+ 373,
196
+ 374,
197
+ 375,
198
+ 376,
199
+ 377,
200
+ 378
201
+ ]
202
+ ).freeze
203
+ end
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TTFunk
4
+ class Table
5
+ class Cff < TTFunk::Table
6
+ module Encodings
7
+ STANDARD = OneBasedArray.new(
8
+ [
9
+ *[0] * 31,
10
+ 1,
11
+ 2,
12
+ 3,
13
+ 4,
14
+ 5,
15
+ 6,
16
+ 7,
17
+ 8,
18
+ 9,
19
+ 10,
20
+ 11,
21
+ 12,
22
+ 13,
23
+ 14,
24
+ 15,
25
+ 16,
26
+ 17,
27
+ 18,
28
+ 19,
29
+ 20,
30
+ 21,
31
+ 22,
32
+ 23,
33
+ 24,
34
+ 25,
35
+ 26,
36
+ 27,
37
+ 28,
38
+ 29,
39
+ 30,
40
+ 31,
41
+ 32,
42
+ 33,
43
+ 34,
44
+ 35,
45
+ 36,
46
+ 37,
47
+ 38,
48
+ 39,
49
+ 40,
50
+ 41,
51
+ 42,
52
+ 43,
53
+ 44,
54
+ 45,
55
+ 46,
56
+ 47,
57
+ 48,
58
+ 49,
59
+ 50,
60
+ 51,
61
+ 52,
62
+ 53,
63
+ 54,
64
+ 55,
65
+ 56,
66
+ 57,
67
+ 58,
68
+ 59,
69
+ 60,
70
+ 61,
71
+ 62,
72
+ 63,
73
+ 64,
74
+ 65,
75
+ 66,
76
+ 67,
77
+ 68,
78
+ 69,
79
+ 70,
80
+ 71,
81
+ 72,
82
+ 73,
83
+ 74,
84
+ 75,
85
+ 76,
86
+ 77,
87
+ 78,
88
+ 79,
89
+ 80,
90
+ 81,
91
+ 82,
92
+ 83,
93
+ 84,
94
+ 85,
95
+ 86,
96
+ 87,
97
+ 88,
98
+ 89,
99
+ 90,
100
+ 91,
101
+ 92,
102
+ 93,
103
+ 94,
104
+ 95,
105
+ *[0] * 34,
106
+ 96,
107
+ 97,
108
+ 98,
109
+ 99,
110
+ 100,
111
+ 101,
112
+ 102,
113
+ 103,
114
+ 104,
115
+ 105,
116
+ 106,
117
+ 107,
118
+ 108,
119
+ 109,
120
+ 110,
121
+ 0,
122
+ 111,
123
+ 112,
124
+ 113,
125
+ 114,
126
+ 0,
127
+ 115,
128
+ 116,
129
+ 117,
130
+ 118,
131
+ 119,
132
+ 120,
133
+ 121,
134
+ 122,
135
+ 0,
136
+ 123,
137
+ 0,
138
+ 124,
139
+ 125,
140
+ 126,
141
+ 127,
142
+ 128,
143
+ 129,
144
+ 130,
145
+ 131,
146
+ 0,
147
+ 132,
148
+ 133,
149
+ 0,
150
+ 134,
151
+ 135,
152
+ 136,
153
+ 137,
154
+ *[0] * 16,
155
+ 138,
156
+ 0,
157
+ 139,
158
+ *[0] * 4,
159
+ 140,
160
+ 141,
161
+ 142,
162
+ 143,
163
+ *[0] * 5,
164
+ 144,
165
+ 0,
166
+ 0,
167
+ 0,
168
+ 145,
169
+ 0,
170
+ 0,
171
+ 146,
172
+ 147,
173
+ 148,
174
+ 149,
175
+ *[0] * 4
176
+ ]
177
+ ).freeze
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TTFunk
4
+ class Table
5
+ class Cff < TTFunk::Table
6
+ class FdSelector < TTFunk::SubTable
7
+ include Enumerable
8
+
9
+ ARRAY_FORMAT = 0
10
+ RANGE_FORMAT = 3
11
+
12
+ RANGE_ENTRY_SIZE = 3
13
+ ARRAY_ENTRY_SIZE = 1
14
+
15
+ attr_reader :top_dict, :count, :entries, :n_glyphs
16
+
17
+ def initialize(top_dict, file, offset, length = nil)
18
+ @top_dict = top_dict
19
+ super(file, offset, length)
20
+ end
21
+
22
+ def [](glyph_id)
23
+ case format_sym
24
+ when :array_format
25
+ entries[glyph_id]
26
+
27
+ when :range_format
28
+ if (entry = range_cache[glyph_id])
29
+ return entry
30
+ end
31
+
32
+ range, entry = entries.bsearch do |rng, _|
33
+ if rng.cover?(glyph_id)
34
+ 0
35
+ elsif glyph_id < rng.first
36
+ -1
37
+ else
38
+ 1
39
+ end
40
+ end
41
+
42
+ range.each { |i| range_cache[i] = entry }
43
+ entry
44
+ end
45
+ end
46
+
47
+ def each
48
+ return to_enum(__method__) unless block_given?
49
+
50
+ count.times { |i| yield self[i] }
51
+ end
52
+
53
+ # mapping is new -> old glyph ids
54
+ def encode(mapping)
55
+ # get list of [new_gid, fd_index] pairs
56
+ new_indices = mapping.keys.sort.map do |new_gid|
57
+ [new_gid, self[mapping[new_gid]]]
58
+ end
59
+
60
+ ranges = rangify_gids(new_indices)
61
+ total_range_size = ranges.size * RANGE_ENTRY_SIZE
62
+ total_array_size = new_indices.size * ARRAY_ENTRY_SIZE
63
+
64
+ ''.b.tap do |result|
65
+ if total_array_size <= total_range_size
66
+ result << [ARRAY_FORMAT].pack('C')
67
+ result << new_indices.map(&:last).pack('C*')
68
+ else
69
+ result << [RANGE_FORMAT, ranges.size].pack('Cn')
70
+ ranges.each { |range| result << range.pack('nC') }
71
+
72
+ # "A sentinel GID follows the last range element and serves to
73
+ # delimit the last range in the array. (The sentinel GID is set
74
+ # equal to the number of glyphs in the font. That is, its value
75
+ # is 1 greater than the last GID in the font)."
76
+ result << [new_indices.size].pack('n')
77
+ end
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ def range_cache
84
+ @range_cache ||= {}
85
+ end
86
+
87
+ # values is an array of [new_gid, fd_index] pairs
88
+ def rangify_gids(values)
89
+ start_gid = 0
90
+
91
+ [].tap do |ranges|
92
+ values.each_cons(2) do |(_, first_idx), (sec_gid, sec_idx)|
93
+ if first_idx != sec_idx
94
+ ranges << [start_gid, first_idx]
95
+ start_gid = sec_gid
96
+ end
97
+ end
98
+
99
+ ranges << [start_gid, values.last[1]]
100
+ end
101
+ end
102
+
103
+ def parse!
104
+ @format = read(1, 'C').first
105
+ @length = 1
106
+
107
+ case format_sym
108
+ when :array_format
109
+ @n_glyphs = top_dict.charstrings_index.count
110
+ data = io.read(n_glyphs)
111
+ @length += data.bytesize
112
+ @count = data.bytesize
113
+ @entries = data.bytes
114
+
115
+ when :range_format
116
+ # +2 for sentinel GID, +2 for num_ranges
117
+ num_ranges = read(2, 'n').first
118
+ @length += (num_ranges * RANGE_ENTRY_SIZE) + 4
119
+
120
+ ranges = Array.new(num_ranges) { read(RANGE_ENTRY_SIZE, 'nC') }
121
+
122
+ @entries = ranges.each_cons(2).map do |first, second|
123
+ first_gid, fd_index = first
124
+ second_gid, = second
125
+ [(first_gid...second_gid), fd_index]
126
+ end
127
+
128
+ # read the sentinel GID, otherwise known as the number of glyphs
129
+ # in the font
130
+ @n_glyphs = read(2, 'n').first
131
+
132
+ last_start_gid, last_fd_index = ranges.last
133
+ @entries << [(last_start_gid...(n_glyphs + 1)), last_fd_index]
134
+
135
+ @count = entries.inject(0) { |sum, entry| sum + entry.first.size }
136
+ end
137
+ end
138
+
139
+ def format_sym
140
+ case @format
141
+ when ARRAY_FORMAT then :array_format
142
+ when RANGE_FORMAT then :range_format
143
+ else
144
+ raise "unsupported fd select format '#{@format}'"
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end