All formats

python-djvulibre

LanguagePython 3 (Cython extension)
LicenseGPL-2.0
Repositorygithub.com/FriedrichFroebel/python-djvulibre
Documentationdjvulibre-python.readthedocs.io
Depends onDjVuLibre 3.5.26 or newer, pkg-config and a C compiler to build
Read / writeyes / save whole documents or page subsets (no page encoding)

python-djvulibre exposes the DjVuLibre ddjvuapi to Python as two modules: djvu.decode (documents, pages, rendering, messages) and djvu.sexpr (the s-expressions DjVuLibre uses for text, outline and annotations). It’s used by ocrodjvu and other DjVu tools.

The original project by Jakub Wilk is archived; the maintained fork is published on PyPI as djvulibre-python (the import name is still djvu). The fork supports Python 3 only and doesn’t build on Windows. For Windows or a non-GPL license, use djvu-rs.

Installation#

Install DjVuLibre with its headers first, then the package:

Shell
# Debian / Ubuntu
sudo apt install libdjvulibre-dev pkg-config
# macOS
brew install djvulibre pkg-config

pip install djvulibre-python

Basic usage: open a document and render a page#

Context runs DjVuLibre’s decoding threads. wait() blocks until a job is done. The example renders page 1 to 24-bit RGB and saves it with Pillow:

Python
import djvu.decode
from PIL import Image

ctx = djvu.decode.Context()
doc = ctx.new_document(djvu.decode.FileURI("book.djvu"))
doc.decoding_job.wait()
print(len(doc.pages), "pages")

page = doc.pages[0]               # 0-based
job = page.decode(wait=True)
width, height = job.size
print(width, height, job.dpi, "dpi")

fmt = djvu.decode.PixelFormatRgb()
fmt.rows_top_to_bottom = 1        # the default is bottom row first
rect = (0, 0, width, height)
data = job.render(djvu.decode.RENDER_COLOR, rect, rect, fmt)
Image.frombytes("RGB", (width, height), data).save("page1.png")

How to render a page at a smaller size#

The page is scaled to the size of the first rectangle; the second rectangle selects the part to return:

Python
dpi = 100
w = width * dpi // job.dpi
h = height * dpi // job.dpi
data = job.render(djvu.decode.RENDER_COLOR, (0, 0, w, h), (0, 0, w, h), fmt)
Image.frombytes("RGB", (w, h), data).save("page1-small.png")

Other modes: RENDER_BLACK (black-and-white mask only, falls back to color), RENDER_BACKGROUND, RENDER_FOREGROUND.

How to extract text from a DjVu file#

TEXT_DETAILS_PAGE returns one zone per page: (page x0 y0 x1 y1 "text"). The text is the 6th element:

Python
import djvu.decode

ctx = djvu.decode.Context()
doc = ctx.new_document(djvu.decode.FileURI("book.djvu"))
doc.decoding_job.wait()

for i, page in enumerate(doc.pages):
    text = djvu.decode.PageText(page, djvu.decode.TEXT_DETAILS_PAGE)
    text.wait()
    sexpr = text.sexpr
    if len(sexpr) > 5:
        print(f"--- page {i + 1} ---")
        print(sexpr[5].value)

Use TEXT_DETAILS_WORD or TEXT_DETAILS_LINE to get nested zones: each zone is (type x0 y0 x1 y1 children-or-text...), with coordinates in pixels from the bottom-left corner.

How to read the table of contents#

Python
outline = doc.outline
outline.wait()
print(outline.sexpr)   # (bookmarks ("Chapter 1" "#5" ...) ...)

How to read metadata#

DjVu metadata (author, title, year) lives in the annotations:

Python
annotations = doc.annotations
annotations.wait()
for key, value in annotations.metadata.items():
    print(key, value)

How to save some pages to a new file#

Python
with open("part.djvu", "wb") as f:
    doc.save(f, pages=[0, 1, 2])   # waits until the file is written

Tips#