ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
reflow_layout_driver.c
Go to the documentation of this file.
1
25
26#include <stddef.h>
27#include <stdint.h>
28
29#include "ra8_attributes.h"
30#include "ra8_err.h"
31#include "ra8_stbtt_guard.h"
32#include "reflow.h"
33#include "reflow_internal.h"
35#include "stb_truetype.h"
36
54static ra8_err_t internal_layout_tokens(reflow_t* engine, const stbtt_fontinfo* font)
55{
56 priv_cursor_t cur = {
57 .x = (int32_t)k_reflow_margin_px,
58 .y = (int32_t)k_reflow_margin_px,
59 .line_top = (int32_t)k_reflow_margin_px,
60 .line_height_px = priv_reflow_layout_line_height(engine->font_px),
61 .active_font_px = engine->font_px,
62 .active_style = k_reflow_style_normal,
63 .indent_px = 0U,
64 .page_first_glyph = 0U,
65 .page_first_image = 0U,
66 .line_first_glyph = 0U,
67 .line_has_content = 0U,
68 .align = (uint8_t)k_reflow_align_left,
69 .reserved8 = {0U, 0U},
70 };
71
72 uint32_t i = 0U;
73 while (i < engine->token_count) {
74 const reflow_token_t* tok = &engine->tokens[i];
75 if ((tok->kind == (uint8_t)k_reflow_tok_block_start) &&
76 (tok->tag == (uint8_t)k_reflow_tag_table)) {
77 ra8_err_t err = priv_reflow_layout_table(engine, &cur, font, i, &i);
78 if (err != k_ra8_ok) {
79 return err;
80 }
81 } else {
82 ra8_err_t err = priv_reflow_layout_apply_token(engine, &cur, font, tok);
83 if (err != k_ra8_ok) {
84 return err;
85 }
86 i++;
87 }
88 }
89
90 /* Flush the final page if it has glyph or image content. */
91 if ((engine->glyph_count > cur.page_first_glyph) ||
92 (engine->image_box_count > cur.page_first_image)) {
93 if (!priv_reflow_layout_finish_page(engine, &cur)) {
94 return k_ra8_err_no_mem;
95 }
96 }
97 return k_ra8_ok;
98}
99
100/* ===========================================================================
101 * Public helpers (declared in reflow.h)
102 * ===========================================================================
103 */
104
106{
107 if (engine == nullptr) {
108 return k_ra8_err_null_ptr;
109 }
110 if (engine->in_use == 0U) {
112 }
113
114 /* Wipe glyph, page, image, link and anchor state but keep the token stream. */
115 engine->glyph_count = 0U;
116 engine->page_count = 0U;
117 engine->image_box_count = 0U;
118 engine->link_rect_count = 0U;
119 engine->anchor_count = 0U;
120
121 stbtt_fontinfo font;
122 ra8_err_t err = priv_reflow_layout_init_font(engine, &font);
123 if (err != k_ra8_ok) {
124 return err;
125 }
126 err = internal_layout_tokens(engine, &font);
127 if (err != k_ra8_ok) {
128 return err;
129 }
130 /* Guarantee at least one page on non-empty input. */
132 if (engine->page_count >= k_reflow_max_pages) {
133 return k_ra8_err_no_mem;
134 }
135 engine->pages[0].glyph_first = 0U;
136 engine->pages[0].glyph_count = engine->glyph_count;
137 engine->page_count = (uint32_t)k_priv_min_chapter_pages;
138 }
139 /* Build tappable link rectangles from the link-tagged glyphs (#110). */
141 return k_ra8_ok;
142}
143
144/* ===========================================================================
145 * Public API entry points -- lifecycle + chapter dispatch
146 * ===========================================================================
147 */
148
149ra8_err_t reflow_init(uint16_t viewport_w,
150 uint16_t viewport_h,
151 const uint8_t* font_data,
152 size_t font_len,
153 uint16_t font_px,
154 uint32_t body_color,
155 uint32_t link_color,
156 reflow_t* out_engine)
157{
158 if ((font_data == nullptr) || (out_engine == nullptr)) {
159 return k_ra8_err_null_ptr;
160 }
161 if ((viewport_w == 0U) || (viewport_h == 0U)) {
163 }
164 if ((font_px < k_reflow_min_font_px) || (font_px > k_reflow_max_font_px)) {
166 }
167 if (font_len < 16U) {
169 }
170
171 priv_reflow_layout_byte_zero((uint8_t*)out_engine, sizeof(*out_engine));
172
173 out_engine->viewport_w = viewport_w;
174 out_engine->viewport_h = viewport_h;
175 out_engine->font_data = font_data;
176 out_engine->font_len = font_len;
177 out_engine->font_px = font_px;
178 out_engine->body_color = body_color;
179 out_engine->link_color = link_color;
180 out_engine->in_use = 1U;
181 return k_ra8_ok;
182}
183
185{
186 if (engine == nullptr) {
187 return k_ra8_err_null_ptr;
188 }
189 if (engine->in_use == 0U) {
191 }
192 engine->in_use = 0U;
193 engine->page_count = 0U;
194 engine->glyph_count = 0U;
195 engine->token_count = 0U;
196 engine->text_pool_used = 0U;
197 engine->image_box_count = 0U;
198 engine->xhtml_buf = nullptr;
199 engine->xhtml_len = 0U;
200 return k_ra8_ok;
201}
202
205 void* ctx,
206 ra8_img_arena_t* arena)
207{
208 if (engine == nullptr) {
209 return k_ra8_err_null_ptr;
210 }
211 if (engine->in_use == 0U) {
213 }
214 engine->img_loader = loader;
215 engine->img_loader_ctx = ctx;
216 engine->img_arena = arena;
217 return k_ra8_ok;
218}
219
221{
222 if (engine == nullptr) {
223 return k_ra8_err_null_ptr;
224 }
225 if (engine->in_use == 0U) {
227 }
228 engine->css_loader = loader;
229 engine->css_loader_ctx = ctx;
230 return k_ra8_ok;
231}
232
234 const uint8_t* xhtml_buf,
235 size_t xhtml_len,
236 uint32_t* out_total_pages)
237{
238 if ((engine == nullptr) || (xhtml_buf == nullptr) || (out_total_pages == nullptr)) {
239 return k_ra8_err_null_ptr;
240 }
241 if (engine->in_use == 0U) {
243 }
244 if (xhtml_len == 0U) {
246 }
247
248 *out_total_pages = 0U;
249
250 ra8_err_t err = reflow_parse_xhtml(engine, xhtml_buf, xhtml_len);
251 if (err != k_ra8_ok) {
252 return err;
253 }
254 err = reflow_run_layout(engine);
255 if (err != k_ra8_ok) {
256 return err;
257 }
258
259 /* Cache the pointer so set_font_size() can re-flow without the
260 * caller resupplying the buffer. */
261 engine->xhtml_buf = xhtml_buf;
262 engine->xhtml_len = xhtml_len;
263 *out_total_pages = engine->page_count;
264 return k_ra8_ok;
265}
266
267ra8_err_t reflow_get_page_count(const reflow_t* engine, uint32_t* out_count)
268{
269 if ((engine == nullptr) || (out_count == nullptr)) {
270 return k_ra8_err_null_ptr;
271 }
272 if (engine->in_use == 0U) {
274 }
275 *out_count = engine->page_count;
276 return k_ra8_ok;
277}
278
279ra8_err_t reflow_set_font_size(reflow_t* engine, uint16_t new_font_px)
280{
281 if (engine == nullptr) {
282 return k_ra8_err_null_ptr;
283 }
284 if (engine->in_use == 0U) {
286 }
287 if ((new_font_px < k_reflow_min_font_px) || (new_font_px > k_reflow_max_font_px)) {
289 }
292 }
293 engine->font_px = new_font_px;
294
295 /* Re-flow against the cached buffer. */
296 uint32_t pages = 0U;
297 return reflow_layout_chapter(engine, engine->xhtml_buf, engine->xhtml_len, &pages);
298}
299
301ra8_err_t reflow_bind_font(reflow_t* engine, const uint8_t* font_data, size_t font_len)
302{
303 if ((engine == nullptr) || (font_data == nullptr)) {
304 return k_ra8_err_null_ptr;
305 }
306 if (engine->in_use == 0U) {
308 }
309 if (font_len < (size_t)k_reflow_min_font_bytes) {
311 }
312
313 /* Validate the blob BEFORE swapping; on failure the current face is kept
314 * (graceful degradation). The probe fontinfo is a stack temporary -- the
315 * layout / render paths re-init their own per-call fontinfo from font_data. */
316 const int32_t offset = stbtt_GetFontOffsetForIndex(font_data, 0);
317 /* Bound-check the sfnt table directory before stbtt_InitFont walks it: the
318 * @font-face bytes are attacker-controlled and stb_truetype reads the
319 * directory with no length check (#217). A non-sfnt blob (offset < 0) is
320 * left to the existing decision below. */
321 if (offset >= 0) {
322 if (!ra8_stbtt_sfnt_dir_in_bounds(font_data, font_len, (uint32_t)offset)) {
324 }
325 }
326 stbtt_fontinfo probe;
327 if ((offset < 0) || (stbtt_InitFont(&probe, font_data, (int)font_len, offset) == 0)) {
329 }
330
331 engine->font_data = font_data;
332 engine->font_len = font_len;
333
334 /* If a chapter is already laid out, re-flow it against the new face so the
335 * change is immediate (mirrors reflow_set_font_size); otherwise the next
336 * layout picks it up. */
338 uint32_t pages = 0U;
339 return reflow_layout_chapter(engine, engine->xhtml_buf, engine->xhtml_len, &pages);
340 }
341 return k_ra8_ok;
342}
343
346reflow_register_face(reflow_t* engine, uint8_t css_face_idx, const uint8_t* blob, size_t len)
347{
348 if ((engine == nullptr) || (blob == nullptr)) {
349 return k_ra8_err_null_ptr;
350 }
351 if (engine->in_use == 0U) {
353 }
354 if (len < (size_t)k_reflow_min_font_bytes) {
356 }
357 if (engine->face_count >= (uint8_t)k_reflow_max_faces) {
358 return k_ra8_err_no_mem;
359 }
360 /* Validate the blob before recording it; a bad face is rejected so layout falls
361 * back to the default face (graceful degradation). The probe is a stack
362 * temporary -- the render pass re-inits its own per-face fontinfo. */
363 const int32_t offset = stbtt_GetFontOffsetForIndex(blob, 0);
364 /* Bound-check the sfnt table directory before stbtt_InitFont walks it: the
365 * @font-face bytes are attacker-controlled and stb_truetype reads the
366 * directory with no length check (#217). A non-sfnt blob (offset < 0) is
367 * left to the existing decision below. */
368 if (offset >= 0) {
369 if (!ra8_stbtt_sfnt_dir_in_bounds(blob, len, (uint32_t)offset)) {
371 }
372 }
373 stbtt_fontinfo probe;
374 if ((offset < 0) || (stbtt_InitFont(&probe, blob, (int)len, offset) == 0)) {
376 }
377 reflow_face_t* face = &engine->faces[engine->face_count];
378 face->blob = blob;
379 face->len = len;
380 face->css_face_idx = css_face_idx;
381 engine->face_count++;
382 return k_ra8_ok;
383}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Error Code Definitions for ra8-firmware.
@ k_ra8_err_not_supported
Requested feature not compiled in, not wired, or not supported by this MCU variant.
Definition ra8_err.h:180
@ k_ra8_err_no_mem
Static buffer exhausted (no dynamic memory on this project).
Definition ra8_err.h:142
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_invalid_state
Module in wrong state for requested operation.
Definition ra8_err.h:161
@ k_ra8_err_not_initialized
Module not initialized – _init() not yet called successfully.
Definition ra8_err.h:235
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
Definition ra8_err.h:167
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
sfnt (TrueType/OpenType) table-directory bounds guard for stb_truetype.
bool ra8_stbtt_sfnt_dir_in_bounds(const uint8_t *data, size_t len, uint32_t fontstart)
Verify that a font's sfnt table directory lies within its buffer.
HTML / CSS reflow + paginate engine for the ra8d2 ereader.
ra8_err_t reflow_parse_xhtml(reflow_t *engine, const uint8_t *xhtml_buf, size_t xhtml_len)
Parse the XHTML buffer into the engine's token stream.
Test-access surface for reflow internal helpers (MC/DC).
bool priv_reflow_internal_final_page_needed(uint32_t page_count, uint32_t token_count)
Decide whether the layout pass produced zero pages but the token stream was non-empty (must synthesis...
bool priv_reflow_internal_xhtml_invalid(const void *xhtml_buf, size_t xhtml_len)
Decide whether the cached XHTML buffer pointer/length pair is unusable for a re-flow (NULL pointer OR...
void priv_reflow_layout_build_link_rects(reflow_t *engine, const stbtt_fontinfo *font)
Implementation of priv_reflow_layout_build_link_rects() – per-page link runs.
ra8_err_t priv_reflow_layout_apply_token(reflow_t *engine, priv_cursor_t *cur, const stbtt_fontinfo *font, const reflow_token_t *tok)
Implementation of priv_reflow_layout_apply_token() – per-token switch.
bool priv_reflow_layout_finish_page(reflow_t *engine, priv_cursor_t *cur)
Implementation of priv_reflow_layout_finish_page() – flush + reset cursor.
void priv_reflow_layout_byte_zero(uint8_t *dst, size_t n)
Implementation of priv_reflow_layout_byte_zero() – bounded byte-walk.
ra8_err_t priv_reflow_layout_init_font(const reflow_t *engine, stbtt_fontinfo *out_font)
Implementation of priv_reflow_layout_init_font() – parse the TTF blob.
uint16_t priv_reflow_layout_line_height(uint16_t font_px)
Implementation of priv_reflow_layout_line_height() – scaled integer ratio.
ra8_err_t reflow_run_layout(reflow_t *engine)
Run the line-break + page-break pass over engine->tokens[].
ra8_err_t reflow_layout_chapter(reflow_t *engine, const uint8_t *xhtml_buf, size_t xhtml_len, uint32_t *out_total_pages)
Parse + lay out one chapter of XHTML.
ra8_err_t reflow_set_css_loader(reflow_t *engine, reflow_css_loader_fn loader, void *ctx)
Bind the external-stylesheet loader for <link rel="stylesheet">.
static ra8_err_t internal_layout_tokens(reflow_t *engine, const stbtt_fontinfo *font)
Run one pass over the token stream populating engine->glyphs[] and engine->pages[].
ra8_err_t reflow_bind_font(reflow_t *engine, const uint8_t *font_data, size_t font_len)
Implementation of reflow_bind_font() – validate then swap.
ra8_err_t reflow_set_font_size(reflow_t *engine, uint16_t new_font_px)
Change the body font size and re-flow the cached chapter.
ra8_err_t reflow_init(uint16_t viewport_w, uint16_t viewport_h, const uint8_t *font_data, size_t font_len, uint16_t font_px, uint32_t body_color, uint32_t link_color, reflow_t *out_engine)
Initialise a reflow engine for a given viewport / font.
ra8_err_t reflow_get_page_count(const reflow_t *engine, uint32_t *out_count)
Report the laid-out page count.
ra8_err_t reflow_set_image_loader(reflow_t *engine, reflow_image_loader_fn loader, void *ctx, ra8_img_arena_t *arena)
Bind an <img> byte loader + decode arena to enable image rendering.
ra8_err_t reflow_register_face(reflow_t *engine, uint8_t css_face_idx, const uint8_t *blob, size_t len)
Implementation of reflow_register_face() – validate then append.
ra8_err_t reflow_close(reflow_t *engine)
Release a previously initialized engine.
Cross-TU shared declarations for the reflow layout engine.
@ k_priv_min_chapter_pages
Floor for non-empty input.
ra8_err_t priv_reflow_layout_table(reflow_t *engine, priv_cursor_t *cur, const stbtt_fontinfo *font, uint32_t start, uint32_t *out_next)
Lay out a <table> token range as an equal-column grid.
@ k_reflow_tag_table
Table (grid layout).
ra8_err_t(* reflow_css_loader_fn)(void *ctx, const char *href, uint32_t href_len, const uint8_t **out_bytes, size_t *out_len)
External-stylesheet byte loader for <link rel="stylesheet" href>.
@ k_reflow_max_faces
Max embedded @font-face typefaces.
@ k_reflow_max_pages
Max paginated pages.
ra8_err_t(* reflow_image_loader_fn)(void *ctx, const char *href, uint32_t href_len, const uint8_t **out_bytes, size_t *out_len)
Resolve an <img src> href to its encoded image bytes.
@ k_reflow_margin_px
Page edge inset, pixels.
@ k_reflow_min_font_px
Smallest accepted font size.
@ k_reflow_max_font_px
Largest accepted font size.
@ k_reflow_min_font_bytes
Smallest plausible font blob.
@ k_reflow_align_left
Left (default, ragged right).
@ k_reflow_tok_block_start
Open of a block-flow element.
@ k_reflow_style_normal
No emphasis.
Mutable state carried through the layout loop.
uint32_t page_first_image
First image-box index of the active page.
uint32_t page_first_glyph
First glyph index of the active page.
Caller-owned bump arena backing a single image decode.
One registered embedded @font-face typeface (#109).
uint8_t css_face_idx
@font-face table index this blob satisfies.
const uint8_t * blob
TTF/OTF bytes; caller-owned, outlives engine.
size_t len
Length of blob, bytes.
uint32_t glyph_count
Number of glyphs in this page.
uint32_t glyph_first
Index of first glyph in this page.
Reflow / pagination engine state.
uint32_t page_count
Pages used.
reflow_image_loader_fn img_loader
<img> byte loader (NULL = off).
reflow_css_loader_fn css_loader
<link> CSS loader (NULL = off).
const uint8_t * font_data
TTF blob; outlives the engine.
ra8_img_arena_t * img_arena
Decode scratch (NULL = off).
uint32_t link_color
Anchor text colour (0xRRGGBB).
uint32_t glyph_count
Glyphs used.
reflow_page_t pages[k_reflow_max_pages]
Page index ranges.
uint32_t text_pool_used
Bytes consumed in text_pool.
uint32_t token_count
Tokens used.
reflow_face_t faces[k_reflow_max_faces]
Embedded @font-face set.
uint8_t in_use
1 = initialized, 0 = closed.
size_t font_len
Length of font_data, bytes.
void * img_loader_ctx
Opaque context for img_loader.
uint16_t font_px
Body font size in pixels.
uint16_t viewport_w
Viewport width, pixels.
reflow_token_t tokens[k_reflow_max_tokens]
Parsed tokens.
const uint8_t * xhtml_buf
Last layout_chapter input.
uint32_t anchor_count
Anchor positions used.
uint16_t viewport_h
Viewport height, pixels.
size_t xhtml_len
Length of xhtml_buf.
uint32_t body_color
Body text colour (0xRRGGBB).
uint32_t image_box_count
Image boxes used.
void * css_loader_ctx
Opaque context for css_loader.
uint32_t link_rect_count
Link rects used.
uint8_t face_count
Embedded face count (0 = single-face).
One parsed token in the engine's token stream.
uint8_t kind
reflow_token_kind_t.
uint8_t tag
reflow_html_tag_t (0 if N/A).