NovaSVG 0.10.0
A header-only SVG library
Loading...
Searching...
No Matches
font.h
Go to the documentation of this file.
1#pragma once
2
3// Font: TrueType font loading, face caching, and text measurement.
4// Self-contained -- doesn't need Bitmap, Canvas, or the SVG layer -- so it
5// can be used entirely on its own (e.g. to measure how wide some text
6// would come out in a given font) without pulling in the rest of novasvg.
7
8#include "svgparserutils.h" // stripLeadingAndTrailingSpaces() -- generic string utils only, no SVG-tree dependency, consistent with this header's own self-contained goal
9#include "render/path.h" // path_move_to/line_to/cubic_to, used to extract glyph outlines
10#include "render/font.h"
11
12#include <string>
13#include <string_view>
14#include <cctype>
15#include <cstddef>
16#include <utility>
17#include <vector>
18
19namespace novasvg {
20using namespace render;
21
22class FontFace {
23public:
24 FontFace() = default;
25 explicit FontFace(font_face_t* face);
26 FontFace(const void* data, size_t length, destroy_func_t destroy_func, void* closure);
27 FontFace(const char* filename);
28 FontFace(const FontFace& face);
29 FontFace(FontFace&& face);
30 ~FontFace();
31
32 FontFace& operator=(const FontFace& face);
34
35 void swap(FontFace& face);
36
37 bool isNull() const { return m_face == nullptr; }
38 font_face_t* get() const { return m_face; }
39
40 // Raw font design units (unscaled by any particular pixel size) -- the
41 // same space a font file's own tables (head.unitsPerEm, hmtx advance
42 // widths) are defined in. Together these let a caller build a
43 // size-independent advance table once and rescale it later (by
44 // size / unitsPerEm) without coming back through novasvg per size --
45 // e.g. handing the whole table to a JS engine that can't make
46 // synchronous calls back into Python/C++ (see mermaidx's v8_engine,
47 // which needs exactly this to replace its own hand-rolled TTF-table
48 // parser). Font::measureText()/ascent()/etc. remain the right choice
49 // whenever a single already-known pixel size is enough.
50 float unitsPerEm() const;
51 float ascentUnits() const;
52 float descentUnits() const;
53 float advanceWidthUnits(char32_t codepoint) const;
54
55 // Kerning adjustment between two consecutive codepoints, in raw font
56 // design units (unscaled) -- the same "kern"/GPOS lookup
57 // Font::measureText() itself applies between every glyph pair (see
58 // font_face_text_extents() in detail/render/font.h), exposed per-pair
59 // so a caller that (unlike Font::measureText()) can't call back into
60 // novasvg per string -- e.g. mermaidx's V8 engine, which sums a
61 // shipped per-codepoint advance table in JS instead -- can still fold
62 // kerning into that sum for whichever pairs it queries up front.
63 // Deliberately NOT a bulk enumeration like codepoints(): unlike the
64 // simple, single-format cmap table codepoints() walks, kerning may
65 // live in either a legacy "kern" table or an OpenType GPOS Pair
66 // Adjustment Positioning lookup (format 1, a per-pair list, or format
67 // 2, a glyph-class matrix) -- stb_truetype's own bulk accessors
68 // (stbtt_GetKerningTable()) only cover the "kern" table (see that
69 // function's own comment), not GPOS, so a from-scratch GPOS enumerator
70 // would be a second, substantially larger parser to maintain
71 // alongside stb_truetype's -- while this per-pair query reuses
72 // stb_truetype's own GPOS-aware lookup path exactly as-is.
73 float kernAdvanceUnits(char32_t first, char32_t second) const;
74
75 // The advance width used for any codepoint outside the font's cmap
76 // (glyph id 0, the ".notdef" glyph) -- what advanceWidthUnits() itself
77 // already falls back to for such a codepoint (stbtt_FindGlyphIndex()
78 // returns glyph 0 when nothing matches), exposed under its own name
79 // purely so callers building a fallback value don't have to know that.
80 float notdefAdvanceWidthUnits() const { return advanceWidthUnits(0); }
81
82 // Every Unicode codepoint this face's cmap maps to a glyph (see
83 // font_face_enumerate_codepoints() for exactly which cmap formats are
84 // covered). Pair with advanceWidthUnits()/unitsPerEm() to build a
85 // complete, size-independent advance table -- e.g. for a caller that
86 // needs to reproduce Font::measureText() without being able to call
87 // back into novasvg per string (see mermaidx's v8_engine, which ships
88 // such a table into a V8 isolate once at boot).
89 std::vector<char32_t> codepoints() const;
90
91private:
92 font_face_t* release();
93 font_face_t* m_face = nullptr;
94};
95
96// The last-resort fonts: Latin subsets of DejaVu Sans (regular, bold, oblique and bold oblique)
97// compiled into the library (see default_font.h); the face matching `bold` / `italic` is returned.
98// A null face when built with NOVASVG_DISABLE_EMBEDDED_FONT.
99// Defined in svgparser.hpp, next to the base64 decoding it shares with @font-face.
100FontFace embeddedFallbackFont(bool bold = false, bool italic = false);
101
103public:
104 bool addFontFace(const std::string& family, bool bold, bool italic, const FontFace& face);
105
106 // Local-only lookup: just this cache's literal-name scan (system
107 // fonts by their own internal name, plus anything explicitly
108 // registered via addFontFace() -- including an @font-face-embedded
109 // font under the exact family name its CSS declared). No generic
110 // "sans-serif"-style fallback table and no OS substitution -- used
111 // by SVGLayoutState::font()'s per-name stack loop so an embedded
112 // font is found (and wins) before falling through to guessing at a
113 // substitute for names later in the stack.
114 FontFace getFontFaceLocal(const std::string& family, bool bold, bool italic) const;
115
116 // getFontFaceLocal(), then the hardcoded generic-family table
117 // ("sans-serif" -> "DejaVu Sans" and similar) as a last resort.
118 FontFace getFontFace(const std::string& family, bool bold, bool italic) const;
119
120 // Resolves a CSS-style comma-separated family stack (e.g.
121 // `"trebuchet ms", verdana, arial, sans-serif`) the way a browser walks
122 // it: name by name, in order, and the first name that yields a face
123 // wins. For each name: a font registered or installed under exactly
124 // that name, then a metric-compatible substitute from novasvg's own
125 // table (`arial` -> Liberation Sans / Arimo / ..., matched
126 // case-insensitively), then the generic keywords ("sans-serif", ...).
127 // A name nothing matches is skipped, not guessed at; returns a null
128 // face when the whole stack is unresolved. A generic keyword nothing on
129 // the machine satisfies resolves to the embedded fallback font.
130 FontFace getFontFaceForFamilyStack(const std::string& familyStack, bool bold, bool italic) const;
131
132private:
134 font_face_cache_t* m_cache;
136};
137
139
140class Font {
141public:
142 Font() = default;
143 Font(const FontFace& face, float size);
144
145 float ascent() const { return m_ascent; }
146 float descent() const { return m_descent; }
147 float height() const { return m_ascent - m_descent; }
148 float lineGap() const { return m_lineGap; }
149 float xHeight() const;
150
151 float measureText(const std::u32string_view& text) const;
152
153 // One entry per glyph that has an outline (spaces and other empty glyphs
154 // are left out): where its pen starts and where its ink begins/ends, in
155 // pixels. inkLeft/inkRight are along the baseline relative to the glyph's
156 // own pen; inkTop/inkBottom are relative to the baseline with y pointing
157 // DOWN (so ink above the baseline is negative). Walks the text exactly
158 // like measureText() (ligatures, then kerning), so `pen` is where the
159 // glyph is painted. These are the raw outline extents -- no pixel
160 // rounding; that is a consumer's (browser-emulation) decision, not the
161 // font engine's.
162 struct GlyphBox {
163 float pen;
164 float inkLeft;
165 float inkRight;
166 float inkTop;
168 };
169 std::vector<GlyphBox> glyphBoxes(const std::u32string_view& text) const;
170
171 const FontFace& face() const { return m_face; }
172 float size() const { return m_size; }
173
174 bool isNull() const { return m_size <= 0.f || m_face.isNull(); }
175
176private:
177 FontFace m_face;
178 float m_size = 0.f;
179 float m_ascent = 0.f;
180 float m_descent = 0.f;
181 float m_lineGap = 0.f;
182};
183
192bool addFontFaceFromFile(const char* family, bool bold, bool italic, const char* filename);
193
205bool addFontFaceFromData(const char* family, bool bold, bool italic, const void* data, size_t length, novasvg_destroy_func_t destroy_func, void* closure);
206
207} // namespace novasvg
208
209namespace novasvg {
210
215
216NOVASVG_INLINE FontFace::FontFace(const void* data, size_t length, destroy_func_t destroy_func, void* closure)
217 : m_face(font_face_load_from_data(data, length, 0, destroy_func, closure))
218{
219}
220
222 : m_face(font_face_load_from_file(filename, 0))
223{
224}
225
227 : m_face(font_face_reference(face.get()))
228{
229}
230
232 : m_face(face.release())
233{
234}
235
240
242{
243 FontFace(face).swap(*this);
244 return *this;
245}
246
248{
249 FontFace(std::move(face)).swap(*this);
250 return *this;
251}
252
254{
255 std::swap(m_face, face.m_face);
256}
257
258NOVASVG_INLINE font_face_t* FontFace::release()
259{
260 return std::exchange(m_face, nullptr);
261}
262
264{
265 if(isNull())
266 return 0.f;
267 return font_face_get_units_per_em(m_face);
268}
269
271{
272 if(isNull())
273 return 0.f;
274 float ascent = 0.f;
275 font_face_get_metrics(m_face, unitsPerEm(), &ascent, nullptr, nullptr, nullptr);
276 return ascent;
277}
278
280{
281 if(isNull())
282 return 0.f;
283 float descent = 0.f;
284 font_face_get_metrics(m_face, unitsPerEm(), nullptr, &descent, nullptr, nullptr);
285 return descent;
286}
287
288NOVASVG_INLINE float FontFace::advanceWidthUnits(char32_t codepoint) const
289{
290 if(isNull())
291 return 0.f;
292 // font_face_get_glyph_metrics() always returns advance_width * scale
293 // (see detail/render/font.h) -- passing unitsPerEm() as the "size"
294 // makes scale == 1, i.e. the raw, size-independent font-unit value,
295 // with no separate unscaled code path needed on the render side.
296 float advance = 0.f;
297 font_face_get_glyph_metrics(m_face, unitsPerEm(), codepoint, &advance, nullptr, nullptr);
298 return advance;
299}
300
301NOVASVG_INLINE float FontFace::kernAdvanceUnits(char32_t first, char32_t second) const
302{
303 if(isNull())
304 return 0.f;
305 // Same unitsPerEm()-as-size trick as advanceWidthUnits(): makes
306 // font_face_get_kern_advance()'s internal scale multiplication a
307 // no-op, yielding the raw, size-independent value.
308 return font_face_get_kern_advance(m_face, unitsPerEm(), first, second);
309}
310
311NOVASVG_INLINE std::vector<char32_t> FontFace::codepoints() const
312{
313 std::vector<char32_t> result;
314 if(isNull())
315 return result;
316 font_face_enumerate_codepoints(m_face, [](uint32_t codepoint, void* closure) {
317 static_cast<std::vector<char32_t>*>(closure)->push_back(static_cast<char32_t>(codepoint));
318 }, &result);
319 return result;
320}
321
322NOVASVG_INLINE bool FontFaceCache::addFontFace(const std::string& family, bool bold, bool italic, const FontFace& face)
323{
324 if(!face.isNull())
325 font_face_cache_add(m_cache, family.data(), bold, italic, face.get());
326 return !face.isNull();
327}
328
329NOVASVG_INLINE FontFace FontFaceCache::getFontFaceLocal(const std::string& family, bool bold, bool italic) const
330{
331 return FontFace(font_face_cache_get(m_cache, family.data(), bold, italic));
332}
333
334NOVASVG_INLINE FontFace FontFaceCache::getFontFace(const std::string& family, bool bold, bool italic) const
335{
336 if(auto face = getFontFaceLocal(family, bold, italic); !face.isNull()) {
337 return face;
338 }
339
340 static const struct {
341 const char* generic;
342 const char* fallback;
343 } generic_fallbacks[] = {
344#if defined(__linux__)
345 {"sans-serif", "DejaVu Sans"},
346 {"serif", "DejaVu Serif"},
347 {"monospace", "DejaVu Sans Mono"},
348#else
349 {"sans-serif", "Arial"},
350 {"serif", "Times New Roman"},
351 {"monospace", "Courier New"},
352#endif
353 {"cursive", "Comic Sans MS"},
354 {"fantasy", "Impact"}
355 };
356
357 for(auto value : generic_fallbacks) {
358 if(value.generic == family || family.empty()) {
359 if(auto face = FontFace(font_face_cache_get(m_cache, value.fallback, bold, italic)); !face.isNull())
360 return face;
361 // Not even the platform's stand-in is installed: use the embedded font
362 // so text is still drawn (and drawn the same way) on a machine without fonts.
363 return embeddedFallbackFont(bold, italic);
364 }
365 }
366
367 return FontFace();
368}
369
370NOVASVG_INLINE FontFace FontFaceCache::getFontFaceForFamilyStack(const std::string& familyStack, bool bold, bool italic) const
371{
372 // Metric-compatible stand-ins only: a substitute must lay text out the
373 // same way as the font it replaces, so a name with no such stand-in
374 // (verdana, trebuchet ms, ...) is skipped and the stack moves on. Each
375 // key is tried under its own spelling first, so a differently-cased
376 // request (`helvetica`, `ARIAL`) still finds an installed original.
377 static const char* const sans[] = {"Arial", "Liberation Sans", "Arimo", "Nimbus Sans", "Nimbus Sans L", "FreeSans", nullptr};
378 static const char* const serif[] = {"Times New Roman", "Liberation Serif", "Tinos", "Nimbus Roman", "Nimbus Roman No9 L", "FreeSerif", nullptr};
379 static const char* const mono[] = {"Courier New", "Liberation Mono", "Cousine", "Nimbus Mono PS", "Nimbus Mono", "FreeMono", nullptr};
380 static const char* const calibri[] = {"Carlito", nullptr};
381 static const char* const cambria[] = {"Caladea", nullptr};
382 static const char* const georgia[] = {"Gelasio", nullptr};
383 static const struct {
384 const char* family; // real spelling; matched case-insensitively
385 const char* const* substitutes;
386 } aliases[] = {
387 {"Arial", sans}, {"Helvetica", sans}, {"Helvetica Neue", sans},
388 {"Times New Roman", serif}, {"Times", serif},
389 {"Courier New", mono}, {"Courier", mono},
390 {"Calibri", calibri}, {"Cambria", cambria}, {"Georgia", georgia},
391 };
392
393 std::string_view input(familyStack);
394 while(!input.empty()) {
395 auto family = input.substr(0, input.find(','));
396 input.remove_prefix(family.length());
397 if(!input.empty() && input.front() == ',')
398 input.remove_prefix(1);
400 if(!family.empty() && (family.front() == '\'' || family.front() == '"')) {
401 auto quote = family.front();
402 family.remove_prefix(1);
403 if(!family.empty() && family.back() == quote)
404 family.remove_suffix(1);
406 }
407 if(family.empty())
408 continue;
409
410 std::string name(family);
411 if(auto face = getFontFaceLocal(name, bold, italic); !face.isNull())
412 return face;
413
414 std::string lower(name);
415 for(auto& ch : lower)
416 ch = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
417
418 for(const auto& alias : aliases) {
419 std::string key(alias.family);
420 for(auto& ch : key)
421 ch = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
422 if(lower != key)
423 continue;
424 if(auto face = getFontFaceLocal(alias.family, bold, italic); !face.isNull())
425 return face;
426 for(auto substitute = alias.substitutes; *substitute; ++substitute) {
427 if(auto face = getFontFaceLocal(*substitute, bold, italic); !face.isNull())
428 return face;
429 }
430 }
431
432 // Generic keywords ("sans-serif", ...). Anything else is not a
433 // generic name, so this stays empty for it and the walk goes on.
434 if(auto face = getFontFace(lower, bold, italic); !face.isNull())
435 return face;
436 }
437
438 return FontFace();
439}
440
441NOVASVG_INLINE FontFaceCache::FontFaceCache()
442 : m_cache(font_face_cache_create())
443{
444#ifndef NOVASVG_DISABLE_LOAD_SYSTEM_FONTS
446#endif
447}
448
450{
451 static FontFaceCache cache;
452 return &cache;
453}
454
455NOVASVG_INLINE Font::Font(const FontFace& face, float size)
456 : m_face(face), m_size(size)
457{
458 if(m_size > 0.f && !m_face.isNull()) {
459 font_face_get_metrics(m_face.get(), m_size, &m_ascent, &m_descent, &m_lineGap, nullptr);
460 }
461}
462
464{
465 rect_t extents = {0};
466 if(m_size > 0.f && !m_face.isNull())
467 font_face_get_glyph_metrics(m_face.get(), m_size, 'x', nullptr, nullptr, &extents);
468 return extents.h;
469}
470
471NOVASVG_INLINE float Font::measureText(const std::u32string_view& text) const
472{
473 if(m_size > 0.f && !m_face.isNull())
474 return font_face_text_extents(m_face.get(), m_size, text.data(), text.length(), NOVASVG_TEXT_ENCODING_UTF32, nullptr);
475 return 0;
476}
477NOVASVG_INLINE std::vector<Font::GlyphBox> Font::glyphBoxes(const std::u32string_view& text) const
478{
479 std::vector<GlyphBox> boxes;
480 if(m_size <= 0.f || m_face.isNull())
481 return boxes;
482
483 font_face_t* face = m_face.get();
485 text_iterator_init(&it, text.data(), static_cast<int>(text.length()), NOVASVG_TEXT_ENCODING_UTF32);
486 float pen = 0.f;
487 codepoint_t previous = 0;
488 bool hasPrevious = false;
489 while(text_iterator_has_next(&it)) {
490 codepoint_t codepoint = text_iterator_next(&it);
491 codepoint = font_face_apply_ligature(face, &it, codepoint);
492 if(hasPrevious)
493 pen += font_face_get_kern_advance(face, m_size, previous, codepoint);
494 previous = codepoint;
495 hasPrevious = true;
496
497 float advance = 0.f;
498 rect_t extents = {0};
499 font_face_get_glyph_metrics(face, m_size, codepoint, &advance, nullptr, &extents);
500 if(extents.w > 0.f || extents.h > 0.f)
501 boxes.push_back({pen, extents.x, extents.x + extents.w, extents.y, extents.y + extents.h});
502 pen += advance;
503 }
504 return boxes;
505}
506
507NOVASVG_INLINE bool addFontFaceFromFile(const char* family, bool bold, bool italic, const char* filename)
508{
509 return fontFaceCache()->addFontFace(family, bold, italic, FontFace(filename));
510}
511
512NOVASVG_INLINE bool addFontFaceFromData(const char* family, bool bold, bool italic, const void* data, size_t length, destroy_func_t destroy_func, void* closure)
513{
514 return fontFaceCache()->addFontFace(family, bold, italic, FontFace(data, length, destroy_func, closure));
515}
516
517} // namespace novasvg
friend FontFaceCache * fontFaceCache()
Definition font.h:449
bool addFontFace(const std::string &family, bool bold, bool italic, const FontFace &face)
Definition font.h:322
FontFace getFontFace(const std::string &family, bool bold, bool italic) const
Definition font.h:334
FontFace getFontFaceLocal(const std::string &family, bool bold, bool italic) const
Definition font.h:329
FontFace getFontFaceForFamilyStack(const std::string &familyStack, bool bold, bool italic) const
Definition font.h:370
void swap(FontFace &face)
Definition font.h:253
float ascentUnits() const
Definition font.h:270
float descentUnits() const
Definition font.h:279
FontFace()=default
float notdefAdvanceWidthUnits() const
Definition font.h:80
font_face_t * get() const
Definition font.h:38
float kernAdvanceUnits(char32_t first, char32_t second) const
Definition font.h:301
std::vector< char32_t > codepoints() const
Definition font.h:311
float advanceWidthUnits(char32_t codepoint) const
Definition font.h:288
bool isNull() const
Definition font.h:37
float unitsPerEm() const
Definition font.h:263
FontFace & operator=(const FontFace &face)
Definition font.h:241
float measureText(const std::u32string_view &text) const
Definition font.h:471
float height() const
Definition font.h:147
float descent() const
Definition font.h:146
float ascent() const
Definition font.h:145
float size() const
Definition font.h:172
float lineGap() const
Definition font.h:148
Font()=default
float xHeight() const
Definition font.h:463
const FontFace & face() const
Definition font.h:171
std::vector< GlyphBox > glyphBoxes(const std::u32string_view &text) const
Definition font.h:477
bool isNull() const
Definition font.h:174
NOVASVG_INLINE codepoint_t font_face_apply_ligature(font_face_t *face, text_iterator_t *it, codepoint_t codepoint)
Definition font.h:5592
NOVASVG_INLINE void font_face_cache_add(font_face_cache_t *cache, const char *family, bool bold, bool italic, font_face_t *face)
Add a font face to the cache with the specified family and style.
Definition font.h:5770
NOVASVG_INLINE float font_face_text_extents(font_face_t *face, float size, const void *text, int length, text_encoding_t encoding, rect_t *extents)
Computes the bounding box of a text string and its advance width.
Definition font.h:5616
NOVASVG_INLINE float font_face_get_units_per_em(const font_face_t *face)
Definition font.h:5370
NOVASVG_INLINE void font_face_destroy(font_face_t *face)
Decrements the reference count and potentially destroys the font face.
Definition font.h:5340
unsigned int codepoint_t
Represents a Unicode code point.
Definition render.h:768
NOVASVG_INLINE font_face_t * font_face_load_from_data(const void *data, unsigned int length, int ttcindex, destroy_func_t destroy_func, void *closure)
Loads a font face from memory.
Definition font.h:5310
NOVASVG_INLINE font_face_t * font_face_load_from_file(const char *filename, int ttcindex)
Loads a font face from a file.
Definition font.h:5278
NOVASVG_INLINE void font_face_get_glyph_metrics(font_face_t *face, float size, codepoint_t codepoint, float *advance_width, float *left_side_bearing, rect_t *extents)
Retrieves metrics for a specified glyph at a given size.
Definition font.h:5441
NOVASVG_INLINE font_face_t * font_face_cache_get(font_face_cache_t *cache, const char *family, bool bold, bool italic)
Retrieve a font face from the cache by family and style.
Definition font.h:5812
void(* destroy_func_t)(void *closure)
A function pointer type for a cleanup callback.
Definition render.h:92
NOVASVG_INLINE float font_face_get_kern_advance(font_face_t *face, float size, codepoint_t ch1, codepoint_t ch2)
Definition font.h:5466
NOVASVG_INLINE void font_face_enumerate_codepoints(const font_face_t *face, void(*callback)(uint32_t codepoint, void *closure), void *closure)
Definition font.h:5388
NOVASVG_INLINE codepoint_t text_iterator_next(text_iterator_t *it)
Retrieves the next code point and advances the iterator.
Definition font.h:5046
@ NOVASVG_TEXT_ENCODING_UTF32
UTF-32 encoding.
Definition render.h:752
NOVASVG_INLINE void font_face_get_metrics(const font_face_t *face, float size, float *ascent, float *descent, float *line_gap, rect_t *extents)
Retrieves metrics for a font face at a specified size.
Definition font.h:5422
NOVASVG_INLINE bool text_iterator_has_next(const text_iterator_t *it)
Checks if there are more code points to iterate.
Definition font.h:5041
NOVASVG_INLINE int font_face_cache_load_sys(font_face_cache_t *cache)
Load all available system font faces and add them to the cache.
Definition font.h:6231
NOVASVG_INLINE void text_iterator_init(text_iterator_t *it, const void *text, int length, text_encoding_t encoding)
Initializes a text iterator.
Definition font.h:5031
NOVASVG_INLINE font_face_t * font_face_reference(font_face_t *face)
Increments the reference count of a font face.
Definition font.h:5334
NOVASVG_INLINE font_face_cache_t * font_face_cache_create(void)
Create a new, empty font‐face cache.
Definition font.h:5690
Main namespace for the NovaSVG library.
Definition bitmap.h:23
FontFace embeddedFallbackFont(bool bold=false, bool italic=false)
FontFaceCache * fontFaceCache()
Definition font.h:449
constexpr void stripLeadingAndTrailingSpaces(std::string_view &input)
bool addFontFaceFromFile(const char *family, bool bold, bool italic, const char *filename)
Add a font face from a file to the cache.
Definition font.h:507
bool addFontFaceFromData(const char *family, bool bold, bool italic, const void *data, size_t length, novasvg_destroy_func_t destroy_func, void *closure)
Add a font face from memory to the cache.
Definition font.h:512
void(* novasvg_destroy_func_t)(void *closure)
Callback for cleaning up resources.
Definition novasvg.h:121
#define NOVASVG_INLINE
Definition novasvg.h:42
A structure representing a rectangle in 2D space.
Definition render.h:125
float x
The x-coordinate of the top-left corner of the rectangle.
Definition render.h:126
float w
The width of the rectangle.
Definition render.h:128
float h
The height of the rectangle.
Definition render.h:129
float y
The y-coordinate of the top-left corner of the rectangle.
Definition render.h:127
Iterator for traversing code points in text data.
Definition render.h:758
std::string text
UTF-8; ' ' marks a forced line break ( ,.