diff --git a/artifacts/api-server/src/lib/pdf-renderer.ts b/artifacts/api-server/src/lib/pdf-renderer.ts index eb5e2c9c..bbad9735 100644 --- a/artifacts/api-server/src/lib/pdf-renderer.ts +++ b/artifacts/api-server/src/lib/pdf-renderer.ts @@ -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 { @@ -639,9 +639,8 @@ export async function renderSchedulePdf(input: RenderPdfInput): Promise 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 : [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 // 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); diff --git a/artifacts/tx-os/public/opengraph.jpg b/artifacts/tx-os/public/opengraph.jpg index f45afc6f..72a70c3c 100644 Binary files a/artifacts/tx-os/public/opengraph.jpg and b/artifacts/tx-os/public/opengraph.jpg differ