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 +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +120 -104
- package/dist/index.mjs +120 -104
- package/dist/index.umd.js +120 -104
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
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(
|
|
672
|
+
doc.setFont(italicFont.name, italicFont.style);
|
|
626
673
|
break;
|
|
627
674
|
case "bolditalic":
|
|
628
|
-
doc.setFont(
|
|
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
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
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
|
-
|
|
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
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
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
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
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
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
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(
|
|
648
|
+
doc.setFont(italicFont.name, italicFont.style);
|
|
602
649
|
break;
|
|
603
650
|
case "bolditalic":
|
|
604
|
-
doc.setFont(
|
|
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
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
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
|
-
|
|
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
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
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
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
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
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
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(
|
|
677
|
+
doc.setFont(italicFont.name, italicFont.style);
|
|
631
678
|
break;
|
|
632
679
|
case "bolditalic":
|
|
633
|
-
doc.setFont(
|
|
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
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
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
|
-
|
|
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
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
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
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
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
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
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
|