PDF Toolbox — Everything Runs Locally A full set of PDF tools that run entirely in your browser — nothing is uploaded to a server. Merge, split, organize, rotate, crop, resize, convert, extract and clean, all client-side.
{{ busy ? 'Cancel and clear' : 'Clear / choose another file' }} Back to previous step Processing… Settings are locked until this operation finishes.
Page copying keeps standard form fields and renames conflicting field names. Bookmarks, internal navigation links, page labels, attachments and accessibility structure are not transferred.
{{ CATEGORY_LABELS[cat] }}
← All tools {{ result.error }}
Done {{ result.pageCount }} pages · {{ result.sizeLabel }}
Output file name ⬇ Download PDF
← All tools {{ splitResult.error }}
Done {{ splitResult.files.length }} file(s) {{ splitResult.oversized }} single-page file(s) exceed the target and cannot be split further.
Output file name ⬇ Download {{ splitResult.files.length === 1 ? 'PDF' : 'ZIP' }}
← All tools Split by pages — click between pages to divide
Drop a PDF file here or click to choose
Loading PDF…
Click the gap after a page to toggle a split point. {{ clickPicker.dividerGroups().length }} file(s) will be produced.
Page {{ pos + 1 }}
{{ pos+1 }}
{{ clickPicker.dividers.has(pos) ? '✂ split' : '+ split' }} {{ clickRunning ? 'Working…' : '▶ Run' }}
{{ clickResult.error }}
Done {{ clickResult.files.length }} file(s)
Output file name ⬇ Download {{ clickResult.files.length === 1 ? 'PDF' : 'ZIP' }}
← All tools ← Back to {{ chainSteps[chainSteps.length-1].label }} From: {{ chainSteps.map(s => s.label).join(' → ') }}
{{ orgMode==='organize' ? 'Organize pages' : orgMode==='extract' ? 'Extract pages' : 'Delete pages' }}
Organize Extract selected Delete selected
Drop a PDF file here or click to choose
Loading PDF…
Click a page to select it (shift/ctrl to extend), drag tiles to reorder. Inserted pages land after the last selected tile.
Click pages to select them (shift/ctrl to extend), then Run.
+ Insert blank page + Insert pages from file… 🗑 Delete selected ({{ orgSelectedCount }})
blank Page {{ pos + 1 }} {{ pos+1 }}
{{ orgRunning ? 'Working…' : '▶ Run' }}
{{ orgResult.error }}
Done {{ orgResult.pageCount }} pages · {{ orgResult.sizeLabel }}
Output file name ⬇ Download PDF Continue to {{ chainNextLabel }} →
← All tools ← Back to {{ chainSteps[chainSteps.length-1].label }} From: {{ chainSteps.map(s => s.label).join(' → ') }}
Rotate / Crop
Crop hides margins; it does not erase their content and is not redaction.
Rotate Crop
Drop a PDF file here or click to choose
Loading PDF…
Apply to: All pages Selected ({{ rcSelectedCount }})
↺ Rotate left 90° ↻ Rotate right 90° ↕ Rotate 180°
Click a thumbnail to select it for "Selected" scope (shift/ctrl to extend).
Page {{ pos + 1 }} {{ pos+1 }}{{ rcRotations[pageIndex] ? ' (' + rcRotations[pageIndex] + '°)' : '' }}
Apply to all pages (otherwise just the previewed page)Preview page: ← of {{ rcPicker.pages.length }} →
Rendering page {{ cropPreviewIndex + 1 }}…
Orange area is what stays after cropping — preview shown on page {{ cropPreviewIndex + 1 }}.
{{ cropValidationError }}
{{ rcRunning ? 'Working…' : '▶ Run' }}
{{ rcResult.error }}
Done {{ rcResult.pageCount }} pages · {{ rcResult.sizeLabel }}
Output file name ⬇ Download PDF
← All tools {{ rnResult.error }}
Done {{ rnResult.pageCount }} pages · {{ rnResult.sizeLabel }}
Output file name ⬇ Download PDF
← All tools {{ ipResult.error }}
Done {{ ipResult.pageCount }} pages · {{ ipResult.sizeLabel }}
Done {{ ipResult.files.length }} file(s) · {{ ipResult.sizeLabel }}
Output file name ⬇ Download {{ ipMode === 'images-to-pdf' ? 'PDF' : (ipResult.files.length === 1 ? ipFormat.toUpperCase() : 'ZIP') }}
← All tools Extract text
Drop a PDF file here or click to choose
{{ etFile.name }} — {{ etFile.pageCount }} pages
Loading PDF…
Output: One .txt file One .txt per page (zip)
Pages
This pulls the embedded text layer only — a scanned/image-only PDF will correctly produce an empty result, not an error. Turn on OCR below to read scans.
OCR scanned pages Include comments & highlights
Comment, note and highlight contents are appended per page, labelled [Annotation · subtype] — the reader's notes, not the document's own text.
Run OCR on: Pages with no text layer Images inside pages Every page
Keeps the text layer untouched and reads only the rasters embedded in each page — screenshots, charts, captions baked into artwork. Decorative images below 200×200 are skipped, and a logo repeated across pages is recognised once.
⚠ This PDF already has a text layer. Every page throws it away and keeps OCR's guess instead — expect lost headings, merged columns and misread words, plus stray text picked out of screenshots. Use it only when the existing text layer is itself broken.
Languages: {{ choice.label }}
OCR runs in your browser — the engine (~3 MB) and each language model (0.5–2 MB) download once on first use, then stay cached. Recognition takes a few seconds per page and its output is a best guess: pages read by OCR are labelled in the result.
{{ etRunning ? 'Working…' : '▶ Run' }}
{{ etProgress }}
{{ etResult.error }}
Done {{ etResult.totalChars }} chars extracted across {{ etFile.pageCount }} page(s)
🔍 Page{{ etResult.ocrPages.length > 1 ? 's' : '' }} {{ etResult.ocrPages.join(', ') }} read by OCR — recognised text, not the document's own text layer.
🔍 {{ etResult.ocrImageCount }} image{{ etResult.ocrImageCount > 1 ? 's' : '' }} read by OCR on page{{ etResult.ocrImagePages.length > 1 ? 's' : '' }} {{ etResult.ocrImagePages.join(', ') }} — marked [Image n — OCR] in the output, below each page's own text.
💬 {{ etResult.annotationCount }} annotation{{ etResult.annotationCount > 1 ? 's' : '' }} on page{{ etResult.annotationPages.length > 1 ? 's' : '' }} {{ etResult.annotationPages.join(', ') }} — marked [Annotation] in the output.
⚠ No text found on page{{ etResult.emptyPages.length > 1 ? 's' : '' }} {{ etResult.emptyPages.join(', ') }}{{ etOcrEnabled ? ' — OCR could not read them either.' : ' (likely scanned) — expected for image-only pages, not an error. Turn on OCR to read them.' }}
Output file name ⬇ Download {{ etResult.files.length === 1 ? 'TXT' : 'ZIP' }}
← All tools {{ eiResult.error }}
Done {{ eiResult.imagesExtracted }} image(s) found{{ eiResult.imagesSkipped ? ', ' + eiResult.imagesSkipped + ' skipped' : '' }}
No embedded images found in this PDF.
⦿ {{ eiResult.imagesSmall }} image(s) skipped by the minimum size filter — set Min size to Any to keep them.
p{{ img.pageNum }} · {{ img.width }}×{{ img.height }}
⬇
Output file name ⬇ Download {{ eiResult.images.length === 1 ? (eiFormat === 'jpeg' ? 'JPG' : 'PNG') : 'ZIP' }}
← All tools {{ evResult.error }}
Done {{ evResult.files.length }} SVG file(s) · {{ evResult.sizeLabel }}
No vector drawings found in this PDF.
{{ evResult.unsupportedEffects }} shapes with unsupported transparency or blend effects were omitted. This SVG is a partial extraction.
⚠ {{ evResult.unsupportedColors }} paint(s) with pattern or gradient colors were skipped — only solid colors can be reconstructed.
p{{ f.pageNum }}· fill · {{ f.w }}×{{ f.h }}
⬇
Output file name ⬇ Download {{ evResult.files.length === 1 ? 'SVG' : 'ZIP' }}
← All tools Metadata / Forms / Clean
Edit metadata Flatten forms Clean PDF
Drop a PDF file here or click to choose
{{ mfFile.name }} — {{ mfFile.pageCount }} pages
Loading PDF…
This PDF has {{ mfFile.formFieldCount }} form field(s).
Nothing to flatten — this PDF has no fillable form fields.
Remove document Info and XMP metadata, including dates and producer
Remove annotations, links and form fields ({{ mfFile.annotationCount }} annotations found)
Does not touch embedded JavaScript or file attachments.
{{ mfRunning ? 'Working…' : '▶ Run' }}
{{ mfResult.error }}
Done {{ mfResult.pageCount }} pages · {{ mfResult.sizeLabel }}
Output file name ⬇ Download PDF
← All tools Fill forms
Drop a PDF file here or click to choose
{{ ffFile.name }} — {{ ffFile.pageCount }} pages
Loading PDF…
This PDF has no fillable form fields.
{{ ffFields.length }} field(s). Unchanged values and appearances are preserved; read-only fields are locked. New text must be supported by the built-in Latin font. Unsupported changes are reported and left unchanged.
{{ ffRunning ? 'Working…' : '▶ Run' }}
{{ ffResult.error }}
Done {{ ffResult.applied }} of {{ ffResult.fieldCount }} field(s) written · {{ ffResult.sizeLabel }}
⚠ {{ ffResult.skipped.length }} field(s) could not be written: {{ ffResult.skipped.join(', ') }}
Output file name ⬇ Download PDF
← All tools {{ wmResult.error }}
Done {{ wmResult.stamped }} of {{ wmResult.pageCount }} page(s) stamped · {{ wmResult.sizeLabel }}
Output file name ⬇ Download PDF
← All tools Page numbers
Drop a PDF file here or click to choose
{{ pnFile.name }} — {{ pnFile.pageCount }} pages
Loading PDF…
Pages
Numbers follow each page's real position in the document, N is the document's total page count.
{{ pnRunning ? 'Working…' : '▶ Run' }}
{{ pnResult.error }}
Done {{ pnResult.numbered }} of {{ pnResult.pageCount }} page(s) numbered · {{ pnResult.sizeLabel }}
Output file name ⬇ Download PDF
← All tools {{ coResult.error }}
Done {{ coResult.beforeLabel }} → {{ coResult.sizeLabel }}· −{{ coResult.savedPercent }}%
{{ coResult.recompressed }} of {{ coResult.found }} image(s) re-encoded, {{ coResult.kept }} kept (would not shrink) , {{ coResult.failed }} skipped (could not decode) .
{{ coResult.total }} raster objects found; {{ coResult.unsupported.length }} left untouched (unsupported encoding, color space or transparency).
These settings did not reduce the file size. The download contains the original PDF.
Preview page Compare original / compressed
{{ coPreview.error }}
Original Compressed Output file name ⬇ Download PDF
← All tools Remove Password
Drop a password-protected PDF here or click to choose
{{ rpFile.name }}
Checking PDF encryption…
Encrypted — {{ rpEncryptionInfo.algorithm }} ({{ rpEncryptionInfo.keyLength }}-bit) Not encrypted — this PDF has no password
Password
The password is only used in your browser to unlock the file — it is never sent anywhere. The downloaded copy opens with no password at all.
{{ rpRunning ? 'Unlocking…' : '▶ Remove password' }}
{{ rpResult.error }}
Unlocked {{ rpResult.pageCount }} pages · {{ rpResult.sizeLabel }}
Output file name ⬇ Download PDF
← All tools {{ ppResult.error }}
Protected {{ ppResult.pageCount }} pages · {{ ppResult.sizeLabel }}
Output file name ⬇ Download PDF
Continue with {{ tool.label }} Use this PDF
About PDF Toolbox
Every operation here runs with pdf-lib and pdf.js loaded straight into your browser tab — files never leave your machine, there is no upload step and no server-side processing.
Privacy: 100% client-side. Nothing you load into this tool is sent anywhere.