@segnavia/format 0.1.0 → 0.2.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 +152 -10
- package/dist/index.js +109 -48
- package/dist/paper.d.ts +47 -0
- package/dist/paper.js +159 -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,6 @@
|
|
|
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";
|
|
2
4
|
import { z } from "zod";
|
|
3
5
|
//#region src/index.d.ts
|
|
4
6
|
export declare const Size: z.ZodObject<{
|
|
@@ -30,6 +32,7 @@ export declare const Paper: z.ZodUnion<readonly [z.ZodEnum<{
|
|
|
30
32
|
unit: z.ZodEnum<{
|
|
31
33
|
mm: "mm";
|
|
32
34
|
in: "in";
|
|
35
|
+
pt: "pt";
|
|
33
36
|
}>;
|
|
34
37
|
}, z.core.$strip>]>;
|
|
35
38
|
export type Paper = z.infer<typeof Paper>;
|
|
@@ -53,6 +56,7 @@ export declare const Print: z.ZodObject<{
|
|
|
53
56
|
unit: z.ZodEnum<{
|
|
54
57
|
mm: "mm";
|
|
55
58
|
in: "in";
|
|
59
|
+
pt: "pt";
|
|
56
60
|
}>;
|
|
57
61
|
}, z.core.$strip>]>;
|
|
58
62
|
orientation: z.ZodDefault<z.ZodEnum<{
|
|
@@ -65,8 +69,81 @@ export declare const Print: z.ZodObject<{
|
|
|
65
69
|
bottom: z.ZodDefault<z.ZodNumber>;
|
|
66
70
|
left: z.ZodDefault<z.ZodNumber>;
|
|
67
71
|
}, z.core.$strip>>;
|
|
72
|
+
bleed: z.ZodDefault<z.ZodNumber>;
|
|
73
|
+
safe: z.ZodDefault<z.ZodNumber>;
|
|
74
|
+
marks: z.ZodDefault<z.ZodBoolean>;
|
|
68
75
|
}, z.core.$strip>;
|
|
69
76
|
export type Print = z.infer<typeof Print>;
|
|
77
|
+
export type PrintInput = z.input<typeof Print>;
|
|
78
|
+
export declare const MAX_IMPOSITION_CELLS = 10000;
|
|
79
|
+
export declare const Imposition: z.ZodObject<{
|
|
80
|
+
paper: z.ZodUnion<readonly [z.ZodEnum<{
|
|
81
|
+
letter: "letter";
|
|
82
|
+
legal: "legal";
|
|
83
|
+
tabloid: "tabloid";
|
|
84
|
+
a3: "a3";
|
|
85
|
+
a4: "a4";
|
|
86
|
+
a5: "a5";
|
|
87
|
+
}>, z.ZodObject<{
|
|
88
|
+
width: z.ZodNumber;
|
|
89
|
+
height: z.ZodNumber;
|
|
90
|
+
unit: z.ZodEnum<{
|
|
91
|
+
mm: "mm";
|
|
92
|
+
in: "in";
|
|
93
|
+
pt: "pt";
|
|
94
|
+
}>;
|
|
95
|
+
}, z.core.$strip>]>;
|
|
96
|
+
orientation: z.ZodDefault<z.ZodEnum<{
|
|
97
|
+
portrait: "portrait";
|
|
98
|
+
landscape: "landscape";
|
|
99
|
+
}>>;
|
|
100
|
+
unit: z.ZodDefault<z.ZodEnum<{
|
|
101
|
+
mm: "mm";
|
|
102
|
+
in: "in";
|
|
103
|
+
pt: "pt";
|
|
104
|
+
}>>;
|
|
105
|
+
margin: z.ZodDefault<z.ZodObject<{
|
|
106
|
+
top: z.ZodDefault<z.ZodNumber>;
|
|
107
|
+
right: z.ZodDefault<z.ZodNumber>;
|
|
108
|
+
bottom: z.ZodDefault<z.ZodNumber>;
|
|
109
|
+
left: z.ZodDefault<z.ZodNumber>;
|
|
110
|
+
}, z.core.$strip>>;
|
|
111
|
+
columns: z.ZodNumber;
|
|
112
|
+
rows: z.ZodNumber;
|
|
113
|
+
cell: z.ZodObject<{
|
|
114
|
+
width: z.ZodNumber;
|
|
115
|
+
height: z.ZodNumber;
|
|
116
|
+
}, z.core.$strip>;
|
|
117
|
+
gap: z.ZodDefault<z.ZodObject<{
|
|
118
|
+
column: z.ZodDefault<z.ZodNumber>;
|
|
119
|
+
row: z.ZodDefault<z.ZodNumber>;
|
|
120
|
+
}, z.core.$strip>>;
|
|
121
|
+
bleed: z.ZodDefault<z.ZodNumber>;
|
|
122
|
+
safe: z.ZodDefault<z.ZodNumber>;
|
|
123
|
+
fit: z.ZodDefault<z.ZodEnum<{
|
|
124
|
+
contain: "contain";
|
|
125
|
+
cover: "cover";
|
|
126
|
+
}>>;
|
|
127
|
+
fill: z.ZodDefault<z.ZodEnum<{
|
|
128
|
+
sequence: "sequence";
|
|
129
|
+
repeat: "repeat";
|
|
130
|
+
}>>;
|
|
131
|
+
sides: z.ZodDefault<z.ZodEnum<{
|
|
132
|
+
one: "one";
|
|
133
|
+
two: "two";
|
|
134
|
+
}>>;
|
|
135
|
+
flip: z.ZodDefault<z.ZodEnum<{
|
|
136
|
+
long: "long";
|
|
137
|
+
short: "short";
|
|
138
|
+
}>>;
|
|
139
|
+
skip: z.ZodDefault<z.ZodNumber>;
|
|
140
|
+
dir: z.ZodDefault<z.ZodEnum<{
|
|
141
|
+
ltr: "ltr";
|
|
142
|
+
rtl: "rtl";
|
|
143
|
+
}>>;
|
|
144
|
+
}, z.core.$strip>;
|
|
145
|
+
export type Imposition = z.infer<typeof Imposition>;
|
|
146
|
+
export type ImpositionInput = z.input<typeof Imposition>;
|
|
70
147
|
export declare const PageSize: z.ZodUnion<readonly [z.ZodLiteral<"fluid">, z.ZodObject<{
|
|
71
148
|
width: z.ZodNumber;
|
|
72
149
|
height: z.ZodNumber;
|
|
@@ -108,6 +185,7 @@ export declare const Page: z.ZodObject<{
|
|
|
108
185
|
unit: z.ZodEnum<{
|
|
109
186
|
mm: "mm";
|
|
110
187
|
in: "in";
|
|
188
|
+
pt: "pt";
|
|
111
189
|
}>;
|
|
112
190
|
}, z.core.$strip>]>;
|
|
113
191
|
orientation: z.ZodDefault<z.ZodEnum<{
|
|
@@ -120,6 +198,9 @@ export declare const Page: z.ZodObject<{
|
|
|
120
198
|
bottom: z.ZodDefault<z.ZodNumber>;
|
|
121
199
|
left: z.ZodDefault<z.ZodNumber>;
|
|
122
200
|
}, z.core.$strip>>;
|
|
201
|
+
bleed: z.ZodDefault<z.ZodNumber>;
|
|
202
|
+
safe: z.ZodDefault<z.ZodNumber>;
|
|
203
|
+
marks: z.ZodDefault<z.ZodBoolean>;
|
|
123
204
|
}, z.core.$strip>>;
|
|
124
205
|
}, z.core.$strip>;
|
|
125
206
|
export type Page = z.infer<typeof Page>;
|
|
@@ -655,6 +736,7 @@ export declare function jsonSchemas(): {
|
|
|
655
736
|
unit: z.ZodEnum<{
|
|
656
737
|
mm: "mm";
|
|
657
738
|
in: "in";
|
|
739
|
+
pt: "pt";
|
|
658
740
|
}>;
|
|
659
741
|
}, z.core.$strip>]>;
|
|
660
742
|
orientation: z.ZodDefault<z.ZodEnum<{
|
|
@@ -667,7 +749,76 @@ export declare function jsonSchemas(): {
|
|
|
667
749
|
bottom: z.ZodDefault<z.ZodNumber>;
|
|
668
750
|
left: z.ZodDefault<z.ZodNumber>;
|
|
669
751
|
}, z.core.$strip>>;
|
|
752
|
+
bleed: z.ZodDefault<z.ZodNumber>;
|
|
753
|
+
safe: z.ZodDefault<z.ZodNumber>;
|
|
754
|
+
marks: z.ZodDefault<z.ZodBoolean>;
|
|
755
|
+
}, z.core.$strip>>;
|
|
756
|
+
}, z.core.$strip>>;
|
|
757
|
+
imposition: z.core.ZodStandardJSONSchemaPayload<z.ZodObject<{
|
|
758
|
+
paper: z.ZodUnion<readonly [z.ZodEnum<{
|
|
759
|
+
letter: "letter";
|
|
760
|
+
legal: "legal";
|
|
761
|
+
tabloid: "tabloid";
|
|
762
|
+
a3: "a3";
|
|
763
|
+
a4: "a4";
|
|
764
|
+
a5: "a5";
|
|
765
|
+
}>, z.ZodObject<{
|
|
766
|
+
width: z.ZodNumber;
|
|
767
|
+
height: z.ZodNumber;
|
|
768
|
+
unit: z.ZodEnum<{
|
|
769
|
+
mm: "mm";
|
|
770
|
+
in: "in";
|
|
771
|
+
pt: "pt";
|
|
772
|
+
}>;
|
|
773
|
+
}, z.core.$strip>]>;
|
|
774
|
+
orientation: z.ZodDefault<z.ZodEnum<{
|
|
775
|
+
portrait: "portrait";
|
|
776
|
+
landscape: "landscape";
|
|
777
|
+
}>>;
|
|
778
|
+
unit: z.ZodDefault<z.ZodEnum<{
|
|
779
|
+
mm: "mm";
|
|
780
|
+
in: "in";
|
|
781
|
+
pt: "pt";
|
|
782
|
+
}>>;
|
|
783
|
+
margin: z.ZodDefault<z.ZodObject<{
|
|
784
|
+
top: z.ZodDefault<z.ZodNumber>;
|
|
785
|
+
right: z.ZodDefault<z.ZodNumber>;
|
|
786
|
+
bottom: z.ZodDefault<z.ZodNumber>;
|
|
787
|
+
left: z.ZodDefault<z.ZodNumber>;
|
|
788
|
+
}, z.core.$strip>>;
|
|
789
|
+
columns: z.ZodNumber;
|
|
790
|
+
rows: z.ZodNumber;
|
|
791
|
+
cell: z.ZodObject<{
|
|
792
|
+
width: z.ZodNumber;
|
|
793
|
+
height: z.ZodNumber;
|
|
794
|
+
}, z.core.$strip>;
|
|
795
|
+
gap: z.ZodDefault<z.ZodObject<{
|
|
796
|
+
column: z.ZodDefault<z.ZodNumber>;
|
|
797
|
+
row: z.ZodDefault<z.ZodNumber>;
|
|
670
798
|
}, z.core.$strip>>;
|
|
799
|
+
bleed: z.ZodDefault<z.ZodNumber>;
|
|
800
|
+
safe: z.ZodDefault<z.ZodNumber>;
|
|
801
|
+
fit: z.ZodDefault<z.ZodEnum<{
|
|
802
|
+
contain: "contain";
|
|
803
|
+
cover: "cover";
|
|
804
|
+
}>>;
|
|
805
|
+
fill: z.ZodDefault<z.ZodEnum<{
|
|
806
|
+
sequence: "sequence";
|
|
807
|
+
repeat: "repeat";
|
|
808
|
+
}>>;
|
|
809
|
+
sides: z.ZodDefault<z.ZodEnum<{
|
|
810
|
+
one: "one";
|
|
811
|
+
two: "two";
|
|
812
|
+
}>>;
|
|
813
|
+
flip: z.ZodDefault<z.ZodEnum<{
|
|
814
|
+
long: "long";
|
|
815
|
+
short: "short";
|
|
816
|
+
}>>;
|
|
817
|
+
skip: z.ZodDefault<z.ZodNumber>;
|
|
818
|
+
dir: z.ZodDefault<z.ZodEnum<{
|
|
819
|
+
ltr: "ltr";
|
|
820
|
+
rtl: "rtl";
|
|
821
|
+
}>>;
|
|
671
822
|
}, z.core.$strip>>;
|
|
672
823
|
deck: z.core.ZodStandardJSONSchemaPayload<z.ZodObject<{
|
|
673
824
|
title: z.ZodString;
|
|
@@ -872,14 +1023,5 @@ export declare function jsonSchemas(): {
|
|
|
872
1023
|
}, z.core.$strip>;
|
|
873
1024
|
}, z.core.$strip>>;
|
|
874
1025
|
};
|
|
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
1026
|
//#endregion
|
|
885
|
-
export { type TextDirection, firstStrongDirection, languageDirection, resolveDirection };
|
|
1027
|
+
export { type Imposed, MARK_GAP_MM, MARK_LENGTH_MM, MM_PER_PT, type MillimeterEdges, PAPER_MM, PX_PER_MM, type PageFit, type PagePlacementKind, type PagePlacementLayout, type PaperBox, type Placement, type TextDirection, firstStrongDirection, fitPage, impose, languageDirection, mirrorPlacement, pagePlacementLayout, printBoxes, printPadding, printProperties, resolveDirection, roundMillimeters, sheetMm, toMillimeters };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
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";
|
|
1
2
|
import { firstStrongDirection, languageDirection, resolveDirection } from "./direction.js";
|
|
3
|
+
import { fitPage, impose, mirrorPlacement, pagePlacementLayout } from "./imposition.js";
|
|
2
4
|
import { z } from "zod";
|
|
3
5
|
//#region src/index.ts
|
|
4
6
|
const Size = z.object({
|
|
@@ -22,7 +24,11 @@ const NAMED_PAPERS = [
|
|
|
22
24
|
const Paper = z.union([z.enum(NAMED_PAPERS), z.object({
|
|
23
25
|
width: z.number().positive(),
|
|
24
26
|
height: z.number().positive(),
|
|
25
|
-
unit: z.enum([
|
|
27
|
+
unit: z.enum([
|
|
28
|
+
"mm",
|
|
29
|
+
"in",
|
|
30
|
+
"pt"
|
|
31
|
+
])
|
|
26
32
|
})]);
|
|
27
33
|
const Margin = z.object({
|
|
28
34
|
top: z.number().nonnegative().default(0),
|
|
@@ -33,7 +39,106 @@ const Margin = z.object({
|
|
|
33
39
|
const Print = z.object({
|
|
34
40
|
paper: Paper,
|
|
35
41
|
orientation: z.enum(["portrait", "landscape"]).default("portrait"),
|
|
36
|
-
margin: Margin.optional()
|
|
42
|
+
margin: Margin.optional(),
|
|
43
|
+
bleed: z.number().nonnegative().default(0),
|
|
44
|
+
safe: z.number().nonnegative().default(0),
|
|
45
|
+
marks: z.boolean().default(false)
|
|
46
|
+
}).superRefine((print, ctx) => {
|
|
47
|
+
const trim = sheetMm(print);
|
|
48
|
+
if (print.safe * 2 >= trim.width) ctx.addIssue({
|
|
49
|
+
code: "custom",
|
|
50
|
+
path: ["safe"],
|
|
51
|
+
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`
|
|
52
|
+
});
|
|
53
|
+
if (print.safe * 2 >= trim.height) ctx.addIssue({
|
|
54
|
+
code: "custom",
|
|
55
|
+
path: ["safe"],
|
|
56
|
+
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`
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
const ImpositionGap = z.object({
|
|
60
|
+
column: z.number().nonnegative().default(0),
|
|
61
|
+
row: z.number().nonnegative().default(0)
|
|
62
|
+
});
|
|
63
|
+
const MAX_IMPOSITION_CELLS = 1e4;
|
|
64
|
+
const Imposition = z.object({
|
|
65
|
+
paper: Paper,
|
|
66
|
+
orientation: z.enum(["portrait", "landscape"]).default("portrait"),
|
|
67
|
+
unit: z.enum([
|
|
68
|
+
"mm",
|
|
69
|
+
"in",
|
|
70
|
+
"pt"
|
|
71
|
+
]).default("mm"),
|
|
72
|
+
margin: Margin.default({
|
|
73
|
+
top: 0,
|
|
74
|
+
right: 0,
|
|
75
|
+
bottom: 0,
|
|
76
|
+
left: 0
|
|
77
|
+
}),
|
|
78
|
+
columns: z.number().int().positive(),
|
|
79
|
+
rows: z.number().int().positive(),
|
|
80
|
+
cell: z.object({
|
|
81
|
+
width: z.number().positive(),
|
|
82
|
+
height: z.number().positive()
|
|
83
|
+
}),
|
|
84
|
+
gap: ImpositionGap.default({
|
|
85
|
+
column: 0,
|
|
86
|
+
row: 0
|
|
87
|
+
}),
|
|
88
|
+
bleed: z.number().nonnegative().default(0),
|
|
89
|
+
safe: z.number().nonnegative().default(0),
|
|
90
|
+
fit: z.enum(["contain", "cover"]).default("contain"),
|
|
91
|
+
fill: z.enum(["sequence", "repeat"]).default("sequence"),
|
|
92
|
+
sides: z.enum(["one", "two"]).default("one"),
|
|
93
|
+
flip: z.enum(["long", "short"]).default("long"),
|
|
94
|
+
skip: z.number().int().nonnegative().default(0),
|
|
95
|
+
dir: z.enum(["ltr", "rtl"]).default("ltr")
|
|
96
|
+
}).superRefine((imposition, ctx) => {
|
|
97
|
+
const cellCount = BigInt(imposition.columns) * BigInt(imposition.rows);
|
|
98
|
+
if (imposition.columns > 1e4 || imposition.rows > 1e4 || cellCount > BigInt(1e4)) {
|
|
99
|
+
ctx.addIssue({
|
|
100
|
+
code: "custom",
|
|
101
|
+
path: ["columns"],
|
|
102
|
+
message: `the grid has ${cellCount} cells and the ceiling is ${MAX_IMPOSITION_CELLS}; fewer columns or rows clears this refusal`
|
|
103
|
+
});
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const mm = (value) => toMillimeters(value, imposition.unit);
|
|
107
|
+
const paper = sheetMm({
|
|
108
|
+
paper: imposition.paper,
|
|
109
|
+
orientation: imposition.orientation
|
|
110
|
+
});
|
|
111
|
+
const gridWidth = mm(imposition.margin.left + imposition.columns * imposition.cell.width + (imposition.columns - 1) * imposition.gap.column + imposition.margin.right);
|
|
112
|
+
const gridHeight = mm(imposition.margin.top + imposition.rows * imposition.cell.height + (imposition.rows - 1) * imposition.gap.row + imposition.margin.bottom);
|
|
113
|
+
if (gridWidth - paper.width > .01) ctx.addIssue({
|
|
114
|
+
code: "custom",
|
|
115
|
+
path: ["columns"],
|
|
116
|
+
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`
|
|
117
|
+
});
|
|
118
|
+
if (gridHeight - paper.height > .01) ctx.addIssue({
|
|
119
|
+
code: "custom",
|
|
120
|
+
path: ["rows"],
|
|
121
|
+
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`
|
|
122
|
+
});
|
|
123
|
+
const safe = mm(imposition.safe);
|
|
124
|
+
const cellWidth = mm(imposition.cell.width);
|
|
125
|
+
const cellHeight = mm(imposition.cell.height);
|
|
126
|
+
if (safe * 2 >= cellWidth) ctx.addIssue({
|
|
127
|
+
code: "custom",
|
|
128
|
+
path: ["safe"],
|
|
129
|
+
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`
|
|
130
|
+
});
|
|
131
|
+
if (safe * 2 >= cellHeight) ctx.addIssue({
|
|
132
|
+
code: "custom",
|
|
133
|
+
path: ["safe"],
|
|
134
|
+
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`
|
|
135
|
+
});
|
|
136
|
+
const cells = imposition.columns * imposition.rows;
|
|
137
|
+
if (imposition.skip >= cells) ctx.addIssue({
|
|
138
|
+
code: "custom",
|
|
139
|
+
path: ["skip"],
|
|
140
|
+
message: `skip is ${imposition.skip}, but the grid has ${cells} cells; skip must be less than ${cells}`
|
|
141
|
+
});
|
|
37
142
|
});
|
|
38
143
|
const PageSize = z.union([
|
|
39
144
|
z.literal("fluid"),
|
|
@@ -229,55 +334,11 @@ function shotsUsed(deck) {
|
|
|
229
334
|
function jsonSchemas() {
|
|
230
335
|
return {
|
|
231
336
|
page: z.toJSONSchema(Page, { io: "input" }),
|
|
337
|
+
imposition: z.toJSONSchema(Imposition, { io: "input" }),
|
|
232
338
|
deck: z.toJSONSchema(DeckShape, { io: "input" }),
|
|
233
339
|
manifest: z.toJSONSchema(Manifest, { io: "input" }),
|
|
234
340
|
publishedShot: z.toJSONSchema(PublishedShot, { io: "input" })
|
|
235
341
|
};
|
|
236
342
|
}
|
|
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
343
|
//#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 };
|
|
344
|
+
export { Annotation, Box, Cue, DECK_SIZE, Deck, Direction, Imposition, MARK_GAP_MM, MARK_LENGTH_MM, MAX_IMPOSITION_CELLS, 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, 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 };
|