Back to Bentopdf

README

README.md

2.8.461.0 KB
Original Source
<p align="center"></p> <h1 align="center">BentoPDF</h1> <p align="center"> <a href="https://www.digitalocean.com/?refcode=d93c189ef6d0&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"> </a> </p>

BentoPDF is a powerful, privacy-first, client-side PDF toolkit that is self hostable and allows you to manipulate, edit, merge, and process PDF files directly in your browser. No server-side processing is required, ensuring your files remain secure and private.

FOSS Hack 2026


Table of Contents


πŸ“’ Join Us on Discord

Have questions, feature requests, or want to chat with the community? Join our Discord server!


πŸ“š Documentation

Visit our Documentation for:

  • Getting Started guide
  • Tools Reference (50+ tools)
  • Self-Hosting guides (Docker, Vercel, Netlify, Cloudflare, AWS, Hostinger, Nginx, Apache)
  • Contributing guide
  • Commercial License details

πŸ“œ Licensing

BentoPDF is dual-licensed to fit your needs:

LicenseBest ForPrice
AGPL-3.0Open-source projects with public source codeFree
CommercialProprietary / closed-source applications$79 (lifetime)
<p align="center"> <a href="https://buy.polar.sh/polar_cl_ThDfffbl733x7oAodcIryCzhlO57ZtcWPq6HJ1qMChd"> </a> </p>

One-time purchase Β· Unlimited devices & users Β· Lifetime updates Β· No AGPL obligations

πŸ“– For more details, see our Licensing Page

AGPL Components (Pre-configured via CDN)

BentoPDF does not bundle AGPL-licensed processing libraries in its source code, but pre-configures CDN URLs so all features work out of the box with zero setup:

ComponentLicenseFeatures Enabled
PyMuPDFAGPL-3.0PDF to Text/Markdown/SVG/DOCX, Extract Images/Tables, EPUB/MOBI/XPS conversion, Compression, Deskew
GhostscriptAGPL-3.0PDF/A Conversion, Font to Outline
CoherentPDF (CPDF)AGPL-3.0Merge, Split by Bookmarks, Table of Contents, PDF to/from JSON, Attachments

[!TIP] Zero-config by default. WASM modules are loaded at runtime from jsDelivr CDN. No manual configuration is needed. For custom deployments (air-gapped, self-hosted), see WASM Configuration below.

<hr>

⭐ Stargazers over time


πŸ’– Thank You to Our Sponsors

We're incredibly grateful to all our sponsors and supporters who help keep BentoPDF free and open source!

<!-- sponsors --> <!-- sponsors -->

✨ Why BentoPDF?

  • Privacy First: All processing happens in your browser. Your files are never uploaded to a server, guaranteeing 100% privacy.
  • No Limits: Manipulate as many files as you want, as often you want. There are no restrictions or upload limits.
  • High Performance: Built with modern web technologies, BentoPDF is fast and efficient, handling even large PDF files with ease.
  • Completely Free: BentoPDF is a free and open-source tool for everyone.

πŸ› οΈ Features / Tools Supported

BentoPDF offers a comprehensive suite of tools to handle all your PDF needs.

Organize & Manage PDFs

Tool NameDescription
Merge PDFsCombine multiple PDF files into one. Preserves Bookmarks.
Split PDFsExtract specific pages or divide a document into smaller files.
Organize PagesReorder, duplicate, or delete pages with a simple drag-and-drop interface.
Extract PagesSave a specific range of pages as a new PDF.
Delete PagesRemove unwanted pages from your document.
Rotate PDFRotate individual or all pages in a document.
Rotate by Custom DegreesRotate pages by any custom angle.
N-Up PDFCombine multiple pages onto a single page.
View PDFA powerful, integrated PDF viewer.
Alternate & Mix PagesMerge pages by alternating pages from each PDF. Preserves Bookmarks.
Posterize PDFSplit a PDF into multiple smaller pages for print.
PDF Multi ToolMerge, Split, Organize, Delete, Rotate, Add Blank Pages, Extract and Duplicate in an unified interface.
PDF BookletRearrange pages for double-sided booklet printing. Fold and staple to create a booklet.
Add AttachmentsEmbed one or more files into your PDF.
Extract AttachmentsExtract all embedded files from PDF(s) as a ZIP.
Edit AttachmentsView or remove attachments in your PDF.
Divide PagesDivide pages horizontally or vertically.
Combine to Single PageStitch all pages into one continuous scroll.
Add Blank PageInsert an empty page anywhere in your PDF.
Reverse PagesFlip the order of all pages in your document.
View MetadataInspect the hidden properties of your PDF.
PDFs to ZIPPackage multiple PDF files into a ZIP archive.
Compare PDFsCompare two PDFs side by side.

Edit & Modify PDFs

Tool NameDescription
PDF EditorAnnotate, highlight, redact, comment, add shapes/images, search, and view PDFs.
Create Fillable FormsCreate professional fillable PDF forms with text fields, checkboxes, dropdowns, radio buttons, signatures, and more. Fully compliant with PDF standards for compatibility with all PDF viewers.
PDF Form FillerFill in forms directly in the browser. Also supports XFA forms.
Add Page NumbersEasily add page numbers with customizable formatting.
Bates NumberingAdd sequential Bates numbers across one or more PDF files.
Add WatermarkAdd text or image watermarks to protect your documents.
Header & FooterAdd customizable headers and footers.
Crop PDFCrop specific pages or the entire document.
Deskew PDFAutomatically straighten tilted scanned pages using OpenCV.
Font to OutlineConvert all fonts to vector outlines for consistent rendering across all devices.
Invert ColorsInvert the colors of your PDF pages for better readability.
Change BackgroundModify the background color of your PDF.
Change Text ColorChange the color of text content within the PDF.
Flatten PDFFlatten form fields and annotations into static content.
Remove AnnotationsRemove comments, highlights, and other annotations.
Remove Blank PagesAuto detect and remove blank pages in a PDF.
Edit BookmarksAdd, Edit, Create, Import and Export PDF Bookmarks.
Add StampsAdd image stamps to your PDF using the annotation toolbar.
Table of ContentsGenerate a table of contents page from PDF bookmarks.
Redact ContentPermanently remove sensitive content from your PDFs.
Scanner EffectMake your PDF look like a scanned document.
Adjust ColorsFine-tune brightness, contrast, saturation and more.

Automate

Tool NameDescription
PDF Workflow BuilderBuild custom PDF processing pipelines with a visual node editor.

Convert to PDF

Tool NameDescription
Image to PDFConvert JPG, PNG, BMP, GIF, TIFF, PNM, PGM, PBM, PPM, PAM, JXR, JPX, JP2, PSD, SVG, HEIC, WebP to PDF.
JPG to PDFConvert JPG, JPEG, and JPEG2000 (JP2/JPX) images to PDF.
PNG to PDFConvert PNG images to PDF.
WebP to PDFConvert WebP images to PDF.
SVG to PDFConvert SVG images to PDF.
BMP to PDFConvert BMP images to PDF.
HEIC to PDFConvert HEIC images to PDF.
TIFF to PDFConvert TIFF images to PDF.
PSD to PDFConvert Adobe Photoshop (PSD) files to PDF.
Word to PDFConvert Word documents (DOCX, DOC, ODT, RTF) to PDF.
Excel to PDFConvert Excel spreadsheets (XLSX, XLS, ODS, CSV) to PDF.
PowerPoint to PDFConvert PowerPoint presentations (PPTX, PPT, ODP) to PDF.
ODT to PDFConvert OpenDocument Text files to PDF.
ODS to PDFConvert OpenDocument Spreadsheet (ODS) files to PDF.
ODP to PDFConvert OpenDocument Presentation (ODP) files to PDF.
ODG to PDFConvert OpenDocument Graphics (ODG) files to PDF.
RTF to PDFConvert Rich Text Format documents to PDF.
CSV to PDFConvert CSV spreadsheet files to PDF.
Markdown to PDFWrite or paste Markdown and export it as a beautifully formatted PDF.
Text to PDFConvert plain text files into a PDF.
JSON to PDFConvert JSON files to PDF.
XML to PDFConvert XML documents to PDF.
EPUB to PDFConvert EPUB e-books to PDF.
MOBI to PDFConvert MOBI e-books to PDF.
FB2 to PDFConvert FictionBook (FB2) e-books to PDF.
CBZ to PDFConvert comic book archives (CBZ/CBR) to PDF.
XPS to PDFConvert XPS/OXPS documents to PDF.
Email to PDFConvert email files (EML, MSG) to PDF. Supports Outlook exports.
Pages to PDFConvert Apple Pages documents to PDF.
WPD to PDFConvert WordPerfect documents (WPD) to PDF.
WPS to PDFConvert WPS Office documents to PDF.
PUB to PDFConvert Microsoft Publisher (PUB) files to PDF.
VSD to PDFConvert Microsoft Visio (VSD, VSDX) files to PDF.

Convert from PDF

Tool NameDescription
PDF to ImageConvert PDF pages to JPG, PNG, WebP, BMP, or TIFF formats.
PDF to JPGConvert each PDF page into a JPG image.
PDF to PNGConvert each PDF page into a PNG image.
PDF to WebPConvert each PDF page into a WebP image.
PDF to BMPConvert each PDF page into a BMP image.
PDF to TIFFConvert each PDF page into a TIFF image.
PDF to CBZConvert a PDF into a CBZ (Comic Book Archive) for comic readers and Calibre.
PDF to SVGConvert each page into a scalable vector graphic (SVG) for perfect quality.
PDF to GreyscaleConvert a color PDF into a black-and-white version.
PDF to TextExtract text from PDF files and save as plain text (.txt).
PDF to JSONConvert PDF files to JSON format.
PDF to CSVExtract tables from PDF and convert to CSV format.
PDF to ExcelExtract tables from PDF and convert to Excel (XLSX) format.
Extract TablesExtract tables from PDF files and export as CSV, JSON, or Markdown.
OCR PDFMake scanned PDFs searchable and copyable using Optical Character Recognition.

Secure & Optimize PDFs

Tool NameDescription
Compress PDFReduce file size while maintaining quality.
Repair PDFAttempt to repair and recover data from a corrupted PDF.
Encrypt PDFAdd a password to protect your PDF from unauthorized access.
Decrypt PDFRemove password protection from a PDF (password required).
Change PermissionsSet or modify user permissions for printing, copying, and editing.
Sign PDFDraw, type, or upload your signature.
Digital SignatureAdd cryptographic digital signatures using X.509 certificates (PFX/PEM). Private key never leaves browser.
Validate SignatureVerify digital signatures, check certificate validity, and confirm document integrity.
Redact ContentPermanently remove sensitive content from your PDFs.
Edit MetadataView and modify PDF metadata (author, title, keywords, etc.).
Remove MetadataStrip all metadata from your PDF for privacy.
Linearize PDFOptimize PDF for fast web viewing.
Sanitize PDFRemove metadata, annotations, scripts, and more.
Fix Page SizeStandardize all pages to a uniform size.
Page DimensionsAnalyze page size, orientation, and units.
Remove RestrictionsRemove password protection and security restrictions associated with digitally signed PDF files.

🌍 Translations

BentoPDF is available in multiple languages:

LanguageStatus
English
Chinese
Traditional Chinese
French
German
Indonesian
Italian
Portuguese
Turkish
Vietnamese
Korean
Russian

Want to help translate BentoPDF into your language? Check out our Translation Guide!


πŸš€ Getting Started

You can run BentoPDF locally for development or personal use.

Prerequisites

πŸš€ Quick Start

Run BentoPDF instantly from GitHub Container Registry (Recommended):

bash
docker run -p 3000:8080 ghcr.io/alam00000/bentopdf:latest

Open your browser at: http://localhost:3000

<details> <summary><b>Alternative: Using Docker Hub or Podman</b></summary>

Docker Hub:

bash
docker run -p 3000:8080 bentopdfteam/bentopdf:latest

Podman (GHCR):

bash
podman run -p 3000:8080 ghcr.io/alam00000/bentopdf:latest

Podman (Docker Hub):

bash
podman run -p 3000:8080 docker.io/bentopdfteam/bentopdf:latest

[!NOTE] All docker commands in this documentation work with Podman by replacing docker with podman.

</details>

Static Hosting using Netlify, Vercel, and GitHub Pages

It is very straightforward to host your own instance of BentoPDF using a static web page hosting service. Plus, services such as Netlify, Vercel, and GitHub Pages all offer a free tier for getting started. See Static Hosting for details.

🏠 Self-Hosting Locally

Since BentoPDF is fully client-side, all processing happens in the user's browser and no server-side processing is required. This means you can host BentoPDF as simple static files on any web server or hosting platform.

[!IMPORTANT] Office file conversion uses LibreOffice WASM, which requires SharedArrayBuffer. That means the app must be both cross-origin isolated and served from a secure context. http://localhost works for local testing, but http://192.168.x.x or other LAN IPs usually require HTTPS even if the server already sends the correct COOP/COEP headers.

Download from Releases (Recommended):

The easiest way to self-host is to download the pre-built distribution file from our GitHub releases. Each release includes a dist-{version}.zip file that contains all necessary files for self-hosting.

  1. Go to BentoPDF Releases
  2. Download the latest dist-{version}.zip file
  3. Extract the zip file
  4. Serve the extracted folder with your preferred web server

Serve the extracted folder (requires Node.js):

bash
# Navigate to the extracted folder
cd dist-1.7.3  # Replace with your version

# Start a local server
npx http-server -c-1

The website will be accessible at: http://localhost:8080/

[!NOTE] The -c-1 flag disables caching for development.

Build from Source (Advanced):

If you prefer to build from source:

bash
# Clone the repository
git clone https://github.com/alam00000/bentopdf.git
cd bentopdf

# Install dependencies
npm install

# Build the project
npm run build

# Package the distribution for hosting (optional)
npm run package

# Preview the build locally
npm run preview

# The website will be accessible at: http://localhost:4173/

Compression Modes:

BentoPDF supports different compression modes for optimized builds:

bash
# Gzip only (smallest Docker image size)
npm run build:gzip
docker build --build-arg COMPRESSION_MODE=g -t bentopdf:gzip .

# Brotli only (best compression ratio)
npm run build:brotli
docker build --build-arg COMPRESSION_MODE=b -t bentopdf:brotli .

# No compression (fastest build time)
npm run build:original
docker build --build-arg COMPRESSION_MODE=o -t bentopdf:original .

# All formats (default, maximum browser compatibility)
npm run build:all
docker build --build-arg COMPRESSION_MODE=all -t bentopdf:all .
ModeFiles KeptUse Case
g.gz onlyStandard nginx or minimal size
b.br onlyModern CDN with Brotli support
ooriginalsDevelopment or custom compression
allall formatsMaximum compatibility (default)

CDN Optimization:

BentoPDF can use jsDelivr CDN to serve large WASM files (LibreOffice, Ghostscript, PyMuPDF) for improved performance and reduced bandwidth costs:

bash
# Production build with CDN (Recommended)
VITE_USE_CDN=true npm run build

# Standard build with local files only
npm run build

How it works:

  • When VITE_USE_CDN=true: Browser loads WASM files from jsDelivr CDN (fast, global delivery)
  • Local files are always included as automatic fallback
  • If CDN fails then it falls back to local files
<h3 id="wasm-configuration">βš™οΈ WASM Configuration</h3>

Advanced PDF features (PyMuPDF, Ghostscript, CoherentPDF) are pre-configured to load from jsDelivr CDN via environment variables. This means all features work out of the box β€” no manual setup needed.

The default URLs are set in .env.production:

bash
VITE_WASM_PYMUPDF_URL=https://cdn.jsdelivr.net/npm/@bentopdf/[email protected]/
VITE_WASM_GS_URL=https://cdn.jsdelivr.net/npm/@bentopdf/[email protected]/assets/
VITE_WASM_CPDF_URL=https://cdn.jsdelivr.net/npm/[email protected]/dist/
VITE_TESSERACT_WORKER_URL=
VITE_TESSERACT_CORE_URL=
VITE_TESSERACT_LANG_URL=
VITE_TESSERACT_AVAILABLE_LANGUAGES=
VITE_OCR_FONT_BASE_URL=

To override via Docker build args:

bash
docker build \
  --build-arg VITE_WASM_PYMUPDF_URL=https://your-server.com/pymupdf/ \
  --build-arg VITE_WASM_GS_URL=https://your-server.com/gs/ \
  --build-arg VITE_WASM_CPDF_URL=https://your-server.com/cpdf/ \
  --build-arg VITE_TESSERACT_WORKER_URL=https://your-server.com/ocr/worker.min.js \
  --build-arg VITE_TESSERACT_CORE_URL=https://your-server.com/ocr/core \
  --build-arg VITE_TESSERACT_LANG_URL=https://your-server.com/ocr/lang-data \
  --build-arg VITE_TESSERACT_AVAILABLE_LANGUAGES=eng,deu \
  --build-arg VITE_OCR_FONT_BASE_URL=https://your-server.com/ocr/fonts \
  -t bentopdf .

To disable a module (require manual user config via Advanced Settings), set its variable to an empty string.

For OCR, either leave all VITE_TESSERACT_* variables empty and use the default online assets, or set the worker/core/lang URLs together for self-hosted/offline OCR. If your self-hosted bundle only includes a subset such as eng,deu, also set VITE_TESSERACT_AVAILABLE_LANGUAGES=eng,deu so the UI only shows bundled languages and OCR fails with a descriptive message for unsupported ones. For fully offline searchable-PDF output, also set VITE_OCR_FONT_BASE_URL to the internal directory that serves the bundled OCR text-layer fonts.

Users can also override these defaults per-browser via Advanced Settings in the UI β€” user overrides take priority over the environment defaults.

[!IMPORTANT] These URLs are baked into the JavaScript at build time. The WASM files themselves are downloaded by the user's browser at runtime β€” Docker does not download them during the build.

<h3 id="air-gapped--offline-deployment">πŸ”’ Air-Gapped / Offline Deployment</h3>

For networks with no internet access (government, healthcare, financial, etc.), you need to prepare everything on a machine with internet, then transfer the bundle into the isolated network.

Automated Script (Recommended)

The included prepare-airgap.sh script automates the entire process β€” downloading WASM packages, building the Docker image, exporting everything into a self-contained bundle with a setup script.

bash
git clone https://github.com/alam00000/bentopdf.git
cd bentopdf

# Show supported OCR language codes (for --ocr-languages)
bash scripts/prepare-airgap.sh --list-ocr-languages

# Search OCR language codes by name or abbreviation
bash scripts/prepare-airgap.sh --search-ocr-language german

# Interactive mode β€” prompts for all options
bash scripts/prepare-airgap.sh

# Or fully automated
bash scripts/prepare-airgap.sh --wasm-base-url https://internal.example.com/wasm

This produces a bundle directory containing:

bentopdf-airgap-bundle/
  bentopdf.tar              # Docker image
  *.tgz                     # WASM packages (PyMuPDF, Ghostscript, CoherentPDF, Tesseract)
  tesseract-langdata/       # OCR traineddata files
  ocr-fonts/                # OCR text-layer font files
  setup.sh                  # Setup script for the air-gapped side
  README.md                 # Instructions

Transfer the bundle into the air-gapped network via USB, internal artifact repo, or approved method. Then run the included setup script:

bash
cd bentopdf-airgap-bundle
bash setup.sh

The setup script loads the Docker image, extracts WASM files, and optionally starts the container.

<details> <summary><strong>Script options</strong></summary>
FlagDescriptionDefault
--wasm-base-url <url>Where WASMs will be hosted internally(required, prompted if missing)
--image-name <name>Docker image tagbentopdf
--output-dir <path>Output bundle directory./bentopdf-airgap-bundle
--simple-modeEnable Simple Modeoff
--base-url <path>Subdirectory base URL (e.g. /pdf/)/
--language <code>Default UI language (e.g. fr, de)(none)
--brand-name <name>Custom brand name(none)
--brand-logo <path>Logo path relative to public/(none)
--footer-text <text>Custom footer text(none)
--ocr-languages <list>Comma-separated OCR languages to bundleeng
--list-ocr-languagesPrint supported OCR codes and names, then exitoff
--search-ocr-language <term>Search OCR codes by name or abbreviationoff
--dockerfile <path>Dockerfile to useDockerfile
--skip-dockerSkip Docker build and exportoff
--skip-wasmSkip WASM download (reuse existing .tgz files)off
</details>

The interactive prompt also accepts list to print the full supported Tesseract code list and search <term> to find matches such as search german or search chi.

[!IMPORTANT] WASM files must be served from the same origin as the BentoPDF app. Web Workers use importScripts() which cannot load scripts cross-origin. For example, if BentoPDF runs at https://internal.example.com, the WASM base URL should also be https://internal.example.com/wasm.

Manual Steps

<details> <summary>If you prefer to do it manually without the script</summary>

Step 1: Download the WASM and OCR packages (on a machine with internet)

bash
npm pack @bentopdf/[email protected]
npm pack @bentopdf/gs-wasm
npm pack coherentpdf
npm pack [email protected]
npm pack [email protected]
mkdir -p tesseract-langdata
curl -fsSL https://cdn.jsdelivr.net/npm/@tesseract.js-data/eng/4.0.0_best_int/eng.traineddata.gz -o tesseract-langdata/eng.traineddata.gz
mkdir -p ocr-fonts
curl -fsSL https://raw.githack.com/googlefonts/noto-fonts/main/hinted/ttf/NotoSans/NotoSans-Regular.ttf -o ocr-fonts/NotoSans-Regular.ttf

Step 2: Build the Docker image with internal URLs

bash
git clone https://github.com/alam00000/bentopdf.git
cd bentopdf

docker build \
  --build-arg VITE_WASM_PYMUPDF_URL=https://internal-server.example.com/wasm/pymupdf/ \
  --build-arg VITE_WASM_GS_URL=https://internal-server.example.com/wasm/gs/ \
  --build-arg VITE_WASM_CPDF_URL=https://internal-server.example.com/wasm/cpdf/ \
  --build-arg VITE_TESSERACT_WORKER_URL=https://internal-server.example.com/wasm/ocr/worker.min.js \
  --build-arg VITE_TESSERACT_CORE_URL=https://internal-server.example.com/wasm/ocr/core \
  --build-arg VITE_TESSERACT_LANG_URL=https://internal-server.example.com/wasm/ocr/lang-data \
  --build-arg VITE_OCR_FONT_BASE_URL=https://internal-server.example.com/wasm/ocr/fonts \
  -t bentopdf .

Step 3: Export the Docker image

bash
docker save bentopdf -o bentopdf.tar

Step 4: Transfer into the air-gapped network

Copy these files via USB drive, internal artifact repository, or approved transfer method:

  • bentopdf.tar β€” the Docker image
  • bentopdf-pymupdf-wasm-0.11.14.tgz β€” PyMuPDF WASM package
  • bentopdf-gs-wasm-*.tgz β€” Ghostscript WASM package
  • coherentpdf-*.tgz β€” CoherentPDF WASM package
  • tesseract.js-7.0.0.tgz β€” Tesseract worker package
  • tesseract.js-core-7.0.0.tgz β€” Tesseract core runtime package
  • tesseract-langdata/ β€” OCR traineddata files
  • ocr-fonts/ β€” OCR text-layer font files

Step 5: Set up inside the air-gapped network

bash
# Load the Docker image
docker load -i bentopdf.tar

# Extract the WASM packages
mkdir -p ./wasm/pymupdf ./wasm/gs ./wasm/cpdf ./wasm/ocr/core ./wasm/ocr/lang-data ./wasm/ocr/fonts
tar xzf bentopdf-pymupdf-wasm-0.11.14.tgz -C ./wasm/pymupdf --strip-components=1
tar xzf bentopdf-gs-wasm-*.tgz -C ./wasm/gs --strip-components=1
tar xzf coherentpdf-*.tgz -C ./wasm/cpdf --strip-components=1
TEMP_TESS=$(mktemp -d)
tar xzf tesseract.js-7.0.0.tgz -C "$TEMP_TESS"
cp "$TEMP_TESS/package/dist/worker.min.js" ./wasm/ocr/worker.min.js
rm -rf "$TEMP_TESS"
tar xzf tesseract.js-core-7.0.0.tgz -C ./wasm/ocr/core --strip-components=1
cp ./tesseract-langdata/*.traineddata.gz ./wasm/ocr/lang-data/
cp ./ocr-fonts/* ./wasm/ocr/fonts/

# Run BentoPDF
docker run -d -p 3000:8080 --restart unless-stopped bentopdf

Make sure the files are accessible at the URLs you configured in Step 2, including .../ocr/worker.min.js, .../ocr/core, .../ocr/lang-data, and .../ocr/fonts.

</details>

[!NOTE] If you're building from source instead of Docker, set the variables in .env.production before running npm run build:

bash
VITE_WASM_PYMUPDF_URL=https://internal-server.example.com/wasm/pymupdf/
VITE_WASM_GS_URL=https://internal-server.example.com/wasm/gs/
VITE_WASM_CPDF_URL=https://internal-server.example.com/wasm/cpdf/
VITE_TESSERACT_WORKER_URL=https://internal-server.example.com/wasm/ocr/worker.min.js
VITE_TESSERACT_CORE_URL=https://internal-server.example.com/wasm/ocr/core
VITE_TESSERACT_LANG_URL=https://internal-server.example.com/wasm/ocr/lang-data
VITE_OCR_FONT_BASE_URL=https://internal-server.example.com/wasm/ocr/fonts

Subdirectory Hosting:

BentoPDF can also be hosted from a subdirectory (e.g., example.com/tools/bentopdf/):

bash

# Example:
# 1. Build the app with the specific BASE_URL. BASE_URL must have a trailing and leading slash. The BASE_URL can be any url of your choice. Here we are using /tools/bentopdf/ as an example.

BASE_URL=/tools/bentopdf/ npm run build

# 2. Create the nested directory structure inside serve-test (or any folder of your choice for local testing. In case of production, create the nested directory structure inside the root directory)
mkdir -p serve-test/tools/bentopdf

# 3. Copy all files from the 'dist' folder into that nested directory
cp -r dist/* serve-test/tools/bentopdf/

# 4. Serve the 'serve-test' folder
npx serve serve-test

The website can be accessible at: http://localhost:3000/tools/bentopdf/

The npm run package command creates a dist-{version}.zip file that you can use for self-hosting.

Docker Subdirectory Deployment:

BentoPDF's Docker image also supports the BASE_URL build argument for subdirectory deployments:

bash
# Build for subdirectory deployment
docker build --build-arg BASE_URL=/bentopdf/ -t bentopdf .

# Run the container
docker run -p 3000:8080 bentopdf

# The app will be accessible at http://localhost:3000/bentopdf/

Default Language:

Set the default UI language at build time. Users can still switch languages β€” this only changes the initial default. Supported: en, ar, be, fr, de, es, zh, zh-TW, vi, tr, id, it, pt, nl, da.

bash
docker build --build-arg VITE_DEFAULT_LANGUAGE=fr -t bentopdf .

Combined with Simple Mode:

bash
# Build with both BASE_URL and SIMPLE_MODE
docker build \
  --build-arg BASE_URL=/tools/pdf/ \
  --build-arg SIMPLE_MODE=true \
  -t bentopdf-simple .

docker run -p 3000:8080 bentopdf-simple

[!IMPORTANT]

  • Always include trailing slashes in BASE_URL (e.g., /bentopdf/ not /bentopdf)
  • The default value is / for root deployment

For a more robust setup with auto-restart capabilities:

  1. Download the repo and create a docker-compose.yml file or use the one given in repo:
yaml
services:
  bentopdf:
    image: ghcr.io/alam00000/bentopdf:latest # Recommended
    # image: bentopdfteam/bentopdf:latest     # Alternative: Docker Hub
    container_name: bentopdf
    ports:
      - '3000:8080'
    restart: unless-stopped
  1. Start the application:
bash
# Docker Compose
docker-compose up -d

# Podman Compose
podman-compose up -d

The application will be available at http://localhost:3000.

🐧 Podman Quadlet (Systemd Integration)

For Linux production deployments, you can run BentoPDF as a systemd service using Podman Quadlet.

Create ~/.config/containers/systemd/bentopdf.container:

ini
[Unit]
Description=BentoPDF - Privacy-first PDF toolkit
After=network-online.target

[Container]
Image=ghcr.io/alam00000/bentopdf:latest
ContainerName=bentopdf
PublishPort=3000:8080
AutoUpdate=registry

[Service]
Restart=always

[Install]
WantedBy=default.target

Then enable and start:

bash
systemctl --user daemon-reload
systemctl --user enable --now bentopdf

For detailed Quadlet configuration, see Self-Hosting Docker Guide.

🏒 Simple Mode for Internal Use

For organizations that want a clean, distraction-free interface focused solely on PDF tools, BentoPDF supports a Simple Mode that hides all branding and marketing content.

What Simple Mode does:

  • Hides navigation, hero section, features, FAQ, testimonials, and footer
  • Shows only the essential PDF tools
  • Updates page title to "PDF Tools"
  • Perfect for internal company tools and educational institutions

For more details, see SIMPLE_MODE.md.

🎨 Custom Branding

Replace the default BentoPDF logo, name, and footer text with your own. Branding is configured via environment variables at build time and works across all deployment methods (Docker, static hosting, air-gapped VMs).

VariableDescriptionDefault
VITE_BRAND_NAMEBrand name shown in header and footerBentoPDF
VITE_BRAND_LOGOPath to logo file relative to public/images/favicon-no-bg.svg
VITE_FOOTER_TEXTCustom footer/copyright textΒ© 2026 BentoPDF. All rights reserved.

Docker:

bash
docker build \
  --build-arg VITE_BRAND_NAME="AcmePDF" \
  --build-arg VITE_BRAND_LOGO="images/acme-logo.svg" \
  --build-arg VITE_FOOTER_TEXT="Β© 2026 Acme Corp. Internal use only." \
  -t acmepdf .

Building from source:

Place your logo in the public/ folder, then build:

bash
VITE_BRAND_NAME="AcmePDF" \
VITE_BRAND_LOGO="images/acme-logo.svg" \
VITE_FOOTER_TEXT="Β© 2026 Acme Corp. Internal use only." \
npm run build

Or set the values in .env.production before building.

[!TIP] Branding works in both full mode and Simple Mode. You can combine it with other build-time options like SIMPLE_MODE, BASE_URL, and VITE_DEFAULT_LANGUAGE.

🚫 Disabling Specific Tools

Hide tools from the UI for compliance or security requirements. Disabled tools are removed from the homepage, search, keyboard shortcuts, workflow builder, and direct URL access.

Tool IDs are the page URL without .html β€” open any tool and look at the URL (e.g., edit-pdf, sign-pdf, encrypt-pdf).

Build-time (baked into the bundle):

bash
docker build --build-arg DISABLE_TOOLS="edit-pdf,sign-pdf,encrypt-pdf" -t bentopdf .

Runtime (no rebuild β€” mount a config.json):

json
{
  "disabledTools": ["edit-pdf", "sign-pdf", "encrypt-pdf"]
}
bash
docker run -d -p 3000:8080 \
  -v ./config.json:/usr/share/nginx/html/config.json:ro \
  ghcr.io/alam00000/bentopdf:latest

Both methods can be combined β€” the lists are merged. For the full list of tool IDs, see the self-hosting docs.

You can also disable specific features inside the PDF Editor (e.g., redaction, forms) without disabling the entire editor. Add editorDisabledCategories to your config.json:

json
{
  "editorDisabledCategories": ["redaction"]
}

For the full list of editor categories, see the self-hosting docs.

πŸ”’ Security Features

BentoPDF runs as a non-root user using nginx-unprivileged for enhanced security:

  • Non-Root Execution: Container runs with minimal privileges using nginx-unprivileged
  • Port 8080: Uses high port number to avoid requiring root privileges (configurable via PORT env var)
  • Security Best Practices: Follows Principle of Least Privilege

Basic Usage

bash
docker build -t bentopdf .
docker run -p 8080:8080 bentopdf

Custom Port

By default, BentoPDF listens on port 8080 inside the container. To change this, set the PORT environment variable:

bash
docker run -p 3000:9090 -e PORT=9090 ghcr.io/alam00000/bentopdf:latest
VariableDescriptionDefault
PORTNginx listen port in container8080

Custom User ID (PUID/PGID)

For environments that require running as a specific non-root user (e.g., NAS devices, Kubernetes with security contexts), use the non-root Dockerfile:

bash
# Build the non-root image
docker build -f Dockerfile.nonroot -t bentopdf-nonroot .

# Run with custom UID/GID
docker run -d -p 3000:8080 -e PUID=1000 -e PGID=1000 bentopdf-nonroot
VariableDescriptionDefault
PUIDUser ID to run as1000
PGIDGroup ID to run as1000

[!NOTE] The standard Dockerfile uses nginx-unprivileged (UID 101) and is recommended for most deployments. Use Dockerfile.nonroot only when you need a specific UID/GID.

For detailed security configuration, see SECURITY.md.

Digital Signature CORS Proxy (Required)

The Digital Signature tool uses a signing library that may need to fetch certificate chain data from certificate authority providers. Since many certificate servers don't include CORS headers (and often serve over HTTP, which is blocked by browsers on HTTPS sites), a proxy is required for this feature to work.

When is the proxy needed?

  • Only when using the Digital Signature tool
  • Only if your certificate requires fetching issuer certificates from external URLs
  • Self-signed certificates typically don't need this

Deploying the CORS Proxy (Cloudflare Workers):

  1. Navigate to the cloudflare directory:

    bash
    cd cloudflare
    
  2. Login to Cloudflare (if not already):

    bash
    npx wrangler login
    
  3. Update allowed origins β€” open cors-proxy-worker.js and change ALLOWED_ORIGINS to your domain:

    js
    const ALLOWED_ORIGINS = [
      'https://your-domain.com',
      'https://www.your-domain.com',
    ];
    

    [!IMPORTANT] Without this step, the proxy will reject all requests from your site with a 403 error. The default only allows bentopdf.com.

  4. Deploy the worker:

    bash
    npx wrangler deploy
    
  5. Note your worker URL (e.g., https://bentopdf-cors-proxy.your-subdomain.workers.dev)

  6. Set the environment variable when building:

    bash
    VITE_CORS_PROXY_URL=https://your-worker-url.workers.dev npm run build
    

    Or with Docker:

    bash
    export VITE_CORS_PROXY_URL="https://your-worker-url.workers.dev"
    DOCKER_BUILDKIT=1 docker build \
     --secret id=VITE_CORS_PROXY_URL,env=VITE_CORS_PROXY_URL \
     -t your-bentopdf .
    

Production Security Features

The CORS proxy includes several security measures:

FeatureDescription
Origin ValidationOnly allows requests from domains listed in ALLOWED_ORIGINS
URL RestrictionsOnly allows certificate URLs (.crt, .cer, .pem, /certs/, /ocsp, /crl)
Private IP BlockingBlocks IPv4/IPv6 private ranges, link-local, loopback, decimal IPs, and cloud metadata
Content-Type SafetyOnly returns safe certificate MIME types, blocks upstream content-type injection
File Size LimitStreams response with 10MB limit, aborts mid-download if exceeded
Rate Limiting60 requests per IP per minute (requires KV)
HMAC SignaturesOptional client-side signing (deters casual abuse)

Enabling Rate Limiting (Recommended)

Rate limiting requires Cloudflare KV storage:

bash
cd cloudflare

# Create KV namespace
npx wrangler kv namespace create "RATE_LIMIT_KV"

# Copy the returned ID and add to wrangler.toml:
# [[kv_namespaces]]
# binding = "RATE_LIMIT_KV"
# id = "YOUR_ID_HERE"

# Redeploy
npx wrangler deploy

Free tier limits: 100,000 reads/day, 1,000 writes/day (~300-500 signatures/day)

HMAC Signature Verification (Optional)

[!WARNING] Client-side secrets can be extracted from bundled JavaScript. For production deployments with sensitive requirements, use your own backend server to proxy requests instead of embedding secrets in frontend code.

BentoPDF uses client-side HMAC as a deterrent against casual abuse, but accepts this tradeoff due to its fully client-side architecture. To enable:

bash
# Generate a secret
openssl rand -hex 32

# Set on Cloudflare Worker
npx wrangler secret put PROXY_SECRET

# Set in build environment
VITE_CORS_PROXY_SECRET=your-secret npm run build

# Or with Docker (optional; URL secret also shown for completeness)
export VITE_CORS_PROXY_URL="https://your-worker-url.workers.dev"
export VITE_CORS_PROXY_SECRET="your-secret"
DOCKER_BUILDKIT=1 docker build \
  --secret id=VITE_CORS_PROXY_URL,env=VITE_CORS_PROXY_URL \
  --secret id=VITE_CORS_PROXY_SECRET,env=VITE_CORS_PROXY_SECRET \
  -t your-bentopdf .

πŸ“¦ Version Management

BentoPDF supports semantic versioning with multiple container tags available:

GitHub Container Registry (Recommended):

  • Latest: ghcr.io/alam00000/bentopdf:latest
  • Specific Version: ghcr.io/alam00000/bentopdf:1.0.0
  • Version with Prefix: ghcr.io/alam00000/bentopdf:v1.0.0

Docker Hub:

  • Latest: bentopdfteam/bentopdf:latest
  • Specific Version: bentopdfteam/bentopdf:1.0.0
  • Version with Prefix: bentopdfteam/bentopdf:v1.0.0

Quick Release

bash
# Release a patch version (0.0.1 β†’ 0.0.2)
npm run release

# Release a minor version (0.0.1 β†’ 0.1.0)
npm run release:minor

# Release a major version (0.0.1 β†’ 1.0.0)
npm run release:major

For detailed release instructions, see RELEASE.md.

πŸš€ Development Setup

Option 1: Run with npm

  1. Clone the Repository:

    bash
    git clone https://github.com/alam00000/bentopdf.git
    cd bentopdf
    
  2. Install Dependencies:

    bash
    npm install
    
  3. Run the Development Server:

    bash
    npm run dev
    

    The application will be available at http://localhost:5173.

    The dev server binds to localhost only by default. To expose it on your LAN (e.g. for mobile device testing), set VITE_DEV_HOST=0.0.0.0 npm run dev. The built-in CORS proxy at /cors-proxy?url= restricts targets to a known host allowlist; to permit additional hosts in development, set VITE_DEV_CORS_PROXY_EXTRA_HOSTS="host1.example.com,host2.example.com".

Option 2: Build and Run with Docker Compose

  1. Clone the Repository:

    bash
    git clone https://github.com/alam00000/bentopdf.git
    cd bentopdf
    
  2. Run with Docker Compose:

    bash
    docker-compose -f docker-compose.dev.yml up -d
    

    The application will be available at http://localhost:3000.

    [!NOTE] After making any local changes to the code, rebuild the Docker image using:

    bash
    docker-compose -f docker-compose.dev.yml up --build -d
    

    This ensures your latest changes are applied inside the container.


πŸ› οΈ Tech Stack & Background

BentoPDF was originally built using HTML, CSS, and vanilla JavaScript. As the project grew, it was migrated to a modern stack for better maintainability and scalability:

  • Vite: A fast build tool for modern web development.
  • TypeScript: For type safety and an improved developer experience.
  • Tailwind CSS: For rapid and consistent UI development.

[!NOTE] Some parts of the codebase still use legacy structures from the original implementation. Contributors should expect gradual updates as testing and refactoring continue.


πŸ—ΊοΈ Roadmap

Planned Features:

  • HTML to PDF: Convert HTML files or web pages into PDF documents.
  • Markdown to PDF: Enhanced support for converting .md files to PDF.
  • Convert to PDF/A: Convert PDFs to the PDF/A archival format.
  • Edit PDF Content: Directly edit text and other content within your PDF.
  • PDF to Office: Converts PDF files into editable Word, Excel, and PowerPoint formats.
  • Office to PDF: Converts Word, Excel, and PowerPoint documents into optimized PDFs.

Contributions and discussions on the roadmap are welcome! Join the conversation via Discord.


🀝 Contributing

We welcome contributions from the community! Here's how you can get started:

  1. Fork the repository and create your branch from main.
  2. Follow the Getting Started steps to set up your local environment.
  3. Make your changes and commit them with a clear message.
  4. Open a Pull Request and describe the changes you've made.

Have an idea for a new tool or an improvement? Open an issue to discuss it first.

πŸ“– Contributing to Documentation

Our documentation is built with VitePress. Here's how to contribute:

bash
# Install dependencies
npm install

# Start docs dev server
npm run docs:dev

# Build docs for production
npm run docs:build

# Preview the built docs
npm run docs:preview

Documentation files are in the docs/ folder:

  • docs/index.md - Home page
  • docs/getting-started.md - Getting started guide
  • docs/tools/ - Tools reference
  • docs/self-hosting/ - Self-hosting guides (Docker, Vercel, Netlify, Hostinger, etc.)
  • docs/contributing.md - Contributing guide
  • docs/licensing.md - Commercial license info

Special Thanks

BentoPDF wouldn't be possible without the amazing open-source tools and libraries that power it. We'd like to extend our heartfelt thanks to the creators and maintainers of:

Bundled Libraries:

  • PDFLib.js – For enabling powerful client-side PDF manipulation.
  • PDF.js – For the robust PDF rendering engine in the browser.
  • PDFKit – For creating and editing PDFs with ease.
  • EmbedPDF – For seamless PDF editing in pure JS.
  • Cropper.js – For intuitive image cropping functionality.
  • Vite – For lightning-fast development and build tooling.
  • Tailwind CSS – For rapid, flexible, and beautiful UI styling.
  • qpdf and qpdf-wasm – For inspecting, repairing, and transforming PDF files.
  • LibreOffice – For powerful document conversion capabilities.
  • wasm-vips – For advanced TIFF encoding with compression (LZW, Deflate, CCITT Group 4).
  • pixelmatch – For fast, accurate image comparison and diff detection.
  • diff – For computing text differences.
  • microdiff – For lightweight, fast object diffing.

AGPL Libraries (Pre-configured via CDN):

  • CoherentPDF (cpdf) – For content-preserving PDF operations. (AGPL-3.0)
  • PyMuPDF – For high-performance PDF manipulation and data extraction. (AGPL-3.0)
  • Ghostscript (GhostPDL) – For PDF/A conversion and font outlining. (AGPL-3.0)

[!NOTE] AGPL-licensed libraries are not bundled in BentoPDF's source code. They are loaded at runtime from CDN (pre-configured) and can be overridden via environment variables or Advanced Settings.

Your work inspires and empowers developers everywhere. Thank you for making open-source amazing!