@segnavia/format 0.1.0 → 0.3.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.
@@ -0,0 +1,62 @@
1
+ import { PaperBox } from "./paper.js";
2
+ import { Imposition, Page, Print } from "./index.js";
3
+ //#region src/imposition.d.ts
4
+ export interface MillimeterEdges {
5
+ top: number;
6
+ right: number;
7
+ bottom: number;
8
+ left: number;
9
+ }
10
+ export interface Placement {
11
+ page: number;
12
+ paper: number;
13
+ side: 'front' | 'back';
14
+ column: number;
15
+ row: number;
16
+ trim: PaperBox;
17
+ bleed: MillimeterEdges;
18
+ bleedBox: PaperBox;
19
+ safeBox: PaperBox;
20
+ clipped: Array<'top' | 'right' | 'bottom' | 'left'>;
21
+ }
22
+ export interface Imposed {
23
+ paper: {
24
+ width: number;
25
+ height: number;
26
+ };
27
+ papers: number;
28
+ sides: 1 | 2;
29
+ placements: Placement[];
30
+ }
31
+ export interface PageFit {
32
+ flow: boolean;
33
+ width: number;
34
+ height: number | null;
35
+ scale: number;
36
+ x: number;
37
+ y: number;
38
+ bands: {
39
+ inline: number;
40
+ block: number;
41
+ };
42
+ crop: MillimeterEdges;
43
+ differs: boolean;
44
+ }
45
+ export type PagePlacementKind = 'fluid' | 'growing' | 'fixed';
46
+ export interface PagePlacementLayout {
47
+ kind: PagePlacementKind;
48
+ piece: {
49
+ width: number;
50
+ height: number;
51
+ };
52
+ inset: boolean;
53
+ paper: PaperBox;
54
+ }
55
+ export declare function mirrorPlacement(front: Placement, paper: Imposed['paper'], flip: Imposition['flip'], page?: number): Placement;
56
+ export declare function impose(imposition: Imposition, pageCount: number): Imposed;
57
+ export declare function fitPage(page: {
58
+ size: Page['size'];
59
+ print?: Print;
60
+ }, placement: Placement, fit: 'contain' | 'cover'): PageFit;
61
+ export declare function pagePlacementLayout(size: Page['size'], placement: Placement, fit: PageFit): PagePlacementLayout;
62
+ //#endregion
@@ -0,0 +1,294 @@
1
+ import { PX_PER_MM, roundMillimeters, sheetMm, toMillimeters } from "./paper.js";
2
+ //#region src/imposition.ts
3
+ const zeroCrop = () => ({
4
+ top: 0,
5
+ right: 0,
6
+ bottom: 0,
7
+ left: 0
8
+ });
9
+ function placementAt(imposition, paper, cell, page, paperSize) {
10
+ const mm = (value) => toMillimeters(value, imposition.unit);
11
+ const column = cell % imposition.columns;
12
+ const row = Math.floor(cell / imposition.columns);
13
+ const physicalColumn = imposition.dir === "rtl" ? imposition.columns - column - 1 : column;
14
+ const cellWidth = mm(imposition.cell.width);
15
+ const cellHeight = mm(imposition.cell.height);
16
+ const columnGap = mm(imposition.gap.column);
17
+ const rowGap = mm(imposition.gap.row);
18
+ const x = imposition.dir === "rtl" ? paperSize.width - mm(imposition.margin.right) - cellWidth - column * (cellWidth + columnGap) : mm(imposition.margin.left) + column * (cellWidth + columnGap);
19
+ const y = mm(imposition.margin.top) + row * (cellHeight + rowGap);
20
+ const requested = mm(imposition.bleed);
21
+ const bleed = {
22
+ top: Math.min(requested, row > 0 ? rowGap / 2 : y),
23
+ right: Math.min(requested, physicalColumn < imposition.columns - 1 ? columnGap / 2 : Math.max(0, paperSize.width - x - cellWidth)),
24
+ bottom: Math.min(requested, row < imposition.rows - 1 ? rowGap / 2 : Math.max(0, paperSize.height - y - cellHeight)),
25
+ left: Math.min(requested, physicalColumn > 0 ? columnGap / 2 : Math.max(0, x))
26
+ };
27
+ const clipped = [
28
+ "top",
29
+ "right",
30
+ "bottom",
31
+ "left"
32
+ ].filter((side) => bleed[side] < requested);
33
+ const trim = {
34
+ x: roundMillimeters(x),
35
+ y: roundMillimeters(y),
36
+ width: roundMillimeters(cellWidth),
37
+ height: roundMillimeters(cellHeight)
38
+ };
39
+ const roundedBleed = {
40
+ top: roundMillimeters(bleed.top),
41
+ right: roundMillimeters(bleed.right),
42
+ bottom: roundMillimeters(bleed.bottom),
43
+ left: roundMillimeters(bleed.left)
44
+ };
45
+ const safe = mm(imposition.safe);
46
+ return {
47
+ page,
48
+ paper,
49
+ side: "front",
50
+ column,
51
+ row,
52
+ trim,
53
+ bleed: roundedBleed,
54
+ bleedBox: {
55
+ x: roundMillimeters(x - bleed.left),
56
+ y: roundMillimeters(y - bleed.top),
57
+ width: roundMillimeters(cellWidth + bleed.left + bleed.right),
58
+ height: roundMillimeters(cellHeight + bleed.top + bleed.bottom)
59
+ },
60
+ safeBox: {
61
+ x: roundMillimeters(x + safe),
62
+ y: roundMillimeters(y + safe),
63
+ width: roundMillimeters(cellWidth - safe * 2),
64
+ height: roundMillimeters(cellHeight - safe * 2)
65
+ },
66
+ clipped
67
+ };
68
+ }
69
+ const mirrorBox = (box, paper, axis) => axis === "x" ? {
70
+ ...box,
71
+ x: roundMillimeters(paper.width - box.x - box.width)
72
+ } : {
73
+ ...box,
74
+ y: roundMillimeters(paper.height - box.y - box.height)
75
+ };
76
+ const EDGE_ORDER = [
77
+ "top",
78
+ "right",
79
+ "bottom",
80
+ "left"
81
+ ];
82
+ function mirrorEdge(edge, axis) {
83
+ if (axis === "x") return edge === "left" ? "right" : edge === "right" ? "left" : edge;
84
+ return edge === "top" ? "bottom" : edge === "bottom" ? "top" : edge;
85
+ }
86
+ function mirrorEdges(edges, axis) {
87
+ const mirrored = { ...edges };
88
+ for (const edge of EDGE_ORDER) mirrored[mirrorEdge(edge, axis)] = edges[edge];
89
+ return mirrored;
90
+ }
91
+ function mirrorPlacement(front, paper, flip, page = front.page) {
92
+ const portrait = paper.width <= paper.height;
93
+ const axis = flip === "long" === portrait ? "x" : "y";
94
+ const mirroredClipped = new Set(front.clipped.map((edge) => mirrorEdge(edge, axis)));
95
+ return {
96
+ ...front,
97
+ page,
98
+ side: "back",
99
+ trim: mirrorBox(front.trim, paper, axis),
100
+ bleed: mirrorEdges(front.bleed, axis),
101
+ bleedBox: mirrorBox(front.bleedBox, paper, axis),
102
+ safeBox: mirrorBox(front.safeBox, paper, axis),
103
+ clipped: EDGE_ORDER.filter((edge) => mirroredClipped.has(edge))
104
+ };
105
+ }
106
+ function assignPieces(imposition, pieces, papers) {
107
+ const cells = imposition.columns * imposition.rows;
108
+ const assignments = [];
109
+ let piece = 0;
110
+ for (let paperIndex = 0; paperIndex < papers; paperIndex += 1) {
111
+ const firstCell = paperIndex === 0 ? imposition.skip : 0;
112
+ for (let cell = firstCell; cell < cells; cell += 1) {
113
+ const assignedPiece = imposition.fill === "repeat" ? paperIndex : piece;
114
+ if (assignedPiece >= pieces) break;
115
+ assignments.push({
116
+ paper: paperIndex,
117
+ cell,
118
+ piece: assignedPiece
119
+ });
120
+ if (imposition.fill === "sequence") piece += 1;
121
+ }
122
+ }
123
+ return assignments;
124
+ }
125
+ function expandAssignments(imposition, assignments, pageCount, paper) {
126
+ const placements = [];
127
+ let currentPaper = -1;
128
+ let fronts = [];
129
+ let backs = [];
130
+ const flush = () => {
131
+ placements.push(...fronts, ...backs);
132
+ fronts = [];
133
+ backs = [];
134
+ };
135
+ for (const assignment of assignments) {
136
+ if (assignment.paper !== currentPaper) {
137
+ flush();
138
+ currentPaper = assignment.paper;
139
+ }
140
+ const frontPage = imposition.sides === "two" ? assignment.piece * 2 : assignment.piece;
141
+ const front = placementAt(imposition, assignment.paper, assignment.cell, frontPage, paper);
142
+ fronts.push(front);
143
+ if (imposition.sides === "two" && frontPage + 1 < pageCount) backs.push(mirrorPlacement(front, paper, imposition.flip, frontPage + 1));
144
+ }
145
+ flush();
146
+ return placements;
147
+ }
148
+ function impose(imposition, pageCount) {
149
+ if (!Number.isInteger(pageCount) || pageCount < 0) throw new RangeError(`pageCount must be a nonnegative integer; received ${String(pageCount)}`);
150
+ const paper = sheetMm({
151
+ paper: imposition.paper,
152
+ orientation: imposition.orientation
153
+ });
154
+ const sides = imposition.sides === "two" ? 2 : 1;
155
+ if (pageCount === 0) return {
156
+ paper,
157
+ papers: 0,
158
+ sides,
159
+ placements: []
160
+ };
161
+ const cells = imposition.columns * imposition.rows;
162
+ const pieces = imposition.sides === "two" ? Math.ceil(pageCount / 2) : pageCount;
163
+ const papers = imposition.fill === "repeat" ? pieces : Math.ceil((pieces + imposition.skip) / cells);
164
+ return {
165
+ paper,
166
+ papers,
167
+ sides,
168
+ placements: expandAssignments(imposition, assignPieces(imposition, pieces, papers), pageCount, paper)
169
+ };
170
+ }
171
+ function trimDiffers(page, placement) {
172
+ if (!page.print) return false;
173
+ const trim = sheetMm(page.print);
174
+ return Math.abs(trim.width - placement.trim.width) > .01 || Math.abs(trim.height - placement.trim.height) > .01;
175
+ }
176
+ function fitPage(page, placement, fit) {
177
+ const differs = trimDiffers(page, placement);
178
+ if (page.size === "fluid") return {
179
+ flow: true,
180
+ width: placement.bleedBox.width * PX_PER_MM,
181
+ height: null,
182
+ scale: 1,
183
+ x: placement.bleedBox.x,
184
+ y: placement.bleedBox.y,
185
+ bands: {
186
+ inline: 0,
187
+ block: 0
188
+ },
189
+ crop: zeroCrop(),
190
+ differs
191
+ };
192
+ if (page.size.height === "auto") return {
193
+ flow: true,
194
+ width: page.size.width,
195
+ height: null,
196
+ scale: placement.trim.width * PX_PER_MM / page.size.width,
197
+ x: placement.trim.x,
198
+ y: placement.trim.y,
199
+ bands: {
200
+ inline: 0,
201
+ block: 0
202
+ },
203
+ crop: zeroCrop(),
204
+ differs
205
+ };
206
+ if (fit === "contain") {
207
+ const scale = Math.min(placement.trim.width * PX_PER_MM / page.size.width, placement.trim.height * PX_PER_MM / page.size.height);
208
+ const width = page.size.width * scale / PX_PER_MM;
209
+ const height = page.size.height * scale / PX_PER_MM;
210
+ const inline = (placement.trim.width - width) / 2;
211
+ const block = (placement.trim.height - height) / 2;
212
+ return {
213
+ flow: false,
214
+ width: page.size.width,
215
+ height: page.size.height,
216
+ scale,
217
+ x: roundMillimeters(placement.trim.x + inline),
218
+ y: roundMillimeters(placement.trim.y + block),
219
+ bands: {
220
+ inline: roundMillimeters(inline),
221
+ block: roundMillimeters(block)
222
+ },
223
+ crop: zeroCrop(),
224
+ differs
225
+ };
226
+ }
227
+ const horizontalBleed = Math.max(placement.bleed.left, placement.bleed.right);
228
+ const verticalBleed = Math.max(placement.bleed.top, placement.bleed.bottom);
229
+ const scale = Math.max((placement.trim.width + horizontalBleed * 2) * PX_PER_MM / page.size.width, (placement.trim.height + verticalBleed * 2) * PX_PER_MM / page.size.height);
230
+ const width = page.size.width * scale / PX_PER_MM;
231
+ const height = page.size.height * scale / PX_PER_MM;
232
+ const x = placement.trim.x + (placement.trim.width - width) / 2;
233
+ const y = placement.trim.y + (placement.trim.height - height) / 2;
234
+ return {
235
+ flow: false,
236
+ width: page.size.width,
237
+ height: page.size.height,
238
+ scale,
239
+ x: roundMillimeters(x),
240
+ y: roundMillimeters(y),
241
+ bands: {
242
+ inline: 0,
243
+ block: 0
244
+ },
245
+ crop: {
246
+ top: roundMillimeters(Math.max(0, placement.bleedBox.y - y)),
247
+ right: roundMillimeters(Math.max(0, x + width - placement.bleedBox.x - placement.bleedBox.width)),
248
+ bottom: roundMillimeters(Math.max(0, y + height - placement.bleedBox.y - placement.bleedBox.height)),
249
+ left: roundMillimeters(Math.max(0, placement.bleedBox.x - x))
250
+ },
251
+ differs
252
+ };
253
+ }
254
+ function pagePlacementLayout(size, placement, fit) {
255
+ if (size === "fluid") return {
256
+ kind: "fluid",
257
+ piece: {
258
+ width: placement.bleedBox.width * PX_PER_MM,
259
+ height: placement.bleedBox.height * PX_PER_MM
260
+ },
261
+ inset: true,
262
+ paper: placement.bleedBox
263
+ };
264
+ if (size.height === "auto") return {
265
+ kind: "growing",
266
+ piece: {
267
+ width: size.width,
268
+ height: placement.trim.height * PX_PER_MM / fit.scale
269
+ },
270
+ inset: false,
271
+ paper: {
272
+ x: fit.x,
273
+ y: fit.y,
274
+ width: placement.trim.width,
275
+ height: placement.trim.height
276
+ }
277
+ };
278
+ return {
279
+ kind: "fixed",
280
+ piece: {
281
+ width: size.width,
282
+ height: size.height
283
+ },
284
+ inset: false,
285
+ paper: {
286
+ x: fit.x,
287
+ y: fit.y,
288
+ width: size.width * fit.scale / PX_PER_MM,
289
+ height: size.height * fit.scale / PX_PER_MM
290
+ }
291
+ };
292
+ }
293
+ //#endregion
294
+ export { fitPage, impose, mirrorPlacement, pagePlacementLayout };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,9 @@
1
1
  import { TextDirection, firstStrongDirection, languageDirection, resolveDirection } from "./direction.js";
2
+ import { MARK_GAP_MM, MARK_LENGTH_MM, MM_PER_PT, PAPER_MM, PX_PER_MM, PaperBox, printBoxes, printPadding, printProperties, roundMillimeters, sheetMm, toMillimeters } from "./paper.js";
3
+ import { Imposed, MillimeterEdges, PageFit, PagePlacementKind, PagePlacementLayout, Placement, fitPage, impose, mirrorPlacement, pagePlacementLayout } from "./imposition.js";
4
+ import { ShotUse, shotsUsed } from "./shots-used.js";
5
+ import { MAX_SLIDE_TIMING_MS } from "./timing.js";
6
+ import { DEFAULT_TRANSITION_DURATION_MS, MAX_TRANSITION_DURATION_MS, MIN_TRANSITION_DURATION_MS, Transition, TransitionInput } from "./transition.js";
2
7
  import { z } from "zod";
3
8
  //#region src/index.d.ts
4
9
  export declare const Size: z.ZodObject<{
@@ -30,6 +35,7 @@ export declare const Paper: z.ZodUnion<readonly [z.ZodEnum<{
30
35
  unit: z.ZodEnum<{
31
36
  mm: "mm";
32
37
  in: "in";
38
+ pt: "pt";
33
39
  }>;
34
40
  }, z.core.$strip>]>;
35
41
  export type Paper = z.infer<typeof Paper>;
@@ -53,6 +59,7 @@ export declare const Print: z.ZodObject<{
53
59
  unit: z.ZodEnum<{
54
60
  mm: "mm";
55
61
  in: "in";
62
+ pt: "pt";
56
63
  }>;
57
64
  }, z.core.$strip>]>;
58
65
  orientation: z.ZodDefault<z.ZodEnum<{
@@ -65,8 +72,81 @@ export declare const Print: z.ZodObject<{
65
72
  bottom: z.ZodDefault<z.ZodNumber>;
66
73
  left: z.ZodDefault<z.ZodNumber>;
67
74
  }, z.core.$strip>>;
75
+ bleed: z.ZodDefault<z.ZodNumber>;
76
+ safe: z.ZodDefault<z.ZodNumber>;
77
+ marks: z.ZodDefault<z.ZodBoolean>;
68
78
  }, z.core.$strip>;
69
79
  export type Print = z.infer<typeof Print>;
80
+ export type PrintInput = z.input<typeof Print>;
81
+ export declare const MAX_IMPOSITION_CELLS = 10000;
82
+ export declare const Imposition: z.ZodObject<{
83
+ paper: z.ZodUnion<readonly [z.ZodEnum<{
84
+ letter: "letter";
85
+ legal: "legal";
86
+ tabloid: "tabloid";
87
+ a3: "a3";
88
+ a4: "a4";
89
+ a5: "a5";
90
+ }>, z.ZodObject<{
91
+ width: z.ZodNumber;
92
+ height: z.ZodNumber;
93
+ unit: z.ZodEnum<{
94
+ mm: "mm";
95
+ in: "in";
96
+ pt: "pt";
97
+ }>;
98
+ }, z.core.$strip>]>;
99
+ orientation: z.ZodDefault<z.ZodEnum<{
100
+ portrait: "portrait";
101
+ landscape: "landscape";
102
+ }>>;
103
+ unit: z.ZodDefault<z.ZodEnum<{
104
+ mm: "mm";
105
+ in: "in";
106
+ pt: "pt";
107
+ }>>;
108
+ margin: z.ZodDefault<z.ZodObject<{
109
+ top: z.ZodDefault<z.ZodNumber>;
110
+ right: z.ZodDefault<z.ZodNumber>;
111
+ bottom: z.ZodDefault<z.ZodNumber>;
112
+ left: z.ZodDefault<z.ZodNumber>;
113
+ }, z.core.$strip>>;
114
+ columns: z.ZodNumber;
115
+ rows: z.ZodNumber;
116
+ cell: z.ZodObject<{
117
+ width: z.ZodNumber;
118
+ height: z.ZodNumber;
119
+ }, z.core.$strip>;
120
+ gap: z.ZodDefault<z.ZodObject<{
121
+ column: z.ZodDefault<z.ZodNumber>;
122
+ row: z.ZodDefault<z.ZodNumber>;
123
+ }, z.core.$strip>>;
124
+ bleed: z.ZodDefault<z.ZodNumber>;
125
+ safe: z.ZodDefault<z.ZodNumber>;
126
+ fit: z.ZodDefault<z.ZodEnum<{
127
+ contain: "contain";
128
+ cover: "cover";
129
+ }>>;
130
+ fill: z.ZodDefault<z.ZodEnum<{
131
+ sequence: "sequence";
132
+ repeat: "repeat";
133
+ }>>;
134
+ sides: z.ZodDefault<z.ZodEnum<{
135
+ one: "one";
136
+ two: "two";
137
+ }>>;
138
+ flip: z.ZodDefault<z.ZodEnum<{
139
+ long: "long";
140
+ short: "short";
141
+ }>>;
142
+ skip: z.ZodDefault<z.ZodNumber>;
143
+ dir: z.ZodDefault<z.ZodEnum<{
144
+ ltr: "ltr";
145
+ rtl: "rtl";
146
+ }>>;
147
+ }, z.core.$strip>;
148
+ export type Imposition = z.infer<typeof Imposition>;
149
+ export type ImpositionInput = z.input<typeof Imposition>;
70
150
  export declare const PageSize: z.ZodUnion<readonly [z.ZodLiteral<"fluid">, z.ZodObject<{
71
151
  width: z.ZodNumber;
72
152
  height: z.ZodNumber;
@@ -108,6 +188,7 @@ export declare const Page: z.ZodObject<{
108
188
  unit: z.ZodEnum<{
109
189
  mm: "mm";
110
190
  in: "in";
191
+ pt: "pt";
111
192
  }>;
112
193
  }, z.core.$strip>]>;
113
194
  orientation: z.ZodDefault<z.ZodEnum<{
@@ -120,6 +201,9 @@ export declare const Page: z.ZodObject<{
120
201
  bottom: z.ZodDefault<z.ZodNumber>;
121
202
  left: z.ZodDefault<z.ZodNumber>;
122
203
  }, z.core.$strip>>;
204
+ bleed: z.ZodDefault<z.ZodNumber>;
205
+ safe: z.ZodDefault<z.ZodNumber>;
206
+ marks: z.ZodDefault<z.ZodBoolean>;
123
207
  }, z.core.$strip>>;
124
208
  }, z.core.$strip>;
125
209
  export type Page = z.infer<typeof Page>;
@@ -445,6 +529,9 @@ export declare const Slide: z.ZodObject<{
445
529
  untilWord: z.ZodOptional<z.ZodNumber>;
446
530
  }, z.core.$strip>>>;
447
531
  }, z.core.$strip>>;
532
+ beforeMs: z.ZodOptional<z.ZodNumber>;
533
+ afterMs: z.ZodOptional<z.ZodNumber>;
534
+ holdMs: z.ZodOptional<z.ZodNumber>;
448
535
  }, z.core.$strip>;
449
536
  export type Slide = z.infer<typeof Slide>;
450
537
  export declare const DECK_SIZE: {
@@ -468,6 +555,23 @@ export declare const Deck: z.ZodObject<{
468
555
  auto: "auto";
469
556
  }>>;
470
557
  lang: z.ZodOptional<z.ZodString>;
558
+ transition: z.ZodOptional<z.ZodObject<{
559
+ kind: z.ZodEnum<{
560
+ none: "none";
561
+ fade: "fade";
562
+ slide: "slide";
563
+ }>;
564
+ duration: z.ZodDefault<z.ZodNumber>;
565
+ }, z.core.$strip>>;
566
+ playback: z.ZodOptional<z.ZodEnum<{
567
+ step: "step";
568
+ continuous: "continuous";
569
+ }>>;
570
+ captions: z.ZodOptional<z.ZodEnum<{
571
+ on: "on";
572
+ off: "off";
573
+ viewer: "viewer";
574
+ }>>;
471
575
  slides: z.ZodArray<z.ZodObject<{
472
576
  id: z.ZodString;
473
577
  content: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -556,6 +660,9 @@ export declare const Deck: z.ZodObject<{
556
660
  untilWord: z.ZodOptional<z.ZodNumber>;
557
661
  }, z.core.$strip>>>;
558
662
  }, z.core.$strip>>;
663
+ beforeMs: z.ZodOptional<z.ZodNumber>;
664
+ afterMs: z.ZodOptional<z.ZodNumber>;
665
+ holdMs: z.ZodOptional<z.ZodNumber>;
559
666
  }, z.core.$strip>>;
560
667
  }, z.core.$strip>;
561
668
  export type Deck = z.infer<typeof Deck>;
@@ -615,12 +722,6 @@ export interface DeckLimits {
615
722
  maxNotesBytes?: number;
616
723
  }
617
724
  export declare function exceedsLimits(deck: Deck, limits: DeckLimits): string[];
618
- export interface ShotUse {
619
- slide: string;
620
- key?: string;
621
- href?: string;
622
- }
623
- export declare function shotsUsed(deck: Deck): ShotUse[];
624
725
  export declare function jsonSchemas(): {
625
726
  page: z.core.ZodStandardJSONSchemaPayload<z.ZodObject<{
626
727
  html: z.ZodString;
@@ -655,6 +756,7 @@ export declare function jsonSchemas(): {
655
756
  unit: z.ZodEnum<{
656
757
  mm: "mm";
657
758
  in: "in";
759
+ pt: "pt";
658
760
  }>;
659
761
  }, z.core.$strip>]>;
660
762
  orientation: z.ZodDefault<z.ZodEnum<{
@@ -667,8 +769,77 @@ export declare function jsonSchemas(): {
667
769
  bottom: z.ZodDefault<z.ZodNumber>;
668
770
  left: z.ZodDefault<z.ZodNumber>;
669
771
  }, z.core.$strip>>;
772
+ bleed: z.ZodDefault<z.ZodNumber>;
773
+ safe: z.ZodDefault<z.ZodNumber>;
774
+ marks: z.ZodDefault<z.ZodBoolean>;
670
775
  }, z.core.$strip>>;
671
776
  }, z.core.$strip>>;
777
+ imposition: z.core.ZodStandardJSONSchemaPayload<z.ZodObject<{
778
+ paper: z.ZodUnion<readonly [z.ZodEnum<{
779
+ letter: "letter";
780
+ legal: "legal";
781
+ tabloid: "tabloid";
782
+ a3: "a3";
783
+ a4: "a4";
784
+ a5: "a5";
785
+ }>, z.ZodObject<{
786
+ width: z.ZodNumber;
787
+ height: z.ZodNumber;
788
+ unit: z.ZodEnum<{
789
+ mm: "mm";
790
+ in: "in";
791
+ pt: "pt";
792
+ }>;
793
+ }, z.core.$strip>]>;
794
+ orientation: z.ZodDefault<z.ZodEnum<{
795
+ portrait: "portrait";
796
+ landscape: "landscape";
797
+ }>>;
798
+ unit: z.ZodDefault<z.ZodEnum<{
799
+ mm: "mm";
800
+ in: "in";
801
+ pt: "pt";
802
+ }>>;
803
+ margin: z.ZodDefault<z.ZodObject<{
804
+ top: z.ZodDefault<z.ZodNumber>;
805
+ right: z.ZodDefault<z.ZodNumber>;
806
+ bottom: z.ZodDefault<z.ZodNumber>;
807
+ left: z.ZodDefault<z.ZodNumber>;
808
+ }, z.core.$strip>>;
809
+ columns: z.ZodNumber;
810
+ rows: z.ZodNumber;
811
+ cell: z.ZodObject<{
812
+ width: z.ZodNumber;
813
+ height: z.ZodNumber;
814
+ }, z.core.$strip>;
815
+ gap: z.ZodDefault<z.ZodObject<{
816
+ column: z.ZodDefault<z.ZodNumber>;
817
+ row: z.ZodDefault<z.ZodNumber>;
818
+ }, z.core.$strip>>;
819
+ bleed: z.ZodDefault<z.ZodNumber>;
820
+ safe: z.ZodDefault<z.ZodNumber>;
821
+ fit: z.ZodDefault<z.ZodEnum<{
822
+ contain: "contain";
823
+ cover: "cover";
824
+ }>>;
825
+ fill: z.ZodDefault<z.ZodEnum<{
826
+ sequence: "sequence";
827
+ repeat: "repeat";
828
+ }>>;
829
+ sides: z.ZodDefault<z.ZodEnum<{
830
+ one: "one";
831
+ two: "two";
832
+ }>>;
833
+ flip: z.ZodDefault<z.ZodEnum<{
834
+ long: "long";
835
+ short: "short";
836
+ }>>;
837
+ skip: z.ZodDefault<z.ZodNumber>;
838
+ dir: z.ZodDefault<z.ZodEnum<{
839
+ ltr: "ltr";
840
+ rtl: "rtl";
841
+ }>>;
842
+ }, z.core.$strip>>;
672
843
  deck: z.core.ZodStandardJSONSchemaPayload<z.ZodObject<{
673
844
  title: z.ZodString;
674
845
  size: z.ZodDefault<z.ZodObject<{
@@ -685,6 +856,23 @@ export declare function jsonSchemas(): {
685
856
  auto: "auto";
686
857
  }>>;
687
858
  lang: z.ZodOptional<z.ZodString>;
859
+ transition: z.ZodOptional<z.ZodObject<{
860
+ kind: z.ZodEnum<{
861
+ none: "none";
862
+ fade: "fade";
863
+ slide: "slide";
864
+ }>;
865
+ duration: z.ZodDefault<z.ZodNumber>;
866
+ }, z.core.$strip>>;
867
+ playback: z.ZodOptional<z.ZodEnum<{
868
+ step: "step";
869
+ continuous: "continuous";
870
+ }>>;
871
+ captions: z.ZodOptional<z.ZodEnum<{
872
+ on: "on";
873
+ off: "off";
874
+ viewer: "viewer";
875
+ }>>;
688
876
  slides: z.ZodArray<z.ZodObject<{
689
877
  id: z.ZodString;
690
878
  content: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -773,6 +961,9 @@ export declare function jsonSchemas(): {
773
961
  untilWord: z.ZodOptional<z.ZodNumber>;
774
962
  }, z.core.$strip>>>;
775
963
  }, z.core.$strip>>;
964
+ beforeMs: z.ZodOptional<z.ZodNumber>;
965
+ afterMs: z.ZodOptional<z.ZodNumber>;
966
+ holdMs: z.ZodOptional<z.ZodNumber>;
776
967
  }, z.core.$strip>>;
777
968
  }, z.core.$strip>>;
778
969
  manifest: z.core.ZodStandardJSONSchemaPayload<z.ZodObject<{
@@ -872,14 +1063,5 @@ export declare function jsonSchemas(): {
872
1063
  }, z.core.$strip>;
873
1064
  }, z.core.$strip>>;
874
1065
  };
875
- export declare const PAPER_MM: Record<(typeof NAMED_PAPERS)[number], {
876
- width: number;
877
- height: number;
878
- }>;
879
- export declare function sheetMm(print: Print): {
880
- width: number;
881
- height: number;
882
- };
883
- export declare const PX_PER_MM: number;
884
1066
  //#endregion
885
- export { type TextDirection, firstStrongDirection, languageDirection, resolveDirection };
1067
+ export { DEFAULT_TRANSITION_DURATION_MS, type Imposed, MARK_GAP_MM, MARK_LENGTH_MM, MAX_SLIDE_TIMING_MS, MAX_TRANSITION_DURATION_MS, MIN_TRANSITION_DURATION_MS, MM_PER_PT, type MillimeterEdges, PAPER_MM, PX_PER_MM, type PageFit, type PagePlacementKind, type PagePlacementLayout, type PaperBox, type Placement, type ShotUse, type TextDirection, Transition, TransitionInput, firstStrongDirection, fitPage, impose, languageDirection, mirrorPlacement, pagePlacementLayout, printBoxes, printPadding, printProperties, resolveDirection, roundMillimeters, sheetMm, shotsUsed, toMillimeters };
package/dist/index.js CHANGED
@@ -1,4 +1,9 @@
1
+ import { MARK_GAP_MM, MARK_LENGTH_MM, MM_PER_PT, PAPER_MM, PX_PER_MM, printBoxes, printPadding, printProperties, roundMillimeters, sheetMm, toMillimeters } from "./paper.js";
2
+ import { MAX_SLIDE_TIMING_MS, SlideTiming, slideTimingIssues } from "./timing.js";
3
+ import { DEFAULT_TRANSITION_DURATION_MS, MAX_TRANSITION_DURATION_MS, MIN_TRANSITION_DURATION_MS, Transition } from "./transition.js";
1
4
  import { firstStrongDirection, languageDirection, resolveDirection } from "./direction.js";
5
+ import { fitPage, impose, mirrorPlacement, pagePlacementLayout } from "./imposition.js";
6
+ import { shotsUsed } from "./shots-used.js";
2
7
  import { z } from "zod";
3
8
  //#region src/index.ts
4
9
  const Size = z.object({
@@ -22,7 +27,11 @@ const NAMED_PAPERS = [
22
27
  const Paper = z.union([z.enum(NAMED_PAPERS), z.object({
23
28
  width: z.number().positive(),
24
29
  height: z.number().positive(),
25
- unit: z.enum(["mm", "in"])
30
+ unit: z.enum([
31
+ "mm",
32
+ "in",
33
+ "pt"
34
+ ])
26
35
  })]);
27
36
  const Margin = z.object({
28
37
  top: z.number().nonnegative().default(0),
@@ -33,7 +42,106 @@ const Margin = z.object({
33
42
  const Print = z.object({
34
43
  paper: Paper,
35
44
  orientation: z.enum(["portrait", "landscape"]).default("portrait"),
36
- margin: Margin.optional()
45
+ margin: Margin.optional(),
46
+ bleed: z.number().nonnegative().default(0),
47
+ safe: z.number().nonnegative().default(0),
48
+ marks: z.boolean().default(false)
49
+ }).superRefine((print, ctx) => {
50
+ const trim = sheetMm(print);
51
+ if (print.safe * 2 >= trim.width) ctx.addIssue({
52
+ code: "custom",
53
+ path: ["safe"],
54
+ message: `safe is ${print.safe}mm on the left and right, but the trim is ${trim.width}mm wide; safe must leave area inside the trim`
55
+ });
56
+ if (print.safe * 2 >= trim.height) ctx.addIssue({
57
+ code: "custom",
58
+ path: ["safe"],
59
+ message: `safe is ${print.safe}mm on the top and bottom, but the trim is ${trim.height}mm high; safe must leave area inside the trim`
60
+ });
61
+ });
62
+ const ImpositionGap = z.object({
63
+ column: z.number().nonnegative().default(0),
64
+ row: z.number().nonnegative().default(0)
65
+ });
66
+ const MAX_IMPOSITION_CELLS = 1e4;
67
+ const Imposition = z.object({
68
+ paper: Paper,
69
+ orientation: z.enum(["portrait", "landscape"]).default("portrait"),
70
+ unit: z.enum([
71
+ "mm",
72
+ "in",
73
+ "pt"
74
+ ]).default("mm"),
75
+ margin: Margin.default({
76
+ top: 0,
77
+ right: 0,
78
+ bottom: 0,
79
+ left: 0
80
+ }),
81
+ columns: z.number().int().positive(),
82
+ rows: z.number().int().positive(),
83
+ cell: z.object({
84
+ width: z.number().positive(),
85
+ height: z.number().positive()
86
+ }),
87
+ gap: ImpositionGap.default({
88
+ column: 0,
89
+ row: 0
90
+ }),
91
+ bleed: z.number().nonnegative().default(0),
92
+ safe: z.number().nonnegative().default(0),
93
+ fit: z.enum(["contain", "cover"]).default("contain"),
94
+ fill: z.enum(["sequence", "repeat"]).default("sequence"),
95
+ sides: z.enum(["one", "two"]).default("one"),
96
+ flip: z.enum(["long", "short"]).default("long"),
97
+ skip: z.number().int().nonnegative().default(0),
98
+ dir: z.enum(["ltr", "rtl"]).default("ltr")
99
+ }).superRefine((imposition, ctx) => {
100
+ const cellCount = BigInt(imposition.columns) * BigInt(imposition.rows);
101
+ if (imposition.columns > 1e4 || imposition.rows > 1e4 || cellCount > BigInt(1e4)) {
102
+ ctx.addIssue({
103
+ code: "custom",
104
+ path: ["columns"],
105
+ message: `the grid has ${cellCount} cells and the ceiling is ${MAX_IMPOSITION_CELLS}; fewer columns or rows clears this refusal`
106
+ });
107
+ return;
108
+ }
109
+ const mm = (value) => toMillimeters(value, imposition.unit);
110
+ const paper = sheetMm({
111
+ paper: imposition.paper,
112
+ orientation: imposition.orientation
113
+ });
114
+ const gridWidth = mm(imposition.margin.left + imposition.columns * imposition.cell.width + (imposition.columns - 1) * imposition.gap.column + imposition.margin.right);
115
+ const gridHeight = mm(imposition.margin.top + imposition.rows * imposition.cell.height + (imposition.rows - 1) * imposition.gap.row + imposition.margin.bottom);
116
+ if (gridWidth - paper.width > .01) ctx.addIssue({
117
+ code: "custom",
118
+ path: ["columns"],
119
+ message: `the grid is ${roundMillimeters(gridWidth)}mm wide and the paper is ${paper.width}mm wide, an overflow of ${roundMillimeters(gridWidth - paper.width)}mm; reduce the horizontal margins, columns, cell width or column gap`
120
+ });
121
+ if (gridHeight - paper.height > .01) ctx.addIssue({
122
+ code: "custom",
123
+ path: ["rows"],
124
+ message: `the grid is ${roundMillimeters(gridHeight)}mm high and the paper is ${paper.height}mm high, an overflow of ${roundMillimeters(gridHeight - paper.height)}mm; reduce the vertical margins, rows, cell height or row gap`
125
+ });
126
+ const safe = mm(imposition.safe);
127
+ const cellWidth = mm(imposition.cell.width);
128
+ const cellHeight = mm(imposition.cell.height);
129
+ if (safe * 2 >= cellWidth) ctx.addIssue({
130
+ code: "custom",
131
+ path: ["safe"],
132
+ message: `safe is ${roundMillimeters(safe)}mm on the left and right, but the cell is ${roundMillimeters(cellWidth)}mm wide; safe must leave area inside the cell`
133
+ });
134
+ if (safe * 2 >= cellHeight) ctx.addIssue({
135
+ code: "custom",
136
+ path: ["safe"],
137
+ message: `safe is ${roundMillimeters(safe)}mm on the top and bottom, but the cell is ${roundMillimeters(cellHeight)}mm high; safe must leave area inside the cell`
138
+ });
139
+ const cells = imposition.columns * imposition.rows;
140
+ if (imposition.skip >= cells) ctx.addIssue({
141
+ code: "custom",
142
+ path: ["skip"],
143
+ message: `skip is ${imposition.skip}, but the grid has ${cells} cells; skip must be less than ${cells}`
144
+ });
37
145
  });
38
146
  const PageSize = z.union([
39
147
  z.literal("fluid"),
@@ -141,7 +249,10 @@ const Slide = z.object({
141
249
  id: z.string(),
142
250
  content: SlideContent,
143
251
  notes: z.string().optional(),
144
- narration: Narration.optional()
252
+ narration: Narration.optional(),
253
+ beforeMs: SlideTiming.optional(),
254
+ afterMs: SlideTiming.optional(),
255
+ holdMs: SlideTiming.optional()
145
256
  });
146
257
  const DECK_SIZE = {
147
258
  width: 1280,
@@ -153,6 +264,13 @@ const DeckShape = z.object({
153
264
  scripts: Scripts.default("refuse"),
154
265
  dir: Direction.optional(),
155
266
  lang: z.string().optional(),
267
+ transition: Transition.optional(),
268
+ playback: z.enum(["step", "continuous"]).optional(),
269
+ captions: z.enum([
270
+ "on",
271
+ "off",
272
+ "viewer"
273
+ ]).optional(),
156
274
  slides: z.array(Slide).min(1)
157
275
  });
158
276
  function annotationCueIssues(narration, annotations) {
@@ -184,6 +302,18 @@ const Deck = DeckShape.superRefine((deck, ctx) => {
184
302
  ],
185
303
  message
186
304
  });
305
+ for (const field of slideTimingIssues(slide)) {
306
+ const kind = field === "holdMs" ? "without" : "with";
307
+ ctx.addIssue({
308
+ code: "custom",
309
+ path: [
310
+ "slides",
311
+ index,
312
+ field
313
+ ],
314
+ message: `${field} is allowed only ${kind} narration`
315
+ });
316
+ }
187
317
  });
188
318
  const ids = /* @__PURE__ */ new Set();
189
319
  deck.slides.forEach((slide, index) => {
@@ -213,71 +343,14 @@ function exceedsLimits(deck, limits) {
213
343
  });
214
344
  return reasons;
215
345
  }
216
- function shotsUsed(deck) {
217
- return deck.slides.flatMap((slide) => {
218
- if (slide.content.kind === "shot") return [{
219
- slide: slide.id,
220
- key: slide.content.shot.key
221
- }];
222
- if (slide.content.kind === "shot-ref") return [{
223
- slide: slide.id,
224
- href: slide.content.href
225
- }];
226
- return [];
227
- });
228
- }
229
346
  function jsonSchemas() {
230
347
  return {
231
348
  page: z.toJSONSchema(Page, { io: "input" }),
349
+ imposition: z.toJSONSchema(Imposition, { io: "input" }),
232
350
  deck: z.toJSONSchema(DeckShape, { io: "input" }),
233
351
  manifest: z.toJSONSchema(Manifest, { io: "input" }),
234
352
  publishedShot: z.toJSONSchema(PublishedShot, { io: "input" })
235
353
  };
236
354
  }
237
- const PAPER_MM = {
238
- letter: {
239
- width: 215.9,
240
- height: 279.4
241
- },
242
- legal: {
243
- width: 215.9,
244
- height: 355.6
245
- },
246
- tabloid: {
247
- width: 279.4,
248
- height: 431.8
249
- },
250
- a3: {
251
- width: 297,
252
- height: 420
253
- },
254
- a4: {
255
- width: 210,
256
- height: 297
257
- },
258
- a5: {
259
- width: 148,
260
- height: 210
261
- }
262
- };
263
- function sheetMm(print) {
264
- const portrait = typeof print.paper === "string" ? PAPER_MM[print.paper] : print.paper.unit === "in" ? {
265
- width: print.paper.width * 25.4,
266
- height: print.paper.height * 25.4
267
- } : {
268
- width: print.paper.width,
269
- height: print.paper.height
270
- };
271
- const [short, long] = [Math.min(portrait.width, portrait.height), Math.max(portrait.width, portrait.height)];
272
- const round = (mm) => Math.round(mm * 100) / 100;
273
- return print.orientation === "landscape" ? {
274
- width: round(long),
275
- height: round(short)
276
- } : {
277
- width: round(short),
278
- height: round(long)
279
- };
280
- }
281
- const PX_PER_MM = 96 / 25.4;
282
355
  //#endregion
283
- export { Annotation, Box, Cue, DECK_SIZE, Deck, Direction, Manifest, Margin, NAMED_PAPERS, Narration, PAPER_MM, PX_PER_MM, Page, PageSize, Paper, Point, Print, PublishedShot, SHOT_KEY, Scripts, Shot, Size, Slide, SlideContent, Word, annotationCueIssues, exceedsLimits, firstStrongDirection, jsonSchemas, languageDirection, resolveDirection, sheetMm, shotsUsed };
356
+ export { Annotation, Box, Cue, DECK_SIZE, DEFAULT_TRANSITION_DURATION_MS, Deck, Direction, Imposition, MARK_GAP_MM, MARK_LENGTH_MM, MAX_IMPOSITION_CELLS, MAX_SLIDE_TIMING_MS, MAX_TRANSITION_DURATION_MS, MIN_TRANSITION_DURATION_MS, MM_PER_PT, Manifest, Margin, NAMED_PAPERS, Narration, PAPER_MM, PX_PER_MM, Page, PageSize, Paper, Point, Print, PublishedShot, SHOT_KEY, Scripts, Shot, Size, Slide, SlideContent, Transition, Word, annotationCueIssues, exceedsLimits, firstStrongDirection, fitPage, impose, jsonSchemas, languageDirection, mirrorPlacement, pagePlacementLayout, printBoxes, printPadding, printProperties, resolveDirection, roundMillimeters, sheetMm, shotsUsed, toMillimeters };
@@ -0,0 +1,47 @@
1
+ import { Paper, Print } from "./index.js";
2
+ //#region src/paper.d.ts
3
+ export declare const MM_PER_PT: number;
4
+ export declare const MARK_LENGTH_MM = 5;
5
+ export declare const MARK_GAP_MM = 1;
6
+ export declare const PAPER_MM: Record<Extract<Paper, string>, {
7
+ width: number;
8
+ height: number;
9
+ }>;
10
+ export declare const roundMillimeters: (mm: number) => number;
11
+ export declare function toMillimeters(value: number, unit: 'mm' | 'in' | 'pt'): number;
12
+ export declare function sheetMm(print: Pick<Print, 'paper' | 'orientation'>): {
13
+ width: number;
14
+ height: number;
15
+ };
16
+ export declare const PX_PER_MM: number;
17
+ export declare function printProperties(print: Pick<Print, 'bleed' | 'safe'>, unit?: 'mm' | 'in' | 'pt'): string;
18
+ export declare function printPadding(edges: {
19
+ top: number;
20
+ right: number;
21
+ bottom: number;
22
+ left: number;
23
+ }): string;
24
+ export interface PaperBox {
25
+ x: number;
26
+ y: number;
27
+ width: number;
28
+ height: number;
29
+ }
30
+ export interface MarkSegment {
31
+ x1: number;
32
+ y1: number;
33
+ x2: number;
34
+ y2: number;
35
+ }
36
+ export interface PrintBoxes {
37
+ media: {
38
+ width: number;
39
+ height: number;
40
+ };
41
+ bleedBox: PaperBox;
42
+ trim: PaperBox;
43
+ safeBox: PaperBox;
44
+ marks: MarkSegment[];
45
+ }
46
+ export declare function printBoxes(print: Print): PrintBoxes;
47
+ //#endregion
package/dist/paper.js ADDED
@@ -0,0 +1,159 @@
1
+ //#region src/paper.ts
2
+ const MM_PER_PT = 25.4 / 72;
3
+ const MARK_LENGTH_MM = 5;
4
+ const MARK_GAP_MM = 1;
5
+ const PAPER_MM = {
6
+ letter: {
7
+ width: 215.9,
8
+ height: 279.4
9
+ },
10
+ legal: {
11
+ width: 215.9,
12
+ height: 355.6
13
+ },
14
+ tabloid: {
15
+ width: 279.4,
16
+ height: 431.8
17
+ },
18
+ a3: {
19
+ width: 297,
20
+ height: 420
21
+ },
22
+ a4: {
23
+ width: 210,
24
+ height: 297
25
+ },
26
+ a5: {
27
+ width: 148,
28
+ height: 210
29
+ }
30
+ };
31
+ const roundMillimeters = (mm) => Math.round(mm * 100) / 100;
32
+ function toMillimeters(value, unit) {
33
+ return value * (unit === "in" ? 25.4 : unit === "pt" ? MM_PER_PT : 1);
34
+ }
35
+ function sheetMm(print) {
36
+ const portrait = typeof print.paper === "string" ? PAPER_MM[print.paper] : {
37
+ width: toMillimeters(print.paper.width, print.paper.unit),
38
+ height: toMillimeters(print.paper.height, print.paper.unit)
39
+ };
40
+ const [short, long] = [Math.min(portrait.width, portrait.height), Math.max(portrait.width, portrait.height)];
41
+ return print.orientation === "landscape" ? {
42
+ width: roundMillimeters(long),
43
+ height: roundMillimeters(short)
44
+ } : {
45
+ width: roundMillimeters(short),
46
+ height: roundMillimeters(long)
47
+ };
48
+ }
49
+ const PX_PER_MM = 96 / 25.4;
50
+ function printProperties(print, unit = "mm") {
51
+ return `:root{--segnavia-bleed:${roundMillimeters(toMillimeters(print.bleed, unit))}mm;--segnavia-safe:${roundMillimeters(toMillimeters(print.safe, unit))}mm}`;
52
+ }
53
+ function printPadding(edges) {
54
+ return `:where(html){padding:${[
55
+ edges.top,
56
+ edges.right,
57
+ edges.bottom,
58
+ edges.left
59
+ ].map((value) => `${value}mm`).join(" ")}}`;
60
+ }
61
+ function printBoxes(print) {
62
+ const sheet = sheetMm(print);
63
+ const outside = print.marks ? 6 : 0;
64
+ const bleedBox = {
65
+ x: outside,
66
+ y: outside,
67
+ width: roundMillimeters(sheet.width + print.bleed * 2),
68
+ height: roundMillimeters(sheet.height + print.bleed * 2)
69
+ };
70
+ const trim = {
71
+ x: roundMillimeters(outside + print.bleed),
72
+ y: roundMillimeters(outside + print.bleed),
73
+ width: sheet.width,
74
+ height: sheet.height
75
+ };
76
+ const safeBox = {
77
+ x: roundMillimeters(trim.x + print.safe),
78
+ y: roundMillimeters(trim.y + print.safe),
79
+ width: roundMillimeters(trim.width - print.safe * 2),
80
+ height: roundMillimeters(trim.height - print.safe * 2)
81
+ };
82
+ const media = {
83
+ width: roundMillimeters(bleedBox.width + outside * 2),
84
+ height: roundMillimeters(bleedBox.height + outside * 2)
85
+ };
86
+ if (!print.marks) return {
87
+ media,
88
+ bleedBox,
89
+ trim,
90
+ safeBox,
91
+ marks: []
92
+ };
93
+ const leftStart = roundMillimeters(bleedBox.x - 1);
94
+ const rightStart = roundMillimeters(bleedBox.x + bleedBox.width + 1);
95
+ const topStart = roundMillimeters(bleedBox.y - 1);
96
+ const bottomStart = roundMillimeters(bleedBox.y + bleedBox.height + 1);
97
+ const right = roundMillimeters(rightStart + 5);
98
+ const bottom = roundMillimeters(bottomStart + 5);
99
+ const trimRight = roundMillimeters(trim.x + trim.width);
100
+ const trimBottom = roundMillimeters(trim.y + trim.height);
101
+ return {
102
+ media,
103
+ bleedBox,
104
+ trim,
105
+ safeBox,
106
+ marks: [
107
+ {
108
+ x1: roundMillimeters(leftStart - 5),
109
+ y1: trim.y,
110
+ x2: leftStart,
111
+ y2: trim.y
112
+ },
113
+ {
114
+ x1: trim.x,
115
+ y1: roundMillimeters(topStart - 5),
116
+ x2: trim.x,
117
+ y2: topStart
118
+ },
119
+ {
120
+ x1: rightStart,
121
+ y1: trim.y,
122
+ x2: right,
123
+ y2: trim.y
124
+ },
125
+ {
126
+ x1: trimRight,
127
+ y1: roundMillimeters(topStart - 5),
128
+ x2: trimRight,
129
+ y2: topStart
130
+ },
131
+ {
132
+ x1: roundMillimeters(leftStart - 5),
133
+ y1: trimBottom,
134
+ x2: leftStart,
135
+ y2: trimBottom
136
+ },
137
+ {
138
+ x1: trim.x,
139
+ y1: bottomStart,
140
+ x2: trim.x,
141
+ y2: bottom
142
+ },
143
+ {
144
+ x1: rightStart,
145
+ y1: trimBottom,
146
+ x2: right,
147
+ y2: trimBottom
148
+ },
149
+ {
150
+ x1: trimRight,
151
+ y1: bottomStart,
152
+ x2: trimRight,
153
+ y2: bottom
154
+ }
155
+ ]
156
+ };
157
+ }
158
+ //#endregion
159
+ export { MARK_GAP_MM, MARK_LENGTH_MM, MM_PER_PT, PAPER_MM, PX_PER_MM, printBoxes, printPadding, printProperties, roundMillimeters, sheetMm, toMillimeters };
@@ -0,0 +1,9 @@
1
+ import { Deck } from "./index.js";
2
+ //#region src/shots-used.d.ts
3
+ export interface ShotUse {
4
+ slide: string;
5
+ key?: string;
6
+ href?: string;
7
+ }
8
+ export declare function shotsUsed(deck: Deck): ShotUse[];
9
+ //#endregion
@@ -0,0 +1,16 @@
1
+ //#region src/shots-used.ts
2
+ function shotsUsed(deck) {
3
+ return deck.slides.flatMap((slide) => {
4
+ if (slide.content.kind === "shot") return [{
5
+ slide: slide.id,
6
+ key: slide.content.shot.key
7
+ }];
8
+ if (slide.content.kind === "shot-ref") return [{
9
+ slide: slide.id,
10
+ href: slide.content.href
11
+ }];
12
+ return [];
13
+ });
14
+ }
15
+ //#endregion
16
+ export { shotsUsed };
@@ -0,0 +1,4 @@
1
+ import { z } from "zod";
2
+ //#region src/timing.d.ts
3
+ export declare const MAX_SLIDE_TIMING_MS = 600000;
4
+ //#endregion
package/dist/timing.js ADDED
@@ -0,0 +1,10 @@
1
+ import { z } from "zod";
2
+ //#region src/timing.ts
3
+ const MAX_SLIDE_TIMING_MS = 6e5;
4
+ const SlideTiming = z.number().finite().min(0).max(MAX_SLIDE_TIMING_MS);
5
+ function slideTimingIssues(slide) {
6
+ if (slide.narration) return slide.holdMs === void 0 ? [] : ["holdMs"];
7
+ return ["beforeMs", "afterMs"].filter((field) => slide[field] !== void 0);
8
+ }
9
+ //#endregion
10
+ export { MAX_SLIDE_TIMING_MS, SlideTiming, slideTimingIssues };
@@ -0,0 +1,16 @@
1
+ import { z } from "zod";
2
+ //#region src/transition.d.ts
3
+ export declare const MIN_TRANSITION_DURATION_MS = 0;
4
+ export declare const MAX_TRANSITION_DURATION_MS = 2000;
5
+ export declare const DEFAULT_TRANSITION_DURATION_MS = 300;
6
+ export declare const Transition: z.ZodObject<{
7
+ kind: z.ZodEnum<{
8
+ none: "none";
9
+ fade: "fade";
10
+ slide: "slide";
11
+ }>;
12
+ duration: z.ZodDefault<z.ZodNumber>;
13
+ }, z.core.$strip>;
14
+ export type Transition = z.infer<typeof Transition>;
15
+ export type TransitionInput = z.input<typeof Transition>;
16
+ //#endregion
@@ -0,0 +1,15 @@
1
+ import { z } from "zod";
2
+ //#region src/transition.ts
3
+ const MIN_TRANSITION_DURATION_MS = 0;
4
+ const MAX_TRANSITION_DURATION_MS = 2e3;
5
+ const DEFAULT_TRANSITION_DURATION_MS = 300;
6
+ const Transition = z.object({
7
+ kind: z.enum([
8
+ "none",
9
+ "fade",
10
+ "slide"
11
+ ]),
12
+ duration: z.number().int().min(0).max(MAX_TRANSITION_DURATION_MS).default(300)
13
+ });
14
+ //#endregion
15
+ export { DEFAULT_TRANSITION_DURATION_MS, MAX_TRANSITION_DURATION_MS, MIN_TRANSITION_DURATION_MS, Transition };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@segnavia/format",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {