@segnavia/format 0.38.0 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/board-edit.d.ts +2 -1
  2. package/dist/board-geometry.d.ts +2 -1
  3. package/dist/board-section-grid.d.ts +2 -1
  4. package/dist/board-sections.d.ts +2 -1
  5. package/dist/board.d.ts +8 -8
  6. package/dist/board.js +15 -11
  7. package/dist/book-exports.d.ts +2 -2
  8. package/dist/book-section.d.ts +171 -0
  9. package/dist/book-section.js +13 -2
  10. package/dist/book.d.ts +189 -47
  11. package/dist/book.js +18 -2
  12. package/dist/cover.d.ts +500 -0
  13. package/dist/cover.js +100 -0
  14. package/dist/deck-from-slides.d.ts +23 -4
  15. package/dist/deck.d.ts +144 -42
  16. package/dist/deck.js +22 -24
  17. package/dist/direction.d.ts +1 -1
  18. package/dist/font-collection.d.ts +8 -0
  19. package/dist/font-collection.js +191 -0
  20. package/dist/font-face.d.ts +8 -0
  21. package/dist/font-face.js +24 -0
  22. package/dist/font-refusal.d.ts +5 -0
  23. package/dist/font-refusal.js +9 -0
  24. package/dist/font-signature.d.ts +5 -0
  25. package/dist/font-signature.js +13 -0
  26. package/dist/fonts.d.ts +6 -1
  27. package/dist/fonts.js +3 -1
  28. package/dist/geometry.d.ts +20 -0
  29. package/dist/geometry.js +18 -0
  30. package/dist/image-fit.d.ts +10 -0
  31. package/dist/image-fit.js +10 -0
  32. package/dist/imposition-marks.d.ts +1 -1
  33. package/dist/imposition-schema.d.ts +185 -0
  34. package/dist/imposition-schema.js +122 -0
  35. package/dist/imposition.d.ts +8 -6
  36. package/dist/imposition.js +53 -42
  37. package/dist/index.d.ts +23 -1756
  38. package/dist/index.js +29 -212
  39. package/dist/json-schemas.d.ts +2181 -0
  40. package/dist/json-schemas.js +43 -0
  41. package/dist/media.d.ts +152 -0
  42. package/dist/media.js +119 -0
  43. package/dist/note-series.d.ts +4 -4
  44. package/dist/note-series.js +0 -5
  45. package/dist/page.d.ts +359 -0
  46. package/dist/page.js +110 -0
  47. package/dist/paper.d.ts +5 -5
  48. package/dist/placement-cycle.d.ts +5 -0
  49. package/dist/placement-cycle.js +30 -0
  50. package/dist/placement-refusal.d.ts +5 -0
  51. package/dist/placement-refusal.js +6 -0
  52. package/dist/placement.d.ts +76 -0
  53. package/dist/placement.js +100 -0
  54. package/dist/print.d.ts +72 -0
  55. package/dist/print.js +38 -0
  56. package/dist/printed-page-imposition.d.ts +2 -1
  57. package/dist/shots-used.d.ts +0 -1
  58. package/dist/shots-used.js +2 -2
  59. package/dist/slots.d.ts +13 -13
  60. package/package.json +1 -1
@@ -0,0 +1,13 @@
1
+ //#region src/font-signature.ts
2
+ const COLLECTION_TAG = "ttcf";
3
+ function classifyFont(bytes, offset = 0) {
4
+ if (!Number.isSafeInteger(offset) || offset < 0 || offset > bytes.length - 4) return;
5
+ const tag = String.fromCharCode(bytes[offset] ?? 0, bytes[offset + 1] ?? 0, bytes[offset + 2] ?? 0, bytes[offset + 3] ?? 0);
6
+ if (tag === "wOF2") return "woff2";
7
+ if (tag === "wOFF") return "woff";
8
+ if (tag === "OTTO") return "opentype";
9
+ if (tag === "ttcf") return "collection";
10
+ if (tag === "true" || tag === "\0\0\0") return "truetype";
11
+ }
12
+ //#endregion
13
+ export { COLLECTION_TAG, classifyFont };
package/dist/fonts.d.ts CHANGED
@@ -20,7 +20,7 @@ export declare function localFontReferences(references: readonly FontReference[]
20
20
  local: string;
21
21
  fallback: string;
22
22
  }>;
23
- export declare const FONT_FORMATS: readonly ["woff2", "woff", "truetype", "opentype"];
23
+ export declare const FONT_FORMATS: readonly ["woff2", "woff", "truetype", "opentype", "collection"];
24
24
  export type FontFormat = (typeof FONT_FORMATS)[number];
25
25
  export declare const LocalFaceTransport: z.ZodObject<{
26
26
  family: z.ZodString;
@@ -29,12 +29,14 @@ export declare const LocalFaceTransport: z.ZodObject<{
29
29
  woff: "woff";
30
30
  truetype: "truetype";
31
31
  opentype: "opentype";
32
+ collection: "collection";
32
33
  }>;
33
34
  weight: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
34
35
  style: z.ZodOptional<z.ZodEnum<{
35
36
  normal: "normal";
36
37
  italic: "italic";
37
38
  }>>;
39
+ postscriptName: z.ZodOptional<z.ZodString>;
38
40
  data: z.ZodString;
39
41
  }, z.core.$strict>;
40
42
  export type LocalFaceTransport = z.infer<typeof LocalFaceTransport>;
@@ -45,12 +47,14 @@ export declare const LocalFacesTransport: z.ZodArray<z.ZodObject<{
45
47
  woff: "woff";
46
48
  truetype: "truetype";
47
49
  opentype: "opentype";
50
+ collection: "collection";
48
51
  }>;
49
52
  weight: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
50
53
  style: z.ZodOptional<z.ZodEnum<{
51
54
  normal: "normal";
52
55
  italic: "italic";
53
56
  }>>;
57
+ postscriptName: z.ZodOptional<z.ZodString>;
54
58
  data: z.ZodString;
55
59
  }, z.core.$strict>>;
56
60
  export type LocalFacesInput = LocalFaceTransport[];
@@ -63,6 +67,7 @@ export interface FontFace {
63
67
  weight?: number | string;
64
68
  style?: 'normal' | 'italic';
65
69
  unicodeRange?: string;
70
+ postscriptName?: string;
66
71
  }
67
72
  export interface AppFont {
68
73
  family: string;
package/dist/fonts.js CHANGED
@@ -26,13 +26,15 @@ const FONT_FORMATS = [
26
26
  "woff2",
27
27
  "woff",
28
28
  "truetype",
29
- "opentype"
29
+ "opentype",
30
+ "collection"
30
31
  ];
31
32
  const LocalFaceTransportShape = {
32
33
  family: z.string().min(1),
33
34
  format: z.enum(FONT_FORMATS),
34
35
  weight: z.union([z.number(), z.string()]).optional(),
35
36
  style: z.enum(["normal", "italic"]).optional(),
37
+ postscriptName: z.string().min(1).optional(),
36
38
  data: z.string()
37
39
  };
38
40
  const LocalFaceTransport = z.strictObject(LocalFaceTransportShape);
@@ -0,0 +1,20 @@
1
+ import { z } from "zod";
2
+ //#region src/geometry.d.ts
3
+ export declare const Size: z.ZodObject<{
4
+ width: z.ZodNumber;
5
+ height: z.ZodNumber;
6
+ }, z.core.$strip>;
7
+ export type Size = z.infer<typeof Size>;
8
+ export declare const Box: z.ZodObject<{
9
+ x: z.ZodNumber;
10
+ y: z.ZodNumber;
11
+ width: z.ZodNumber;
12
+ height: z.ZodNumber;
13
+ }, z.core.$strip>;
14
+ export type Box = z.infer<typeof Box>;
15
+ export declare const Point: z.ZodObject<{
16
+ x: z.ZodNumber;
17
+ y: z.ZodNumber;
18
+ }, z.core.$strip>;
19
+ export type Point = z.infer<typeof Point>;
20
+ //#endregion
@@ -0,0 +1,18 @@
1
+ import { z } from "zod";
2
+ //#region src/geometry.ts
3
+ const Size = z.object({
4
+ width: z.number().int().positive(),
5
+ height: z.number().int().positive()
6
+ });
7
+ const Box = z.object({
8
+ x: z.number(),
9
+ y: z.number(),
10
+ width: z.number(),
11
+ height: z.number()
12
+ });
13
+ const Point = z.object({
14
+ x: z.number(),
15
+ y: z.number()
16
+ });
17
+ //#endregion
18
+ export { Box, Point, Size };
@@ -0,0 +1,10 @@
1
+ import { z } from "zod";
2
+ //#region src/image-fit.d.ts
3
+ export declare const ImageFit: z.ZodEnum<{
4
+ width: "width";
5
+ contain: "contain";
6
+ cover: "cover";
7
+ focus: "focus";
8
+ }>;
9
+ export type ImageFit = z.infer<typeof ImageFit>;
10
+ //#endregion
@@ -0,0 +1,10 @@
1
+ import { z } from "zod";
2
+ //#region src/image-fit.ts
3
+ const ImageFit = z.enum([
4
+ "contain",
5
+ "cover",
6
+ "width",
7
+ "focus"
8
+ ]);
9
+ //#endregion
10
+ export { ImageFit };
@@ -1,5 +1,5 @@
1
1
  import { MarkSegment } from "./paper.js";
2
- import { Imposition } from "./index.js";
2
+ import { Imposition } from "./imposition-schema.js";
3
3
  //#region src/imposition-marks.d.ts
4
4
  export declare const MIN_MARK_LENGTH_MM = 2;
5
5
  export type ImpositionMarkEdge = 'top' | 'right' | 'bottom' | 'left';
@@ -0,0 +1,185 @@
1
+ import { z } from "zod";
2
+ //#region src/imposition-schema.d.ts
3
+ export declare const MAX_IMPOSITION_CELLS = 10000;
4
+ export declare const Imposition: z.ZodPipe<z.ZodObject<{
5
+ paper: z.ZodUnion<readonly [z.ZodEnum<{
6
+ letter: "letter";
7
+ legal: "legal";
8
+ tabloid: "tabloid";
9
+ a3: "a3";
10
+ a4: "a4";
11
+ a5: "a5";
12
+ "business-card-us": "business-card-us";
13
+ "business-card-eu": "business-card-eu";
14
+ "business-card-square-us": "business-card-square-us";
15
+ "business-card-slim-us": "business-card-slim-us";
16
+ "postcard-4x6": "postcard-4x6";
17
+ "postcard-5x7": "postcard-5x7";
18
+ "postcard-6x9": "postcard-6x9";
19
+ "postcard-6x11": "postcard-6x11";
20
+ "card-square-4in": "card-square-4in";
21
+ "half-letter": "half-letter";
22
+ a6: "a6";
23
+ dl: "dl";
24
+ "rack-card-4x9": "rack-card-4x9";
25
+ "rack-card-3.5x8.5": "rack-card-3.5x8.5";
26
+ "poster-12x18": "poster-12x18";
27
+ "poster-16x20": "poster-16x20";
28
+ "poster-18x24": "poster-18x24";
29
+ "poster-24x36": "poster-24x36";
30
+ a2: "a2";
31
+ a1: "a1";
32
+ a0: "a0";
33
+ }>, z.ZodObject<{
34
+ width: z.ZodNumber;
35
+ height: z.ZodNumber;
36
+ unit: z.ZodEnum<{
37
+ in: "in";
38
+ mm: "mm";
39
+ pt: "pt";
40
+ }>;
41
+ }, z.core.$strip>]>;
42
+ orientation: z.ZodDefault<z.ZodEnum<{
43
+ portrait: "portrait";
44
+ landscape: "landscape";
45
+ }>>;
46
+ unit: z.ZodDefault<z.ZodEnum<{
47
+ in: "in";
48
+ mm: "mm";
49
+ pt: "pt";
50
+ }>>;
51
+ margin: z.ZodDefault<z.ZodObject<{
52
+ top: z.ZodDefault<z.ZodNumber>;
53
+ right: z.ZodDefault<z.ZodNumber>;
54
+ bottom: z.ZodDefault<z.ZodNumber>;
55
+ left: z.ZodDefault<z.ZodNumber>;
56
+ }, z.core.$strip>>;
57
+ columns: z.ZodOptional<z.ZodNumber>;
58
+ rows: z.ZodOptional<z.ZodNumber>;
59
+ cell: z.ZodObject<{
60
+ width: z.ZodOptional<z.ZodNumber>;
61
+ height: z.ZodNumber;
62
+ }, z.core.$strip>;
63
+ gap: z.ZodDefault<z.ZodObject<{
64
+ column: z.ZodDefault<z.ZodNumber>;
65
+ row: z.ZodDefault<z.ZodNumber>;
66
+ }, z.core.$strip>>;
67
+ bleed: z.ZodDefault<z.ZodNumber>;
68
+ safe: z.ZodDefault<z.ZodNumber>;
69
+ fit: z.ZodDefault<z.ZodEnum<{
70
+ contain: "contain";
71
+ cover: "cover";
72
+ }>>;
73
+ fill: z.ZodDefault<z.ZodEnum<{
74
+ sequence: "sequence";
75
+ repeat: "repeat";
76
+ }>>;
77
+ sides: z.ZodOptional<z.ZodEnum<{
78
+ one: "one";
79
+ two: "two";
80
+ }>>;
81
+ flip: z.ZodDefault<z.ZodEnum<{
82
+ long: "long";
83
+ short: "short";
84
+ }>>;
85
+ skip: z.ZodDefault<z.ZodNumber>;
86
+ dir: z.ZodDefault<z.ZodEnum<{
87
+ ltr: "ltr";
88
+ rtl: "rtl";
89
+ }>>;
90
+ marks: z.ZodDefault<z.ZodBoolean>;
91
+ markColor: z.ZodDefault<z.ZodEnum<{
92
+ black: "black";
93
+ registration: "registration";
94
+ }>>;
95
+ cornerMarks: z.ZodDefault<z.ZodBoolean>;
96
+ fold: z.ZodOptional<z.ZodEnum<{
97
+ custom: "custom";
98
+ letter: "letter";
99
+ half: "half";
100
+ z: "z";
101
+ gate: "gate";
102
+ }>>;
103
+ panels: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
104
+ }, z.core.$strip>, z.ZodTransform<{
105
+ paper: "letter" | "legal" | "tabloid" | "a3" | "a4" | "a5" | "business-card-us" | "business-card-eu" | "business-card-square-us" | "business-card-slim-us" | "postcard-4x6" | "postcard-5x7" | "postcard-6x9" | "postcard-6x11" | "card-square-4in" | "half-letter" | "a6" | "dl" | "rack-card-4x9" | "rack-card-3.5x8.5" | "poster-12x18" | "poster-16x20" | "poster-18x24" | "poster-24x36" | "a2" | "a1" | "a0" | {
106
+ width: number;
107
+ height: number;
108
+ unit: "in" | "mm" | "pt";
109
+ };
110
+ orientation: "portrait" | "landscape";
111
+ unit: "in" | "mm" | "pt";
112
+ margin: {
113
+ top: number;
114
+ right: number;
115
+ bottom: number;
116
+ left: number;
117
+ };
118
+ cell: {
119
+ height: number;
120
+ width?: number | undefined;
121
+ };
122
+ gap: {
123
+ column: number;
124
+ row: number;
125
+ };
126
+ bleed: number;
127
+ safe: number;
128
+ fit: "contain" | "cover";
129
+ fill: "sequence" | "repeat";
130
+ flip: "long" | "short";
131
+ skip: number;
132
+ dir: "ltr" | "rtl";
133
+ marks: boolean;
134
+ markColor: "black" | "registration";
135
+ cornerMarks: boolean;
136
+ columns?: number | undefined;
137
+ rows?: number | undefined;
138
+ sides?: "one" | "two" | undefined;
139
+ fold?: "custom" | "letter" | "half" | "z" | "gate" | undefined;
140
+ panels?: number[] | undefined;
141
+ } & {
142
+ columns: number;
143
+ rows: number;
144
+ sides: "one" | "two";
145
+ }, {
146
+ paper: "letter" | "legal" | "tabloid" | "a3" | "a4" | "a5" | "business-card-us" | "business-card-eu" | "business-card-square-us" | "business-card-slim-us" | "postcard-4x6" | "postcard-5x7" | "postcard-6x9" | "postcard-6x11" | "card-square-4in" | "half-letter" | "a6" | "dl" | "rack-card-4x9" | "rack-card-3.5x8.5" | "poster-12x18" | "poster-16x20" | "poster-18x24" | "poster-24x36" | "a2" | "a1" | "a0" | {
147
+ width: number;
148
+ height: number;
149
+ unit: "in" | "mm" | "pt";
150
+ };
151
+ orientation: "portrait" | "landscape";
152
+ unit: "in" | "mm" | "pt";
153
+ margin: {
154
+ top: number;
155
+ right: number;
156
+ bottom: number;
157
+ left: number;
158
+ };
159
+ cell: {
160
+ height: number;
161
+ width?: number | undefined;
162
+ };
163
+ gap: {
164
+ column: number;
165
+ row: number;
166
+ };
167
+ bleed: number;
168
+ safe: number;
169
+ fit: "contain" | "cover";
170
+ fill: "sequence" | "repeat";
171
+ flip: "long" | "short";
172
+ skip: number;
173
+ dir: "ltr" | "rtl";
174
+ marks: boolean;
175
+ markColor: "black" | "registration";
176
+ cornerMarks: boolean;
177
+ columns?: number | undefined;
178
+ rows?: number | undefined;
179
+ sides?: "one" | "two" | undefined;
180
+ fold?: "custom" | "letter" | "half" | "z" | "gate" | undefined;
181
+ panels?: number[] | undefined;
182
+ }>>;
183
+ export type Imposition = z.infer<typeof Imposition>;
184
+ export type ImpositionInput = z.input<typeof Imposition>;
185
+ //#endregion
@@ -0,0 +1,122 @@
1
+ import { Orientation, Paper, PaperUnit, sheetMm, toMillimeters } from "./paper.js";
2
+ import { Margin, MarkColor } from "./print.js";
3
+ import { refuseOverflow, refuseSafe } from "./imposition-fit.js";
4
+ import { FOLD_KINDS, refineFold, resolveImposition } from "./fold.js";
5
+ import { z } from "zod";
6
+ //#region src/imposition-schema.ts
7
+ const ImpositionGap = z.object({
8
+ column: z.number().nonnegative().default(0),
9
+ row: z.number().nonnegative().default(0)
10
+ });
11
+ const MAX_IMPOSITION_CELLS = 1e4;
12
+ const Imposition = z.object({
13
+ paper: Paper,
14
+ orientation: Orientation.default("portrait"),
15
+ unit: PaperUnit.default("mm"),
16
+ margin: Margin.default({
17
+ top: 0,
18
+ right: 0,
19
+ bottom: 0,
20
+ left: 0
21
+ }),
22
+ columns: z.number().int().positive().optional(),
23
+ rows: z.number().int().positive().optional(),
24
+ cell: z.object({
25
+ width: z.number().positive().optional(),
26
+ height: z.number().positive()
27
+ }),
28
+ gap: ImpositionGap.default({
29
+ column: 0,
30
+ row: 0
31
+ }),
32
+ bleed: z.number().nonnegative().default(0),
33
+ safe: z.number().nonnegative().default(0),
34
+ fit: z.enum(["contain", "cover"]).default("contain"),
35
+ fill: z.enum(["sequence", "repeat"]).default("sequence"),
36
+ sides: z.enum(["one", "two"]).optional(),
37
+ flip: z.enum(["long", "short"]).default("long"),
38
+ skip: z.number().int().nonnegative().default(0),
39
+ dir: z.enum(["ltr", "rtl"]).default("ltr"),
40
+ marks: z.boolean().default(false),
41
+ markColor: MarkColor.default("black"),
42
+ cornerMarks: z.boolean().default(false),
43
+ fold: z.enum(FOLD_KINDS).optional(),
44
+ panels: z.array(z.number().positive()).optional()
45
+ }).superRefine((imposition, ctx) => {
46
+ if (imposition.markColor === "registration" && !imposition.marks) ctx.addIssue({
47
+ code: "custom",
48
+ path: ["markColor"],
49
+ message: "markColor requires marks to be true"
50
+ });
51
+ if (imposition.cornerMarks && !imposition.marks) ctx.addIssue({
52
+ code: "custom",
53
+ path: ["cornerMarks"],
54
+ message: "cornerMarks requires marks to be true"
55
+ });
56
+ if (imposition.panels !== void 0 && !imposition.fold) ctx.addIssue({
57
+ code: "custom",
58
+ path: ["panels"],
59
+ message: "panels needs fold; set fold or remove panels"
60
+ });
61
+ if (imposition.fold) {
62
+ const panelCount = imposition.panels?.length ?? 0;
63
+ if (panelCount > 1e4) {
64
+ ctx.addIssue({
65
+ code: "custom",
66
+ path: ["panels"],
67
+ message: `the fold has ${panelCount} panels and the ceiling is ${MAX_IMPOSITION_CELLS}; fewer panels clears this refusal`
68
+ });
69
+ return;
70
+ }
71
+ refineFold(imposition, ctx);
72
+ return;
73
+ }
74
+ if (imposition.columns === void 0) ctx.addIssue({
75
+ code: "custom",
76
+ path: ["columns"],
77
+ message: "columns is required; give a column count to clear this refusal"
78
+ });
79
+ if (imposition.rows === void 0) ctx.addIssue({
80
+ code: "custom",
81
+ path: ["rows"],
82
+ message: "rows is required; give a row count to clear this refusal"
83
+ });
84
+ if (imposition.cell.width === void 0) ctx.addIssue({
85
+ code: "custom",
86
+ path: ["cell", "width"],
87
+ message: "cell.width is required; give a cell width to clear this refusal"
88
+ });
89
+ if (imposition.columns === void 0 || imposition.rows === void 0 || imposition.cell.width === void 0) return;
90
+ const cellCount = BigInt(imposition.columns) * BigInt(imposition.rows);
91
+ if (imposition.columns > 1e4 || imposition.rows > 1e4 || cellCount > BigInt(1e4)) {
92
+ ctx.addIssue({
93
+ code: "custom",
94
+ path: ["columns"],
95
+ message: `the grid has ${cellCount} cells and the ceiling is ${MAX_IMPOSITION_CELLS}; fewer columns or rows clears this refusal`
96
+ });
97
+ return;
98
+ }
99
+ const mm = (value) => toMillimeters(value, imposition.unit);
100
+ const paper = sheetMm({
101
+ paper: imposition.paper,
102
+ orientation: imposition.orientation
103
+ });
104
+ const gridWidth = mm(imposition.margin.left + imposition.columns * imposition.cell.width + (imposition.columns - 1) * imposition.gap.column + imposition.margin.right);
105
+ const gridHeight = mm(imposition.margin.top + imposition.rows * imposition.cell.height + (imposition.rows - 1) * imposition.gap.row + imposition.margin.bottom);
106
+ refuseOverflow(ctx, paper, {
107
+ width: gridWidth,
108
+ height: gridHeight
109
+ });
110
+ refuseSafe(ctx, mm(imposition.safe), {
111
+ width: mm(imposition.cell.width),
112
+ height: mm(imposition.cell.height)
113
+ });
114
+ const cells = imposition.columns * imposition.rows;
115
+ if (imposition.skip >= cells) ctx.addIssue({
116
+ code: "custom",
117
+ path: ["skip"],
118
+ message: `skip is ${imposition.skip}, but the grid has ${cells} cells; skip must be less than ${cells}`
119
+ });
120
+ }).transform(resolveImposition);
121
+ //#endregion
122
+ export { Imposition, MAX_IMPOSITION_CELLS };
@@ -1,5 +1,7 @@
1
+ import { Page, PageWidths } from "./page.js";
2
+ import { Print } from "./print.js";
1
3
  import { PaperBox } from "./paper.js";
2
- import { Imposition, Page, Print } from "./index.js";
4
+ import { Imposition } from "./imposition-schema.js";
3
5
  //#region src/imposition.d.ts
4
6
  export interface MillimeterEdges {
5
7
  top: number;
@@ -7,7 +9,7 @@ export interface MillimeterEdges {
7
9
  bottom: number;
8
10
  left: number;
9
11
  }
10
- export interface Placement {
12
+ export interface ImpositionPlacement {
11
13
  page: number;
12
14
  paper: number;
13
15
  side: 'front' | 'back';
@@ -26,7 +28,7 @@ export interface Imposed {
26
28
  };
27
29
  papers: number;
28
30
  sides: 1 | 2;
29
- placements: Placement[];
31
+ placements: ImpositionPlacement[];
30
32
  }
31
33
  export interface PageFit {
32
34
  flow: boolean;
@@ -52,11 +54,11 @@ export interface PagePlacementLayout {
52
54
  inset: boolean;
53
55
  paper: PaperBox;
54
56
  }
55
- export declare function mirrorPlacement(front: Placement, paper: Imposed['paper'], flip: Imposition['flip'], page?: number): Placement;
57
+ export declare function mirrorPlacement(front: ImpositionPlacement, paper: Imposed['paper'], flip: Imposition['flip'], page?: number): ImpositionPlacement;
56
58
  export declare function impose(imposition: Imposition, pageCount: number): Imposed;
57
59
  export declare function fitPage(page: {
58
60
  size: Page['size'];
59
61
  print?: Print;
60
- }, placement: Placement, fit: 'contain' | 'cover'): PageFit;
61
- export declare function pagePlacementLayout(size: Page['size'], placement: Placement, fit: PageFit): PagePlacementLayout;
62
+ }, placement: ImpositionPlacement, fit: 'contain' | 'cover', pageWidths?: PageWidths): PageFit;
63
+ export declare function pagePlacementLayout(size: Page['size'], placement: ImpositionPlacement, fit: PageFit): PagePlacementLayout;
62
64
  //#endregion
@@ -1,4 +1,5 @@
1
1
  import { PX_PER_MM, roundMillimeters, sheetMm, toMillimeters } from "./paper.js";
2
+ import { resolvePageWidth } from "./page.js";
2
3
  import { foldCellPages, foldPageCountMessage } from "./fold.js";
3
4
  //#region src/imposition.ts
4
5
  const zeroCrop = () => ({
@@ -225,46 +226,56 @@ function trimDiffers(page, placement) {
225
226
  const trim = sheetMm(page.print);
226
227
  return Math.abs(trim.width - placement.trim.width) > .01 || Math.abs(trim.height - placement.trim.height) > .01;
227
228
  }
228
- function fitPage(page, placement, fit) {
229
+ function fitPage(page, placement, fit, pageWidths) {
229
230
  const differs = trimDiffers(page, placement);
230
- if (page.size === "fluid") return {
231
- flow: true,
232
- width: placement.bleedBox.width * PX_PER_MM,
233
- height: null,
234
- scale: 1,
235
- x: placement.bleedBox.x,
236
- y: placement.bleedBox.y,
237
- bands: {
238
- inline: 0,
239
- block: 0
240
- },
241
- crop: zeroCrop(),
242
- differs
243
- };
244
- if (page.size.height === "auto") return {
245
- flow: true,
246
- width: page.size.width,
247
- height: null,
248
- scale: placement.trim.width * PX_PER_MM / page.size.width,
249
- x: placement.trim.x,
250
- y: placement.trim.y,
251
- bands: {
252
- inline: 0,
253
- block: 0
254
- },
255
- crop: zeroCrop(),
256
- differs
257
- };
231
+ const size = page.size;
232
+ if (size.height === "auto") {
233
+ const resolved = resolvePageWidth(size, pageWidths);
234
+ if (size.width !== "full") {
235
+ const named = typeof size.width === "string";
236
+ const width = named ? Math.min(resolved.viewportWidth, placement.trim.width * PX_PER_MM) : resolved.viewportWidth;
237
+ const scale = named ? 1 : placement.trim.width * PX_PER_MM / width;
238
+ const inline = named ? (placement.trim.width - width / PX_PER_MM) / 2 : 0;
239
+ return {
240
+ flow: true,
241
+ width,
242
+ height: null,
243
+ scale,
244
+ x: roundMillimeters(placement.trim.x + inline),
245
+ y: placement.trim.y,
246
+ bands: {
247
+ inline: 0,
248
+ block: 0
249
+ },
250
+ crop: zeroCrop(),
251
+ differs
252
+ };
253
+ }
254
+ return {
255
+ flow: true,
256
+ width: placement.bleedBox.width * PX_PER_MM,
257
+ height: null,
258
+ scale: 1,
259
+ x: placement.bleedBox.x,
260
+ y: placement.bleedBox.y,
261
+ bands: {
262
+ inline: 0,
263
+ block: 0
264
+ },
265
+ crop: zeroCrop(),
266
+ differs
267
+ };
268
+ }
258
269
  if (fit === "contain") {
259
- const scale = Math.min(placement.trim.width * PX_PER_MM / page.size.width, placement.trim.height * PX_PER_MM / page.size.height);
260
- const width = page.size.width * scale / PX_PER_MM;
261
- const height = page.size.height * scale / PX_PER_MM;
270
+ const scale = Math.min(placement.trim.width * PX_PER_MM / size.width, placement.trim.height * PX_PER_MM / size.height);
271
+ const width = size.width * scale / PX_PER_MM;
272
+ const height = size.height * scale / PX_PER_MM;
262
273
  const inline = (placement.trim.width - width) / 2;
263
274
  const block = (placement.trim.height - height) / 2;
264
275
  return {
265
276
  flow: false,
266
- width: page.size.width,
267
- height: page.size.height,
277
+ width: size.width,
278
+ height: size.height,
268
279
  scale,
269
280
  x: roundMillimeters(placement.trim.x + inline),
270
281
  y: roundMillimeters(placement.trim.y + block),
@@ -278,15 +289,15 @@ function fitPage(page, placement, fit) {
278
289
  }
279
290
  const horizontalBleed = Math.max(placement.bleed.left, placement.bleed.right);
280
291
  const verticalBleed = Math.max(placement.bleed.top, placement.bleed.bottom);
281
- 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);
282
- const width = page.size.width * scale / PX_PER_MM;
283
- const height = page.size.height * scale / PX_PER_MM;
292
+ const scale = Math.max((placement.trim.width + horizontalBleed * 2) * PX_PER_MM / size.width, (placement.trim.height + verticalBleed * 2) * PX_PER_MM / size.height);
293
+ const width = size.width * scale / PX_PER_MM;
294
+ const height = size.height * scale / PX_PER_MM;
284
295
  const x = placement.trim.x + (placement.trim.width - width) / 2;
285
296
  const y = placement.trim.y + (placement.trim.height - height) / 2;
286
297
  return {
287
298
  flow: false,
288
- width: page.size.width,
289
- height: page.size.height,
299
+ width: size.width,
300
+ height: size.height,
290
301
  scale,
291
302
  x: roundMillimeters(x),
292
303
  y: roundMillimeters(y),
@@ -304,7 +315,7 @@ function fitPage(page, placement, fit) {
304
315
  };
305
316
  }
306
317
  function pagePlacementLayout(size, placement, fit) {
307
- if (size === "fluid") return {
318
+ if (size.width === "full") return {
308
319
  kind: "fluid",
309
320
  piece: {
310
321
  width: placement.bleedBox.width * PX_PER_MM,
@@ -316,14 +327,14 @@ function pagePlacementLayout(size, placement, fit) {
316
327
  if (size.height === "auto") return {
317
328
  kind: "growing",
318
329
  piece: {
319
- width: size.width,
330
+ width: fit.width,
320
331
  height: placement.trim.height * PX_PER_MM / fit.scale
321
332
  },
322
333
  inset: false,
323
334
  paper: {
324
335
  x: fit.x,
325
336
  y: fit.y,
326
- width: placement.trim.width,
337
+ width: fit.width * fit.scale / PX_PER_MM,
327
338
  height: placement.trim.height
328
339
  }
329
340
  };