ultisuite-client/lib/drive/docx-page-setup-export.ts
R3D347HR4Y 303b2b1074
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
wow
2026-06-11 01:22:40 +02:00

72 lines
2.5 KiB
TypeScript

import type { DocPageSetup } from "@/lib/drive/doc-page-setup"
type DocxArchive = Record<string, Uint8Array>
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 = `<w:pgSz w:w="${w}" w:h="${h}"${orient}/>`
const pgMar = `<w:pgMar w:top="${top}" w:right="${right}" w:bottom="${bottom}" w:left="${left}" w:header="${header}" w:footer="${footer}" w:gutter="0"/>`
if (/<w:sectPr\b[^>]*>[\s\S]*?<\/w:sectPr>/i.test(documentXml)) {
return documentXml.replace(/<w:sectPr\b[^>]*>[\s\S]*?<\/w:sectPr>/i, (match) => {
let inner = match
.replace(/<w:pgSz\b[^>]*\/?>/gi, "")
.replace(/<w:pgMar\b[^>]*\/?>/gi, "")
inner = inner.replace("</w:sectPr>", `${pgSz}${pgMar}</w:sectPr>`)
return inner
})
}
if (/<w:sectPr\b[^>]*\/>/i.test(documentXml)) {
return documentXml.replace(
/<w:sectPr\b[^>]*\/>/i,
`<w:sectPr>${pgSz}${pgMar}</w:sectPr>`
)
}
return documentXml.replace(/<\/w:body>/i, `<w:sectPr>${pgSz}${pgMar}</w:sectPr></w:body>`)
}
/** Patch page size and margins in a generated DOCX buffer. */
export async function patchDocxPageSetup(
buffer: ArrayBuffer | Uint8Array,
pageSetup: DocPageSetup
): Promise<Uint8Array> {
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)
}