Why our PDF pipeline avoids Ghostscript and MuPDF
Ghostscript and MuPDF are AGPL-licensed. Here is what that license asks of a hosted API, and how our build blocks AGPL and GPL dependencies.
Ghostscript and MuPDF are two of the most widely used open source PDF engines, and both are licensed under the AGPL. Our compression API uses neither. This post explains what that license asks of a hosted service, and how our build enforces the rule.
Anyone who searches for a PDF compression engine finds these two quickly. Both can shrink a file, and both can be downloaded for free. The license is the reason we do not run them. If you run your own PDF pipeline, the same question applies to you, and it is easy to miss.
We are not lawyers. This is our reading of the license and how we apply it. If your company builds on these tools, ask your own counsel.
Two engines, one license
Ghostscript is available under a dual license: the GNU Affero General Public License, or a commercial license. The Ghostscript licensing page says that licensing is handled exclusively by Artifex Software.
MuPDF is licensed under AGPLv3, as its licensing page states. That page refers readers to Artifex's licensing page for other terms.
Artifex is behind both licensing pages we checked. So for either tool, the choice is the same: accept the AGPL, or buy a commercial license.
What the AGPL asks of a hosted service
The AGPL is a copyleft license. If you distribute a program under it, you must give the recipients its source under the same terms. Most developers know that part.
The part that matters for an API is section 13 of the AGPL text. It covers software that you modify and then let users interact with over a network. In that case, you must offer those users the source of your modified version.
Two points follow from that wording:
- The duty is tied to modification. If you run an unmodified copy behind an endpoint, section 13 does not clearly apply, and readers of the license disagree about what else it requires.
- Changes bring the duty into play. Once you patch the engine, for example to fix a bug or to tune a profile, every user who reaches that version over the network has the right to its source.
For a paid API, the second point is the one that matters. A customer calls an endpoint. Behind it sits a modified copy of a copyleft engine. Answering the source question for every patch, for every customer, is a cost we do not want to carry.
Why we decided not to use them
Three reasons, all from our own rules:
- We run a hosted API. Customers reach our code over the network. Once we modify an AGPL engine, section 13 applies to that setup.
- Our service code is private. Our rules do not publish the source of the service. An AGPL component inside it would put that rule at risk.
- Free and permissive options come first. A commercial license from Artifex is a real option. It is also a recurring cost, and our rule is to use free and permissive tools before we add any cost.
Libraries under permissive or weak-copyleft licenses cover the steps we need. Each step is measured before it ships. The check does not wave weak-copyleft packages through silently: it lists each LGPL package in the production tree so that someone reviews it. That is slower to set up than calling one engine, but the license question stays visible.
How the build enforces it
A rule that exists only on paper breaks the first time someone adds a package. The repository runs a license check on its installed Node dependencies as part of npm run check. The script is scripts/check-licenses.mjs. Our internal rules say that a failure blocks the merge.
The script does two things that a plain search for "GPL" does not do:
- It reads the installed tree. It inventories what is physically in
node_modules, not the list in each manifest. What is installed is what can end up in a bundle. - It evaluates SPDX expressions. A string like
(MIT OR GPL-2.0)offers a choice, and the MIT branch can be taken, so it passes.Apache-2.0 AND LGPL-3.0-or-laterapplies both licenses, so the stricter one governs.LGPL-3.0-or-latercontains the letters "GPL", but it is a weaker copyleft and is not on the prohibited list.
A license that the script cannot determine also fails the check. An unverified license might be GPL, and a check that lets it through is not doing its job.
What to check in your own pipeline
A transitive dependency can bring an AGPL package in without anyone choosing it. That is why the check should run on every build, not only at review time. A short routine:
- List the licenses of the full installed tree. Your package manager or a license report tool can produce this. Do not stop at the packages you named yourself.
- Search for AGPL, GPL, and anything you cannot identify.
- Treat an unknown license as a failure until someone has read it.
- Run the check in CI, so a new indirect dependency cannot pass unnoticed.
Our engineering notes list the permissive libraries the PDF stack is built on. Among them are qpdf (Apache-2.0), pikepdf (MPL-2.0), and mozjpeg (BSD-3-Clause and IJG). Check your own list against the same kind of table. A name that looks familiar is not a license.
What this means for you as a customer
You do not need to know which engines we use to call the API. The license decision does not change how you call it. The questions that matter for your files are different: what the output looks like, and what the compression removes. For those, read what PDF compression strips by default and how to analyze a PDF before you compress it.
The request format and every option are in the PDF optimization API reference. The product overview is on the PDF compression API page.
Summary
- Ghostscript is dual-licensed under the AGPL and a commercial license from Artifex. MuPDF is AGPLv3.
- Section 13 of the AGPL matters for hosted services once the code is modified and users interact with it over a network.
- Our rule is that the service runs no AGPL or GPL code. The automated check covers the installed Node dependencies and runs on every
npm run check. It fails on any prohibited or unidentified license. - If you run your own pipeline, check the full dependency tree, not only the packages you chose.
Next, read why JBIG2 can turn a 6 into an 8, and why our default is lossless. That post covers a different risk, with no license question involved: a compression mode that changes what a scanned page says.