@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
package/README.md
CHANGED
|
@@ -87,7 +87,7 @@ const imported = await pdfToMarkdownDoc(pdfBuffer);
|
|
|
87
87
|
const container = await pdfToContainer(pdfBuffer);
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
-
**Fidelity:** export uses pdf-lib's standard 14 fonts (`themeId` affects colors only). Pages stay white for printing. Theme text, heading, and link colors are retained when they meet 4.5:1 contrast against both white paper and the shaded table header; otherwise the exporter uses its default dark ink for that role. This also applies to inherited and custom themes. Pass `images` (keyed by the markdown URL, as for DOCX) to draw PNG and JPEG images that stand alone in a paragraph; other images print as an `[Image: alt]` note. PDF has no semantic structure, so import is heuristic and best-effort: headings are detected by font size, and tables / code blocks / blockquotes / links via the `detectTables` / `detectCodeBlocks` / `detectBlockquotes` / `detectLinks` options (all default true). `pdfToMarkdownDoc` is text-only; `pdfToContainer` also extracts embedded XObject and inline raster images in browsers and Node, placed **by page** — each image is inserted after the last content block from its page (image-only pages fall back to the nearest preceding page with content, else the document end). Placement is page-level only; masks, vector artwork, and images that PDF.js cannot expose as decoded pixels are not emitted.
|
|
90
|
+
**Fidelity:** export uses pdf-lib's standard 14 fonts (`themeId` affects colors only). Text they cannot encode — Greek, Cyrillic, Vietnamese, arrows, math, box drawing, symbols — is drawn with an embedded Unicode font: the bundled DejaVu Sans faces, loaded and subset only for a document that needs them (`unicodeFonts` supplies your own faces, or `false` turns this off). CJK and emoji are still replaced, and reported once through `onWarning`. Pages stay white for printing. Theme text, heading, and link colors are retained when they meet 4.5:1 contrast against both white paper and the shaded table header; otherwise the exporter uses its default dark ink for that role. This also applies to inherited and custom themes. Pass `images` (keyed by the markdown URL, as for DOCX) to draw PNG and JPEG images that stand alone in a paragraph; other images print as an `[Image: alt]` note. PDF has no semantic structure, so import is heuristic and best-effort: headings are detected by font size, and tables / code blocks / blockquotes / links via the `detectTables` / `detectCodeBlocks` / `detectBlockquotes` / `detectLinks` options (all default true). `pdfToMarkdownDoc` is text-only; `pdfToContainer` also extracts embedded XObject and inline raster images in browsers and Node, placed **by page** — each image is inserted after the last content block from its page (image-only pages fall back to the nearest preceding page with content, else the document end). Placement is page-level only; masks, vector artwork, and images that PDF.js cannot expose as decoded pixels are not emitted.
|
|
91
91
|
|
|
92
92
|
### Diagrams as pictures
|
|
93
93
|
|
package/THIRD_PARTY_LICENSES.txt
CHANGED
|
@@ -6,6 +6,7 @@ repository license, the pinned upstream copy is vendored and identified below.
|
|
|
6
6
|
|
|
7
7
|
COMPONENTS
|
|
8
8
|
- @fortawesome/fontawesome-free@7.3.1 ((CC-BY-4.0 AND OFL-1.1 AND MIT)); files: LICENSE.txt
|
|
9
|
+
- dejavu-fonts-ttf@2.37.3 (SEE LICENSE IN README.md AND LICENSE); files: LICENSE
|
|
9
10
|
|
|
10
11
|
==============================================================================
|
|
11
12
|
@fortawesome/fontawesome-free@7.3.1 - LICENSE.txt
|
|
@@ -176,3 +177,194 @@ trademarks does not indicate endorsement of the trademark holder by Font
|
|
|
176
177
|
Awesome, nor vice versa. **Please do not use brand logos for any purpose except
|
|
177
178
|
to represent the company, product, or service to which they refer.**
|
|
178
179
|
|
|
180
|
+
==============================================================================
|
|
181
|
+
dejavu-fonts-ttf@2.37.3 - LICENSE
|
|
182
|
+
==============================================================================
|
|
183
|
+
Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
|
|
184
|
+
Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
Bitstream Vera Fonts Copyright
|
|
188
|
+
------------------------------
|
|
189
|
+
|
|
190
|
+
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
|
|
191
|
+
a trademark of Bitstream, Inc.
|
|
192
|
+
|
|
193
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
194
|
+
of the fonts accompanying this license ("Fonts") and associated
|
|
195
|
+
documentation files (the "Font Software"), to reproduce and distribute the
|
|
196
|
+
Font Software, including without limitation the rights to use, copy, merge,
|
|
197
|
+
publish, distribute, and/or sell copies of the Font Software, and to permit
|
|
198
|
+
persons to whom the Font Software is furnished to do so, subject to the
|
|
199
|
+
following conditions:
|
|
200
|
+
|
|
201
|
+
The above copyright and trademark notices and this permission notice shall
|
|
202
|
+
be included in all copies of one or more of the Font Software typefaces.
|
|
203
|
+
|
|
204
|
+
The Font Software may be modified, altered, or added to, and in particular
|
|
205
|
+
the designs of glyphs or characters in the Fonts may be modified and
|
|
206
|
+
additional glyphs or characters may be added to the Fonts, only if the fonts
|
|
207
|
+
are renamed to names not containing either the words "Bitstream" or the word
|
|
208
|
+
"Vera".
|
|
209
|
+
|
|
210
|
+
This License becomes null and void to the extent applicable to Fonts or Font
|
|
211
|
+
Software that has been modified and is distributed under the "Bitstream
|
|
212
|
+
Vera" names.
|
|
213
|
+
|
|
214
|
+
The Font Software may be sold as part of a larger software package but no
|
|
215
|
+
copy of one or more of the Font Software typefaces may be sold by itself.
|
|
216
|
+
|
|
217
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
218
|
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
|
|
219
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
|
|
220
|
+
TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
|
|
221
|
+
FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
|
|
222
|
+
ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
|
|
223
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
|
224
|
+
THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
|
|
225
|
+
FONT SOFTWARE.
|
|
226
|
+
|
|
227
|
+
Except as contained in this notice, the names of Gnome, the Gnome
|
|
228
|
+
Foundation, and Bitstream Inc., shall not be used in advertising or
|
|
229
|
+
otherwise to promote the sale, use or other dealings in this Font Software
|
|
230
|
+
without prior written authorization from the Gnome Foundation or Bitstream
|
|
231
|
+
Inc., respectively. For further information, contact: fonts at gnome dot
|
|
232
|
+
org.
|
|
233
|
+
|
|
234
|
+
Arev Fonts Copyright
|
|
235
|
+
------------------------------
|
|
236
|
+
|
|
237
|
+
Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
|
|
238
|
+
|
|
239
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
240
|
+
a copy of the fonts accompanying this license ("Fonts") and
|
|
241
|
+
associated documentation files (the "Font Software"), to reproduce
|
|
242
|
+
and distribute the modifications to the Bitstream Vera Font Software,
|
|
243
|
+
including without limitation the rights to use, copy, merge, publish,
|
|
244
|
+
distribute, and/or sell copies of the Font Software, and to permit
|
|
245
|
+
persons to whom the Font Software is furnished to do so, subject to
|
|
246
|
+
the following conditions:
|
|
247
|
+
|
|
248
|
+
The above copyright and trademark notices and this permission notice
|
|
249
|
+
shall be included in all copies of one or more of the Font Software
|
|
250
|
+
typefaces.
|
|
251
|
+
|
|
252
|
+
The Font Software may be modified, altered, or added to, and in
|
|
253
|
+
particular the designs of glyphs or characters in the Fonts may be
|
|
254
|
+
modified and additional glyphs or characters may be added to the
|
|
255
|
+
Fonts, only if the fonts are renamed to names not containing either
|
|
256
|
+
the words "Tavmjong Bah" or the word "Arev".
|
|
257
|
+
|
|
258
|
+
This License becomes null and void to the extent applicable to Fonts
|
|
259
|
+
or Font Software that has been modified and is distributed under the
|
|
260
|
+
"Tavmjong Bah Arev" names.
|
|
261
|
+
|
|
262
|
+
The Font Software may be sold as part of a larger software package but
|
|
263
|
+
no copy of one or more of the Font Software typefaces may be sold by
|
|
264
|
+
itself.
|
|
265
|
+
|
|
266
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
267
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
268
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
269
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
|
|
270
|
+
TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
271
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
272
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
273
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
274
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
275
|
+
|
|
276
|
+
Except as contained in this notice, the name of Tavmjong Bah shall not
|
|
277
|
+
be used in advertising or otherwise to promote the sale, use or other
|
|
278
|
+
dealings in this Font Software without prior written authorization
|
|
279
|
+
from Tavmjong Bah. For further information, contact: tavmjong @ free
|
|
280
|
+
. fr.
|
|
281
|
+
|
|
282
|
+
TeX Gyre DJV Math
|
|
283
|
+
-----------------
|
|
284
|
+
Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
|
|
285
|
+
|
|
286
|
+
Math extensions done by B. Jackowski, P. Strzelczyk and P. Pianowski
|
|
287
|
+
(on behalf of TeX users groups) are in public domain.
|
|
288
|
+
|
|
289
|
+
Letters imported from Euler Fraktur from AMSfonts are (c) American
|
|
290
|
+
Mathematical Society (see below).
|
|
291
|
+
Bitstream Vera Fonts Copyright
|
|
292
|
+
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera
|
|
293
|
+
is a trademark of Bitstream, Inc.
|
|
294
|
+
|
|
295
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
296
|
+
of the fonts accompanying this license (“Fonts”) and associated
|
|
297
|
+
documentation
|
|
298
|
+
files (the “Font Software”), to reproduce and distribute the Font Software,
|
|
299
|
+
including without limitation the rights to use, copy, merge, publish,
|
|
300
|
+
distribute,
|
|
301
|
+
and/or sell copies of the Font Software, and to permit persons to whom
|
|
302
|
+
the Font Software is furnished to do so, subject to the following
|
|
303
|
+
conditions:
|
|
304
|
+
|
|
305
|
+
The above copyright and trademark notices and this permission notice
|
|
306
|
+
shall be
|
|
307
|
+
included in all copies of one or more of the Font Software typefaces.
|
|
308
|
+
|
|
309
|
+
The Font Software may be modified, altered, or added to, and in particular
|
|
310
|
+
the designs of glyphs or characters in the Fonts may be modified and
|
|
311
|
+
additional
|
|
312
|
+
glyphs or characters may be added to the Fonts, only if the fonts are
|
|
313
|
+
renamed
|
|
314
|
+
to names not containing either the words “Bitstream” or the word “Vera”.
|
|
315
|
+
|
|
316
|
+
This License becomes null and void to the extent applicable to Fonts or
|
|
317
|
+
Font Software
|
|
318
|
+
that has been modified and is distributed under the “Bitstream Vera”
|
|
319
|
+
names.
|
|
320
|
+
|
|
321
|
+
The Font Software may be sold as part of a larger software package but
|
|
322
|
+
no copy
|
|
323
|
+
of one or more of the Font Software typefaces may be sold by itself.
|
|
324
|
+
|
|
325
|
+
THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
326
|
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
|
|
327
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
|
|
328
|
+
TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
|
|
329
|
+
FOUNDATION
|
|
330
|
+
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL,
|
|
331
|
+
SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN
|
|
332
|
+
ACTION
|
|
333
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR
|
|
334
|
+
INABILITY TO USE
|
|
335
|
+
THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
336
|
+
Except as contained in this notice, the names of GNOME, the GNOME
|
|
337
|
+
Foundation,
|
|
338
|
+
and Bitstream Inc., shall not be used in advertising or otherwise to promote
|
|
339
|
+
the sale, use or other dealings in this Font Software without prior written
|
|
340
|
+
authorization from the GNOME Foundation or Bitstream Inc., respectively.
|
|
341
|
+
For further information, contact: fonts at gnome dot org.
|
|
342
|
+
|
|
343
|
+
AMSFonts (v. 2.2) copyright
|
|
344
|
+
|
|
345
|
+
The PostScript Type 1 implementation of the AMSFonts produced by and
|
|
346
|
+
previously distributed by Blue Sky Research and Y&Y, Inc. are now freely
|
|
347
|
+
available for general use. This has been accomplished through the
|
|
348
|
+
cooperation
|
|
349
|
+
of a consortium of scientific publishers with Blue Sky Research and Y&Y.
|
|
350
|
+
Members of this consortium include:
|
|
351
|
+
|
|
352
|
+
Elsevier Science IBM Corporation Society for Industrial and Applied
|
|
353
|
+
Mathematics (SIAM) Springer-Verlag American Mathematical Society (AMS)
|
|
354
|
+
|
|
355
|
+
In order to assure the authenticity of these fonts, copyright will be
|
|
356
|
+
held by
|
|
357
|
+
the American Mathematical Society. This is not meant to restrict in any way
|
|
358
|
+
the legitimate use of the fonts, such as (but not limited to) electronic
|
|
359
|
+
distribution of documents containing these fonts, inclusion of these fonts
|
|
360
|
+
into other public domain or commercial font collections or computer
|
|
361
|
+
applications, use of the outline data to create derivative fonts and/or
|
|
362
|
+
faces, etc. However, the AMS does require that the AMS copyright notice be
|
|
363
|
+
removed from any derivative versions of the fonts which have been altered in
|
|
364
|
+
any way. In addition, to ensure the fidelity of TeX documents using Computer
|
|
365
|
+
Modern fonts, Professor Donald Knuth, creator of the Computer Modern faces,
|
|
366
|
+
has requested that any alterations which yield different font metrics be
|
|
367
|
+
given a different name.
|
|
368
|
+
|
|
369
|
+
$Id$
|
|
370
|
+
|
|
@@ -3,11 +3,16 @@ import {
|
|
|
3
3
|
} from "./chunk-YVCUNZB5.js";
|
|
4
4
|
import {
|
|
5
5
|
RelIdAllocator,
|
|
6
|
+
blipXml,
|
|
6
7
|
inlineNodeToRuns,
|
|
7
8
|
inlineNodesToRuns,
|
|
8
9
|
sanitizeOfficeHyperlink,
|
|
9
10
|
toOoxmlHex
|
|
10
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-XVBQB2SL.js";
|
|
12
|
+
import {
|
|
13
|
+
svgImageFromBytes,
|
|
14
|
+
svgIslandImage
|
|
15
|
+
} from "./chunk-VCZUXQUL.js";
|
|
11
16
|
import {
|
|
12
17
|
extractPlainText,
|
|
13
18
|
stripHtmlTags
|
|
@@ -74,7 +79,87 @@ import {
|
|
|
74
79
|
resolveThemeForDoc
|
|
75
80
|
} from "@bendyline/squisq/doc";
|
|
76
81
|
import { splitIconMarkers } from "@bendyline/squisq/icon-marker";
|
|
77
|
-
import {
|
|
82
|
+
import {
|
|
83
|
+
isSvgImageSource,
|
|
84
|
+
parseVideoEmbedUrl,
|
|
85
|
+
readFrontmatterThemeId
|
|
86
|
+
} from "@bendyline/squisq/markdown";
|
|
87
|
+
|
|
88
|
+
// src/pptx/pictureLayers.ts
|
|
89
|
+
import { svgSourceFromBytes } from "@bendyline/squisq/markdown";
|
|
90
|
+
function buildImageShape(img, shapeId, left, top, maxWidth, maxHeight, hyperlinkRelId, svgRelId) {
|
|
91
|
+
const name = escapeXml(img.alt || "Picture");
|
|
92
|
+
const descr = img.alt ? ` descr="${escapeXml(img.alt)}"` : "";
|
|
93
|
+
const link = hyperlinkRelId ? `<a:hlinkClick r:id="${hyperlinkRelId}"/>` : "";
|
|
94
|
+
return `<p:pic><p:nvPicPr>` + (link ? `<p:cNvPr id="${shapeId}" name="${name}"${descr}>${link}</p:cNvPr>` : `<p:cNvPr id="${shapeId}" name="${name}"${descr}/>`) + `<p:cNvPicPr><a:picLocks noChangeAspect="1"/></p:cNvPicPr><p:nvPr/></p:nvPicPr><p:blipFill>` + blipXml(img.relId, svgRelId) + `<a:stretch><a:fillRect/></a:stretch></p:blipFill><p:spPr><a:xfrm><a:off x="${left}" y="${top}"/><a:ext cx="${maxWidth}" cy="${maxHeight}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></p:spPr></p:pic>`;
|
|
95
|
+
}
|
|
96
|
+
function fitCentered(area, width, height) {
|
|
97
|
+
const scale = Math.min(area.width / width, area.height / height);
|
|
98
|
+
const fitted = { width: Math.round(width * scale), height: Math.round(height * scale) };
|
|
99
|
+
return {
|
|
100
|
+
x: area.x + Math.round((area.width - fitted.width) / 2),
|
|
101
|
+
y: area.y + Math.round((area.height - fitted.height) / 2),
|
|
102
|
+
...fitted
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function arrayBufferOf(data) {
|
|
106
|
+
return data instanceof Uint8Array ? data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) : data;
|
|
107
|
+
}
|
|
108
|
+
function usablePicture(picture) {
|
|
109
|
+
return !!picture && picture.width > 0 && picture.height > 0;
|
|
110
|
+
}
|
|
111
|
+
function mermaidTitle(source) {
|
|
112
|
+
return /^\s*accTitle\s*:\s*(.+?)\s*$/mu.exec(source)?.[1] ?? "Diagram";
|
|
113
|
+
}
|
|
114
|
+
function mermaidPictureShapes(layer, area, ctx) {
|
|
115
|
+
const picture = ctx.diagramImages?.get(layer.content.source.trim());
|
|
116
|
+
if (!usablePicture(picture)) return null;
|
|
117
|
+
const alt = mermaidTitle(layer.content.source);
|
|
118
|
+
const relId = ctx.addImage("diagram.png", arrayBufferOf(picture.data), alt);
|
|
119
|
+
const rect = fitCentered(area, picture.width, picture.height);
|
|
120
|
+
return [
|
|
121
|
+
buildImageShape({ relId, alt }, ctx.allocShapeId(), rect.x, rect.y, rect.width, rect.height)
|
|
122
|
+
];
|
|
123
|
+
}
|
|
124
|
+
function svgIslandPictureShapes(layer, area, ctx) {
|
|
125
|
+
const image = svgIslandImage(layer.content.source, { color: layer.content.color });
|
|
126
|
+
if (!image) return null;
|
|
127
|
+
return svgPictureShapes(image, layer.content.source, area, ctx, layer.content.alt ?? image.alt);
|
|
128
|
+
}
|
|
129
|
+
function svgImagePictureShapes(layer, area, ctx) {
|
|
130
|
+
const data = ctx.images?.get(layer.content.src);
|
|
131
|
+
const image = data ? svgImageFromBytes(data) : null;
|
|
132
|
+
if (!data || !image) return null;
|
|
133
|
+
const alt = layer.content.alt || image.alt;
|
|
134
|
+
return svgPictureShapes(
|
|
135
|
+
image,
|
|
136
|
+
svgSourceFromBytes(data),
|
|
137
|
+
area,
|
|
138
|
+
ctx,
|
|
139
|
+
alt,
|
|
140
|
+
layer.content.fit === "fill"
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
function svgPictureShapes(image, source, area, ctx, alt, stretch = false) {
|
|
144
|
+
const hostPicture = ctx.diagramImages?.get(source.trim());
|
|
145
|
+
const fallback = usablePicture(hostPicture) ? arrayBufferOf(hostPicture.data) : ctx.svgIslandPosters?.svgIslandPosterPng(image.width / image.height)?.data;
|
|
146
|
+
if (!fallback) return null;
|
|
147
|
+
const relId = ctx.addImage("svg-island.png", fallback, alt);
|
|
148
|
+
const svgRelId = ctx.addImage("svg-island.svg", image.data, alt);
|
|
149
|
+
const rect = stretch ? area : fitCentered(area, image.width, image.height);
|
|
150
|
+
return [
|
|
151
|
+
buildImageShape(
|
|
152
|
+
{ relId, alt },
|
|
153
|
+
ctx.allocShapeId(),
|
|
154
|
+
rect.x,
|
|
155
|
+
rect.y,
|
|
156
|
+
rect.width,
|
|
157
|
+
rect.height,
|
|
158
|
+
null,
|
|
159
|
+
svgRelId
|
|
160
|
+
)
|
|
161
|
+
];
|
|
162
|
+
}
|
|
78
163
|
|
|
79
164
|
// src/pptx/styles.ts
|
|
80
165
|
var SLIDE_WIDTH = 12192e3;
|
|
@@ -202,9 +287,12 @@ async function docToPptx(doc, options = {}) {
|
|
|
202
287
|
if (slideBlocks.length === 0) {
|
|
203
288
|
slideBlocks.push({ id: "empty-slide", startTime: 0, duration: 0, audioSegment: 0, layers: [] });
|
|
204
289
|
}
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
290
|
+
const hasLayer = (type) => slideBlocks.some((b) => b.layers?.some((l) => l.type === type));
|
|
291
|
+
const videoPosters = hasLayer("videoEmbed") ? await import("./videoPoster-D7DTZC2L.js") : null;
|
|
292
|
+
const hasSvgImage = slideBlocks.some(
|
|
293
|
+
(b) => b.layers?.some((l) => l.type === "image" && isSvgImageSource(l.content.src))
|
|
294
|
+
);
|
|
295
|
+
const svgIslandPosters = hasLayer("svg") || hasSvgImage ? await import("./svgIslandPoster-RYA6PDX7.js") : null;
|
|
208
296
|
const slideXmls = [];
|
|
209
297
|
const slideContexts = [];
|
|
210
298
|
for (let index = 0; index < slideBlocks.length; index++) {
|
|
@@ -218,6 +306,7 @@ async function docToPptx(doc, options = {}) {
|
|
|
218
306
|
);
|
|
219
307
|
ctx.diagramImages = effectiveOptions.diagramImages;
|
|
220
308
|
ctx.videoPosters = videoPosters;
|
|
309
|
+
ctx.svgIslandPosters = svgIslandPosters;
|
|
221
310
|
slideXmls.push(buildLayerSlideXml(slideBlocks[index], ctx));
|
|
222
311
|
slideContexts.push(ctx);
|
|
223
312
|
}
|
|
@@ -321,6 +410,8 @@ var SlideContext = class {
|
|
|
321
410
|
this.relationships = [];
|
|
322
411
|
/** Poster helpers for hosted videos, loaded only when the deck has one. */
|
|
323
412
|
this.videoPosters = null;
|
|
413
|
+
/** Stand-in PNGs for SVG islands, loaded only when the deck has one. */
|
|
414
|
+
this.svgIslandPosters = null;
|
|
324
415
|
this.embeddedImages = [];
|
|
325
416
|
this.nextShapeId = 4;
|
|
326
417
|
/** Font Awesome families referenced by semantic or materialized text runs. */
|
|
@@ -472,7 +563,11 @@ function convertLayerToShapes(layer, ctx, textBottom) {
|
|
|
472
563
|
case "shape":
|
|
473
564
|
return [buildLayerShape(layer, ctx.allocShapeId())];
|
|
474
565
|
case "image": {
|
|
475
|
-
const
|
|
566
|
+
const svg = isSvgImageSource(layer.content.src);
|
|
567
|
+
const area = toEmuRect(resolveLayerRect(layer.position, 1920, 1080));
|
|
568
|
+
const picture = svg ? svgImagePictureShapes(layer, area, ctx) : null;
|
|
569
|
+
if (picture) return picture;
|
|
570
|
+
const data = svg ? void 0 : ctx.images?.get(layer.content.src);
|
|
476
571
|
if (data) {
|
|
477
572
|
const image = ctx.addImage(layer.content.src, data, layer.content.alt);
|
|
478
573
|
const rect = toEmuRect(resolveLayerRect(layer.position, 1920, 1080));
|
|
@@ -539,7 +634,7 @@ function convertLayerToShapes(layer, ctx, textBottom) {
|
|
|
539
634
|
}
|
|
540
635
|
case "video": {
|
|
541
636
|
const poster = layer.content.posterSrc;
|
|
542
|
-
const data = poster ? ctx.images?.get(poster) : void 0;
|
|
637
|
+
const data = poster && !isSvgImageSource(poster) ? ctx.images?.get(poster) : void 0;
|
|
543
638
|
if (poster && data) {
|
|
544
639
|
const relId = ctx.addImage(poster, data, layer.content.alt);
|
|
545
640
|
const embedded = ctx.embeddedImages.find((candidate) => candidate.relId === relId);
|
|
@@ -598,45 +693,28 @@ function convertLayerToShapes(layer, ctx, textBottom) {
|
|
|
598
693
|
];
|
|
599
694
|
}
|
|
600
695
|
case "mermaid": {
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
picture.data.byteOffset,
|
|
605
|
-
picture.data.byteOffset + picture.data.byteLength
|
|
606
|
-
) : picture.data;
|
|
607
|
-
const relId = ctx.addImage(
|
|
608
|
-
`diagram-${ctx.embeddedImages.length + 1}.png`,
|
|
609
|
-
data,
|
|
610
|
-
mermaidTitle(layer.content.source)
|
|
611
|
-
);
|
|
612
|
-
const embedded = ctx.embeddedImages.find((candidate) => candidate.relId === relId);
|
|
613
|
-
const area = toEmuRect(resolveLayerRect(layer.position, 1920, 1080));
|
|
614
|
-
const scale = Math.min(area.width / picture.width, area.height / picture.height);
|
|
615
|
-
const width = Math.round(picture.width * scale);
|
|
616
|
-
const height = Math.round(picture.height * scale);
|
|
617
|
-
return [
|
|
618
|
-
buildImageShape(
|
|
619
|
-
embedded,
|
|
620
|
-
ctx.allocShapeId(),
|
|
621
|
-
area.x + Math.round((area.width - width) / 2),
|
|
622
|
-
area.y + Math.round((area.height - height) / 2),
|
|
623
|
-
width,
|
|
624
|
-
height
|
|
625
|
-
)
|
|
626
|
-
];
|
|
627
|
-
}
|
|
628
|
-
return [
|
|
696
|
+
const area = toEmuRect(resolveLayerRect(layer.position, 1920, 1080));
|
|
697
|
+
const picture = mermaidPictureShapes(layer, area, ctx);
|
|
698
|
+
return picture ?? [
|
|
629
699
|
buildLayerTextShape(
|
|
630
700
|
textLayerForPlaceholder(layer.position, layer.content.source, ctx),
|
|
631
701
|
ctx.allocShapeId()
|
|
632
702
|
)
|
|
633
703
|
];
|
|
634
704
|
}
|
|
705
|
+
case "svg": {
|
|
706
|
+
const area = toEmuRect(resolveLayerRect(layer.position, 1920, 1080));
|
|
707
|
+
const picture = svgIslandPictureShapes(layer, area, ctx);
|
|
708
|
+
const label = layer.content.alt ?? "SVG illustration";
|
|
709
|
+
return picture ?? [
|
|
710
|
+
buildLayerTextShape(
|
|
711
|
+
textLayerForPlaceholder(layer.position, `[${label}]`, ctx),
|
|
712
|
+
ctx.allocShapeId()
|
|
713
|
+
)
|
|
714
|
+
];
|
|
715
|
+
}
|
|
635
716
|
}
|
|
636
717
|
}
|
|
637
|
-
function mermaidTitle(source) {
|
|
638
|
-
return /^\s*accTitle\s*:\s*(.+?)\s*$/mu.exec(source)?.[1] ?? "Diagram";
|
|
639
|
-
}
|
|
640
718
|
function resolvePositionValue(value, dimension) {
|
|
641
719
|
if (typeof value === "number") return value;
|
|
642
720
|
if (typeof value === "string") {
|
|
@@ -1157,12 +1235,6 @@ function buildBodyShapeCustom(bodyContent, top, height, style) {
|
|
|
1157
1235
|
const defRPr = style.hasTheme ? `<a:defRPr><a:solidFill><a:srgbClr val="${style.text}"/></a:solidFill><a:latin typeface="${escapeXml(style.bodyFont)}"/></a:defRPr>` : "";
|
|
1158
1236
|
return `<p:sp><p:nvSpPr><p:cNvPr id="3" name="Content"/><p:cNvSpPr><a:spLocks noGrp="1"/></p:cNvSpPr><p:nvPr><p:ph idx="1"/></p:nvPr></p:nvSpPr><p:spPr><a:xfrm><a:off x="${BODY_LEFT}" y="${top}"/><a:ext cx="${BODY_WIDTH}" cy="${height}"/></a:xfrm></p:spPr><p:txBody><a:bodyPr/><a:lstStyle><a:lvl1pPr>${defRPr}</a:lvl1pPr></a:lstStyle>` + bodyContent + `</p:txBody></p:sp>`;
|
|
1159
1237
|
}
|
|
1160
|
-
function buildImageShape(img, shapeId, left, top, maxWidth, maxHeight, hyperlinkRelId) {
|
|
1161
|
-
const name = escapeXml(img.alt || "Picture");
|
|
1162
|
-
const descr = img.alt ? ` descr="${escapeXml(img.alt)}"` : "";
|
|
1163
|
-
const link = hyperlinkRelId ? `<a:hlinkClick r:id="${hyperlinkRelId}"/>` : "";
|
|
1164
|
-
return `<p:pic><p:nvPicPr>` + (link ? `<p:cNvPr id="${shapeId}" name="${name}"${descr}>${link}</p:cNvPr>` : `<p:cNvPr id="${shapeId}" name="${name}"${descr}/>`) + `<p:cNvPicPr><a:picLocks noChangeAspect="1"/></p:cNvPicPr><p:nvPr/></p:nvPicPr><p:blipFill><a:blip r:embed="${img.relId}"/><a:stretch><a:fillRect/></a:stretch></p:blipFill><p:spPr><a:xfrm><a:off x="${left}" y="${top}"/><a:ext cx="${maxWidth}" cy="${maxHeight}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></p:spPr></p:pic>`;
|
|
1165
|
-
}
|
|
1166
1238
|
function convertBodyBlocks(nodes, ctx) {
|
|
1167
1239
|
const parts = [];
|
|
1168
1240
|
for (const node of nodes) {
|
|
@@ -1212,7 +1284,7 @@ function isImageOnlyParagraph(node) {
|
|
|
1212
1284
|
function convertParagraph(node, ctx) {
|
|
1213
1285
|
const soloImage = isImageOnlyParagraph(node);
|
|
1214
1286
|
if (soloImage && ctx.images) {
|
|
1215
|
-
const data = ctx.images.get(soloImage.url);
|
|
1287
|
+
const data = isSvgImageSource(soloImage.url) ? void 0 : ctx.images.get(soloImage.url);
|
|
1216
1288
|
if (data) {
|
|
1217
1289
|
ctx.addImage(soloImage.url, data, soloImage.alt || "");
|
|
1218
1290
|
return "";
|
|
@@ -1364,7 +1436,7 @@ function makeHyperlinkRun(text, rId, format) {
|
|
|
1364
1436
|
}
|
|
1365
1437
|
function convertImage(node, format, ctx) {
|
|
1366
1438
|
if (ctx.images) {
|
|
1367
|
-
const data = ctx.images.get(node.url);
|
|
1439
|
+
const data = isSvgImageSource(node.url) ? void 0 : ctx.images.get(node.url);
|
|
1368
1440
|
if (data) {
|
|
1369
1441
|
ctx.addImage(node.url, data, node.alt || "");
|
|
1370
1442
|
return "";
|
|
@@ -3,6 +3,9 @@ import {
|
|
|
3
3
|
extractFilename,
|
|
4
4
|
inferMimeType
|
|
5
5
|
} from "./chunk-YVCUNZB5.js";
|
|
6
|
+
import {
|
|
7
|
+
exportImageBytes
|
|
8
|
+
} from "./chunk-VCZUXQUL.js";
|
|
6
9
|
import {
|
|
7
10
|
FootnoteIndex,
|
|
8
11
|
footnoteIds
|
|
@@ -137,7 +140,8 @@ function buildInlineImageMaps(images) {
|
|
|
137
140
|
continue;
|
|
138
141
|
}
|
|
139
142
|
canonicalKeyFor.set(buffer, path);
|
|
140
|
-
|
|
143
|
+
const bytes = exportImageBytes(path, buffer);
|
|
144
|
+
if (bytes) imageMap[path] = arrayBufferToBase64DataUrl(bytes, inferMimeType(path));
|
|
141
145
|
}
|
|
142
146
|
return { imageMap, imageAliases };
|
|
143
147
|
}
|
|
@@ -286,7 +290,11 @@ function escapeHtml(str) {
|
|
|
286
290
|
// src/html/plainHtml.ts
|
|
287
291
|
import {
|
|
288
292
|
findBlockVideoEmbed,
|
|
293
|
+
isSvgIslandLang,
|
|
294
|
+
parseSvgIsland,
|
|
289
295
|
sanitizeHtmlNodes,
|
|
296
|
+
svgIslandDataUrl,
|
|
297
|
+
svgSourceFromDataUrl,
|
|
290
298
|
sanitizeUrl,
|
|
291
299
|
videoEmbedFromIframe,
|
|
292
300
|
videoEmbedIframeAttributes,
|
|
@@ -308,7 +316,14 @@ function markdownDocToPlainHtml(doc, options = {}) {
|
|
|
308
316
|
const resolveId = themeId ?? readFrontmatterThemeId2(doc.frontmatter);
|
|
309
317
|
const theme = options.theme ?? (resolveId ? resolveThemeForDoc(doc, resolveId, options.themeRegistry) : void 0);
|
|
310
318
|
const footnotes = new FootnoteIndex(doc);
|
|
311
|
-
const ctx = {
|
|
319
|
+
const ctx = {
|
|
320
|
+
images,
|
|
321
|
+
links,
|
|
322
|
+
htmlPolicy,
|
|
323
|
+
footnotes,
|
|
324
|
+
videoEmbeds,
|
|
325
|
+
textColor: theme?.colors.text
|
|
326
|
+
};
|
|
312
327
|
const body = renderTopLevel(doc.children, ctx) + renderFootnotesSection(footnotes, ctx);
|
|
313
328
|
const fontsLink = theme && externalResources === "allow" ? renderFontsLink(theme) : "";
|
|
314
329
|
const usesIcons = docUsesIcons(doc);
|
|
@@ -322,8 +337,10 @@ ${escapeStyleText(iconsCss)}
|
|
|
322
337
|
const themedCss = theme ? renderThemedCss(theme) : DEFAULT_CSS;
|
|
323
338
|
const videoCss = docUsesVideoEmbeds(doc, htmlPolicy) ? `
|
|
324
339
|
${VIDEO_EMBED_CSS}` : "";
|
|
340
|
+
const svgCss = docUsesSvgIslands(doc) ? `
|
|
341
|
+
${SVG_ISLAND_CSS}` : "";
|
|
325
342
|
const safeCss = escapeStyleText(`${themedCss}
|
|
326
|
-
${FEATURE_CSS}${videoCss}`);
|
|
343
|
+
${FEATURE_CSS}${videoCss}${svgCss}`);
|
|
327
344
|
return `<!DOCTYPE html>
|
|
328
345
|
<html lang="en">
|
|
329
346
|
<head>
|
|
@@ -372,6 +389,15 @@ function docUsesVideoEmbeds(doc, htmlPolicy) {
|
|
|
372
389
|
(node) => node.type === "paragraph" && findBlockVideoEmbed(node) !== null || visit(node)
|
|
373
390
|
);
|
|
374
391
|
}
|
|
392
|
+
function docUsesSvgIslands(doc) {
|
|
393
|
+
const visit = (node) => {
|
|
394
|
+
if (!node || typeof node !== "object") return false;
|
|
395
|
+
const n = node;
|
|
396
|
+
if (n.type === "code") return isSvgIslandLang(n.lang);
|
|
397
|
+
return Array.isArray(n.children) && n.children.some(visit);
|
|
398
|
+
};
|
|
399
|
+
return visit(doc);
|
|
400
|
+
}
|
|
375
401
|
function renderTopLevel(children, ctx) {
|
|
376
402
|
const out = [];
|
|
377
403
|
for (let i = 0; i < children.length; i++) {
|
|
@@ -726,6 +752,10 @@ function nodeToHtml(node, ctx) {
|
|
|
726
752
|
case "inlineCode":
|
|
727
753
|
return `<code>${escapeHtml2(node.value ?? "")}</code>`;
|
|
728
754
|
case "code": {
|
|
755
|
+
if (isSvgIslandLang(node.lang)) {
|
|
756
|
+
const island = svgIslandToHtml(node.value ?? "", ctx);
|
|
757
|
+
if (island) return island;
|
|
758
|
+
}
|
|
729
759
|
const lang = node.lang ? ` class="language-${escapeAttr(node.lang)}"` : "";
|
|
730
760
|
return `<pre class="squisq-code-block"><code${lang}>${escapeHtml2(node.value ?? "")}</code></pre>`;
|
|
731
761
|
}
|
|
@@ -748,7 +778,7 @@ function nodeToHtml(node, ctx) {
|
|
|
748
778
|
case "image": {
|
|
749
779
|
const original = node.url ?? "";
|
|
750
780
|
const resolved = ctx?.images?.get(original) ?? original;
|
|
751
|
-
const safeSrc = sanitizeUrl(resolved, "media");
|
|
781
|
+
const safeSrc = sanitizeUrl(resolved, "media") ?? svgDataImageToHtmlSrc(resolved);
|
|
752
782
|
if (!safeSrc) return escapeHtml2(node.alt ?? "");
|
|
753
783
|
return `<img src="${escapeAttr(safeSrc)}" alt="${escapeAttr(node.alt ?? "")}" />`;
|
|
754
784
|
}
|
|
@@ -896,6 +926,20 @@ var VIDEO_EMBED_CSS = ` .squisq-video-embed { display: block; max-width: 720px;
|
|
|
896
926
|
.squisq-video-embed-play::after { content: ''; position: absolute; left: 26px; top: 12px; border-style: solid; border-width: 10px 0 10px 16px; border-color: transparent transparent transparent #fff; }
|
|
897
927
|
.squisq-video-embed-provider { position: absolute; left: 12px; bottom: 10px; color: #fff; font-size: 0.8em; font-weight: 600; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8); }
|
|
898
928
|
.squisq-video-embed-caption { margin-top: 0.4em; font-size: 0.875em; opacity: 0.8; }`;
|
|
929
|
+
function svgDataImageToHtmlSrc(url) {
|
|
930
|
+
const source = svgSourceFromDataUrl(url);
|
|
931
|
+
const result = source === null ? null : parseSvgIsland(source);
|
|
932
|
+
return result?.ok ? svgIslandDataUrl(result.island) : null;
|
|
933
|
+
}
|
|
934
|
+
function svgIslandToHtml(source, ctx) {
|
|
935
|
+
const result = parseSvgIsland(source);
|
|
936
|
+
if (!result.ok) return null;
|
|
937
|
+
const { island } = result;
|
|
938
|
+
const src = svgIslandDataUrl(island, { color: ctx?.textColor });
|
|
939
|
+
return `<figure class="squisq-svg-island"><img src="${escapeAttr(src)}" alt="${escapeAttr(island.title ?? "Illustration")}" width="${island.width}" height="${island.height}"></figure>`;
|
|
940
|
+
}
|
|
941
|
+
var SVG_ISLAND_CSS = ` .squisq-svg-island { margin: 1.25em 0; }
|
|
942
|
+
.squisq-svg-island img { display: block; max-width: 100%; height: auto; }`;
|
|
899
943
|
function escapeHtml2(s) {
|
|
900
944
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
901
945
|
}
|
|
@@ -1069,7 +1113,8 @@ async function readImagesForDoc(mdDoc, docDir, readBinary) {
|
|
|
1069
1113
|
const cleanAuthored = authored.replace(/[#?].*$/, "");
|
|
1070
1114
|
const resolved = resolveRelative(docDir, cleanAuthored);
|
|
1071
1115
|
if (resolved === null) continue;
|
|
1072
|
-
const
|
|
1116
|
+
const read = await readBinary(resolved);
|
|
1117
|
+
const data = read && exportImageBytes(resolved, read);
|
|
1073
1118
|
if (!data) continue;
|
|
1074
1119
|
out.set(authored, { data, zipPath: resolved });
|
|
1075
1120
|
}
|
|
@@ -1194,7 +1239,8 @@ async function markdownDocsToHtmlBundle(options) {
|
|
|
1194
1239
|
const clean = authored.replace(/[#?].*$/, "");
|
|
1195
1240
|
const resolvedImg = resolveRelative(docDir, clean);
|
|
1196
1241
|
if (resolvedImg === null) continue;
|
|
1197
|
-
const
|
|
1242
|
+
const read = await readBinary(resolvedImg);
|
|
1243
|
+
const data = read && exportImageBytes(resolvedImg, read);
|
|
1198
1244
|
if (!data) continue;
|
|
1199
1245
|
const safe = sanitizeZipPath2(resolvedImg);
|
|
1200
1246
|
if (!safe) continue;
|
|
@@ -2020,7 +2066,9 @@ async function docToHtmlZip(doc, options) {
|
|
|
2020
2066
|
);
|
|
2021
2067
|
continue;
|
|
2022
2068
|
}
|
|
2023
|
-
|
|
2069
|
+
const bytes = exportImageBytes(originalPath, buffer);
|
|
2070
|
+
if (!bytes) continue;
|
|
2071
|
+
zip.file(zipPath, bytes);
|
|
2024
2072
|
imagePathMap[originalPath] = zipPath;
|
|
2025
2073
|
}
|
|
2026
2074
|
}
|