tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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(&centerX) ||
    351      !subtable.ReadS16(&centerY) ||
    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(&centerX) ||
    515                        !subtable.ReadS16(&centerY))) ||
    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(&centerX) ||
    547                        !subtable.ReadS16(&centerY))) ||
    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(&centerX) ||
    580                        !subtable.ReadS16(&centerY))) ||
    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