All formats

libspectre

LanguageC
LicenseGPL-2.0-or-later (uses Ghostscript, which is AGPL-3.0)
Repositorygitlab.freedesktop.org/libspectre/libspectre
Documentationcomments in the headers (spectre-document.h, spectre-page.h)
Depends onGhostscript (libgs)
Read / writeyes / no (renders pages; saves a copy as PS or PDF)

libspectre wraps Ghostscript in a document and page API made for viewers. It parses the DSC comments to find pages, title, creator and bounding boxes without running the whole file, then asks Ghostscript to render a single page into a 32-bit RGB buffer. Evince, Okular and other viewers use it for PostScript and EPS.

It’s small and stable (bug-fix releases only; 0.2.12 is from 2023). It depends on DSC comments: a PostScript file without %%Page: comments shows up as a single page. For conversions and anything beyond viewing, call Ghostscript directly.

Installation#

Shell
# Debian / Ubuntu
sudo apt install libspectre-dev
# Fedora
sudo dnf install libspectre-devel
# macOS
brew install libspectre

Compile with pkg-config:

Shell
cc app.c $(pkg-config --cflags --libs libspectre) -o app

Basic usage: open a file and read pages and metadata#

C
#include <libspectre/spectre.h>
#include <stdio.h>

static const char *str(const char *s) { return s ? s : "(none)"; }

int main(int argc, char **argv) {
    const char *path = argc > 1 ? argv[1] : "in.ps";
    SpectreDocument *doc = spectre_document_new();
    spectre_document_load(doc, path);
    SpectreStatus status = spectre_document_status(doc);
    if (status != SPECTRE_STATUS_SUCCESS) {
        fprintf(stderr, "cannot load %s: %s\n", path, spectre_status_to_string(status));
        spectre_document_free(doc);
        return 1;
    }
    printf("pages:    %u\n", spectre_document_get_n_pages(doc));
    printf("title:    %s\n", str(spectre_document_get_title(doc)));
    printf("creator:  %s\n", str(spectre_document_get_creator(doc)));
    printf("created:  %s\n", str(spectre_document_get_creation_date(doc)));
    printf("format:   %s\n", str(spectre_document_get_format(doc)));
    printf("EPS:      %s\n", spectre_document_is_eps(doc) ? "yes" : "no");
    printf("level:    %u\n", spectre_document_get_language_level(doc));
    spectre_document_free(doc);
    return 0;
}

How to render a PostScript page to an image#

spectre_page_render returns a buffer of 4 bytes per pixel in the same layout as cairo’s CAIRO_FORMAT_RGB24 (on little-endian machines: blue, green, red, unused). Rows can be padded: use row_length, not width * 4. This writes a PPM file:

C
#include <libspectre/spectre.h>
#include <stdio.h>
#include <stdlib.h>

int render_page(SpectreDocument *doc, unsigned index, double scale, const char *out) {
    SpectrePage *page = spectre_document_get_page(doc, index);  // 0-based
    if (!page) return 1;

    int w, h;
    spectre_page_get_size(page, &w, &h);  // in points (1/72 inch)

    SpectreRenderContext *rc = spectre_render_context_new();
    spectre_render_context_set_scale(rc, scale, scale);   // 2.0 = 144 dpi
    spectre_render_context_set_antialias_bits(rc, 4, 4);  // graphics, text

    unsigned char *data = NULL;
    int row_length = 0;
    spectre_page_render(page, rc, &data, &row_length);
    SpectreStatus status = spectre_page_status(page);
    spectre_render_context_free(rc);
    spectre_page_free(page);
    if (status != SPECTRE_STATUS_SUCCESS || !data) {
        free(data);
        return 1;
    }

    int pw = (int)(w * scale), ph = (int)(h * scale);
    FILE *f = fopen(out, "wb");
    fprintf(f, "P6\n%d %d\n255\n", pw, ph);
    for (int y = 0; y < ph; y++) {
        unsigned char *row = data + y * row_length;
        for (int x = 0; x < pw; x++) {
            unsigned char rgb[3] = { row[x * 4 + 2], row[x * 4 + 1], row[x * 4] };
            fwrite(rgb, 1, 3, f);
        }
    }
    fclose(f);
    free(data);
    return 0;
}

How to get the size of each page#

C
unsigned n = spectre_document_get_n_pages(doc);
for (unsigned i = 0; i < n; i++) {
    SpectrePage *page = spectre_document_get_page(doc, i);
    int w, h;
    spectre_page_get_size(page, &w, &h);
    printf("page %s: %d x %d pt\n", spectre_page_get_label(page), w, h);
    spectre_page_free(page);
}

How to convert PostScript to PDF#

C
spectre_document_save_to_pdf(doc, "out.pdf");
if (spectre_document_status(doc) != SPECTRE_STATUS_SUCCESS)
    fprintf(stderr, "export failed\n");

Tips#