diff --git a/src/js/logic/crop-pdf-page.ts b/src/js/logic/crop-pdf-page.ts
index b399e04..c509b24 100644
--- a/src/js/logic/crop-pdf-page.ts
+++ b/src/js/logic/crop-pdf-page.ts
@@ -1,373 +1,442 @@
import { createIcons, icons } from 'lucide';
import { showLoader, hideLoader, showAlert } from '../ui.js';
-import { downloadFile, readFileAsArrayBuffer, formatBytes, getPDFDocument } from '../utils/helpers.js';
+import {
+ downloadFile,
+ readFileAsArrayBuffer,
+ formatBytes,
+ getPDFDocument,
+} from '../utils/helpers.js';
import Cropper from 'cropperjs';
import * as pdfjsLib from 'pdfjs-dist';
import { PDFDocument as PDFLibDocument } from 'pdf-lib';
import { CropperState } from '@/types';
-pdfjsLib.GlobalWorkerOptions.workerSrc = new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url).toString();
+pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
+ 'pdfjs-dist/build/pdf.worker.min.mjs',
+ import.meta.url
+).toString();
const cropperState: CropperState = {
- pdfDoc: null,
- currentPageNum: 1,
- cropper: null,
- originalPdfBytes: null,
- pageCrops: {},
- file: null,
+ pdfDoc: null,
+ currentPageNum: 1,
+ cropper: null,
+ originalPdfBytes: null,
+ pageCrops: {},
+ file: null,
};
if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', initializePage);
+ document.addEventListener('DOMContentLoaded', initializePage);
} else {
- initializePage();
+ initializePage();
}
function initializePage() {
- createIcons({ icons });
+ createIcons({ icons });
- const fileInput = document.getElementById('file-input') as HTMLInputElement;
- const dropZone = document.getElementById('drop-zone');
+ const fileInput = document.getElementById('file-input') as HTMLInputElement;
+ const dropZone = document.getElementById('drop-zone');
- if (fileInput) fileInput.addEventListener('change', handleFileUpload);
+ if (fileInput) fileInput.addEventListener('change', handleFileUpload);
- if (dropZone) {
- dropZone.addEventListener('dragover', (e) => { e.preventDefault(); dropZone.classList.add('bg-gray-700'); });
- dropZone.addEventListener('dragleave', () => { dropZone.classList.remove('bg-gray-700'); });
- dropZone.addEventListener('drop', (e) => {
- e.preventDefault();
- dropZone.classList.remove('bg-gray-700');
- const droppedFiles = e.dataTransfer?.files;
- if (droppedFiles && droppedFiles.length > 0) handleFile(droppedFiles[0]);
- });
- // Clear value on click to allow re-selecting the same file
- fileInput?.addEventListener('click', () => {
- if (fileInput) fileInput.value = '';
- });
- }
-
- document.getElementById('back-to-tools')?.addEventListener('click', () => {
- window.location.href = import.meta.env.BASE_URL;
+ if (dropZone) {
+ dropZone.addEventListener('dragover', (e) => {
+ e.preventDefault();
+ dropZone.classList.add('bg-gray-700');
});
+ dropZone.addEventListener('dragleave', () => {
+ dropZone.classList.remove('bg-gray-700');
+ });
+ dropZone.addEventListener('drop', (e) => {
+ e.preventDefault();
+ dropZone.classList.remove('bg-gray-700');
+ const droppedFiles = e.dataTransfer?.files;
+ if (droppedFiles && droppedFiles.length > 0) handleFile(droppedFiles[0]);
+ });
+ // Clear value on click to allow re-selecting the same file
+ fileInput?.addEventListener('click', () => {
+ if (fileInput) fileInput.value = '';
+ });
+ }
- document.getElementById('prev-page')?.addEventListener('click', () => changePage(-1));
- document.getElementById('next-page')?.addEventListener('click', () => changePage(1));
- document.getElementById('crop-button')?.addEventListener('click', performCrop);
+ document.getElementById('back-to-tools')?.addEventListener('click', () => {
+ window.location.href = import.meta.env.BASE_URL;
+ });
+
+ document
+ .getElementById('prev-page')
+ ?.addEventListener('click', () => changePage(-1));
+ document
+ .getElementById('next-page')
+ ?.addEventListener('click', () => changePage(1));
+ document
+ .getElementById('crop-button')
+ ?.addEventListener('click', performCrop);
}
function handleFileUpload(e: Event) {
- const input = e.target as HTMLInputElement;
- if (input.files && input.files.length > 0) handleFile(input.files[0]);
+ const input = e.target as HTMLInputElement;
+ if (input.files && input.files.length > 0) handleFile(input.files[0]);
}
async function handleFile(file: File) {
- if (file.type !== 'application/pdf' && !file.name.toLowerCase().endsWith('.pdf')) {
- showAlert('Invalid File', 'Please select a PDF file.');
- return;
- }
+ if (
+ file.type !== 'application/pdf' &&
+ !file.name.toLowerCase().endsWith('.pdf')
+ ) {
+ showAlert('Invalid File', 'Please select a PDF file.');
+ return;
+ }
- showLoader('Loading PDF...');
- cropperState.file = file;
- cropperState.pageCrops = {};
+ showLoader('Loading PDF...');
+ cropperState.file = file;
+ cropperState.pageCrops = {};
- try {
- const arrayBuffer = await readFileAsArrayBuffer(file);
- cropperState.originalPdfBytes = arrayBuffer as ArrayBuffer;
- cropperState.pdfDoc = await getPDFDocument({ data: (arrayBuffer as ArrayBuffer).slice(0) }).promise;
- cropperState.currentPageNum = 1;
+ try {
+ const arrayBuffer = await readFileAsArrayBuffer(file);
+ cropperState.originalPdfBytes = arrayBuffer as ArrayBuffer;
+ cropperState.pdfDoc = await getPDFDocument({
+ data: (arrayBuffer as ArrayBuffer).slice(0),
+ }).promise;
+ cropperState.currentPageNum = 1;
- updateFileDisplay();
- await displayPageAsImage(cropperState.currentPageNum);
- hideLoader();
- } catch (error) {
- console.error('Error loading PDF:', error);
- hideLoader();
- showAlert('Error', 'Failed to load PDF file.');
- }
+ updateFileDisplay();
+ await displayPageAsImage(cropperState.currentPageNum);
+ hideLoader();
+ } catch (error) {
+ console.error('Error loading PDF:', error);
+ hideLoader();
+ showAlert('Error', 'Failed to load PDF file.');
+ }
}
function updateFileDisplay() {
- const fileDisplayArea = document.getElementById('file-display-area');
- if (!fileDisplayArea || !cropperState.file) return;
+ const fileDisplayArea = document.getElementById('file-display-area');
+ if (!fileDisplayArea || !cropperState.file) return;
- fileDisplayArea.innerHTML = '';
- const fileDiv = document.createElement('div');
- fileDiv.className = 'flex items-center justify-between bg-gray-700 p-3 rounded-lg';
+ fileDisplayArea.innerHTML = '';
+ const fileDiv = document.createElement('div');
+ fileDiv.className =
+ 'flex items-center justify-between bg-gray-700 p-3 rounded-lg';
- const infoContainer = document.createElement('div');
- infoContainer.className = 'flex flex-col flex-1 min-w-0';
+ const infoContainer = document.createElement('div');
+ infoContainer.className = 'flex flex-col flex-1 min-w-0';
- const nameSpan = document.createElement('div');
- nameSpan.className = 'truncate font-medium text-gray-200 text-sm mb-1';
- nameSpan.textContent = cropperState.file.name;
+ const nameSpan = document.createElement('div');
+ nameSpan.className = 'truncate font-medium text-gray-200 text-sm mb-1';
+ nameSpan.textContent = cropperState.file.name;
- const metaSpan = document.createElement('div');
- metaSpan.className = 'text-xs text-gray-400';
- metaSpan.textContent = `${formatBytes(cropperState.file.size)} • ${cropperState.pdfDoc?.numPages || 0} pages`;
+ const metaSpan = document.createElement('div');
+ metaSpan.className = 'text-xs text-gray-400';
+ metaSpan.textContent = `${formatBytes(cropperState.file.size)} • ${cropperState.pdfDoc?.numPages || 0} pages`;
- infoContainer.append(nameSpan, metaSpan);
+ infoContainer.append(nameSpan, metaSpan);
- const removeBtn = document.createElement('button');
- removeBtn.className = 'ml-4 text-red-400 hover:text-red-300 flex-shrink-0';
- removeBtn.innerHTML = '';
- removeBtn.onclick = () => resetState();
+ const removeBtn = document.createElement('button');
+ removeBtn.className = 'ml-4 text-red-400 hover:text-red-300 flex-shrink-0';
+ removeBtn.innerHTML = '';
+ removeBtn.onclick = () => resetState();
- fileDiv.append(infoContainer, removeBtn);
- fileDisplayArea.appendChild(fileDiv);
- createIcons({ icons });
+ fileDiv.append(infoContainer, removeBtn);
+ fileDisplayArea.appendChild(fileDiv);
+ createIcons({ icons });
}
function saveCurrentCrop() {
- if (cropperState.cropper) {
- const currentCrop = cropperState.cropper.getData(true);
- const imageData = cropperState.cropper.getImageData();
- const cropPercentages = {
- x: currentCrop.x / imageData.naturalWidth,
- y: currentCrop.y / imageData.naturalHeight,
- width: currentCrop.width / imageData.naturalWidth,
- height: currentCrop.height / imageData.naturalHeight,
- };
- cropperState.pageCrops[cropperState.currentPageNum] = cropPercentages;
- }
+ if (cropperState.cropper) {
+ const currentCrop = cropperState.cropper.getData(true);
+ const imageData = cropperState.cropper.getImageData();
+ const cropPercentages = {
+ x: currentCrop.x / imageData.naturalWidth,
+ y: currentCrop.y / imageData.naturalHeight,
+ width: currentCrop.width / imageData.naturalWidth,
+ height: currentCrop.height / imageData.naturalHeight,
+ };
+ cropperState.pageCrops[cropperState.currentPageNum] = cropPercentages;
+ }
}
async function displayPageAsImage(num: number) {
- showLoader(`Rendering Page ${num}...`);
+ showLoader(`Rendering Page ${num}...`);
- try {
- const page = await cropperState.pdfDoc.getPage(num);
- const viewport = page.getViewport({ scale: 2.5 });
+ try {
+ const page = await cropperState.pdfDoc.getPage(num);
+ const viewport = page.getViewport({ scale: 2.5 });
- const tempCanvas = document.createElement('canvas');
- const tempCtx = tempCanvas.getContext('2d');
- tempCanvas.width = viewport.width;
- tempCanvas.height = viewport.height;
- await page.render({ canvasContext: tempCtx, viewport: viewport }).promise;
+ const tempCanvas = document.createElement('canvas');
+ const tempCtx = tempCanvas.getContext('2d');
+ tempCanvas.width = viewport.width;
+ tempCanvas.height = viewport.height;
+ await page.render({ canvasContext: tempCtx, viewport: viewport }).promise;
- if (cropperState.cropper) cropperState.cropper.destroy();
+ if (cropperState.cropper) cropperState.cropper.destroy();
- const cropperEditor = document.getElementById('cropper-editor');
- if (cropperEditor) cropperEditor.classList.remove('hidden');
+ const cropperEditor = document.getElementById('cropper-editor');
+ if (cropperEditor) cropperEditor.classList.remove('hidden');
- const container = document.getElementById('cropper-container');
- if (!container) return;
+ const container = document.getElementById('cropper-container');
+ if (!container) return;
- container.innerHTML = '';
- const image = document.createElement('img');
- image.src = tempCanvas.toDataURL('image/png');
- container.appendChild(image);
+ container.innerHTML = '';
+ const image = document.createElement('img');
+ image.src = tempCanvas.toDataURL('image/png');
+ container.appendChild(image);
- image.onload = () => {
- cropperState.cropper = new Cropper(image, {
- viewMode: 1,
- background: false,
- autoCropArea: 0.8,
- responsive: true,
- rotatable: false,
- zoomable: false,
- });
+ image.onload = () => {
+ cropperState.cropper = new Cropper(image, {
+ viewMode: 1,
+ background: false,
+ autoCropArea: 0.8,
+ responsive: true,
+ rotatable: false,
+ zoomable: false,
+ });
- const savedCrop = cropperState.pageCrops[num];
- if (savedCrop) {
- const imageData = cropperState.cropper.getImageData();
- cropperState.cropper.setData({
- x: savedCrop.x * imageData.naturalWidth,
- y: savedCrop.y * imageData.naturalHeight,
- width: savedCrop.width * imageData.naturalWidth,
- height: savedCrop.height * imageData.naturalHeight,
- });
- }
+ const savedCrop = cropperState.pageCrops[num];
+ if (savedCrop) {
+ const imageData = cropperState.cropper.getImageData();
+ cropperState.cropper.setData({
+ x: savedCrop.x * imageData.naturalWidth,
+ y: savedCrop.y * imageData.naturalHeight,
+ width: savedCrop.width * imageData.naturalWidth,
+ height: savedCrop.height * imageData.naturalHeight,
+ });
+ }
- updatePageInfo();
- enableControls();
- hideLoader();
- };
- } catch (error) {
- console.error('Error rendering page:', error);
- showAlert('Error', 'Failed to render page.');
- hideLoader();
- }
+ updatePageInfo();
+ enableControls();
+ hideLoader();
+ };
+ } catch (error) {
+ console.error('Error rendering page:', error);
+ showAlert('Error', 'Failed to render page.');
+ hideLoader();
+ }
}
async function changePage(offset: number) {
- saveCurrentCrop();
- const newPageNum = cropperState.currentPageNum + offset;
- if (newPageNum > 0 && newPageNum <= cropperState.pdfDoc.numPages) {
- cropperState.currentPageNum = newPageNum;
- await displayPageAsImage(cropperState.currentPageNum);
- }
+ saveCurrentCrop();
+ const newPageNum = cropperState.currentPageNum + offset;
+ if (newPageNum > 0 && newPageNum <= cropperState.pdfDoc.numPages) {
+ cropperState.currentPageNum = newPageNum;
+ await displayPageAsImage(cropperState.currentPageNum);
+ }
}
function updatePageInfo() {
- const pageInfo = document.getElementById('page-info');
- if (pageInfo) pageInfo.textContent = `Page ${cropperState.currentPageNum} of ${cropperState.pdfDoc.numPages}`;
+ const pageInfo = document.getElementById('page-info');
+ if (pageInfo)
+ pageInfo.textContent = `Page ${cropperState.currentPageNum} of ${cropperState.pdfDoc.numPages}`;
}
function enableControls() {
- const prevBtn = document.getElementById('prev-page') as HTMLButtonElement;
- const nextBtn = document.getElementById('next-page') as HTMLButtonElement;
- const cropBtn = document.getElementById('crop-button') as HTMLButtonElement;
+ const prevBtn = document.getElementById('prev-page') as HTMLButtonElement;
+ const nextBtn = document.getElementById('next-page') as HTMLButtonElement;
+ const cropBtn = document.getElementById('crop-button') as HTMLButtonElement;
- if (prevBtn) prevBtn.disabled = cropperState.currentPageNum <= 1;
- if (nextBtn) nextBtn.disabled = cropperState.currentPageNum >= cropperState.pdfDoc.numPages;
- if (cropBtn) cropBtn.disabled = false;
+ if (prevBtn) prevBtn.disabled = cropperState.currentPageNum <= 1;
+ if (nextBtn)
+ nextBtn.disabled =
+ cropperState.currentPageNum >= cropperState.pdfDoc.numPages;
+ if (cropBtn) cropBtn.disabled = false;
}
async function performCrop() {
- saveCurrentCrop();
+ saveCurrentCrop();
- const isDestructive = (document.getElementById('destructive-crop-toggle') as HTMLInputElement)?.checked;
- const isApplyToAll = (document.getElementById('apply-to-all-toggle') as HTMLInputElement)?.checked;
+ const isDestructive = (
+ document.getElementById('destructive-crop-toggle') as HTMLInputElement
+ )?.checked;
+ const isApplyToAll = (
+ document.getElementById('apply-to-all-toggle') as HTMLInputElement
+ )?.checked;
- let finalCropData: Record = {};
+ let finalCropData: Record = {};
- if (isApplyToAll) {
- const currentCrop = cropperState.pageCrops[cropperState.currentPageNum];
- if (!currentCrop) {
- showAlert('No Crop Area', 'Please select an area to crop first.');
- return;
- }
- for (let i = 1; i <= cropperState.pdfDoc.numPages; i++) {
- finalCropData[i] = currentCrop;
- }
+ if (isApplyToAll) {
+ const currentCrop = cropperState.pageCrops[cropperState.currentPageNum];
+ if (!currentCrop) {
+ showAlert('No Crop Area', 'Please select an area to crop first.');
+ return;
+ }
+ for (let i = 1; i <= cropperState.pdfDoc.numPages; i++) {
+ finalCropData[i] = currentCrop;
+ }
+ } else {
+ finalCropData = { ...cropperState.pageCrops };
+ }
+
+ if (Object.keys(finalCropData).length === 0) {
+ showAlert(
+ 'No Crop Area',
+ 'Please select an area on at least one page to crop.'
+ );
+ return;
+ }
+
+ showLoader('Applying crop...');
+
+ try {
+ let finalPdfBytes;
+ if (isDestructive) {
+ finalPdfBytes = await performFlatteningCrop(finalCropData);
} else {
- finalCropData = { ...cropperState.pageCrops };
+ finalPdfBytes = await performMetadataCrop(finalCropData);
}
- if (Object.keys(finalCropData).length === 0) {
- showAlert('No Crop Area', 'Please select an area on at least one page to crop.');
- return;
- }
-
- showLoader('Applying crop...');
-
- try {
- let finalPdfBytes;
- if (isDestructive) {
- finalPdfBytes = await performFlatteningCrop(finalCropData);
- } else {
- finalPdfBytes = await performMetadataCrop(finalCropData);
- }
-
- const fileName = isDestructive ? 'flattened_crop.pdf' : 'standard_crop.pdf';
- downloadFile(new Blob([finalPdfBytes], { type: 'application/pdf' }), fileName);
- showAlert('Success', 'Crop complete! Your download has started.', 'success', () => resetState());
- } catch (e) {
- console.error(e);
- showAlert('Error', 'An error occurred during cropping.');
- } finally {
- hideLoader();
- }
+ const fileName = isDestructive ? 'flattened_crop.pdf' : 'standard_crop.pdf';
+ downloadFile(
+ new Blob([finalPdfBytes], { type: 'application/pdf' }),
+ fileName
+ );
+ showAlert(
+ 'Success',
+ 'Crop complete! Your download has started.',
+ 'success',
+ () => resetState()
+ );
+ } catch (e) {
+ console.error(e);
+ showAlert('Error', 'An error occurred during cropping.');
+ } finally {
+ hideLoader();
+ }
}
-async function performMetadataCrop(cropData: Record): Promise {
- const pdfToModify = await PDFLibDocument.load(cropperState.originalPdfBytes!, { ignoreEncryption: true, throwOnInvalidObject: false });
+async function performMetadataCrop(
+ cropData: Record
+): Promise {
+ const pdfToModify = await PDFLibDocument.load(
+ cropperState.originalPdfBytes!,
+ { ignoreEncryption: true, throwOnInvalidObject: false }
+ );
- for (const pageNum in cropData) {
- const pdfJsPage = await cropperState.pdfDoc.getPage(Number(pageNum));
- const viewport = pdfJsPage.getViewport({ scale: 1 });
- const crop = cropData[pageNum];
+ for (const pageNum in cropData) {
+ const pdfJsPage = await cropperState.pdfDoc.getPage(Number(pageNum));
+ const viewport = pdfJsPage.getViewport({ scale: 1 });
+ const crop = cropData[pageNum];
- const cropX = viewport.width * crop.x;
- const cropY = viewport.height * crop.y;
- const cropW = viewport.width * crop.width;
- const cropH = viewport.height * crop.height;
+ const cropX = viewport.width * crop.x;
+ const cropY = viewport.height * crop.y;
+ const cropW = viewport.width * crop.width;
+ const cropH = viewport.height * crop.height;
- const visualCorners = [
- { x: cropX, y: cropY },
- { x: cropX + cropW, y: cropY },
- { x: cropX + cropW, y: cropY + cropH },
- { x: cropX, y: cropY + cropH },
- ];
+ const visualCorners = [
+ { x: cropX, y: cropY },
+ { x: cropX + cropW, y: cropY },
+ { x: cropX + cropW, y: cropY + cropH },
+ { x: cropX, y: cropY + cropH },
+ ];
- const pdfCorners = visualCorners.map(p => viewport.convertToPdfPoint(p.x, p.y));
- const pdfXs = pdfCorners.map(p => p[0]);
- const pdfYs = pdfCorners.map(p => p[1]);
+ const pdfCorners = visualCorners.map((p) =>
+ viewport.convertToPdfPoint(p.x, p.y)
+ );
+ const pdfXs = pdfCorners.map((p) => p[0]);
+ const pdfYs = pdfCorners.map((p) => p[1]);
- const minX = Math.min(...pdfXs);
- const maxX = Math.max(...pdfXs);
- const minY = Math.min(...pdfYs);
- const maxY = Math.max(...pdfYs);
+ const minX = Math.min(...pdfXs);
+ const maxX = Math.max(...pdfXs);
+ const minY = Math.min(...pdfYs);
+ const maxY = Math.max(...pdfYs);
- const page = pdfToModify.getPages()[Number(pageNum) - 1];
- page.setCropBox(minX, minY, maxX - minX, maxY - minY);
- }
+ const page = pdfToModify.getPages()[Number(pageNum) - 1];
+ page.setCropBox(minX, minY, maxX - minX, maxY - minY);
+ }
- return pdfToModify.save();
+ return pdfToModify.save();
}
-async function performFlatteningCrop(cropData: Record): Promise {
- const newPdfDoc = await PDFLibDocument.create();
- const sourcePdfDocForCopying = await PDFLibDocument.load(cropperState.originalPdfBytes!, { ignoreEncryption: true, throwOnInvalidObject: false });
- const totalPages = cropperState.pdfDoc.numPages;
+async function performFlatteningCrop(
+ cropData: Record
+): Promise {
+ const newPdfDoc = await PDFLibDocument.create();
+ const sourcePdfDocForCopying = await PDFLibDocument.load(
+ cropperState.originalPdfBytes!,
+ { ignoreEncryption: true, throwOnInvalidObject: false }
+ );
+ const totalPages = cropperState.pdfDoc.numPages;
- for (let i = 0; i < totalPages; i++) {
- const pageNum = i + 1;
- showLoader(`Processing page ${pageNum} of ${totalPages}...`);
+ for (let i = 0; i < totalPages; i++) {
+ const pageNum = i + 1;
+ showLoader(`Processing page ${pageNum} of ${totalPages}...`);
- if (cropData[pageNum]) {
- const page = await cropperState.pdfDoc.getPage(pageNum);
- const viewport = page.getViewport({ scale: 2.5 });
+ if (cropData[pageNum]) {
+ const page = await cropperState.pdfDoc.getPage(pageNum);
+ const viewport = page.getViewport({ scale: 2.5 });
- const tempCanvas = document.createElement('canvas');
- const tempCtx = tempCanvas.getContext('2d');
- tempCanvas.width = viewport.width;
- tempCanvas.height = viewport.height;
- await page.render({ canvasContext: tempCtx, viewport: viewport }).promise;
+ const tempCanvas = document.createElement('canvas');
+ const tempCtx = tempCanvas.getContext('2d');
+ tempCanvas.width = viewport.width;
+ tempCanvas.height = viewport.height;
+ await page.render({ canvasContext: tempCtx, viewport: viewport }).promise;
- const finalCanvas = document.createElement('canvas');
- const finalCtx = finalCanvas.getContext('2d');
- const crop = cropData[pageNum];
- const finalWidth = tempCanvas.width * crop.width;
- const finalHeight = tempCanvas.height * crop.height;
- finalCanvas.width = finalWidth;
- finalCanvas.height = finalHeight;
+ const finalCanvas = document.createElement('canvas');
+ const finalCtx = finalCanvas.getContext('2d');
+ const crop = cropData[pageNum];
+ const finalWidth = tempCanvas.width * crop.width;
+ const finalHeight = tempCanvas.height * crop.height;
+ finalCanvas.width = finalWidth;
+ finalCanvas.height = finalHeight;
- finalCtx?.drawImage(
- tempCanvas,
- tempCanvas.width * crop.x,
- tempCanvas.height * crop.y,
- finalWidth,
- finalHeight,
- 0, 0, finalWidth, finalHeight
- );
+ finalCtx?.drawImage(
+ tempCanvas,
+ tempCanvas.width * crop.x,
+ tempCanvas.height * crop.y,
+ finalWidth,
+ finalHeight,
+ 0,
+ 0,
+ finalWidth,
+ finalHeight
+ );
- const pngBytes = await new Promise((res) =>
- finalCanvas.toBlob((blob) => blob?.arrayBuffer().then(res), 'image/jpeg', 0.9)
- );
- const embeddedImage = await newPdfDoc.embedPng(pngBytes);
- const newPage = newPdfDoc.addPage([finalWidth, finalHeight]);
- newPage.drawImage(embeddedImage, { x: 0, y: 0, width: finalWidth, height: finalHeight });
- } else {
- const [copiedPage] = await newPdfDoc.copyPages(sourcePdfDocForCopying, [i]);
- newPdfDoc.addPage(copiedPage);
- }
+ const pngBytes = await new Promise((res) =>
+ finalCanvas.toBlob(
+ (blob) => blob?.arrayBuffer().then(res),
+ 'image/jpeg',
+ 0.9
+ )
+ );
+ const embeddedImage = await newPdfDoc.embedPng(pngBytes);
+ const newPage = newPdfDoc.addPage([finalWidth, finalHeight]);
+ newPage.drawImage(embeddedImage, {
+ x: 0,
+ y: 0,
+ width: finalWidth,
+ height: finalHeight,
+ });
+ } else {
+ const [copiedPage] = await newPdfDoc.copyPages(sourcePdfDocForCopying, [
+ i,
+ ]);
+ newPdfDoc.addPage(copiedPage);
}
+ }
- return newPdfDoc.save();
+ return newPdfDoc.save();
}
function resetState() {
- if (cropperState.cropper) {
- cropperState.cropper.destroy();
- cropperState.cropper = null;
- }
+ if (cropperState.cropper) {
+ cropperState.cropper.destroy();
+ cropperState.cropper = null;
+ }
- cropperState.pdfDoc = null;
- cropperState.originalPdfBytes = null;
- cropperState.pageCrops = {};
- cropperState.currentPageNum = 1;
- cropperState.file = null;
+ cropperState.pdfDoc = null;
+ cropperState.originalPdfBytes = null;
+ cropperState.pageCrops = {};
+ cropperState.currentPageNum = 1;
+ cropperState.file = null;
- const cropperEditor = document.getElementById('cropper-editor');
- if (cropperEditor) cropperEditor.classList.add('hidden');
+ const cropperEditor = document.getElementById('cropper-editor');
+ if (cropperEditor) cropperEditor.classList.add('hidden');
- const container = document.getElementById('cropper-container');
- if (container) container.innerHTML = '';
+ const container = document.getElementById('cropper-container');
+ if (container) container.innerHTML = '';
- const fileDisplayArea = document.getElementById('file-display-area');
- if (fileDisplayArea) fileDisplayArea.innerHTML = '';
+ const fileDisplayArea = document.getElementById('file-display-area');
+ if (fileDisplayArea) fileDisplayArea.innerHTML = '';
- const cropBtn = document.getElementById('crop-button') as HTMLButtonElement;
- if (cropBtn) cropBtn.disabled = true;
+ const cropBtn = document.getElementById('crop-button') as HTMLButtonElement;
+ if (cropBtn) cropBtn.disabled = true;
}