jspdf-md-renderer 4.0.0 → 4.0.1

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/dist/index.d.mts CHANGED
@@ -52,6 +52,8 @@ type RenderOption = {
52
52
  bold: FontItem;
53
53
  regular: FontItem;
54
54
  light: FontItem;
55
+ italic?: FontItem;
56
+ boldItalic?: FontItem;
55
57
  code?: FontItem;
56
58
  };
57
59
  heading?: {
package/dist/index.d.ts CHANGED
@@ -52,6 +52,8 @@ type RenderOption = {
52
52
  bold: FontItem;
53
53
  regular: FontItem;
54
54
  light: FontItem;
55
+ italic?: FontItem;
56
+ boldItalic?: FontItem;
55
57
  code?: FontItem;
56
58
  };
57
59
  heading?: {
package/dist/index.js CHANGED
@@ -413,6 +413,22 @@ const ensureSpace = (doc, store, minHeight) => {
413
413
  const getCharHight = (doc) => {
414
414
  return doc.getFontSize() / doc.internal.scaleFactor;
415
415
  };
416
+ /**
417
+ * Saves the current jsPDF font, size, and text color, executes `fn`,
418
+ * then restores those properties — even if `fn` throws.
419
+ */
420
+ const withSavedDocState = (doc, fn) => {
421
+ const savedFont = doc.getFont();
422
+ const savedSize = doc.getFontSize();
423
+ const savedColor = doc.getTextColor();
424
+ try {
425
+ return fn();
426
+ } finally {
427
+ doc.setFont(savedFont.fontName, savedFont.fontStyle);
428
+ doc.setFontSize(savedSize);
429
+ doc.setTextColor(savedColor);
430
+ }
431
+ };
416
432
  //#endregion
417
433
  //#region src/utils/image-utils.ts
418
434
  /**
@@ -436,6 +452,29 @@ const pxToDocUnit = (px, unit = "mm") => {
436
452
  }
437
453
  };
438
454
  /**
455
+ * Detects the image format from a ParsedElement's data URI and source URL.
456
+ * Returns a format string suitable for jsPDF's addImage (e.g. 'PNG', 'JPEG').
457
+ */
458
+ const detectImageFormat = (element) => {
459
+ if (element.data) {
460
+ if (element.data.startsWith("data:image/png")) return "PNG";
461
+ if (element.data.startsWith("data:image/jpeg") || element.data.startsWith("data:image/jpg")) return "JPEG";
462
+ if (element.data.startsWith("data:image/webp")) return "WEBP";
463
+ if (element.data.startsWith("data:image/gif")) return "GIF";
464
+ }
465
+ if (element.src) {
466
+ const ext = element.src.split("?")[0].split("#")[0].split(".").pop()?.toUpperCase();
467
+ if (ext && [
468
+ "PNG",
469
+ "JPEG",
470
+ "JPG",
471
+ "WEBP",
472
+ "GIF"
473
+ ].includes(ext)) return ext === "JPG" ? "JPEG" : ext;
474
+ }
475
+ return "JPEG";
476
+ };
477
+ /**
439
478
  * Extracts width and height from an SVG data URI if possible.
440
479
  */
441
480
  const extractSvgDimensions = (dataUri) => {
@@ -613,6 +652,14 @@ const applyStyleToDoc = (doc, style, store) => {
613
652
  const curSize = doc.getFontSize();
614
653
  const boldFont = store.options.font.bold?.name || curFont;
615
654
  const regularFont = store.options.font.regular?.name || curFont;
655
+ const italicFont = store.options.font.italic || {
656
+ name: regularFont,
657
+ style: "italic"
658
+ };
659
+ const boldItalicFont = store.options.font.boldItalic || {
660
+ name: italicFont.name,
661
+ style: "bolditalic"
662
+ };
616
663
  const codeFont = store.options.font.code || {
617
664
  name: "courier",
618
665
  style: "normal"
@@ -622,10 +669,10 @@ const applyStyleToDoc = (doc, style, store) => {
622
669
  doc.setFont(boldFont, store.options.font.bold?.style || "bold");
623
670
  break;
624
671
  case "italic":
625
- doc.setFont(regularFont, "italic");
672
+ doc.setFont(italicFont.name, italicFont.style);
626
673
  break;
627
674
  case "bolditalic":
628
- doc.setFont(boldFont, "bolditalic");
675
+ doc.setFont(boldItalicFont.name, boldItalicFont.style);
629
676
  break;
630
677
  case "codespan":
631
678
  doc.setFont(codeFont.name, codeFont.style);
@@ -831,23 +878,19 @@ const renderLine = (doc, line, x, y, maxWidth, store, alignment = "left") => {
831
878
  }
832
879
  };
833
880
  const renderSingleWord = (doc, word, x, y, store) => {
834
- const savedFont = doc.getFont();
835
- const savedSize = doc.getFontSize();
836
- const savedColor = doc.getTextColor();
837
- applyStyleToDoc(doc, word.style, store);
838
- if (word.isLink && word.linkColor) doc.setTextColor(...word.linkColor);
839
- if (word.isImage && word.imageElement?.data) renderInlineImage(doc, word, x, y);
840
- else {
841
- if (word.style === "codespan") renderCodespanBackground(doc, word, x, y, store);
842
- doc.text(word.text, x, y, { baseline: "top" });
843
- }
844
- if (word.isLink && word.href) {
845
- const h = word.isImage && word.imageHeight ? word.imageHeight : doc.getTextDimensions("H").h;
846
- doc.link(x, y, word.width, h, { url: word.href });
847
- }
848
- doc.setFont(savedFont.fontName, savedFont.fontStyle);
849
- doc.setFontSize(savedSize);
850
- doc.setTextColor(savedColor);
881
+ withSavedDocState(doc, () => {
882
+ applyStyleToDoc(doc, word.style, store);
883
+ if (word.isLink && word.linkColor) doc.setTextColor(...word.linkColor);
884
+ if (word.isImage && word.imageElement?.data) renderInlineImage(doc, word, x, y);
885
+ else {
886
+ if (word.style === "codespan") renderCodespanBackground(doc, word, x, y, store);
887
+ doc.text(word.text, x, y, { baseline: "top" });
888
+ }
889
+ if (word.isLink && word.href) {
890
+ const h = word.isImage && word.imageHeight ? word.imageHeight : doc.getTextDimensions("H").h;
891
+ doc.link(x, y, word.width, h, { url: word.href });
892
+ }
893
+ });
851
894
  };
852
895
  const renderCodespanBackground = (doc, word, x, y, store) => {
853
896
  const opts = store.options.codespan ?? {};
@@ -861,10 +904,7 @@ const renderCodespanBackground = (doc, word, x, y, store) => {
861
904
  };
862
905
  const renderInlineImage = (doc, word, x, y) => {
863
906
  const el = word.imageElement;
864
- let fmt = "JPEG";
865
- if (el.data.startsWith("data:image/png")) fmt = "PNG";
866
- else if (el.data.startsWith("data:image/webp")) fmt = "WEBP";
867
- else if (el.data.startsWith("data:image/gif")) fmt = "GIF";
907
+ const fmt = detectImageFormat(el);
868
908
  if (word.width > 0 && (word.imageHeight || 0) > 0) doc.addImage(el.data, fmt, x, y, word.width, word.imageHeight);
869
909
  };
870
910
  //#endregion
@@ -917,29 +957,26 @@ const renderPlainText = (doc, text, x, y, maxWidth, store, opts = {}) => {
917
957
  //#endregion
918
958
  //#region src/renderer/components/heading.ts
919
959
  const renderHeading = (doc, element, indent, store) => {
920
- const savedColor = doc.getTextColor();
921
- const headingKey = `h${element?.depth ?? 1}`;
922
- const fontSize = store.options.heading?.[headingKey] ?? store.options.page.defaultFontSize;
923
- const headingColor = store.options.heading?.[`${headingKey}Color`] ?? store.options.heading?.color ?? "#000000";
924
- const savedSize = doc.getFontSize();
925
- doc.setFontSize(fontSize);
926
- doc.setFont(store.options.font.bold.name, store.options.font.bold.style || "bold");
927
- doc.setTextColor(headingColor);
928
- breakIfOverflow(doc, store, getCharHight(doc) * 1.8);
929
- const maxWidth = store.options.page.maxContentWidth - indent;
930
- if (element.items && element.items.length > 0) renderInlineContent(doc, element.items, store.X + indent, store.Y, maxWidth, store, {
931
- alignment: "left",
932
- trimLastLine: true
933
- });
934
- else renderPlainText(doc, element?.content ?? "", store.X + indent, store.Y, maxWidth, store, {
935
- alignment: "left",
936
- trimLastLine: true
960
+ withSavedDocState(doc, () => {
961
+ const headingKey = `h${element?.depth ?? 1}`;
962
+ const fontSize = store.options.heading?.[headingKey] ?? store.options.page.defaultFontSize;
963
+ const headingColor = store.options.heading?.[`${headingKey}Color`] ?? store.options.heading?.color ?? "#000000";
964
+ doc.setFontSize(fontSize);
965
+ doc.setFont(store.options.font.bold.name, store.options.font.bold.style || "bold");
966
+ doc.setTextColor(headingColor);
967
+ breakIfOverflow(doc, store, getCharHight(doc) * 1.8);
968
+ const maxWidth = store.options.page.maxContentWidth - indent;
969
+ if (element.items && element.items.length > 0) renderInlineContent(doc, element.items, store.X + indent, store.Y, maxWidth, store, {
970
+ alignment: "left",
971
+ trimLastLine: true
972
+ });
973
+ else renderPlainText(doc, element?.content ?? "", store.X + indent, store.Y, maxWidth, store, {
974
+ alignment: "left",
975
+ trimLastLine: true
976
+ });
977
+ const bottomSpacing = store.options.heading?.bottomSpacing ?? store.options.spacing?.afterHeading ?? 2;
978
+ store.updateY(bottomSpacing, "add");
937
979
  });
938
- const bottomSpacing = store.options.heading?.bottomSpacing ?? store.options.spacing?.afterHeading ?? 2;
939
- store.updateY(bottomSpacing, "add");
940
- doc.setFontSize(savedSize);
941
- doc.setFont(store.options.font.regular.name, store.options.font.regular.style);
942
- doc.setTextColor(savedColor);
943
980
  store.updateX(store.options.page.xpading, "set");
944
981
  };
945
982
  //#endregion
@@ -1241,29 +1278,6 @@ const renderBlockquote = (doc, element, indentLevel, store, renderElement) => {
1241
1278
  //#endregion
1242
1279
  //#region src/renderer/components/image.ts
1243
1280
  /**
1244
- * Detects the image format from element data and source.
1245
- */
1246
- const detectImageFormat = (element) => {
1247
- if (element.data) {
1248
- if (element.data.startsWith("data:image/png")) return "PNG";
1249
- if (element.data.startsWith("data:image/jpeg") || element.data.startsWith("data:image/jpg")) return "JPEG";
1250
- if (element.data.startsWith("data:image/webp")) return "WEBP";
1251
- if (element.data.startsWith("data:image/webp")) return "WEBP";
1252
- if (element.data.startsWith("data:image/gif")) return "GIF";
1253
- }
1254
- if (element.src) {
1255
- const ext = element.src.split("?")[0].split("#")[0].split(".").pop()?.toUpperCase();
1256
- if (ext && [
1257
- "PNG",
1258
- "JPEG",
1259
- "JPG",
1260
- "WEBP",
1261
- "GIF"
1262
- ].includes(ext)) return ext === "JPG" ? "JPEG" : ext;
1263
- }
1264
- return "JPEG";
1265
- };
1266
- /**
1267
1281
  * Renders an image element into the jsPDF document with smart sizing and alignment.
1268
1282
  *
1269
1283
  * Sizing logic (in order of priority):
@@ -1474,6 +1488,14 @@ const DEFAULT_FONT = {
1474
1488
  name: "helvetica",
1475
1489
  style: "light"
1476
1490
  },
1491
+ italic: {
1492
+ name: "helvetica",
1493
+ style: "italic"
1494
+ },
1495
+ boldItalic: {
1496
+ name: "helvetica",
1497
+ style: "bolditalic"
1498
+ },
1477
1499
  code: {
1478
1500
  name: "courier",
1479
1501
  style: "normal"
@@ -1511,6 +1533,8 @@ const validateOptions = (options) => {
1511
1533
  ...options.font
1512
1534
  };
1513
1535
  if (!font.bold?.name) font.bold = DEFAULT_FONT.bold;
1536
+ if (!font.italic?.name) font.italic = DEFAULT_FONT.italic;
1537
+ if (!font.boldItalic?.name) font.boldItalic = DEFAULT_FONT.boldItalic;
1514
1538
  if (!font.code?.name) font.code = DEFAULT_FONT.code;
1515
1539
  const heading = {
1516
1540
  ...DEFAULT_HEADING_SIZES,
@@ -1524,7 +1548,7 @@ const validateOptions = (options) => {
1524
1548
  "h5",
1525
1549
  "h6"
1526
1550
  ].forEach((k) => {
1527
- if (heading[k] < 6 || heading[k] > 72) heading[k] = DEFAULT_HEADING_SIZES[k];
1551
+ if ((heading[k] ?? 0) < 6 || (heading[k] ?? 0) > 72) heading[k] = DEFAULT_HEADING_SIZES[k];
1528
1552
  });
1529
1553
  const codespan = {
1530
1554
  backgroundColor: "#EEEEEE",
@@ -1621,49 +1645,41 @@ const applyHeader = (doc, options, pageNum, totalPages) => {
1621
1645
  if (!hOpts) return;
1622
1646
  const text = typeof hOpts.text === "function" ? hOpts.text(pageNum, totalPages) : hOpts.text ?? "";
1623
1647
  if (!text.trim()) return;
1624
- const savedFont = doc.getFont();
1625
- const savedSize = doc.getFontSize();
1626
- const savedColor = doc.getTextColor();
1627
- doc.setFontSize(hOpts.fontSize ?? 9);
1628
- doc.setTextColor(hOpts.color ?? "#666666");
1629
- const y = hOpts.y ?? 5;
1630
- const align = hOpts.align ?? "center";
1631
- const pageWidth = doc.internal.pageSize.getWidth();
1632
- let x = pageWidth / 2;
1633
- if (align === "left") x = options.page.xmargin;
1634
- if (align === "right") x = pageWidth - options.page.xmargin;
1635
- doc.text(text, x, y, {
1636
- align,
1637
- baseline: "top"
1648
+ withSavedDocState(doc, () => {
1649
+ doc.setFontSize(hOpts.fontSize ?? 9);
1650
+ doc.setTextColor(hOpts.color ?? "#666666");
1651
+ const y = hOpts.y ?? 5;
1652
+ const align = hOpts.align ?? "center";
1653
+ const pageWidth = doc.internal.pageSize.getWidth();
1654
+ let x = pageWidth / 2;
1655
+ if (align === "left") x = options.page.xmargin;
1656
+ if (align === "right") x = pageWidth - options.page.xmargin;
1657
+ doc.text(text, x, y, {
1658
+ align,
1659
+ baseline: "top"
1660
+ });
1638
1661
  });
1639
- doc.setFont(savedFont.fontName, savedFont.fontStyle);
1640
- doc.setFontSize(savedSize);
1641
- doc.setTextColor(savedColor);
1642
1662
  };
1643
1663
  const applyFooter = (doc, options, pageNum, totalPages) => {
1644
1664
  const fOpts = options.footer;
1645
1665
  if (!fOpts) return;
1646
1666
  const text = fOpts.showPageNumbers ? `Page ${pageNum} of ${totalPages}` : typeof fOpts.text === "function" ? fOpts.text(pageNum, totalPages) : fOpts.text ?? "";
1647
1667
  if (!text.trim()) return;
1648
- const savedFont = doc.getFont();
1649
- const savedSize = doc.getFontSize();
1650
- const savedColor = doc.getTextColor();
1651
- doc.setFontSize(fOpts.fontSize ?? 9);
1652
- doc.setTextColor(fOpts.color ?? "#666666");
1653
- const pageHeight = doc.internal.pageSize.getHeight();
1654
- const y = fOpts.y ?? pageHeight - 5;
1655
- const align = fOpts.align ?? "right";
1656
- const pageWidth = doc.internal.pageSize.getWidth();
1657
- let x = pageWidth / 2;
1658
- if (align === "left") x = options.page.xmargin;
1659
- if (align === "right") x = pageWidth - options.page.xmargin;
1660
- doc.text(text, x, y, {
1661
- align,
1662
- baseline: "bottom"
1668
+ withSavedDocState(doc, () => {
1669
+ doc.setFontSize(fOpts.fontSize ?? 9);
1670
+ doc.setTextColor(fOpts.color ?? "#666666");
1671
+ const pageHeight = doc.internal.pageSize.getHeight();
1672
+ const y = fOpts.y ?? pageHeight - 5;
1673
+ const align = fOpts.align ?? "right";
1674
+ const pageWidth = doc.internal.pageSize.getWidth();
1675
+ let x = pageWidth / 2;
1676
+ if (align === "left") x = options.page.xmargin;
1677
+ if (align === "right") x = pageWidth - options.page.xmargin;
1678
+ doc.text(text, x, y, {
1679
+ align,
1680
+ baseline: "bottom"
1681
+ });
1663
1682
  });
1664
- doc.setFont(savedFont.fontName, savedFont.fontStyle);
1665
- doc.setFontSize(savedSize);
1666
- doc.setTextColor(savedColor);
1667
1683
  };
1668
1684
  //#endregion
1669
1685
  //#region src/renderer/MdTextRender.ts
package/dist/index.mjs CHANGED
@@ -389,6 +389,22 @@ const ensureSpace = (doc, store, minHeight) => {
389
389
  const getCharHight = (doc) => {
390
390
  return doc.getFontSize() / doc.internal.scaleFactor;
391
391
  };
392
+ /**
393
+ * Saves the current jsPDF font, size, and text color, executes `fn`,
394
+ * then restores those properties — even if `fn` throws.
395
+ */
396
+ const withSavedDocState = (doc, fn) => {
397
+ const savedFont = doc.getFont();
398
+ const savedSize = doc.getFontSize();
399
+ const savedColor = doc.getTextColor();
400
+ try {
401
+ return fn();
402
+ } finally {
403
+ doc.setFont(savedFont.fontName, savedFont.fontStyle);
404
+ doc.setFontSize(savedSize);
405
+ doc.setTextColor(savedColor);
406
+ }
407
+ };
392
408
  //#endregion
393
409
  //#region src/utils/image-utils.ts
394
410
  /**
@@ -412,6 +428,29 @@ const pxToDocUnit = (px, unit = "mm") => {
412
428
  }
413
429
  };
414
430
  /**
431
+ * Detects the image format from a ParsedElement's data URI and source URL.
432
+ * Returns a format string suitable for jsPDF's addImage (e.g. 'PNG', 'JPEG').
433
+ */
434
+ const detectImageFormat = (element) => {
435
+ if (element.data) {
436
+ if (element.data.startsWith("data:image/png")) return "PNG";
437
+ if (element.data.startsWith("data:image/jpeg") || element.data.startsWith("data:image/jpg")) return "JPEG";
438
+ if (element.data.startsWith("data:image/webp")) return "WEBP";
439
+ if (element.data.startsWith("data:image/gif")) return "GIF";
440
+ }
441
+ if (element.src) {
442
+ const ext = element.src.split("?")[0].split("#")[0].split(".").pop()?.toUpperCase();
443
+ if (ext && [
444
+ "PNG",
445
+ "JPEG",
446
+ "JPG",
447
+ "WEBP",
448
+ "GIF"
449
+ ].includes(ext)) return ext === "JPG" ? "JPEG" : ext;
450
+ }
451
+ return "JPEG";
452
+ };
453
+ /**
415
454
  * Extracts width and height from an SVG data URI if possible.
416
455
  */
417
456
  const extractSvgDimensions = (dataUri) => {
@@ -589,6 +628,14 @@ const applyStyleToDoc = (doc, style, store) => {
589
628
  const curSize = doc.getFontSize();
590
629
  const boldFont = store.options.font.bold?.name || curFont;
591
630
  const regularFont = store.options.font.regular?.name || curFont;
631
+ const italicFont = store.options.font.italic || {
632
+ name: regularFont,
633
+ style: "italic"
634
+ };
635
+ const boldItalicFont = store.options.font.boldItalic || {
636
+ name: italicFont.name,
637
+ style: "bolditalic"
638
+ };
592
639
  const codeFont = store.options.font.code || {
593
640
  name: "courier",
594
641
  style: "normal"
@@ -598,10 +645,10 @@ const applyStyleToDoc = (doc, style, store) => {
598
645
  doc.setFont(boldFont, store.options.font.bold?.style || "bold");
599
646
  break;
600
647
  case "italic":
601
- doc.setFont(regularFont, "italic");
648
+ doc.setFont(italicFont.name, italicFont.style);
602
649
  break;
603
650
  case "bolditalic":
604
- doc.setFont(boldFont, "bolditalic");
651
+ doc.setFont(boldItalicFont.name, boldItalicFont.style);
605
652
  break;
606
653
  case "codespan":
607
654
  doc.setFont(codeFont.name, codeFont.style);
@@ -807,23 +854,19 @@ const renderLine = (doc, line, x, y, maxWidth, store, alignment = "left") => {
807
854
  }
808
855
  };
809
856
  const renderSingleWord = (doc, word, x, y, store) => {
810
- const savedFont = doc.getFont();
811
- const savedSize = doc.getFontSize();
812
- const savedColor = doc.getTextColor();
813
- applyStyleToDoc(doc, word.style, store);
814
- if (word.isLink && word.linkColor) doc.setTextColor(...word.linkColor);
815
- if (word.isImage && word.imageElement?.data) renderInlineImage(doc, word, x, y);
816
- else {
817
- if (word.style === "codespan") renderCodespanBackground(doc, word, x, y, store);
818
- doc.text(word.text, x, y, { baseline: "top" });
819
- }
820
- if (word.isLink && word.href) {
821
- const h = word.isImage && word.imageHeight ? word.imageHeight : doc.getTextDimensions("H").h;
822
- doc.link(x, y, word.width, h, { url: word.href });
823
- }
824
- doc.setFont(savedFont.fontName, savedFont.fontStyle);
825
- doc.setFontSize(savedSize);
826
- doc.setTextColor(savedColor);
857
+ withSavedDocState(doc, () => {
858
+ applyStyleToDoc(doc, word.style, store);
859
+ if (word.isLink && word.linkColor) doc.setTextColor(...word.linkColor);
860
+ if (word.isImage && word.imageElement?.data) renderInlineImage(doc, word, x, y);
861
+ else {
862
+ if (word.style === "codespan") renderCodespanBackground(doc, word, x, y, store);
863
+ doc.text(word.text, x, y, { baseline: "top" });
864
+ }
865
+ if (word.isLink && word.href) {
866
+ const h = word.isImage && word.imageHeight ? word.imageHeight : doc.getTextDimensions("H").h;
867
+ doc.link(x, y, word.width, h, { url: word.href });
868
+ }
869
+ });
827
870
  };
828
871
  const renderCodespanBackground = (doc, word, x, y, store) => {
829
872
  const opts = store.options.codespan ?? {};
@@ -837,10 +880,7 @@ const renderCodespanBackground = (doc, word, x, y, store) => {
837
880
  };
838
881
  const renderInlineImage = (doc, word, x, y) => {
839
882
  const el = word.imageElement;
840
- let fmt = "JPEG";
841
- if (el.data.startsWith("data:image/png")) fmt = "PNG";
842
- else if (el.data.startsWith("data:image/webp")) fmt = "WEBP";
843
- else if (el.data.startsWith("data:image/gif")) fmt = "GIF";
883
+ const fmt = detectImageFormat(el);
844
884
  if (word.width > 0 && (word.imageHeight || 0) > 0) doc.addImage(el.data, fmt, x, y, word.width, word.imageHeight);
845
885
  };
846
886
  //#endregion
@@ -893,29 +933,26 @@ const renderPlainText = (doc, text, x, y, maxWidth, store, opts = {}) => {
893
933
  //#endregion
894
934
  //#region src/renderer/components/heading.ts
895
935
  const renderHeading = (doc, element, indent, store) => {
896
- const savedColor = doc.getTextColor();
897
- const headingKey = `h${element?.depth ?? 1}`;
898
- const fontSize = store.options.heading?.[headingKey] ?? store.options.page.defaultFontSize;
899
- const headingColor = store.options.heading?.[`${headingKey}Color`] ?? store.options.heading?.color ?? "#000000";
900
- const savedSize = doc.getFontSize();
901
- doc.setFontSize(fontSize);
902
- doc.setFont(store.options.font.bold.name, store.options.font.bold.style || "bold");
903
- doc.setTextColor(headingColor);
904
- breakIfOverflow(doc, store, getCharHight(doc) * 1.8);
905
- const maxWidth = store.options.page.maxContentWidth - indent;
906
- if (element.items && element.items.length > 0) renderInlineContent(doc, element.items, store.X + indent, store.Y, maxWidth, store, {
907
- alignment: "left",
908
- trimLastLine: true
909
- });
910
- else renderPlainText(doc, element?.content ?? "", store.X + indent, store.Y, maxWidth, store, {
911
- alignment: "left",
912
- trimLastLine: true
936
+ withSavedDocState(doc, () => {
937
+ const headingKey = `h${element?.depth ?? 1}`;
938
+ const fontSize = store.options.heading?.[headingKey] ?? store.options.page.defaultFontSize;
939
+ const headingColor = store.options.heading?.[`${headingKey}Color`] ?? store.options.heading?.color ?? "#000000";
940
+ doc.setFontSize(fontSize);
941
+ doc.setFont(store.options.font.bold.name, store.options.font.bold.style || "bold");
942
+ doc.setTextColor(headingColor);
943
+ breakIfOverflow(doc, store, getCharHight(doc) * 1.8);
944
+ const maxWidth = store.options.page.maxContentWidth - indent;
945
+ if (element.items && element.items.length > 0) renderInlineContent(doc, element.items, store.X + indent, store.Y, maxWidth, store, {
946
+ alignment: "left",
947
+ trimLastLine: true
948
+ });
949
+ else renderPlainText(doc, element?.content ?? "", store.X + indent, store.Y, maxWidth, store, {
950
+ alignment: "left",
951
+ trimLastLine: true
952
+ });
953
+ const bottomSpacing = store.options.heading?.bottomSpacing ?? store.options.spacing?.afterHeading ?? 2;
954
+ store.updateY(bottomSpacing, "add");
913
955
  });
914
- const bottomSpacing = store.options.heading?.bottomSpacing ?? store.options.spacing?.afterHeading ?? 2;
915
- store.updateY(bottomSpacing, "add");
916
- doc.setFontSize(savedSize);
917
- doc.setFont(store.options.font.regular.name, store.options.font.regular.style);
918
- doc.setTextColor(savedColor);
919
956
  store.updateX(store.options.page.xpading, "set");
920
957
  };
921
958
  //#endregion
@@ -1217,29 +1254,6 @@ const renderBlockquote = (doc, element, indentLevel, store, renderElement) => {
1217
1254
  //#endregion
1218
1255
  //#region src/renderer/components/image.ts
1219
1256
  /**
1220
- * Detects the image format from element data and source.
1221
- */
1222
- const detectImageFormat = (element) => {
1223
- if (element.data) {
1224
- if (element.data.startsWith("data:image/png")) return "PNG";
1225
- if (element.data.startsWith("data:image/jpeg") || element.data.startsWith("data:image/jpg")) return "JPEG";
1226
- if (element.data.startsWith("data:image/webp")) return "WEBP";
1227
- if (element.data.startsWith("data:image/webp")) return "WEBP";
1228
- if (element.data.startsWith("data:image/gif")) return "GIF";
1229
- }
1230
- if (element.src) {
1231
- const ext = element.src.split("?")[0].split("#")[0].split(".").pop()?.toUpperCase();
1232
- if (ext && [
1233
- "PNG",
1234
- "JPEG",
1235
- "JPG",
1236
- "WEBP",
1237
- "GIF"
1238
- ].includes(ext)) return ext === "JPG" ? "JPEG" : ext;
1239
- }
1240
- return "JPEG";
1241
- };
1242
- /**
1243
1257
  * Renders an image element into the jsPDF document with smart sizing and alignment.
1244
1258
  *
1245
1259
  * Sizing logic (in order of priority):
@@ -1450,6 +1464,14 @@ const DEFAULT_FONT = {
1450
1464
  name: "helvetica",
1451
1465
  style: "light"
1452
1466
  },
1467
+ italic: {
1468
+ name: "helvetica",
1469
+ style: "italic"
1470
+ },
1471
+ boldItalic: {
1472
+ name: "helvetica",
1473
+ style: "bolditalic"
1474
+ },
1453
1475
  code: {
1454
1476
  name: "courier",
1455
1477
  style: "normal"
@@ -1487,6 +1509,8 @@ const validateOptions = (options) => {
1487
1509
  ...options.font
1488
1510
  };
1489
1511
  if (!font.bold?.name) font.bold = DEFAULT_FONT.bold;
1512
+ if (!font.italic?.name) font.italic = DEFAULT_FONT.italic;
1513
+ if (!font.boldItalic?.name) font.boldItalic = DEFAULT_FONT.boldItalic;
1490
1514
  if (!font.code?.name) font.code = DEFAULT_FONT.code;
1491
1515
  const heading = {
1492
1516
  ...DEFAULT_HEADING_SIZES,
@@ -1500,7 +1524,7 @@ const validateOptions = (options) => {
1500
1524
  "h5",
1501
1525
  "h6"
1502
1526
  ].forEach((k) => {
1503
- if (heading[k] < 6 || heading[k] > 72) heading[k] = DEFAULT_HEADING_SIZES[k];
1527
+ if ((heading[k] ?? 0) < 6 || (heading[k] ?? 0) > 72) heading[k] = DEFAULT_HEADING_SIZES[k];
1504
1528
  });
1505
1529
  const codespan = {
1506
1530
  backgroundColor: "#EEEEEE",
@@ -1597,49 +1621,41 @@ const applyHeader = (doc, options, pageNum, totalPages) => {
1597
1621
  if (!hOpts) return;
1598
1622
  const text = typeof hOpts.text === "function" ? hOpts.text(pageNum, totalPages) : hOpts.text ?? "";
1599
1623
  if (!text.trim()) return;
1600
- const savedFont = doc.getFont();
1601
- const savedSize = doc.getFontSize();
1602
- const savedColor = doc.getTextColor();
1603
- doc.setFontSize(hOpts.fontSize ?? 9);
1604
- doc.setTextColor(hOpts.color ?? "#666666");
1605
- const y = hOpts.y ?? 5;
1606
- const align = hOpts.align ?? "center";
1607
- const pageWidth = doc.internal.pageSize.getWidth();
1608
- let x = pageWidth / 2;
1609
- if (align === "left") x = options.page.xmargin;
1610
- if (align === "right") x = pageWidth - options.page.xmargin;
1611
- doc.text(text, x, y, {
1612
- align,
1613
- baseline: "top"
1624
+ withSavedDocState(doc, () => {
1625
+ doc.setFontSize(hOpts.fontSize ?? 9);
1626
+ doc.setTextColor(hOpts.color ?? "#666666");
1627
+ const y = hOpts.y ?? 5;
1628
+ const align = hOpts.align ?? "center";
1629
+ const pageWidth = doc.internal.pageSize.getWidth();
1630
+ let x = pageWidth / 2;
1631
+ if (align === "left") x = options.page.xmargin;
1632
+ if (align === "right") x = pageWidth - options.page.xmargin;
1633
+ doc.text(text, x, y, {
1634
+ align,
1635
+ baseline: "top"
1636
+ });
1614
1637
  });
1615
- doc.setFont(savedFont.fontName, savedFont.fontStyle);
1616
- doc.setFontSize(savedSize);
1617
- doc.setTextColor(savedColor);
1618
1638
  };
1619
1639
  const applyFooter = (doc, options, pageNum, totalPages) => {
1620
1640
  const fOpts = options.footer;
1621
1641
  if (!fOpts) return;
1622
1642
  const text = fOpts.showPageNumbers ? `Page ${pageNum} of ${totalPages}` : typeof fOpts.text === "function" ? fOpts.text(pageNum, totalPages) : fOpts.text ?? "";
1623
1643
  if (!text.trim()) return;
1624
- const savedFont = doc.getFont();
1625
- const savedSize = doc.getFontSize();
1626
- const savedColor = doc.getTextColor();
1627
- doc.setFontSize(fOpts.fontSize ?? 9);
1628
- doc.setTextColor(fOpts.color ?? "#666666");
1629
- const pageHeight = doc.internal.pageSize.getHeight();
1630
- const y = fOpts.y ?? pageHeight - 5;
1631
- const align = fOpts.align ?? "right";
1632
- const pageWidth = doc.internal.pageSize.getWidth();
1633
- let x = pageWidth / 2;
1634
- if (align === "left") x = options.page.xmargin;
1635
- if (align === "right") x = pageWidth - options.page.xmargin;
1636
- doc.text(text, x, y, {
1637
- align,
1638
- baseline: "bottom"
1644
+ withSavedDocState(doc, () => {
1645
+ doc.setFontSize(fOpts.fontSize ?? 9);
1646
+ doc.setTextColor(fOpts.color ?? "#666666");
1647
+ const pageHeight = doc.internal.pageSize.getHeight();
1648
+ const y = fOpts.y ?? pageHeight - 5;
1649
+ const align = fOpts.align ?? "right";
1650
+ const pageWidth = doc.internal.pageSize.getWidth();
1651
+ let x = pageWidth / 2;
1652
+ if (align === "left") x = options.page.xmargin;
1653
+ if (align === "right") x = pageWidth - options.page.xmargin;
1654
+ doc.text(text, x, y, {
1655
+ align,
1656
+ baseline: "bottom"
1657
+ });
1639
1658
  });
1640
- doc.setFont(savedFont.fontName, savedFont.fontStyle);
1641
- doc.setFontSize(savedSize);
1642
- doc.setTextColor(savedColor);
1643
1659
  };
1644
1660
  //#endregion
1645
1661
  //#region src/renderer/MdTextRender.ts
package/dist/index.umd.js CHANGED
@@ -418,6 +418,22 @@
418
418
  const getCharHight = (doc) => {
419
419
  return doc.getFontSize() / doc.internal.scaleFactor;
420
420
  };
421
+ /**
422
+ * Saves the current jsPDF font, size, and text color, executes `fn`,
423
+ * then restores those properties — even if `fn` throws.
424
+ */
425
+ const withSavedDocState = (doc, fn) => {
426
+ const savedFont = doc.getFont();
427
+ const savedSize = doc.getFontSize();
428
+ const savedColor = doc.getTextColor();
429
+ try {
430
+ return fn();
431
+ } finally {
432
+ doc.setFont(savedFont.fontName, savedFont.fontStyle);
433
+ doc.setFontSize(savedSize);
434
+ doc.setTextColor(savedColor);
435
+ }
436
+ };
421
437
  //#endregion
422
438
  //#region src/utils/image-utils.ts
423
439
  /**
@@ -441,6 +457,29 @@
441
457
  }
442
458
  };
443
459
  /**
460
+ * Detects the image format from a ParsedElement's data URI and source URL.
461
+ * Returns a format string suitable for jsPDF's addImage (e.g. 'PNG', 'JPEG').
462
+ */
463
+ const detectImageFormat = (element) => {
464
+ if (element.data) {
465
+ if (element.data.startsWith("data:image/png")) return "PNG";
466
+ if (element.data.startsWith("data:image/jpeg") || element.data.startsWith("data:image/jpg")) return "JPEG";
467
+ if (element.data.startsWith("data:image/webp")) return "WEBP";
468
+ if (element.data.startsWith("data:image/gif")) return "GIF";
469
+ }
470
+ if (element.src) {
471
+ const ext = element.src.split("?")[0].split("#")[0].split(".").pop()?.toUpperCase();
472
+ if (ext && [
473
+ "PNG",
474
+ "JPEG",
475
+ "JPG",
476
+ "WEBP",
477
+ "GIF"
478
+ ].includes(ext)) return ext === "JPG" ? "JPEG" : ext;
479
+ }
480
+ return "JPEG";
481
+ };
482
+ /**
444
483
  * Extracts width and height from an SVG data URI if possible.
445
484
  */
446
485
  const extractSvgDimensions = (dataUri) => {
@@ -618,6 +657,14 @@
618
657
  const curSize = doc.getFontSize();
619
658
  const boldFont = store.options.font.bold?.name || curFont;
620
659
  const regularFont = store.options.font.regular?.name || curFont;
660
+ const italicFont = store.options.font.italic || {
661
+ name: regularFont,
662
+ style: "italic"
663
+ };
664
+ const boldItalicFont = store.options.font.boldItalic || {
665
+ name: italicFont.name,
666
+ style: "bolditalic"
667
+ };
621
668
  const codeFont = store.options.font.code || {
622
669
  name: "courier",
623
670
  style: "normal"
@@ -627,10 +674,10 @@
627
674
  doc.setFont(boldFont, store.options.font.bold?.style || "bold");
628
675
  break;
629
676
  case "italic":
630
- doc.setFont(regularFont, "italic");
677
+ doc.setFont(italicFont.name, italicFont.style);
631
678
  break;
632
679
  case "bolditalic":
633
- doc.setFont(boldFont, "bolditalic");
680
+ doc.setFont(boldItalicFont.name, boldItalicFont.style);
634
681
  break;
635
682
  case "codespan":
636
683
  doc.setFont(codeFont.name, codeFont.style);
@@ -836,23 +883,19 @@
836
883
  }
837
884
  };
838
885
  const renderSingleWord = (doc, word, x, y, store) => {
839
- const savedFont = doc.getFont();
840
- const savedSize = doc.getFontSize();
841
- const savedColor = doc.getTextColor();
842
- applyStyleToDoc(doc, word.style, store);
843
- if (word.isLink && word.linkColor) doc.setTextColor(...word.linkColor);
844
- if (word.isImage && word.imageElement?.data) renderInlineImage(doc, word, x, y);
845
- else {
846
- if (word.style === "codespan") renderCodespanBackground(doc, word, x, y, store);
847
- doc.text(word.text, x, y, { baseline: "top" });
848
- }
849
- if (word.isLink && word.href) {
850
- const h = word.isImage && word.imageHeight ? word.imageHeight : doc.getTextDimensions("H").h;
851
- doc.link(x, y, word.width, h, { url: word.href });
852
- }
853
- doc.setFont(savedFont.fontName, savedFont.fontStyle);
854
- doc.setFontSize(savedSize);
855
- doc.setTextColor(savedColor);
886
+ withSavedDocState(doc, () => {
887
+ applyStyleToDoc(doc, word.style, store);
888
+ if (word.isLink && word.linkColor) doc.setTextColor(...word.linkColor);
889
+ if (word.isImage && word.imageElement?.data) renderInlineImage(doc, word, x, y);
890
+ else {
891
+ if (word.style === "codespan") renderCodespanBackground(doc, word, x, y, store);
892
+ doc.text(word.text, x, y, { baseline: "top" });
893
+ }
894
+ if (word.isLink && word.href) {
895
+ const h = word.isImage && word.imageHeight ? word.imageHeight : doc.getTextDimensions("H").h;
896
+ doc.link(x, y, word.width, h, { url: word.href });
897
+ }
898
+ });
856
899
  };
857
900
  const renderCodespanBackground = (doc, word, x, y, store) => {
858
901
  const opts = store.options.codespan ?? {};
@@ -866,10 +909,7 @@
866
909
  };
867
910
  const renderInlineImage = (doc, word, x, y) => {
868
911
  const el = word.imageElement;
869
- let fmt = "JPEG";
870
- if (el.data.startsWith("data:image/png")) fmt = "PNG";
871
- else if (el.data.startsWith("data:image/webp")) fmt = "WEBP";
872
- else if (el.data.startsWith("data:image/gif")) fmt = "GIF";
912
+ const fmt = detectImageFormat(el);
873
913
  if (word.width > 0 && (word.imageHeight || 0) > 0) doc.addImage(el.data, fmt, x, y, word.width, word.imageHeight);
874
914
  };
875
915
  //#endregion
@@ -922,29 +962,26 @@
922
962
  //#endregion
923
963
  //#region src/renderer/components/heading.ts
924
964
  const renderHeading = (doc, element, indent, store) => {
925
- const savedColor = doc.getTextColor();
926
- const headingKey = `h${element?.depth ?? 1}`;
927
- const fontSize = store.options.heading?.[headingKey] ?? store.options.page.defaultFontSize;
928
- const headingColor = store.options.heading?.[`${headingKey}Color`] ?? store.options.heading?.color ?? "#000000";
929
- const savedSize = doc.getFontSize();
930
- doc.setFontSize(fontSize);
931
- doc.setFont(store.options.font.bold.name, store.options.font.bold.style || "bold");
932
- doc.setTextColor(headingColor);
933
- breakIfOverflow(doc, store, getCharHight(doc) * 1.8);
934
- const maxWidth = store.options.page.maxContentWidth - indent;
935
- if (element.items && element.items.length > 0) renderInlineContent(doc, element.items, store.X + indent, store.Y, maxWidth, store, {
936
- alignment: "left",
937
- trimLastLine: true
938
- });
939
- else renderPlainText(doc, element?.content ?? "", store.X + indent, store.Y, maxWidth, store, {
940
- alignment: "left",
941
- trimLastLine: true
965
+ withSavedDocState(doc, () => {
966
+ const headingKey = `h${element?.depth ?? 1}`;
967
+ const fontSize = store.options.heading?.[headingKey] ?? store.options.page.defaultFontSize;
968
+ const headingColor = store.options.heading?.[`${headingKey}Color`] ?? store.options.heading?.color ?? "#000000";
969
+ doc.setFontSize(fontSize);
970
+ doc.setFont(store.options.font.bold.name, store.options.font.bold.style || "bold");
971
+ doc.setTextColor(headingColor);
972
+ breakIfOverflow(doc, store, getCharHight(doc) * 1.8);
973
+ const maxWidth = store.options.page.maxContentWidth - indent;
974
+ if (element.items && element.items.length > 0) renderInlineContent(doc, element.items, store.X + indent, store.Y, maxWidth, store, {
975
+ alignment: "left",
976
+ trimLastLine: true
977
+ });
978
+ else renderPlainText(doc, element?.content ?? "", store.X + indent, store.Y, maxWidth, store, {
979
+ alignment: "left",
980
+ trimLastLine: true
981
+ });
982
+ const bottomSpacing = store.options.heading?.bottomSpacing ?? store.options.spacing?.afterHeading ?? 2;
983
+ store.updateY(bottomSpacing, "add");
942
984
  });
943
- const bottomSpacing = store.options.heading?.bottomSpacing ?? store.options.spacing?.afterHeading ?? 2;
944
- store.updateY(bottomSpacing, "add");
945
- doc.setFontSize(savedSize);
946
- doc.setFont(store.options.font.regular.name, store.options.font.regular.style);
947
- doc.setTextColor(savedColor);
948
985
  store.updateX(store.options.page.xpading, "set");
949
986
  };
950
987
  //#endregion
@@ -1246,29 +1283,6 @@
1246
1283
  //#endregion
1247
1284
  //#region src/renderer/components/image.ts
1248
1285
  /**
1249
- * Detects the image format from element data and source.
1250
- */
1251
- const detectImageFormat = (element) => {
1252
- if (element.data) {
1253
- if (element.data.startsWith("data:image/png")) return "PNG";
1254
- if (element.data.startsWith("data:image/jpeg") || element.data.startsWith("data:image/jpg")) return "JPEG";
1255
- if (element.data.startsWith("data:image/webp")) return "WEBP";
1256
- if (element.data.startsWith("data:image/webp")) return "WEBP";
1257
- if (element.data.startsWith("data:image/gif")) return "GIF";
1258
- }
1259
- if (element.src) {
1260
- const ext = element.src.split("?")[0].split("#")[0].split(".").pop()?.toUpperCase();
1261
- if (ext && [
1262
- "PNG",
1263
- "JPEG",
1264
- "JPG",
1265
- "WEBP",
1266
- "GIF"
1267
- ].includes(ext)) return ext === "JPG" ? "JPEG" : ext;
1268
- }
1269
- return "JPEG";
1270
- };
1271
- /**
1272
1286
  * Renders an image element into the jsPDF document with smart sizing and alignment.
1273
1287
  *
1274
1288
  * Sizing logic (in order of priority):
@@ -1479,6 +1493,14 @@
1479
1493
  name: "helvetica",
1480
1494
  style: "light"
1481
1495
  },
1496
+ italic: {
1497
+ name: "helvetica",
1498
+ style: "italic"
1499
+ },
1500
+ boldItalic: {
1501
+ name: "helvetica",
1502
+ style: "bolditalic"
1503
+ },
1482
1504
  code: {
1483
1505
  name: "courier",
1484
1506
  style: "normal"
@@ -1516,6 +1538,8 @@
1516
1538
  ...options.font
1517
1539
  };
1518
1540
  if (!font.bold?.name) font.bold = DEFAULT_FONT.bold;
1541
+ if (!font.italic?.name) font.italic = DEFAULT_FONT.italic;
1542
+ if (!font.boldItalic?.name) font.boldItalic = DEFAULT_FONT.boldItalic;
1519
1543
  if (!font.code?.name) font.code = DEFAULT_FONT.code;
1520
1544
  const heading = {
1521
1545
  ...DEFAULT_HEADING_SIZES,
@@ -1529,7 +1553,7 @@
1529
1553
  "h5",
1530
1554
  "h6"
1531
1555
  ].forEach((k) => {
1532
- if (heading[k] < 6 || heading[k] > 72) heading[k] = DEFAULT_HEADING_SIZES[k];
1556
+ if ((heading[k] ?? 0) < 6 || (heading[k] ?? 0) > 72) heading[k] = DEFAULT_HEADING_SIZES[k];
1533
1557
  });
1534
1558
  const codespan = {
1535
1559
  backgroundColor: "#EEEEEE",
@@ -1626,49 +1650,41 @@
1626
1650
  if (!hOpts) return;
1627
1651
  const text = typeof hOpts.text === "function" ? hOpts.text(pageNum, totalPages) : hOpts.text ?? "";
1628
1652
  if (!text.trim()) return;
1629
- const savedFont = doc.getFont();
1630
- const savedSize = doc.getFontSize();
1631
- const savedColor = doc.getTextColor();
1632
- doc.setFontSize(hOpts.fontSize ?? 9);
1633
- doc.setTextColor(hOpts.color ?? "#666666");
1634
- const y = hOpts.y ?? 5;
1635
- const align = hOpts.align ?? "center";
1636
- const pageWidth = doc.internal.pageSize.getWidth();
1637
- let x = pageWidth / 2;
1638
- if (align === "left") x = options.page.xmargin;
1639
- if (align === "right") x = pageWidth - options.page.xmargin;
1640
- doc.text(text, x, y, {
1641
- align,
1642
- baseline: "top"
1653
+ withSavedDocState(doc, () => {
1654
+ doc.setFontSize(hOpts.fontSize ?? 9);
1655
+ doc.setTextColor(hOpts.color ?? "#666666");
1656
+ const y = hOpts.y ?? 5;
1657
+ const align = hOpts.align ?? "center";
1658
+ const pageWidth = doc.internal.pageSize.getWidth();
1659
+ let x = pageWidth / 2;
1660
+ if (align === "left") x = options.page.xmargin;
1661
+ if (align === "right") x = pageWidth - options.page.xmargin;
1662
+ doc.text(text, x, y, {
1663
+ align,
1664
+ baseline: "top"
1665
+ });
1643
1666
  });
1644
- doc.setFont(savedFont.fontName, savedFont.fontStyle);
1645
- doc.setFontSize(savedSize);
1646
- doc.setTextColor(savedColor);
1647
1667
  };
1648
1668
  const applyFooter = (doc, options, pageNum, totalPages) => {
1649
1669
  const fOpts = options.footer;
1650
1670
  if (!fOpts) return;
1651
1671
  const text = fOpts.showPageNumbers ? `Page ${pageNum} of ${totalPages}` : typeof fOpts.text === "function" ? fOpts.text(pageNum, totalPages) : fOpts.text ?? "";
1652
1672
  if (!text.trim()) return;
1653
- const savedFont = doc.getFont();
1654
- const savedSize = doc.getFontSize();
1655
- const savedColor = doc.getTextColor();
1656
- doc.setFontSize(fOpts.fontSize ?? 9);
1657
- doc.setTextColor(fOpts.color ?? "#666666");
1658
- const pageHeight = doc.internal.pageSize.getHeight();
1659
- const y = fOpts.y ?? pageHeight - 5;
1660
- const align = fOpts.align ?? "right";
1661
- const pageWidth = doc.internal.pageSize.getWidth();
1662
- let x = pageWidth / 2;
1663
- if (align === "left") x = options.page.xmargin;
1664
- if (align === "right") x = pageWidth - options.page.xmargin;
1665
- doc.text(text, x, y, {
1666
- align,
1667
- baseline: "bottom"
1673
+ withSavedDocState(doc, () => {
1674
+ doc.setFontSize(fOpts.fontSize ?? 9);
1675
+ doc.setTextColor(fOpts.color ?? "#666666");
1676
+ const pageHeight = doc.internal.pageSize.getHeight();
1677
+ const y = fOpts.y ?? pageHeight - 5;
1678
+ const align = fOpts.align ?? "right";
1679
+ const pageWidth = doc.internal.pageSize.getWidth();
1680
+ let x = pageWidth / 2;
1681
+ if (align === "left") x = options.page.xmargin;
1682
+ if (align === "right") x = pageWidth - options.page.xmargin;
1683
+ doc.text(text, x, y, {
1684
+ align,
1685
+ baseline: "bottom"
1686
+ });
1668
1687
  });
1669
- doc.setFont(savedFont.fontName, savedFont.fontStyle);
1670
- doc.setFontSize(savedSize);
1671
- doc.setTextColor(savedColor);
1672
1688
  };
1673
1689
  //#endregion
1674
1690
  //#region src/renderer/MdTextRender.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jspdf-md-renderer",
3
- "version": "4.0.0",
3
+ "version": "4.0.1",
4
4
  "description": "A jsPDF utility to render Markdown directly into formatted PDFs with custom designs.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",