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

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: Add <limits> header. 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
« no previous file with comments | « 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 <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
OLDNEW
« no previous file with comments | « src/layout.cc ('k') | src/math_.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698