| // Copyright (c) 2009-2017 The OTS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "sill.h" |
| |
| #include "feat.h" |
| #include <cmath> |
| #include <unordered_set> |
| |
| namespace ots { |
| |
| bool OpenTypeSILL::Parse(const uint8_t* data, size_t length) { |
| if (GetFont()->dropped_graphite) { |
| return Drop("Skipping Graphite table"); |
| } |
| Buffer table(data, length); |
| |
| if (!table.ReadU32(&this->version) || this->version >> 16 != 1) { |
| return Drop("Failed to read valid version"); |
| } |
| if (!table.ReadU16(&this->numLangs)) { |
| return Drop("Failed to read numLangs"); |
| } |
| |
| // The following three fields are deprecated and ignored. We fix them up here |
| // just for internal consistency, but the Graphite engine doesn't care. |
| if (!table.ReadU16(&this->searchRange) || |
| !table.ReadU16(&this->entrySelector) || |
| !table.ReadU16(&this->rangeShift)) { |
| return Drop("Failed to read searchRange..rangeShift"); |
| } |
| if (this->numLangs == 0) { |
| if (this->searchRange != 0 || this->entrySelector != 0 || this->rangeShift != 0) { |
| this->searchRange = this->entrySelector = this->rangeShift = 0; |
| } |
| } else { |
| unsigned floorLog2 = std::floor(log2(this->numLangs)); |
| if (this->searchRange != (unsigned)std::pow(2, floorLog2) || |
| this->entrySelector != floorLog2 || |
| this->rangeShift != this->numLangs - this->searchRange) { |
| this->searchRange = (unsigned)std::pow(2, floorLog2); |
| this->entrySelector = floorLog2; |
| this->rangeShift = this->numLangs - this->searchRange; |
| } |
| } |
| |
| std::unordered_set<size_t> unverified; |
| //this->entries.resize(static_cast<unsigned long>(this->numLangs) + 1, this); |
| for (unsigned long i = 0; i <= this->numLangs; ++i) { |
| this->entries.emplace_back(this); |
| LanguageEntry& entry = this->entries[i]; |
| if (!entry.ParsePart(table)) { |
| return Drop("Failed to read entries[%u]", i); |
| } |
| for (unsigned j = 0; j < entry.numSettings; ++j) { |
| size_t offset = entry.offset + j * 8; |
| if (offset < entry.offset || offset > length) { |
| return DropGraphite("Invalid LangFeatureSetting offset %zu/%zu", |
| offset, length); |
| } |
| unverified.insert(offset); |
| // need to verify that this LanguageEntry points to valid |
| // LangFeatureSetting |
| } |
| } |
| |
| while (table.remaining()) { |
| unverified.erase(table.offset()); |
| LangFeatureSetting setting(this); |
| if (!setting.ParsePart(table)) { |
| return Drop("Failed to read a LangFeatureSetting"); |
| } |
| settings.push_back(setting); |
| } |
| |
| if (!unverified.empty()) { |
| return Drop("%zu incorrect offsets into settings", unverified.size()); |
| } |
| if (table.remaining()) { |
| return Warning("%zu bytes unparsed", table.remaining()); |
| } |
| return true; |
| } |
| |
| bool OpenTypeSILL::Serialize(OTSStream* out) { |
| if (!out->WriteU32(this->version) || |
| !out->WriteU16(this->numLangs) || |
| !out->WriteU16(this->searchRange) || |
| !out->WriteU16(this->entrySelector) || |
| !out->WriteU16(this->rangeShift) || |
| !SerializeParts(this->entries, out) || |
| !SerializeParts(this->settings, out)) { |
| return Error("Failed to write table"); |
| } |
| return true; |
| } |
| |
| bool OpenTypeSILL::LanguageEntry::ParsePart(Buffer& table) { |
| if (!table.ReadU8(&this->langcode[0]) || |
| !table.ReadU8(&this->langcode[1]) || |
| !table.ReadU8(&this->langcode[2]) || |
| !table.ReadU8(&this->langcode[3])) { |
| return parent->Error("LanguageEntry: Failed to read langcode"); |
| } |
| if (!table.ReadU16(&this->numSettings)) { |
| return parent->Error("LanguageEntry: Failed to read numSettings"); |
| } |
| if (!table.ReadU16(&this->offset)) { |
| return parent->Error("LanguageEntry: Failed to read offset"); |
| } |
| return true; |
| } |
| |
| bool OpenTypeSILL::LanguageEntry::SerializePart(OTSStream* out) const { |
| if (!out->WriteU8(this->langcode[0]) || |
| !out->WriteU8(this->langcode[1]) || |
| !out->WriteU8(this->langcode[2]) || |
| !out->WriteU8(this->langcode[3]) || |
| !out->WriteU16(this->numSettings) || |
| !out->WriteU16(this->offset)) { |
| return parent->Error("LanguageEntry: Failed to write"); |
| } |
| return true; |
| } |
| |
| bool OpenTypeSILL::LangFeatureSetting::ParsePart(Buffer& table) { |
| OpenTypeFEAT* feat = static_cast<OpenTypeFEAT*>( |
| parent->GetFont()->GetTypedTable(OTS_TAG_FEAT)); |
| if (!feat) { |
| return parent->Error("FeatureDefn: Required Feat table is missing"); |
| } |
| |
| if (!table.ReadU32(&this->featureId) || |
| !feat->IsValidFeatureId(this->featureId)) { |
| return parent->Error("LangFeatureSetting: Failed to read valid featureId"); |
| } |
| if (!table.ReadS16(&this->value)) { |
| return parent->Error("LangFeatureSetting: Failed to read value"); |
| } |
| if (!table.ReadU16(&this->reserved)) { |
| return parent->Error("LangFeatureSetting: Failed to read reserved"); |
| } |
| if (this->reserved != 0) { |
| parent->Warning("LangFeatureSetting: Nonzero reserved"); |
| } |
| return true; |
| } |
| |
| bool OpenTypeSILL::LangFeatureSetting::SerializePart(OTSStream* out) const { |
| if (!out->WriteU32(this->featureId) || |
| !out->WriteS16(this->value) || |
| !out->WriteU16(this->reserved)) { |
| return parent->Error("LangFeatureSetting: Failed to read reserved"); |
| } |
| return true; |
| } |
| |
| } // namespace ots |