Fixed caption border & parser
X-Lovable-Edit-ID: edt-18208ab2-8406-425f-9ccf-bf3f1a1df2e7 Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
@@ -85,7 +85,7 @@ const captionLeftBorders = {
|
||||
|
||||
const captionRightBorders = {
|
||||
top: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
bottom: { style: BorderStyle.SINGLE, size: 8, color: "000000" },
|
||||
bottom: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
left: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
right: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
};
|
||||
|
||||
+58
-38
@@ -62,38 +62,53 @@ function htmlToBlocks(html: string): Block[] {
|
||||
|
||||
const walkList = (listEl: Element, ordered: boolean, level: number) => {
|
||||
let idx = 1;
|
||||
listEl.querySelectorAll(":scope > li").forEach((li) => {
|
||||
const tokens: Token[] = [];
|
||||
li.childNodes.forEach((child) => {
|
||||
if (child.nodeType === Node.ELEMENT_NODE && /^(ul|ol)$/i.test((child as Element).tagName)) return;
|
||||
tokenize(child, {}, tokens);
|
||||
Array.from(listEl.children)
|
||||
.filter((child): child is Element => child.tagName.toLowerCase() === "li")
|
||||
.forEach((li) => {
|
||||
const tokens: Token[] = [];
|
||||
li.childNodes.forEach((child) => {
|
||||
if (child.nodeType === Node.ELEMENT_NODE && /^(ul|ol)$/i.test((child as Element).tagName)) return;
|
||||
tokenize(child, {}, tokens);
|
||||
});
|
||||
out.push({ kind: "list-item", ordered, level, index: idx++, tokens });
|
||||
Array.from(li.children)
|
||||
.filter((child): child is Element => /^(ul|ol)$/i.test(child.tagName))
|
||||
.forEach((nested) => walkList(nested, nested.tagName.toLowerCase() === "ol", level + 1));
|
||||
});
|
||||
out.push({ kind: "list-item", ordered, level, index: idx++, tokens });
|
||||
li.querySelectorAll(":scope > ul").forEach((n) => walkList(n as Element, false, level + 1));
|
||||
li.querySelectorAll(":scope > ol").forEach((n) => walkList(n as Element, true, level + 1));
|
||||
});
|
||||
};
|
||||
|
||||
Array.from(root.children).forEach((el) => {
|
||||
const tag = el.tagName.toLowerCase();
|
||||
if (tag === "ul") return walkList(el, false, 0);
|
||||
if (tag === "ol") return walkList(el, true, 0);
|
||||
const walk = (node: Element) => {
|
||||
const tag = node.tagName.toLowerCase();
|
||||
if (tag === "ul") return walkList(node, false, 0);
|
||||
if (tag === "ol") return walkList(node, true, 0);
|
||||
if (tag === "blockquote") {
|
||||
const tokens: Token[] = [];
|
||||
el.childNodes.forEach((c) => tokenize(c, {}, tokens));
|
||||
out.push({ kind: "para", align: alignOf(el), tokens, indent: 36 });
|
||||
node.childNodes.forEach((c) => tokenize(c, {}, tokens));
|
||||
out.push({ kind: "para", align: alignOf(node), tokens, indent: 36 });
|
||||
return;
|
||||
}
|
||||
if (tag === "h1" || tag === "h2" || tag === "h3") {
|
||||
const tokens: Token[] = [];
|
||||
el.childNodes.forEach((c) => tokenize(c, { bold: true }, tokens));
|
||||
out.push({ kind: "para", align: alignOf(el), tokens });
|
||||
node.childNodes.forEach((c) => tokenize(c, { bold: true }, tokens));
|
||||
out.push({ kind: "para", align: alignOf(node), tokens });
|
||||
return;
|
||||
}
|
||||
if (tag === "p") {
|
||||
const tokens: Token[] = [];
|
||||
node.childNodes.forEach((c) => tokenize(c, {}, tokens));
|
||||
out.push({ kind: "para", align: alignOf(node), tokens });
|
||||
return;
|
||||
}
|
||||
if (tag === "div" || tag === "section" || tag === "article") {
|
||||
Array.from(node.children).forEach((child) => walk(child));
|
||||
return;
|
||||
}
|
||||
const tokens: Token[] = [];
|
||||
el.childNodes.forEach((c) => tokenize(c, {}, tokens));
|
||||
out.push({ kind: "para", align: alignOf(el), tokens });
|
||||
});
|
||||
node.childNodes.forEach((c) => tokenize(c, {}, tokens));
|
||||
if (tokens.length > 0) out.push({ kind: "para", align: alignOf(node), tokens });
|
||||
};
|
||||
|
||||
Array.from(root.children).forEach((el) => walk(el));
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -106,16 +121,24 @@ function layoutTokens(pdf: jsPDF, tokens: Token[], maxWidth: number): Piece[][]
|
||||
};
|
||||
const lineWidth = (line: Piece[]) => line.reduce((a, p) => a + p.w, 0);
|
||||
|
||||
const pushWord = (word: string, style: Style) => {
|
||||
if (!word) return;
|
||||
const w = measure(word, style);
|
||||
const pushPiece = (text: string, style: Style) => {
|
||||
if (!text) return;
|
||||
const cur = lines[lines.length - 1];
|
||||
if (lineWidth(cur) + w > maxWidth && cur.length > 0) {
|
||||
while (cur.length && /\s+$/.test(cur[cur.length - 1].text)) cur.pop();
|
||||
lines.push([{ text: word, style, w }]);
|
||||
} else {
|
||||
cur.push({ text: word, style, w });
|
||||
const isWhitespace = /^\s+$/.test(text);
|
||||
if (isWhitespace) {
|
||||
if (cur.length === 0) return;
|
||||
cur.push({ text, style, w: measure(text, style) });
|
||||
return;
|
||||
}
|
||||
|
||||
const w = measure(text, style);
|
||||
if (lineWidth(cur) + w > maxWidth && cur.length > 0) {
|
||||
while (cur.length && /^\s+$/.test(cur[cur.length - 1].text)) cur.pop();
|
||||
lines.push([{ text, style, w }]);
|
||||
return;
|
||||
}
|
||||
|
||||
cur.push({ text, style, w });
|
||||
};
|
||||
|
||||
for (const tok of tokens) {
|
||||
@@ -123,8 +146,8 @@ function layoutTokens(pdf: jsPDF, tokens: Token[], maxWidth: number): Piece[][]
|
||||
for (const part of parts) {
|
||||
if (part === "\n") { lines.push([]); continue; }
|
||||
if (!part) continue;
|
||||
const words = part.match(/\S+\s*/g) || [part];
|
||||
for (const w of words) pushWord(w, tok.style);
|
||||
const chunks = part.match(/\s+|[^\s]+/g) || [part];
|
||||
for (const chunk of chunks) pushPiece(chunk, tok.style);
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
@@ -140,7 +163,7 @@ function drawLine(pdf: jsPDF, line: Piece[], x: number, y: number, align: "left"
|
||||
if (align === "center") startX = x + (maxWidth - totalW) / 2;
|
||||
else if (align === "right") startX = x + (maxWidth - totalW);
|
||||
else if (align === "justify" && !isLast) {
|
||||
const spaces = trimmed.filter((p) => /\s$/.test(p.text)).length;
|
||||
const spaces = trimmed.filter((p) => /^\s+$/.test(p.text)).length;
|
||||
if (spaces > 0) extraSpace = (maxWidth - totalW) / spaces;
|
||||
}
|
||||
|
||||
@@ -149,13 +172,13 @@ function drawLine(pdf: jsPDF, line: Piece[], x: number, y: number, align: "left"
|
||||
const sz = p.style.superscript ? FONT_SIZE * 0.75 : FONT_SIZE;
|
||||
setFont(pdf, p.style, sz);
|
||||
const drawY = p.style.superscript ? y - 4 : y;
|
||||
pdf.text(p.text, cx, drawY);
|
||||
if (!/^\s+$/.test(p.text)) pdf.text(p.text, cx, drawY);
|
||||
if (p.style.underline) {
|
||||
const tw = p.w;
|
||||
pdf.setLineWidth(0.5);
|
||||
pdf.line(cx, y + 1.5, cx + tw, y + 1.5);
|
||||
}
|
||||
cx += p.w + (/\s$/.test(p.text) ? extraSpace : 0);
|
||||
cx += p.w + (/^\s+$/.test(p.text) ? extraSpace : 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +220,6 @@ export async function downloadPleadingPdf(input: PleadingInput, filename: string
|
||||
const leftColW = CONTENT_W * 0.58;
|
||||
const leftX = MARGIN;
|
||||
const rightX = MARGIN + leftColW + 24;
|
||||
const captionRightEdge = MARGIN + CONTENT_W;
|
||||
const labelIndent = 24; // ~1/3" indent for "Plaintiff(s)," / "Defendant(s)."
|
||||
|
||||
const plaintiffLines = (input.plaintiffs || "").split("\n").filter((l) => l.trim());
|
||||
@@ -230,10 +252,8 @@ export async function downloadPleadingPdf(input: PleadingInput, filename: string
|
||||
|
||||
const captionEndY = ly;
|
||||
pdf.setLineWidth(0.6);
|
||||
// Vertical divider between caption columns
|
||||
pdf.line(MARGIN + leftColW + 12, captionStartY - LINE_H + 4, MARGIN + leftColW + 12, captionEndY);
|
||||
// Bottom border spanning BOTH caption cells (full content width)
|
||||
pdf.line(leftX, captionEndY + 2, captionRightEdge, captionEndY + 2);
|
||||
// Bottom border only under the left caption cell
|
||||
pdf.line(leftX, captionEndY + 2, MARGIN + leftColW + 12, captionEndY + 2);
|
||||
|
||||
y = captionEndY + LINE_H;
|
||||
|
||||
|
||||
@@ -304,8 +304,8 @@ function PleadingNewPage() {
|
||||
<div>{headerLine2}</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 border-b border-black flex">
|
||||
<div className="flex-1 pr-4 border-r border-black font-bold pb-2">
|
||||
<div className="mt-6 flex">
|
||||
<div className="flex-1 pr-4 border-b border-black font-bold pb-2">
|
||||
<div className="whitespace-pre-line min-h-[1.5em]">{plaintiffs || " "}</div>
|
||||
<div className="mt-3 pl-8">Plaintiff(s),</div>
|
||||
<div className="mt-3">v.</div>
|
||||
|
||||
Reference in New Issue
Block a user