@stll/folio-core 0.7.0 → 0.9.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 (193) hide show
  1. package/README.md +39 -0
  2. package/dist/ai-edits/apply.js +18 -4
  3. package/dist/ai-edits/blockRange.d.ts +13 -2
  4. package/dist/ai-edits/blockRange.js +23 -2
  5. package/dist/ai-edits/headless.d.ts +31 -4
  6. package/dist/ai-edits/headless.js +148 -35
  7. package/dist/ai-edits/index.d.ts +6 -4
  8. package/dist/ai-edits/index.js +4 -1
  9. package/dist/ai-edits/scoped-reading.d.ts +9 -0
  10. package/dist/ai-edits/scoped-reading.js +60 -0
  11. package/dist/ai-edits/snapshot.js +53 -7
  12. package/dist/ai-edits/types.d.ts +46 -3
  13. package/dist/compat/eigenpal.d.ts +8 -3
  14. package/dist/compat/eigenpal.js +7 -2
  15. package/dist/controller/fontReadiness.d.ts +29 -0
  16. package/dist/controller/fontReadiness.js +139 -0
  17. package/dist/controller/latestRequestGate.d.ts +13 -0
  18. package/dist/controller/latestRequestGate.js +16 -0
  19. package/dist/controller/layoutPipeline.js +88 -10
  20. package/dist/document-operations.d.ts +10 -3
  21. package/dist/document-operations.js +29 -14
  22. package/dist/docx/blockContentParser.js +53 -10
  23. package/dist/docx/commentParser.js +1 -1
  24. package/dist/docx/compatibility.d.ts +30 -2
  25. package/dist/docx/compatibility.js +118 -39
  26. package/dist/docx/conformance.d.ts +6 -0
  27. package/dist/docx/conformance.js +18 -0
  28. package/dist/docx/encryption/agileDecryption.js +1 -1
  29. package/dist/docx/encryption/encryptionInfo.js +1 -1
  30. package/dist/docx/encryption/openEncryptedDocx.js +1 -1
  31. package/dist/docx/ensureParaIds.d.ts +29 -0
  32. package/dist/docx/ensureParaIds.js +423 -0
  33. package/dist/docx/fontTableParser.d.ts +6 -0
  34. package/dist/docx/fontTableParser.js +72 -0
  35. package/dist/docx/groupDrawingParser.js +7 -2
  36. package/dist/docx/headerFooterParser.js +1 -1
  37. package/dist/docx/normalizeBaseDirection.js +1 -1
  38. package/dist/docx/numberingParser.js +2 -0
  39. package/dist/docx/paragraphParser.js +21 -1
  40. package/dist/docx/paragraphTextBoxEnrichment.js +141 -2
  41. package/dist/docx/parser.d.ts +1 -1
  42. package/dist/docx/parser.js +14 -9
  43. package/dist/docx/rezip.js +70 -5
  44. package/dist/docx/runConsolidator.js +4 -0
  45. package/dist/docx/runParser.js +19 -1
  46. package/dist/docx/selectiveSave.js +3 -3
  47. package/dist/docx/selectiveXmlPatch.d.ts +10 -1
  48. package/dist/docx/selectiveXmlPatch.js +1 -1
  49. package/dist/docx/serializer/fontTableSerializer.d.ts +6 -0
  50. package/dist/docx/serializer/fontTableSerializer.js +26 -0
  51. package/dist/docx/serializer/headerFooterSerializer.js +1 -1
  52. package/dist/docx/serializer/paragraphSerializer.js +8 -0
  53. package/dist/docx/serializer/runSerializer.js +14 -2
  54. package/dist/docx/serializer/settingsSerializer.d.ts +6 -0
  55. package/dist/docx/serializer/settingsSerializer.js +16 -0
  56. package/dist/docx/serializer/stylesSerializer.d.ts +6 -0
  57. package/dist/docx/serializer/stylesSerializer.js +56 -0
  58. package/dist/docx/serializer/tableSerializer.js +29 -26
  59. package/dist/docx/serializer/themeSerializer.d.ts +6 -0
  60. package/dist/docx/serializer/themeSerializer.js +36 -0
  61. package/dist/docx/settingsParser.js +46 -0
  62. package/dist/docx/styleParser.js +26 -1
  63. package/dist/docx/tableParser.js +19 -1
  64. package/dist/docx/textBoxParser.js +6 -1
  65. package/dist/docx/vmlImageParser.js +146 -1
  66. package/dist/i18n/messages/catalogs.gen.d.ts +5916 -5916
  67. package/dist/i18n/messages/catalogs.gen.js +5910 -5910
  68. package/dist/index.d.ts +8 -3
  69. package/dist/index.js +7 -2
  70. package/dist/layout-bridge/convert/footnoteLayout.d.ts +3 -0
  71. package/dist/layout-bridge/convert/footnoteLayout.js +3 -1
  72. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +3 -0
  73. package/dist/layout-bridge/convert/headerFooterLayout.js +22 -3
  74. package/dist/layout-bridge/convert/paragraphFrames.d.ts +7 -0
  75. package/dist/layout-bridge/convert/paragraphFrames.js +136 -0
  76. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +14 -2
  77. package/dist/layout-bridge/convert/toFlowBlocks.js +93 -17
  78. package/dist/layout-bridge/engine/hitTest.js +2 -1
  79. package/dist/layout-bridge/engine/measuring/index.d.ts +2 -1
  80. package/dist/layout-bridge/engine/measuring/index.js +2 -1
  81. package/dist/layout-bridge/engine/selectionRects.js +2 -1
  82. package/dist/layout-engine/headerFooterRefs.d.ts +7 -0
  83. package/dist/layout-engine/headerFooterRefs.js +40 -0
  84. package/dist/layout-engine/index.d.ts +3 -2
  85. package/dist/layout-engine/index.js +68 -106
  86. package/dist/layout-engine/measure/cache.js +11 -3
  87. package/dist/layout-engine/measure/font-metrics.worker.js +1 -0
  88. package/dist/layout-engine/measure/index.d.ts +2 -1
  89. package/dist/layout-engine/measure/index.js +2 -1
  90. package/dist/layout-engine/measure/lineBreakProvider.d.ts +33 -0
  91. package/dist/layout-engine/measure/lineBreakProvider.js +252 -0
  92. package/dist/layout-engine/measure/lineBreaks.d.ts +7 -2
  93. package/dist/layout-engine/measure/lineBreaks.js +11 -18
  94. package/dist/layout-engine/measure/listMarkerWidth.d.ts +4 -1
  95. package/dist/layout-engine/measure/listMarkerWidth.js +33 -8
  96. package/dist/layout-engine/measure/measureBlocks.d.ts +4 -2
  97. package/dist/layout-engine/measure/measureBlocks.js +138 -86
  98. package/dist/layout-engine/measure/measureContainer.js +32 -9
  99. package/dist/layout-engine/measure/measureHelpers.js +5 -2
  100. package/dist/layout-engine/measure/measureParagraph.js +346 -74
  101. package/dist/layout-engine/measure/measureTypes.d.ts +2 -1
  102. package/dist/layout-engine/measure/measureWorker.d.ts +2 -2
  103. package/dist/layout-engine/measure/measureWorker.js +8 -19
  104. package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +3 -14
  105. package/dist/layout-engine/measure/tableCellFloating.d.ts +10 -3
  106. package/dist/layout-engine/measure/tableCellFloating.js +37 -29
  107. package/dist/layout-engine/measure/tableCellFlow.d.ts +27 -0
  108. package/dist/layout-engine/measure/tableCellFlow.js +73 -0
  109. package/dist/layout-engine/measure/tableCellGrid.d.ts +15 -0
  110. package/dist/layout-engine/measure/tableCellGrid.js +62 -0
  111. package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +18 -0
  112. package/dist/layout-engine/measure/textBoxParagraphLayout.js +32 -0
  113. package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +18 -0
  114. package/dist/layout-engine/measure/textMeasurementPolicy.js +24 -0
  115. package/dist/layout-engine/paginator.d.ts +2 -1
  116. package/dist/layout-engine/paginator.js +7 -6
  117. package/dist/layout-engine/paragraphFrame.d.ts +22 -0
  118. package/dist/layout-engine/paragraphFrame.js +17 -0
  119. package/dist/layout-engine/paragraphSequence.d.ts +7 -0
  120. package/dist/layout-engine/paragraphSequence.js +25 -0
  121. package/dist/layout-engine/paragraphSpacing.d.ts +17 -0
  122. package/dist/layout-engine/paragraphSpacing.js +30 -0
  123. package/dist/layout-engine/renderedBreakReconciliation.d.ts +63 -0
  124. package/dist/layout-engine/renderedBreakReconciliation.js +86 -0
  125. package/dist/layout-engine/tableRowBreak.d.ts +5 -2
  126. package/dist/layout-engine/tableRowBreak.js +62 -29
  127. package/dist/layout-engine/types.d.ts +54 -12
  128. package/dist/layout-engine/types.js +15 -2
  129. package/dist/layout-painter/anchoredImagePosition.d.ts +23 -0
  130. package/dist/layout-painter/anchoredImagePosition.js +95 -0
  131. package/dist/layout-painter/borderStroke.d.ts +23 -0
  132. package/dist/layout-painter/borderStroke.js +39 -0
  133. package/dist/layout-painter/renderImage.d.ts +5 -4
  134. package/dist/layout-painter/renderImage.js +18 -4
  135. package/dist/layout-painter/renderPage.d.ts +2 -27
  136. package/dist/layout-painter/renderPage.js +15 -101
  137. package/dist/layout-painter/renderParagraph.js +45 -26
  138. package/dist/layout-painter/renderTable.d.ts +2 -0
  139. package/dist/layout-painter/renderTable.js +257 -47
  140. package/dist/layout-painter/renderTextBox.js +17 -7
  141. package/dist/managers/DocumentLoaderManager.d.ts +1 -1
  142. package/dist/managers/DocumentLoaderManager.js +2 -2
  143. package/dist/paged-layout/sectionBlockWidths.d.ts +3 -0
  144. package/dist/paged-layout/sectionBlockWidths.js +9 -0
  145. package/dist/paged-layout/sectionGeometry.js +1 -1
  146. package/dist/prosemirror/attrs/index.d.ts +4 -2
  147. package/dist/prosemirror/attrs/index.js +32 -2
  148. package/dist/prosemirror/commands/propertyChangeScope.js +1 -1
  149. package/dist/prosemirror/conversion/fromProseDoc.js +35 -18
  150. package/dist/prosemirror/conversion/toProseDoc.js +73 -37
  151. package/dist/prosemirror/extensions/StarterKit.js +2 -0
  152. package/dist/prosemirror/extensions/core/ParagraphExtension.js +8 -2
  153. package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +1 -1
  154. package/dist/prosemirror/extensions/features/ListExtension.js +2 -0
  155. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +68 -16
  156. package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +1 -0
  157. package/dist/prosemirror/extensions/marks/HighlightExtension.js +1 -1
  158. package/dist/prosemirror/extensions/marks/LanguageExtension.d.ts +7 -0
  159. package/dist/prosemirror/extensions/marks/LanguageExtension.js +30 -0
  160. package/dist/prosemirror/extensions/marks/RunShadingExtension.js +1 -1
  161. package/dist/prosemirror/extensions/marks/TextColorExtension.js +1 -1
  162. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +1 -1
  163. package/dist/prosemirror/extensions/marks/markUtils.js +12 -0
  164. package/dist/prosemirror/extensions/nodes/ImageExtension.js +2 -1
  165. package/dist/prosemirror/extensions/nodes/TableExtension.js +4 -2
  166. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +2 -1
  167. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +7 -0
  168. package/dist/prosemirror/schema/index.d.ts +2 -2
  169. package/dist/prosemirror/schema/marks.d.ts +6 -1
  170. package/dist/prosemirror/schema/nodes.d.ts +16 -12
  171. package/dist/prosemirror/styles/resolvedStyleAttrs.js +2 -0
  172. package/dist/prosemirror/utils/tabCalculator.d.ts +3 -1
  173. package/dist/prosemirror/utils/tabCalculator.js +12 -10
  174. package/dist/prosemirror/validation.js +4 -1
  175. package/dist/redline.js +13 -3
  176. package/dist/server.d.ts +9 -4
  177. package/dist/server.js +7 -2
  178. package/dist/style-sets/extract.d.ts +35 -0
  179. package/dist/style-sets/extract.js +123 -0
  180. package/dist/style-sets/stellaStyle.d.ts +13 -0
  181. package/dist/style-sets/stellaStyle.js +516 -0
  182. package/dist/style-sets/types.d.ts +31 -0
  183. package/dist/style-sets/types.js +5 -0
  184. package/dist/types/index.d.ts +2 -1
  185. package/dist/utils/createDocument.d.ts +12 -2
  186. package/dist/utils/createDocument.js +55 -36
  187. package/dist/utils/fontResolver.js +6 -6
  188. package/dist/utils/formatToStyle.js +1 -1
  189. package/dist/utils/hexId.d.ts +8 -1
  190. package/dist/utils/hexId.js +11 -3
  191. package/dist/utils/paragraphFormattingMerge.d.ts +14 -3
  192. package/dist/utils/paragraphFormattingMerge.js +14 -4
  193. package/package.json +5 -1
@@ -0,0 +1,252 @@
1
+ import czechHyphenation from "hyphen/cs/index.js";
2
+ import britishEnglishHyphenation from "hyphen/en-gb/index.js";
3
+ import americanEnglishHyphenation from "hyphen/en-us/index.js";
4
+ import slovakHyphenation from "hyphen/sk/index.js";
5
+ //#region src/layout-engine/measure/lineBreakProvider.ts
6
+ /**
7
+ * Replaceable line-break policy for the layout engine.
8
+ *
9
+ * Providers return UTF-16 offsets so their output composes directly with the
10
+ * run and ProseMirror positions used by the rest of the layout pipeline. The
11
+ * default provider uses the platform's Unicode segmenter, then applies the
12
+ * OOXML line-edge restrictions that are available in the flow model.
13
+ */
14
+ const { hyphenateSync: hyphenateCzech } = czechHyphenation;
15
+ const { hyphenateSync: hyphenateBritishEnglish } = britishEnglishHyphenation;
16
+ const { hyphenateSync: hyphenateAmericanEnglish } = americanEnglishHyphenation;
17
+ const { hyphenateSync: hyphenateSlovak } = slovakHyphenation;
18
+ const SEGMENTER_CACHE_LIMIT = 16;
19
+ const DEFAULT_SEGMENTER_KEY = "";
20
+ const SOFT_HYPHEN = "­";
21
+ const MAX_HYPHENATION_WORD_LENGTH = 256;
22
+ const wordSegmenters = /* @__PURE__ */ new Map();
23
+ const graphemeSegmenters = /* @__PURE__ */ new Map();
24
+ const segmenterLocale = (locale) => {
25
+ const language = locale?.trim().replaceAll("_", "-").split("-").at(0);
26
+ return language && /^[a-z]{2,3}$/iu.test(language) ? language.toLowerCase() : void 0;
27
+ };
28
+ const DICTIONARY_BREAK_SCRIPT = /[\p{Script=Thai}\p{Script=Lao}\p{Script=Khmer}\p{Script=Myanmar}]/u;
29
+ const CJK_SCRIPT = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u;
30
+ const BREAK_AFTER_CHARACTER = /* @__PURE__ */ new Set([
31
+ "-",
32
+ "֊",
33
+ "־",
34
+ "᐀",
35
+ "᠆",
36
+ "‐",
37
+ "⸗",
38
+ "⹀",
39
+ "゠",
40
+ "﹣",
41
+ "-",
42
+ "​",
43
+ "­"
44
+ ]);
45
+ const CZECH_ONE_LETTER_PREPOSITIONS = /* @__PURE__ */ new Set([
46
+ "k",
47
+ "o",
48
+ "s",
49
+ "u",
50
+ "v",
51
+ "z"
52
+ ]);
53
+ const DEFAULT_PROHIBITED_LINE_START = /* @__PURE__ */ new Set([
54
+ "!",
55
+ "%",
56
+ ")",
57
+ ",",
58
+ ".",
59
+ ":",
60
+ ";",
61
+ "?",
62
+ "]",
63
+ "}",
64
+ "、",
65
+ "。",
66
+ "〉",
67
+ "》",
68
+ "」",
69
+ "』",
70
+ "】",
71
+ "〕",
72
+ ")",
73
+ "]",
74
+ "}",
75
+ ",",
76
+ ".",
77
+ ":",
78
+ ";",
79
+ "!",
80
+ "?",
81
+ "…",
82
+ "’",
83
+ "”"
84
+ ]);
85
+ const DEFAULT_PROHIBITED_LINE_END = /* @__PURE__ */ new Set([
86
+ "$",
87
+ "(",
88
+ "[",
89
+ "{",
90
+ "£",
91
+ "¥",
92
+ "〈",
93
+ "《",
94
+ "「",
95
+ "『",
96
+ "【",
97
+ "〔",
98
+ "(",
99
+ "[",
100
+ "{",
101
+ "‘",
102
+ "“"
103
+ ]);
104
+ const getSegmenter = (cache, granularity, locale) => {
105
+ const normalizedLocale = segmenterLocale(locale);
106
+ const key = normalizedLocale ?? DEFAULT_SEGMENTER_KEY;
107
+ const cached = cache.get(key);
108
+ if (cached) return cached;
109
+ const segmenter = new Intl.Segmenter(normalizedLocale, { granularity });
110
+ if (cache.size >= SEGMENTER_CACHE_LIMIT) {
111
+ const oldestKey = cache.keys().next().value;
112
+ if (typeof oldestKey === "string") cache.delete(oldestKey);
113
+ }
114
+ cache.set(key, segmenter);
115
+ return segmenter;
116
+ };
117
+ const firstCodePoint = (text, index) => {
118
+ if (index >= text.length) return;
119
+ return String.fromCodePoint(text.codePointAt(index) ?? 0);
120
+ };
121
+ const previousCodePoint = (text, index) => {
122
+ if (index <= 0) return;
123
+ const trailing = text.charCodeAt(index - 1);
124
+ if (trailing >= 56320 && trailing <= 57343 && index >= 2) return text.slice(index - 2, index);
125
+ return text[index - 1];
126
+ };
127
+ const isProhibitedLineStart = (character, policy) => character !== void 0 && policy?.kinsoku !== false && (policy?.noLineBreaksBefore !== void 0 ? policy.noLineBreaksBefore.includes(character) : DEFAULT_PROHIBITED_LINE_START.has(character));
128
+ const isProhibitedLineEnd = (character, policy) => character !== void 0 && policy?.kinsoku !== false && (policy?.noLineBreaksAfter !== void 0 ? policy.noLineBreaksAfter.includes(character) : DEFAULT_PROHIBITED_LINE_END.has(character));
129
+ const isCjkLineBreakParticipant = (character, policy) => character !== void 0 && (CJK_SCRIPT.test(character) || DEFAULT_PROHIBITED_LINE_START.has(character) || DEFAULT_PROHIBITED_LINE_END.has(character) || policy?.noLineBreaksBefore?.includes(character) === true || policy?.noLineBreaksAfter?.includes(character) === true);
130
+ const isLegacyEthiopicBreakCharacter = (character, policy) => {
131
+ if (!policy?.useLegacyEthiopicAmharicRules || character === void 0) return false;
132
+ const codePoint = character.codePointAt(0);
133
+ return codePoint !== void 0 && codePoint >= 4961 && codePoint <= 4968;
134
+ };
135
+ const czechProtectedBreaks = (text, policy) => {
136
+ const breaks = /* @__PURE__ */ new Set();
137
+ if (segmenterLocale(policy?.locale) !== "cs") return breaks;
138
+ let tokenStart = 0;
139
+ let index = 0;
140
+ while (index < text.length) {
141
+ const character = firstCodePoint(text, index);
142
+ if (character === void 0) break;
143
+ if (!/\s/u.test(character)) {
144
+ index += character.length;
145
+ continue;
146
+ }
147
+ const token = text.slice(tokenStart, index);
148
+ const protectsNextWord = token.length === 1 && CZECH_ONE_LETTER_PREPOSITIONS.has(token.toLocaleLowerCase("cs"));
149
+ while (index < text.length) {
150
+ const whitespace = firstCodePoint(text, index);
151
+ if (whitespace === void 0 || !/\s/u.test(whitespace)) break;
152
+ index += whitespace.length;
153
+ if (protectsNextWord) breaks.add(index);
154
+ }
155
+ tokenStart = index;
156
+ }
157
+ return breaks;
158
+ };
159
+ const allowsBreak = (text, index, policy) => {
160
+ const previous = previousCodePoint(text, index);
161
+ const next = firstCodePoint(text, index);
162
+ return !isProhibitedLineEnd(previous, policy) && !isProhibitedLineStart(next, policy);
163
+ };
164
+ const pushBreak = (breaks, text, index, policy) => {
165
+ if (index <= 0 || index > text.length || breaks.at(-1) === index) return;
166
+ if (allowsBreak(text, index, policy)) breaks.push(index);
167
+ };
168
+ const findUnicodeGraphemeBreaks = (text, policy) => {
169
+ const breaks = [];
170
+ for (const segment of getSegmenter(graphemeSegmenters, "grapheme", policy?.locale).segment(text)) {
171
+ const end = segment.index + segment.segment.length;
172
+ breaks.push(end);
173
+ }
174
+ return breaks;
175
+ };
176
+ const findUnicodeBreaks = (text, policy) => {
177
+ if (text.length === 0) return [];
178
+ const breaks = [];
179
+ const graphemeBreaks = findUnicodeGraphemeBreaks(text, policy);
180
+ for (const index of graphemeBreaks) {
181
+ const previous = previousCodePoint(text, index);
182
+ if (previous !== void 0 && (/\s/u.test(previous) || BREAK_AFTER_CHARACTER.has(previous) || isLegacyEthiopicBreakCharacter(previous, policy))) pushBreak(breaks, text, index, policy);
183
+ }
184
+ const wordSegments = [...getSegmenter(wordSegmenters, "word", policy?.locale).segment(text)];
185
+ for (const [segmentIndex, segment] of wordSegments.entries()) {
186
+ const end = segment.index + segment.segment.length;
187
+ const nextSegment = wordSegments[segmentIndex + 1];
188
+ if (segment.isWordLike && nextSegment?.isWordLike === true && DICTIONARY_BREAK_SCRIPT.test(segment.segment + nextSegment.segment)) pushBreak(breaks, text, end, policy);
189
+ }
190
+ if (CJK_SCRIPT.test(text)) for (const index of graphemeBreaks) {
191
+ const previous = previousCodePoint(text, index);
192
+ if (isCjkLineBreakParticipant(previous, policy)) pushBreak(breaks, text, index, policy);
193
+ }
194
+ const protectedBreaks = czechProtectedBreaks(text, policy);
195
+ return [...new Set(breaks)].filter((index) => !protectedBreaks.has(index)).sort((left, right) => left - right);
196
+ };
197
+ const hyphenatorFor = (locale) => {
198
+ const normalized = locale?.trim().replaceAll("_", "-").toLowerCase();
199
+ if (!normalized) return;
200
+ if (normalized === "en-gb" || normalized.startsWith("en-gb-")) return hyphenateBritishEnglish;
201
+ if (normalized === "en-us" || normalized.startsWith("en-us-")) return hyphenateAmericanEnglish;
202
+ if (normalized === "cs" || normalized.startsWith("cs-")) return hyphenateCzech;
203
+ if (normalized === "sk" || normalized.startsWith("sk-")) return hyphenateSlovak;
204
+ };
205
+ const isAllCapsWord = (text, locale) => {
206
+ const letters = [...text].filter((character) => /\p{Letter}/u.test(character)).join("");
207
+ if (letters.length === 0) return false;
208
+ const casingLocale = segmenterLocale(locale);
209
+ return letters === letters.toLocaleUpperCase(casingLocale) && letters !== letters.toLocaleLowerCase(casingLocale);
210
+ };
211
+ const findPatternHyphenationBreaks = (text, policy) => {
212
+ if (text.length > 256 || text.includes(SOFT_HYPHEN)) return [];
213
+ const hyphenate = hyphenatorFor(policy?.locale);
214
+ if (!hyphenate) return [];
215
+ if (policy?.doNotHyphenateCaps && (policy.renderedAllCaps === true || isAllCapsWord(text, policy.locale))) return [];
216
+ const hyphenated = hyphenate(text);
217
+ const breaks = [];
218
+ let sourceOffset = 0;
219
+ for (const character of hyphenated) {
220
+ if (character === SOFT_HYPHEN) {
221
+ breaks.push(sourceOffset);
222
+ continue;
223
+ }
224
+ sourceOffset += character.length;
225
+ }
226
+ return sourceOffset === text.length ? breaks : [];
227
+ };
228
+ const isDefaultHangingPunctuation = (text, policy) => {
229
+ const characters = [...text];
230
+ return characters.length > 0 && characters.every((character) => isProhibitedLineStart(character, policy));
231
+ };
232
+ const defaultLineBreakProvider = {
233
+ findBreaks: findUnicodeBreaks,
234
+ findGraphemeBreaks: findUnicodeGraphemeBreaks,
235
+ findHyphenationBreaks: findPatternHyphenationBreaks,
236
+ isHangingPunctuation: isDefaultHangingPunctuation
237
+ };
238
+ let activeLineBreakProvider = defaultLineBreakProvider;
239
+ let lineBreakProviderGeneration = 0;
240
+ const getLineBreakProvider = () => activeLineBreakProvider;
241
+ /** Changes whenever the active provider changes, so layout caches cannot go stale. */
242
+ const getLineBreakProviderGeneration = () => lineBreakProviderGeneration;
243
+ const setLineBreakProvider = (provider) => {
244
+ activeLineBreakProvider = provider;
245
+ lineBreakProviderGeneration += 1;
246
+ };
247
+ const resetLineBreakProvider = () => {
248
+ activeLineBreakProvider = defaultLineBreakProvider;
249
+ lineBreakProviderGeneration += 1;
250
+ };
251
+ //#endregion
252
+ export { MAX_HYPHENATION_WORD_LENGTH, defaultLineBreakProvider, getLineBreakProvider, getLineBreakProviderGeneration, resetLineBreakProvider, setLineBreakProvider };
@@ -1,10 +1,15 @@
1
+ import { LineBreakPolicy } from "./lineBreakProvider.js";
2
+
1
3
  //#region src/layout-engine/measure/lineBreaks.d.ts
2
4
  /**
3
5
  * Indices in `text` where a line may end. Latin text breaks after
4
6
  * whitespace or hyphen; CJK text can break after every ideograph so a
5
7
  * partial line is filled instead of pushing the whole run to the next line.
6
8
  */
7
- declare function findWordBreaks(text: string): number[];
9
+ declare const findWordBreaks: (text: string, policy?: LineBreakPolicy) => number[];
10
+ declare const findGraphemeBreaks: (text: string, policy?: LineBreakPolicy) => number[];
11
+ declare const findHyphenationBreaks: (text: string, policy?: LineBreakPolicy) => number[];
12
+ declare const isHangingPunctuation: (text: string, policy?: LineBreakPolicy) => boolean;
8
13
  declare function isBreakChar(char: string | undefined): boolean;
9
14
  //#endregion
10
- export { findWordBreaks, isBreakChar };
15
+ export { findGraphemeBreaks, findHyphenationBreaks, findWordBreaks, isBreakChar, isHangingPunctuation };
@@ -1,3 +1,4 @@
1
+ import { defaultLineBreakProvider, getLineBreakProvider } from "./lineBreakProvider.js";
1
2
  import { isCjkCodePoint } from "../../utils/scriptSegments.js";
2
3
  //#region src/layout-engine/measure/lineBreaks.ts
3
4
  /**
@@ -5,26 +6,18 @@ import { isCjkCodePoint } from "../../utils/scriptSegments.js";
5
6
  * whitespace or hyphen; CJK text can break after every ideograph so a
6
7
  * partial line is filled instead of pushing the whole run to the next line.
7
8
  */
8
- function findWordBreaks(text) {
9
- const breaks = [];
10
- for (let index = 0; index < text.length;) {
11
- const codePoint = text.codePointAt(index);
12
- if (codePoint === void 0) break;
13
- const charLength = codePoint > 65535 ? 2 : 1;
14
- const char = text[index];
15
- if (char === " " || char === "-" || char === " ") breaks.push(index + charLength);
16
- else if (isCjkCodePoint(codePoint)) breaks.push(index + charLength);
17
- index += charLength;
18
- }
19
- return breaks;
20
- }
9
+ const findWordBreaks = (text, policy) => getLineBreakProvider().findBreaks(text, policy);
10
+ const findGraphemeBreaks = (text, policy) => getLineBreakProvider().findGraphemeBreaks(text, policy);
11
+ const findHyphenationBreaks = (text, policy) => getLineBreakProvider().findHyphenationBreaks?.(text, policy) ?? [];
12
+ const isHangingPunctuation = (text, policy) => {
13
+ return (getLineBreakProvider().isHangingPunctuation ?? defaultLineBreakProvider.isHangingPunctuation)?.(text, policy) ?? false;
14
+ };
21
15
  function isBreakChar(char) {
22
16
  if (char === void 0) return false;
23
- if (char === " " || char === "-" || char === " ") return true;
24
- const codePoint = char.codePointAt(0);
25
- if (codePoint === void 0) return false;
17
+ if (/\s/u.test(char) || char === "-" || char === "­" || char === "") return true;
26
18
  if (char >= "\udc00" && char <= "\udfff") return true;
27
- return isCjkCodePoint(codePoint);
19
+ const codePoint = char.codePointAt(0);
20
+ return codePoint !== void 0 && isCjkCodePoint(codePoint);
28
21
  }
29
22
  //#endregion
30
- export { findWordBreaks, isBreakChar };
23
+ export { findGraphemeBreaks, findHyphenationBreaks, findWordBreaks, isBreakChar, isHangingPunctuation };
@@ -16,6 +16,7 @@ declare const DEFAULT_TAB_STOP_TWIPS = 720;
16
16
  declare function resolveListMarkerFont(block: ParagraphBlock): {
17
17
  fontFamily: string;
18
18
  fontSize: number;
19
+ bold?: boolean;
19
20
  };
20
21
  /**
21
22
  * Compute the marker's inline-block width in pixels, or 0 if the paragraph
@@ -36,5 +37,7 @@ declare function resolveListMarkerFont(block: ParagraphBlock): {
36
37
  * grid is not erased by custom tabs, just augmented).
37
38
  */
38
39
  declare function getListMarkerInlineWidth(block: ParagraphBlock): number;
40
+ /** Paint-only offset that places the marker around its authored list anchor. */
41
+ declare function getListMarkerVisualOffset(block: ParagraphBlock): number;
39
42
  //#endregion
40
- export { DEFAULT_TAB_STOP_TWIPS, getListMarkerInlineWidth, resolveListMarkerFont };
43
+ export { DEFAULT_TAB_STOP_TWIPS, getListMarkerInlineWidth, getListMarkerVisualOffset, resolveListMarkerFont };
@@ -19,9 +19,13 @@ const TWIPS_TO_PX = 96 / 1440;
19
19
  function resolveListMarkerFont(block) {
20
20
  const attrs = block.attrs;
21
21
  const firstTextRun = block.runs.find((r) => r.kind === "text");
22
+ const fontFamily = attrs?.listMarkerFontFamily ?? firstTextRun?.fontFamily ?? attrs?.defaultFontFamily ?? DEFAULT_FONT_FAMILY;
23
+ const fontSize = attrs?.listMarkerFontSize ?? firstTextRun?.fontSize ?? attrs?.defaultFontSize ?? DEFAULT_FONT_SIZE;
24
+ const bold = attrs?.listMarkerBold ?? firstTextRun?.bold;
22
25
  return {
23
- fontFamily: attrs?.listMarkerFontFamily ?? firstTextRun?.fontFamily ?? attrs?.defaultFontFamily ?? DEFAULT_FONT_FAMILY,
24
- fontSize: attrs?.listMarkerFontSize ?? firstTextRun?.fontSize ?? attrs?.defaultFontSize ?? DEFAULT_FONT_SIZE
26
+ fontFamily,
27
+ fontSize,
28
+ ...bold !== void 0 ? { bold } : {}
25
29
  };
26
30
  }
27
31
  /**
@@ -45,21 +49,23 @@ function resolveListMarkerFont(block) {
45
49
  function getListMarkerInlineWidth(block) {
46
50
  const attrs = block.attrs;
47
51
  if (!attrs?.listMarker || attrs.listMarkerHidden) return 0;
48
- const { fontFamily, fontSize } = resolveListMarkerFont(block);
52
+ const { fontFamily, fontSize, bold } = resolveListMarkerFont(block);
49
53
  const style = {
50
54
  fontFamily,
51
- fontSize
55
+ fontSize,
56
+ ...bold !== void 0 ? { bold } : {}
52
57
  };
53
58
  const naturalWidth = measureTextWidth(attrs.listMarker, style);
59
+ const markerEndOffset = getMarkerEndOffset(naturalWidth, attrs.listMarkerAlignment);
54
60
  const suffix = attrs.listMarkerSuffix ?? "tab";
55
- if (suffix === "nothing") return naturalWidth;
56
- if (suffix === "space") return naturalWidth + measureTextWidth(" ", style);
61
+ if (suffix === "nothing") return markerEndOffset;
62
+ if (suffix === "space") return markerEndOffset + measureTextWidth(" ", style);
57
63
  const indent = attrs.indent;
58
64
  const indentLeft = indent?.left ?? 0;
59
65
  const firstLine = indent?.firstLine ?? 0;
60
66
  const hanging = indent?.hanging ?? 0;
61
67
  const markerStartPx = hanging > 0 ? indentLeft - hanging : indentLeft + firstLine;
62
- const minBodyStart = markerStartPx + naturalWidth;
68
+ const minBodyStart = markerStartPx + markerEndOffset;
63
69
  const customTabs = (attrs.tabs ?? []).filter((t) => t.val !== "clear" && t.val !== "bar").map((t) => t.pos * TWIPS_TO_PX);
64
70
  if (hanging > 0) customTabs.push(indentLeft);
65
71
  const searchStart = hanging > 0 ? Math.max(minBodyStart, indentLeft) : minBodyStart;
@@ -72,5 +78,24 @@ function getListMarkerInlineWidth(block) {
72
78
  if (bodyStart === void 0) return naturalWidth + ptToPx(fontSize) * .5;
73
79
  return bodyStart - markerStartPx;
74
80
  }
81
+ /** Paint-only offset that places the marker around its authored list anchor. */
82
+ function getListMarkerVisualOffset(block) {
83
+ const attrs = block.attrs;
84
+ if (!attrs?.listMarker || attrs.listMarkerHidden) return 0;
85
+ const { fontFamily, fontSize, bold } = resolveListMarkerFont(block);
86
+ const naturalWidth = measureTextWidth(attrs.listMarker, {
87
+ fontFamily,
88
+ fontSize,
89
+ ...bold !== void 0 ? { bold } : {}
90
+ });
91
+ if (attrs.listMarkerAlignment === "right") return -naturalWidth;
92
+ if (attrs.listMarkerAlignment === "center") return -naturalWidth / 2;
93
+ return 0;
94
+ }
95
+ const getMarkerEndOffset = (naturalWidth, alignment) => {
96
+ if (alignment === "right") return 0;
97
+ if (alignment === "center") return naturalWidth / 2;
98
+ return naturalWidth;
99
+ };
75
100
  //#endregion
76
- export { DEFAULT_TAB_STOP_TWIPS, getListMarkerInlineWidth, resolveListMarkerFont };
101
+ export { DEFAULT_TAB_STOP_TWIPS, getListMarkerInlineWidth, getListMarkerVisualOffset, resolveListMarkerFont };
@@ -1,10 +1,12 @@
1
1
  import { FlowBlock, Measure, TableBlock, TableMeasure, TextBoxBlock, TextBoxMeasure } from "../types.js";
2
2
  import { FloatingImageZone } from "./floatingZones.js";
3
3
  //#region src/layout-engine/measure/measureBlocks.d.ts
4
- declare function measureTableCellBlockVisualHeight(block: FlowBlock, blockMeasure: Measure): number;
5
4
  declare function measureTableBlock(tableBlock: TableBlock, contentWidth: number, fieldValues?: ReadonlyMap<number, string>): TableMeasure;
6
5
  type BandPageGeometry = {
6
+ pageWidth?: number | number[];
7
7
  pageHeight: number | number[];
8
+ marginLeft?: number | number[];
9
+ marginRight?: number | number[];
8
10
  marginBottom: number | number[];
9
11
  };
10
12
  /**
@@ -22,4 +24,4 @@ declare function measureTextBoxBlock(tb: TextBoxBlock, fieldValues?: ReadonlyMap
22
24
  declare function measureBlocks(blocks: FlowBlock[], contentWidth: number | number[], marginTop?: number | number[], pageGeometry?: BandPageGeometry, fieldValues?: ReadonlyMap<number, string>): Measure[];
23
25
  declare function measureSingleBlockWithoutFloatingZones(block: FlowBlock, blockWidth: number, blockIndex: number): Measure;
24
26
  //#endregion
25
- export { measureBlock, measureBlocks, measureSingleBlockWithoutFloatingZones, measureTableBlock, measureTableCellBlockVisualHeight, measureTextBoxBlock };
27
+ export { measureBlock, measureBlocks, measureSingleBlockWithoutFloatingZones, measureTableBlock, measureTextBoxBlock };