@speajus/markdown-to-pdf 1.0.19 → 1.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.pdf CHANGED
Binary file
package/dist/renderer.js CHANGED
@@ -411,6 +411,21 @@ async function renderMarkdownToPdf(markdown, options) {
411
411
  doc.moveDown(0.5);
412
412
  break;
413
413
  }
414
+ case 'html': {
415
+ const htmlText = (tok.text ?? tok.raw ?? '').trim();
416
+ if (/^<br\s*\/?>$/i.test(htmlText)) {
417
+ doc.moveDown(0.5);
418
+ }
419
+ else {
420
+ // Non-br HTML: render as text (fall through to default behavior)
421
+ const raw = tok.text ?? tok.raw ?? '';
422
+ if (raw) {
423
+ applyBodyFont(insideBold, insideItalic);
424
+ renderText(raw, { continued: cont, underline: false, strike: false });
425
+ }
426
+ }
427
+ break;
428
+ }
414
429
  default: {
415
430
  const raw = tok.text ?? tok.raw ?? '';
416
431
  if (raw) {
@@ -449,6 +464,7 @@ async function renderMarkdownToPdf(markdown, options) {
449
464
  if (linkUrl) {
450
465
  doc.link(imgX, imgY, displayWidth, displayHeight, linkUrl);
451
466
  }
467
+ doc.y = imgY + displayHeight;
452
468
  doc.moveDown(0.5);
453
469
  }
454
470
  catch {
@@ -504,39 +520,101 @@ async function renderMarkdownToPdf(markdown, options) {
504
520
  }
505
521
  doc.y = savedY;
506
522
  }
523
+ /**
524
+ * Extract plain text from cell tokens, converting <br> HTML tokens to \n
525
+ * so that heightOfString can measure the full multiline content.
526
+ */
527
+ function cellPlainText(cell) {
528
+ if (!cell.tokens || cell.tokens.length === 0)
529
+ return cell.text;
530
+ function extract(tokens) {
531
+ let result = '';
532
+ for (const tok of tokens) {
533
+ if (tok.type === 'br') {
534
+ result += '\n';
535
+ }
536
+ else if (tok.type === 'html') {
537
+ const raw = (tok.text ?? tok.raw ?? '').trim();
538
+ if (/^<br\s*\/?>$/i.test(raw)) {
539
+ result += '\n';
540
+ }
541
+ else {
542
+ result += tok.text ?? tok.raw ?? '';
543
+ }
544
+ }
545
+ else if (tok.tokens && tok.tokens.length > 0) {
546
+ result += extract(tok.tokens);
547
+ }
548
+ else {
549
+ result += tok.text ?? tok.raw ?? '';
550
+ }
551
+ }
552
+ return result;
553
+ }
554
+ return extract(cell.tokens);
555
+ }
556
+ /**
557
+ * Measure the height a cell needs given its content and available width.
558
+ */
559
+ function measureCellHeight(cell, cellWidth, bold) {
560
+ const text = cellPlainText(cell);
561
+ const font = bold
562
+ ? safeFont(resolveFont(theme.body.font, true, false))
563
+ : safeFont(theme.body.font);
564
+ doc.font(font).fontSize(theme.body.fontSize);
565
+ return doc.heightOfString(text, { width: cellWidth });
566
+ }
507
567
  async function renderTable(table) {
508
568
  const colCount = table.header.length;
509
569
  if (colCount === 0)
510
570
  return;
511
571
  const cellPad = theme.table.cellPadding;
512
572
  const colWidth = contentWidth / colCount;
513
- const rowH = theme.body.fontSize + cellPad * 2 + 4;
514
- const textInsetY = (rowH - theme.body.fontSize) / 2;
515
- ensureSpace(rowH * 2);
573
+ const minRowH = theme.body.fontSize + cellPad * 2 + 4;
574
+ const textWidth = colWidth - cellPad * 2;
575
+ ensureSpace(minRowH * 2);
516
576
  const startX = margins.left;
517
577
  let y = doc.y;
518
- // Header row
578
+ // ── Measure header row height ──
579
+ let headerH = minRowH;
580
+ let maxHeaderTextHeight = 0;
581
+ for (let c = 0; c < colCount; c++) {
582
+ const h = measureCellHeight(table.header[c], textWidth, true);
583
+ maxHeaderTextHeight = Math.max(maxHeaderTextHeight, h);
584
+ headerH = Math.max(headerH, h + cellPad * 2 + 4);
585
+ }
586
+ const headerTextInsetY = (headerH - maxHeaderTextHeight) / 2;
587
+ // Header row background
519
588
  doc.save();
520
- doc.rect(startX, y, contentWidth, rowH).fill(theme.table.headerBackground);
589
+ doc.rect(startX, y, contentWidth, headerH).fill(theme.table.headerBackground);
521
590
  doc.restore();
522
591
  for (let c = 0; c < colCount; c++) {
523
592
  const cellX = startX + c * colWidth;
524
- await renderCellTokens(table.header[c], cellX + cellPad, y + textInsetY, colWidth - cellPad * 2, table.align[c] || 'left', true);
593
+ await renderCellTokens(table.header[c], cellX + cellPad, y + headerTextInsetY, textWidth, table.align[c] || 'left', true);
525
594
  }
526
595
  // Header border
527
596
  doc.save();
528
597
  doc.strokeColor(theme.table.borderColor).lineWidth(0.5);
529
- doc.rect(startX, y, contentWidth, rowH).stroke();
598
+ doc.rect(startX, y, contentWidth, headerH).stroke();
530
599
  for (let c = 1; c < colCount; c++) {
531
600
  const cx = startX + c * colWidth;
532
- doc.moveTo(cx, y).lineTo(cx, y + rowH).stroke();
601
+ doc.moveTo(cx, y).lineTo(cx, y + headerH).stroke();
533
602
  }
534
603
  doc.restore();
535
- y += rowH;
604
+ y += headerH;
536
605
  // Body rows
537
606
  const zebraColor = theme.table.zebraColor ?? '#f9f9f9';
538
607
  for (let r = 0; r < table.rows.length; r++) {
539
608
  const row = table.rows[r];
609
+ // ── Measure row height ──
610
+ let rowH = minRowH;
611
+ let maxRowTextHeight = 0;
612
+ for (let c = 0; c < colCount; c++) {
613
+ const h = measureCellHeight(row[c], textWidth, false);
614
+ maxRowTextHeight = Math.max(maxRowTextHeight, h);
615
+ rowH = Math.max(rowH, h + cellPad * 2 + 4);
616
+ }
617
+ const textInsetY = (rowH - maxRowTextHeight) / 2;
540
618
  doc.y = y; // sync doc.y BEFORE ensureSpace check
541
619
  ensureSpace(rowH);
542
620
  y = doc.y; // re-sync AFTER possible page break
@@ -548,7 +626,7 @@ async function renderMarkdownToPdf(markdown, options) {
548
626
  }
549
627
  for (let c = 0; c < colCount; c++) {
550
628
  const cellX = startX + c * colWidth;
551
- await renderCellTokens(row[c], cellX + cellPad, y + textInsetY, colWidth - cellPad * 2, table.align[c] || 'left', false);
629
+ await renderCellTokens(row[c], cellX + cellPad, y + textInsetY, textWidth, table.align[c] || 'left', false);
552
630
  }
553
631
  doc.save();
554
632
  doc.strokeColor(theme.table.borderColor).lineWidth(0.5);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@speajus/markdown-to-pdf",
3
- "version": "1.0.19",
3
+ "version": "1.0.20",
4
4
  "description": "A new project created with Intent by Augment.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",