Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(359)

Side by Side Diff: src/math.cc

Issue 139563002: [OTS] Add support for the MATH table. (Closed) Base URL: https://chromium.googlesource.com/external/ots.git@master
Patch Set: Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« ots-common.gypi ('K') | « src/layout.cc ('k') | src/math_.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« ots-common.gypi ('K') | « src/layout.cc ('k') | src/math_.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698