Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
bundled_fonts.hpp
Go to the documentation of this file.
1// bundled_fonts.hpp
2//
3// Pulp ships a small set of curated fonts under `external/fonts/` so plugin UIs
4// can render deterministically without depending on what happens to be
5// installed system-wide. Those .ttf files are baked into `pulp-canvas` at
6// build time (see core/canvas/CMakeLists.txt → pulp_add_binary_data) and this
7// module makes them visible to Skia's font lookup path.
8//
9// The Skia prebuilt binaries shipped via skia-builder are linked without
10// FreeType/Fontations, which means `SkFontMgr_New_Custom_Data` is not exported
11// from libskia.a. Instead we go one rung lower: use the *platform* font
12// manager (CoreText / DirectWrite / fontconfig / Android) to materialise the
13// bundled .ttfs into `SkTypeface`s via `SkFontMgr::makeFromData`, and cache
14// those typefaces by family name. Callers can then ask for the bundled face
15// before falling back to a `matchFamilyStyle` query that depends on the host
16// system.
17//
18// The embedded families are materialized through Skia on first lookup so
19// `canvas.set_font("JetBrains Mono", ...)` succeeds even on machines that
20// do not have JetBrains Mono installed system-wide.
21//
22// On Apple platforms the same faces are also kept in a CoreText registry, which
23// the CoreGraphics canvas paints with and, without Skia, TextShaper measures
24// with. Elsewhere, without Skia, bundled-font registration is a no-op.
25
26#pragma once
27
28#include <atomic>
29#include <cstddef>
30#include <cstdint>
31#include <future>
32#include <string>
33#include <optional>
34#include <vector>
35
36#ifdef PULP_HAS_SKIA
37#include "include/core/SkFontStyle.h"
38#include "include/core/SkRefCnt.h"
39class SkFontMgr;
40class SkTypeface;
41#endif
42
43namespace pulp::canvas {
44
45// ── Public font-registration API ─────────────────────────────────────────
46// Plugin authors bundle their own .ttf/.otf files (e.g. via the CMake
47// `pulp_register_font(target NAME path/to/font.ttf [FAMILY "..."])` macro
48// or by hand-rolling pulp_add_binary_data) and call `register_font(...)`
49// during plugin startup so subsequent `canvas.set_font("My Family", ...)`
50// and `setFontFamily(label, "My Family")` calls resolve to the supplied
51// face instead of silently falling back to the platform font manager.
52//
53// These entry points are available regardless of `PULP_HAS_SKIA`. On Apple
54// platforms every registration also reaches the CoreText registry; without
55// Skia that is the only registry, and `register_font` succeeds when CoreText
56// can read the bytes. On other platforms without Skia they degrade to a no-op
57// that returns `false`, which lets plugin startup code call them
58// unconditionally.
59
80bool register_font(const std::uint8_t* data, std::size_t size,
81 const std::string& family_override = "");
82
87bool register_font_file(const std::string& path,
88 const std::string& family_override = "");
89
94 int min = 400;
95 int max = 400;
96};
97
100void declare_font_face_weight_range(const std::string& family, int weight_min, int weight_max,
101 const std::string& scope = {});
102
105void clear_declared_font_face_weight_ranges(const std::string& scope);
106
108bool has_declared_font_face_weights(const std::string& family);
109
115int css_match_declared_weight(const std::vector<DeclaredWeightRange>& ranges, int desired_weight);
116
121std::optional<bool> declared_face_synthesizes_bold(const std::string& family, int requested_weight);
122
127std::uint64_t woff2_file_routes() noexcept;
128
136bool is_font_registered(const std::string& family);
137
141
146
147// ── Async lifecycle + font validation ────────────────────────────────────
148
156
161bool validate_font_bytes(const std::uint8_t* data, std::size_t size);
162
178std::future<FontState> register_font_url(const std::string& url,
179 const std::string& family_override = "");
180
181// ── Compressed fonts + text boundary helpers ─────────────────────────────
182
204bool register_font_woff2(const std::uint8_t* woff2_data, std::size_t size,
205 const std::string& family_override = "");
206
216
223std::size_t cluster_step(const std::string& text, std::size_t byte_offset,
224 bool forward = true);
225
232std::size_t word_break_step(const std::string& text,
233 std::size_t byte_offset,
234 const std::string& locale = "",
235 bool forward = true);
236
242std::vector<std::size_t> line_break_opportunities(const std::string& text,
243 const std::string& locale = "");
244
245#ifdef PULP_HAS_SKIA
246
251sk_sp<SkFontMgr> platform_font_manager();
252
275sk_sp<SkTypeface> match_bundled_typeface(SkFontMgr* mgr,
276 const std::string& family,
277 SkFontStyle style);
278
290sk_sp<SkTypeface> match_registered_typeface(const std::string& family,
291 SkFontStyle style);
292
304bool face_wght_axis(const SkTypeface* face,
305 float& out_min, float& out_max, float& out_default);
306
309struct RegisteredTypeface {
310 std::string family;
311 sk_sp<SkTypeface> typeface;
312};
313
329std::vector<RegisteredTypeface> registered_typefaces_snapshot();
330
337std::vector<RegisteredTypeface> bundled_typefaces_snapshot();
338
350bool platform_font_db_usable();
351
358sk_sp<SkTypeface> bundled_fallback_typeface();
359
364std::size_t bundled_font_count();
365
376struct FontProbe {
377 std::string family;
378 std::uint32_t codepoint = 0;
379 bool family_resolved = false;
380 bool glyph_present = false;
381 std::string resolved_family;
382};
383
384FontProbe probe_font_glyph(const std::string& family,
385 int weight, int slant,
386 std::uint32_t codepoint);
387
388#endif // PULP_HAS_SKIA
389
390} // namespace pulp::canvas
Definition affine_transform.hpp:24
std::size_t word_break_step(const std::string &text, std::size_t byte_offset, const std::string &locale="", bool forward=true)
Locale-aware word break iterator.
bool has_declared_font_face_weights(const std::string &family)
Whether any @font-face weight range has been declared for family.
void declare_font_face_weight_range(const std::string &family, int weight_min, int weight_max, const std::string &scope={})
Records that an @font-face rule bound family at a declared weight range for scope.
bool is_font_registered(const std::string &family)
Returns true iff the requested family name was previously registered via register_font / register_fon...
bool register_font_woff2(const std::uint8_t *woff2_data, std::size_t size, const std::string &family_override="")
Register a WOFF2-compressed font at runtime.
void clear_declared_font_face_weight_ranges(const std::string &scope)
Remove all declared ranges owned by scope.
std::vector< std::size_t > line_break_opportunities(const std::string &text, const std::string &locale="")
Find line-break opportunities in text.
bool register_font_file(const std::string &path, const std::string &family_override="")
Convenience overload: register a font from a file on disk.
bool register_font(const std::uint8_t *data, std::size_t size, const std::string &family_override="")
Register a font from raw TTF/OTF bytes.
FontState
Async font lifecycle state.
Definition bundled_fonts.hpp:150
@ Pending
Definition bundled_fonts.hpp:151
@ Loaded
Definition bundled_fonts.hpp:152
@ Substituted
Definition bundled_fonts.hpp:154
@ Failed
Definition bundled_fonts.hpp:153
std::uint64_t font_registration_generation() noexcept
Monotonic counter that bumps every time a typeface registration mutates process-global font state.
bool validate_font_bytes(const std::uint8_t *data, std::size_t size)
Validate font bytes before handing them to Skia.
std::optional< bool > declared_face_synthesizes_bold(const std::string &family, int requested_weight)
Whether Chromium synthesizes bold for requested_weight text in family: it does exactly when the reque...
std::uint64_t woff2_file_routes() noexcept
How many register_font_file calls this process has routed to register_font_woff2 because the file sta...
std::future< FontState > register_font_url(const std::string &url, const std::string &family_override="")
Schedule a font registration from a URL or filesystem path and return a future that resolves to the f...
bool woff2_decoder_available() noexcept
Runtime check for WOFF2 decoder availability.
std::size_t cluster_step(const std::string &text, std::size_t byte_offset, bool forward=true)
Grapheme-aware cursor step for TextEditor.
void bump_font_registration_generation() noexcept
Force-bump the registration generation.
@ text
Definition scene_node.hpp:96
@ path
Definition scene_node.hpp:93
int css_match_declared_weight(const std::vector< DeclaredWeightRange > &ranges, int desired_weight)
The declared range CSS font matching picks for desired_weight (CSS Fonts 4 section 5....
STL namespace.
One CSS @font-face rule's declared font-weight range (a single weight has min == max).
Definition bundled_fonts.hpp:93
int min
Definition bundled_fonts.hpp:94
int max
Definition bundled_fonts.hpp:95