A native macOS workspace for turning scattered documents into one organized PDF workflow.
Import, arrange, annotate, sign, search, save, and export without sending your files anywhere.
β‘Β Setup Β Β Β β¨Β Features Β Β Β ποΈΒ Architecture Β Β Β π’Β Release Β Β Β β Β Quality Β Β Β π§°Β Help
Paste this into Terminal:
curl -fsSL https://raw.githubusercontent.com/udhawan97/PDFold/main/install.sh | zshThe installer downloads the latest prebuilt app, places it in ~/Applications/PDFold.app, and adds Desktop commands for launching/updating and clean uninstall.
No Xcode. No GitHub account. No compile step. Just paste, install, and get back to the documents.
How to open Terminal
Open Applications -> Utilities -> Terminal, paste the command above, and press Return.
PDFold is a local-first Mac app for the moment when "just send me the PDF" turns into six PDFs, two screenshots, a Word document, a form, and one file named final_final_revised_ACTUAL.pdf.
It gives those files a calm place to live. Drop in documents, combine them into one workspace, reorder pages, add notes and markup, place signatures, search the full set, save your project, and export the result in the format you need.
Everything happens on your Mac. No account, no upload queue, and no mystery cloud detour.
| Signal | Why It Matters | |
|---|---|---|
| π₯οΈ | Native macOS | Built with SwiftUI, PDFKit, document-based app architecture, and sandboxed file access |
| π₯ | Broad import support | Handles PDFs, Word documents, HTML, Markdown, text files, structured data, and images |
| π§ | Real workflow tools | Combine, reorder, rotate, delete, annotate, tag, comment, sign, search, save, print, and export |
| π | Local-first privacy | Files stay on the user's Mac instead of being uploaded to a service |
| β‘ | Simple installation | One pasted command installs the prebuilt app and creates Desktop launch/update and uninstall commands |
| π§± | Release-minded engineering | Includes installer automation, crash hardening, import safety, export checks, and CI coverage |
| Audience | What to Notice | |
|---|---|---|
| π | People with document chaos | Pull scattered files into one workspace, clean them up, and export something usable |
| π§βπΌ | Recruiters and hiring teams | A polished desktop product with clear user empathy, practical scope, and visible product judgment |
| π§βπ» | Engineers | SwiftUI, PDFKit, PDFium-backed validation, file conversion, document persistence, undo-aware page operations, export pipelines, sandboxing, and installer automation |
| Capability | Details | |
|---|---|---|
| π₯ | Import | PDFs, Word docs, HTML, RTF, Markdown, plain text, CSV, JSON, XML, and common image formats |
| ποΈ | Organize | Combine files, reorder source documents, move pages, rotate pages, delete pages, and keep navigation aligned |
| π | Read | Native PDF canvas, generated section banners, table of contents, sidebar navigation, inspector views, and workspace search |
| βοΈ | Mark up | Highlight, note, editable text overlay, ink, underline, strikeout, and drawn signature placement |
| π·οΈ | Track | Workspace tags, workspace comments, document metadata, and an inspector list for reviewing annotations |
| πΎ | Save | Persistent .pdfoldproj workspaces with metadata, comments, tags, signatures, page order, and source PDF data |
| π€ | Export | PDF, Word .docx, Markdown .md, plain text, HTML, PNG pages, JPEG pages, or print-ready output |
| π | Unlock | Password-protected PDF prompt using native PDFKit behavior |
| π‘οΈ | Protect | Local-first design with sandboxed file access, local PDF validation, and no document upload pipeline |
Bring in messy inputs, work with them as one local workspace, and export a clean result.
flowchart LR
Files["Source files<br/>PDF, Word, HTML, Markdown, text, images"]
UI["SwiftUI app<br/>sidebar, reader, inspector"]
State["Workspace state<br/>documents, pages, tags, comments"]
Engine["Processing backend<br/>PDFium validate, PDFKit compose"]
LocalState["Local document state<br/>metadata and PDF data"]
Export["Export artifacts<br/>PDF, DOCX, MD, TXT, HTML, PNG, JPG"]
Files --> UI
UI --> State
State --> Engine
Engine <--> LocalState
Engine --> Export
SwiftUI handles the workspace experience, PDFKit powers document composition and annotation, and local persistence keeps projects editable.
| Layer | Responsibility | |
|---|---|---|
| π₯οΈ | SwiftUI app | Presents the document workspace, sidebar, reader, annotation tools, search, inspector, and export actions |
| βοΈ | Document engine | Converts imports, validates PDFs through the processing backend, composes PDFs, manages page state, preserves annotations, and writes export formats |
| πΎ | Local storage | Saves workspace metadata, page order, source PDF data, comments, tags, signatures, and generated output |
Most PDF tools live at one of two extremes: too tiny to handle a real workflow, or so large that opening them feels like clocking in for a shift. PDFold aims for the useful middle.
It is built for document assembly as a workflow, not just PDF viewing as a feature. The goal is simple: take the pile, make sense of it, mark what matters, and send out one clean artifact.
For reviewers, the interesting part is not just that PDFold works. It is that the app ties together product thinking, native Mac engineering, file handling, persistence, export reliability, and distribution polish into one coherent project.
PDFold v3 is a release-hardened local-first macOS app for collecting scattered documents, turning them into one editable workspace, marking them up, tracking context, and exporting clean deliverables.
| Detail | Status | |
|---|---|---|
| π’ | Version | 3.0 |
| π§Ύ | App metadata | CFBundleShortVersionString 3.0, CFBundleVersion 3 |
| β‘ | Install path | One-line installer downloads the latest prebuilt GitHub release |
| π§ͺ | Smoke test | ./scripts/install-mac.sh --no-open |
| π | Signing | Local ad-hoc signing for source and release packaging |
| π¦ | Distribution style | Prebuilt release zip, with source build fallback for developers |
| Area | Release Update | |
|---|---|---|
| π | Automatic updates | The Desktop PDFold.command launcher checks the latest GitHub release every time it opens the app |
| π§Ή | Clean uninstall | The installer now creates Uninstall PDFold.command for removing the app, generated commands, installer cache, and PDFold app data |
| π§ͺ | PDF processing backend | PDF imports pass through an injectable PDFProcessingEngine, with PDFium validation and a PDFKit fallback path |
| π§ | Simpler setup | The old separate update command is treated as a legacy artifact and cleaned up by install/update/uninstall scripts |
| π | Release docs | README setup, update, uninstall, quality, and troubleshooting guidance now match the v3 install flow |
| Area | Release Hardening | |
|---|---|---|
| π·οΈ | Workspace context | Tags and workspace comments persist with saved projects and appear in dedicated inspector tabs |
| βοΈ | Text editing | The text tool can create clean free-text boxes or convert selected PDF text into editable overlays |
| π | Markdown export | Workspaces export .md files with a summary, comments, document sections, and extracted PDF text |
| ποΈ | Ink stability | Ink annotations use PDFKit-native paths, with malformed legacy ink data sanitized before display |
| π‘οΈ | Import safety | Import failures show actionable messages, oversized files are rejected early, and selected files use security-scoped access |
| π | Protected PDFs | Password-protected documents unlock from the already-loaded PDF instance for safer sandbox behavior |
| β©οΈ | Undo reliability | Page deletion and reordering undo restore serialized PDF state, not just sidebar metadata |
| ποΈ | Page order | Reordered pages rebuild the workspace page map so navigation, export, signatures, and saved projects stay aligned |
| π€ | Export reliability | PDF and multi-format exports report write failures instead of failing quietly |
| π§― | Crash hardening | Reordering, page operations, PDF serialization, HTML rendering, image export, and signature storage guard failure cases |
Paste one command into Terminal:
curl -fsSL https://raw.githubusercontent.com/udhawan97/PDFold/main/install.sh | zshThe installer downloads the latest prebuilt PDFold.zip from GitHub Releases, installs PDFold.app to ~/Applications, creates self-updating launch and uninstall Desktop commands, removes download quarantine metadata, and opens the app.
The normal path does not require Xcode, Apple's Command Line Tools, a package manager, or a GitHub account. The installer is intentionally uneventful, which is exactly how installers should behave.
Important release note: the zero-compile installer depends on a published GitHub release containing PDFold.zip. If no prebuilt release is available, the installer falls back to a source build and macOS may ask for Apple's free Command Line Tools.
For detailed install diagnostics, run the same command with verbose logging enabled:
PDFOLD_INSTALL_VERBOSE=1 curl -fsSL https://raw.githubusercontent.com/udhawan97/PDFold/main/install.sh | zshDeveloper source install
git clone https://github.com/udhawan97/PDFold.git
cd PDFold
./scripts/install-mac.shDouble-click PDFold.command on the Desktop. The launcher checks for the latest release, refreshes the installed app when needed, and then opens PDFold.
You can also paste the installer command again:
curl -fsSL https://raw.githubusercontent.com/udhawan97/PDFold/main/install.sh | zshIf PDFold is already installed, the updater closes the running app if needed, replaces ~/Applications/PDFold.app, refreshes the Desktop launcher, removes quarantine metadata, and opens the updated app.
Double-click Uninstall PDFold.command on the Desktop to remove the installed app, generated Desktop commands, installer cache, and PDFold app data.
Saved .pdfoldproj workspace files are not removed. To keep app support, preferences, caches, and sandbox data too, run:
curl -fsSL https://raw.githubusercontent.com/udhawan97/PDFold/main/scripts/uninstall-mac.sh | zsh -s -- --keep-user-dataDeveloper source update
git pull
./scripts/install-mac.shUseful terminal options:
./scripts/install-mac.sh --clean
./scripts/install-mac.sh --no-open
./scripts/install-mac.sh --verbose
./scripts/install-mac.sh --helpThe local script can install a release build, package a release zip, or build from the current source checkout.
| Requirement | Version | |
|---|---|---|
| π | macOS | 14 Sonoma or newer |
| π¦ | Normal install | Published PDFold.zip release |
| π§° | Source build fallback | Apple Command Line Tools with Swift 5.9+ |
Why might Command Line Tools appear?
The normal installer downloads a prebuilt .app. If no release artifact is available, PDFold falls back to a source build with SwiftPM, which requires Apple's free Command Line Tools. Full Xcode is not required.
- Launch PDFold.
- Drag in PDFs, Word documents, text files, web exports, data files, or images.
- Reorder documents and pages until the workspace matches the story you need to tell.
- Highlight, annotate, add notes, tag the workspace, capture comments, or place a drawn signature.
- Search across the combined document set when the one detail you need is hiding on page 37.
- Save the workspace if you want to keep editing later.
- Export a PDF, Word document, Markdown file, text file, HTML file, or page images.
PDFold/
App/ App entry point and command wiring
Document/ macOS document package read/write support
Engine/ PDF loading, conversion, composition, manifests, export helpers
Models/ Workspace, page, annotation, comment, and signature data models
Resources/ App metadata, entitlements, and asset catalogs
ViewModels/ Workspace state, document operations, search, export, undo
Views/ SwiftUI interface components
scripts/
install-mac.command Compatibility double-click installer
install-mac.sh Release-first installer, source builder, and release packager
uninstall-mac.sh Clean uninstaller for installed app artifacts and PDFold app data
install.sh
Hosted one-line bootstrap
Install or Update PDFold.app
Finder installer/updater that bypasses Terminal shell startup
Install or Update PDFold.command
Compatibility Terminal installer/updater
Uninstall PDFold.command
Compatibility Terminal uninstaller
Open the project in Xcode:
open PDFold.xcodeprojBuild with SwiftPM:
swift buildCreate the same release zip used by GitHub Releases:
./scripts/install-mac.sh --package-only --package /tmp/PDFold.zipInstall from the current source checkout without opening the app:
./scripts/install-mac.sh --no-openPDFold is local-first by design. Documents are opened, edited, saved, and exported on your machine.
The app uses macOS sandboxing and file access through user-selected documents. Its new PDF processing backend runs locally for import validation; it is not a remote upload service. In plain English: PDFold works with the files you hand it, not your entire digital attic.
Release v3 also includes practical guardrails around failure-prone paths:
- A supplemental PDFium processing backend performs a non-blocking validation smoke check before PDFKit proceeds with the normal import path.
- Files larger than 512 MB are rejected before loading to avoid memory pressure from accidental giant imports.
- PDF serialization failures preserve existing package data or report an actionable import error instead of writing broken workspace state.
- Malformed legacy ink annotations are rebuilt before display so PDFKit does not crash while drawing them.
- Markdown exports include workspace metadata, comments, document headings, and extracted document text.
- HTML rendering, image export, page operations, and signature storage guard invalid or unavailable state.
- Export failures are surfaced to the user, including failed writes and image-rendering errors.
Sandbox details
The app enables:
com.apple.security.app-sandboxcom.apple.security.files.user-selected.read-write
These entitlements allow sandboxed read/write access to files selected by the user.
Before shipping a build, verify both the developer path and the human-with-documents path.
| Check | What To Verify | |
|---|---|---|
| β | Build | swift build completes |
| π§ͺ | Installer smoke test | ./scripts/install-mac.sh --no-open installs the release or builds, signs, installs, and refreshes Desktop commands |
| π¦ | Release package | ./scripts/install-mac.sh --package-only --package /tmp/PDFold.zip creates the release artifact |
| π₯ | Import | Drag-and-drop works with supported file types |
| π§ͺ | Processing backend | PDFium validation runs when available, and PDFKit fallback remains usable |
| π | Protected PDFs | Password-protected PDFs show the unlock flow |
| πΎ | Persistence | Saved workspaces reopen with metadata, markup, comments, signatures, and document data intact |
| π | Search | Search results navigate across the combined workspace |
| π·οΈ | Inspector | Tags, comments, info, and markup tabs reflect workspace state |
| βοΈ | Annotation | Highlight, note, editable text, ink, underline, strikeout, and undo behavior work |
| ποΈ | Pages | Page rotation, deletion, and reordering stay aligned with navigation and export |
| π€ | Export | PDF, Word, Markdown, text, HTML, PNG, and JPEG exports complete successfully |
| π | Launch/update | Desktop launcher updates and opens the installed app |
| π§Ή | Uninstall | ./scripts/uninstall-mac.sh --help prints usage and the Desktop uninstaller removes install artifacts |
For v3 release preparation, the local verification pass should include:
plutil -lint PDFold/Resources/Info.plist
plutil -lint PDFold/Resources/PDFold.entitlements
zsh -n install.sh
zsh -n scripts/install-mac.sh
zsh -n scripts/uninstall-mac.sh
zsh -n scripts/install-mac.command
zsh -n "Install or Update PDFold.command"
zsh -n "Uninstall PDFold.command"
plutil -lint "Install or Update PDFold.app/Contents/Info.plist"
swift build
./scripts/install-mac.sh --package-only --package /tmp/PDFold.zip- Richer signature management.
- More export presets.
- Improved document thumbnails and faster page navigation.
- Automated UI smoke tests.
- Developer ID signing and notarized release builds for the smoothest possible macOS install path.
Contributions are welcome when they keep the product focused and the workflow calm.
- Create a focused branch.
- Keep changes scoped.
- Run the local verification checks.
- Include screenshots or notes for UI changes.
- Open a pull request with the problem, approach, and verification steps.
Clicking the installer opens a GitHub page instead of installing
That is expected on GitHub. README links open files in the browser; they do not execute Mac installer scripts.
Use the Quick Start command from Terminal, or download the repository first and run the installer from Finder.
Double-clicking the installer says it cannot be opened
Open Terminal in the project folder and run:
chmod +x "Install or Update PDFold.command" "Uninstall PDFold.command" "Install or Update PDFold.app/Contents/MacOS/PDFoldInstaller" scripts/install-mac.sh scripts/install-mac.command scripts/uninstall-mac.shThen double-click Install or Update PDFold.app again from Finder.
The installer says Command Line Tools are needed
The normal installer uses a prebuilt app and does not need developer tools. This message means a prebuilt release was not available, so the installer fell back to a source build.
Install Apple's free Command Line Tools from the macOS prompt, then run the installer again. Full Xcode is not required.
The Desktop launcher does not open the app
Run the installer again. It refreshes ~/Applications/PDFold.app and recreates the Desktop launcher.
For more detail, run the installer with verbose logging:
PDFOLD_INSTALL_VERBOSE=1 curl -fsSL https://raw.githubusercontent.com/udhawan97/PDFold/main/install.sh | zshmacOS warns the app is from an unidentified developer
PDFold release builds are ad-hoc signed but not notarized. The installer removes download quarantine from the installed app. If macOS still warns, open it from Finder, then use Open from the security prompt.
Fully silent Gatekeeper behavior requires Apple Developer ID signing and notarization, which is listed on the roadmap.
The app did not update
Double-click PDFold.command on the Desktop, or paste the Quick Start command again.
For a fully fresh developer source build:
./scripts/install-mac.sh --cleanThe build fails and the terminal window closes too fast
Open .build/install.log in the project folder. It contains the latest installer/build output.
When using the README install command, the prebuilt release attempt is also logged at ~/.pdfold/prebuilt-install.log. If PDFold opens and immediately exits, the installer records recent macOS launch diagnostics in the install log before failing.
You can also run the installer from Terminal to keep the output visible:
./scripts/install-mac.sh --verbosePDFold is available under the MIT License.