CLI tool
py-pdf/pdfly avatar
py-pdf/pdfly

pdfly: a pure Python command line for PDF work

CLI tool to extract (meta)data from PDF and manipulate PDF files

573 stars53 forksPythonBSD-3-Clause

At a glance

What is it?
pdfly is a pure Python CLI application for manipulating PDF files, covering text and image extraction, metadata, page removal and rotation, booklet layout, compression, and signing and verification, all driven by a Typer command line with Rich output. It sits on pypdf, and the README is careful to say it has nothing to do with the unrelated pdfly.net and gopdfly.com projects.
Who is it for?
Adopt pdfly if you script PDF handling in Python and want one dependency that covers extraction, page surgery, metadata, signing and verification without shelling out to a separate binary. Do not adopt it if you need a graphical editor or a formatter that rewrites layout, since it is a command line tool with no interactive mode, and check the name collision first, because other projects use pdfly in their domain.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A command line tool, not a library and not an editor

pdfly, which the README says to pronounce as PDF-li, is a pure Python CLI application for manipulating PDF files. The repository description puts it slightly more plainly: a CLI tool to extract meta data from PDF and manipulate PDF files.

That framing settles the expectations. There is no interactive interface, no viewer, and no document editing in the word processor sense. What there is, is a set of composable operations exposed as subcommands, which is exactly the shape you want when the PDF step in a pipeline should be reproducible and inspectable.

The command line is built with Typer and rendered with Rich, and the usage output in the README is a good description of the surface:

console
$ pdfly --help

and the commands themselves are listed with one line of help each, among them 2-up for a booklet-style PDF from a single input, booklet to reorder and two up pages for printing, cat to extract and concatenate pages, check-sign to verify a signature, compress, extract-annotated-pages, extract-images without resampling or altering, extract-links, extract-text, meta, pagemeta for a single page, rm to remove pages, rotate, sign, uncompress, update-offsets and x2pdf, which converts one or more files to PDF with each file becoming a page.

Two of those deserve attention for what they signal about the project. update-offsets updates offsets and lengths in a simple PDF file, and uncompress works on PDF content streams, so this is not only a text extraction utility. It reaches into the file format.

The README also puts in a note that pdfly has nothing to do with pdfly.net or gopdfly.com, which is worth knowing before you search for it, because the name is used by unrelated projects.

The subcommands cover extraction, page surgery, layout and signatures

The command list from the help output reads like a catalogue of the operations people actually script against PDFs:

console
│ 2-up                      Create a booklet-style PDF from a single input.                      │
│ booklet                   Reorder and two-up PDF pages for booklet printing.                   │
│ cat                       Extract and concatenate pages from PDF files into a single PDF file. │
│ check-sign                Verifies the signature of a signed PDF.                              │
│ compress                  Compress a PDF.                                                      │
│ extract-annotated-pages   Extract only the annotated pages from a PDF.                         │
│ extract-images            Extract images from PDF without resampling or altering.              │
│ extract-links             Extract all links from a PDF document.                               │
│ extract-text              Extract text from a PDF file.                                        │
│ meta                      Show metadata of a PDF file                                          │
│ pagemeta                  Give details about a single page.                                    │
│ rm                        Remove pages from PDF files.                                         │
│ rotate                    Rotate specified pages by the specified amount                       │
│ sign                      Creates a signed PDF from an existing PDF file.                      │
│ uncompress                Module for uncompressing PDF content streams.                        │
│ update-offsets            Updates offsets and lengths in a simple PDF file.                    │
│ x2pdf                     Convert one or more files to PDF. Each file is a page.               │

The grouping is useful. Extraction is extract-text, extract-images, extract-links and extract-annotated-pages, and the extract-images description is specific about not resampling or altering, which is the behaviour you want when images carry meaning rather than decoration. Page manipulation is rm, rotate and cat. Layout is 2-up and booklet. Inspection is meta and pagemeta. And then there is the cryptographic pair, sign and check-sign.

The booklet and 2-up commands are the ones that go beyond what a generic PDF library offers, because they reorder pages as well as place two on a sheet, and the subcommand help shows the shape of the interface, with a required PDF argument and a required OUT argument and a usage line that reads python 2-up.py input_file output_file.

Every subcommand has its own help, and the README shows that with pdfly 2-up --help, so the documentation is the tool itself rather than a separate manual.

Signing is the feature that pulls in the cryptography stack

The dependency list in the project metadata tells you how the commands are implemented, and it is worth reading because it explains what each family of commands costs you.

The core is pypdf[full] at 5.1.0 or later, which is the PDF implementation the extraction, metadata and page operations sit on. The command line layer is Typer, the output is rendered with Rich, and Pillow is there for image handling, with pydantic for data validation and fpdf2 for writing PDFs.

The signing commands pull in a different stack entirely: asn1crypto, cryptography, endesive and requests, the last one noted in the metadata as required by endesive.signer. So sign and check-sign are built on endesive, with the cryptographic primitives from cryptography and the ASN.1 handling from asn1crypto, and requests is there to satisfy the signer.

That is a fair amount of dependency for two subcommands, and it is the price of doing digital signatures properly in Python rather than shelling out to an external tool. The counter-argument is that it happens in process, in the same environment as the rest of your pipeline, with no subprocess and no separate signing service to operate.

The license story is simple. The project is BSD-3-Clause, the license file is in the repository, and the metadata declares it with the license-files entry pointing at LICENSE, so the terms are the permissive ones with the usual notice requirement and no copyleft.

Installation: pip, pipx or uvx

The installation section is one line:

bash
pip install -U pdfly

The README then adds the advice that matters for a command line application rather than a library. Because pdfly is an application, you might want to install it with pipx or uv tool, and the example given is:

bash
uvx pdfly --help

That is a sensible distinction that people routinely get wrong. Installing a CLI with pip into a project environment puts its dependencies, including Pillow, pydantic, cryptography and asn1crypto, into the same site-packages as your application, and then a CLI upgrade can move a version your code depends on. An isolated tool environment avoids that.

The uvx form goes further, since it downloads and runs the tool in a throwaway environment without installing anything into your system at all, which is the right model for a one-off conversion in a CI job.

The runtime requirement is Python 3.10.0 or later, and the classifiers list Python 3.10 through 3.14, so the project is tracking current Python releases. The documentation is on readthedocs with a badge for the documentation status, and the homepage recorded in the repository metadata is the same site.

flit, a Makefile and a version file

The packaging tells you how the project is built and released, and it uses two different tools for the two jobs.

The pyproject.toml declares the build backend as flit_core with a requirement of flit_core 3.2 or later, and the project name is pdfly with a dynamic version and readme. The version is read from a file rather than typed into the metadata, and the Makefile shows the publishing path: the upload target runs make clean and then flit publish, while the release target runs python make_release.py and commits with a generated commit message.

Alongside that, a setup.py is still present, and it does something specific: it opens pdfly/_version.py, matches the version string out of it with a regular expression, and raises a RuntimeError if it cannot find one. So the version has a single home in the package, and the legacy setuptools entry point reads it from there.

The rest of the Makefile is a maintainer's toolkit. The maint target updates the pre-commit hooks, upgrades the installed package, regenerates the pip lock file for the dev and docs groups, installs from pylock.toml with uv, and updates the git submodules. The lint target runs mypy over the package and tests and then ruff with fixes, and the test target runs pytest with coverage, an HTML report, the three slowest durations and a 30 second timeout per test.

A lock file per dependency group, uv, ruff, mypy, pre-commit, dependabot and renovate all present at the root, and a .typos.toml for spell checking, is a well instrumented project. The dev group in the metadata is correspondingly long, with black, flake8 and a set of flake8 plugins, mypy, pytest with a socket plugin, ruff and sphinx for the docs group.

A 0.5 line with a Planning classifier and a busy contributor list

The release history is short and recent. Version 0.5.1 came on 2025-10-13, version 0.5.0 on the same day, and that release is titled for what it added, new sign, check-sign, rotate and extract-annotated-pages commands. Version 0.4.0 is from 2024-12-08, and the last push to the repository was on 2026-10-07.

So the two cryptographic commands, sign and check-sign, are recent additions, which is consistent with the dependency set and means the signing path is the least battle tested part of the tool.

The metadata carries a Development Status classifier of 1 - Planning, which sits oddly next to a project with a release history, a documentation site and a long contributor table. The README is candid in a different register, saying that pdfly is a free software project without any company affiliation, that the project cannot pay contributors but does value their contributions.

The contributor list is the other notable thing about the repository, and the maintenance roles are assigned by name: code, documentation, ideas, infrastructure, maintenance, project management and tutorials are separate roles in the all-contributors table. That is a level of process a small tool project rarely bothers with, and it is consistent with a project that treats contributions as the scarce resource.

The repository layout is conventional and tidy: the pdfly package, docs, tests, resources and sample-files directories, a Makefile, a make_release.py, mypy.ini, pylock.toml, dependabot.yml, renovate.json, a CHANGELOG.md and a CONTRIBUTORS.md, with a .all-contributorsrc at the root.

Editorial conclusion

Adopt pdfly if you script PDF handling in Python and want one dependency that covers extraction, page surgery, metadata, signing and verification without shelling out to a separate binary. Do not adopt it if you need a graphical editor or a formatter that rewrites layout, since it is a command line tool with no interactive mode, and check the name collision first, because other projects use pdfly in their domain. Verify first which Python you are on, since the package requires 3.10 or later, that the operation you need is in the command list, and that your PDFs are handled by the pypdf release you install, because the extraction and page commands sit on top of that library rather than on a PDF implementation of its own.

Frequently asked questions

What is pdfly?

pdfly is a pure Python CLI application for manipulating PDF files, installed from PyPI as pdfly. It covers text, image, link and annotation extraction, metadata, page removal and rotation, booklet layout, compression, and signing and verification of signed PDFs.

How do I install pdfly without polluting my project environment?

The README notes that because pdfly is an application, you may want to install it with pipx or uv tool instead of pip, and gives uvx pdfly --help as the example, which downloads and runs the tool in a throwaway environment. A plain pip install -U pdfly works too, and Python 3.10 or later is required.

Which library does pdfly use for reading and writing PDFs?

The project metadata lists pypdf[full] at 5.1.0 or later as a dependency, alongside Typer and Rich for the command line, Pillow for images, pydantic for validation and fpdf2 for writing. Signing and verification come from endesive, with cryptography and asn1crypto behind it.

Does pdfly have anything to do with other projects named pdfly?

No. The README states that pdfly has nothing to do with pdfly.net or gopdfly.com, and it is worth checking that before searching for the tool, since the name is used by unrelated projects. The project itself is BSD-3-Clause licensed and has no company affiliation.

Official sources

  1. License: BSD-3-Clause
  2. Project website
  3. py-pdf/pdfly on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/py-pdf-pdfly.svg)](https://hysenlabs.com/projects/py-pdf-pdfly)

Was this useful?