All formats

djvu-rs

LanguageRust (edition 2024, Rust 1.88+)
LicenseMIT
Repositorygithub.com/matyushkin/djvu-rs
Documentationdocs.rs/djvu-rs, library guide
Depends onno C libraries; optional features pull in image, miniz_oxide, zip, rayon, tokio
Read / writeyes / yes (JB2 and IW44 encoders, document editing)

djvu-rs reads, renders, converts and creates DjVu files. It’s written from the DjVu v3 specification without DjVuLibre code, so it’s MIT-licensed. It decodes JB2, IW44, BZZ and JPEG layers, reads text and annotations, and exports to PDF (with selectable text and bookmarks), EPUB, TIFF, CBZ, hOCR and ALTO XML. The same crate builds the djvu command-line tool, WebAssembly bindings and the Python package.

The crate is new (first released in 2026) and the 0.x API still changes; pin the version in Cargo.toml. The core decoder needs only std; exporters and integrations are behind feature flags.

Installation#

Shell
cargo add djvu-rs                          # decode and render
cargo add djvu-rs --features pdf,image     # plus PDF export and image crate integration
cargo install djvu-rs --features cli       # the `djvu` command-line tool

Basic usage: open a document and render a page#

Rust
use djvu_rs::{DjVuDocument, djvu_render::{render_pixmap, RenderOptions}};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let data = std::fs::read("book.djvu")?;
    let doc = DjVuDocument::parse(&data)?;
    println!("{} pages", doc.page_count());

    let page = doc.page(0)?; // 0-based
    println!("{}x{} @ {} dpi", page.width(), page.height(), page.dpi());

    // Render at 150 dpi to RGBA.
    let target_dpi = 150u32;
    let opts = RenderOptions {
        width: ((page.width() as u32 * target_dpi) / page.dpi() as u32).max(1),
        height: ((page.height() as u32 * target_dpi) / page.dpi() as u32).max(1),
        ..Default::default()
    };
    let pixmap = render_pixmap(page, &opts)?;
    // pixmap.data: RGBA bytes, pixmap.width x pixmap.height, top row first
    std::fs::write("page1.ppm", pixmap.to_ppm())?;
    Ok(())
}

How to save a DjVu page as PNG#

With the image feature, a page works as an image::ImageDecoder (also run cargo add image):

Rust
use djvu_rs::{DjVuDocument, image_compat::DjVuDecoder};
use image::DynamicImage;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let data = std::fs::read("book.djvu")?;
    let doc = DjVuDocument::parse(&data)?;
    let page = doc.page(0)?;

    let decoder = DjVuDecoder::new(page)?.with_size(1200, 1600);
    let img = DynamicImage::from_decoder(decoder)?;
    img.save("page1.png")?;
    Ok(())
}

How to extract text from a DjVu file#

page.text() returns None when the page has no text layer:

Rust
use djvu_rs::DjVuDocument;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let data = std::fs::read("book.djvu")?;
    let doc = DjVuDocument::parse(&data)?;
    for i in 0..doc.page_count() {
        if let Some(text) = doc.page(i)?.text()? {
            println!("--- page {} ---\n{text}", i + 1);
        }
    }
    Ok(())
}

For word coordinates, djvu_rs::text_serialize::to_hocr exports the whole document as hOCR.

How to convert DjVu to PDF#

Needs the pdf feature. The PDF keeps the text layer, bookmarks and links:

Rust
use djvu_rs::{DjVuDocument, pdf::djvu_to_pdf};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let doc = DjVuDocument::parse(&std::fs::read("book.djvu")?)?;
    std::fs::write("book.pdf", djvu_to_pdf(&doc)?)?;
    Ok(())
}

How to read bookmarks (table of contents)#

Rust
use djvu_rs::{DjVuBookmark, DjVuDocument};

fn print(b: &[DjVuBookmark], level: usize) {
    for bm in b {
        println!("{}{} -> {}", "  ".repeat(level), bm.title, bm.url);
        print(&bm.children, level + 1);
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let doc = DjVuDocument::parse(&std::fs::read("book.djvu")?)?;
    print(doc.bookmarks(), 0);
    Ok(())
}

How to list the chunks of a DjVu file#

The iff module parses the container without decoding anything:

Rust
use djvu_rs::iff::parse_form;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let data = std::fs::read("page.djvu")?;
    let form = parse_form(&data)?;
    println!("FORM type: {:?}", std::str::from_utf8(&form.form_type));
    for chunk in &form.chunks {
        println!("  {:?} ({} bytes)", std::str::from_utf8(&chunk.id), chunk.data.len());
    }
    Ok(())
}

Tips#