@bendyline/squisq-cli 2.9.28 → 2.9.30
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 +21 -0
- package/dist/{api-6NVYOTM6.js → api-775UOV72.js} +1 -1
- package/dist/api.d.ts +16 -2
- package/dist/api.js +23 -2
- package/dist/{chunk-KWE5OXEY.js → chunk-6H22H2VX.js} +26 -5
- package/dist/index.js +1 -1
- package/dist/squisq-player.full.global.js +425 -425
- package/dist/squisq-player.global.js +82 -82
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -256,6 +256,27 @@ console.log(`Rendered ${result.frameCount} frames (${result.duration}s) → ${re
|
|
|
256
256
|
|
|
257
257
|
The container's audio segments become the MP4's audio track; timed media clips and block videos found in the doc are embedded and mixed at their scheduled positions.
|
|
258
258
|
|
|
259
|
+
The capture page loads nothing from the network, so a theme font the rendering
|
|
260
|
+
machine lacks (any Google Fonts stack, such as warm-earth's `DM Sans` body)
|
|
261
|
+
renders in a fallback face. Pass the font files as `fonts`, one entry per file;
|
|
262
|
+
every renderer here (MP4, GIF, cover and dashboard PNG) accepts them:
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
const fonts: RenderFontFace[] = [
|
|
266
|
+
{ family: 'DM Sans', data: await readFile('DMSans-Variable.woff2'), weight: '100 1000' },
|
|
267
|
+
{
|
|
268
|
+
family: 'DM Sans',
|
|
269
|
+
data: await readFile('DMSans-VariableItalic.woff2'),
|
|
270
|
+
weight: '100 1000',
|
|
271
|
+
style: 'italic',
|
|
272
|
+
},
|
|
273
|
+
];
|
|
274
|
+
await renderDocToMp4(doc, container, { outputPath: './output.mp4', fonts });
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
`format` defaults to `'woff2'` (also `'woff'`, `'truetype'`, `'opentype'`), and
|
|
278
|
+
`unicodeRange` takes a CSS `unicode-range` for a file holding one subset.
|
|
279
|
+
|
|
259
280
|
### `renderDocToGif`
|
|
260
281
|
|
|
261
282
|
```ts
|
package/dist/api.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { Doc, MotionSpec } from '@bendyline/squisq/schemas';
|
|
|
2
2
|
import { DashboardStyleId } from '@bendyline/squisq/doc';
|
|
3
3
|
import { ContentContainer } from '@bendyline/squisq/storage';
|
|
4
4
|
export { MemoryContentContainer } from '@bendyline/squisq/storage';
|
|
5
|
-
import { VideoQuality, VideoOrientation, GifDither, DashboardResolutionId, VideoExportOptions } from '@bendyline/squisq-video';
|
|
6
|
-
export { DASHBOARD_RESOLUTIONS, DEFAULT_DASHBOARD_RESOLUTION, DashboardResolutionId, DashboardResolutionPreset, GifDither, VideoOrientation, VideoQuality, resolveDashboardDimensions, validateDashboardImageDimensions } from '@bendyline/squisq-video';
|
|
5
|
+
import { VideoQuality, VideoOrientation, GifDither, DashboardResolutionId, VideoExportOptions, RenderFontFace } from '@bendyline/squisq-video';
|
|
6
|
+
export { DASHBOARD_RESOLUTIONS, DEFAULT_DASHBOARD_RESOLUTION, DashboardResolutionId, DashboardResolutionPreset, GifDither, RenderFontFace, VideoOrientation, VideoQuality, resolveDashboardDimensions, validateDashboardImageDimensions } from '@bendyline/squisq-video';
|
|
7
7
|
import { FormatRegistry, FormatId, ConvertOptions, ConvertSource, ConversionResult, PreparedConversion } from '@bendyline/squisq-formats';
|
|
8
8
|
export { ConversionError, ConversionErrorCode, ConversionErrorOptions, ConversionResult, ConvertOptions, ConvertSource, FormatDefinition, FormatId, FormatRegistry, NormalizedInput, PreparedConversion, PreparedExportOptions } from '@bendyline/squisq-formats';
|
|
9
9
|
import { MarkdownDocument } from '@bendyline/squisq/markdown';
|
|
@@ -68,6 +68,8 @@ interface FrameManifest {
|
|
|
68
68
|
animationsEnabled: boolean;
|
|
69
69
|
coverPreRoll: number;
|
|
70
70
|
captureFormat: CaptureFormat;
|
|
71
|
+
/** Digest of the embedded font files; absent when the render embedded none. */
|
|
72
|
+
fontsHash?: string;
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
/**
|
|
@@ -392,6 +394,12 @@ interface RenderDocToMp4Options {
|
|
|
392
394
|
* callers get exactly the duration they ask for.
|
|
393
395
|
*/
|
|
394
396
|
coverPreRoll?: number;
|
|
397
|
+
/**
|
|
398
|
+
* Font files to embed in the capture page. It loads nothing from the
|
|
399
|
+
* network, so a theme font the machine lacks (such as a Google Fonts stack)
|
|
400
|
+
* renders in a fallback face unless it is supplied here. Default: none.
|
|
401
|
+
*/
|
|
402
|
+
fonts?: readonly RenderFontFace[];
|
|
395
403
|
/**
|
|
396
404
|
* Progress callback. Called with a phase name and 0-100 percentage.
|
|
397
405
|
*/
|
|
@@ -427,6 +435,8 @@ interface RenderDocToGifOptions {
|
|
|
427
435
|
dither?: GifDither;
|
|
428
436
|
/** Bayer strength when dither is bayer (0-5, default: 3). */
|
|
429
437
|
bayerScale?: number;
|
|
438
|
+
/** Font files to embed (see {@link RenderDocToMp4Options.fonts}). */
|
|
439
|
+
fonts?: readonly RenderFontFace[];
|
|
430
440
|
/** Progress callback. */
|
|
431
441
|
onProgress?: (phase: string, percent: number) => void;
|
|
432
442
|
}
|
|
@@ -488,6 +498,8 @@ interface RenderDashboardPngOptions {
|
|
|
488
498
|
style?: DashboardStyleId;
|
|
489
499
|
/** Host-supplied title fallback when the doc has no frontmatter title. */
|
|
490
500
|
documentTitle?: string;
|
|
501
|
+
/** Font files to embed (see {@link RenderDocToMp4Options.fonts}). */
|
|
502
|
+
fonts?: readonly RenderFontFace[];
|
|
491
503
|
onProgress?: (phase: string, percent: number) => void;
|
|
492
504
|
}
|
|
493
505
|
/** Result returned by {@link renderDocToDashboardPng}. */
|
|
@@ -505,6 +517,8 @@ interface RenderCoverPngOptions {
|
|
|
505
517
|
width?: number;
|
|
506
518
|
height?: number;
|
|
507
519
|
animationsEnabled?: boolean;
|
|
520
|
+
/** Font files to embed (see {@link RenderDocToMp4Options.fonts}). */
|
|
521
|
+
fonts?: readonly RenderFontFace[];
|
|
508
522
|
onProgress?: (phase: string, percent: number) => void;
|
|
509
523
|
}
|
|
510
524
|
/** Result returned by {@link renderDocCoverToPng}. */
|
package/dist/api.js
CHANGED
|
@@ -469,6 +469,15 @@ function createFfmpegPipeSink(options) {
|
|
|
469
469
|
function docRenderHash(doc) {
|
|
470
470
|
return createHash("sha256").update(JSON.stringify(doc)).digest("hex");
|
|
471
471
|
}
|
|
472
|
+
function fontsRenderHash(fonts) {
|
|
473
|
+
const hash = createHash("sha256");
|
|
474
|
+
for (const font of fonts) {
|
|
475
|
+
const { data, ...face } = font;
|
|
476
|
+
hash.update(JSON.stringify(face));
|
|
477
|
+
hash.update(data instanceof Uint8Array ? data : new Uint8Array(data));
|
|
478
|
+
}
|
|
479
|
+
return hash.digest("hex");
|
|
480
|
+
}
|
|
472
481
|
var MANIFEST_NAME = "manifest.json";
|
|
473
482
|
async function openDirectoryFrameStore(dir, manifest, resume) {
|
|
474
483
|
await mkdir(dir, { recursive: true });
|
|
@@ -1133,6 +1142,7 @@ async function withRenderPage(doc, container, options, fn) {
|
|
|
1133
1142
|
animationsEnabled,
|
|
1134
1143
|
displayMode,
|
|
1135
1144
|
dashboard,
|
|
1145
|
+
fonts,
|
|
1136
1146
|
onProgress
|
|
1137
1147
|
} = options;
|
|
1138
1148
|
onProgress?.("collecting media", 0);
|
|
@@ -1179,6 +1189,7 @@ async function withRenderPage(doc, container, options, fn) {
|
|
|
1179
1189
|
images.set(src, data);
|
|
1180
1190
|
}
|
|
1181
1191
|
}
|
|
1192
|
+
for (const font of fonts ?? []) budget.admit(`font:${font.family}`, font.data);
|
|
1182
1193
|
onProgress?.("generating render HTML", 10);
|
|
1183
1194
|
signal?.throwIfAborted();
|
|
1184
1195
|
const playerBundle = await (selectStandalonePlayerVariant(doc) === "full" ? loadFullPlayerBundle() : loadPlayerBundle());
|
|
@@ -1193,7 +1204,8 @@ async function withRenderPage(doc, container, options, fn) {
|
|
|
1193
1204
|
motion,
|
|
1194
1205
|
animationsEnabled,
|
|
1195
1206
|
displayMode,
|
|
1196
|
-
dashboard
|
|
1207
|
+
dashboard,
|
|
1208
|
+
fonts
|
|
1197
1209
|
});
|
|
1198
1210
|
onProgress?.("launching browser", 15);
|
|
1199
1211
|
signal?.throwIfAborted();
|
|
@@ -1359,6 +1371,7 @@ async function captureDocFrames(doc, container, options) {
|
|
|
1359
1371
|
motion,
|
|
1360
1372
|
coverPreRoll,
|
|
1361
1373
|
animationsEnabled,
|
|
1374
|
+
fonts,
|
|
1362
1375
|
onProgress,
|
|
1363
1376
|
signal
|
|
1364
1377
|
} = options;
|
|
@@ -1378,6 +1391,7 @@ async function captureDocFrames(doc, container, options) {
|
|
|
1378
1391
|
captionStyle,
|
|
1379
1392
|
motion,
|
|
1380
1393
|
animationsEnabled,
|
|
1394
|
+
fonts,
|
|
1381
1395
|
onProgress
|
|
1382
1396
|
},
|
|
1383
1397
|
async (session) => {
|
|
@@ -1429,7 +1443,8 @@ async function renderDocToMp4Piped(doc, container, options, render) {
|
|
|
1429
1443
|
motion: motion === null ? null : JSON.stringify(motion),
|
|
1430
1444
|
animationsEnabled,
|
|
1431
1445
|
coverPreRoll,
|
|
1432
|
-
captureFormat
|
|
1446
|
+
captureFormat,
|
|
1447
|
+
...options.fonts?.length ? { fontsHash: fontsRenderHash(options.fonts) } : {}
|
|
1433
1448
|
};
|
|
1434
1449
|
const store = options.framesDir ? await openDirectoryFrameStore(
|
|
1435
1450
|
resolvePath(options.framesDir),
|
|
@@ -1452,6 +1467,7 @@ async function renderDocToMp4Piped(doc, container, options, render) {
|
|
|
1452
1467
|
captionStyle,
|
|
1453
1468
|
motion,
|
|
1454
1469
|
animationsEnabled,
|
|
1470
|
+
fonts: options.fonts,
|
|
1455
1471
|
onProgress
|
|
1456
1472
|
},
|
|
1457
1473
|
async (session) => {
|
|
@@ -1547,6 +1563,7 @@ async function renderDocCoverToPng(doc, container, options = {}) {
|
|
|
1547
1563
|
includeAudio: false,
|
|
1548
1564
|
animationsEnabled: options.animationsEnabled ?? false,
|
|
1549
1565
|
displayMode: "slideshow",
|
|
1566
|
+
fonts: options.fonts,
|
|
1550
1567
|
onProgress: options.onProgress
|
|
1551
1568
|
},
|
|
1552
1569
|
async ({ page, renderAPI }) => {
|
|
@@ -1579,6 +1596,7 @@ async function renderDocToDashboardPng(doc, container, options = {}) {
|
|
|
1579
1596
|
title,
|
|
1580
1597
|
style,
|
|
1581
1598
|
documentTitle,
|
|
1599
|
+
fonts,
|
|
1582
1600
|
onProgress
|
|
1583
1601
|
} = options;
|
|
1584
1602
|
signal?.throwIfAborted();
|
|
@@ -1594,6 +1612,7 @@ async function renderDocToDashboardPng(doc, container, options = {}) {
|
|
|
1594
1612
|
animationsEnabled: false,
|
|
1595
1613
|
displayMode: "dashboard",
|
|
1596
1614
|
dashboard: { layout, title, style, documentTitle },
|
|
1615
|
+
fonts,
|
|
1597
1616
|
onProgress
|
|
1598
1617
|
},
|
|
1599
1618
|
async ({ page, renderAPI }) => {
|
|
@@ -1662,6 +1681,7 @@ async function renderDocToMp4(doc, container, options) {
|
|
|
1662
1681
|
motion: options.motion ?? null,
|
|
1663
1682
|
coverPreRoll: options.coverPreRoll ?? 0,
|
|
1664
1683
|
animationsEnabled: options.animationsEnabled ?? true,
|
|
1684
|
+
fonts: options.fonts,
|
|
1665
1685
|
onProgress: options.onProgress,
|
|
1666
1686
|
signal: options.signal
|
|
1667
1687
|
});
|
|
@@ -1726,6 +1746,7 @@ async function renderDocToGif(doc, container, options) {
|
|
|
1726
1746
|
motion: options.motion ?? null,
|
|
1727
1747
|
coverPreRoll: options.coverPreRoll ?? 0,
|
|
1728
1748
|
animationsEnabled: options.animationsEnabled ?? false,
|
|
1749
|
+
fonts: options.fonts,
|
|
1729
1750
|
onProgress: options.onProgress,
|
|
1730
1751
|
signal: options.signal
|
|
1731
1752
|
});
|
|
@@ -466,6 +466,15 @@ function createFfmpegPipeSink(options) {
|
|
|
466
466
|
function docRenderHash(doc) {
|
|
467
467
|
return createHash("sha256").update(JSON.stringify(doc)).digest("hex");
|
|
468
468
|
}
|
|
469
|
+
function fontsRenderHash(fonts) {
|
|
470
|
+
const hash = createHash("sha256");
|
|
471
|
+
for (const font of fonts) {
|
|
472
|
+
const { data, ...face } = font;
|
|
473
|
+
hash.update(JSON.stringify(face));
|
|
474
|
+
hash.update(data instanceof Uint8Array ? data : new Uint8Array(data));
|
|
475
|
+
}
|
|
476
|
+
return hash.digest("hex");
|
|
477
|
+
}
|
|
469
478
|
var MANIFEST_NAME = "manifest.json";
|
|
470
479
|
async function openDirectoryFrameStore(dir, manifest, resume) {
|
|
471
480
|
await mkdir(dir, { recursive: true });
|
|
@@ -543,7 +552,7 @@ function mp4Format() {
|
|
|
543
552
|
const animationsEnabled = typeof mp4Opts.animationsEnabled === "boolean" ? mp4Opts.animationsEnabled : MP4_DEFAULTS.animationsEnabled;
|
|
544
553
|
const outputPath = join2(tmpdir(), `squisq-mp4-${randomBytes(8).toString("hex")}.mp4`);
|
|
545
554
|
try {
|
|
546
|
-
const { renderDocToMp4: renderDocToMp42 } = await import("./api-
|
|
555
|
+
const { renderDocToMp4: renderDocToMp42 } = await import("./api-775UOV72.js");
|
|
547
556
|
await renderDocToMp42(input.doc, input.container, {
|
|
548
557
|
outputPath,
|
|
549
558
|
fps,
|
|
@@ -584,7 +593,7 @@ function gifFormat() {
|
|
|
584
593
|
const defaultHeight = portrait ? GIF_DEFAULTS.width : GIF_DEFAULTS.height;
|
|
585
594
|
const outputPath = join2(tmpdir(), `squisq-gif-${randomBytes(8).toString("hex")}.gif`);
|
|
586
595
|
try {
|
|
587
|
-
const { renderDocToGif: renderDocToGif2 } = await import("./api-
|
|
596
|
+
const { renderDocToGif: renderDocToGif2 } = await import("./api-775UOV72.js");
|
|
588
597
|
const result = await renderDocToGif2(input.doc, input.container, {
|
|
589
598
|
outputPath,
|
|
590
599
|
fps: typeof gifOpts.fps === "number" ? gifOpts.fps : GIF_DEFAULTS.fps,
|
|
@@ -627,7 +636,7 @@ function pngFormat() {
|
|
|
627
636
|
async exportDoc(input, options) {
|
|
628
637
|
options.signal?.throwIfAborted();
|
|
629
638
|
const pngOpts = options.formatOptions?.png ?? {};
|
|
630
|
-
const { renderDocToDashboardPng: renderDocToDashboardPng2 } = await import("./api-
|
|
639
|
+
const { renderDocToDashboardPng: renderDocToDashboardPng2 } = await import("./api-775UOV72.js");
|
|
631
640
|
const result = await renderDocToDashboardPng2(input.doc, input.container, {
|
|
632
641
|
resolution: pngOpts.resolution,
|
|
633
642
|
width: pngOpts.width,
|
|
@@ -1130,6 +1139,7 @@ async function withRenderPage(doc, container, options, fn) {
|
|
|
1130
1139
|
animationsEnabled,
|
|
1131
1140
|
displayMode,
|
|
1132
1141
|
dashboard,
|
|
1142
|
+
fonts,
|
|
1133
1143
|
onProgress
|
|
1134
1144
|
} = options;
|
|
1135
1145
|
onProgress?.("collecting media", 0);
|
|
@@ -1176,6 +1186,7 @@ async function withRenderPage(doc, container, options, fn) {
|
|
|
1176
1186
|
images.set(src, data);
|
|
1177
1187
|
}
|
|
1178
1188
|
}
|
|
1189
|
+
for (const font of fonts ?? []) budget.admit(`font:${font.family}`, font.data);
|
|
1179
1190
|
onProgress?.("generating render HTML", 10);
|
|
1180
1191
|
signal?.throwIfAborted();
|
|
1181
1192
|
const playerBundle = await (selectStandalonePlayerVariant(doc) === "full" ? loadFullPlayerBundle() : loadPlayerBundle());
|
|
@@ -1190,7 +1201,8 @@ async function withRenderPage(doc, container, options, fn) {
|
|
|
1190
1201
|
motion,
|
|
1191
1202
|
animationsEnabled,
|
|
1192
1203
|
displayMode,
|
|
1193
|
-
dashboard
|
|
1204
|
+
dashboard,
|
|
1205
|
+
fonts
|
|
1194
1206
|
});
|
|
1195
1207
|
onProgress?.("launching browser", 15);
|
|
1196
1208
|
signal?.throwIfAborted();
|
|
@@ -1356,6 +1368,7 @@ async function captureDocFrames(doc, container, options) {
|
|
|
1356
1368
|
motion,
|
|
1357
1369
|
coverPreRoll,
|
|
1358
1370
|
animationsEnabled,
|
|
1371
|
+
fonts,
|
|
1359
1372
|
onProgress,
|
|
1360
1373
|
signal
|
|
1361
1374
|
} = options;
|
|
@@ -1375,6 +1388,7 @@ async function captureDocFrames(doc, container, options) {
|
|
|
1375
1388
|
captionStyle,
|
|
1376
1389
|
motion,
|
|
1377
1390
|
animationsEnabled,
|
|
1391
|
+
fonts,
|
|
1378
1392
|
onProgress
|
|
1379
1393
|
},
|
|
1380
1394
|
async (session) => {
|
|
@@ -1426,7 +1440,8 @@ async function renderDocToMp4Piped(doc, container, options, render) {
|
|
|
1426
1440
|
motion: motion === null ? null : JSON.stringify(motion),
|
|
1427
1441
|
animationsEnabled,
|
|
1428
1442
|
coverPreRoll,
|
|
1429
|
-
captureFormat
|
|
1443
|
+
captureFormat,
|
|
1444
|
+
...options.fonts?.length ? { fontsHash: fontsRenderHash(options.fonts) } : {}
|
|
1430
1445
|
};
|
|
1431
1446
|
const store = options.framesDir ? await openDirectoryFrameStore(
|
|
1432
1447
|
resolvePath(options.framesDir),
|
|
@@ -1449,6 +1464,7 @@ async function renderDocToMp4Piped(doc, container, options, render) {
|
|
|
1449
1464
|
captionStyle,
|
|
1450
1465
|
motion,
|
|
1451
1466
|
animationsEnabled,
|
|
1467
|
+
fonts: options.fonts,
|
|
1452
1468
|
onProgress
|
|
1453
1469
|
},
|
|
1454
1470
|
async (session) => {
|
|
@@ -1544,6 +1560,7 @@ async function renderDocCoverToPng(doc, container, options = {}) {
|
|
|
1544
1560
|
includeAudio: false,
|
|
1545
1561
|
animationsEnabled: options.animationsEnabled ?? false,
|
|
1546
1562
|
displayMode: "slideshow",
|
|
1563
|
+
fonts: options.fonts,
|
|
1547
1564
|
onProgress: options.onProgress
|
|
1548
1565
|
},
|
|
1549
1566
|
async ({ page, renderAPI }) => {
|
|
@@ -1576,6 +1593,7 @@ async function renderDocToDashboardPng(doc, container, options = {}) {
|
|
|
1576
1593
|
title,
|
|
1577
1594
|
style,
|
|
1578
1595
|
documentTitle,
|
|
1596
|
+
fonts,
|
|
1579
1597
|
onProgress
|
|
1580
1598
|
} = options;
|
|
1581
1599
|
signal?.throwIfAborted();
|
|
@@ -1591,6 +1609,7 @@ async function renderDocToDashboardPng(doc, container, options = {}) {
|
|
|
1591
1609
|
animationsEnabled: false,
|
|
1592
1610
|
displayMode: "dashboard",
|
|
1593
1611
|
dashboard: { layout, title, style, documentTitle },
|
|
1612
|
+
fonts,
|
|
1594
1613
|
onProgress
|
|
1595
1614
|
},
|
|
1596
1615
|
async ({ page, renderAPI }) => {
|
|
@@ -1659,6 +1678,7 @@ async function renderDocToMp4(doc, container, options) {
|
|
|
1659
1678
|
motion: options.motion ?? null,
|
|
1660
1679
|
coverPreRoll: options.coverPreRoll ?? 0,
|
|
1661
1680
|
animationsEnabled: options.animationsEnabled ?? true,
|
|
1681
|
+
fonts: options.fonts,
|
|
1662
1682
|
onProgress: options.onProgress,
|
|
1663
1683
|
signal: options.signal
|
|
1664
1684
|
});
|
|
@@ -1723,6 +1743,7 @@ async function renderDocToGif(doc, container, options) {
|
|
|
1723
1743
|
motion: options.motion ?? null,
|
|
1724
1744
|
coverPreRoll: options.coverPreRoll ?? 0,
|
|
1725
1745
|
animationsEnabled: options.animationsEnabled ?? false,
|
|
1746
|
+
fonts: options.fonts,
|
|
1726
1747
|
onProgress: options.onProgress,
|
|
1727
1748
|
signal: options.signal
|
|
1728
1749
|
});
|