@segnavia/html 0.37.0 → 0.38.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.
@@ -1,7 +1,23 @@
1
+ import { selectBookParent } from "../printed-pages.js";
1
2
  import { paginateColumns } from "./paginate-columns.js";
2
3
  import { paginateMarks } from "./paginate-marks.js";
3
4
  //#region src/book/paginate-sequence.ts
4
5
  function paginateSequence(input, fillPage) {
6
+ function selected(flow, opening, blank, role, pageIndex, parents = flow.parents) {
7
+ const fact = {
8
+ blank,
9
+ sectionOpening: opening,
10
+ ...role === void 0 ? {} : { role },
11
+ ...parents === void 0 ? {} : { parents }
12
+ };
13
+ return selectBookParent(fact, pageIndex, pageIndex, input.settings.direction, input.parentNames);
14
+ }
15
+ function pageHeight(parent) {
16
+ return input.parents[parent]?.height ?? 0;
17
+ }
18
+ function notesOnlyOpeningPage(page, opener) {
19
+ return opener && page.placed.length === 0 && page.fragments.length > 0 && !page.refusal;
20
+ }
5
21
  function emptyMarks() {
6
22
  return Object.create(null);
7
23
  }
@@ -37,7 +53,7 @@ function paginateSequence(input, fillPage) {
37
53
  }
38
54
  }
39
55
  function isFront(flow) {
40
- if (flow.kind === "fixed") return flow.sequence === "front";
56
+ if (flow.sequence !== void 0) return flow.sequence === "front";
41
57
  return flow.kind === "front" || flow.kind === "contents";
42
58
  }
43
59
  function unitsOf(flow) {
@@ -62,9 +78,6 @@ function paginateSequence(input, fillPage) {
62
78
  }
63
79
  return units;
64
80
  }
65
- function flowParents(flow) {
66
- return flow.parents === void 0 ? {} : { parents: flow.parents };
67
- }
68
81
  function openingOf(flow) {
69
82
  if (flow.opens === "recto" || flow.opens === "verso" || flow.opens === "any") return flow.opens;
70
83
  return input.settings.rectoOpenings ? "recto" : "any";
@@ -75,7 +88,8 @@ function paginateSequence(input, fillPage) {
75
88
  if (opens === "verso") return nextIsRecto;
76
89
  return false;
77
90
  }
78
- function makeBlank(number, sequence, flow) {
91
+ function makeBlank(number, sequence, flow, physicalIndex) {
92
+ const planned = selected(flow, false, true, "body", physicalIndex);
79
93
  return {
80
94
  kind: "blank",
81
95
  number,
@@ -86,10 +100,11 @@ function paginateSequence(input, fillPage) {
86
100
  notes: [],
87
101
  blank: true,
88
102
  sectionOpening: false,
89
- ...flowParents(flow)
103
+ parent: planned.parent,
104
+ side: planned.side
90
105
  };
91
106
  }
92
- function makeContent(part, number, flow, opener, placed, fragments, marks) {
107
+ function makeContent(part, number, flow, opener, placed, fragments, marks, planned) {
93
108
  const layout = paginateColumns(placed);
94
109
  return {
95
110
  kind: part,
@@ -103,12 +118,15 @@ function paginateSequence(input, fillPage) {
103
118
  blank: false,
104
119
  sectionOpening: opener,
105
120
  ...opener || flow.runningHead === void 0 ? {} : { runningHead: flow.runningHead },
121
+ ...flow.parts === void 0 ? {} : { parts: flow.parts },
106
122
  marks,
107
- ...flowParents(flow)
123
+ parent: planned.parent,
124
+ side: planned.side
108
125
  };
109
126
  }
110
- function makeFixed(part, number, flow, opener, marks) {
127
+ function makeFixed(part, number, flow, opener, marks, physicalIndex, parents = flow.parents) {
111
128
  const templated = flow.kind === "fixed" && flow.role === "body";
129
+ const planned = selected(flow, opener, false, templated ? "body" : "fixed", physicalIndex, parents);
112
130
  return {
113
131
  kind: part,
114
132
  number,
@@ -120,38 +138,47 @@ function paginateSequence(input, fillPage) {
120
138
  blank: false,
121
139
  sectionOpening: opener,
122
140
  ...opener && !templated || flow.runningHead === void 0 ? {} : { runningHead: flow.runningHead },
141
+ ...flow.parts === void 0 ? {} : { parts: flow.parts },
123
142
  marks,
124
- ...flowParents(flow)
143
+ parent: planned.parent,
144
+ side: planned.side
125
145
  };
126
146
  }
127
- function emitMarked(part, flow, opener, placed, fragments, pages, carried) {
147
+ function emitMarked(part, flow, opener, placed, fragments, pages, carried, planned) {
128
148
  const marked = paginateMarks(placed, carried);
129
- pages.push(makeContent(part, pages.length + 1, flow, opener, placed, fragments, marked.marks));
149
+ pages.push(makeContent(part, pages.length + 1, flow, opener, placed, fragments, marked.marks, planned));
130
150
  return marked.carried;
131
151
  }
132
- function appendFixed(flow, part, pages, continued, carried) {
152
+ function appendFixed(flow, part, pages, continued, carried, physicalOffset) {
133
153
  const count = flow.kind === "fixed" ? flow.pages : 0;
134
154
  let next = carried;
135
155
  for (let index = 0; index < count; index += 1) {
136
156
  const opener = index === 0;
137
157
  const marked = paginateMarks([], next);
138
158
  next = marked.carried;
139
- pages.push(makeFixed(part, pages.length + 1, flow, opener, marked.marks));
159
+ pages.push(makeFixed(part, pages.length + 1, flow, opener, marked.marks, physicalOffset + pages.length + 1, flow.kind === "fixed" ? flow.pageParents?.[index] : void 0));
140
160
  }
141
161
  return {
142
162
  pending: continued,
143
163
  carried: next
144
164
  };
145
165
  }
146
- function paginateFlow(flow, part, pages, continued, carried) {
147
- if (flow.kind === "fixed") return appendFixed(flow, part, pages, continued, carried);
166
+ function paginateFlowing(flow, part, pages, continued, carried, physicalOffset) {
148
167
  const units = unitsOf(flow);
149
168
  let index = 0;
150
169
  let pending = continued;
151
170
  let opener = true;
152
171
  let nextCarried = carried;
153
172
  while (index < units.length || pending.length > 0) {
154
- const page = fillPage(units, index, pending);
173
+ let planned = selected(flow, opener, false, void 0, physicalOffset + pages.length + 1);
174
+ const height = pageHeight(planned.parent);
175
+ const textArea = opener ? `the "${planned.parent}" opener parent's ${height}px text area` : void 0;
176
+ let page = fillPage(units, index, pending, height, textArea);
177
+ const isOpening = opener && page.placed.length > 0;
178
+ if (notesOnlyOpeningPage(page, opener)) {
179
+ planned = selected(flow, isOpening, false, void 0, physicalOffset + pages.length + 1);
180
+ page = fillPage(units, index, pending, pageHeight(planned.parent));
181
+ }
155
182
  if (page.refusal) return {
156
183
  refusal: page.refusal,
157
184
  pending,
@@ -161,7 +188,7 @@ function paginateSequence(input, fillPage) {
161
188
  pending,
162
189
  carried: nextCarried
163
190
  };
164
- nextCarried = emitMarked(part, flow, opener && page.placed.length > 0, page.placed, page.fragments, pages, nextCarried);
191
+ nextCarried = emitMarked(part, flow, isOpening, page.placed, page.fragments, pages, nextCarried, planned);
165
192
  opener = opener && page.placed.length === 0;
166
193
  index = page.index;
167
194
  pending = page.pending;
@@ -171,27 +198,32 @@ function paginateSequence(input, fillPage) {
171
198
  carried: nextCarried
172
199
  };
173
200
  }
174
- function flushNotes(continued, flow, part, pages, carried) {
201
+ function paginateFlow(flow, part, pages, continued, carried, physicalOffset) {
202
+ if (flow.kind === "fixed") return appendFixed(flow, part, pages, continued, carried, physicalOffset);
203
+ return paginateFlowing(flow, part, pages, continued, carried, physicalOffset);
204
+ }
205
+ function flushNotes(continued, flow, part, pages, carried, physicalOffset) {
175
206
  let pending = continued;
176
207
  let nextCarried = carried;
177
208
  while (pending.length > 0) {
178
- const page = fillPage([], 0, pending);
209
+ const planned = selected(flow, false, false, "body", physicalOffset + pages.length + 1);
210
+ const page = fillPage([], 0, pending, pageHeight(planned.parent));
179
211
  if (page.refusal) return {
180
212
  refusal: page.refusal,
181
213
  carried: nextCarried
182
214
  };
183
215
  if (page.done || page.placed.length === 0 && page.fragments.length === 0) return { carried: nextCarried };
184
- nextCarried = emitMarked(part, flow, false, page.placed, page.fragments, pages, nextCarried);
216
+ nextCarried = emitMarked(part, flow, false, page.placed, page.fragments, pages, nextCarried, planned);
185
217
  pending = page.pending;
186
218
  }
187
219
  return { carried: nextCarried };
188
220
  }
189
- function flushBeforeFixed(flow, previous, continued, carried, part, pages) {
221
+ function flushBeforeFixed(flow, previous, continued, carried, part, pages, physicalOffset) {
190
222
  if (flow.kind !== "fixed" || continued.length === 0 || !previous) return {
191
223
  continued,
192
224
  carried
193
225
  };
194
- const flushed = flushNotes(continued, previous, part, pages, carried);
226
+ const flushed = flushNotes(continued, previous, part, pages, carried, physicalOffset);
195
227
  if ("refusal" in flushed && flushed.refusal) return { refusal: flushed.refusal };
196
228
  return {
197
229
  continued: [],
@@ -199,10 +231,10 @@ function paginateSequence(input, fillPage) {
199
231
  };
200
232
  }
201
233
  function emitFlow(flow, previous, continued, carried, part, pages, physicalOffset) {
202
- const held = flushBeforeFixed(flow, previous, continued, carried, part, pages);
234
+ const held = flushBeforeFixed(flow, previous, continued, carried, part, pages, physicalOffset);
203
235
  if ("refusal" in held && held.refusal) return { refusal: held.refusal };
204
- if (needsOpeningBlank(openingOf(flow), physicalOffset + pages.length)) pages.push(makeBlank(pages.length + 1, part, flow));
205
- const result = paginateFlow(flow, part, pages, held.continued, held.carried);
236
+ if (needsOpeningBlank(openingOf(flow), physicalOffset + pages.length)) pages.push(makeBlank(pages.length + 1, part, flow, physicalOffset + pages.length + 1));
237
+ const result = paginateFlow(flow, part, pages, held.continued, held.carried, physicalOffset);
206
238
  if ("refusal" in result && result.refusal) return { refusal: result.refusal };
207
239
  return {
208
240
  continued: result.pending,
@@ -226,7 +258,7 @@ function paginateSequence(input, fillPage) {
226
258
  }
227
259
  const last = flows[flows.length - 1];
228
260
  if (last) {
229
- const flushed = flushNotes(continued, last, part, pages, carried);
261
+ const flushed = flushNotes(continued, last, part, pages, carried, physicalOffset);
230
262
  if ("refusal" in flushed && flushed.refusal) return {
231
263
  pages,
232
264
  refusals: [flushed.refusal]
@@ -248,7 +280,7 @@ function paginateSequence(input, fillPage) {
248
280
  const firstBody = bodyFlows[0];
249
281
  const lastFront = frontFlows[frontFlows.length - 1];
250
282
  const padFront = input.settings.rectoOpenings ? front.pages.length % 2 === 1 : firstBody !== void 0 && needsOpeningBlank(openingOf(firstBody), front.pages.length);
251
- if (front.pages.length > 0 && lastFront && padFront) front.pages.push(makeBlank(front.pages.length + 1, "front", lastFront));
283
+ if (front.pages.length > 0 && lastFront && padFront) front.pages.push(makeBlank(front.pages.length + 1, "front", lastFront, front.pages.length + 1));
252
284
  const body = paginatePart(bodyFlows, "body", front.pages.length);
253
285
  if (body.refusals.length > 0) return {
254
286
  pages: [...front.pages, ...body.pages],
@@ -1,4 +1,4 @@
1
- import { BookOpens, BookSectionParents } from "@segnavia/format";
1
+ import { BOOK_PARENT_NAMES, BookOpens, BookSectionParents, TextArea } from "@segnavia/format";
2
2
  //#region src/book/paginate-types.d.ts
3
3
  export type PaginateSequence = 'front' | 'body';
4
4
  export type PaginatePageRole = 'opener' | 'body' | 'blank' | 'fixed';
@@ -36,18 +36,20 @@ export type PaginateFlowParents = BookSectionParents;
36
36
  export type PaginateOpens = BookOpens;
37
37
  interface PaginateFlowBase {
38
38
  id: string;
39
+ sequence?: PaginateSequence;
39
40
  runningHead?: string;
40
41
  opens?: PaginateOpens;
41
42
  parents?: PaginateFlowParents;
43
+ parts?: Record<string, string>;
42
44
  }
43
45
  export type PaginateFlow = (PaginateFlowBase & {
44
- kind: 'front' | 'contents' | 'chapter' | 'endnotes';
46
+ kind: 'front' | 'contents' | 'part' | 'chapter' | 'endnotes';
45
47
  blocks: PaginateBlock[];
46
48
  }) | (PaginateFlowBase & {
47
49
  kind: 'fixed';
48
50
  pages: number;
49
- sequence?: PaginateSequence;
50
51
  role?: 'fixed' | 'body';
52
+ pageParents?: PaginateFlowParents[];
51
53
  });
52
54
  export interface PaginateNote {
53
55
  id: string;
@@ -57,9 +59,10 @@ export interface PaginateBookInput {
57
59
  flows: PaginateFlow[];
58
60
  notes: PaginateNote[];
59
61
  geometry: {
60
- textHeight: number;
61
62
  notesGap: number;
62
63
  };
64
+ parents: Record<string, TextArea>;
65
+ parentNames: typeof BOOK_PARENT_NAMES;
63
66
  settings: {
64
67
  orphans?: number;
65
68
  widows?: number;
@@ -98,8 +101,10 @@ export interface PaginateBookPage {
98
101
  blank: boolean;
99
102
  sectionOpening: boolean;
100
103
  runningHead?: string;
104
+ parts?: Record<string, string>;
101
105
  marks?: PaginatePageMarks;
102
- parents?: PaginateFlowParents;
106
+ parent: string;
107
+ side: 'left' | 'right';
103
108
  }
104
109
  export type PaginateBookRefusal = {
105
110
  tag: string;
@@ -0,0 +1,16 @@
1
+ //#region src/book/region-fit.d.ts
2
+ export declare const ABSOLUTE_UNITS: readonly ["px", "pt", "pc", "in", "cm", "mm", "q"];
3
+ type AbsoluteUnit = (typeof ABSOLUTE_UNITS)[number];
4
+ export declare const ABSOLUTE_PX: Record<AbsoluteUnit, number>;
5
+ export declare function parseCssLength(value: string): {
6
+ value: number;
7
+ unit: string;
8
+ } | undefined;
9
+ export declare function lengthPx(value: string, table: Readonly<Record<string, number>>): number | undefined;
10
+ export declare function fitRegionSize(max: number, min: number, step: number, overflows: (size: number) => boolean): {
11
+ overflow: boolean;
12
+ size: number;
13
+ } | {
14
+ usable: false;
15
+ };
16
+ //#endregion
@@ -0,0 +1,111 @@
1
+ import { PX_PER_MM, PX_PER_PT } from "@segnavia/format";
2
+ //#region src/book/region-fit.ts
3
+ const ABSOLUTE_UNITS = [
4
+ "px",
5
+ "pt",
6
+ "pc",
7
+ "in",
8
+ "cm",
9
+ "mm",
10
+ "q"
11
+ ];
12
+ const ABSOLUTE_PX = {
13
+ px: 1,
14
+ pt: PX_PER_PT,
15
+ pc: 12 * PX_PER_PT,
16
+ in: 72 * PX_PER_PT,
17
+ cm: 10 * PX_PER_MM,
18
+ mm: PX_PER_MM,
19
+ q: PX_PER_MM / 4
20
+ };
21
+ function listedAbsoluteUnits() {
22
+ const last = ABSOLUTE_UNITS.length - 1;
23
+ return `${ABSOLUTE_UNITS.slice(0, last).join(", ")} or ${ABSOLUTE_UNITS[last]}`;
24
+ }
25
+ function isAbsoluteUnit(unit) {
26
+ return ABSOLUTE_UNITS.includes(unit);
27
+ }
28
+ function parseCssLength(value) {
29
+ const held = value.trim().replace(/\s*!important\s*$/i, "");
30
+ const match = /^([+-]?(?:\d+(?:\.\d+)?|\.\d+))([a-z%]+)$/i.exec(held);
31
+ const amount = match?.[1];
32
+ const unit = match?.[2];
33
+ if (amount === void 0 || unit === void 0) return void 0;
34
+ return {
35
+ value: Number(amount),
36
+ unit: unit.toLowerCase()
37
+ };
38
+ }
39
+ function lengthPx(value, table) {
40
+ const parsed = parseCssLength(value);
41
+ if (parsed === void 0 || !(parsed.value > 0)) return void 0;
42
+ const factor = table[parsed.unit];
43
+ return factor === void 0 ? void 0 : parsed.value * factor;
44
+ }
45
+ function fitExample(attribute) {
46
+ return attribute === "data-fit-min" ? "48pt" : "1pt";
47
+ }
48
+ function fitLengthIssues(where, attribute, value) {
49
+ const example = fitExample(attribute);
50
+ const parsed = parseCssLength(value);
51
+ if (parsed === void 0) return [`${where} ${attribute} "${value}" is not an absolute CSS length; use an absolute CSS length such as ${example} to clear this refusal`];
52
+ if (!isAbsoluteUnit(parsed.unit)) return [`${where} ${attribute} "${value}" uses unit "${parsed.unit}"; use an absolute CSS length (${listedAbsoluteUnits()}) such as ${example} to clear this refusal`];
53
+ if (!(parsed.value > 0)) return [`${where} ${attribute} "${value}" is not a positive absolute CSS length; use a positive absolute CSS length such as ${example} to clear this refusal`];
54
+ return [];
55
+ }
56
+ function regionFitIssues(where, attribs) {
57
+ const minText = attribs["data-fit-min"];
58
+ const stepText = attribs["data-fit-step"];
59
+ if (minText === void 0 && stepText === void 0) return [];
60
+ const issues = [];
61
+ if (minText !== void 0) issues.push(...fitLengthIssues(where, "data-fit-min", minText));
62
+ if (minText !== void 0 || stepText !== void 0) issues.push(...fitLengthIssues(where, "data-fit-step", stepText ?? ""));
63
+ return issues;
64
+ }
65
+ function namedRegionFitIssues(where, regions) {
66
+ return regions.flatMap((node) => {
67
+ const region = node.attribs["data-region"];
68
+ return region ? regionFitIssues(`${where} region "${region}"`, node.attribs) : [];
69
+ });
70
+ }
71
+ function templateRegionFitIssues(template, regions) {
72
+ return namedRegionFitIssues(`template "${template}"`, regions);
73
+ }
74
+ function fitRegionSize(max, min, step, overflows) {
75
+ function sizeAt(index) {
76
+ return max - index * step;
77
+ }
78
+ if (!(max >= min) || !(step > 0) || !Number.isFinite(max) || !Number.isFinite(min) || !Number.isFinite(step)) return { usable: false };
79
+ const last = Math.floor((max - min) / step + 1e-9);
80
+ if (!Number.isSafeInteger(last)) {
81
+ if (!overflows(max)) return {
82
+ size: max,
83
+ overflow: false
84
+ };
85
+ return {
86
+ size: min,
87
+ overflow: overflows(min)
88
+ };
89
+ }
90
+ let low = 0;
91
+ let high = last;
92
+ let fitted = last + 1;
93
+ while (low <= high) {
94
+ const mid = Math.floor((low + high) / 2);
95
+ if (overflows(sizeAt(mid))) low = mid + 1;
96
+ else {
97
+ fitted = mid;
98
+ high = mid - 1;
99
+ }
100
+ }
101
+ if (fitted > last) return {
102
+ size: sizeAt(last),
103
+ overflow: true
104
+ };
105
+ return {
106
+ size: sizeAt(fitted),
107
+ overflow: false
108
+ };
109
+ }
110
+ //#endregion
111
+ export { ABSOLUTE_PX, ABSOLUTE_UNITS, fitRegionSize, lengthPx, namedRegionFitIssues, parseCssLength, templateRegionFitIssues };
@@ -10,17 +10,19 @@ export interface BookDocumentOptions {
10
10
  export type BookBuild = 'print' | 'galley' | 'flow';
11
11
  export interface BookSectionInfo {
12
12
  id: string;
13
- kind: 'front' | 'contents' | 'chapter' | 'endnotes' | 'designed' | 'templated';
13
+ kind: 'front' | 'contents' | 'part' | 'chapter' | 'endnotes' | 'designed' | 'templated';
14
+ division: 'front' | 'body' | 'end';
14
15
  title?: string;
15
16
  runningHead?: string;
16
17
  }
17
18
  export type BookPrintPart = 'front' | 'body';
18
- export declare function bookPrintPart(kind: BookSectionInfo['kind']): BookPrintPart;
19
+ export declare function bookPrintPart(division: BookSectionInfo['division']): BookPrintPart;
19
20
  export interface BookHeading {
20
21
  id: string;
21
22
  level: number;
22
23
  text: string;
23
24
  sectionId: string;
25
+ depth?: number;
24
26
  }
25
27
  export interface BookDocument {
26
28
  html: string;
@@ -1,6 +1,6 @@
1
1
  //#region src/book/types.ts
2
- function bookPrintPart(kind) {
3
- return kind === "front" || kind === "contents" ? "front" : "body";
2
+ function bookPrintPart(division) {
3
+ return division === "front" ? "front" : "body";
4
4
  }
5
5
  //#endregion
6
6
  export { bookPrintPart };
package/dist/index.d.ts CHANGED
@@ -2,19 +2,20 @@ import { base64ToBytes, bytesToBase64 } from "./base64.js";
2
2
  import { REFUSED_ELEMENTS, SanitizeOptions, findActiveContent, sanitize } from "./sanitize.js";
3
3
  import { BINDINGS, FillOptions, Filled, bindingNames, bindingRoot, fill, hasBindings } from "./bindings.js";
4
4
  import { BookBuild, BookDocument, BookDocumentOptions, BookHeading, BookPrintPart, BookSectionInfo, bookPrintPart } from "./book/types.js";
5
- import { bookDocument } from "./book/book-document.js";
6
5
  import { BOOK_COLUMN_MAX, BOOK_COLUMN_MIN, BOOK_FLOW_COLUMN_MIN_PX, BookPositionKey, parseBookPositionKey } from "./book/book-flow.js";
6
+ import { bookDocument } from "./book/book-document.js";
7
7
  import { bookView, bookViewFromHtml } from "./book/book-view.js";
8
8
  import { PaginateBlock, PaginateBookInput, PaginateBookPage, PaginateBookRefusal, PaginateBookResult, PaginateFlow, PaginateFlowParents, PaginateLine, PaginateMark, PaginateNote, PaginateNoteRef, PaginateOpens, PaginatePageMarks, PaginatePageNote, PaginatePageRole, PaginateSequence, PaginateWindow } from "./book/paginate-types.js";
9
9
  import { BookFurnitureValues, embedFunction, fillBookFurniture, furnitureScript } from "./book/furniture.js";
10
+ import { BookPageFacts, BookPrintedPagePlan, BookPrintedPageRole, BookSectionParents, PrintedPagePlan, PrintedPageSide, planBookPages, planPrintedPages, printedPageAt, selectBookParent } from "./printed-pages.js";
10
11
  import { PAGINATE_FUNCTIONS, paginateBook, paginateScript } from "./book/paginate-book.js";
11
12
  import { PaginateTarget, PaginateTargetPage, heldPageNumber, openerPageIndex, pageIndexForTarget, windowPageIndex } from "./book/paginate-target.js";
13
+ import { ABSOLUTE_PX, ABSOLUTE_UNITS, fitRegionSize, lengthPx, parseCssLength } from "./book/region-fit.js";
12
14
  import { bookStylesheet, sanitizeBookCss } from "./book-css.js";
13
15
  import { FontCssRefusedError, fontCss, fontFaceCss } from "./font-css.js";
14
16
  import { fontFormat } from "./font-format.js";
15
17
  import { fontReferenceRefusals } from "./font-references.js";
16
- import { ExpandMotifsOptions, ExpandedMotifs, MOTIF_DEPTH_LIMIT, MotifRefusedError, PlacedMotif, documentLanguage, expandMotifs, hasMotifPlacement, libraryMotifPlacementRefusal, motifPlacementRefusals, renderMotifPlacements } from "./motifs.js";
17
- import { BookPageFacts, BookPrintedPagePlan, BookPrintedPageRole, BookSectionParents, PrintedPagePlan, PrintedPageSide, planBookPages, planPrintedPages, printedPageAt, selectBookParent } from "./printed-pages.js";
18
+ import { ExpandMotifsOptions, ExpandedMotifs, MOTIF_DEPTH_LIMIT, MotifRefusedError, PlacedMotif, documentLanguage, expandMotifs, hasHostMotifPlacement, hasMotifPlacement, libraryMotifPlacementRefusal, motifPlacementRefusals, renderMotifPlacements } from "./motifs.js";
18
19
  import { SlotFillEntry, SlotFillRefusal, SlotFillsConflictError, SlotFillsRefusedError, SlotPrintOptions, assertSingleSlotCopy, assertSlotPrintOptions, collectSlotPrintCopies, isSlotCopyRefusal, slotFillCopyId, slotFillsConflict } from "./slot-print.js";
19
20
  import { RequiredSlotsRefusedError, UnmeasuredSlotsRefusedError, mergeSlotFitResults, requiredSlotRefusal, unmeasuredSlotRefusal } from "./slot-policy.js";
20
21
  import { MAX_SLOT_DECLARATIONS, PreparedSlots, SlotFillRefusedError, hasSlotWork, prepareSlotCollection, prepareSlots, slotFillRefusal } from "./slots.js";
@@ -24,4 +25,4 @@ export type HtmlKind = 'plain' | 'live-data' | 'scripted' | 'refused';
24
25
  export declare function classify(html: string, prefix: string): HtmlKind;
25
26
  export declare function searchText(html: string): string;
26
27
  //#endregion
27
- export { BINDINGS, BOOK_COLUMN_MAX, BOOK_COLUMN_MIN, BOOK_FLOW_COLUMN_MIN_PX, type BookBuild, type BookDocument, type BookDocumentOptions, type BookFurnitureValues, type BookHeading, BookPageFacts, type BookPositionKey, type BookPrintPart, BookPrintedPagePlan, BookPrintedPageRole, type BookSectionInfo, type BookSectionParents, type ExpandMotifsOptions, type ExpandedMotifs, type FillOptions, type Filled, FontCssRefusedError, MAX_SLOT_DECLARATIONS, MOTIF_DEPTH_LIMIT, MotifRefusedError, PAGINATE_FUNCTIONS, type PaginateBlock, type PaginateBookInput, type PaginateBookPage, type PaginateBookRefusal, type PaginateBookResult, type PaginateFlow, type PaginateFlowParents, type PaginateLine, type PaginateMark, type PaginateNote, type PaginateNoteRef, type PaginateOpens, type PaginatePageMarks, type PaginatePageNote, type PaginatePageRole, type PaginateSequence, type PaginateTarget, type PaginateTargetPage, type PaginateWindow, type PlacedMotif, type PreparedSlots, PrintedPagePlan, PrintedPageSide, REFUSED_ELEMENTS, RequiredSlotsRefusedError, type SanitizeOptions, type SlotFillEntry, type SlotFillRefusal, SlotFillRefusedError, SlotFillsConflictError, SlotFillsRefusedError, type SlotPrintOptions, UNICODE_RANGE_CLEARANCE, type UnicodeRangeParse, UnmeasuredSlotsRefusedError, assertSingleSlotCopy, assertSlotPrintOptions, base64ToBytes, bindingNames, bindingRoot, bookDocument, bookPrintPart, bookStylesheet, bookView, bookViewFromHtml, bytesToBase64, collectSlotPrintCopies, documentLanguage, embedFunction, expandMotifs, fill, fillBookFurniture, findActiveContent, fontCss, fontFaceCss, fontFormat, fontReferenceRefusals, furnitureScript, hasBindings, hasMotifPlacement, hasSlotWork, heldPageNumber, isSlotCopyRefusal, libraryMotifPlacementRefusal, mergeSlotFitResults, motifPlacementRefusals, openerPageIndex, pageIndexForTarget, paginateBook, paginateScript, parseBookPositionKey, parseUnicodeRange, planBookPages, planPrintedPages, prepareSlotCollection, prepareSlots, printedPageAt, renderMotifPlacements, requiredSlotRefusal, sanitize, sanitizeBookCss, selectBookParent, slotFillCopyId, slotFillRefusal, slotFillsConflict, unmeasuredSlotRefusal, windowPageIndex };
28
+ export { ABSOLUTE_PX, ABSOLUTE_UNITS, BINDINGS, BOOK_COLUMN_MAX, BOOK_COLUMN_MIN, BOOK_FLOW_COLUMN_MIN_PX, type BookBuild, type BookDocument, type BookDocumentOptions, type BookFurnitureValues, type BookHeading, BookPageFacts, type BookPositionKey, type BookPrintPart, BookPrintedPagePlan, BookPrintedPageRole, type BookSectionInfo, type BookSectionParents, type ExpandMotifsOptions, type ExpandedMotifs, type FillOptions, type Filled, FontCssRefusedError, MAX_SLOT_DECLARATIONS, MOTIF_DEPTH_LIMIT, MotifRefusedError, PAGINATE_FUNCTIONS, type PaginateBlock, type PaginateBookInput, type PaginateBookPage, type PaginateBookRefusal, type PaginateBookResult, type PaginateFlow, type PaginateFlowParents, type PaginateLine, type PaginateMark, type PaginateNote, type PaginateNoteRef, type PaginateOpens, type PaginatePageMarks, type PaginatePageNote, type PaginatePageRole, type PaginateSequence, type PaginateTarget, type PaginateTargetPage, type PaginateWindow, type PlacedMotif, type PreparedSlots, PrintedPagePlan, PrintedPageSide, REFUSED_ELEMENTS, RequiredSlotsRefusedError, type SanitizeOptions, type SlotFillEntry, type SlotFillRefusal, SlotFillRefusedError, SlotFillsConflictError, SlotFillsRefusedError, type SlotPrintOptions, UNICODE_RANGE_CLEARANCE, type UnicodeRangeParse, UnmeasuredSlotsRefusedError, assertSingleSlotCopy, assertSlotPrintOptions, base64ToBytes, bindingNames, bindingRoot, bookDocument, bookPrintPart, bookStylesheet, bookView, bookViewFromHtml, bytesToBase64, collectSlotPrintCopies, documentLanguage, embedFunction, expandMotifs, fill, fillBookFurniture, findActiveContent, fitRegionSize, fontCss, fontFaceCss, fontFormat, fontReferenceRefusals, furnitureScript, hasBindings, hasHostMotifPlacement, hasMotifPlacement, hasSlotWork, heldPageNumber, isSlotCopyRefusal, lengthPx, libraryMotifPlacementRefusal, mergeSlotFitResults, motifPlacementRefusals, openerPageIndex, pageIndexForTarget, paginateBook, paginateScript, parseBookPositionKey, parseCssLength, parseUnicodeRange, planBookPages, planPrintedPages, prepareSlotCollection, prepareSlots, printedPageAt, renderMotifPlacements, requiredSlotRefusal, sanitize, sanitizeBookCss, selectBookParent, slotFillCopyId, slotFillRefusal, slotFillsConflict, unmeasuredSlotRefusal, windowPageIndex };
package/dist/index.js CHANGED
@@ -2,10 +2,12 @@ import { REFUSED_ELEMENTS, elements, findActiveContent, parse, sanitize } from "
2
2
  import { BINDINGS, bindingNames, bindingRoot, fill, hasBindings } from "./bindings.js";
3
3
  import { base64ToBytes, bytesToBase64 } from "./base64.js";
4
4
  import { BOOK_COLUMN_MAX, BOOK_COLUMN_MIN, BOOK_FLOW_COLUMN_MIN_PX, parseBookPositionKey } from "./book/book-flow.js";
5
+ import { ABSOLUTE_PX, ABSOLUTE_UNITS, fitRegionSize, lengthPx, parseCssLength } from "./book/region-fit.js";
5
6
  import { bookPrintPart } from "./book/types.js";
6
7
  import { bookDocument } from "./book/book-document.js";
7
8
  import { bookView, bookViewFromHtml } from "./book/book-view.js";
8
9
  import { embedFunction, fillBookFurniture, furnitureScript } from "./book/furniture.js";
10
+ import { planBookPages, planPrintedPages, printedPageAt, selectBookParent } from "./printed-pages.js";
9
11
  import { PAGINATE_FUNCTIONS, paginateBook, paginateScript } from "./book/paginate-book.js";
10
12
  import { heldPageNumber, openerPageIndex, pageIndexForTarget, windowPageIndex } from "./book/paginate-target.js";
11
13
  import { bookStylesheet, sanitizeBookCss } from "./book-css.js";
@@ -13,8 +15,7 @@ import { UNICODE_RANGE_CLEARANCE, parseUnicodeRange } from "./unicode-range.js";
13
15
  import { FontCssRefusedError, fontCss, fontFaceCss } from "./font-css.js";
14
16
  import { fontFormat } from "./font-format.js";
15
17
  import { fontReferenceRefusals } from "./font-references.js";
16
- import { MOTIF_DEPTH_LIMIT, MotifRefusedError, documentLanguage, expandMotifs, hasMotifPlacement, libraryMotifPlacementRefusal, motifPlacementRefusals, renderMotifPlacements } from "./motifs.js";
17
- import { planBookPages, planPrintedPages, printedPageAt, selectBookParent } from "./printed-pages.js";
18
+ import { MOTIF_DEPTH_LIMIT, MotifRefusedError, documentLanguage, expandMotifs, hasHostMotifPlacement, hasMotifPlacement, libraryMotifPlacementRefusal, motifPlacementRefusals, renderMotifPlacements } from "./motifs.js";
18
19
  import { RequiredSlotsRefusedError, UnmeasuredSlotsRefusedError, mergeSlotFitResults, requiredSlotRefusal, unmeasuredSlotRefusal } from "./slot-policy.js";
19
20
  import { MAX_SLOT_DECLARATIONS, SlotFillRefusedError, hasSlotWork, prepareSlotCollection, prepareSlots, slotFillRefusal } from "./slots.js";
20
21
  import { SlotFillsConflictError, SlotFillsRefusedError, assertSingleSlotCopy, assertSlotPrintOptions, collectSlotPrintCopies, isSlotCopyRefusal, slotFillCopyId, slotFillsConflict } from "./slot-print.js";
@@ -30,4 +31,4 @@ function searchText(html) {
30
31
  return html.replace(/<(script|style)\b[^>]*>[\s\S]*?<\/\1\s*>/gi, " ").replace(/<[^>]+>/g, " ").replace(/&nbsp;/g, " ").replace(/\s+/g, " ").trim();
31
32
  }
32
33
  //#endregion
33
- export { BINDINGS, BOOK_COLUMN_MAX, BOOK_COLUMN_MIN, BOOK_FLOW_COLUMN_MIN_PX, FontCssRefusedError, MAX_SLOT_DECLARATIONS, MOTIF_DEPTH_LIMIT, MotifRefusedError, PAGINATE_FUNCTIONS, REFUSED_ELEMENTS, RequiredSlotsRefusedError, SlotFillRefusedError, SlotFillsConflictError, SlotFillsRefusedError, UNICODE_RANGE_CLEARANCE, UnmeasuredSlotsRefusedError, assertSingleSlotCopy, assertSlotPrintOptions, base64ToBytes, bindingNames, bindingRoot, bookDocument, bookPrintPart, bookStylesheet, bookView, bookViewFromHtml, bytesToBase64, classify, collectSlotPrintCopies, documentLanguage, embedFunction, expandMotifs, fill, fillBookFurniture, findActiveContent, fontCss, fontFaceCss, fontFormat, fontReferenceRefusals, furnitureScript, hasBindings, hasMotifPlacement, hasSlotWork, heldPageNumber, isSlotCopyRefusal, libraryMotifPlacementRefusal, mergeSlotFitResults, motifPlacementRefusals, openerPageIndex, pageIndexForTarget, paginateBook, paginateScript, parseBookPositionKey, parseUnicodeRange, planBookPages, planPrintedPages, prepareSlotCollection, prepareSlots, printedPageAt, renderMotifPlacements, requiredSlotRefusal, sanitize, sanitizeBookCss, searchText, selectBookParent, slotFillCopyId, slotFillRefusal, slotFillsConflict, unmeasuredSlotRefusal, windowPageIndex };
34
+ export { ABSOLUTE_PX, ABSOLUTE_UNITS, BINDINGS, BOOK_COLUMN_MAX, BOOK_COLUMN_MIN, BOOK_FLOW_COLUMN_MIN_PX, FontCssRefusedError, MAX_SLOT_DECLARATIONS, MOTIF_DEPTH_LIMIT, MotifRefusedError, PAGINATE_FUNCTIONS, REFUSED_ELEMENTS, RequiredSlotsRefusedError, SlotFillRefusedError, SlotFillsConflictError, SlotFillsRefusedError, UNICODE_RANGE_CLEARANCE, UnmeasuredSlotsRefusedError, assertSingleSlotCopy, assertSlotPrintOptions, base64ToBytes, bindingNames, bindingRoot, bookDocument, bookPrintPart, bookStylesheet, bookView, bookViewFromHtml, bytesToBase64, classify, collectSlotPrintCopies, documentLanguage, embedFunction, expandMotifs, fill, fillBookFurniture, findActiveContent, fitRegionSize, fontCss, fontFaceCss, fontFormat, fontReferenceRefusals, furnitureScript, hasBindings, hasHostMotifPlacement, hasMotifPlacement, hasSlotWork, heldPageNumber, isSlotCopyRefusal, lengthPx, libraryMotifPlacementRefusal, mergeSlotFitResults, motifPlacementRefusals, openerPageIndex, pageIndexForTarget, paginateBook, paginateScript, parseBookPositionKey, parseCssLength, parseUnicodeRange, planBookPages, planPrintedPages, prepareSlotCollection, prepareSlots, printedPageAt, renderMotifPlacements, requiredSlotRefusal, sanitize, sanitizeBookCss, searchText, selectBookParent, slotFillCopyId, slotFillRefusal, slotFillsConflict, unmeasuredSlotRefusal, windowPageIndex };
package/dist/motifs.d.ts CHANGED
@@ -11,6 +11,7 @@ export interface ExpandMotifsOptions {
11
11
  dataFonts?: boolean;
12
12
  signal: AbortSignal;
13
13
  deferPaths?: Iterable<string>;
14
+ deferLibrary?: boolean;
14
15
  }
15
16
  export interface PlacedMotif extends MotifPlacement {
16
17
  version: number;
@@ -26,6 +27,7 @@ export declare class MotifRefusedError extends Error {
26
27
  }
27
28
  export declare function libraryMotifPlacementRefusal(html: string): string | undefined;
28
29
  export declare function hasMotifPlacement(html: string): boolean;
30
+ export declare function hasHostMotifPlacement(html: string): boolean;
29
31
  export declare function renderMotifPlacements(html: string, replacement: (key: string) => string | undefined): string;
30
32
  export declare function documentLanguage(html: string): string | undefined;
31
33
  export declare function expandMotifs(html: string, options: ExpandMotifsOptions): Promise<ExpandedMotifs>;
package/dist/motifs.js CHANGED
@@ -26,6 +26,9 @@ function libraryMotifPlacementRefusal(html) {
26
26
  function hasMotifPlacement(html) {
27
27
  return motifElements(html).length > 0;
28
28
  }
29
+ function hasHostMotifPlacement(html) {
30
+ return motifElements(html).some((element) => !(element.attribs[MOTIF_ATTRIBUTE] ?? "").startsWith(LIBRARY_MOTIF_PREFIX));
31
+ }
29
32
  function renderMotifPlacements(html, replacement) {
30
33
  const document = parse(html);
31
34
  const visit = (nodes) => {
@@ -207,7 +210,7 @@ async function expandNodes(nodes, inheritedLang, stack, state) {
207
210
  for (const node of [...nodes]) {
208
211
  state.options.signal.throwIfAborted();
209
212
  if (!isTag(node)) continue;
210
- if (Object.hasOwn(node.attribs, MOTIF_ATTRIBUTE)) await expandPlacement(node, inheritedLang, stack, state);
213
+ if (Object.hasOwn(node.attribs, MOTIF_ATTRIBUTE) && !(state.options.deferLibrary && (node.attribs[MOTIF_ATTRIBUTE] ?? "").startsWith(LIBRARY_MOTIF_PREFIX))) await expandPlacement(node, inheritedLang, stack, state);
211
214
  else await expandNodes(node.children, inheritedLang, stack, state);
212
215
  }
213
216
  }
@@ -246,4 +249,4 @@ async function motifPlacementRefusals(html, options) {
246
249
  return (await run(html, options, true)).state.refusals;
247
250
  }
248
251
  //#endregion
249
- export { MOTIF_DEPTH_LIMIT, MotifRefusedError, documentLanguage, expandMotifs, hasMotifPlacement, libraryMotifPlacementRefusal, motifPlacementRefusals, renderMotifPlacements };
252
+ export { MOTIF_DEPTH_LIMIT, MotifRefusedError, documentLanguage, expandMotifs, hasHostMotifPlacement, hasMotifPlacement, libraryMotifPlacementRefusal, motifPlacementRefusals, renderMotifPlacements };
@@ -1,4 +1,4 @@
1
- import { BookSectionParents as BookSectionParents$1, NumberSystem, PrintedPageRole, PrintedPageRules } from "@segnavia/format";
1
+ import { BOOK_PARENT_NAMES, BookSectionParents as BookSectionParents$1, NumberSystem, PrintedPageRole, PrintedPageRules } from "@segnavia/format";
2
2
  //#region src/printed-pages.d.ts
3
3
  export type PrintedPageSide = 'left' | 'right';
4
4
  export interface PrintedPagePlan {
@@ -26,12 +26,7 @@ export interface BookPrintedPagePlan extends Omit<BookPageFacts, 'blank' | 'sect
26
26
  }
27
27
  export declare function printedPageAt(rules: PrintedPageRules, index: number, count: number, direction: 'ltr' | 'rtl'): Pick<PrintedPagePlan, 'role' | 'parent' | 'side'>;
28
28
  export declare function planPrintedPages(rules: PrintedPageRules, count: number, direction: 'ltr' | 'rtl', numbering?: NumberSystem): PrintedPagePlan[];
29
- export declare function selectBookParent(fact: BookPageFacts, index: number, count: number, direction: 'ltr' | 'rtl', names?: {
30
- readonly recto: "segnavia:recto";
31
- readonly verso: "segnavia:verso";
32
- readonly opener: "segnavia:opener";
33
- readonly blank: "segnavia:blank";
34
- }): Pick<BookPrintedPagePlan, 'role' | 'parent' | 'side'>;
29
+ export declare function selectBookParent(fact: BookPageFacts, index: number, count: number, direction: 'ltr' | 'rtl', names: typeof BOOK_PARENT_NAMES): Pick<BookPrintedPagePlan, 'role' | 'parent' | 'side'>;
35
30
  export declare function planBookPages(facts: BookPageFacts[], direction: 'ltr' | 'rtl', names?: {
36
31
  readonly recto: "segnavia:recto";
37
32
  readonly verso: "segnavia:verso";
@@ -39,7 +39,7 @@ function planPrintedPages(rules, count, direction, numbering = "arabic") {
39
39
  };
40
40
  });
41
41
  }
42
- function selectBookParent(fact, index, count, direction, names = BOOK_PARENT_NAMES) {
42
+ function selectBookParent(fact, index, count, direction, names) {
43
43
  const physical = printedPageAt({
44
44
  every: names.recto,
45
45
  recto: names.recto,
@@ -4,6 +4,7 @@ export declare const REFUSED_ELEMENTS: Set<string>;
4
4
  export interface SanitizeOptions {
5
5
  linkOrigins?: Iterable<string>;
6
6
  keepScripts?: boolean;
7
+ dependencyOrigins?: Iterable<string>;
7
8
  dataFonts?: boolean;
8
9
  refuseElements?: Iterable<string>;
9
10
  }