Crystal wrapper around the gs (Ghostscript) binary for PDF
compression and post-processing. Pure shell-out via Process.run —
no FFI, no C bindings.
French version: README.fr.adoc.
Ghostscript is a 35+ year old C codebase (~300k lines) implementing a full PostScript interpreter, a PDF interpreter, image processing (libjpeg, libtiff, ICC color management) and a dozen rendering backends. A pure-Crystal port is unrealistic.
The gs binary is everywhere : brew install ghostscript on
macOS, pkg install ghostscript10 on FreeBSD, apt install ghostscript
on Debian/Ubuntu. Crystal already has clean shell-out support
(Process.run, Process.find_executable).
The wrapper itself stays MIT-licensed. The upstream gs binary on
disk is AGPLv3 — but invoking it from Crystal code does not
contaminate your code (same legal footing as calling tar or git).
Runtime requirement : gs available in $PATH.
# macOS
brew install ghostscript
# FreeBSD
pkg install ghostscript10
# Debian / Ubuntu
apt install ghostscript
# Verify
gs -vAdd to your shard.yml :
dependencies:
ghostscript:
github: aloli-crystal/ghostscript
version: ~> 0.1require "ghostscript"
# ── Detection ────────────────────────────────────────────────
Ghostscript.available? # => true / false
Ghostscript.path # => "/opt/homebrew/bin/gs" | nil
Ghostscript.version # => "10.04.0" | nil
# ── Typical use : PDF compression ────────────────────────────
result = Ghostscript.compress(
"in.pdf",
"out.pdf",
quality: :ebook, # :screen | :ebook (default) | :printer | :prepress
)
result.success? # => true
result.input_size # => 12345678 (bytes)
result.output_size # => 4321098
result.reduction_percent # => 65.0
puts result # => "12.0 Mo → 4.2 Mo (-65.0 %)"
# ── Low-level escape hatch ───────────────────────────────────
res = Ghostscript.run([
"-sDEVICE=pdfwrite",
"-sOutputFile=out.pdf",
"-dPDFSETTINGS=/screen",
"-dNOPAUSE",
"-dBATCH",
"in.pdf",
])
res.success? # => true
res.stdout # => raw stdout output
res.stderr # => raw stderr output
res.exit_code # => Int32The five Quality values mirror Ghostscript’s -dPDFSETTINGS=…
presets :
| Symbol | gs setting | Use case |
|---|---|---|
|
|
72 dpi, smallest size, screen reading only |
|
|
150 dpi, default — good size/quality trade-off |
|
|
300 dpi, office printing |
|
|
300 dpi, color-preserving, professional printing |
|
|
Let |
You can also pass a Ghostscript::Quality enum value directly :
Ghostscript.compress("in.pdf", "out.pdf", quality: Ghostscript::Quality::Screen)
Ghostscript::Quality.parse("printer") # => Quality::Printerbegin
Ghostscript.compress("in.pdf", "out.pdf")
rescue Ghostscript::Error => ex
STDERR.puts ex.message
# "Ghostscript binary `gs` not found in PATH"
# "Input file not found: in.pdf"
endWhen Ghostscript.available? returns false, you can fall back to
a pure-Crystal alternative or instruct the user to install gs.
MIT — see LICENSE.
The gs binary you call is licensed under AGPLv3 (or commercial)
by Artifex Software. Calling it from this wrapper does not
contaminate your application’s license.