libmobi
| Language | C (C99, usable from C++) |
|---|---|
| License | LGPL-3.0 |
| Repository | github.com/bfabiszewski/libmobi |
| Documentation | Doxygen API reference and the tools source |
| Depends on | zlib and libxml2 (both optional, bundled replacements: miniz and an internal XML writer) |
| Read / write | yes / partial (metadata editing, writing loaded documents back) |
libmobi reads every common Kindle file format: old PalmDOC (.pdb), Mobipocket (.prc, .mobi), KF8 (.azw3), hybrid MOBI 6 + KF8 files, and Print Replica (.azw4). It decompresses PalmDOC and HUFF/CDIC text, parses the MOBI and EXTH headers and the indexes, and reconstructs the source: HTML parts with working links, CSS, images, fonts and the NCX table of contents. It can edit metadata and save the file, and decrypt or encrypt DRM with a device PID or serial number.
It comes with command line tools: mobitool (print metadata, dump the cover, recreate source files, create EPUB), mobimeta (edit metadata) and mobidrm (DRM). Their source is the best example code. The LGPL-3.0 license allows linking from closed-source programs if you link dynamically or let users relink.
Installation#
# Debian / Ubuntu
sudo apt install libmobi-dev
# macOS
brew install libmobi
# from source
./autogen.sh && ./configure && make && sudo make install
Include <mobi.h> and link with -lmobi. The project also has basic CMake and MSVC build files.
Basic usage: read metadata#
#include <mobi.h>
#include <stdio.h>
#include <stdlib.h>
static void print_meta(const char *label, char *value) {
if (value) {
printf("%-10s %s\n", label, value);
free(value); // mobi_meta_get_* return malloc'ed strings
}
}
int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "usage: %s book.mobi\n", argv[0]);
return 1;
}
MOBIData *m = mobi_init();
if (!m) return 1;
MOBI_RET ret = mobi_load_filename(m, argv[1]);
if (ret != MOBI_SUCCESS) {
fprintf(stderr, "cannot load file (error %d)\n", ret);
mobi_free(m);
return 1;
}
print_meta("Title:", mobi_meta_get_title(m));
print_meta("Author:", mobi_meta_get_author(m));
print_meta("Publisher:", mobi_meta_get_publisher(m));
print_meta("ISBN:", mobi_meta_get_isbn(m));
print_meta("ASIN:", mobi_meta_get_asin(m));
print_meta("Language:", mobi_meta_get_language(m));
printf("File version: %zu\n", mobi_get_fileversion(m));
printf("KF8: %s, hybrid: %s, Print Replica: %s, encrypted: %s\n",
mobi_is_kf8(m) ? "yes" : "no", mobi_is_hybrid(m) ? "yes" : "no",
mobi_is_replica(m) ? "yes" : "no", mobi_is_encrypted(m) ? "yes" : "no");
mobi_free(m);
return 0;
}
For hybrid files libmobi uses the KF8 part by default; set m->use_kf8 = false before loading to read the MOBI 6 part.
How to extract HTML, CSS and images from a MOBI file#
mobi_parse_rawml decompresses the text and rebuilds the parts:
MOBIRawml *rawml = mobi_init_rawml(m);
if (rawml && mobi_parse_rawml(rawml, m) == MOBI_SUCCESS) {
// HTML parts (one for MOBI 6, one per file for KF8)
for (MOBIPart *part = rawml->markup; part; part = part->next) {
char name[64];
snprintf(name, sizeof name, "part%05zu.html", part->uid);
FILE *f = fopen(name, "wb");
if (f) {
fwrite(part->data, 1, part->size, f);
fclose(f);
}
}
// CSS and SVG flows (KF8)
for (MOBIPart *part = rawml->flow; part; part = part->next) {
if (part->type == T_CSS) printf("css flow: %zu bytes\n", part->size);
}
// images, fonts, audio
for (MOBIPart *part = rawml->resources; part; part = part->next) {
if (part->type == T_JPG) printf("jpeg resource %zu: %zu bytes\n", part->uid, part->size);
}
}
mobi_free_rawml(rawml);
How to extract the cover image#
The EXTH record 201 gives the cover’s index relative to the first image record:
MOBIExthHeader *exth = mobi_get_exthrecord_by_tag(m, EXTH_COVEROFFSET);
if (exth) {
uint32_t offset = mobi_decode_exthvalue(exth->data, exth->size);
size_t uid = mobi_get_first_resource_record(m) + offset;
MOBIPdbRecord *rec = mobi_get_record_by_seqnumber(m, uid);
if (rec && rec->size > 3) {
FILE *f = fopen("cover.jpg", "wb"); // check rec->data for JPEG/GIF/PNG magic bytes
if (f) {
fwrite(rec->data, 1, rec->size, f);
fclose(f);
}
}
}
How to change the title and author of a MOBI file#
mobi_meta_set_title(m, "New Title");
mobi_meta_set_author(m, "Jane Doe");
FILE *out = fopen("out.mobi", "wb");
if (out) {
MOBI_RET ret = mobi_write_file(out, m);
fclose(out);
if (ret != MOBI_SUCCESS) fprintf(stderr, "write failed (%d)\n", ret);
}
The command line equivalent: mobimeta -s title="New Title",author="Jane Doe" in.mobi out.mobi.
How to convert MOBI or AZW3 to EPUB#
EPUB export lives in the mobitool program, not in the library API:
mobitool -e book.azw3 # writes book.epub (needs libmobi built with its XML writer)
mobitool -s -o out book.mobi # dump the recreated source files into out/
mobitool -c book.mobi # dump the cover
Print Replica (.azw4) books can’t become EPUB; mobitool -s extracts the embedded PDF (in mobi_parse_rawml it is a markup part of type T_PDF).
Tips#
mobi_dump_rawml(m, file)writes the decompressed text record content (raw HTML) without reconstruction.- For encrypted books,
mobi_drm_setkey(m, pid)ormobi_drm_setkey_serial(m, serial)with the owner’s key enables decryption before parsing. mobi_remove_hybrid_part(m, true)drops the KF8 part of a hybrid file (falsedrops the MOBI 6 part) before writing.