Compact PDF table layout to match reference design (Task #359)
Changes to artifacts/api-server/src/lib/pdf-renderer.ts: - Reduced cellPadX from 6 to 4 (horizontal cell padding) - Reduced cellPadY from 3 to 2 (vertical cell padding) - Tightened lineHeight multiplier from 1.25 to 1.2 - Removed +2 padding buffer from heightOfString measurement loop - Removed +2 from drawWrappingLine return value - Tightened drawMixedLine return value (1.25→1.2) - Consolidated header spacing from two moveDown(0.2+0.4) to one moveDown(0.3) - Tightened title Y offset multiplier from 1.25 to 1.2 - Fixed row height probe to detect script per-line (matching draw path) instead of per-cell, preventing measurement/draw mismatch in mixed Arabic/Latin content now that the +2 cushion is removed Result: Table rows are compact with no visible gaps between them, matching the user's reference PDF (attached_assets/rrr1_1777879598338.pdf). Row colors, cell borders, and footer positioning are unchanged. Both AR and EN PDFs generate successfully (200 status).
This commit is contained in:
@@ -529,7 +529,7 @@ function drawMixedLine(
|
||||
});
|
||||
cursorX += r.width;
|
||||
}
|
||||
return opts.fontSize * 1.25;
|
||||
return opts.fontSize * 1.2;
|
||||
}
|
||||
|
||||
function drawWrappingLine(
|
||||
@@ -561,7 +561,7 @@ function drawWrappingLine(
|
||||
lineGap: -1,
|
||||
...(features ? { features } : {}),
|
||||
});
|
||||
return Math.max(opts.fontSize * 1.25, doc.y - before + 2);
|
||||
return Math.max(opts.fontSize * 1.2, doc.y - before);
|
||||
}
|
||||
|
||||
export async function renderSchedulePdf(input: RenderPdfInput): Promise<Buffer> {
|
||||
@@ -639,9 +639,8 @@ export async function renderSchedulePdf(input: RenderPdfInput): Promise<Buffer>
|
||||
baseDirection: baseDir,
|
||||
mapping,
|
||||
});
|
||||
doc.y = headerTopY + Math.max(titleSize * 1.25, input.logo ? logoBoxSize : 0);
|
||||
doc.moveDown(0.2);
|
||||
doc.moveDown(0.4);
|
||||
doc.y = headerTopY + Math.max(titleSize * 1.2, input.logo ? logoBoxSize : 0);
|
||||
doc.moveDown(0.3);
|
||||
|
||||
if (input.meetings.length === 0) {
|
||||
drawMixedLine(doc, input.labels.none, {
|
||||
@@ -728,11 +727,9 @@ export async function renderSchedulePdf(input: RenderPdfInput): Promise<Buffer>
|
||||
: [input.labels.no, input.labels.meeting, input.labels.attendees, input.labels.time];
|
||||
const headerWidths = isRtl ? [...colWidths].reverse() : colWidths;
|
||||
|
||||
const cellPadX = 6;
|
||||
// Tightened from 5→3 to keep the table compact (we aim to fit a typical
|
||||
// day on one page, mirroring the user's reference layout).
|
||||
const cellPadY = 3;
|
||||
const lineHeight = input.font.fontSize * 1.25;
|
||||
const cellPadX = 4;
|
||||
const cellPadY = 2;
|
||||
const lineHeight = input.font.fontSize * 1.2;
|
||||
|
||||
function drawHeader(): number {
|
||||
const headerHeight = lineHeight + cellPadY * 2;
|
||||
@@ -840,32 +837,25 @@ export async function renderSchedulePdf(input: RenderPdfInput): Promise<Buffer>
|
||||
// sooner than a simple division would, causing rows to overlap.
|
||||
const lines = c.text.split("\n");
|
||||
let h = 0;
|
||||
const arabicChars = [...c.text].filter(isArabicChar).length;
|
||||
const dominantArabic = arabicChars * 2 > c.text.length;
|
||||
const fk = fontKeyFor(dominantArabic, input.font.fontWeight, mapping);
|
||||
const features = featuresFor(dominantArabic);
|
||||
doc.font(fk).fontSize(input.font.fontSize);
|
||||
const measureOpts: PDFKit.Mixins.TextOptions = {
|
||||
width: widths[i] - cellPadX * 2,
|
||||
align: c.align,
|
||||
lineBreak: true,
|
||||
// Mirror drawWrappingLine's lineGap so the probe matches the draw.
|
||||
lineGap: -1,
|
||||
...(features ? { features } : {}),
|
||||
};
|
||||
const cellW = widths[i] - cellPadX * 2;
|
||||
for (const line of lines) {
|
||||
if (!line) {
|
||||
h += lineHeight;
|
||||
continue;
|
||||
}
|
||||
// heightOfString returns pdfkit's exact rendered height for the
|
||||
// wrapped paragraph (including its internal line gaps). Use it
|
||||
// directly — rounding up to whole lineHeight units inflates rows
|
||||
// by half a line and leaves visible empty space below cells.
|
||||
// The +2 mirrors drawWrappingLine's per-line padding fallback so
|
||||
// multi-line cells never get under-reserved.
|
||||
const measured = doc.heightOfString(line, measureOpts);
|
||||
h += Math.max(lineHeight, measured) + 2;
|
||||
const lineArabicChars = [...line].filter(isArabicChar).length;
|
||||
const lineDominantArabic = lineArabicChars * 2 > line.length;
|
||||
const lineFk = fontKeyFor(lineDominantArabic, input.font.fontWeight, mapping);
|
||||
const lineFeatures = featuresFor(lineDominantArabic);
|
||||
doc.font(lineFk).fontSize(input.font.fontSize);
|
||||
const measured = doc.heightOfString(line, {
|
||||
width: cellW,
|
||||
align: c.align,
|
||||
lineBreak: true,
|
||||
lineGap: -1,
|
||||
...(lineFeatures ? { features: lineFeatures } : {}),
|
||||
});
|
||||
h += Math.max(lineHeight, measured);
|
||||
}
|
||||
const finalH = h + cellPadY * 2;
|
||||
cellHeights.push(finalH);
|
||||
|
||||
Reference in New Issue
Block a user