@stll/folio-core 0.49.0 → 0.51.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 (107) hide show
  1. package/dist/ai-edits/headless.d.ts +37 -1
  2. package/dist/ai-edits/headless.js +47 -16
  3. package/dist/ai-edits/word-diff.js +134 -1
  4. package/dist/compare/content-alignment.js +220 -5
  5. package/dist/compare/content.d.ts +1 -0
  6. package/dist/compare/content.js +51 -20
  7. package/dist/display-list/dom/renderDisplayListToDom.d.ts +10 -1
  8. package/dist/display-list/dom/renderDisplayListToDom.js +8 -7
  9. package/dist/display-list/html/renderDisplayListToHtml.d.ts +21 -0
  10. package/dist/display-list/html/renderDisplayListToHtml.js +126 -0
  11. package/dist/display-list/selectDisplayPages.d.ts +6 -0
  12. package/dist/display-list/selectDisplayPages.js +44 -0
  13. package/dist/docx/selectiveSave.d.ts +1 -1
  14. package/dist/docx/selectiveSave.js +16 -6
  15. package/dist/docx/structuralXmlPatch.d.ts +14 -0
  16. package/dist/docx/structuralXmlPatch.js +209 -0
  17. package/dist/export-pdf.d.ts +35 -2
  18. package/dist/export-pdf.js +51 -20
  19. package/dist/fonts/fontsourceFaces.d.ts +47 -0
  20. package/dist/fonts/fontsourceFaces.js +230 -0
  21. package/dist/layout-bridge/convert/flowBlockPostprocessing.d.ts +8 -0
  22. package/dist/layout-bridge/convert/flowBlockPostprocessing.js +106 -0
  23. package/dist/layout-bridge/convert/flowBorders.d.ts +17 -0
  24. package/dist/layout-bridge/convert/flowBorders.js +31 -0
  25. package/dist/layout-bridge/convert/flowConversionShared.d.ts +111 -0
  26. package/dist/layout-bridge/convert/flowConversionShared.js +23 -0
  27. package/dist/layout-bridge/convert/footnoteLayout.d.ts +2 -1
  28. package/dist/layout-bridge/convert/footnoteLayout.js +2 -1
  29. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +2 -1
  30. package/dist/layout-bridge/convert/imageConversion.d.ts +33 -0
  31. package/dist/layout-bridge/convert/imageConversion.js +118 -0
  32. package/dist/layout-bridge/convert/listMarkers.d.ts +28 -0
  33. package/dist/layout-bridge/convert/listMarkers.js +98 -0
  34. package/dist/layout-bridge/convert/noteReferences.d.ts +14 -0
  35. package/dist/layout-bridge/convert/noteReferences.js +29 -0
  36. package/dist/layout-bridge/convert/pageBreakSplitting.d.ts +16 -0
  37. package/dist/layout-bridge/convert/pageBreakSplitting.js +140 -0
  38. package/dist/layout-bridge/convert/paragraphAttrs.d.ts +20 -0
  39. package/dist/layout-bridge/convert/paragraphAttrs.js +234 -0
  40. package/dist/layout-bridge/convert/paragraphConversion.d.ts +12 -0
  41. package/dist/layout-bridge/convert/paragraphConversion.js +112 -0
  42. package/dist/layout-bridge/convert/paragraphRuns.d.ts +15 -0
  43. package/dist/layout-bridge/convert/paragraphRuns.js +281 -0
  44. package/dist/layout-bridge/convert/runFormattingMerge.d.ts +19 -0
  45. package/dist/layout-bridge/convert/runFormattingMerge.js +167 -0
  46. package/dist/layout-bridge/convert/runMarkFormatting.d.ts +11 -0
  47. package/dist/layout-bridge/convert/runMarkFormatting.js +291 -0
  48. package/dist/layout-bridge/convert/sectionBoundaries.d.ts +35 -0
  49. package/dist/layout-bridge/convert/sectionBoundaries.js +114 -0
  50. package/dist/layout-bridge/convert/tableConversion.d.ts +12 -0
  51. package/dist/layout-bridge/convert/tableConversion.js +303 -0
  52. package/dist/layout-bridge/convert/textBoxFill.d.ts +20 -0
  53. package/dist/layout-bridge/convert/textBoxFill.js +42 -0
  54. package/dist/layout-bridge/convert/textFormattingConversion.d.ts +22 -0
  55. package/dist/layout-bridge/convert/textFormattingConversion.js +120 -0
  56. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +5 -113
  57. package/dist/layout-bridge/convert/toFlowBlocks.js +11 -2082
  58. package/dist/layout-bridge/engine/hitTest.js +1 -1
  59. package/dist/layout-bridge/engine/measuring/index.d.ts +2 -1
  60. package/dist/layout-bridge/engine/measuring/index.js +2 -1
  61. package/dist/layout-bridge/engine/selectionRects.js +1 -1
  62. package/dist/layout-engine/imageLayout.d.ts +9 -0
  63. package/dist/layout-engine/imageLayout.js +48 -0
  64. package/dist/layout-engine/index.d.ts +3 -18
  65. package/dist/layout-engine/index.js +12 -937
  66. package/dist/layout-engine/layoutFlowShared.d.ts +6 -0
  67. package/dist/layout-engine/layoutFlowShared.js +8 -0
  68. package/dist/layout-engine/measure/crossRunWords.d.ts +45 -0
  69. package/dist/layout-engine/measure/crossRunWords.js +172 -0
  70. package/dist/layout-engine/measure/floatingLineClearance.d.ts +24 -0
  71. package/dist/layout-engine/measure/floatingLineClearance.js +44 -0
  72. package/dist/layout-engine/measure/inlineObjectMetrics.d.ts +6 -0
  73. package/dist/layout-engine/measure/inlineObjectMetrics.js +52 -0
  74. package/dist/layout-engine/measure/justification.d.ts +32 -0
  75. package/dist/layout-engine/measure/justification.js +83 -0
  76. package/dist/layout-engine/measure/lineFitting.d.ts +48 -0
  77. package/dist/layout-engine/measure/lineFitting.js +145 -0
  78. package/dist/layout-engine/measure/lineTypography.d.ts +44 -0
  79. package/dist/layout-engine/measure/lineTypography.js +124 -0
  80. package/dist/layout-engine/measure/measureBlocks.js +2 -1
  81. package/dist/layout-engine/measure/measureParagraph.d.ts +1 -20
  82. package/dist/layout-engine/measure/measureParagraph.js +32 -734
  83. package/dist/layout-engine/measure/paragraphMeasureShared.d.ts +109 -0
  84. package/dist/layout-engine/measure/paragraphMeasureShared.js +82 -0
  85. package/dist/layout-engine/measure/paragraphTabs.d.ts +18 -0
  86. package/dist/layout-engine/measure/paragraphTabs.js +78 -0
  87. package/dist/layout-engine/paragraphLayout.d.ts +23 -0
  88. package/dist/layout-engine/paragraphLayout.js +204 -0
  89. package/dist/layout-engine/sectionLayout.d.ts +45 -0
  90. package/dist/layout-engine/sectionLayout.js +216 -0
  91. package/dist/layout-engine/tableLayout.d.ts +17 -0
  92. package/dist/layout-engine/tableLayout.js +369 -0
  93. package/dist/layout-engine/textBoxLayout.d.ts +15 -0
  94. package/dist/layout-engine/textBoxLayout.js +137 -0
  95. package/dist/layout-painter/renderPage.js +1 -0
  96. package/dist/managers/editorShortcuts.d.ts +16 -3
  97. package/dist/managers/editorShortcuts.js +6 -4
  98. package/dist/paged-layout/sectionBlockWidths.d.ts +2 -1
  99. package/dist/paged-layout/sectionBlockWidths.js +1 -1
  100. package/dist/prosemirror/extensions/StarterKit.d.ts +3 -0
  101. package/dist/prosemirror/extensions/StarterKit.js +2 -1
  102. package/dist/prosemirror/extensions/core/HistoryExtension.d.ts +9 -1
  103. package/dist/prosemirror/extensions/core/HistoryExtension.js +4 -3
  104. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +12 -5
  105. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +91 -33
  106. package/dist/server.d.ts +3 -2
  107. package/package.json +2 -2
@@ -0,0 +1,44 @@
1
+ import { ParagraphAttrs, ParagraphSpacing, TextRun } from "../types.js";
2
+ import { FontMetrics, FontStyle } from "./measureTypes.js";
3
+ import { LineTypography } from "./paragraphMeasureShared.js";
4
+ //#region src/layout-engine/measure/lineTypography.d.ts
5
+ /**
6
+ * Line-HEIGHT style for a text run. Word derives a CJK line's height from an
7
+ * East-Asian face, not the run's ascii font: real documents routinely put CJK
8
+ * glyphs in runs whose ascii/eastAsia fonts are Latin (e.g. `w:eastAsia`
9
+ * "Century"), and Word font-links those glyphs to the default East-Asian face
10
+ * and uses its taller single-line height (≈1.303 vs the ≈1.15 Latin default).
11
+ *
12
+ * When the run holds CJK text, swap `fontFamily` to the face whose
13
+ * `singleLineRatio` Word would actually use: the run's `w:eastAsia` font when
14
+ * it is a real CJK face, the run's own ascii font when THAT is a CJK face, or
15
+ * `CJK_FALLBACK_FONT_FAMILY` otherwise. Non-CJK runs return `baseStyle`
16
+ * unchanged, so Latin-only documents are unaffected by construction.
17
+ *
18
+ * The result feeds `updateMaxFont` (line height) ONLY — width measurement
19
+ * keeps the base style so wrapping is untouched.
20
+ */
21
+ declare function cjkLineHeightStyle(run: TextRun, baseStyle: FontStyle): FontStyle;
22
+ /** Pick the dominant typography metrics for a run containing complex script. */
23
+ declare function complexScriptLineHeightStyle(run: TextRun, baseStyle: FontStyle): FontStyle;
24
+ /**
25
+ * Calculate typography metrics from font size and spacing settings
26
+ *
27
+ * @param fontSize - Font size in points
28
+ * @param spacing - Paragraph spacing settings
29
+ * @param metrics - Pre-calculated font metrics (in pixels)
30
+ */
31
+ declare function calculateTypographyMetrics(fontSize: number, spacing?: ParagraphSpacing, metrics?: FontMetrics | null): LineTypography;
32
+ /**
33
+ * Calculate metrics for an empty paragraph.
34
+ *
35
+ * Word renders an empty paragraph as a single readable line — its line
36
+ * height never collapses below 1.15 × font size, even when the doc
37
+ * explicitly writes `<w:line w:val="240"/>` (1.0×). The floor is scoped to
38
+ * `auto`/`atLeast` line rules; `exact` means exact (per OOXML §17.3.1.33)
39
+ * and stays untouched.
40
+ */
41
+ declare function calculateEmptyParagraphMetrics(fontSize: number, spacing?: ParagraphSpacing, fontFamily?: string, attrs?: ParagraphAttrs): LineTypography;
42
+ declare function applyDocumentGrid(typography: LineTypography, attrs: ParagraphAttrs | undefined, spacing: ParagraphSpacing | undefined): LineTypography;
43
+ //#endregion
44
+ export { applyDocumentGrid, calculateEmptyParagraphMetrics, calculateTypographyMetrics, cjkLineHeightStyle, complexScriptLineHeightStyle };
@@ -0,0 +1,124 @@
1
+ import { CJK_FALLBACK_FONT_FAMILY, isCjkFont } from "../../utils/fontResolver.js";
2
+ import { hasCjk, hasComplexScript, hasEastAsiaSlotText } from "../../utils/scriptSegments.js";
3
+ import { applyComplexScriptFormatting, hasComplexScriptFormatting } from "./complexScriptFormatting.js";
4
+ import { ptToPx } from "./measureHelpers.js";
5
+ import { getFontMetrics } from "./measureProvider.js";
6
+ import "./paragraphMeasureShared.js";
7
+ //#region src/layout-engine/measure/lineTypography.ts
8
+ /**
9
+ * Line height and typography metrics: font-driven line heights, East Asian
10
+ * and complex-script line height styles, empty-paragraph metrics, and
11
+ * document-grid line pitch.
12
+ */
13
+ /**
14
+ * Line-HEIGHT style for a text run. Word derives a CJK line's height from an
15
+ * East-Asian face, not the run's ascii font: real documents routinely put CJK
16
+ * glyphs in runs whose ascii/eastAsia fonts are Latin (e.g. `w:eastAsia`
17
+ * "Century"), and Word font-links those glyphs to the default East-Asian face
18
+ * and uses its taller single-line height (≈1.303 vs the ≈1.15 Latin default).
19
+ *
20
+ * When the run holds CJK text, swap `fontFamily` to the face whose
21
+ * `singleLineRatio` Word would actually use: the run's `w:eastAsia` font when
22
+ * it is a real CJK face, the run's own ascii font when THAT is a CJK face, or
23
+ * `CJK_FALLBACK_FONT_FAMILY` otherwise. Non-CJK runs return `baseStyle`
24
+ * unchanged, so Latin-only documents are unaffected by construction.
25
+ *
26
+ * The result feeds `updateMaxFont` (line height) ONLY — width measurement
27
+ * keeps the base style so wrapping is untouched.
28
+ */
29
+ function cjkLineHeightStyle(run, baseStyle) {
30
+ if (!run.text) return baseStyle;
31
+ const eastAsia = baseStyle.eastAsiaFontFamily;
32
+ const eastAsiaFace = eastAsia !== void 0 && isCjkFont(eastAsia);
33
+ if (!hasCjk(run.text) && !(eastAsiaFace && hasEastAsiaSlotText(run.text, baseStyle.eastAsiaHint))) return baseStyle;
34
+ if (eastAsia !== void 0 && eastAsiaFace) {
35
+ const result = {
36
+ ...baseStyle,
37
+ fontFamily: eastAsia
38
+ };
39
+ delete result.alternateFontFamily;
40
+ if (baseStyle.eastAsiaAlternateFontFamily !== void 0) result.alternateFontFamily = baseStyle.eastAsiaAlternateFontFamily;
41
+ return result;
42
+ }
43
+ if (isCjkFont(baseStyle.fontFamily ?? "Times New Roman")) return baseStyle;
44
+ return {
45
+ ...baseStyle,
46
+ fontFamily: CJK_FALLBACK_FONT_FAMILY
47
+ };
48
+ }
49
+ /** Pick the dominant typography metrics for a run containing complex script. */
50
+ function complexScriptLineHeightStyle(run, baseStyle) {
51
+ if (!hasComplexScriptFormatting(run) || !run.forceComplexScript && !hasComplexScript(run.text)) return baseStyle;
52
+ const complexStyle = applyComplexScriptFormatting(baseStyle, run);
53
+ return (complexStyle.fontSize ?? 11) >= (baseStyle.fontSize ?? 11) ? complexStyle : baseStyle;
54
+ }
55
+ /**
56
+ * Calculate typography metrics from font size and spacing settings
57
+ *
58
+ * @param fontSize - Font size in points
59
+ * @param spacing - Paragraph spacing settings
60
+ * @param metrics - Pre-calculated font metrics (in pixels)
61
+ */
62
+ function calculateTypographyMetrics(fontSize, spacing, metrics) {
63
+ const fontSizePx = ptToPx(fontSize);
64
+ const ascent = metrics?.ascent ?? fontSizePx * .8;
65
+ const descent = metrics?.descent ?? fontSizePx * .2;
66
+ const singleLineBase = fontSizePx * (metrics?.singleLineRatio ?? 1.15);
67
+ let lineHeight;
68
+ if (spacing?.lineRule === "exact" && spacing.line !== void 0) lineHeight = spacing.line;
69
+ else if (spacing?.lineRule === "atLeast" && spacing.line !== void 0) {
70
+ const defaultHeight = singleLineBase * 1;
71
+ lineHeight = Math.max(spacing.line, defaultHeight);
72
+ } else if (spacing?.line !== void 0 && spacing.lineUnit === "multiplier") lineHeight = singleLineBase * spacing.line;
73
+ else if (spacing?.line !== void 0 && spacing.lineUnit === "px") lineHeight = spacing.line;
74
+ else lineHeight = singleLineBase * 1;
75
+ return {
76
+ ascent,
77
+ descent,
78
+ lineHeight
79
+ };
80
+ }
81
+ /**
82
+ * Word's "single line spacing" floor (≈ 1.15×) applied to empty paragraphs
83
+ * with `auto`/`atLeast` line rules. Without this, narrow-metric fonts
84
+ * (Arial Narrow, OS/2 ratio ≈ 1.117) collapse empty rows visibly tighter
85
+ * than Word renders them. See eigenpal #391/#394.
86
+ */
87
+ const WORD_SINGLE_LINE_FLOOR = 1.15;
88
+ /**
89
+ * Calculate metrics for an empty paragraph.
90
+ *
91
+ * Word renders an empty paragraph as a single readable line — its line
92
+ * height never collapses below 1.15 × font size, even when the doc
93
+ * explicitly writes `<w:line w:val="240"/>` (1.0×). The floor is scoped to
94
+ * `auto`/`atLeast` line rules; `exact` means exact (per OOXML §17.3.1.33)
95
+ * and stays untouched.
96
+ */
97
+ function calculateEmptyParagraphMetrics(fontSize, spacing, fontFamily, attrs) {
98
+ const result = calculateTypographyMetrics(fontSize, spacing, getFontMetrics({
99
+ fontSize,
100
+ fontFamily: fontFamily ?? "Times New Roman",
101
+ ...attrs?.defaultAlternateFontFamily !== void 0 ? { alternateFontFamily: attrs.defaultAlternateFontFamily } : {}
102
+ }));
103
+ const lineRule = spacing?.lineRule ?? "auto";
104
+ if (lineRule === "auto" || lineRule === "atLeast") {
105
+ const floor = ptToPx(fontSize) * WORD_SINGLE_LINE_FLOOR;
106
+ if (result.lineHeight < floor) return applyDocumentGrid({
107
+ ...result,
108
+ lineHeight: floor
109
+ }, attrs, spacing);
110
+ }
111
+ return applyDocumentGrid(result, attrs, spacing);
112
+ }
113
+ function applyDocumentGrid(typography, attrs, spacing) {
114
+ const pitch = attrs?.documentGridLinePitch;
115
+ if (pitch === void 0 || pitch <= 0 || attrs?.snapToGrid === false) return typography;
116
+ if (spacing?.lineRule === "exact") return typography;
117
+ const lineHeight = Math.ceil(typography.lineHeight / pitch - 1e-9) * pitch;
118
+ return lineHeight === typography.lineHeight ? typography : {
119
+ ...typography,
120
+ lineHeight
121
+ };
122
+ }
123
+ //#endregion
124
+ export { applyDocumentGrid, calculateEmptyParagraphMetrics, calculateTypographyMetrics, cjkLineHeightStyle, complexScriptLineHeightStyle };
@@ -7,9 +7,10 @@ import { bandFragmentX, bandTopContentY, isPageFrameRelativeAnchor } from "../te
7
7
  import { getTextBoxGroupId } from "../textBoxGroup.js";
8
8
  import { DEFAULT_TEXTBOX_MARGINS, floatingTextBoxReservesBand, floatingTextBoxWrapsText, resolveTableCellPadding, resolveTableWidthPx, tableColumnsArePinned } from "../types.js";
9
9
  import { getCachedParagraphMeasure, setCachedParagraphMeasure } from "./cache.js";
10
+ import { findClearLineY } from "./floatingLineClearance.js";
10
11
  import { resolveFloatingTablePageX } from "./floatingTablePosition.js";
11
12
  import { getHyphenationDictionaryMisses } from "./hyphenationDictionaries.js";
12
- import { findClearLineY, measureParagraph } from "./measureParagraph.js";
13
+ import { measureParagraph } from "./measureParagraph.js";
13
14
  import { buildTableCellFloatingZones, getTableCellFloatingImages } from "./tableCellFloating.js";
14
15
  import { createTableCellFlowState, finishTableCellFlow, placeTableCellBlock } from "./tableCellFlow.js";
15
16
  import { buildTableCellGrid, getFirstAvailableColumn, getTableCellSpacingInsetsX, getTableCellSpacingInsetsY, getTableCellVerticalBorderHeight } from "./tableCellGrid.js";
@@ -1,6 +1,7 @@
1
1
  import { ParagraphBlock, ParagraphMeasure, TextRun } from "../types.js";
2
2
  import { FloatingImageZone } from "./floatingZones.js";
3
3
  import { clampFloatingWrapMargins } from "./clampFloatingWrapMargins.js";
4
+ import { MIN_WRAP_SEGMENT_WIDTH, findClearLineY } from "./floatingLineClearance.js";
4
5
  //#region src/layout-engine/measure/measureParagraph.d.ts
5
6
  /**
6
7
  * Options for paragraph measurement
@@ -16,26 +17,6 @@ type MeasureParagraphOptions = {
16
17
  /** Header/footer tabs may be authored in the page margin, beyond body width. */
17
18
  allowEndTabOverflow?: boolean;
18
19
  };
19
- /**
20
- * Minimum horizontal room a line must offer before we treat it as usable for
21
- * body text. Below this threshold the line is bumped past obstructing floats
22
- * via `findClearLineY` instead of being rendered into the unusable sliver.
23
- * Without this guard, a near-full-width float (e.g. floating table) produces
24
- * a ~2px segment that collapses every wrap line to one glyph per row.
25
- */
26
- declare const MIN_WRAP_SEGMENT_WIDTH = 24;
27
- /**
28
- * Find the next vertical position at or below `startY` where the available
29
- * text width is at least `minWidth`. Used to skip lines past stacked floats
30
- * when there is no horizontal room for meaningful text at the current Y.
31
- *
32
- * Returns `startY` if the current position already has enough room, otherwise
33
- * the lowest `bottomY` of any zone currently obstructing the line. The caller
34
- * is expected to re-query margins at the returned Y.
35
- *
36
- * Coordinates are absolute (i.e., already include any paragraphYOffset).
37
- */
38
- declare function findClearLineY(startY: number, lineHeight: number, zones: FloatingImageZone[] | undefined, contentWidth: number, minWidth: number): number;
39
20
  /**
40
21
  * Measure a paragraph block and compute line breaks
41
22
  *