Avoid finding a font with simulations
When requesting a font match for fonts like Sylfaen or Gabriola, which absolutely do not have a bold or italic version, Windows silently produces DirectWrite simulations which synthetic bolding or synthetic italic or both. From Chromium's perspective, we clearly do not want Windows to produce silent simulations for us and this makes it impossible for us to determine what the font's actual weight or slant is. As a result, we are blocked from implementing the font-synthesis CSS property correctly. For the latter, we need to be able to programmatically enable setEmbolden() or setSkew() on SkFont and have Skia apply artifical synthesis, instead of Windows instantiating such a font silently. Addresses TODO to avoid simulations in SkFontStyleSet_DirectWrite::matchStyle. Bug: skia:12455 Cq-Include-Trybots: luci.skia.skia.primary:Test-Win10-Clang-NUC5i7RYH-CPU-AVX2-x86_64-Debug-All-NativeFonts,Test-Win10-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-All-NativeFonts,Test-Win7-Clang-Golo-CPU-AVX512-x86_64-Debug-All-NativeFonts,Test-Win7-Clang-Golo-CPU-AVX512-x86_64-Debug-All-NativeFonts_GDI Change-Id: If965d2501dff45ee7881544c6473b3fb36a8d90d Reviewed-on: https://skia-review.googlesource.com/c/skia/+/450676 Commit-Queue: Ben Wagner <bungeman@google.com> Reviewed-by: Ben Wagner <bungeman@google.com>
This commit is contained in:
parent
a106472197
commit
76a22af02a
@ -265,6 +265,52 @@ SK_STDMETHODIMP StreamFontCollectionLoader::CreateEnumeratorFromKey(
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifdef SK_WIN_FONTMGR_NO_SIMULATIONS
|
||||||
|
noSimulations = simulations == DWRITE_FONT_SIMULATIONS_NONE ||
|
||||||
|
(dwStyle.fWeight == DWRITE_FONT_WEIGHT_REGULAR &&
|
||||||
|
dwStyle.fSlant == DWRITE_FONT_STYLE_NORMAL);
|
||||||
|
#else
|
||||||
|
noSimulations = true;
|
||||||
|
#endif
|
||||||
|
if (noSimulations) {
|
||||||
|
font = std::move(searchFont);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (simulations & DWRITE_FONT_SIMULATIONS_BOLD) {
|
||||||
|
dwStyle.fWeight = DWRITE_FONT_WEIGHT_REGULAR;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (simulations & DWRITE_FONT_SIMULATIONS_OBLIQUE) {
|
||||||
|
dwStyle.fSlant = DWRITE_FONT_STYLE_NORMAL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
class SkFontMgr_DirectWrite : public SkFontMgr {
|
class SkFontMgr_DirectWrite : public SkFontMgr {
|
||||||
public:
|
public:
|
||||||
/** localeNameLength and defaultFamilyNameLength must include the null terminator. */
|
/** localeNameLength and defaultFamilyNameLength must include the null terminator. */
|
||||||
@ -585,6 +631,7 @@ public:
|
|||||||
fResolvedTypeface = fOuter->makeTypefaceFromDWriteFont(glyphRun->fontFace,
|
fResolvedTypeface = fOuter->makeTypefaceFromDWriteFont(glyphRun->fontFace,
|
||||||
font.get(),
|
font.get(),
|
||||||
fontFamily.get());
|
fontFamily.get());
|
||||||
|
fHasSimulations = font->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
@ -644,6 +691,8 @@ public:
|
|||||||
|
|
||||||
sk_sp<SkTypeface> ConsumeFallbackTypeface() { return std::move(fResolvedTypeface); }
|
sk_sp<SkTypeface> ConsumeFallbackTypeface() { return std::move(fResolvedTypeface); }
|
||||||
|
|
||||||
|
bool FallbackTypefaceHasSimulations() { return fHasSimulations; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual ~FontFallbackRenderer() { }
|
virtual ~FontFallbackRenderer() { }
|
||||||
|
|
||||||
@ -651,6 +700,7 @@ private:
|
|||||||
sk_sp<const SkFontMgr_DirectWrite> fOuter;
|
sk_sp<const SkFontMgr_DirectWrite> fOuter;
|
||||||
UINT32 fCharacter;
|
UINT32 fCharacter;
|
||||||
sk_sp<SkTypeface> fResolvedTypeface;
|
sk_sp<SkTypeface> fResolvedTypeface;
|
||||||
|
bool fHasSimulations{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
class FontFallbackSource : public IDWriteTextAnalysisSource {
|
class FontFallbackSource : public IDWriteTextAnalysisSource {
|
||||||
@ -756,9 +806,8 @@ private:
|
|||||||
SkTypeface* SkFontMgr_DirectWrite::onMatchFamilyStyleCharacter(const char familyName[],
|
SkTypeface* SkFontMgr_DirectWrite::onMatchFamilyStyleCharacter(const char familyName[],
|
||||||
const SkFontStyle& style,
|
const SkFontStyle& style,
|
||||||
const char* bcp47[], int bcp47Count,
|
const char* bcp47[], int bcp47Count,
|
||||||
SkUnichar character) const
|
SkUnichar character) const {
|
||||||
{
|
DWriteStyle dwStyle(style);
|
||||||
const DWriteStyle dwStyle(style);
|
|
||||||
|
|
||||||
const WCHAR* dwFamilyName = nullptr;
|
const WCHAR* dwFamilyName = nullptr;
|
||||||
SkSMallocWCHAR dwFamilyNameLocal;
|
SkSMallocWCHAR dwFamilyNameLocal;
|
||||||
@ -790,8 +839,7 @@ SkTypeface* SkFontMgr_DirectWrite::onMatchFamilyStyleCharacter(const char family
|
|||||||
sk_sp<SkTypeface> SkFontMgr_DirectWrite::fallback(const WCHAR* dwFamilyName,
|
sk_sp<SkTypeface> SkFontMgr_DirectWrite::fallback(const WCHAR* dwFamilyName,
|
||||||
DWriteStyle dwStyle,
|
DWriteStyle dwStyle,
|
||||||
const WCHAR* dwBcp47,
|
const WCHAR* dwBcp47,
|
||||||
UINT32 character) const
|
UINT32 character) const {
|
||||||
{
|
|
||||||
WCHAR str[16];
|
WCHAR str[16];
|
||||||
UINT32 strLen = SkTo<UINT32>(SkUTF::ToUTF16(character, reinterpret_cast<uint16_t*>(str)));
|
UINT32 strLen = SkTo<UINT32>(SkUTF::ToUTF16(character, reinterpret_cast<uint16_t*>(str)));
|
||||||
|
|
||||||
@ -809,20 +857,43 @@ sk_sp<SkTypeface> SkFontMgr_DirectWrite::fallback(const WCHAR* dwFamilyName,
|
|||||||
UINT32 mappedLength;
|
UINT32 mappedLength;
|
||||||
SkTScopedComPtr<IDWriteFont> font;
|
SkTScopedComPtr<IDWriteFont> font;
|
||||||
FLOAT scale;
|
FLOAT scale;
|
||||||
HRNM(fFontFallback->MapCharacters(fontFallbackSource.get(),
|
|
||||||
0, // textPosition,
|
bool noSimulations = false;
|
||||||
strLen,
|
while (!noSimulations) {
|
||||||
fFontCollection.get(),
|
font.reset();
|
||||||
dwFamilyName,
|
HRNM(fFontFallback->MapCharacters(fontFallbackSource.get(),
|
||||||
dwStyle.fWeight,
|
0, // textPosition,
|
||||||
dwStyle.fSlant,
|
strLen,
|
||||||
dwStyle.fWidth,
|
fFontCollection.get(),
|
||||||
&mappedLength,
|
dwFamilyName,
|
||||||
&font,
|
dwStyle.fWeight,
|
||||||
&scale),
|
dwStyle.fSlant,
|
||||||
"Could not map characters");
|
dwStyle.fWidth,
|
||||||
if (!font.get()) {
|
&mappedLength,
|
||||||
return nullptr;
|
&font,
|
||||||
|
&scale),
|
||||||
|
"Could not map characters");
|
||||||
|
if (!font.get()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWRITE_FONT_SIMULATIONS simulations = font->GetSimulations();
|
||||||
|
|
||||||
|
#ifdef SK_WIN_FONTMGR_NO_SIMULATIONS
|
||||||
|
noSimulations = simulations == DWRITE_FONT_SIMULATIONS_NONE;
|
||||||
|
#else
|
||||||
|
noSimulations = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (simulations & DWRITE_FONT_SIMULATIONS_BOLD) {
|
||||||
|
dwStyle.fWeight = DWRITE_FONT_WEIGHT_REGULAR;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (simulations & DWRITE_FONT_SIMULATIONS_OBLIQUE) {
|
||||||
|
dwStyle.fSlant = DWRITE_FONT_STYLE_NORMAL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SkTScopedComPtr<IDWriteFontFace> fontFace;
|
SkTScopedComPtr<IDWriteFontFace> fontFace;
|
||||||
@ -841,34 +912,57 @@ sk_sp<SkTypeface> SkFontMgr_DirectWrite::layoutFallback(const WCHAR* dwFamilyNam
|
|||||||
WCHAR str[16];
|
WCHAR str[16];
|
||||||
UINT32 strLen = SkTo<UINT32>(SkUTF::ToUTF16(character, reinterpret_cast<uint16_t*>(str)));
|
UINT32 strLen = SkTo<UINT32>(SkUTF::ToUTF16(character, reinterpret_cast<uint16_t*>(str)));
|
||||||
|
|
||||||
SkTScopedComPtr<IDWriteTextFormat> fallbackFormat;
|
bool noSimulations = false;
|
||||||
HRNM(fFactory->CreateTextFormat(dwFamilyName ? dwFamilyName : L"",
|
sk_sp<SkTypeface> returnTypeface(nullptr);
|
||||||
fFontCollection.get(),
|
while (!noSimulations) {
|
||||||
dwStyle.fWeight,
|
SkTScopedComPtr<IDWriteTextFormat> fallbackFormat;
|
||||||
dwStyle.fSlant,
|
HRNM(fFactory->CreateTextFormat(dwFamilyName ? dwFamilyName : L"",
|
||||||
dwStyle.fWidth,
|
fFontCollection.get(),
|
||||||
72.0f,
|
dwStyle.fWeight,
|
||||||
dwBcp47,
|
dwStyle.fSlant,
|
||||||
&fallbackFormat),
|
dwStyle.fWidth,
|
||||||
"Could not create text format.");
|
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
|
// 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.
|
// disable use of the system font collection in fallback.
|
||||||
SkTScopedComPtr<IDWriteTextLayout> fallbackLayout;
|
SkTScopedComPtr<IDWriteTextLayout> fallbackLayout;
|
||||||
HRNM(fFactory->CreateTextLayout(str, strLen, fallbackFormat.get(),
|
HRNM(fFactory->CreateTextLayout(
|
||||||
200.0f, 200.0f,
|
str, strLen, fallbackFormat.get(), 200.0f, 200.0f, &fallbackLayout),
|
||||||
&fallbackLayout),
|
"Could not create text layout.");
|
||||||
"Could not create text layout.");
|
|
||||||
|
|
||||||
SkTScopedComPtr<FontFallbackRenderer> fontFallbackRenderer(
|
SkTScopedComPtr<FontFallbackRenderer> fontFallbackRenderer(
|
||||||
new FontFallbackRenderer(this, character));
|
new FontFallbackRenderer(this, character));
|
||||||
|
|
||||||
HRNM(fallbackLayout->SetFontCollection(fFontCollection.get(), { 0, strLen }),
|
HRNM(fallbackLayout->SetFontCollection(fFontCollection.get(), {0, strLen}),
|
||||||
"Could not set layout font collection.");
|
"Could not set layout font collection.");
|
||||||
HRNM(fallbackLayout->Draw(nullptr, fontFallbackRenderer.get(), 50.0f, 50.0f),
|
HRNM(fallbackLayout->Draw(nullptr, fontFallbackRenderer.get(), 50.0f, 50.0f),
|
||||||
"Could not draw layout with renderer.");
|
"Could not draw layout with renderer.");
|
||||||
|
|
||||||
return fontFallbackRenderer->ConsumeFallbackTypeface();
|
#ifdef SK_WIN_FONTMGR_NO_SIMULATIONS
|
||||||
|
noSimulations = !fontFallbackRenderer->FallbackTypefaceHasSimulations();
|
||||||
|
#else
|
||||||
|
noSimulations = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (noSimulations) {
|
||||||
|
returnTypeface = fontFallbackRenderer->ConsumeFallbackTypeface();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dwStyle.fWeight != DWRITE_FONT_WEIGHT_REGULAR) {
|
||||||
|
dwStyle.fWeight = DWRITE_FONT_WEIGHT_REGULAR;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dwStyle.fSlant != DWRITE_FONT_STYLE_NORMAL) {
|
||||||
|
dwStyle.fSlant = DWRITE_FONT_STYLE_NORMAL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnTypeface;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> class SkAutoIDWriteUnregister {
|
template <typename T> class SkAutoIDWriteUnregister {
|
||||||
@ -1032,13 +1126,14 @@ HRESULT SkFontMgr_DirectWrite::getByFamilyName(const WCHAR wideFamilyName[],
|
|||||||
sk_sp<SkTypeface> SkFontMgr_DirectWrite::onLegacyMakeTypeface(const char familyName[],
|
sk_sp<SkTypeface> SkFontMgr_DirectWrite::onLegacyMakeTypeface(const char familyName[],
|
||||||
SkFontStyle style) const {
|
SkFontStyle style) const {
|
||||||
SkTScopedComPtr<IDWriteFontFamily> fontFamily;
|
SkTScopedComPtr<IDWriteFontFamily> fontFamily;
|
||||||
const DWriteStyle dwStyle(style);
|
DWriteStyle dwStyle(style);
|
||||||
if (familyName) {
|
if (familyName) {
|
||||||
SkSMallocWCHAR dwFamilyName;
|
SkSMallocWCHAR dwFamilyName;
|
||||||
if (SUCCEEDED(sk_cstring_to_wchar(familyName, &dwFamilyName))) {
|
if (SUCCEEDED(sk_cstring_to_wchar(familyName, &dwFamilyName))) {
|
||||||
this->getByFamilyName(dwFamilyName, &fontFamily);
|
this->getByFamilyName(dwFamilyName, &fontFamily);
|
||||||
if (!fontFamily && fFontFallback) {
|
if (!fontFamily && fFontFallback) {
|
||||||
return this->fallback(dwFamilyName, dwStyle, fLocaleName.get(), 32);
|
return this->fallback(
|
||||||
|
dwFamilyName, dwStyle, fLocaleName.get(), 32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1060,8 +1155,8 @@ sk_sp<SkTypeface> SkFontMgr_DirectWrite::onLegacyMakeTypeface(const char familyN
|
|||||||
}
|
}
|
||||||
|
|
||||||
SkTScopedComPtr<IDWriteFont> font;
|
SkTScopedComPtr<IDWriteFont> font;
|
||||||
HRNM(fontFamily->GetFirstMatchingFont(dwStyle.fWeight, dwStyle.fWidth, dwStyle.fSlant, &font),
|
HRNM(FirstMatchingFontWithoutSimulations(fontFamily, dwStyle, font),
|
||||||
"Could not get matching font.");
|
"No font found from family.");
|
||||||
|
|
||||||
SkTScopedComPtr<IDWriteFontFace> fontFace;
|
SkTScopedComPtr<IDWriteFontFace> fontFace;
|
||||||
HRNM(font->CreateFontFace(&fontFace), "Could not create font face.");
|
HRNM(font->CreateFontFace(&fontFace), "Could not create font face.");
|
||||||
@ -1104,9 +1199,9 @@ void SkFontStyleSet_DirectWrite::getStyle(int index, SkFontStyle* fs, SkString*
|
|||||||
SkTypeface* SkFontStyleSet_DirectWrite::matchStyle(const SkFontStyle& pattern) {
|
SkTypeface* SkFontStyleSet_DirectWrite::matchStyle(const SkFontStyle& pattern) {
|
||||||
SkTScopedComPtr<IDWriteFont> font;
|
SkTScopedComPtr<IDWriteFont> font;
|
||||||
DWriteStyle dwStyle(pattern);
|
DWriteStyle dwStyle(pattern);
|
||||||
// TODO: perhaps use GetMatchingFonts and get the least simulated?
|
|
||||||
HRNM(fFontFamily->GetFirstMatchingFont(dwStyle.fWeight, dwStyle.fWidth, dwStyle.fSlant, &font),
|
HRNM(FirstMatchingFontWithoutSimulations(fFontFamily, dwStyle, font),
|
||||||
"Could not match font in family.");
|
"No font found from family.");
|
||||||
|
|
||||||
SkTScopedComPtr<IDWriteFontFace> fontFace;
|
SkTScopedComPtr<IDWriteFontFace> fontFace;
|
||||||
HRNM(font->CreateFontFace(&fontFace), "Could not create font face.");
|
HRNM(font->CreateFontFace(&fontFace), "Could not create font face.");
|
||||||
|
Loading…
Reference in New Issue
Block a user