Line | Count | Source |
1 | | // Copyright (c) 2009-2017 The OTS Authors. All rights reserved. |
2 | | // Use of this source code is governed by a BSD-style license that can be |
3 | | // found in the LICENSE file. |
4 | | |
5 | | #include "cmap.h" |
6 | | |
7 | | #include <algorithm> |
8 | | #include <set> |
9 | | #include <utility> |
10 | | #include <vector> |
11 | | |
12 | | #include "maxp.h" |
13 | | #include "os2.h" |
14 | | |
15 | | // cmap - Character To Glyph Index Mapping Table |
16 | | // http://www.microsoft.com/typography/otspec/cmap.htm |
17 | | |
18 | | namespace { |
19 | | |
20 | | struct CMAPSubtableHeader { |
21 | | uint16_t platform; |
22 | | uint16_t encoding; |
23 | | uint32_t offset; |
24 | | uint16_t format; |
25 | | uint32_t length; |
26 | | uint32_t language; |
27 | | }; |
28 | | |
29 | | struct Subtable314Range { |
30 | | uint16_t start_range; |
31 | | uint16_t end_range; |
32 | | int16_t id_delta; |
33 | | uint16_t id_range_offset; |
34 | | uint32_t id_range_offset_offset; |
35 | | }; |
36 | | |
37 | | // Glyph array size for the Mac Roman (format 0) table. |
38 | | const size_t kFormat0ArraySize = 256; |
39 | | |
40 | | // The upper limit of the Unicode code point. |
41 | | const uint32_t kUnicodeUpperLimit = 0x10FFFF; |
42 | | |
43 | | // The maximum number of UVS records (See below). |
44 | | const uint32_t kMaxCMAPSelectorRecords = 259; |
45 | | // The range of UVSes are: |
46 | | // 0x180B-0x180D (3 code points) |
47 | | // 0xFE00-0xFE0F (16 code points) |
48 | | // 0xE0100-0xE01EF (240 code points) |
49 | | const uint32_t kMongolianVSStart = 0x180B; |
50 | | const uint32_t kMongolianVSEnd = 0x180D; |
51 | | const uint32_t kVSStart = 0xFE00; |
52 | | const uint32_t kVSEnd = 0xFE0F; |
53 | | const uint32_t kIVSStart = 0xE0100; |
54 | | const uint32_t kIVSEnd = 0xE01EF; |
55 | | const uint32_t kUVSUpperLimit = 0xFFFFFF; |
56 | | |
57 | | } // namespace |
58 | | |
59 | | namespace ots { |
60 | | |
61 | | // Parses Format 4 tables |
62 | | bool OpenTypeCMAP::ParseFormat4(int platform, int encoding, |
63 | 79.4k | const uint8_t *data, size_t length, uint16_t num_glyphs) { |
64 | 79.4k | ots::Buffer subtable(data, length); |
65 | | |
66 | | // 0.3.4, 3.0.4 or 3.1.4 subtables are complex and, rather than expanding the |
67 | | // whole thing and recompacting it, we validate it and include it verbatim |
68 | | // in the output. |
69 | | |
70 | 79.4k | OpenTypeOS2 *os2 = static_cast<OpenTypeOS2*>( |
71 | 79.4k | GetFont()->GetTypedTable(OTS_TAG_OS2)); |
72 | 79.4k | if (!os2) { |
73 | 0 | return Error("Required OS/2 table missing"); |
74 | 0 | } |
75 | | |
76 | 79.4k | if (!subtable.Skip(4)) { |
77 | 42 | return Error("Can't read 4 bytes at start of cmap format 4 subtable"); |
78 | 42 | } |
79 | 79.4k | uint16_t language = 0; |
80 | 79.4k | if (!subtable.ReadU16(&language)) { |
81 | 43 | return Error("Can't read language"); |
82 | 43 | } |
83 | 79.4k | if (language) { |
84 | | // Platform ID 3 (windows) subtables should have language '0'. |
85 | 45 | return Error("Languages should be 0 (%d)", language); |
86 | 45 | } |
87 | | |
88 | 79.3k | uint16_t segcountx2, search_range, entry_selector, range_shift; |
89 | 79.3k | segcountx2 = search_range = entry_selector = range_shift = 0; |
90 | 79.3k | if (!subtable.ReadU16(&segcountx2) || |
91 | 79.3k | !subtable.ReadU16(&search_range) || |
92 | 79.3k | !subtable.ReadU16(&entry_selector) || |
93 | 79.2k | !subtable.ReadU16(&range_shift)) { |
94 | 118 | return Error("Failed to read subcmap structure"); |
95 | 118 | } |
96 | | |
97 | 79.2k | if (segcountx2 & 1 || search_range & 1) { |
98 | 86 | return Error("Bad subcmap structure"); |
99 | 86 | } |
100 | 79.1k | const uint16_t segcount = segcountx2 >> 1; |
101 | | // There must be at least one segment according the spec. |
102 | 79.1k | if (segcount < 1) { |
103 | 42 | return Error("Segcount < 1 (%d)", segcount); |
104 | 42 | } |
105 | | |
106 | | // log2segcount is the maximal x s.t. 2^x < segcount |
107 | 79.1k | unsigned log2segcount = 0; |
108 | 236k | while (1u << (log2segcount + 1) <= segcount) { |
109 | 157k | log2segcount++; |
110 | 157k | } |
111 | | |
112 | 79.1k | const uint16_t expected_search_range = 2 * 1u << log2segcount; |
113 | 79.1k | if (expected_search_range != search_range) { |
114 | 54 | return Error("expected search range != search range (%d != %d)", expected_search_range, search_range); |
115 | 54 | } |
116 | | |
117 | 79.0k | if (entry_selector != log2segcount) { |
118 | 50 | return Error("entry selector != log2(segement count) (%d != %d)", entry_selector, log2segcount); |
119 | 50 | } |
120 | | |
121 | 79.0k | const uint16_t expected_range_shift = segcountx2 - search_range; |
122 | 79.0k | if (range_shift != expected_range_shift) { |
123 | 50 | return Error("unexpected range shift (%d != %d)", range_shift, expected_range_shift); |
124 | 50 | } |
125 | | |
126 | 78.9k | std::vector<Subtable314Range> ranges(segcount); |
127 | | |
128 | 513k | for (unsigned i = 0; i < segcount; ++i) { |
129 | 434k | if (!subtable.ReadU16(&ranges[i].end_range)) { |
130 | 26 | return Error("Failed to read segment %d", i); |
131 | 26 | } |
132 | 434k | } |
133 | | |
134 | 78.9k | uint16_t padding; |
135 | 78.9k | if (!subtable.ReadU16(&padding)) { |
136 | 38 | return Error("Failed to read cmap subtable segment padding"); |
137 | 38 | } |
138 | 78.8k | if (padding) { |
139 | 44 | return Error("Non zero cmap subtable segment padding (%d)", padding); |
140 | 44 | } |
141 | | |
142 | 513k | for (unsigned i = 0; i < segcount; ++i) { |
143 | 434k | if (!subtable.ReadU16(&ranges[i].start_range)) { |
144 | 43 | return Error("Failed to read segment start range %d", i); |
145 | 43 | } |
146 | 434k | } |
147 | 513k | for (unsigned i = 0; i < segcount; ++i) { |
148 | 434k | if (!subtable.ReadS16(&ranges[i].id_delta)) { |
149 | 42 | return Error("Failed to read segment delta %d", i); |
150 | 42 | } |
151 | 434k | } |
152 | 512k | for (unsigned i = 0; i < segcount; ++i) { |
153 | 433k | ranges[i].id_range_offset_offset = subtable.offset(); |
154 | 433k | if (!subtable.ReadU16(&ranges[i].id_range_offset)) { |
155 | 40 | return Error("Failed to read segment range offset %d", i); |
156 | 40 | } |
157 | | |
158 | 433k | if (ranges[i].id_range_offset & 1) { |
159 | | // Some font generators seem to put 65535 on id_range_offset |
160 | | // for 0xFFFF-0xFFFF range. |
161 | | // (e.g., many fonts in http://www.princexml.com/fonts/) |
162 | 35.0k | if (i == segcount - 1u) { |
163 | 34.9k | Warning("bad id_range_offset"); |
164 | 34.9k | ranges[i].id_range_offset = 0; |
165 | | // The id_range_offset value in the transcoded font will not change |
166 | | // since this table is not actually "transcoded" yet. |
167 | 34.9k | } else { |
168 | 43 | return Error("Bad segment offset (%d)", ranges[i].id_range_offset); |
169 | 43 | } |
170 | 35.0k | } |
171 | 433k | } |
172 | | |
173 | | // ranges must be ascending order, based on the end_code. Ranges may not |
174 | | // overlap. |
175 | 433k | for (unsigned i = 1; i < segcount; ++i) { |
176 | 354k | if ((i == segcount - 1u) && |
177 | 78.4k | (ranges[i - 1].start_range == 0xffff) && |
178 | 3.16k | (ranges[i - 1].end_range == 0xffff) && |
179 | 84 | (ranges[i].start_range == 0xffff) && |
180 | 51 | (ranges[i].end_range == 0xffff)) { |
181 | | // Some fonts (e.g., Germania.ttf) have multiple 0xffff terminators. |
182 | | // We'll accept them as an exception. |
183 | 33 | Warning("multiple 0xffff terminators found"); |
184 | 33 | continue; |
185 | 33 | } |
186 | | |
187 | | // Note: some Linux fonts (e.g., LucidaSansOblique.ttf, bsmi00lp.ttf) have |
188 | | // unsorted table... |
189 | 354k | if (ranges[i].end_range <= ranges[i - 1].end_range) { |
190 | 94 | return Error("Out of order end range (%d <= %d)", ranges[i].end_range, ranges[i-1].end_range); |
191 | 94 | } |
192 | 354k | if (ranges[i].start_range <= ranges[i - 1].end_range) { |
193 | 44 | return Error("out of order start range (%d <= %d)", ranges[i].start_range, ranges[i-1].end_range); |
194 | 44 | } |
195 | | |
196 | | // On many fonts, the value of {first, last}_char_index are incorrect. |
197 | | // Fix them. |
198 | 354k | if (os2->table.first_char_index != 0xFFFF && |
199 | 341k | ranges[i].start_range != 0xFFFF && |
200 | 251k | os2->table.first_char_index > ranges[i].start_range) { |
201 | 36.1k | os2->table.first_char_index = ranges[i].start_range; |
202 | 36.1k | } |
203 | 354k | if (os2->table.last_char_index != 0xFFFF && |
204 | 290k | ranges[i].end_range != 0xFFFF && |
205 | 221k | os2->table.last_char_index < ranges[i].end_range) { |
206 | 79.5k | os2->table.last_char_index = ranges[i].end_range; |
207 | 79.5k | } |
208 | 354k | } |
209 | | |
210 | | // The last range must end at 0xffff |
211 | 78.5k | if (ranges[segcount - 1].start_range != 0xffff || ranges[segcount - 1].end_range != 0xffff) { |
212 | 119 | return Error("Final segment start and end must be 0xFFFF (0x%04X-0x%04X)", |
213 | 119 | ranges[segcount - 1].start_range, ranges[segcount - 1].end_range); |
214 | 119 | } |
215 | | |
216 | | // A format 4 CMAP subtable is complex. To be safe we simulate a lookup of |
217 | | // each code-point defined in the table and make sure that they are all valid |
218 | | // glyphs and that we don't access anything out-of-bounds. |
219 | 510k | for (unsigned i = 0; i < segcount; ++i) { |
220 | 2.62M | for (unsigned cp = ranges[i].start_range; cp <= ranges[i].end_range; ++cp) { |
221 | 2.18M | const uint16_t code_point = static_cast<uint16_t>(cp); |
222 | 2.18M | if (ranges[i].id_range_offset == 0) { |
223 | | // this is explictly allowed to overflow in the spec |
224 | 1.80M | const uint16_t glyph = code_point + ranges[i].id_delta; |
225 | 1.80M | if (glyph >= num_glyphs) { |
226 | 86 | return Error("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1); |
227 | 86 | } |
228 | 1.80M | } else { |
229 | 389k | const uint16_t range_delta = code_point - ranges[i].start_range; |
230 | | // this might seem odd, but it's true. The offset is relative to the |
231 | | // location of the offset value itself. |
232 | 389k | const uint32_t glyph_id_offset = ranges[i].id_range_offset_offset + |
233 | 389k | ranges[i].id_range_offset + |
234 | 389k | range_delta * 2; |
235 | | // We need to be able to access a 16-bit value from this offset |
236 | 389k | if (glyph_id_offset + 1 >= length) { |
237 | 40 | return Error("bad glyph id offset (%d > %ld)", glyph_id_offset, length); |
238 | 40 | } |
239 | 389k | uint16_t glyph; |
240 | 389k | std::memcpy(&glyph, data + glyph_id_offset, 2); |
241 | 389k | glyph = ots_ntohs(glyph); |
242 | 389k | if (glyph >= num_glyphs) { |
243 | 45 | return Error("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1); |
244 | 45 | } |
245 | 389k | } |
246 | 2.18M | } |
247 | 432k | } |
248 | | |
249 | | // We accept the table. |
250 | | // TODO(yusukes): transcode the subtable. |
251 | 78.2k | if (platform == 3 && encoding == 0) { |
252 | 1.60k | this->subtable_3_0_4_data = data; |
253 | 1.60k | this->subtable_3_0_4_length = length; |
254 | 76.6k | } else if (platform == 3 && encoding == 1) { |
255 | 68.2k | this->subtable_3_1_4_data = data; |
256 | 68.2k | this->subtable_3_1_4_length = length; |
257 | 68.2k | } else if (platform == 0 && encoding == 3) { |
258 | 8.36k | this->subtable_0_3_4_data = data; |
259 | 8.36k | this->subtable_0_3_4_length = length; |
260 | 8.36k | } else { |
261 | 0 | return Error("Unknown cmap subtable type (platform=%d, encoding=%d)", platform, encoding); |
262 | 0 | } |
263 | | |
264 | 78.2k | return true; |
265 | 78.2k | } |
266 | | |
267 | | bool OpenTypeCMAP::Parse31012(const uint8_t *data, size_t length, |
268 | 2.46k | uint16_t num_glyphs) { |
269 | 2.46k | ots::Buffer subtable(data, length); |
270 | | |
271 | | // Format 12 tables are simple. We parse these and fully serialise them |
272 | | // later. |
273 | | |
274 | 2.46k | if (!subtable.Skip(8)) { |
275 | 60 | return Error("failed to skip the first 8 bytes of format 12 subtable"); |
276 | 60 | } |
277 | 2.40k | uint32_t language = 0; |
278 | 2.40k | if (!subtable.ReadU32(&language)) { |
279 | 38 | return Error("can't read format 12 subtable language"); |
280 | 38 | } |
281 | 2.36k | if (language) { |
282 | 68 | return Error("format 12 subtable language should be zero (%d)", language); |
283 | 68 | } |
284 | | |
285 | 2.29k | uint32_t num_groups = 0; |
286 | 2.29k | if (!subtable.ReadU32(&num_groups)) { |
287 | 20 | return Error("can't read number of format 12 subtable groups"); |
288 | 20 | } |
289 | 2.27k | if (num_groups == 0 || subtable.remaining() / 12 < num_groups) { |
290 | 134 | return Error("Bad format 12 subtable group count %d", num_groups); |
291 | 134 | } |
292 | | |
293 | 2.14k | std::vector<ots::OpenTypeCMAPSubtableRange> &groups |
294 | 2.14k | = this->subtable_3_10_12; |
295 | 2.14k | groups.resize(num_groups); |
296 | | |
297 | 111k | for (unsigned i = 0; i < num_groups; ++i) { |
298 | 109k | if (!subtable.ReadU32(&groups[i].start_range) || |
299 | 109k | !subtable.ReadU32(&groups[i].end_range) || |
300 | 109k | !subtable.ReadU32(&groups[i].start_glyph_id)) { |
301 | 0 | return Error("can't read format 12 subtable group"); |
302 | 0 | } |
303 | | |
304 | 109k | if (groups[i].start_range > kUnicodeUpperLimit || |
305 | 109k | groups[i].end_range > kUnicodeUpperLimit || |
306 | 109k | groups[i].start_glyph_id > 0xFFFF) { |
307 | 177 | return Error("bad format 12 subtable group (startCharCode=0x%4X, endCharCode=0x%4X, startGlyphID=%d)", |
308 | 177 | groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id); |
309 | 177 | } |
310 | | |
311 | | // We assert that the glyph value is within range. Because of the range |
312 | | // limits, above, we don't need to worry about overflow. |
313 | 109k | if (groups[i].end_range < groups[i].start_range) { |
314 | 44 | return Error("format 12 subtable group endCharCode before startCharCode (0x%4X < 0x%4X)", |
315 | 44 | groups[i].end_range, groups[i].start_range); |
316 | 44 | } |
317 | 109k | if ((groups[i].end_range - groups[i].start_range) + |
318 | 109k | groups[i].start_glyph_id >= num_glyphs) { |
319 | 56 | return Error("bad format 12 subtable group startGlyphID (%d)", groups[i].start_glyph_id); |
320 | 56 | } |
321 | 109k | } |
322 | | |
323 | | // the groups must be sorted by start code and may not overlap |
324 | 109k | for (unsigned i = 1; i < num_groups; ++i) { |
325 | 107k | if (groups[i].start_range <= groups[i - 1].start_range) { |
326 | 22 | return Error("out of order format 12 subtable group (startCharCode=0x%4X <= startCharCode=0x%4X of previous group)", |
327 | 22 | groups[i].start_range, groups[i-1].start_range); |
328 | 22 | } |
329 | 107k | if (groups[i].start_range <= groups[i - 1].end_range) { |
330 | 6 | return Error("overlapping format 12 subtable groups (startCharCode=0x%4X <= endCharCode=0x%4X of previous group)", |
331 | 6 | groups[i].start_range, groups[i-1].end_range); |
332 | 6 | } |
333 | 107k | } |
334 | | |
335 | 1.83k | return true; |
336 | 1.86k | } |
337 | | |
338 | | bool OpenTypeCMAP::Parse31013(const uint8_t *data, size_t length, |
339 | 729 | uint16_t num_glyphs) { |
340 | 729 | ots::Buffer subtable(data, length); |
341 | | |
342 | | // Format 13 tables are simple. We parse these and fully serialise them |
343 | | // later. |
344 | | |
345 | 729 | if (!subtable.Skip(8)) { |
346 | 41 | return Error("Bad cmap subtable length"); |
347 | 41 | } |
348 | 688 | uint32_t language = 0; |
349 | 688 | if (!subtable.ReadU32(&language)) { |
350 | 39 | return Error("Can't read cmap subtable language"); |
351 | 39 | } |
352 | 649 | if (language) { |
353 | 78 | return Error("Cmap subtable language should be zero but is %d", language); |
354 | 78 | } |
355 | | |
356 | 571 | uint32_t num_groups = 0; |
357 | 571 | if (!subtable.ReadU32(&num_groups)) { |
358 | 46 | return Error("Can't read number of groups in a cmap subtable"); |
359 | 46 | } |
360 | | |
361 | | // We limit the number of groups in the same way as in 3.10.12 tables. See |
362 | | // the comment there in |
363 | 525 | if (num_groups == 0 || subtable.remaining() / 12 < num_groups) { |
364 | 136 | return Error("Bad format 13 subtable group count %d", num_groups); |
365 | 136 | } |
366 | | |
367 | 389 | std::vector<ots::OpenTypeCMAPSubtableRange> &groups = this->subtable_3_10_13; |
368 | 389 | groups.resize(num_groups); |
369 | | |
370 | 1.56k | for (unsigned i = 0; i < num_groups; ++i) { |
371 | 1.39k | if (!subtable.ReadU32(&groups[i].start_range) || |
372 | 1.39k | !subtable.ReadU32(&groups[i].end_range) || |
373 | 1.39k | !subtable.ReadU32(&groups[i].start_glyph_id)) { |
374 | 0 | return Error("Can't read subrange structure in a cmap subtable"); |
375 | 0 | } |
376 | | |
377 | | // We conservatively limit all of the values to protect some parsers from |
378 | | // overflows |
379 | 1.39k | if (groups[i].start_range > kUnicodeUpperLimit || |
380 | 1.34k | groups[i].end_range > kUnicodeUpperLimit || |
381 | 1.28k | groups[i].start_glyph_id > 0xFFFF) { |
382 | 176 | return Error("Bad subrange with start_range=%d, end_range=%d, start_glyph_id=%d", groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id); |
383 | 176 | } |
384 | | |
385 | 1.21k | if (groups[i].start_glyph_id >= num_glyphs) { |
386 | 48 | return Error("Subrange starting glyph id too high (%d > %d)", groups[i].start_glyph_id, num_glyphs); |
387 | 48 | } |
388 | 1.21k | } |
389 | | |
390 | | // the groups must be sorted by start code and may not overlap |
391 | 796 | for (unsigned i = 1; i < num_groups; ++i) { |
392 | 677 | if (groups[i].start_range <= groups[i - 1].start_range) { |
393 | 24 | return Error("Overlapping subrange starts (%d >= %d)", groups[i]. start_range, groups[i-1].start_range); |
394 | 24 | } |
395 | 653 | if (groups[i].start_range <= groups[i - 1].end_range) { |
396 | 22 | return Error("Overlapping subranges (%d <= %d)", groups[i].start_range, groups[i-1].end_range); |
397 | 22 | } |
398 | 653 | } |
399 | | |
400 | 119 | return true; |
401 | 165 | } |
402 | | |
403 | 1.39k | bool OpenTypeCMAP::Parse0514(const uint8_t *data, size_t length) { |
404 | | // Unicode Variation Selector table |
405 | 1.39k | ots::Buffer subtable(data, length); |
406 | | |
407 | | // Format 14 tables are simple. We parse these and fully serialise them |
408 | | // later. |
409 | | |
410 | | // Skip format (USHORT) and length (ULONG) |
411 | 1.39k | if (!subtable.Skip(6)) { |
412 | 36 | return Error("Can't read start of cmap subtable"); |
413 | 36 | } |
414 | | |
415 | 1.36k | uint32_t num_records = 0; |
416 | 1.36k | if (!subtable.ReadU32(&num_records)) { |
417 | 16 | return Error("Can't read number of records in cmap subtable"); |
418 | 16 | } |
419 | 1.34k | if (num_records == 0 || num_records > kMaxCMAPSelectorRecords) { |
420 | 109 | return Error("Bad format 14 subtable records count %d", num_records); |
421 | 109 | } |
422 | | |
423 | 1.23k | std::vector<ots::OpenTypeCMAPSubtableVSRecord>& records |
424 | 1.23k | = this->subtable_0_5_14; |
425 | 1.23k | records.resize(num_records); |
426 | | |
427 | 5.72k | for (unsigned i = 0; i < num_records; ++i) { |
428 | 4.95k | if (!subtable.ReadU24(&records[i].var_selector) || |
429 | 4.95k | !subtable.ReadU32(&records[i].default_offset) || |
430 | 4.91k | !subtable.ReadU32(&records[i].non_default_offset)) { |
431 | 76 | return Error("Can't read record structure of record %d in cmap subtale", i); |
432 | 76 | } |
433 | | // Checks the value of variation selector |
434 | 4.88k | if (!((records[i].var_selector >= kMongolianVSStart && |
435 | 4.82k | records[i].var_selector <= kMongolianVSEnd) || |
436 | 4.85k | (records[i].var_selector >= kVSStart && |
437 | 4.79k | records[i].var_selector <= kVSEnd) || |
438 | 4.40k | (records[i].var_selector >= kIVSStart && |
439 | 4.31k | records[i].var_selector <= kIVSEnd))) { |
440 | 156 | return Error("Bad record variation selector (%04X) in record %i", records[i].var_selector, i); |
441 | 156 | } |
442 | 4.72k | if (i > 0 && |
443 | 3.65k | records[i-1].var_selector >= records[i].var_selector) { |
444 | 38 | return Error("Out of order variation selector (%04X >= %04X) in record %d", records[i-1].var_selector, records[i].var_selector, i); |
445 | 38 | } |
446 | | |
447 | | // Checks offsets |
448 | 4.68k | if (!records[i].default_offset && !records[i].non_default_offset) { |
449 | 42 | return Error("No default aoffset in variation selector record %d", i); |
450 | 42 | } |
451 | 4.64k | if (records[i].default_offset && |
452 | 1.92k | records[i].default_offset >= length) { |
453 | 64 | return Error("Default offset too high (%d >= %ld) in record %d", records[i].default_offset, length, i); |
454 | 64 | } |
455 | 4.58k | if (records[i].non_default_offset && |
456 | 3.58k | records[i].non_default_offset >= length) { |
457 | 92 | return Error("Non default offset too high (%d >= %ld) in record %d", records[i].non_default_offset, length, i); |
458 | 92 | } |
459 | 4.58k | } |
460 | | |
461 | 1.91k | for (unsigned i = 0; i < num_records; ++i) { |
462 | | // Checks default UVS table |
463 | 1.69k | if (records[i].default_offset) { |
464 | 680 | subtable.set_offset(records[i].default_offset); |
465 | 680 | uint32_t num_ranges = 0; |
466 | 680 | if (!subtable.ReadU32(&num_ranges)) { |
467 | 37 | return Error("Can't read number of ranges in record %d", i); |
468 | 37 | } |
469 | 643 | if (num_ranges == 0 || subtable.remaining() / 4 < num_ranges) { |
470 | 92 | return Error("Bad number of ranges (%d) in record %d", num_ranges, i); |
471 | 92 | } |
472 | | |
473 | 551 | uint32_t last_unicode_value = 0; |
474 | 551 | std::vector<ots::OpenTypeCMAPSubtableVSRange>& ranges |
475 | 551 | = records[i].ranges; |
476 | 551 | ranges.resize(num_ranges); |
477 | | |
478 | 1.15k | for (unsigned j = 0; j < num_ranges; ++j) { |
479 | 745 | if (!subtable.ReadU24(&ranges[j].unicode_value) || |
480 | 745 | !subtable.ReadU8(&ranges[j].additional_count)) { |
481 | 0 | return Error("Can't read range info in variation selector record %d", i); |
482 | 0 | } |
483 | 745 | const uint32_t check_value = |
484 | 745 | ranges[j].unicode_value + ranges[j].additional_count; |
485 | 745 | if (ranges[j].unicode_value == 0 || |
486 | 704 | ranges[j].unicode_value > kUnicodeUpperLimit || |
487 | 655 | check_value > kUVSUpperLimit || |
488 | 655 | (last_unicode_value && |
489 | 162 | ranges[j].unicode_value <= last_unicode_value)) { |
490 | 144 | return Error("Bad Unicode value *%04X) in variation selector range %d record %d", ranges[j].unicode_value, j, i); |
491 | 144 | } |
492 | 601 | last_unicode_value = check_value; |
493 | 601 | } |
494 | 551 | } |
495 | | |
496 | | // Checks non default UVS table |
497 | 1.42k | if (records[i].non_default_offset) { |
498 | 1.14k | subtable.set_offset(records[i].non_default_offset); |
499 | 1.14k | uint32_t num_mappings = 0; |
500 | 1.14k | if (!subtable.ReadU32(&num_mappings)) { |
501 | 34 | return Error("Can't read number of mappings in variation selector record %d", i); |
502 | 34 | } |
503 | 1.10k | if (num_mappings == 0 || subtable.remaining() / 5 < num_mappings) { |
504 | 90 | return Error("Bad number of mappings (%d) in variation selector record %d", num_mappings, i); |
505 | 90 | } |
506 | | |
507 | 1.01k | uint32_t last_unicode_value = 0; |
508 | 1.01k | std::vector<ots::OpenTypeCMAPSubtableVSMapping>& mappings |
509 | 1.01k | = records[i].mappings; |
510 | 1.01k | mappings.resize(num_mappings); |
511 | | |
512 | 2.11k | for (unsigned j = 0; j < num_mappings; ++j) { |
513 | 1.25k | if (!subtable.ReadU24(&mappings[j].unicode_value) || |
514 | 1.25k | !subtable.ReadU16(&mappings[j].glyph_id)) { |
515 | 0 | return Error("Can't read mapping %d in variation selector record %d", j, i); |
516 | 0 | } |
517 | 1.25k | if (mappings[j].glyph_id == 0 || mappings[j].unicode_value == 0) { |
518 | 86 | return Error("Bad mapping (%04X -> %d) in mapping %d of variation selector %d", mappings[j].unicode_value, mappings[j].glyph_id, j, i); |
519 | 86 | } |
520 | 1.16k | if (mappings[j].unicode_value > kUnicodeUpperLimit) { |
521 | 44 | return Error("Invalid Unicode value (%04X > %04X) in mapping %d of variation selector %d", mappings[j].unicode_value, kUnicodeUpperLimit, j, i); |
522 | 44 | } |
523 | 1.12k | if (last_unicode_value && |
524 | 192 | mappings[j].unicode_value <= last_unicode_value) { |
525 | 28 | return Error("Out of order Unicode value (%04X <= %04X) in mapping %d of variation selector %d", mappings[j].unicode_value, last_unicode_value, j, i); |
526 | 28 | } |
527 | 1.09k | last_unicode_value = mappings[j].unicode_value; |
528 | 1.09k | } |
529 | 1.01k | } |
530 | 1.42k | } |
531 | | |
532 | 213 | if (subtable.offset() != length) { |
533 | 22 | return Error("Bad subtable offset (%ld != %ld)", subtable.offset(), length); |
534 | 22 | } |
535 | 191 | this->subtable_0_5_14_length = subtable.offset(); |
536 | 191 | return true; |
537 | 213 | } |
538 | | |
539 | 485 | bool OpenTypeCMAP::Parse100(const uint8_t *data, size_t length) { |
540 | | // Mac Roman table |
541 | 485 | ots::Buffer subtable(data, length); |
542 | | |
543 | 485 | if (!subtable.Skip(4)) { |
544 | 105 | return Error("Bad cmap subtable"); |
545 | 105 | } |
546 | 380 | uint16_t language = 0; |
547 | 380 | if (!subtable.ReadU16(&language)) { |
548 | 42 | return Error("Can't read language in cmap subtable"); |
549 | 42 | } |
550 | 338 | if (language) { |
551 | | // simsun.ttf has non-zero language id. |
552 | 164 | Warning("language id should be zero: %u", language); |
553 | 164 | } |
554 | | |
555 | 338 | this->subtable_1_0_0.reserve(kFormat0ArraySize); |
556 | 55.6k | for (size_t i = 0; i < kFormat0ArraySize; ++i) { |
557 | 55.4k | uint8_t glyph_id = 0; |
558 | 55.4k | if (!subtable.ReadU8(&glyph_id)) { |
559 | 175 | return Error("Can't read glyph id at array[%ld] in cmap subtable", i); |
560 | 175 | } |
561 | 55.3k | this->subtable_1_0_0.push_back(glyph_id); |
562 | 55.3k | } |
563 | | |
564 | 163 | return true; |
565 | 338 | } |
566 | | |
567 | 94.6k | bool OpenTypeCMAP::Parse(const uint8_t *data, size_t length) { |
568 | 94.6k | Buffer table(data, length); |
569 | | |
570 | 94.6k | uint16_t version = 0; |
571 | 94.6k | uint16_t num_tables = 0; |
572 | 94.6k | if (!table.ReadU16(&version) || |
573 | 94.5k | !table.ReadU16(&num_tables)) { |
574 | 87 | return Error("Can't read structure of cmap"); |
575 | 87 | } |
576 | | |
577 | 94.5k | if (version != 0) { |
578 | 73 | return Error("Non zero cmap version (%d)", version); |
579 | 73 | } |
580 | 94.4k | if (!num_tables) { |
581 | 75 | return Error("No subtables in cmap!"); |
582 | 75 | } |
583 | | |
584 | 94.3k | std::vector<CMAPSubtableHeader> subtable_headers; |
585 | | |
586 | | // read the subtable headers |
587 | 94.3k | subtable_headers.reserve(num_tables); |
588 | 261k | for (unsigned i = 0; i < num_tables; ++i) { |
589 | 167k | CMAPSubtableHeader subt; |
590 | | |
591 | 167k | if (!table.ReadU16(&subt.platform) || |
592 | 167k | !table.ReadU16(&subt.encoding) || |
593 | 167k | !table.ReadU32(&subt.offset)) { |
594 | 131 | return Error("Can't read subtable information cmap subtable %d", i); |
595 | 131 | } |
596 | | |
597 | 167k | subtable_headers.push_back(subt); |
598 | 167k | } |
599 | | |
600 | 94.2k | const size_t data_offset = table.offset(); |
601 | | |
602 | | // make sure that all the offsets are valid. |
603 | 256k | for (unsigned i = 0; i < num_tables; ++i) { |
604 | 162k | if (subtable_headers[i].offset > 1024 * 1024 * 1024) { |
605 | 47 | return Error("Bad subtable offset in cmap subtable %d", i); |
606 | 47 | } |
607 | 162k | if (subtable_headers[i].offset < data_offset || |
608 | 162k | subtable_headers[i].offset >= length) { |
609 | 127 | return Error("Bad subtable offset (%d) in cmap subtable %d", subtable_headers[i].offset, i); |
610 | 127 | } |
611 | 162k | } |
612 | | |
613 | | // the format of the table is the first couple of bytes in the table. The |
614 | | // length of the table is stored in a format-specific way. |
615 | 255k | for (unsigned i = 0; i < num_tables; ++i) { |
616 | 161k | table.set_offset(subtable_headers[i].offset); |
617 | 161k | if (!table.ReadU16(&subtable_headers[i].format)) { |
618 | 36 | return Error("Can't read cmap subtable header format %d", i); |
619 | 36 | } |
620 | | |
621 | 161k | uint16_t len = 0; |
622 | 161k | uint16_t lang = 0; |
623 | 161k | switch (subtable_headers[i].format) { |
624 | 16.0k | case 0: |
625 | 100k | case 4: |
626 | 100k | if (!table.ReadU16(&len)) { |
627 | 40 | return Error("Can't read cmap subtable %d length", i); |
628 | 40 | } |
629 | 100k | if (!table.ReadU16(&lang)) { |
630 | 42 | return Error("Can't read cmap subtable %d language", i); |
631 | 42 | } |
632 | 100k | subtable_headers[i].length = len; |
633 | 100k | subtable_headers[i].language = lang; |
634 | 100k | break; |
635 | 3.84k | case 12: |
636 | 6.22k | case 13: |
637 | 6.22k | if (!table.Skip(2)) { |
638 | 37 | return Error("Bad cmap subtable %d structure", i); |
639 | 37 | } |
640 | 6.18k | if (!table.ReadU32(&subtable_headers[i].length)) { |
641 | 39 | return Error("Can read cmap subtable %d length", i); |
642 | 39 | } |
643 | 6.14k | if (!table.ReadU32(&subtable_headers[i].language)) { |
644 | 7 | return Error("Can't read cmap subtable %d language", i); |
645 | 7 | } |
646 | 6.14k | break; |
647 | 6.14k | case 14: |
648 | 1.96k | if (!table.ReadU32(&subtable_headers[i].length)) { |
649 | 34 | return Error("Can't read cmap subtable %d length", i); |
650 | 34 | } |
651 | 1.93k | subtable_headers[i].language = 0; |
652 | 1.93k | break; |
653 | 53.3k | default: |
654 | 53.3k | subtable_headers[i].length = 0; |
655 | 53.3k | subtable_headers[i].language = 0; |
656 | 53.3k | break; |
657 | 161k | } |
658 | 161k | } |
659 | | |
660 | | // check if the table is sorted first by platform ID, then by encoding ID. |
661 | 160k | for (unsigned i = 1; i < num_tables; ++i) { |
662 | 66.9k | if (subtable_headers[i - 1].platform > subtable_headers[i].platform || |
663 | 41.4k | (subtable_headers[i - 1].platform == subtable_headers[i].platform && |
664 | 11.2k | (subtable_headers[i - 1].encoding > subtable_headers[i].encoding || |
665 | 7.75k | (subtable_headers[i - 1].encoding == subtable_headers[i].encoding && |
666 | 1.15k | subtable_headers[i - 1].language > subtable_headers[i].language)))) |
667 | 29.2k | Warning("subtable %d with platform ID %d, encoding ID %d, language ID %d " |
668 | 29.2k | "following subtable with platform ID %d, encoding ID %d, language ID %d", |
669 | 29.2k | i, |
670 | 29.2k | subtable_headers[i].platform, |
671 | 29.2k | subtable_headers[i].encoding, |
672 | 29.2k | subtable_headers[i].language, |
673 | 29.2k | subtable_headers[i - 1].platform, |
674 | 29.2k | subtable_headers[i - 1].encoding, |
675 | 29.2k | subtable_headers[i - 1].language); |
676 | 66.9k | } |
677 | | |
678 | | // Now, verify that all the lengths are sane |
679 | 253k | for (unsigned i = 0; i < num_tables; ++i) { |
680 | 159k | if (!subtable_headers[i].length) continue; |
681 | 97.3k | if (subtable_headers[i].length > 1024 * 1024 * 1024) { |
682 | 92 | return Error("Bad cmap subtable %d length", i); |
683 | 92 | } |
684 | | // We know that both the offset and length are < 1GB, so the following |
685 | | // addition doesn't overflow |
686 | 97.2k | const uint32_t end_byte |
687 | 97.2k | = subtable_headers[i].offset + subtable_headers[i].length; |
688 | 97.2k | if (end_byte > length) { |
689 | 189 | return Error("Over long cmap subtable %d @ %d for %d", i, subtable_headers[i].offset, subtable_headers[i].length); |
690 | 189 | } |
691 | 97.2k | } |
692 | | |
693 | | // check that the cmap subtables are not overlapping. |
694 | 93.5k | std::set<std::pair<uint32_t, uint32_t> > uniq_checker; |
695 | 93.5k | std::vector<std::pair<uint32_t, uint8_t> > overlap_checker; |
696 | 252k | for (unsigned i = 0; i < num_tables; ++i) { |
697 | 159k | const uint32_t end_byte |
698 | 159k | = subtable_headers[i].offset + subtable_headers[i].length; |
699 | | |
700 | 159k | if (!uniq_checker.insert(std::make_pair(subtable_headers[i].offset, |
701 | 159k | end_byte)).second) { |
702 | | // Sometimes Unicode table and MS table share exactly the same data. |
703 | | // We'll allow this. |
704 | 13.6k | continue; |
705 | 13.6k | } |
706 | 145k | overlap_checker.push_back( |
707 | 145k | std::make_pair(subtable_headers[i].offset, |
708 | 145k | static_cast<uint8_t>(1) /* start */)); |
709 | 145k | overlap_checker.push_back( |
710 | 145k | std::make_pair(end_byte, static_cast<uint8_t>(0) /* end */)); |
711 | 145k | } |
712 | 93.5k | std::sort(overlap_checker.begin(), overlap_checker.end()); |
713 | 93.5k | int overlap_count = 0; |
714 | 342k | for (unsigned i = 0; i < overlap_checker.size(); ++i) { |
715 | 249k | overlap_count += (overlap_checker[i].second ? 1 : -1); |
716 | 249k | if (overlap_count > 1) { |
717 | 906 | return Error("Excessive overlap count %d", overlap_count); |
718 | 906 | } |
719 | 249k | } |
720 | | |
721 | | // we grab the number of glyphs in the file from the maxp table to make sure |
722 | | // that the character map isn't referencing anything beyound this range. |
723 | 92.6k | OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>( |
724 | 92.6k | GetFont()->GetTypedTable(OTS_TAG_MAXP)); |
725 | 92.6k | if (!maxp) { |
726 | 0 | return Error("No maxp table in font! Needed by cmap."); |
727 | 0 | } |
728 | 92.6k | const uint16_t num_glyphs = maxp->num_glyphs; |
729 | | |
730 | | // We only support a subset of the possible character map tables. Microsoft |
731 | | // 'strongly recommends' that everyone supports the Unicode BMP table with |
732 | | // the UCS-4 table for non-BMP glyphs. We'll pass the following subtables: |
733 | | // Platform ID Encoding ID Format |
734 | | // 0 0 4 (Unicode Default) |
735 | | // 0 1 4 (Unicode 1.1) |
736 | | // 0 3 4 (Unicode BMP) |
737 | | // 0 3 12 (Unicode UCS-4) |
738 | | // 0 5 14 (Unicode Variation Sequences) |
739 | | // 1 0 0 (Mac Roman) |
740 | | // 3 0 4 (MS Symbol) |
741 | | // 3 1 4 (MS Unicode BMP) |
742 | | // 3 10 12 (MS Unicode UCS-4) |
743 | | // 3 10 13 (MS UCS-4 Fallback mapping) |
744 | | // |
745 | | // Note: |
746 | | // * 0-0-4 and 0-1-4 tables are (usually) written as a 3-1-4 table. If 3-1-4 table |
747 | | // also exists, the 0-0-4 or 0-1-4 tables are ignored. |
748 | | // * Unlike 0-0-4 table, 0-3-4 table is written as a 0-3-4 table. |
749 | | // Some fonts which include 0-5-14 table seems to be required 0-3-4 |
750 | | // table. The 0-3-4 table will be wriiten even if 3-1-4 table also exists. |
751 | | // * 0-3-12 table is written as a 3-10-12 table. If 3-10-12 table also |
752 | | // exists, the 0-3-12 table is ignored. |
753 | | // |
754 | | |
755 | 209k | for (unsigned i = 0; i < num_tables; ++i) { |
756 | 121k | if (subtable_headers[i].platform == 0) { |
757 | | // Unicode platform |
758 | | |
759 | 83.8k | if ((subtable_headers[i].encoding == 0 || subtable_headers[i].encoding == 1) && |
760 | 66.0k | (subtable_headers[i].format == 4)) { |
761 | | // parse and output the 0-0-4 and 0-1-4 tables as 3-1-4 table. Sometimes the 0-0-4 |
762 | | // table actually points to MS symbol data and thus should be parsed as |
763 | | // 3-0-4 table (e.g., marqueem.ttf and quixotic.ttf). This error will be |
764 | | // recovered in ots_cmap_serialise(). |
765 | 65.4k | if (!ParseFormat4(3, 1, data + subtable_headers[i].offset, |
766 | 65.4k | subtable_headers[i].length, num_glyphs)) { |
767 | 490 | return Error("Failed to parse format 4 cmap subtable %d", i); |
768 | 490 | } |
769 | 65.4k | } else if ((subtable_headers[i].encoding == 3) && |
770 | 10.5k | (subtable_headers[i].format == 4)) { |
771 | | // parse and output the 0-3-4 table as 0-3-4 table. |
772 | 8.55k | if (!ParseFormat4(0, 3, data + subtable_headers[i].offset, |
773 | 8.55k | subtable_headers[i].length, num_glyphs)) { |
774 | 182 | return Error("Failed to parse format 4 cmap subtable %d", i); |
775 | 182 | } |
776 | 9.90k | } else if ((subtable_headers[i].encoding == 3 || |
777 | 7.91k | subtable_headers[i].encoding == 4) && |
778 | 4.10k | (subtable_headers[i].format == 12)) { |
779 | | // parse and output the 0-3-12 or 0-4-12 tables as 3-10-12 table. |
780 | 1.31k | if (!Parse31012(data + subtable_headers[i].offset, |
781 | 1.31k | subtable_headers[i].length, num_glyphs)) { |
782 | 462 | return Error("Failed to parse format 12 cmap subtable %d", i); |
783 | 462 | } |
784 | 8.59k | } else if ((subtable_headers[i].encoding == 5) && |
785 | 1.64k | (subtable_headers[i].format == 14)) { |
786 | 1.39k | if (!Parse0514(data + subtable_headers[i].offset, |
787 | 1.39k | subtable_headers[i].length)) { |
788 | 1.20k | return Error("Failed to parse format 14 cmap subtable %d", i); |
789 | 1.20k | } |
790 | 1.39k | } |
791 | 83.8k | } else if (subtable_headers[i].platform == 1) { |
792 | | // Mac platform |
793 | | |
794 | 2.30k | if ((subtable_headers[i].encoding == 0) && |
795 | 1.35k | (subtable_headers[i].format == 0)) { |
796 | | // parse and output the 1-0-0 table. |
797 | 485 | if (!Parse100(data + subtable_headers[i].offset, |
798 | 485 | subtable_headers[i].length)) { |
799 | 322 | return OTS_FAILURE(); |
800 | 322 | } |
801 | 485 | } |
802 | 35.0k | } else if (subtable_headers[i].platform == 3) { |
803 | | // MS platform |
804 | | |
805 | 12.0k | switch (subtable_headers[i].encoding) { |
806 | 1.98k | case 0: |
807 | 7.64k | case 1: |
808 | 7.64k | if (subtable_headers[i].format == 4) { |
809 | | // parse 3-0-4 or 3-1-4 table. |
810 | 5.50k | if (!ParseFormat4(subtable_headers[i].platform, |
811 | 5.50k | subtable_headers[i].encoding, |
812 | 5.50k | data + subtable_headers[i].offset, |
813 | 5.50k | subtable_headers[i].length, num_glyphs)) { |
814 | 562 | return OTS_FAILURE(); |
815 | 562 | } |
816 | 5.50k | } |
817 | 7.08k | break; |
818 | 7.08k | case 10: |
819 | 2.61k | if (subtable_headers[i].format == 12) { |
820 | 1.15k | this->subtable_3_10_12.clear(); |
821 | 1.15k | if (!Parse31012(data + subtable_headers[i].offset, |
822 | 1.15k | subtable_headers[i].length, num_glyphs)) { |
823 | 163 | return OTS_FAILURE(); |
824 | 163 | } |
825 | 1.46k | } else if (subtable_headers[i].format == 13) { |
826 | 729 | this->subtable_3_10_13.clear(); |
827 | 729 | if (!Parse31013(data + subtable_headers[i].offset, |
828 | 729 | subtable_headers[i].length, num_glyphs)) { |
829 | 610 | return OTS_FAILURE(); |
830 | 610 | } |
831 | 729 | } |
832 | 1.83k | break; |
833 | 12.0k | } |
834 | 12.0k | } |
835 | 121k | } |
836 | | |
837 | 88.6k | return true; |
838 | 92.6k | } |
839 | | |
840 | 47.0k | bool OpenTypeCMAP::Serialize(OTSStream *out) { |
841 | 47.0k | const bool have_034 = this->subtable_0_3_4_data != NULL; |
842 | 47.0k | const bool have_0514 = this->subtable_0_5_14.size() != 0; |
843 | 47.0k | const bool have_100 = this->subtable_1_0_0.size() != 0; |
844 | 47.0k | const bool have_304 = this->subtable_3_0_4_data != NULL; |
845 | | // MS Symbol and MS Unicode tables should not co-exist. |
846 | | // See the comment above in 0-0-4 parser. |
847 | 47.0k | const bool have_314 = (!have_304) && this->subtable_3_1_4_data; |
848 | 47.0k | const bool have_31012 = this->subtable_3_10_12.size() != 0; |
849 | 47.0k | const bool have_31013 = this->subtable_3_10_13.size() != 0; |
850 | 47.0k | const uint16_t num_subtables = static_cast<uint16_t>(have_034) + |
851 | 47.0k | static_cast<uint16_t>(have_0514) + |
852 | 47.0k | static_cast<uint16_t>(have_100) + |
853 | 47.0k | static_cast<uint16_t>(have_304) + |
854 | 47.0k | static_cast<uint16_t>(have_314) + |
855 | 47.0k | static_cast<uint16_t>(have_31012) + |
856 | 47.0k | static_cast<uint16_t>(have_31013); |
857 | 47.0k | const off_t table_start = out->Tell(); |
858 | | |
859 | | // Some fonts don't have 3-0-4 MS Symbol nor 3-1-4 Unicode BMP tables |
860 | | // (e.g., old fonts for Mac). We don't support them. |
861 | 47.0k | if (!have_304 && !have_314 && !have_034 && !have_31012 && !have_31013) { |
862 | 744 | return Error("no supported subtables were found"); |
863 | 744 | } |
864 | | |
865 | 46.2k | if (!out->WriteU16(0) || |
866 | 46.2k | !out->WriteU16(num_subtables)) { |
867 | 0 | return OTS_FAILURE(); |
868 | 0 | } |
869 | | |
870 | 46.2k | const off_t record_offset = out->Tell(); |
871 | 46.2k | if (!out->Pad(num_subtables * 8)) { |
872 | 0 | return OTS_FAILURE(); |
873 | 0 | } |
874 | | |
875 | 46.2k | const off_t offset_034 = out->Tell(); |
876 | 46.2k | if (have_034) { |
877 | 4.34k | if (!out->Write(this->subtable_0_3_4_data, |
878 | 4.34k | this->subtable_0_3_4_length)) { |
879 | 0 | return OTS_FAILURE(); |
880 | 0 | } |
881 | 4.34k | } |
882 | | |
883 | 46.2k | const off_t offset_0514 = out->Tell(); |
884 | 46.2k | if (have_0514) { |
885 | 82 | const std::vector<ots::OpenTypeCMAPSubtableVSRecord> &records |
886 | 82 | = this->subtable_0_5_14; |
887 | 82 | const unsigned num_records = records.size(); |
888 | 82 | if (!out->WriteU16(14) || |
889 | 82 | !out->WriteU32(this->subtable_0_5_14_length) || |
890 | 82 | !out->WriteU32(num_records)) { |
891 | 0 | return OTS_FAILURE(); |
892 | 0 | } |
893 | 333 | for (unsigned i = 0; i < num_records; ++i) { |
894 | 251 | if (!out->WriteU24(records[i].var_selector) || |
895 | 251 | !out->WriteU32(records[i].default_offset) || |
896 | 251 | !out->WriteU32(records[i].non_default_offset)) { |
897 | 0 | return OTS_FAILURE(); |
898 | 0 | } |
899 | 251 | } |
900 | 333 | for (unsigned i = 0; i < num_records; ++i) { |
901 | 251 | if (records[i].default_offset) { |
902 | 61 | const std::vector<ots::OpenTypeCMAPSubtableVSRange> &ranges |
903 | 61 | = records[i].ranges; |
904 | 61 | const unsigned num_ranges = ranges.size(); |
905 | 61 | if (!out->Seek(records[i].default_offset + offset_0514) || |
906 | 61 | !out->WriteU32(num_ranges)) { |
907 | 0 | return OTS_FAILURE(); |
908 | 0 | } |
909 | 124 | for (unsigned j = 0; j < num_ranges; ++j) { |
910 | 63 | if (!out->WriteU24(ranges[j].unicode_value) || |
911 | 63 | !out->WriteU8(ranges[j].additional_count)) { |
912 | 0 | return OTS_FAILURE(); |
913 | 0 | } |
914 | 63 | } |
915 | 61 | } |
916 | 251 | if (records[i].non_default_offset) { |
917 | 208 | const std::vector<ots::OpenTypeCMAPSubtableVSMapping> &mappings |
918 | 208 | = records[i].mappings; |
919 | 208 | const unsigned num_mappings = mappings.size(); |
920 | 208 | if (!out->Seek(records[i].non_default_offset + offset_0514) || |
921 | 208 | !out->WriteU32(num_mappings)) { |
922 | 0 | return OTS_FAILURE(); |
923 | 0 | } |
924 | 491 | for (unsigned j = 0; j < num_mappings; ++j) { |
925 | 283 | if (!out->WriteU24(mappings[j].unicode_value) || |
926 | 283 | !out->WriteU16(mappings[j].glyph_id)) { |
927 | 0 | return OTS_FAILURE(); |
928 | 0 | } |
929 | 283 | } |
930 | 208 | } |
931 | 251 | } |
932 | 82 | } |
933 | | |
934 | 46.2k | const off_t offset_100 = out->Tell(); |
935 | 46.2k | if (have_100) { |
936 | 35 | if (!out->WriteU16(0) || // format |
937 | 35 | !out->WriteU16(6 + kFormat0ArraySize) || // length |
938 | 35 | !out->WriteU16(0)) { // language |
939 | 0 | return OTS_FAILURE(); |
940 | 0 | } |
941 | 35 | if (!out->Write(&(this->subtable_1_0_0[0]), kFormat0ArraySize)) { |
942 | 0 | return OTS_FAILURE(); |
943 | 0 | } |
944 | 35 | } |
945 | | |
946 | 46.2k | const off_t offset_304 = out->Tell(); |
947 | 46.2k | if (have_304) { |
948 | 968 | if (!out->Write(this->subtable_3_0_4_data, |
949 | 968 | this->subtable_3_0_4_length)) { |
950 | 0 | return OTS_FAILURE(); |
951 | 0 | } |
952 | 968 | } |
953 | | |
954 | 46.2k | const off_t offset_314 = out->Tell(); |
955 | 46.2k | if (have_314) { |
956 | 41.0k | if (!out->Write(this->subtable_3_1_4_data, |
957 | 41.0k | this->subtable_3_1_4_length)) { |
958 | 0 | return OTS_FAILURE(); |
959 | 0 | } |
960 | 41.0k | } |
961 | | |
962 | 46.2k | const off_t offset_31012 = out->Tell(); |
963 | 46.2k | if (have_31012) { |
964 | 160 | std::vector<OpenTypeCMAPSubtableRange> &groups |
965 | 160 | = this->subtable_3_10_12; |
966 | 160 | const unsigned num_groups = groups.size(); |
967 | 160 | if (!out->WriteU16(12) || |
968 | 160 | !out->WriteU16(0) || |
969 | 160 | !out->WriteU32(num_groups * 12 + 16) || |
970 | 160 | !out->WriteU32(0) || |
971 | 160 | !out->WriteU32(num_groups)) { |
972 | 0 | return OTS_FAILURE(); |
973 | 0 | } |
974 | | |
975 | 5.17k | for (unsigned i = 0; i < num_groups; ++i) { |
976 | 5.01k | if (!out->WriteU32(groups[i].start_range) || |
977 | 5.01k | !out->WriteU32(groups[i].end_range) || |
978 | 5.01k | !out->WriteU32(groups[i].start_glyph_id)) { |
979 | 0 | return OTS_FAILURE(); |
980 | 0 | } |
981 | 5.01k | } |
982 | 160 | } |
983 | | |
984 | 46.2k | const off_t offset_31013 = out->Tell(); |
985 | 46.2k | if (have_31013) { |
986 | 82 | std::vector<OpenTypeCMAPSubtableRange> &groups |
987 | 82 | = this->subtable_3_10_13; |
988 | 82 | const unsigned num_groups = groups.size(); |
989 | 82 | if (!out->WriteU16(13) || |
990 | 82 | !out->WriteU16(0) || |
991 | 82 | !out->WriteU32(num_groups * 12 + 16) || |
992 | 82 | !out->WriteU32(0) || |
993 | 82 | !out->WriteU32(num_groups)) { |
994 | 0 | return OTS_FAILURE(); |
995 | 0 | } |
996 | | |
997 | 496 | for (unsigned i = 0; i < num_groups; ++i) { |
998 | 414 | if (!out->WriteU32(groups[i].start_range) || |
999 | 414 | !out->WriteU32(groups[i].end_range) || |
1000 | 414 | !out->WriteU32(groups[i].start_glyph_id)) { |
1001 | 0 | return OTS_FAILURE(); |
1002 | 0 | } |
1003 | 414 | } |
1004 | 82 | } |
1005 | | |
1006 | 46.2k | const off_t table_end = out->Tell(); |
1007 | | |
1008 | | // Now seek back and write the table of offsets |
1009 | 46.2k | if (!out->Seek(record_offset)) { |
1010 | 0 | return OTS_FAILURE(); |
1011 | 0 | } |
1012 | | |
1013 | 46.2k | if (have_034) { |
1014 | 4.34k | if (!out->WriteU16(0) || |
1015 | 4.34k | !out->WriteU16(3) || |
1016 | 4.34k | !out->WriteU32(offset_034 - table_start)) { |
1017 | 0 | return OTS_FAILURE(); |
1018 | 0 | } |
1019 | 4.34k | } |
1020 | | |
1021 | 46.2k | if (have_0514) { |
1022 | 82 | if (!out->WriteU16(0) || |
1023 | 82 | !out->WriteU16(5) || |
1024 | 82 | !out->WriteU32(offset_0514 - table_start)) { |
1025 | 0 | return OTS_FAILURE(); |
1026 | 0 | } |
1027 | 82 | } |
1028 | | |
1029 | 46.2k | if (have_100) { |
1030 | 35 | if (!out->WriteU16(1) || |
1031 | 35 | !out->WriteU16(0) || |
1032 | 35 | !out->WriteU32(offset_100 - table_start)) { |
1033 | 0 | return OTS_FAILURE(); |
1034 | 0 | } |
1035 | 35 | } |
1036 | | |
1037 | 46.2k | if (have_304) { |
1038 | 968 | if (!out->WriteU16(3) || |
1039 | 968 | !out->WriteU16(0) || |
1040 | 968 | !out->WriteU32(offset_304 - table_start)) { |
1041 | 0 | return OTS_FAILURE(); |
1042 | 0 | } |
1043 | 968 | } |
1044 | | |
1045 | 46.2k | if (have_314) { |
1046 | 41.0k | if (!out->WriteU16(3) || |
1047 | 41.0k | !out->WriteU16(1) || |
1048 | 41.0k | !out->WriteU32(offset_314 - table_start)) { |
1049 | 0 | return OTS_FAILURE(); |
1050 | 0 | } |
1051 | 41.0k | } |
1052 | | |
1053 | 46.2k | if (have_31012) { |
1054 | 160 | if (!out->WriteU16(3) || |
1055 | 160 | !out->WriteU16(10) || |
1056 | 160 | !out->WriteU32(offset_31012 - table_start)) { |
1057 | 0 | return OTS_FAILURE(); |
1058 | 0 | } |
1059 | 160 | } |
1060 | | |
1061 | 46.2k | if (have_31013) { |
1062 | 82 | if (!out->WriteU16(3) || |
1063 | 82 | !out->WriteU16(10) || |
1064 | 82 | !out->WriteU32(offset_31013 - table_start)) { |
1065 | 0 | return OTS_FAILURE(); |
1066 | 0 | } |
1067 | 82 | } |
1068 | | |
1069 | 46.2k | if (!out->Seek(table_end)) { |
1070 | 0 | return OTS_FAILURE(); |
1071 | 0 | } |
1072 | | |
1073 | 46.2k | return true; |
1074 | 46.2k | } |
1075 | | |
1076 | | } // namespace ots |