| #ifndef _RIVE_FONT_HB_HPP_ | |
| #define _RIVE_FONT_HB_HPP_ | |
| #include "rive/factory.hpp" | |
| #include "rive/text_engine.hpp" | |
| #include <unordered_map> | |
| #include <memory> | |
| #include <vector> | |
| // Platforms where HBFont::DecodeFile can map a file. Declared here so callers | |
| // and tests branch on the same condition the implementation uses. | |
| #if defined(__APPLE__) || defined(__linux__) || defined(__ANDROID__) | |
| #define RIVE_HB_FILE_MAPPING 1 | |
| #endif | |
| struct hb_font_t; | |
| struct hb_draw_funcs_t; | |
| struct hb_paint_funcs_t; | |
| struct hb_feature_t; | |
| using hb_color_t = uint32_t; | |
| class HBFont : public rive::Font | |
| { | |
| public: | |
| /** A mapped face for nominal coverage queries, without Rive font | |
| * initialization. */ | |
| class FileProbe | |
| { | |
| public: | |
| FileProbe(const FileProbe&) = delete; | |
| FileProbe& operator=(const FileProbe&) = delete; | |
| /** Releases the probe's font and its reference to the mapped file. */ | |
| ~FileProbe(); | |
| /** | |
| * Checks nominal coverage without shaping, metrics, or drawing setup. | |
| * @param codepoint Unicode character to look up. | |
| * @return Whether the face contains the character, or false after | |
| * promotion. | |
| */ | |
| bool hasGlyph(rive::Unichar codepoint) const; | |
| /** | |
| * Transfers the existing HarfBuzz font and mapping into a Rive font. | |
| * @return The initialized font, or nullptr if already promoted. | |
| */ | |
| rive::rcp<rive::Font> makeFont(); | |
| private: | |
| friend class HBFont; | |
| /** Takes ownership of a HarfBuzz font and enables nominal glyph lookup. | |
| */ | |
| explicit FileProbe(hb_font_t* font); | |
| hb_font_t* m_font; | |
| }; | |
| /** | |
| * Maps a file for coverage queries without initializing Rive font metrics | |
| * or drawing. The trusted, stable file restrictions documented on | |
| * DecodeFile also apply here. | |
| * @param path Stable local font file to map. | |
| * @param faceIndex Face within a font collection. | |
| * @return An owning probe, or nullptr on detectable failure or unsupported | |
| * platforms. | |
| */ | |
| static std::unique_ptr<FileProbe> ProbeFile( | |
| const char* path, | |
| unsigned faceIndex = 0); | |
| // We assume ownership of font! | |
| HBFont(hb_font_t* font); | |
| ~HBFont() override; | |
| Axis getAxis(uint16_t index) const override; | |
| uint16_t getAxisCount() const override; | |
| float getAxisValue(uint32_t axisTag) const override; | |
| uint32_t getFeatureValue(uint32_t featureTag) const override; | |
| uint16_t getWeight() const override; | |
| bool isItalic() const override; | |
| rive::RawPath getPath(rive::GlyphID) const override; | |
| const rive::RawPath& glyphPath(rive::GlyphID) const override; | |
| bool hasColorGlyphs() const override; | |
| bool isColorGlyph(rive::GlyphID) const override; | |
| size_t getColorLayers( | |
| rive::GlyphID, | |
| std::vector<ColorGlyphLayer>& out, | |
| rive::ColorInt foreground = 0xFF000000) const override; | |
| rive::SimpleArray<rive::Paragraph> onShapeText( | |
| rive::Span<const rive::Unichar>, | |
| rive::Span<const rive::TextRun>, | |
| int textDirectionFlag) const override; | |
| rive::SimpleArray<uint32_t> features() const override; | |
| rive::rcp<Font> withOptions( | |
| rive::Span<const Coord> variableAxes, | |
| rive::Span<const Feature> features) const override; | |
| bool hasGlyph(const rive::Unichar) const override; | |
| std::vector<rive::Unichar> codepoints() const override; | |
| static rive::rcp<rive::Font> Decode(rive::Span<const uint8_t>); | |
| /// Decodes [bytes] in place, keeping them for the font's lifetime instead | |
| /// of copying them. | |
| static rive::rcp<rive::Font> Decode(std::vector<uint8_t>&& bytes); | |
| /// Decodes the font at [path] by mapping the file rather than copying it. | |
| /// [faceIndex] picks a face within a font collection (.ttc). | |
| /// | |
| /// Only for trusted, stable local files -- system fonts are the intended | |
| /// use. The mapping stays live for the font's lifetime, and a null return | |
| /// covers ONLY failures detectable during this call (unsupported platform, | |
| /// missing/unreadable/empty file, oversized file, a blob harfbuzz refuses). | |
| /// It is not a guarantee about later access: if the file is truncated or | |
| /// its backing storage disappears, faulting a page in raises SIGBUS and | |
| /// terminates the process. Do not point this at user-supplied paths, | |
| /// removable media or network volumes -- use Decode with your own bytes. | |
| /// | |
| /// Returns null where RIVE_HB_FILE_MAPPING is undefined, so callers must | |
| /// always have a Decode fallback. | |
| static rive::rcp<rive::Font> DecodeFile( | |
| const char* path, | |
| unsigned faceIndex = 0); | |
| static rive::rcp<rive::Font> FromSystem( | |
| void* systemFont, | |
| bool useSystemShaper, | |
| uint16_t weight, | |
| uint8_t width); | |
| static float GetStyle(hb_font_t*, uint32_t); | |
| hb_font_t* font() const { return m_font; } | |
| private: | |
| HBFont( | |
| hb_font_t* font, | |
| std::unordered_map<uint32_t, float> axisValues, | |
| std::unordered_map<uint32_t, uint32_t> featureValues, | |
| std::vector<hb_feature_t> features); | |
| public: | |
| hb_font_t* m_font; | |
| virtual void shapeFallbackRun( | |
| rive::SimpleArrayBuilder<rive::GlyphRun>& gruns, | |
| const rive::Unichar text[], | |
| const unsigned textStart, | |
| const rive::TextRun& textRun, | |
| const rive::TextRun& originalTextRun, | |
| const uint32_t fallbackIndex); | |
| // Draws [glyph]'s outline. glyphPath (and getPath) keep what this returns, | |
| // so a font that draws its outlines elsewhere overrides this instead. | |
| virtual rive::RawPath drawGlyphPath(rive::GlyphID glyph) const; | |
| // The features list to pass directly to Harfbuzz. | |
| std::vector<hb_feature_t> m_features; | |
| private: | |
| hb_draw_funcs_t* m_drawFuncs; | |
| hb_paint_funcs_t* m_paintFuncs; | |
| // Feature value lookup based on tag. | |
| std::unordered_map<uint32_t, uint32_t> m_featureValues; | |
| // Axis value lookup based on for the feature. | |
| std::unordered_map<uint32_t, float> m_axisValues; | |
| // Color glyph (emoji) support — COLRv0 (layers), COLRv1 (paint), | |
| // and/or SBIX/CBDT (PNG bitmap). | |
| bool m_hasColorLayers = false; // COLRv0 | |
| bool m_hasColorPaint = false; // COLRv1 | |
| bool m_hasPNG = false; // SBIX or CBDT | |
| std::vector<hb_color_t> m_paletteColors; | |
| mutable std::unordered_map<rive::GlyphID, std::vector<ColorGlyphLayer>> | |
| m_colorLayerCache; | |
| // Outlines already drawn, so a text that reshapes (or a list row made for | |
| // a new item) reuses its glyphs instead of walking the glyf table again. | |
| // Bounded; it starts over once full. | |
| mutable std::unordered_map<rive::GlyphID, rive::RawPath> m_glyphPathCache; | |
| }; | |
| #endif |