@segnavia/html 0.26.0 → 0.28.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 (50) hide show
  1. package/dist/base64.d.ts +4 -0
  2. package/dist/base64.js +51 -0
  3. package/dist/bindings.d.ts +4 -1
  4. package/dist/bindings.js +19 -2
  5. package/dist/book/book-document.js +146 -65
  6. package/dist/book/book-held.js +182 -0
  7. package/dist/book/book-view.d.ts +5 -0
  8. package/dist/book/book-view.js +26 -0
  9. package/dist/book/furniture.d.ts +14 -0
  10. package/dist/book/furniture.js +36 -0
  11. package/dist/book/notes.js +18 -9
  12. package/dist/book/paginate-book.d.ts +11 -0
  13. package/dist/book/paginate-book.js +195 -0
  14. package/dist/book/paginate-columns.d.ts +8 -0
  15. package/dist/book/paginate-columns.js +119 -0
  16. package/dist/book/paginate-keeps.d.ts +4 -0
  17. package/dist/book/paginate-keeps.js +80 -0
  18. package/dist/book/paginate-marks.d.ts +7 -0
  19. package/dist/book/paginate-marks.js +34 -0
  20. package/dist/book/paginate-notes.d.ts +21 -0
  21. package/dist/book/paginate-notes.js +105 -0
  22. package/dist/book/paginate-sequence.d.ts +4 -0
  23. package/dist/book/paginate-sequence.js +267 -0
  24. package/dist/book/paginate-target.d.ts +13 -0
  25. package/dist/book/paginate-target.js +36 -0
  26. package/dist/book/paginate-types.d.ts +170 -0
  27. package/dist/book/types.d.ts +1 -1
  28. package/dist/book-css.d.ts +5 -0
  29. package/dist/book-css.js +14 -0
  30. package/dist/control-char.js +10 -0
  31. package/dist/css-tokens.js +189 -0
  32. package/dist/font-css.d.ts +11 -0
  33. package/dist/font-css.js +75 -0
  34. package/dist/font-format.d.ts +4 -0
  35. package/dist/font-format.js +11 -0
  36. package/dist/font-references.d.ts +4 -0
  37. package/dist/font-references.js +192 -0
  38. package/dist/index.d.ts +15 -2
  39. package/dist/index.js +14 -2
  40. package/dist/motifs.d.ts +32 -0
  41. package/dist/motifs.js +245 -0
  42. package/dist/printed-pages.d.ts +42 -0
  43. package/dist/printed-pages.js +97 -0
  44. package/dist/sanitize-css.d.ts +6 -0
  45. package/dist/sanitize-css.js +308 -0
  46. package/dist/sanitize.d.ts +1 -0
  47. package/dist/sanitize.js +37 -15
  48. package/dist/unicode-range.d.ts +11 -0
  49. package/dist/unicode-range.js +86 -0
  50. package/package.json +2 -2
@@ -0,0 +1,189 @@
1
+ //#region src/css-tokens.ts
2
+ function newlineEnd(source, at) {
3
+ return source[at] === "\r" && source[at + 1] === "\n" ? at + 2 : at + 1;
4
+ }
5
+ function cssEscape(source, at) {
6
+ let end = at + 1;
7
+ if (end >= source.length) return ["", end];
8
+ if (/[0-9a-f]/i.test(source[end] ?? "")) {
9
+ const start = end;
10
+ while (end < source.length && end - start < 6 && /[0-9a-f]/i.test(source[end] ?? "")) end++;
11
+ const point = Number.parseInt(source.slice(start, end), 16);
12
+ if (source[end] === "\r" && source[end + 1] === "\n") end += 2;
13
+ else if (/\s/.test(source[end] ?? "")) end++;
14
+ return [point === 0 || point > 1114111 ? "�" : String.fromCodePoint(point), end];
15
+ }
16
+ if (source[end] === "\n" || source[end] === "\r" || source[end] === "\f") return ["", newlineEnd(source, end)];
17
+ return [source[end] ?? "", end + 1];
18
+ }
19
+ function name(source, at) {
20
+ let value = "";
21
+ let end = at;
22
+ while (end < source.length) {
23
+ const character = source[end] ?? "";
24
+ if (/[\w-]/.test(character) || character.charCodeAt(0) >= 128) {
25
+ value += character;
26
+ end++;
27
+ continue;
28
+ }
29
+ if (character !== "\\") break;
30
+ const escaped = cssEscape(source, end);
31
+ value += escaped[0];
32
+ end = escaped[1];
33
+ }
34
+ return [value.toLowerCase(), end];
35
+ }
36
+ function skipTrivia(source, at) {
37
+ if (/\s/.test(source[at] ?? "")) return at + 1;
38
+ if (!source.startsWith("/*", at)) return at;
39
+ const close = source.indexOf("*/", at + 2);
40
+ return close < 0 ? source.length : close + 2;
41
+ }
42
+ function stringToken(source, start) {
43
+ const quote = source[start] ?? "";
44
+ let at = start + 1;
45
+ let value = "";
46
+ while (at < source.length && source[at] !== quote) {
47
+ if (source[at] === "\n" || source[at] === "\r" || source[at] === "\f") break;
48
+ if (source[at] === "\\") {
49
+ const escaped = cssEscape(source, at);
50
+ value += escaped[0];
51
+ at = escaped[1];
52
+ } else value += source[at++] ?? "";
53
+ }
54
+ if (source[at] === quote) at++;
55
+ return {
56
+ end: at,
57
+ kind: "string",
58
+ start,
59
+ value
60
+ };
61
+ }
62
+ function badUrlEnd(source, at) {
63
+ while (at < source.length) {
64
+ if (source[at] === ")") return at + 1;
65
+ if (source[at] === "\\") at = cssEscape(source, at)[1];
66
+ else at++;
67
+ }
68
+ return at;
69
+ }
70
+ function invalidUrlCharacter(character) {
71
+ const code = character.charCodeAt(0);
72
+ return character === "\"" || character === "'" || character === "(" || code <= 8 || code === 11 || code >= 14 && code <= 31 || code === 127;
73
+ }
74
+ function urlAfterWhitespace(source, start, at, value) {
75
+ let end = at;
76
+ while (/\s/.test(source[end] ?? "")) end++;
77
+ if (source[end] === ")") return {
78
+ end: end + 1,
79
+ kind: "url",
80
+ start,
81
+ value: value.trim().toLowerCase()
82
+ };
83
+ return {
84
+ end: badUrlEnd(source, end),
85
+ kind: "bad-url",
86
+ start,
87
+ value
88
+ };
89
+ }
90
+ function escapedUrlPart(source, start, at, value) {
91
+ const escaped = cssEscape(source, at);
92
+ if (escaped[1] === at + 1 || escaped[0] === "") return {
93
+ end: badUrlEnd(source, escaped[1]),
94
+ kind: "bad-url",
95
+ start,
96
+ value
97
+ };
98
+ return [escaped[0], escaped[1]];
99
+ }
100
+ function urlToken(source, start, content) {
101
+ let at = content;
102
+ let value = "";
103
+ while (/\s/.test(source[at] ?? "")) at++;
104
+ if (source[at] === "\"" || source[at] === "'") return {
105
+ end: content,
106
+ kind: "function",
107
+ start,
108
+ value: "url"
109
+ };
110
+ while (at < source.length) {
111
+ const character = source[at] ?? "";
112
+ if (character === ")") return {
113
+ end: at + 1,
114
+ kind: "url",
115
+ start,
116
+ value: value.trim().toLowerCase()
117
+ };
118
+ if (/\s/.test(character)) return urlAfterWhitespace(source, start, at, value);
119
+ if (invalidUrlCharacter(character)) return {
120
+ end: badUrlEnd(source, at + 1),
121
+ kind: "bad-url",
122
+ start,
123
+ value
124
+ };
125
+ if (character === "\\") {
126
+ const escaped = escapedUrlPart(source, start, at, value);
127
+ if (!Array.isArray(escaped)) return escaped;
128
+ value += escaped[0];
129
+ at = escaped[1];
130
+ } else {
131
+ value += character;
132
+ at++;
133
+ }
134
+ }
135
+ return {
136
+ end: at,
137
+ kind: "url",
138
+ start,
139
+ value: value.trim().toLowerCase()
140
+ };
141
+ }
142
+ function tokenAt(source, at) {
143
+ const character = source[at] ?? "";
144
+ if (character === "\"" || character === "'") return stringToken(source, at);
145
+ const offset = character === "@" ? 1 : 0;
146
+ const parsed = name(source, at + offset);
147
+ if (parsed[0]) {
148
+ const isFunction = offset === 0 && source[parsed[1]] === "(";
149
+ if (isFunction && parsed[0] === "url") return urlToken(source, at, parsed[1] + 1);
150
+ const kind = offset === 1 ? "at" : isFunction ? "function" : "word";
151
+ return {
152
+ end: parsed[1] + (isFunction ? 1 : 0),
153
+ kind,
154
+ start: at,
155
+ value: parsed[0]
156
+ };
157
+ }
158
+ return {
159
+ end: at + 1,
160
+ kind: {
161
+ "{": "open",
162
+ "}": "close",
163
+ ";": "semicolon",
164
+ "(": "open",
165
+ "[": "open",
166
+ ")": "close",
167
+ "]": "close"
168
+ }[character] ?? "word",
169
+ start: at,
170
+ value: character
171
+ };
172
+ }
173
+ function cssTokens(source) {
174
+ const found = [];
175
+ let at = 0;
176
+ while (at < source.length) {
177
+ const significant = skipTrivia(source, at);
178
+ if (significant !== at) {
179
+ at = significant;
180
+ continue;
181
+ }
182
+ const token = tokenAt(source, at);
183
+ found.push(token);
184
+ at = token.end;
185
+ }
186
+ return found;
187
+ }
188
+ //#endregion
189
+ export { cssEscape, cssTokens };
@@ -0,0 +1,11 @@
1
+ import { AppFont, FontFace } from "@segnavia/format";
2
+ //#region src/font-css.d.ts
3
+ export declare class FontCssRefusedError extends Error {
4
+ constructor(message: string);
5
+ }
6
+ export declare function fontFaceCss(font: FontFace): string;
7
+ export declare function fontCss(options: {
8
+ app: Map<string, AppFont>;
9
+ local?: readonly FontFace[];
10
+ }): string;
11
+ //#endregion
@@ -0,0 +1,75 @@
1
+ import { bytesToBase64 } from "./base64.js";
2
+ import { hasControlChar } from "./control-char.js";
3
+ import { UNICODE_RANGE_CLEARANCE, parseUnicodeRange } from "./unicode-range.js";
4
+ import { APP_FONT_NAME, FONT_FORMATS } from "@segnavia/format";
5
+ //#region src/font-css.ts
6
+ var FontCssRefusedError = class extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = "FontCssRefusedError";
10
+ }
11
+ };
12
+ const CSS_WEIGHT_KEYWORD = /^(?:normal|bold|bolder|lighter)$/;
13
+ const CSS_WEIGHT_NUMBER = /^(?:[1-9]\d{0,2}|1000)$/;
14
+ function mimeType(format) {
15
+ if (format === "truetype") return "ttf";
16
+ if (format === "opentype") return "otf";
17
+ return format;
18
+ }
19
+ function unicodeRangeDeclaration(font) {
20
+ const range = font.unicodeRange;
21
+ if (range === void 0) return "";
22
+ const refused = (reason) => new FontCssRefusedError(`The unicode-range ${JSON.stringify(range)} on face ${JSON.stringify(font.family)} (${font.weight ?? "normal"}, ${font.style ?? "normal"}) is malformed: ${reason}. Supply a CSS unicode-range list (${UNICODE_RANGE_CLEARANCE}) to clear this refusal.`);
23
+ if (typeof range !== "string") throw refused(`it is not a string (${typeof range})`);
24
+ const parsed = parseUnicodeRange(range);
25
+ if (!parsed.ok) throw refused(parsed.reason);
26
+ return `unicode-range:${parsed.value};`;
27
+ }
28
+ function faceRefusal(font, problem, remedy) {
29
+ return new FontCssRefusedError(`The face ${JSON.stringify(font.family)} could not establish ${problem}; ${remedy} to clear this refusal.`);
30
+ }
31
+ function isCssWeight(value) {
32
+ if (value === void 0) return true;
33
+ if (typeof value === "number") return Number.isInteger(value) && value >= 1 && value <= 1e3;
34
+ return CSS_WEIGHT_KEYWORD.test(value) || CSS_WEIGHT_NUMBER.test(value) && Number(value) >= 1 && Number(value) <= 1e3;
35
+ }
36
+ function isFontFormat(value) {
37
+ return FONT_FORMATS.some((format) => format === value);
38
+ }
39
+ function quoteFamily(family) {
40
+ if (hasControlChar(family)) throw new FontCssRefusedError(`The family ${JSON.stringify(family)} contains a control character; remove control characters from the family name to clear this refusal.`);
41
+ let quoted = "\"";
42
+ for (const ch of family) if (ch === "\\" || ch === "\"") quoted += `\\${ch}`;
43
+ else if (ch === "<") quoted += "\\3c ";
44
+ else quoted += ch;
45
+ return `${quoted}"`;
46
+ }
47
+ function validatedFace(font) {
48
+ if (!isFontFormat(font.format)) throw faceRefusal(font, `a font format that is one of ${FONT_FORMATS.join(", ")}`, `supply one of ${FONT_FORMATS.join(", ")}`);
49
+ if (!isCssWeight(font.weight)) throw faceRefusal(font, "a CSS font-weight", "supply a number 1-1000 or a CSS weight keyword");
50
+ if (font.style !== void 0 && font.style !== "normal" && font.style !== "italic") throw faceRefusal(font, "a font-style of 'normal' or 'italic'", "supply 'normal' or 'italic'");
51
+ return font;
52
+ }
53
+ function fontFaceCss(font) {
54
+ const face = validatedFace(font);
55
+ return `@font-face{font-family:${quoteFamily(face.family)};src:url(data:font/${mimeType(face.format)};base64,${bytesToBase64(face.data)}) format(${JSON.stringify(face.format)});font-weight:${face.weight ?? "normal"};font-style:${face.style ?? "normal"};${unicodeRangeDeclaration(face)}font-display:block}`;
56
+ }
57
+ function customProperty(name, font) {
58
+ if (!APP_FONT_NAME.test(name)) throw new FontCssRefusedError(`The app font name ${JSON.stringify(name)} could not establish an app font name matching ${APP_FONT_NAME}; use a lowercase name of letters, digits and hyphens to clear this refusal.`);
59
+ const generic = font.generic ? `,${font.generic}` : "";
60
+ return `--segnavia-font-${name}:${quoteFamily(font.family)}${generic}`;
61
+ }
62
+ function fontCss(options) {
63
+ const faces = [];
64
+ const properties = [];
65
+ for (const [name, font] of options.app) {
66
+ faces.push(...font.faces);
67
+ properties.push(customProperty(name, font));
68
+ }
69
+ for (const font of options.local ?? []) faces.push(font);
70
+ const rules = faces.map(fontFaceCss).join("");
71
+ if (properties.length === 0) return rules;
72
+ return `${rules}:root{${properties.join(";")}}`;
73
+ }
74
+ //#endregion
75
+ export { FontCssRefusedError, fontCss, fontFaceCss };
@@ -0,0 +1,4 @@
1
+ import { FontFormat } from "@segnavia/format";
2
+ //#region src/font-format.d.ts
3
+ export declare function fontFormat(data: Uint8Array): FontFormat | undefined;
4
+ //#endregion
@@ -0,0 +1,11 @@
1
+ //#region src/font-format.ts
2
+ function fontFormat(data) {
3
+ if (data.length < 4) return;
4
+ const tag = String.fromCharCode(data[0] ?? 0, data[1] ?? 0, data[2] ?? 0, data[3] ?? 0);
5
+ if (tag === "wOF2") return "woff2";
6
+ if (tag === "wOFF") return "woff";
7
+ if (tag === "OTTO") return "opentype";
8
+ if (tag === "true" || data[0] === 0 && data[1] === 1 && data[2] === 0 && data[3] === 0) return "truetype";
9
+ }
10
+ //#endregion
11
+ export { fontFormat };
@@ -0,0 +1,4 @@
1
+ import { FontReference } from "@segnavia/format";
2
+ //#region src/font-references.d.ts
3
+ export declare function fontReferenceRefusals(html: string, references: FontReference[], known: ReadonlySet<string>): string[];
4
+ //#endregion
@@ -0,0 +1,192 @@
1
+ import { elements, parse } from "./sanitize.js";
2
+ import { hasControlChar } from "./control-char.js";
3
+ import { APP_FONT_NAME } from "@segnavia/format";
4
+ //#region src/font-references.ts
5
+ const FONT_PROPERTY = /(?:^|[;{])\s*(font-family|font)\s*:([^;{}]+)/gi;
6
+ const FONT_PREFIX_KEYWORD = /^(?:italic|oblique|normal|small-caps|bold|bolder|lighter|(?:ultra-|extra-|semi-)?(?:condensed|expanded)|[1-9]00)\s+/i;
7
+ const FONT_SIZE = /^(?:xx-small|x-small|small|medium|large|x-large|xx-large|xxx-large|smaller|larger|\d+(?:\.\d+)?(?:px|em|rem|pt|pc|in|cm|mm|ex|ch|vw|vh|%))(?:\s*\/\s*(?:normal|\d+(?:\.\d+)?(?:px|em|rem|pt|pc|in|cm|mm|ex|ch|vw|vh|%)?))?\s*/i;
8
+ const FONT_VAR = /var\(\s*--segnavia-font-([A-Za-z0-9-]+)\s*\)/g;
9
+ function refusal(place, problem, remedy) {
10
+ return `${place} could not establish ${problem}; ${remedy} to clear this refusal.`;
11
+ }
12
+ function referencePlace(ref) {
13
+ if ("app" in ref) return `font reference {app:${JSON.stringify(ref.app)}}`;
14
+ return `font reference {local:${JSON.stringify(ref.local)}, fallback:${JSON.stringify(ref.fallback)}}`;
15
+ }
16
+ function appName(ref) {
17
+ return "app" in ref ? ref.app : void 0;
18
+ }
19
+ function localRef(ref) {
20
+ return "local" in ref ? {
21
+ local: ref.local,
22
+ fallback: ref.fallback
23
+ } : void 0;
24
+ }
25
+ function styleText(element) {
26
+ return element.children.map((child) => "data" in child ? child.data : "").join("");
27
+ }
28
+ function cssChunks(html) {
29
+ const chunks = [];
30
+ for (const element of elements(parse(html).children)) {
31
+ const style = element.attribs.style;
32
+ if (style) chunks.push(style);
33
+ if (element.name.toLowerCase() === "style") chunks.push(styleText(element));
34
+ }
35
+ return chunks;
36
+ }
37
+ function skipSpace(value, index) {
38
+ let i = index;
39
+ while (i < value.length && /\s/.test(value[i] ?? "")) i += 1;
40
+ return i;
41
+ }
42
+ function readQuoted(value, index) {
43
+ const quote = value[index] ?? "";
44
+ let i = index + 1;
45
+ let text = "";
46
+ while (i < value.length) {
47
+ const ch = value[i] ?? "";
48
+ if (ch === "\\" && i + 1 < value.length) {
49
+ text += value[i + 1] ?? "";
50
+ i += 2;
51
+ continue;
52
+ }
53
+ if (ch === quote) return {
54
+ text,
55
+ next: i + 1
56
+ };
57
+ text += ch;
58
+ i += 1;
59
+ }
60
+ return {
61
+ text,
62
+ next: i
63
+ };
64
+ }
65
+ function readVar(value, index) {
66
+ let depth = 0;
67
+ let i = index;
68
+ while (i < value.length) {
69
+ const ch = value[i] ?? "";
70
+ if (ch === "(") depth += 1;
71
+ if (ch === ")") {
72
+ depth -= 1;
73
+ if (depth === 0) return {
74
+ text: value.slice(index, i + 1).replace(/\s+/g, ""),
75
+ next: i + 1
76
+ };
77
+ }
78
+ i += 1;
79
+ }
80
+ return {
81
+ text: value.slice(index).replace(/\s+/g, ""),
82
+ next: value.length
83
+ };
84
+ }
85
+ function readIdent(value, index) {
86
+ let i = index;
87
+ while (i < value.length && value[i] !== ",") i += 1;
88
+ return {
89
+ text: value.slice(index, i).trim(),
90
+ next: i
91
+ };
92
+ }
93
+ function stripFontShorthand(value) {
94
+ let rest = value.trim();
95
+ while (FONT_PREFIX_KEYWORD.test(rest)) rest = rest.replace(FONT_PREFIX_KEYWORD, "");
96
+ if (!FONT_SIZE.test(rest)) return value.trim();
97
+ return rest.replace(FONT_SIZE, "");
98
+ }
99
+ function familyItems(value) {
100
+ const items = [];
101
+ let i = 0;
102
+ while (i < value.length) {
103
+ i = skipSpace(value, i);
104
+ if (i >= value.length) break;
105
+ const next = value.startsWith("var(", i) ? readVar(value, i) : value[i] === "\"" || value[i] === "'" ? readQuoted(value, i) : readIdent(value, i);
106
+ if (next.text) items.push(next.text);
107
+ i = next.next;
108
+ i = skipSpace(value, i);
109
+ if (value[i] === ",") i += 1;
110
+ }
111
+ return items;
112
+ }
113
+ function familyLists(css) {
114
+ const lists = [];
115
+ FONT_PROPERTY.lastIndex = 0;
116
+ for (const match of css.matchAll(FONT_PROPERTY)) {
117
+ const property = (match[1] ?? "").toLowerCase();
118
+ const value = match[2] ?? "";
119
+ const items = familyItems(property === "font" ? stripFontShorthand(value) : value);
120
+ if (items.length > 0) lists.push(items);
121
+ }
122
+ return lists;
123
+ }
124
+ function appNames(references) {
125
+ const names = /* @__PURE__ */ new Set();
126
+ for (const ref of references) {
127
+ const name = appName(ref);
128
+ if (name) names.add(name);
129
+ }
130
+ return names;
131
+ }
132
+ function referenceRefusals(references, known) {
133
+ const refusals = [];
134
+ for (const ref of references) {
135
+ const app = appName(ref);
136
+ const local = localRef(ref);
137
+ const name = app ?? local?.fallback ?? "";
138
+ if (!APP_FONT_NAME.test(name)) {
139
+ refusals.push(refusal(referencePlace(ref), `an app font name matching ${APP_FONT_NAME}`, "use a lowercase name of letters, digits and hyphens"));
140
+ continue;
141
+ }
142
+ if (!known.has(name)) refusals.push(refusal(referencePlace(ref), `app font ${JSON.stringify(name)}`, "make that font available from the source"));
143
+ if (local && hasControlChar(local.local)) refusals.push(refusal(referencePlace(ref), "a local family without control characters", "remove control characters from the family name"));
144
+ }
145
+ return refusals;
146
+ }
147
+ function fallbackVar(fallback) {
148
+ return `var(--segnavia-font-${fallback})`;
149
+ }
150
+ function localCssRefusals(lists, references) {
151
+ const refusals = [];
152
+ for (const ref of references) {
153
+ const local = localRef(ref);
154
+ if (!local) continue;
155
+ const expected = fallbackVar(local.fallback);
156
+ for (const items of lists) {
157
+ const index = items.indexOf(local.local);
158
+ if (index < 0) continue;
159
+ const next = items[index + 1];
160
+ if (next === expected) continue;
161
+ const place = `page CSS font-family ${JSON.stringify(items.join(", "))}`;
162
+ if (!next?.startsWith("var(--segnavia-font-")) refusals.push(refusal(place, `the declared fallback after ${JSON.stringify(local.local)}`, `write font-family: ${JSON.stringify(local.local)}, ${expected}`));
163
+ else refusals.push(refusal(place, `fallback ${next} matching declared ${expected}`, `write font-family: ${JSON.stringify(local.local)}, ${expected}`));
164
+ }
165
+ }
166
+ return refusals;
167
+ }
168
+ function undeclaredVarRefusals(chunks, declared) {
169
+ const refusals = [];
170
+ const seen = /* @__PURE__ */ new Set();
171
+ for (const css of chunks) {
172
+ FONT_VAR.lastIndex = 0;
173
+ for (const match of css.matchAll(FONT_VAR)) {
174
+ const name = match[1] ?? "";
175
+ if (declared.has(name) || seen.has(name)) continue;
176
+ seen.add(name);
177
+ refusals.push(refusal(`page CSS ${match[0]}`, `a fonts reference {app:${JSON.stringify(name)}}`, `add {app:${JSON.stringify(name)}} to the page or placed motif fonts`));
178
+ }
179
+ }
180
+ return refusals;
181
+ }
182
+ function fontReferenceRefusals(html, references, known) {
183
+ const chunks = cssChunks(html);
184
+ const lists = chunks.flatMap(familyLists);
185
+ return [
186
+ ...referenceRefusals(references, known),
187
+ ...localCssRefusals(lists, references),
188
+ ...undeclaredVarRefusals(chunks, appNames(references))
189
+ ];
190
+ }
191
+ //#endregion
192
+ export { fontReferenceRefusals };
package/dist/index.d.ts CHANGED
@@ -1,13 +1,26 @@
1
+ import { base64ToBytes, bytesToBase64 } from "./base64.js";
1
2
  import { REFUSED_ELEMENTS, SanitizeOptions, findActiveContent, sanitize } from "./sanitize.js";
2
- import { BINDINGS, FillOptions, Filled, bindingNames, fill, hasBindings } from "./bindings.js";
3
+ import { BINDINGS, FillOptions, Filled, bindingNames, bindingRoot, fill, hasBindings } from "./bindings.js";
3
4
  import { BookDocument, BookDocumentOptions, BookHeading, BookPrintPart, BookSectionInfo, bookPrintPart } from "./book/types.js";
4
5
  import { bookDocument } from "./book/book-document.js";
6
+ import { bookView, bookViewFromHtml } from "./book/book-view.js";
7
+ import { PaginateBlock, PaginateBookInput, PaginateBookPage, PaginateBookRefusal, PaginateBookResult, PaginateFlow, PaginateFlowParents, PaginateLine, PaginateMark, PaginateNote, PaginateNoteRef, PaginateOpens, PaginatePageMarks, PaginatePageNote, PaginatePageRole, PaginateSequence, PaginateWindow } from "./book/paginate-types.js";
8
+ import { BookFurnitureValues, embedFunction, fillBookFurniture, furnitureScript } from "./book/furniture.js";
9
+ import { PAGINATE_FUNCTIONS, paginateBook, paginateScript } from "./book/paginate-book.js";
10
+ import { PaginateTarget, PaginateTargetPage, heldPageNumber, openerPageIndex, pageIndexForTarget, windowPageIndex } from "./book/paginate-target.js";
11
+ import { bookStylesheet, sanitizeBookCss } from "./book-css.js";
12
+ import { FontCssRefusedError, fontCss, fontFaceCss } from "./font-css.js";
13
+ import { fontFormat } from "./font-format.js";
14
+ import { fontReferenceRefusals } from "./font-references.js";
15
+ import { ExpandMotifsOptions, ExpandedMotifs, MOTIF_DEPTH_LIMIT, MotifRefusedError, PlacedMotif, expandMotifs, hasMotifPlacement, libraryMotifPlacementRefusal, motifPlacementRefusals, renderMotifPlacements } from "./motifs.js";
16
+ import { BookPageFacts, BookPrintedPagePlan, BookPrintedPageRole, BookSectionParents, PrintedPagePlan, PrintedPageSide, planBookPages, planPrintedPages, printedPageAt, selectBookParent } from "./printed-pages.js";
5
17
  import { SlotFillEntry, SlotFillRefusal, SlotFillsConflictError, SlotFillsRefusedError, SlotPrintOptions, assertSingleSlotCopy, assertSlotPrintOptions, collectSlotPrintCopies, isSlotCopyRefusal, slotFillCopyId, slotFillsConflict } from "./slot-print.js";
6
18
  import { RequiredSlotsRefusedError, UnmeasuredSlotsRefusedError, mergeSlotFitResults, requiredSlotRefusal, unmeasuredSlotRefusal } from "./slot-policy.js";
7
19
  import { MAX_SLOT_DECLARATIONS, PreparedSlots, SlotFillRefusedError, hasSlotWork, prepareSlotCollection, prepareSlots, slotFillRefusal } from "./slots.js";
20
+ import { UNICODE_RANGE_CLEARANCE, UnicodeRangeParse, parseUnicodeRange } from "./unicode-range.js";
8
21
  //#region src/index.d.ts
9
22
  export type HtmlKind = 'plain' | 'live-data' | 'scripted' | 'refused';
10
23
  export declare function classify(html: string, prefix: string): HtmlKind;
11
24
  export declare function searchText(html: string): string;
12
25
  //#endregion
13
- export { BINDINGS, type BookDocument, type BookDocumentOptions, type BookHeading, type BookPrintPart, type BookSectionInfo, type FillOptions, type Filled, MAX_SLOT_DECLARATIONS, type PreparedSlots, REFUSED_ELEMENTS, RequiredSlotsRefusedError, type SanitizeOptions, type SlotFillEntry, type SlotFillRefusal, SlotFillRefusedError, SlotFillsConflictError, SlotFillsRefusedError, type SlotPrintOptions, UnmeasuredSlotsRefusedError, assertSingleSlotCopy, assertSlotPrintOptions, bindingNames, bookDocument, bookPrintPart, collectSlotPrintCopies, fill, findActiveContent, hasBindings, hasSlotWork, isSlotCopyRefusal, mergeSlotFitResults, prepareSlotCollection, prepareSlots, requiredSlotRefusal, sanitize, slotFillCopyId, slotFillRefusal, slotFillsConflict, unmeasuredSlotRefusal };
26
+ export { BINDINGS, type BookDocument, type BookDocumentOptions, type BookFurnitureValues, type BookHeading, BookPageFacts, 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, embedFunction, expandMotifs, fill, fillBookFurniture, findActiveContent, fontCss, fontFaceCss, fontFormat, fontReferenceRefusals, furnitureScript, hasBindings, hasMotifPlacement, hasSlotWork, heldPageNumber, isSlotCopyRefusal, libraryMotifPlacementRefusal, mergeSlotFitResults, motifPlacementRefusals, openerPageIndex, pageIndexForTarget, paginateBook, paginateScript, parseUnicodeRange, planBookPages, planPrintedPages, prepareSlotCollection, prepareSlots, printedPageAt, renderMotifPlacements, requiredSlotRefusal, sanitize, sanitizeBookCss, selectBookParent, slotFillCopyId, slotFillRefusal, slotFillsConflict, unmeasuredSlotRefusal, windowPageIndex };
package/dist/index.js CHANGED
@@ -1,7 +1,19 @@
1
1
  import { REFUSED_ELEMENTS, elements, findActiveContent, parse, sanitize } from "./sanitize.js";
2
- import { BINDINGS, bindingNames, fill, hasBindings } from "./bindings.js";
2
+ import { BINDINGS, bindingNames, bindingRoot, fill, hasBindings } from "./bindings.js";
3
+ import { base64ToBytes, bytesToBase64 } from "./base64.js";
3
4
  import { bookPrintPart } from "./book/types.js";
4
5
  import { bookDocument } from "./book/book-document.js";
6
+ import { bookView, bookViewFromHtml } from "./book/book-view.js";
7
+ import { embedFunction, fillBookFurniture, furnitureScript } from "./book/furniture.js";
8
+ import { PAGINATE_FUNCTIONS, paginateBook, paginateScript } from "./book/paginate-book.js";
9
+ import { heldPageNumber, openerPageIndex, pageIndexForTarget, windowPageIndex } from "./book/paginate-target.js";
10
+ import { bookStylesheet, sanitizeBookCss } from "./book-css.js";
11
+ import { UNICODE_RANGE_CLEARANCE, parseUnicodeRange } from "./unicode-range.js";
12
+ import { FontCssRefusedError, fontCss, fontFaceCss } from "./font-css.js";
13
+ import { fontFormat } from "./font-format.js";
14
+ import { fontReferenceRefusals } from "./font-references.js";
15
+ import { MOTIF_DEPTH_LIMIT, MotifRefusedError, expandMotifs, hasMotifPlacement, libraryMotifPlacementRefusal, motifPlacementRefusals, renderMotifPlacements } from "./motifs.js";
16
+ import { planBookPages, planPrintedPages, printedPageAt, selectBookParent } from "./printed-pages.js";
5
17
  import { RequiredSlotsRefusedError, UnmeasuredSlotsRefusedError, mergeSlotFitResults, requiredSlotRefusal, unmeasuredSlotRefusal } from "./slot-policy.js";
6
18
  import { MAX_SLOT_DECLARATIONS, SlotFillRefusedError, hasSlotWork, prepareSlotCollection, prepareSlots, slotFillRefusal } from "./slots.js";
7
19
  import { SlotFillsConflictError, SlotFillsRefusedError, assertSingleSlotCopy, assertSlotPrintOptions, collectSlotPrintCopies, isSlotCopyRefusal, slotFillCopyId, slotFillsConflict } from "./slot-print.js";
@@ -17,4 +29,4 @@ function searchText(html) {
17
29
  return html.replace(/<(script|style)\b[^>]*>[\s\S]*?<\/\1\s*>/gi, " ").replace(/<[^>]+>/g, " ").replace(/&nbsp;/g, " ").replace(/\s+/g, " ").trim();
18
30
  }
19
31
  //#endregion
20
- export { BINDINGS, MAX_SLOT_DECLARATIONS, REFUSED_ELEMENTS, RequiredSlotsRefusedError, SlotFillRefusedError, SlotFillsConflictError, SlotFillsRefusedError, UnmeasuredSlotsRefusedError, assertSingleSlotCopy, assertSlotPrintOptions, bindingNames, bookDocument, bookPrintPart, classify, collectSlotPrintCopies, fill, findActiveContent, hasBindings, hasSlotWork, isSlotCopyRefusal, mergeSlotFitResults, prepareSlotCollection, prepareSlots, requiredSlotRefusal, sanitize, searchText, slotFillCopyId, slotFillRefusal, slotFillsConflict, unmeasuredSlotRefusal };
32
+ export { BINDINGS, 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, embedFunction, expandMotifs, fill, fillBookFurniture, findActiveContent, fontCss, fontFaceCss, fontFormat, fontReferenceRefusals, furnitureScript, hasBindings, hasMotifPlacement, hasSlotWork, heldPageNumber, isSlotCopyRefusal, libraryMotifPlacementRefusal, mergeSlotFitResults, motifPlacementRefusals, openerPageIndex, pageIndexForTarget, paginateBook, paginateScript, parseUnicodeRange, planBookPages, planPrintedPages, prepareSlotCollection, prepareSlots, printedPageAt, renderMotifPlacements, requiredSlotRefusal, sanitize, sanitizeBookCss, searchText, selectBookParent, slotFillCopyId, slotFillRefusal, slotFillsConflict, unmeasuredSlotRefusal, windowPageIndex };
@@ -0,0 +1,32 @@
1
+ import { FontReference, MotifPlacement, MotifSource, Scripts } from "@segnavia/format";
2
+ //#region src/motifs.d.ts
3
+ export declare const MOTIF_DEPTH_LIMIT = 8;
4
+ export interface ExpandMotifsOptions {
5
+ source: MotifSource;
6
+ pins?: Record<string, string>;
7
+ lang?: string;
8
+ scripts: Scripts;
9
+ prefix: string;
10
+ linkOrigins?: Iterable<string>;
11
+ dataFonts?: boolean;
12
+ signal: AbortSignal;
13
+ deferPaths?: Iterable<string>;
14
+ }
15
+ export interface PlacedMotif extends MotifPlacement {
16
+ version: number;
17
+ }
18
+ export interface ExpandedMotifs {
19
+ html: string;
20
+ placed: PlacedMotif[];
21
+ fonts: FontReference[];
22
+ unavailable: string[];
23
+ }
24
+ export declare class MotifRefusedError extends Error {
25
+ constructor(message: string);
26
+ }
27
+ export declare function libraryMotifPlacementRefusal(html: string): string | undefined;
28
+ export declare function hasMotifPlacement(html: string): boolean;
29
+ export declare function renderMotifPlacements(html: string, replacement: (key: string) => string | undefined): string;
30
+ export declare function expandMotifs(html: string, options: ExpandMotifsOptions): Promise<ExpandedMotifs>;
31
+ export declare function motifPlacementRefusals(html: string, options: ExpandMotifsOptions): Promise<string[]>;
32
+ //#endregion