diff --git a/src/lib/docx-pleading.ts b/src/lib/docx-pleading.ts index e9030aa..e994a14 100644 --- a/src/lib/docx-pleading.ts +++ b/src/lib/docx-pleading.ts @@ -13,6 +13,7 @@ import { BorderStyle, Footer, LevelFormat, + PageNumber, } from "docx"; import pkg from "file-saver"; const { saveAs } = pkg; @@ -33,6 +34,9 @@ export interface PleadingInput { bodyHtml?: string; // rich HTML body body?: string; // legacy plain text fallback footnotes?: PleadingFootnote[]; + footerLeft?: string; + footerCenter?: string; + footerRight?: string; // user text prepended to "Page X of Y" } const FONT = "Bookman Old Style"; @@ -179,6 +183,54 @@ function htmlToParagraphs(html: string): Paragraph[] { return out; } +function footerCell(width: number, align: (typeof AlignmentType)[keyof typeof AlignmentType], children: TextRun[]) { + return new TableCell({ + width: { size: width, type: WidthType.DXA }, + borders: noBorder, + margins: { top: 40, bottom: 40, left: 60, right: 60 }, + children: [ + new Paragraph({ + alignment: align, + children: children.length ? children : [run("")], + }), + ], + }); +} + +function buildFooterTable(left?: string, center?: string, right?: string): Table { + const total = 9360; + const col = Math.floor(total / 3); + const cols = [col, col, total - 2 * col]; + + const leftRuns: TextRun[] = left ? [run(left, { size: 20 })] : []; + const centerRuns: TextRun[] = center ? [run(center, { size: 20 })] : []; + + // Right column: optional user text + always "Page X of Y" + const rightRuns: TextRun[] = []; + if (right && right.trim()) { + rightRuns.push(run(`${right} `, { size: 20 })); + } + rightRuns.push(run("Page ", { size: 20 })); + rightRuns.push(new TextRun({ children: [PageNumber.CURRENT], font: FONT, size: 20 })); + rightRuns.push(run(" of ", { size: 20 })); + rightRuns.push(new TextRun({ children: [PageNumber.TOTAL_PAGES], font: FONT, size: 20 })); + + return new Table({ + width: { size: total, type: WidthType.DXA }, + columnWidths: cols, + borders: noBorder, + rows: [ + new TableRow({ + children: [ + footerCell(cols[0], AlignmentType.LEFT, leftRuns), + footerCell(cols[1], AlignmentType.CENTER, centerRuns), + footerCell(cols[2], AlignmentType.RIGHT, rightRuns), + ], + }), + ], + }); +} + export function buildPleadingDoc(input: PleadingInput): Document { const headerLine1 = `IN THE ${input.courtType} COURT OF THE ${input.circuit} JUDICIAL CIRCUIT,`; const headerLine2 = `IN AND FOR ${input.county.toUpperCase()} COUNTY, FLORIDA`; @@ -297,7 +349,7 @@ export function buildPleadingDoc(input: PleadingInput): Document { }, footers: { default: new Footer({ - children: [new Paragraph({ alignment: AlignmentType.CENTER, children: [run("")] })], + children: [buildFooterTable(input.footerLeft, input.footerCenter, input.footerRight)], }), }, children: [ diff --git a/src/routes/documents.pleading.new.tsx b/src/routes/documents.pleading.new.tsx index 8daf654..fd7dc8e 100644 --- a/src/routes/documents.pleading.new.tsx +++ b/src/routes/documents.pleading.new.tsx @@ -35,6 +35,9 @@ function PleadingNewPage() { const [title, setTitle] = useState(""); const [bodyHtml, setBodyHtml] = useState("
"); const [footnotes, setFootnotes] = useState
+ Three columns. The right column always ends with Page X of Y.
+