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