@bendyline/squisq-formats 2.7.1 → 2.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/THIRD_PARTY_LICENSES.txt +192 -0
- package/dist/{chunk-BHWIHYUJ.js → chunk-52RXDH24.js} +118 -46
- package/dist/{chunk-3NKXJF4C.js → chunk-ETK2SKVQ.js} +55 -7
- package/dist/{chunk-ERT4EVR2.js → chunk-GHHIYBUT.js} +1 -1
- package/dist/{chunk-YERFQ2SB.js → chunk-GRIARAMQ.js} +214 -80
- package/dist/{chunk-TX2HE34S.js → chunk-PKFWF4FX.js} +12 -9
- package/dist/chunk-VCZUXQUL.js +90 -0
- package/dist/{chunk-A6N6IN3I.js → chunk-XVBQB2SL.js} +9 -0
- package/dist/{chunk-5ACTORJI.js → chunk-YITBSNPM.js} +82 -15
- package/dist/diagrams/index.d.ts +15 -4
- package/dist/diagrams/index.js +33 -6
- package/dist/docx/index.d.ts +11 -0
- package/dist/docx/index.js +3 -2
- package/dist/epub/index.d.ts +1 -1
- package/dist/epub/index.js +2 -1
- package/dist/html/index.js +2 -1
- package/dist/index.js +9 -8
- package/dist/outside-in/index.js +2 -2
- package/dist/pdf/index.d.ts +38 -0
- package/dist/pdf/index.js +1 -1
- package/dist/pptx/index.js +3 -2
- package/dist/registry/index.js +1 -1
- package/dist/svgIslandPoster-RYA6PDX7.js +73 -0
- package/dist/unicodeFontData.generated-YY6T2K5S.js +22 -0
- package/package.json +4 -3
- /package/dist/{chunk-HNGS7A5Z.js → chunk-CUQ7YITW.js} +0 -0
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
RelIdAllocator,
|
|
3
|
+
blipXml,
|
|
3
4
|
inlineNodeToRuns,
|
|
4
5
|
inlineNodesToRuns,
|
|
5
6
|
normalizeOoxmlHex,
|
|
6
7
|
sanitizeOfficeHyperlink
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-XVBQB2SL.js";
|
|
9
|
+
import {
|
|
10
|
+
docHasSvgIslands,
|
|
11
|
+
isSvgImageEntry,
|
|
12
|
+
svgImageFromBytes,
|
|
13
|
+
svgIslandImage
|
|
14
|
+
} from "./chunk-VCZUXQUL.js";
|
|
8
15
|
import {
|
|
9
16
|
stripHtmlTags
|
|
10
17
|
} from "./chunk-AVOZAKGP.js";
|
|
@@ -70,7 +77,9 @@ import { resolveFontFamily } from "@bendyline/squisq/schemas";
|
|
|
70
77
|
import { docToMarkdown, resolveThemeForDoc } from "@bendyline/squisq/doc";
|
|
71
78
|
import {
|
|
72
79
|
findBlockVideoEmbed,
|
|
73
|
-
|
|
80
|
+
isSvgIslandLang,
|
|
81
|
+
readFrontmatterThemeId,
|
|
82
|
+
svgSourceFromBytes
|
|
74
83
|
} from "@bendyline/squisq/markdown";
|
|
75
84
|
|
|
76
85
|
// src/docx/videoEmbed.ts
|
|
@@ -133,6 +142,22 @@ function readWordVideo(drawing, resolveHyperlink) {
|
|
|
133
142
|
return { embed, title };
|
|
134
143
|
}
|
|
135
144
|
|
|
145
|
+
// src/docx/svgIsland.ts
|
|
146
|
+
var NS_A2 = "http://schemas.openxmlformats.org/drawingml/2006/main";
|
|
147
|
+
var NS_PIC2 = "http://schemas.openxmlformats.org/drawingml/2006/picture";
|
|
148
|
+
var EMU_PER_INCH2 = 914400;
|
|
149
|
+
var MAX_WIDTH_EMU = 6 * EMU_PER_INCH2;
|
|
150
|
+
var MAX_HEIGHT_EMU = 8 * EMU_PER_INCH2;
|
|
151
|
+
function buildWordSvgIslandRun(parts) {
|
|
152
|
+
let cx = parts.width / 96 * EMU_PER_INCH2;
|
|
153
|
+
let cy = parts.height / 96 * EMU_PER_INCH2;
|
|
154
|
+
const scale = Math.min(1, MAX_WIDTH_EMU / cx, MAX_HEIGHT_EMU / cy);
|
|
155
|
+
cx = Math.max(1, Math.round(cx * scale));
|
|
156
|
+
cy = Math.max(1, Math.round(cy * scale));
|
|
157
|
+
const alt = escapeXml(parts.alt);
|
|
158
|
+
return `<w:r><w:drawing><wp:inline distT="0" distB="0" distL="0" distR="0"><wp:extent cx="${cx}" cy="${cy}"/><wp:docPr id="${parts.docPrId}" name="${alt}" descr="${alt}"/><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="${NS_A2}" noChangeAspect="1"/></wp:cNvGraphicFramePr><a:graphic xmlns:a="${NS_A2}"><a:graphicData uri="${NS_PIC2}"><pic:pic xmlns:pic="${NS_PIC2}"><pic:nvPicPr><pic:cNvPr id="0" name="${alt}"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill>${blipXml(parts.pngRelId, parts.svgRelId)}<a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing></w:r>`;
|
|
159
|
+
}
|
|
160
|
+
|
|
136
161
|
// src/docx/styles.ts
|
|
137
162
|
var HEADING_STYLE_MAP = {
|
|
138
163
|
Heading1: 1,
|
|
@@ -196,6 +221,12 @@ async function markdownDocToDocx(doc, options = {}) {
|
|
|
196
221
|
if ([...stories.body, ...stories.header, ...stories.footer].some(isVideoBlock)) {
|
|
197
222
|
ctx.videoPosters = await import("./videoPoster-D7DTZC2L.js");
|
|
198
223
|
}
|
|
224
|
+
const hasSvgImages = [...options.images ?? []].some(
|
|
225
|
+
([url, image]) => isSvgImageEntry(url, image.contentType)
|
|
226
|
+
);
|
|
227
|
+
if (docHasSvgIslands(doc) || hasSvgImages) {
|
|
228
|
+
ctx.svgIslandPosters = await import("./svgIslandPoster-RYA6PDX7.js");
|
|
229
|
+
}
|
|
199
230
|
ctx.setRelationshipSource("document");
|
|
200
231
|
const bodyXml = convertBlocks(stories.body, ctx);
|
|
201
232
|
ctx.setRelationshipSource("header");
|
|
@@ -254,6 +285,8 @@ var ExportContext = class {
|
|
|
254
285
|
this.iconFamilies = /* @__PURE__ */ new Set();
|
|
255
286
|
/** Poster + embed-code helpers, loaded only when the document has a video. */
|
|
256
287
|
this.videoPosters = null;
|
|
288
|
+
/** Stand-in PNGs for SVG islands, loaded only when the document has one. */
|
|
289
|
+
this.svgIslandPosters = null;
|
|
257
290
|
this.nextDocPrId = 1;
|
|
258
291
|
let themeFont;
|
|
259
292
|
let themeTitleFont;
|
|
@@ -261,6 +294,7 @@ var ExportContext = class {
|
|
|
261
294
|
let themeBodyColor;
|
|
262
295
|
let themeMutedColor;
|
|
263
296
|
let themeBackgroundColor;
|
|
297
|
+
let themeTextColor;
|
|
264
298
|
if (options.themeId) {
|
|
265
299
|
const theme = resolveThemeForDoc(doc, options.themeId, options.themeRegistry);
|
|
266
300
|
themeFont = firstFontFromStack(resolveFontFamily(theme.typography?.bodyFont, ""));
|
|
@@ -269,6 +303,7 @@ var ExportContext = class {
|
|
|
269
303
|
themeBodyColor = normalizeOoxmlHex(theme.colors?.text);
|
|
270
304
|
themeMutedColor = normalizeOoxmlHex(theme.colors?.textMuted);
|
|
271
305
|
themeBackgroundColor = normalizeOoxmlHex(theme.colors?.background);
|
|
306
|
+
themeTextColor = theme.colors?.text;
|
|
272
307
|
}
|
|
273
308
|
this.font = options.defaultFont ?? themeFont ?? DEFAULT_FONT;
|
|
274
309
|
this.headingFont = themeTitleFont ?? this.font;
|
|
@@ -278,6 +313,8 @@ var ExportContext = class {
|
|
|
278
313
|
this.mutedColor = themeMutedColor;
|
|
279
314
|
this.backgroundColor = themeBackgroundColor;
|
|
280
315
|
this.resolvedImages = options.images ?? /* @__PURE__ */ new Map();
|
|
316
|
+
this.svgIslandFallbacks = options.svgIslandFallbacks;
|
|
317
|
+
this.islandColor = themeTextColor;
|
|
281
318
|
this.allowRelativeHyperlinks = options.allowRelativeHyperlinks ?? false;
|
|
282
319
|
this.signal = options.signal;
|
|
283
320
|
}
|
|
@@ -429,7 +466,7 @@ function convertBlock(node, ctx, listDepth) {
|
|
|
429
466
|
case "list":
|
|
430
467
|
return convertList(node, ctx, listDepth);
|
|
431
468
|
case "code":
|
|
432
|
-
return convertCodeBlock(node);
|
|
469
|
+
return isSvgIslandLang(node.lang) && convertSvgIsland(node, ctx) || convertCodeBlock(node);
|
|
433
470
|
case "table":
|
|
434
471
|
return convertTable(node, ctx);
|
|
435
472
|
case "thematicBreak":
|
|
@@ -497,6 +534,27 @@ function convertListItem(item, ctx, numId, depth) {
|
|
|
497
534
|
}
|
|
498
535
|
return parts.join("");
|
|
499
536
|
}
|
|
537
|
+
function convertSvgIsland(node, ctx) {
|
|
538
|
+
const image = svgIslandImage(node.value, { color: ctx.islandColor });
|
|
539
|
+
const run = image ? svgPictureRun(image, node.value, ctx) : "";
|
|
540
|
+
return run ? `<w:p>${run}</w:p>` : "";
|
|
541
|
+
}
|
|
542
|
+
function svgPictureRun(image, source, ctx, display = {}) {
|
|
543
|
+
const host = ctx.svgIslandFallbacks?.get(source.trim());
|
|
544
|
+
const fallback = host && host.width > 0 && host.height > 0 ? host.data : ctx.svgIslandPosters?.svgIslandPosterPng(image.width / image.height)?.data;
|
|
545
|
+
if (!fallback) return "";
|
|
546
|
+
const png = ctx.addImage(fallback, "image/png", `image${ctx.images.length + 1}.png`);
|
|
547
|
+
const svg = ctx.addImage(image.data, "image/svg+xml", `image${ctx.images.length + 1}.svg`);
|
|
548
|
+
const sized = Boolean(display.width && display.height);
|
|
549
|
+
return buildWordSvgIslandRun({
|
|
550
|
+
pngRelId: png.relId,
|
|
551
|
+
svgRelId: svg.relId,
|
|
552
|
+
docPrId: png.docPrId,
|
|
553
|
+
width: sized ? display.width : image.width,
|
|
554
|
+
height: sized ? display.height : image.height,
|
|
555
|
+
alt: display.alt || image.alt
|
|
556
|
+
});
|
|
557
|
+
}
|
|
500
558
|
function convertCodeBlock(node) {
|
|
501
559
|
const lines = node.value.split("\n");
|
|
502
560
|
const parts = [];
|
|
@@ -632,34 +690,43 @@ function convertImage(node, ctx) {
|
|
|
632
690
|
const alt = node.alt || node.url;
|
|
633
691
|
return makeRun(`[Image: ${alt}]`, { italic: true });
|
|
634
692
|
}
|
|
693
|
+
if (isSvgImageEntry(node.url, imageEntry.contentType)) {
|
|
694
|
+
const image = svgImageFromBytes(imageEntry.data);
|
|
695
|
+
const run = image && svgPictureRun(image, svgSourceFromBytes(imageEntry.data), ctx, {
|
|
696
|
+
width: imageEntry.width,
|
|
697
|
+
height: imageEntry.height,
|
|
698
|
+
alt: node.alt
|
|
699
|
+
});
|
|
700
|
+
return run || makeRun(`[Image: ${node.alt || node.url}]`, { italic: true });
|
|
701
|
+
}
|
|
635
702
|
const { data, contentType } = imageEntry;
|
|
636
703
|
const ext = contentType.split("/")[1]?.replace("jpeg", "jpg") || "png";
|
|
637
704
|
const filename = `image${ctx.images.length + 1}.${ext}`;
|
|
638
705
|
const { relId, docPrId } = ctx.addImage(data, contentType, filename);
|
|
639
706
|
const dims = imageEntry.width && imageEntry.height ? { width: imageEntry.width, height: imageEntry.height } : readImageDimensions(data);
|
|
640
|
-
const
|
|
641
|
-
const
|
|
642
|
-
const
|
|
707
|
+
const EMU_PER_INCH3 = 914400;
|
|
708
|
+
const MAX_WIDTH_EMU2 = 6 * EMU_PER_INCH3;
|
|
709
|
+
const MAX_HEIGHT_EMU2 = 8 * EMU_PER_INCH3;
|
|
643
710
|
let cx;
|
|
644
711
|
let cy;
|
|
645
712
|
if (dims) {
|
|
646
713
|
const fitted = fitWithin(
|
|
647
|
-
dims.width / 96 *
|
|
648
|
-
dims.height / 96 *
|
|
649
|
-
|
|
650
|
-
|
|
714
|
+
dims.width / 96 * EMU_PER_INCH3,
|
|
715
|
+
dims.height / 96 * EMU_PER_INCH3,
|
|
716
|
+
MAX_WIDTH_EMU2,
|
|
717
|
+
MAX_HEIGHT_EMU2
|
|
651
718
|
);
|
|
652
719
|
cx = Math.round(fitted.width);
|
|
653
720
|
cy = Math.round(fitted.height);
|
|
654
721
|
} else {
|
|
655
|
-
cx = 5 *
|
|
656
|
-
cy = 3 *
|
|
722
|
+
cx = 5 * EMU_PER_INCH3;
|
|
723
|
+
cy = 3 * EMU_PER_INCH3;
|
|
657
724
|
}
|
|
658
725
|
const name = escapeXml(node.alt || filename);
|
|
659
726
|
const description = node.alt ? ` descr="${escapeXml(node.alt)}"` : "";
|
|
660
|
-
const
|
|
661
|
-
const
|
|
662
|
-
return `<w:r><w:drawing><wp:inline distT="0" distB="0" distL="0" distR="0"><wp:extent cx="${cx}" cy="${cy}"/><wp:docPr id="${docPrId}" name="${name}"${description}/><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="${
|
|
727
|
+
const NS_A3 = "http://schemas.openxmlformats.org/drawingml/2006/main";
|
|
728
|
+
const NS_PIC3 = "http://schemas.openxmlformats.org/drawingml/2006/picture";
|
|
729
|
+
return `<w:r><w:drawing><wp:inline distT="0" distB="0" distL="0" distR="0"><wp:extent cx="${cx}" cy="${cy}"/><wp:docPr id="${docPrId}" name="${name}"${description}/><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="${NS_A3}" noChangeAspect="1"/></wp:cNvGraphicFramePr><a:graphic xmlns:a="${NS_A3}"><a:graphicData uri="${NS_PIC3}"><pic:pic xmlns:pic="${NS_PIC3}"><pic:nvPicPr><pic:cNvPr id="0" name="${name}"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed="${relId}"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing></w:r>`;
|
|
663
730
|
}
|
|
664
731
|
function convertFootnoteRef(node, ctx) {
|
|
665
732
|
const fnId = ctx.getFootnoteId(node.identifier);
|
package/dist/diagrams/index.d.ts
CHANGED
|
@@ -16,14 +16,20 @@ import { MarkdownDocument, MarkdownCodeBlock } from '@bendyline/squisq/markdown'
|
|
|
16
16
|
* - ASCII-art ```diagram, ```tree, and ```timeline fences (also unlabeled or
|
|
17
17
|
* `text` fences whose content the detectors accept);
|
|
18
18
|
* - heading blocks with a container template — `{[drawing]}`, `{[layout]}`,
|
|
19
|
-
* `{[diagram]}` — together with every child heading they own
|
|
19
|
+
* `{[diagram]}` — together with every child heading they own;
|
|
20
|
+
* - ```svg fences (SVG islands) — but only when `kinds` asks for `svg`: DOCX
|
|
21
|
+
* and PPTX embed an island natively as vector art, so by default it is
|
|
22
|
+
* left in place. Ask for it to picture islands for PDF, or to get the
|
|
23
|
+
* `svg` map of PNG fallbacks the Office exporters place beside the vector.
|
|
20
24
|
*
|
|
21
25
|
* `docToPptx` keeps drawings, timelines and ASCII diagrams as native shapes;
|
|
22
26
|
* for it, use the returned `mermaid` map as its `diagramImages` option.
|
|
23
27
|
*/
|
|
24
28
|
|
|
25
29
|
/** What kind of diagram a picture is wanted for. */
|
|
26
|
-
type ExportDiagramKind = 'mermaid' | 'diagram' | 'timeline' | 'tree' | 'container';
|
|
30
|
+
type ExportDiagramKind = 'mermaid' | 'diagram' | 'timeline' | 'tree' | 'container' | 'svg';
|
|
31
|
+
/** Every kind, `svg` included — for exporters with no vector picture of their own (PDF). */
|
|
32
|
+
declare const EXPORT_DIAGRAM_KINDS: readonly ExportDiagramKind[];
|
|
27
33
|
interface ExportDiagram {
|
|
28
34
|
readonly kind: ExportDiagramKind;
|
|
29
35
|
/**
|
|
@@ -49,7 +55,7 @@ interface DiagramPicture {
|
|
|
49
55
|
type DiagramRasterizer = (diagram: ExportDiagram) => Promise<DiagramPicture | null>;
|
|
50
56
|
interface RasterizeDiagramsOptions {
|
|
51
57
|
signal?: AbortSignal;
|
|
52
|
-
/**
|
|
58
|
+
/** The kinds to picture. Default: all of them except `svg` (see above). */
|
|
53
59
|
kinds?: readonly ExportDiagramKind[];
|
|
54
60
|
/** Prefix for the image URLs written into the document. Default: `diagram`. */
|
|
55
61
|
urlPrefix?: string;
|
|
@@ -71,6 +77,11 @@ interface RasterizedDiagrams {
|
|
|
71
77
|
}>;
|
|
72
78
|
/** Pictures of Mermaid diagrams keyed by trimmed source, for `docToPptx`'s `diagramImages`. */
|
|
73
79
|
readonly mermaid: Map<string, DiagramPicture>;
|
|
80
|
+
/**
|
|
81
|
+
* Pictures of SVG islands keyed by trimmed source: the PNG fallbacks for
|
|
82
|
+
* DOCX's `svgIslandFallbacks` and PPTX's `diagramImages`.
|
|
83
|
+
*/
|
|
84
|
+
readonly svg: Map<string, DiagramPicture>;
|
|
74
85
|
}
|
|
75
86
|
/** The diagram kind of a fence, or null for ordinary code. */
|
|
76
87
|
declare function fenceDiagramKind(node: MarkdownCodeBlock): ExportDiagramKind | null;
|
|
@@ -83,4 +94,4 @@ declare function findDiagrams(doc: MarkdownDocument): ExportDiagram[];
|
|
|
83
94
|
*/
|
|
84
95
|
declare function rasterizeDiagrams(doc: MarkdownDocument, render: DiagramRasterizer, options?: RasterizeDiagramsOptions): Promise<RasterizedDiagrams>;
|
|
85
96
|
|
|
86
|
-
export { type DiagramPicture, type DiagramRasterizer, type ExportDiagram, type ExportDiagramKind, type RasterizeDiagramsOptions, type RasterizedDiagrams, fenceDiagramKind, findDiagrams, rasterizeDiagrams };
|
|
97
|
+
export { type DiagramPicture, type DiagramRasterizer, EXPORT_DIAGRAM_KINDS, type ExportDiagram, type ExportDiagramKind, type RasterizeDiagramsOptions, type RasterizedDiagrams, fenceDiagramKind, findDiagrams, rasterizeDiagrams };
|
package/dist/diagrams/index.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// src/diagrams/index.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
extractPlainText,
|
|
4
|
+
isSvgIslandLang,
|
|
5
|
+
parseSvgIsland,
|
|
6
|
+
stringifyMarkdown
|
|
7
|
+
} from "@bendyline/squisq/markdown";
|
|
3
8
|
import {
|
|
4
9
|
detectAsciiDiagram,
|
|
5
10
|
detectAsciiTimeline,
|
|
@@ -13,16 +18,26 @@ import {
|
|
|
13
18
|
isExplicitTreeLang,
|
|
14
19
|
resolveTemplateName
|
|
15
20
|
} from "@bendyline/squisq/doc";
|
|
21
|
+
var DEFAULT_KINDS = [
|
|
22
|
+
"mermaid",
|
|
23
|
+
"diagram",
|
|
24
|
+
"timeline",
|
|
25
|
+
"tree",
|
|
26
|
+
"container"
|
|
27
|
+
];
|
|
28
|
+
var EXPORT_DIAGRAM_KINDS = [...DEFAULT_KINDS, "svg"];
|
|
16
29
|
var KIND_NAMES = {
|
|
17
30
|
mermaid: "Diagram",
|
|
18
31
|
diagram: "Diagram",
|
|
19
32
|
timeline: "Timeline",
|
|
20
33
|
tree: "Tree",
|
|
21
|
-
container: "Diagram"
|
|
34
|
+
container: "Diagram",
|
|
35
|
+
svg: "Illustration"
|
|
22
36
|
};
|
|
23
37
|
function fenceDiagramKind(node) {
|
|
24
38
|
const lang = node.lang ?? null;
|
|
25
39
|
if ((lang ?? "").trim().toLowerCase() === "mermaid") return "mermaid";
|
|
40
|
+
if (isSvgIslandLang(lang)) return parseSvgIsland(node.value).ok ? "svg" : null;
|
|
26
41
|
if (isEligibleAsciiFenceLang(lang)) {
|
|
27
42
|
const detection = detectAsciiDiagram(node.value, { explicit: isExplicitDiagramLang(lang) });
|
|
28
43
|
if (detection.isDiagram) return "diagram";
|
|
@@ -47,6 +62,14 @@ function mermaidAlt(source) {
|
|
|
47
62
|
const description = /^\s*accDescr\s*:\s*(.+?)\s*$/mu.exec(source)?.[1] ?? "";
|
|
48
63
|
return [title, description].filter(Boolean).join(". ");
|
|
49
64
|
}
|
|
65
|
+
function fenceAlt(kind, source) {
|
|
66
|
+
if (kind === "mermaid") return mermaidAlt(source);
|
|
67
|
+
if (kind === "svg") {
|
|
68
|
+
const result = parseSvgIsland(source);
|
|
69
|
+
return result.ok && result.island.title || "";
|
|
70
|
+
}
|
|
71
|
+
return "";
|
|
72
|
+
}
|
|
50
73
|
function fenceMarkdown(node) {
|
|
51
74
|
return stringifyMarkdown({ type: "document", children: [node] }).trim();
|
|
52
75
|
}
|
|
@@ -66,7 +89,7 @@ function findInDocument(doc) {
|
|
|
66
89
|
markdown: fenceMarkdown(fence),
|
|
67
90
|
source: fence.value,
|
|
68
91
|
template: "",
|
|
69
|
-
alt: (kind
|
|
92
|
+
alt: fenceAlt(kind, fence.value) || KIND_NAMES[kind]
|
|
70
93
|
}
|
|
71
94
|
});
|
|
72
95
|
}
|
|
@@ -147,7 +170,7 @@ function replaceFences(nodes, replacements) {
|
|
|
147
170
|
return changed ? next : nodes;
|
|
148
171
|
}
|
|
149
172
|
async function rasterizeDiagrams(doc, render, options = {}) {
|
|
150
|
-
const kinds =
|
|
173
|
+
const kinds = new Set(options.kinds ?? DEFAULT_KINDS);
|
|
151
174
|
const prefix = options.urlPrefix ?? "diagram";
|
|
152
175
|
const taken = imageUrls(doc.children, /* @__PURE__ */ new Set());
|
|
153
176
|
let counter = 0;
|
|
@@ -161,11 +184,12 @@ async function rasterizeDiagrams(doc, render, options = {}) {
|
|
|
161
184
|
};
|
|
162
185
|
const images = /* @__PURE__ */ new Map();
|
|
163
186
|
const mermaid = /* @__PURE__ */ new Map();
|
|
187
|
+
const svg = /* @__PURE__ */ new Map();
|
|
164
188
|
const fenceReplacements = /* @__PURE__ */ new Map();
|
|
165
189
|
const containerReplacements = [];
|
|
166
190
|
for (const entry of findInDocument(doc)) {
|
|
167
191
|
options.signal?.throwIfAborted();
|
|
168
|
-
if (
|
|
192
|
+
if (!kinds.has(entry.diagram.kind)) continue;
|
|
169
193
|
let picture = null;
|
|
170
194
|
try {
|
|
171
195
|
picture = await render(entry.diagram);
|
|
@@ -184,6 +208,7 @@ async function rasterizeDiagrams(doc, render, options = {}) {
|
|
|
184
208
|
height: picture.height
|
|
185
209
|
});
|
|
186
210
|
if (entry.diagram.kind === "mermaid") mermaid.set(entry.diagram.source.trim(), picture);
|
|
211
|
+
if (entry.diagram.kind === "svg") svg.set(entry.diagram.source.trim(), picture);
|
|
187
212
|
const paragraph = imageParagraph(url, entry.diagram.alt);
|
|
188
213
|
if (entry.fence) {
|
|
189
214
|
fenceReplacements.set(entry.fence, paragraph);
|
|
@@ -212,10 +237,12 @@ async function rasterizeDiagrams(doc, render, options = {}) {
|
|
|
212
237
|
return {
|
|
213
238
|
markdownDoc: children === doc.children ? doc : { ...doc, children },
|
|
214
239
|
images,
|
|
215
|
-
mermaid
|
|
240
|
+
mermaid,
|
|
241
|
+
svg
|
|
216
242
|
};
|
|
217
243
|
}
|
|
218
244
|
export {
|
|
245
|
+
EXPORT_DIAGRAM_KINDS,
|
|
219
246
|
fenceDiagramKind,
|
|
220
247
|
findDiagrams,
|
|
221
248
|
rasterizeDiagrams
|
package/dist/docx/index.d.ts
CHANGED
|
@@ -65,6 +65,17 @@ interface DocxExportOptions {
|
|
|
65
65
|
}>;
|
|
66
66
|
/** Permit carefully validated relative hyperlink targets. Default: false. */
|
|
67
67
|
allowRelativeHyperlinks?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* PNG renderings of SVG islands (```svg fences), keyed by the fence source
|
|
70
|
+
* as written (trimmed) — the `svg` map from `rasterizeDiagrams` with
|
|
71
|
+
* `kinds: ['svg']`. Word draws an island's SVG; the PNG is what older
|
|
72
|
+
* readers show. Without one, a generated stand-in is used.
|
|
73
|
+
*/
|
|
74
|
+
svgIslandFallbacks?: Map<string, {
|
|
75
|
+
data: ArrayBuffer | Uint8Array;
|
|
76
|
+
width: number;
|
|
77
|
+
height: number;
|
|
78
|
+
}>;
|
|
68
79
|
}
|
|
69
80
|
/**
|
|
70
81
|
* Convert a MarkdownDocument to a .docx Blob.
|
package/dist/docx/index.js
CHANGED
|
@@ -4,8 +4,9 @@ import {
|
|
|
4
4
|
docxToDoc,
|
|
5
5
|
docxToMarkdownDoc,
|
|
6
6
|
markdownDocToDocx
|
|
7
|
-
} from "../chunk-
|
|
8
|
-
import "../chunk-
|
|
7
|
+
} from "../chunk-YITBSNPM.js";
|
|
8
|
+
import "../chunk-XVBQB2SL.js";
|
|
9
|
+
import "../chunk-VCZUXQUL.js";
|
|
9
10
|
import "../chunk-AVOZAKGP.js";
|
|
10
11
|
import "../chunk-ILCJ3WFD.js";
|
|
11
12
|
import "../chunk-JU2RHXUB.js";
|
package/dist/epub/index.d.ts
CHANGED
|
@@ -66,7 +66,7 @@ interface EpubExportOptions {
|
|
|
66
66
|
* Chapters are split at H1/H2 heading boundaries. All referenced images
|
|
67
67
|
* (provided via `options.images`) are embedded in the archive.
|
|
68
68
|
*/
|
|
69
|
-
declare function markdownDocToEpub(
|
|
69
|
+
declare function markdownDocToEpub(sourceDoc: MarkdownDocument, options?: EpubExportOptions): Promise<ArrayBuffer>;
|
|
70
70
|
/**
|
|
71
71
|
* Convert a squisq Doc to an EPUB 3 file.
|
|
72
72
|
*
|
package/dist/epub/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
docToEpub,
|
|
3
3
|
markdownDocToEpub
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-PKFWF4FX.js";
|
|
5
5
|
import "../chunk-YVCUNZB5.js";
|
|
6
|
+
import "../chunk-VCZUXQUL.js";
|
|
6
7
|
import "../chunk-AVOZAKGP.js";
|
|
7
8
|
import "../chunk-JU2RHXUB.js";
|
|
8
9
|
import "../chunk-BXWNU4T5.js";
|
package/dist/html/index.js
CHANGED
|
@@ -10,12 +10,13 @@ import {
|
|
|
10
10
|
markdownDocToPlainHtml,
|
|
11
11
|
markdownDocsToHtmlBundle,
|
|
12
12
|
markdownDocsToPlainHtmlBundle
|
|
13
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-ETK2SKVQ.js";
|
|
14
14
|
import {
|
|
15
15
|
arrayBufferToBase64DataUrl,
|
|
16
16
|
extractFilename,
|
|
17
17
|
inferMimeType
|
|
18
18
|
} from "../chunk-YVCUNZB5.js";
|
|
19
|
+
import "../chunk-VCZUXQUL.js";
|
|
19
20
|
import "../chunk-BXWNU4T5.js";
|
|
20
21
|
import "../chunk-RSU3V5IQ.js";
|
|
21
22
|
export {
|
package/dist/index.js
CHANGED
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
htmlToMarkdown,
|
|
6
6
|
htmlToMarkdownDoc,
|
|
7
7
|
htmlToMarkdownDocSync
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-ETK2SKVQ.js";
|
|
9
9
|
import {
|
|
10
10
|
docToEpub,
|
|
11
11
|
markdownDocToEpub
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-PKFWF4FX.js";
|
|
13
13
|
import {
|
|
14
14
|
BUILTIN_FORMAT_IDS
|
|
15
15
|
} from "./chunk-2P5HJAQL.js";
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
renderOutsideInDocument,
|
|
29
29
|
withOutsideInMarkdownEditing,
|
|
30
30
|
withOutsideInMetadata
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-GHHIYBUT.js";
|
|
32
32
|
import {
|
|
33
33
|
DEFAULT_CONVERSION_LIMITS,
|
|
34
34
|
convert,
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
defaultRegistry,
|
|
38
38
|
prepareConversion,
|
|
39
39
|
resolveConversionLimits
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-CUQ7YITW.js";
|
|
41
41
|
import {
|
|
42
42
|
ConversionError
|
|
43
43
|
} from "./chunk-KXOZMWBS.js";
|
|
@@ -52,16 +52,17 @@ import {
|
|
|
52
52
|
docxToDoc,
|
|
53
53
|
docxToMarkdownDoc,
|
|
54
54
|
markdownDocToDocx
|
|
55
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-YITBSNPM.js";
|
|
56
56
|
import {
|
|
57
57
|
docToPptx,
|
|
58
58
|
markdownDocToPptx,
|
|
59
59
|
pptxToDoc,
|
|
60
60
|
pptxToMarkdownDoc
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-52RXDH24.js";
|
|
62
62
|
import "./chunk-6N2J7C2B.js";
|
|
63
63
|
import "./chunk-YVCUNZB5.js";
|
|
64
|
-
import "./chunk-
|
|
64
|
+
import "./chunk-XVBQB2SL.js";
|
|
65
|
+
import "./chunk-VCZUXQUL.js";
|
|
65
66
|
import {
|
|
66
67
|
docToXlsx,
|
|
67
68
|
markdownDocToXlsx,
|
|
@@ -93,7 +94,7 @@ import {
|
|
|
93
94
|
markdownDocToPdf,
|
|
94
95
|
pdfToDoc,
|
|
95
96
|
pdfToMarkdownDoc
|
|
96
|
-
} from "./chunk-
|
|
97
|
+
} from "./chunk-GRIARAMQ.js";
|
|
97
98
|
import "./chunk-34KECNZO.js";
|
|
98
99
|
import "./chunk-BXWNU4T5.js";
|
|
99
100
|
import "./chunk-IIQYS2YH.js";
|
package/dist/outside-in/index.js
CHANGED
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
renderOutsideInDocument,
|
|
7
7
|
withOutsideInMarkdownEditing,
|
|
8
8
|
withOutsideInMetadata
|
|
9
|
-
} from "../chunk-
|
|
10
|
-
import "../chunk-
|
|
9
|
+
} from "../chunk-GHHIYBUT.js";
|
|
10
|
+
import "../chunk-CUQ7YITW.js";
|
|
11
11
|
import "../chunk-KXOZMWBS.js";
|
|
12
12
|
import {
|
|
13
13
|
OUTSIDE_IN_FORMAT_IDS,
|
package/dist/pdf/index.d.ts
CHANGED
|
@@ -2,6 +2,36 @@ import { ThemeRegistry, Doc } from '@bendyline/squisq/schemas';
|
|
|
2
2
|
import { MarkdownDocument } from '@bendyline/squisq/markdown';
|
|
3
3
|
import { ContentContainer } from '@bendyline/squisq/storage';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Unicode text for PDF export.
|
|
7
|
+
*
|
|
8
|
+
* The 14 standard PDF fonts encode only WinAnsi (see `winAnsi.ts`), so Greek,
|
|
9
|
+
* Cyrillic, Vietnamese, arrows, math, box drawing and most symbols used to
|
|
10
|
+
* reach the page as `?` or an ASCII stand-in. When a document holds such text,
|
|
11
|
+
* the writer embeds a Unicode font beside the standard ones — by default the
|
|
12
|
+
* bundled DejaVu Sans faces — and draws those characters with it.
|
|
13
|
+
*
|
|
14
|
+
* Text is split into runs, each with the font that draws it:
|
|
15
|
+
* - characters WinAnsi encodes keep the standard font, so ordinary Latin text
|
|
16
|
+
* looks exactly as it did before;
|
|
17
|
+
* - a word that needs the Unicode font is drawn in it whole, so its letters and
|
|
18
|
+
* punctuation match rather than alternating faces mid-word;
|
|
19
|
+
* - a character neither font can draw (CJK and emoji, with the bundled faces)
|
|
20
|
+
* still goes through the WinAnsi sanitizer, which replaces it and counts it
|
|
21
|
+
* toward the export's one warning.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* TrueType or OpenType bytes for the faces that draw what the standard fonts
|
|
26
|
+
* cannot. Bold falls back to `regular`, and `mono` (used for code) to
|
|
27
|
+
* `regular` as well.
|
|
28
|
+
*/
|
|
29
|
+
interface PdfUnicodeFonts {
|
|
30
|
+
regular: Uint8Array | ArrayBuffer;
|
|
31
|
+
bold?: Uint8Array | ArrayBuffer;
|
|
32
|
+
mono?: Uint8Array | ArrayBuffer;
|
|
33
|
+
}
|
|
34
|
+
|
|
5
35
|
/**
|
|
6
36
|
* PDF Export
|
|
7
37
|
*
|
|
@@ -74,6 +104,14 @@ interface PdfExportOptions {
|
|
|
74
104
|
* `ConversionResult.warnings` instead.
|
|
75
105
|
*/
|
|
76
106
|
onWarning?: (message: string) => void;
|
|
107
|
+
/**
|
|
108
|
+
* Fonts that draw what the standard PDF fonts cannot encode — Greek,
|
|
109
|
+
* Cyrillic, Vietnamese, arrows, math, box drawing, symbols. Default: the
|
|
110
|
+
* bundled DejaVu Sans faces, embedded (subset) only in a document that has
|
|
111
|
+
* such text. Pass your own faces to cover other scripts, or `false` to keep
|
|
112
|
+
* the standard fonts alone and replace those characters.
|
|
113
|
+
*/
|
|
114
|
+
unicodeFonts?: PdfUnicodeFonts | false;
|
|
77
115
|
}
|
|
78
116
|
/**
|
|
79
117
|
* Convert a MarkdownDocument to a PDF ArrayBuffer.
|
package/dist/pdf/index.js
CHANGED
package/dist/pptx/index.js
CHANGED
|
@@ -4,13 +4,14 @@ import {
|
|
|
4
4
|
pptxToContainer,
|
|
5
5
|
pptxToDoc,
|
|
6
6
|
pptxToMarkdownDoc
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-52RXDH24.js";
|
|
8
8
|
import {
|
|
9
9
|
analyzePptxLayouts,
|
|
10
10
|
inspectPptxLayouts
|
|
11
11
|
} from "../chunk-6N2J7C2B.js";
|
|
12
12
|
import "../chunk-YVCUNZB5.js";
|
|
13
|
-
import "../chunk-
|
|
13
|
+
import "../chunk-XVBQB2SL.js";
|
|
14
|
+
import "../chunk-VCZUXQUL.js";
|
|
14
15
|
import "../chunk-AVOZAKGP.js";
|
|
15
16
|
import "../chunk-ILCJ3WFD.js";
|
|
16
17
|
import "../chunk-JU2RHXUB.js";
|
package/dist/registry/index.js
CHANGED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {
|
|
2
|
+
upngEncoder
|
|
3
|
+
} from "./chunk-34KECNZO.js";
|
|
4
|
+
|
|
5
|
+
// src/shared/svgIslandPoster.ts
|
|
6
|
+
var LONG_EDGE = 480;
|
|
7
|
+
var posterCache = /* @__PURE__ */ new Map();
|
|
8
|
+
function svgIslandPosterPng(aspectRatio) {
|
|
9
|
+
const ratio = Number.isFinite(aspectRatio) && aspectRatio > 0 ? aspectRatio : 4 / 3;
|
|
10
|
+
const width = Math.max(48, Math.round(ratio >= 1 ? LONG_EDGE : LONG_EDGE * ratio));
|
|
11
|
+
const height = Math.max(48, Math.round(ratio >= 1 ? LONG_EDGE / ratio : LONG_EDGE));
|
|
12
|
+
const key = `${width}x${height}`;
|
|
13
|
+
if (posterCache.has(key)) return posterCache.get(key) ?? null;
|
|
14
|
+
if (!upngEncoder) {
|
|
15
|
+
posterCache.set(key, null);
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const panel = [243, 244, 246];
|
|
19
|
+
const ink = [156, 163, 175];
|
|
20
|
+
const rgba = new Uint8Array(width * height * 4);
|
|
21
|
+
for (let i = 0; i < width * height; i++) {
|
|
22
|
+
rgba[i * 4] = panel[0];
|
|
23
|
+
rgba[i * 4 + 1] = panel[1];
|
|
24
|
+
rgba[i * 4 + 2] = panel[2];
|
|
25
|
+
rgba[i * 4 + 3] = 255;
|
|
26
|
+
}
|
|
27
|
+
const size = Math.round(Math.min(width, height) * 0.28);
|
|
28
|
+
const left = (width - size) / 2;
|
|
29
|
+
const top = (height - size * 0.8) / 2;
|
|
30
|
+
const glyphH = size * 0.8;
|
|
31
|
+
const stroke = Math.max(2, size * 0.06);
|
|
32
|
+
const inFrame = (x, y) => {
|
|
33
|
+
const inside = x >= left && x <= left + size && y >= top && y <= top + glyphH;
|
|
34
|
+
const inner = x >= left + stroke && x <= left + size - stroke && y >= top + stroke && y <= top + glyphH - stroke;
|
|
35
|
+
return inside && !inner;
|
|
36
|
+
};
|
|
37
|
+
const sunX = left + size * 0.32;
|
|
38
|
+
const sunY = top + glyphH * 0.34;
|
|
39
|
+
const sunR = size * 0.09;
|
|
40
|
+
const inSun = (x, y) => (x - sunX) ** 2 + (y - sunY) ** 2 <= sunR ** 2;
|
|
41
|
+
const base = top + glyphH - stroke;
|
|
42
|
+
const inMountain = (x, y) => {
|
|
43
|
+
if (y > base || x < left + stroke || x > left + size - stroke) return false;
|
|
44
|
+
const peakX = left + size * 0.62;
|
|
45
|
+
const peakY = top + glyphH * 0.42;
|
|
46
|
+
const halfW = size * 0.38;
|
|
47
|
+
return y >= peakY + (base - peakY) * Math.abs(x - peakX) / halfW;
|
|
48
|
+
};
|
|
49
|
+
const SAMPLES = 4;
|
|
50
|
+
for (let y = Math.floor(top); y < Math.ceil(top + glyphH); y++) {
|
|
51
|
+
for (let x = Math.floor(left); x < Math.ceil(left + size); x++) {
|
|
52
|
+
let hits = 0;
|
|
53
|
+
for (let sy = 0; sy < SAMPLES; sy++) {
|
|
54
|
+
for (let sx = 0; sx < SAMPLES; sx++) {
|
|
55
|
+
const px = x + (sx + 0.5) / SAMPLES;
|
|
56
|
+
const py = y + (sy + 0.5) / SAMPLES;
|
|
57
|
+
if (inFrame(px, py) || inSun(px, py) || inMountain(px, py)) hits++;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (!hits) continue;
|
|
61
|
+
const t = hits / SAMPLES ** 2;
|
|
62
|
+
const at = (y * width + x) * 4;
|
|
63
|
+
for (let c = 0; c < 3; c++) rgba[at + c] = Math.round(panel[c] + (ink[c] - panel[c]) * t);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const data = upngEncoder.encode([rgba.buffer], width, height, 0);
|
|
67
|
+
const poster = { data, contentType: "image/png", width, height };
|
|
68
|
+
posterCache.set(key, poster);
|
|
69
|
+
return poster;
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
svgIslandPosterPng
|
|
73
|
+
};
|