| /* | |
| * Copyright 2014 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "src/utils/win/SkDWriteNTDDI_VERSION.h" | |
| #include "include/core/SkTypes.h" | |
| #if defined(SK_BUILD_FOR_WIN) | |
| #include "include/core/SkFontMgr.h" | |
| #include "include/core/SkStream.h" | |
| #include "include/core/SkTypeface.h" | |
| #include "include/core/SkTypes.h" | |
| #include "include/private/SkMutex.h" | |
| #include "include/private/SkTPin.h" | |
| #include "src/core/SkEndian.h" | |
| #include "src/core/SkFontDescriptor.h" | |
| #include "src/core/SkTypefaceCache.h" | |
| #include "src/core/SkUTF.h" | |
| #include "src/core/SkZip.h" | |
| #include "src/ports/SkTypeface_win_dw.h" | |
| #include "src/utils/win/SkDWrite.h" | |
| #include "src/utils/win/SkDWriteFontFileStream.h" | |
| #include "src/utils/win/SkHRESULT.h" | |
| #include "src/utils/win/SkObjBase.h" | |
| #include "src/utils/win/SkTScopedComPtr.h" | |
| #include <dwrite.h> | |
| #include <dwrite_2.h> | |
| #include <dwrite_3.h> | |
| using namespace skia_private; | |
| namespace { | |
| #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)); | |
| AutoDWriteTable ebdtTable(fontFace.get(), DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'D', 'T')); | |
| return ebdtTable.fExists; | |
| } | |
| #endif | |
| // 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) || | |
| HasBitmapStrikes(searchFont); | |
| #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 { | |
| public: | |
| /** localeNameLength and defaultFamilyNameLength must include the null terminator. */ | |
| SkFontMgr_DirectWrite(IDWriteFactory* factory, IDWriteFontCollection* fontCollection, | |
| IDWriteFontFallback* fallback, | |
| const WCHAR* localeName, int localeNameLength, | |
| const WCHAR* defaultFamilyName, int defaultFamilyNameLength) | |
| : fFactory(SkRefComPtr(factory)) | |
| , fFontFallback(SkSafeRefComPtr(fallback)) | |
| , fFontCollection(SkRefComPtr(fontCollection)) | |
| , fLocaleName(localeNameLength) | |
| , fDefaultFamilyName(defaultFamilyNameLength) | |
| { | |
| memcpy(fLocaleName.get(), localeName, localeNameLength * sizeof(WCHAR)); | |
| memcpy(fDefaultFamilyName.get(), defaultFamilyName, defaultFamilyNameLength*sizeof(WCHAR)); | |
| } | |
| protected: | |
| int onCountFamilies() const override; | |
| void onGetFamilyName(int index, SkString* familyName) const override; | |
| sk_sp<SkFontStyleSet> onCreateStyleSet(int index) const override; | |
| sk_sp<SkFontStyleSet> onMatchFamily(const char familyName[]) const override; | |
| sk_sp<SkTypeface> onMatchFamilyStyle(const char familyName[], | |
| const SkFontStyle& fontstyle) const override; | |
| sk_sp<SkTypeface> onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&, | |
| const char* bcp47[], int bcp47Count, | |
| SkUnichar character) const override; | |
| sk_sp<SkTypeface> onMatch(const Request&) const override; | |
| sk_sp<SkTypeface> onFallback(const Request&) const override; | |
| sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset>, int ttcIndex) const override; | |
| sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset>, const SkFontArguments&) const override; | |
| sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData>, int ttcIndex) const override; | |
| sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override; | |
| sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle) const override; | |
| private: | |
| HRESULT getByFamilyName(const WCHAR familyName[], IDWriteFontFamily** fontFamily) const; | |
| sk_sp<SkTypeface> fallback1(IDWriteFontFallback1* fontFallback1, | |
| const WCHAR* dwFamilyName, | |
| SkSpan<const DWRITE_FONT_AXIS_VALUE> model, | |
| bool italicRequested, | |
| const WCHAR* dwBcp47, | |
| SkSpan<const SkFontMgr::Request::CMapEntry> cmapEntries, | |
| DWRITE_FONT_SIMULATIONS allowedSimulations) const; | |
| sk_sp<SkTypeface> fallback(const WCHAR* dwFamilyName, DWriteStyle, | |
| const WCHAR* dwBcp47, UINT32 character, | |
| DWRITE_FONT_SIMULATIONS allowedSimulations) const; | |
| sk_sp<SkTypeface> layoutFallback(const WCHAR* dwFamilyName, DWriteStyle, | |
| const WCHAR* dwBcp47, UINT32 character, | |
| DWRITE_FONT_SIMULATIONS allowedSimulations) const; | |
| static constexpr DWRITE_FONT_SIMULATIONS kDefaultSimulations = | |
| DWRITE_FONT_SIMULATIONS_BOLD | DWRITE_FONT_SIMULATIONS_OBLIQUE; | |
| /** Creates a typeface using a typeface cache. */ | |
| sk_sp<SkTypeface> makeTypefaceFromDWriteFont(IDWriteFontFace* fontFace, | |
| IDWriteFont* font, | |
| IDWriteFontFamily* fontFamily) const; | |
| sk_sp<SkTypeface> makeTypefaceFromDWriteFontFace(IDWriteFontFace3* fontFace) const; | |
| SkTScopedComPtr<IDWriteFactory> fFactory; | |
| SkTScopedComPtr<IDWriteFontFallback> fFontFallback; | |
| SkTScopedComPtr<IDWriteFontCollection> fFontCollection; | |
| SkSMallocWCHAR fLocaleName; | |
| SkSMallocWCHAR fDefaultFamilyName; | |
| mutable SkMutex fTFCacheMutex; | |
| mutable SkTypefaceCache fTFCache; | |
| friend class SkFontStyleSet_DirectWrite; | |
| friend class FontFallbackRenderer; | |
| }; | |
| class SkFontStyleSet_DirectWrite : public SkFontStyleSet { | |
| public: | |
| SkFontStyleSet_DirectWrite(const SkFontMgr_DirectWrite* fontMgr, | |
| IDWriteFontFamily* fontFamily) | |
| : fFontMgr(SkRef(fontMgr)) | |
| , fFontFamily(SkRefComPtr(fontFamily)) | |
| { } | |
| int count() override; | |
| void getStyle(int index, SkFontStyle* fs, SkString* styleName) override; | |
| sk_sp<SkTypeface> createTypeface(int index) override; | |
| sk_sp<SkTypeface> matchStyle(const SkFontStyle& pattern) override; | |
| private: | |
| sk_sp<const SkFontMgr_DirectWrite> fFontMgr; | |
| SkTScopedComPtr<IDWriteFontFamily> fFontFamily; | |
| }; | |
| static HRESULT are_same(IUnknown* a, IUnknown* b, bool& same) { | |
| SkTScopedComPtr<IUnknown> iunkA; | |
| HRM(a->QueryInterface(&iunkA), "Failed to QI<IUnknown> for a."); | |
| SkTScopedComPtr<IUnknown> iunkB; | |
| HRM(b->QueryInterface(&iunkB), "Failed to QI<IUnknown> for b."); | |
| same = (iunkA.get() == iunkB.get()); | |
| return S_OK; | |
| } | |
| struct ProtoDWriteTypeface { | |
| IDWriteFontFace* fDWriteFontFace; | |
| IDWriteFont* fDWriteFont; | |
| IDWriteFontFamily* fDWriteFontFamily; | |
| }; | |
| static bool FindByDWriteFont(SkTypeface* cached, void* ctx) { | |
| DWriteFontTypeface* cshFace = reinterpret_cast<DWriteFontTypeface*>(cached); | |
| ProtoDWriteTypeface* ctxFace = reinterpret_cast<ProtoDWriteTypeface*>(ctx); | |
| // IDWriteFontFace5 introduced both Equals and HasVariations | |
| SkTScopedComPtr<IDWriteFontFace5> cshFontFace5; | |
| SkTScopedComPtr<IDWriteFontFace5> ctxFontFace5; | |
| cshFace->fDWriteFontFace->QueryInterface(&cshFontFace5); | |
| ctxFace->fDWriteFontFace->QueryInterface(&ctxFontFace5); | |
| if (cshFontFace5 && ctxFontFace5) { | |
| return cshFontFace5->Equals(ctxFontFace5.get()); | |
| } | |
| SkTScopedComPtr<IDWriteFontFace3> cshFontFace3; | |
| SkTScopedComPtr<IDWriteFontFace3> ctxFontFace3; | |
| cshFace->fDWriteFontFace->QueryInterface(&cshFontFace3); | |
| ctxFace->fDWriteFontFace->QueryInterface(&ctxFontFace3); | |
| bool same; | |
| //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) { | |
| return false; | |
| } | |
| SkTScopedComPtr<IDWriteFontFile> cshFontFile; | |
| SkTScopedComPtr<IDWriteFontFile> ctxFontFile; | |
| HRB(cshFace->fDWriteFontFace->GetFiles(&cshNumFiles, &cshFontFile)); | |
| HRB(ctxFace->fDWriteFontFace->GetFiles(&ctxNumFiles, &ctxFontFile)); | |
| //for (each file) { //we currently only admit fonts from one file. | |
| SkTScopedComPtr<IDWriteFontFileLoader> cshFontFileLoader; | |
| SkTScopedComPtr<IDWriteFontFileLoader> ctxFontFileLoader; | |
| HRB(cshFontFile->GetLoader(&cshFontFileLoader)); | |
| HRB(ctxFontFile->GetLoader(&ctxFontFileLoader)); | |
| HRB(are_same(cshFontFileLoader.get(), ctxFontFileLoader.get(), same)); | |
| if (!same) { | |
| return false; | |
| } | |
| //} | |
| const void* cshRefKey; | |
| UINT32 cshRefKeySize; | |
| const void* ctxRefKey; | |
| UINT32 ctxRefKeySize; | |
| HRB(cshFontFile->GetReferenceKey(&cshRefKey, &cshRefKeySize)); | |
| HRB(ctxFontFile->GetReferenceKey(&ctxRefKey, &ctxRefKeySize)); | |
| if (cshRefKeySize != ctxRefKeySize) { | |
| return false; | |
| } | |
| if (0 != memcmp(cshRefKey, ctxRefKey, ctxRefKeySize)) { | |
| return false; | |
| } | |
| //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)); | |
| SkTScopedComPtr<IDWriteLocalizedStrings> ctxFamilyNames; | |
| SkTScopedComPtr<IDWriteLocalizedStrings> ctxFaceNames; | |
| if (ctxFontFace3) { | |
| HRB(ctxFontFace3->GetFamilyNames(&ctxFamilyNames)); | |
| HRB(ctxFontFace3->GetFaceNames(&ctxFaceNames)); | |
| } else { | |
| HRB(ctxFace->fDWriteFontFamily->GetFamilyNames(&ctxFamilyNames)); | |
| HRB(ctxFace->fDWriteFont->GetFaceNames(&ctxFaceNames)); | |
| } | |
| UINT32 ctxFamilyNameLength; | |
| UINT32 ctxFaceNameLength; | |
| HRB(ctxFamilyNames->GetStringLength(0, &ctxFamilyNameLength)); | |
| HRB(ctxFaceNames->GetStringLength(0, &ctxFaceNameLength)); | |
| if (cshFamilyNameLength != ctxFamilyNameLength || | |
| cshFaceNameLength != ctxFaceNameLength) | |
| { | |
| return false; | |
| } | |
| SkSMallocWCHAR cshFamilyName(cshFamilyNameLength+1); | |
| SkSMallocWCHAR cshFaceName(cshFaceNameLength+1); | |
| HRB(cshFamilyNames->GetString(0, cshFamilyName.get(), cshFamilyNameLength+1)); | |
| HRB(cshFaceNames->GetString(0, cshFaceName.get(), cshFaceNameLength+1)); | |
| SkSMallocWCHAR ctxFamilyName(ctxFamilyNameLength+1); | |
| SkSMallocWCHAR ctxFaceName(ctxFaceNameLength+1); | |
| HRB(ctxFamilyNames->GetString(0, ctxFamilyName.get(), ctxFamilyNameLength+1)); | |
| HRB(ctxFaceNames->GetString(0, ctxFaceName.get(), ctxFaceNameLength+1)); | |
| return wcscmp(cshFamilyName.get(), ctxFamilyName.get()) == 0 && | |
| wcscmp(cshFaceName.get(), ctxFaceName.get()) == 0; | |
| } | |
| sk_sp<SkTypeface> SkFontMgr_DirectWrite::makeTypefaceFromDWriteFont( | |
| IDWriteFontFace* fontFace, | |
| IDWriteFont* font, | |
| IDWriteFontFamily* fontFamily) const { | |
| SkAutoMutexExclusive ama(fTFCacheMutex); | |
| ProtoDWriteTypeface spec = { fontFace, font, fontFamily }; | |
| sk_sp<SkTypeface> face = fTFCache.findByProcAndRef(FindByDWriteFont, &spec); | |
| if (nullptr == face) { | |
| face = DWriteFontTypeface::Make(fFactory.get(), fontFace, font, fontFamily, nullptr, | |
| SkFontArguments::Palette{0, nullptr, 0}); | |
| if (face) { | |
| fTFCache.add(face); | |
| } | |
| } | |
| return face; | |
| } | |
| sk_sp<SkTypeface> SkFontMgr_DirectWrite::makeTypefaceFromDWriteFontFace( | |
| IDWriteFontFace3* fontFace) const { | |
| return this->makeTypefaceFromDWriteFont(fontFace, nullptr, nullptr); | |
| } | |
| int SkFontMgr_DirectWrite::onCountFamilies() const { | |
| return fFontCollection->GetFontFamilyCount(); | |
| } | |
| void SkFontMgr_DirectWrite::onGetFamilyName(int index, SkString* familyName) const { | |
| SkTScopedComPtr<IDWriteFontFamily> fontFamily; | |
| HRVM(fFontCollection->GetFontFamily(index, &fontFamily), "Could not get requested family."); | |
| SkTScopedComPtr<IDWriteLocalizedStrings> familyNames; | |
| HRVM(fontFamily->GetFamilyNames(&familyNames), "Could not get family names."); | |
| sk_get_locale_string(familyNames.get(), fLocaleName.get(), familyName); | |
| } | |
| sk_sp<SkFontStyleSet> SkFontMgr_DirectWrite::onCreateStyleSet(int index) const { | |
| SkTScopedComPtr<IDWriteFontFamily> fontFamily; | |
| HRNM(fFontCollection->GetFontFamily(index, &fontFamily), "Could not get requested family."); | |
| return sk_sp<SkFontStyleSet>(new SkFontStyleSet_DirectWrite(this, fontFamily.get())); | |
| } | |
| sk_sp<SkFontStyleSet> SkFontMgr_DirectWrite::onMatchFamily(const char familyName[]) const { | |
| if (!familyName) { | |
| return nullptr; | |
| } | |
| SkSMallocWCHAR dwFamilyName; | |
| HRN(sk_cstring_to_wchar(familyName, &dwFamilyName)); | |
| UINT32 index; | |
| BOOL exists; | |
| HRNM(fFontCollection->FindFamilyName(dwFamilyName.get(), &index, &exists), | |
| "Failed while finding family by name."); | |
| if (!exists) { | |
| return nullptr; | |
| } | |
| return this->onCreateStyleSet(index); | |
| } | |
| sk_sp<SkTypeface> SkFontMgr_DirectWrite::onMatchFamilyStyle(const char familyName[], | |
| const SkFontStyle& fontstyle) const { | |
| sk_sp<SkFontStyleSet> sset(this->matchFamily(familyName)); | |
| return sset->matchStyle(fontstyle); | |
| } | |
| class FontFallbackRenderer : public IDWriteTextRenderer { | |
| public: | |
| FontFallbackRenderer(const SkFontMgr_DirectWrite* outer, UINT32 character) | |
| : fRefCount(1), fOuter(SkSafeRef(outer)), fCharacter(character), fResolvedTypeface(nullptr) { | |
| } | |
| // IUnknown methods | |
| SK_STDMETHODIMP QueryInterface(IID const& riid, void** ppvObject) override { | |
| if (__uuidof(IUnknown) == riid || | |
| __uuidof(IDWritePixelSnapping) == riid || | |
| __uuidof(IDWriteTextRenderer) == riid) | |
| { | |
| *ppvObject = this; | |
| this->AddRef(); | |
| return S_OK; | |
| } | |
| *ppvObject = nullptr; | |
| return E_FAIL; | |
| } | |
| SK_STDMETHODIMP_(ULONG) AddRef() override { | |
| return InterlockedIncrement(&fRefCount); | |
| } | |
| SK_STDMETHODIMP_(ULONG) Release() override { | |
| ULONG newCount = InterlockedDecrement(&fRefCount); | |
| if (0 == newCount) { | |
| delete this; | |
| } | |
| return newCount; | |
| } | |
| // 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(); | |
| } | |
| return S_OK; | |
| } | |
| SK_STDMETHODIMP DrawUnderline( | |
| void* clientDrawingContext, | |
| FLOAT baselineOriginX, | |
| FLOAT baselineOriginY, | |
| DWRITE_UNDERLINE const* underline, | |
| IUnknown* clientDrawingEffect) override | |
| { return E_NOTIMPL; } | |
| SK_STDMETHODIMP DrawStrikethrough( | |
| void* clientDrawingContext, | |
| FLOAT baselineOriginX, | |
| FLOAT baselineOriginY, | |
| DWRITE_STRIKETHROUGH const* strikethrough, | |
| IUnknown* clientDrawingEffect) override | |
| { return E_NOTIMPL; } | |
| SK_STDMETHODIMP DrawInlineObject( | |
| void* clientDrawingContext, | |
| FLOAT originX, | |
| FLOAT originY, | |
| IDWriteInlineObject* inlineObject, | |
| BOOL isSideways, | |
| BOOL isRightToLeft, | |
| IUnknown* clientDrawingEffect) override | |
| { return E_NOTIMPL; } | |
| // IDWritePixelSnapping methods | |
| SK_STDMETHODIMP IsPixelSnappingDisabled( | |
| void* clientDrawingContext, | |
| BOOL* isDisabled) override | |
| { | |
| *isDisabled = FALSE; | |
| return S_OK; | |
| } | |
| SK_STDMETHODIMP GetCurrentTransform( | |
| void* clientDrawingContext, | |
| DWRITE_MATRIX* transform) override | |
| { | |
| const DWRITE_MATRIX ident = { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }; | |
| *transform = ident; | |
| return S_OK; | |
| } | |
| SK_STDMETHODIMP GetPixelsPerDip( | |
| void* clientDrawingContext, | |
| FLOAT* pixelsPerDip) override | |
| { | |
| *pixelsPerDip = 1.0f; | |
| return S_OK; | |
| } | |
| sk_sp<SkTypeface> ConsumeFallbackTypeface() { return std::move(fResolvedTypeface); } | |
| DWRITE_FONT_SIMULATIONS FallbackTypefaceSimulations() { return fSimulations; } | |
| private: | |
| virtual ~FontFallbackRenderer() { } | |
| ULONG fRefCount; | |
| sk_sp<const SkFontMgr_DirectWrite> fOuter; | |
| UINT32 fCharacter; | |
| sk_sp<SkTypeface> fResolvedTypeface; | |
| DWRITE_FONT_SIMULATIONS fSimulations{DWRITE_FONT_SIMULATIONS_NONE}; | |
| }; | |
| class FontFallbackSource : public IDWriteTextAnalysisSource { | |
| public: | |
| FontFallbackSource(const WCHAR* string, UINT32 length, const WCHAR* locale, | |
| IDWriteNumberSubstitution* numberSubstitution) | |
| : fRefCount(1) | |
| , fString(string) | |
| , fLength(length) | |
| , fLocale(locale) | |
| , fNumberSubstitution(numberSubstitution) | |
| { } | |
| // IUnknown methods | |
| SK_STDMETHODIMP QueryInterface(IID const& riid, void** ppvObject) override { | |
| if (__uuidof(IUnknown) == riid || | |
| __uuidof(IDWriteTextAnalysisSource) == riid) | |
| { | |
| *ppvObject = this; | |
| this->AddRef(); | |
| return S_OK; | |
| } | |
| *ppvObject = nullptr; | |
| return E_FAIL; | |
| } | |
| SK_STDMETHODIMP_(ULONG) AddRef() override { | |
| return InterlockedIncrement(&fRefCount); | |
| } | |
| SK_STDMETHODIMP_(ULONG) Release() override { | |
| ULONG newCount = InterlockedDecrement(&fRefCount); | |
| if (0 == newCount) { | |
| delete this; | |
| } | |
| return newCount; | |
| } | |
| // IDWriteTextAnalysisSource methods | |
| SK_STDMETHODIMP GetTextAtPosition( | |
| UINT32 textPosition, | |
| WCHAR const** textString, | |
| UINT32* textLength) override | |
| { | |
| if (fLength <= textPosition) { | |
| *textString = nullptr; | |
| *textLength = 0; | |
| return S_OK; | |
| } | |
| *textString = fString + textPosition; | |
| *textLength = fLength - textPosition; | |
| return S_OK; | |
| } | |
| SK_STDMETHODIMP GetTextBeforePosition( | |
| UINT32 textPosition, | |
| WCHAR const** textString, | |
| UINT32* textLength) override | |
| { | |
| if (textPosition < 1 || fLength <= textPosition) { | |
| *textString = nullptr; | |
| *textLength = 0; | |
| return S_OK; | |
| } | |
| *textString = fString; | |
| *textLength = textPosition; | |
| return S_OK; | |
| } | |
| SK_STDMETHODIMP_(DWRITE_READING_DIRECTION) GetParagraphReadingDirection() override { | |
| // TODO: this is also interesting. | |
| return DWRITE_READING_DIRECTION_LEFT_TO_RIGHT; | |
| } | |
| SK_STDMETHODIMP GetLocaleName( | |
| UINT32 textPosition, | |
| UINT32* textLength, | |
| WCHAR const** localeName) override | |
| { | |
| *localeName = fLocale; | |
| return S_OK; | |
| } | |
| SK_STDMETHODIMP GetNumberSubstitution( | |
| UINT32 textPosition, | |
| UINT32* textLength, | |
| IDWriteNumberSubstitution** numberSubstitution) override | |
| { | |
| *numberSubstitution = fNumberSubstitution; | |
| return S_OK; | |
| } | |
| private: | |
| virtual ~FontFallbackSource() { } | |
| ULONG fRefCount; | |
| const WCHAR* fString; | |
| UINT32 fLength; | |
| const WCHAR* fLocale; | |
| IDWriteNumberSubstitution* fNumberSubstitution; | |
| }; | |
| sk_sp<SkTypeface> SkFontMgr_DirectWrite::onMatchFamilyStyleCharacter( | |
| const char familyName[], const SkFontStyle& style, | |
| const char* bcp47[], int bcp47Count, | |
| SkUnichar character) const | |
| { | |
| DWriteStyle dwStyle(style); | |
| const WCHAR* dwFamilyName = nullptr; | |
| SkSMallocWCHAR dwFamilyNameLocal; | |
| if (familyName) { | |
| HRN(sk_cstring_to_wchar(familyName, &dwFamilyNameLocal)); | |
| dwFamilyName = dwFamilyNameLocal; | |
| } | |
| 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); | |
| } | |
| sk_sp<SkTypeface> SkFontMgr_DirectWrite::onMatch(const Request& request) const { | |
| if (!request.familyName) { | |
| return nullptr; | |
| } | |
| SkTScopedComPtr<IDWriteFontCollection1> fontCollection1; | |
| fFontCollection->QueryInterface(&fontCollection1); | |
| if (!fontCollection1) { | |
| return this->SkFontMgr::onMatch(request); | |
| } | |
| SkTScopedComPtr<IDWriteFontSet> fontSet; | |
| fontCollection1->GetFontSet(&fontSet); | |
| if (!fontSet) { | |
| return this->SkFontMgr::onMatch(request); | |
| } | |
| SkTScopedComPtr<IDWriteFontSet4> fontSet4; | |
| fontSet->QueryInterface(&fontSet4); | |
| if (!fontSet4) { | |
| return this->SkFontMgr::onMatch(request); | |
| } | |
| SkSMallocWCHAR dwFamilyName; | |
| HRN(sk_cstring_to_wchar(request.familyName, &dwFamilyName)); | |
| 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; | |
| } | |
| 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<const char*>(&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)); | |
| } | |
| } | |
| if (!matches->GetFontCount()) { | |
| return nullptr; | |
| } | |
| SkTScopedComPtr<IDWriteFontFace5> fontFace5; | |
| HRNM(matches->CreateFontFace(0, &fontFace5), "Failed to create match"); | |
| return this->makeTypefaceFromDWriteFontFace(fontFace5.get()); | |
| } | |
| sk_sp<SkTypeface> SkFontMgr_DirectWrite::onFallback(const Request& request) const { | |
| const WCHAR* dwFamilyName = nullptr; | |
| SkSMallocWCHAR dwFamilyNameLocal; | |
| if (request.familyName) { | |
| HRN(sk_cstring_to_wchar(request.familyName, &dwFamilyNameLocal)); | |
| dwFamilyName = dwFamilyNameLocal; | |
| } | |
| 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; | |
| if (dwAxis.axisTag == DWRITE_FONT_AXIS_TAG_ITALIC) { | |
| italicRequested = 0.5f < dwAxis.value; | |
| } | |
| } | |
| return this->fallback1(fontFallback1.get(), | |
| dwFamilyName, | |
| SkSpan(dwModel.data(), request.model.size()), | |
| italicRequested, | |
| dwBcp47->get(), | |
| request.cmapEntries, | |
| allowedSimulations); | |
| } | |
| } | |
| 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); | |
| } | |
| static bool has_only_allowed(DWRITE_FONT_SIMULATIONS actual, DWRITE_FONT_SIMULATIONS allowed) { | |
| return (actual & ~allowed) == 0; | |
| } | |
| sk_sp<SkTypeface> SkFontMgr_DirectWrite::fallback1( | |
| IDWriteFontFallback1* fontFallback1, | |
| const WCHAR* dwFamilyName, | |
| SkSpan<const DWRITE_FONT_AXIS_VALUE> model, | |
| bool italicRequested, | |
| const WCHAR* dwBcp47, | |
| SkSpan<const SkFontMgr::Request::CMapEntry> cmapEntries, | |
| DWRITE_FONT_SIMULATIONS allowedSimulations) const | |
| { | |
| SkASSERT(fontFallback1); | |
| size_t utf16Size = 0; | |
| for (auto&& cmapEntry : cmapEntries) { | |
| utf16Size += SkUTF::ToUTF16(cmapEntry.character); | |
| if (cmapEntry.variation) { | |
| utf16Size += SkUTF::ToUTF16(cmapEntry.variation); | |
| } | |
| } | |
| AutoSTMalloc<16, WCHAR> str(utf16Size); | |
| uint16_t* curr = reinterpret_cast<uint16_t*>(str.data()); | |
| for (auto&& cmapEntry : cmapEntries) { | |
| curr += SkUTF::ToUTF16(cmapEntry.character, curr); | |
| if (cmapEntry.variation) { | |
| curr += SkUTF::ToUTF16(cmapEntry.variation, curr); | |
| } | |
| } | |
| UINT32 strLen = SkTo<UINT32>(utf16Size); | |
| SkTScopedComPtr<IDWriteNumberSubstitution> numberSubstitution; | |
| HRNM(fFactory->CreateNumberSubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, dwBcp47, | |
| TRUE, &numberSubstitution), | |
| "Could not create number substitution."); | |
| SkTScopedComPtr<FontFallbackSource> fontFallbackSource( | |
| new FontFallbackSource(str.get(), strLen, dwBcp47, numberSubstitution.get())); | |
| UINT32 mappedLength; | |
| SkTScopedComPtr<IDWriteFontFace5> fontFace5; | |
| FLOAT scale; | |
| HRNM(fontFallback1->MapCharacters(fontFallbackSource.get(), | |
| 0, // textPosition, | |
| strLen, | |
| fFontCollection.get(), | |
| dwFamilyName, | |
| model.data(), | |
| model.size(), | |
| &mappedLength, | |
| &scale, | |
| &fontFace5), | |
| "Could not map characters"); | |
| if (!fontFace5.get()) { | |
| return nullptr; | |
| } | |
| // 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. | |
| const DWRITE_FONT_SIMULATIONS simulations = fontFace5->GetSimulations(); | |
| const DWRITE_FONT_STYLE style = fontFace5->GetStyle(); | |
| const bool removeSimulations = !has_only_allowed(simulations, allowedSimulations); | |
| const bool syntheticObliqueAllowed = allowedSimulations & DWRITE_FONT_SIMULATIONS_OBLIQUE; | |
| const bool isNormalStyle = style == DWRITE_FONT_STYLE_NORMAL; | |
| const bool addObliqueSimulation = italicRequested && isNormalStyle && syntheticObliqueAllowed; | |
| if (!removeSimulations && !addObliqueSimulation) { | |
| return this->makeTypefaceFromDWriteFontFace(fontFace5.get()); | |
| } | |
| const DWRITE_FONT_SIMULATIONS finalSimulations = | |
| (simulations & allowedSimulations) | | |
| (addObliqueSimulation ? DWRITE_FONT_SIMULATIONS_OBLIQUE : DWRITE_FONT_SIMULATIONS_NONE); | |
| SkTScopedComPtr<IDWriteFontFaceReference> fontFaceReference; | |
| fontFace5->GetFontFaceReference(&fontFaceReference); | |
| if (!fontFaceReference) { | |
| return this->makeTypefaceFromDWriteFontFace(fontFace5.get()); | |
| } | |
| SkTScopedComPtr<IDWriteFontFace3> fontFace3; | |
| fontFaceReference->CreateFontFaceWithSimulations(finalSimulations, &fontFace3); | |
| if (!fontFace3) { | |
| return this->makeTypefaceFromDWriteFontFace(fontFace5.get()); | |
| } | |
| return this->makeTypefaceFromDWriteFontFace(fontFace3.get()); | |
| } | |
| sk_sp<SkTypeface> SkFontMgr_DirectWrite::fallback( | |
| const WCHAR* dwFamilyName, | |
| DWriteStyle dwStyle, | |
| const WCHAR* dwBcp47, | |
| UINT32 character, | |
| DWRITE_FONT_SIMULATIONS allowedSimulations) const | |
| { | |
| if (!fFontFallback) { | |
| return nullptr; | |
| } | |
| WCHAR str[16]; | |
| UINT32 strLen = SkTo<UINT32>(SkUTF::ToUTF16(character, reinterpret_cast<uint16_t*>(str))); | |
| SkTScopedComPtr<IDWriteNumberSubstitution> numberSubstitution; | |
| HRNM(fFactory->CreateNumberSubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, dwBcp47, | |
| TRUE, &numberSubstitution), | |
| "Could not create number substitution."); | |
| SkTScopedComPtr<FontFallbackSource> fontFallbackSource( | |
| new FontFallbackSource(str, strLen, dwBcp47, numberSubstitution.get())); | |
| UINT32 mappedLength; | |
| SkTScopedComPtr<IDWriteFont> font; | |
| FLOAT scale; | |
| while (true) { | |
| font.reset(); | |
| HRNM(fFontFallback->MapCharacters(fontFallbackSource.get(), | |
| 0, // textPosition, | |
| strLen, | |
| fFontCollection.get(), | |
| dwFamilyName, | |
| dwStyle.fWeight, | |
| dwStyle.fSlant, | |
| dwStyle.fWidth, | |
| &mappedLength, | |
| &font, | |
| &scale), | |
| "Could not map characters"); | |
| if (!font.get()) { | |
| return nullptr; | |
| } | |
| DWRITE_FONT_SIMULATIONS simulations = font->GetSimulations(); | |
| if (has_only_allowed(simulations, allowedSimulations) || | |
| (dwStyle.fWeight == DWRITE_FONT_WEIGHT_REGULAR && | |
| dwStyle.fSlant == DWRITE_FONT_STYLE_NORMAL)) | |
| { | |
| 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; | |
| } | |
| } | |
| 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()); | |
| } | |
| sk_sp<SkTypeface> SkFontMgr_DirectWrite::layoutFallback( | |
| const WCHAR* dwFamilyName, | |
| DWriteStyle dwStyle, | |
| const WCHAR* dwBcp47, | |
| UINT32 character, | |
| DWRITE_FONT_SIMULATIONS allowedSimulations) const | |
| { | |
| WCHAR str[16]; | |
| UINT32 strLen = SkTo<UINT32>(SkUTF::ToUTF16(character, reinterpret_cast<uint16_t*>(str))); | |
| 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."); | |
| DWRITE_FONT_SIMULATIONS simulations = fontFallbackRenderer->FallbackTypefaceSimulations(); | |
| if (has_only_allowed(simulations, allowedSimulations) || | |
| (dwStyle.fWeight == DWRITE_FONT_WEIGHT_REGULAR && | |
| dwStyle.fSlant == DWRITE_FONT_STYLE_NORMAL)) | |
| { | |
| break; | |
| } | |
| if (noSimulations) { | |
| returnTypeface = fontFallbackRenderer->ConsumeFallbackTypeface(); | |
| break; | |
| } | |
| 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; | |
| } | |
| sk_sp<SkTypeface> SkFontMgr_DirectWrite::onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream, | |
| int ttcIndex) const { | |
| SkFontArguments args; | |
| args.setCollectionIndex(ttcIndex); | |
| return this->onMakeFromStreamArgs(std::move(stream), args); | |
| } | |
| sk_sp<SkTypeface> SkFontMgr_DirectWrite::onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> stream, | |
| const SkFontArguments& args) const { | |
| return DWriteFontTypeface::MakeFromStream(std::move(stream), args); | |
| } | |
| sk_sp<SkTypeface> SkFontMgr_DirectWrite::onMakeFromData(sk_sp<SkData> data, int ttcIndex) const { | |
| return this->makeFromStream(std::make_unique<SkMemoryStream>(std::move(data)), ttcIndex); | |
| } | |
| sk_sp<SkTypeface> SkFontMgr_DirectWrite::onMakeFromFile(const char path[], int ttcIndex) const { | |
| return this->makeFromStream(SkStream::MakeFromFile(path), ttcIndex); | |
| } | |
| HRESULT SkFontMgr_DirectWrite::getByFamilyName(const WCHAR wideFamilyName[], | |
| IDWriteFontFamily** fontFamily) const { | |
| UINT32 index; | |
| BOOL exists; | |
| HR(fFontCollection->FindFamilyName(wideFamilyName, &index, &exists)); | |
| if (exists) { | |
| HR(fFontCollection->GetFontFamily(index, fontFamily)); | |
| } | |
| return S_OK; | |
| } | |
| sk_sp<SkTypeface> SkFontMgr_DirectWrite::onLegacyMakeTypeface(const char familyName[], | |
| SkFontStyle style) const { | |
| SkTScopedComPtr<IDWriteFontFamily> fontFamily; | |
| DWriteStyle dwStyle(style); | |
| if (familyName) { | |
| SkSMallocWCHAR dwFamilyName; | |
| if (SUCCEEDED(sk_cstring_to_wchar(familyName, &dwFamilyName))) { | |
| this->getByFamilyName(dwFamilyName, &fontFamily); | |
| if (!fontFamily && fFontFallback) { | |
| return this->fallback( | |
| dwFamilyName, dwStyle, fLocaleName.get(), 32, kDefaultSimulations); | |
| } | |
| } | |
| } | |
| 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."); | |
| return this->makeTypefaceFromDWriteFont(fontFace.get(), font.get(), fontFamily.get()); | |
| } | |
| /////////////////////////////////////////////////////////////////////////////// | |
| int SkFontStyleSet_DirectWrite::count() { | |
| return fFontFamily->GetFontCount(); | |
| } | |
| sk_sp<SkTypeface> SkFontStyleSet_DirectWrite::createTypeface(int index) { | |
| SkTScopedComPtr<IDWriteFont> font; | |
| HRNM(fFontFamily->GetFont(index, &font), "Could not get font."); | |
| SkTScopedComPtr<IDWriteFontFace> fontFace; | |
| HRNM(font->CreateFontFace(&fontFace), "Could not create font face."); | |
| return fFontMgr->makeTypefaceFromDWriteFont(fontFace.get(), font.get(), fFontFamily.get()); | |
| } | |
| 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); | |
| } | |
| } | |
| } | |
| sk_sp<SkTypeface> SkFontStyleSet_DirectWrite::matchStyle(const SkFontStyle& pattern) { | |
| SkTScopedComPtr<IDWriteFont> font; | |
| DWriteStyle dwStyle(pattern); | |
| HRNM(FirstMatchingFontWithoutSimulations(fFontFamily, dwStyle, font), | |
| "No font found from family."); | |
| SkTScopedComPtr<IDWriteFontFace> fontFace; | |
| HRNM(font->CreateFontFace(&fontFace), "Could not create font face."); | |
| return fFontMgr->makeTypefaceFromDWriteFont(fontFace.get(), font.get(), fFontFamily.get()); | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| #include "include/ports/SkTypeface_win.h" | |
| sk_sp<SkFontMgr> SkFontMgr_New_DirectWrite(IDWriteFactory* factory, | |
| IDWriteFontCollection* collection) { | |
| return SkFontMgr_New_DirectWrite(factory, collection, nullptr); | |
| } | |
| sk_sp<SkFontMgr> SkFontMgr_New_DirectWrite(IDWriteFactory* factory, | |
| IDWriteFontCollection* collection, | |
| IDWriteFontFallback* fallback) { | |
| if (nullptr == factory) { | |
| factory = sk_get_dwrite_factory(); | |
| if (nullptr == factory) { | |
| return nullptr; | |
| } | |
| } | |
| 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(); | |
| } | |
| } | |
| const WCHAR* defaultFamilyName = L""; | |
| int defaultFamilyNameLen = 1; | |
| NONCLIENTMETRICSW metrics; | |
| metrics.cbSize = sizeof(metrics); | |
| #ifndef SK_WINUWP | |
| if (nullptr == fallback) { | |
| if (SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(metrics), &metrics, 0)) { | |
| defaultFamilyName = metrics.lfMessageFont.lfFaceName; | |
| defaultFamilyNameLen = LF_FACESIZE; | |
| } | |
| } | |
| #endif //SK_WINUWP | |
| WCHAR localeNameStorage[LOCALE_NAME_MAX_LENGTH]; | |
| const WCHAR* localeName = L""; | |
| int localeNameLen = 1; | |
| // 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; | |
| } | |
| } | |
| return sk_make_sp<SkFontMgr_DirectWrite>(factory, collection, fallback, | |
| localeName, localeNameLen, | |
| defaultFamilyName, defaultFamilyNameLen); | |
| } | |
| #endif//defined(SK_BUILD_FOR_WIN) |