libspectre
| Language | C |
|---|---|
| License | GPL-2.0-or-later (uses Ghostscript, which is AGPL-3.0) |
| Repository | gitlab.freedesktop.org/libspectre/libspectre |
| Documentation | comments in the headers (spectre-document.h) |
| Depends on | Ghostscript (libgs) |
| Read / write | yes / 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#
sudo apt install libspectre-dev # Debian / Ubuntu
sudo dnf install libspectre-devel # Fedora
brew install libspectre # macOS
cc thumb.c $(pkg-config --cflags --libs libspectre) -o thumb
Basic usage: check that a file is EPS and read its size#
#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:
#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:
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#
spectre_render_context_set_antialias_bits(rc, 4, 4)gives smoother text than the default (4 for graphics, 2 for text).spectre_document_save_to_pdf(doc, "out.pdf")converts the EPS to PDF through Ghostscript.