Pagecraft

The command line

pagecraft in a shell script or a CI job, with the same engine and no upload.

pagecraft is the engine with a different front door. Same operations, same options, same guarantee: it opens no network connection, and there is nowhere for a file to go.

npx pagecraft merge cover.pdf body.pdf --output report.pdf

Or install it, if you are going to use it more than once:

Not on npm yet. Until it is, build it from source:

git clone https://github.com/amanawasthi2025/pagecraft
cd pagecraft
pnpm install && pnpm build
node packages/cli/dist/bin.js --help

The shape of a command

pagecraft <operation> [files…] [options]
pagecraft recipe <recipe.json> [files…] [options]
pagecraft <operation> --help

Every subcommand comes from the registry, and every flag comes from that operation’s own options schema — so the help below is written by the operations themselves rather than maintained beside them. What pagecraft --help prints on this build is what this build can do.

Files in

Name them, match them with a pattern, or point at a directory:

pagecraft rotate report.pdf --angle 90
pagecraft rotate 'scans/*.pdf' --angle 90
pagecraft rotate scans/ --angle 90

Quote the pattern. Otherwise your shell expands it first, which usually works and occasionally produces a command line longer than the kernel will take.

Given no files at all, the file is read from standard input — but tell it what it is, because a pipe carries no name and the name is how the format is known:

cat report.pdf | pagecraft compress --name report.pdf --output -

An operation that takes one file at a time is applied to each in turn, so a pattern matching forty invoices is forty runs and forty files.

Files out

By default, into the working directory, under a name derived from the input: report.pdf compressed is report-compressed.pdf, so an operation does not normally land on top of the file it read.

pagecraft compress report.pdf                      # ./report-compressed.pdf
pagecraft compress report.pdf --output out/        # out/report-compressed.pdf
pagecraft compress report.pdf --output small.pdf   # ./small.pdf
pagecraft compress report.pdf --output -           # standard output

--output decides which of those by looking at what is already there: a path that exists is whatever it is, and a path that does not is a filename if it has an extension and a directory otherwise. - is standard output.

An operation that produces several files needs a directory. Pointed at one filename or at standard output, it stops and says so — That produced 4 files, and standard output takes one. — rather than picking one or inventing an archive. (pagecraft recipe is the exception, and writes a zip: see Recipes.)

A path you name is written to as you named it. There is no prompt and no .bak: pagecraft compress report.pdf --output report.pdf replaces the file it just read. That is the behaviour a script wants, and it is the one to remember before you run something over a directory you care about.

Options are the schema, dashed

A field an operation calls imageQuality is --image-quality. A boolean that is off is turned on by naming it; a boolean that is already on is turned off with --no-. A field that takes several values is given them separated by commas. A list of objects — areas on a page, bookmarks — is JSON, or @ and a file holding it, because nobody types an area at a shell.

pagecraft compress big.pdf --mode quality --image-quality 60
pagecraft extract-text report.pdf --no-dehyphenate
pagecraft protect payroll.pdf --user-password 'correct horse battery staple' --allow print,accessibility
pagecraft bookmarks report.pdf --entries @outline.json

--help on an operation lists all of it, with defaults, with what each option is for, and with an honest list of what the operation cannot do:

pagecraft redact --help

The flags the command line owns

Flag What it does
-o, --output <path> A directory, a filename, or - for standard output
--name <filename> What to call the file read from standard input
--open-password <password> The password that opens the input files
-q, --quiet No progress and no summary
-h, --help What this command takes
-v, --version Which Pagecraft this is

An operation’s own names win. unlock declares a password option, and no global flag may take that spelling from it — a global that quietly shadowed a field would make an operation unusable from a terminal with nothing the user could type to get it back.

Passwords

Two different things, and it matters which you mean:

pagecraft merge locked.pdf other.pdf --open-password hunter2   # to read the input
pagecraft unlock locked.pdf --password hunter2                 # to take it off

Either way, the password is an argument, and on a shared machine an argument is visible in the process list for as long as the command runs. There is no environment variable and no prompt: that is a real limitation rather than an oversight, and the honest advice is not to type a password on a machine where somebody else can read ps.

Exit codes, for a script that has to branch

Code What it means
0 It worked
1 Something failed while running: a file would not open, a step could not finish
2 The command was wrong: an unknown operation, a bad option, a file that is not there
130 Interrupted

Progress and the summary of what was written go to standard error, so standard output is only ever the file you asked for. That is what makes this safe:

pagecraft compress report.pdf --output - > small.pdf

In CI

There is nothing to configure, no key to set and no service to reach. A job that has Node has everything:

- run: npx pagecraft recipe .github/tidy.json 'docs/*.pdf' --output out/

Because it makes no network calls, it also works on a runner with egress blocked — which is the point, if the documents are the reason egress is blocked.

What it cannot do

  • OCR needs a recognizer. The command line will tell you so rather than quietly producing a document with no text in it.
  • Rasterizing needs a canvas. @napi-rs/canvas is an optional dependency and is installed for you on the common platforms; on one where it is not, the operations that draw pages say what is missing.
  • A 200MB document needs the memory a 200MB document needs. The engine streams what it can, and there is a size past which the honest answer is a bigger machine.

Where to go next

  • Recipes — several operations in one pass, over many files.
  • Getting started — the same engine from code.
  • The reference — every operation, with the command line for its example.