nowo-tech / pdf-editor-bundle
Symfony bundle with an isolated, access-controlled PDF editor workspace: edit text, form fields, watermarks, pages and annotations in the browser, then download the result.
Package info
github.com/nowo-tech/PdfEditorBundle
Language:JavaScript
Type:symfony-bundle
pkg:composer/nowo-tech/pdf-editor-bundle
Requires
- php: >=8.2 <8.6
- nowo-tech/form-kit-bundle: ^2.16
- nowo-tech/ui-kit-bundle: ^2.17.3
- psr/clock: ^1.0
- psr/log: ^1.1 || ^2.0 || ^3.0
- symfony/asset: ^7.4 || ^8.0
- symfony/config: ^7.4 || ^8.0
- symfony/dependency-injection: ^7.4 || ^8.0
- symfony/deprecation-contracts: ^3.0
- symfony/form: ^7.4 || ^8.0
- symfony/framework-bundle: ^7.4 || ^8.0
- symfony/http-foundation: ^7.4 || ^8.0
- symfony/http-kernel: ^7.4 || ^8.0
- symfony/options-resolver: ^7.4 || ^8.0
- symfony/process: ^7.4 || ^8.0
- symfony/routing: ^7.4 || ^8.0
- symfony/security-bundle: ^7.4 || ^8.0
- symfony/security-core: ^7.4 || ^8.0
- symfony/security-csrf: ^7.4 || ^8.0
- symfony/twig-bundle: ^7.4 || ^8.0
- symfony/yaml: ^7.4 || ^8.0
- twig/extra-bundle: ^3.12
- twig/string-extra: ^3.12
- twig/twig: ^3.8
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- igor-php/igor-php: ^0.9.7
- nowo-tech/phpstan-frankenphp: ^1.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^2.0
- phpstan/phpstan-phpunit: ^2.0
- phpstan/phpstan-symfony: ^2.0
- phpunit/phpunit: ^10.0 || ^11.0
- rector/rector: ^2.0
- symfony/console: ^7.4 || ^8.0
Suggests
- symfony/flex: For automatic recipe installation
- symfony/security-bundle: Role-based editor access when allow_unauthenticated is false
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-13 17:26:51 UTC
README
⭐ Found this useful? Give it a star on GitHub.
Standalone Symfony PDF editor: upload a PDF, edit it in a React editor (embedpdf + pdf-lib) and save the rewritten file — text, form fields, pages, notes and watermarks.
This bundle is FrankenPHP worker mode friendly.
This includes reset_kernel: false (long-lived kernel).
FrankenPHP worker mode: Supported at 100% under scenario B (kernel not rebooted). All shared services are stateless; Process timeouts stop the Python engine on expiry. Demo defaults: FRANKENPHP_MODE=worker, FRANKENPHP_RESET_KERNEL unset (false). See Demo with FrankenPHP and the worker audit.
Editor workspace: tools, page rail, PDF canvas and properties |
Draft-then-save: page operations queue until Save to PDF |
Upload page: UiKit form scaffold in the host layout |
The screenshots come from the Symfony 8 demo (make -C demo/symfony8 up, then Scenarios). Refresh them with make -C demo/symfony8 demo-screenshots; make -C demo/symfony8 test-e2e runs the Playwright happy path.
Table of contents
What is this?
PdfEditorBundle is a standalone Symfony editor for PDF files, with a document UI comparable to a rich-text editor (toolbar, page canvas, properties panel).
It is not an extension of PdfSignableBundle. Signing workflows stay in that package. This bundle owns content editing.
Users can:
- edit existing text (replace / insert / delete)
- create and maintain AcroForm fields
- detect and remove watermarks, or stamp a new one
- rotate, delete, insert and reorder pages
- add notes
- download the rewritten PDF
PHP orchestrates HTTP, security and the UI. The browser runs a React editor (embedpdf + pdf-lib, with XObject helpers inspired by vensas/pdf-editor). On Save to PDF, the client exports the document and Symfony stores the rewritten bytes.
A legacy Python 3 + PyMuPDF engine remains available when engine_mode: python is set in the profile.
A PDF is not a word-processor file: scanned pages without text need OCR (out of v1). Flattened artwork cannot always be un-drawn. The editor documents those limits instead of pretending otherwise.
Features
- Private admin UI (
/pdf-editor) on the UiKit page contract (REQ-UI-001 / REQ-UI-002 / REQ-UI-003): renders inside your layout (nowo_ui_kit.layouts.admin), themed once in UiKit;security.access_roles/access_checker - Draft-then-save: the React client queues edits, exports via embedpdf/pdf-lib, then Symfony persists the PDF
- Default
engine_mode: client(no Python required); optionalpythonprofile for PyMuPDF - Editor profile (
default_profile+profiles) — REQ-CFG-001; only the default profile is used (extra profiles are deprecated) - Workspace TTL:
nowo:pdf-editor:purge --older-than=24hdeletes abandoned workspaces - Process timeouts and stop-on-expiry — REQ-RUNTIME-001
- Twig forms only (
form_start/form_row/form_end) - Translations:
en,es,it,fr,pt,de,nl
Requirements
- PHP >= 8.2, < 8.6
- Symfony 7.4 / 8.0 / 8.1+ and UiKitBundle 2.17.3+ (installed with the bundle)
- Node >= 20 + pnpm (to build the React editor assets)
- Python 3.9+ and
pymupdf— only whenengine_mode: python
Installation
composer require nowo-tech/pdf-editor-bundle cd vendor/nowo-tech/pdf-editor-bundle && pnpm install && pnpm run build php bin/console assets:install public # Optional legacy Python engine: # python3 -m pip install -r vendor/nowo-tech/pdf-editor-bundle/engine/requirements.txt # php bin/console nowo:pdf-editor:check-engine
See docs/INSTALLATION.md.
Documentation
- Installation
- Configuration
- Usage
- Architecture, ports and DDD hosts
- Contributing
- Code of Conduct
- Changelog
- Upgrading
- Release
- Security
- Engram
- Spec-driven development
- GitHub Spec Kit
Additional documentation
- Templates and page contract (UiKit, overrides)
- Architecture decisions (ADR)
- Demo with FrankenPHP
- FrankenPHP worker audit (
reset_kernel: false) - GitHub CI
- PSR evaluation (REQ-CS-007)
- Testing
- Baseline spec
Tests and coverage
- Tests: PHPUnit (
Unit,Integrationsuites); see docs/TESTING.md. - PHP: 100% line coverage of
src/(composer coverage-check/composer test-coverage-100fails below 100%). - TS/JS: N/A — no unit tests yet; the React editor is type-checked (
make test-ts) and built in CI. - Python: 98.46% lines of
engine/pdf_editor_engine.py(make test-python, pytest-cov, fails below 90%).
make test
make test-coverage
make release-check
License
MIT. The PHP bundle is MIT. The required PyMuPDF engine is a separate free Python dependency installed on the host (see engine/requirements.txt).



