diff --git a/src/js/config/pdf-tools.ts b/src/js/config/pdf-tools.ts
new file mode 100644
index 0000000..27e3e78
--- /dev/null
+++ b/src/js/config/pdf-tools.ts
@@ -0,0 +1,17 @@
+export const singlePdfLoadTools = [
+ 'split', 'organize', 'rotate', 'add-page-numbers',
+ 'pdf-to-jpg', 'pdf-to-png', 'pdf-to-webp', 'compress', 'pdf-to-greyscale',
+ 'edit-metadata', 'remove-metadata', 'flatten', 'delete-pages', 'add-blank-page',
+ 'extract-pages', 'add-watermark', 'add-header-footer', 'invert-colors', 'view-metadata',
+ 'reverse-pages', 'crop', 'redact', 'pdf-to-bmp', 'pdf-to-tiff', 'split-in-half',
+ 'page-dimensions', 'n-up', 'duplicate-organize', 'combine-single-page', 'fix-dimensions', 'change-background-color',
+ 'change-text-color', 'ocr-pdf', 'sign-pdf', 'remove-annotations', 'cropper', 'form-filler',
+];
+
+export const simpleTools = [
+ 'encrypt', 'decrypt', 'change-permissions', 'pdf-to-markdown', 'word-to-pdf',
+];
+
+export const multiFileTools = [
+ 'merge', 'pdf-to-zip', 'jpg-to-pdf', 'png-to-pdf', 'webp-to-pdf', 'image-to-pdf', 'svg-to-pdf', 'bmp-to-pdf', 'heic-to-pdf', 'tiff-to-pdf',
+];
\ No newline at end of file
diff --git a/src/js/handlers/fileHandler.ts b/src/js/handlers/fileHandler.ts
index 6b7038d..6684a46 100644
--- a/src/js/handlers/fileHandler.ts
+++ b/src/js/handlers/fileHandler.ts
@@ -1,5 +1,3 @@
-// FILE: js/handlers/fileHandler.js
-
import { state } from '../state.js';
import { showLoader, hideLoader, showAlert, renderPageThumbnails, renderFileDisplay, switchView } from '../ui.js';
import { readFileAsArrayBuffer } from '../utils/helpers.js';
@@ -9,12 +7,14 @@ import { renderDuplicateOrganizeThumbnails } from '../logic/duplicate-organize.j
import { PDFDocument as PDFLibDocument } from 'pdf-lib';
import { icons, createIcons } from 'lucide';
import Sortable from 'sortablejs';
+import { multiFileTools, simpleTools, singlePdfLoadTools } from '../config/pdf-tools.js';
+import * as pdfjsLib from 'pdfjs-dist';
-async function handleSinglePdfUpload(toolId: any, file: any) {
+async function handleSinglePdfUpload(toolId, file) {
showLoader('Loading PDF...');
try {
const pdfBytes = await readFileAsArrayBuffer(file);
- state.pdfDoc = await PDFLibDocument.load(pdfBytes as ArrayBuffer, {
+ state.pdfDoc = await PDFLibDocument.load(pdfBytes as ArrayBuffer, {
ignoreEncryption: true
});
hideLoader();
@@ -30,8 +30,7 @@ async function handleSinglePdfUpload(toolId: any, file: any) {
const processBtn = document.getElementById('process-btn');
if (processBtn) {
- // @ts-expect-error TS(2339) FIXME: Property 'disabled' does not exist on type 'HTMLEl... Remove this comment to see the full error message
- processBtn.disabled = false;
+ (processBtn as HTMLButtonElement).disabled = false;
processBtn.classList.remove('hidden');
const logic = toolLogic[toolId];
if (logic) {
@@ -41,7 +40,7 @@ async function handleSinglePdfUpload(toolId: any, file: any) {
}
if (['split', 'delete-pages', 'add-blank-page', 'extract-pages', 'add-header-footer'].includes(toolId)) {
- document.getElementById('total-pages').textContent = state.pdfDoc.getPageCount();
+ document.getElementById('total-pages').textContent = state.pdfDoc.getPageCount().toString();
}
if (toolId === 'organize' || toolId === 'rotate') {
@@ -52,32 +51,23 @@ async function handleSinglePdfUpload(toolId: any, file: any) {
const rotateAllLeftBtn = document.getElementById('rotate-all-left-btn');
const rotateAllRightBtn = document.getElementById('rotate-all-right-btn');
- // Show the buttons
rotateAllControls.classList.remove('hidden');
- createIcons({icons}); // Render the new icons
+ createIcons({ icons });
- const rotateAll = (direction: any) => {
+ const rotateAll = (direction) => {
document.querySelectorAll('.page-rotator-item').forEach(item => {
- // @ts-expect-error TS(2339) FIXME: Property 'dataset' does not exist on type 'Element... Remove this comment to see the full error message
- const currentRotation = parseInt(item.dataset.rotation || '0');
- // Calculate new rotation, ensuring it wraps around 0-270 degrees
+ const currentRotation = parseInt((item as HTMLElement).dataset.rotation || '0');
const newRotation = (currentRotation + (direction * 90) + 360) % 360;
-
- // @ts-expect-error TS(2339) FIXME: Property 'dataset' does not exist on type 'Element... Remove this comment to see the full error message
- item.dataset.rotation = newRotation;
-
+ (item as HTMLElement).dataset.rotation = newRotation.toString();
const thumbnail = item.querySelector('canvas, img');
if (thumbnail) {
- // @ts-expect-error TS(2339) FIXME: Property 'style' does not exist on type 'Element'.
- thumbnail.style.transform = `rotate(${newRotation}deg)`;
+ (thumbnail as HTMLElement).style.transform = `rotate(${newRotation}deg)`;
}
});
};
-
- rotateAllLeftBtn.onclick = () => rotateAll(-1); // -1 for counter-clockwise
- rotateAllRightBtn.onclick = () => rotateAll(1); // 1 for clockwise
+ rotateAllLeftBtn.onclick = () => rotateAll(-1);
+ rotateAllRightBtn.onclick = () => rotateAll(1);
}
-
}
if (toolId === 'duplicate-organize') {
@@ -93,9 +83,7 @@ async function handleSinglePdfUpload(toolId: any, file: any) {
try {
const pdfBytes = await readFileAsArrayBuffer(state.files[0]);
- // @ts-expect-error TS(2304) FIXME: Cannot find name 'pdfjsLib'.
- const pdfjsDoc = await pdfjsLib.getDocument({ data: pdfBytes }).promise;
-
+ const pdfjsDoc = await pdfjsLib.getDocument({ data: pdfBytes as ArrayBuffer }).promise;
const [metadata, fieldObjects] = await Promise.all([
pdfjsDoc.getMetadata(),
pdfjsDoc.getFieldObjects()
@@ -103,7 +91,34 @@ async function handleSinglePdfUpload(toolId: any, file: any) {
const { info, metadata: rawXmpString } = metadata;
- const parsePdfDate = (pdfDate: any) => {
+ resultsDiv.textContent = ''; // Clear safely
+
+ const createSection = (title) => {
+ const wrapper = document.createElement('div');
+ wrapper.className = 'mb-4';
+ const h3 = document.createElement('h3');
+ h3.className = 'text-lg font-semibold text-white mb-2';
+ h3.textContent = title;
+ const ul = document.createElement('ul');
+ ul.className = 'space-y-3 text-sm bg-gray-900 p-4 rounded-lg border border-gray-700';
+ wrapper.append(h3, ul);
+ return { wrapper, ul };
+ };
+
+ const createListItem = (key, value) => {
+ const li = document.createElement('li');
+ li.className = 'flex flex-col sm:flex-row';
+ const strong = document.createElement('strong');
+ strong.className = 'w-40 flex-shrink-0 text-gray-400';
+ strong.textContent = key;
+ const div = document.createElement('div');
+ div.className = 'flex-grow text-white break-all';
+ div.textContent = value;
+ li.append(strong, div);
+ return li;
+ };
+
+ const parsePdfDate = (pdfDate) => {
if (!pdfDate || typeof pdfDate !== 'string' || !pdfDate.startsWith('D:')) return pdfDate;
try {
const year = pdfDate.substring(2, 6);
@@ -113,91 +128,49 @@ async function handleSinglePdfUpload(toolId: any, file: any) {
const minute = pdfDate.substring(12, 14);
const second = pdfDate.substring(14, 16);
return new Date(`${year}-${month}-${day}T${hour}:${minute}:${second}Z`).toLocaleString();
- } catch {
- return pdfDate;
- }
+ } catch { return pdfDate; }
};
- let htmlContent = '';
-
- htmlContent += `
-
-
Info Dictionary
-
`;
-
+ const infoSection = createSection('Info Dictionary');
if (info && Object.keys(info).length > 0) {
for (const key in info) {
- let value = info[key];
- let displayValue;
-
- if (value === null || value === undefined || String(value).trim() === '') {
- displayValue = `- Not Set -`;
- } else if ((key === 'CreationDate' || key === 'ModDate') && typeof value === 'string') {
- displayValue = `${parsePdfDate(value)}`;
- } else if (typeof value === 'object' && !Array.isArray(value)) {
- try {
- let nestedList = '';
- for (const [nestedKey, nestedValue] of Object.entries(value)) {
- nestedList += `- ${nestedKey}: ${nestedValue}
`;
- }
- nestedList += '
';
- displayValue = nestedList;
- } catch (e) {
- displayValue = `[Unserializable Object]`;
- }
- } else {
- // Fallback for simple values (strings, numbers, arrays)
- displayValue = `${String(value)}`;
+ let value = info[key] || '- Not Set -';
+ if ((key === 'CreationDate' || key === 'ModDate') && typeof value === 'string') {
+ value = parsePdfDate(value);
}
- htmlContent += `- ${key}
${displayValue}
`;
+ infoSection.ul.appendChild(createListItem(key, String(value)));
}
} else {
- htmlContent += `- - No Info Dictionary data found -
`;
+ infoSection.ul.innerHTML = `- - No Info Dictionary data found -
`;
}
- htmlContent += `
`;
-
- htmlContent += `
-
-
Interactive Form Fields
-
`;
+ resultsDiv.appendChild(infoSection.wrapper);
+ const fieldsSection = createSection('Interactive Form Fields');
if (fieldObjects && Object.keys(fieldObjects).length > 0) {
- const getFriendlyFieldType = (type: any) => {
- switch (type) {
- case 'Tx': return 'Text'; case 'Btn': return 'Button/Checkbox';
- case 'Ch': return 'Choice/Dropdown'; case 'Sig': return 'Signature';
- default: return type || 'Unknown';
- }
- };
for (const fieldName in fieldObjects) {
const field = fieldObjects[fieldName][0];
- const fieldType = getFriendlyFieldType(field.fieldType);
- const fieldValue = field.fieldValue ? `${field.fieldValue}` : `- Not Set -`;
- htmlContent += `
- -
- ${fieldName}
-
[${fieldType}]${fieldValue}
- `;
+ const value = (field as any).fieldValue || '- Not Set -';
+ fieldsSection.ul.appendChild(createListItem(fieldName, String(value)));
}
} else {
- htmlContent += `- - No interactive form fields found -
`;
+ fieldsSection.ul.innerHTML = `- - No interactive form fields found -
`;
}
- htmlContent += `
`;
-
- htmlContent += `
-
-
XMP Metadata (Raw XML)
-
`;
+ resultsDiv.appendChild(fieldsSection.wrapper);
+ const xmpSection = createSection('XMP Metadata (Raw XML)');
+ const xmpContainer = document.createElement('div');
+ xmpContainer.className = 'bg-gray-900 p-4 rounded-lg border border-gray-700';
if (rawXmpString) {
- const escapedXml = rawXmpString.replace(/&/g, '&').replace(//g, '>');
- htmlContent += `
${escapedXml}`;
+ const pre = document.createElement('pre');
+ pre.className = 'text-xs text-gray-300 whitespace-pre-wrap break-all';
+ pre.textContent = String(rawXmpString);
+ xmpContainer.appendChild(pre);
} else {
- htmlContent += `
- No XMP metadata found -
`;
+ xmpContainer.innerHTML = `
- No XMP metadata found -
`;
}
- htmlContent += `
`;
+ xmpSection.wrapper.appendChild(xmpContainer);
+ resultsDiv.appendChild(xmpSection.wrapper);
- resultsDiv.innerHTML = htmlContent;
resultsDiv.classList.remove('hidden');
} catch (e) {
@@ -208,209 +181,53 @@ async function handleSinglePdfUpload(toolId: any, file: any) {
}
}
- if (toolId === 'edit-metadata') {
- const form = document.getElementById('metadata-form');
- const formatDateForInput = (date: any) => {
- if (!date) return '';
- const pad = (num: any) => num.toString().padStart(2, '0');
- return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`;
- };
-
- // @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
- document.getElementById('meta-title').value = state.pdfDoc.getTitle() || '';
- // @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
- document.getElementById('meta-author').value = state.pdfDoc.getAuthor() || '';
- // @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
- document.getElementById('meta-subject').value = state.pdfDoc.getSubject() || '';
- // @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
- document.getElementById('meta-keywords').value = state.pdfDoc.getKeywords() || '';
- // @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
- document.getElementById('meta-creator').value = state.pdfDoc.getCreator() || '';
- // @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
- document.getElementById('meta-producer').value = state.pdfDoc.getProducer() || '';
- // @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
- document.getElementById('meta-creation-date').value = formatDateForInput(state.pdfDoc.getCreationDate());
- // @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
- document.getElementById('meta-mod-date').value = formatDateForInput(state.pdfDoc.getModificationDate());
-
- form.classList.remove('hidden');
-
- const addBtn = document.getElementById('add-custom-meta-btn');
- const container = document.getElementById('custom-metadata-container');
-
- addBtn.onclick = () => {
- const newFieldId = `custom-field-${Date.now()}`;
- const fieldWrapper = document.createElement('div');
- fieldWrapper.id = newFieldId;
- fieldWrapper.className = 'flex items-center gap-2';
- fieldWrapper.innerHTML = `
-
-
-
- `;
- container.appendChild(fieldWrapper);
-
- createIcons({icons}); // Re-render icons
- };
-
- createIcons({icons});
- }
if (toolId === 'edit-metadata') {
const form = document.getElementById('metadata-form');
const container = document.getElementById('custom-metadata-container');
const addBtn = document.getElementById('add-custom-meta-btn');
- // Helper to format Date objects
- const formatDateForInput = (date: any) => {
+ const formatDateForInput = (date) => {
if (!date) return '';
- const pad = (num: any) => num.toString().padStart(2, '0');
+ const pad = (num) => num.toString().padStart(2, '0');
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`;
};
-
-
- // Comprehensive decoder for PDF values
- const decodePDFValue = (valueNode: any) => {
- if (!valueNode) return '';
-
- // @ts-expect-error TS(2339) FIXME: Property 'PDFLib' does not exist on type 'Window &... Remove this comment to see the full error message
- const { PDFHexString, PDFString, PDFName, PDFNumber } = window.PDFLib;
-
- try {
- // Handle PDFHexString
- if (valueNode instanceof PDFHexString) {
- // Try the built-in decoder first
- try {
- return valueNode.decodeText();
- } catch (e) {
- console.warn('Built-in decodeText failed for PDFHexString, trying manual decode');
- // Manual hex decoding
- const hexStr = valueNode.toString();
- return decodeHexStringManual(hexStr);
- }
- }
-
- // Handle PDFString
- if (valueNode instanceof PDFString) {
- try {
- return valueNode.decodeText();
- } catch (e) {
- console.warn('Built-in decodeText failed for PDFString, using toString');
- return valueNode.toString();
- }
- }
-
- // Handle other types
- if (valueNode instanceof PDFName) {
- return valueNode.decodeText ? valueNode.decodeText() : valueNode.toString();
- }
-
- if (valueNode instanceof PDFNumber) {
- return valueNode.toString();
- }
-
- // Fallback - check if the toString() result needs hex decoding
- const strValue = valueNode.toString();
-
- // Check for various hex encoding patterns
- if (strValue.includes('#')) {
- // Pattern like "helllo#20h"
- return strValue.replace(/#([0-9A-Fa-f]{2})/g, (match: any, hex: any) => {
- return String.fromCharCode(parseInt(hex, 16));
- });
- }
-
- // Check if it's a hex string in angle brackets like <48656C6C6C6F20>
- if (strValue.match(/^<[0-9A-Fa-f\s]+>$/)) {
- return decodeHexStringManual(strValue);
- }
-
- // Check if it's a parentheses-wrapped string
- if (strValue.match(/^\([^)]*\)$/)) {
- return strValue.slice(1, -1); // Remove parentheses
- }
-
- return strValue;
-
- } catch (error) {
- console.error('Error decoding PDF value:', error);
- return valueNode.toString();
- }
- };
-
- // Manual hex string decoder
- const decodeHexStringManual = (hexStr: any) => {
- try {
- // Remove angle brackets if present
- let cleanHex = hexStr.replace(/^<|>$/g, '');
- // Remove any whitespace
- cleanHex = cleanHex.replace(/\s/g, '');
-
- let result = '';
- for (let i = 0; i < cleanHex.length; i += 2) {
- const hexPair = cleanHex.substr(i, 2);
- if (hexPair.length === 2 && /^[0-9A-Fa-f]{2}$/.test(hexPair)) {
- const charCode = parseInt(hexPair, 16);
- // Only add printable characters or common whitespace
- if (charCode >= 32 && charCode <= 126) {
- result += String.fromCharCode(charCode);
- } else if (charCode === 10 || charCode === 13 || charCode === 9) {
- result += String.fromCharCode(charCode);
- } else {
- // For non-printable characters, you might want to skip or use a placeholder
- result += String.fromCharCode(charCode);
- }
- }
- }
- return result;
- } catch (error) {
- console.error('Manual hex decode failed:', error);
- return hexStr;
- }
- };
-
- // --- 1. Pre-fill Standard Fields ---
- // @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
- document.getElementById('meta-title').value = state.pdfDoc.getTitle() || '';
- // @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
- document.getElementById('meta-author').value = state.pdfDoc.getAuthor() || '';
- // @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
- document.getElementById('meta-subject').value = state.pdfDoc.getSubject() || '';
- // @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
- document.getElementById('meta-keywords').value = state.pdfDoc.getKeywords() || '';
- // @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
- document.getElementById('meta-creator').value = state.pdfDoc.getCreator() || '';
- // @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
- document.getElementById('meta-producer').value = state.pdfDoc.getProducer() || '';
- // @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
- document.getElementById('meta-creation-date').value = formatDateForInput(state.pdfDoc.getCreationDate());
- // @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
- document.getElementById('meta-mod-date').value = formatDateForInput(state.pdfDoc.getModificationDate());
-
- container.querySelectorAll('.custom-field-wrapper').forEach(el => el.remove());
+ (document.getElementById('meta-title') as HTMLInputElement).value = state.pdfDoc.getTitle() || '';
+ (document.getElementById('meta-author') as HTMLInputElement).value = state.pdfDoc.getAuthor() || '';
+ (document.getElementById('meta-subject') as HTMLInputElement).value = state.pdfDoc.getSubject() || '';
+ (document.getElementById('meta-keywords') as HTMLInputElement).value = state.pdfDoc.getKeywords() || '';
+ (document.getElementById('meta-creator') as HTMLInputElement).value = state.pdfDoc.getCreator() || '';
+ (document.getElementById('meta-producer') as HTMLInputElement).value = state.pdfDoc.getProducer() || '';
+ (document.getElementById('meta-creation-date') as HTMLInputElement).value = formatDateForInput(state.pdfDoc.getCreationDate());
+ (document.getElementById('meta-mod-date') as HTMLInputElement).value = formatDateForInput(state.pdfDoc.getModificationDate());
addBtn.onclick = () => {
- const newFieldId = `custom-field-${Date.now()}`;
const fieldWrapper = document.createElement('div');
- fieldWrapper.id = newFieldId;
fieldWrapper.className = 'flex items-center gap-2 custom-field-wrapper';
- fieldWrapper.innerHTML = `
-
-
-
- `;
- container.appendChild(fieldWrapper);
- createIcons({icons});
+ const keyInput = document.createElement('input');
+ keyInput.type = 'text';
+ keyInput.placeholder = 'Key (e.g., Department)';
+ keyInput.className = 'custom-meta-key w-1/3 bg-gray-800 border border-gray-600 text-white rounded-lg p-2';
+
+ const valueInput = document.createElement('input');
+ valueInput.type = 'text';
+ valueInput.placeholder = 'Value (e.g., Marketing)';
+ valueInput.className = 'custom-meta-value flex-grow bg-gray-800 border border-gray-600 text-white rounded-lg p-2';
+
+ const removeBtn = document.createElement('button');
+ removeBtn.type = 'button';
+ removeBtn.className = 'btn p-2 text-red-500 hover:bg-gray-700 rounded-full';
+ removeBtn.innerHTML = '';
+ removeBtn.addEventListener('click', () => fieldWrapper.remove());
+
+ fieldWrapper.append(keyInput, valueInput, removeBtn);
+ container.appendChild(fieldWrapper);
+ createIcons({ icons });
};
form.classList.remove('hidden');
-
- createIcons({icons}); // Render all icons after dynamic changes
+ createIcons({ icons });
}
if (toolId === 'cropper') {
@@ -431,11 +248,10 @@ async function handleSinglePdfUpload(toolId: any, file: any) {
}
}
-function handleMultiFileUpload(toolId: any) {
+function handleMultiFileUpload(toolId) {
const processBtn = document.getElementById('process-btn');
if (processBtn) {
- // @ts-expect-error TS(2339) FIXME: Property 'disabled' does not exist on type 'HTMLEl... Remove this comment to see the full error message
- processBtn.disabled = false;
+ (processBtn as HTMLButtonElement).disabled = false;
const logic = toolLogic[toolId];
if (logic) {
const func = typeof logic.process === 'function' ? logic.process : logic;
@@ -447,15 +263,23 @@ function handleMultiFileUpload(toolId: any) {
toolLogic.merge.setup();
} else if (toolId === 'image-to-pdf') {
const imageList = document.getElementById('image-list');
-
- imageList.innerHTML = '';
+ imageList.textContent = ''; // Clear safely
state.files.forEach(file => {
const url = URL.createObjectURL(file);
const li = document.createElement('li');
li.className = "relative group cursor-move";
li.dataset.fileName = file.name;
- li.innerHTML = `
${file.name}
`;
+
+ const img = document.createElement('img');
+ img.src = url;
+ img.className = "w-full h-full object-cover rounded-md border-2 border-gray-600";
+
+ const p = document.createElement('p');
+ p.className = "absolute bottom-0 left-0 right-0 bg-black bg-opacity-50 text-white text-xs text-center truncate p-1";
+ p.textContent = file.name; // Safe insertion
+
+ li.append(img, p);
imageList.appendChild(li);
});
@@ -463,14 +287,12 @@ function handleMultiFileUpload(toolId: any) {
}
}
-
-export function setupFileInputHandler(toolId: any) {
+export function setupFileInputHandler(toolId) {
const fileInput = document.getElementById('file-input');
- const multiFileTools = ['merge', 'pdf-to-zip', 'jpg-to-pdf', 'png-to-pdf', 'webp-to-pdf', 'image-to-pdf', 'svg-to-pdf', 'bmp-to-pdf', 'heic-to-pdf', 'tiff-to-pdf'];
const isMultiFileTool = multiFileTools.includes(toolId);
let isFirstUpload = true;
- const processFiles = async (newFiles: any) => {
+ const processFiles = async (newFiles) => {
if (newFiles.length === 0) return;
if (!isMultiFileTool || isFirstUpload) {
@@ -478,7 +300,7 @@ export function setupFileInputHandler(toolId: any) {
} else {
state.files = [...state.files, ...newFiles];
}
- isFirstUpload = false;
+ isFirstUpload = false;
const fileDisplayArea = document.getElementById('file-display-area');
if (fileDisplayArea) {
@@ -488,20 +310,9 @@ export function setupFileInputHandler(toolId: any) {
const fileControls = document.getElementById('file-controls');
if (fileControls) {
fileControls.classList.remove('hidden');
-
- createIcons({icons});
+ createIcons({ icons });
}
- const singlePdfLoadTools = ['split', 'organize', 'rotate', 'add-page-numbers',
- 'pdf-to-jpg', 'pdf-to-png', 'pdf-to-webp', 'compress', 'pdf-to-greyscale',
- 'edit-metadata', 'remove-metadata', 'flatten', 'delete-pages', 'add-blank-page',
- 'extract-pages', 'add-watermark', 'add-header-footer', 'invert-colors', 'view-metadata',
- 'reverse-pages', 'crop', 'redact', 'pdf-to-bmp', 'pdf-to-tiff', 'split-in-half',
- 'page-dimensions', 'n-up', 'duplicate-organize', 'combine-single-page', 'fix-dimensions', 'change-background-color',
- 'change-text-color', 'ocr-pdf', 'sign-pdf', 'remove-annotations', 'cropper', 'form-filler',
- ];
- const simpleTools = ['encrypt', 'decrypt', 'change-permissions', 'pdf-to-markdown', 'word-to-pdf'];
-
if (isMultiFileTool) {
handleMultiFileUpload(toolId);
} else if (singlePdfLoadTools.includes(toolId)) {
@@ -512,8 +323,7 @@ export function setupFileInputHandler(toolId: any) {
if (optionsDiv) optionsDiv.classList.remove('hidden');
const processBtn = document.getElementById('process-btn');
if (processBtn) {
- // @ts-expect-error TS(2339) FIXME: Property 'disabled' does not exist on type 'HTMLEl... Remove this comment to see the full error message
- processBtn.disabled = false;
+ (processBtn as HTMLButtonElement).disabled = false;
processBtn.onclick = () => {
const logic = toolLogic[toolId];
if (logic) {
@@ -522,29 +332,25 @@ export function setupFileInputHandler(toolId: any) {
}
};
}
- }
- else if (toolId === 'edit') {
+ } else if (toolId === 'edit') {
const file = state.files[0];
if (!file) return;
const pdfWrapper = document.getElementById('embed-pdf-wrapper');
const pdfContainer = document.getElementById('embed-pdf-container');
- pdfContainer.innerHTML = '';
+ pdfContainer.textContent = ''; // Clear safely
if (state.currentPdfUrl) {
URL.revokeObjectURL(state.currentPdfUrl);
}
-
pdfWrapper.classList.remove('hidden');
-
const fileURL = URL.createObjectURL(file);
-
state.currentPdfUrl = fileURL;
const script = document.createElement('script');
script.type = 'module';
- script.innerHTML = `
+ script.textContent = `
import EmbedPDF from 'https://snippet.embedpdf.com/embedpdf.js';
EmbedPDF.init({
type: 'container',
@@ -558,22 +364,19 @@ export function setupFileInputHandler(toolId: any) {
const backBtn = document.getElementById('back-to-grid');
const urlRevoker = () => {
URL.revokeObjectURL(fileURL);
- state.currentPdfUrl = null; // Clear from state as well
+ state.currentPdfUrl = null;
backBtn.removeEventListener('click', urlRevoker);
};
backBtn.addEventListener('click', urlRevoker);
}
};
- // @ts-expect-error TS(2339) FIXME: Property 'files' does not exist on type 'EventTarg... Remove this comment to see the full error message
- fileInput.addEventListener('change', (e) => processFiles(Array.from(e.target.files)));
+ fileInput.addEventListener('change', (e) => processFiles(Array.from((e.target as HTMLInputElement).files || [])));
const setupAddMoreButton = () => {
const addMoreBtn = document.getElementById('add-more-btn');
if (addMoreBtn) {
- addMoreBtn.addEventListener('click', () => {
- fileInput.click();
- });
+ addMoreBtn.addEventListener('click', () => fileInput.click());
}
};
@@ -583,31 +386,28 @@ export function setupFileInputHandler(toolId: any) {
clearBtn.addEventListener('click', () => {
state.files = [];
isFirstUpload = true;
- // @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
- fileInput.value = '';
-
+ (fileInput as HTMLInputElement).value = '';
+
const fileDisplayArea = document.getElementById('file-display-area');
- if (fileDisplayArea) fileDisplayArea.innerHTML = '';
-
+ if (fileDisplayArea) fileDisplayArea.textContent = '';
+
const fileControls = document.getElementById('file-controls');
if (fileControls) fileControls.classList.add('hidden');
-
- // Clear tool-specific UI
+
const toolSpecificUI = ['file-list', 'page-merge-preview', 'image-list'];
toolSpecificUI.forEach(id => {
const el = document.getElementById(id);
- if (el) el.innerHTML = '';
+ if (el) el.textContent = '';
});
-
+
const processBtn = document.getElementById('process-btn');
- // @ts-expect-error TS(2339) FIXME: Property 'disabled' does not exist on type 'HTMLEl... Remove this comment to see the full error message
- if (processBtn) processBtn.disabled = true;
+ if (processBtn) (processBtn as HTMLButtonElement).disabled = true;
});
}
};
-
+
setTimeout(() => {
setupAddMoreButton();
setupClearButton();
}, 100);
-}
\ No newline at end of file
+}
diff --git a/src/js/logic/compare-pdfs.ts b/src/js/logic/compare-pdfs.ts
index d7c1d42..ae711b3 100644
--- a/src/js/logic/compare-pdfs.ts
+++ b/src/js/logic/compare-pdfs.ts
@@ -6,7 +6,7 @@ const state = {
pdfDoc1: null,
pdfDoc2: null,
currentPage: 1,
- viewMode: 'overlay',
+ viewMode: 'overlay',
isSyncScroll: true,
};
@@ -19,7 +19,7 @@ const state = {
*/
async function renderPage(pdfDoc: any, pageNum: any, canvas: any, container: any) {
const page = await pdfDoc.getPage(pageNum);
-
+
// Calculate scale to fit the container width.
const containerWidth = container.clientWidth - 2; // Subtract border width
const viewport = page.getViewport({ scale: 1.0 });
@@ -38,15 +38,15 @@ async function renderPage(pdfDoc: any, pageNum: any, canvas: any, container: any
async function renderBothPages() {
if (!state.pdfDoc1 || !state.pdfDoc2) return;
-
+
showLoader(`Loading page ${state.currentPage}...`);
-
+
const canvas1 = document.getElementById('canvas-compare-1');
const canvas2 = document.getElementById('canvas-compare-2');
const panel1 = document.getElementById('panel-1');
const panel2 = document.getElementById('panel-2');
const wrapper = document.getElementById('compare-viewer-wrapper');
-
+
// Determine the correct container based on the view mode
const container1 = state.viewMode === 'overlay' ? wrapper : panel1;
const container2 = state.viewMode === 'overlay' ? wrapper : panel2;
@@ -55,7 +55,7 @@ async function renderBothPages() {
renderPage(state.pdfDoc1, Math.min(state.currentPage, state.pdfDoc1.numPages), canvas1, container1),
renderPage(state.pdfDoc2, Math.min(state.currentPage, state.pdfDoc2.numPages), canvas2, container2)
]);
-
+
updateNavControls();
hideLoader();
}
@@ -78,11 +78,26 @@ async function setupFileInput(inputId: any, docKey: any, displayId: any) {
const handleFile = async (file: any) => {
if (!file || file.type !== 'application/pdf') return showAlert('Invalid File', 'Please select a valid PDF file.');
-
+
const displayDiv = document.getElementById(displayId);
- displayDiv.innerHTML = `${file.name}
`;
- createIcons({icons});
-
+ displayDiv.textContent = '';
+
+ // 2. Create the icon element
+ const icon = document.createElement('i');
+ icon.setAttribute('data-lucide', 'check-circle');
+ icon.className = 'w-10 h-10 mb-3 text-green-500';
+
+ // 3. Create the paragraph element for the file name
+ const p = document.createElement('p');
+ p.className = 'text-sm text-gray-300 truncate';
+
+ // 4. Set the file name safely using textContent
+ p.textContent = file.name;
+
+ // 5. Append the safe elements to the container
+ displayDiv.append(icon, p);
+ createIcons({ icons });
+
try {
showLoader(`Loading ${file.name}...`);
const pdfBytes = await readFileAsArrayBuffer(file);
@@ -174,7 +189,7 @@ export function setupCompareTool() {
const syncToggle = document.getElementById('sync-scroll-toggle');
// @ts-expect-error TS(2339) FIXME: Property 'checked' does not exist on type 'HTMLEle... Remove this comment to see the full error message
syncToggle.addEventListener('change', () => { state.isSyncScroll = syncToggle.checked; });
-
+
let scrollingPanel: any = null;
panel1.addEventListener('scroll', () => {
if (state.isSyncScroll && scrollingPanel !== panel2) {
diff --git a/src/js/logic/duplicate-organize.ts b/src/js/logic/duplicate-organize.ts
index 1a189d6..429e877 100644
--- a/src/js/logic/duplicate-organize.ts
+++ b/src/js/logic/duplicate-organize.ts
@@ -82,7 +82,7 @@ export async function renderDuplicateOrganizeThumbnails() {
// @ts-expect-error TS(2304) FIXME: Cannot find name 'pdfjsLib'.
const pdfjsDoc = await pdfjsLib.getDocument({ data: pdfData }).promise;
- grid.innerHTML = '';
+ grid.textContent = '';
for (let i = 1; i <= pdfjsDoc.numPages; i++) {
const page = await pdfjsDoc.getPage(i);
@@ -97,20 +97,39 @@ export async function renderDuplicateOrganizeThumbnails() {
// @ts-expect-error TS(2322) FIXME: Type 'number' is not assignable to type 'string'.
wrapper.dataset.originalPageIndex = i - 1;
- wrapper.innerHTML = `
-
-
})
-
- ${i}
-
-
-
-
-`;
+ const imgContainer = document.createElement('div');
+ imgContainer.className = 'w-full h-36 bg-gray-900 rounded-lg flex items-center justify-center overflow-hidden border-2 border-gray-600';
+
+ const img = document.createElement('img');
+ img.src = canvas.toDataURL();
+ img.className = 'max-w-full max-h-full object-contain';
+ imgContainer.appendChild(img);
+
+ const pageNumberSpan = document.createElement('span');
+ pageNumberSpan.className = 'page-number absolute top-1 left-1 bg-gray-900 bg-opacity-75 text-white text-xs rounded-full px-2 py-1';
+ pageNumberSpan.textContent = i.toString();
+
+ const controlsDiv = document.createElement('div');
+ controlsDiv.className = 'flex items-center justify-center gap-4';
+
+ const duplicateBtn = document.createElement('button');
+ duplicateBtn.className = 'duplicate-btn bg-green-600 hover:bg-green-700 text-white rounded-full w-8 h-8 flex items-center justify-center';
+ duplicateBtn.title = 'Duplicate Page';
+ const duplicateIcon = document.createElement('i');
+ duplicateIcon.setAttribute('data-lucide', 'copy-plus');
+ duplicateIcon.className = 'w-5 h-5';
+ duplicateBtn.appendChild(duplicateIcon);
+
+ const deleteBtn = document.createElement('button');
+ deleteBtn.className = 'delete-btn bg-red-600 hover:bg-red-700 text-white rounded-full w-8 h-8 flex items-center justify-center';
+ deleteBtn.title = 'Delete Page';
+ const deleteIcon = document.createElement('i');
+ deleteIcon.setAttribute('data-lucide', 'x-circle');
+ deleteIcon.className = 'w-5 h-5';
+ deleteBtn.appendChild(deleteIcon);
+
+ controlsDiv.append(duplicateBtn, deleteBtn);
+ wrapper.append(imgContainer, pageNumberSpan, controlsDiv);
grid.appendChild(wrapper);
attachEventListeners(wrapper);
}
diff --git a/src/js/logic/form-filler.ts b/src/js/logic/form-filler.ts
index af079c7..596c5c3 100644
--- a/src/js/logic/form-filler.ts
+++ b/src/js/logic/form-filler.ts
@@ -1,15 +1,15 @@
import { showLoader, hideLoader, showAlert } from '../ui.js';
import { downloadFile } from '../utils/helpers.js';
import { state } from '../state.js';
-import {
- PDFDocument as PDFLibDocument,
- PDFTextField,
- PDFCheckBox,
- PDFRadioGroup,
- PDFDropdown,
- PDFButton,
- PDFSignature,
- PDFOptionList
+import {
+ PDFDocument as PDFLibDocument,
+ PDFTextField,
+ PDFCheckBox,
+ PDFRadioGroup,
+ PDFDropdown,
+ PDFButton,
+ PDFSignature,
+ PDFOptionList
} from 'pdf-lib';
import * as pdfjsLib from 'pdfjs-dist';
@@ -22,7 +22,7 @@ pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
let pdfJsDoc: any = null;
let currentPageNum = 1;
let pdfRendering = false;
-let renderTimeout: any = null;
+let renderTimeout: any = null;
const formState = {
scale: 2,
fields: [],
@@ -41,14 +41,14 @@ async function renderPage() {
const canvas = document.getElementById('pdf-canvas') as HTMLCanvasElement;
const context = canvas.getContext('2d');
-
+
if (!context) {
console.error('Could not get canvas context');
pdfRendering = false;
hideLoader();
return;
}
-
+
canvas.height = viewport.height;
canvas.width = viewport.width;
@@ -75,9 +75,8 @@ async function renderPage() {
} else if (field instanceof PDFDropdown) {
field.select(fieldValues[fieldName]);
} else if (field instanceof PDFOptionList) {
- // Handle multi-select list box
if (Array.isArray(fieldValues[fieldName])) {
- fieldValues[fieldName].forEach((option: any) => field.select(option));
+ (fieldValues[fieldName] as any[]).forEach(option => field.select(option));
}
}
} catch (e) {
@@ -89,7 +88,6 @@ async function renderPage() {
const tempPdfJsDoc = await pdfjsLib.getDocument({ data: tempPdfBytes }).promise;
const tempPage = await tempPdfJsDoc.getPage(currentPageNum);
- // Use the newer PDF.js render API
await tempPage.render({
canvasContext: context,
viewport: viewport
@@ -104,15 +102,11 @@ async function renderPage() {
if (totalPagesDisplay) totalPagesDisplay.textContent = String(pdfJsDoc.numPages);
if (prevPageBtn) prevPageBtn.disabled = currentPageNum <= 1;
if (nextPageBtn) nextPageBtn.disabled = currentPageNum >= pdfJsDoc.numPages;
-
+
pdfRendering = false;
hideLoader();
}
-/**
- * Navigates to the next or previous page.
- * @param {number} offset 1 for next, -1 for previous.
- */
async function changePage(offset: number) {
const newPageNum = currentPageNum + offset;
if (newPageNum > 0 && newPageNum <= pdfJsDoc.numPages) {
@@ -121,10 +115,6 @@ async function changePage(offset: number) {
}
}
-/**
- * Sets the zoom level of the PDF viewer.
- * @param {number} factor The zoom factor.
- */
async function setZoom(factor: number) {
formState.scale = factor;
await renderPage();
@@ -134,82 +124,137 @@ function handleFormChange(event: Event) {
const input = event.target as HTMLInputElement | HTMLSelectElement;
const name = input.name;
let value: any;
-
+
if (input instanceof HTMLInputElement && input.type === 'checkbox') {
value = input.checked ? 'on' : 'off';
} else if (input instanceof HTMLSelectElement && input.multiple) {
- // Handle multi-select list box
value = Array.from(input.options)
- .filter(option => option.selected)
- .map(option => option.value);
+ .filter(option => option.selected)
+ .map(option => option.value);
} else {
value = input.value;
}
-
+
fieldValues[name] = value;
-
+
clearTimeout(renderTimeout);
renderTimeout = setTimeout(() => {
renderPage();
}, 350);
}
-function createFormFieldHtml(field: any) {
+function createFormFieldHtml(field: any): HTMLElement {
const name = field.getName();
const isRequired = field.isRequired();
-
- const labelText = name.replace(/[_-]/g, ' ');
- const labelHtml = ``;
+ const labelText = name.replace(/[_-]/g, ' ');
+
+ const wrapper = document.createElement('div');
+ wrapper.className = 'form-field-group p-4 bg-gray-800 rounded-lg border border-gray-700';
+
+ const label = document.createElement('label');
+ label.htmlFor = `field-${name}`;
+ label.className = 'block text-sm font-medium text-gray-300 capitalize mb-1';
+ label.textContent = labelText;
+
+ if (isRequired) {
+ const requiredSpan = document.createElement('span');
+ requiredSpan.className = 'text-red-500';
+ requiredSpan.textContent = ' *';
+ label.appendChild(requiredSpan);
+ }
+
+ wrapper.appendChild(label);
+
+ let inputElement: HTMLElement | DocumentFragment;
- let inputHtml = '';
-
if (field instanceof PDFTextField) {
fieldValues[name] = field.getText() || '';
- inputHtml = ``;
+ const input = document.createElement('input');
+ input.type = 'text';
+ input.id = `field-${name}`;
+ input.name = name;
+ input.value = fieldValues[name];
+ input.className = 'w-full bg-gray-700 border border-gray-600 text-white rounded-lg p-2.5';
+ inputElement = input;
} else if (field instanceof PDFCheckBox) {
fieldValues[name] = field.isChecked() ? 'on' : 'off';
- inputHtml = ``;
+ const input = document.createElement('input');
+ input.type = 'checkbox';
+ input.id = `field-${name}`;
+ input.name = name;
+ input.className = 'w-4 h-4 rounded text-indigo-600 bg-gray-700 border-gray-600 focus:ring-indigo-500';
+ input.checked = field.isChecked();
+ inputElement = input;
} else if (field instanceof PDFRadioGroup) {
fieldValues[name] = field.getSelected();
const options = field.getOptions();
- inputHtml = options.map((opt: any) => `
-
- `).join('');
- } else if (field instanceof PDFDropdown) {
- fieldValues[name] = field.getSelected();
- const dropdownOptions = field.getOptions();
- inputHtml = ``;
- } else if (field instanceof PDFOptionList) {
+ const fragment = document.createDocumentFragment();
+ options.forEach((opt: string) => {
+ const optionLabel = document.createElement('label');
+ optionLabel.className = 'flex items-center gap-2';
+
+ const radio = document.createElement('input');
+ radio.type = 'radio';
+ radio.name = name;
+ radio.value = opt;
+ radio.className = 'w-4 h-4 rounded text-indigo-600 bg-gray-700 border-gray-600 focus:ring-indigo-500';
+ if (opt === field.getSelected()) radio.checked = true;
+
+ const span = document.createElement('span');
+ span.className = 'text-gray-300 text-sm';
+ span.textContent = opt;
+
+ optionLabel.append(radio, span);
+ fragment.appendChild(optionLabel);
+ });
+ inputElement = fragment;
+ } else if (field instanceof PDFDropdown || field instanceof PDFOptionList) {
const selectedValues = field.getSelected();
fieldValues[name] = selectedValues;
- const listOptions = field.getOptions();
- inputHtml = ``;
- } else if (field instanceof PDFSignature) {
- inputHtml = `Signature field: Not supported for direct editing.
`;
- } else if (field instanceof PDFButton) {
- inputHtml = ``;
+ const options = field.getOptions();
+
+ const select = document.createElement('select');
+ select.id = `field-${name}`;
+ select.name = name;
+ select.className = 'w-full bg-gray-700 border border-gray-600 text-white rounded-lg p-2.5';
+
+ if (field instanceof PDFOptionList) {
+ select.multiple = true;
+ select.size = Math.min(10, options.length);
+ select.classList.add('h-auto');
+ }
+
+ options.forEach((opt: string) => {
+ const option = document.createElement('option');
+ option.value = opt;
+ option.textContent = opt;
+ if (selectedValues.includes(opt)) option.selected = true;
+ select.appendChild(option);
+ });
+ inputElement = select;
} else {
- return `Unsupported field type: ${field.constructor.name}
`;
+ const unsupportedDiv = document.createElement('div');
+ unsupportedDiv.className = 'p-4 bg-gray-800 rounded-lg border border-gray-700';
+ const p = document.createElement('p');
+ p.className = 'text-sm text-gray-400';
+ if (field instanceof PDFSignature) {
+ p.textContent = 'Signature field: Not supported for direct editing.';
+ } else if (field instanceof PDFButton) {
+ p.textContent = `Button: ${labelText}`;
+ } else {
+ p.textContent = `Unsupported field type: ${field.constructor.name}`;
+ }
+ unsupportedDiv.appendChild(p);
+ return unsupportedDiv;
}
- return `
-
- ${labelHtml}
- ${inputHtml}
-
- `;
+ wrapper.appendChild(inputElement);
+ return wrapper;
}
export async function setupFormFiller() {
if (!state.pdfDoc) return;
-
+
showLoader('Analyzing form fields...');
const formContainer = document.getElementById('form-fields-container');
const processBtn = document.getElementById('process-btn');
@@ -225,28 +270,37 @@ export async function setupFormFiller() {
const fields = form.getFields();
formState.fields = fields;
+ formContainer.textContent = '';
+
if (fields.length === 0) {
- formContainer.innerHTML = 'This PDF contains no form fields.
';
+ formContainer.innerHTML = 'This PDF contains no form fields.
';
processBtn.classList.add('hidden');
} else {
- let formHtml = '';
-
fields.forEach((field: any) => {
try {
- formHtml += createFormFieldHtml(field);
+ const fieldElement = createFormFieldHtml(field);
+ formContainer.appendChild(fieldElement);
} catch (e: any) {
console.error(`Error processing field "${field.getName()}":`, e);
- formHtml += `Unsupported field: ${field.getName()}
${e.message}
`;
+ const errorDiv = document.createElement('div');
+ errorDiv.className = 'p-4 bg-gray-800 rounded-lg border border-gray-700';
+ // Sanitize error message display
+ const p1 = document.createElement('p');
+ p1.className = 'text-sm text-gray-500';
+ p1.textContent = `Unsupported field: ${field.getName()}`;
+ const p2 = document.createElement('p');
+ p2.className = 'text-xs text-gray-500';
+ p2.textContent = e.message;
+ errorDiv.append(p1, p2);
+ formContainer.appendChild(errorDiv);
}
});
- formContainer.innerHTML = formHtml;
processBtn.classList.remove('hidden');
-
formContainer.addEventListener('change', handleFormChange);
formContainer.addEventListener('input', handleFormChange);
}
-
+
const pdfBytes = await state.pdfDoc.save();
pdfJsDoc = await pdfjsLib.getDocument({ data: pdfBytes }).promise;
currentPageNum = 1;
@@ -257,13 +311,13 @@ export async function setupFormFiller() {
const prevPageBtn = document.getElementById('prev-page');
const nextPageBtn = document.getElementById('next-page');
- if (zoomInBtn) zoomInBtn.onclick = () => setZoom(formState.scale + 0.25);
- if (zoomOutBtn) zoomOutBtn.onclick = () => setZoom(Math.max(0.25, formState.scale - 0.25));
- if (prevPageBtn) prevPageBtn.onclick = () => changePage(-1);
- if (nextPageBtn) nextPageBtn.onclick = () => changePage(1);
-
+ if (zoomInBtn) zoomInBtn.addEventListener('click', () => setZoom(formState.scale + 0.25));
+ if (zoomOutBtn) zoomOutBtn.addEventListener('click', () => setZoom(Math.max(1, formState.scale - 0.25)));
+ if (prevPageBtn) prevPageBtn.addEventListener('click', () => changePage(-1));
+ if (nextPageBtn) nextPageBtn.addEventListener('click', () => changePage(1));
+
hideLoader();
-
+
const formFillerOptions = document.getElementById('form-filler-options');
if (formFillerOptions) formFillerOptions.classList.remove('hidden');
@@ -278,7 +332,7 @@ export async function processAndDownloadForm() {
showLoader('Applying form data...');
try {
const form = state.pdfDoc.getForm();
-
+
Object.keys(fieldValues).forEach(fieldName => {
try {
const field = form.getField(fieldName);
@@ -297,7 +351,6 @@ export async function processAndDownloadForm() {
} else if (field instanceof PDFDropdown) {
field.select(value);
} else if (field instanceof PDFOptionList) {
- // Handle multi-select list box
if (Array.isArray(value)) {
value.forEach(option => field.select(option));
}
@@ -306,10 +359,10 @@ export async function processAndDownloadForm() {
console.error(`Error processing field "${fieldName}" during download:`, e);
}
});
-
+
const newPdfBytes = await state.pdfDoc.save();
downloadFile(new Blob([newPdfBytes], { type: 'application/pdf' }), 'filled-form.pdf');
-
+
showAlert('Success', 'Form has been filled and downloaded.');
} catch (e) {
diff --git a/src/js/main.ts b/src/js/main.ts
index fd2a60b..41d83b6 100644
--- a/src/js/main.ts
+++ b/src/js/main.ts
@@ -115,5 +115,4 @@ const init = () => {
console.log('Please share our tool and share the love!');
};
-// --- START THE APP ---
document.addEventListener('DOMContentLoaded', init);
\ No newline at end of file