All formats

libgxps

LanguageC (GObject)
LicenseLGPL-2.1-or-later
Repositorygitlab.gnome.org/GNOME/libgxps
Documentationlibgxps project page, API comments in the headers
Depends onGLib/GIO, cairo, libarchive, FreeType, libpng, libjpeg, libtiff, lcms2 (optional)
Read / writeyes / no (renders onto any cairo surface)

libgxps opens XPS and OpenXPS packages, parses the page markup and draws it with cairo. Because cairo can target images, PDF, PostScript and SVG, rendering a page onto the right surface is also how you convert XPS. Evince uses it, and it ships with command-line tools: xpstopng, xpstojpeg, xpstopdf, xpstops and xpstosvg.

The API has three objects: GXPSFile (the package), GXPSDocument (a FixedDocument; most files have one) and GXPSPage. Page sizes are in XPS units of 1/96 inch. libgxps doesn’t extract text and doesn’t write XPS. It’s maintained with occasional fixes; the last release is 0.3.2 (2021).

Installation#

Shell
# Debian / Ubuntu
sudo apt install libgxps-dev
# Fedora
sudo dnf install libgxps-devel
# macOS
brew install libgxps
Shell
cc xps2png.c $(pkg-config --cflags --libs libgxps) -o xps2png

Basic usage: render the first page to PNG#

C
#include <libgxps/gxps.h>
#include <cairo.h>

int main(int argc, char **argv) {
    GError *error = NULL;
    GFile *file = g_file_new_for_path(argc > 1 ? argv[1] : "in.xps");
    GXPSFile *xps = gxps_file_new(file, &error);
    g_object_unref(file);
    if (!xps) {
        g_printerr("cannot open: %s\n", error->message);
        g_error_free(error);
        return 1;
    }

    GXPSDocument *doc = gxps_file_get_document(xps, 0, &error);
    if (!doc) {
        g_printerr("no document: %s\n", error->message);
        g_error_free(error);
        g_object_unref(xps);
        return 1;
    }
    g_print("documents: %u, pages: %u\n",
            gxps_file_get_n_documents(xps), gxps_document_get_n_pages(doc));

    GXPSPage *page = gxps_document_get_page(doc, 0, &error);
    if (!page) {
        g_printerr("cannot load page: %s\n", error->message);
        return 1;
    }
    gdouble w, h;
    gxps_page_get_size(page, &w, &h);       // 1/96 inch
    double scale = 150.0 / 96.0;            // render at 150 dpi

    cairo_surface_t *surface = cairo_image_surface_create(
        CAIRO_FORMAT_RGB24, (int)(w * scale), (int)(h * scale));
    cairo_t *cr = cairo_create(surface);
    cairo_set_source_rgb(cr, 1, 1, 1);      // white background
    cairo_paint(cr);
    cairo_scale(cr, scale, scale);
    if (!gxps_page_render(page, cr, &error)) {
        g_printerr("render error: %s\n", error->message);
        g_clear_error(&error);
    }
    cairo_destroy(cr);
    cairo_surface_write_to_png(surface, "page1.png");
    cairo_surface_destroy(surface);

    g_object_unref(page);
    g_object_unref(doc);
    g_object_unref(xps);
    return 0;
}

How to convert XPS to PDF#

Render every page onto a cairo PDF surface. PDF uses points (1/72 inch), so scale by 72/96:

C
#include <libgxps/gxps.h>
#include <cairo.h>
#include <cairo-pdf.h>

int xps_to_pdf(GXPSDocument *doc, const char *out) {
    cairo_surface_t *pdf = cairo_pdf_surface_create(out, 612, 792);
    cairo_t *cr = cairo_create(pdf);
    guint n = gxps_document_get_n_pages(doc);
    for (guint i = 0; i < n; i++) {
        GError *error = NULL;
        GXPSPage *page = gxps_document_get_page(doc, i, &error);
        if (!page) {
            g_printerr("page %u: %s\n", i + 1, error->message);
            g_error_free(error);
            continue;
        }
        gdouble w, h;
        gxps_page_get_size(page, &w, &h);
        cairo_pdf_surface_set_size(pdf, w * 72.0 / 96.0, h * 72.0 / 96.0);
        cairo_save(cr);
        cairo_scale(cr, 72.0 / 96.0, 72.0 / 96.0);
        if (!gxps_page_render(page, cr, &error)) {
            g_printerr("page %u: %s\n", i + 1, error->message);
            g_error_free(error);
        }
        cairo_restore(cr);
        cairo_show_page(cr);
        g_object_unref(page);
    }
    cairo_destroy(cr);
    cairo_surface_finish(pdf);
    int ok = cairo_surface_status(pdf) == CAIRO_STATUS_SUCCESS;
    cairo_surface_destroy(pdf);
    return ok ? 0 : 1;
}

The same command-line tool exists: xpstopdf in.xps out.pdf.

How to read the title and author of an XPS file#

Core properties are optional; the function returns NULL when a file has none:

C
GError *error = NULL;
GXPSCoreProperties *props = gxps_file_get_core_properties(xps, &error);
if (props) {
    const gchar *title = gxps_core_properties_get_title(props);
    const gchar *creator = gxps_core_properties_get_creator(props);
    g_print("title: %s\ncreator: %s\n", title ? title : "-", creator ? creator : "-");
    g_object_unref(props);
} else if (error) {
    g_printerr("%s\n", error->message);
    g_error_free(error);
}

How to get page sizes without rendering#

C
guint n = gxps_document_get_n_pages(doc);
for (guint i = 0; i < n; i++) {
    gdouble w, h;
    if (gxps_document_get_page_size(doc, i, &w, &h))
        g_print("page %u: %.0f x %.0f (%.1f x %.1f in)\n", i + 1, w, h, w / 96, h / 96);
}

gxps_document_get_page_size uses the sizes from the FixedDocument and returns FALSE if they’re missing; then load the page and call gxps_page_get_size.

Tips#