feat: Add page number string reordering to Organize PDF tool
This commit introduces a new feature to the "Organize PDF" tool that allows users to reorder pages by providing a comma-separated string of page numbers. An "Advanced Settings" section has been added to the UI, containing a text input for the page order and an "Apply Order" button. The implementation includes validation for the input and updates the page thumbnail grid accordingly.
This commit is contained in:
7
dummy.pdf
Normal file
7
dummy.pdf
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
%PDF-1.4
|
||||||
|
1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj
|
||||||
|
2 0 obj<</Type/Pages/Count 3/Kids[3 0 R 4 0 R 5 0 R]>>endobj
|
||||||
|
3 0 obj<</Type/Page/MediaBox[0 0 612 792]>>endobj
|
||||||
|
4 0 obj<</Type/Page/MediaBox[0 0 612 792]>>endobj
|
||||||
|
5 0 obj<</Type/Page/MediaBox[0 0 612 792]>>endobj
|
||||||
|
trailer<</Root 1 0 R>>
|
||||||
5
package-lock.json
generated
5
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "bento-pdf",
|
"name": "bento-pdf",
|
||||||
"version": "1.15.4",
|
"version": "1.16.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "bento-pdf",
|
"name": "bento-pdf",
|
||||||
"version": "1.15.4",
|
"version": "1.16.0",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bentopdf/gs-wasm": "^0.1.0",
|
"@bentopdf/gs-wasm": "^0.1.0",
|
||||||
@@ -10537,7 +10537,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz",
|
||||||
"integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==",
|
"integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "1.0.8"
|
"@types/estree": "1.0.8"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -58,6 +58,74 @@ function initializePage() {
|
|||||||
document.getElementById('back-to-tools')?.addEventListener('click', () => {
|
document.getElementById('back-to-tools')?.addEventListener('click', () => {
|
||||||
window.location.href = import.meta.env.BASE_URL;
|
window.location.href = import.meta.env.BASE_URL;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const applyOrderBtn = document.getElementById('apply-order-btn');
|
||||||
|
if (applyOrderBtn) applyOrderBtn.addEventListener('click', applyCustomOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyCustomOrder() {
|
||||||
|
const orderInput = document.getElementById('page-order-input') as HTMLInputElement;
|
||||||
|
const grid = document.getElementById('page-grid');
|
||||||
|
|
||||||
|
if (!orderInput || !grid) return;
|
||||||
|
|
||||||
|
const orderString = orderInput.value;
|
||||||
|
if (!orderString) {
|
||||||
|
showAlert('Invalid Order', 'Please enter a page order.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newOrder = orderString.split(',').map(s => parseInt(s.trim(), 10));
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
const currentGridCount = grid.children.length;
|
||||||
|
const validNumbers = newOrder.every(n => !isNaN(n) && n > 0); // Basic check, will validate against available thumbnails
|
||||||
|
if (!validNumbers) {
|
||||||
|
showAlert('Invalid Page Numbers', `Please enter positive numbers.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newOrder.length !== currentGridCount) {
|
||||||
|
showAlert('Incorrect Page Count', `The number of pages specified (${newOrder.length}) does not match the current number of pages in the document (${currentGridCount}). Please provide a complete ordering for all pages.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uniqueNumbers = new Set(newOrder);
|
||||||
|
if (uniqueNumbers.size !== newOrder.length) {
|
||||||
|
showAlert('Duplicate Page Numbers', 'Please ensure all page numbers in the order are unique.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentThumbnails = Array.from(grid.children) as HTMLElement[];
|
||||||
|
const reorderedThumbnails: HTMLElement[] = [];
|
||||||
|
const foundIndices = new Set();
|
||||||
|
|
||||||
|
for (const pageNum of newOrder) {
|
||||||
|
const originalIndexToFind = pageNum - 1; // pageNum is 1-based, originalPageIndex is 0-based
|
||||||
|
const foundThumbnail = currentThumbnails.find(
|
||||||
|
thumb => thumb.dataset.originalPageIndex === originalIndexToFind.toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (foundThumbnail) {
|
||||||
|
reorderedThumbnails.push(foundThumbnail);
|
||||||
|
foundIndices.add(originalIndexToFind.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const allOriginalIndicesPresent = currentThumbnails.every(thumb => foundIndices.has(thumb.dataset.originalPageIndex));
|
||||||
|
|
||||||
|
if (reorderedThumbnails.length !== currentGridCount || !allOriginalIndicesPresent) {
|
||||||
|
showAlert('Invalid Page Order', 'The specified page order is incomplete or contains invalid page numbers. Please ensure you provide a new position for every original page.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the grid and append the reordered thumbnails
|
||||||
|
grid.innerHTML = '';
|
||||||
|
reorderedThumbnails.forEach(thumb => grid.appendChild(thumb));
|
||||||
|
|
||||||
|
initializeSortable(); // Re-initialize sortable on the new order
|
||||||
|
|
||||||
|
showAlert('Success', 'Pages have been reordered.', 'success');
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFileUpload(e: Event) {
|
function handleFileUpload(e: Event) {
|
||||||
@@ -160,11 +228,13 @@ function attachEventListeners(element: HTMLElement) {
|
|||||||
async function renderThumbnails() {
|
async function renderThumbnails() {
|
||||||
const grid = document.getElementById('page-grid');
|
const grid = document.getElementById('page-grid');
|
||||||
const processBtn = document.getElementById('process-btn');
|
const processBtn = document.getElementById('process-btn');
|
||||||
if (!grid) return;
|
const advancedSettings = document.getElementById('advanced-settings');
|
||||||
|
if (!grid || !processBtn || !advancedSettings) return;
|
||||||
|
|
||||||
grid.innerHTML = '';
|
grid.innerHTML = '';
|
||||||
grid.classList.remove('hidden');
|
grid.classList.remove('hidden');
|
||||||
processBtn?.classList.remove('hidden');
|
processBtn.classList.remove('hidden');
|
||||||
|
advancedSettings.classList.remove('hidden');
|
||||||
|
|
||||||
for (let i = 1; i <= organizeState.totalPages; i++) {
|
for (let i = 1; i <= organizeState.totalPages; i++) {
|
||||||
const page = await organizeState.pdfJsDoc.getPage(i);
|
const page = await organizeState.pdfJsDoc.getPage(i);
|
||||||
@@ -289,6 +359,7 @@ function resetState() {
|
|||||||
grid.classList.add('hidden');
|
grid.classList.add('hidden');
|
||||||
}
|
}
|
||||||
document.getElementById('process-btn')?.classList.add('hidden');
|
document.getElementById('process-btn')?.classList.add('hidden');
|
||||||
|
document.getElementById('advanced-settings')?.classList.add('hidden');
|
||||||
const fileDisplayArea = document.getElementById('file-display-area');
|
const fileDisplayArea = document.getElementById('file-display-area');
|
||||||
if (fileDisplayArea) fileDisplayArea.innerHTML = '';
|
if (fileDisplayArea) fileDisplayArea.innerHTML = '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,6 +174,18 @@
|
|||||||
class="hidden grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 gap-4 my-6"
|
class="hidden grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 gap-4 my-6"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
|
<!-- Advanced Settings -->
|
||||||
|
<div id="advanced-settings" class="hidden mt-6">
|
||||||
|
<h3 class="text-lg font-semibold text-white mb-2">Advanced Settings</h3>
|
||||||
|
<div class="bg-gray-700 p-4 rounded-lg">
|
||||||
|
<label for="page-order-input" class="block text-sm font-medium text-gray-300 mb-2">Page Order (comma-separated)</label>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<input type="text" id="page-order-input" class="w-full bg-gray-900 text-white rounded-md px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-500 border border-gray-600" placeholder="e.g., 3,1,4,2">
|
||||||
|
<button id="apply-order-btn" class="btn-secondary">Apply Order</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button id="process-btn" class="hidden btn-gradient w-full mt-6">
|
<button id="process-btn" class="hidden btn-gradient w-full mt-6">
|
||||||
Save Changes
|
Save Changes
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user