blob: 41897e92bc20c51764a1d1e4c33d4a2eb1b08709 [file]
/*
* Copyright 2026 Rive
*/
#ifndef _RIVE_LINE_BREAK_DICTIONARY_HPP_
#define _RIVE_LINE_BREAK_DICTIONARY_HPP_
#include "rive/text/line_break.hpp"
#include <cstdint>
#ifdef WITH_RIVE_TEXT_DICT_COMPRESSED
#include <vector>
#endif
#if defined(WITH_RIVE_TEXT_DICT_THAI) || defined(WITH_RIVE_TEXT_DICT_LAO) || \
defined(WITH_RIVE_TEXT_DICT_KHMER) || defined(WITH_RIVE_TEXT_DICT_BURMESE)
#define RIVE_LINE_BREAK_DICTIONARIES
#endif
namespace rive
{
// A word list for a script written without spaces between words (UAX #14
// class SA), stored as a minimized trie. Without one, LB1 turns the whole
// script into AL and an unspaced phrase becomes a single word. Each language
// is compiled in by its own WITH_RIVE_TEXT_DICT_<LANGUAGE> define, see
// dev/unicode/generate_line_break_dictionaries.py.
struct LineBreakDictionary
{
// Inclusive {first, last} codepoint pairs the dictionary segments.
Span<const uint32_t> ranges;
// Sorted codepoints the words use; a codepoint's index is its symbol.
Span<const uint32_t> alphabet;
// Codepoints a word boundary may not precede or follow, so a syllable
// stays whole when a word isn't in the list.
Span<const uint32_t> noBreakBefore;
Span<const uint32_t> noBreakAfter;
// Repetition and abbreviation signs. They can't start a word either, they
// belong to the word before them.
Span<const uint32_t> suffixes;
// Marks that make the consonant they directly follow end the previous
// syllable (killers, asat, virama), so no boundary may precede that
// consonant. Only codaSkip marks may sit between the two.
Span<const uint32_t> codaMarks;
Span<const uint32_t> codaSkip;
#ifdef WITH_RIVE_TEXT_DICT_COMPRESSED
// The sorted words, front coded and deflated (raw RFC 1951), and their
// inflated size. lineBreakDictionaryEdges() builds the trie from them on
// first use.
Span<const uint8_t> words;
uint32_t wordsSize;
#else
// The trie. Each state's edges are contiguous, sorted by symbol, and the
// root's start at 0.
Span<const uint32_t> edges;
#endif
// Size and FNV-1a checksum of the trie's edges, so a rebuilt trie can be
// checked against the one the generator made.
uint32_t edgeCount;
uint32_t edgeChecksum;
static constexpr uint32_t symbolMask = 0x7F;
// Set on the last edge of a state.
static constexpr uint32_t lastEdge = 1 << 7;
// Set when a word ends after this edge's symbol.
static constexpr uint32_t wordEnd = 1 << 8;
// The rest is the index of the target state's first edge, 0 when the
// target has no edges.
static constexpr uint32_t targetShift = 9;
};
#ifdef RIVE_LINE_BREAK_DICTIONARIES
// The compiled in dictionary that segments codepoint, if any.
const LineBreakDictionary* lineBreakDictionaryFor(uint32_t codepoint);
// The dictionary's trie edges. With WITH_RIVE_TEXT_DICT_COMPRESSED the first
// call builds them from the word list, later calls share that copy.
Span<const uint32_t> lineBreakDictionaryEdges(
const LineBreakDictionary& dictionary);
#ifdef WITH_RIVE_TEXT_DICT_COMPRESSED
// Inflates the dictionary's word list and builds its trie, the same edges
// the generator would have compiled in. Empty if the data is corrupt.
std::vector<uint32_t> buildLineBreakDictionaryEdges(
const LineBreakDictionary& dictionary);
#endif
// Adds break opportunities between dictionary words inside every run of a
// dictionary's script. Only turns none into allowed, so it runs after the
// UAX #14 rules have filled out.
void applyLineBreakDictionaries(Span<const uint32_t> text, Span<LineBreak> out);
#endif
} // namespace rive
#endif