@stll/folio-core 0.8.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.
- package/README.md +39 -0
- package/dist/ai-edits/apply.js +18 -4
- package/dist/ai-edits/headless.d.ts +31 -4
- package/dist/ai-edits/headless.js +148 -35
- package/dist/ai-edits/index.d.ts +3 -3
- package/dist/ai-edits/index.js +2 -1
- package/dist/ai-edits/snapshot.js +40 -3
- package/dist/ai-edits/types.d.ts +2 -1
- package/dist/compat/eigenpal.d.ts +6 -4
- package/dist/compat/eigenpal.js +5 -3
- package/dist/controller/latestRequestGate.d.ts +13 -0
- package/dist/controller/latestRequestGate.js +16 -0
- package/dist/controller/layoutPipeline.js +12 -1
- package/dist/document-operations.d.ts +10 -3
- package/dist/document-operations.js +29 -14
- package/dist/docx/blockContentParser.js +2 -2
- package/dist/docx/commentParser.js +1 -1
- package/dist/docx/compatibility.d.ts +3 -7
- package/dist/docx/compatibility.js +0 -11
- package/dist/docx/encryption/agileDecryption.js +1 -1
- package/dist/docx/encryption/encryptionInfo.js +1 -1
- package/dist/docx/encryption/openEncryptedDocx.js +1 -1
- package/dist/docx/fontTableParser.d.ts +6 -0
- package/dist/docx/fontTableParser.js +72 -0
- package/dist/docx/groupDrawingParser.js +7 -2
- package/dist/docx/headerFooterParser.js +1 -1
- package/dist/docx/normalizeBaseDirection.js +1 -1
- package/dist/docx/numberingParser.js +1 -0
- package/dist/docx/paragraphParser.js +12 -1
- package/dist/docx/parser.d.ts +1 -1
- package/dist/docx/parser.js +12 -9
- package/dist/docx/rezip.js +70 -5
- package/dist/docx/runConsolidator.js +4 -0
- package/dist/docx/runParser.js +19 -1
- package/dist/docx/selectiveSave.js +3 -3
- package/dist/docx/serializer/fontTableSerializer.d.ts +6 -0
- package/dist/docx/serializer/fontTableSerializer.js +26 -0
- package/dist/docx/serializer/headerFooterSerializer.js +1 -1
- package/dist/docx/serializer/paragraphSerializer.js +4 -0
- package/dist/docx/serializer/runSerializer.js +7 -0
- package/dist/docx/serializer/settingsSerializer.d.ts +6 -0
- package/dist/docx/serializer/settingsSerializer.js +16 -0
- package/dist/docx/serializer/stylesSerializer.d.ts +6 -0
- package/dist/docx/serializer/stylesSerializer.js +56 -0
- package/dist/docx/serializer/tableSerializer.js +25 -26
- package/dist/docx/serializer/themeSerializer.d.ts +6 -0
- package/dist/docx/serializer/themeSerializer.js +36 -0
- package/dist/docx/settingsParser.js +46 -0
- package/dist/docx/styleParser.js +26 -1
- package/dist/docx/tableParser.js +11 -1
- package/dist/docx/vmlImageParser.js +1 -0
- package/dist/i18n/messages/catalogs.gen.d.ts +5916 -5916
- package/dist/i18n/messages/catalogs.gen.js +5910 -5910
- package/dist/index.d.ts +6 -4
- package/dist/index.js +5 -3
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +3 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +3 -1
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +3 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +6 -1
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +14 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +51 -14
- package/dist/layout-bridge/engine/measuring/index.d.ts +2 -1
- package/dist/layout-bridge/engine/measuring/index.js +2 -1
- package/dist/layout-engine/index.js +36 -28
- package/dist/layout-engine/measure/cache.js +11 -3
- package/dist/layout-engine/measure/font-metrics.worker.js +1 -0
- package/dist/layout-engine/measure/index.d.ts +2 -1
- package/dist/layout-engine/measure/index.js +2 -1
- package/dist/layout-engine/measure/lineBreakProvider.d.ts +33 -0
- package/dist/layout-engine/measure/lineBreakProvider.js +252 -0
- package/dist/layout-engine/measure/lineBreaks.d.ts +7 -2
- package/dist/layout-engine/measure/lineBreaks.js +11 -18
- package/dist/layout-engine/measure/listMarkerWidth.d.ts +1 -0
- package/dist/layout-engine/measure/listMarkerWidth.js +12 -6
- package/dist/layout-engine/measure/measureBlocks.d.ts +1 -2
- package/dist/layout-engine/measure/measureBlocks.js +6 -36
- package/dist/layout-engine/measure/measureContainer.js +32 -9
- package/dist/layout-engine/measure/measureHelpers.js +5 -2
- package/dist/layout-engine/measure/measureParagraph.js +335 -72
- package/dist/layout-engine/measure/measureTypes.d.ts +2 -1
- package/dist/layout-engine/measure/measureWorker.d.ts +2 -2
- package/dist/layout-engine/measure/measureWorker.js +8 -19
- package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +3 -14
- package/dist/layout-engine/measure/tableCellFloating.d.ts +10 -3
- package/dist/layout-engine/measure/tableCellFloating.js +37 -29
- package/dist/layout-engine/measure/tableCellFlow.d.ts +27 -0
- package/dist/layout-engine/measure/tableCellFlow.js +73 -0
- package/dist/layout-engine/measure/tableCellGrid.js +1 -1
- package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +18 -0
- package/dist/layout-engine/measure/textMeasurementPolicy.js +24 -0
- package/dist/layout-engine/renderedBreakReconciliation.d.ts +6 -1
- package/dist/layout-engine/renderedBreakReconciliation.js +34 -11
- package/dist/layout-engine/tableRowBreak.d.ts +5 -2
- package/dist/layout-engine/tableRowBreak.js +62 -29
- package/dist/layout-engine/types.d.ts +40 -7
- package/dist/layout-painter/anchoredImagePosition.d.ts +23 -0
- package/dist/layout-painter/anchoredImagePosition.js +95 -0
- package/dist/layout-painter/borderStroke.d.ts +23 -0
- package/dist/layout-painter/borderStroke.js +39 -0
- package/dist/layout-painter/renderImage.d.ts +5 -4
- package/dist/layout-painter/renderImage.js +18 -4
- package/dist/layout-painter/renderPage.d.ts +2 -27
- package/dist/layout-painter/renderPage.js +13 -99
- package/dist/layout-painter/renderParagraph.js +41 -23
- package/dist/layout-painter/renderTable.d.ts +2 -0
- package/dist/layout-painter/renderTable.js +223 -43
- package/dist/managers/DocumentLoaderManager.d.ts +1 -1
- package/dist/managers/DocumentLoaderManager.js +2 -2
- package/dist/prosemirror/attrs/index.d.ts +4 -2
- package/dist/prosemirror/attrs/index.js +21 -2
- package/dist/prosemirror/commands/propertyChangeScope.js +1 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +33 -18
- package/dist/prosemirror/conversion/toProseDoc.js +50 -18
- package/dist/prosemirror/extensions/StarterKit.js +2 -0
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +7 -2
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +1 -1
- package/dist/prosemirror/extensions/features/ListExtension.js +1 -0
- package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +1 -0
- package/dist/prosemirror/extensions/marks/HighlightExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/LanguageExtension.d.ts +7 -0
- package/dist/prosemirror/extensions/marks/LanguageExtension.js +30 -0
- package/dist/prosemirror/extensions/marks/RunShadingExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/TextColorExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +1 -1
- package/dist/prosemirror/extensions/marks/markUtils.js +12 -0
- package/dist/prosemirror/extensions/nodes/ImageExtension.js +2 -1
- package/dist/prosemirror/extensions/nodes/TableExtension.js +4 -2
- package/dist/prosemirror/schema/index.d.ts +2 -2
- package/dist/prosemirror/schema/marks.d.ts +6 -1
- package/dist/prosemirror/schema/nodes.d.ts +13 -11
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
- package/dist/prosemirror/utils/tabCalculator.d.ts +3 -1
- package/dist/prosemirror/utils/tabCalculator.js +12 -10
- package/dist/prosemirror/validation.js +4 -1
- package/dist/redline.js +13 -3
- package/dist/server.d.ts +6 -3
- package/dist/server.js +5 -2
- package/dist/style-sets/extract.d.ts +35 -0
- package/dist/style-sets/extract.js +123 -0
- package/dist/style-sets/stellaStyle.d.ts +13 -0
- package/dist/style-sets/stellaStyle.js +516 -0
- package/dist/style-sets/types.d.ts +31 -0
- package/dist/style-sets/types.js +5 -0
- package/dist/utils/createDocument.d.ts +12 -2
- package/dist/utils/createDocument.js +42 -31
- package/dist/utils/fontResolver.js +6 -6
- package/dist/utils/formatToStyle.js +1 -1
- package/dist/utils/paragraphFormattingMerge.d.ts +14 -3
- package/dist/utils/paragraphFormattingMerge.js +14 -4
- package/package.json +5 -1
- package/dist/docx/capabilities.d.ts +0 -41
- package/dist/docx/capabilities.js +0 -322
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getLineBreakProviderGeneration } from "./lineBreakProvider.js";
|
|
1
2
|
//#region src/layout-engine/measure/cache.ts
|
|
2
3
|
/**
|
|
3
4
|
* Current max size for text width cache
|
|
@@ -152,10 +153,10 @@ const paragraphMeasureCache = /* @__PURE__ */ new Map();
|
|
|
152
153
|
* Used as cache key to identify identical content
|
|
153
154
|
*/
|
|
154
155
|
function hashParagraphBlock(block) {
|
|
155
|
-
const parts = [];
|
|
156
|
-
for (const run of block.runs) if (run.kind === "text") parts.push(`t:${run.text}|${run.fontFamily}|${run.eastAsiaFontFamily}|${run.fontSize}|${run.bold}|${run.italic}|${run.allCaps}|${run.smallCaps}|${run.horizontalScale}|${run.letterSpacing}`);
|
|
156
|
+
const parts = [`lbp:${getLineBreakProviderGeneration()}`];
|
|
157
|
+
for (const run of block.runs) if (run.kind === "text") parts.push(`t:${run.text}|${run.fontFamily}|${run.eastAsiaFontFamily}|${run.fontSize}|${run.bold}|${run.italic}|${run.allCaps}|${run.smallCaps}|${run.horizontalScale}|${run.letterSpacing}|${run.language?.val}|${run.language?.eastAsia}|${run.language?.bidi}`);
|
|
157
158
|
else if (run.kind === "tab") parts.push(`tab:${run.width}`);
|
|
158
|
-
else if (run.kind === "image") parts.push(`img:${run.width}x${run.height}`);
|
|
159
|
+
else if (run.kind === "image") parts.push(`img:${run.width}x${run.height}:${run.exactLineHeight === true ? "exact" : "text"}`);
|
|
159
160
|
else if (run.kind === "lineBreak") parts.push("br");
|
|
160
161
|
const attrs = block.attrs;
|
|
161
162
|
if (attrs) {
|
|
@@ -167,6 +168,13 @@ function hashParagraphBlock(block) {
|
|
|
167
168
|
if (attrs.defaultFontFamily != null) parts.push(`dff:${attrs.defaultFontFamily}`);
|
|
168
169
|
if (attrs.suppressEmptyParagraphHeight) parts.push("sup");
|
|
169
170
|
if (attrs.reserveEmptyOutlineHeight) parts.push("outline-empty-reserve");
|
|
171
|
+
if (attrs.kinsoku !== void 0) parts.push(`kinsoku:${attrs.kinsoku}`);
|
|
172
|
+
if (attrs.overflowPunctuation !== void 0) parts.push(`overflow-punct:${attrs.overflowPunctuation}`);
|
|
173
|
+
if (attrs.suppressAutoHyphens !== void 0) parts.push(`suppress-auto-hyphens:${attrs.suppressAutoHyphens}`);
|
|
174
|
+
const automaticHyphenation = attrs.automaticHyphenation;
|
|
175
|
+
if (automaticHyphenation) parts.push(`auto-hyphens:${automaticHyphenation.doNotHyphenateCaps}|${automaticHyphenation.consecutiveLineLimit}`);
|
|
176
|
+
const lineBreakRules = attrs.lineBreakRules;
|
|
177
|
+
if (lineBreakRules) parts.push(`line-break-rules:${lineBreakRules.noLineBreaksBefore?.language}|${lineBreakRules.noLineBreaksBefore?.characters}|${lineBreakRules.noLineBreaksAfter?.language}|${lineBreakRules.noLineBreaksAfter?.characters}|${lineBreakRules.useLegacyEthiopicAmharicRules}`);
|
|
170
178
|
const borders = attrs.borders;
|
|
171
179
|
if (borders) {
|
|
172
180
|
const signature = (border) => border ? `${border.width ?? ""},${border.style ?? ""},${border.color ?? ""}` : "";
|
|
@@ -44,6 +44,7 @@ function isFontFingerprintMatch(context, expectedWidth) {
|
|
|
44
44
|
function measureEntry(entry) {
|
|
45
45
|
const context = getCtx();
|
|
46
46
|
context.font = entry.font;
|
|
47
|
+
context.fontKerning = entry.fontKerning;
|
|
47
48
|
if (!isFontFingerprintMatch(context, entry.fontFingerprintWidth)) return null;
|
|
48
49
|
let width = context.measureText(entry.text).width;
|
|
49
50
|
const codePoints = countCodePoints(entry.text);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { FontMetrics, FontStyle, RunMeasurement, TextMeasurement } from "./measureTypes.js";
|
|
2
2
|
import { MeasureProvider, getFontMetrics, getMeasureProvider, measureRun, measureText, measureTextWidth, resetMeasureProvider, setMeasureProvider } from "./measureProvider.js";
|
|
3
3
|
import { buildFontString, findCharacterAtX, getXForCharacter, halfPtToPx, ptToPx, pxToHalfPt, pxToPt, pxToTwips, twipsToPx } from "./measureHelpers.js";
|
|
4
|
+
import { LineBreakPolicy, LineBreakProvider, defaultLineBreakProvider, getLineBreakProvider, getLineBreakProviderGeneration, resetLineBreakProvider, setLineBreakProvider } from "./lineBreakProvider.js";
|
|
4
5
|
import { FloatingExclusionRect, FloatingImageZone, FloatingLineMargins, FloatingLineSegmentZone, WrapTextDirection, getFloatingAvailableWidth, getFloatingMargins, rectsToFloatingZones } from "./floatingZones.js";
|
|
5
6
|
import { clampFloatingWrapMargins } from "./clampFloatingWrapMargins.js";
|
|
6
7
|
import { MeasureParagraphOptions, getRunCharWidths, measureParagraph, measureParagraphs } from "./measureParagraph.js";
|
|
7
8
|
import { clearAllCaches, clearFontMetricsCache, clearParagraphMeasureCache, clearTextWidthCache, getCachedFontMetrics, getCachedParagraphMeasure, getCachedTextWidth, getFontCacheSize, getParagraphCacheSize, getTextCacheSize, getTotalCacheSize, hashParagraphBlock, setCachedFontMetrics, setCachedParagraphMeasure, setCachedTextWidth, setFontCacheSize, setParagraphCacheSize, setTextCacheSize } from "./cache.js";
|
|
8
9
|
import { FolioMeasurementFeatureFlags, isWorkerFontMetricsEnabled, setFolioMeasurementFlags } from "./featureFlags.js";
|
|
9
|
-
export { type FloatingExclusionRect, type FloatingImageZone, type FloatingLineMargins, type FloatingLineSegmentZone, type FolioMeasurementFeatureFlags, type FontMetrics, type FontStyle, type MeasureParagraphOptions, type MeasureProvider, type RunMeasurement, type TextMeasurement, type WrapTextDirection, buildFontString, clampFloatingWrapMargins, clearAllCaches, clearFontMetricsCache, clearParagraphMeasureCache, clearTextWidthCache, findCharacterAtX, getCachedFontMetrics, getCachedParagraphMeasure, getCachedTextWidth, getFloatingAvailableWidth, getFloatingMargins, getFontCacheSize, getFontMetrics, getMeasureProvider, getParagraphCacheSize, getRunCharWidths, getTextCacheSize, getTotalCacheSize, getXForCharacter, halfPtToPx, hashParagraphBlock, isWorkerFontMetricsEnabled, measureParagraph, measureParagraphs, measureRun, measureText, measureTextWidth, ptToPx, pxToHalfPt, pxToPt, pxToTwips, rectsToFloatingZones, resetMeasureProvider, setCachedFontMetrics, setCachedParagraphMeasure, setCachedTextWidth, setFolioMeasurementFlags, setFontCacheSize, setMeasureProvider, setParagraphCacheSize, setTextCacheSize, twipsToPx };
|
|
10
|
+
export { type FloatingExclusionRect, type FloatingImageZone, type FloatingLineMargins, type FloatingLineSegmentZone, type FolioMeasurementFeatureFlags, type FontMetrics, type FontStyle, type LineBreakPolicy, type LineBreakProvider, type MeasureParagraphOptions, type MeasureProvider, type RunMeasurement, type TextMeasurement, type WrapTextDirection, buildFontString, clampFloatingWrapMargins, clearAllCaches, clearFontMetricsCache, clearParagraphMeasureCache, clearTextWidthCache, defaultLineBreakProvider, findCharacterAtX, getCachedFontMetrics, getCachedParagraphMeasure, getCachedTextWidth, getFloatingAvailableWidth, getFloatingMargins, getFontCacheSize, getFontMetrics, getLineBreakProvider, getLineBreakProviderGeneration, getMeasureProvider, getParagraphCacheSize, getRunCharWidths, getTextCacheSize, getTotalCacheSize, getXForCharacter, halfPtToPx, hashParagraphBlock, isWorkerFontMetricsEnabled, measureParagraph, measureParagraphs, measureRun, measureText, measureTextWidth, ptToPx, pxToHalfPt, pxToPt, pxToTwips, rectsToFloatingZones, resetLineBreakProvider, resetMeasureProvider, setCachedFontMetrics, setCachedParagraphMeasure, setCachedTextWidth, setFolioMeasurementFlags, setFontCacheSize, setLineBreakProvider, setMeasureProvider, setParagraphCacheSize, setTextCacheSize, twipsToPx };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { getFontMetrics, getMeasureProvider, measureRun, measureText, measureTextWidth, resetMeasureProvider, setMeasureProvider } from "./measureProvider.js";
|
|
2
2
|
import { buildFontString, findCharacterAtX, getXForCharacter, halfPtToPx, ptToPx, pxToHalfPt, pxToPt, pxToTwips, twipsToPx } from "./measureHelpers.js";
|
|
3
|
+
import { defaultLineBreakProvider, getLineBreakProvider, getLineBreakProviderGeneration, resetLineBreakProvider, setLineBreakProvider } from "./lineBreakProvider.js";
|
|
3
4
|
import { clampFloatingWrapMargins } from "./clampFloatingWrapMargins.js";
|
|
4
5
|
import { getFloatingAvailableWidth, getFloatingMargins, rectsToFloatingZones } from "./floatingZones.js";
|
|
5
6
|
import { getRunCharWidths, measureParagraph, measureParagraphs } from "./measureParagraph.js";
|
|
6
7
|
import { clearAllCaches, clearFontMetricsCache, clearParagraphMeasureCache, clearTextWidthCache, getCachedFontMetrics, getCachedParagraphMeasure, getCachedTextWidth, getFontCacheSize, getParagraphCacheSize, getTextCacheSize, getTotalCacheSize, hashParagraphBlock, setCachedFontMetrics, setCachedParagraphMeasure, setCachedTextWidth, setFontCacheSize, setParagraphCacheSize, setTextCacheSize } from "./cache.js";
|
|
7
8
|
import { isWorkerFontMetricsEnabled, setFolioMeasurementFlags } from "./featureFlags.js";
|
|
8
|
-
export { buildFontString, clampFloatingWrapMargins, clearAllCaches, clearFontMetricsCache, clearParagraphMeasureCache, clearTextWidthCache, findCharacterAtX, getCachedFontMetrics, getCachedParagraphMeasure, getCachedTextWidth, getFloatingAvailableWidth, getFloatingMargins, getFontCacheSize, getFontMetrics, getMeasureProvider, getParagraphCacheSize, getRunCharWidths, getTextCacheSize, getTotalCacheSize, getXForCharacter, halfPtToPx, hashParagraphBlock, isWorkerFontMetricsEnabled, measureParagraph, measureParagraphs, measureRun, measureText, measureTextWidth, ptToPx, pxToHalfPt, pxToPt, pxToTwips, rectsToFloatingZones, resetMeasureProvider, setCachedFontMetrics, setCachedParagraphMeasure, setCachedTextWidth, setFolioMeasurementFlags, setFontCacheSize, setMeasureProvider, setParagraphCacheSize, setTextCacheSize, twipsToPx };
|
|
9
|
+
export { buildFontString, clampFloatingWrapMargins, clearAllCaches, clearFontMetricsCache, clearParagraphMeasureCache, clearTextWidthCache, defaultLineBreakProvider, findCharacterAtX, getCachedFontMetrics, getCachedParagraphMeasure, getCachedTextWidth, getFloatingAvailableWidth, getFloatingMargins, getFontCacheSize, getFontMetrics, getLineBreakProvider, getLineBreakProviderGeneration, getMeasureProvider, getParagraphCacheSize, getRunCharWidths, getTextCacheSize, getTotalCacheSize, getXForCharacter, halfPtToPx, hashParagraphBlock, isWorkerFontMetricsEnabled, measureParagraph, measureParagraphs, measureRun, measureText, measureTextWidth, ptToPx, pxToHalfPt, pxToPt, pxToTwips, rectsToFloatingZones, resetLineBreakProvider, resetMeasureProvider, setCachedFontMetrics, setCachedParagraphMeasure, setCachedTextWidth, setFolioMeasurementFlags, setFontCacheSize, setLineBreakProvider, setMeasureProvider, setParagraphCacheSize, setTextCacheSize, twipsToPx };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/layout-engine/measure/lineBreakProvider.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Replaceable line-break policy for the layout engine.
|
|
4
|
+
*
|
|
5
|
+
* Providers return UTF-16 offsets so their output composes directly with the
|
|
6
|
+
* run and ProseMirror positions used by the rest of the layout pipeline. The
|
|
7
|
+
* default provider uses the platform's Unicode segmenter, then applies the
|
|
8
|
+
* OOXML line-edge restrictions that are available in the flow model.
|
|
9
|
+
*/
|
|
10
|
+
type LineBreakPolicy = {
|
|
11
|
+
/** BCP-47 language tag resolved from `w:lang` for this run. */locale?: string; /** Apply East Asian first/last-character restrictions (`w:kinsoku`). */
|
|
12
|
+
kinsoku?: boolean; /** Document replacement list: characters that may not begin a line. */
|
|
13
|
+
noLineBreaksBefore?: string; /** Document replacement list: characters that may not end a line. */
|
|
14
|
+
noLineBreaksAfter?: string; /** Use the legacy Ethiopic/Amharic compatibility behavior. */
|
|
15
|
+
useLegacyEthiopicAmharicRules?: boolean; /** Keep words made entirely of capital letters unhyphenated. */
|
|
16
|
+
doNotHyphenateCaps?: boolean; /** The run renders with the DOCX all-caps transform. */
|
|
17
|
+
renderedAllCaps?: boolean;
|
|
18
|
+
};
|
|
19
|
+
type LineBreakProvider = {
|
|
20
|
+
/** Legal soft-wrap offsets, in ascending UTF-16 order. */findBreaks: (text: string, policy?: LineBreakPolicy) => number[]; /** Grapheme-safe emergency-wrap offsets, in ascending UTF-16 order. */
|
|
21
|
+
findGraphemeBreaks: (text: string, policy?: LineBreakPolicy) => number[]; /** Dictionary-based discretionary hyphen offsets, in ascending UTF-16 order. */
|
|
22
|
+
findHyphenationBreaks?: (text: string, policy?: LineBreakPolicy) => number[]; /** Whether a trailing grapheme may overhang the line edge. */
|
|
23
|
+
isHangingPunctuation?: (text: string, policy?: LineBreakPolicy) => boolean;
|
|
24
|
+
};
|
|
25
|
+
declare const MAX_HYPHENATION_WORD_LENGTH = 256;
|
|
26
|
+
declare const defaultLineBreakProvider: LineBreakProvider;
|
|
27
|
+
declare const getLineBreakProvider: () => LineBreakProvider;
|
|
28
|
+
/** Changes whenever the active provider changes, so layout caches cannot go stale. */
|
|
29
|
+
declare const getLineBreakProviderGeneration: () => number;
|
|
30
|
+
declare const setLineBreakProvider: (provider: LineBreakProvider) => void;
|
|
31
|
+
declare const resetLineBreakProvider: () => void;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { LineBreakPolicy, LineBreakProvider, MAX_HYPHENATION_WORD_LENGTH, defaultLineBreakProvider, getLineBreakProvider, getLineBreakProviderGeneration, resetLineBreakProvider, setLineBreakProvider };
|
|
@@ -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
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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 === "
|
|
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
|
-
|
|
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
|
|
@@ -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
|
|
24
|
-
fontSize
|
|
26
|
+
fontFamily,
|
|
27
|
+
fontSize,
|
|
28
|
+
...bold !== void 0 ? { bold } : {}
|
|
25
29
|
};
|
|
26
30
|
}
|
|
27
31
|
/**
|
|
@@ -45,10 +49,11 @@ 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);
|
|
54
59
|
const markerEndOffset = getMarkerEndOffset(naturalWidth, attrs.listMarkerAlignment);
|
|
@@ -77,10 +82,11 @@ function getListMarkerInlineWidth(block) {
|
|
|
77
82
|
function getListMarkerVisualOffset(block) {
|
|
78
83
|
const attrs = block.attrs;
|
|
79
84
|
if (!attrs?.listMarker || attrs.listMarkerHidden) return 0;
|
|
80
|
-
const { fontFamily, fontSize } = resolveListMarkerFont(block);
|
|
85
|
+
const { fontFamily, fontSize, bold } = resolveListMarkerFont(block);
|
|
81
86
|
const naturalWidth = measureTextWidth(attrs.listMarker, {
|
|
82
87
|
fontFamily,
|
|
83
|
-
fontSize
|
|
88
|
+
fontSize,
|
|
89
|
+
...bold !== void 0 ? { bold } : {}
|
|
84
90
|
});
|
|
85
91
|
if (attrs.listMarkerAlignment === "right") return -naturalWidth;
|
|
86
92
|
if (attrs.listMarkerAlignment === "center") return -naturalWidth / 2;
|
|
@@ -1,7 +1,6 @@
|
|
|
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 = {
|
|
7
6
|
pageWidth?: number | number[];
|
|
@@ -25,4 +24,4 @@ declare function measureTextBoxBlock(tb: TextBoxBlock, fieldValues?: ReadonlyMap
|
|
|
25
24
|
declare function measureBlocks(blocks: FlowBlock[], contentWidth: number | number[], marginTop?: number | number[], pageGeometry?: BandPageGeometry, fieldValues?: ReadonlyMap<number, string>): Measure[];
|
|
26
25
|
declare function measureSingleBlockWithoutFloatingZones(block: FlowBlock, blockWidth: number, blockIndex: number): Measure;
|
|
27
26
|
//#endregion
|
|
28
|
-
export { measureBlock, measureBlocks, measureSingleBlockWithoutFloatingZones, measureTableBlock,
|
|
27
|
+
export { measureBlock, measureBlocks, measureSingleBlockWithoutFloatingZones, measureTableBlock, measureTextBoxBlock };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { DEFAULT_TEXTBOX_MARGINS, floatingTextBoxReservesBand, floatingTextBoxWrapsText,
|
|
1
|
+
import { DEFAULT_TEXTBOX_MARGINS, floatingTextBoxReservesBand, floatingTextBoxWrapsText, tableColumnsArePinned } from "../types.js";
|
|
2
2
|
import { findClearLineY, measureParagraph } from "./measureParagraph.js";
|
|
3
3
|
import { getCachedParagraphMeasure, setCachedParagraphMeasure } from "./cache.js";
|
|
4
|
+
import { createTableCellFlowState, finishTableCellFlow, placeTableCellBlock } from "./tableCellFlow.js";
|
|
4
5
|
import { layoutTextBoxParagraphs } from "./textBoxParagraphLayout.js";
|
|
5
6
|
import { getTextBoxGroupId } from "../textBoxGroup.js";
|
|
6
7
|
import { isParagraphFrameTextBox } from "../paragraphFrame.js";
|
|
@@ -52,39 +53,6 @@ function resolveTableWidthPx(width, widthType, contentWidth) {
|
|
|
52
53
|
if (widthType === "pct") return contentWidth * width / 5e3;
|
|
53
54
|
if (widthType === "dxa" || !widthType || widthType === "auto") return Math.round(width / 20 * 1.333);
|
|
54
55
|
}
|
|
55
|
-
function isInlineFlowImageRun(run) {
|
|
56
|
-
if (run.displayMode === "float") return false;
|
|
57
|
-
if (run.wrapType === "square" || run.wrapType === "tight" || run.wrapType === "through" || run.wrapType === "behind" || run.wrapType === "inFront") return false;
|
|
58
|
-
if (run.displayMode === "block" || run.wrapType === "topAndBottom") return false;
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
function measureTableCellBlockVisualHeight(block, blockMeasure) {
|
|
62
|
-
if (block.kind === "textBox" && isFloatingTextBoxBlock(block)) return 0;
|
|
63
|
-
if (block.kind !== "paragraph" || blockMeasure.kind !== "paragraph") {
|
|
64
|
-
if ("totalHeight" in blockMeasure) return blockMeasure.totalHeight;
|
|
65
|
-
if ("height" in blockMeasure) return blockMeasure.height;
|
|
66
|
-
return 0;
|
|
67
|
-
}
|
|
68
|
-
const paragraphBlock = block;
|
|
69
|
-
const paragraphMeasure = blockMeasure;
|
|
70
|
-
const nonEmptyRuns = paragraphBlock.runs.filter((run) => run.kind !== "text" || run.text.replace(/\u00a0/gu, " ").trim().length > 0);
|
|
71
|
-
if (paragraphMeasure.lines.length !== 1 || nonEmptyRuns.length === 0) return paragraphMeasure.totalHeight;
|
|
72
|
-
const inlineImageRuns = [];
|
|
73
|
-
for (const run of nonEmptyRuns) {
|
|
74
|
-
if (run.kind !== "image" || !isInlineFlowImageRun(run)) return paragraphMeasure.totalHeight;
|
|
75
|
-
inlineImageRuns.push(run);
|
|
76
|
-
}
|
|
77
|
-
let maxImageHeight = 0;
|
|
78
|
-
for (const run of inlineImageRuns) maxImageHeight = Math.max(maxImageHeight, run.height);
|
|
79
|
-
if (inlineImageRuns.length === 1) {
|
|
80
|
-
const spacingBefore = paragraphBlock.attrs?.spacing?.before ?? 0;
|
|
81
|
-
const spacingAfter = paragraphBlock.attrs?.spacing?.after ?? 0;
|
|
82
|
-
return spacingBefore + maxImageHeight + spacingAfter;
|
|
83
|
-
}
|
|
84
|
-
const spacingBefore = paragraphBlock.attrs?.spacing?.before ?? 0;
|
|
85
|
-
const spacingAfter = paragraphBlock.attrs?.spacing?.after ?? 0;
|
|
86
|
-
return spacingBefore + maxImageHeight + spacingAfter;
|
|
87
|
-
}
|
|
88
56
|
function measureTableBlock(tableBlock, contentWidth, fieldValues) {
|
|
89
57
|
const DEFAULT_CELL_PADDING_X = 7;
|
|
90
58
|
const DEFAULT_CELL_PADDING_Y = 0;
|
|
@@ -147,12 +115,14 @@ function measureTableBlock(tableBlock, contentWidth, fieldValues) {
|
|
|
147
115
|
const cell = row.cells[cellIdx];
|
|
148
116
|
const sourceCell = sourceRowCells?.[cellIdx];
|
|
149
117
|
cell.height = 0;
|
|
118
|
+
const flowState = createTableCellFlowState();
|
|
150
119
|
for (let blockIdx = 0; blockIdx < cell.blocks.length; blockIdx++) {
|
|
151
120
|
const sourceBlock = sourceCell?.blocks[blockIdx];
|
|
152
121
|
const blockMeasure = cell.blocks[blockIdx];
|
|
153
122
|
if (!sourceBlock || !blockMeasure) continue;
|
|
154
|
-
|
|
123
|
+
placeTableCellBlock(flowState, sourceBlock, blockMeasure);
|
|
155
124
|
}
|
|
125
|
+
cell.height = finishTableCellFlow(flowState);
|
|
156
126
|
const padTop = sourceCell?.padding?.top ?? DEFAULT_CELL_PADDING_Y;
|
|
157
127
|
const padBottom = sourceCell?.padding?.bottom ?? DEFAULT_CELL_PADDING_Y;
|
|
158
128
|
cell.height += padTop + padBottom;
|
|
@@ -576,4 +546,4 @@ function measureSingleBlockWithoutFloatingZones(block, blockWidth, blockIndex) {
|
|
|
576
546
|
return measureBlock(block, blockWidth);
|
|
577
547
|
}
|
|
578
548
|
//#endregion
|
|
579
|
-
export { measureBlock, measureBlocks, measureSingleBlockWithoutFloatingZones, measureTableBlock,
|
|
549
|
+
export { measureBlock, measureBlocks, measureSingleBlockWithoutFloatingZones, measureTableBlock, measureTextBoxBlock };
|