From 6deb508e96aec92ab8db6bc8f4a2359dd041aade Mon Sep 17 00:00:00 2001 From: reuk Date: Wed, 9 Apr 2025 13:30:32 +0100 Subject: [PATCH] Direct2D: Move Direct2DFactories to juce_DirectX_windows.h --- .../juce_DirectWriteTypeface_windows.cpp | 514 ------------------ .../native/juce_DirectX_windows.h | 514 ++++++++++++++++++ 2 files changed, 514 insertions(+), 514 deletions(-) diff --git a/modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp b/modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp index dee0481ff9..091c0ae409 100644 --- a/modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp +++ b/modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp @@ -35,520 +35,6 @@ namespace juce { -static String getLocalisedName (IDWriteLocalizedStrings* names) -{ - jassert (names != nullptr); - - uint32 index = 0; - BOOL exists = false; - [[maybe_unused]] auto hr = names->FindLocaleName (L"en-us", &index, &exists); - - if (! exists) - index = 0; - - uint32 length = 0; - hr = names->GetStringLength (index, &length); - - HeapBlock name (length + 1); - hr = names->GetString (index, name, length + 1); - - return static_cast (name); -} - -static String getFontFamilyName (IDWriteFontFamily* family) -{ - jassert (family != nullptr); - ComSmartPtr familyNames; - auto hr = family->GetFamilyNames (familyNames.resetAndGetPointerAddress()); - jassertquiet (SUCCEEDED (hr)); - return getLocalisedName (familyNames); -} - -static String getFontFaceName (IDWriteFont* font) -{ - jassert (font != nullptr); - ComSmartPtr faceNames; - auto hr = font->GetFaceNames (faceNames.resetAndGetPointerAddress()); - jassertquiet (SUCCEEDED (hr)); - return getLocalisedName (faceNames); -} - -template -static StringArray stringArrayFromRange (Range&& range) -{ - StringArray result; - - for (const auto& item : range) - result.add (item); - - return result; -} - -class AggregateFontCollection -{ -public: - explicit AggregateFontCollection (ComSmartPtr baseCollection) - : collections { std::move (baseCollection) } {} - - StringArray findAllTypefaceNames() - { - const std::scoped_lock lock { mutex }; - - std::set strings; - - for (const auto& collection : collections) - { - const auto count = collection->GetFontFamilyCount(); - - for (auto i = decltype (count){}; i < count; ++i) - { - ComSmartPtr family; - - if (FAILED (collection->GetFontFamily (i, family.resetAndGetPointerAddress())) || family == nullptr) - continue; - - strings.insert (getFontFamilyName (family)); - } - } - - return stringArrayFromRange (strings); - } - - static std::vector> getAllFontsInFamily (IDWriteFontFamily* fontFamily) - { - const auto fontFacesCount = fontFamily->GetFontCount(); - std::vector> result; - result.reserve (fontFacesCount); - - for (UINT32 i = 0; i < fontFacesCount; ++i) - { - ComSmartPtr dwFont; - - if (FAILED (fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress()))) - continue; - - if (dwFont->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) - continue; - - result.push_back (dwFont); - } - - return result; - } - - StringArray findAllTypefaceStyles (const String& family) - { - const std::scoped_lock lock { mutex }; - - for (const auto& collection : collections) - { - BOOL fontFound = false; - uint32 fontIndex = 0; - - if (FAILED (collection->FindFamilyName (family.toWideCharPointer(), &fontIndex, &fontFound)) || ! fontFound) - continue; - - ComSmartPtr fontFamily; - - if (FAILED (collection->GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress())) || fontFamily == nullptr) - continue; - - std::set uniqueResults; - StringArray orderedResults; - - for (const auto& font : getAllFontsInFamily (fontFamily)) - { - const auto name = getFontFaceName (font); - - if (uniqueResults.insert (name).second) - orderedResults.add (name); - } - - return orderedResults; - } - - return {}; - } - - ComSmartPtr getFamilyByName (const wchar_t* name) - { - const std::scoped_lock lock { mutex }; - - for (const auto& collection : collections) - { - const auto fontIndex = [&] - { - BOOL found = false; - UINT32 index = 0; - - return (SUCCEEDED (collection->FindFamilyName (name, &index, &found)) && found) - ? index - : (UINT32) -1; - }(); - - if (fontIndex == (UINT32) -1) - continue; - - ComSmartPtr family; - - if (FAILED (collection->GetFontFamily (fontIndex, family.resetAndGetPointerAddress())) || family == nullptr) - continue; - - return family; - } - - return {}; - } - - ComSmartPtr findFontForFace (IDWriteFontFace* face) - { - for (const auto& collection : collections) - { - ComSmartPtr result; - - if (SUCCEEDED (collection->GetFontFromFontFace (face, result.resetAndGetPointerAddress()))) - return result; - } - - return {}; - } - - void addCollection (ComSmartPtr collection) - { - const std::scoped_lock lock { mutex }; - collections.push_back (std::move (collection)); - } - - void removeCollection (ComSmartPtr collection) - { - const std::scoped_lock lock { mutex }; - const auto iter = std::find (collections.begin(), collections.end(), collection); - - if (iter != collections.end()) - collections.erase (iter); - } - - struct MapResult - { - ComSmartPtr font; - UINT32 length{}; - float scale{}; - }; - - /* Tries matching against each collection in turn. - If any collection is able to match the entire string, then uses the appropriate font - from that collection. - Otherwise, returns the font that is able to match the longest sequence of characters, - preferring user-provided fonts. - */ - MapResult mapCharacters (IDWriteFontFallback* fallback, - IDWriteTextAnalysisSource* analysisSource, - UINT32 textPosition, - UINT32 textLength, - wchar_t const* baseFamilyName, - DWRITE_FONT_WEIGHT baseWeight, - DWRITE_FONT_STYLE baseStyle, - DWRITE_FONT_STRETCH baseStretch) noexcept - { - const std::scoped_lock lock { mutex }; - - // For reasons I don't understand, the system may pick better substitutions when passing - // nullptr, instead of the system collection, as the "default collection to use". - auto collectionsToCheck = collections; - collectionsToCheck.insert (collectionsToCheck.begin(), nullptr); - - MapResult bestMatch; - for (const auto& collection : collectionsToCheck) - { - MapResult result; - const auto status = fallback->MapCharacters (analysisSource, - textPosition, - textLength, - collection, - baseFamilyName, - baseWeight, - baseStyle, - baseStretch, - &result.length, - result.font.resetAndGetPointerAddress(), - &result.scale); - - if (FAILED (status) || result.font == nullptr) - continue; - - if (result.length == textLength) - return result; - - if (result.length >= bestMatch.length) - bestMatch = result; - } - - return bestMatch; - } - -private: - std::vector> collections; - std::mutex mutex; -}; - -class MemoryFontFileStream final : public ComBaseClassHelper -{ -public: - explicit MemoryFontFileStream (std::shared_ptr blockIn) - : block (std::move (blockIn)) - { - } - - JUCE_COMRESULT GetFileSize (UINT64* fileSize) noexcept override - { - *fileSize = block->getSize(); - return S_OK; - } - - JUCE_COMRESULT GetLastWriteTime (UINT64* lastWriteTime) noexcept override - { - *lastWriteTime = 0; - return S_OK; - } - - JUCE_COMRESULT ReadFileFragment (const void** fragmentStart, - UINT64 fileOffset, - UINT64 fragmentSize, - void** fragmentContext) noexcept override - { - if (fileOffset + fragmentSize > block->getSize()) - { - *fragmentStart = nullptr; - *fragmentContext = nullptr; - return E_INVALIDARG; - } - - *fragmentStart = addBytesToPointer (block->getData(), fileOffset); - *fragmentContext = this; - return S_OK; - } - - void WINAPI ReleaseFileFragment (void*) noexcept override {} - -private: - std::shared_ptr block; -}; - -class MemoryFontFileLoader final : public ComBaseClassHelper -{ -public: - explicit MemoryFontFileLoader (MemoryBlock blob) - : block (std::make_shared (std::move (blob))) - { - } - - HRESULT WINAPI CreateStreamFromKey (const void* fontFileReferenceKey, - UINT32 keySize, - IDWriteFontFileStream** fontFileStream) noexcept override - { - if (keySize != Uuid::size()) - return E_INVALIDARG; - - Uuid requestedKey { static_cast (fontFileReferenceKey) }; - - if (requestedKey == uuid) - { - *fontFileStream = new MemoryFontFileStream { block }; - return S_OK; - } - - return E_INVALIDARG; - } - - Uuid getUuid() const { return uuid; } - -private: - std::shared_ptr block; - Uuid uuid; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryFontFileLoader) -}; - -class FontFileEnumerator final : public ComBaseClassHelper -{ -public: - FontFileEnumerator (IDWriteFactory& factoryIn, ComSmartPtr loaderIn) - : factory (factoryIn), loader (loaderIn) {} - - HRESULT WINAPI GetCurrentFontFile (IDWriteFontFile** fontFile) noexcept override - { - *fontFile = nullptr; - - if (! isPositiveAndBelow (rawDataIndex, 1)) - return E_FAIL; - - const auto uuid = loader->getUuid(); - return factory.CreateCustomFontFileReference (uuid.getRawData(), - (UINT32) uuid.size(), - loader, - fontFile); - } - - HRESULT WINAPI MoveNext (BOOL* hasCurrentFile) noexcept override - { - ++rawDataIndex; - *hasCurrentFile = rawDataIndex < 1 ? TRUE : FALSE; - return S_OK; - } - -private: - IDWriteFactory& factory; - ComSmartPtr loader; - size_t rawDataIndex = std::numeric_limits::max(); - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FontFileEnumerator) -}; - -class DirectWriteCustomFontCollectionLoader final : public ComBaseClassHelper -{ -public: - explicit DirectWriteCustomFontCollectionLoader (IDWriteFactory& factoryIn) - : factory (factoryIn) - { - } - - ~DirectWriteCustomFontCollectionLoader() override - { - for (const auto& loader : fileLoaders) - factory.UnregisterFontFileLoader (loader); - } - - Uuid addRawFontData (Span blob) - { - const auto loader = becomeComSmartPtrOwner (new MemoryFontFileLoader { { blob.data(), blob.size() } }); - - factory.RegisterFontFileLoader (loader); - - fileLoaders.push_back (loader); - - return fileLoaders.back()->getUuid(); - } - - HRESULT WINAPI CreateEnumeratorFromKey (IDWriteFactory* factoryIn, - const void* collectionKey, - UINT32 collectionKeySize, - IDWriteFontFileEnumerator** fontFileEnumerator) noexcept override - { - if (collectionKeySize != Uuid::size()) - return E_INVALIDARG; - - const Uuid requestedCollectionKey { static_cast (collectionKey) }; - - for (const auto& loader : fileLoaders) - { - if (loader->getUuid() != requestedCollectionKey) - continue; - - *fontFileEnumerator = new FontFileEnumerator { *factoryIn, loader }; - return S_OK; - } - - return E_INVALIDARG; - } - -private: - IDWriteFactory& factory; - std::vector> fileLoaders; -}; - -//============================================================================== -class Direct2DFactories -{ -public: - Direct2DFactories() - { - ComSmartPtr collection; - - if (SUCCEEDED (directWriteFactory->GetSystemFontCollection (collection.resetAndGetPointerAddress(), FALSE)) && collection != nullptr) - fonts.emplace (collection); - else - jassertfalse; - } - - ~Direct2DFactories() - { - if (directWriteFactory == nullptr) - return; - - directWriteFactory->UnregisterFontCollectionLoader (collectionLoader); - } - - [[nodiscard]] ComSmartPtr getDWriteFactory() const { return directWriteFactory; } - [[nodiscard]] ComSmartPtr getDWriteFactory4() const { return directWriteFactory4; } - [[nodiscard]] AggregateFontCollection& getFonts() { jassert (fonts.has_value()); return *fonts; } - [[nodiscard]] ComSmartPtr getCollectionLoader() const { return collectionLoader; } - -private: - DynamicLibrary direct2dDll { "d2d1.dll" }, directWriteDll { "DWrite.dll" }; - - const ComSmartPtr d2dFactory = [&]() -> ComSmartPtr - { - JUCE_LOAD_WINAPI_FUNCTION (direct2dDll, - D2D1CreateFactory, - d2d1CreateFactory, - HRESULT, - (D2D1_FACTORY_TYPE, REFIID, D2D1_FACTORY_OPTIONS*, void**)) - - if (d2d1CreateFactory == nullptr) - return {}; - - D2D1_FACTORY_OPTIONS options; - options.debugLevel = D2D1_DEBUG_LEVEL_NONE; - - ComSmartPtr result; - - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") - d2d1CreateFactory (D2D1_FACTORY_TYPE_SINGLE_THREADED, - __uuidof (ID2D1Factory), - &options, - (void**) result.resetAndGetPointerAddress()); - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - - return result; - }(); - - const ComSmartPtr directWriteFactory = [&]() -> ComSmartPtr - { - JUCE_LOAD_WINAPI_FUNCTION (directWriteDll, - DWriteCreateFactory, - dWriteCreateFactory, - HRESULT, - (DWRITE_FACTORY_TYPE, REFIID, IUnknown**)) - - if (dWriteCreateFactory == nullptr) - return {}; - - ComSmartPtr result; - - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") - dWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, - __uuidof (IDWriteFactory), - (IUnknown**) result.resetAndGetPointerAddress()); - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - - return result; - }(); - - const ComSmartPtr collectionLoader = [&]() -> ComSmartPtr - { - auto result = becomeComSmartPtrOwner (new DirectWriteCustomFontCollectionLoader { *directWriteFactory }); - directWriteFactory->RegisterFontCollectionLoader (result); - - return result; - }(); - - const ComSmartPtr directWriteFactory4 = directWriteFactory.getInterface(); - - std::optional fonts; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Direct2DFactories) -}; - StringArray Font::findAllTypefaceNames() { SharedResourcePointer factories; diff --git a/modules/juce_graphics/native/juce_DirectX_windows.h b/modules/juce_graphics/native/juce_DirectX_windows.h index c36acb8486..76e6dddff4 100644 --- a/modules/juce_graphics/native/juce_DirectX_windows.h +++ b/modules/juce_graphics/native/juce_DirectX_windows.h @@ -1423,4 +1423,518 @@ private: ComSmartPtr compositionVisual; }; +static String getLocalisedName (IDWriteLocalizedStrings* names) +{ + jassert (names != nullptr); + + uint32 index = 0; + BOOL exists = false; + [[maybe_unused]] auto hr = names->FindLocaleName (L"en-us", &index, &exists); + + if (! exists) + index = 0; + + uint32 length = 0; + hr = names->GetStringLength (index, &length); + + HeapBlock name (length + 1); + hr = names->GetString (index, name, length + 1); + + return static_cast (name); +} + +static String getFontFamilyName (IDWriteFontFamily* family) +{ + jassert (family != nullptr); + ComSmartPtr familyNames; + auto hr = family->GetFamilyNames (familyNames.resetAndGetPointerAddress()); + jassertquiet (SUCCEEDED (hr)); + return getLocalisedName (familyNames); +} + +static String getFontFaceName (IDWriteFont* font) +{ + jassert (font != nullptr); + ComSmartPtr faceNames; + auto hr = font->GetFaceNames (faceNames.resetAndGetPointerAddress()); + jassertquiet (SUCCEEDED (hr)); + return getLocalisedName (faceNames); +} + +template +static StringArray stringArrayFromRange (Range&& range) +{ + StringArray result; + + for (const auto& item : range) + result.add (item); + + return result; +} + +class AggregateFontCollection +{ +public: + explicit AggregateFontCollection (ComSmartPtr baseCollection) + : collections { std::move (baseCollection) } {} + + StringArray findAllTypefaceNames() + { + const std::scoped_lock lock { mutex }; + + std::set strings; + + for (const auto& collection : collections) + { + const auto count = collection->GetFontFamilyCount(); + + for (auto i = decltype (count){}; i < count; ++i) + { + ComSmartPtr family; + + if (FAILED (collection->GetFontFamily (i, family.resetAndGetPointerAddress())) || family == nullptr) + continue; + + strings.insert (getFontFamilyName (family)); + } + } + + return stringArrayFromRange (strings); + } + + static std::vector> getAllFontsInFamily (IDWriteFontFamily* fontFamily) + { + const auto fontFacesCount = fontFamily->GetFontCount(); + std::vector> result; + result.reserve (fontFacesCount); + + for (UINT32 i = 0; i < fontFacesCount; ++i) + { + ComSmartPtr dwFont; + + if (FAILED (fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress()))) + continue; + + if (dwFont->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) + continue; + + result.push_back (dwFont); + } + + return result; + } + + StringArray findAllTypefaceStyles (const String& family) + { + const std::scoped_lock lock { mutex }; + + for (const auto& collection : collections) + { + BOOL fontFound = false; + uint32 fontIndex = 0; + + if (FAILED (collection->FindFamilyName (family.toWideCharPointer(), &fontIndex, &fontFound)) || ! fontFound) + continue; + + ComSmartPtr fontFamily; + + if (FAILED (collection->GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress())) || fontFamily == nullptr) + continue; + + std::set uniqueResults; + StringArray orderedResults; + + for (const auto& font : getAllFontsInFamily (fontFamily)) + { + const auto name = getFontFaceName (font); + + if (uniqueResults.insert (name).second) + orderedResults.add (name); + } + + return orderedResults; + } + + return {}; + } + + ComSmartPtr getFamilyByName (const wchar_t* name) + { + const std::scoped_lock lock { mutex }; + + for (const auto& collection : collections) + { + const auto fontIndex = [&] + { + BOOL found = false; + UINT32 index = 0; + + return (SUCCEEDED (collection->FindFamilyName (name, &index, &found)) && found) + ? index + : (UINT32) -1; + }(); + + if (fontIndex == (UINT32) -1) + continue; + + ComSmartPtr family; + + if (FAILED (collection->GetFontFamily (fontIndex, family.resetAndGetPointerAddress())) || family == nullptr) + continue; + + return family; + } + + return {}; + } + + ComSmartPtr findFontForFace (IDWriteFontFace* face) + { + for (const auto& collection : collections) + { + ComSmartPtr result; + + if (SUCCEEDED (collection->GetFontFromFontFace (face, result.resetAndGetPointerAddress()))) + return result; + } + + return {}; + } + + void addCollection (ComSmartPtr collection) + { + const std::scoped_lock lock { mutex }; + collections.push_back (std::move (collection)); + } + + void removeCollection (ComSmartPtr collection) + { + const std::scoped_lock lock { mutex }; + const auto iter = std::find (collections.begin(), collections.end(), collection); + + if (iter != collections.end()) + collections.erase (iter); + } + + struct MapResult + { + ComSmartPtr font; + UINT32 length{}; + float scale{}; + }; + + /* Tries matching against each collection in turn. + If any collection is able to match the entire string, then uses the appropriate font + from that collection. + Otherwise, returns the font that is able to match the longest sequence of characters, + preferring user-provided fonts. + */ + MapResult mapCharacters (IDWriteFontFallback* fallback, + IDWriteTextAnalysisSource* analysisSource, + UINT32 textPosition, + UINT32 textLength, + wchar_t const* baseFamilyName, + DWRITE_FONT_WEIGHT baseWeight, + DWRITE_FONT_STYLE baseStyle, + DWRITE_FONT_STRETCH baseStretch) noexcept + { + const std::scoped_lock lock { mutex }; + + // For reasons I don't understand, the system may pick better substitutions when passing + // nullptr, instead of the system collection, as the "default collection to use". + auto collectionsToCheck = collections; + collectionsToCheck.insert (collectionsToCheck.begin(), nullptr); + + MapResult bestMatch; + for (const auto& collection : collectionsToCheck) + { + MapResult result; + const auto status = fallback->MapCharacters (analysisSource, + textPosition, + textLength, + collection, + baseFamilyName, + baseWeight, + baseStyle, + baseStretch, + &result.length, + result.font.resetAndGetPointerAddress(), + &result.scale); + + if (FAILED (status) || result.font == nullptr) + continue; + + if (result.length == textLength) + return result; + + if (result.length >= bestMatch.length) + bestMatch = result; + } + + return bestMatch; + } + +private: + std::vector> collections; + std::mutex mutex; +}; + +class MemoryFontFileStream final : public ComBaseClassHelper +{ +public: + explicit MemoryFontFileStream (std::shared_ptr blockIn) + : block (std::move (blockIn)) + { + } + + JUCE_COMRESULT GetFileSize (UINT64* fileSize) noexcept override + { + *fileSize = block->getSize(); + return S_OK; + } + + JUCE_COMRESULT GetLastWriteTime (UINT64* lastWriteTime) noexcept override + { + *lastWriteTime = 0; + return S_OK; + } + + JUCE_COMRESULT ReadFileFragment (const void** fragmentStart, + UINT64 fileOffset, + UINT64 fragmentSize, + void** fragmentContext) noexcept override + { + if (fileOffset + fragmentSize > block->getSize()) + { + *fragmentStart = nullptr; + *fragmentContext = nullptr; + return E_INVALIDARG; + } + + *fragmentStart = addBytesToPointer (block->getData(), fileOffset); + *fragmentContext = this; + return S_OK; + } + + void WINAPI ReleaseFileFragment (void*) noexcept override {} + +private: + std::shared_ptr block; +}; + +class MemoryFontFileLoader final : public ComBaseClassHelper +{ +public: + explicit MemoryFontFileLoader (MemoryBlock blob) + : block (std::make_shared (std::move (blob))) + { + } + + HRESULT WINAPI CreateStreamFromKey (const void* fontFileReferenceKey, + UINT32 keySize, + IDWriteFontFileStream** fontFileStream) noexcept override + { + if (keySize != Uuid::size()) + return E_INVALIDARG; + + Uuid requestedKey { static_cast (fontFileReferenceKey) }; + + if (requestedKey == uuid) + { + *fontFileStream = new MemoryFontFileStream { block }; + return S_OK; + } + + return E_INVALIDARG; + } + + Uuid getUuid() const { return uuid; } + +private: + std::shared_ptr block; + Uuid uuid; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryFontFileLoader) +}; + +class FontFileEnumerator final : public ComBaseClassHelper +{ +public: + FontFileEnumerator (IDWriteFactory& factoryIn, ComSmartPtr loaderIn) + : factory (factoryIn), loader (loaderIn) {} + + HRESULT WINAPI GetCurrentFontFile (IDWriteFontFile** fontFile) noexcept override + { + *fontFile = nullptr; + + if (! isPositiveAndBelow (rawDataIndex, 1)) + return E_FAIL; + + const auto uuid = loader->getUuid(); + return factory.CreateCustomFontFileReference (uuid.getRawData(), + (UINT32) uuid.size(), + loader, + fontFile); + } + + HRESULT WINAPI MoveNext (BOOL* hasCurrentFile) noexcept override + { + ++rawDataIndex; + *hasCurrentFile = rawDataIndex < 1 ? TRUE : FALSE; + return S_OK; + } + +private: + IDWriteFactory& factory; + ComSmartPtr loader; + size_t rawDataIndex = std::numeric_limits::max(); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FontFileEnumerator) +}; + +class DirectWriteCustomFontCollectionLoader final : public ComBaseClassHelper +{ +public: + explicit DirectWriteCustomFontCollectionLoader (IDWriteFactory& factoryIn) + : factory (factoryIn) + { + } + + ~DirectWriteCustomFontCollectionLoader() override + { + for (const auto& loader : fileLoaders) + factory.UnregisterFontFileLoader (loader); + } + + Uuid addRawFontData (Span blob) + { + const auto loader = becomeComSmartPtrOwner (new MemoryFontFileLoader { { blob.data(), blob.size() } }); + + factory.RegisterFontFileLoader (loader); + + fileLoaders.push_back (loader); + + return fileLoaders.back()->getUuid(); + } + + HRESULT WINAPI CreateEnumeratorFromKey (IDWriteFactory* factoryIn, + const void* collectionKey, + UINT32 collectionKeySize, + IDWriteFontFileEnumerator** fontFileEnumerator) noexcept override + { + if (collectionKeySize != Uuid::size()) + return E_INVALIDARG; + + const Uuid requestedCollectionKey { static_cast (collectionKey) }; + + for (const auto& loader : fileLoaders) + { + if (loader->getUuid() != requestedCollectionKey) + continue; + + *fontFileEnumerator = new FontFileEnumerator { *factoryIn, loader }; + return S_OK; + } + + return E_INVALIDARG; + } + +private: + IDWriteFactory& factory; + std::vector> fileLoaders; +}; + +//============================================================================== +class Direct2DFactories +{ +public: + Direct2DFactories() + { + ComSmartPtr collection; + + if (SUCCEEDED (directWriteFactory->GetSystemFontCollection (collection.resetAndGetPointerAddress(), FALSE)) && collection != nullptr) + fonts.emplace (collection); + else + jassertfalse; + } + + ~Direct2DFactories() + { + if (directWriteFactory == nullptr) + return; + + directWriteFactory->UnregisterFontCollectionLoader (collectionLoader); + } + + [[nodiscard]] ComSmartPtr getDWriteFactory() const { return directWriteFactory; } + [[nodiscard]] ComSmartPtr getDWriteFactory4() const { return directWriteFactory4; } + [[nodiscard]] AggregateFontCollection& getFonts() { jassert (fonts.has_value()); return *fonts; } + [[nodiscard]] ComSmartPtr getCollectionLoader() const { return collectionLoader; } + +private: + DynamicLibrary direct2dDll { "d2d1.dll" }, directWriteDll { "DWrite.dll" }; + + const ComSmartPtr d2dFactory = [&]() -> ComSmartPtr + { + JUCE_LOAD_WINAPI_FUNCTION (direct2dDll, + D2D1CreateFactory, + d2d1CreateFactory, + HRESULT, + (D2D1_FACTORY_TYPE, REFIID, D2D1_FACTORY_OPTIONS*, void**)) + + if (d2d1CreateFactory == nullptr) + return {}; + + D2D1_FACTORY_OPTIONS options; + options.debugLevel = D2D1_DEBUG_LEVEL_NONE; + + ComSmartPtr result; + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") + d2d1CreateFactory (D2D1_FACTORY_TYPE_SINGLE_THREADED, + __uuidof (ID2D1Factory), + &options, + (void**) result.resetAndGetPointerAddress()); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + return result; + }(); + + const ComSmartPtr directWriteFactory = [&]() -> ComSmartPtr + { + JUCE_LOAD_WINAPI_FUNCTION (directWriteDll, + DWriteCreateFactory, + dWriteCreateFactory, + HRESULT, + (DWRITE_FACTORY_TYPE, REFIID, IUnknown**)) + + if (dWriteCreateFactory == nullptr) + return {}; + + ComSmartPtr result; + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") + dWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, + __uuidof (IDWriteFactory), + (IUnknown**) result.resetAndGetPointerAddress()); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + return result; + }(); + + const ComSmartPtr collectionLoader = [&]() -> ComSmartPtr + { + auto result = becomeComSmartPtrOwner (new DirectWriteCustomFontCollectionLoader { *directWriteFactory }); + directWriteFactory->RegisterFontCollectionLoader (result); + + return result; + }(); + + const ComSmartPtr directWriteFactory4 = directWriteFactory.getInterface(); + + std::optional fonts; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Direct2DFactories) +}; + } // namespace juce