Local PDF is a private, native macOS and iOS utility for reducing camera-scan PDFs without sending them to a compression service. It always keeps the original and creates a clearly named compressed copy.
Everything runs on the device. There is no account, analytics SDK, network service, or upload path.
- macOS app: SwiftUI window and menu-bar controls, drag-and-drop, and multi-file selection.
- Finder: right-click a PDF, then choose Quick Actions → Compress PDF Locally.
- CLI: agent-friendly
local-pdfcommands with a stable JSON contract. - iPhone and iPad: import from Files and export through the system Save picker.
- Files Action: select a PDF, choose Share → Compress with Local PDF, compress, and save the copy.
- Shortcuts: a non-opening Compress PDF action that accepts and returns a PDF file.
- Presets: High Quality, Balanced, and Smallest, with Balanced as the default.
Balanced uses PDFKit's public image-optimization options and preserves searchable text and document structure where PDFKit supports it. Smallest rasterizes each page for strict upload limits and can flatten OCR, links, forms, annotations, accessibility data, and signatures. The original is always retained. Rewriting any digitally signed PDF can invalidate its signature.
- macOS 14 or later
- iOS or iPadOS 17 or later for the mobile companion
- Xcode 26.6 or a compatible Swift 6.2 toolchain to build from source
- XcodeGen 2.45.4 to regenerate the mobile project
The user-scoped installer places files here:
~/Applications/Local PDF.app
~/Library/Services/Compress PDF Locally.workflow
~/Library/Application Support/LocalPDF/bin/local-pdf
If the Quick Action is hidden, enable it in System Settings → General → Login Items & Extensions → Finder. The app’s Finder Settings button opens that settings page.
"$HOME/Library/Application Support/LocalPDF/bin/local-pdf" \
compress --preset balanced --json -- "/path/to/private scan.pdf"The -- protects paths beginning with a dash. Multiple PDF paths may be supplied and are processed sequentially to keep memory use bounded.
Available commands:
local-pdf compress [--preset balanced|high-quality|smallest] [--json] -- PDF...
local-pdf presets [--json]
local-pdf versionJSON mode emits one newline-terminated object containing schemaVersion, ok, results, and failures. Exit status is 0 when every file succeeds, 1 when any file cannot be compressed, and 64 for invalid command syntax.
swift test
./script/build_and_run.sh --verify
./script/install.shThe Codex Run action is configured in .codex/environments/environment.toml. script/package_app.sh release creates the local ad-hoc-signed bundle in dist/Local PDF.app. Developer ID signing and notarization are not required for this user-local build, but would be required before distributing it to other Macs without Gatekeeper warnings.
The mobile companion requires iOS 17 or later. It offers three local workflows:
- In Files, touch and hold a PDF, choose Share, then choose Compress with Local PDF. Pick a preset, compress, and choose where to save the copy.
- Open Local PDF, tap Choose PDF, compress it, and use Save Compressed PDF.
- In Shortcuts, add Local PDF's Compress PDF action. It accepts one PDF and returns the compressed file for a following Save File, Share, or other action.
iOS asks you where to save exported documents. A third-party app cannot silently create a sibling beside an arbitrary iCloud Drive or external file-provider document, so the mobile app uses the system Save picker instead of pretending it has broader Files access.
Install XcodeGen once if needed, generate the project, then open it in Xcode:
brew install xcodegen
./script/generate_mobile_project.sh
open "Mobile/LocalPDFMobile.xcodeproj"In Xcode, select your Apple development team for both LocalPDFMobile and LocalPDFAction, connect your iPhone or iPad, choose it as the run destination, and press Run. Launch Local PDF once after installation so iOS can index its Shortcuts action. If the default bundle identifier is unavailable to your team, generate a project with your own reverse-DNS identifier:
LOCAL_PDF_IOS_BUNDLE_ID=com.example.localpdf \
./script/generate_mobile_project.shThe Action extension automatically uses the same identifier with .action appended. If the Files action is not initially visible, open the Share sheet's action editor and enable Compress with Local PDF.
For an unsigned Simulator build from the command line:
./script/mobile_build.shThe generated project is Mobile/LocalPDFMobile.xcodeproj; Mobile/project.yml is its source of truth.
LocalPDFCore contains presets, PDFKit/ImageIO compression, collision-safe
output naming, cancellation, and structured results. The macOS app, CLI, iOS
app, Files Action extension, and App Intent are thin native surfaces around the
same local core.
The strongest preset bounds each page's raster allocation and processes one page at a time. Mobile imports are copied through security-scoped access into a private temporary staging directory; only the explicitly exported copy leaves that directory.
Contributions are welcome. Read CONTRIBUTING.md before opening a pull request. Report privacy or security problems privately as described in SECURITY.md, and never attach a real confidential document to a public issue.
Local PDF is available under the MIT License.