OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2014 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 // We use an underscore to avoid confusion with the standard math.h library. | |
6 #include "math_.h" | |
7 | |
8 #include <vector> | |
9 | |
10 #include "layout.h" | |
11 #include "maxp.h" | |
12 | |
13 // MATH - The MATH Table | |
14 // The specification is not yet public but has been submitted to the MPEG group | |
15 // in response to the 'Call for Proposals for ISO/IEC 14496-22 "Open Font | |
16 // Format" Color Font Technology and MATH layout support'. Meanwhile, you can | |
17 // contact Microsoft's engineer Murray Sargent to obtain a copy. | |
18 | |
19 namespace { | |
20 | |
21 // The size of MATH header. | |
22 // Version | |
23 // MathConstants | |
24 // MathGlyphInfo | |
25 // MathVariants | |
26 const unsigned kMathHeaderSize = 4 + 3 * 2; | |
27 | |
28 // The size of the MathGlyphInfo header. | |
29 // MathItalicsCorrectionInfo | |
30 // MathTopAccentAttachment | |
31 // ExtendedShapeCoverage | |
32 // MathKernInfo | |
33 const unsigned kMathGlyphInfoHeaderSize = 4 * 2; | |
34 | |
35 // The size of the MathValueRecord. | |
36 // Value | |
37 // DeviceTable | |
38 const unsigned kMathValueRecordSize = 2 * 2; | |
39 | |
40 // The size of the GlyphPartRecord. | |
41 // glyph | |
42 // StartConnectorLength | |
43 // EndConnectorLength | |
44 // FullAdvance | |
45 // PartFlags | |
46 const unsigned kGlyphPartRecordSize = 5 * 2; | |
47 | |
48 // Shared Table: MathValueRecord | |
49 | |
50 bool ParseMathValueRecord(ots::Buffer* subtable, const uint8_t *data, | |
51 const size_t length) { | |
52 // Check the Value field. | |
53 if (!subtable->Skip(2)) { | |
54 return OTS_FAILURE(); | |
55 } | |
56 | |
57 // Check the offset to device table. | |
58 uint16_t offset = 0; | |
59 if (!subtable->ReadU16(&offset)) { | |
60 return OTS_FAILURE(); | |
61 } | |
62 if (offset) { | |
63 if (offset >= length) { | |
64 return OTS_FAILURE(); | |
65 } | |
66 if (!ots::ParseDeviceTable(data + offset, length - offset)) { | |
67 return OTS_FAILURE(); | |
68 } | |
69 } | |
70 | |
71 return true; | |
72 } | |
73 | |
74 bool ParseMathConstantsTable(const uint8_t *data, size_t length) { | |
75 ots::Buffer subtable(data, length); | |
76 | |
77 // Part 1: int16 or uint16 constants. | |
78 // ScriptPercentScaleDown | |
79 // ScriptScriptPercentScaleDown | |
80 // DelimitedSubFormulaMinHeight | |
81 // DisplayOperatorMinHeight | |
82 if (!subtable.Skip(4 * 2)) { | |
83 return OTS_FAILURE(); | |
84 } | |
85 | |
86 // Part 2: MathValueRecord constants. | |
87 // MathLeading | |
88 // AxisHeight | |
89 // AccentBaseHeight | |
90 // FlattenedAccentBaseHeight | |
91 // SubscriptShiftDown | |
92 // SubscriptTopMax | |
93 // SubscriptBaselineDropMin | |
94 // SuperscriptShiftUp | |
95 // SuperscriptShiftUpCramped | |
96 // SuperscriptBottomMin | |
97 // | |
98 // SuperscriptBaselineDropMax | |
99 // SubSuperscriptGapMin | |
100 // SuperscriptBottomMaxWithSubscript | |
101 // SpaceAfterScript | |
102 // UpperLimitGapMin | |
103 // UpperLimitBaselineRiseMin | |
104 // LowerLimitGapMin | |
105 // LowerLimitBaselineDropMin | |
106 // StackTopShiftUp | |
107 // StackTopDisplayStyleShiftUp | |
108 // | |
109 // StackBottomShiftDown | |
110 // StackBottomDisplayStyleShiftDown | |
111 // StackGapMin | |
112 // StackDisplayStyleGapMin | |
113 // StretchStackTopShiftUp | |
114 // StretchStackBottomShiftDown | |
115 // StretchStackGapAboveMin | |
116 // StretchStackGapBelowMin | |
117 // FractionNumeratorShiftUp | |
118 // FractionNumeratorDisplayStyleShiftUp | |
119 // | |
120 // FractionDenominatorShiftDown | |
121 // FractionDenominatorDisplayStyleShiftDown | |
122 // FractionNumeratorGapMin | |
123 // FractionNumDisplayStyleGapMin | |
124 // FractionRuleThickness | |
125 // FractionDenominatorGapMin | |
126 // FractionDenomDisplayStyleGapMin | |
127 // SkewedFractionHorizontalGap | |
128 // SkewedFractionVerticalGap | |
129 // OverbarVerticalGap | |
130 // | |
131 // OverbarRuleThickness | |
132 // OverbarExtraAscender | |
133 // UnderbarVerticalGap | |
134 // UnderbarRuleThickness | |
135 // UnderbarExtraDescender | |
136 // RadicalVerticalGap | |
137 // RadicalDisplayStyleVerticalGap | |
138 // RadicalRuleThickness | |
139 // RadicalExtraAscender | |
140 // RadicalKernBeforeDegree | |
141 // | |
142 // RadicalKernAfterDegree | |
143 for (unsigned i = 0; i < static_cast<unsigned>(51); ++i) { | |
144 if (!ParseMathValueRecord(&subtable, data, length)) { | |
145 return OTS_FAILURE(); | |
146 } | |
147 } | |
148 | |
149 // Part 3: uint16 constant | |
150 // RadicalDegreeBottomRaisePercent | |
151 if (!subtable.Skip(2)) { | |
152 return OTS_FAILURE(); | |
153 } | |
154 | |
155 return true; | |
156 } | |
157 | |
158 bool ParseMathValueRecordSequenceForGlyphs(ots::Buffer* subtable, | |
159 const uint8_t *data, | |
160 const size_t length, | |
161 const uint16_t num_glyphs) { | |
162 // Check the header. | |
163 uint16_t offset_coverage = 0; | |
164 uint16_t sequence_count = 0; | |
165 if (!subtable->ReadU16(&offset_coverage) || | |
166 !subtable->ReadU16(&sequence_count)) { | |
167 return OTS_FAILURE(); | |
168 } | |
169 | |
170 const unsigned sequence_end = static_cast<unsigned>(2 * 2) + | |
171 sequence_count * kMathValueRecordSize; | |
172 if (sequence_end > std::numeric_limits<uint16_t>::max()) { | |
173 return OTS_FAILURE(); | |
174 } | |
175 | |
176 // Check coverage table. | |
177 if (offset_coverage < sequence_end || offset_coverage >= length) { | |
178 return OTS_FAILURE(); | |
179 } | |
180 if (!ots::ParseCoverageTable(data + offset_coverage, | |
181 length - offset_coverage, | |
182 num_glyphs, sequence_count)) { | |
183 return OTS_FAILURE(); | |
184 } | |
185 | |
186 // Check sequence. | |
187 for (unsigned i = 0; i < sequence_count; ++i) { | |
188 if (!ParseMathValueRecord(subtable, data, length)) { | |
189 return OTS_FAILURE(); | |
190 } | |
191 } | |
192 | |
193 return true; | |
194 } | |
195 | |
196 bool ParseMathItalicsCorrectionInfoTable(const uint8_t *data, | |
197 size_t length, | |
198 const uint16_t num_glyphs) { | |
199 ots::Buffer subtable(data, length); | |
200 return ParseMathValueRecordSequenceForGlyphs(&subtable, data, length, | |
201 num_glyphs); | |
202 } | |
203 | |
204 bool ParseMathTopAccentAttachmentTable(const uint8_t *data, | |
205 size_t length, | |
206 const uint16_t num_glyphs) { | |
207 ots::Buffer subtable(data, length); | |
208 return ParseMathValueRecordSequenceForGlyphs(&subtable, data, length, | |
209 num_glyphs); | |
210 } | |
211 | |
212 bool ParseMathKernTable(const uint8_t *data, size_t length) { | |
213 ots::Buffer subtable(data, length); | |
214 | |
215 // Check the Height count. | |
216 uint16_t height_count = 0; | |
217 if (!subtable.ReadU16(&height_count)) { | |
218 return OTS_FAILURE(); | |
219 } | |
220 | |
221 // Check the Correction Heights. | |
222 for (unsigned i = 0; i < height_count; ++i) { | |
223 if (!ParseMathValueRecord(&subtable, data, length)) { | |
224 return OTS_FAILURE(); | |
225 } | |
226 } | |
227 | |
228 // Check the Kern Values. | |
229 for (unsigned i = 0; i <= height_count; ++i) { | |
230 if (!ParseMathValueRecord(&subtable, data, length)) { | |
231 return OTS_FAILURE(); | |
232 } | |
233 } | |
234 | |
235 return true; | |
236 } | |
237 | |
238 bool ParseMathKernInfoTable(const uint8_t *data, size_t length, | |
239 const uint16_t num_glyphs) { | |
240 ots::Buffer subtable(data, length); | |
241 | |
242 // Check the header. | |
243 uint16_t offset_coverage = 0; | |
244 uint16_t sequence_count = 0; | |
245 if (!subtable.ReadU16(&offset_coverage) || | |
246 !subtable.ReadU16(&sequence_count)) { | |
247 return OTS_FAILURE(); | |
248 } | |
249 | |
250 const unsigned sequence_end = static_cast<unsigned>(2 * 2) + | |
251 sequence_count * 4 * 2; | |
252 if (sequence_end > std::numeric_limits<uint16_t>::max()) { | |
253 return OTS_FAILURE(); | |
254 } | |
255 | |
256 // Check coverage table. | |
257 if (offset_coverage < sequence_end || offset_coverage >= length) { | |
258 return OTS_FAILURE(); | |
259 } | |
260 if (!ots::ParseCoverageTable(data + offset_coverage, length - offset_coverage, | |
261 num_glyphs, sequence_count)) { | |
262 return OTS_FAILURE(); | |
263 } | |
264 | |
265 // Check sequence of MathKernInfoRecord | |
266 for (unsigned i = 0; i < sequence_count; ++i) { | |
267 // Check TopRigh, TopLeft, BottomRight and BottomLeft Math Kern. | |
Kunihiko Sakamoto
2014/01/17 10:51:36
TopRigh -> TopRight
| |
268 for (unsigned j = 0; j < 4; ++j) { | |
269 uint16_t offset_math_kern = 0; | |
270 if (!subtable.ReadU16(&offset_math_kern)) { | |
271 return OTS_FAILURE(); | |
272 } | |
273 if (offset_math_kern) { | |
274 if (offset_math_kern < sequence_end || offset_math_kern >= length || | |
275 !ParseMathKernTable(data + offset_math_kern, | |
276 length - offset_math_kern)) { | |
277 return OTS_FAILURE(); | |
278 } | |
279 } | |
280 } | |
281 } | |
282 | |
283 return true; | |
284 } | |
285 | |
286 bool ParseMathGlyphInfoTable(const uint8_t *data, size_t length, | |
287 const uint16_t num_glyphs) { | |
288 ots::Buffer subtable(data, length); | |
289 | |
290 // Check Header. | |
291 uint16_t offset_math_italics_correction_info = 0; | |
292 uint16_t offset_math_top_accent_attachment = 0; | |
293 uint16_t offset_extended_shaped_coverage = 0; | |
294 uint16_t offset_math_kern_info = 0; | |
295 if (!subtable.ReadU16(&offset_math_italics_correction_info) || | |
296 !subtable.ReadU16(&offset_math_top_accent_attachment) || | |
297 !subtable.ReadU16(&offset_extended_shaped_coverage) || | |
298 !subtable.ReadU16(&offset_math_kern_info)) { | |
299 return OTS_FAILURE(); | |
300 } | |
301 | |
302 // Check subtables. | |
303 // The specification does not say whether the offsets for | |
304 // MathItalicsCorrectionInfo, MathTopAccentAttachment and MathKernInfo may | |
305 // be NULL, but that's the case in some fonts (e.g STIX) so we accept that. | |
306 if (offset_math_italics_correction_info) { | |
307 if (offset_math_italics_correction_info >= length || | |
308 offset_math_italics_correction_info < kMathGlyphInfoHeaderSize || | |
309 !ParseMathItalicsCorrectionInfoTable(data + offset_math_italics_correcti on_info, | |
Kunihiko Sakamoto
2014/01/17 10:51:36
Please keep <=80 characters per line.
Break after
| |
310 length - offset_math_italics_correc tion_info, | |
311 num_glyphs)) { | |
312 return OTS_FAILURE(); | |
313 } | |
314 } | |
315 if (offset_math_top_accent_attachment) { | |
316 if (offset_math_top_accent_attachment >= length || | |
317 offset_math_top_accent_attachment < kMathGlyphInfoHeaderSize || | |
318 !ParseMathTopAccentAttachmentTable(data + | |
319 offset_math_top_accent_attachment, | |
320 length - | |
321 offset_math_top_accent_attachment, | |
322 num_glyphs)) { | |
323 return OTS_FAILURE(); | |
324 } | |
325 } | |
326 if (offset_extended_shaped_coverage) { | |
327 if (offset_extended_shaped_coverage >= length || | |
328 offset_extended_shaped_coverage < kMathGlyphInfoHeaderSize || | |
329 !ots::ParseCoverageTable(data + offset_extended_shaped_coverage, | |
330 length - offset_extended_shaped_coverage, | |
331 num_glyphs)) { | |
332 return OTS_FAILURE(); | |
333 } | |
334 } | |
335 if (offset_math_kern_info) { | |
336 if (offset_math_kern_info >= length || | |
337 offset_math_kern_info < kMathGlyphInfoHeaderSize || | |
338 !ParseMathKernInfoTable(data + offset_math_kern_info, | |
339 length - offset_math_kern_info, num_glyphs)) { | |
340 return OTS_FAILURE(); | |
341 } | |
342 } | |
343 | |
344 return true; | |
345 } | |
346 | |
347 bool ParseGlyphAssemblyTable(const uint8_t *data, | |
348 size_t length, const uint16_t num_glyphs) { | |
Kunihiko Sakamoto
2014/01/17 10:51:36
Please fix indent.
| |
349 ots::Buffer subtable(data, length); | |
350 | |
351 // Check the header. | |
352 uint16_t part_count = 0; | |
353 if (!ParseMathValueRecord(&subtable, data, length) || | |
354 !subtable.ReadU16(&part_count)) { | |
355 return OTS_FAILURE(); | |
356 } | |
357 | |
358 const unsigned sequence_end = kMathValueRecordSize + | |
359 static_cast<unsigned>(2) + part_count * kGlyphPartRecordSize; | |
360 if (sequence_end > std::numeric_limits<uint16_t>::max()) { | |
361 return OTS_FAILURE(); | |
362 } | |
363 | |
364 // Check the sequence of GlyphPartRecord. | |
365 for (unsigned i = 0; i < part_count; ++i) { | |
366 uint16_t glyph = 0; | |
367 uint16_t part_flags = 0; | |
368 if (!subtable.ReadU16(&glyph) || | |
369 !subtable.Skip(2 * 3) || | |
370 !subtable.ReadU16(&part_flags)) { | |
371 return OTS_FAILURE(); | |
372 } | |
373 if (glyph > num_glyphs) { | |
Kunihiko Sakamoto
2014/01/17 10:51:36
should be >=
| |
374 OTS_WARNING("bad glyph ID: %u", glyph); | |
375 return OTS_FAILURE(); | |
376 } | |
377 if (part_flags & ~0x00000001) { | |
378 OTS_WARNING("unknown part flag: %u", part_flags); | |
379 return OTS_FAILURE(); | |
380 } | |
381 } | |
382 | |
383 return true; | |
384 } | |
385 | |
386 bool ParseMathGlyphConstructionTable(const uint8_t *data, | |
387 size_t length, const uint16_t num_glyphs) { | |
388 ots::Buffer subtable(data, length); | |
389 | |
390 // Check the header. | |
391 uint16_t offset_glyph_assembly = 0; | |
392 uint16_t variant_count = 0; | |
393 if (!subtable.ReadU16(&offset_glyph_assembly) || | |
394 !subtable.ReadU16(&variant_count)) { | |
395 return OTS_FAILURE(); | |
396 } | |
397 | |
398 const unsigned sequence_end = static_cast<unsigned>(2 * 2) + | |
399 variant_count * 2 * 2; | |
400 if (sequence_end > std::numeric_limits<uint16_t>::max()) { | |
401 return OTS_FAILURE(); | |
402 } | |
403 | |
404 // Check the GlyphAssembly offset. | |
405 if (offset_glyph_assembly) { | |
406 if (offset_glyph_assembly >= length || | |
407 offset_glyph_assembly < sequence_end) { | |
408 return OTS_FAILURE(); | |
409 } | |
410 if (!ParseGlyphAssemblyTable(data + offset_glyph_assembly, | |
411 length - offset_glyph_assembly, num_glyphs)) { | |
412 return OTS_FAILURE(); | |
413 } | |
414 } | |
415 | |
416 // Check the sequence of MathGlyphVariantRecord. | |
417 for (unsigned i = 0; i < variant_count; ++i) { | |
418 uint16_t glyph = 0; | |
419 if (!subtable.ReadU16(&glyph) || | |
420 !subtable.Skip(2)) { | |
421 return OTS_FAILURE(); | |
422 } | |
423 if (glyph > num_glyphs) { | |
Kunihiko Sakamoto
2014/01/17 10:51:36
should be >=
| |
424 OTS_WARNING("bad glyph ID: %u", glyph); | |
425 return OTS_FAILURE(); | |
426 } | |
427 } | |
428 | |
429 return true; | |
430 } | |
431 | |
432 bool ParseMathGlyphConstructionSequence(ots::Buffer* subtable, | |
433 const uint8_t *data, | |
434 size_t length, | |
435 const uint16_t num_glyphs, | |
436 uint16_t offset_coverage, | |
437 uint16_t glyph_count, | |
438 const unsigned sequence_end) { | |
439 // Check coverage table. | |
440 if (offset_coverage < sequence_end || offset_coverage >= length) { | |
441 return OTS_FAILURE(); | |
442 } | |
443 if (!ots::ParseCoverageTable(data + offset_coverage, | |
444 length - offset_coverage, | |
445 num_glyphs, glyph_count)) { | |
446 return OTS_FAILURE(); | |
447 } | |
448 | |
449 // Check sequence of MathGlyphConstruction. | |
450 for (unsigned i = 0; i < glyph_count; ++i) { | |
451 uint16_t offset_glyph_construction = 0; | |
452 if (!subtable->ReadU16(&offset_glyph_construction)) { | |
453 return OTS_FAILURE(); | |
454 } | |
455 if (offset_glyph_construction < sequence_end || | |
456 offset_glyph_construction >= length || | |
457 !ParseMathGlyphConstructionTable(data + offset_glyph_construction, | |
458 length - offset_glyph_construction, | |
459 num_glyphs)) { | |
460 return OTS_FAILURE(); | |
461 } | |
462 } | |
463 | |
464 return true; | |
465 } | |
466 | |
467 bool ParseMathVariantsTable(const uint8_t *data, | |
468 size_t length, const uint16_t num_glyphs) { | |
469 ots::Buffer subtable(data, length); | |
470 | |
471 // Check the header. | |
472 uint16_t offset_vert_glyph_coverage = 0; | |
473 uint16_t offset_horiz_glyph_coverage = 0; | |
474 uint16_t vert_glyph_count = 0; | |
475 uint16_t horiz_glyph_count = 0; | |
476 if (!subtable.Skip(2) || // MinConnectorOverlap | |
Kunihiko Sakamoto
2014/01/17 10:51:36
Two spaces between code and comment
| |
477 !subtable.ReadU16(&offset_vert_glyph_coverage) || | |
478 !subtable.ReadU16(&offset_horiz_glyph_coverage) || | |
479 !subtable.ReadU16(&vert_glyph_count) || | |
480 !subtable.ReadU16(&horiz_glyph_count)) { | |
481 return OTS_FAILURE(); | |
482 } | |
483 | |
484 const unsigned sequence_end = 5 * 2 + vert_glyph_count * 2 + | |
485 horiz_glyph_count * 2; | |
486 if (sequence_end > std::numeric_limits<uint16_t>::max()) { | |
487 return OTS_FAILURE(); | |
488 } | |
489 | |
490 if (!ParseMathGlyphConstructionSequence(&subtable, data, length, num_glyphs, | |
491 offset_vert_glyph_coverage, | |
492 vert_glyph_count, | |
493 sequence_end) || | |
494 !ParseMathGlyphConstructionSequence(&subtable, data, length, num_glyphs, | |
495 offset_horiz_glyph_coverage, | |
496 horiz_glyph_count, | |
497 sequence_end)) { | |
498 return OTS_FAILURE(); | |
499 } | |
500 | |
501 return true; | |
502 } | |
503 | |
504 } // namespace | |
505 | |
506 #define DROP_THIS_TABLE \ | |
507 do { file->math->data = 0; file->math->length = 0; } while (0) | |
508 | |
509 namespace ots { | |
510 | |
511 bool ots_math_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { | |
512 // Grab the number of glyphs in the file from the maxp table to check | |
513 // GlyphIDs in MATH table. | |
514 if (!file->maxp) { | |
515 return OTS_FAILURE(); | |
516 } | |
517 const uint16_t num_glyphs = file->maxp->num_glyphs; | |
518 | |
519 Buffer table(data, length); | |
520 | |
521 OpenTypeMATH *math = new OpenTypeMATH; | |
522 file->math = math; | |
523 | |
524 uint32_t version = 0; | |
525 if (!table.ReadU32(&version)) { | |
526 return OTS_FAILURE(); | |
527 } | |
528 if (version != 0x00010000) { | |
529 OTS_WARNING("bad MATH version"); | |
530 DROP_THIS_TABLE; | |
531 return true; | |
532 } | |
533 | |
534 uint16_t offset_math_constants = 0; | |
535 uint16_t offset_math_glyph_info = 0; | |
536 uint16_t offset_math_variants = 0; | |
537 if (!table.ReadU16(&offset_math_constants) || | |
538 !table.ReadU16(&offset_math_glyph_info) || | |
539 !table.ReadU16(&offset_math_variants)) { | |
540 return OTS_FAILURE(); | |
541 } | |
542 | |
543 if (offset_math_constants >= length || | |
544 offset_math_constants < kMathHeaderSize || | |
545 offset_math_glyph_info >= length || | |
546 offset_math_glyph_info < kMathHeaderSize || | |
547 offset_math_variants >= length || | |
548 offset_math_variants < kMathHeaderSize) { | |
549 return OTS_FAILURE(); | |
Kunihiko Sakamoto
2014/01/17 10:51:36
This makes the entire font fail.
Maybe drop the ta
| |
550 } | |
551 | |
552 if (!ParseMathConstantsTable(data + offset_math_constants, | |
553 length - offset_math_constants)) { | |
554 DROP_THIS_TABLE; | |
555 return true; | |
556 } | |
557 if (!ParseMathGlyphInfoTable(data + offset_math_glyph_info, | |
558 length - offset_math_glyph_info, num_glyphs)) { | |
559 DROP_THIS_TABLE; | |
560 return true; | |
561 } | |
562 if (!ParseMathVariantsTable(data + offset_math_variants, | |
563 length - offset_math_variants, num_glyphs)) { | |
564 DROP_THIS_TABLE; | |
565 return true; | |
566 } | |
567 | |
568 math->data = data; | |
569 math->length = length; | |
570 return true; | |
571 } | |
572 | |
573 bool ots_math_should_serialise(OpenTypeFile *file) { | |
574 return file->math != NULL && file->math->data != NULL; | |
575 } | |
576 | |
577 bool ots_math_serialise(OTSStream *out, OpenTypeFile *file) { | |
578 if (!out->Write(file->math->data, file->math->length)) { | |
579 return OTS_FAILURE(); | |
580 } | |
581 | |
582 return true; | |
583 } | |
584 | |
585 void ots_math_free(OpenTypeFile *file) { | |
586 delete file->math; | |
587 } | |
588 | |
589 } // namespace ots | |
590 | |
OLD | NEW |