feat(multi-tool): replace the page grid with the pdfium organizer

This commit is contained in:
alam00000
2026-10-30 16:11:50 +05:30
parent 3ce02c9ef7
commit 6ac0599ce3
4 changed files with 277 additions and 1753 deletions
+35 -22
View File
@@ -1,47 +1,60 @@
---
title: PDF Multi Tool
description: An all-in-one page manager for merging, splitting, rotating, duplicating, deleting, and reordering PDF pages.
description: An all-in-one page organizer for merging, splitting, rotating, duplicating, deleting, reordering, and resizing PDF pages.
---
# PDF Multi Tool
A unified workspace for every page-level PDF operation. Upload one or more PDFs and work with all their pages in a single visual grid. Rotate, reorder, duplicate, delete, insert new PDFs, add blank pages, and split the result -- all without leaving the tool.
A unified workspace for every page-level PDF operation. Upload one or more PDFs and work with all their pages in a single visual grid. Rotate, reorder, duplicate, delete, insert PDFs, images, and blank pages, renumber, resize, and split the result, all without leaving the tool.
## How It Works
1. Click **Upload PDFs** or drag files onto the upload area. Multiple files are supported.
2. All pages from every uploaded file appear in a scrollable grid with thumbnails.
3. Use the toolbar or per-page action buttons to manipulate pages.
4. Select pages using the checkbox on each card, then apply bulk operations.
5. Mark split points with the scissors icon to divide the output into separate files.
6. Click **Export PDF** to download the final result. If split markers are present, the output is a ZIP of multiple PDFs.
1. Click **Select Files** or drag files onto the upload area. Multiple files are supported, and they are merged in upload order.
2. All pages appear in a scrollable grid with thumbnails.
3. Click a page to select it. Use Shift-click for a range and Cmd/Ctrl-click to add or remove single pages.
4. Use the organizer toolbar or right-click a page for page actions.
5. Click **Export PDF** to download the final result.
## Features
- **Merge**: Upload multiple PDFs and they combine into one workspace automatically
- **Reorder**: Drag and drop page cards to rearrange the sequence
- **Rotate**: Per-page or bulk rotation in 90-degree increments (left or right)
- **Duplicate**: Clone any page with one click
- **Delete**: Remove individual pages or bulk-delete selected ones
- **Insert PDF**: Add a new PDF after any specific page
- **Add Blank Page**: Append a blank A4 page at the end
- **Split**: Place split markers between pages, then export as a ZIP of separate PDFs
- **Select All / Deselect All**: Quick selection for bulk operations
- **Undo / Redo**: Full undo/redo history for every operation
- **Download selected**: Export only the selected pages as a standalone PDF
- **Merge**: Upload multiple PDFs and images and they combine into one document automatically
- **Reorder**: Drag and drop pages to rearrange the sequence, or reverse the whole document
- **Rotate**: Rotate selected pages left or right in 90-degree steps
- **Duplicate**: Copy selected pages and place the copies right after them
- **Delete**: Remove the selected pages
- **Insert**: Add pages from another PDF or image, a blank page, pages from the clipboard, or alternate the pages of a second PDF with the current ones
- **Replace**: Swap a page for the first page of another PDF
- **Cut, copy, and paste**: Move or copy pages within the document
- **Extract**: Download the selected pages as a PDF or as images
- **Page Numbers**: Set page labels such as i, ii, iii or A-1, A-2 for a range of pages
- **Page Size**: Fit pages to a standard paper size or a custom size
- **Split**: Divide the document by page ranges, odd and even pages, every page, bookmarks, every N pages, or file size
- **Undo / Redo**: Every page operation can be undone and redone
- Auto-repairs corrupted PDFs before loading
## Keyboard Shortcuts
| Action | Shortcut |
| ------------------- | ----------------------------- |
| Rotate right / left | R / Shift+R |
| Select all | Cmd/Ctrl+A |
| Cut, copy, paste | Cmd/Ctrl+X, C, V |
| Duplicate | Cmd/Ctrl+D |
| Delete | Delete or Backspace |
| Undo / Redo | Cmd/Ctrl+Z / Cmd/Ctrl+Shift+Z |
| Move selection | Arrow keys, Home, End |
## Use Cases
- Building a custom document from pages across multiple source PDFs
- Cleaning up a scanned document by deleting blank pages, rotating sideways scans, and reordering sections
- Splitting a large PDF at chapter boundaries using visual split markers
- Splitting a large PDF at chapter boundaries using its bookmarks
- Quick page-level editing without needing a full PDF editor
## Tips
- The undo/redo system tracks every operation. Use it freely -- you cannot accidentally destroy your work.
- Split markers appear as dashed blue lines between pages. Place them visually where you want the document divided, then export to get a ZIP.
- The undo/redo history tracks every page operation. Use it freely, you cannot accidentally destroy your work.
- Use **Insert > From File** or drop more PDFs or images onto the page grid at any time to add them to the document.
- For simple merge or split tasks, the dedicated [Merge PDF](./merge-pdf) or [Split PDF](./split-pdf) tools may be faster. The Multi Tool shines when you need multiple operations in one session.
## Related Tools
File diff suppressed because it is too large Load Diff
+46 -236
View File
@@ -132,231 +132,51 @@
<div class="flex flex-col flex-1 overflow-hidden relative">
<!-- Toolbar -->
<div
class="bg-gray-800 border-b border-gray-700 p-2 sm:p-3 toolbar-container flex-none z-20"
class="bg-gray-800 border-b border-gray-700 flex items-center gap-2 px-2 sm:px-4 py-2 toolbar-container flex-none z-20"
>
<div
class="flex flex-wrap items-center justify-center sm:justify-start gap-2 bg-gray-900 p-2 sm:p-4 rounded-lg w-full"
<button
id="reset-btn"
style="display: none"
class="flex items-center gap-1.5 bg-gray-700 hover:bg-gray-600 text-white px-2.5 sm:px-3 py-1.5 rounded-lg text-sm transition-colors"
>
<!-- Upload -->
<button
id="upload-pdfs-btn"
class="flex items-center gap-1 sm:gap-2 bg-indigo-600 hover:bg-indigo-700 text-white px-2 sm:px-3 md:px-4 py-1 sm:py-1.5 md:py-2 rounded text-xs sm:text-sm md:text-base"
<i data-lucide="refresh-ccw" class="w-4 h-4"></i>
<span class="hidden min-[390px]:inline" data-i18n="multiTool.reset"
>Reset</span
>
<i data-lucide="upload" class="w-3 h-3 sm:w-4 sm:h-4"></i>
<span class="hidden sm:inline" data-i18n="multiTool.uploadPdfs"
>Upload PDFs</span
>
<span class="sm:hidden" data-i18n="multiTool.upload">Upload</span>
</button>
</button>
<div class="border-l border-gray-600 h-5 sm:h-6 mx-1"></div>
<button
id="export-pdf-btn"
style="display: none"
class="flex items-center gap-1.5 bg-indigo-600 hover:bg-indigo-700 text-white px-2.5 sm:px-3 py-1.5 rounded-lg text-sm transition-colors"
>
<i data-lucide="download" class="w-4 h-4"></i>
<span data-i18n="multiTool.exportPdf">Export PDF</span>
</button>
<!-- Add Blank -->
<button
id="add-blank-page-btn"
class="flex items-center gap-1 sm:gap-2 bg-gray-700 hover:bg-gray-600 text-white px-2 sm:px-3 md:px-4 py-1 sm:py-1.5 md:py-2 rounded text-xs sm:text-sm md:text-base"
>
<i data-lucide="file-plus" class="w-3 h-3 sm:w-4 sm:h-4"></i>
<span class="hidden sm:inline" data-i18n="multiTool.addBlankPage"
>Add Blank Page</span
>
</button>
<div class="flex-1"></div>
<div class="border-l border-gray-600 h-5 sm:h-6 mx-1"></div>
<span
class="text-gray-400 text-xs sm:text-sm md:inline"
data-i18n="multiTool.edit"
>Edit:</span
>
<button
id="faq-open-btn"
type="button"
class="flex items-center gap-1.5 bg-gray-700 hover:bg-gray-600 text-white px-2.5 sm:px-3 py-1.5 rounded-lg text-sm transition-colors"
aria-label="FAQ"
aria-haspopup="dialog"
aria-controls="faq-modal"
aria-expanded="false"
>
<i data-lucide="circle-help" class="w-4 h-4"></i>
<span class="hidden sm:inline">FAQ</span>
</button>
<!-- Undo / Redo / Reset -->
<button
id="undo-btn"
class="flex items-center gap-1 sm:gap-2 bg-gray-700 hover:bg-gray-600 text-white px-2 sm:px-3 md:px-4 py-1 sm:py-1.5 md:py-2 rounded text-xs sm:text-sm md:text-base"
>
<i data-lucide="undo-2" class="w-3 h-3 sm:w-4 sm:h-4"></i>
<span class="hidden lg:inline" data-i18n="multiTool.undo"
>Undo</span
>
</button>
<button
id="redo-btn"
class="flex items-center gap-1 sm:gap-2 bg-gray-700 hover:bg-gray-600 text-white px-2 sm:px-3 md:px-4 py-1 sm:py-1.5 md:py-2 rounded text-xs sm:text-sm md:text-base"
>
<i data-lucide="redo-2" class="w-3 h-3 sm:w-4 sm:h-4"></i>
<span class="hidden lg:inline" data-i18n="multiTool.redo"
>Redo</span
>
</button>
<button
id="reset-btn"
class="flex items-center gap-1 sm:gap-2 bg-gray-700 hover:bg-gray-600 text-white px-2 sm:px-3 md:px-4 py-1 sm:py-1.5 md:py-2 rounded text-xs sm:text-sm md:text-base"
>
<i data-lucide="refresh-ccw" class="w-3 h-3 sm:w-4 sm:h-4"></i>
<span class="hidden lg:inline" data-i18n="multiTool.reset"
>Reset</span
>
</button>
<div class="border-l border-gray-600 h-5 sm:h-6 mx-1"></div>
<!-- Selection -->
<span
class="text-gray-400 text-xs sm:text-sm md:inline"
data-i18n="multiTool.selection"
>Selection:</span
>
<button
id="select-all-btn"
class="flex items-center gap-1 sm:gap-2 bg-gray-700 hover:bg-gray-600 text-white px-2 sm:px-3 md:px-4 py-1 sm:py-1.5 md:py-2 rounded text-xs sm:text-sm md:text-base"
>
<i data-lucide="check-square" class="w-3 h-3 sm:w-4 sm:h-4"></i>
<span class="hidden lg:inline" data-i18n="multiTool.selectAll"
>Select All</span
>
</button>
<button
id="deselect-all-btn"
class="flex items-center gap-1 sm:gap-2 bg-gray-700 hover:bg-gray-600 text-white px-2 sm:px-3 md:px-4 py-1 sm:py-1.5 md:py-2 rounded text-xs sm:text-sm md:text-base"
>
<i data-lucide="square" class="w-3 h-3 sm:w-4 sm:h-4"></i>
<span class="hidden lg:inline" data-i18n="multiTool.deselectAll"
>Deselect All</span
>
</button>
<div class="border-l border-gray-600 h-5 sm:h-6 mx-1"></div>
<!-- Rotate -->
<span
class="text-gray-400 text-xs sm:text-sm md:inline"
data-i18n="multiTool.rotate"
>Rotate:</span
>
<button
id="bulk-rotate-left-btn"
class="flex items-center gap-1 sm:gap-2 bg-gray-700 hover:bg-gray-600 text-white px-2 sm:px-3 md:px-4 py-1 sm:py-1.5 md:py-2 rounded text-xs sm:text-sm md:text-base"
>
<i data-lucide="rotate-ccw" class="w-3 h-3 sm:w-4 sm:h-4"></i>
<span class="hidden xl:inline" data-i18n="multiTool.rotateLeft"
>Left</span
>
</button>
<button
id="bulk-rotate-btn"
class="flex items-center gap-1 sm:gap-2 bg-gray-700 hover:bg-gray-600 text-white px-2 sm:px-3 md:px-4 py-1 sm:py-1.5 md:py-2 rounded text-xs sm:text-sm md:text-base"
>
<i data-lucide="rotate-cw" class="w-3 h-3 sm:w-4 sm:h-4"></i>
<span class="hidden xl:inline" data-i18n="multiTool.rotateRight"
>Right</span
>
</button>
<div class="border-l border-gray-600 h-5 sm:h-6 mx-1"></div>
<span
class="text-gray-400 text-xs sm:text-sm md:inline"
data-i18n="multiTool.transform"
>Transform:</span
>
<!-- Duplicate / Split -->
<button
id="bulk-duplicate-btn"
class="flex items-center gap-1 sm:gap-2 bg-gray-700 hover:bg-gray-600 text-white px-2 sm:px-3 md:px-4 py-1 sm:py-1.5 md:py-2 rounded text-xs sm:text-sm md:text-base"
>
<i data-lucide="copy" class="w-3 h-3 sm:w-4 sm:h-4"></i>
<span class="hidden xl:inline" data-i18n="multiTool.duplicate"
>Duplicate</span
>
</button>
<button
id="bulk-split-btn"
class="flex items-center gap-1 sm:gap-2 bg-gray-700 hover:bg-gray-600 text-white px-2 sm:px-3 md:px-4 py-1 sm:py-1.5 md:py-2 rounded text-xs sm:text-sm md:text-base"
>
<i data-lucide="scissors" class="w-3 h-3 sm:w-4 sm:h-4"></i>
<span class="hidden xl:inline" data-i18n="multiTool.split"
>Split</span
>
</button>
<div class="border-l border-gray-600 h-5 sm:h-6 mx-1"></div>
<span
class="text-gray-400 text-xs sm:text-sm md:inline"
data-i18n="multiTool.clear"
>Clear:</span
>
<!-- Delete -->
<button
id="bulk-delete-btn"
class="flex items-center gap-1 sm:gap-2 bg-red-500 hover:bg-red-700 text-white px-2 sm:px-3 md:px-4 py-1 sm:py-1.5 md:py-2 rounded text-xs sm:text-sm md:text-base"
>
<i data-lucide="trash-2" class="w-3 h-3 sm:w-4 sm:h-4"></i>
<span class="hidden xl:inline" data-i18n="multiTool.delete"
>Delete</span
>
</button>
<div class="border-l border-gray-600 h-5 sm:h-6 mx-1"></div>
<!-- Download -->
<span
class="text-gray-400 text-xs sm:text-sm md:inline"
data-i18n="multiTool.download"
>Download:</span
>
<button
id="bulk-download-btn"
class="flex items-center gap-1 sm:gap-2 bg-green-600 hover:bg-green-700 text-white px-2 sm:px-3 md:px-4 py-1 sm:py-1.5 md:py-2 rounded text-xs sm:text-sm md:text-base"
>
<i data-lucide="download" class="w-3 h-3 sm:w-4 sm:h-4"></i>
<span
class="hidden xl:inline"
data-i18n="multiTool.downloadSelected"
>Download Selected</span
>
</button>
<button
id="export-pdf-btn"
class="flex items-center gap-1 sm:gap-2 bg-indigo-600 hover:bg-indigo-700 text-white px-2 sm:px-3 md:px-4 py-1 sm:py-1.5 md:py-2 rounded text-xs sm:text-sm md:text-base"
>
<i data-lucide="download" class="w-3 h-3 sm:w-4 sm:h-4"></i>
<span class="hidden xl:inline" data-i18n="multiTool.exportPdf"
>Export PDF</span
>
</button>
<div class="border-l border-gray-600 h-5 sm:h-6 mx-1"></div>
<button
id="faq-open-btn"
type="button"
class="flex items-center gap-1 sm:gap-2 bg-gray-700 hover:bg-gray-600 text-white px-2 sm:px-3 md:px-4 py-1 sm:py-1.5 md:py-2 rounded text-xs sm:text-sm md:text-base"
aria-label="FAQ"
aria-haspopup="dialog"
aria-controls="faq-modal"
aria-expanded="false"
>
<i data-lucide="circle-help" class="w-3 h-3 sm:w-4 sm:h-4"></i>
<span class="hidden sm:inline">FAQ</span>
</button>
<button
id="close-tool-btn"
class="flex items-center gap-1 sm:gap-2 bg-gray-700 hover:bg-gray-600 text-white px-2 sm:px-3 md:px-4 py-1 sm:py-1.5 md:py-2 rounded text-xs sm:text-sm md:text-base"
aria-label="Close"
>
<i data-lucide="x" class="w-3 h-3 sm:w-4 sm:h-4"></i>
<span class="hidden sm:inline" data-i18n="common.close">Close</span>
</button>
</div>
<button
id="close-tool-btn"
class="flex items-center gap-1.5 bg-gray-700 hover:bg-gray-600 text-white px-2.5 sm:px-3 py-1.5 rounded-lg text-sm transition-colors"
aria-label="Close"
>
<i data-lucide="x" class="w-4 h-4"></i>
<span class="hidden sm:inline" data-i18n="common.close">Close</span>
</button>
</div>
<!-- Content Area -->
@@ -398,12 +218,9 @@
Select Files
</button>
</div>
<div
id="pages-container"
class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-3 sm:gap-4"
></div>
</div>
<div id="organize-viewer" class="hidden flex-1 min-h-0"></div>
</div>
{{> footer-compact }}
@@ -463,13 +280,6 @@
</div>
<!-- Hidden File Input -->
<input
type="file"
id="insert-pdf-input"
accept="application/pdf"
class="hidden"
/>
<!-- Scripts -->
<!-- FAQ Modal -->
<div
@@ -534,8 +344,8 @@
<p class="faq-d-a">
Drop JPG, PNG, WebP, HEIC, TIFF, BMP, GIF, or SVG files
alongside your PDFs and each image becomes a page you can move,
rotate, or delete like any other. Blank A4 pages can also be
appended with the Add Blank Page button.
rotate, or delete like any other. Blank pages can also be added
from the Insert menu.
</p>
</details>
<details class="faq-d">
@@ -544,10 +354,10 @@
<i data-lucide="plus" class="faq-d-icon"></i>
</summary>
<p class="faq-d-a">
Click the scissors icon on a page to place a split marker after
it; a dashed line shows where the document will be divided.
Export then produces split-documents.zip containing document -
1.pdf, document - 2.pdf, and so on.
Click Split and choose how to divide the document: by page
ranges, odd and even pages, every page, bookmarks, every N
pages, or file size. One part downloads as a PDF; several parts
download together as a ZIP.
</p>
</details>
<details class="faq-d">
+5 -85
View File
@@ -2629,12 +2629,6 @@
],
"fileCount": 1
},
"Downloaded {{length}} PDF files in a ZIP archive.": {
"files": [
"logic/pdf-multi-tool.ts"
],
"fileCount": 1
},
"Downloads started for all files.": {
"files": [
"logic/deskew-pdf-page.ts"
@@ -2895,7 +2889,7 @@
"logic/add-attachments-page.ts",
"logic/add-blank-page-page.ts"
],
"fileCount": 91
"fileCount": 90
},
"Error during execution": {
"files": [
@@ -3292,12 +3286,6 @@
],
"fileCount": 1
},
"Failed to create PDF.": {
"files": [
"logic/pdf-multi-tool.ts"
],
"fileCount": 1
},
"Failed to create PNG blob": {
"files": [
"logic/bmp-to-pdf-page.ts"
@@ -3311,12 +3299,6 @@
],
"fileCount": 2
},
"Failed to create split PDFs.": {
"files": [
"logic/pdf-multi-tool.ts"
],
"fileCount": 1
},
"Failed to decode image: {{name}}": {
"files": [
"utils/images-to-pdf-lib.ts"
@@ -3432,12 +3414,6 @@
],
"fileCount": 2
},
"Failed to insert PDF. The file may be corrupted.": {
"files": [
"logic/pdf-multi-tool.ts"
],
"fileCount": 1
},
"Failed to load Ghostscript from {{normalizedUrl}}: {{msg}}": {
"files": [
"utils/ghostscript-dynamic-loader.ts"
@@ -5414,16 +5390,9 @@
"No Pages": {
"files": [
"logic/delete-pages-page.ts",
"logic/extract-pages-page.ts",
"logic/pdf-multi-tool.ts"
"logic/extract-pages-page.ts"
],
"fileCount": 3
},
"No Selection": {
"files": [
"logic/pdf-multi-tool.ts"
],
"fileCount": 1
"fileCount": 2
},
"No Signatures Found": {
"files": [
@@ -5934,12 +5903,6 @@
],
"fileCount": 1
},
"PDF downloaded successfully.": {
"files": [
"logic/pdf-multi-tool.ts"
],
"fileCount": 1
},
"PDF encrypted successfully with 256-bit AES!": {
"files": [
"logic/protect-pdf-page.ts"
@@ -6222,12 +6185,6 @@
],
"fileCount": 1
},
"Page {{n}} ": {
"files": [
"logic/pdf-multi-tool.ts"
],
"fileCount": 1
},
"Page {{n}} of {{length}}": {
"files": [
"logic/form-creator.ts"
@@ -6745,26 +6702,7 @@
},
"Please select pages to delete.": {
"files": [
"logic/delete-pages-page.ts",
"logic/pdf-multi-tool.ts"
],
"fileCount": 2
},
"Please select pages to duplicate.": {
"files": [
"logic/pdf-multi-tool.ts"
],
"fileCount": 1
},
"Please select pages to mark for splitting.": {
"files": [
"logic/pdf-multi-tool.ts"
],
"fileCount": 1
},
"Please select pages to rotate.": {
"files": [
"logic/pdf-multi-tool.ts"
"logic/delete-pages-page.ts"
],
"fileCount": 1
},
@@ -6780,12 +6718,6 @@
],
"fileCount": 1
},
"Please upload PDFs first.": {
"files": [
"logic/pdf-multi-tool.ts"
],
"fileCount": 1
},
"Please upload a .docx file first.": {
"files": [
"logic/word-to-pdf.ts"
@@ -7693,12 +7625,6 @@
],
"fileCount": 1
},
"Repairing {{name}}...": {
"files": [
"logic/pdf-multi-tool.ts"
],
"fileCount": 1
},
"Replaced \"{{beforeText}}\" with \"{{afterText}}\"": {
"files": [
"compare/worker-api.ts"
@@ -8394,7 +8320,7 @@
"logic/add-blank-page-page.ts",
"logic/add-watermark-page.ts"
],
"fileCount": 64
"fileCount": 63
},
"Success!": {
"files": [
@@ -9961,12 +9887,6 @@
],
"fileCount": 2
},
"{{page}} {{n}}": {
"files": [
"logic/pdf-multi-tool.ts"
],
"fileCount": 1
},
"{{progress}}%": {
"files": [
"ui.ts"