#ifdef SK_WIN_FONTMGR_NO_SIMULATIONS // Korean fonts Gulim, Dotum, Batang, Gungsuh have bitmap strikes that get // artifically emboldened by Windows without antialiasing. Korean users prefer // these over the synthetic boldening performed by Skia. So let's make an // exception for fonts with bitmap strikes and allow passing through Windows // simulations for those, until Skia provides more control over simulations in // font matching, see https://crbug.com/1258378 bool HasBitmapStrikes(const SkTScopedComPtr<IDWriteFont>& font) {
SkTScopedComPtr<IDWriteFontFace> fontFace;
HRB(font->CreateFontFace(&fontFace));
// Iterate calls to GetFirstMatchingFont incrementally removing bold or italic // styling that can trigger the simulations. Implementing it this way gets us a // IDWriteFont that can be used as before and has the correct information on its // own style. Stripping simulations from IDWriteFontFace is possible via // IDWriteFontList1, IDWriteFontFaceReference and CreateFontFace, but this way // we won't have a matching IDWriteFont which is still used in get_style().
HRESULT FirstMatchingFontWithoutSimulations(const SkTScopedComPtr<IDWriteFontFamily>& family,
DWriteStyle dwStyle,
SkTScopedComPtr<IDWriteFont>& font) { bool noSimulations = false; while (!noSimulations) {
SkTScopedComPtr<IDWriteFont> searchFont;
HR(family->GetFirstMatchingFont(
dwStyle.fWeight, dwStyle.fWidth, dwStyle.fSlant, &searchFont));
DWRITE_FONT_SIMULATIONS simulations = searchFont->GetSimulations(); // If we still get simulations even though we're not asking for bold or // italic, we can't help it and exit the loop.
//Check to see if the two fonts are identical.
HRB(are_same(cshFace->fDWriteFontFace.get(), ctxFace->fDWriteFontFace, same)); if (same) { return true;
}
//Check if the two fonts share the same loader and have the same key.
UINT32 cshNumFiles;
UINT32 ctxNumFiles;
HRB(cshFace->fDWriteFontFace->GetFiles(&cshNumFiles, nullptr));
HRB(ctxFace->fDWriteFontFace->GetFiles(&ctxNumFiles, nullptr)); if (cshNumFiles != ctxNumFiles) { returnfalse;
}
//TODO: better means than comparing name strings? //NOTE: .ttc and fake bold/italic will end up here.
SkTScopedComPtr<IDWriteLocalizedStrings> cshFamilyNames;
SkTScopedComPtr<IDWriteLocalizedStrings> cshFaceNames; if (cshFontFace3) {
HRB(cshFontFace3->GetFamilyNames(&cshFamilyNames));
HRB(cshFontFace3->GetFaceNames(&cshFaceNames));
} else {
HRB(cshFace->fDWriteFontFamily->GetFamilyNames(&cshFamilyNames));
HRB(cshFace->fDWriteFont->GetFaceNames(&cshFaceNames));
}
UINT32 cshFamilyNameLength;
UINT32 cshFaceNameLength;
HRB(cshFamilyNames->GetStringLength(0, &cshFamilyNameLength));
HRB(cshFaceNames->GetStringLength(0, &cshFaceNameLength));
UINT32 index; BOOL exists;
HRNM(fFontCollection->FindFamilyName(dwFamilyName.get(), &index, &exists), "Failed while finding family by name."); if (!exists) { return nullptr;
}
// IDWriteTextRenderer methods
SK_STDMETHODIMP DrawGlyphRun( void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY,
DWRITE_MEASURING_MODE measuringMode,
DWRITE_GLYPH_RUN const* glyphRun,
DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
IUnknown* clientDrawingEffect) override
{ if (!glyphRun->fontFace) {
HRM(E_INVALIDARG, "Glyph run without font face.");
}
SkTScopedComPtr<IDWriteFont> font;
HRM(fOuter->fFontCollection->GetFontFromFontFace(glyphRun->fontFace, &font), "Could not get font from font face.");
// It is possible that the font passed does not actually have the requested character, // due to no font being found and getting the fallback font. // Check that the font actually contains the requested character. BOOL exists;
HRM(font->HasCharacter(fCharacter, &exists), "Could not find character.");
if (exists) {
SkTScopedComPtr<IDWriteFontFamily> fontFamily;
HRM(font->GetFontFamily(&fontFamily), "Could not get family.");
fResolvedTypeface = fOuter->makeTypefaceFromDWriteFont(glyphRun->fontFace,
font.get(),
fontFamily.get());
fSimulations = font->GetSimulations();
}
SK_STDMETHODIMP_(DWRITE_READING_DIRECTION) GetParagraphReadingDirection() override { // TODO: this is also interesting. return DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
}
const SkSMallocWCHAR* dwBcp47;
SkSMallocWCHAR dwBcp47Local; if (bcp47Count < 1) {
dwBcp47 = &fLocaleName;
} else { // TODO: support fallback stack. // TODO: DirectWrite supports 'zh-CN' or 'zh-Hans', but 'zh' misses completely // and may produce a Japanese font.
HRN(sk_cstring_to_wchar(bcp47[bcp47Count - 1], &dwBcp47Local));
dwBcp47 = &dwBcp47Local;
}
if (fFontFallback) { return this->fallback(dwFamilyName, dwStyle, *dwBcp47, character, kDefaultSimulations);
}
// LayoutFallback may use the system font collection for fallback. return this->layoutFallback(dwFamilyName, dwStyle, *dwBcp47, character, kDefaultSimulations);
}
DWRITE_FONT_SIMULATIONS allowedSimulations = DWRITE_FONT_SIMULATIONS_NONE; if (request.syntheticBold.value_or(true)) {
allowedSimulations |= DWRITE_FONT_SIMULATIONS_BOLD;
} if (request.syntheticOblique.value_or(true)) {
allowedSimulations |= DWRITE_FONT_SIMULATIONS_OBLIQUE;
}
// Fake oblique slant is usually -10. There is a font "MV Boli" with post::italicAngle -16. // It is given a fake bold and fake oblique version in the system font collection. // IDWriteFontSet4::GetMatchingFonts appears to ignore fake oblique on already oblique fonts, // even if the font collection has a fake oblique version. // "MV Boli" in particular appears to set OS2::fsSelection::bit9 (OBLIQUE). // (In OS2::Version is 3 which means this bit should be ignored?)
SkTScopedComPtr<IDWriteFontSet4> matches;
HRNM(fontSet4->GetMatchingFonts(dwFamilyName,
dwModel.data(), request.model.size(), allowedSimulations,
&matches), "Failed to match");
if constexpr (false) {
UINT32 numMatches = matches->GetFontCount();
SkString modelString; for (size_t i = 0; i < request.model.size(); ++i) {
modelString.append(reinterpret_cast<constchar*>(&dwModel[i].axisTag), 4);
modelString.append(":");
modelString.appendScalar(dwModel[i].value);
modelString.append(" ");
}
SkDebugf("Matches for %s [%s]\n", request.familyName, modelString.c_str()); for (UINT32 i = 0; i < numMatches; ++i) {
SkTScopedComPtr<IDWriteFontFace5> fontFace; if (FAILED(matches->CreateFontFace(i, &fontFace))) {
SkDebugf("Failed to create face!"); continue;
}
DWRITE_FONT_SIMULATIONS simulations = fontFace->GetSimulations();
sk_sp<SkTypeface> tf = this->makeTypefaceFromDWriteFontFace(fontFace.get());
SkString name;
tf->getFamilyName(&name);
SkFontStyle s = tf->fontStyle();
SkDebugf("%s [%3d %d %d]%s%s,%s%s\n",
name.c_str(), s.weight(), s.width(), s.slant(),
tf->isSyntheticBold() ? "(B)" : "",
tf->isSyntheticOblique() ? "(O)" : "",
simulations & DWRITE_FONT_SIMULATIONS_BOLD ? "(b)" : "",
simulations & DWRITE_FONT_SIMULATIONS_OBLIQUE ? "(o)" : "");
SkFontArguments::VariationPosition::Coordinate coords[100];
tf->getVariationDesignPosition(SkSpan(coords, 100));
}
}
const SkSMallocWCHAR* dwBcp47;
SkSMallocWCHAR dwBcp47Local; if (request.bcp47.empty()) {
dwBcp47 = &fLocaleName;
} else { // TODO: support fallback stack. // TODO: DirectWrite supports 'zh-CN' or 'zh-Hans', but 'zh' misses completely // and may produce a Japanese font.
HRN(sk_cstring_to_wchar(request.bcp47.back(), &dwBcp47Local));
dwBcp47 = &dwBcp47Local;
}
DWRITE_FONT_SIMULATIONS allowedSimulations = DWRITE_FONT_SIMULATIONS_NONE; if (request.syntheticBold.value_or(true)) {
allowedSimulations |= DWRITE_FONT_SIMULATIONS_BOLD;
} if (request.syntheticOblique.value_or(true)) {
allowedSimulations |= DWRITE_FONT_SIMULATIONS_OBLIQUE;
}
if (fFontFallback) {
SkTScopedComPtr<IDWriteFontFallback1> fontFallback1;
fFontFallback->QueryInterface(&fontFallback1); if (fontFallback1) { bool italicRequested = false;
AutoSTMalloc<8, DWRITE_FONT_AXIS_VALUE> dwModel(request.model.size()); for (auto&& [axis, dwAxis] : SkMakeZip(request.model, dwModel.data())) {
dwAxis.axisTag = (DWRITE_FONT_AXIS_TAG)SkEndianSwap32(axis.axis);
dwAxis.value = axis.value;
DWriteStyle dwStyle(request.fontStyleFromModel());
SkUnichar character = 0x20; if (!request.cmapEntries.empty()) {
character = request.cmapEntries[0].character;
}
if (fFontFallback) { return this->fallback(dwFamilyName, dwStyle, *dwBcp47, character, allowedSimulations);
}
// LayoutFallback may use the system font collection for fallback. return this->layoutFallback(dwFamilyName, dwStyle, *dwBcp47, character, allowedSimulations);
}
// IDWriteFontFallback1::MapCharacters does not return a fake oblique on an italic request. // If italic was requested but result is not italic or oblique and synthetic oblique is allowed, // add the synthetic oblique to match previous behavior.
SkTScopedComPtr<IDWriteFontFace> fontFace;
HRNM(font->CreateFontFace(&fontFace), "Could not get font face from font.");
SkTScopedComPtr<IDWriteFontFamily> fontFamily;
HRNM(font->GetFontFamily(&fontFamily), "Could not get family from font."); return this->makeTypefaceFromDWriteFont(fontFace.get(), font.get(), fontFamily.get());
}
bool noSimulations = false;
sk_sp<SkTypeface> returnTypeface(nullptr); while (!noSimulations) {
SkTScopedComPtr<IDWriteTextFormat> fallbackFormat;
HRNM(fFactory->CreateTextFormat(dwFamilyName ? dwFamilyName : L"",
fFontCollection.get(),
dwStyle.fWeight,
dwStyle.fSlant,
dwStyle.fWidth, 72.0f,
dwBcp47,
&fallbackFormat), "Could not create text format.");
// No matter how the font collection is set on this IDWriteTextLayout, it is not possible to // disable use of the system font collection in fallback.
SkTScopedComPtr<IDWriteTextLayout> fallbackLayout;
HRNM(fFactory->CreateTextLayout(
str, strLen, fallbackFormat.get(), 200.0f, 200.0f, &fallbackLayout), "Could not create text layout.");
SkTScopedComPtr<FontFallbackRenderer> fontFallbackRenderer( new FontFallbackRenderer(this, character));
HRNM(fallbackLayout->SetFontCollection(fFontCollection.get(), {0, strLen}), "Could not set layout font collection.");
HRNM(fallbackLayout->Draw(nullptr, fontFallbackRenderer.get(), 50.0f, 50.0f), "Could not draw layout with renderer.");
if (!fontFamily) { if (fFontFallback) { return this->fallback(nullptr, dwStyle, fLocaleName.get(), 32, kDefaultSimulations);
} // SPI_GETNONCLIENTMETRICS lfMessageFont can fail in Win8. (DisallowWin32kSystemCalls) // layoutFallback causes DCHECK in Chromium. (Uses system font collection.)
HRNM(this->getByFamilyName(fDefaultFamilyName, &fontFamily), "Could not create DWrite font family from LOGFONT.");
}
if (!fontFamily) { // Could not obtain the default font.
HRNM(fFontCollection->GetFontFamily(0, &fontFamily), "Could not get default-default font family.");
}
SkTScopedComPtr<IDWriteFont> font;
HRNM(FirstMatchingFontWithoutSimulations(fontFamily, dwStyle, font), "No font found from family.");
SkTScopedComPtr<IDWriteFontFace> fontFace;
HRNM(font->CreateFontFace(&fontFace), "Could not create font face.");
void SkFontStyleSet_DirectWrite::getStyle(int index, SkFontStyle* fs, SkString* styleName) {
SkTScopedComPtr<IDWriteFont> font;
HRVM(fFontFamily->GetFont(index, &font), "Could not get font.");
if (fs) {
SkTScopedComPtr<IDWriteFontFace> face;
HRVM(font->CreateFontFace(&face), "Could not get face.");
*fs = DWriteFontTypeface::GetStyle(font.get(), face.get());
}
if (styleName) {
SkTScopedComPtr<IDWriteLocalizedStrings> faceNames; if (SUCCEEDED(font->GetFaceNames(&faceNames))) {
sk_get_locale_string(faceNames.get(), fFontMgr->fLocaleName.get(), styleName);
}
}
}
SkTScopedComPtr<IDWriteFontCollection> systemFontCollection; if (nullptr == collection) {
HRNM(factory->GetSystemFontCollection(&systemFontCollection, FALSE), "Could not get system font collection.");
collection = systemFontCollection.get();
}
// It is possible to have been provided a font fallback when factory2 is not available.
SkTScopedComPtr<IDWriteFontFallback> systemFontFallback; if (nullptr == fallback) {
SkTScopedComPtr<IDWriteFactory2> factory2; if (!SUCCEEDED(factory->QueryInterface(&factory2))) { // IUnknown::QueryInterface states that if it fails, punk will be set to nullptr. // http://blogs.msdn.com/b/oldnewthing/archive/2004/03/26/96777.aspx
SkASSERT_RELEASE(nullptr == factory2.get());
} else {
HRNM(factory2->GetSystemFontFallback(&systemFontFallback), "Could not get system fallback.");
fallback = systemFontFallback.get();
}
}
// Dynamically load GetUserDefaultLocaleName function, as it is not available on XP.
SkGetUserDefaultLocaleNameProc getUserDefaultLocaleNameProc = nullptr;
HRESULT hr = SkGetGetUserDefaultLocaleNameProc(&getUserDefaultLocaleNameProc); if (nullptr == getUserDefaultLocaleNameProc) {
SK_TRACEHR(hr, "Could not get GetUserDefaultLocaleName.");
} else { int size = getUserDefaultLocaleNameProc(localeNameStorage, LOCALE_NAME_MAX_LENGTH); if (size) {
localeName = localeNameStorage;
localeNameLen = size;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.