All formats

MuPDF

LanguageC (C++ and Java bindings included)
LicenseAGPL-3.0 or commercial license from Artifex
Repositorygithub.com/ArtifexSoftware/mupdf
DocumentationMuPDF documentation, C API reference
Depends onbundled third-party libraries (FreeType, HarfBuzz, zlib, libjpeg, openjpeg, jbig2dec, lcms2, ...)
Read / writeyes / no (XPS and OpenXPS; writes PDF, PNG, SVG, text, CBZ and others)

MuPDF is a small, fast renderer for PDF, XPS, OpenXPS, EPUB, CBZ, FB2 and images. The same fz_document API works for all of them: fz_open_document picks the handler from the file name or content, and the page functions render, extract text or copy the page to another writer. It’s the engine of SumatraPDF’s XPS support, mutool, and many mobile document viewers.

License: AGPL-3.0. A program that links MuPDF must be released under the AGPL (including for network services) unless you buy a commercial license from Artifex.

Installation#

Shell
# Debian / Ubuntu
sudo apt install libmupdf-dev
# macOS
brew install mupdf
# Windows (vcpkg)
vcpkg install mupdf

Or build from source with make (Linux, macOS) or the Visual Studio solution in platform/win32. Link with -lmupdf -lmupdf-third (static builds) or -lmupdf.

Basic usage: render every page of an XPS file to PNG#

C
#include <mupdf/fitz.h>
#include <stdio.h>

int main(int argc, char **argv) {
    const char *input = argc > 1 ? argv[1] : "in.xps";
    fz_context *ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED);
    if (!ctx) return 1;

    fz_document *doc = NULL;
    fz_try(ctx) {
        fz_register_document_handlers(ctx);
        doc = fz_open_document(ctx, input);  // .xps and .oxps
        int n = fz_count_pages(ctx, doc);
        fz_matrix ctm = fz_scale(150.0f / 72, 150.0f / 72);  // 150 dpi
        for (int i = 0; i < n; i++) {
            fz_pixmap *pix = fz_new_pixmap_from_page_number(ctx, doc, i, ctm,
                                                            fz_device_rgb(ctx), 0);
            char name[64];
            snprintf(name, sizeof(name), "page-%03d.png", i + 1);
            fz_save_pixmap_as_png(ctx, pix, name);
            fz_drop_pixmap(ctx, pix);
        }
    }
    fz_always(ctx)
        fz_drop_document(ctx, doc);
    fz_catch(ctx) {
        fz_report_error(ctx);
        fz_drop_context(ctx);
        return 1;
    }
    fz_drop_context(ctx);
    return 0;
}

MuPDF measures in points (1/72 inch) for all formats, so an XPS Letter page (816 x 1056 XPS units) is 612 x 792.

How to extract text from an XPS file#

C
fz_try(ctx) {
    int n = fz_count_pages(ctx, doc);
    for (int i = 0; i < n; i++) {
        fz_buffer *buf = fz_new_buffer_from_page_number(ctx, doc, i, NULL);
        printf("%s\f", fz_string_from_buffer(ctx, buf));
        fz_drop_buffer(ctx, buf);
    }
}
fz_catch(ctx)
    fz_report_error(ctx);

Text comes from the UnicodeString of Glyphs elements. For positions of each character, use fz_new_stext_page_from_page_number and walk its blocks, lines and chars.

How to convert XPS to PDF#

Run each page into a PDF document writer; vector graphics and fonts are kept:

C
fz_document_writer *wri = NULL;
fz_page *page = NULL;
fz_var(wri);
fz_var(page);
fz_try(ctx) {
    wri = fz_new_document_writer(ctx, "out.pdf", "pdf", NULL);
    int n = fz_count_pages(ctx, doc);
    for (int i = 0; i < n; i++) {
        page = fz_load_page(ctx, doc, i);
        fz_rect box = fz_bound_page(ctx, page);
        fz_device *dev = fz_begin_page(ctx, wri, box);
        fz_run_page(ctx, page, dev, fz_identity, NULL);
        fz_end_page(ctx, wri);
        fz_drop_page(ctx, page);
        page = NULL;
    }
    fz_close_document_writer(ctx, wri);
}
fz_always(ctx) {
    fz_drop_page(ctx, page);
    fz_drop_document_writer(ctx, wri);
}
fz_catch(ctx)
    fz_report_error(ctx);

mutool convert -o out.pdf in.xps does the same from the command line.

How to check that a file is XPS#

C
char buf[64];
if (fz_lookup_metadata(ctx, doc, FZ_META_FORMAT, buf, sizeof(buf)) > 0)
    printf("format: %s
", buf);   // "XPS" for both .xps and .oxps

For XPS, MuPDF reports only the format: it doesn’t expose the title or author from the package’s core properties. Read that part (usually docProps/core.xml) with a ZIP library, or use libgxps.

Tips#