blob: 1b1dae31b6320627aa5869f5d35ac003c1fe7863 [file]
#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