31 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
32 "<container version=\"1.0\" "
33 "xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\"><rootfiles>"
34 "<rootfile full-path=\"OEBPS/content.opf\" "
35 "media-type=\"application/oebps-package+xml\"/></rootfiles></container>";
74 if (
strcmp(mime,
"image/png") == 0) {
78 if (
strcmp(mime,
"image/gif") == 0) {
82 if (
strcmp(mime,
"image/webp") == 0) {
86 if (
strcmp(mime,
"image/jpeg") == 0) {
109 const char* dot =
strrchr(name,
'.');
110 if (dot !=
nullptr) {
111 if ((
strcmp(dot,
".png") == 0) || (
strcmp(dot,
".PNG") == 0)) {
115 if ((
strcmp(dot,
".gif") == 0) || (
strcmp(dot,
".GIF") == 0)) {
119 if ((
strcmp(dot,
".webp") == 0) || (
strcmp(dot,
".WEBP") == 0)) {
123 if ((
strcmp(dot,
".bmp") == 0) || (
strcmp(dot,
".BMP") == 0)) {
155 if (dir !=
nullptr) {
170 const size_t cur =
strlen(dst);
171 const size_t add =
strlen(text);
172 if (cur + add + 1U > cap) {
175 memcpy(dst + cur, text, add + 1U);
183 (
const uint8_t*)body,
217 const int entry_len = snprintf(archive_entry,
sizeof(archive_entry),
"OEBPS/%s", cover->
entry);
227 const int frag_len = snprintf(frag,
229 "<item id=\"cover-image\" href=\"%s\" media-type=\"%s\" "
230 "properties=\"cover-image\"/>",
266 const char* esc_name,
273 const char* prop_attr = is_cover ?
" properties=\"cover-image\"" :
"";
274 const int fn = snprintf(frag,
276 "<item id=\"pg%zu\" href=\"page_%03u.xhtml\" "
277 "media-type=\"application/xhtml+xml\"/>"
278 "<item id=\"img%zu\" href=\"images/%s\" media-type=\"%s\"%s/>",
291 (void)snprintf(frag,
sizeof(frag),
"<itemref idref=\"pg%zu\"/>", idx);
295 (void)snprintf(frag,
sizeof(frag),
"<li><a href=\"page_%03u.xhtml\">Page %u</a></li>", n, n);
323 if (meta ==
nullptr) {
361 const int xn = snprintf(xhtml,
363 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
364 "<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><title>Page %u"
365 "</title></head><body><img src=\"images/%s\" alt=\"Page %u\"/>"
374 (void)snprintf(entry,
sizeof(entry),
"OEBPS/page_%03u.xhtml", n);
416 const unsigned n = (unsigned)(idx + 1U);
427 const int en = snprintf(entry,
sizeof(entry),
"OEBPS/images/%s", name);
437 const char* media_type =
nullptr;
511 if ((*mani ==
nullptr) || (*spine ==
nullptr) || (*nav ==
nullptr) || (*opf ==
nullptr) ||
512 (*navdoc ==
nullptr)) {
549 (void)mz_zip_writer_end(zip);
611 zip.m_pIO_opaque = output;
612 const bool zip_open = (mz_zip_writer_init(&zip, 0) != MZ_FALSE);
624 for (
size_t i = 0U; (rc ==
k_ra8_ok) && (i < count); ++i) {
@ k_fw_fs_path_cap
Largest portable path including its NUL.
Package a downloaded chapter folder into a reader-openable container.
void * mdl_export_workspace_take(mdl_export_workspace_t *ws, size_t bytes, size_t alignment)
Reserve aligned bytes from an exporter arena.
struct mdl_export_workspace mdl_export_workspace_t
Caller-owned bounded arena for all exporter scratch state.
static ra8_err_t internal_epub_add_external_cover(mz_zip_archive *zip, mdl_storage_t *storage, const mdl_external_cover_t *cover, char *mani, size_t cap)
Store and declare a validated external EPUB cover.
static ra8_err_t internal_epub_finish_writer(mz_zip_archive *zip, bool zip_open, mdl_zip_allocator_t *zip_alloc, ra8_err_t rc, const mdl_export_output_t *output)
End the ZIP writer and translate exhaustion/output faults to status.
bool priv_mdl_epub_add_str(mz_zip_archive *zip, const char *name, const char *body)
Add an in-memory string as a stored ZIP entry.
ra8_err_t priv_mdl_export_epub(mdl_storage_t *storage, const char *dir, char names[][k_name_max], size_t count, mdl_export_output_t *output, const mdl_export_meta_t *meta, mdl_export_workspace_t *ws)
Package chapter pages into a valid fixed-layout EPUB3.
static ra8_err_t internal_epub_add_page(mdl_storage_t *storage, mz_zip_archive *zip, const char *dir, const char *name, size_t idx, char *mani, char *spine, char *nav, size_t cap, const mdl_export_meta_t *meta)
Add one fixed-layout EPUB page and its image.
static ra8_err_t internal_epub_append_frags(char *mani, char *spine, char *nav, size_t cap, const char *esc_name, const char *media, size_t idx, unsigned n, bool is_cover)
Append one page's manifest, spine, and navigation fragments.
static ra8_err_t internal_epub_media_type_from_sniff(mdl_storage_t *storage, const char *dir, const char *name, const char **out)
Classify one page's image media type from sniffed portable bytes.
static ra8_err_t internal_epub_carve_workspace(mdl_export_workspace_t *ws, size_t cap, char **mani, char **spine, char **nav, char **opf, char **navdoc)
Carve the five bounded XML/ZIP accumulators from workspace arena.
static ra8_err_t internal_epub_media_type(mdl_storage_t *storage, const char *dir, const char *name, const char **out)
Resolve one page's image media type from portable bytes then suffix.
bool priv_mdl_epub_str_cat(char *dst, size_t cap, const char *text)
Append text to NUL-terminated dst; report whether it fully fit.
static bool internal_epub_page_is_cover(const mdl_export_meta_t *meta, const char *name, size_t idx)
Is one exported image the publication's declared cover?
static ra8_err_t internal_epub_write_page_xhtml(mz_zip_archive *zip, const char *name, unsigned n, char *esc, size_t esc_cap)
Escape a page name and write its XHTML wrapper into the ZIP.
static const mdl_export_meta_t * internal_epub_page_meta(const mdl_external_cover_t *cover, const mdl_export_meta_t *meta, mdl_export_meta_t *page_meta)
Select page metadata after accounting for an external cover.
static void internal_epub_media_type_from_suffix(const char *name, const char **out)
Classify one page's image media type from its filename suffix.
static const char *const s_epub_container_xml
OCF container pointing at the OPF package (fixed).
Private bounded EPUB3 writer contracts for the media downloader.
ra8_err_t priv_mdl_epub_add_meta(mz_zip_archive *zip, const char *mani, const char *spine, const char *nav, size_t page_count, const mdl_export_meta_t *meta, char *opf, size_t opf_buf_cap, char *navdoc, size_t nav_buf_cap)
Build EPUB package metadata and finalize the archive.
@ k_epub_xhtml_max
One page's xhtml document (embeds the name).
@ k_epub_name_esc_max
XML-escaped page name (k_name_max * 6).
@ k_epub_base_bytes
Fixed opf/nav overhead.
@ k_epub_entry_max
A zip entry path ("OEBPS/images/" + name).
@ k_epub_per_page_bytes
Per-page opf/nav accumulator growth.
@ k_epub_frag_max
One manifest fragment (fixed + escaped name).
Module-private contracts shared by chapter export writers.
ra8_err_t priv_mdl_zip_workspace_error(const mdl_zip_allocator_t *alloc)
Classify a miniz writer failure.
void priv_mdl_zip_workspace_bind(mz_zip_archive *zip, mdl_zip_allocator_t *alloc, mdl_export_workspace_t *ws)
Bind a miniz archive to caller-owned bounded storage.
@ k_name_max
Maximum entry-name bytes.
void priv_mdl_zip_workspace_release(mdl_zip_allocator_t *alloc)
Restore a workspace after a miniz writer ends.
bool priv_mdl_export_snprintf_fit(int written, size_t cap)
Report whether an snprintf result fit its destination.
ra8_err_t priv_mdl_export_prepare_cover(mdl_storage_t *storage, const mdl_export_meta_t *meta, const char names[][k_name_max], size_t count, mdl_external_cover_t *cover)
Validate and canonicalize an external cover.
ra8_err_t priv_mdl_export_zip_add_file(mz_zip_archive *zip, mdl_storage_t *storage, const char *member, const char *path, mz_uint flags)
Add one portable source file through miniz callbacks.
ra8_err_t priv_mdl_export_path_join(char *out, size_t capacity, const char *directory, const char *leaf)
Join a canonical directory and leaf without truncation.
size_t priv_mdl_export_zip_write(void *opaque, mz_uint64 file_offset, const void *bytes, size_t length)
Adapt random-offset miniz output to the active export stage.
ra8_err_t priv_mdl_export_zip_add_memory(mz_zip_archive *zip, const char *member, const uint8_t *bytes, size_t length, mz_uint flags)
Add caller-owned memory through the deterministic ZIP read seam.
Neutralise untrusted names before they reach a filesystem or XML sink.
bool mdl_xml_escape(const char *src, char *out, size_t cap)
XML-escape src into out, failing rather than truncating.
Bounded URL naming and portable image-classification helpers.
ra8_err_t mdl_urlname_sniff_file(mdl_storage_t *storage, const char *file_path, const char *content_type, char *out_ext, size_t ext_cap, char *out_mime, size_t mime_cap)
Sniff true image extension and MIME type from portable storage.
Annotation-attribute framework macros for ra8-firmware.
#define RA8_PRIV
Module-private helper: shared across TUs but only inside one library.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_fail
Generic unspecified failure.
@ k_ra8_err_validation_failed
Validation rule failed (caller-supplied invariant not satisfied).
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
@ k_ra8_err_not_found
Requested item not found (lookup / search missed).
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
int strcmp(const char *s1, const char *s2)
Compare two null-terminated strings.
size_t strlen(const char *s)
Calculate string length.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
char * strrchr(const char *s, int c)
Locate last occurrence of character in string.
One validated staged publication, including random ZIP backfill.
ra8_err_t error
First callback error, if any.
Validated external cover details retained without owning storage.
const char * source
Trusted caller-owned source path.
char entry[k_cover_entry_bytes]
Canonical relative member path.
char mime[k_cover_mime_bytes]
MIME derived from magic bytes.
bool external
True when a separate cover exists.
One non-reentrant downloader filesystem dependency bundle.
Per-writer adapter from miniz callbacks to one caller workspace.
bool exhausted
A callback exceeded bounded space.