/** Return the number of tables, or if this is a TTC (collection), return the number of tables in the first element of the collection. In either case, if offsetToDir is not-null, set it to the offset to the beginning of the table headers (SkSFNTDirEntry), relative to the start of the stream. On an error, return 0 for number of tables, and ignore offsetToDir */ static int count_tables(SkStream* stream, size_t* offsetToDir = NULL) { SkSharedTTHeader shared; if (stream->read(&shared, sizeof(shared)) != sizeof(shared)) { return 0; } // by default, SkSFNTHeader is at the start of the stream size_t offset = 0; // if we're really a collection, the first 4-bytes will be 'ttcf' uint32_t tag = SkEndian_SwapBE32(shared.fCollection.fTag); if (SkSetFourByteTag('t', 't', 'c', 'f') == tag) { if (shared.fCollection.fNumOffsets == 0) { return 0; } // this is the offset to the first local SkSFNTHeader offset = SkEndian_SwapBE32(shared.fCollection.fOffset0); stream->rewind(); if (stream->skip(offset) != offset) { return 0; } if (stream->read(&shared, sizeof(shared)) != sizeof(shared)) { return 0; } } if (offsetToDir) { // add the size of the header, so we will point to the DirEntries *offsetToDir = offset + sizeof(SkSFNTHeader); } return SkEndian_SwapBE16(shared.fSingle.fNumTables); }
bool SkHeifCodec::IsHeif(const void* buffer, size_t bytesRead) { // Parse the ftyp box up to bytesRead to determine if this is HEIF. // Any valid ftyp box should have at least 8 bytes. if (bytesRead < 8) { return false; } uint32_t* ptr = (uint32_t*)buffer; uint64_t chunkSize = SkEndian_SwapBE32(ptr[0]); uint32_t chunkType = SkEndian_SwapBE32(ptr[1]); if (chunkType != FOURCC('f', 't', 'y', 'p')) { return false; } int64_t offset = 8; if (chunkSize == 1) { // This indicates that the next 8 bytes represent the chunk size, // and chunk data comes after that. if (bytesRead < 16) { return false; } auto* chunkSizePtr = SkTAddOffset<const uint64_t>(buffer, offset); chunkSize = SkEndian_SwapBE64(*chunkSizePtr); if (chunkSize < 16) { // The smallest valid chunk is 16 bytes long in this case. return false; } offset += 8; } else if (chunkSize < 8) { // The smallest valid chunk is 8 bytes long. return false; } if (chunkSize > bytesRead) { chunkSize = bytesRead; } int64_t chunkDataSize = chunkSize - offset; // It should at least have major brand (4-byte) and minor version (4-bytes). // The rest of the chunk (if any) is a list of (4-byte) compatible brands. if (chunkDataSize < 8) { return false; } uint32_t numCompatibleBrands = (chunkDataSize - 8) / 4; for (size_t i = 0; i < numCompatibleBrands + 2; ++i) { if (i == 1) { // Skip this index, it refers to the minorVersion, // not a brand. continue; } auto* brandPtr = SkTAddOffset<const uint32_t>(buffer, offset + 4 * i); uint32_t brand = SkEndian_SwapBE32(*brandPtr); if (brand == FOURCC('m', 'i', 'f', '1') || brand == FOURCC('h', 'e', 'i', 'c') || brand == FOURCC('m', 's', 'f', '1') || brand == FOURCC('h', 'e', 'v', 'c')) { return true; } } return false; }
int SkFontStream::CountTTCEntries(SkStream* stream) { stream->rewind(); SkSharedTTHeader shared; if (!read(stream, &shared, sizeof(shared))) { return 0; } // if we're really a collection, the first 4-bytes will be 'ttcf' uint32_t tag = SkEndian_SwapBE32(shared.fCollection.fTag); if (SkSetFourByteTag('t', 't', 'c', 'f') == tag) { return SkEndian_SwapBE32(shared.fCollection.fNumOffsets); } else { return 1; // normal 'sfnt' has 1 dir entry } }
/** Return the number of tables, or if this is a TTC (collection), return the number of tables in the first element of the collection. In either case, if offsetToDir is not-null, set it to the offset to the beginning of the table headers (SkSFNTDirEntry), relative to the start of the stream. On an error, return 0 for number of tables, and ignore offsetToDir */ static int count_tables(SkStream* stream, int ttcIndex, size_t* offsetToDir) { SkASSERT(ttcIndex >= 0); SkAutoSMalloc<1024> storage(sizeof(SkSharedTTHeader)); SkSharedTTHeader* header = (SkSharedTTHeader*)storage.get(); if (!read(stream, header, sizeof(SkSharedTTHeader))) { return 0; } // by default, SkSFNTHeader is at the start of the stream size_t offset = 0; // if we're really a collection, the first 4-bytes will be 'ttcf' uint32_t tag = SkEndian_SwapBE32(header->fCollection.fTag); if (SkSetFourByteTag('t', 't', 'c', 'f') == tag) { unsigned count = SkEndian_SwapBE32(header->fCollection.fNumOffsets); if ((unsigned)ttcIndex >= count) { return 0; } if (ttcIndex > 0) { // need to read more of the shared header stream->rewind(); size_t amount = sizeof(SkSharedTTHeader) + ttcIndex * sizeof(uint32_t); header = (SkSharedTTHeader*)storage.reset(amount); if (!read(stream, header, amount)) { return 0; } } // this is the offset to the local SkSFNTHeader offset = SkEndian_SwapBE32((&header->fCollection.fOffset0)[ttcIndex]); stream->rewind(); if (!skip(stream, offset)) { return 0; } if (!read(stream, header, sizeof(SkSFNTHeader))) { return 0; } } if (offsetToDir) { // add the size of the header, so we will point to the DirEntries *offsetToDir = offset + sizeof(SkSFNTHeader); } return SkEndian_SwapBE16(header->fSingle.fNumTables); }
uint32_t SkOTUtils::CalcTableChecksum(SK_OT_ULONG *data, size_t length) { uint32_t sum = 0; SK_OT_ULONG *dataEnd = data + ((length + 3) & ~3) / sizeof(SK_OT_ULONG); for (; data < dataEnd; ++data) { sum += SkEndian_SwapBE32(*data); } return sum; }
size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag, size_t offset, size_t length, void* data) { SkStream* stream = SkFontHost::OpenStream(fontID); if (NULL == stream) { return 0; } SkAutoUnref au(stream); SfntHeader header; if (!header.init(stream)) { return 0; } for (int i = 0; i < header.fCount; i++) { if (SkEndian_SwapBE32(header.fDir[i].fTag) == tag) { size_t realOffset = SkEndian_SwapBE32(header.fDir[i].fOffset); size_t realLength = SkEndian_SwapBE32(header.fDir[i].fLength); // now sanity check the caller's offset/length if (offset >= realLength) { return 0; } // if the caller is trusting the length from the file, then a // hostile file might choose a value which would overflow offset + // length. if (offset + length < offset) { return 0; } if (offset + length > realLength) { length = realLength - offset; } // skip the stream to the part of the table we want to copy from stream->rewind(); size_t bytesToSkip = realOffset + offset; if (stream->skip(bytesToSkip) != bytesToSkip) { return 0; } if (stream->read(data, length) != length) { return 0; } return length; } } return 0; }
size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) { SkStream* stream = SkFontHost::OpenStream(fontID); if (NULL == stream) { return 0; } SkAutoUnref au(stream); SfntHeader header; if (!header.init(stream)) { return 0; } for (int i = 0; i < header.fCount; i++) { if (SkEndian_SwapBE32(header.fDir[i].fTag) == tag) { return SkEndian_SwapBE32(header.fDir[i].fLength); } } return 0; }
static void TypefaceStyle_test(skiatest::Reporter* reporter, uint16_t weight, uint16_t width, SkData* data) { sk_sp<SkData> dataCopy; SkData* dataToUse = data; if (!dataToUse->unique()) { dataCopy = SkData::MakeWithCopy(data->data(), data->size()); dataToUse = dataCopy.get(); } SkSFNTHeader* sfntHeader = static_cast<SkSFNTHeader*>(dataToUse->writable_data()); SkSFNTHeader::TableDirectoryEntry* tableEntry = SkTAfter<SkSFNTHeader::TableDirectoryEntry>(sfntHeader); SkSFNTHeader::TableDirectoryEntry* os2TableEntry = nullptr; int numTables = SkEndian_SwapBE16(sfntHeader->numTables); for (int tableEntryIndex = 0; tableEntryIndex < numTables; ++tableEntryIndex) { if (SkOTTableOS2::TAG == tableEntry[tableEntryIndex].tag) { os2TableEntry = tableEntry + tableEntryIndex; break; } } SkASSERT_RELEASE(os2TableEntry); size_t os2TableOffset = SkEndian_SwapBE32(os2TableEntry->offset); SkOTTableOS2_V0* os2Table = SkTAddOffset<SkOTTableOS2_V0>(sfntHeader, os2TableOffset); os2Table->usWeightClass.value = SkEndian_SwapBE16(weight); using WidthType = SkOTTableOS2_V0::WidthClass::Value; os2Table->usWidthClass.value = static_cast<WidthType>(SkEndian_SwapBE16(width)); sk_sp<SkTypeface> newTypeface(SkTypeface::MakeFromStream(new SkMemoryStream(dataToUse))); SkASSERT_RELEASE(newTypeface); SkFontStyle newStyle = newTypeface->fontStyle(); //printf("%d, %f\n", weight, (newStyle.weight() - (float)0x7FFF) / (float)0x7FFF); //printf("%d, %f\n", width , (newStyle.width() - (float)0x7F) / (float)0x7F); //printf("%d, %d\n", weight, newStyle.weight()); //printf("%d, %d\n", width , newStyle.width()); // Some back-ends (CG, GDI, DW) support OS/2 version A which uses 0 - 10 (but all differently). REPORTER_ASSERT(reporter, newStyle.weight() == weight || (weight <= 10 && newStyle.weight() == 100 * weight) || (weight == 4 && newStyle.weight() == 350) || // GDI weirdness (weight == 5 && newStyle.weight() == 400) || // GDI weirdness (weight == 0 && newStyle.weight() == 1) || // DW weirdness (weight == 1000 && newStyle.weight() == 999) // DW weirdness ); // Some back-ends (GDI) don't support width, ensure these always report 'medium'. REPORTER_ASSERT(reporter, newStyle.width() == width || newStyle.width() == 5); }
size_t SkFontStream::GetTableData(SkStream* stream, int ttcIndex, SkFontTableTag tag, size_t offset, size_t length, void* data) { SfntHeader header; if (!header.init(stream, ttcIndex)) { return 0; } for (int i = 0; i < header.fCount; i++) { if (SkEndian_SwapBE32(header.fDir[i].fTag) == tag) { size_t realOffset = SkEndian_SwapBE32(header.fDir[i].fOffset); size_t realLength = SkEndian_SwapBE32(header.fDir[i].fLength); // now sanity check the caller's offset/length if (offset >= realLength) { return 0; } // if the caller is trusting the length from the file, then a // hostile file might choose a value which would overflow offset + // length. if (offset + length < offset) { return 0; } if (length > realLength - offset) { length = realLength - offset; } if (data) { // skip the stream to the part of the table we want to copy from stream->rewind(); size_t bytesToSkip = realOffset + offset; if (!skip(stream, bytesToSkip)) { return 0; } if (!read(stream, data, length)) { return 0; } } return length; } } return 0; }
int SkFontStream::GetTableTags(SkStream* stream, int ttcIndex, SkFontTableTag tags[]) { SfntHeader header; if (!header.init(stream, ttcIndex)) { return 0; } if (tags) { for (int i = 0; i < header.fCount; i++) { tags[i] = SkEndian_SwapBE32(header.fDir[i].fTag); } } return header.fCount; }
int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[]) { SkStream* stream = SkFontHost::OpenStream(fontID); if (NULL == stream) { return 0; } SkAutoUnref au(stream); SfntHeader header; if (!header.init(stream)) { return 0; } for (int i = 0; i < header.fCount; i++) { tags[i] = SkEndian_SwapBE32(header.fDir[i].fTag); } return header.fCount; }
size_t DWriteFontTypeface::onGetTableData(SkFontTableTag tag, size_t offset, size_t length, void* data) const { AutoDWriteTable table(fDWriteFontFace.get(), SkEndian_SwapBE32(tag)); if (!table.fExists) { return 0; } if (offset > table.fSize) { return 0; } size_t size = SkTMin(length, table.fSize - offset); if (data) { memcpy(data, table.fData + offset, size); } return size; }
SkData* SkOTUtils::RenameFont(SkStream* fontData, const char* fontName, int fontNameLen) { // Get the sfnt header. SkSFNTHeader sfntHeader; if (fontData->read(&sfntHeader, sizeof(sfntHeader)) < sizeof(sfntHeader)) { return NULL; } // Find the existing 'name' table. int tableIndex; SkSFNTHeader::TableDirectoryEntry tableEntry; int numTables = SkEndian_SwapBE16(sfntHeader.numTables); for (tableIndex = 0; tableIndex < numTables; ++tableIndex) { if (fontData->read(&tableEntry, sizeof(tableEntry)) < sizeof(tableEntry)) { return NULL; } if (SkOTTableName::TAG == tableEntry.tag) { break; } } if (tableIndex == numTables) { return NULL; } if (!fontData->rewind()) { return NULL; } // The required 'name' record types: Family, Style, Unique, Full and PostScript. const SkOTTableName::Record::NameID::Predefined::Value namesToCreate[] = { SkOTTableName::Record::NameID::Predefined::FontFamilyName, SkOTTableName::Record::NameID::Predefined::FontSubfamilyName, SkOTTableName::Record::NameID::Predefined::UniqueFontIdentifier, SkOTTableName::Record::NameID::Predefined::FullFontName, SkOTTableName::Record::NameID::Predefined::PostscriptName, }; const int namesCount = SK_ARRAY_COUNT(namesToCreate); // Copy the data, leaving out the old name table. // In theory, we could also remove the DSIG table if it exists. size_t nameTableLogicalSize = sizeof(SkOTTableName) + (namesCount * sizeof(SkOTTableName::Record)) + (fontNameLen * sizeof(wchar_t)); size_t nameTablePhysicalSize = (nameTableLogicalSize + 3) & ~3; // Rounded up to a multiple of 4. size_t oldNameTablePhysicalSize = (SkEndian_SwapBE32(tableEntry.logicalLength) + 3) & ~3; // Rounded up to a multiple of 4. size_t oldNameTableOffset = SkEndian_SwapBE32(tableEntry.offset); //originalDataSize is the size of the original data without the name table. size_t originalDataSize = fontData->getLength() - oldNameTablePhysicalSize; size_t newDataSize = originalDataSize + nameTablePhysicalSize; SK_OT_BYTE* data = static_cast<SK_OT_BYTE*>(sk_malloc_throw(newDataSize)); SkAutoTUnref<SkData> rewrittenFontData(SkData::NewFromMalloc(data, newDataSize)); if (fontData->read(data, oldNameTableOffset) < oldNameTableOffset) { return NULL; } if (fontData->skip(oldNameTablePhysicalSize) < oldNameTablePhysicalSize) { return NULL; } if (fontData->read(data + oldNameTableOffset, originalDataSize - oldNameTableOffset) < originalDataSize - oldNameTableOffset) { return NULL; } //Fix up the offsets of the directory entries after the old 'name' table entry. SkSFNTHeader::TableDirectoryEntry* currentEntry = reinterpret_cast<SkSFNTHeader::TableDirectoryEntry*>(data + sizeof(SkSFNTHeader)); SkSFNTHeader::TableDirectoryEntry* endEntry = currentEntry + numTables; SkSFNTHeader::TableDirectoryEntry* headTableEntry = NULL; for (; currentEntry < endEntry; ++currentEntry) { uint32_t oldOffset = SkEndian_SwapBE32(currentEntry->offset); if (oldOffset > oldNameTableOffset) { currentEntry->offset = SkEndian_SwapBE32(oldOffset - oldNameTablePhysicalSize); } if (SkOTTableHead::TAG == currentEntry->tag) { headTableEntry = currentEntry; } } // Make the table directory entry point to the new 'name' table. SkSFNTHeader::TableDirectoryEntry* nameTableEntry = reinterpret_cast<SkSFNTHeader::TableDirectoryEntry*>(data + sizeof(SkSFNTHeader)) + tableIndex; nameTableEntry->logicalLength = SkEndian_SwapBE32(nameTableLogicalSize); nameTableEntry->offset = SkEndian_SwapBE32(originalDataSize); // Write the new 'name' table after the original font data. SkOTTableName* nameTable = reinterpret_cast<SkOTTableName*>(data + originalDataSize); unsigned short stringOffset = sizeof(SkOTTableName) + (namesCount * sizeof(SkOTTableName::Record)); nameTable->format = SkOTTableName::format_0; nameTable->count = SkEndian_SwapBE16(namesCount); nameTable->stringOffset = SkEndian_SwapBE16(stringOffset); SkOTTableName::Record* nameRecords = reinterpret_cast<SkOTTableName::Record*>(data + originalDataSize + sizeof(SkOTTableName)); for (int i = 0; i < namesCount; ++i) { nameRecords[i].platformID.value = SkOTTableName::Record::PlatformID::Windows; nameRecords[i].encodingID.windows.value = SkOTTableName::Record::EncodingID::Windows::UnicodeBMPUCS2; nameRecords[i].languageID.windows.value = SkOTTableName::Record::LanguageID::Windows::English_UnitedStates; nameRecords[i].nameID.predefined.value = namesToCreate[i]; nameRecords[i].offset = SkEndian_SwapBE16(0); nameRecords[i].length = SkEndian_SwapBE16(fontNameLen * sizeof(wchar_t)); } SK_OT_USHORT* nameString = reinterpret_cast<SK_OT_USHORT*>(data + originalDataSize + stringOffset); for (int i = 0; i < fontNameLen; ++i) { nameString[i] = SkEndian_SwapBE16(fontName[i]); } unsigned char* logical = data + originalDataSize + nameTableLogicalSize; unsigned char* physical = data + originalDataSize + nameTablePhysicalSize; for (; logical < physical; ++logical) { *logical = 0; } // Update the table checksum in the directory entry. nameTableEntry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum(reinterpret_cast<SK_OT_ULONG*>(nameTable), nameTableLogicalSize)); // Update the checksum adjustment in the head table. if (headTableEntry) { size_t headTableOffset = SkEndian_SwapBE32(headTableEntry->offset); if (headTableOffset + sizeof(SkOTTableHead) < originalDataSize) { SkOTTableHead* headTable = reinterpret_cast<SkOTTableHead*>(data + headTableOffset); headTable->checksumAdjustment = SkEndian_SwapBE32(0); uint32_t unadjustedFontChecksum = SkOTUtils::CalcTableChecksum(reinterpret_cast<SK_OT_ULONG*>(data), originalDataSize + nameTablePhysicalSize); headTable->checksumAdjustment = SkEndian_SwapBE32(SkOTTableHead::fontChecksum - unadjustedFontChecksum); } } return rewrittenFontData.detach(); }