From 534c6c7c0ed2972cb3a6cd140b087b6b39821ebe Mon Sep 17 00:00:00 2001 From: adilallo <39313955+adilallo@users.noreply.github.com> Date: Fri, 8 May 2026 21:51:55 -0600 Subject: [PATCH] Create flow cleanup leftover --- lib/create/ruleExport.ts | 48 +++++++++++++++++++++++-------- tests/unit/lib/ruleExport.test.ts | 32 +++++++++++++++++++++ 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/lib/create/ruleExport.ts b/lib/create/ruleExport.ts index 359dbb8..f766676 100644 --- a/lib/create/ruleExport.ts +++ b/lib/create/ruleExport.ts @@ -112,10 +112,12 @@ export function sectionsToCsv( const { img, file, bodyTrim } = labeledBlockMedia(b); const content = img ? bodyTrim.length > 0 - ? `${b.body}\n${img}` + ? `${bodyTrim}\n${img}` : img : file - ? `${b.body}\n${file}` + ? bodyTrim.length > 0 + ? `${bodyTrim}\n${file}` + : file : b.body; rows.push([sec.categoryName, ent.title, b.label, content]); } @@ -235,6 +237,29 @@ export function sectionsToPdfBlob( } } + function writeBlockBodyParagraphs(body: string): void { + for (const p of splitDisplayParagraphs(body)) { + doc.setFont("helvetica", "normal"); + doc.setFontSize(11); + const lines = doc.splitTextToSize(p, maxW); + const dim = doc.getTextDimensions(lines.join("\n"), { maxWidth: maxW }); + ensureSpace(dim.h + 1); + doc.text(lines, margin, y); + y += dim.h + 2; + } + } + + /** Plain URL line(s); italic for images so captions vs URL read distinctly in print. */ + function writeMediaUrlLines(url: string, fontStyle: "normal" | "italic"): void { + doc.setFont("helvetica", fontStyle); + doc.setFontSize(10); + const urlLines = doc.splitTextToSize(url, maxW); + const urlDim = doc.getTextDimensions(urlLines.join("\n"), { maxWidth: maxW }); + ensureSpace(urlDim.h + 1); + doc.text(urlLines, margin, y); + y += urlDim.h + 2; + } + doc.setFont("helvetica", "bold"); doc.setFontSize(16); { @@ -294,16 +319,15 @@ export function sectionsToPdfBlob( doc.text(lines, margin, y); y += dim.h + 2; } - for (const p of splitDisplayParagraphs(b.body)) { - doc.setFont("helvetica", "normal"); - doc.setFontSize(11); - const lines = doc.splitTextToSize(p, maxW); - const dim = doc.getTextDimensions(lines.join("\n"), { - maxWidth: maxW, - }); - ensureSpace(dim.h + 1); - doc.text(lines, margin, y); - y += dim.h + 2; + const { img, file } = labeledBlockMedia(b); + if (img) { + writeBlockBodyParagraphs(b.body); + writeMediaUrlLines(img, "italic"); + } else if (file) { + writeBlockBodyParagraphs(b.body); + writeMediaUrlLines(file, "normal"); + } else { + writeBlockBodyParagraphs(b.body); } } } else { diff --git a/tests/unit/lib/ruleExport.test.ts b/tests/unit/lib/ruleExport.test.ts index 77f6315..1c412d6 100644 --- a/tests/unit/lib/ruleExport.test.ts +++ b/tests/unit/lib/ruleExport.test.ts @@ -6,6 +6,7 @@ import { exportFilenameBase, sectionsToCsv, sectionsToMarkdown, + sectionsToPdfBlob, } from "../../../lib/create/ruleExport"; import type { CommunityRuleSection } from "../../../app/components/type/CommunityRule/CommunityRule.types"; @@ -134,6 +135,37 @@ describe("ruleExport", () => { expect(html).toContain("Download"); }); + it("sectionsToPdfBlob embeds image and file URLs as text for labeled blocks", async () => { + const sections: CommunityRuleSection[] = [ + { + categoryName: "Values", + entries: [ + { + title: "Entry", + body: "", + blocks: [ + { + label: "Photo", + body: "Caption", + imageUrl: "https://cdn.example.com/pic.jpg", + }, + { + label: "Handbook", + body: "Download", + fileUrl: "https://cdn.example.com/guidance.pdf", + }, + ], + }, + ], + }, + ]; + const blob = sectionsToPdfBlob("Rule", null, sections); + const buf = new Uint8Array(await readBlobAsArrayBuffer(blob)); + const raw = new TextDecoder("latin1").decode(buf); + expect(raw).toContain("https://cdn.example.com/pic.jpg"); + expect(raw).toContain("https://cdn.example.com/guidance.pdf"); + }); + it("buildPrintableRuleHtmlDocument escapes HTML in user content", () => { const sections: CommunityRuleSection[] = [ {