| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // Determine coverage of font given its raw "cmap" OpenType table |
| |
| #include "minikin/CmapCoverage.h" |
| |
| #include <algorithm> |
| #include <vector> |
| |
| #include "minikin/Range.h" |
| #include "minikin/SparseBitSet.h" |
| |
| #include "MinikinInternal.h" |
| |
| namespace minikin { |
| |
| // These could perhaps be optimized to use __builtin_bswap16 and friends. |
| static uint32_t readU16(const uint8_t* data, size_t offset) { |
| return ((uint32_t)data[offset]) << 8 | ((uint32_t)data[offset + 1]); |
| } |
| |
| static uint32_t readU24(const uint8_t* data, size_t offset) { |
| return ((uint32_t)data[offset]) << 16 | ((uint32_t)data[offset + 1]) << 8 | |
| ((uint32_t)data[offset + 2]); |
| } |
| |
| static uint32_t readU32(const uint8_t* data, size_t offset) { |
| return ((uint32_t)data[offset]) << 24 | ((uint32_t)data[offset + 1]) << 16 | |
| ((uint32_t)data[offset + 2]) << 8 | ((uint32_t)data[offset + 3]); |
| } |
| |
| // The start must be larger than or equal to coverage.back() if coverage is not empty. |
| // Returns true if the range is appended. Otherwise returns false as an error. |
| static bool addRange(std::vector<uint32_t>& coverage, uint32_t start, uint32_t end) { |
| if (coverage.empty() || coverage.back() < start) { |
| coverage.push_back(start); |
| coverage.push_back(end); |
| return true; |
| } else if (coverage.back() == start) { |
| coverage.back() = end; |
| return true; |
| } else { |
| // Reject unordered range input since SparseBitSet assumes that the given range vector is |
| // sorted. OpenType specification says cmap entries are sorted in order of code point |
| // values, thus for OpenType compliant font files, we don't reach here. |
| android_errorWriteLog(0x534e4554, "32178311"); |
| return false; |
| } |
| } |
| |
| // Returns true if the range is appended. Otherwise returns false as an error. |
| static bool addRangeCmap4(std::vector<uint32_t>& coverage, uint32_t start, uint32_t end) { |
| if (!coverage.empty() && coverage.back() > end) { |
| // Reject unordered end code points. |
| return false; |
| } |
| if (coverage.empty() || coverage.back() < start) { |
| coverage.push_back(start); |
| coverage.push_back(end); |
| return true; |
| } else { |
| coverage.back() = end; |
| return true; |
| } |
| } |
| |
| // Returns Range from given ranges vector. Returns invalidRange if i is out of range. |
| static inline Range getRange(const std::vector<uint32_t>& r, size_t i) { |
| return i + 1 < r.size() ? Range({r[i], r[i + 1]}) : Range::invalidRange(); |
| } |
| |
| // Merge two sorted lists of ranges into one sorted list. |
| static std::vector<uint32_t> mergeRanges(const std::vector<uint32_t>& lRanges, |
| const std::vector<uint32_t>& rRanges) { |
| std::vector<uint32_t> out; |
| |
| const size_t lsize = lRanges.size(); |
| const size_t rsize = rRanges.size(); |
| out.reserve(lsize + rsize); |
| size_t ri = 0; |
| size_t li = 0; |
| while (li < lsize || ri < rsize) { |
| Range left = getRange(lRanges, li); |
| Range right = getRange(rRanges, ri); |
| |
| if (!right.isValid()) { |
| // No ranges left in rRanges. Just put all remaining ranges in lRanges. |
| do { |
| Range r = getRange(lRanges, li); |
| addRange(out, r.getStart(), r.getEnd()); // Input is sorted. Never returns false. |
| li += 2; |
| } while (li < lsize); |
| break; |
| } else if (!left.isValid()) { |
| // No ranges left in lRanges. Just put all remaining ranges in rRanges. |
| do { |
| Range r = getRange(rRanges, ri); |
| addRange(out, r.getStart(), r.getEnd()); // Input is sorted. Never returns false. |
| ri += 2; |
| } while (ri < rsize); |
| break; |
| } else if (!Range::intersects(left, right)) { |
| // No intersection. Add smaller range. |
| if (left.getStart() < right.getStart()) { |
| // Input is sorted. Never returns false. |
| addRange(out, left.getStart(), left.getEnd()); |
| li += 2; |
| } else { |
| // Input is sorted. Never returns false. |
| addRange(out, right.getStart(), right.getEnd()); |
| ri += 2; |
| } |
| } else { |
| Range merged = Range::merge(left, right); |
| li += 2; |
| ri += 2; |
| left = getRange(lRanges, li); |
| right = getRange(rRanges, ri); |
| while (Range::intersects(merged, left) || Range::intersects(merged, right)) { |
| if (Range::intersects(merged, left)) { |
| merged = Range::merge(merged, left); |
| li += 2; |
| left = getRange(lRanges, li); |
| } else { |
| merged = Range::merge(merged, right); |
| ri += 2; |
| right = getRange(rRanges, ri); |
| } |
| } |
| // Input is sorted. Never returns false. |
| addRange(out, merged.getStart(), merged.getEnd()); |
| } |
| } |
| |
| return out; |
| } |
| |
| // Get the coverage information out of a Format 4 subtable, storing it in the coverage vector |
| static bool getCoverageFormat4(std::vector<uint32_t>& coverage, const uint8_t* data, size_t size) { |
| const size_t kSegCountOffset = 6; |
| const size_t kEndCountOffset = 14; |
| const size_t kHeaderSize = 16; |
| const size_t kSegmentSize = 8; // total size of array elements for one segment |
| if (kEndCountOffset > size) { |
| return false; |
| } |
| size_t segCount = readU16(data, kSegCountOffset) >> 1; |
| if (kHeaderSize + segCount * kSegmentSize > size) { |
| return false; |
| } |
| for (size_t i = 0; i < segCount; i++) { |
| uint32_t end = readU16(data, kEndCountOffset + 2 * i); |
| uint32_t start = readU16(data, kHeaderSize + 2 * (segCount + i)); |
| if (end < start) { |
| // invalid segment range: size must be positive |
| android_errorWriteLog(0x534e4554, "26413177"); |
| return false; |
| } |
| uint32_t rangeOffset = readU16(data, kHeaderSize + 2 * (3 * segCount + i)); |
| if (rangeOffset == 0) { |
| uint32_t delta = readU16(data, kHeaderSize + 2 * (2 * segCount + i)); |
| if (((end + delta) & 0xffff) > end - start) { |
| if (!addRangeCmap4(coverage, start, end + 1)) { |
| return false; |
| } |
| } else { |
| for (uint32_t j = start; j < end + 1; j++) { |
| if (((j + delta) & 0xffff) != 0) { |
| if (!addRangeCmap4(coverage, j, j + 1)) { |
| return false; |
| } |
| } |
| } |
| } |
| } else { |
| for (uint32_t j = start; j < end + 1; j++) { |
| uint32_t actualRangeOffset = |
| kHeaderSize + 6 * segCount + rangeOffset + (i + j - start) * 2; |
| if (actualRangeOffset + 2 > size) { |
| // invalid rangeOffset is considered a "warning" by OpenType Sanitizer |
| continue; |
| } |
| uint32_t glyphId = readU16(data, actualRangeOffset); |
| if (glyphId != 0) { |
| if (!addRangeCmap4(coverage, j, j + 1)) { |
| return false; |
| } |
| } |
| } |
| } |
| } |
| return true; |
| } |
| |
| // Get the coverage information out of a Format 12 subtable, storing it in the coverage vector |
| static bool getCoverageFormat12(std::vector<uint32_t>& coverage, const uint8_t* data, size_t size) { |
| const size_t kNGroupsOffset = 12; |
| const size_t kFirstGroupOffset = 16; |
| const size_t kGroupSize = 12; |
| const size_t kStartCharCodeOffset = 0; |
| const size_t kEndCharCodeOffset = 4; |
| const size_t kMaxNGroups = 0xfffffff0 / kGroupSize; // protection against overflow |
| // For all values < kMaxNGroups, kFirstGroupOffset + nGroups * kGroupSize fits in 32 bits. |
| if (kFirstGroupOffset > size) { |
| return false; |
| } |
| uint32_t nGroups = readU32(data, kNGroupsOffset); |
| if (nGroups >= kMaxNGroups || kFirstGroupOffset + nGroups * kGroupSize > size) { |
| android_errorWriteLog(0x534e4554, "25645298"); |
| return false; |
| } |
| for (uint32_t i = 0; i < nGroups; i++) { |
| uint32_t groupOffset = kFirstGroupOffset + i * kGroupSize; |
| uint32_t start = readU32(data, groupOffset + kStartCharCodeOffset); |
| uint32_t end = readU32(data, groupOffset + kEndCharCodeOffset); |
| if (end < start) { |
| // invalid group range: size must be positive |
| android_errorWriteLog(0x534e4554, "26413177"); |
| return false; |
| } |
| |
| // No need to read outside of Unicode code point range. |
| if (start > MAX_UNICODE_CODE_POINT) { |
| return true; |
| } |
| if (end > MAX_UNICODE_CODE_POINT) { |
| // file is inclusive, vector is exclusive |
| if (end == 0xFFFFFFFF) { |
| android_errorWriteLog(0x534e4554, "62134807"); |
| } |
| return addRange(coverage, start, MAX_UNICODE_CODE_POINT + 1); |
| } |
| if (!addRange(coverage, start, end + 1)) { // file is inclusive, vector is exclusive |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| // Lower value has higher priority. 0 for the highest priority table. |
| // kLowestPriority for unsupported tables. |
| // This order comes from HarfBuzz's hb-ot-font.cc and needs to be kept in sync with it. |
| constexpr uint8_t kLowestPriority = 255; |
| uint8_t getTablePriority(uint16_t platformId, uint16_t encodingId) { |
| if (platformId == 3 && encodingId == 10) { |
| return 0; |
| } |
| if (platformId == 0 && encodingId == 6) { |
| return 1; |
| } |
| if (platformId == 0 && encodingId == 4) { |
| return 2; |
| } |
| if (platformId == 3 && encodingId == 1) { |
| return 3; |
| } |
| if (platformId == 0 && encodingId == 3) { |
| return 4; |
| } |
| if (platformId == 0 && encodingId == 2) { |
| return 5; |
| } |
| if (platformId == 0 && encodingId == 1) { |
| return 6; |
| } |
| if (platformId == 0 && encodingId == 0) { |
| return 7; |
| } |
| // Tables other than above are not supported. |
| return kLowestPriority; |
| } |
| |
| // Get merged coverage information from default UVS Table and non-default UVS Table. Note that this |
| // function assumes code points in both default UVS Table and non-default UVS table are stored in |
| // ascending order. This is required by the standard. |
| static bool getVSCoverage(std::vector<uint32_t>* out_ranges, const uint8_t* data, size_t size, |
| uint32_t defaultUVSTableOffset, uint32_t nonDefaultUVSTableOffset, |
| const SparseBitSet& baseCoverage) { |
| // Need to merge supported ranges from default UVS Table and non-default UVS Table. |
| // First, collect all supported code points from non default UVS table. |
| std::vector<uint32_t> rangesFromNonDefaultUVSTable; |
| if (nonDefaultUVSTableOffset != 0) { |
| constexpr size_t kHeaderSize = 4; |
| constexpr size_t kUVSMappingRecordSize = 5; |
| |
| const uint8_t* nonDefaultUVSTable = data + nonDefaultUVSTableOffset; |
| // This subtraction doesn't underflow since the caller already checked |
| // size > nonDefaultUVSTableOffset. |
| const size_t nonDefaultUVSTableRemaining = size - nonDefaultUVSTableOffset; |
| if (nonDefaultUVSTableRemaining < kHeaderSize) { |
| return false; |
| } |
| const uint64_t numRecords = readU32(nonDefaultUVSTable, 0); |
| const uint64_t sizeToRead = numRecords * kUVSMappingRecordSize + kHeaderSize; |
| if (sizeToRead > nonDefaultUVSTableRemaining) { |
| if (sizeToRead > UINT_MAX) { |
| android_errorWriteLog(0x534e4554, "70808908"); |
| } |
| return false; |
| } |
| for (uint32_t i = 0; i < numRecords; ++i) { |
| const size_t recordOffset = kHeaderSize + kUVSMappingRecordSize * i; |
| const uint32_t codePoint = readU24(nonDefaultUVSTable, recordOffset); |
| if (!addRange(rangesFromNonDefaultUVSTable, codePoint, codePoint + 1)) { |
| return false; |
| } |
| } |
| } |
| |
| // Then, construct range from default UVS Table with merging code points from non default UVS |
| // table. |
| std::vector<uint32_t> rangesFromDefaultUVSTable; |
| if (defaultUVSTableOffset != 0) { |
| constexpr size_t kHeaderSize = 4; |
| constexpr size_t kUnicodeRangeRecordSize = 4; |
| |
| const uint8_t* defaultUVSTable = data + defaultUVSTableOffset; |
| // This subtraction doesn't underflow since the caller already checked |
| // size > defaultUVSTableOffset. |
| const size_t defaultUVSTableRemaining = size - defaultUVSTableOffset; |
| |
| if (defaultUVSTableRemaining < kHeaderSize) { |
| return false; |
| } |
| const uint64_t numRecords = readU32(defaultUVSTable, 0); |
| const uint64_t sizeToRead = numRecords * kUnicodeRangeRecordSize + kHeaderSize; |
| if (sizeToRead > defaultUVSTableRemaining) { |
| if (sizeToRead > UINT_MAX) { |
| android_errorWriteLog(0x534e4554, "70808908"); |
| } |
| return false; |
| } |
| |
| for (uint32_t i = 0; i < numRecords; ++i) { |
| const size_t recordOffset = kHeaderSize + kUnicodeRangeRecordSize * i; |
| const uint32_t startCp = readU24(defaultUVSTable, recordOffset); |
| const uint8_t rangeLength = defaultUVSTable[recordOffset + 3]; |
| |
| // Then insert range from default UVS Table, but exclude if the base codepoint is not |
| // supported. |
| for (uint32_t cp = startCp; cp <= startCp + rangeLength; ++cp) { |
| // All codepoints in default UVS table should go to the glyphs of the codepoints |
| // without variation selectors. We need to check the default glyph availability and |
| // exclude the codepoint if it is not supported by defualt cmap table. |
| if (baseCoverage.get(cp)) { |
| if (!addRange(rangesFromDefaultUVSTable, cp, cp + 1 /* exclusive */)) { |
| return false; |
| } |
| } |
| } |
| } |
| } |
| *out_ranges = mergeRanges(rangesFromDefaultUVSTable, rangesFromNonDefaultUVSTable); |
| return true; |
| } |
| |
| static void getCoverageFormat14(std::vector<std::unique_ptr<SparseBitSet>>* out, |
| const uint8_t* data, size_t size, |
| const SparseBitSet& baseCoverage) { |
| constexpr size_t kHeaderSize = 10; |
| constexpr size_t kRecordSize = 11; |
| constexpr size_t kLengthOffset = 2; |
| constexpr size_t kNumRecordOffset = 6; |
| |
| out->clear(); |
| if (size < kHeaderSize) { |
| return; |
| } |
| |
| const uint32_t length = readU32(data, kLengthOffset); |
| if (size < length) { |
| return; |
| } |
| |
| const uint64_t numRecords = readU32(data, kNumRecordOffset); |
| const uint64_t sizeToRead = kHeaderSize + kRecordSize * numRecords; |
| if (numRecords == 0 || sizeToRead > length) { |
| if (sizeToRead > UINT_MAX) { |
| android_errorWriteLog(0x534e4554, "70808908"); |
| } |
| return; |
| } |
| |
| for (uint32_t i = 0; i < numRecords; ++i) { |
| // Insert from the largest code points since it determines the size of the output vector. |
| const uint32_t recordHeadOffset = kHeaderSize + kRecordSize * (numRecords - i - 1); |
| const uint32_t vsCodePoint = readU24(data, recordHeadOffset); |
| const uint32_t defaultUVSOffset = readU32(data, recordHeadOffset + 3); |
| const uint32_t nonDefaultUVSOffset = readU32(data, recordHeadOffset + 7); |
| if (defaultUVSOffset > length || nonDefaultUVSOffset > length) { |
| continue; |
| } |
| |
| const uint16_t vsIndex = getVsIndex(vsCodePoint); |
| if (vsIndex == INVALID_VS_INDEX) { |
| continue; |
| } |
| std::vector<uint32_t> ranges; |
| if (!getVSCoverage(&ranges, data, length, defaultUVSOffset, nonDefaultUVSOffset, |
| baseCoverage)) { |
| continue; |
| } |
| if (out->size() < vsIndex + 1) { |
| out->resize(vsIndex + 1); |
| } |
| (*out)[vsIndex].reset(new SparseBitSet(ranges.data(), ranges.size() >> 1)); |
| } |
| |
| out->shrink_to_fit(); |
| } |
| |
| SparseBitSet CmapCoverage::getCoverage(const uint8_t* cmap_data, size_t cmap_size, |
| std::vector<std::unique_ptr<SparseBitSet>>* out) { |
| constexpr size_t kHeaderSize = 4; |
| constexpr size_t kNumTablesOffset = 2; |
| constexpr size_t kTableSize = 8; |
| constexpr size_t kPlatformIdOffset = 0; |
| constexpr size_t kEncodingIdOffset = 2; |
| constexpr size_t kOffsetOffset = 4; |
| constexpr size_t kFormatOffset = 0; |
| constexpr uint32_t kNoTable = UINT32_MAX; |
| |
| if (kHeaderSize > cmap_size) { |
| return SparseBitSet(); |
| } |
| uint32_t numTables = readU16(cmap_data, kNumTablesOffset); |
| if (kHeaderSize + numTables * kTableSize > cmap_size) { |
| return SparseBitSet(); |
| } |
| |
| uint32_t bestTableOffset = kNoTable; |
| uint16_t bestTableFormat = 0; |
| uint8_t bestTablePriority = kLowestPriority; |
| uint32_t vsTableOffset = kNoTable; |
| for (uint32_t i = 0; i < numTables; ++i) { |
| const uint32_t tableHeadOffset = kHeaderSize + i * kTableSize; |
| const uint16_t platformId = readU16(cmap_data, tableHeadOffset + kPlatformIdOffset); |
| const uint16_t encodingId = readU16(cmap_data, tableHeadOffset + kEncodingIdOffset); |
| const uint32_t offset = readU32(cmap_data, tableHeadOffset + kOffsetOffset); |
| |
| if (offset > cmap_size - 2) { |
| continue; // Invalid table: not enough space to read. |
| } |
| const uint16_t format = readU16(cmap_data, offset + kFormatOffset); |
| |
| if (platformId == 0 /* Unicode */ && encodingId == 5 /* Variation Sequences */) { |
| if (vsTableOffset == kNoTable && format == 14) { |
| vsTableOffset = offset; |
| } else { |
| // Ignore the (0, 5) table if we have already seen another valid one or it's in a |
| // format we don't understand. |
| } |
| } else { |
| uint32_t length; |
| uint32_t language; |
| |
| if (format == 4) { |
| constexpr size_t lengthOffset = 2; |
| constexpr size_t languageOffset = 4; |
| constexpr size_t minTableSize = languageOffset + 2; |
| if (offset > cmap_size - minTableSize) { |
| continue; // Invalid table: not enough space to read. |
| } |
| length = readU16(cmap_data, offset + lengthOffset); |
| language = readU16(cmap_data, offset + languageOffset); |
| } else if (format == 12) { |
| constexpr size_t lengthOffset = 4; |
| constexpr size_t languageOffset = 8; |
| constexpr size_t minTableSize = languageOffset + 4; |
| if (offset > cmap_size - minTableSize) { |
| continue; // Invalid table: not enough space to read. |
| } |
| length = readU32(cmap_data, offset + lengthOffset); |
| language = readU32(cmap_data, offset + languageOffset); |
| } else { |
| continue; |
| } |
| |
| if (length > cmap_size - offset) { |
| continue; // Invalid table: table length is larger than whole cmap data size. |
| } |
| if (language != 0) { |
| // Unsupported or invalid table: this is either a subtable for the Macintosh |
| // platform (which we don't support), or an invalid subtable since language field |
| // should be zero for non-Macintosh subtables. |
| continue; |
| } |
| const uint8_t priority = getTablePriority(platformId, encodingId); |
| if (priority < bestTablePriority) { |
| bestTableOffset = offset; |
| bestTablePriority = priority; |
| bestTableFormat = format; |
| } |
| } |
| if (vsTableOffset != kNoTable && bestTablePriority == 0 /* highest priority */) { |
| // Already found the highest priority table and variation sequences table. No need to |
| // look at remaining tables. |
| break; |
| } |
| } |
| |
| SparseBitSet coverage; |
| |
| if (bestTableOffset != kNoTable) { |
| const uint8_t* tableData = cmap_data + bestTableOffset; |
| const size_t tableSize = cmap_size - bestTableOffset; |
| bool success; |
| std::vector<uint32_t> coverageVec; |
| if (bestTableFormat == 4) { |
| success = getCoverageFormat4(coverageVec, tableData, tableSize); |
| } else { |
| success = getCoverageFormat12(coverageVec, tableData, tableSize); |
| } |
| |
| if (success) { |
| coverage = SparseBitSet(&coverageVec.front(), coverageVec.size() >> 1); |
| } |
| } |
| |
| if (vsTableOffset != kNoTable) { |
| const uint8_t* tableData = cmap_data + vsTableOffset; |
| const size_t tableSize = cmap_size - vsTableOffset; |
| getCoverageFormat14(out, tableData, tableSize, coverage); |
| } |
| return coverage; |
| } |
| |
| } // namespace minikin |