@segnavia/format 0.38.0 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/board-edit.d.ts +2 -1
- package/dist/board-geometry.d.ts +2 -1
- package/dist/board-section-grid.d.ts +2 -1
- package/dist/board-sections.d.ts +2 -1
- package/dist/board.d.ts +8 -8
- package/dist/board.js +15 -11
- package/dist/book-exports.d.ts +2 -2
- package/dist/book-section.d.ts +171 -0
- package/dist/book-section.js +13 -2
- package/dist/book.d.ts +189 -47
- package/dist/book.js +18 -2
- package/dist/cover.d.ts +500 -0
- package/dist/cover.js +100 -0
- package/dist/deck-from-slides.d.ts +23 -4
- package/dist/deck.d.ts +144 -42
- package/dist/deck.js +22 -24
- package/dist/direction.d.ts +1 -1
- package/dist/font-collection.d.ts +8 -0
- package/dist/font-collection.js +191 -0
- package/dist/font-face.d.ts +8 -0
- package/dist/font-face.js +24 -0
- package/dist/font-refusal.d.ts +5 -0
- package/dist/font-refusal.js +9 -0
- package/dist/font-signature.d.ts +5 -0
- package/dist/font-signature.js +13 -0
- package/dist/fonts.d.ts +6 -1
- package/dist/fonts.js +3 -1
- package/dist/geometry.d.ts +20 -0
- package/dist/geometry.js +18 -0
- package/dist/image-fit.d.ts +10 -0
- package/dist/image-fit.js +10 -0
- package/dist/imposition-marks.d.ts +1 -1
- package/dist/imposition-schema.d.ts +185 -0
- package/dist/imposition-schema.js +122 -0
- package/dist/imposition.d.ts +8 -6
- package/dist/imposition.js +53 -42
- package/dist/index.d.ts +23 -1756
- package/dist/index.js +29 -212
- package/dist/json-schemas.d.ts +2181 -0
- package/dist/json-schemas.js +43 -0
- package/dist/media.d.ts +152 -0
- package/dist/media.js +119 -0
- package/dist/note-series.d.ts +4 -4
- package/dist/note-series.js +0 -5
- package/dist/page.d.ts +359 -0
- package/dist/page.js +110 -0
- package/dist/paper.d.ts +5 -5
- package/dist/placement-cycle.d.ts +5 -0
- package/dist/placement-cycle.js +30 -0
- package/dist/placement-refusal.d.ts +5 -0
- package/dist/placement-refusal.js +6 -0
- package/dist/placement.d.ts +76 -0
- package/dist/placement.js +100 -0
- package/dist/print.d.ts +72 -0
- package/dist/print.js +38 -0
- package/dist/printed-page-imposition.d.ts +2 -1
- package/dist/shots-used.d.ts +0 -1
- package/dist/shots-used.js +2 -2
- package/dist/slots.d.ts +13 -13
- package/package.json +1 -1
package/dist/deck.d.ts
CHANGED
|
@@ -1,29 +1,7 @@
|
|
|
1
|
+
import { Box, Point, Size } from "./geometry.js";
|
|
2
|
+
import { ImageFit } from "./image-fit.js";
|
|
1
3
|
import { z } from "zod";
|
|
2
4
|
//#region src/deck.d.ts
|
|
3
|
-
export declare const Size: z.ZodObject<{
|
|
4
|
-
width: z.ZodNumber;
|
|
5
|
-
height: z.ZodNumber;
|
|
6
|
-
}, z.core.$strip>;
|
|
7
|
-
export type Size = z.infer<typeof Size>;
|
|
8
|
-
export declare const Box: z.ZodObject<{
|
|
9
|
-
x: z.ZodNumber;
|
|
10
|
-
y: z.ZodNumber;
|
|
11
|
-
width: z.ZodNumber;
|
|
12
|
-
height: z.ZodNumber;
|
|
13
|
-
}, z.core.$strip>;
|
|
14
|
-
export type Box = z.infer<typeof Box>;
|
|
15
|
-
export declare const Point: z.ZodObject<{
|
|
16
|
-
x: z.ZodNumber;
|
|
17
|
-
y: z.ZodNumber;
|
|
18
|
-
}, z.core.$strip>;
|
|
19
|
-
export type Point = z.infer<typeof Point>;
|
|
20
|
-
export declare const ImageFit: z.ZodEnum<{
|
|
21
|
-
width: "width";
|
|
22
|
-
contain: "contain";
|
|
23
|
-
cover: "cover";
|
|
24
|
-
focus: "focus";
|
|
25
|
-
}>;
|
|
26
|
-
export type ImageFit = z.infer<typeof ImageFit>;
|
|
27
5
|
export declare const Annotation: z.ZodObject<{
|
|
28
6
|
ring: z.ZodNullable<z.ZodObject<{
|
|
29
7
|
x: z.ZodNumber;
|
|
@@ -163,9 +141,9 @@ export declare const Narration: z.ZodObject<{
|
|
|
163
141
|
text: z.ZodString;
|
|
164
142
|
lang: z.ZodOptional<z.ZodString>;
|
|
165
143
|
dir: z.ZodOptional<z.ZodEnum<{
|
|
144
|
+
auto: "auto";
|
|
166
145
|
ltr: "ltr";
|
|
167
146
|
rtl: "rtl";
|
|
168
|
-
auto: "auto";
|
|
169
147
|
}>>;
|
|
170
148
|
voice: z.ZodOptional<z.ZodString>;
|
|
171
149
|
words: z.ZodArray<z.ZodObject<{
|
|
@@ -183,7 +161,7 @@ export declare const Narration: z.ZodObject<{
|
|
|
183
161
|
}, z.core.$strip>>>;
|
|
184
162
|
}, z.core.$strip>;
|
|
185
163
|
export type Narration = z.infer<typeof Narration>;
|
|
186
|
-
export declare const SlideContent: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
164
|
+
export declare const SlideContent: z.ZodUnion<readonly [z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
187
165
|
kind: z.ZodLiteral<"html">;
|
|
188
166
|
html: z.ZodString;
|
|
189
167
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -265,11 +243,52 @@ export declare const SlideContent: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
265
243
|
focus: "focus";
|
|
266
244
|
}>>;
|
|
267
245
|
frame: z.ZodOptional<z.ZodBoolean>;
|
|
268
|
-
}, z.core.$strip>], "kind"
|
|
246
|
+
}, z.core.$strip>], "kind">, z.ZodObject<{
|
|
247
|
+
media: z.ZodObject<{
|
|
248
|
+
kind: z.ZodEnum<{
|
|
249
|
+
deck: "deck";
|
|
250
|
+
page: "page";
|
|
251
|
+
paper: "paper";
|
|
252
|
+
book: "book";
|
|
253
|
+
media: "media";
|
|
254
|
+
shot: "shot";
|
|
255
|
+
}>;
|
|
256
|
+
address: z.ZodString;
|
|
257
|
+
page: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
|
|
258
|
+
box: z.ZodOptional<z.ZodEnum<{
|
|
259
|
+
media: "media";
|
|
260
|
+
crop: "crop";
|
|
261
|
+
trim: "trim";
|
|
262
|
+
bleed: "bleed";
|
|
263
|
+
art: "art";
|
|
264
|
+
}>>;
|
|
265
|
+
crop: z.ZodOptional<z.ZodObject<{
|
|
266
|
+
top: z.ZodOptional<z.ZodString>;
|
|
267
|
+
right: z.ZodOptional<z.ZodString>;
|
|
268
|
+
bottom: z.ZodOptional<z.ZodString>;
|
|
269
|
+
left: z.ZodOptional<z.ZodString>;
|
|
270
|
+
}, z.core.$strip>>;
|
|
271
|
+
fit: z.ZodOptional<z.ZodEnum<{
|
|
272
|
+
width: "width";
|
|
273
|
+
contain: "contain";
|
|
274
|
+
cover: "cover";
|
|
275
|
+
focus: "focus";
|
|
276
|
+
}>>;
|
|
277
|
+
alt: z.ZodOptional<z.ZodObject<{
|
|
278
|
+
text: z.ZodString;
|
|
279
|
+
lang: z.ZodOptional<z.ZodString>;
|
|
280
|
+
dir: z.ZodOptional<z.ZodEnum<{
|
|
281
|
+
auto: "auto";
|
|
282
|
+
ltr: "ltr";
|
|
283
|
+
rtl: "rtl";
|
|
284
|
+
}>>;
|
|
285
|
+
}, z.core.$strip>>;
|
|
286
|
+
}, z.core.$strip>;
|
|
287
|
+
}, z.core.$strip>]>;
|
|
269
288
|
export type SlideContent = z.infer<typeof SlideContent>;
|
|
270
289
|
export declare const Slide: z.ZodObject<{
|
|
271
290
|
id: z.ZodString;
|
|
272
|
-
content: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
291
|
+
content: z.ZodUnion<readonly [z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
273
292
|
kind: z.ZodLiteral<"html">;
|
|
274
293
|
html: z.ZodString;
|
|
275
294
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -351,15 +370,56 @@ export declare const Slide: z.ZodObject<{
|
|
|
351
370
|
focus: "focus";
|
|
352
371
|
}>>;
|
|
353
372
|
frame: z.ZodOptional<z.ZodBoolean>;
|
|
354
|
-
}, z.core.$strip>], "kind"
|
|
373
|
+
}, z.core.$strip>], "kind">, z.ZodObject<{
|
|
374
|
+
media: z.ZodObject<{
|
|
375
|
+
kind: z.ZodEnum<{
|
|
376
|
+
deck: "deck";
|
|
377
|
+
page: "page";
|
|
378
|
+
paper: "paper";
|
|
379
|
+
book: "book";
|
|
380
|
+
media: "media";
|
|
381
|
+
shot: "shot";
|
|
382
|
+
}>;
|
|
383
|
+
address: z.ZodString;
|
|
384
|
+
page: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
|
|
385
|
+
box: z.ZodOptional<z.ZodEnum<{
|
|
386
|
+
media: "media";
|
|
387
|
+
crop: "crop";
|
|
388
|
+
trim: "trim";
|
|
389
|
+
bleed: "bleed";
|
|
390
|
+
art: "art";
|
|
391
|
+
}>>;
|
|
392
|
+
crop: z.ZodOptional<z.ZodObject<{
|
|
393
|
+
top: z.ZodOptional<z.ZodString>;
|
|
394
|
+
right: z.ZodOptional<z.ZodString>;
|
|
395
|
+
bottom: z.ZodOptional<z.ZodString>;
|
|
396
|
+
left: z.ZodOptional<z.ZodString>;
|
|
397
|
+
}, z.core.$strip>>;
|
|
398
|
+
fit: z.ZodOptional<z.ZodEnum<{
|
|
399
|
+
width: "width";
|
|
400
|
+
contain: "contain";
|
|
401
|
+
cover: "cover";
|
|
402
|
+
focus: "focus";
|
|
403
|
+
}>>;
|
|
404
|
+
alt: z.ZodOptional<z.ZodObject<{
|
|
405
|
+
text: z.ZodString;
|
|
406
|
+
lang: z.ZodOptional<z.ZodString>;
|
|
407
|
+
dir: z.ZodOptional<z.ZodEnum<{
|
|
408
|
+
auto: "auto";
|
|
409
|
+
ltr: "ltr";
|
|
410
|
+
rtl: "rtl";
|
|
411
|
+
}>>;
|
|
412
|
+
}, z.core.$strip>>;
|
|
413
|
+
}, z.core.$strip>;
|
|
414
|
+
}, z.core.$strip>]>;
|
|
355
415
|
notes: z.ZodOptional<z.ZodString>;
|
|
356
416
|
narration: z.ZodOptional<z.ZodObject<{
|
|
357
417
|
text: z.ZodString;
|
|
358
418
|
lang: z.ZodOptional<z.ZodString>;
|
|
359
419
|
dir: z.ZodOptional<z.ZodEnum<{
|
|
420
|
+
auto: "auto";
|
|
360
421
|
ltr: "ltr";
|
|
361
422
|
rtl: "rtl";
|
|
362
|
-
auto: "auto";
|
|
363
423
|
}>>;
|
|
364
424
|
voice: z.ZodOptional<z.ZodString>;
|
|
365
425
|
words: z.ZodArray<z.ZodObject<{
|
|
@@ -389,9 +449,9 @@ export declare const DECK_SIZE: {
|
|
|
389
449
|
export declare const PLAYBACK_MODES: readonly ["segmented", "continuous"];
|
|
390
450
|
export type PlaybackMode = (typeof PLAYBACK_MODES)[number];
|
|
391
451
|
export declare const Playback: z.ZodUnion<readonly [z.ZodEnum<{
|
|
392
|
-
continuous: "continuous";
|
|
393
452
|
segmented: "segmented";
|
|
394
|
-
|
|
453
|
+
continuous: "continuous";
|
|
454
|
+
}>, z.ZodPipe<z.ZodLiteral<"step">, z.ZodTransform<"segmented" | "continuous", "step">>]>;
|
|
395
455
|
export declare function parsePlayback(value: unknown): PlaybackMode | undefined;
|
|
396
456
|
export declare function annotationCueIssues(narration: Slide['narration'], annotations: number): string[];
|
|
397
457
|
export declare const Deck: z.ZodObject<{
|
|
@@ -416,9 +476,9 @@ export declare const Deck: z.ZodObject<{
|
|
|
416
476
|
sandboxed: "sandboxed";
|
|
417
477
|
}>>;
|
|
418
478
|
dir: z.ZodOptional<z.ZodEnum<{
|
|
479
|
+
auto: "auto";
|
|
419
480
|
ltr: "ltr";
|
|
420
481
|
rtl: "rtl";
|
|
421
|
-
auto: "auto";
|
|
422
482
|
}>>;
|
|
423
483
|
lang: z.ZodOptional<z.ZodString>;
|
|
424
484
|
transition: z.ZodOptional<z.ZodObject<{
|
|
@@ -430,9 +490,9 @@ export declare const Deck: z.ZodObject<{
|
|
|
430
490
|
duration: z.ZodDefault<z.ZodNumber>;
|
|
431
491
|
}, z.core.$strip>>;
|
|
432
492
|
playback: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
|
|
433
|
-
continuous: "continuous";
|
|
434
493
|
segmented: "segmented";
|
|
435
|
-
|
|
494
|
+
continuous: "continuous";
|
|
495
|
+
}>, z.ZodPipe<z.ZodLiteral<"step">, z.ZodTransform<"segmented" | "continuous", "step">>]>>;
|
|
436
496
|
captions: z.ZodOptional<z.ZodEnum<{
|
|
437
497
|
on: "on";
|
|
438
498
|
off: "off";
|
|
@@ -443,11 +503,11 @@ export declare const Deck: z.ZodObject<{
|
|
|
443
503
|
label: z.ZodString;
|
|
444
504
|
type: z.ZodEnum<{
|
|
445
505
|
text: "text";
|
|
506
|
+
image: "image";
|
|
446
507
|
phone: "phone";
|
|
447
508
|
email: "email";
|
|
448
509
|
link: "link";
|
|
449
510
|
qr: "qr";
|
|
450
|
-
image: "image";
|
|
451
511
|
}>;
|
|
452
512
|
required: z.ZodDefault<z.ZodBoolean>;
|
|
453
513
|
placeholder: z.ZodOptional<z.ZodString>;
|
|
@@ -461,7 +521,7 @@ export declare const Deck: z.ZodObject<{
|
|
|
461
521
|
}, z.core.$strip>, z.ZodTransform<{
|
|
462
522
|
key: string;
|
|
463
523
|
label: string;
|
|
464
|
-
type: "text" | "
|
|
524
|
+
type: "text" | "image" | "phone" | "email" | "link" | "qr";
|
|
465
525
|
required: boolean;
|
|
466
526
|
overflow: "refuse" | "shrink" | "truncate" | "wrap";
|
|
467
527
|
placeholder?: string | undefined;
|
|
@@ -469,7 +529,7 @@ export declare const Deck: z.ZodObject<{
|
|
|
469
529
|
}, {
|
|
470
530
|
key: string;
|
|
471
531
|
label: string;
|
|
472
|
-
type: "text" | "
|
|
532
|
+
type: "text" | "image" | "phone" | "email" | "link" | "qr";
|
|
473
533
|
required: boolean;
|
|
474
534
|
overflow: "refuse" | "shrink" | "truncate" | "wrap";
|
|
475
535
|
placeholder?: string | undefined;
|
|
@@ -477,7 +537,7 @@ export declare const Deck: z.ZodObject<{
|
|
|
477
537
|
}>>>>;
|
|
478
538
|
slides: z.ZodArray<z.ZodObject<{
|
|
479
539
|
id: z.ZodString;
|
|
480
|
-
content: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
540
|
+
content: z.ZodUnion<readonly [z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
481
541
|
kind: z.ZodLiteral<"html">;
|
|
482
542
|
html: z.ZodString;
|
|
483
543
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -559,15 +619,56 @@ export declare const Deck: z.ZodObject<{
|
|
|
559
619
|
focus: "focus";
|
|
560
620
|
}>>;
|
|
561
621
|
frame: z.ZodOptional<z.ZodBoolean>;
|
|
562
|
-
}, z.core.$strip>], "kind"
|
|
622
|
+
}, z.core.$strip>], "kind">, z.ZodObject<{
|
|
623
|
+
media: z.ZodObject<{
|
|
624
|
+
kind: z.ZodEnum<{
|
|
625
|
+
deck: "deck";
|
|
626
|
+
page: "page";
|
|
627
|
+
paper: "paper";
|
|
628
|
+
book: "book";
|
|
629
|
+
media: "media";
|
|
630
|
+
shot: "shot";
|
|
631
|
+
}>;
|
|
632
|
+
address: z.ZodString;
|
|
633
|
+
page: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
|
|
634
|
+
box: z.ZodOptional<z.ZodEnum<{
|
|
635
|
+
media: "media";
|
|
636
|
+
crop: "crop";
|
|
637
|
+
trim: "trim";
|
|
638
|
+
bleed: "bleed";
|
|
639
|
+
art: "art";
|
|
640
|
+
}>>;
|
|
641
|
+
crop: z.ZodOptional<z.ZodObject<{
|
|
642
|
+
top: z.ZodOptional<z.ZodString>;
|
|
643
|
+
right: z.ZodOptional<z.ZodString>;
|
|
644
|
+
bottom: z.ZodOptional<z.ZodString>;
|
|
645
|
+
left: z.ZodOptional<z.ZodString>;
|
|
646
|
+
}, z.core.$strip>>;
|
|
647
|
+
fit: z.ZodOptional<z.ZodEnum<{
|
|
648
|
+
width: "width";
|
|
649
|
+
contain: "contain";
|
|
650
|
+
cover: "cover";
|
|
651
|
+
focus: "focus";
|
|
652
|
+
}>>;
|
|
653
|
+
alt: z.ZodOptional<z.ZodObject<{
|
|
654
|
+
text: z.ZodString;
|
|
655
|
+
lang: z.ZodOptional<z.ZodString>;
|
|
656
|
+
dir: z.ZodOptional<z.ZodEnum<{
|
|
657
|
+
auto: "auto";
|
|
658
|
+
ltr: "ltr";
|
|
659
|
+
rtl: "rtl";
|
|
660
|
+
}>>;
|
|
661
|
+
}, z.core.$strip>>;
|
|
662
|
+
}, z.core.$strip>;
|
|
663
|
+
}, z.core.$strip>]>;
|
|
563
664
|
notes: z.ZodOptional<z.ZodString>;
|
|
564
665
|
narration: z.ZodOptional<z.ZodObject<{
|
|
565
666
|
text: z.ZodString;
|
|
566
667
|
lang: z.ZodOptional<z.ZodString>;
|
|
567
668
|
dir: z.ZodOptional<z.ZodEnum<{
|
|
669
|
+
auto: "auto";
|
|
568
670
|
ltr: "ltr";
|
|
569
671
|
rtl: "rtl";
|
|
570
|
-
auto: "auto";
|
|
571
672
|
}>>;
|
|
572
673
|
voice: z.ZodOptional<z.ZodString>;
|
|
573
674
|
words: z.ZodArray<z.ZodObject<{
|
|
@@ -647,4 +748,5 @@ export interface DeckLimits {
|
|
|
647
748
|
maxNotesBytes?: number;
|
|
648
749
|
}
|
|
649
750
|
export declare function exceedsLimits(deck: Deck, limits: DeckLimits): string[];
|
|
650
|
-
//#endregion
|
|
751
|
+
//#endregion
|
|
752
|
+
export { Box, ImageFit, Point, Size };
|
package/dist/deck.js
CHANGED
|
@@ -1,30 +1,13 @@
|
|
|
1
1
|
import { Direction } from "./direction.js";
|
|
2
|
+
import { Box, Point, Size } from "./geometry.js";
|
|
3
|
+
import { ImageFit } from "./image-fit.js";
|
|
4
|
+
import { Placement, PlacementShape } from "./placement.js";
|
|
2
5
|
import { Scripts } from "./scripts.js";
|
|
3
6
|
import { Slots } from "./slots.js";
|
|
4
7
|
import { SlideTiming, slideTimingIssues } from "./timing.js";
|
|
5
8
|
import { Transition } from "./transition.js";
|
|
6
9
|
import { z } from "zod";
|
|
7
10
|
//#region src/deck.ts
|
|
8
|
-
const Size = z.object({
|
|
9
|
-
width: z.number().int().positive(),
|
|
10
|
-
height: z.number().int().positive()
|
|
11
|
-
});
|
|
12
|
-
const Box = z.object({
|
|
13
|
-
x: z.number(),
|
|
14
|
-
y: z.number(),
|
|
15
|
-
width: z.number(),
|
|
16
|
-
height: z.number()
|
|
17
|
-
});
|
|
18
|
-
const Point = z.object({
|
|
19
|
-
x: z.number(),
|
|
20
|
-
y: z.number()
|
|
21
|
-
});
|
|
22
|
-
const ImageFit = z.enum([
|
|
23
|
-
"contain",
|
|
24
|
-
"cover",
|
|
25
|
-
"width",
|
|
26
|
-
"focus"
|
|
27
|
-
]);
|
|
28
11
|
const Annotation = z.object({
|
|
29
12
|
ring: Box.nullable(),
|
|
30
13
|
badge: z.object({
|
|
@@ -77,7 +60,7 @@ const Narration = z.object({
|
|
|
77
60
|
live: z.boolean().default(false),
|
|
78
61
|
cues: z.array(Cue).default([])
|
|
79
62
|
});
|
|
80
|
-
const
|
|
63
|
+
const DirectSlideContent = z.discriminatedUnion("kind", [
|
|
81
64
|
z.object({
|
|
82
65
|
kind: z.literal("html"),
|
|
83
66
|
html: z.string()
|
|
@@ -107,6 +90,7 @@ const SlideContent = z.discriminatedUnion("kind", [
|
|
|
107
90
|
frame: z.boolean().optional()
|
|
108
91
|
})
|
|
109
92
|
]);
|
|
93
|
+
const SlideContent = z.union([DirectSlideContent, z.object({ media: PlacementShape })]);
|
|
110
94
|
const Slide = z.object({
|
|
111
95
|
id: z.string(),
|
|
112
96
|
content: SlideContent,
|
|
@@ -153,18 +137,32 @@ function annotationCueIssues(narration, annotations) {
|
|
|
153
137
|
function cueIssues(slide) {
|
|
154
138
|
const narration = slide.narration;
|
|
155
139
|
if (!narration) return [];
|
|
156
|
-
const annotations = slide.content.kind === "shot" ? slide.content.shot.annotations.length : null;
|
|
140
|
+
const annotations = "kind" in slide.content && slide.content.kind === "shot" ? slide.content.shot.annotations.length : null;
|
|
157
141
|
const words = narration.live ? null : narration.words.length;
|
|
158
142
|
const issues = [];
|
|
159
143
|
for (const cue of narration.cues) {
|
|
160
144
|
for (const word of [cue.word, cue.untilWord]) if (word !== void 0 && words !== null && word >= words) issues.push(`a cue names word ${word}, but the narration has ${words} words`);
|
|
161
|
-
if (slide.content.kind !== "shot" && slide.content.kind !== "shot-ref") issues.push("a cue names an annotation, but only a shot carries annotations");
|
|
145
|
+
if (!("kind" in slide.content) || slide.content.kind !== "shot" && slide.content.kind !== "shot-ref") issues.push("a cue names an annotation, but only a shot carries annotations");
|
|
162
146
|
}
|
|
163
147
|
if (annotations !== null) issues.push(...annotationCueIssues(narration, annotations));
|
|
164
148
|
return issues;
|
|
165
149
|
}
|
|
166
150
|
const Deck = DeckShape.superRefine((deck, ctx) => {
|
|
167
151
|
deck.slides.forEach((slide, index) => {
|
|
152
|
+
if ("media" in slide.content) try {
|
|
153
|
+
Placement.parse(slide.content.media, "deck");
|
|
154
|
+
} catch (error) {
|
|
155
|
+
ctx.addIssue({
|
|
156
|
+
code: "custom",
|
|
157
|
+
path: [
|
|
158
|
+
"slides",
|
|
159
|
+
index,
|
|
160
|
+
"content",
|
|
161
|
+
"media"
|
|
162
|
+
],
|
|
163
|
+
message: error instanceof Error ? error.message : String(error)
|
|
164
|
+
});
|
|
165
|
+
}
|
|
168
166
|
for (const message of cueIssues(slide)) ctx.addIssue({
|
|
169
167
|
code: "custom",
|
|
170
168
|
path: [
|
|
@@ -211,7 +209,7 @@ function exceedsLimits(deck, limits) {
|
|
|
211
209
|
const bytes = (text) => new TextEncoder().encode(text).length;
|
|
212
210
|
if (limits.maxSlides !== void 0 && deck.slides.length > limits.maxSlides) reasons.push(`the deck has ${deck.slides.length} slides; the limit is ${limits.maxSlides}`);
|
|
213
211
|
deck.slides.forEach((slide, index) => {
|
|
214
|
-
if (limits.maxSlideBytes !== void 0 && slide.content.kind === "html" && bytes(slide.content.html) > limits.maxSlideBytes) reasons.push(`slide ${index + 1} is ${bytes(slide.content.html)} bytes; the limit is ${limits.maxSlideBytes}`);
|
|
212
|
+
if (limits.maxSlideBytes !== void 0 && "kind" in slide.content && slide.content.kind === "html" && bytes(slide.content.html) > limits.maxSlideBytes) reasons.push(`slide ${index + 1} is ${bytes(slide.content.html)} bytes; the limit is ${limits.maxSlideBytes}`);
|
|
215
213
|
if (limits.maxNotesBytes !== void 0 && slide.notes && bytes(slide.notes) > limits.maxNotesBytes) reasons.push(`slide ${index + 1} has ${bytes(slide.notes)} bytes of notes; the limit is ${limits.maxNotesBytes}`);
|
|
216
214
|
});
|
|
217
215
|
return reasons;
|
package/dist/direction.d.ts
CHANGED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/font-collection.d.ts
|
|
2
|
+
export type ExtractedFontFace = {
|
|
3
|
+
data: Uint8Array;
|
|
4
|
+
format: 'truetype' | 'opentype';
|
|
5
|
+
};
|
|
6
|
+
export declare function collectionFaces(bytes: Uint8Array): string[];
|
|
7
|
+
export declare function extractFace(bytes: Uint8Array, postscriptName: string): ExtractedFontFace;
|
|
8
|
+
//#endregion
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { FontFaceRefusedError } from "./font-refusal.js";
|
|
2
|
+
import { classifyFont } from "./font-signature.js";
|
|
3
|
+
//#region src/font-collection.ts
|
|
4
|
+
const MAX_COLLECTION_FACES = 64;
|
|
5
|
+
const MAX_FACE_TABLES = 64;
|
|
6
|
+
const MAX_FACE_NAME_RECORDS = 1024;
|
|
7
|
+
const MAX_FACE_NAME_BYTES = 1048576;
|
|
8
|
+
const CHECKSUM_MAGIC = 2981146554;
|
|
9
|
+
function tagAt(bytes, offset) {
|
|
10
|
+
return String.fromCharCode(bytes[offset] ?? 0, bytes[offset + 1] ?? 0, bytes[offset + 2] ?? 0, bytes[offset + 3] ?? 0);
|
|
11
|
+
}
|
|
12
|
+
function refuse(message, remedy) {
|
|
13
|
+
throw new FontFaceRefusedError(`${message}; ${remedy} to clear this refusal.`);
|
|
14
|
+
}
|
|
15
|
+
function malformedCollection(offset) {
|
|
16
|
+
refuse(`font collection header is malformed at byte ${offset}`, "supply a complete font collection");
|
|
17
|
+
}
|
|
18
|
+
function malformedFace(offset) {
|
|
19
|
+
refuse(`font face table directory is malformed at byte ${offset}`, "supply a complete supported font face");
|
|
20
|
+
}
|
|
21
|
+
function collectionView(bytes) {
|
|
22
|
+
return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
23
|
+
}
|
|
24
|
+
function requireCollectionRange(bytes, offset, length) {
|
|
25
|
+
const end = offset + length;
|
|
26
|
+
if (!Number.isSafeInteger(offset) || !Number.isSafeInteger(length) || !Number.isSafeInteger(end) || offset < 0 || length < 0 || end > bytes.length) malformedCollection(Math.max(0, offset));
|
|
27
|
+
}
|
|
28
|
+
function requireFaceRange(bytes, offset, length) {
|
|
29
|
+
const end = offset + length;
|
|
30
|
+
if (!Number.isSafeInteger(offset) || !Number.isSafeInteger(length) || !Number.isSafeInteger(end) || offset < 0 || length < 0 || end > bytes.length) malformedFace(Math.max(0, offset));
|
|
31
|
+
}
|
|
32
|
+
function collectionOffsets(bytes) {
|
|
33
|
+
requireCollectionRange(bytes, 0, 12);
|
|
34
|
+
if (tagAt(bytes, 0) !== "ttcf") malformedCollection(0);
|
|
35
|
+
const view = collectionView(bytes);
|
|
36
|
+
const version = view.getUint32(4);
|
|
37
|
+
if (version !== 65536 && version !== 131072) malformedCollection(4);
|
|
38
|
+
const count = view.getUint32(8);
|
|
39
|
+
if (count > MAX_COLLECTION_FACES) refuse(`font collection declares ${count} faces, above the limit ${MAX_COLLECTION_FACES}`, `supply a collection with at most ${MAX_COLLECTION_FACES} faces`);
|
|
40
|
+
requireCollectionRange(bytes, 12, count * 4);
|
|
41
|
+
const offsets = [];
|
|
42
|
+
for (let index = 0; index < count; index += 1) offsets.push(view.getUint32(12 + index * 4));
|
|
43
|
+
if (version === 131072) requireCollectionRange(bytes, 12 + count * 4, 12);
|
|
44
|
+
return offsets;
|
|
45
|
+
}
|
|
46
|
+
function sfntFormat(bytes, offset) {
|
|
47
|
+
requireFaceRange(bytes, offset, 4);
|
|
48
|
+
const format = classifyFont(bytes, offset);
|
|
49
|
+
if (format === "opentype" || format === "truetype") return format;
|
|
50
|
+
malformedFace(offset);
|
|
51
|
+
}
|
|
52
|
+
function faceTables(bytes, offset) {
|
|
53
|
+
requireFaceRange(bytes, offset, 12);
|
|
54
|
+
const view = collectionView(bytes);
|
|
55
|
+
const count = view.getUint16(offset + 4);
|
|
56
|
+
if (count > MAX_FACE_TABLES) refuse(`font face declares ${count} tables, above the limit ${MAX_FACE_TABLES}`, `supply a face with at most ${MAX_FACE_TABLES} tables`);
|
|
57
|
+
requireFaceRange(bytes, offset + 12, count * 16);
|
|
58
|
+
const tables = [];
|
|
59
|
+
for (let index = 0; index < count; index += 1) {
|
|
60
|
+
const record = offset + 12 + index * 16;
|
|
61
|
+
const table = {
|
|
62
|
+
tag: tagAt(bytes, record),
|
|
63
|
+
offset: view.getUint32(record + 8),
|
|
64
|
+
length: view.getUint32(record + 12)
|
|
65
|
+
};
|
|
66
|
+
const end = table.offset + table.length;
|
|
67
|
+
if (!Number.isSafeInteger(end) || end > bytes.length) refuse(`font table ${table.tag} lies outside the font file`, "supply table offsets and lengths within the font data");
|
|
68
|
+
tables.push(table);
|
|
69
|
+
}
|
|
70
|
+
const ordered = tables.filter((table) => table.length > 0).sort((left, right) => left.offset - right.offset);
|
|
71
|
+
for (let index = 1; index < ordered.length; index += 1) {
|
|
72
|
+
const previous = ordered[index - 1];
|
|
73
|
+
const table = ordered[index];
|
|
74
|
+
if (previous && table && table.offset < previous.offset + previous.length) refuse(`font table ${table.tag} overlaps font table ${previous.tag}`, "supply a face whose table byte ranges do not overlap");
|
|
75
|
+
}
|
|
76
|
+
return tables;
|
|
77
|
+
}
|
|
78
|
+
function decodeUtf16Be(bytes) {
|
|
79
|
+
let value = "";
|
|
80
|
+
for (let index = 0; index + 1 < bytes.length; index += 2) value += String.fromCharCode((bytes[index] ?? 0) * 256 + (bytes[index + 1] ?? 0));
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
function decodeMacRoman(bytes) {
|
|
84
|
+
return new TextDecoder("macintosh").decode(bytes);
|
|
85
|
+
}
|
|
86
|
+
function sfntPostScriptNames(bytes, faceOffset = 0) {
|
|
87
|
+
sfntFormat(bytes, faceOffset);
|
|
88
|
+
const name = faceTables(bytes, faceOffset).find((table) => table.tag === "name");
|
|
89
|
+
if (!name) return [];
|
|
90
|
+
if (name.length < 6) refuse("font table name lies outside the font file", "supply a complete name table");
|
|
91
|
+
requireFaceRange(bytes, name.offset, name.length);
|
|
92
|
+
const view = collectionView(bytes);
|
|
93
|
+
const count = view.getUint16(name.offset + 2);
|
|
94
|
+
if (count > MAX_FACE_NAME_RECORDS) refuse(`font name table declares ${count} records, above the limit ${MAX_FACE_NAME_RECORDS}`, `supply a face with at most ${MAX_FACE_NAME_RECORDS} name records`);
|
|
95
|
+
const strings = view.getUint16(name.offset + 4);
|
|
96
|
+
if (6 + count * 12 > name.length) refuse("font table name lies outside the font file", "supply a complete name table");
|
|
97
|
+
const records = /* @__PURE__ */ new Map();
|
|
98
|
+
let decodedBytes = 0;
|
|
99
|
+
for (let index = 0; index < count; index += 1) {
|
|
100
|
+
const record = name.offset + 6 + index * 12;
|
|
101
|
+
const platform = view.getUint16(record);
|
|
102
|
+
if (view.getUint16(record + 6) !== 6 || platform !== 1 && platform !== 3) continue;
|
|
103
|
+
const length = view.getUint16(record + 8);
|
|
104
|
+
const relative = view.getUint16(record + 10);
|
|
105
|
+
if (strings + relative + length > name.length) refuse("font table name lies outside the font file", "supply a complete name table");
|
|
106
|
+
const start = name.offset + strings + relative;
|
|
107
|
+
const key = `${platform}:${start}:${length}`;
|
|
108
|
+
if (records.has(key)) continue;
|
|
109
|
+
decodedBytes += length;
|
|
110
|
+
if (decodedBytes > MAX_FACE_NAME_BYTES) refuse(`font name table requires ${decodedBytes} decoded bytes, above the limit ${MAX_FACE_NAME_BYTES}`, `supply a face whose PostScript names occupy at most ${MAX_FACE_NAME_BYTES} bytes`);
|
|
111
|
+
records.set(key, {
|
|
112
|
+
platform,
|
|
113
|
+
start,
|
|
114
|
+
length
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
const names = [...records.values()].map(({ platform, start, length }) => {
|
|
118
|
+
const encoded = bytes.subarray(start, start + length);
|
|
119
|
+
return {
|
|
120
|
+
platform,
|
|
121
|
+
value: platform === 3 ? decodeUtf16Be(encoded) : decodeMacRoman(encoded)
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
const preferred = names.filter((name) => name.platform === 3);
|
|
125
|
+
return [...new Set((preferred.length > 0 ? preferred : names).map((name) => name.value))];
|
|
126
|
+
}
|
|
127
|
+
function collectionFaces(bytes) {
|
|
128
|
+
return collectionOffsets(bytes).flatMap((offset) => sfntPostScriptNames(bytes, offset));
|
|
129
|
+
}
|
|
130
|
+
function checksum(bytes, offset = 0, length = bytes.length) {
|
|
131
|
+
let sum = 0;
|
|
132
|
+
for (let index = 0; index < length; index += 4) {
|
|
133
|
+
const word = ((bytes[offset + index] ?? 0) << 24 | (bytes[offset + index + 1] ?? 0) << 16 | (bytes[offset + index + 2] ?? 0) << 8 | (bytes[offset + index + 3] ?? 0)) >>> 0;
|
|
134
|
+
sum = sum + word >>> 0;
|
|
135
|
+
}
|
|
136
|
+
return sum;
|
|
137
|
+
}
|
|
138
|
+
function directoryFields(count) {
|
|
139
|
+
const power = 2 ** Math.floor(Math.log2(Math.max(1, count)));
|
|
140
|
+
return {
|
|
141
|
+
searchRange: power * 16,
|
|
142
|
+
entrySelector: Math.log2(power),
|
|
143
|
+
rangeShift: count * 16 - power * 16
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function extractAt(bytes, faceOffset) {
|
|
147
|
+
const format = sfntFormat(bytes, faceOffset);
|
|
148
|
+
const tables = faceTables(bytes, faceOffset).filter((table) => table.tag !== "DSIG");
|
|
149
|
+
const directoryLength = 12 + tables.length * 16;
|
|
150
|
+
let total = directoryLength;
|
|
151
|
+
for (const table of tables) total += Math.ceil(table.length / 4) * 4;
|
|
152
|
+
if (!Number.isSafeInteger(total) || total > bytes.length) refuse(`font face extraction requires ${total} bytes, above the font file size ${bytes.length}`, "supply a face whose table data fits within the font file");
|
|
153
|
+
const data = new Uint8Array(total);
|
|
154
|
+
const view = new DataView(data.buffer);
|
|
155
|
+
data.set(bytes.subarray(faceOffset, faceOffset + 4), 0);
|
|
156
|
+
view.setUint16(4, tables.length);
|
|
157
|
+
const fields = directoryFields(tables.length);
|
|
158
|
+
view.setUint16(6, fields.searchRange);
|
|
159
|
+
view.setUint16(8, fields.entrySelector);
|
|
160
|
+
view.setUint16(10, fields.rangeShift);
|
|
161
|
+
let outputOffset = directoryLength;
|
|
162
|
+
let headOffset;
|
|
163
|
+
for (let index = 0; index < tables.length; index += 1) {
|
|
164
|
+
const table = tables[index];
|
|
165
|
+
if (!table) continue;
|
|
166
|
+
const record = 12 + index * 16;
|
|
167
|
+
for (let byte = 0; byte < 4; byte += 1) data[record + byte] = table.tag.charCodeAt(byte);
|
|
168
|
+
data.set(bytes.subarray(table.offset, table.offset + table.length), outputOffset);
|
|
169
|
+
if (table.tag === "head") {
|
|
170
|
+
if (table.length < 12) refuse("font table head lies outside the font file", "supply a complete head table");
|
|
171
|
+
new DataView(data.buffer).setUint32(outputOffset + 8, 0);
|
|
172
|
+
headOffset = outputOffset;
|
|
173
|
+
}
|
|
174
|
+
view.setUint32(record + 4, checksum(data, outputOffset, table.length));
|
|
175
|
+
view.setUint32(record + 8, outputOffset);
|
|
176
|
+
view.setUint32(record + 12, table.length);
|
|
177
|
+
outputOffset += Math.ceil(table.length / 4) * 4;
|
|
178
|
+
}
|
|
179
|
+
if (headOffset !== void 0) view.setUint32(headOffset + 8, CHECKSUM_MAGIC - checksum(data) >>> 0);
|
|
180
|
+
return {
|
|
181
|
+
data,
|
|
182
|
+
format
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function extractFace(bytes, postscriptName) {
|
|
186
|
+
const offsets = collectionOffsets(bytes);
|
|
187
|
+
for (const offset of offsets) if (sfntPostScriptNames(bytes, offset).includes(postscriptName)) return extractAt(bytes, offset);
|
|
188
|
+
refuse(`font collection has no face named ${postscriptName}; it holds ${collectionFaces(bytes).join(", ").slice(0, 1024) || "(none)"}`, "send the PostScript name of a face the collection holds");
|
|
189
|
+
}
|
|
190
|
+
//#endregion
|
|
191
|
+
export { collectionFaces, extractFace, sfntPostScriptNames };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { FontFormat } from "./fonts.js";
|
|
2
|
+
//#region src/font-face.d.ts
|
|
3
|
+
export interface ResolvedFontFace {
|
|
4
|
+
data: Uint8Array;
|
|
5
|
+
format: Exclude<FontFormat, 'collection'>;
|
|
6
|
+
}
|
|
7
|
+
export declare function resolveFontFace(data: Uint8Array, format: FontFormat, postscriptName?: string): ResolvedFontFace;
|
|
8
|
+
//#endregion
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { FontFaceRefusedError } from "./font-refusal.js";
|
|
2
|
+
import { classifyFont } from "./font-signature.js";
|
|
3
|
+
import { collectionFaces, extractFace, sfntPostScriptNames } from "./font-collection.js";
|
|
4
|
+
//#region src/font-face.ts
|
|
5
|
+
function resolveFontFace(data, format, postscriptName) {
|
|
6
|
+
const detected = classifyFont(data);
|
|
7
|
+
if (detected === "collection") {
|
|
8
|
+
if (!postscriptName) throw new FontFaceRefusedError(`font collection requires a PostScript name; it holds ${collectionFaces(data).join(", ").slice(0, 1024) || "(none)"}; send one of those PostScript names to clear this refusal.`);
|
|
9
|
+
return extractFace(data, postscriptName);
|
|
10
|
+
}
|
|
11
|
+
if (format === "collection") throw new FontFaceRefusedError("font declared as a collection does not contain a font collection; send bytes with a ttcf signature to clear this refusal.");
|
|
12
|
+
if (!postscriptName || detected === "woff" || detected === "woff2") return {
|
|
13
|
+
data,
|
|
14
|
+
format
|
|
15
|
+
};
|
|
16
|
+
const names = sfntPostScriptNames(data);
|
|
17
|
+
if (names.includes(postscriptName) && (detected === "opentype" || detected === "truetype")) return {
|
|
18
|
+
data,
|
|
19
|
+
format: detected
|
|
20
|
+
};
|
|
21
|
+
throw new FontFaceRefusedError(`font face has PostScript name ${names.join(", ").slice(0, 1024) || "(none)"}, not ${postscriptName}; send its matching PostScript name to clear this refusal.`);
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { resolveFontFace };
|