36typedef enum : uint32_t {
58[[gnu::section(
".sdram_data"),
66 static const char*
const k_block[] = {
67 "p",
"br",
"div",
"h1",
"h2",
"h3",
"h4",
"h5",
68 "h6",
"li",
"ul",
"ol",
"tr",
"hr",
"pre",
"section",
69 "blockquote",
"article",
"header",
"footer",
"figure",
"figcaption"};
70 for (
size_t i = 0U; i < (
sizeof(k_block) /
sizeof(k_block[0])); ++i) {
71 if ((
strlen(k_block[i]) == n) && (
strncmp(tag, k_block[i], n) == 0)) {
81 static const char*
const k_skip[] = {
"head",
"title",
"style",
"script"};
82 for (
size_t i = 0U; i < (
sizeof(k_skip) /
sizeof(k_skip[0])); ++i) {
83 if ((
strlen(k_skip[i]) == n) && (
strncmp(tag, k_skip[i], n) == 0)) {
91typedef enum : uint32_t {
106static void sh_emit_cp(
char* out,
size_t cap,
size_t* pos, uint32_t cp)
109 out[(*pos)++] = (char)cp;
110 }
else if ((cp <
k_u8_2max) && (*pos + 1U < cap)) {
113 }
else if (*pos + 2U < cap) {
123 if ((c >=
'0') && (c <=
'9')) {
124 return (uint32_t)(c -
'0');
126 if ((base ==
k_ent_hex) && (c >=
'a') && (c <=
'f')) {
129 if ((base ==
k_ent_hex) && (c >=
'A') && (c <=
'F')) {
138 if ((n < 4U) || (s[1] !=
'#')) {
141 const bool hex = (s[2] ==
'x') || (s[2] ==
'X');
143 size_t j = hex ? 3U : 2U;
146 while ((j < n) && (s[j] !=
';')) {
147 const uint32_t d =
sh_digit(s[j], base);
148 if (d == UINT32_MAX) {
155 if ((dig == 0U) || (j >= n) || (s[j] !=
';')) {
166 static const struct {
169 } k_named[] = {{
"&",
'&'}, {
"<",
'<'}, {
">",
'>'}, {
""",
'"'}, {
"'",
'\''}};
170 for (
size_t i = 0U; i < (
sizeof(k_named) /
sizeof(k_named[0])); ++i) {
171 const size_t ln =
strlen(k_named[i].name);
172 if ((n >= ln) && (
strncmp(s, k_named[i].name, ln) == 0)) {
194 while ((st->
pos > 0U) && (out[st->
pos - 1U] ==
' ')) {
198 if ((st->
pos > 0U) && (out[st->
pos - 1U] !=
'\n')) {
199 out[st->
pos++] =
'\n';
206 const bool ws = (cp ==
' ') || (cp ==
'\t') || (cp ==
'\n') || (cp ==
'\r');
209 out[st->
pos++] =
' ';
222 const bool close = (j < n) && (x[j] ==
'/');
227 while ((j < n) && (((x[j] >=
'a') && (x[j] <=
'z')) || ((x[j] >=
'A') && (x[j] <=
'Z')) ||
228 ((x[j] >=
'0') && (x[j] <=
'9')))) {
231 const size_t tn = j - ts;
240 while ((i < n) && (x[i] !=
'>')) {
243 return (i < n) ? (i + 1U) : n;
249 sh_strip_t st = {.pos = 0U, .at_break =
true, .suppress = 0};
315 for (int32_t y = 0; y < h; ++y) {
316 for (int32_t x = 0; x < w; ++x) {
320 g_sh.thumb_w[idx] = (uint16_t)w;
321 g_sh.thumb_h[idx] = (uint16_t)h;
330 g_sh.thumb_w[idx] = 0U;
331 g_sh.thumb_h[idx] = 0U;
335 if (
g_sh.book_src.vm !=
nullptr) {
338 return g_sh.thumb_w[idx] > 0U;
353 if (
g_sh.book_src.vm ==
nullptr) {
356 const uint32_t cover =
g_sh.book_src.hdr.cover_image_index;
367 (void)
strncpy(dst, (src !=
nullptr) ? src :
"", cap - 1U);
368 dst[cap - 1U] =
'\0';
387 if (
abs >= pool_end) {
390 uint64_t n = pool_end -
abs;
391 if (n > (uint64_t)(cap - 1U)) {
392 n = (uint64_t)(cap - 1U);
411 }
else if (
g_sh.book_src.vm !=
nullptr) {
433 if (idx >=
g_sh.book_count) {
436 if (
s_epub.in_use != 0U) {
449 g_sh.chapter_count = 0U;
463 g_sh.chapter_count = n;
472 g_sh.chapter_count =
g_sh.book_src.hdr.chapter_count;
502 for (uint16_t i = 0U; i < tc; ++i) {
503 uint16_t mapped = UINT16_MAX;
505 ((uint32_t)mapped == chapter)) {
520 if (src->
vm ==
nullptr) {
533 return out[0] !=
'\0';
@ k_book_nil
Absent index / "applies to all chapters".
ra8_err_t book_src_read(const book_src_t *src, uint32_t off, void *dst, uint32_t len)
Copy a byte range out of a book source (resident memcpy or paged fault).
ra8_err_t book_chapter_text_src(const book_src_t *src, uint32_t chapter_idx, char *out, size_t cap, size_t *out_len)
Extract one chapter's plain text from a book source (resident or paged).
static uint8_t s_arena[k_c6_cam_arena_bytes]
EPUB (.epub) reader and chapter iterator for ra8-firmware.
ra8_err_t epub_toc_entry_to_chapter(const epub_book_t *book, uint16_t toc_idx, uint16_t *out_chapter_idx)
Resolve a TOC entry to the spine chapter index it points into.
ra8_err_t epub_get_cover_image(epub_book_t *book, uint8_t *out_buf, size_t max_len, size_t *got_len)
Copy the raw bytes of the cover image into the caller's buffer.
ra8_err_t epub_get_metadata(const epub_book_t *book, epub_metadata_t *out_meta)
Return Dublin Core metadata strings.
ra8_err_t epub_load_chapter(epub_book_t *book, uint16_t idx, uint8_t *out_xhtml, size_t max_len, size_t *got_len)
Extract the raw XHTML bytes of one chapter into the caller's buffer.
ra8_err_t epub_get_toc_entry(const epub_book_t *book, uint16_t idx, epub_toc_entry_t *out_entry)
Copy one table-of-contents entry into the caller's struct.
ra8_err_t epub_get_chapter_count(const epub_book_t *book, uint16_t *out_count)
Report how many chapters the spine contains.
ra8_err_t epub_get_toc_count(const epub_book_t *book, uint16_t *out_count)
Report how many table-of-contents entries the book exposes.
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
int strncmp(const char *s1, const char *s2, size_t n)
Compare two strings up to a specified length.
char * strncpy(char *dst, const char *src, size_t n)
Copy bounded string to destination buffer.
size_t strlen(const char *s)
Calculate string length.
int abs(int j)
Compute absolute value of integer.
Software 2D graphics primitives layered on top of a caller-ownedframebuffer (DRW / D/AVE 2D / GLCDC r...
@ k_ra8_gfx_format_rgb565
16-bit RGB565, little-endian in memory.
ra8_err_t ra8_gfx_clear(uint32_t color)
Fill the bound framebuffer's clip region with a single colour.
ra8_err_t ra8_gfx_init(void *fb, uint16_t width, uint16_t height, ra8_gfx_format_t format)
Bind ra8_gfx to a caller-owned framebuffer.
Zero-heap raster image decode + scale + blit for reflow (#106).
ra8_err_t ra8_img_decode_blit(ra8_img_arena_t *arena, const uint8_t *bytes, size_t len, int32_t dst_x, int32_t dst_y, int32_t box_w, int32_t box_h, int32_t *out_w, int32_t *out_h)
Decode bytes and blit it, scaled to fit, into the bound framebuffer.
Shared contract for the ereader_shelf multi-screen e-reader.
void sh_decode_cover(uint16_t idx, const book_src_t *src)
Decode the open rabook source's 4bpp cover into thumbnail slot idx.
bool sh_paged_open(ra8_vsource_read_fn file_read, void *file_ctx, uint64_t file_len)
Open a chunked .rabook container as the demand-paged book source.
bool sh_paged_open_mram(const uint8_t *blob, uint32_t blob_len)
Open a memory-mapped (baked MRAM) RBKC container as the paged source.
bool sh_sd_open_epub(const char *name, void *out_book)
Stream-open SD file name as an EPUB via epub_open_streamed_fs.
ra8_err_t sh_image_blit_cover(const book_src_t *src, uint32_t img_idx, int32_t dst_x, int32_t dst_y, int32_t box_w, int32_t box_h, int32_t *out_w, int32_t *out_h)
Decode + aspect-fit a 4bpp image straight into the framebuffer box.
bool sh_sd_book_open(const char *name, uint32_t *out_len)
Open SD file name and hold it as the paged book backing.
void sh_comic_close(void)
Close any open comic and release its backing (idempotent).
@ k_sh_thumb_h
Shelf cover-thumbnail box height.
@ k_sh_fb_h
Panel height in pixels.
@ k_sh_thumb_w
Shelf cover-thumbnail box width.
@ k_sh_fb_w
Panel width in pixels.
void sh_sd_close_epub(void *book)
Close a streamed EPUB opened by sh_sd_open_epub and its source file.
size_t sh_fmt_uint(char *dst, size_t pos, uint32_t v)
Append unsigned v as decimal into dst at pos; returns new pos.
sh_state_t g_sh
The single whole-app state instance (defined in main.c).
uint16_t * sh_fb_pixels(void)
The live RGB565 framebuffer ra8_gfx scans (defined in main.c).
ra8_err_t sh_sd_book_read(void *ctx, uint64_t offset, uint8_t *buf, uint32_t len)
ra8_vsource_read_fn over the held-open SD book file (seek + read).
void sh_paged_close(void)
Unbind the paged book source and release its SD backing (idempotent).
bool sh_comic_open(uint16_t idx)
Open shelf entry idx as a comic archive (CBZ / CBR) for page reading.
static void sh_book_capture_meta(uint16_t idx)
Populate SD entry idx title/author/cover from the just-opened book.
static void sh_copy_str(char *dst, size_t cap, const char *src)
Copy src into dst (cap bytes) NUL-terminated.
void sh_book_cover_fullscreen(int32_t x, int32_t y, int32_t w, int32_t h)
Draw the open book's cover full-size, aspect-fit into the box.
static epub_book_t s_epub
The open EPUB (in_use when active).
static void sh_src_string(const book_src_t *src, uint32_t off, char *out, size_t cap)
Copy the string-pool entry at pool offset off out of the paged source into out (cap bytes,...
static bool sh_epub_label(uint32_t chapter, char *out, size_t cap)
Reverse-lookup the EPUB TOC label that targets spine chapter.
static bool sh_epub_thumb(uint16_t idx)
Decode the open EPUB's cover into gray8 thumbnail slot idx.
bool sh_book_open(uint16_t idx)
Open shelf entry idx by its format; sets g_sh.open_fmt + chapter count, and (for SD books on first op...
void sh_book_chapter_label(uint32_t chapter, char *out, size_t cap)
Resolve a chapter's TOC label (or "Chapter N") into out.
static void sh_strip_text(char *out, size_t cap, sh_strip_t *st, uint32_t cp)
Emit one decoded code point as collapsed body text.
bool sh_book_decode_thumb(uint16_t idx)
Decode the open book's cover into thumbnail slot idx; returns ok.
static uint8_t sh_luma565(uint16_t px)
Rec.601 luma of an RGB565 pixel (0..255).
static uint32_t sh_digit(char c, uint32_t base)
Value of hex/decimal digit c in base base, or UINT32_MAX if invalid.
ra8_err_t sh_book_chapter_text(uint32_t chapter, char *out, size_t cap, size_t *out_len)
Extract one chapter's plain text from the open book (either backend).
static bool sh_decode_entity(const char *s, size_t n, size_t *consumed, uint32_t *cp)
Decode the &..; entity at s; sets cp + consumed; false if invalid.
static bool sh_xhtml_is_suppress(const char *tag, size_t n)
Is tag an element whose text content must be suppressed entirely?
static uint8_t s_cover[k_sh_cover_cap]
static void sh_emit_cp(char *out, size_t cap, size_t *pos, uint32_t cp)
Append code point cp to out as UTF-8 (the reader folds to ASCII).
sh_u8enc_t
UTF-8 encode boundaries / lead-byte prefixes.
@ k_ent_hex
Hex numeric-entity base.
@ k_ent_a10
Hex 'a'/'A' digit value.
@ k_u8_sh12
Two continuation shift.
@ k_u8_2max
Below: 2 bytes.
@ k_u8_data
Continuation payload mask.
@ k_ent_dec
Decimal numeric-entity base.
@ k_u8_cont
Continuation prefix.
@ k_u8_2lead
2-byte lead prefix.
@ k_u8_sh6
One continuation shift.
@ k_u8_1max
Code points below: 1 byte.
@ k_u8_3lead
3-byte lead prefix.
static size_t sh_strip_tag(const uint8_t *x, size_t n, size_t i, char *out, sh_strip_t *st)
Handle one <...> tag: break/suppress bookkeeping; returns index past >.
static bool sh_book_open_rabook(const sh_entry_t *e)
Bind shelf entry e's RBKC container as the demand-paged source.
static size_t sh_strip_xhtml(const uint8_t *x, size_t n, char *out, size_t cap)
Strip XHTML x[0,n) to collapsed plain text in out; returns length.
sh_book_const_t
Backend buffer + format constants.
@ k_sh_cover_cap
Compressed cover image bytes.
@ k_sh_r5_mask
5-bit channel mask.
@ k_sh_luma_r
Rec.601 red weight (/256).
@ k_sh_r5_sh
RGB565 red shift.
@ k_sh_col_white
Thumbnail letterbox fill.
@ k_sh_arena_cap
stb_image decode bump arena.
@ k_sh_xhtml_cap
One chapter's XHTML scratch.
@ k_sh_5to8
5-bit -> 8-bit shift.
@ k_sh_g6_mask
6-bit channel mask.
@ k_sh_luma_sh
Rec.601 divide shift (/256).
@ k_sh_g6_sh
RGB565 green shift.
@ k_sh_luma_g
Rec.601 green weight.
@ k_sh_6to8
6-bit -> 8-bit shift.
@ k_sh_luma_b
Rec.601 blue weight.
static void sh_strip_break(char *out, sh_strip_t *st)
Emit a paragraph break: trim trailing spaces, collapse runs of ' '.
static bool sh_rabook_label(uint32_t chapter, char *out, size_t cap)
Paged rabook TOC label: read the chapter record, then its string.
static uint8_t s_xhtml[k_sh_xhtml_cap]
static bool sh_parse_numeric(const char *s, size_t n, size_t *consumed, uint32_t *cp)
Parse a numeric entity body #[x]NNN; at s; sets cp + consumed.
static uint16_t s_thumb_rgb[k_sh_thumb_w *k_sh_thumb_h]
static bool sh_xhtml_is_block(const char *tag, size_t n)
Is tag (length n) a block element that should break the line?
@ k_sh_fmt_epub
EPUB parsed on-device by epub.
@ k_sh_fmt_rabook
book RBKC container (demand-paged chunks).
static bool sh_fmt_is_comic(sh_book_fmt_t fmt)
True if fmt is a comic-archive container (CBZ, CBR, or CBT).
One spine document (a renderable chapter) plus its TOC label.
uint32_t title_off
String-pool offset of the TOC label ("" if none).
A book data source: resident (zero-copy) or paged (demand-fetched).
book_header_t hdr
Resident copy of the blob header.
ra8_vmem_t * vm
Paged page cache, or NULL in resident mode.
One titled entry in the table of contents.
char title[k_epub_meta_len]
Navigation label text ("" if none).
Caller-owned bump arena backing a single image decode.
One shelf book, sourced from MRAM (baked) or the SD card, in either the .rabook or ....
bool from_sd
true: read sd_name from SD; false: use blob.
const uint8_t * blob
Baked RBKC bytes (MRAM), or NULL when SD.
uint32_t blob_len
Baked length, or SD file size in bytes.
char sd_name[k_sh_name_cap]
SD file name, e.g.
sh_book_fmt_t fmt
Container format (rabook / epub).
char title[k_sh_title_cap]
Display title.
char author[k_sh_author_cap]
Display author.
Mutable state threaded through the XHTML strip.
bool at_break
Suppress a leading space (post-break).
int suppress
>0 inside head/title/style/script.
size_t pos
Bytes written to out.