DEV Community

Cover image for How I Compress PDF Files Server-Side Using Ghostscript
kabir daki
kabir daki

Posted on • Edited on

How I Compress PDF Files Server-Side Using Ghostscript

When I started building PDFOnlineLovePDF, I had to pick an approach for PDF compression: do it entirely in the browser, or run it on a server.
I chose server-side, using Ghostscript. Here's why, and how it actually works under the hood.
Why not client-side?
Client-side PDF compression (rendering pages to canvas, re-encoding as JPEG, rebuilding the PDF with pdf-lib) is a real technique, and it works. But it has a real cost: it rasterizes every page into an image. That means the output PDF loses its text layer entirely — you can no longer select, search, or copy text from it. For a tool where a lot of users upload text-heavy documents (contracts, reports, invoices), that tradeoff didn't feel acceptable.
Ghostscript, on the other hand, works at the PDF structure level. It recompresses embedded images, downsamples resolution, and optimizes the internal object structure — without flattening pages into pictures. Text stays selectable. That was the deciding factor.
The actual implementation
The client only handles the upload and a lightweight preview (rendering page 1 to a canvas with pdf.js, just so the user can see what they're about to compress):
tsconst pdfjsLib = (window as any).pdfjsLib;
pdfjsLib.GlobalWorkerOptions.workerSrc =
"https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js";

const ab = await file.arrayBuffer();
const pdf = await pdfjsLib.getDocument({ data: ab }).promise;
const page = await pdf.getPage(1);
const vp = page.getViewport({ scale: 0.5 });
const canvas = document.createElement("canvas");
canvas.width = vp.width;
canvas.height = vp.height;
await page.render({ canvasContext: canvas.getContext("2d")!, viewport: vp }).promise;
That's it for the browser side — a thumbnail, nothing more. The file is then sent to the server:
tsconst res = await fetch("/api/tools", { method: "POST", body: form });
The server side: three quality presets, one Ghostscript flag
On the server, I expose three compression levels, each mapped to one of Ghostscript's built-in PDF settings presets:
tsconst LEVELS = [
{ key: "extreme", gs: "/screen" },
{ key: "recommended", gs: "/ebook" },
{ key: "less", gs: "/printer" },
];
These presets aren't arbitrary — they're Ghostscript's own tuning profiles:

/screen — low resolution (~72 dpi), smallest file, best for sharing quickly
/ebook — medium resolution (~150 dpi), the balance most users want
/printer — higher resolution (~300 dpi), for when print quality matters

The actual compression call:
tsawait execAsync(
gs -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -dPDFSETTINGS=${gsPreset} -sOutputFile="${outFile}" "${input}",
{ timeout: 60000 }
);
Why this tradeoff made sense for me
Running Ghostscript per-request does mean every upload touches my server's CPU — that's a real cost client-side compression avoids. But in exchange:

Documents keep their text layer (searchable, selectable, copy-pasteable)
Compression quality is more predictable across different PDF structures (scanned images, vector graphics, mixed content) than a canvas-rasterization approach
The three presets map cleanly to Ghostscript's own tested profiles, instead of me tuning JPEG quality percentages by trial and error

The honest downside: files do get uploaded to a server for processing. I don't market this tool as "your files never touch a server" — because they do. What I do guarantee is that uploaded files are processed in a temporary directory and not retained afterward.
What I'd explore next
If I revisit this, the interesting middle ground is: use pdf.js client-side just to detect whether a PDF is text-heavy or image-heavy, and route text-heavy documents through Ghostscript (structure-preserving) while offering an optional aggressive image-rasterization mode for scanned documents where text-selectability was never possible anyway.

The compress tool is live at PDFOnlineLovePDF.com — free, no signup. Curious how others are approaching PDF compression — canvas rasterization, Ghostscript, something else entirely? Drop a comment.

Top comments (0)