| /* |
| * 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/base/SkMutex.h" |
| #include "include/private/base/SkTPin.h" |
| #include "src/base/SkUTF.h" |
| #include "src/core/SkEndian.h" |
| #include "src/core/SkFontDescriptor.h" |
| #include "src/core/SkTypefaceCache.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; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| class StreamFontFileLoader : public IDWriteFontFileLoader { |
| public: |
| // IUnknown methods |
| SK_STDMETHODIMP QueryInterface(REFIID iid, void** ppvObject) override; |
| SK_STDMETHODIMP_(ULONG) AddRef() override; |
| SK_STDMETHODIMP_(ULONG) Release() override; |
| |
| // IDWriteFontFileLoader methods |
| SK_STDMETHODIMP CreateStreamFromKey( |
| void const* fontFileReferenceKey, |
| UINT32 fontFileReferenceKeySize, |
| IDWriteFontFileStream** fontFileStream) override; |
| |
| // Takes ownership of stream. |
| static HRESULT Create(std::unique_ptr<SkStreamAsset> stream, |
| StreamFontFileLoader** streamFontFileLoader) { |
| *streamFontFileLoader = new StreamFontFileLoader(std::move(stream)); |
| if (nullptr == *streamFontFileLoader) { |
| return E_OUTOFMEMORY; |
| } |
| return S_OK; |
| } |
| |
| private: |
| StreamFontFileLoader(std::unique_ptr<SkStreamAsset> stream) |
| : fStream(std::move(stream)), fRefCount(1) |
| {} |
| virtual ~StreamFontFileLoader() { } |
| |
| std::unique_ptr<SkStreamAsset> fStream; |
| ULONG fRefCount; |
| }; |
| |
| SK_STDMETHODIMP StreamFontFileLoader::QueryInterface(REFIID iid, void** ppvObject) { |
| if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileLoader)) { |
| *ppvObject = this; |
| AddRef(); |
| return S_OK; |
| } else { |
| *ppvObject = nullptr; |
| return E_NOINTERFACE; |
| } |
| } |
| |
| SK_STDMETHODIMP_(ULONG) StreamFontFileLoader::AddRef() { |
| return InterlockedIncrement(&fRefCount); |
| } |
| |
| SK_STDMETHODIMP_(ULONG) StreamFontFileLoader::Release() { |
| ULONG newCount = InterlockedDecrement(&fRefCount); |
| if (0 == newCount) { |
| delete this; |
| } |
| return newCount; |
| } |
| |
| SK_STDMETHODIMP StreamFontFileLoader::CreateStreamFromKey( |
| void const* fontFileReferenceKey, |
| UINT32 fontFileReferenceKeySize, |
| IDWriteFontFileStream** fontFileStream) |
| { |
| SkTScopedComPtr<SkDWriteFontFileStreamWrapper> stream; |
| HR(SkDWriteFontFileStreamWrapper::Create(fStream->duplicate().release(), &stream)); |
| *fontFileStream = stream.release(); |
| return S_OK; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| class StreamFontFileEnumerator : public IDWriteFontFileEnumerator { |
| public: |
| // IUnknown methods |
| SK_STDMETHODIMP QueryInterface(REFIID iid, void** ppvObject) override; |
| SK_STDMETHODIMP_(ULONG) AddRef() override; |
| SK_STDMETHODIMP_(ULONG) Release() override; |
| |
| // IDWriteFontFileEnumerator methods |
| SK_STDMETHODIMP MoveNext(BOOL* hasCurrentFile) override; |
| SK_STDMETHODIMP GetCurrentFontFile(IDWriteFontFile** fontFile) override; |
| |
| static HRESULT Create(IDWriteFactory* factory, IDWriteFontFileLoader* fontFileLoader, |
| StreamFontFileEnumerator** streamFontFileEnumerator) { |
| *streamFontFileEnumerator = new StreamFontFileEnumerator(factory, fontFileLoader); |
| if (nullptr == *streamFontFileEnumerator) { |
| return E_OUTOFMEMORY; |
| } |
| return S_OK; |
| } |
| private: |
| StreamFontFileEnumerator(IDWriteFactory* factory, IDWriteFontFileLoader* fontFileLoader); |
| virtual ~StreamFontFileEnumerator() { } |
| |
| ULONG fRefCount; |
| |
| SkTScopedComPtr<IDWriteFactory> fFactory; |
| SkTScopedComPtr<IDWriteFontFile> fCurrentFile; |
| SkTScopedComPtr<IDWriteFontFileLoader> fFontFileLoader; |
| bool fHasNext; |
| }; |
| |
| StreamFontFileEnumerator::StreamFontFileEnumerator(IDWriteFactory* factory, |
| IDWriteFontFileLoader* fontFileLoader) |
| : fRefCount(1) |
| , fFactory(SkRefComPtr(factory)) |
| , fCurrentFile() |
| , fFontFileLoader(SkRefComPtr(fontFileLoader)) |
| , fHasNext(true) |
| { } |
| |
| SK_STDMETHODIMP StreamFontFileEnumerator::QueryInterface(REFIID iid, void** ppvObject) { |
| if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileEnumerator)) { |
| *ppvObject = this; |
| AddRef(); |
| return S_OK; |
| } else { |
| *ppvObject = nullptr; |
| return E_NOINTERFACE; |
| } |
| } |
| |
| SK_STDMETHODIMP_(ULONG) StreamFontFileEnumerator::AddRef() { |
| return InterlockedIncrement(&fRefCount); |
| } |
| |
| SK_STDMETHODIMP_(ULONG) StreamFontFileEnumerator::Release() { |
| ULONG newCount = InterlockedDecrement(&fRefCount); |
| if (0 == newCount) { |
| delete this; |
| } |
| return newCount; |
| } |
| |
| SK_STDMETHODIMP StreamFontFileEnumerator::MoveNext(BOOL* hasCurrentFile) { |
| *hasCurrentFile = FALSE; |
| |
| if (!fHasNext) { |
| return S_OK; |
| } |
| fHasNext = false; |
| |
| UINT32 fontFileReferenceKey = 0; |
| HR(fFactory->CreateCustomFontFileReference( |
| &fontFileReferenceKey, //cannot be nullptr |
| sizeof(fontFileReferenceKey), //even if this is 0 |
| fFontFileLoader.get(), |
| &fCurrentFile)); |
| |
| *hasCurrentFile = TRUE; |
| return S_OK; |
| } |
| |
| SK_STDMETHODIMP StreamFontFileEnumerator::GetCurrentFontFile(IDWriteFontFile** fontFile) { |
| if (fCurrentFile.get() == nullptr) { |
| *fontFile = nullptr; |
| return E_FAIL; |
| } |
| |
| *fontFile = SkRefComPtr(fCurrentFile.get()); |
| return S_OK; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| class StreamFontCollectionLoader : public IDWriteFontCollectionLoader { |
| public: |
| // IUnknown methods |
| SK_STDMETHODIMP QueryInterface(REFIID iid, void** ppvObject) override; |
| SK_STDMETHODIMP_(ULONG) AddRef() override; |
| SK_STDMETHODIMP_(ULONG) Release() override; |
| |
| // IDWriteFontCollectionLoader methods |
| SK_STDMETHODIMP CreateEnumeratorFromKey( |
| IDWriteFactory* factory, |
| void const* collectionKey, |
| UINT32 collectionKeySize, |
| IDWriteFontFileEnumerator** fontFileEnumerator) override; |
| |
| static HRESULT Create(IDWriteFontFileLoader* fontFileLoader, |
| StreamFontCollectionLoader** streamFontCollectionLoader) { |
| *streamFontCollectionLoader = new StreamFontCollectionLoader(fontFileLoader); |
| if (nullptr == *streamFontCollectionLoader) { |
| return E_OUTOFMEMORY; |
| } |
| return S_OK; |
| } |
| private: |
| StreamFontCollectionLoader(IDWriteFontFileLoader* fontFileLoader) |
| : fRefCount(1) |
| , fFontFileLoader(SkRefComPtr(fontFileLoader)) |
| { } |
| virtual ~StreamFontCollectionLoader() { } |
| |
| ULONG fRefCount; |
| SkTScopedComPtr<IDWriteFontFileLoader> fFontFileLoader; |
| }; |
| |
| SK_STDMETHODIMP StreamFontCollectionLoader::QueryInterface(REFIID iid, void** ppvObject) { |
| if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontCollectionLoader)) { |
| *ppvObject = this; |
| AddRef(); |
| return S_OK; |
| } else { |
| *ppvObject = nullptr; |
| return E_NOINTERFACE; |
| } |
| } |
| |
| SK_STDMETHODIMP_(ULONG) StreamFontCollectionLoader::AddRef() { |
| return InterlockedIncrement(&fRefCount); |
| } |
| |
| SK_STDMETHODIMP_(ULONG) StreamFontCollectionLoader::Release() { |
| ULONG newCount = InterlockedDecrement(&fRefCount); |
| if (0 == newCount) { |
| delete this; |
| } |
| return newCount; |
| } |
| |
| SK_STDMETHODIMP StreamFontCollectionLoader::CreateEnumeratorFromKey( |
| IDWriteFactory* factory, |
| void const* collectionKey, |
| UINT32 collectionKeySize, |
| IDWriteFontFileEnumerator** fontFileEnumerator) |
| { |
| SkTScopedComPtr<StreamFontFileEnumerator> enumerator; |
| HR(StreamFontFileEnumerator::Create(factory, fFontFileLoader.get(), &enumerator)); |
| *fontFileEnumerator = enumerator.release(); |
| return S_OK; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| namespace { |
| |
| // 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(), |
| SkEndian_SwapBE32(SkSetFourByteTag('E', 'B', 'D', 'T'))); |
| return ebdtTable.fExists; |
| } |
| |
| // 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; |
| SkFontStyleSet* onCreateStyleSet(int index) const override; |
| SkFontStyleSet* onMatchFamily(const char familyName[]) const override; |
| SkTypeface* onMatchFamilyStyle(const char familyName[], |
| const SkFontStyle& fontstyle) const override; |
| SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&, |
| const char* bcp47[], int bcp47Count, |
| SkUnichar character) 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> fallback(const WCHAR* dwFamilyName, DWriteStyle, |
| const WCHAR* dwBcp47, UINT32 character) const; |
| sk_sp<SkTypeface> layoutFallback(const WCHAR* dwFamilyName, DWriteStyle, |
| const WCHAR* dwBcp47, UINT32 character) const; |
| |
| /** Creates a typeface using a typeface cache. */ |
| sk_sp<SkTypeface> makeTypefaceFromDWriteFont(IDWriteFontFace* fontFace, |
| IDWriteFont* font, |
| IDWriteFontFamily* fontFamily) 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; |
| SkTypeface* createTypeface(int index) override; |
| 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()); |
| } |
| |
| bool same; |
| |
| //Check to see if the two fonts are identical. |
| HRB(are_same(cshFace->fDWriteFont.get(), ctxFace->fDWriteFont, same)); |
| if (same) { |
| return true; |
| } |
| |
| 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; |
| 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; |
| 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; |
| } |
| |
| 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); |
| } |
| |
| SkFontStyleSet* SkFontMgr_DirectWrite::onCreateStyleSet(int index) const { |
| SkTScopedComPtr<IDWriteFontFamily> fontFamily; |
| HRNM(fFontCollection->GetFontFamily(index, &fontFamily), "Could not get requested family."); |
| |
| return new SkFontStyleSet_DirectWrite(this, fontFamily.get()); |
| } |
| |
| 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); |
| } |
| |
| 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()); |
| fHasSimulations = (font->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) && |
| !HasBitmapStrikes(font); |
| } |
| |
| 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); } |
| |
| bool FallbackTypefaceHasSimulations() { return fHasSimulations; } |
| |
| private: |
| virtual ~FontFallbackRenderer() { } |
| |
| ULONG fRefCount; |
| sk_sp<const SkFontMgr_DirectWrite> fOuter; |
| UINT32 fCharacter; |
| sk_sp<SkTypeface> fResolvedTypeface; |
| bool fHasSimulations{false}; |
| }; |
| |
| 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; |
| }; |
| |
| 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->get(), character).release(); |
| } |
| |
| // LayoutFallback may use the system font collection for fallback. |
| return this->layoutFallback(dwFamilyName, dwStyle, dwBcp47->get(), character).release(); |
| } |
| |
| sk_sp<SkTypeface> SkFontMgr_DirectWrite::fallback(const WCHAR* dwFamilyName, |
| DWriteStyle dwStyle, |
| const WCHAR* dwBcp47, |
| UINT32 character) const { |
| WCHAR str[16]; |
| UINT32 strLen = SkTo<UINT32>(SkUTF::ToUTF16(character, reinterpret_cast<uint16_t*>(str))); |
| |
| if (!fFontFallback) { |
| return nullptr; |
| } |
| |
| 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; |
| |
| bool noSimulations = false; |
| while (!noSimulations) { |
| 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(); |
| |
| #ifdef SK_WIN_FONTMGR_NO_SIMULATIONS |
| noSimulations = simulations == DWRITE_FONT_SIMULATIONS_NONE || HasBitmapStrikes(font); |
| #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; |
| 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) 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."); |
| |
| #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 { |
| public: |
| SkAutoIDWriteUnregister(IDWriteFactory* factory, T* unregister) |
| : fFactory(factory), fUnregister(unregister) |
| { } |
| SkAutoIDWriteUnregister(const SkAutoIDWriteUnregister&) = delete; |
| SkAutoIDWriteUnregister& operator=(const SkAutoIDWriteUnregister&) = delete; |
| SkAutoIDWriteUnregister(SkAutoIDWriteUnregister&&) = delete; |
| SkAutoIDWriteUnregister& operator=(SkAutoIDWriteUnregister&&) = delete; |
| |
| ~SkAutoIDWriteUnregister() { |
| if (fUnregister) { |
| unregister(fFactory, fUnregister); |
| } |
| } |
| |
| T* detatch() { |
| T* old = fUnregister; |
| fUnregister = nullptr; |
| return old; |
| } |
| |
| private: |
| HRESULT unregister(IDWriteFactory* factory, IDWriteFontFileLoader* unregister) { |
| return factory->UnregisterFontFileLoader(unregister); |
| } |
| |
| HRESULT unregister(IDWriteFactory* factory, IDWriteFontCollectionLoader* unregister) { |
| return factory->UnregisterFontCollectionLoader(unregister); |
| } |
| |
| IDWriteFactory* fFactory; |
| T* fUnregister; |
| }; |
| |
| 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); |
| } |
| |
| static HRESULT apply_fontargument_variation(SkTScopedComPtr<IDWriteFontFace>& fontFace, |
| const SkFontArguments& args) |
| { |
| #if defined(NTDDI_WIN10_RS3) && NTDDI_VERSION >= NTDDI_WIN10_RS3 |
| |
| SkTScopedComPtr<IDWriteFontFace5> fontFace5; |
| if (FAILED(fontFace->QueryInterface(&fontFace5)) || !fontFace5->HasVariations()) { |
| return S_OK; |
| } |
| |
| UINT32 fontAxisCount = fontFace5->GetFontAxisValueCount(); |
| UINT32 argsCoordCount = args.getVariationDesignPosition().coordinateCount; |
| AutoSTMalloc<8, DWRITE_FONT_AXIS_VALUE> variation(fontAxisCount); |
| SkTScopedComPtr<IDWriteFontResource> fontResource; |
| HR(fontFace5->GetFontResource(&fontResource)); |
| HR(fontResource->GetDefaultFontAxisValues(variation, fontAxisCount)); |
| |
| for (UINT32 fontAxisIndex = 0; fontAxisIndex < fontAxisCount; ++fontAxisIndex) { |
| DWRITE_FONT_AXIS_VALUE& fontCoordinate = variation[fontAxisIndex]; |
| |
| for (UINT32 argsCoordIndex = argsCoordCount; argsCoordIndex --> 0;) { |
| const SkFontArguments::VariationPosition::Coordinate& argsCoordinate = |
| args.getVariationDesignPosition().coordinates[argsCoordIndex]; |
| if (SkEndian_SwapBE32(fontCoordinate.axisTag) == argsCoordinate.axis) { |
| fontCoordinate.value = argsCoordinate.value; |
| break; |
| } |
| } |
| } |
| |
| SkTScopedComPtr<IDWriteFontFace5> fontFace5_Out; |
| HR(fontResource->CreateFontFace(DWRITE_FONT_SIMULATIONS_NONE, |
| variation.get(), fontAxisCount, |
| &fontFace5_Out)); |
| fontFace.reset(); |
| HR(fontFace5_Out->QueryInterface(&fontFace)); |
| #endif |
| return S_OK; |
| } |
| sk_sp<SkTypeface> SkFontMgr_DirectWrite::onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> stream, |
| const SkFontArguments& args) const { |
| SkTScopedComPtr<StreamFontFileLoader> fontFileLoader; |
| HRN(StreamFontFileLoader::Create(std::move(stream), &fontFileLoader)); |
| HRN(fFactory->RegisterFontFileLoader(fontFileLoader.get())); |
| SkAutoIDWriteUnregister<StreamFontFileLoader> autoUnregisterFontFileLoader( |
| fFactory.get(), fontFileLoader.get()); |
| |
| SkTScopedComPtr<StreamFontCollectionLoader> fontCollectionLoader; |
| HRN(StreamFontCollectionLoader::Create(fontFileLoader.get(), &fontCollectionLoader)); |
| HRN(fFactory->RegisterFontCollectionLoader(fontCollectionLoader.get())); |
| SkAutoIDWriteUnregister<StreamFontCollectionLoader> autoUnregisterFontCollectionLoader( |
| fFactory.get(), fontCollectionLoader.get()); |
| |
| SkTScopedComPtr<IDWriteFontCollection> fontCollection; |
| HRN(fFactory->CreateCustomFontCollection(fontCollectionLoader.get(), nullptr, 0, |
| &fontCollection)); |
| |
| // Find the first non-simulated font which has the given ttc index. |
| UINT32 familyCount = fontCollection->GetFontFamilyCount(); |
| for (UINT32 familyIndex = 0; familyIndex < familyCount; ++familyIndex) { |
| SkTScopedComPtr<IDWriteFontFamily> fontFamily; |
| HRN(fontCollection->GetFontFamily(familyIndex, &fontFamily)); |
| |
| UINT32 fontCount = fontFamily->GetFontCount(); |
| for (UINT32 fontIndex = 0; fontIndex < fontCount; ++fontIndex) { |
| SkTScopedComPtr<IDWriteFont> font; |
| HRN(fontFamily->GetFont(fontIndex, &font)); |
| |
| // Skip if the current font is simulated |
| if (font->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) { |
| continue; |
| } |
| SkTScopedComPtr<IDWriteFontFace> fontFace; |
| HRN(font->CreateFontFace(&fontFace)); |
| int faceIndex = fontFace->GetIndex(); |
| int ttcIndex = args.getCollectionIndex(); |
| |
| // Skip if the current face index does not match the ttcIndex |
| if (faceIndex != ttcIndex) { |
| continue; |
| } |
| |
| apply_fontargument_variation(fontFace, args); |
| |
| return DWriteFontTypeface::Make( |
| fFactory.get(), fontFace.get(), font.get(), fontFamily.get(), |
| sk_make_sp<DWriteFontTypeface::Loaders>( |
| fFactory.get(), |
| autoUnregisterFontFileLoader.detatch(), |
| autoUnregisterFontCollectionLoader.detatch()), |
| args.getPalette()); |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| 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); |
| } |
| } |
| } |
| |
| if (!fontFamily) { |
| if (fFontFallback) { |
| return this->fallback(nullptr, dwStyle, fLocaleName.get(), 32); |
| } |
| // 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(); |
| } |
| |
| 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()).release(); |
| } |
| |
| void SkFontStyleSet_DirectWrite::getStyle(int index, SkFontStyle* fs, SkString* styleName) { |
| SkTScopedComPtr<IDWriteFont> font; |
| HRVM(fFontFamily->GetFont(index, &font), "Could not get font."); |
| |
| if (fs) { |
| *fs = get_style(font.get()); |
| } |
| |
| if (styleName) { |
| SkTScopedComPtr<IDWriteLocalizedStrings> faceNames; |
| if (SUCCEEDED(font->GetFaceNames(&faceNames))) { |
| sk_get_locale_string(faceNames.get(), fFontMgr->fLocaleName.get(), styleName); |
| } |
| } |
| } |
| |
| 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()).release(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| #include "include/ports/SkTypeface_win.h" |
| |
| SK_API sk_sp<SkFontMgr> SkFontMgr_New_DirectWrite(IDWriteFactory* factory, |
| IDWriteFontCollection* collection) { |
| return SkFontMgr_New_DirectWrite(factory, collection, nullptr); |
| } |
| |
| SK_API 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); |
| } |
| |
| #include "include/ports/SkFontMgr_indirect.h" |
| SK_API sk_sp<SkFontMgr> SkFontMgr_New_DirectWriteRenderer(sk_sp<SkRemotableFontMgr> proxy) { |
| sk_sp<SkFontMgr> impl(SkFontMgr_New_DirectWrite()); |
| if (!impl) { |
| return nullptr; |
| } |
| return sk_make_sp<SkFontMgr_Indirect>(std::move(impl), std::move(proxy)); |
| } |
| #endif//defined(SK_BUILD_FOR_WIN) |