blob: d244f04530e9abf8bfc014cbb39ba516e2f068a5 [file] [log] [blame]
/*
* Copyright 2022 Rive
*/
#ifndef _RIVE_TEXT_ENGINE_HPP_
#define _RIVE_TEXT_ENGINE_HPP_
#include "rive/math/raw_path.hpp"
#include "rive/refcnt.hpp"
#include "rive/span.hpp"
#include "rive/simple_array.hpp"
namespace rive
{
// Representation of a single unicode codepoint.
using Unichar = uint32_t;
// Id for a glyph within a font.
using GlyphID = uint16_t;
struct TextRun;
struct GlyphRun;
bool isWhiteSpace(Unichar c);
// Direction a paragraph or run flows in.
enum class TextDirection : uint8_t
{
ltr = 0,
rtl = 1
};
// The alignment of each word wrapped line in a paragraph.
enum class TextAlign : uint8_t
{
left = 0,
right = 1,
center = 2
};
// A horizontal line of text within a paragraph, after line-breaking.
struct GlyphLine
{
uint32_t startRunIndex;
uint32_t startGlyphIndex;
uint32_t endRunIndex;
uint32_t endGlyphIndex;
float startX;
float top = 0, baseline = 0, bottom = 0;
bool operator==(const GlyphLine& o) const
{
return startRunIndex == o.startRunIndex && startGlyphIndex == o.startGlyphIndex &&
endRunIndex == o.endRunIndex && endGlyphIndex == o.endGlyphIndex;
}
GlyphLine() :
startRunIndex(0), startGlyphIndex(0), endRunIndex(0), endGlyphIndex(0), startX(0.0f)
{}
GlyphLine(uint32_t run, uint32_t index) :
startRunIndex(run),
startGlyphIndex(index),
endRunIndex(run),
endGlyphIndex(index),
startX(0.0f)
{}
bool empty() const { return startRunIndex == endRunIndex && startGlyphIndex == endGlyphIndex; }
static SimpleArray<GlyphLine> BreakLines(Span<const GlyphRun> runs, float width);
// Compute values for top/baseline/bottom per line
static void ComputeLineSpacing(bool isFirstLine,
Span<GlyphLine>,
Span<const GlyphRun>,
float width,
TextAlign align);
static float ComputeMaxWidth(Span<GlyphLine> lines, Span<const GlyphRun> runs);
};
// A paragraph represents of set of runs that flow in a specific direction. The
// runs are always provided in LTR and must be drawn in reverse when the
// baseDirection is RTL. These are built by the system during shaping where the
// user provided string and text styling is converted to shaped paragraphs.
struct Paragraph
{
SimpleArray<GlyphRun> runs;
TextDirection baseDirection;
};
// An abstraction for interfacing with an individual font.
class Font : public RefCnt<Font>
{
public:
virtual ~Font() {}
struct LineMetrics
{
float ascent, descent;
};
const LineMetrics& lineMetrics() const { return m_lineMetrics; }
// Variable axis available for the font.
struct Axis
{
uint32_t tag;
float min;
float def; // default value
float max;
};
// Variable axis setting.
struct Coord
{
uint32_t axis;
float value;
};
// Returns the count of variable axes available for this font.
virtual uint16_t getAxisCount() const = 0;
// Returns the definition of the Axis at the provided index.
virtual Axis getAxis(uint16_t index) const = 0;
// Value for the axis, if a Coord has been provided the value from the Coord
// will be used. Otherwise the default value for the axis will be returned.
virtual float getAxisValue(uint32_t axisTag) const = 0;
// Font feature.
struct Feature
{
uint32_t tag;
uint32_t value;
};
// Returns the features available for this font.
virtual SimpleArray<uint32_t> features() const = 0;
virtual bool hasGlyph(rive::Span<const rive::Unichar>) const = 0;
// Value for the feature, if no value has been provided a (uint32_t)-1 is
// returned to signal that the text engine will pick the best feature value
// for the content.
virtual uint32_t getFeatureValue(uint32_t featureTag) const = 0;
rcp<Font> makeAtCoords(Span<const Coord> coords) const
{
return withOptions(coords, Span<const Feature>(nullptr, 0));
}
rcp<Font> makeAtCoord(Coord c) { return this->makeAtCoords(Span<const Coord>(&c, 1)); }
virtual rcp<Font> withOptions(Span<const Coord> variableAxes,
Span<const Feature> features) const = 0;
// Returns a 1-point path for this glyph. It will be positioned
// relative to (0,0) with the typographic baseline at y = 0.
//
virtual RawPath getPath(GlyphID) const = 0;
SimpleArray<Paragraph> shapeText(Span<const Unichar> text, Span<const TextRun> runs) const;
// If the platform can supply fallback font(s), set this function pointer.
// It will be called with a span of unichars, and the platform attempts to
// return a font that can draw (at least some of) them. If no font is available
// just return nullptr.
using FallbackProc = rive::rcp<rive::Font> (*)(rive::Span<const rive::Unichar>);
static FallbackProc gFallbackProc;
protected:
Font(const LineMetrics& lm) : m_lineMetrics(lm) {}
virtual SimpleArray<Paragraph> onShapeText(Span<const Unichar> text,
Span<const TextRun> runs) const = 0;
private:
/// The font specified line metrics (automatic line metrics).
const LineMetrics m_lineMetrics;
};
// A user defined styling guide for a set of unicode codepoints within a larger text string.
struct TextRun
{
rcp<Font> font;
float size;
float lineHeight;
float letterSpacing;
uint32_t unicharCount;
uint32_t script;
uint16_t styleId;
TextDirection dir;
};
// The corresponding system generated run for the user provided TextRuns. GlyphRuns may not match
// TextRuns if the system needs to split the run (for fallback fonts) or if codepoints get
// ligated/shaped to a single glyph.
struct GlyphRun
{
GlyphRun(size_t glyphCount = 0) :
glyphs(glyphCount),
textIndices(glyphCount),
advances(glyphCount),
xpos(glyphCount + 1),
offsets(glyphCount)
{}
GlyphRun(SimpleArray<GlyphID> glyphIds,
SimpleArray<uint32_t> offsets,
SimpleArray<float> ws,
SimpleArray<float> xs,
SimpleArray<rive::Vec2D> offs) :
glyphs(glyphIds), textIndices(offsets), advances(ws), xpos(xs), offsets(offs)
{}
rcp<Font> font;
float size;
float lineHeight;
float letterSpacing;
// List of glyphs, represented by font specific glyph ids. Length is equal to number of glyphs
// in the run.
SimpleArray<GlyphID> glyphs;
// Index in the unicode text array representing the text displayed in this run. Because each
// glyph can be composed of multiple unicode values, this index points to the first index in the
// unicode text. Length is equal to number of glyphs in the run.
SimpleArray<uint32_t> textIndices;
// X position of each glyph in visual order (xpos is in logical/memory order).
SimpleArray<float> advances;
// X position of each glyph, with an extra value at the end for the right most extent of the
// last glyph.
SimpleArray<float> xpos;
// X and Y offset each glyphs draws at relative to its baseline and advance position.
SimpleArray<rive::Vec2D> offsets;
// List of possible indices to line break at. Has a stride of 2 uint32_ts where each pair marks
// the start and end of a word, with the exception of a return character (forced linebreak)
// which is represented as a 0 length word (where start/end index is the same).
SimpleArray<uint32_t> breaks;
// The unique identifier for the styling (fill/stroke colors, anything not determined by the
// font or font size) applied to this run.
uint16_t styleId;
// The text direction (LTR = 0/RTL = 1)
TextDirection dir;
};
} // namespace rive
#endif