From 50dacbc8fc2fd386bdddef998a95d6e942bf3697 Mon Sep 17 00:00:00 2001 From: reuk Date: Tue, 25 Jan 2022 22:17:25 +0000 Subject: [PATCH] Mac Fonts: Fix rendering of AttributedStrings with empty ranges Previously, lifetime management of CTFontRefs was not implemented correctly. For zero-length ranges, the font may not be retained when applying it to a CFAttributedString, meaning that the reference stored in the fontMap sometimes became invalid before createCFAttributedString() returned. We now retain font refs when adding them to the font map, and release them when the map is destroyed, ensuring that the font references remain valid throughout the lifetime of the map. --- .../juce_graphics/native/juce_mac_Fonts.mm | 50 ++++++++++++++++--- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/modules/juce_graphics/native/juce_mac_Fonts.mm b/modules/juce_graphics/native/juce_mac_Fonts.mm index 4802dc492c..0a9e4f6a01 100644 --- a/modules/juce_graphics/native/juce_mac_Fonts.mm +++ b/modules/juce_graphics/native/juce_mac_Fonts.mm @@ -208,15 +208,51 @@ namespace CoreTextTypeLayout } //============================================================================== + // A flatmap that properly retains/releases font refs + class FontMap + { + public: + void emplace (CTFontRef ctFontRef, Font value) + { + pairs.emplace (std::lower_bound (pairs.begin(), pairs.end(), ctFontRef), ctFontRef, std::move (value)); + } + + const Font* find (CTFontRef ctFontRef) const + { + const auto iter = std::lower_bound (pairs.begin(), pairs.end(), ctFontRef); + + if (iter == pairs.end()) + return nullptr; + + if (iter->key.get() != ctFontRef) + return nullptr; + + return &iter->value; + } + + private: + struct Pair + { + Pair (CTFontRef ref, Font font) : key (ref), value (std::move (font)) { CFRetain (ref); } + + auto operator< (CTFontRef other) const { return key.get() < other; } + + CFUniquePtr key; + Font value; + }; + + std::vector pairs; + }; + struct AttributedStringAndFontMap { CFUniquePtr string; - std::map fontMap; + FontMap fontMap; }; static AttributedStringAndFontMap createCFAttributedString (const AttributedString& text) { - std::map fontMap; + FontMap fontMap; const detail::ColorSpacePtr rgbColourSpace { CGColorSpaceCreateWithName (kCGColorSpaceSRGB) }; @@ -300,7 +336,7 @@ namespace CoreTextTypeLayout struct FramesetterAndFontMap { CFUniquePtr framesetter; - std::map fontMap; + FontMap fontMap; }; static FramesetterAndFontMap createCTFramesetter (const AttributedString& text) @@ -324,7 +360,7 @@ namespace CoreTextTypeLayout struct FrameAndFontMap { CFUniquePtr frame; - std::map fontMap; + FontMap fontMap; }; static FrameAndFontMap createCTFrame (const AttributedString& text, CGRect bounds) @@ -476,10 +512,8 @@ namespace CoreTextTypeLayout { glyphRun->font = [&] { - auto it = frameAndMap.fontMap.find (ctRunFont); - - if (it != frameAndMap.fontMap.end()) - return it->second; + if (auto* it = frameAndMap.fontMap.find (ctRunFont)) + return *it; CFUniquePtr cfsFontName (CTFontCopyPostScriptName (ctRunFont)); CFUniquePtr ctFontRef (CTFontCreateWithName (cfsFontName.get(), referenceFontSize, nullptr));