All formats

libmobi

LanguageC (C99, usable from C++)
LicenseLGPL-3.0
Repositorygithub.com/bfabiszewski/libmobi
DocumentationDoxygen API reference and the tools source
Depends onzlib and libxml2 (both optional, bundled replacements: miniz and an internal XML writer)
Read / writeyes / 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#

Shell
# 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#

C
#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:

C
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:

C
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#

C
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:

Shell
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#