Files
bentopdf/src/js/logic/n-up.ts
T
abdullahalam123 671297320e feat: add initial project setup with core PDF tools and utilities
- Implement core PDF manipulation tools (split, merge, convert, etc.)
- Add state management and UI utilities
- Set up build configuration with Vite and TailwindCSS
- Include essential dependencies for PDF processing
- Add gitignore and basic project configuration files
2025-10-12 11:55:45 +05:30

111 lines
5.5 KiB
TypeScript

import { showLoader, hideLoader, showAlert } from '../ui.js';
import { downloadFile, hexToRgb } from '../utils/helpers.js';
import { state } from '../state.js';
import { PDFDocument as PDFLibDocument, rgb, PageSizes } from 'pdf-lib';
export function setupNUpUI() {
const addBorderCheckbox = document.getElementById('add-border');
const borderColorWrapper = document.getElementById('border-color-wrapper');
if (addBorderCheckbox && borderColorWrapper) {
addBorderCheckbox.addEventListener('change', () => {
// @ts-expect-error TS(2339) FIXME: Property 'checked' does not exist on type 'HTMLEle... Remove this comment to see the full error message
borderColorWrapper.classList.toggle('hidden', !addBorderCheckbox.checked);
});
}
}
export async function nUpTool() {
// 1. Gather all options from the UI
// @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
const n = parseInt(document.getElementById('pages-per-sheet').value);
// @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
const pageSizeKey = document.getElementById('output-page-size').value;
// @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
let orientation = document.getElementById('output-orientation').value;
// @ts-expect-error TS(2339) FIXME: Property 'checked' does not exist on type 'HTMLEle... Remove this comment to see the full error message
const useMargins = document.getElementById('add-margins').checked;
// @ts-expect-error TS(2339) FIXME: Property 'checked' does not exist on type 'HTMLEle... Remove this comment to see the full error message
const addBorder = document.getElementById('add-border').checked;
// @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
const borderColor = hexToRgb(document.getElementById('border-color').value);
showLoader('Creating N-Up PDF...');
try {
const sourceDoc = state.pdfDoc;
const newDoc = await PDFLibDocument.create();
const sourcePages = sourceDoc.getPages();
const gridDims = { 2: [2, 1], 4: [2, 2], 9: [3, 3], 16: [4, 4] }[n];
let [pageWidth, pageHeight] = PageSizes[pageSizeKey];
if (orientation === 'auto') {
const firstPage = sourcePages[0];
const isSourceLandscape = firstPage.getWidth() > firstPage.getHeight();
// If source is landscape and grid is wider than tall (like 2x1), output landscape.
orientation = (isSourceLandscape && gridDims[0] > gridDims[1]) ? 'landscape' : 'portrait';
}
if (orientation === 'landscape' && pageWidth < pageHeight) {
[pageWidth, pageHeight] = [pageHeight, pageWidth];
}
const margin = useMargins ? 36 : 0;
const gutter = useMargins ? 10 : 0;
const usableWidth = pageWidth - (margin * 2);
const usableHeight = pageHeight - (margin * 2);
// Loop through the source pages in chunks of 'n'
for (let i = 0; i < sourcePages.length; i += n) {
showLoader(`Processing sheet ${Math.floor(i / n) + 1}...`);
const chunk = sourcePages.slice(i, i + n);
const outputPage = newDoc.addPage([pageWidth, pageHeight]);
// Calculate dimensions of each cell in the grid
const cellWidth = (usableWidth - (gutter * (gridDims[0] - 1))) / gridDims[0];
const cellHeight = (usableHeight - (gutter * (gridDims[1] - 1))) / gridDims[1];
for (let j = 0; j < chunk.length; j++) {
const sourcePage = chunk[j];
const embeddedPage = await newDoc.embedPage(sourcePage);
// Calculate scaled dimensions to fit the cell, preserving aspect ratio
const scale = Math.min(cellWidth / embeddedPage.width, cellHeight / embeddedPage.height);
const scaledWidth = embeddedPage.width * scale;
const scaledHeight = embeddedPage.height * scale;
// Calculate position (x, y) for this cell
const row = Math.floor(j / gridDims[0]);
const col = j % gridDims[0];
const cellX = margin + col * (cellWidth + gutter);
const cellY = pageHeight - margin - (row + 1) * cellHeight - row * gutter;
// Center the page within its cell
const x = cellX + (cellWidth - scaledWidth) / 2;
const y = cellY + (cellHeight - scaledHeight) / 2;
outputPage.drawPage(embeddedPage, { x, y, width: scaledWidth, height: scaledHeight });
if (addBorder) {
outputPage.drawRectangle({
x, y,
width: scaledWidth,
height: scaledHeight,
borderColor: rgb(borderColor.r, borderColor.g, borderColor.b),
borderWidth: 1,
});
}
}
}
const newPdfBytes = await newDoc.save();
downloadFile(new Blob([newPdfBytes], { type: 'application/pdf' }), `n-up_${n}.pdf`);
} catch (e) {
console.error(e);
showAlert('Error', 'An error occurred while creating the N-Up PDF.');
} finally {
hideLoader();
}
}