All formats

Ghostscript

LanguageC (usable from C++)
LicenseAGPL-3.0 or commercial license from Artifex
Repositorygithub.com/ArtifexSoftware/ghostpdl
DocumentationGhostscript API, command-line options
Depends onnothing required (bundles its own copies of zlib, libpng, libjpeg, FreeType, lcms2)
Read / writeyes / yes: interprets PostScript Level 3, writes PostScript (ps2write), EPS (eps2write), PDF and raster images

Ghostscript is the standard PostScript interpreter. It’s the engine behind ps2pdf, the PostScript support in SumatraPDF, Evince, Okular, ImageMagick and GIMP, and most print servers. The shared library libgs (gsdll64.dll on Windows) exposes the interpreter through a small C API: you pass the same arguments as to the gs command and Ghostscript runs in your process.

The work is chosen with an output device: pdfwrite (to PDF), ps2write (to clean Level 2 PostScript), eps2write, png16m/pngalpha/jpeg/tiff24nc (raster), txtwrite (text), bbox (bounding box), nullpage (run without output).

License: Ghostscript is AGPL-3.0. Linking it into a program you distribute, or running it in a network service, means your program must be released under the AGPL too, unless you buy a commercial license from Artifex. Calling the separate gs executable is the usual way around linking, but check the license terms for your case.

Installation#

Shell
# Debian / Ubuntu: headers in /usr/include/ghostscript, library libgs.so
sudo apt install libgs-dev
# Fedora
sudo dnf install libgs-devel
# macOS (installs gs, libgs and headers)
brew install ghostscript

On Windows, the Ghostscript installer provides gsdll64.dll. Load it at run time, or build ghostpdl from source to get the import library; the headers iapi.h and ierrors.h are in the psi/ folder of the source.

Link with -lgs.

Basic usage: convert PostScript to PDF#

gsapi_init_with_args takes an argv array exactly like the gs command. The first element is ignored.

C
#include <ghostscript/iapi.h>
#include <ghostscript/ierrors.h>
#include <stdio.h>

int main(void) {
    char *args[] = {
        "gs",  // argv[0], ignored
        "-dNOPAUSE", "-dBATCH", "-dSAFER", "-dQUIET",
        "-sDEVICE=pdfwrite",
        "-sOutputFile=out.pdf",
        "in.ps",
    };
    int argc = sizeof(args) / sizeof(args[0]);

    void *gs = NULL;
    int code = gsapi_new_instance(&gs, NULL);
    if (code < 0) {
        fprintf(stderr, "cannot create Ghostscript instance: %d\n", code);
        return 1;
    }
    gsapi_set_arg_encoding(gs, GS_ARG_ENCODING_UTF8);

    code = gsapi_init_with_args(gs, argc, args);
    int code1 = gsapi_exit(gs);
    if (code == 0 || code == gs_error_Quit)
        code = code1;
    gsapi_delete_instance(gs);

    if (code != 0 && code != gs_error_Quit) {
        fprintf(stderr, "Ghostscript failed: %d\n", code);
        return 1;
    }
    return 0;
}

gs_error_Quit means the PostScript program ended with quit: that’s success.

How to render PostScript pages to PNG#

Use a raster device. %03d in the output name is replaced with the page number:

C
char *args[] = {
    "gs", "-dNOPAUSE", "-dBATCH", "-dSAFER", "-dQUIET",
    "-sDEVICE=png16m",      // 24-bit RGB; pngalpha for transparency, pnggray for grayscale
    "-r150",                // resolution in dpi
    "-dTextAlphaBits=4",    // anti-aliasing
    "-dGraphicsAlphaBits=4",
    "-sOutputFile=page-%03d.png",
    "in.ps",
};

Add -dFirstPage=2 -dLastPage=2 (or -sPageList=2,5-7) to render only some pages. For PostScript input Ghostscript still has to run all pages before the last one you ask for.

How to extract text from a PostScript file#

C
char *args[] = {
    "gs", "-dNOPAUSE", "-dBATCH", "-dSAFER", "-dQUIET",
    "-sDEVICE=txtwrite",
    "-sOutputFile=out.txt",   // "-" writes to stdout
    "in.ps",
};

Text comes out in reading order as well as Ghostscript can guess it. Files that draw text with Type 3 bitmap fonts (old dvips output) may give garbage.

How to get the bounding box of a PostScript page#

The bbox device computes the area that is actually drawn and prints %%BoundingBox and %%HiResBoundingBox lines to stderr. Capture Ghostscript’s output with gsapi_set_stdio:

C
#include <ghostscript/iapi.h>
#include <ghostscript/ierrors.h>
#include <stdio.h>

static int gs_stdin(void *handle, char *buf, int len) { return 0; }
static int gs_stdout(void *handle, const char *str, int len) { return len; } // ignore
static int gs_stderr(void *handle, const char *str, int len) {
    fwrite(str, 1, len, stdout);  // %%BoundingBox: 72 600 372 744 ...
    return len;
}

int print_bbox(const char *path) {
    char file_arg[1024];
    snprintf(file_arg, sizeof(file_arg), "%s", path);
    char *args[] = { "gs", "-dNOPAUSE", "-dBATCH", "-dSAFER", "-dQUIET",
                     "-sDEVICE=bbox", file_arg };
    void *gs = NULL;
    if (gsapi_new_instance(&gs, NULL) < 0) return 1;
    gsapi_set_stdio(gs, gs_stdin, gs_stdout, gs_stderr);
    int code = gsapi_init_with_args(gs, 7, args);
    int code1 = gsapi_exit(gs);
    if (code == 0 || code == gs_error_Quit) code = code1;
    gsapi_delete_instance(gs);
    return (code == 0 || code == gs_error_Quit) ? 0 : 1;
}

There’s one pair of lines per page.

How to run PostScript code from a string#

Start Ghostscript without an input file, then feed PostScript with gsapi_run_string:

C
char *args[] = { "gs", "-dNOPAUSE", "-dSAFER", "-dQUIET",
                 "-sDEVICE=pdfwrite", "-sOutputFile=hello.pdf" };
void *gs = NULL;
int exit_code = 0;
gsapi_new_instance(&gs, NULL);
int code = gsapi_init_with_args(gs, 6, args);
if (code == 0) {
    code = gsapi_run_string(gs,
        "/Helvetica findfont 24 scalefont setfont "
        "72 720 moveto (Hello) show showpage",
        0, &exit_code);
}
int code1 = gsapi_exit(gs);   // closes the device and writes hello.pdf
if (code == 0 || code == gs_error_Quit) code = code1;
gsapi_delete_instance(gs);

Tips#