void SkScalerContext_DW::generateFontMetrics(SkPaint::FontMetrics* metrics) { if (NULL == metrics) { return; } sk_bzero(metrics, sizeof(*metrics)); DWRITE_FONT_METRICS dwfm; if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode || DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode) { fTypeface->fDWriteFontFace->GetGdiCompatibleMetrics( fTextSizeRender, 1.0f, // pixelsPerDip &fXform, &dwfm); } else { fTypeface->fDWriteFontFace->GetMetrics(&dwfm); } SkScalar upem = SkIntToScalar(dwfm.designUnitsPerEm); metrics->fAscent = -fTextSizeRender * SkIntToScalar(dwfm.ascent) / upem; metrics->fDescent = fTextSizeRender * SkIntToScalar(dwfm.descent) / upem; metrics->fLeading = fTextSizeRender * SkIntToScalar(dwfm.lineGap) / upem; metrics->fXHeight = fTextSizeRender * SkIntToScalar(dwfm.xHeight) / upem; metrics->fUnderlineThickness = fTextSizeRender * SkIntToScalar(dwfm.underlineThickness) / upem; metrics->fUnderlinePosition = -(fTextSizeRender * SkIntToScalar(dwfm.underlinePosition) / upem); metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag; metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag; if (fTypeface->fDWriteFontFace1.get()) { DWRITE_FONT_METRICS1 dwfm1; fTypeface->fDWriteFontFace1->GetMetrics(&dwfm1); metrics->fTop = -fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxTop) / upem; metrics->fBottom = -fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxBottom) / upem; metrics->fXMin = fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxLeft) / upem; metrics->fXMax = fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxRight) / upem; metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin; } else { AutoTDWriteTable<SkOTTableHead> head(fTypeface->fDWriteFontFace.get()); if (head.fExists && head.fSize >= sizeof(SkOTTableHead) && head->version == SkOTTableHead::version1) { metrics->fTop = -fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->yMax) / upem; metrics->fBottom = -fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->yMin) / upem; metrics->fXMin = fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->xMin) / upem; metrics->fXMax = fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->xMax) / upem; metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin; } else { metrics->fTop = metrics->fAscent; metrics->fBottom = metrics->fDescent; } } }
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); }
/** 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); }
static SkUnichar SkUTF16BE_NextUnichar(const uint16_t** srcPtr) { SkASSERT(srcPtr && *srcPtr); const uint16_t* src = *srcPtr; SkUnichar c = SkEndian_SwapBE16(*src++); SkASSERT(!SkUTF16_IsLowSurrogate(c)); if (SkUTF16_IsHighSurrogate(c)) { unsigned c2 = SkEndian_SwapBE16(*src++); SkASSERT(SkUTF16_IsLowSurrogate(c2)); c = (c << 10) + c2 + (0x10000 - (0xD800 << 10) - 0xDC00); } *srcPtr = src; return c; }
/** 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); }
SkAdvancedTypefaceMetrics* DWriteFontTypeface::onGetAdvancedTypefaceMetrics( PerGlyphInfo perGlyphInfo, const uint32_t* glyphIDs, uint32_t glyphIDsCount) const { SkAdvancedTypefaceMetrics* info = nullptr; HRESULT hr = S_OK; const unsigned glyphCount = fDWriteFontFace->GetGlyphCount(); DWRITE_FONT_METRICS dwfm; fDWriteFontFace->GetMetrics(&dwfm); info = new SkAdvancedTypefaceMetrics; info->fEmSize = dwfm.designUnitsPerEm; info->fLastGlyphID = SkToU16(glyphCount - 1); info->fAscent = SkToS16(dwfm.ascent); info->fDescent = SkToS16(dwfm.descent); info->fCapHeight = SkToS16(dwfm.capHeight); // SkAdvancedTypefaceMetrics::fFontName is in theory supposed to be // the PostScript name of the font. However, due to the way it is currently // used, it must actually be a family name. SkTScopedComPtr<IDWriteLocalizedStrings> familyNames; hr = fDWriteFontFamily->GetFamilyNames(&familyNames); UINT32 familyNameLen; hr = familyNames->GetStringLength(0, &familyNameLen); SkSMallocWCHAR familyName(familyNameLen+1); hr = familyNames->GetString(0, familyName.get(), familyNameLen+1); hr = sk_wchar_to_skstring(familyName.get(), familyNameLen, &info->fFontName); if (perGlyphInfo & kToUnicode_PerGlyphInfo) { populate_glyph_to_unicode(fDWriteFontFace.get(), glyphCount, &(info->fGlyphToUnicode)); } DWRITE_FONT_FACE_TYPE fontType = fDWriteFontFace->GetType(); if (fontType != DWRITE_FONT_FACE_TYPE_TRUETYPE && fontType != DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION) { return info; } // Simulated fonts aren't really TrueType fonts. if (fDWriteFontFace->GetSimulations() == DWRITE_FONT_SIMULATIONS_NONE) { info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font; } AutoTDWriteTable<SkOTTableHead> headTable(fDWriteFontFace.get()); AutoTDWriteTable<SkOTTablePostScript> postTable(fDWriteFontFace.get()); AutoTDWriteTable<SkOTTableHorizontalHeader> hheaTable(fDWriteFontFace.get()); AutoTDWriteTable<SkOTTableOS2> os2Table(fDWriteFontFace.get()); if (!headTable.fExists || !postTable.fExists || !hheaTable.fExists || !os2Table.fExists) { return info; } //There exist CJK fonts which set the IsFixedPitch and Monospace bits, //but have full width, latin half-width, and half-width kana. bool fixedWidth = (postTable->isFixedPitch && (1 == SkEndian_SwapBE16(hheaTable->numberOfHMetrics))); //Monospace if (fixedWidth) { info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style; } //Italic if (os2Table->version.v0.fsSelection.field.Italic) { info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style; } //Serif using SerifStyle = SkPanose::Data::TextAndDisplay::SerifStyle; SerifStyle serifStyle = os2Table->version.v0.panose.data.textAndDisplay.bSerifStyle; if (SkPanose::FamilyType::TextAndDisplay == os2Table->version.v0.panose.bFamilyType) { if (SerifStyle::Cove == serifStyle || SerifStyle::ObtuseCove == serifStyle || SerifStyle::SquareCove == serifStyle || SerifStyle::ObtuseSquareCove == serifStyle || SerifStyle::Square == serifStyle || SerifStyle::Thin == serifStyle || SerifStyle::Bone == serifStyle || SerifStyle::Exaggerated == serifStyle || SerifStyle::Triangle == serifStyle) { info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style; } //Script } else if (SkPanose::FamilyType::Script == os2Table->version.v0.panose.bFamilyType) { info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style; } info->fItalicAngle = SkEndian_SwapBE32(postTable->italicAngle) >> 16; info->fBBox = SkIRect::MakeLTRB((int32_t)SkEndian_SwapBE16((uint16_t)headTable->xMin), (int32_t)SkEndian_SwapBE16((uint16_t)headTable->yMax), (int32_t)SkEndian_SwapBE16((uint16_t)headTable->xMax), (int32_t)SkEndian_SwapBE16((uint16_t)headTable->yMin)); return info; }
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(); }
bool SkOTTableName::Iterator::next(SkOTTableName::Iterator::Record& record) { const size_t nameRecordsCount = SkEndian_SwapBE16(fName.count); const SkOTTableName::Record* nameRecords = SkTAfter<const SkOTTableName::Record>(&fName); const SkOTTableName::Record* nameRecord; // Find the next record which matches the requested type. do { if (fIndex >= nameRecordsCount) { return false; } nameRecord = &nameRecords[fIndex]; ++fIndex; } while (fType != -1 && nameRecord->nameID.fontSpecific != fType); record.type = nameRecord->nameID.fontSpecific; const uint16_t stringTableOffset = SkEndian_SwapBE16(fName.stringOffset); const char* stringTable = SkTAddOffset<const char>(&fName, stringTableOffset); // Decode the name into UTF-8. const uint16_t nameOffset = SkEndian_SwapBE16(nameRecord->offset); const uint16_t nameLength = SkEndian_SwapBE16(nameRecord->length); const char* nameString = SkTAddOffset<const char>(stringTable, nameOffset); switch (nameRecord->platformID.value) { case SkOTTableName::Record::PlatformID::Windows: if (SkOTTableName::Record::EncodingID::Windows::UnicodeBMPUCS2 != nameRecord->encodingID.windows.value && SkOTTableName::Record::EncodingID::Windows::UnicodeUCS4 != nameRecord->encodingID.windows.value && SkOTTableName::Record::EncodingID::Windows::Symbol != nameRecord->encodingID.windows.value) { record.name.reset(); break; } case SkOTTableName::Record::PlatformID::Unicode: case SkOTTableName::Record::PlatformID::ISO: SkStringFromUTF16BE((const uint16_t*)nameString, nameLength, record.name); break; case SkOTTableName::Record::PlatformID::Macintosh: // TODO: need better decoding, especially on Mac. if (SkOTTableName::Record::EncodingID::Macintosh::Roman != nameRecord->encodingID.macintosh.value) { record.name.reset(); break; } SkStringFromMacRoman((const uint8_t*)nameString, nameLength, record.name); break; case SkOTTableName::Record::PlatformID::Custom: // These should never appear in a 'name' table. default: SkASSERT(false); record.name.reset(); break; } // Determine the language. const uint16_t languageID = SkEndian_SwapBE16(nameRecord->languageID.languageTagID); // Handle format 1 languages. if (SkOTTableName::format_1 == fName.format && languageID >= 0x8000) { const uint16_t languageTagRecordIndex = languageID - 0x8000; const SkOTTableName::Format1Ext* format1ext = SkTAfter<const SkOTTableName::Format1Ext>(nameRecords, nameRecordsCount); if (languageTagRecordIndex < SkEndian_SwapBE16(format1ext->langTagCount)) { const SkOTTableName::Format1Ext::LangTagRecord* languageTagRecord = SkTAfter<const SkOTTableName::Format1Ext::LangTagRecord>(format1ext); uint16_t offset = SkEndian_SwapBE16(languageTagRecord[languageTagRecordIndex].offset); uint16_t length = SkEndian_SwapBE16(languageTagRecord[languageTagRecordIndex].length); const uint16_t* string = SkTAddOffset<const uint16_t>(stringTable, offset); SkStringFromUTF16BE(string, length, record.language); return true; } } // Handle format 0 languages, translating them into BCP 47. const BCP47FromLanguageId target = { languageID, "" }; int languageIndex = SkTSearch<BCP47FromLanguageId, BCP47FromLanguageIdLess>( BCP47FromLanguageID, SK_ARRAY_COUNT(BCP47FromLanguageID), target, sizeof(target)); if (languageIndex >= 0) { record.language = BCP47FromLanguageID[languageIndex].bcp47; return true; } // Unknown language, return the BCP 47 code 'und' for 'undetermined'. SkASSERT(false); record.language = "und"; return true; }
SkAdvancedTypefaceMetrics* DWriteFontTypeface::onGetAdvancedTypefaceMetrics( PerGlyphInfo perGlyphInfo, const uint32_t* glyphIDs, uint32_t glyphIDsCount) const { SkAdvancedTypefaceMetrics* info = nullptr; HRESULT hr = S_OK; const unsigned glyphCount = fDWriteFontFace->GetGlyphCount(); DWRITE_FONT_METRICS dwfm; fDWriteFontFace->GetMetrics(&dwfm); info = new SkAdvancedTypefaceMetrics; info->fEmSize = dwfm.designUnitsPerEm; info->fLastGlyphID = SkToU16(glyphCount - 1); // SkAdvancedTypefaceMetrics::fFontName is in theory supposed to be // the PostScript name of the font. However, due to the way it is currently // used, it must actually be a family name. SkTScopedComPtr<IDWriteLocalizedStrings> familyNames; hr = fDWriteFontFamily->GetFamilyNames(&familyNames); UINT32 familyNameLen; hr = familyNames->GetStringLength(0, &familyNameLen); SkSMallocWCHAR familyName(familyNameLen+1); hr = familyNames->GetString(0, familyName.get(), familyNameLen+1); hr = sk_wchar_to_skstring(familyName.get(), familyNameLen, &info->fFontName); if (perGlyphInfo & kToUnicode_PerGlyphInfo) { populate_glyph_to_unicode(fDWriteFontFace.get(), glyphCount, &(info->fGlyphToUnicode)); } DWRITE_FONT_FACE_TYPE fontType = fDWriteFontFace->GetType(); if (fontType == DWRITE_FONT_FACE_TYPE_TRUETYPE || fontType == DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION) { info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font; } else { info->fAscent = dwfm.ascent; info->fDescent = dwfm.descent; info->fCapHeight = dwfm.capHeight; return info; } AutoTDWriteTable<SkOTTableHead> headTable(fDWriteFontFace.get()); AutoTDWriteTable<SkOTTablePostScript> postTable(fDWriteFontFace.get()); AutoTDWriteTable<SkOTTableHorizontalHeader> hheaTable(fDWriteFontFace.get()); AutoTDWriteTable<SkOTTableOS2> os2Table(fDWriteFontFace.get()); if (!headTable.fExists || !postTable.fExists || !hheaTable.fExists || !os2Table.fExists) { info->fAscent = dwfm.ascent; info->fDescent = dwfm.descent; info->fCapHeight = dwfm.capHeight; return info; } //There exist CJK fonts which set the IsFixedPitch and Monospace bits, //but have full width, latin half-width, and half-width kana. bool fixedWidth = (postTable->isFixedPitch && (1 == SkEndian_SwapBE16(hheaTable->numberOfHMetrics))); //Monospace if (fixedWidth) { info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style; } //Italic if (os2Table->version.v0.fsSelection.field.Italic) { info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style; } //Script if (SkPanose::FamilyType::Script == os2Table->version.v0.panose.bFamilyType.value) { info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style; //Serif } else if (SkPanose::FamilyType::TextAndDisplay == os2Table->version.v0.panose.bFamilyType.value && SkPanose::Data::TextAndDisplay::SerifStyle::Triangle <= os2Table->version.v0.panose.data.textAndDisplay.bSerifStyle.value && SkPanose::Data::TextAndDisplay::SerifStyle::NoFit != os2Table->version.v0.panose.data.textAndDisplay.bSerifStyle.value) { info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style; } info->fItalicAngle = SkEndian_SwapBE32(postTable->italicAngle) >> 16; info->fAscent = SkToS16(dwfm.ascent); info->fDescent = SkToS16(dwfm.descent); info->fCapHeight = SkToS16(dwfm.capHeight); info->fBBox = SkIRect::MakeLTRB((int32_t)SkEndian_SwapBE16((uint16_t)headTable->xMin), (int32_t)SkEndian_SwapBE16((uint16_t)headTable->yMax), (int32_t)SkEndian_SwapBE16((uint16_t)headTable->xMax), (int32_t)SkEndian_SwapBE16((uint16_t)headTable->yMin)); //TODO: is this even desired? It seems PDF only wants this value for Type1 //fonts, and we only get here for TrueType fonts. info->fStemV = 0; /* // Figure out a good guess for StemV - Min width of i, I, !, 1. // This probably isn't very good with an italic font. int16_t min_width = SHRT_MAX; info->fStemV = 0; char stem_chars[] = {'i', 'I', '!', '1'}; for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) { ABC abcWidths; if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) { int16_t width = abcWidths.abcB; if (width > 0 && width < min_width) { min_width = width; info->fStemV = min_width; } } } */ if (perGlyphInfo & kHAdvance_PerGlyphInfo) { if (fixedWidth) { SkAdvancedTypefaceMetrics::WidthRange range(0); int16_t advance; getWidthAdvance(fDWriteFontFace.get(), 1, &advance); range.fAdvance.append(1, &advance); SkAdvancedTypefaceMetrics::FinishRange( &range, 0, SkAdvancedTypefaceMetrics::WidthRange::kDefault); info->fGlyphWidths.emplace_back(std::move(range)); } else { IDWriteFontFace* borrowedFontFace = fDWriteFontFace.get(); info->setGlyphWidths( glyphCount, glyphIDs, glyphIDsCount, SkAdvancedTypefaceMetrics::GetAdvance([borrowedFontFace](int gId, int16_t* data) { return getWidthAdvance(borrowedFontFace, gId, data); }) ); } } return info; }