Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PDF_JM Web

License Python Platform FastAPI

Lightweight, 100% local PDF editor with a web UI to open, reorder, combine, split, and stamp documents — no external services required.

Overview

PDF_JM Web is a local PDF editor with a browser-based interface. It runs a FastAPI backend on 127.0.0.1 and serves a vanilla HTML/CSS/JS frontend, so your documents never leave your machine. Open, order, combine, split, and save PDFs without depending on external services.

msedge_gQN5s0USlt

Features

  • Open PDFs from the native system picker or from the browser
  • Thumbnail view of all pages
  • Select individual pages, multiple pages, or ranges with Shift + click
  • Reorder pages with buttons or drag & drop
  • Rotate, duplicate, and delete pages
  • Insert pages from another PDF or from images
  • Extract selected pages into a new PDF
  • Split the document into several parts (save individually or as a ZIP)
  • Merge several PDFs into one
  • Stamp a 10-digit number onto selected pages
  • Detect and remove likely-blank pages (analysis by text, marks, and visual sampling)
  • Attach, extract, and delete embedded files in the PDF
  • Save with export presets (profile, compression, and linearization)
  • Overwrite the original file when saving, or create an edited copy (your choice)
  • Save the document to a local folder
  • Inspect a page in high resolution with an on-demand zoomed preview
  • Light / dark theme and recent files

UI flow

  • Home: open a PDF, see recent files, and enter the main flow
  • Editor:
    • top bar with the main actions
    • selection strip with quick status
    • compact summary with Pages, Selection, Changes, and History
    • main page grid with 4 columns on desktop and responsive fallbacks
    • per-page magnifier button opening a centered HD preview
  • Modals: save, insert, extract, split, merge, attachments, blank-page cleanup, metadata, bookmarks, shortcuts, and zoomed view

Main shortcuts

  • ↑ / ↓ — move the current page
  • Space — select the current page
  • Shift + click — select a range
  • Ctrl + A — select or clear everything
  • Ctrl + E — open extraction
  • Ctrl + S — open save
  • Ctrl + D — toggle light / dark theme
  • R — rotate the current page
  • Delete / Backspace — delete the selection
  • Esc — close a modal or clear the selection
msedge_OnQWMOCjwc

Tech Stack

  • FastAPI + Uvicorn
  • PyMuPDF (fitz)
  • tkinter for optional native pickers (run in a subprocess)
  • Vanilla HTML + CSS + JavaScript (no frameworks, no build step)

Requirements

  • Python 3.10 or higher
  • Windows 10/11 (tested)
  • Linux/macOS should work with minor adjustments in a local environment

Installation

  1. Clone or download the repository

  2. Create a virtual environment:

    python -m venv venv
  3. Activate the virtual environment:

    • Windows: ./venv/Scripts/activate
    • Linux/macOS: source venv/bin/activate
  4. Install dependencies:

    pip install -r requirements.txt

Usage

python main.py

The application starts at:

  • http://127.0.0.1:8080
  • API docs: http://127.0.0.1:8080/docs

main.py also tries to open the default browser automatically.

Configuration

A config.json is generated automatically next to the code (or next to the .exe in packaged builds) with these values:

Key Default Description
theme dark Initial theme (dark/light)
default_output_dir ~/Documents Suggested folder when saving
thumbnail_dpi / thumbnail_width 100 / 200 Thumbnail rendering
max_file_size_mb 100 File upload limit
history_limit 20 Maximum undo/redo snapshots
session_ttl_minutes 120 Minutes of inactivity before a session expires
history_max_bytes 536870912 Undo/redo memory cap per document
recent_files [] Recent files (maximum 10)
export_presets [] Saved export presets

Project Structure

PDF_JM/
├── main.py                    # Entry point: starts the app at http://127.0.0.1:8080
├── backend/
│   ├── app.py                 # HTTP app, local-protection middleware and asset serving
│   ├── schemas.py             # DTOs / API contracts
│   ├── routers/
│   │   ├── documents.py       # Editing and export endpoints
│   │   └── system.py          # Preferences, recents and native pickers
│   └── services/
│       └── document_service.py # In-memory state and document orchestration
├── core/
│   ├── pdf_manager.py         # Base PDF manipulation with PyMuPDF
│   ├── page_operations.py     # High-level page operations
│   └── utils.py               # Validations, formats and native pickers
├── web/
│   ├── index.html             # HTML shell
│   ├── main.js                # Web UI and API interaction
│   ├── api.js                 # Frontend HTTP client
│   ├── modal_feedback.mjs     # Busy/feedback state for modals
│   └── styles.css             # Visual system and layout
├── tests/
│   ├── test_web_api.py        # API and state regressions
│   ├── test_pdf_manager.py    # Preview rendering regressions
│   └── test_web_ui_state.py   # UI states via Node runners (.mjs)
├── config.py                  # Persisted preferences
├── requirements.txt           # Python dependencies
└── scripts/
    └── build_exe.py           # Windows packaging with PyInstaller

Advanced Usage

Local API security

The app listens only on 127.0.0.1, and the API is hardened with a middleware that:

  • Rejects requests whose Host header is not localhost/127.0.0.1 (blocks DNS rebinding).
  • Rejects mutating requests (POST/PUT/PATCH/DELETE) with a non-local Origin header (blocks CSRF from external web pages).
  • Adds security headers: CSP, X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy.

Local save filenames are sanitized (no .. or path separators), and all uploads respect the configured limit (max_file_size_mb).

Technical validation

Python syntax:

python -m py_compile main.py backend/app.py backend/schemas.py backend/routers/system.py backend/routers/documents.py backend/services/document_service.py core/pdf_manager.py core/page_operations.py core/utils.py scripts/build_exe.py

Frontend syntax:

node --check web/main.js
node --check web/api.js

Tests:

pytest tests/test_pdf_manager.py tests/test_web_api.py tests/test_web_ui_state.py

test_web_ui_state.py requires node on the PATH (it runs the tests/*.mjs runners).

Build a Windows executable

python scripts/build_exe.py

The .exe keeps its config.json in the same folder as the executable.

Limitations

  1. It does not edit text already present in the PDF
  2. Password-protected PDFs are not supported
  3. PDF forms may not keep all of their interactive behavior
  4. Large PDFs may take longer to generate the initial thumbnails
  5. Open documents are kept in memory while the session is active; a session idle for more than session_ttl_minutes (120 by default) is discarded, and the undo history is trimmed by count (history_limit) and by accumulated bytes (history_max_bytes, 512 MB per document)

License

Distributed under the GNU Affero General Public License v3.0 (AGPL-3.0), as required by its use of the AGPL-licensed PyMuPDF library. See LICENSE for the full license text.

Copyright (c) 2026 Jose Miguel Maldonado Garcia

Author

Jose Miguel Maldonado Garcia@JoanMike

About

Lightweight, 100% local PDF editor built with Python (FastAPI + PyMuPDF) and a vanilla web UI. Combine, split, reorder, and stamp pages — no external services.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages