@ai-matrx/print 0.8.2 → 0.8.4
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/CHANGELOG.md +30 -0
- package/dist/document.cjs +235 -36
- package/dist/document.cjs.map +1 -1
- package/dist/document.d.cts +11 -1
- package/dist/document.d.ts +11 -1
- package/dist/document.js +235 -36
- package/dist/document.js.map +1 -1
- package/dist/markdown.cjs +40 -1
- package/dist/markdown.cjs.map +1 -1
- package/dist/markdown.d.cts +9 -1
- package/dist/markdown.d.ts +9 -1
- package/dist/markdown.js +40 -1
- package/dist/markdown.js.map +1 -1
- package/package.json +2 -1
package/dist/document.d.cts
CHANGED
|
@@ -322,14 +322,22 @@ interface FallbackFontLoaders {
|
|
|
322
322
|
emoji?: FontLoader | undefined;
|
|
323
323
|
symbols?: FontLoader | undefined;
|
|
324
324
|
cjk?: CjkFontLoader | undefined;
|
|
325
|
+
arabic?: FontLoader | undefined;
|
|
326
|
+
hebrew?: FontLoader | undefined;
|
|
325
327
|
}
|
|
326
328
|
/** Noto Sans JP (SIL OFL) from fontsource: 124 unicode-range subsets; only the needed ones load. */
|
|
327
329
|
declare const NOTO_CJK_BASE = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-jp@5.3.0";
|
|
330
|
+
/** Noto Sans KR (SIL OFL) — Hangul, same unicode-range scheme. */
|
|
331
|
+
declare const NOTO_KR_BASE = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-kr@5.3.0";
|
|
332
|
+
/** Noto Sans Arabic / Hebrew (SIL OFL) — one script subset each. */
|
|
333
|
+
declare const NOTO_ARABIC_URL = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-arabic@5.3.0/files/noto-sans-arabic-arabic-400-normal.woff";
|
|
334
|
+
declare const NOTO_HEBREW_URL = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-hebrew@5.3.0/files/noto-sans-hebrew-hebrew-400-normal.woff";
|
|
328
335
|
/** Parse a fontsource stylesheet: each face's file stem and its unicode ranges. */
|
|
329
336
|
declare function parseUnicodeRangeCss(css: string): Array<{
|
|
330
337
|
file: string;
|
|
331
338
|
ranges: Array<[number, number]>;
|
|
332
339
|
}>;
|
|
340
|
+
/** Hangul from Noto Sans KR, every other CJK character from Noto Sans JP. */
|
|
333
341
|
declare const defaultCjkFontLoader: CjkFontLoader;
|
|
334
342
|
/**
|
|
335
343
|
* Words pdfmake did not put on a page: a text node with no position was never
|
|
@@ -416,6 +424,8 @@ declare class CitationEngine {
|
|
|
416
424
|
/** A note style: every citation becomes a numbered note. */
|
|
417
425
|
readonly isNote: boolean;
|
|
418
426
|
private readonly numeric;
|
|
427
|
+
/** APA 7 §8.11: narrative citations join authors with "and", not "&". */
|
|
428
|
+
private readonly apaNarrativeAnd;
|
|
419
429
|
constructor(styleXml: string, localeXml: string, items: CslItem[], lang: string);
|
|
420
430
|
/** Cite a group; `noteIndex` is the note number for note styles (0 = in text). */
|
|
421
431
|
cite(refs: CiteRef[], opts?: {
|
|
@@ -671,4 +681,4 @@ declare function exportDocument(source: string, format: DocumentFormat, options?
|
|
|
671
681
|
/** Browser helper: save an export through a download link. */
|
|
672
682
|
declare function downloadDocumentExport(exp: DocumentExport): void;
|
|
673
683
|
|
|
674
|
-
export { BUNDLED_STYLE_IDS, type BibliographyEntry, type Block, CSL_STYLE_ALIASES, type Caption, CitationEngine, type CitationStyle, type CjkFontLoader, type CslFetcher, type CslItem, type CslName, DEFAULT_FOOTER, DOCUMENT_FORMATS, type DocSection, type DocTree, type DocumentExport, type DocumentFormat, type DocumentNotice, type DocumentSettings, type ExportDocumentOptions, type FallbackFontLoaders, type FontLoader, type ImageLoader, type ImageMap, type Inline, type ListItem, type Margins, type MathSpan, NOTO_CJK_BASE, NOTO_EMOJI_URL, NOTO_SYMBOLS_URL, type Orientation, PAGE_SIZES, type PageSizeName, type ParseDocumentOptions, type PrepareDocumentOptions, type ResolvedImage, type RunningTemplate, type TocEntry, type TypesetMath, buildPdfDefinition, countUnplacedWords, cslStyleId, defaultCjkFontLoader, defaultCslFetcher, defaultEmojiFontLoader, defaultImageLoader, defaultSymbolFontLoader, documentCss, documentToDocx, documentToEpub, documentToHtml, documentToPdf, downloadDocumentExport, exportDocument, extractMath, fillTemplate, loadCslLocale, loadCslStyle, mathKey, normalizeReference, parseCitationGroup, parseDocument, parseLength, parseLocator, parseMathML, parseUnicodeRangeCss, plainText, prepareDocument, renderBlocksHtml, renderPdfDefinition, resolveDocumentImages, resolveSettings, scanCitationKeys, splitFrontmatter, typesetDocumentMath };
|
|
684
|
+
export { BUNDLED_STYLE_IDS, type BibliographyEntry, type Block, CSL_STYLE_ALIASES, type Caption, CitationEngine, type CitationStyle, type CjkFontLoader, type CslFetcher, type CslItem, type CslName, DEFAULT_FOOTER, DOCUMENT_FORMATS, type DocSection, type DocTree, type DocumentExport, type DocumentFormat, type DocumentNotice, type DocumentSettings, type ExportDocumentOptions, type FallbackFontLoaders, type FontLoader, type ImageLoader, type ImageMap, type Inline, type ListItem, type Margins, type MathSpan, NOTO_ARABIC_URL, NOTO_CJK_BASE, NOTO_EMOJI_URL, NOTO_HEBREW_URL, NOTO_KR_BASE, NOTO_SYMBOLS_URL, type Orientation, PAGE_SIZES, type PageSizeName, type ParseDocumentOptions, type PrepareDocumentOptions, type ResolvedImage, type RunningTemplate, type TocEntry, type TypesetMath, buildPdfDefinition, countUnplacedWords, cslStyleId, defaultCjkFontLoader, defaultCslFetcher, defaultEmojiFontLoader, defaultImageLoader, defaultSymbolFontLoader, documentCss, documentToDocx, documentToEpub, documentToHtml, documentToPdf, downloadDocumentExport, exportDocument, extractMath, fillTemplate, loadCslLocale, loadCslStyle, mathKey, normalizeReference, parseCitationGroup, parseDocument, parseLength, parseLocator, parseMathML, parseUnicodeRangeCss, plainText, prepareDocument, renderBlocksHtml, renderPdfDefinition, resolveDocumentImages, resolveSettings, scanCitationKeys, splitFrontmatter, typesetDocumentMath };
|
package/dist/document.d.ts
CHANGED
|
@@ -322,14 +322,22 @@ interface FallbackFontLoaders {
|
|
|
322
322
|
emoji?: FontLoader | undefined;
|
|
323
323
|
symbols?: FontLoader | undefined;
|
|
324
324
|
cjk?: CjkFontLoader | undefined;
|
|
325
|
+
arabic?: FontLoader | undefined;
|
|
326
|
+
hebrew?: FontLoader | undefined;
|
|
325
327
|
}
|
|
326
328
|
/** Noto Sans JP (SIL OFL) from fontsource: 124 unicode-range subsets; only the needed ones load. */
|
|
327
329
|
declare const NOTO_CJK_BASE = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-jp@5.3.0";
|
|
330
|
+
/** Noto Sans KR (SIL OFL) — Hangul, same unicode-range scheme. */
|
|
331
|
+
declare const NOTO_KR_BASE = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-kr@5.3.0";
|
|
332
|
+
/** Noto Sans Arabic / Hebrew (SIL OFL) — one script subset each. */
|
|
333
|
+
declare const NOTO_ARABIC_URL = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-arabic@5.3.0/files/noto-sans-arabic-arabic-400-normal.woff";
|
|
334
|
+
declare const NOTO_HEBREW_URL = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-hebrew@5.3.0/files/noto-sans-hebrew-hebrew-400-normal.woff";
|
|
328
335
|
/** Parse a fontsource stylesheet: each face's file stem and its unicode ranges. */
|
|
329
336
|
declare function parseUnicodeRangeCss(css: string): Array<{
|
|
330
337
|
file: string;
|
|
331
338
|
ranges: Array<[number, number]>;
|
|
332
339
|
}>;
|
|
340
|
+
/** Hangul from Noto Sans KR, every other CJK character from Noto Sans JP. */
|
|
333
341
|
declare const defaultCjkFontLoader: CjkFontLoader;
|
|
334
342
|
/**
|
|
335
343
|
* Words pdfmake did not put on a page: a text node with no position was never
|
|
@@ -416,6 +424,8 @@ declare class CitationEngine {
|
|
|
416
424
|
/** A note style: every citation becomes a numbered note. */
|
|
417
425
|
readonly isNote: boolean;
|
|
418
426
|
private readonly numeric;
|
|
427
|
+
/** APA 7 §8.11: narrative citations join authors with "and", not "&". */
|
|
428
|
+
private readonly apaNarrativeAnd;
|
|
419
429
|
constructor(styleXml: string, localeXml: string, items: CslItem[], lang: string);
|
|
420
430
|
/** Cite a group; `noteIndex` is the note number for note styles (0 = in text). */
|
|
421
431
|
cite(refs: CiteRef[], opts?: {
|
|
@@ -671,4 +681,4 @@ declare function exportDocument(source: string, format: DocumentFormat, options?
|
|
|
671
681
|
/** Browser helper: save an export through a download link. */
|
|
672
682
|
declare function downloadDocumentExport(exp: DocumentExport): void;
|
|
673
683
|
|
|
674
|
-
export { BUNDLED_STYLE_IDS, type BibliographyEntry, type Block, CSL_STYLE_ALIASES, type Caption, CitationEngine, type CitationStyle, type CjkFontLoader, type CslFetcher, type CslItem, type CslName, DEFAULT_FOOTER, DOCUMENT_FORMATS, type DocSection, type DocTree, type DocumentExport, type DocumentFormat, type DocumentNotice, type DocumentSettings, type ExportDocumentOptions, type FallbackFontLoaders, type FontLoader, type ImageLoader, type ImageMap, type Inline, type ListItem, type Margins, type MathSpan, NOTO_CJK_BASE, NOTO_EMOJI_URL, NOTO_SYMBOLS_URL, type Orientation, PAGE_SIZES, type PageSizeName, type ParseDocumentOptions, type PrepareDocumentOptions, type ResolvedImage, type RunningTemplate, type TocEntry, type TypesetMath, buildPdfDefinition, countUnplacedWords, cslStyleId, defaultCjkFontLoader, defaultCslFetcher, defaultEmojiFontLoader, defaultImageLoader, defaultSymbolFontLoader, documentCss, documentToDocx, documentToEpub, documentToHtml, documentToPdf, downloadDocumentExport, exportDocument, extractMath, fillTemplate, loadCslLocale, loadCslStyle, mathKey, normalizeReference, parseCitationGroup, parseDocument, parseLength, parseLocator, parseMathML, parseUnicodeRangeCss, plainText, prepareDocument, renderBlocksHtml, renderPdfDefinition, resolveDocumentImages, resolveSettings, scanCitationKeys, splitFrontmatter, typesetDocumentMath };
|
|
684
|
+
export { BUNDLED_STYLE_IDS, type BibliographyEntry, type Block, CSL_STYLE_ALIASES, type Caption, CitationEngine, type CitationStyle, type CjkFontLoader, type CslFetcher, type CslItem, type CslName, DEFAULT_FOOTER, DOCUMENT_FORMATS, type DocSection, type DocTree, type DocumentExport, type DocumentFormat, type DocumentNotice, type DocumentSettings, type ExportDocumentOptions, type FallbackFontLoaders, type FontLoader, type ImageLoader, type ImageMap, type Inline, type ListItem, type Margins, type MathSpan, NOTO_ARABIC_URL, NOTO_CJK_BASE, NOTO_EMOJI_URL, NOTO_HEBREW_URL, NOTO_KR_BASE, NOTO_SYMBOLS_URL, type Orientation, PAGE_SIZES, type PageSizeName, type ParseDocumentOptions, type PrepareDocumentOptions, type ResolvedImage, type RunningTemplate, type TocEntry, type TypesetMath, buildPdfDefinition, countUnplacedWords, cslStyleId, defaultCjkFontLoader, defaultCslFetcher, defaultEmojiFontLoader, defaultImageLoader, defaultSymbolFontLoader, documentCss, documentToDocx, documentToEpub, documentToHtml, documentToPdf, downloadDocumentExport, exportDocument, extractMath, fillTemplate, loadCslLocale, loadCslStyle, mathKey, normalizeReference, parseCitationGroup, parseDocument, parseLength, parseLocator, parseMathML, parseUnicodeRangeCss, plainText, prepareDocument, renderBlocksHtml, renderPdfDefinition, resolveDocumentImages, resolveSettings, scanCitationKeys, splitFrontmatter, typesetDocumentMath };
|
package/dist/document.js
CHANGED
|
@@ -1,3 +1,68 @@
|
|
|
1
|
+
// src/document/bidi.ts
|
|
2
|
+
import bidiFactory from "bidi-js";
|
|
3
|
+
var BIDI = /* @__PURE__ */ Symbol.for("ai-matrx.print.document.bidi");
|
|
4
|
+
function bidi() {
|
|
5
|
+
const g = globalThis;
|
|
6
|
+
if (!g[BIDI]) g[BIDI] = bidiFactory();
|
|
7
|
+
return g[BIDI];
|
|
8
|
+
}
|
|
9
|
+
var RTL_RE = /[-ࣿיִ-﷿ﹰ-]/;
|
|
10
|
+
function isRtlParagraph(text) {
|
|
11
|
+
if (!RTL_RE.test(text)) return false;
|
|
12
|
+
return (bidi().getEmbeddingLevels(text).paragraphs[0]?.level ?? 0) % 2 === 1;
|
|
13
|
+
}
|
|
14
|
+
function visualOrder(input, rtl) {
|
|
15
|
+
const b = bidi();
|
|
16
|
+
const runs3 = input.map((r) => typeof r === "string" ? { text: r } : { ...r, text: String(r.text ?? "") });
|
|
17
|
+
const lines = [[]];
|
|
18
|
+
runs3.forEach((r, i) => {
|
|
19
|
+
for (const m of r.text.matchAll(/\n|[^\S\n]+|[\u0590-\u08FF\uFB1D-\uFDFF\uFE70-\uFEFF]+|[^\s\u0590-\u08FF\uFB1D-\uFDFF\uFE70-\uFEFF]+/g)) {
|
|
20
|
+
if (m[0] === "\n") lines.push([]);
|
|
21
|
+
else lines[lines.length - 1].push({ run: i, text: m[0] });
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
const out = [];
|
|
25
|
+
lines.forEach((line, li) => {
|
|
26
|
+
if (li > 0) out.push({ ...runs3[0], text: "\n" });
|
|
27
|
+
const text = line.map((t) => t.text).join("");
|
|
28
|
+
if (!text) return;
|
|
29
|
+
const { levels } = b.getEmbeddingLevels(text, rtl ? "rtl" : "ltr");
|
|
30
|
+
let at = 0;
|
|
31
|
+
const tokens = line.map((t) => {
|
|
32
|
+
const level = levels[at] ?? 0;
|
|
33
|
+
let s = t.text;
|
|
34
|
+
if (level % 2 === 1) {
|
|
35
|
+
s = [...s].map((ch) => b.getMirroredCharacter(ch) ?? ch).join("");
|
|
36
|
+
if (!RTL_RE.test(s) && !/^\d/.test(s)) s = [...s].reverse().join("");
|
|
37
|
+
}
|
|
38
|
+
at += t.text.length;
|
|
39
|
+
return { ...t, text: s, level };
|
|
40
|
+
});
|
|
41
|
+
const max = Math.max(...tokens.map((t) => t.level));
|
|
42
|
+
const minOdd = Math.min(...tokens.map((t) => t.level % 2 === 1 ? t.level : Infinity));
|
|
43
|
+
for (let lvl = max; lvl >= minOdd && Number.isFinite(minOdd); lvl--) {
|
|
44
|
+
for (let i = 0; i < tokens.length; ) {
|
|
45
|
+
if (tokens[i].level < lvl) {
|
|
46
|
+
i++;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
let j = i;
|
|
50
|
+
while (j < tokens.length && tokens[j].level >= lvl) j++;
|
|
51
|
+
tokens.splice(i, j - i, ...tokens.slice(i, j).reverse());
|
|
52
|
+
i = j;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
for (const t of tokens) {
|
|
56
|
+
const last = out[out.length - 1];
|
|
57
|
+
const src = runs3[t.run];
|
|
58
|
+
if (last && last.text !== "\n" && last.__run === t.run) last.text += t.text;
|
|
59
|
+
else out.push({ ...src, text: t.text, __run: t.run });
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
for (const r of out) delete r.__run;
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
|
|
1
66
|
// src/document/math.ts
|
|
2
67
|
import { pairDisplayMath, singleDollarMathEnd } from "@ai-matrx/content-ir/source";
|
|
3
68
|
var MATH_OPEN = "\uE100";
|
|
@@ -2045,6 +2110,8 @@ var CitationEngine = class {
|
|
|
2045
2110
|
/** A note style: every citation becomes a numbered note. */
|
|
2046
2111
|
isNote;
|
|
2047
2112
|
numeric;
|
|
2113
|
+
/** APA 7 §8.11: narrative citations join authors with "and", not "&". */
|
|
2114
|
+
apaNarrativeAnd;
|
|
2048
2115
|
constructor(styleXml, localeXml, items, lang) {
|
|
2049
2116
|
const byId = new Map(items.map((i) => [i.id, toCslJson(i)]));
|
|
2050
2117
|
const sys = {
|
|
@@ -2055,6 +2122,7 @@ var CitationEngine = class {
|
|
|
2055
2122
|
this.engine = new Engine(sys, styleXml, cslLocaleTag(lang), true);
|
|
2056
2123
|
this.engine.setOutputFormat("html");
|
|
2057
2124
|
this.isNote = this.engine.opt.xclass === "note";
|
|
2125
|
+
this.apaNarrativeAnd = /<title>[^<]*(?:\bAPA\b|American Psychological Association)/i.test(styleXml) || /<link[^>]*href="[^"]*\/styles\/apa(?:-[\w-]*)?"[^>]*rel="(?:self|template|independent-parent)"/i.test(styleXml);
|
|
2058
2126
|
this.numeric = /citation-number/.test(styleXml.slice(styleXml.indexOf("<citation"), styleXml.indexOf("</citation>")));
|
|
2059
2127
|
}
|
|
2060
2128
|
/** Cite a group; `noteIndex` is the note number for note styles (0 = in text). */
|
|
@@ -2091,7 +2159,8 @@ var CitationEngine = class {
|
|
|
2091
2159
|
if (opts.narrative && refs.length === 1 && !this.isNote && !this.numeric) {
|
|
2092
2160
|
const author = make(items);
|
|
2093
2161
|
const rest = make(refs.map((r, i) => ({ ...items[i], "author-only": void 0, "suppress-author": true, id: r.key })));
|
|
2094
|
-
const
|
|
2162
|
+
const who = () => this.apaNarrativeAnd ? author.label.replace(/,? & /g, (m) => m.startsWith(",") ? ", and " : " and ") : author.label;
|
|
2163
|
+
const join = () => `${who()}${rest.label ? ` ${/^\(/.test(rest.label) ? rest.label : `(${rest.label})`}` : ""}`;
|
|
2095
2164
|
const listeners = [];
|
|
2096
2165
|
author.listeners.push(() => listeners.forEach((l) => l(join())));
|
|
2097
2166
|
rest.listeners.push(() => listeners.forEach((l) => l(join())));
|
|
@@ -2570,7 +2639,7 @@ function runs(nodes, c, st = {}) {
|
|
|
2570
2639
|
for (const n of nodes) {
|
|
2571
2640
|
switch (n.type) {
|
|
2572
2641
|
case "text":
|
|
2573
|
-
out.push(tr(n.value));
|
|
2642
|
+
out.push(RTL_RE.test(n.value) ? tr(n.value, { rightToLeft: true }) : tr(n.value));
|
|
2574
2643
|
break;
|
|
2575
2644
|
case "strong":
|
|
2576
2645
|
out.push(...runs(n.children, c, { ...st, bold: true }));
|
|
@@ -2722,6 +2791,10 @@ function numberingRef(c, ordered, start) {
|
|
|
2722
2791
|
}
|
|
2723
2792
|
return ref;
|
|
2724
2793
|
}
|
|
2794
|
+
function bidiPara(nodes) {
|
|
2795
|
+
const text = nodes.map((n) => "value" in n ? n.value : "").join("");
|
|
2796
|
+
return isRtlParagraph(text) ? { bidirectional: true } : {};
|
|
2797
|
+
}
|
|
2725
2798
|
function blocks(list, c, o = {}) {
|
|
2726
2799
|
const out = [];
|
|
2727
2800
|
for (const b of list) out.push(...block(b, c, o));
|
|
@@ -2746,6 +2819,7 @@ function block(b, c, o) {
|
|
|
2746
2819
|
const kids = [...b.number ? [new d.TextRun({ text: `${b.number} `, color: HEX.muted })] : [], ...runs(b.children, c)];
|
|
2747
2820
|
return [
|
|
2748
2821
|
new d.Paragraph({
|
|
2822
|
+
...bidiPara(b.children),
|
|
2749
2823
|
heading,
|
|
2750
2824
|
keepNext: true,
|
|
2751
2825
|
keepLines: true,
|
|
@@ -2755,7 +2829,7 @@ function block(b, c, o) {
|
|
|
2755
2829
|
];
|
|
2756
2830
|
}
|
|
2757
2831
|
case "paragraph":
|
|
2758
|
-
return [para(c, o, { children: runs(b.children, c), widowControl: true })];
|
|
2832
|
+
return [para(c, o, { children: runs(b.children, c), widowControl: true, ...bidiPara(b.children) })];
|
|
2759
2833
|
case "list": {
|
|
2760
2834
|
const level = o.listLevel ?? 0;
|
|
2761
2835
|
const ref = numberingRef(c, b.ordered, b.start);
|
|
@@ -2772,7 +2846,14 @@ function block(b, c, o) {
|
|
|
2772
2846
|
})
|
|
2773
2847
|
);
|
|
2774
2848
|
} else {
|
|
2775
|
-
out.push(
|
|
2849
|
+
out.push(
|
|
2850
|
+
new d.Paragraph({
|
|
2851
|
+
numbering: { reference: ref, level, instance },
|
|
2852
|
+
children: firstRuns,
|
|
2853
|
+
widowControl: true,
|
|
2854
|
+
...first?.type === "paragraph" ? bidiPara(first.children) : {}
|
|
2855
|
+
})
|
|
2856
|
+
);
|
|
2776
2857
|
}
|
|
2777
2858
|
const tail = first && first.type !== "paragraph" ? [first, ...rest] : rest;
|
|
2778
2859
|
for (const child of tail) {
|
|
@@ -2814,6 +2895,7 @@ function block(b, c, o) {
|
|
|
2814
2895
|
borders: { top: none, left: none, right: none, bottom: head ? { ...border, size: 10, color: HEX.heading } : border },
|
|
2815
2896
|
children: [
|
|
2816
2897
|
new d.Paragraph({
|
|
2898
|
+
...bidiPara(cells[i] ?? []),
|
|
2817
2899
|
alignment: alignOf(i),
|
|
2818
2900
|
keepNext: keep && !last,
|
|
2819
2901
|
children: runs(cells[i] ?? [], c, head ? { bold: true } : {})
|
|
@@ -3027,7 +3109,11 @@ async function documentToDocx(tree, options = {}) {
|
|
|
3027
3109
|
numbering: { config: [...c.numbering.entries()].map(([reference, levels2]) => ({ reference, levels: levels2 })) },
|
|
3028
3110
|
styles: {
|
|
3029
3111
|
default: {
|
|
3030
|
-
|
|
3112
|
+
// East-Asian text gets a CJK face (Word and LibreOffice substitute an installed one).
|
|
3113
|
+
document: {
|
|
3114
|
+
run: { font: { ascii: "Cambria", hAnsi: "Cambria", cs: "Arial", eastAsia: "Yu Gothic" }, size, sizeComplexScript: size, color: HEX.text },
|
|
3115
|
+
paragraph: { spacing: { after: 140, line: 300 } }
|
|
3116
|
+
}
|
|
3031
3117
|
},
|
|
3032
3118
|
paragraphStyles: [
|
|
3033
3119
|
heading("Heading1", "Heading 1", s.fontSize * 1.8, 0),
|
|
@@ -3170,7 +3256,7 @@ function renderInlineHtml(nodes, x) {
|
|
|
3170
3256
|
case "cite":
|
|
3171
3257
|
return n.resolved ? `<a class="doc-cite" href="${escapeHtml(href("ref-" + (n.keys[0] ?? ""), x))}">${escapeHtml(n.label)}</a>` : `<span class="doc-cite doc-cite-missing">${escapeHtml(n.label)}</span>`;
|
|
3172
3258
|
case "math":
|
|
3173
|
-
return mathHtml(n.tex, n.display, x);
|
|
3259
|
+
return mathHtml(n.tex, n.display, x, true);
|
|
3174
3260
|
case "noteRef":
|
|
3175
3261
|
return `<sup class="doc-note-ref"><a href="${escapeHtml(href(`note-${n.n}`, x))}" id="noteref-${n.n}">${n.n}</a></sup>`;
|
|
3176
3262
|
default:
|
|
@@ -3178,10 +3264,11 @@ function renderInlineHtml(nodes, x) {
|
|
|
3178
3264
|
}
|
|
3179
3265
|
}).join("");
|
|
3180
3266
|
}
|
|
3181
|
-
function mathHtml(tex, display, x) {
|
|
3267
|
+
function mathHtml(tex, display, x, inText = false) {
|
|
3182
3268
|
const m = x.tree.math.get(mathKey(tex, display));
|
|
3183
3269
|
if (m?.mathml && !m.error) {
|
|
3184
|
-
|
|
3270
|
+
if (!display) return `<span class="doc-math">${m.mathml}</span>`;
|
|
3271
|
+
return inText ? `<span class="doc-math-display">${m.mathml}</span>` : `<div class="doc-math-display">${m.mathml}</div>`;
|
|
3185
3272
|
}
|
|
3186
3273
|
return `<code class="doc-math-tex">${escapeHtml(display ? `$$${tex}$$` : `$${tex}$`)}</code>`;
|
|
3187
3274
|
}
|
|
@@ -3212,10 +3299,10 @@ function renderBlockHtml(b, x) {
|
|
|
3212
3299
|
switch (b.type) {
|
|
3213
3300
|
case "heading": {
|
|
3214
3301
|
const num = b.number ? `<span class="doc-heading-number">${b.number}</span> ` : "";
|
|
3215
|
-
return `<h${b.level} id="${escapeHtml(b.id)}">${num}${renderInlineHtml(b.children, x)}</h${b.level}>`;
|
|
3302
|
+
return `<h${b.level} id="${escapeHtml(b.id)}" dir="auto">${num}${renderInlineHtml(b.children, x)}</h${b.level}>`;
|
|
3216
3303
|
}
|
|
3217
3304
|
case "paragraph":
|
|
3218
|
-
return `<p>${renderInlineHtml(b.children, x)}</p>`;
|
|
3305
|
+
return `<p dir="auto">${renderInlineHtml(b.children, x)}</p>`;
|
|
3219
3306
|
case "list": {
|
|
3220
3307
|
const tag = b.ordered ? "ol" : "ul";
|
|
3221
3308
|
const start = b.ordered && b.start !== 1 ? ` start="${b.start}"` : "";
|
|
@@ -3223,12 +3310,12 @@ function renderBlockHtml(b, x) {
|
|
|
3223
3310
|
const items = b.items.map((i) => {
|
|
3224
3311
|
const box = i.checked === null ? "" : `<span class="doc-task-box${i.checked ? " is-checked" : ""}" role="img" aria-label="${i.checked ? "done" : "not done"}"></span>`;
|
|
3225
3312
|
const inner = i.children.length === 1 && i.children[0]?.type === "paragraph" ? renderInlineHtml(i.children[0].children, x) : renderBlocksHtml(i.children, x);
|
|
3226
|
-
return `<li${i.checked !== null ? ' class="doc-task"' : ""}>${box}${inner}</li>`;
|
|
3313
|
+
return `<li dir="auto"${i.checked !== null ? ' class="doc-task"' : ""}>${box}${inner}</li>`;
|
|
3227
3314
|
}).join("");
|
|
3228
3315
|
return `<${tag}${start}${task ? ' class="doc-task-list"' : ""}>${items}</${tag}>`;
|
|
3229
3316
|
}
|
|
3230
3317
|
case "blockquote":
|
|
3231
|
-
return `<blockquote>${renderBlocksHtml(b.children, x)}</blockquote>`;
|
|
3318
|
+
return `<blockquote dir="auto">${renderBlocksHtml(b.children, x)}</blockquote>`;
|
|
3232
3319
|
case "code":
|
|
3233
3320
|
return `<pre class="doc-code"${b.lang ? ` data-language="${escapeHtml(b.lang)}"` : ""}><code>${escapeHtml(
|
|
3234
3321
|
b.value
|
|
@@ -3238,8 +3325,8 @@ function renderBlockHtml(b, x) {
|
|
|
3238
3325
|
const a = b.align[i];
|
|
3239
3326
|
return a ? ` style="text-align:${a}"` : "";
|
|
3240
3327
|
};
|
|
3241
|
-
const head = `<thead><tr>${b.header.map((c, i) => `<th${al(i)}>${renderInlineHtml(c, x)}</th>`).join("")}</tr></thead>`;
|
|
3242
|
-
const body = `<tbody>${b.rows.map((r) => `<tr>${r.map((c, i) => `<td${al(i)}>${renderInlineHtml(c, x)}</td>`).join("")}</tr>`).join("")}</tbody>`;
|
|
3328
|
+
const head = `<thead><tr>${b.header.map((c, i) => `<th dir="auto"${al(i)}>${renderInlineHtml(c, x)}</th>`).join("")}</tr></thead>`;
|
|
3329
|
+
const body = `<tbody>${b.rows.map((r) => `<tr>${r.map((c, i) => `<td dir="auto"${al(i)}>${renderInlineHtml(c, x)}</td>`).join("")}</tr>`).join("")}</tbody>`;
|
|
3243
3330
|
const id = b.caption?.id ? ` id="tbl:${escapeHtml(b.caption.id)}"` : "";
|
|
3244
3331
|
return `<div class="doc-table-wrap"${id}><table>${b.caption ? captionHtml(b.caption, "caption", x) : ""}${head}${body}</table></div>`;
|
|
3245
3332
|
}
|
|
@@ -3343,7 +3430,7 @@ table caption{caption-side:top;text-align:left;margin-bottom:4pt;}
|
|
|
3343
3430
|
.doc-figure figcaption{margin-top:4pt;}
|
|
3344
3431
|
.doc-image-missing{border:1px dashed var(--matrx-print-rule,#d1d5db);padding:16pt;color:var(--matrx-print-muted,#6b7280);}
|
|
3345
3432
|
.doc-inline-img{max-height:1.2em;vertical-align:middle;}
|
|
3346
|
-
.doc-math-display{margin:0.6em 0 0.9em;text-align:center;break-inside:avoid;overflow-x:auto;}
|
|
3433
|
+
.doc-math-display{display:block;margin:0.6em 0 0.9em;text-align:center;break-inside:avoid;overflow-x:auto;}
|
|
3347
3434
|
.doc-math-display math{font-size:1.15em;}
|
|
3348
3435
|
.doc-math-tex{color:var(--matrx-print-danger,#b91c1c);}
|
|
3349
3436
|
.doc-task-list{list-style:none;padding-left:0.2em;}
|
|
@@ -3422,7 +3509,7 @@ blockquote{margin:0 0 0.8em;padding-left:1em;border-left:3px solid #d1d5db;color
|
|
|
3422
3509
|
.doc-cover-logo{max-height:5em;}
|
|
3423
3510
|
.doc-cover-title{font-size:2em;}
|
|
3424
3511
|
.matrx-page-break{page-break-after:always;break-after:page;}
|
|
3425
|
-
.doc-math-display{margin:0.6em 0;text-align:center;}
|
|
3512
|
+
.doc-math-display{display:block;margin:0.6em 0;text-align:center;}
|
|
3426
3513
|
`.trim();
|
|
3427
3514
|
function extOf(mime) {
|
|
3428
3515
|
return mime === "image/png" ? "png" : mime === "image/gif" ? "gif" : "jpg";
|
|
@@ -3709,7 +3796,7 @@ function breakRuns(input, width, t) {
|
|
|
3709
3796
|
for (let guard = 0; guard < 50 && atoms.length; guard++) {
|
|
3710
3797
|
const gw = atoms.reduce((n, a) => n + w(a), 0);
|
|
3711
3798
|
const sw = pendingSpace && line > 0 ? w(pendingSpace) : 0;
|
|
3712
|
-
if (line + sw + gw <= W || line === 0 && atoms.length !== 1) {
|
|
3799
|
+
if (line + sw + gw <= W || line === 0 && (atoms.length !== 1 || gw <= width)) {
|
|
3713
3800
|
if (sw && pendingSpace) out.push(pendingSpace);
|
|
3714
3801
|
out.push(...atoms);
|
|
3715
3802
|
line += sw + gw;
|
|
@@ -3833,11 +3920,14 @@ function withFallbackFonts(nodes, c) {
|
|
|
3833
3920
|
const fb = fallbackFor(cp);
|
|
3834
3921
|
if (fb) {
|
|
3835
3922
|
flush();
|
|
3836
|
-
|
|
3923
|
+
const prev = out[out.length - 1];
|
|
3924
|
+
if (prev && typeof prev === "object" && prev.font === fb && prev.__fb) prev.text = `${prev.text}${ch}`;
|
|
3925
|
+
else out.push({ ...n, text: ch, font: fb, bold: false, italics: false, __fb: true });
|
|
3837
3926
|
} else dropped = true;
|
|
3838
3927
|
}
|
|
3839
3928
|
flush();
|
|
3840
3929
|
}
|
|
3930
|
+
for (const o of out) if (typeof o === "object") delete o.__fb;
|
|
3841
3931
|
if (dropped && !c.glyphs.announced) {
|
|
3842
3932
|
c.glyphs.announced = true;
|
|
3843
3933
|
c.tree.notices.push({
|
|
@@ -3864,8 +3954,13 @@ function drawable(text, c) {
|
|
|
3864
3954
|
}
|
|
3865
3955
|
function bodyRuns(nodes, c, width = c.width) {
|
|
3866
3956
|
const r = runs2(nodes, c);
|
|
3867
|
-
|
|
3957
|
+
const text = plain(nodes);
|
|
3958
|
+
if (!RTL_RE.test(text)) return c.typo ? breakRuns(r, width, c.typo) : r;
|
|
3959
|
+
const typo = c.typo ?? (c.faces ? { split: (w) => [w], measure: c.faces.measure } : void 0);
|
|
3960
|
+
const lines = typo ? breakRuns(r, width, typo) : r;
|
|
3961
|
+
return visualOrder(lines, isRtlParagraph(text));
|
|
3868
3962
|
}
|
|
3963
|
+
var alignFor = (nodes) => isRtlParagraph(plain(nodes)) ? { alignment: "right" } : {};
|
|
3869
3964
|
function runs2(nodes, c, st = {}) {
|
|
3870
3965
|
const out = [];
|
|
3871
3966
|
const base = (raw) => {
|
|
@@ -4057,6 +4152,7 @@ function estimateLines(text, c, size = c.s.fontSize) {
|
|
|
4057
4152
|
function columnWidths(b, n, size, c) {
|
|
4058
4153
|
const measure = (text, bold) => c.faces ? c.faces.measure(text, { bold, fontSize: size }) : text.length * size * 0.5;
|
|
4059
4154
|
const pad = 10;
|
|
4155
|
+
const slack = 1;
|
|
4060
4156
|
const min = new Array(n).fill(0);
|
|
4061
4157
|
const max = new Array(n).fill(0);
|
|
4062
4158
|
const rows = [[b.header, true], ...b.rows.map((r) => [r, false])];
|
|
@@ -4064,8 +4160,8 @@ function columnWidths(b, n, size, c) {
|
|
|
4064
4160
|
for (let i = 0; i < n; i++) {
|
|
4065
4161
|
const t = plain(r[i] ?? []);
|
|
4066
4162
|
const longest = t.split(/\s+/).reduce((m, w) => Math.max(m, measure(w, head)), 0);
|
|
4067
|
-
min[i] = Math.max(min[i], Math.min(longest, c.width * 0.35) + pad);
|
|
4068
|
-
max[i] = Math.max(max[i], measure(t, head) + pad);
|
|
4163
|
+
min[i] = Math.max(min[i], Math.min(longest * slack, c.width * 0.35) + pad);
|
|
4164
|
+
max[i] = Math.max(max[i], measure(t, head) * slack + pad);
|
|
4069
4165
|
}
|
|
4070
4166
|
}
|
|
4071
4167
|
const avail = c.width;
|
|
@@ -4129,8 +4225,11 @@ function block2(b, c) {
|
|
|
4129
4225
|
switch (b.type) {
|
|
4130
4226
|
case "heading": {
|
|
4131
4227
|
const rel = Math.min(6, b.level - c.minLevel + 1);
|
|
4228
|
+
const rtlHeading = RTL_RE.test(b.plain);
|
|
4229
|
+
const headRuns = rtlHeading ? visualOrder(runs2(b.children, c), isRtlParagraph(b.plain)) : runs2(b.children, c);
|
|
4132
4230
|
const node = {
|
|
4133
|
-
|
|
4231
|
+
...rtlHeading ? alignFor(b.children) : {},
|
|
4232
|
+
text: [...b.number ? [{ text: `${b.number} `, color: COLOR.muted }] : [], ...headRuns],
|
|
4134
4233
|
style: `h${Math.min(6, b.level)}`,
|
|
4135
4234
|
headlineLevel: b.level,
|
|
4136
4235
|
id: b.id
|
|
@@ -4144,12 +4243,12 @@ function block2(b, c) {
|
|
|
4144
4243
|
}
|
|
4145
4244
|
case "paragraph": {
|
|
4146
4245
|
const text = bodyRuns(b.children, c);
|
|
4147
|
-
const p = { text, style: "p" };
|
|
4246
|
+
const p = { text, style: "p", ...alignFor(b.children) };
|
|
4148
4247
|
return estimateLines(plain(b.children), c) <= 5 ? { stack: [p], unbreakable: true } : p;
|
|
4149
4248
|
}
|
|
4150
4249
|
case "list": {
|
|
4151
4250
|
const items = b.items.map((it) => {
|
|
4152
|
-
const content = it.children.length === 1 && it.children[0]?.type === "paragraph" ? [{ text: bodyRuns(it.children[0].children, c, c.width - 20 - (it.checked !== null ? 8 : 0)), margin: [0, 0, 0, 2] }] : blocks2(it.children, { ...c, width: c.width - 18 });
|
|
4251
|
+
const content = it.children.length === 1 && it.children[0]?.type === "paragraph" ? [{ text: bodyRuns(it.children[0].children, c, c.width - 20 - (it.checked !== null ? 8 : 0)), margin: [0, 0, 0, 2], ...alignFor(it.children[0].children) }] : blocks2(it.children, { ...c, width: c.width - 18 });
|
|
4153
4252
|
if (it.checked !== null) return { ...taskItem(it.checked, content, c), listType: "none" };
|
|
4154
4253
|
return content.length === 1 ? content[0] : { stack: content };
|
|
4155
4254
|
});
|
|
@@ -4202,15 +4301,24 @@ function block2(b, c) {
|
|
|
4202
4301
|
case "table": {
|
|
4203
4302
|
const n = Math.max(b.header.length, ...b.rows.map((r) => r.length), 1);
|
|
4204
4303
|
const size = c.s.fontSize * 0.92;
|
|
4304
|
+
const widths = columnWidths(b, n, size, c);
|
|
4305
|
+
const cellRuns = (inl, i, head) => {
|
|
4306
|
+
const r = runs2(inl, c).map((x) => typeof x === "object" ? { ...x, fontSize: x.fontSize ?? size, ...head ? { bold: true } : {} } : x);
|
|
4307
|
+
const w = (widths[i] ?? c.width) - 10;
|
|
4308
|
+
if (RTL_RE.test(plain(inl))) {
|
|
4309
|
+
const typo = c.typo ?? (c.faces ? { split: (x) => [x], measure: c.faces.measure } : void 0);
|
|
4310
|
+
return visualOrder(typo ? breakRuns(r, w, typo) : r, isRtlParagraph(plain(inl)));
|
|
4311
|
+
}
|
|
4312
|
+
return c.typo && !head ? breakRuns(r, w, c.typo) : r;
|
|
4313
|
+
};
|
|
4205
4314
|
const cell = (inl, i, head) => ({
|
|
4206
|
-
text:
|
|
4315
|
+
text: cellRuns(inl ?? [], i, head),
|
|
4207
4316
|
bold: head,
|
|
4208
|
-
alignment: b.align[i] ?? "left",
|
|
4317
|
+
alignment: b.align[i] ?? (isRtlParagraph(plain(inl ?? [])) ? "right" : "left"),
|
|
4209
4318
|
fontSize: size,
|
|
4210
4319
|
color: head ? COLOR.heading : COLOR.text
|
|
4211
4320
|
});
|
|
4212
4321
|
const row = (r, head) => Array.from({ length: n }, (_, i) => cell(r[i], i, head));
|
|
4213
|
-
const widths = columnWidths(b, n, size, c);
|
|
4214
4322
|
const lineH = size * 1.3;
|
|
4215
4323
|
const pageH = c.pageHeight;
|
|
4216
4324
|
const rowHeight = (r) => Math.max(...r.map((inl, i) => estimateLines(plain(inl), { ...c, width: (widths[i] ?? c.width) - 10 }, size) * lineH)) + 8;
|
|
@@ -4456,12 +4564,16 @@ async function loadTypography(tree, loaders = {}) {
|
|
|
4456
4564
|
const base = await loadFaces(vfs, s.fontSize);
|
|
4457
4565
|
let needEmoji = false;
|
|
4458
4566
|
let needSymbols = false;
|
|
4567
|
+
let needArabic = false;
|
|
4568
|
+
let needHebrew = false;
|
|
4459
4569
|
const cjk = /* @__PURE__ */ new Set();
|
|
4460
4570
|
for (const ch of documentText(tree)) {
|
|
4461
4571
|
const cp = ch.codePointAt(0);
|
|
4462
4572
|
if (cp < 32 || cp === 8205 || cp === 65039 || base.body(cp)) continue;
|
|
4463
4573
|
if (EMOJI_CP.test(ch)) needEmoji = true;
|
|
4464
|
-
else if (isCjk(cp)) cjk.add(cp);
|
|
4574
|
+
else if (isCjk(cp) || isHangul(cp)) cjk.add(cp);
|
|
4575
|
+
else if (isArabic(cp)) needArabic = true;
|
|
4576
|
+
else if (isHebrew(cp)) needHebrew = true;
|
|
4465
4577
|
else needSymbols = true;
|
|
4466
4578
|
}
|
|
4467
4579
|
const fallbacks = [];
|
|
@@ -4473,6 +4585,14 @@ async function loadTypography(tree, loaders = {}) {
|
|
|
4473
4585
|
const b = await registerFace("NotoSymbols", "NotoSansMath.woff", loaders.symbols ?? defaultSymbolFontLoader, tree, "symbols");
|
|
4474
4586
|
if (b) fallbacks.push({ font: "NotoSymbols", bytes: b });
|
|
4475
4587
|
}
|
|
4588
|
+
if (needArabic) {
|
|
4589
|
+
const b = await registerFace("NotoArabic", "NotoSansArabic.woff", loaders.arabic ?? urlLoader(NOTO_ARABIC_URL), tree, "Arabic");
|
|
4590
|
+
if (b) fallbacks.push({ font: "NotoArabic", bytes: b });
|
|
4591
|
+
}
|
|
4592
|
+
if (needHebrew) {
|
|
4593
|
+
const b = await registerFace("NotoHebrew", "NotoSansHebrew.woff", loaders.hebrew ?? urlLoader(NOTO_HEBREW_URL), tree, "Hebrew");
|
|
4594
|
+
if (b) fallbacks.push({ font: "NotoHebrew", bytes: b });
|
|
4595
|
+
}
|
|
4476
4596
|
if (cjk.size) {
|
|
4477
4597
|
let subsets = [];
|
|
4478
4598
|
try {
|
|
@@ -4488,7 +4608,7 @@ async function loadTypography(tree, loaders = {}) {
|
|
|
4488
4608
|
if (!subsets.length) {
|
|
4489
4609
|
tree.notices.push({
|
|
4490
4610
|
code: "glyph-substituted",
|
|
4491
|
-
message: "The Japanese /
|
|
4611
|
+
message: "The Chinese / Japanese / Korean font could not be loaded, so those characters were left out of the PDF (Word, EPUB and HTML keep them).",
|
|
4492
4612
|
remedy: "Check the internet connection (the font comes from cdn.jsdelivr.net) and export again."
|
|
4493
4613
|
});
|
|
4494
4614
|
}
|
|
@@ -4516,6 +4636,12 @@ var urlLoader = (url) => async () => {
|
|
|
4516
4636
|
var defaultEmojiFontLoader = urlLoader(NOTO_EMOJI_URL);
|
|
4517
4637
|
var defaultSymbolFontLoader = urlLoader(NOTO_SYMBOLS_URL);
|
|
4518
4638
|
var NOTO_CJK_BASE = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-jp@5.3.0";
|
|
4639
|
+
var NOTO_KR_BASE = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-kr@5.3.0";
|
|
4640
|
+
var NOTO_ARABIC_URL = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-arabic@5.3.0/files/noto-sans-arabic-arabic-400-normal.woff";
|
|
4641
|
+
var NOTO_HEBREW_URL = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-hebrew@5.3.0/files/noto-sans-hebrew-hebrew-400-normal.woff";
|
|
4642
|
+
var isHangul = (cp) => cp >= 44032 && cp <= 55215 || cp >= 4352 && cp <= 4607 || cp >= 12592 && cp <= 12687;
|
|
4643
|
+
var isArabic = (cp) => cp >= 1536 && cp <= 1791 || cp >= 1872 && cp <= 1919 || cp >= 2208 && cp <= 2303 || cp >= 64336 && cp <= 65023 || cp >= 65136 && cp <= 65279;
|
|
4644
|
+
var isHebrew = (cp) => cp >= 1424 && cp <= 1535 || cp >= 64285 && cp <= 64335;
|
|
4519
4645
|
function parseUnicodeRangeCss(css) {
|
|
4520
4646
|
const out = [];
|
|
4521
4647
|
for (const block3 of css.split("@font-face").slice(1)) {
|
|
@@ -4531,19 +4657,25 @@ function parseUnicodeRangeCss(css) {
|
|
|
4531
4657
|
}
|
|
4532
4658
|
return out;
|
|
4533
4659
|
}
|
|
4534
|
-
|
|
4660
|
+
async function unicodeRangeFaces(base, cps) {
|
|
4535
4661
|
if (typeof fetch !== "function" || cps.length === 0) return [];
|
|
4536
|
-
const res = await fetch(`${
|
|
4662
|
+
const res = await fetch(`${base}/400.css`);
|
|
4537
4663
|
if (!res.ok) return [];
|
|
4538
4664
|
const faces = parseUnicodeRangeCss(await res.text()).filter((f) => cps.some((cp) => f.ranges.some(([a, b]) => cp >= a && cp <= b)));
|
|
4539
4665
|
const out = [];
|
|
4540
4666
|
await Promise.all(
|
|
4541
4667
|
faces.map(async (f) => {
|
|
4542
|
-
const r = await fetch(`${
|
|
4668
|
+
const r = await fetch(`${base}/files/${f.file}.woff`);
|
|
4543
4669
|
if (r.ok) out.push({ name: f.file, bytes: new Uint8Array(await r.arrayBuffer()) });
|
|
4544
4670
|
})
|
|
4545
4671
|
);
|
|
4546
4672
|
return out;
|
|
4673
|
+
}
|
|
4674
|
+
var defaultCjkFontLoader = async (cps) => {
|
|
4675
|
+
const kr = cps.filter(isHangul);
|
|
4676
|
+
const jp = cps.filter((cp) => !isHangul(cp));
|
|
4677
|
+
const [a, b] = await Promise.all([unicodeRangeFaces(NOTO_KR_BASE, kr), unicodeRangeFaces(NOTO_CJK_BASE, jp)]);
|
|
4678
|
+
return [...a, ...b];
|
|
4547
4679
|
};
|
|
4548
4680
|
var isCjk = (cp) => cp >= 11904 && cp <= 40959 || cp >= 44032 && cp <= 55215 || cp >= 63744 && cp <= 64255 || cp >= 65072 && cp <= 65103 || cp >= 65280 && cp <= 65519 || cp >= 131072 && cp <= 201551;
|
|
4549
4681
|
var EMOJI_CP = /\p{Extended_Pictographic}|\p{Regional_Indicator}|\u20E3/u;
|
|
@@ -4712,6 +4844,70 @@ function removeThinkingContent(content) {
|
|
|
4712
4844
|
return result.trim();
|
|
4713
4845
|
}
|
|
4714
4846
|
var PAGE_BREAK_HTML = `<div class="${PAGE_BREAK_CLASS}" role="separator" aria-label="${PAGE_BREAK_LABEL}" data-label="${PAGE_BREAK_LABEL}"></div>`;
|
|
4847
|
+
var CODE_ARTIFACT_LANGUAGE = {
|
|
4848
|
+
html: "html",
|
|
4849
|
+
react: "tsx",
|
|
4850
|
+
svg: "svg",
|
|
4851
|
+
mermaid: "mermaid",
|
|
4852
|
+
diff: "diff",
|
|
4853
|
+
iframe: "html",
|
|
4854
|
+
code: ""
|
|
4855
|
+
};
|
|
4856
|
+
function parseTagAttributes(tag) {
|
|
4857
|
+
const attrs = {};
|
|
4858
|
+
for (const m of tag.matchAll(/([a-zA-Z_][\w-]*)\s*=\s*(?:"([^"]*)"|'([^']*)')/g)) {
|
|
4859
|
+
attrs[m[1].toLowerCase()] = m[2] ?? m[3] ?? "";
|
|
4860
|
+
}
|
|
4861
|
+
return attrs;
|
|
4862
|
+
}
|
|
4863
|
+
function unwrapArtifactsToMarkdown(markdown) {
|
|
4864
|
+
if (!/<artifact\b/i.test(markdown)) return markdown;
|
|
4865
|
+
return markdown.split(/(^[ \t]*(?:`{3,}|~{3,})[\s\S]*?^[ \t]*(?:`{3,}|~{3,})[ \t]*$)/m).map(
|
|
4866
|
+
(part, i) => i % 2 === 1 ? part : part.replace(/<artifact\b((?:[^>"']|"[^"]*"|'[^']*')*?)>([\s\S]*?)<\/artifact>/gi, (_m, tag, body) => {
|
|
4867
|
+
const attrs = parseTagAttributes(tag);
|
|
4868
|
+
const type = (attrs.type ?? "").toLowerCase();
|
|
4869
|
+
const trimmed = body.replace(/^\n+|\n+$/g, "");
|
|
4870
|
+
if (!trimmed.trim()) return "";
|
|
4871
|
+
if (/^\s*(`{3,}|~{3,})/.test(trimmed)) return `
|
|
4872
|
+
|
|
4873
|
+
${trimmed}
|
|
4874
|
+
|
|
4875
|
+
`;
|
|
4876
|
+
if (type in CODE_ARTIFACT_LANGUAGE) {
|
|
4877
|
+
const lang = CODE_ARTIFACT_LANGUAGE[type] || attrs.language || attrs.lang || "";
|
|
4878
|
+
return `
|
|
4879
|
+
|
|
4880
|
+
\`\`\`\`${lang}
|
|
4881
|
+
${trimmed}
|
|
4882
|
+
\`\`\`\`
|
|
4883
|
+
|
|
4884
|
+
`;
|
|
4885
|
+
}
|
|
4886
|
+
if (looksLikeJson(trimmed)) return `
|
|
4887
|
+
|
|
4888
|
+
\`\`\`\`json
|
|
4889
|
+
${JSON.stringify(JSON.parse(trimmed), null, 2)}
|
|
4890
|
+
\`\`\`\`
|
|
4891
|
+
|
|
4892
|
+
`;
|
|
4893
|
+
return `
|
|
4894
|
+
|
|
4895
|
+
${trimmed}
|
|
4896
|
+
|
|
4897
|
+
`;
|
|
4898
|
+
})
|
|
4899
|
+
).join("");
|
|
4900
|
+
}
|
|
4901
|
+
function looksLikeJson(body) {
|
|
4902
|
+
const t = body.trim();
|
|
4903
|
+
if (!/^[[{]/.test(t)) return false;
|
|
4904
|
+
try {
|
|
4905
|
+
JSON.parse(t);
|
|
4906
|
+
return true;
|
|
4907
|
+
} catch {
|
|
4908
|
+
return false;
|
|
4909
|
+
}
|
|
4910
|
+
}
|
|
4715
4911
|
var LINK_URL = String.raw`((?:[^()\s]|\([^()\s]*\))+)(?:\s+"[^"]*")?`;
|
|
4716
4912
|
var IMAGE_RE = new RegExp(String.raw`!\[([^\]]*)\]\(` + LINK_URL + String.raw`\)`, "g");
|
|
4717
4913
|
var LINK_RE = new RegExp(String.raw`\[([^\]]+)\]\(` + LINK_URL + String.raw`\)`, "g");
|
|
@@ -4798,7 +4994,7 @@ function uniqueId(ctx, base) {
|
|
|
4798
4994
|
return id;
|
|
4799
4995
|
}
|
|
4800
4996
|
var CITE_GROUP_RE = /\[(-?@[^[\]]+)\]/g;
|
|
4801
|
-
var BARE_RE = /(^|[^\w@.\\])@((?:fig|tbl|sec|eq):[\w-]+|[A-Za-z0-9_](?:[\w:.#$%&+?<>~/-]*\w)?)
|
|
4997
|
+
var BARE_RE = /(^|[^\w@.\\])@((?:fig|tbl|sec|eq):[\w-]+|[A-Za-z0-9_](?:[\w:.#$%&+?<>~/-]*\w)?)(?: \[([^\[\]@]+)\])?/g;
|
|
4802
4998
|
function scanText(value, ctx) {
|
|
4803
4999
|
const out = [];
|
|
4804
5000
|
const pushText = (s) => {
|
|
@@ -4819,8 +5015,8 @@ function scanText(value, ctx) {
|
|
|
4819
5015
|
last2 = start + key.length + 1;
|
|
4820
5016
|
} else if (ctx.refs.has(key)) {
|
|
4821
5017
|
pushText(s.slice(last2, start));
|
|
4822
|
-
out.push(cite([{ key, suppressAuthor: false }], ctx, true));
|
|
4823
|
-
last2 = start + key.length + 1;
|
|
5018
|
+
out.push(cite([{ key, locator: m[3]?.trim(), suppressAuthor: false }], ctx, true));
|
|
5019
|
+
last2 = start + key.length + 1 + (m[3] !== void 0 ? m[3].length + 3 : 0);
|
|
4824
5020
|
}
|
|
4825
5021
|
}
|
|
4826
5022
|
pushText(s.slice(last2));
|
|
@@ -5166,7 +5362,7 @@ function parseDocument(source, options = {}) {
|
|
|
5166
5362
|
const settings = resolveSettings(fm.data, sink, options);
|
|
5167
5363
|
for (const n of sink) notify(n);
|
|
5168
5364
|
const body = removeThinkingContent(fm.body);
|
|
5169
|
-
const lifted = extractMath(body);
|
|
5365
|
+
const lifted = extractMath(unwrapArtifactsToMarkdown(body));
|
|
5170
5366
|
const root = fromMarkdown(prepass(lifted.text), {
|
|
5171
5367
|
extensions: [gfm()],
|
|
5172
5368
|
mdastExtensions: [gfmFromMarkdown()]
|
|
@@ -5561,8 +5757,11 @@ export {
|
|
|
5561
5757
|
CitationEngine,
|
|
5562
5758
|
DEFAULT_FOOTER,
|
|
5563
5759
|
DOCUMENT_FORMATS,
|
|
5760
|
+
NOTO_ARABIC_URL,
|
|
5564
5761
|
NOTO_CJK_BASE,
|
|
5565
5762
|
NOTO_EMOJI_URL,
|
|
5763
|
+
NOTO_HEBREW_URL,
|
|
5764
|
+
NOTO_KR_BASE,
|
|
5566
5765
|
NOTO_SYMBOLS_URL,
|
|
5567
5766
|
PAGE_SIZES,
|
|
5568
5767
|
buildPdfDefinition,
|