colr.cc (36735B)
1 // Copyright (c) 2022 The OTS 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 #include "colr.h" 6 7 #include "cpal.h" 8 #include "maxp.h" 9 #include "variations.h" 10 11 #include <map> 12 #include <set> 13 #include <vector> 14 15 // COLR - Color Table 16 // http://www.microsoft.com/typography/otspec/colr.htm 17 18 #define TABLE_NAME "COLR" 19 20 namespace { 21 22 // Some typedefs so that our local variables will more closely parallel the spec. 23 typedef int16_t F2DOT14; // 2.14 fixed-point 24 typedef int32_t Fixed; // 16.16 fixed-point 25 typedef int16_t FWORD; // A 16-bit integer value in font design units 26 typedef uint16_t UFWORD; 27 typedef uint32_t VarIdxBase; 28 29 constexpr F2DOT14 F2DOT14_one = 0x4000; 30 31 struct colrState 32 { 33 // We track addresses of structs that we have already seen and checked, 34 // because fonts may share these among multiple glyph descriptions. 35 // (We only do this for color lines, which may be large, depending on the 36 // number of color stops, and for paints, which may refer to an extensive 37 // sub-graph; for small records like ClipBox and Affine2x3, we just read 38 // them directly whenever encountered.) 39 std::set<const uint8_t*> colorLines; 40 std::set<const uint8_t*> varColorLines; 41 std::set<const uint8_t*> paints; 42 43 std::map<uint16_t, std::pair<const uint8_t*, size_t>> baseGlyphMap; 44 std::vector<std::pair<const uint8_t*, size_t>> layerList; 45 46 // Set of visited records, for cycle detection. 47 // We don't actually need to track every paint table here, as most of the 48 // paint-offsets that create the graph can only point forwards; 49 // only PaintColrLayers and PaintColrGlyph can cause a backward jump 50 // and hence a potential cycle. 51 std::set<const uint8_t*> visited; 52 53 uint16_t numGlyphs; // from maxp 54 uint16_t numPaletteEntries; // from CPAL 55 }; 56 57 // std::set<T>::contains isn't available until C++20, so we use this instead 58 // for better compatibility with old compilers. 59 template <typename T> 60 bool setContains(const std::set<T>& set, T item) 61 { 62 return set.find(item) != set.end(); 63 } 64 65 enum Extend : uint8_t 66 { 67 EXTEND_PAD = 0, 68 EXTEND_REPEAT = 1, 69 EXTEND_REFLECT = 2, 70 }; 71 72 bool ParseColorLine(const ots::Font* font, 73 const uint8_t* data, size_t length, 74 colrState& state, bool var) 75 { 76 auto& set = var ? state.varColorLines : state.colorLines; 77 if (setContains(set, data)) { 78 return true; 79 } 80 set.insert(data); 81 82 ots::Buffer subtable(data, length); 83 84 uint8_t extend; 85 uint16_t numColorStops; 86 87 if (!subtable.ReadU8(&extend) || 88 !subtable.ReadU16(&numColorStops)) { 89 return OTS_FAILURE_MSG("Failed to read [Var]ColorLine"); 90 } 91 92 if (extend != EXTEND_PAD && extend != EXTEND_REPEAT && extend != EXTEND_REFLECT) { 93 OTS_WARNING("Unknown color-line extend mode %u", extend); 94 } 95 96 for (auto i = 0u; i < numColorStops; ++i) { 97 F2DOT14 stopOffset; 98 uint16_t paletteIndex; 99 F2DOT14 alpha; 100 VarIdxBase varIndexBase; 101 102 if (!subtable.ReadS16(&stopOffset) || 103 !subtable.ReadU16(&paletteIndex) || 104 !subtable.ReadS16(&alpha) || 105 (var && !subtable.ReadU32(&varIndexBase))) { 106 return OTS_FAILURE_MSG("Failed to read [Var]ColorStop"); 107 } 108 109 if (paletteIndex >= state.numPaletteEntries && paletteIndex != 0xffffu) { 110 return OTS_FAILURE_MSG("Invalid palette index %u in color stop", paletteIndex); 111 } 112 113 if (alpha < 0 || alpha > F2DOT14_one) { 114 OTS_WARNING("Alpha value outside valid range 0.0 - 1.0"); 115 } 116 } 117 118 return true; 119 } 120 121 // Composition modes 122 enum CompositeMode : uint8_t 123 { 124 // Porter-Duff modes 125 // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators 126 COMPOSITE_CLEAR = 0, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_clear 127 COMPOSITE_SRC = 1, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_src 128 COMPOSITE_DEST = 2, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dst 129 COMPOSITE_SRC_OVER = 3, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcover 130 COMPOSITE_DEST_OVER = 4, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstover 131 COMPOSITE_SRC_IN = 5, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcin 132 COMPOSITE_DEST_IN = 6, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstin 133 COMPOSITE_SRC_OUT = 7, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcout 134 COMPOSITE_DEST_OUT = 8, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstout 135 COMPOSITE_SRC_ATOP = 9, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcatop 136 COMPOSITE_DEST_ATOP = 10, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstatop 137 COMPOSITE_XOR = 11, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_xor 138 COMPOSITE_PLUS = 12, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_plus 139 140 // Blend modes 141 // https://www.w3.org/TR/compositing-1/#blending 142 COMPOSITE_SCREEN = 13, // https://www.w3.org/TR/compositing-1/#blendingscreen 143 COMPOSITE_OVERLAY = 14, // https://www.w3.org/TR/compositing-1/#blendingoverlay 144 COMPOSITE_DARKEN = 15, // https://www.w3.org/TR/compositing-1/#blendingdarken 145 COMPOSITE_LIGHTEN = 16, // https://www.w3.org/TR/compositing-1/#blendinglighten 146 COMPOSITE_COLOR_DODGE = 17, // https://www.w3.org/TR/compositing-1/#blendingcolordodge 147 COMPOSITE_COLOR_BURN = 18, // https://www.w3.org/TR/compositing-1/#blendingcolorburn 148 COMPOSITE_HARD_LIGHT = 19, // https://www.w3.org/TR/compositing-1/#blendinghardlight 149 COMPOSITE_SOFT_LIGHT = 20, // https://www.w3.org/TR/compositing-1/#blendingsoftlight 150 COMPOSITE_DIFFERENCE = 21, // https://www.w3.org/TR/compositing-1/#blendingdifference 151 COMPOSITE_EXCLUSION = 22, // https://www.w3.org/TR/compositing-1/#blendingexclusion 152 COMPOSITE_MULTIPLY = 23, // https://www.w3.org/TR/compositing-1/#blendingmultiply 153 154 // Modes that, uniquely, do not operate on components 155 // https://www.w3.org/TR/compositing-1/#blendingnonseparable 156 COMPOSITE_HSL_HUE = 24, // https://www.w3.org/TR/compositing-1/#blendinghue 157 COMPOSITE_HSL_SATURATION = 25, // https://www.w3.org/TR/compositing-1/#blendingsaturation 158 COMPOSITE_HSL_COLOR = 26, // https://www.w3.org/TR/compositing-1/#blendingcolor 159 COMPOSITE_HSL_LUMINOSITY = 27, // https://www.w3.org/TR/compositing-1/#blendingluminosity 160 }; 161 162 bool ParseAffine(const ots::Font* font, 163 const uint8_t* data, size_t length, 164 bool var) 165 { 166 ots::Buffer subtable(data, length); 167 168 Fixed xx, yx, xy, yy, dx, dy; 169 VarIdxBase varIndexBase; 170 171 if (!subtable.ReadS32(&xx) || 172 !subtable.ReadS32(&yx) || 173 !subtable.ReadS32(&xy) || 174 !subtable.ReadS32(&yy) || 175 !subtable.ReadS32(&dx) || 176 !subtable.ReadS32(&dy) || 177 (var && !subtable.ReadU32(&varIndexBase))) { 178 return OTS_FAILURE_MSG("Failed to read [Var]Affine2x3"); 179 } 180 181 return true; 182 } 183 184 // Paint-record dispatch function that reads the format byte and then dispatches 185 // to one of the record-specific helpers. 186 bool ParsePaint(const ots::Font* font, const uint8_t* data, size_t length, colrState& state); 187 188 // All these paint record parsers start with Skip(1) to ignore the format field, 189 // which the caller has already read in order to dispatch here. 190 191 bool ParsePaintColrLayers(const ots::Font* font, 192 const uint8_t* data, size_t length, 193 colrState& state) 194 { 195 if (setContains(state.visited, data)) { 196 #ifdef OTS_COLR_CYCLE_CHECK 197 // A cycle would imply an infinite loop during painting, unless the renderer 198 // detects and breaks it. To be safe, reject the table. 199 return OTS_FAILURE_MSG("Cycle detected in PaintColrLayers"); 200 #else 201 // Just issue a warning and return (as we've already checked this subgraph). 202 OTS_WARNING("Cycle detected in COLRv1 glyph paint graph (PaintColrLayers)\n"); 203 return true; 204 #endif 205 } 206 state.visited.insert(data); 207 208 ots::Buffer subtable(data, length); 209 210 uint8_t numLayers; 211 uint32_t firstLayerIndex; 212 213 if (!subtable.Skip(1) || 214 !subtable.ReadU8(&numLayers) || 215 !subtable.ReadU32(&firstLayerIndex)) { 216 return OTS_FAILURE_MSG("Failed to read PaintColrLayers record"); 217 } 218 219 if (uint64_t(firstLayerIndex) + numLayers > state.layerList.size()) { 220 return OTS_FAILURE_MSG("PaintColrLayers exceeds bounds of layer list"); 221 } 222 223 for (auto i = firstLayerIndex; i < firstLayerIndex + numLayers; ++i) { 224 auto layer = state.layerList[i]; 225 if (!ParsePaint(font, layer.first, layer.second, state)) { 226 return OTS_FAILURE_MSG("Failed to parse layer"); 227 } 228 } 229 230 state.visited.erase(data); 231 232 return true; 233 } 234 235 bool ParsePaintSolid(const ots::Font* font, 236 const uint8_t* data, size_t length, 237 colrState& state, bool var) 238 { 239 ots::Buffer subtable(data, length); 240 241 uint16_t paletteIndex; 242 F2DOT14 alpha; 243 244 if (!subtable.Skip(1) || 245 !subtable.ReadU16(&paletteIndex) || 246 !subtable.ReadS16(&alpha)) { 247 return OTS_FAILURE_MSG("Failed to read PaintSolid"); 248 } 249 250 if (paletteIndex >= state.numPaletteEntries && paletteIndex != 0xffffu) { 251 return OTS_FAILURE_MSG("Invalid palette index %u PaintSolid", paletteIndex); 252 } 253 254 if (alpha < 0 || alpha > F2DOT14_one) { 255 OTS_WARNING("Alpha value outside valid range 0.0 - 1.0"); 256 } 257 258 if (var) { 259 VarIdxBase varIndexBase; 260 if (!subtable.ReadU32(&varIndexBase)) { 261 return OTS_FAILURE_MSG("Failed to read PaintVarSolid"); 262 } 263 } 264 265 return true; 266 } 267 268 bool ParsePaintLinearGradient(const ots::Font* font, 269 const uint8_t* data, size_t length, 270 colrState& state, bool var) 271 { 272 ots::Buffer subtable(data, length); 273 274 uint32_t colorLineOffset; 275 FWORD x0, y0, x1, y1, x2, y2; 276 VarIdxBase varIndexBase; 277 278 if (!subtable.Skip(1) || 279 !subtable.ReadU24(&colorLineOffset) || 280 !subtable.ReadS16(&x0) || 281 !subtable.ReadS16(&y0) || 282 !subtable.ReadS16(&x1) || 283 !subtable.ReadS16(&y1) || 284 !subtable.ReadS16(&x2) || 285 !subtable.ReadS16(&y2) || 286 (var && !subtable.ReadU32(&varIndexBase))) { 287 return OTS_FAILURE_MSG("Failed to read Paint[Var]LinearGradient"); 288 } 289 290 if (colorLineOffset >= length) { 291 return OTS_FAILURE_MSG("ColorLine is out of bounds"); 292 } 293 294 if (!ParseColorLine(font, data + colorLineOffset, length - colorLineOffset, state, var)) { 295 return OTS_FAILURE_MSG("Failed to parse [Var]ColorLine"); 296 } 297 298 return true; 299 } 300 301 bool ParsePaintRadialGradient(const ots::Font* font, 302 const uint8_t* data, size_t length, 303 colrState& state, bool var) 304 { 305 ots::Buffer subtable(data, length); 306 307 uint32_t colorLineOffset; 308 FWORD x0, y0; 309 UFWORD radius0; 310 FWORD x1, y1; 311 UFWORD radius1; 312 VarIdxBase varIndexBase; 313 314 if (!subtable.Skip(1) || 315 !subtable.ReadU24(&colorLineOffset) || 316 !subtable.ReadS16(&x0) || 317 !subtable.ReadS16(&y0) || 318 !subtable.ReadU16(&radius0) || 319 !subtable.ReadS16(&x1) || 320 !subtable.ReadS16(&y1) || 321 !subtable.ReadU16(&radius1) || 322 (var && !subtable.ReadU32(&varIndexBase))) { 323 return OTS_FAILURE_MSG("Failed to read Paint[Var]RadialGradient"); 324 } 325 326 if (colorLineOffset >= length) { 327 return OTS_FAILURE_MSG("ColorLine is out of bounds"); 328 } 329 330 if (!ParseColorLine(font, data + colorLineOffset, length - colorLineOffset, state, var)) { 331 return OTS_FAILURE_MSG("Failed to parse [Var]ColorLine"); 332 } 333 334 return true; 335 } 336 337 bool ParsePaintSweepGradient(const ots::Font* font, 338 const uint8_t* data, size_t length, 339 colrState& state, bool var) 340 { 341 ots::Buffer subtable(data, length); 342 343 uint32_t colorLineOffset; 344 FWORD centerX, centerY; 345 F2DOT14 startAngle, endAngle; 346 VarIdxBase varIndexBase; 347 348 if (!subtable.Skip(1) || 349 !subtable.ReadU24(&colorLineOffset) || 350 !subtable.ReadS16(¢erX) || 351 !subtable.ReadS16(¢erY) || 352 !subtable.ReadS16(&startAngle) || 353 !subtable.ReadS16(&endAngle) || 354 (var && !subtable.ReadU32(&varIndexBase))) { 355 return OTS_FAILURE_MSG("Failed to read Paint[Var]SweepGradient"); 356 } 357 358 if (colorLineOffset >= length) { 359 return OTS_FAILURE_MSG("ColorLine is out of bounds"); 360 } 361 362 if (!ParseColorLine(font, data + colorLineOffset, length - colorLineOffset, state, var)) { 363 return OTS_FAILURE_MSG("Failed to parse [Var]ColorLine"); 364 } 365 366 return true; 367 } 368 369 bool ParsePaintGlyph(const ots::Font* font, 370 const uint8_t* data, size_t length, 371 colrState& state) 372 { 373 ots::Buffer subtable(data, length); 374 375 uint32_t paintOffset; 376 uint16_t glyphID; 377 378 if (!subtable.Skip(1) || 379 !subtable.ReadU24(&paintOffset) || 380 !subtable.ReadU16(&glyphID)) { 381 return OTS_FAILURE_MSG("Failed to read PaintGlyph"); 382 } 383 384 if (!paintOffset || paintOffset >= length) { 385 return OTS_FAILURE_MSG("Invalid paint offset in PaintGlyph"); 386 } 387 388 if (glyphID >= state.numGlyphs) { 389 return OTS_FAILURE_MSG("Glyph ID %u out of bounds", glyphID); 390 } 391 392 if (!ParsePaint(font, data + paintOffset, length - paintOffset, state)) { 393 return OTS_FAILURE_MSG("Failed to parse paint for PaintGlyph"); 394 } 395 396 return true; 397 } 398 399 bool ParsePaintColrGlyph(const ots::Font* font, 400 const uint8_t* data, size_t length, 401 colrState& state) 402 { 403 if (setContains(state.visited, data)) { 404 #ifdef OTS_COLR_CYCLE_CHECK 405 return OTS_FAILURE_MSG("Cycle detected in PaintColrGlyph"); 406 #else 407 OTS_WARNING("Cycle detected in COLRv1 glyph paint graph (PaintColrGlyph)\n"); 408 return true; 409 #endif 410 } 411 state.visited.insert(data); 412 413 ots::Buffer subtable(data, length); 414 415 uint16_t glyphID; 416 417 if (!subtable.Skip(1) || 418 !subtable.ReadU16(&glyphID)) { 419 return OTS_FAILURE_MSG("Failed to read PaintColrGlyph"); 420 } 421 422 auto baseGlyph = state.baseGlyphMap.find(glyphID); 423 if (baseGlyph == state.baseGlyphMap.end()) { 424 return OTS_FAILURE_MSG("Glyph ID %u not found in BaseGlyphList", glyphID); 425 } 426 427 if (!ParsePaint(font, baseGlyph->second.first, baseGlyph->second.second, state)) { 428 return OTS_FAILURE_MSG("Failed to parse referenced color glyph %u", glyphID); 429 } 430 431 state.visited.erase(data); 432 433 return true; 434 } 435 436 bool ParsePaintTransform(const ots::Font* font, 437 const uint8_t* data, size_t length, 438 colrState& state, bool var) 439 { 440 ots::Buffer subtable(data, length); 441 442 uint32_t paintOffset; 443 uint32_t transformOffset; 444 445 if (!subtable.Skip(1) || 446 !subtable.ReadU24(&paintOffset) || 447 !subtable.ReadU24(&transformOffset)) { 448 return OTS_FAILURE_MSG("Failed to read Paint[Var]Transform"); 449 } 450 451 if (!paintOffset || paintOffset >= length) { 452 return OTS_FAILURE_MSG("Invalid paint offset in Paint[Var]Transform"); 453 } 454 if (transformOffset >= length) { 455 return OTS_FAILURE_MSG("Transform offset out of bounds"); 456 } 457 458 if (!ParsePaint(font, data + paintOffset, length - paintOffset, state)) { 459 return OTS_FAILURE_MSG("Failed to parse paint for Paint[Var]Transform"); 460 } 461 462 if (!ParseAffine(font, data + transformOffset, length - transformOffset, var)) { 463 return OTS_FAILURE_MSG("Failed to parse affine transform"); 464 } 465 466 return true; 467 } 468 469 bool ParsePaintTranslate(const ots::Font* font, 470 const uint8_t* data, size_t length, 471 colrState& state, bool var) 472 { 473 ots::Buffer subtable(data, length); 474 475 uint32_t paintOffset; 476 FWORD dx, dy; 477 VarIdxBase varIndexBase; 478 479 if (!subtable.Skip(1) || 480 !subtable.ReadU24(&paintOffset) || 481 !subtable.ReadS16(&dx) || 482 !subtable.ReadS16(&dy) || 483 (var && !subtable.ReadU32(&varIndexBase))) { 484 return OTS_FAILURE_MSG("Failed to read Paint[Var]Translate"); 485 } 486 487 if (!paintOffset || paintOffset >= length) { 488 return OTS_FAILURE_MSG("Invalid paint offset in Paint[Var]Translate"); 489 } 490 491 if (!ParsePaint(font, data + paintOffset, length - paintOffset, state)) { 492 return OTS_FAILURE_MSG("Failed to parse paint for Paint[Var]Translate"); 493 } 494 495 return true; 496 } 497 498 bool ParsePaintScale(const ots::Font* font, 499 const uint8_t* data, size_t length, 500 colrState& state, 501 bool var, bool aroundCenter, bool uniform) 502 { 503 ots::Buffer subtable(data, length); 504 505 uint32_t paintOffset; 506 F2DOT14 scaleX, scaleY; 507 FWORD centerX, centerY; 508 VarIdxBase varIndexBase; 509 510 if (!subtable.Skip(1) || 511 !subtable.ReadU24(&paintOffset) || 512 !subtable.ReadS16(&scaleX) || 513 (!uniform && !subtable.ReadS16(&scaleY)) || 514 (aroundCenter && (!subtable.ReadS16(¢erX) || 515 !subtable.ReadS16(¢erY))) || 516 (var && !subtable.ReadU32(&varIndexBase))) { 517 return OTS_FAILURE_MSG("Failed to read Paint[Var]Scale[...]"); 518 } 519 520 if (!paintOffset || paintOffset >= length) { 521 return OTS_FAILURE_MSG("Invalid paint offset in Paint[Var]Scale[...]"); 522 } 523 524 if (!ParsePaint(font, data + paintOffset, length - paintOffset, state)) { 525 return OTS_FAILURE_MSG("Failed to parse paint for Paint[Var]Scale[...]"); 526 } 527 528 return true; 529 } 530 531 bool ParsePaintRotate(const ots::Font* font, 532 const uint8_t* data, size_t length, 533 colrState& state, 534 bool var, bool aroundCenter) 535 { 536 ots::Buffer subtable(data, length); 537 538 uint32_t paintOffset; 539 F2DOT14 angle; 540 FWORD centerX, centerY; 541 VarIdxBase varIndexBase; 542 543 if (!subtable.Skip(1) || 544 !subtable.ReadU24(&paintOffset) || 545 !subtable.ReadS16(&angle) || 546 (aroundCenter && (!subtable.ReadS16(¢erX) || 547 !subtable.ReadS16(¢erY))) || 548 (var && !subtable.ReadU32(&varIndexBase))) { 549 return OTS_FAILURE_MSG("Failed to read Paint[Var]Rotate[...]"); 550 } 551 552 if (!paintOffset || paintOffset >= length) { 553 return OTS_FAILURE_MSG("Invalid paint offset in Paint[Var]Rotate[...]"); 554 } 555 556 if (!ParsePaint(font, data + paintOffset, length - paintOffset, state)) { 557 return OTS_FAILURE_MSG("Failed to parse paint for Paint[Var]Rotate[...]"); 558 } 559 560 return true; 561 } 562 563 bool ParsePaintSkew(const ots::Font* font, 564 const uint8_t* data, size_t length, 565 colrState& state, 566 bool var, bool aroundCenter) 567 { 568 ots::Buffer subtable(data, length); 569 570 uint32_t paintOffset; 571 F2DOT14 xSkewAngle, ySkewAngle; 572 FWORD centerX, centerY; 573 VarIdxBase varIndexBase; 574 575 if (!subtable.Skip(1) || 576 !subtable.ReadU24(&paintOffset) || 577 !subtable.ReadS16(&xSkewAngle) || 578 !subtable.ReadS16(&ySkewAngle) || 579 (aroundCenter && (!subtable.ReadS16(¢erX) || 580 !subtable.ReadS16(¢erY))) || 581 (var && !subtable.ReadU32(&varIndexBase))) { 582 return OTS_FAILURE_MSG("Failed to read Paint[Var]Skew[...]"); 583 } 584 585 if (!paintOffset || paintOffset >= length) { 586 return OTS_FAILURE_MSG("Invalid paint offset in Paint[Var]Skew[...]"); 587 } 588 589 if (!ParsePaint(font, data + paintOffset, length - paintOffset, state)) { 590 return OTS_FAILURE_MSG("Failed to parse paint for Paint[Var]Skew[...]"); 591 } 592 593 return true; 594 } 595 596 bool ParsePaintComposite(const ots::Font* font, 597 const uint8_t* data, size_t length, 598 colrState& state) 599 { 600 ots::Buffer subtable(data, length); 601 602 uint32_t sourcePaintOffset; 603 uint8_t compositeMode; 604 uint32_t backdropPaintOffset; 605 606 if (!subtable.Skip(1) || 607 !subtable.ReadU24(&sourcePaintOffset) || 608 !subtable.ReadU8(&compositeMode) || 609 !subtable.ReadU24(&backdropPaintOffset)) { 610 return OTS_FAILURE_MSG("Failed to read PaintComposite"); 611 } 612 if (compositeMode > COMPOSITE_HSL_LUMINOSITY) { 613 OTS_WARNING("Unknown composite mode %u\n", compositeMode); 614 } 615 616 if (!sourcePaintOffset || sourcePaintOffset >= length) { 617 return OTS_FAILURE_MSG("Invalid source paint offset"); 618 } 619 if (!ParsePaint(font, data + sourcePaintOffset, length - sourcePaintOffset, state)) { 620 return OTS_FAILURE_MSG("Failed to parse source paint"); 621 } 622 623 if (!backdropPaintOffset || backdropPaintOffset >= length) { 624 return OTS_FAILURE_MSG("Invalid backdrop paint offset"); 625 } 626 if (!ParsePaint(font, data + backdropPaintOffset, length - backdropPaintOffset, state)) { 627 return OTS_FAILURE_MSG("Failed to parse backdrop paint"); 628 } 629 630 return true; 631 } 632 633 bool ParsePaint(const ots::Font* font, 634 const uint8_t* data, size_t length, 635 colrState& state) 636 { 637 if (setContains(state.paints, data)) { 638 return true; 639 } 640 641 ots::Buffer subtable(data, length); 642 643 uint8_t format; 644 645 if (!subtable.ReadU8(&format)) { 646 return OTS_FAILURE_MSG("Failed to read paint record format"); 647 } 648 649 bool ok = true; 650 switch (format) { 651 case 1: ok = ParsePaintColrLayers(font, data, length, state); break; 652 case 2: ok = ParsePaintSolid(font, data, length, state, false); break; 653 case 3: ok = ParsePaintSolid(font, data, length, state, true); break; 654 case 4: ok = ParsePaintLinearGradient(font, data, length, state, false); break; 655 case 5: ok = ParsePaintLinearGradient(font, data, length, state, true); break; 656 case 6: ok = ParsePaintRadialGradient(font, data, length, state, false); break; 657 case 7: ok = ParsePaintRadialGradient(font, data, length, state, true); break; 658 case 8: ok = ParsePaintSweepGradient(font, data, length, state, false); break; 659 case 9: ok = ParsePaintSweepGradient(font, data, length, state, true); break; 660 case 10: ok = ParsePaintGlyph(font, data, length, state); break; 661 case 11: ok = ParsePaintColrGlyph(font, data, length, state); break; 662 case 12: ok = ParsePaintTransform(font, data, length, state, false); break; 663 case 13: ok = ParsePaintTransform(font, data, length, state, true); break; 664 case 14: ok = ParsePaintTranslate(font, data, length, state, false); break; 665 case 15: ok = ParsePaintTranslate(font, data, length, state, true); break; 666 case 16: ok = ParsePaintScale(font, data, length, state, false, false, false); break; // Scale 667 case 17: ok = ParsePaintScale(font, data, length, state, true, false, false); break; // VarScale 668 case 18: ok = ParsePaintScale(font, data, length, state, false, true, false); break; // ScaleAroundCenter 669 case 19: ok = ParsePaintScale(font, data, length, state, true, true, false); break; // VarScaleAroundCenter 670 case 20: ok = ParsePaintScale(font, data, length, state, false, false, true); break; // ScaleUniform 671 case 21: ok = ParsePaintScale(font, data, length, state, true, false, true); break; // VarScaleUniform 672 case 22: ok = ParsePaintScale(font, data, length, state, false, true, true); break; // ScaleUniformAroundCenter 673 case 23: ok = ParsePaintScale(font, data, length, state, true, true, true); break; // VarScaleUniformAroundCenter 674 case 24: ok = ParsePaintRotate(font, data, length, state, false, false); break; // Rotate 675 case 25: ok = ParsePaintRotate(font, data, length, state, true, false); break; // VarRotate 676 case 26: ok = ParsePaintRotate(font, data, length, state, false, true); break; // RotateAroundCenter 677 case 27: ok = ParsePaintRotate(font, data, length, state, true, true); break; // VarRotateAroundCenter 678 case 28: ok = ParsePaintSkew(font, data, length, state, false, false); break; // Skew 679 case 29: ok = ParsePaintSkew(font, data, length, state, true, false); break; // VarSkew 680 case 30: ok = ParsePaintSkew(font, data, length, state, false, true); break; // SkewAroundCenter 681 case 31: ok = ParsePaintSkew(font, data, length, state, true, true); break; // VarSkewAroundCenter 682 case 32: ok = ParsePaintComposite(font, data, length, state); break; 683 default: 684 // Clients are supposed to ignore unknown paint types. 685 OTS_WARNING("Unknown paint type %u", format); 686 break; 687 } 688 689 state.paints.insert(data); 690 691 return ok; 692 } 693 694 #pragma pack(1) 695 struct COLRv1 696 { 697 // Version-0 fields 698 uint16_t version; 699 uint16_t numBaseGlyphRecords; 700 uint32_t baseGlyphRecordsOffset; 701 uint32_t layerRecordsOffset; 702 uint16_t numLayerRecords; 703 // Version-1 additions 704 uint32_t baseGlyphListOffset; 705 uint32_t layerListOffset; 706 uint32_t clipListOffset; 707 uint32_t varIdxMapOffset; 708 uint32_t varStoreOffset; 709 }; 710 #pragma pack() 711 712 bool ParseBaseGlyphRecords(const ots::Font* font, 713 const uint8_t* data, size_t length, 714 uint32_t numBaseGlyphRecords, 715 uint32_t numLayerRecords, 716 colrState& state) 717 { 718 ots::Buffer subtable(data, length); 719 720 int32_t prevGlyphID = -1; 721 for (auto i = 0u; i < numBaseGlyphRecords; ++i) { 722 uint16_t glyphID, 723 firstLayerIndex, 724 numLayers; 725 726 if (!subtable.ReadU16(&glyphID) || 727 !subtable.ReadU16(&firstLayerIndex) || 728 !subtable.ReadU16(&numLayers)) { 729 return OTS_FAILURE_MSG("Failed to read base glyph record"); 730 } 731 732 if (glyphID >= int32_t(state.numGlyphs)) { 733 return OTS_FAILURE_MSG("Base glyph record glyph ID %u out of bounds", glyphID); 734 } 735 736 if (int32_t(glyphID) <= prevGlyphID) { 737 return OTS_FAILURE_MSG("Base glyph record for glyph ID %u out of order", glyphID); 738 } 739 740 if (uint32_t(firstLayerIndex) + uint32_t(numLayers) > numLayerRecords) { 741 return OTS_FAILURE_MSG("Layer index out of bounds"); 742 } 743 744 prevGlyphID = glyphID; 745 } 746 747 return true; 748 } 749 750 bool ParseLayerRecords(const ots::Font* font, 751 const uint8_t* data, size_t length, 752 uint32_t numLayerRecords, 753 colrState& state) 754 { 755 ots::Buffer subtable(data, length); 756 757 for (auto i = 0u; i < numLayerRecords; ++i) { 758 uint16_t glyphID, 759 paletteIndex; 760 761 if (!subtable.ReadU16(&glyphID) || 762 !subtable.ReadU16(&paletteIndex)) { 763 return OTS_FAILURE_MSG("Failed to read layer record"); 764 } 765 766 if (glyphID >= int32_t(state.numGlyphs)) { 767 return OTS_FAILURE_MSG("Layer record glyph ID %u out of bounds", glyphID); 768 } 769 770 if (paletteIndex >= state.numPaletteEntries && paletteIndex != 0xffffu) { 771 return OTS_FAILURE_MSG("Invalid palette index %u in layer record", paletteIndex); 772 } 773 } 774 775 return true; 776 } 777 778 bool ParseBaseGlyphList(const ots::Font* font, 779 const uint8_t* data, size_t length, 780 colrState& state) 781 { 782 ots::Buffer subtable(data, length); 783 784 uint32_t numBaseGlyphPaintRecords; 785 786 if (!subtable.ReadU32(&numBaseGlyphPaintRecords)) { 787 return OTS_FAILURE_MSG("Failed to read base glyph list"); 788 } 789 790 int32_t prevGlyphID = -1; 791 // We loop over the list twice, first to collect all the glyph IDs present, 792 // and then to check they can be parsed. 793 size_t saveOffset = subtable.offset(); 794 for (auto i = 0u; i < numBaseGlyphPaintRecords; ++i) { 795 uint16_t glyphID; 796 uint32_t paintOffset; 797 798 if (!subtable.ReadU16(&glyphID) || 799 !subtable.ReadU32(&paintOffset)) { 800 return OTS_FAILURE_MSG("Failed to read base glyph list"); 801 } 802 803 if (glyphID >= int32_t(state.numGlyphs)) { 804 return OTS_FAILURE_MSG("Base glyph list glyph ID %u out of bounds", glyphID); 805 } 806 807 if (int32_t(glyphID) <= prevGlyphID) { 808 return OTS_FAILURE_MSG("Base glyph list record for glyph ID %u out of order", glyphID); 809 } 810 811 if (!paintOffset || paintOffset >= length) { 812 return OTS_FAILURE_MSG("Invalid paint offset for base glyph ID %u", glyphID); 813 } 814 815 // Record the base glyph list records so that we can follow them when processing 816 // PaintColrGlyph records. 817 state.baseGlyphMap[glyphID] = std::pair<const uint8_t*, size_t>(data + paintOffset, length - paintOffset); 818 prevGlyphID = glyphID; 819 } 820 821 subtable.set_offset(saveOffset); 822 for (auto i = 0u; i < numBaseGlyphPaintRecords; ++i) { 823 uint16_t glyphID; 824 uint32_t paintOffset; 825 826 if (!subtable.ReadU16(&glyphID) || 827 !subtable.ReadU32(&paintOffset)) { 828 return OTS_FAILURE_MSG("Failed to read base glyph list"); 829 } 830 831 if (!ParsePaint(font, data + paintOffset, length - paintOffset, state)) { 832 return OTS_FAILURE_MSG("Failed to parse paint for base glyph ID %u", glyphID); 833 } 834 835 // After each base glyph record is fully processed, the visited set should be clear; 836 // otherwise, we have a bug in the cycle-detection logic. 837 assert(state.visited.empty()); 838 } 839 840 return true; 841 } 842 843 // We call this twice: first with parsePaints = false, to just get the number of layers, 844 // and then with parsePaints = true to actually descend the paint graphs. 845 bool ParseLayerList(const ots::Font* font, 846 const uint8_t* data, size_t length, 847 colrState& state) 848 { 849 ots::Buffer subtable(data, length); 850 851 uint32_t numLayers; 852 if (!subtable.ReadU32(&numLayers)) { 853 return OTS_FAILURE_MSG("Failed to read layer list"); 854 } 855 856 for (auto i = 0u; i < numLayers; ++i) { 857 uint32_t paintOffset; 858 859 if (!subtable.ReadU32(&paintOffset)) { 860 return OTS_FAILURE_MSG("Failed to read layer list"); 861 } 862 863 if (!paintOffset || paintOffset >= length) { 864 return OTS_FAILURE_MSG("Invalid paint offset in layer list"); 865 } 866 867 state.layerList.push_back(std::pair<const uint8_t*, size_t>(data + paintOffset, length - paintOffset)); 868 } 869 870 return true; 871 } 872 873 bool ParseClipBox(const ots::Font* font, 874 const uint8_t* data, size_t length) 875 { 876 ots::Buffer subtable(data, length); 877 878 uint8_t format; 879 FWORD xMin, yMin, xMax, yMax; 880 881 if (!subtable.ReadU8(&format) || 882 !subtable.ReadS16(&xMin) || 883 !subtable.ReadS16(&yMin) || 884 !subtable.ReadS16(&xMax) || 885 !subtable.ReadS16(&yMax)) { 886 return OTS_FAILURE_MSG("Failed to read clip box"); 887 } 888 889 switch (format) { 890 case 1: 891 break; 892 case 2: { 893 uint32_t varIndexBase; 894 if (!subtable.ReadU32(&varIndexBase)) { 895 return OTS_FAILURE_MSG("Failed to read clip box"); 896 } 897 break; 898 } 899 default: 900 return OTS_FAILURE_MSG("Invalid clip box format: %u", format); 901 } 902 903 if (xMin > xMax || yMin > yMax) { 904 return OTS_FAILURE_MSG("Invalid clip box bounds"); 905 } 906 907 return true; 908 } 909 910 bool ParseClipList(const ots::Font* font, 911 const uint8_t* data, size_t length, 912 colrState& state) 913 { 914 ots::Buffer subtable(data, length); 915 916 uint8_t format; 917 uint32_t numClipRecords; 918 919 if (!subtable.ReadU8(&format) || 920 !subtable.ReadU32(&numClipRecords)) { 921 return OTS_FAILURE_MSG("Failed to read clip list"); 922 } 923 924 if (format != 1) { 925 return OTS_FAILURE_MSG("Unknown clip list format: %u", format); 926 } 927 928 int32_t prevEndGlyphID = -1; 929 for (auto i = 0u; i < numClipRecords; ++i) { 930 uint16_t startGlyphID, 931 endGlyphID; 932 uint32_t clipBoxOffset; 933 934 if (!subtable.ReadU16(&startGlyphID) || 935 !subtable.ReadU16(&endGlyphID) || 936 !subtable.ReadU24(&clipBoxOffset)) { 937 return OTS_FAILURE_MSG("Failed to read clip list"); 938 } 939 940 if (int32_t(startGlyphID) <= prevEndGlyphID || 941 endGlyphID < startGlyphID || 942 endGlyphID >= int32_t(state.numGlyphs)) { 943 return OTS_FAILURE_MSG("Bad or out-of-order glyph ID range %u-%u in clip list", startGlyphID, endGlyphID); 944 } 945 946 if (clipBoxOffset >= length) { 947 return OTS_FAILURE_MSG("Clip box offset out of bounds for glyphs %u-%u", startGlyphID, endGlyphID); 948 } 949 950 if (!ParseClipBox(font, data + clipBoxOffset, length - clipBoxOffset)) { 951 return OTS_FAILURE_MSG("Failed to parse clip box for glyphs %u-%u", startGlyphID, endGlyphID); 952 } 953 954 prevEndGlyphID = endGlyphID; 955 } 956 957 return true; 958 } 959 960 } // namespace 961 962 namespace ots { 963 964 bool OpenTypeCOLR::Parse(const uint8_t *data, size_t length) { 965 // Parsing COLR table requires |maxp->num_glyphs| and |cpal->num_palette_entries|. 966 Font *font = GetFont(); 967 Buffer table(data, length); 968 969 uint32_t headerSize = offsetof(COLRv1, baseGlyphListOffset); 970 971 // Version 0 header fields. 972 uint16_t version = 0; 973 uint16_t numBaseGlyphRecords = 0; 974 uint32_t offsetBaseGlyphRecords = 0; 975 uint32_t offsetLayerRecords = 0; 976 uint16_t numLayerRecords = 0; 977 if (!table.ReadU16(&version) || 978 !table.ReadU16(&numBaseGlyphRecords) || 979 !table.ReadU32(&offsetBaseGlyphRecords) || 980 !table.ReadU32(&offsetLayerRecords) || 981 !table.ReadU16(&numLayerRecords)) { 982 return Error("Incomplete table"); 983 } 984 985 if (version > 1) { 986 return Error("Unknown COLR table version %u", version); 987 } 988 989 // Additional header fields for Version 1. 990 uint32_t offsetBaseGlyphList = 0; 991 uint32_t offsetLayerList = 0; 992 uint32_t offsetClipList = 0; 993 uint32_t offsetVarIdxMap = 0; 994 uint32_t offsetItemVariationStore = 0; 995 996 if (version == 1) { 997 if (!table.ReadU32(&offsetBaseGlyphList) || 998 !table.ReadU32(&offsetLayerList) || 999 !table.ReadU32(&offsetClipList) || 1000 !table.ReadU32(&offsetVarIdxMap) || 1001 !table.ReadU32(&offsetItemVariationStore)) { 1002 return Error("Incomplete v.1 table"); 1003 } 1004 headerSize = sizeof(COLRv1); 1005 } 1006 1007 colrState state; 1008 1009 auto* maxp = static_cast<ots::OpenTypeMAXP*>(font->GetTypedTable(OTS_TAG_MAXP)); 1010 if (!maxp) { 1011 return OTS_FAILURE_MSG("Required maxp table missing"); 1012 } 1013 state.numGlyphs = maxp->num_glyphs; 1014 1015 auto *cpal = static_cast<ots::OpenTypeCPAL*>(font->GetTypedTable(OTS_TAG_CPAL)); 1016 if (!cpal) { 1017 return OTS_FAILURE_MSG("Required cpal table missing"); 1018 } 1019 state.numPaletteEntries = cpal->num_palette_entries; 1020 1021 if (numBaseGlyphRecords) { 1022 if (offsetBaseGlyphRecords < headerSize || offsetBaseGlyphRecords >= length) { 1023 return Error("Bad base glyph records offset in table header"); 1024 } 1025 if (!ParseBaseGlyphRecords(font, data + offsetBaseGlyphRecords, length - offsetBaseGlyphRecords, 1026 numBaseGlyphRecords, numLayerRecords, state)) { 1027 return Error("Failed to parse base glyph records"); 1028 } 1029 } 1030 1031 if (numLayerRecords) { 1032 if (offsetLayerRecords < headerSize || offsetLayerRecords >= length) { 1033 return Error("Bad layer records offset in table header"); 1034 } 1035 if (!ParseLayerRecords(font, data + offsetLayerRecords, length - offsetLayerRecords, numLayerRecords, 1036 state)) { 1037 return Error("Failed to parse layer records"); 1038 } 1039 } 1040 1041 if (offsetLayerList) { 1042 if (offsetLayerList < headerSize || offsetLayerList >= length) { 1043 return Error("Bad layer list offset in table header"); 1044 } 1045 // This reads the layer list into our state.layerList vector, but does not parse the 1046 // paint graphs within it; these will be checked when visited via PaintColrLayers. 1047 if (!ParseLayerList(font, data + offsetLayerList, length - offsetLayerList, state)) { 1048 return Error("Failed to parse layer list"); 1049 } 1050 } 1051 1052 if (offsetBaseGlyphList) { 1053 if (offsetBaseGlyphList < headerSize || offsetBaseGlyphList >= length) { 1054 return Error("Bad base glyph list offset in table header"); 1055 } 1056 // Here, we recursively check the paint graph starting at each base glyph record. 1057 if (!ParseBaseGlyphList(font, data + offsetBaseGlyphList, length - offsetBaseGlyphList, 1058 state)) { 1059 return Error("Failed to parse base glyph list"); 1060 } 1061 } 1062 1063 if (offsetClipList) { 1064 if (offsetClipList < headerSize || offsetClipList >= length) { 1065 return Error("Bad clip list offset in table header"); 1066 } 1067 if (!ParseClipList(font, data + offsetClipList, length - offsetClipList, state)) { 1068 return Error("Failed to parse clip list"); 1069 } 1070 } 1071 1072 if (offsetVarIdxMap) { 1073 if (offsetVarIdxMap < headerSize || offsetVarIdxMap >= length) { 1074 return Error("Bad delta set index offset in table header"); 1075 } 1076 if (!ParseDeltaSetIndexMap(font, data + offsetVarIdxMap, length - offsetVarIdxMap)) { 1077 return Error("Failed to parse delta set index map"); 1078 } 1079 } 1080 1081 if (offsetItemVariationStore) { 1082 if (offsetItemVariationStore < headerSize || offsetItemVariationStore >= length) { 1083 return Error("Bad item variation store offset in table header"); 1084 } 1085 if (!ParseItemVariationStore(font, data + offsetItemVariationStore, length - offsetItemVariationStore)) { 1086 return Error("Failed to parse item variation store"); 1087 } 1088 } 1089 1090 this->m_data = data; 1091 this->m_length = length; 1092 return true; 1093 } 1094 1095 bool OpenTypeCOLR::Serialize(OTSStream *out) { 1096 if (!out->Write(this->m_data, this->m_length)) { 1097 return Error("Failed to write COLR table"); 1098 } 1099 1100 return true; 1101 } 1102 1103 } // namespace ots 1104 1105 #undef TABLE_NAME