import type { DocPageSetup } from "@/lib/drive/doc-page-setup" type DocxArchive = Record const TWIPS_PER_MM = 1440 / 25.4 function mmToTwips(mm: number): number { return Math.round(mm * TWIPS_PER_MM) } function decodeXml(bytes: Uint8Array | undefined): string { if (!bytes) return "" return new TextDecoder().decode(bytes) } function encodeXml(text: string): Uint8Array { return new TextEncoder().encode(text) } function patchSectPr(documentXml: string, pageSetup: DocPageSetup): string { const w = mmToTwips(pageSetup.widthMm) const h = mmToTwips(pageSetup.heightMm) const orient = pageSetup.orientation === "landscape" || pageSetup.widthMm > pageSetup.heightMm ? ' w:orient="landscape"' : "" const top = mmToTwips(pageSetup.marginsMm.top) const right = mmToTwips(pageSetup.marginsMm.right) const bottom = mmToTwips(pageSetup.marginsMm.bottom) const left = mmToTwips(pageSetup.marginsMm.left) const header = mmToTwips(pageSetup.headerMarginMm ?? pageSetup.marginsMm.top) const footer = mmToTwips(pageSetup.footerMarginMm ?? pageSetup.marginsMm.bottom) const pgSz = `` const pgMar = `` if (/]*>[\s\S]*?<\/w:sectPr>/i.test(documentXml)) { return documentXml.replace(/]*>[\s\S]*?<\/w:sectPr>/i, (match) => { let inner = match .replace(/]*\/?>/gi, "") .replace(/]*\/?>/gi, "") inner = inner.replace("", `${pgSz}${pgMar}`) return inner }) } if (/]*\/>/i.test(documentXml)) { return documentXml.replace( /]*\/>/i, `${pgSz}${pgMar}` ) } return documentXml.replace(/<\/w:body>/i, `${pgSz}${pgMar}`) } /** Patch page size and margins in a generated DOCX buffer. */ export async function patchDocxPageSetup( buffer: ArrayBuffer | Uint8Array, pageSetup: DocPageSetup ): Promise { const { unzipSync, zipSync } = await import("fflate") const archive = { ...(unzipSync(new Uint8Array(buffer)) as DocxArchive) } const documentPath = "word/document.xml" const xml = decodeXml(archive[documentPath]) if (!xml) return new Uint8Array(buffer) archive[documentPath] = encodeXml(patchSectPr(xml, pageSetup)) return zipSync(archive) }