diff --git a/src/js/logic/fix-page-size-page.ts b/src/js/logic/fix-page-size-page.ts
index 76c54d4..976c771 100644
--- a/src/js/logic/fix-page-size-page.ts
+++ b/src/js/logic/fix-page-size-page.ts
@@ -1,230 +1,218 @@
import { showAlert } from '../ui.js';
import { downloadFile, formatBytes, hexToRgb } from '../utils/helpers.js';
-import { PDFDocument as PDFLibDocument, rgb, PageSizes } from 'pdf-lib';
+import { fixPageSize as fixPageSizeCore } from '../utils/pdf-operations';
import { icons, createIcons } from 'lucide';
import { FixPageSizeState } from '@/types';
const pageState: FixPageSizeState = {
- file: null,
+ file: null,
};
function resetState() {
- pageState.file = null;
+ pageState.file = null;
- const fileDisplayArea = document.getElementById('file-display-area');
- if (fileDisplayArea) fileDisplayArea.innerHTML = '';
+ const fileDisplayArea = document.getElementById('file-display-area');
+ if (fileDisplayArea) fileDisplayArea.innerHTML = '';
- const toolOptions = document.getElementById('tool-options');
- if (toolOptions) toolOptions.classList.add('hidden');
+ const toolOptions = document.getElementById('tool-options');
+ if (toolOptions) toolOptions.classList.add('hidden');
- const fileInput = document.getElementById('file-input') as HTMLInputElement;
- if (fileInput) fileInput.value = '';
+ const fileInput = document.getElementById('file-input') as HTMLInputElement;
+ if (fileInput) fileInput.value = '';
}
async function updateUI() {
- const fileDisplayArea = document.getElementById('file-display-area');
- const toolOptions = document.getElementById('tool-options');
+ const fileDisplayArea = document.getElementById('file-display-area');
+ const toolOptions = document.getElementById('tool-options');
- if (!fileDisplayArea) return;
+ if (!fileDisplayArea) return;
- fileDisplayArea.innerHTML = '';
+ fileDisplayArea.innerHTML = '';
- if (pageState.file) {
- const fileDiv = document.createElement('div');
- fileDiv.className = 'flex items-center justify-between bg-gray-700 p-3 rounded-lg text-sm';
+ if (pageState.file) {
+ const fileDiv = document.createElement('div');
+ fileDiv.className =
+ 'flex items-center justify-between bg-gray-700 p-3 rounded-lg text-sm';
- const infoContainer = document.createElement('div');
- infoContainer.className = 'flex flex-col overflow-hidden';
+ const infoContainer = document.createElement('div');
+ infoContainer.className = 'flex flex-col overflow-hidden';
- const nameSpan = document.createElement('div');
- nameSpan.className = 'truncate font-medium text-gray-200 text-sm mb-1';
- nameSpan.textContent = pageState.file.name;
+ const nameSpan = document.createElement('div');
+ nameSpan.className = 'truncate font-medium text-gray-200 text-sm mb-1';
+ nameSpan.textContent = pageState.file.name;
- const metaSpan = document.createElement('div');
- metaSpan.className = 'text-xs text-gray-400';
- metaSpan.textContent = formatBytes(pageState.file.size);
+ const metaSpan = document.createElement('div');
+ metaSpan.className = 'text-xs text-gray-400';
+ metaSpan.textContent = formatBytes(pageState.file.size);
- 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 = function () {
- resetState();
- };
+ const removeBtn = document.createElement('button');
+ removeBtn.className = 'ml-4 text-red-400 hover:text-red-300 flex-shrink-0';
+ removeBtn.innerHTML = '';
+ removeBtn.onclick = function () {
+ resetState();
+ };
- fileDiv.append(infoContainer, removeBtn);
- fileDisplayArea.appendChild(fileDiv);
- createIcons({ icons });
+ fileDiv.append(infoContainer, removeBtn);
+ fileDisplayArea.appendChild(fileDiv);
+ createIcons({ icons });
- if (toolOptions) toolOptions.classList.remove('hidden');
- } else {
- if (toolOptions) toolOptions.classList.add('hidden');
- }
+ if (toolOptions) toolOptions.classList.remove('hidden');
+ } else {
+ if (toolOptions) toolOptions.classList.add('hidden');
+ }
}
function handleFileSelect(files: FileList | null) {
- if (files && files.length > 0) {
- const file = files[0];
- if (file.type === 'application/pdf' || file.name.toLowerCase().endsWith('.pdf')) {
- pageState.file = file;
- updateUI();
- }
+ if (files && files.length > 0) {
+ const file = files[0];
+ if (
+ file.type === 'application/pdf' ||
+ file.name.toLowerCase().endsWith('.pdf')
+ ) {
+ pageState.file = file;
+ updateUI();
}
+ }
}
async function fixPageSize() {
- if (!pageState.file) {
- showAlert('No File', 'Please upload a PDF file first.');
- return;
- }
+ if (!pageState.file) {
+ showAlert('No File', 'Please upload a PDF file first.');
+ return;
+ }
- const targetSizeKey = (document.getElementById('target-size') as HTMLSelectElement).value;
- const orientation = (document.getElementById('orientation') as HTMLSelectElement).value;
- const scalingMode = (document.querySelector('input[name="scaling-mode"]:checked') as HTMLInputElement).value;
- const backgroundColor = hexToRgb((document.getElementById('background-color') as HTMLInputElement).value);
+ const targetSize = (
+ document.getElementById('target-size') as HTMLSelectElement
+ ).value;
+ const orientation = (
+ document.getElementById('orientation') as HTMLSelectElement
+ ).value;
+ const scalingMode = (
+ document.querySelector(
+ 'input[name="scaling-mode"]:checked'
+ ) as HTMLInputElement
+ ).value;
+ const backgroundColor = hexToRgb(
+ (document.getElementById('background-color') as HTMLInputElement).value
+ );
- const loaderModal = document.getElementById('loader-modal');
- const loaderText = document.getElementById('loader-text');
- if (loaderModal) loaderModal.classList.remove('hidden');
- if (loaderText) loaderText.textContent = 'Standardizing pages...';
+ const loaderModal = document.getElementById('loader-modal');
+ const loaderText = document.getElementById('loader-text');
+ if (loaderModal) loaderModal.classList.remove('hidden');
+ if (loaderText) loaderText.textContent = 'Standardizing pages...';
- try {
- let targetWidth, targetHeight;
+ try {
+ const customWidth =
+ parseFloat(
+ (document.getElementById('custom-width') as HTMLInputElement)?.value
+ ) || 210;
+ const customHeight =
+ parseFloat(
+ (document.getElementById('custom-height') as HTMLInputElement)?.value
+ ) || 297;
+ const customUnits =
+ (document.getElementById('custom-units') as HTMLSelectElement)?.value ||
+ 'mm';
- if (targetSizeKey === 'Custom') {
- const width = parseFloat((document.getElementById('custom-width') as HTMLInputElement).value);
- const height = parseFloat((document.getElementById('custom-height') as HTMLInputElement).value);
- const units = (document.getElementById('custom-units') as HTMLSelectElement).value;
+ const arrayBuffer = await pageState.file.arrayBuffer();
+ const pdfBytes = new Uint8Array(arrayBuffer);
- if (units === 'in') {
- targetWidth = width * 72;
- targetHeight = height * 72;
- } else {
- // mm
- targetWidth = width * (72 / 25.4);
- targetHeight = height * (72 / 25.4);
- }
- } else {
- [targetWidth, targetHeight] = PageSizes[targetSizeKey as keyof typeof PageSizes];
- }
+ const newPdfBytes = await fixPageSizeCore(pdfBytes, {
+ targetSize,
+ orientation,
+ scalingMode,
+ backgroundColor,
+ customWidth,
+ customHeight,
+ customUnits,
+ });
- if (orientation === 'landscape' && targetWidth < targetHeight) {
- [targetWidth, targetHeight] = [targetHeight, targetWidth];
- } else if (orientation === 'portrait' && targetWidth > targetHeight) {
- [targetWidth, targetHeight] = [targetHeight, targetWidth];
- }
-
- const arrayBuffer = await pageState.file.arrayBuffer();
- const sourceDoc = await PDFLibDocument.load(arrayBuffer);
- const newDoc = await PDFLibDocument.create();
-
- for (const sourcePage of sourceDoc.getPages()) {
- const { width: sourceWidth, height: sourceHeight } = sourcePage.getSize();
- const embeddedPage = await newDoc.embedPage(sourcePage);
-
- const newPage = newDoc.addPage([targetWidth, targetHeight]);
- newPage.drawRectangle({
- x: 0,
- y: 0,
- width: targetWidth,
- height: targetHeight,
- color: rgb(backgroundColor.r, backgroundColor.g, backgroundColor.b),
- });
-
- const scaleX = targetWidth / sourceWidth;
- const scaleY = targetHeight / sourceHeight;
- const scale = scalingMode === 'fit' ? Math.min(scaleX, scaleY) : Math.max(scaleX, scaleY);
-
- const scaledWidth = sourceWidth * scale;
- const scaledHeight = sourceHeight * scale;
-
- const x = (targetWidth - scaledWidth) / 2;
- const y = (targetHeight - scaledHeight) / 2;
-
- newPage.drawPage(embeddedPage, {
- x,
- y,
- width: scaledWidth,
- height: scaledHeight,
- });
- }
-
- const newPdfBytes = await newDoc.save();
- downloadFile(
- new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }),
- 'standardized.pdf'
- );
- showAlert('Success', 'Page sizes standardized successfully!', 'success', () => { resetState(); });
- } catch (e) {
- console.error(e);
- showAlert('Error', 'An error occurred while standardizing pages.');
- } finally {
- if (loaderModal) loaderModal.classList.add('hidden');
- }
+ downloadFile(
+ new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }),
+ 'standardized.pdf'
+ );
+ showAlert(
+ 'Success',
+ 'Page sizes standardized successfully!',
+ 'success',
+ () => {
+ resetState();
+ }
+ );
+ } catch (e) {
+ console.error(e);
+ showAlert('Error', 'An error occurred while standardizing pages.');
+ } finally {
+ if (loaderModal) loaderModal.classList.add('hidden');
+ }
}
document.addEventListener('DOMContentLoaded', function () {
- const fileInput = document.getElementById('file-input') as HTMLInputElement;
- const dropZone = document.getElementById('drop-zone');
- const processBtn = document.getElementById('process-btn');
- const backBtn = document.getElementById('back-to-tools');
- const targetSizeSelect = document.getElementById('target-size');
- const customSizeWrapper = document.getElementById('custom-size-wrapper');
+ const fileInput = document.getElementById('file-input') as HTMLInputElement;
+ const dropZone = document.getElementById('drop-zone');
+ const processBtn = document.getElementById('process-btn');
+ const backBtn = document.getElementById('back-to-tools');
+ const targetSizeSelect = document.getElementById('target-size');
+ const customSizeWrapper = document.getElementById('custom-size-wrapper');
- if (backBtn) {
- backBtn.addEventListener('click', function () {
- window.location.href = import.meta.env.BASE_URL;
+ if (backBtn) {
+ backBtn.addEventListener('click', function () {
+ window.location.href = import.meta.env.BASE_URL;
+ });
+ }
+
+ // Setup custom size toggle
+ if (targetSizeSelect && customSizeWrapper) {
+ targetSizeSelect.addEventListener('change', function () {
+ customSizeWrapper.classList.toggle(
+ 'hidden',
+ (targetSizeSelect as HTMLSelectElement).value !== 'Custom'
+ );
+ });
+ }
+
+ if (fileInput && dropZone) {
+ fileInput.addEventListener('change', function (e) {
+ handleFileSelect((e.target as HTMLInputElement).files);
+ });
+
+ dropZone.addEventListener('dragover', function (e) {
+ e.preventDefault();
+ dropZone.classList.add('bg-gray-700');
+ });
+
+ dropZone.addEventListener('dragleave', function (e) {
+ e.preventDefault();
+ dropZone.classList.remove('bg-gray-700');
+ });
+
+ dropZone.addEventListener('drop', function (e) {
+ e.preventDefault();
+ dropZone.classList.remove('bg-gray-700');
+ const files = e.dataTransfer?.files;
+ if (files && files.length > 0) {
+ const pdfFiles = Array.from(files).filter(function (f) {
+ return (
+ f.type === 'application/pdf' ||
+ f.name.toLowerCase().endsWith('.pdf')
+ );
});
- }
+ if (pdfFiles.length > 0) {
+ const dataTransfer = new DataTransfer();
+ dataTransfer.items.add(pdfFiles[0]);
+ handleFileSelect(dataTransfer.files);
+ }
+ }
+ });
- // Setup custom size toggle
- if (targetSizeSelect && customSizeWrapper) {
- targetSizeSelect.addEventListener('change', function () {
- customSizeWrapper.classList.toggle(
- 'hidden',
- (targetSizeSelect as HTMLSelectElement).value !== 'Custom'
- );
- });
- }
+ fileInput.addEventListener('click', function () {
+ fileInput.value = '';
+ });
+ }
- if (fileInput && dropZone) {
- fileInput.addEventListener('change', function (e) {
- handleFileSelect((e.target as HTMLInputElement).files);
- });
-
- dropZone.addEventListener('dragover', function (e) {
- e.preventDefault();
- dropZone.classList.add('bg-gray-700');
- });
-
- dropZone.addEventListener('dragleave', function (e) {
- e.preventDefault();
- dropZone.classList.remove('bg-gray-700');
- });
-
- dropZone.addEventListener('drop', function (e) {
- e.preventDefault();
- dropZone.classList.remove('bg-gray-700');
- const files = e.dataTransfer?.files;
- if (files && files.length > 0) {
- const pdfFiles = Array.from(files).filter(function (f) {
- return f.type === 'application/pdf' || f.name.toLowerCase().endsWith('.pdf');
- });
- if (pdfFiles.length > 0) {
- const dataTransfer = new DataTransfer();
- dataTransfer.items.add(pdfFiles[0]);
- handleFileSelect(dataTransfer.files);
- }
- }
- });
-
- fileInput.addEventListener('click', function () {
- fileInput.value = '';
- });
- }
-
- if (processBtn) {
- processBtn.addEventListener('click', fixPageSize);
- }
+ if (processBtn) {
+ processBtn.addEventListener('click', fixPageSize);
+ }
});
diff --git a/src/js/logic/pdf-workflow-page.ts b/src/js/logic/pdf-workflow-page.ts
index a6acaf4..c4ab6bb 100644
--- a/src/js/logic/pdf-workflow-page.ts
+++ b/src/js/logic/pdf-workflow-page.ts
@@ -1133,9 +1133,7 @@ function showNodeSettings(node: BaseWorkflowNode) {
{ label: 'Top Right', value: 'top-right' },
],
orientation: [
- { label: 'Vertical', value: 'vertical' },
- { label: 'Horizontal', value: 'horizontal' },
- { label: 'Auto', value: 'auto' },
+ { label: 'Auto (Keep Original)', value: 'auto' },
{ label: 'Portrait', value: 'portrait' },
{ label: 'Landscape', value: 'landscape' },
],
@@ -1160,6 +1158,23 @@ function showNodeSettings(node: BaseWorkflowNode) {
{ label: 'Letter', value: 'letter' },
{ label: 'Legal', value: 'legal' },
],
+ targetSize: [
+ { label: 'A4', value: 'A4' },
+ { label: 'Letter', value: 'Letter' },
+ { label: 'Legal', value: 'Legal' },
+ { label: 'A3', value: 'A3' },
+ { label: 'A5', value: 'A5' },
+ { label: 'Tabloid', value: 'Tabloid' },
+ { label: 'Custom', value: 'Custom' },
+ ],
+ scalingMode: [
+ { label: 'Fit (keep full page visible)', value: 'fit' },
+ { label: 'Fill (cover full target page)', value: 'fill' },
+ ],
+ customUnits: [
+ { label: 'Millimeters (mm)', value: 'mm' },
+ { label: 'Inches (in)', value: 'in' },
+ ],
numberFormat: [
{ label: 'Simple (1, 2, 3)', value: 'simple' },
{ label: 'Page X of Y', value: 'page_x_of_y' },
@@ -1294,6 +1309,9 @@ function showNodeSettings(node: BaseWorkflowNode) {
text: ['text'],
area: ['x0', 'y0', 'x1', 'y1'],
},
+ targetSize: {
+ Custom: ['customWidth', 'customHeight', 'customUnits'],
+ },
};
const controlWrappers: Record = {};
diff --git a/src/js/utils/pdf-operations.ts b/src/js/utils/pdf-operations.ts
index 67b5f06..5b9a72b 100644
--- a/src/js/utils/pdf-operations.ts
+++ b/src/js/utils/pdf-operations.ts
@@ -1,4 +1,4 @@
-import { PDFDocument, degrees, rgb, StandardFonts } from 'pdf-lib';
+import { PDFDocument, degrees, rgb, StandardFonts, PageSizes } from 'pdf-lib';
export async function mergePdfs(
pdfBytesList: Uint8Array[]
@@ -438,3 +438,87 @@ export async function addPageNumbers(
return new Uint8Array(await pdfDoc.save());
}
+
+export interface FixPageSizeOptions {
+ targetSize: string;
+ orientation: string;
+ scalingMode: string;
+ backgroundColor: { r: number; g: number; b: number };
+ customWidth?: number;
+ customHeight?: number;
+ customUnits?: string;
+}
+
+export async function fixPageSize(
+ pdfBytes: Uint8Array,
+ options: FixPageSizeOptions
+): Promise {
+ let targetWidth: number;
+ let targetHeight: number;
+
+ if (options.targetSize.toLowerCase() === 'custom') {
+ const w = options.customWidth ?? 210;
+ const h = options.customHeight ?? 297;
+ const units = (options.customUnits ?? 'mm').toLowerCase();
+ if (units === 'in') {
+ targetWidth = w * 72;
+ targetHeight = h * 72;
+ } else {
+ targetWidth = w * (72 / 25.4);
+ targetHeight = h * (72 / 25.4);
+ }
+ } else {
+ const selected =
+ PageSizes[options.targetSize as keyof typeof PageSizes] || PageSizes.A4;
+ targetWidth = selected[0];
+ targetHeight = selected[1];
+ }
+
+ const orientation = options.orientation.toLowerCase();
+ if (orientation === 'landscape' && targetWidth < targetHeight) {
+ [targetWidth, targetHeight] = [targetHeight, targetWidth];
+ } else if (orientation === 'portrait' && targetWidth > targetHeight) {
+ [targetWidth, targetHeight] = [targetHeight, targetWidth];
+ }
+
+ const sourceDoc = await PDFDocument.load(pdfBytes);
+ const outputDoc = await PDFDocument.create();
+
+ for (const sourcePage of sourceDoc.getPages()) {
+ const { width: sourceWidth, height: sourceHeight } = sourcePage.getSize();
+ const embeddedPage = await outputDoc.embedPage(sourcePage);
+
+ const outputPage = outputDoc.addPage([targetWidth, targetHeight]);
+ outputPage.drawRectangle({
+ x: 0,
+ y: 0,
+ width: targetWidth,
+ height: targetHeight,
+ color: rgb(
+ options.backgroundColor.r,
+ options.backgroundColor.g,
+ options.backgroundColor.b
+ ),
+ });
+
+ const scaleX = targetWidth / sourceWidth;
+ const scaleY = targetHeight / sourceHeight;
+ const useFill = options.scalingMode.toLowerCase() === 'fill';
+ const scale = useFill ? Math.max(scaleX, scaleY) : Math.min(scaleX, scaleY);
+
+ const scaledWidth = sourceWidth * scale;
+ const scaledHeight = sourceHeight * scale;
+
+ const x = (targetWidth - scaledWidth) / 2;
+ const y = (targetHeight - scaledHeight) / 2;
+
+ outputPage.drawPage(embeddedPage, {
+ x,
+ y,
+ width: scaledWidth,
+ height: scaledHeight,
+ });
+ }
+
+ return new Uint8Array(await outputDoc.save());
+}
diff --git a/src/js/workflow/nodes/fix-page-size-node.ts b/src/js/workflow/nodes/fix-page-size-node.ts
new file mode 100644
index 0000000..7ae8581
--- /dev/null
+++ b/src/js/workflow/nodes/fix-page-size-node.ts
@@ -0,0 +1,104 @@
+import { ClassicPreset } from 'rete';
+import { BaseWorkflowNode } from './base-node';
+import { pdfSocket } from '../sockets';
+import type { SocketData } from '../types';
+import { requirePdfInput, processBatch } from '../types';
+import { fixPageSize } from '../../utils/pdf-operations';
+import { PDFDocument } from 'pdf-lib';
+import { hexToRgb } from '../../utils/helpers.js';
+
+export class FixPageSizeNode extends BaseWorkflowNode {
+ readonly category = 'Organize & Manage' as const;
+ readonly icon = 'ph-frame-corners';
+ readonly description = 'Standardize all pages to a target size';
+
+ constructor() {
+ super('Fix Page Size');
+ this.addInput('pdf', new ClassicPreset.Input(pdfSocket, 'PDF'));
+ this.addOutput(
+ 'pdf',
+ new ClassicPreset.Output(pdfSocket, 'Standardized PDF')
+ );
+ this.addControl(
+ 'targetSize',
+ new ClassicPreset.InputControl('text', { initial: 'A4' })
+ );
+ this.addControl(
+ 'orientation',
+ new ClassicPreset.InputControl('text', { initial: 'auto' })
+ );
+ this.addControl(
+ 'scalingMode',
+ new ClassicPreset.InputControl('text', { initial: 'fit' })
+ );
+ this.addControl(
+ 'backgroundColor',
+ new ClassicPreset.InputControl('text', { initial: '#ffffff' })
+ );
+ this.addControl(
+ 'customWidth',
+ new ClassicPreset.InputControl('number', { initial: 210 })
+ );
+ this.addControl(
+ 'customHeight',
+ new ClassicPreset.InputControl('number', { initial: 297 })
+ );
+ this.addControl(
+ 'customUnits',
+ new ClassicPreset.InputControl('text', { initial: 'mm' })
+ );
+ }
+
+ async data(
+ inputs: Record
+ ): Promise> {
+ const pdfInputs = requirePdfInput(inputs, 'Fix Page Size');
+
+ const getText = (key: string, fallback: string) => {
+ const ctrl = this.controls[key] as
+ | ClassicPreset.InputControl<'text'>
+ | undefined;
+ return (ctrl?.value || fallback).trim();
+ };
+
+ const getNum = (key: string, fallback: number) => {
+ const ctrl = this.controls[key] as
+ | ClassicPreset.InputControl<'number'>
+ | undefined;
+ const value = ctrl?.value;
+ return Number.isFinite(value) ? (value as number) : fallback;
+ };
+
+ const targetSize = getText('targetSize', 'A4');
+ const orientation = getText('orientation', 'auto');
+ const scalingMode = getText('scalingMode', 'fit');
+ const backgroundHex = getText('backgroundColor', '#ffffff');
+ const customWidth = Math.max(1, getNum('customWidth', 210));
+ const customHeight = Math.max(1, getNum('customHeight', 297));
+ const customUnits = getText('customUnits', 'mm');
+ const backgroundColor = hexToRgb(backgroundHex);
+
+ return {
+ pdf: await processBatch(pdfInputs, async (input) => {
+ const resultBytes = await fixPageSize(input.bytes, {
+ targetSize,
+ orientation,
+ scalingMode,
+ backgroundColor,
+ customWidth,
+ customHeight,
+ customUnits,
+ });
+
+ const resultDoc = await PDFDocument.load(resultBytes);
+
+ return {
+ type: 'pdf',
+ document: resultDoc,
+ bytes: resultBytes,
+ filename: input.filename.replace(/\.pdf$/i, '_standardized.pdf'),
+ };
+ }),
+ };
+ }
+}
diff --git a/src/js/workflow/nodes/registry.ts b/src/js/workflow/nodes/registry.ts
index 5f944ec..2520b16 100644
--- a/src/js/workflow/nodes/registry.ts
+++ b/src/js/workflow/nodes/registry.ts
@@ -13,6 +13,7 @@ import { ReversePagesNode } from './reverse-pages-node';
import { AddBlankPageNode } from './add-blank-page-node';
import { DividePagesNode } from './divide-pages-node';
import { NUpNode } from './n-up-node';
+import { FixPageSizeNode } from './fix-page-size-node';
import { CombineSinglePageNode } from './combine-single-page-node';
import { CropNode } from './crop-node';
import { GreyscaleNode } from './greyscale-node';
@@ -298,6 +299,13 @@ export const nodeRegistry: Record = {
description: 'Arrange multiple pages per sheet',
factory: () => new NUpNode(),
},
+ FixPageSizeNode: {
+ label: 'Fix Page Size',
+ category: 'Organize & Manage',
+ icon: 'ph-frame-corners',
+ description: 'Standardize all pages to a target size',
+ factory: () => new FixPageSizeNode(),
+ },
CombineSinglePageNode: {
label: 'Combine to Single Page',
category: 'Organize & Manage',