@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.
- package/dist/imposition.d.ts +62 -0
- package/dist/imposition.js +294 -0
- package/dist/index.d.ts +198 -16
- package/dist/index.js +135 -62
- package/dist/paper.d.ts +47 -0
- package/dist/paper.js +159 -0
- package/dist/shots-used.d.ts +9 -0
- package/dist/shots-used.js +16 -0
- package/dist/timing.d.ts +4 -0
- package/dist/timing.js +10 -0
- package/dist/transition.d.ts +16 -0
- package/dist/transition.js +15 -0
- package/package.json +1 -1
|
@@ -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([
|
|
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 };
|
package/dist/paper.d.ts
ADDED
|
@@ -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,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 };
|
package/dist/timing.d.ts
ADDED
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 };
|