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