@bendyline/squisq-formats 2.4.6 → 2.5.0
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/{chunk-KN62QJYK.js → chunk-2LT3JL7U.js} +75 -24
- package/dist/{chunk-BVGJ54NV.js → chunk-5PUIFU5I.js} +1 -1
- package/dist/chunk-A6LSCIO5.js +1197 -0
- package/dist/{chunk-DWNNYO5H.js → chunk-A6N6IN3I.js} +5 -1
- package/dist/{chunk-6X5XN3CZ.js → chunk-AVOZAKGP.js} +3 -0
- package/dist/chunk-BXWNU4T5.js +108 -0
- package/dist/{chunk-BGDN7BXK.js → chunk-FIOSE4BO.js} +78 -6
- package/dist/{chunk-XCV242AO.js → chunk-FMQWNPCV.js} +5 -2
- package/dist/{chunk-DNGNODJP.js → chunk-JTGWQK5V.js} +12 -2
- package/dist/{chunk-KT5DX2QP.js → chunk-M7XPXGXW.js} +32 -9
- package/dist/{chunk-N2ZN3MQN.js → chunk-T4PX33AG.js} +262 -14
- package/dist/docx/index.js +4 -3
- package/dist/epub/index.js +3 -2
- package/dist/export-m0tr9r9d.d.ts +130 -0
- package/dist/html/index.js +2 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +11 -10
- package/dist/outside-in/index.d.ts +2 -2
- package/dist/outside-in/index.js +2 -2
- package/dist/pdf/index.js +2 -1
- package/dist/pptx/index.js +3 -3
- package/dist/registry/index.d.ts +3 -3
- package/dist/registry/index.js +1 -1
- package/dist/{types-HDLauUoa.d.ts → types-DByrrXeB.d.ts} +1 -1
- package/dist/xlsx/index.d.ts +2 -2
- package/dist/xlsx/index.js +2 -2
- package/package.json +2 -2
- package/dist/chunk-UIBTXX4L.js +0 -446
- package/dist/export-D9msROJS.d.ts +0 -81
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
stripHtmlTags
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-AVOZAKGP.js";
|
|
4
4
|
|
|
5
5
|
// src/ooxml/relIds.ts
|
|
6
6
|
var RelIdAllocator = class {
|
|
@@ -92,6 +92,10 @@ function inlineNodeToRuns(node, handlers, format) {
|
|
|
92
92
|
return inlineNodesToRuns(node.children, handlers, { ...format, italic: true });
|
|
93
93
|
case "delete":
|
|
94
94
|
return inlineNodesToRuns(node.children, handlers, { ...format, strike: true });
|
|
95
|
+
case "superscript":
|
|
96
|
+
return inlineNodesToRuns(node.children, handlers, { ...format, vertAlign: "superscript" });
|
|
97
|
+
case "subscript":
|
|
98
|
+
return inlineNodesToRuns(node.children, handlers, { ...format, vertAlign: "subscript" });
|
|
95
99
|
case "inlineCode":
|
|
96
100
|
return handlers.run(node.value, { ...format, code: true });
|
|
97
101
|
case "inlineMath":
|
|
@@ -11,6 +11,8 @@ function inlineToPlainText(node) {
|
|
|
11
11
|
case "emphasis":
|
|
12
12
|
case "strong":
|
|
13
13
|
case "delete":
|
|
14
|
+
case "superscript":
|
|
15
|
+
case "subscript":
|
|
14
16
|
case "link":
|
|
15
17
|
return extractPlainText(node.children);
|
|
16
18
|
case "image":
|
|
@@ -29,5 +31,6 @@ function extractPlainText(nodes) {
|
|
|
29
31
|
|
|
30
32
|
export {
|
|
31
33
|
stripHtmlTags,
|
|
34
|
+
inlineToPlainText,
|
|
32
35
|
extractPlainText
|
|
33
36
|
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// src/shared/footnotes.ts
|
|
2
|
+
var FootnoteIndex = class {
|
|
3
|
+
constructor(doc) {
|
|
4
|
+
this.numbers = /* @__PURE__ */ new Map();
|
|
5
|
+
this.definitions = /* @__PURE__ */ new Map();
|
|
6
|
+
/** Citations rendered so far, per identifier — see {@link cite}. */
|
|
7
|
+
this.citations = /* @__PURE__ */ new Map();
|
|
8
|
+
collectReferences(doc.children, (id) => {
|
|
9
|
+
if (!this.numbers.has(id)) this.numbers.set(id, this.numbers.size + 1);
|
|
10
|
+
});
|
|
11
|
+
collectDefinitions(doc.children, (def) => {
|
|
12
|
+
this.definitions.set(def.identifier, def);
|
|
13
|
+
if (!this.numbers.has(def.identifier))
|
|
14
|
+
this.numbers.set(def.identifier, this.numbers.size + 1);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
/** Whether the document has any footnotes at all. */
|
|
18
|
+
get isEmpty() {
|
|
19
|
+
return this.numbers.size === 0;
|
|
20
|
+
}
|
|
21
|
+
/** The reader-facing number for an identifier (assigning one if unseen). */
|
|
22
|
+
numberFor(identifier) {
|
|
23
|
+
const existing = this.numbers.get(identifier);
|
|
24
|
+
if (existing !== void 0) return existing;
|
|
25
|
+
const next = this.numbers.size + 1;
|
|
26
|
+
this.numbers.set(identifier, next);
|
|
27
|
+
return next;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Record one citation and return how to anchor it.
|
|
31
|
+
*
|
|
32
|
+
* Prose may cite the same footnote repeatedly. Every citation needs its own
|
|
33
|
+
* element id — reusing one id would emit duplicate ids, which is invalid HTML
|
|
34
|
+
* and leaves the backlink pointing at whichever copy the browser found first.
|
|
35
|
+
* Call this exactly once per rendered reference, in document order.
|
|
36
|
+
*/
|
|
37
|
+
cite(identifier) {
|
|
38
|
+
const number = this.numberFor(identifier);
|
|
39
|
+
const occurrence = (this.citations.get(identifier) ?? 0) + 1;
|
|
40
|
+
this.citations.set(identifier, occurrence);
|
|
41
|
+
return { number, occurrence };
|
|
42
|
+
}
|
|
43
|
+
/** Every footnote in reading order. */
|
|
44
|
+
ordered() {
|
|
45
|
+
return [...this.numbers.entries()].map(([identifier, number]) => ({
|
|
46
|
+
identifier,
|
|
47
|
+
number,
|
|
48
|
+
definition: this.definitions.get(identifier),
|
|
49
|
+
citations: this.citations.get(identifier) ?? 0
|
|
50
|
+
})).sort((a, b) => a.number - b.number);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
function isFootnoteDefinition(node) {
|
|
54
|
+
return node.type === "footnoteDefinition";
|
|
55
|
+
}
|
|
56
|
+
function childrenOf(node) {
|
|
57
|
+
return "children" in node && Array.isArray(node.children) ? node.children : [];
|
|
58
|
+
}
|
|
59
|
+
function collectReferences(nodes, visit) {
|
|
60
|
+
for (const node of nodes) {
|
|
61
|
+
if (node.type === "footnoteDefinition") continue;
|
|
62
|
+
if (node.type === "footnoteReference") {
|
|
63
|
+
visit(node.identifier);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
collectReferences(childrenOf(node), visit);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function collectDefinitions(nodes, visit) {
|
|
70
|
+
for (const node of nodes) {
|
|
71
|
+
if (isFootnoteDefinition(node)) {
|
|
72
|
+
visit(node);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
collectDefinitions(childrenOf(node), visit);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function footnoteIds(identifier, occurrence = 1) {
|
|
79
|
+
const safe = identifier.replace(/[^A-Za-z0-9_-]/g, "-");
|
|
80
|
+
const suffix = occurrence > 1 ? `-${occurrence}` : "";
|
|
81
|
+
return { ref: `fnref-${safe}${suffix}`, def: `fn-${safe}` };
|
|
82
|
+
}
|
|
83
|
+
function renumberFootnotes(doc) {
|
|
84
|
+
const index = new FootnoteIndex(doc);
|
|
85
|
+
if (index.isEmpty) return;
|
|
86
|
+
const renamed = /* @__PURE__ */ new Map();
|
|
87
|
+
for (const fn of index.ordered()) renamed.set(fn.identifier, String(fn.number));
|
|
88
|
+
if ([...renamed].every(([from, to]) => from === to)) return;
|
|
89
|
+
const walk = (nodes) => {
|
|
90
|
+
for (const node of nodes) {
|
|
91
|
+
if (node.type === "footnoteReference" || node.type === "footnoteDefinition") {
|
|
92
|
+
const next = renamed.get(node.identifier);
|
|
93
|
+
if (next !== void 0) {
|
|
94
|
+
node.identifier = next;
|
|
95
|
+
if ("label" in node && node.label !== void 0) node.label = next;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if ("children" in node && Array.isArray(node.children)) walk(node.children);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
walk(doc.children);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export {
|
|
105
|
+
FootnoteIndex,
|
|
106
|
+
footnoteIds,
|
|
107
|
+
renumberFootnotes
|
|
108
|
+
};
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
inlineNodesToRuns,
|
|
5
5
|
normalizeOoxmlHex,
|
|
6
6
|
sanitizeOfficeHyperlink
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-A6N6IN3I.js";
|
|
8
8
|
import {
|
|
9
9
|
stripHtmlTags
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-AVOZAKGP.js";
|
|
11
11
|
import {
|
|
12
12
|
createPackage
|
|
13
13
|
} from "./chunk-ILCJ3WFD.js";
|
|
@@ -59,6 +59,9 @@ import {
|
|
|
59
59
|
import {
|
|
60
60
|
extToMime
|
|
61
61
|
} from "./chunk-AONELFLA.js";
|
|
62
|
+
import {
|
|
63
|
+
renumberFootnotes
|
|
64
|
+
} from "./chunk-BXWNU4T5.js";
|
|
62
65
|
|
|
63
66
|
// src/docx/export.ts
|
|
64
67
|
import { resolveFontFamily } from "@bendyline/squisq/schemas";
|
|
@@ -471,6 +474,7 @@ function makeRun(text, format) {
|
|
|
471
474
|
if (format.strike) rPrParts.push("<w:strike/>");
|
|
472
475
|
if (format.color) rPrParts.push(`<w:color w:val="${format.color}"/>`);
|
|
473
476
|
if (format.code) rPrParts.push(`<w:sz w:val="${DEFAULT_CODE_FONT_SIZE}"/>`);
|
|
477
|
+
if (format.vertAlign) rPrParts.push(`<w:vertAlign w:val="${format.vertAlign}"/>`);
|
|
474
478
|
const rPr = rPrParts.length > 0 ? `<w:rPr>${rPrParts.join("")}</w:rPr>` : "";
|
|
475
479
|
return `<w:r>${rPr}<w:t xml:space="preserve">${escapeXml(text)}</w:t></w:r>`;
|
|
476
480
|
}
|
|
@@ -797,7 +801,9 @@ async function docxToMarkdownDoc(data, options = {}) {
|
|
|
797
801
|
return { type: "document", children: [] };
|
|
798
802
|
}
|
|
799
803
|
const blocks = await convertDocumentStories(body, ctx);
|
|
800
|
-
|
|
804
|
+
const doc = { type: "document", children: blocks };
|
|
805
|
+
renumberFootnotes(doc);
|
|
806
|
+
return doc;
|
|
801
807
|
}
|
|
802
808
|
async function docxToDoc(data, options = {}) {
|
|
803
809
|
const markdownDoc = await docxToMarkdownDoc(data, options);
|
|
@@ -1062,7 +1068,9 @@ async function convertParagraph2(el, ctx) {
|
|
|
1062
1068
|
return { type: "paragraph", children: inlines };
|
|
1063
1069
|
}
|
|
1064
1070
|
async function convertRuns(paragraphEl, ctx) {
|
|
1065
|
-
return
|
|
1071
|
+
return normalizeParagraphWhitespace(
|
|
1072
|
+
await convertInlineElements(Array.from(paragraphEl.children), ctx)
|
|
1073
|
+
);
|
|
1066
1074
|
}
|
|
1067
1075
|
async function convertInlineElements(children, ctx) {
|
|
1068
1076
|
const result = [];
|
|
@@ -1105,6 +1113,9 @@ async function convertRun(runEl, ctx) {
|
|
|
1105
1113
|
result.push({ type: "inlineCode", value: text });
|
|
1106
1114
|
} else {
|
|
1107
1115
|
let node = { type: "text", value: text };
|
|
1116
|
+
if (format.vertAlign) {
|
|
1117
|
+
node = format.vertAlign === "superscript" ? { type: "superscript", children: [node] } : { type: "subscript", children: [node] };
|
|
1118
|
+
}
|
|
1108
1119
|
if (format.strike) {
|
|
1109
1120
|
node = { type: "delete", children: [node] };
|
|
1110
1121
|
}
|
|
@@ -1202,7 +1213,7 @@ function appendInlineGroup(target, group) {
|
|
|
1202
1213
|
target.push(...group);
|
|
1203
1214
|
}
|
|
1204
1215
|
function parseRunFormat(rPr, ctx) {
|
|
1205
|
-
if (!rPr) return { bold: false, italic: false, strike: false, code: false };
|
|
1216
|
+
if (!rPr) return { bold: false, italic: false, strike: false, code: false, vertAlign: null };
|
|
1206
1217
|
const bold = hasChildElement(rPr, "b") && !isFalseToggle(getFirstChildElement(rPr, "b"));
|
|
1207
1218
|
const italic = hasChildElement(rPr, "i") && !isFalseToggle(getFirstChildElement(rPr, "i"));
|
|
1208
1219
|
const strike = hasChildElement(rPr, "strike") && !isFalseToggle(getFirstChildElement(rPr, "strike"));
|
|
@@ -1212,7 +1223,10 @@ function parseRunFormat(rPr, ctx) {
|
|
|
1212
1223
|
const rFonts = getFirstChildElement(rPr, "rFonts");
|
|
1213
1224
|
const fontName = rFonts ? getAttr(rFonts, "ascii") ?? getAttr(rFonts, "hAnsi") ?? "" : "";
|
|
1214
1225
|
const isMonospace = /consolas|courier|mono/i.test(fontName);
|
|
1215
|
-
|
|
1226
|
+
const vertAlignEl = getFirstChildElement(rPr, "vertAlign");
|
|
1227
|
+
const vertAlignVal = vertAlignEl ? getAttr(vertAlignEl, "val") : null;
|
|
1228
|
+
const vertAlign = vertAlignVal === "superscript" || vertAlignVal === "subscript" ? vertAlignVal : null;
|
|
1229
|
+
return { bold, italic, strike, code: isCodeStyle || isMonospace, vertAlign };
|
|
1216
1230
|
}
|
|
1217
1231
|
function isFalseToggle(el) {
|
|
1218
1232
|
const val = getAttr(el, "val");
|
|
@@ -1486,6 +1500,64 @@ function mergeAdjacentText(nodes) {
|
|
|
1486
1500
|
}
|
|
1487
1501
|
return result;
|
|
1488
1502
|
}
|
|
1503
|
+
function isImportedInlineContainer(node) {
|
|
1504
|
+
return node.type === "emphasis" || node.type === "strong" || node.type === "delete" || node.type === "superscript" || node.type === "subscript" || node.type === "link";
|
|
1505
|
+
}
|
|
1506
|
+
function normalizeParagraphWhitespace(nodes) {
|
|
1507
|
+
const result = [];
|
|
1508
|
+
let line = [];
|
|
1509
|
+
const flushLine = () => {
|
|
1510
|
+
const normalized = liftFormattedBoundaryWhitespace(line);
|
|
1511
|
+
takeLeadingHorizontalWhitespace(normalized);
|
|
1512
|
+
takeTrailingHorizontalWhitespace(normalized);
|
|
1513
|
+
result.push(...mergeAdjacentText(normalized));
|
|
1514
|
+
line = [];
|
|
1515
|
+
};
|
|
1516
|
+
for (const node of nodes) {
|
|
1517
|
+
if (node.type === "break") {
|
|
1518
|
+
flushLine();
|
|
1519
|
+
result.push(node);
|
|
1520
|
+
} else {
|
|
1521
|
+
line.push(node);
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
flushLine();
|
|
1525
|
+
return result;
|
|
1526
|
+
}
|
|
1527
|
+
function liftFormattedBoundaryWhitespace(nodes) {
|
|
1528
|
+
const result = [];
|
|
1529
|
+
for (const node of nodes) {
|
|
1530
|
+
if (!isImportedInlineContainer(node)) {
|
|
1531
|
+
result.push(node);
|
|
1532
|
+
continue;
|
|
1533
|
+
}
|
|
1534
|
+
const children = liftFormattedBoundaryWhitespace(node.children);
|
|
1535
|
+
const leading = takeLeadingHorizontalWhitespace(children);
|
|
1536
|
+
const trailing = takeTrailingHorizontalWhitespace(children);
|
|
1537
|
+
if (leading) result.push({ type: "text", value: leading });
|
|
1538
|
+
if (children.length > 0) result.push({ ...node, children });
|
|
1539
|
+
if (trailing) result.push({ type: "text", value: trailing });
|
|
1540
|
+
}
|
|
1541
|
+
return mergeAdjacentText(result);
|
|
1542
|
+
}
|
|
1543
|
+
function takeLeadingHorizontalWhitespace(nodes) {
|
|
1544
|
+
const first = nodes[0];
|
|
1545
|
+
if (first?.type !== "text") return "";
|
|
1546
|
+
const match = /^[\t ]+/.exec(first.value);
|
|
1547
|
+
if (!match) return "";
|
|
1548
|
+
first.value = first.value.slice(match[0].length);
|
|
1549
|
+
if (!first.value) nodes.shift();
|
|
1550
|
+
return match[0];
|
|
1551
|
+
}
|
|
1552
|
+
function takeTrailingHorizontalWhitespace(nodes) {
|
|
1553
|
+
const last = nodes[nodes.length - 1];
|
|
1554
|
+
if (last?.type !== "text") return "";
|
|
1555
|
+
const match = /[\t ]+$/.exec(last.value);
|
|
1556
|
+
if (!match) return "";
|
|
1557
|
+
last.value = last.value.slice(0, -match[0].length);
|
|
1558
|
+
if (!last.value) nodes.pop();
|
|
1559
|
+
return match[0];
|
|
1560
|
+
}
|
|
1489
1561
|
|
|
1490
1562
|
export {
|
|
1491
1563
|
markdownDocToDocx,
|
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
inlineNodesToRuns,
|
|
5
5
|
sanitizeOfficeHyperlink,
|
|
6
6
|
toOoxmlHex
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-A6N6IN3I.js";
|
|
8
8
|
import {
|
|
9
9
|
extractPlainText,
|
|
10
10
|
stripHtmlTags
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-AVOZAKGP.js";
|
|
12
12
|
import {
|
|
13
13
|
createPackage
|
|
14
14
|
} from "./chunk-ILCJ3WFD.js";
|
|
@@ -1232,6 +1232,9 @@ function makeRun(text, format, style) {
|
|
|
1232
1232
|
if (format.bold) rPrParts.push(`b="1"`);
|
|
1233
1233
|
if (format.italic) rPrParts.push(`i="1"`);
|
|
1234
1234
|
if (format.strike) rPrParts.push(`strike="sngStrike"`);
|
|
1235
|
+
if (format.vertAlign) {
|
|
1236
|
+
rPrParts.push(`baseline="${format.vertAlign === "superscript" ? 3e4 : -25e3}"`);
|
|
1237
|
+
}
|
|
1235
1238
|
let innerParts = "";
|
|
1236
1239
|
if (format.code) {
|
|
1237
1240
|
innerParts += `<a:solidFill><a:srgbClr val="${style.codeColor}"/></a:solidFill>`;
|
|
@@ -105,6 +105,8 @@ var CONTENT_NODE_TYPES = {
|
|
|
105
105
|
emphasis: true,
|
|
106
106
|
strong: true,
|
|
107
107
|
delete: true,
|
|
108
|
+
superscript: true,
|
|
109
|
+
subscript: true,
|
|
108
110
|
inlineCode: true,
|
|
109
111
|
link: true,
|
|
110
112
|
image: true,
|
|
@@ -132,6 +134,8 @@ var COMMON_SUPPORTED = [
|
|
|
132
134
|
"emphasis",
|
|
133
135
|
"strong",
|
|
134
136
|
"delete",
|
|
137
|
+
"superscript",
|
|
138
|
+
"subscript",
|
|
135
139
|
"inlineCode",
|
|
136
140
|
"link",
|
|
137
141
|
"image",
|
|
@@ -407,7 +411,10 @@ function defaultFormats() {
|
|
|
407
411
|
};
|
|
408
412
|
const xlsx = {
|
|
409
413
|
id: "xlsx",
|
|
410
|
-
|
|
414
|
+
// Not 'ignored': a `{[dataTable sheet=… anchor=…]}` annotation decides which
|
|
415
|
+
// worksheet a table lands on and at which cell, so annotations demonstrably
|
|
416
|
+
// affect the exported result.
|
|
417
|
+
templateAnnotationHandling: "preserved",
|
|
411
418
|
label: "Excel (XLSX)",
|
|
412
419
|
mimeType: MIME.xlsx,
|
|
413
420
|
extensions: [".xlsx"],
|
|
@@ -424,7 +431,10 @@ function defaultFormats() {
|
|
|
424
431
|
const warnings = omitted > 0 ? [`XLSX export is tables-only; ${omitted} non-table block(s) were omitted.`] : [];
|
|
425
432
|
const blob = await markdownDocToXlsx(markdownDoc, {
|
|
426
433
|
...optionsFor(options, "xlsx"),
|
|
427
|
-
...options.title !== void 0 ? { title: options.title } : {}
|
|
434
|
+
...options.title !== void 0 ? { title: options.title } : {},
|
|
435
|
+
// Placement problems (a bad anchor, overlapping regions) degrade rather
|
|
436
|
+
// than throw, so they have to reach the caller as conversion warnings.
|
|
437
|
+
onWarning: (message) => warnings.push(message)
|
|
428
438
|
});
|
|
429
439
|
return ok(await toBytes(blob), MIME.xlsx, warnings);
|
|
430
440
|
}
|
|
@@ -6,6 +6,9 @@ import {
|
|
|
6
6
|
fontAwesomeFaces,
|
|
7
7
|
fontAwesomeGlyph
|
|
8
8
|
} from "./chunk-USU6HTKB.js";
|
|
9
|
+
import {
|
|
10
|
+
FootnoteIndex
|
|
11
|
+
} from "./chunk-BXWNU4T5.js";
|
|
9
12
|
|
|
10
13
|
// src/pdf/export.ts
|
|
11
14
|
import { PDFDocument, StandardFonts, rgb, PDFString } from "pdf-lib";
|
|
@@ -450,6 +453,7 @@ async function createExportContext(pdfDoc, options, doc) {
|
|
|
450
453
|
contentWidth: pageWidth - 2 * margin,
|
|
451
454
|
bottomY: margin,
|
|
452
455
|
colors: { text: colorText, heading: colorHeading, link: colorLink },
|
|
456
|
+
footnotes: new FootnoteIndex(doc),
|
|
453
457
|
text: new WinAnsiTracker(),
|
|
454
458
|
signal: options.signal
|
|
455
459
|
};
|
|
@@ -464,19 +468,25 @@ function newPage(ctx) {
|
|
|
464
468
|
ctx.page = page;
|
|
465
469
|
ctx.y = ctx.pageHeight - ctx.margin;
|
|
466
470
|
}
|
|
471
|
+
var VERT_ALIGN_SCALE = 0.72;
|
|
472
|
+
function vertAlignShift(kind, baseSize) {
|
|
473
|
+
return kind === "superscript" ? baseSize * 0.33 : baseSize * -0.14;
|
|
474
|
+
}
|
|
467
475
|
function flattenInlines(nodes, ctx, state) {
|
|
468
476
|
const spans = [];
|
|
469
477
|
for (const node of nodes) {
|
|
470
478
|
switch (node.type) {
|
|
471
479
|
case "text": {
|
|
472
480
|
const font = state.code ? state.bold ? ctx.fonts.monoBold : ctx.fonts.mono : pickFont(ctx, state.bold, state.italic);
|
|
481
|
+
const baseSize = state.code ? CODE_FONT_SIZE : ctx.fontSize;
|
|
473
482
|
spans.push({
|
|
474
483
|
text: ctx.text.clean(node.value),
|
|
475
484
|
font,
|
|
476
|
-
fontSize: state.
|
|
485
|
+
fontSize: state.vertAlign ? baseSize * VERT_ALIGN_SCALE : baseSize,
|
|
477
486
|
color: state.code ? COLOR_CODE_TEXT : state.color ?? ctx.colors.text,
|
|
478
487
|
link: state.link,
|
|
479
|
-
strikethrough: state.strikethrough
|
|
488
|
+
strikethrough: state.strikethrough,
|
|
489
|
+
...state.vertAlign ? { baselineShift: vertAlignShift(state.vertAlign, baseSize) } : {}
|
|
480
490
|
});
|
|
481
491
|
break;
|
|
482
492
|
}
|
|
@@ -498,6 +508,15 @@ function flattenInlines(nodes, ctx, state) {
|
|
|
498
508
|
})
|
|
499
509
|
);
|
|
500
510
|
break;
|
|
511
|
+
case "superscript":
|
|
512
|
+
case "subscript":
|
|
513
|
+
spans.push(
|
|
514
|
+
...flattenInlines(node.children, ctx, {
|
|
515
|
+
...state,
|
|
516
|
+
vertAlign: node.type === "superscript" ? "superscript" : "subscript"
|
|
517
|
+
})
|
|
518
|
+
);
|
|
519
|
+
break;
|
|
501
520
|
case "inlineCode": {
|
|
502
521
|
spans.push({
|
|
503
522
|
text: ctx.text.clean(node.value),
|
|
@@ -585,11 +604,13 @@ function flattenInlines(nodes, ctx, state) {
|
|
|
585
604
|
break;
|
|
586
605
|
case "footnoteReference": {
|
|
587
606
|
const ref = node;
|
|
607
|
+
const marker = ctx.footnotes ? String(ctx.footnotes.cite(ref.identifier).number) : ref.label ?? ref.identifier;
|
|
588
608
|
spans.push({
|
|
589
|
-
text: ctx.text.clean(
|
|
609
|
+
text: ctx.text.clean(marker),
|
|
590
610
|
font: ctx.fonts.regular,
|
|
591
|
-
fontSize: ctx.fontSize *
|
|
592
|
-
color: ctx.colors.link
|
|
611
|
+
fontSize: ctx.fontSize * VERT_ALIGN_SCALE,
|
|
612
|
+
color: ctx.colors.link,
|
|
613
|
+
baselineShift: vertAlignShift("superscript", ctx.fontSize)
|
|
593
614
|
});
|
|
594
615
|
break;
|
|
595
616
|
}
|
|
@@ -626,9 +647,10 @@ function drawSpans(spans, ctx, availableWidth, x0) {
|
|
|
626
647
|
ensureSpace(ctx, lineHeight);
|
|
627
648
|
let x = x0;
|
|
628
649
|
for (const span of line) {
|
|
650
|
+
const spanBaseline = ctx.y - span.fontSize + (span.baselineShift ?? 0);
|
|
629
651
|
ctx.page.drawText(span.text, {
|
|
630
652
|
x,
|
|
631
|
-
y:
|
|
653
|
+
y: spanBaseline,
|
|
632
654
|
// pdf-lib y is baseline
|
|
633
655
|
size: span.fontSize,
|
|
634
656
|
font: span.font,
|
|
@@ -636,7 +658,7 @@ function drawSpans(spans, ctx, availableWidth, x0) {
|
|
|
636
658
|
});
|
|
637
659
|
const textWidth = span.font.widthOfTextAtSize(span.text, span.fontSize);
|
|
638
660
|
if (span.link) {
|
|
639
|
-
const baseline =
|
|
661
|
+
const baseline = spanBaseline;
|
|
640
662
|
ctx.page.drawLine({
|
|
641
663
|
start: { x, y: baseline - 1 },
|
|
642
664
|
end: { x: x + textWidth, y: baseline - 1 },
|
|
@@ -659,7 +681,7 @@ function drawSpans(spans, ctx, availableWidth, x0) {
|
|
|
659
681
|
ctx.page.node.addAnnot(annotation);
|
|
660
682
|
}
|
|
661
683
|
if (span.strikethrough) {
|
|
662
|
-
const midY =
|
|
684
|
+
const midY = spanBaseline + span.fontSize * 0.4;
|
|
663
685
|
ctx.page.drawLine({
|
|
664
686
|
start: { x, y: midY },
|
|
665
687
|
end: { x: x + textWidth, y: midY },
|
|
@@ -1022,7 +1044,8 @@ function renderMathBlock(node, ctx, extraIndent) {
|
|
|
1022
1044
|
ctx.y -= PARAGRAPH_SPACING;
|
|
1023
1045
|
}
|
|
1024
1046
|
function renderFootnoteDefinition(node, ctx, extraIndent) {
|
|
1025
|
-
const
|
|
1047
|
+
const number = ctx.footnotes?.numberFor(node.identifier);
|
|
1048
|
+
const label = ctx.text.clean(number !== void 0 ? `${number}.` : `[${node.identifier}]`);
|
|
1026
1049
|
const lineH = ctx.fontSize * LINE_HEIGHT_FACTOR;
|
|
1027
1050
|
ensureSpace(ctx, lineH);
|
|
1028
1051
|
ctx.page.drawText(label, {
|