import { fromBuffer } from "pdf2pic"; import sharp from "sharp"; export interface PageImage { page: number; buffer: Buffer; width: number; height: number; } export async function pdfToImages(pdfBuffer: Buffer): Promise { const converter = fromBuffer(pdfBuffer, { density: 200, format: "jpeg", width: 2200, height: 2200, quality: 90, preserveAspectRatio: true, }); const pageCount = await getPdfPageCount(pdfBuffer); const images: PageImage[] = []; for (let i = 1; i <= pageCount; i++) { const result = await converter(i, { responseType: "buffer" }); if (result.buffer) { let buf = result.buffer as Buffer; let meta = await sharp(buf).metadata(); const w = meta.width || 0; const h = meta.height || 0; // Duplex scanners flip the sheet between sides, so odd pages // (back/response card) are 180° rotated relative to even pages // (front/survey). Landscape pages need rotation to portrait; // the direction depends on which side of the sheet was scanned. const isBackSide = i % 2 === 1; if (w > h) { const angle = isBackSide ? -90 : 90; buf = await sharp(buf).rotate(angle).jpeg({ quality: 90 }).toBuffer(); meta = await sharp(buf).metadata(); } else if (isBackSide) { buf = await sharp(buf).rotate(180).jpeg({ quality: 90 }).toBuffer(); meta = await sharp(buf).metadata(); } images.push({ page: i, buffer: buf, width: meta.width || 1600, height: meta.height || 2200, }); } } return images; } async function getPdfPageCount(pdfBuffer: Buffer): Promise { const text = pdfBuffer.toString("latin1"); const matches = text.match(/\/Type\s*\/Page(?!s)/g); return matches ? matches.length : 2; } export async function imageToBase64(buffer: Buffer): Promise { const processed = await sharp(buffer) .resize(1200, undefined, { withoutEnlargement: true }) .jpeg({ quality: 85 }) .toBuffer(); return processed.toString("base64"); } export async function processUploadedImage(buffer: Buffer): Promise { return sharp(buffer) .resize(1600, undefined, { withoutEnlargement: true }) .normalize() .sharpen() .jpeg({ quality: 90 }) .toBuffer(); }