• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ParseMathValueRecord(ots::Buffer * subtable,const uint8_t * data,const size_t length)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 
ParseMathConstantsTable(const uint8_t * data,size_t length)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 
ParseMathValueRecordSequenceForGlyphs(ots::Buffer * subtable,const uint8_t * data,const size_t length,const uint16_t num_glyphs)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 
ParseMathItalicsCorrectionInfoTable(const uint8_t * data,size_t length,const uint16_t num_glyphs)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 
ParseMathTopAccentAttachmentTable(const uint8_t * data,size_t length,const uint16_t num_glyphs)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 
ParseMathKernTable(const uint8_t * data,size_t length)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 
ParseMathKernInfoTable(const uint8_t * data,size_t length,const uint16_t num_glyphs)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 
ParseMathGlyphInfoTable(const uint8_t * data,size_t length,const uint16_t num_glyphs)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,
312             length - offset_math_italics_correction_info,
313             num_glyphs)) {
314       return OTS_FAILURE();
315     }
316   }
317   if (offset_math_top_accent_attachment) {
318     if (offset_math_top_accent_attachment >= length ||
319         offset_math_top_accent_attachment < kMathGlyphInfoHeaderSize ||
320         !ParseMathTopAccentAttachmentTable(data +
321                                            offset_math_top_accent_attachment,
322                                            length -
323                                            offset_math_top_accent_attachment,
324                                            num_glyphs)) {
325       return OTS_FAILURE();
326     }
327   }
328   if (offset_extended_shaped_coverage) {
329     if (offset_extended_shaped_coverage >= length ||
330         offset_extended_shaped_coverage < kMathGlyphInfoHeaderSize ||
331         !ots::ParseCoverageTable(data + offset_extended_shaped_coverage,
332                                  length - offset_extended_shaped_coverage,
333                                  num_glyphs)) {
334       return OTS_FAILURE();
335     }
336   }
337   if (offset_math_kern_info) {
338     if (offset_math_kern_info >= length ||
339         offset_math_kern_info < kMathGlyphInfoHeaderSize ||
340         !ParseMathKernInfoTable(data + offset_math_kern_info,
341                                 length - offset_math_kern_info, num_glyphs)) {
342       return OTS_FAILURE();
343     }
344   }
345 
346   return true;
347 }
348 
ParseGlyphAssemblyTable(const uint8_t * data,size_t length,const uint16_t num_glyphs)349 bool ParseGlyphAssemblyTable(const uint8_t *data,
350                              size_t length, const uint16_t num_glyphs) {
351   ots::Buffer subtable(data, length);
352 
353   // Check the header.
354   uint16_t part_count = 0;
355   if (!ParseMathValueRecord(&subtable, data, length) ||
356       !subtable.ReadU16(&part_count)) {
357     return OTS_FAILURE();
358   }
359 
360   const unsigned sequence_end = kMathValueRecordSize +
361     static_cast<unsigned>(2) + part_count * kGlyphPartRecordSize;
362   if (sequence_end > std::numeric_limits<uint16_t>::max()) {
363     return OTS_FAILURE();
364   }
365 
366   // Check the sequence of GlyphPartRecord.
367   for (unsigned i = 0; i < part_count; ++i) {
368     uint16_t glyph = 0;
369     uint16_t part_flags = 0;
370     if (!subtable.ReadU16(&glyph) ||
371         !subtable.Skip(2 * 3) ||
372         !subtable.ReadU16(&part_flags)) {
373       return OTS_FAILURE();
374     }
375     if (glyph >= num_glyphs) {
376       OTS_WARNING("bad glyph ID: %u", glyph);
377       return OTS_FAILURE();
378     }
379     if (part_flags & ~0x00000001) {
380       OTS_WARNING("unknown part flag: %u", part_flags);
381       return OTS_FAILURE();
382     }
383   }
384 
385   return true;
386 }
387 
ParseMathGlyphConstructionTable(const uint8_t * data,size_t length,const uint16_t num_glyphs)388 bool ParseMathGlyphConstructionTable(const uint8_t *data,
389                                      size_t length, const uint16_t num_glyphs) {
390   ots::Buffer subtable(data, length);
391 
392   // Check the header.
393   uint16_t offset_glyph_assembly = 0;
394   uint16_t variant_count = 0;
395   if (!subtable.ReadU16(&offset_glyph_assembly) ||
396       !subtable.ReadU16(&variant_count)) {
397     return OTS_FAILURE();
398   }
399 
400   const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
401     variant_count * 2 * 2;
402   if (sequence_end > std::numeric_limits<uint16_t>::max()) {
403     return OTS_FAILURE();
404   }
405 
406   // Check the GlyphAssembly offset.
407   if (offset_glyph_assembly) {
408     if (offset_glyph_assembly >= length ||
409         offset_glyph_assembly < sequence_end) {
410       return OTS_FAILURE();
411     }
412     if (!ParseGlyphAssemblyTable(data + offset_glyph_assembly,
413                                  length - offset_glyph_assembly, num_glyphs)) {
414       return OTS_FAILURE();
415     }
416   }
417 
418   // Check the sequence of MathGlyphVariantRecord.
419   for (unsigned i = 0; i < variant_count; ++i) {
420     uint16_t glyph = 0;
421     if (!subtable.ReadU16(&glyph) ||
422         !subtable.Skip(2)) {
423       return OTS_FAILURE();
424     }
425     if (glyph >= num_glyphs) {
426       OTS_WARNING("bad glyph ID: %u", glyph);
427       return OTS_FAILURE();
428     }
429   }
430 
431   return true;
432 }
433 
ParseMathGlyphConstructionSequence(ots::Buffer * subtable,const uint8_t * data,size_t length,const uint16_t num_glyphs,uint16_t offset_coverage,uint16_t glyph_count,const unsigned sequence_end)434 bool ParseMathGlyphConstructionSequence(ots::Buffer* subtable,
435                                         const uint8_t *data,
436                                         size_t length,
437                                         const uint16_t num_glyphs,
438                                         uint16_t offset_coverage,
439                                         uint16_t glyph_count,
440                                         const unsigned sequence_end) {
441   // Check coverage table.
442   if (offset_coverage < sequence_end || offset_coverage >= length) {
443     return OTS_FAILURE();
444   }
445   if (!ots::ParseCoverageTable(data + offset_coverage,
446                                length - offset_coverage,
447                                num_glyphs, glyph_count)) {
448     return OTS_FAILURE();
449   }
450 
451   // Check sequence of MathGlyphConstruction.
452   for (unsigned i = 0; i < glyph_count; ++i) {
453       uint16_t offset_glyph_construction = 0;
454       if (!subtable->ReadU16(&offset_glyph_construction)) {
455         return OTS_FAILURE();
456       }
457       if (offset_glyph_construction < sequence_end ||
458           offset_glyph_construction >= length ||
459           !ParseMathGlyphConstructionTable(data + offset_glyph_construction,
460                                            length - offset_glyph_construction,
461                                            num_glyphs)) {
462         return OTS_FAILURE();
463       }
464   }
465 
466   return true;
467 }
468 
ParseMathVariantsTable(const uint8_t * data,size_t length,const uint16_t num_glyphs)469 bool ParseMathVariantsTable(const uint8_t *data,
470                             size_t length, const uint16_t num_glyphs) {
471   ots::Buffer subtable(data, length);
472 
473   // Check the header.
474   uint16_t offset_vert_glyph_coverage = 0;
475   uint16_t offset_horiz_glyph_coverage = 0;
476   uint16_t vert_glyph_count = 0;
477   uint16_t horiz_glyph_count = 0;
478   if (!subtable.Skip(2) ||  // MinConnectorOverlap
479       !subtable.ReadU16(&offset_vert_glyph_coverage) ||
480       !subtable.ReadU16(&offset_horiz_glyph_coverage) ||
481       !subtable.ReadU16(&vert_glyph_count) ||
482       !subtable.ReadU16(&horiz_glyph_count)) {
483     return OTS_FAILURE();
484   }
485 
486   const unsigned sequence_end = 5 * 2 + vert_glyph_count * 2 +
487     horiz_glyph_count * 2;
488   if (sequence_end > std::numeric_limits<uint16_t>::max()) {
489     return OTS_FAILURE();
490   }
491 
492   if (!ParseMathGlyphConstructionSequence(&subtable, data, length, num_glyphs,
493                                           offset_vert_glyph_coverage,
494                                           vert_glyph_count,
495                                           sequence_end) ||
496       !ParseMathGlyphConstructionSequence(&subtable, data, length, num_glyphs,
497                                           offset_horiz_glyph_coverage,
498                                           horiz_glyph_count,
499                                           sequence_end)) {
500     return OTS_FAILURE();
501   }
502 
503   return true;
504 }
505 
506 }  // namespace
507 
508 #define DROP_THIS_TABLE \
509   do { file->math->data = 0; file->math->length = 0; } while (0)
510 
511 namespace ots {
512 
ots_math_parse(OpenTypeFile * file,const uint8_t * data,size_t length)513 bool ots_math_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
514   // Grab the number of glyphs in the file from the maxp table to check
515   // GlyphIDs in MATH table.
516   if (!file->maxp) {
517     return OTS_FAILURE();
518   }
519   const uint16_t num_glyphs = file->maxp->num_glyphs;
520 
521   Buffer table(data, length);
522 
523   OpenTypeMATH* math = new OpenTypeMATH;
524   file->math = math;
525 
526   uint32_t version = 0;
527   if (!table.ReadU32(&version)) {
528     return OTS_FAILURE();
529   }
530   if (version != 0x00010000) {
531     OTS_WARNING("bad MATH version");
532     DROP_THIS_TABLE;
533     return true;
534   }
535 
536   uint16_t offset_math_constants = 0;
537   uint16_t offset_math_glyph_info = 0;
538   uint16_t offset_math_variants = 0;
539   if (!table.ReadU16(&offset_math_constants) ||
540       !table.ReadU16(&offset_math_glyph_info) ||
541       !table.ReadU16(&offset_math_variants)) {
542     return OTS_FAILURE();
543   }
544 
545   if (offset_math_constants >= length ||
546       offset_math_constants < kMathHeaderSize ||
547       offset_math_glyph_info >= length ||
548       offset_math_glyph_info < kMathHeaderSize ||
549       offset_math_variants >= length ||
550       offset_math_variants < kMathHeaderSize) {
551     OTS_WARNING("bad offset in MATH header");
552     DROP_THIS_TABLE;
553     return true;
554   }
555 
556   if (!ParseMathConstantsTable(data + offset_math_constants,
557                                length - offset_math_constants)) {
558     DROP_THIS_TABLE;
559     return true;
560   }
561   if (!ParseMathGlyphInfoTable(data + offset_math_glyph_info,
562                                length - offset_math_glyph_info, num_glyphs)) {
563     DROP_THIS_TABLE;
564     return true;
565   }
566   if (!ParseMathVariantsTable(data + offset_math_variants,
567                               length - offset_math_variants, num_glyphs)) {
568     DROP_THIS_TABLE;
569     return true;
570   }
571 
572   math->data = data;
573   math->length = length;
574   return true;
575 }
576 
ots_math_should_serialise(OpenTypeFile * file)577 bool ots_math_should_serialise(OpenTypeFile *file) {
578   return file->math != NULL && file->math->data != NULL;
579 }
580 
ots_math_serialise(OTSStream * out,OpenTypeFile * file)581 bool ots_math_serialise(OTSStream *out, OpenTypeFile *file) {
582   if (!out->Write(file->math->data, file->math->length)) {
583     return OTS_FAILURE();
584   }
585 
586   return true;
587 }
588 
ots_math_free(OpenTypeFile * file)589 void ots_math_free(OpenTypeFile *file) {
590   delete file->math;
591 }
592 
593 }  // namespace ots
594 
595