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)
Depends onGhostscript (libgs)
Read / writeyes / no

libspectre parses the DSC comments of PostScript and EPS files and renders pages through Ghostscript. For EPS it has shortcuts that work on the document directly: spectre_document_is_eps, spectre_document_get_page_size (the bounding box size) and spectre_document_render. Evince and Okular use it to show EPS files.

The size it reports comes from the %%BoundingBox comment, so an EPS file with a wrong box renders clipped. Use Ghostscript’s bbox device to check. libspectre is in maintenance mode (0.2.12, 2023).

Installation#

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

Basic usage: check that a file is EPS and read its size#

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

int main(int argc, char **argv) {
    SpectreDocument *doc = spectre_document_new();
    spectre_document_load(doc, argc > 1 ? argv[1] : "in.eps");
    if (spectre_document_status(doc) != SPECTRE_STATUS_SUCCESS) {
        fprintf(stderr, "error: %s\n", spectre_status_to_string(spectre_document_status(doc)));
        spectre_document_free(doc);
        return 1;
    }
    int w = 0, h = 0;
    spectre_document_get_page_size(doc, &w, &h);  // points (1/72 inch)
    const char *creator = spectre_document_get_creator(doc);
    printf("EPS: %s\n", spectre_document_is_eps(doc) ? "yes" : "no");
    printf("format: %s\n", spectre_document_get_format(doc));
    printf("size: %d x %d pt\n", w, h);
    printf("creator: %s\n", creator ? creator : "(none)");
    spectre_document_free(doc);
    return 0;
}

How to render an EPS file to an image#

spectre_document_render_full renders the only page of an EPS document. The buffer has 4 bytes per pixel (blue, green, red, unused on little-endian machines) and rows row_length bytes apart:

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

int render_eps(const char *in, const char *out_ppm, double scale) {
    SpectreDocument *doc = spectre_document_new();
    spectre_document_load(doc, in);
    if (spectre_document_status(doc) != SPECTRE_STATUS_SUCCESS) {
        spectre_document_free(doc);
        return 1;
    }
    int w, h;
    spectre_document_get_page_size(doc, &w, &h);

    SpectreRenderContext *rc = spectre_render_context_new();
    spectre_render_context_set_scale(rc, scale, scale);
    unsigned char *data = NULL;
    int row_length = 0;
    spectre_document_render_full(doc, rc, &data, &row_length);
    int ok = spectre_document_status(doc) == SPECTRE_STATUS_SUCCESS && data;
    spectre_render_context_free(rc);
    spectre_document_free(doc);
    if (!ok) {
        free(data);
        return 1;
    }

    int pw = (int)(w * scale), ph = (int)(h * scale);
    FILE *f = fopen(out_ppm, "wb");
    fprintf(f, "P6\n%d %d\n255\n", pw, ph);
    for (int y = 0; y < ph; y++) {
        const 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 make a fixed-size thumbnail#

Compute the scale from the bounding box so the longer side is, for example, 256 pixels:

C
int w, h;
spectre_document_get_page_size(doc, &w, &h);
double scale = 256.0 / (w > h ? w : h);
spectre_render_context_set_scale(rc, scale, scale);

Tips#