ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
reflow_css.c
Go to the documentation of this file.
1
22
23#include "reflow_css.h"
24
25#include <stddef.h>
26#include <string.h>
27
28#include "ra8_attributes.h"
29#include "reflow.h" /* style/align enums, k_ra8_css_font_* units */
30#include "reflow_css_internal.h"
31
32/* ===========================================================================
33 * Internal constants (no magic numbers)
34 * ===========================================================================
35 */
36
49
54typedef enum : uint16_t {
58 k_priv_fs_max = 9999U,
61
66typedef enum : uint32_t {
67 k_priv_col_black = 0x000000U,
68 k_priv_col_white = 0xFFFFFFU,
69 k_priv_col_red = 0xFF0000U,
70 k_priv_col_green = 0x008000U,
71 k_priv_col_blue = 0x0000FFU,
72 k_priv_col_gray = 0x808080U,
73 k_priv_col_silver = 0xC0C0C0U,
74 k_priv_col_maroon = 0x800000U,
75 k_priv_col_navy = 0x000080U,
76 k_priv_col_invalid = 0xFFFFFFFFU,
78
79/* ===========================================================================
80 * Small pure string helpers
81 * ===========================================================================
82 */
83
107{
108 return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r') || (c == '\f');
109}
110
134{
135 return (char)(((c >= 'A') && (c <= 'Z')) ? (c + ('a' - 'A')) : c);
136}
137
162bool priv_reflow_css_ci_eq(const char* s, size_t len, const char* lit)
163{
164 if ((s == nullptr) || (lit == nullptr)) {
165 return false;
166 }
167 size_t k = 0U;
168 for (; (k < len) && (lit[k] != '\0'); ++k) {
169 if (priv_reflow_css_lower(s[k]) != priv_reflow_css_lower(lit[k])) {
170 return false;
171 }
172 }
173 return (k == len) && (lit[k] == '\0');
174}
175
203static bool internal_ci_contains(const char* s, size_t len, const char* sub)
204{
205 const size_t sl = strlen(sub);
206 /* mcdc-deactivated: the sole caller passes internal_ci_contains(val, vlen, "underline") -- val is non-NULL (from priv_reflow_css_trim) and sub is a compile-time literal so sl == 9 (never 0); (s == nullptr) and (sl == 0U) are unreachable, only (sl > len) varies. */
207 if ((s == nullptr) || (sl == 0U) || (sl > len)) {
208 return false;
209 }
210 /* Bounded: at most (len - sl + 1) windows; i advances by 1 each step. */
211 for (size_t i = 0U; (i + sl) <= len; ++i) {
212 bool hit = true;
213 /* Bounded: j < sl, the NUL-terminated literal's strlen. */
214 for (size_t j = 0U; j < sl; ++j) {
215 if (priv_reflow_css_lower(s[i + j]) != priv_reflow_css_lower(sub[j])) {
216 hit = false;
217 break;
218 }
219 }
220 if (hit) {
221 return true;
222 }
223 }
224 return false;
225}
226
228const char* priv_reflow_css_trim(const char* s, size_t* len)
229{
230 size_t a = 0U;
231 size_t b = *len;
232 while ((a < b) && priv_reflow_css_is_ws(s[a])) {
233 ++a;
234 }
235 while ((b > a) && priv_reflow_css_is_ws(s[b - 1U])) {
236 --b;
237 }
238 *len = b - a;
239 return &s[a];
240}
241
242/* ===========================================================================
243 * Declaration-body parsing (colour / font-size / emphasis / align)
244 * ===========================================================================
245 */
246
271static uint8_t internal_hex_val(char c)
272{
273 if ((c >= '0') && (c <= '9')) {
274 const int value = (int)c - (int)'0';
275 return (uint8_t)value;
276 }
277 const char l = priv_reflow_css_lower(c);
278 if ((l >= 'a') && (l <= 'f')) {
279 const int value = ((int)l - (int)'a') + (int)k_priv_hex_a10;
280 return (uint8_t)value;
281 }
282 return (uint8_t)k_priv_hex_base;
283}
284
310static uint32_t internal_parse_hex_color(const char* s, size_t len)
311{
312 uint32_t rgb = 0U;
313 if (len == (size_t)k_priv_hex6_len) {
314 /* Bounded: exactly k_priv_hex6_len digits. */
315 for (size_t i = 0U; i < len; ++i) {
316 const uint8_t v = internal_hex_val(s[i]);
317 if (v >= (uint8_t)k_priv_hex_base) {
318 return (uint32_t)k_priv_col_invalid;
319 }
320 rgb = (rgb << (uint32_t)k_priv_hex_nib) | (uint32_t)v;
321 }
322 return rgb;
323 }
324 if (len == (size_t)k_priv_hex3_len) {
325 /* Bounded: exactly k_priv_hex3_len digits; each expands d -> dd. */
326 for (size_t i = 0U; i < len; ++i) {
327 const uint8_t v = internal_hex_val(s[i]);
328 if (v >= (uint8_t)k_priv_hex_base) {
329 return (uint32_t)k_priv_col_invalid;
330 }
331 rgb = (rgb << (uint32_t)k_priv_hex_chan) | ((uint32_t)v << (uint32_t)k_priv_hex_nib) |
332 (uint32_t)v;
333 }
334 return rgb;
335 }
336 return (uint32_t)k_priv_col_invalid;
337}
338
365static uint32_t internal_parse_color(const char* s, size_t len)
366{
367 /* mcdc-deactivated: the sole caller internal_apply_decl passes val (non-NULL, from priv_reflow_css_trim) and vlen > 0 (gated by (plen > 0U) && (vlen > 0U) in priv_reflow_css_parse_decls); (s == nullptr) and (len == 0U) are unreachable on any public path. */
368 if ((s == nullptr) || (len == 0U)) {
369 return (uint32_t)k_priv_col_invalid;
370 }
371 if (s[0] == '#') {
372 return internal_parse_hex_color(&s[1], len - 1U);
373 }
374 if (priv_reflow_css_ci_eq(s, len, "black")) {
375 return (uint32_t)k_priv_col_black;
376 }
377 if (priv_reflow_css_ci_eq(s, len, "white")) {
378 return (uint32_t)k_priv_col_white;
379 }
380 if (priv_reflow_css_ci_eq(s, len, "red")) {
381 return (uint32_t)k_priv_col_red;
382 }
383 if (priv_reflow_css_ci_eq(s, len, "green")) {
384 return (uint32_t)k_priv_col_green;
385 }
386 if (priv_reflow_css_ci_eq(s, len, "blue")) {
387 return (uint32_t)k_priv_col_blue;
388 }
389 if (priv_reflow_css_ci_eq(s, len, "gray") || priv_reflow_css_ci_eq(s, len, "grey")) {
390 return (uint32_t)k_priv_col_gray;
391 }
392 if (priv_reflow_css_ci_eq(s, len, "silver")) {
393 return (uint32_t)k_priv_col_silver;
394 }
395 if (priv_reflow_css_ci_eq(s, len, "maroon")) {
396 return (uint32_t)k_priv_col_maroon;
397 }
398 if (priv_reflow_css_ci_eq(s, len, "navy")) {
399 return (uint32_t)k_priv_col_navy;
400 }
401 return (uint32_t)k_priv_col_invalid;
402}
403
432static uint32_t internal_scan_hundredths(const char* s, size_t len, size_t* i, bool* any)
433{
434 uint32_t hund = 0U;
435 /* Bounded: integer digits, i advances by 1 each step, capped by len. */
436 while ((*i < len) && (s[*i] >= '0') && (s[*i] <= '9')) {
437 const int digit = (int)s[*i] - (int)'0';
438 hund = (hund * (uint32_t)k_priv_fs_dec) + (uint32_t)digit;
439 *any = true;
440 ++(*i);
441 }
442 hund *= (uint32_t)k_priv_fs_pct1; /* integer part -> hundredths */
443 if ((*i < len) && (s[*i] == '.')) {
444 ++(*i);
445 uint32_t place = (uint32_t)k_priv_fs_pct1 / (uint32_t)k_priv_fs_dec; /* tenths */
446 size_t fd = 0U;
447 /* Bounded: at most k_priv_fs_frac fractional digits. */
448 while ((*i < len) && (s[*i] >= '0') && (s[*i] <= '9') && (fd < (size_t)k_priv_fs_frac)) {
449 const int digit = (int)s[*i] - (int)'0';
450 hund += (uint32_t)digit * place;
451 place /= (uint32_t)k_priv_fs_dec;
452 *any = true;
453 ++fd;
454 ++(*i);
455 }
456 /* Bounded: skip any remaining fractional digits, i capped by len. */
457 while ((*i < len) && (s[*i] >= '0') && (s[*i] <= '9')) {
458 ++(*i);
459 }
460 }
461 return hund;
462}
463
486static uint16_t internal_fs_whole(uint32_t hund)
487{
488 const uint32_t whole = hund / (uint32_t)k_priv_fs_pct1;
489 return (uint16_t)((whole > (uint32_t)k_priv_fs_max) ? (uint32_t)k_priv_fs_max : whole);
490}
491
524static bool internal_parse_fontsize(const char* s, size_t len, uint16_t* out_val, uint8_t* out_unit)
525{
526 size_t i = 0U;
527 bool any = false;
528 uint32_t hund = internal_scan_hundredths(s, len, &i, &any);
529 if (!any) {
530 return false;
531 }
532 const size_t rem = len - i;
533 if ((rem == (size_t)k_priv_fs_ulen) && priv_reflow_css_ci_eq(&s[i], rem, "px")) {
534 *out_val = internal_fs_whole(hund);
535 *out_unit = (uint8_t)k_ra8_css_font_px;
536 return true;
537 }
538 if ((rem == 1U) && (s[i] == '%')) {
539 *out_val = internal_fs_whole(hund);
540 *out_unit = (uint8_t)k_ra8_css_font_pct;
541 return true;
542 }
543 if ((rem == (size_t)k_priv_fs_ulen) && priv_reflow_css_ci_eq(&s[i], rem, "em")) {
544 /* 1em = 100%; `hund` is value*100, which is already the percent. */
545 *out_val = (uint16_t)((hund > (uint32_t)k_priv_fs_max) ? (uint32_t)k_priv_fs_max : hund);
546 *out_unit = (uint8_t)k_ra8_css_font_pct;
547 return true;
548 }
549 return false;
550}
551
578static void internal_set_emphasis(ra8_css_style_t* out, uint8_t setbit, uint8_t stylebit, bool on)
579{
580 out->set = (uint8_t)(out->set | setbit);
581 out->style = (uint8_t)(on ? (out->style | stylebit) : (out->style & (uint8_t)~stylebit));
582}
583
615static bool internal_apply_emphasis(const char* prop,
616 size_t plen,
617 const char* val,
618 size_t vlen,
619 ra8_css_style_t* out)
620{
621 if (priv_reflow_css_ci_eq(prop, plen, "font-weight")) {
622 const bool on =
623 priv_reflow_css_ci_eq(val, vlen, "bold") || priv_reflow_css_ci_eq(val, vlen, "bolder") ||
624 priv_reflow_css_ci_eq(val, vlen, "600") || priv_reflow_css_ci_eq(val, vlen, "700") ||
625 priv_reflow_css_ci_eq(val, vlen, "800") || priv_reflow_css_ci_eq(val, vlen, "900");
627 return true;
628 }
629 if (priv_reflow_css_ci_eq(prop, plen, "font-style")) {
630 const bool on =
631 priv_reflow_css_ci_eq(val, vlen, "italic") || priv_reflow_css_ci_eq(val, vlen, "oblique");
633 return true;
634 }
635 if (priv_reflow_css_ci_eq(prop, plen, "text-decoration") ||
636 priv_reflow_css_ci_eq(prop, plen, "text-decoration-line")) {
637 const bool on = internal_ci_contains(val, vlen, "underline");
641 on);
642 return true;
643 }
644 return false;
645}
646
673static void internal_apply_align(const char* val, size_t vlen, ra8_css_style_t* out)
674{
675 out->set = (uint8_t)(out->set | (uint8_t)k_ra8_css_set_align);
676 if (priv_reflow_css_ci_eq(val, vlen, "right")) {
677 out->align = (uint8_t)k_reflow_align_right;
678 } else if (priv_reflow_css_ci_eq(val, vlen, "center")) {
679 out->align = (uint8_t)k_reflow_align_center;
680 } else if (priv_reflow_css_ci_eq(val, vlen, "justify")) {
681 out->align = (uint8_t)k_reflow_align_justify;
682 } else {
683 out->align = (uint8_t)k_reflow_align_left;
684 }
685}
686
718static void internal_apply_decl(const char* prop,
719 size_t plen,
720 const char* val,
721 size_t vlen,
722 ra8_css_style_t* out)
723{
724 if (internal_apply_emphasis(prop, plen, val, vlen, out)) {
725 return;
726 }
727 if (priv_reflow_css_ci_eq(prop, plen, "text-align")) {
728 internal_apply_align(val, vlen, out);
729 } else if (priv_reflow_css_ci_eq(prop, plen, "color")) {
730 const uint32_t rgb = internal_parse_color(val, vlen);
731 if (rgb != (uint32_t)k_priv_col_invalid) {
732 out->set = (uint8_t)(out->set | (uint8_t)k_ra8_css_set_color);
733 out->color = rgb;
734 }
735 } else if (priv_reflow_css_ci_eq(prop, plen, "font-size")) {
736 uint16_t fv = 0U;
737 uint8_t fu = 0U;
738 if (internal_parse_fontsize(val, vlen, &fv, &fu)) {
739 out->set = (uint8_t)(out->set | (uint8_t)k_ra8_css_set_fontsize);
740 out->font_val = fv;
741 out->font_unit = fu;
742 }
743 } else if (priv_reflow_css_ci_eq(prop, plen, "display")) {
744 out->set = (uint8_t)(out->set | (uint8_t)k_ra8_css_set_display);
745 out->display = priv_reflow_css_ci_eq(val, vlen, "none") ? 1U : 0U;
746 } else {
747 /* Unknown property -> ignore (no set bit). */
748 }
749}
750
774void priv_reflow_css_parse_decls(const char* s, size_t len, ra8_css_style_t* out)
775{
776 size_t i = 0U;
777 /* Bounded: each iteration consumes at least one byte via the ';' advance. */
778 while (i < len) {
779 size_t semi = i;
780 while ((semi < len) && (s[semi] != ';')) {
781 ++semi;
782 }
783 size_t colon = i;
784 while ((colon < semi) && (s[colon] != ':')) {
785 ++colon;
786 }
787 if (colon < semi) {
788 size_t plen = colon - i;
789 const char* prop = priv_reflow_css_trim(&s[i], &plen);
790 size_t vlen = semi - (colon + 1U);
791 const char* val = priv_reflow_css_trim(&s[colon + 1U], &vlen);
792 if ((plen > 0U) && (vlen > 0U)) {
793 internal_apply_decl(prop, plen, val, vlen, out);
794 }
795 }
796 i = semi + 1U;
797 }
798}
799
800/* ===========================================================================
801 * Public API -- inline declarations
802 * ===========================================================================
803 */
804
805ra8_err_t ra8_css_parse_inline(const char* decls, uint32_t len, ra8_css_style_t* out)
806{
807 if ((decls == nullptr) || (out == nullptr)) {
808 return k_ra8_err_null_ptr;
809 }
810 *out = (ra8_css_style_t){};
811 priv_reflow_css_parse_decls(decls, (size_t)len, out);
812 return k_ra8_ok;
813}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ 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
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
size_t strlen(const char *s)
Calculate string length.
HTML / CSS reflow + paginate engine for the ra8d2 ereader.
static uint8_t internal_hex_val(char c)
Hex value of one ASCII digit, or k_priv_hex_base if not a hex digit.
Definition reflow_css.c:271
static bool internal_ci_contains(const char *s, size_t len, const char *sub)
Case-insensitive "span contains substring @p sub" (bounded scan).
Definition reflow_css.c:203
priv_css_hex_t
Hex-colour parsing constants (#rgb / #rrggbb).
Definition reflow_css.c:41
@ k_priv_hex_chan
Bits per colour channel.
Definition reflow_css.c:47
@ k_priv_hex6_len
#rrggbb digit count.
Definition reflow_css.c:45
@ k_priv_hex3_len
#rgb short-form digit count.
Definition reflow_css.c:44
@ k_priv_hex_base
Base + "not a hex digit" sentinel.
Definition reflow_css.c:42
@ k_priv_hex_nib
Bits per hex nibble.
Definition reflow_css.c:46
@ k_priv_hex_a10
Value of hex 'a'/'A'.
Definition reflow_css.c:43
ra8_err_t ra8_css_parse_inline(const char *decls, uint32_t len, ra8_css_style_t *out)
Parse an inline style="..." declaration body into one style.
Definition reflow_css.c:805
static uint32_t internal_parse_hex_color(const char *s, size_t len)
Parse rgb / rrggbb hex digits (no #) to 0xRRGGBB, or invalid.
Definition reflow_css.c:310
static uint16_t internal_fs_whole(uint32_t hund)
Clamp a hundredths value to a whole number in [0, k_priv_fs_max].
Definition reflow_css.c:486
static uint32_t internal_parse_color(const char *s, size_t len)
Parse a CSS colour value (#rgb / #rrggbb / a named keyword).
Definition reflow_css.c:365
void priv_reflow_css_parse_decls(const char *s, size_t len, ra8_css_style_t *out)
Parse a prop:value; ... declaration body (no braces) into out.
Definition reflow_css.c:774
char priv_reflow_css_lower(char c)
ASCII-fold one byte to lower case.
Definition reflow_css.c:133
static bool internal_apply_emphasis(const char *prop, size_t plen, const char *val, size_t vlen, ra8_css_style_t *out)
Apply a boolean-emphasis property (font-weight/style/decoration); false if other.
Definition reflow_css.c:615
const char * priv_reflow_css_trim(const char *s, size_t *len)
Trim leading + trailing whitespace from s[0..*len); returns start.
Definition reflow_css.c:228
priv_css_fs_t
font-size parsing constants (decimal + unit scaling).
Definition reflow_css.c:54
@ k_priv_fs_pct1
Hundredths scale; 1em in percent.
Definition reflow_css.c:56
@ k_priv_fs_dec
Decimal base.
Definition reflow_css.c:55
@ k_priv_fs_frac
Fractional digits kept.
Definition reflow_css.c:57
@ k_priv_fs_max
Clamp for a parsed font-size number.
Definition reflow_css.c:58
@ k_priv_fs_ulen
Length of the "px" / "em" unit suffix.
Definition reflow_css.c:59
bool priv_reflow_css_ci_eq(const char *s, size_t len, const char *lit)
Case-insensitive compare of span s[0..len) against NUL literal lit.
Definition reflow_css.c:162
bool priv_reflow_css_is_ws(char c)
True for CSS whitespace (space / tab / newline / CR / FF).
Definition reflow_css.c:106
static bool internal_parse_fontsize(const char *s, size_t len, uint16_t *out_val, uint8_t *out_unit)
Parse Npx / N% / Nem (N may be fractional) into value + unit.
Definition reflow_css.c:524
static void internal_apply_decl(const char *prop, size_t plen, const char *val, size_t vlen, ra8_css_style_t *out)
Apply one trimmed prop:value pair to out, setting the matching bit.
Definition reflow_css.c:718
priv_css_color_t
Common named CSS colours + the "not a colour" sentinel.
Definition reflow_css.c:66
@ k_priv_col_black
CSS black.
Definition reflow_css.c:67
@ k_priv_col_blue
CSS blue.
Definition reflow_css.c:71
@ k_priv_col_maroon
CSS maroon.
Definition reflow_css.c:74
@ k_priv_col_gray
CSS gray / grey.
Definition reflow_css.c:72
@ k_priv_col_red
CSS red.
Definition reflow_css.c:69
@ k_priv_col_silver
CSS silver.
Definition reflow_css.c:73
@ k_priv_col_navy
CSS navy.
Definition reflow_css.c:75
@ k_priv_col_white
CSS white.
Definition reflow_css.c:68
@ k_priv_col_green
CSS green.
Definition reflow_css.c:70
@ k_priv_col_invalid
Sentinel: not a parseable colour.
Definition reflow_css.c:76
static void internal_apply_align(const char *val, size_t vlen, ra8_css_style_t *out)
Apply a parsed text-align value to out.
Definition reflow_css.c:673
static uint32_t internal_scan_hundredths(const char *s, size_t len, size_t *i, bool *any)
Scan a decimal number into hundredths (e.g.
Definition reflow_css.c:432
static void internal_set_emphasis(ra8_css_style_t *out, uint8_t setbit, uint8_t stylebit, bool on)
Set or clear stylebit in out, marking setbit as present.
Definition reflow_css.c:578
Minimal content-CSS cascade for the reflow ereader engine (#111).
@ k_ra8_css_set_fontsize
font-size was declared.
Definition reflow_css.h:96
@ k_ra8_css_set_underline
text-decoration was declared.
Definition reflow_css.h:93
@ k_ra8_css_set_align
text-align was declared.
Definition reflow_css.h:94
@ k_ra8_css_set_display
display was declared.
Definition reflow_css.h:97
@ k_ra8_css_set_bold
font-weight was declared.
Definition reflow_css.h:91
@ k_ra8_css_set_italic
font-style was declared.
Definition reflow_css.h:92
@ k_ra8_css_set_color
color was declared.
Definition reflow_css.h:95
@ k_ra8_css_font_px
Value is an absolute pixel size.
Definition reflow_css.h:109
@ k_ra8_css_font_pct
Value is a percentage of inherited px.
Definition reflow_css.h:110
Cross-TU surface for the split content-CSS cascade (#111).
@ k_reflow_align_center
Centred.
@ k_reflow_align_justify
Justified (last line left-aligned).
@ k_reflow_align_right
Right-aligned.
@ k_reflow_align_left
Left (default, ragged right).
@ k_reflow_style_italic
Italic face.
@ k_reflow_style_bold
Bold face.
@ k_reflow_style_underline
Underlined run.
A declared or computed style: a presence mask plus property values.
Definition reflow_css.h:122
uint16_t font_val
font-size number: px or % (valid iff set & fontsize).
Definition reflow_css.h:128
uint8_t font_unit
ra8_css_font_unit_t (valid iff set & fontsize).
Definition reflow_css.h:126
uint8_t display
1 = display:none (valid iff set & display).
Definition reflow_css.h:129
uint8_t style
reflow_font_style_t bit VALUES (bold/italic/ul).
Definition reflow_css.h:124
uint8_t set
ra8_css_set_t bitmask: which properties are present.
Definition reflow_css.h:123
uint32_t color
0xRRGGBB text colour (valid iff set & color).
Definition reflow_css.h:127
uint8_t align
reflow_align_t (valid iff set & align).
Definition reflow_css.h:125