tor-browser

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

cpal.cc (9145B)


      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 "cpal.h"
      6 #include "name.h"
      7 
      8 // CPAL - Color Palette Table
      9 // http://www.microsoft.com/typography/otspec/cpal.htm
     10 
     11 #define TABLE_NAME "CPAL"
     12 
     13 namespace {
     14 
     15 // Caller has sized the colorRecords array, so we know how much to try and read.
     16 bool ParseColorRecordsArray(const ots::Font* font,
     17                            const uint8_t* data, size_t length,
     18                            std::vector<uint32_t>* colorRecords)
     19 {
     20  ots::Buffer subtable(data, length);
     21 
     22  for (auto& color : *colorRecords) {
     23    if (!subtable.ReadU32(&color)) {
     24      return OTS_FAILURE_MSG("Failed to read color record");
     25    }
     26  }
     27 
     28  return true;
     29 }
     30 
     31 // Caller has sized the paletteTypes array, so we know how much to try and read.
     32 bool ParsePaletteTypesArray(const ots::Font* font,
     33                            const uint8_t* data, size_t length,
     34                            std::vector<uint32_t>* paletteTypes)
     35 {
     36  ots::Buffer subtable(data, length);
     37 
     38  constexpr uint32_t USABLE_WITH_LIGHT_BACKGROUND = 0x0001;
     39  constexpr uint32_t USABLE_WITH_DARK_BACKGROUND = 0x0002;
     40  constexpr uint32_t RESERVED = ~(USABLE_WITH_LIGHT_BACKGROUND | USABLE_WITH_DARK_BACKGROUND);
     41 
     42  for (auto& type : *paletteTypes) {
     43    if (!subtable.ReadU32(&type)) {
     44      return OTS_FAILURE_MSG("Failed to read palette type");
     45    }
     46    if (type & RESERVED) {
     47      // Should we treat this as failure? For now, just a warning; seems unlikely
     48      // to be dangerous.
     49      OTS_WARNING("Invalid (reserved) palette type flags %08x", type);
     50      type &= ~RESERVED;
     51    }
     52  }
     53 
     54  return true;
     55 }
     56 
     57 // Caller has sized the labels array, so we know how much to try and read.
     58 bool ParseLabelsArray(const ots::Font* font,
     59                      const uint8_t* data, size_t length,
     60                      std::vector<uint16_t>* labels,
     61                      const char* labelType)
     62 {
     63  ots::Buffer subtable(data, length);
     64 
     65  auto* name = static_cast<ots::OpenTypeNAME*>(font->GetTypedTable(OTS_TAG_NAME));
     66  if (!name) {
     67    return OTS_FAILURE_MSG("Required name table missing");
     68  }
     69 
     70  for (auto& nameID : *labels) {
     71    if (!subtable.ReadU16(&nameID)) {
     72      return OTS_FAILURE_MSG("Failed to read %s label ID", labelType);
     73    }
     74    if (nameID != 0xffff) {
     75      if (!name->IsValidNameId(nameID)) {
     76        OTS_WARNING("Label ID %u for %s missing from name table", nameID, labelType);
     77        nameID = 0xffff;
     78      }
     79    }
     80  }
     81 
     82  return true;
     83 }
     84 
     85 }  // namespace
     86 
     87 namespace ots {
     88 
     89 bool OpenTypeCPAL::Parse(const uint8_t *data, size_t length) {
     90  Font *font = GetFont();
     91  Buffer table(data, length);
     92 
     93  // Header fields common to versions 0 and 1. These are recomputed
     94  // from the array sizes during serialization.
     95  uint16_t numPalettes;
     96  uint16_t numColorRecords;
     97  uint32_t colorRecordsArrayOffset;
     98 
     99  if (!table.ReadU16(&this->version) ||
    100      !table.ReadU16(&this->num_palette_entries) ||
    101      !table.ReadU16(&numPalettes) ||
    102      !table.ReadU16(&numColorRecords) ||
    103      !table.ReadU32(&colorRecordsArrayOffset)) {
    104    return Error("Failed to read CPAL table header");
    105  }
    106 
    107  if (this->version > 1) {
    108    return Error("Unknown CPAL table version %u", this->version);
    109  }
    110 
    111  if (!this->num_palette_entries || !numPalettes || !numColorRecords) {
    112    return Error("Empty CPAL is not valid");
    113  }
    114 
    115  if (this->num_palette_entries > numColorRecords) {
    116    return Error("Not enough color records for a complete palette");
    117  }
    118 
    119  uint32_t headerSize = 4 * sizeof(uint16_t) + sizeof(uint32_t) +
    120      numPalettes * sizeof(uint16_t);
    121 
    122  // uint16_t colorRecordIndices[numPalettes]
    123  this->colorRecordIndices.resize(numPalettes);
    124  for (auto& colorRecordIndex : this->colorRecordIndices) {
    125    if (!table.ReadU16(&colorRecordIndex)) {
    126      return Error("Failed to read color record index");
    127    }
    128    if (colorRecordIndex > numColorRecords - this->num_palette_entries) {
    129      return Error("Palette overflows color records array");
    130    }
    131  }
    132 
    133  uint32_t paletteTypesArrayOffset = 0;
    134  uint32_t paletteLabelsArrayOffset = 0;
    135  uint32_t paletteEntryLabelsArrayOffset = 0;
    136  if (this->version == 1) {
    137    if (!table.ReadU32(&paletteTypesArrayOffset) ||
    138        !table.ReadU32(&paletteLabelsArrayOffset) ||
    139        !table.ReadU32(&paletteEntryLabelsArrayOffset)) {
    140      return Error("Failed to read CPAL v.1 table header");
    141    }
    142    headerSize += 3 * sizeof(uint32_t);
    143  }
    144 
    145  // The following arrays may occur in any order, as they're independently referenced
    146  // by offsets in the header.
    147 
    148  if (colorRecordsArrayOffset < headerSize || colorRecordsArrayOffset >= length) {
    149    return Error("Bad color records array offset in table header");
    150  }
    151  this->colorRecords.resize(numColorRecords);
    152  if (!ParseColorRecordsArray(font, data + colorRecordsArrayOffset, length - colorRecordsArrayOffset,
    153                              &this->colorRecords)) {
    154    return Error("Failed to parse color records array");
    155  }
    156 
    157  if (paletteTypesArrayOffset) {
    158    if (paletteTypesArrayOffset < headerSize || paletteTypesArrayOffset >= length) {
    159      return Error("Bad palette types array offset in table header");
    160    }
    161    this->paletteTypes.resize(numPalettes);
    162    if (!ParsePaletteTypesArray(font, data + paletteTypesArrayOffset, length - paletteTypesArrayOffset,
    163                                &this->paletteTypes)) {
    164      return Error("Failed to parse palette types array");
    165    }
    166  }
    167 
    168  if (paletteLabelsArrayOffset) {
    169    if (paletteLabelsArrayOffset < headerSize || paletteLabelsArrayOffset >= length) {
    170      return Error("Bad palette labels array offset in table header");
    171    }
    172    this->paletteLabels.resize(numPalettes);
    173    if (!ParseLabelsArray(font, data + paletteLabelsArrayOffset, length - paletteLabelsArrayOffset,
    174                          &this->paletteLabels, "palette")) {
    175      return Error("Failed to parse palette labels array");
    176    }
    177  }
    178 
    179  if (paletteEntryLabelsArrayOffset) {
    180    if (paletteEntryLabelsArrayOffset < headerSize || paletteEntryLabelsArrayOffset >= length) {
    181      return Error("Bad palette entry labels array offset in table header");
    182    }
    183    this->paletteEntryLabels.resize(this->num_palette_entries);
    184    if (!ParseLabelsArray(font, data + paletteEntryLabelsArrayOffset, length - paletteEntryLabelsArrayOffset,
    185                          &this->paletteEntryLabels, "palette entry")) {
    186      return Error("Failed to parse palette entry labels array");
    187    }
    188  }
    189 
    190  return true;
    191 }
    192 
    193 bool OpenTypeCPAL::Serialize(OTSStream *out) {
    194  uint16_t numPalettes = this->colorRecordIndices.size();
    195  uint16_t numColorRecords = this->colorRecords.size();
    196 
    197 #ifndef NDEBUG
    198  off_t start = out->Tell();
    199 #endif
    200 
    201  size_t colorRecordsArrayOffset = 4 * sizeof(uint16_t) + sizeof(uint32_t) +
    202      numPalettes * sizeof(uint16_t);
    203  if (this->version == 1) {
    204    colorRecordsArrayOffset += 3 * sizeof(uint32_t);
    205  }
    206 
    207  size_t totalLen = colorRecordsArrayOffset + numColorRecords * sizeof(uint32_t);
    208 
    209  if (!out->WriteU16(this->version) ||
    210      !out->WriteU16(this->num_palette_entries) ||
    211      !out->WriteU16(numPalettes) ||
    212      !out->WriteU16(numColorRecords) ||
    213      !out->WriteU32(colorRecordsArrayOffset)) {
    214    return Error("Failed to write CPAL header");
    215  }
    216 
    217  for (auto i : this->colorRecordIndices) {
    218    if (!out->WriteU16(i)) {
    219      return Error("Failed to write color record indices");
    220    }
    221  }
    222 
    223  if (this->version == 1) {
    224    size_t paletteTypesArrayOffset = 0;
    225    if (!this->paletteTypes.empty()) {
    226      assert(paletteTypes.size() == numPalettes);
    227      paletteTypesArrayOffset = totalLen;
    228      totalLen += numPalettes * sizeof(uint32_t);
    229    }
    230 
    231    size_t paletteLabelsArrayOffset = 0;
    232    if (!this->paletteLabels.empty()) {
    233      assert(paletteLabels.size() == numPalettes);
    234      paletteLabelsArrayOffset = totalLen;
    235      totalLen += numPalettes * sizeof(uint16_t);
    236    }
    237 
    238    size_t paletteEntryLabelsArrayOffset = 0;
    239    if (!this->paletteEntryLabels.empty()) {
    240      assert(paletteEntryLabels.size() == this->num_palette_entries);
    241      paletteEntryLabelsArrayOffset = totalLen;
    242      totalLen += this->num_palette_entries * sizeof(uint16_t);
    243    }
    244 
    245    if (!out->WriteU32(paletteTypesArrayOffset) ||
    246        !out->WriteU32(paletteLabelsArrayOffset) ||
    247        !out->WriteU32(paletteEntryLabelsArrayOffset)) {
    248      return Error("Failed to write CPAL v.1 header");
    249    }
    250  }
    251 
    252  for (auto i : this->colorRecords) {
    253    if (!out->WriteU32(i)) {
    254      return Error("Failed to write color records");
    255    }
    256  }
    257 
    258  if (this->version == 1) {
    259    for (auto i : this->paletteTypes) {
    260      if (!out->WriteU32(i)) {
    261        return Error("Failed to write palette types");
    262      }
    263    }
    264 
    265    for (auto i : this->paletteLabels) {
    266      if (!out->WriteU16(i)) {
    267        return Error("Failed to write palette labels");
    268      }
    269    }
    270 
    271    for (auto i : this->paletteEntryLabels) {
    272      if (!out->WriteU16(i)) {
    273        return Error("Failed to write palette entry labels");
    274      }
    275    }
    276  }
    277 
    278  assert(size_t(out->Tell() - start) == totalLen);
    279 
    280  return true;
    281 }
    282 
    283 }  // namespace ots
    284 
    285 #undef TABLE_NAME