@stll/folio-core 0.29.0 → 0.30.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/dist/controller/folioEditor.d.ts +28 -1
- package/dist/controller/folioEditor.js +37 -26
- package/dist/controller/fontReadiness.js +28 -24
- package/dist/controller/hiddenEditorManager.d.ts +2 -7
- package/dist/controller/layoutPipeline.js +33 -3
- package/dist/controller/noteEditorManager.d.ts +3 -6
- package/dist/docx/metafileSvg.d.ts +12 -0
- package/dist/docx/metafileSvg.js +462 -0
- package/dist/docx/paragraphTextBoxEnrichment.js +55 -4
- package/dist/docx/parser.d.ts +2 -2
- package/dist/docx/parser.js +26 -0
- package/dist/docx/runParser.js +2 -1
- package/dist/docx/serializer/runSerializer.js +14 -2
- package/dist/docx/styleParser.js +2 -1
- package/dist/docx/textBoxParser.js +14 -0
- package/dist/docx/vmlImageParser.js +72 -152
- package/dist/docx/vmlPreview.d.ts +31 -0
- package/dist/docx/vmlPreview.js +535 -0
- package/dist/docx/wrapTypes.d.ts +2 -36
- package/dist/docx/wrapTypes.js +1 -39
- package/dist/fonts/fontAlternates.d.ts +8 -0
- package/dist/fonts/fontAlternates.js +16 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +34 -3
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +5 -0
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +3 -0
- package/dist/layout-bridge/convert/toFlowBlocks.js +78 -28
- package/dist/layout-bridge/dom/noteStoryDom.d.ts +1 -1
- package/dist/layout-bridge/engine/selectionRects.js +3 -1
- package/dist/layout-engine/index.js +65 -32
- package/dist/layout-engine/justifiedLineFit.d.ts +7 -0
- package/dist/layout-engine/justifiedLineFit.js +6 -0
- package/dist/layout-engine/keep-together.js +15 -10
- package/dist/layout-engine/measure/complexScriptFormatting.d.ts +6 -1
- package/dist/layout-engine/measure/complexScriptFormatting.js +11 -2
- package/dist/layout-engine/measure/floatingTablePosition.d.ts +16 -1
- package/dist/layout-engine/measure/floatingTablePosition.js +29 -6
- package/dist/layout-engine/measure/lineBreakProvider.js +67 -8
- package/dist/layout-engine/measure/listMarkerWidth.d.ts +1 -0
- package/dist/layout-engine/measure/listMarkerWidth.js +23 -10
- package/dist/layout-engine/measure/measureBlocks.d.ts +15 -0
- package/dist/layout-engine/measure/measureBlocks.js +197 -30
- package/dist/layout-engine/measure/measureContainer.js +20 -24
- package/dist/layout-engine/measure/measureHelpers.d.ts +1 -1
- package/dist/layout-engine/measure/measureHelpers.js +14 -8
- package/dist/layout-engine/measure/measureParagraph.js +111 -37
- package/dist/layout-engine/measure/measureTypes.d.ts +3 -0
- package/dist/layout-engine/measure/tabCalculator.d.ts +123 -0
- package/dist/layout-engine/measure/tabCalculator.js +144 -0
- package/dist/layout-engine/measure/tableCellFlow.d.ts +21 -1
- package/dist/layout-engine/measure/tableCellFlow.js +45 -8
- package/dist/layout-engine/measure/tableInlinePlacement.d.ts +10 -2
- package/dist/layout-engine/measure/tableInlinePlacement.js +17 -15
- package/dist/layout-engine/paginator.d.ts +26 -2
- package/dist/layout-engine/paginator.js +76 -14
- package/dist/layout-engine/paragraphSpacing.d.ts +3 -1
- package/dist/layout-engine/paragraphSpacing.js +4 -3
- package/dist/layout-engine/renderedBreakReconciliation.d.ts +14 -3
- package/dist/layout-engine/renderedBreakReconciliation.js +45 -14
- package/dist/layout-engine/section-breaks.d.ts +3 -1
- package/dist/layout-engine/section-breaks.js +6 -2
- package/dist/layout-engine/types.d.ts +18 -1
- package/dist/layout-engine/types.js +1 -1
- package/dist/layout-painter/imageLayout.d.ts +1 -1
- package/dist/layout-painter/renderPage.js +6 -1
- package/dist/layout-painter/renderParagraph.js +169 -82
- package/dist/layout-painter/renderTable.d.ts +1 -1
- package/dist/layout-painter/renderTable.js +18 -7
- package/dist/layout-painter/renderTextBox.d.ts +1 -1
- package/dist/layout-painter/renderTextBox.js +6 -1
- package/dist/model.d.ts +3 -3
- package/dist/model.js +2 -2
- package/dist/paged-layout/sectionBlockWidths.d.ts +14 -0
- package/dist/paged-layout/sectionBlockWidths.js +103 -14
- package/dist/prosemirror/attrs/index.js +12 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +6 -2
- package/dist/prosemirror/conversion/toProseDoc.js +10 -3
- package/dist/prosemirror/extensions/marks/CharacterSpacingExtension.js +9 -5
- package/dist/prosemirror/extensions/nodes/TableExtension.js +2 -0
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +6 -2
- package/dist/prosemirror/schema/nodes.d.ts +4 -0
- package/dist/prosemirror/utils/tabCalculator.d.ts +2 -123
- package/dist/prosemirror/utils/tabCalculator.js +1 -140
- package/dist/render-dom/RemoteSelectionOverlay.d.ts +4 -4
- package/dist/types/documentEnumValues.d.ts +4 -1
- package/dist/types/documentEnumValues.js +12 -1
- package/dist/types/editor-story.d.ts +8 -0
- package/dist/types/editor-story.js +0 -0
- package/dist/types/remote-selection.d.ts +11 -0
- package/dist/types/remote-selection.js +0 -0
- package/dist/types/wrap.d.ts +36 -0
- package/dist/types/wrap.js +40 -0
- package/dist/utils/fontResolver.d.ts +1 -1
- package/dist/utils/fontResolver.js +42 -2
- package/dist/utils/formatToStyle.js +4 -2
- package/dist/utils/horizontalScale.d.ts +28 -0
- package/dist/utils/horizontalScale.js +42 -0
- package/package.json +2 -2
|
@@ -1,123 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* Tab Width Calculator
|
|
4
|
-
*
|
|
5
|
-
* Computes tab widths based on position and tab stops.
|
|
6
|
-
* Follows OOXML tab stop semantics:
|
|
7
|
-
* - Default tab interval: 720 twips (0.5 inch, 48px at 96dpi)
|
|
8
|
-
* - Tab stops can be start (left), end (right), center, decimal, or bar
|
|
9
|
-
* - Explicit tab stops override default stops
|
|
10
|
-
*
|
|
11
|
-
* Based on ECMA-376 specification and clean-room understanding of tab layout.
|
|
12
|
-
*/
|
|
13
|
-
/**
|
|
14
|
-
* Tab alignment types
|
|
15
|
-
*/
|
|
16
|
-
type TabAlignment = "start" | "end" | "center" | "decimal" | "bar" | "clear";
|
|
17
|
-
/**
|
|
18
|
-
* Tab leader (fill character) types
|
|
19
|
-
*/
|
|
20
|
-
type TabLeader = "none" | "dot" | "hyphen" | "underscore" | "middleDot" | "heavy";
|
|
21
|
-
/**
|
|
22
|
-
* Tab stop definition
|
|
23
|
-
*/
|
|
24
|
-
type TabStop = {
|
|
25
|
-
/** Tab alignment mode */
|
|
26
|
-
val: TabAlignment;
|
|
27
|
-
/** Position in twips from left margin */
|
|
28
|
-
pos: number;
|
|
29
|
-
/** Optional leader character */
|
|
30
|
-
leader?: TabLeader;
|
|
31
|
-
};
|
|
32
|
-
/**
|
|
33
|
-
* Context for tab calculations
|
|
34
|
-
*/
|
|
35
|
-
type TabContext = {
|
|
36
|
-
/** Explicit tab stops from paragraph or style */
|
|
37
|
-
explicitStops?: TabStop[];
|
|
38
|
-
/** Default tab interval in twips (default: 720 = 0.5 inch) */
|
|
39
|
-
defaultTabInterval?: number;
|
|
40
|
-
/** Left indent in twips */
|
|
41
|
-
leftIndent?: number;
|
|
42
|
-
};
|
|
43
|
-
/**
|
|
44
|
-
* Result of tab width calculation
|
|
45
|
-
*/
|
|
46
|
-
type TabWidthResult = {
|
|
47
|
-
/** Width of the tab in pixels */
|
|
48
|
-
width: number;
|
|
49
|
-
/** Leader character to render (if any) */
|
|
50
|
-
leader?: TabLeader;
|
|
51
|
-
/** Alignment that was applied */
|
|
52
|
-
alignment: TabAlignment | "default";
|
|
53
|
-
};
|
|
54
|
-
/** Default tab interval: 720 twips = 0.5 inch */
|
|
55
|
-
declare const DEFAULT_TAB_INTERVAL_TWIPS = 720;
|
|
56
|
-
/** Twips per inch */
|
|
57
|
-
declare const TWIPS_PER_INCH = 1440;
|
|
58
|
-
/** Pixels per inch (96 dpi standard for CSS) */
|
|
59
|
-
declare const PIXELS_PER_INCH = 96;
|
|
60
|
-
/**
|
|
61
|
-
* Convert twips to pixels
|
|
62
|
-
* @param twips - Value in twips (1/1440 inch)
|
|
63
|
-
* @returns Value in pixels (at 96 dpi)
|
|
64
|
-
*/
|
|
65
|
-
declare function twipsToPixels(twips: number): number;
|
|
66
|
-
/**
|
|
67
|
-
* Convert pixels to twips
|
|
68
|
-
* @param pixels - Value in pixels (at 96 dpi)
|
|
69
|
-
* @returns Value in twips (1/1440 inch)
|
|
70
|
-
*/
|
|
71
|
-
declare function pixelsToTwips(pixels: number): number;
|
|
72
|
-
/**
|
|
73
|
-
* Compute the list of effective tab stops for a paragraph
|
|
74
|
-
*
|
|
75
|
-
* Merges explicit stops with default stops at regular intervals.
|
|
76
|
-
* Explicit stops before the left indent remain available to a hanging first
|
|
77
|
-
* line. Continuation lines naturally skip them because their cursor already
|
|
78
|
-
* starts at the left indent.
|
|
79
|
-
*
|
|
80
|
-
* @param context - Tab context with explicit stops and settings
|
|
81
|
-
* @returns Sorted array of tab stops in twips
|
|
82
|
-
*/
|
|
83
|
-
declare function computeTabStops(context: TabContext): TabStop[];
|
|
84
|
-
/**
|
|
85
|
-
* Pixel widths of the content after a tab, needed to position non-left stops.
|
|
86
|
-
*
|
|
87
|
-
* Caller measures its own runs (the painter and the layout measurer have
|
|
88
|
-
* different run models and measurement paths) and passes the result here, so
|
|
89
|
-
* this module owns the stop grid + alignment math without owning measurement.
|
|
90
|
-
*/
|
|
91
|
-
type TabFollowingContent = {
|
|
92
|
-
/** Total width of the runs after the tab. Used for `end` and `center` stops. */
|
|
93
|
-
followingWidth?: number;
|
|
94
|
-
/** Width of the content before the decimal separator. Used for `decimal` stops. */
|
|
95
|
-
decimalPrefixWidth?: number;
|
|
96
|
-
};
|
|
97
|
-
/**
|
|
98
|
-
* Calculate the width of a tab character.
|
|
99
|
-
*
|
|
100
|
-
* Finds the next tab stop after the current position and computes the width
|
|
101
|
-
* needed to reach it. For `end`/`center`/`decimal` stops the content after the
|
|
102
|
-
* tab is anchored against the stop, so the caller supplies its measured width
|
|
103
|
-
* via {@link TabFollowingContent}.
|
|
104
|
-
*
|
|
105
|
-
* @param currentXPx - Current horizontal position in pixels (from the content
|
|
106
|
-
* area's left edge — the same origin tab stop positions are measured from)
|
|
107
|
-
* @param context - Tab context with stops and settings
|
|
108
|
-
* @param following - Measured widths of the content after the tab
|
|
109
|
-
* @returns Tab width result with width in pixels
|
|
110
|
-
*/
|
|
111
|
-
declare function calculateTabWidth(currentXPx: number, context: TabContext, following?: TabFollowingContent): TabWidthResult;
|
|
112
|
-
/**
|
|
113
|
-
* Calculate tab width with simple default stops
|
|
114
|
-
*
|
|
115
|
-
* Simplified version for basic tab rendering without explicit stops.
|
|
116
|
-
* Uses the default 0.5 inch (48px) tab interval.
|
|
117
|
-
*
|
|
118
|
-
* @param currentXPx - Current horizontal position in pixels
|
|
119
|
-
* @returns Width of the tab in pixels
|
|
120
|
-
*/
|
|
121
|
-
declare function calculateSimpleTabWidth(currentXPx: number): number;
|
|
122
|
-
//#endregion
|
|
123
|
-
export { DEFAULT_TAB_INTERVAL_TWIPS, PIXELS_PER_INCH, TWIPS_PER_INCH, TabAlignment, TabContext, TabFollowingContent, TabLeader, TabStop, TabWidthResult, calculateSimpleTabWidth, calculateTabWidth, computeTabStops, pixelsToTwips, twipsToPixels };
|
|
1
|
+
import { DEFAULT_TAB_INTERVAL_TWIPS, PIXELS_PER_INCH, TWIPS_PER_INCH, TabAlignment, TabContext, TabFollowingContent, TabLeader, TabStop, TabWidthResult, calculateSimpleTabWidth, calculateTabWidth, computeTabStops, pixelsToTwips, twipsToPixels } from "../../layout-engine/measure/tabCalculator.js";
|
|
2
|
+
export { DEFAULT_TAB_INTERVAL_TWIPS, PIXELS_PER_INCH, TWIPS_PER_INCH, type TabAlignment, type TabContext, type TabFollowingContent, type TabLeader, type TabStop, type TabWidthResult, calculateSimpleTabWidth, calculateTabWidth, computeTabStops, pixelsToTwips, twipsToPixels };
|
|
@@ -1,141 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
/** Default tab interval: 720 twips = 0.5 inch */
|
|
3
|
-
const DEFAULT_TAB_INTERVAL_TWIPS = 720;
|
|
4
|
-
/** Twips per inch */
|
|
5
|
-
const TWIPS_PER_INCH = 1440;
|
|
6
|
-
/** Pixels per inch (96 dpi standard for CSS) */
|
|
7
|
-
const PIXELS_PER_INCH = 96;
|
|
8
|
-
/** Ignore sub-twip round trips when the cursor already occupies a tab stop. */
|
|
9
|
-
const TAB_POSITION_EPSILON_TWIPS = .01;
|
|
10
|
-
/**
|
|
11
|
-
* Convert twips to pixels
|
|
12
|
-
* @param twips - Value in twips (1/1440 inch)
|
|
13
|
-
* @returns Value in pixels (at 96 dpi)
|
|
14
|
-
*/
|
|
15
|
-
function twipsToPixels(twips) {
|
|
16
|
-
return twips / TWIPS_PER_INCH * 96;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Convert pixels to twips
|
|
20
|
-
* @param pixels - Value in pixels (at 96 dpi)
|
|
21
|
-
* @returns Value in twips (1/1440 inch)
|
|
22
|
-
*/
|
|
23
|
-
function pixelsToTwips(pixels) {
|
|
24
|
-
return pixels / 96 * TWIPS_PER_INCH;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Compute the list of effective tab stops for a paragraph
|
|
28
|
-
*
|
|
29
|
-
* Merges explicit stops with default stops at regular intervals.
|
|
30
|
-
* Explicit stops before the left indent remain available to a hanging first
|
|
31
|
-
* line. Continuation lines naturally skip them because their cursor already
|
|
32
|
-
* starts at the left indent.
|
|
33
|
-
*
|
|
34
|
-
* @param context - Tab context with explicit stops and settings
|
|
35
|
-
* @returns Sorted array of tab stops in twips
|
|
36
|
-
*/
|
|
37
|
-
function computeTabStops(context) {
|
|
38
|
-
const { explicitStops = [], defaultTabInterval = 720, leftIndent = 0 } = context;
|
|
39
|
-
const validExplicitStops = explicitStops.filter((stop) => stop.val !== "clear" && stop.val !== "bar");
|
|
40
|
-
const clearPositions = explicitStops.filter((stop) => stop.val === "clear").map((stop) => stop.pos);
|
|
41
|
-
let maxExplicit = 0;
|
|
42
|
-
for (const stop of validExplicitStops) maxExplicit = Math.max(maxExplicit, stop.pos);
|
|
43
|
-
const stops = explicitStops.filter((stop) => stop.val !== "clear");
|
|
44
|
-
const hasLeftIndentClear = clearPositions.some((p) => Math.abs(p - leftIndent) < 20);
|
|
45
|
-
const addsImplicitLeftIndent = leftIndent > 0 && !hasLeftIndentClear && !validExplicitStops.some((stop) => stop.pos <= leftIndent);
|
|
46
|
-
if (addsImplicitLeftIndent) stops.push({
|
|
47
|
-
val: "start",
|
|
48
|
-
pos: leftIndent,
|
|
49
|
-
leader: "none"
|
|
50
|
-
});
|
|
51
|
-
let pos = Math.floor(Math.max(maxExplicit, leftIndent) / defaultTabInterval) * defaultTabInterval;
|
|
52
|
-
const limitPos = leftIndent + 14400;
|
|
53
|
-
while (pos < limitPos) {
|
|
54
|
-
pos += defaultTabInterval;
|
|
55
|
-
let hasExplicitStop = false;
|
|
56
|
-
for (const stop of stops) if (Math.abs(stop.pos - pos) < 20) {
|
|
57
|
-
hasExplicitStop = true;
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
let hasClearStop = false;
|
|
61
|
-
for (const clearPos of clearPositions) if (Math.abs(clearPos - pos) < 20) {
|
|
62
|
-
hasClearStop = true;
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
const isAtLeftIndent = addsImplicitLeftIndent && Math.abs(pos - leftIndent) < 20;
|
|
66
|
-
if (!hasExplicitStop && !hasClearStop && !isAtLeftIndent) stops.push({
|
|
67
|
-
val: "start",
|
|
68
|
-
pos,
|
|
69
|
-
leader: "none"
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
return stops.toSorted((a, b) => a.pos - b.pos);
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Calculate the width of a tab character.
|
|
76
|
-
*
|
|
77
|
-
* Finds the next tab stop after the current position and computes the width
|
|
78
|
-
* needed to reach it. For `end`/`center`/`decimal` stops the content after the
|
|
79
|
-
* tab is anchored against the stop, so the caller supplies its measured width
|
|
80
|
-
* via {@link TabFollowingContent}.
|
|
81
|
-
*
|
|
82
|
-
* @param currentXPx - Current horizontal position in pixels (from the content
|
|
83
|
-
* area's left edge — the same origin tab stop positions are measured from)
|
|
84
|
-
* @param context - Tab context with stops and settings
|
|
85
|
-
* @param following - Measured widths of the content after the tab
|
|
86
|
-
* @returns Tab width result with width in pixels
|
|
87
|
-
*/
|
|
88
|
-
function calculateTabWidth(currentXPx, context, following = {}) {
|
|
89
|
-
const { defaultTabInterval = 720 } = context;
|
|
90
|
-
const currentXTwips = pixelsToTwips(currentXPx);
|
|
91
|
-
const nextStop = computeTabStops(context).find((stop) => stop.pos > currentXTwips + TAB_POSITION_EPSILON_TWIPS);
|
|
92
|
-
if (!nextStop) {
|
|
93
|
-
const defaultTabPx = twipsToPixels(defaultTabInterval);
|
|
94
|
-
let tabWidth = defaultTabPx - currentXPx % defaultTabPx;
|
|
95
|
-
if (tabWidth <= 0) tabWidth = defaultTabPx;
|
|
96
|
-
return {
|
|
97
|
-
width: tabWidth,
|
|
98
|
-
alignment: "default"
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
let width = twipsToPixels(nextStop.pos) - currentXPx;
|
|
102
|
-
if (nextStop.val === "center") width -= (following.followingWidth ?? 0) / 2;
|
|
103
|
-
else if (nextStop.val === "end") width -= following.followingWidth ?? 0;
|
|
104
|
-
else if (nextStop.val === "decimal") width -= following.decimalPrefixWidth ?? 0;
|
|
105
|
-
else if (nextStop.val === "bar") return {
|
|
106
|
-
width: 0,
|
|
107
|
-
...nextStop.leader !== void 0 ? { leader: nextStop.leader } : {},
|
|
108
|
-
alignment: "bar"
|
|
109
|
-
};
|
|
110
|
-
if (width <= 0) {
|
|
111
|
-
const defaultTabPx = twipsToPixels(defaultTabInterval);
|
|
112
|
-
let fallbackWidth = defaultTabPx - currentXPx % defaultTabPx;
|
|
113
|
-
if (fallbackWidth <= 0) fallbackWidth = defaultTabPx;
|
|
114
|
-
return {
|
|
115
|
-
width: fallbackWidth,
|
|
116
|
-
alignment: "default"
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
return {
|
|
120
|
-
width,
|
|
121
|
-
...nextStop.leader !== void 0 ? { leader: nextStop.leader } : {},
|
|
122
|
-
alignment: nextStop.val
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Calculate tab width with simple default stops
|
|
127
|
-
*
|
|
128
|
-
* Simplified version for basic tab rendering without explicit stops.
|
|
129
|
-
* Uses the default 0.5 inch (48px) tab interval.
|
|
130
|
-
*
|
|
131
|
-
* @param currentXPx - Current horizontal position in pixels
|
|
132
|
-
* @returns Width of the tab in pixels
|
|
133
|
-
*/
|
|
134
|
-
function calculateSimpleTabWidth(currentXPx) {
|
|
135
|
-
const defaultTabPx = twipsToPixels(720);
|
|
136
|
-
let tabWidth = defaultTabPx - currentXPx % defaultTabPx;
|
|
137
|
-
if (tabWidth < defaultTabPx / 4) tabWidth += defaultTabPx;
|
|
138
|
-
return tabWidth;
|
|
139
|
-
}
|
|
140
|
-
//#endregion
|
|
1
|
+
import { DEFAULT_TAB_INTERVAL_TWIPS, PIXELS_PER_INCH, TWIPS_PER_INCH, calculateSimpleTabWidth, calculateTabWidth, computeTabStops, pixelsToTwips, twipsToPixels } from "../../layout-engine/measure/tabCalculator.js";
|
|
141
2
|
export { DEFAULT_TAB_INTERVAL_TWIPS, PIXELS_PER_INCH, TWIPS_PER_INCH, calculateSimpleTabWidth, calculateTabWidth, computeTabStops, pixelsToTwips, twipsToPixels };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RemoteSelection } from "../types/remote-selection.js";
|
|
2
2
|
import { RenderedDomContext, RenderedDomPoint, RenderedDomRect } from "./RenderedDomContext.js";
|
|
3
3
|
//#region src/render-dom/RemoteSelectionOverlay.d.ts
|
|
4
4
|
type SyncRemoteSelectionOverlayOptions = {
|
|
5
5
|
pagesContainer: HTMLElement;
|
|
6
|
-
selections: readonly
|
|
6
|
+
selections: readonly RemoteSelection[];
|
|
7
7
|
zoom: number;
|
|
8
8
|
zIndex?: number;
|
|
9
9
|
renderedDomContext?: RenderedDomContext;
|
|
@@ -11,9 +11,9 @@ type SyncRemoteSelectionOverlayOptions = {
|
|
|
11
11
|
type RemoteSelectionOverlayGeometry = {
|
|
12
12
|
caret: RenderedDomPoint | null;
|
|
13
13
|
rects: RenderedDomRect[];
|
|
14
|
-
selection:
|
|
14
|
+
selection: RemoteSelection;
|
|
15
15
|
};
|
|
16
|
-
declare const resolveRemoteSelectionOverlayGeometry: (renderedDomContext: RenderedDomContext, selections: readonly
|
|
16
|
+
declare const resolveRemoteSelectionOverlayGeometry: (renderedDomContext: RenderedDomContext, selections: readonly RemoteSelection[]) => RemoteSelectionOverlayGeometry[];
|
|
17
17
|
/** Paint collaborative cursors and selections over the rendered page DOM. */
|
|
18
18
|
declare class RemoteSelectionOverlay {
|
|
19
19
|
clear(pagesContainer: HTMLElement): void;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { document_d_exports } from "./document.js";
|
|
1
2
|
//#region src/types/documentEnumValues.d.ts
|
|
3
|
+
declare const SHAPE_TEXT_ANCHOR_VALUES: readonly ["top", "middle", "bottom", "distributed", "justified"];
|
|
4
|
+
declare const normalizeShapeTextAnchor: (value: unknown) => document_d_exports.ShapeTextBody["anchor"] | undefined;
|
|
2
5
|
declare const THEME_COLOR_SLOT_VALUES: readonly ["dk1", "lt1", "dk2", "lt2", "accent1", "accent2", "accent3", "accent4", "accent5", "accent6", "hlink", "folHlink", "background1", "text1", "background2", "text2"];
|
|
3
6
|
declare const BORDER_STYLE_VALUES: readonly ["none", "single", "double", "dotted", "dashed", "thick", "triple", "thinThickSmallGap", "thickThinSmallGap", "thinThickMediumGap", "thickThinMediumGap", "thinThickLargeGap", "thickThinLargeGap", "wave", "doubleWave", "dashSmallGap", "dashDotStroked", "threeDEmboss", "threeDEngrave", "outset", "inset", "nil"];
|
|
4
7
|
declare const UNDERLINE_STYLE_VALUES: readonly ["none", "single", "words", "double", "thick", "dotted", "dottedHeavy", "dash", "dashedHeavy", "dashLong", "dashLongHeavy", "dotDash", "dashDotHeavy", "dotDotDash", "dashDotDotHeavy", "wave", "wavyHeavy", "wavyDouble"];
|
|
@@ -90,4 +93,4 @@ type OutlineStyleAttr = (typeof OUTLINE_STYLE_ATTR_VALUES)[number];
|
|
|
90
93
|
declare const NUMBER_FORMAT_VALUES: readonly ["decimal", "upperRoman", "lowerRoman", "upperLetter", "lowerLetter", "ordinal", "cardinalText", "ordinalText", "hex", "chicago", "ideographDigital", "japaneseCounting", "aiueo", "iroha", "decimalFullWidth", "decimalHalfWidth", "japaneseLegal", "japaneseDigitalTenThousand", "decimalEnclosedCircle", "decimalFullWidth2", "aiueoFullWidth", "irohaFullWidth", "decimalZero", "decimalZero3", "decimalZero4", "decimalZero5", "bullet", "ganada", "chosung", "decimalEnclosedFullstop", "decimalEnclosedParen", "decimalEnclosedCircleChinese", "ideographEnclosedCircle", "ideographTraditional", "ideographZodiac", "ideographZodiacTraditional", "taiwaneseCounting", "ideographLegalTraditional", "taiwaneseCountingThousand", "taiwaneseDigital", "chineseCounting", "chineseLegalSimplified", "chineseCountingThousand", "koreanDigital", "koreanCounting", "koreanLegal", "koreanDigital2", "vietnameseCounting", "russianLower", "russianUpper", "none", "numberInDash", "hebrew1", "hebrew2", "arabicAlpha", "arabicAbjad", "hindiVowels", "hindiConsonants", "hindiNumbers", "hindiCounting", "thaiLetters", "thaiNumbers", "thaiCounting"];
|
|
91
94
|
declare const LEVEL_SUFFIX_VALUES: readonly ["tab", "space", "nothing"];
|
|
92
95
|
//#endregion
|
|
93
|
-
export { BORDER_STYLE_VALUES, CONDITIONAL_STYLE_TYPE_VALUES, EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FLOATING_TABLE_X_SPEC_VALUES, FLOATING_TABLE_Y_SPEC_VALUES, FONT_HINT_VALUES, FONT_THEME_VALUES, FRAME_WRAP_VALUES, FRAME_X_ALIGN_VALUES, FRAME_Y_ALIGN_VALUES, HIGHLIGHT_COLOR_VALUES, IMAGE_HORIZONTAL_ALIGNMENT_VALUES, IMAGE_HORIZONTAL_RELATIVE_TO_VALUES, IMAGE_VERTICAL_ALIGNMENT_VALUES, IMAGE_VERTICAL_RELATIVE_TO_VALUES, IMAGE_WRAP_TEXT_VALUES, IMAGE_WRAP_TYPE_VALUES, LEVEL_SUFFIX_VALUES, LINE_SPACING_RULE_VALUES, NUMBER_FORMAT_VALUES, OUTLINE_STYLE_ATTR_VALUES, OUTLINE_STYLE_CSS_ALIASES, OUTLINE_STYLE_CSS_ALIAS_VALUES, OutlineStyleAttr, OutlineStyleCssAlias, PARAGRAPH_ALIGNMENT_VALUES, POSITIONAL_TAB_ALIGNMENT_VALUES, POSITIONAL_TAB_LEADER_VALUES, POSITIONAL_TAB_RELATIVE_TO_VALUES, SDT_LOCK_VALUES, SDT_TYPE_VALUES, SHADING_PATTERN_VALUES, SHAPE_OUTLINE_STYLE_VALUES, SHAPE_TYPE_VALUES, STYLE_TYPE_VALUES, TABLE_CELL_TEXT_DIRECTION_VALUES, TABLE_CELL_VERTICAL_ALIGNMENT_VALUES, TABLE_JUSTIFICATION_VALUES, TABLE_ROW_HEIGHT_RULE_VALUES, TABLE_WIDTH_TYPE_VALUES, TAB_LEADER_VALUES, TAB_STOP_ALIGNMENT_VALUES, TEXT_EFFECT_VALUES, THEME_COLOR_SLOT_VALUES, UNDERLINE_STYLE_VALUES };
|
|
96
|
+
export { BORDER_STYLE_VALUES, CONDITIONAL_STYLE_TYPE_VALUES, EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FLOATING_TABLE_X_SPEC_VALUES, FLOATING_TABLE_Y_SPEC_VALUES, FONT_HINT_VALUES, FONT_THEME_VALUES, FRAME_WRAP_VALUES, FRAME_X_ALIGN_VALUES, FRAME_Y_ALIGN_VALUES, HIGHLIGHT_COLOR_VALUES, IMAGE_HORIZONTAL_ALIGNMENT_VALUES, IMAGE_HORIZONTAL_RELATIVE_TO_VALUES, IMAGE_VERTICAL_ALIGNMENT_VALUES, IMAGE_VERTICAL_RELATIVE_TO_VALUES, IMAGE_WRAP_TEXT_VALUES, IMAGE_WRAP_TYPE_VALUES, LEVEL_SUFFIX_VALUES, LINE_SPACING_RULE_VALUES, NUMBER_FORMAT_VALUES, OUTLINE_STYLE_ATTR_VALUES, OUTLINE_STYLE_CSS_ALIASES, OUTLINE_STYLE_CSS_ALIAS_VALUES, OutlineStyleAttr, OutlineStyleCssAlias, PARAGRAPH_ALIGNMENT_VALUES, POSITIONAL_TAB_ALIGNMENT_VALUES, POSITIONAL_TAB_LEADER_VALUES, POSITIONAL_TAB_RELATIVE_TO_VALUES, SDT_LOCK_VALUES, SDT_TYPE_VALUES, SHADING_PATTERN_VALUES, SHAPE_OUTLINE_STYLE_VALUES, SHAPE_TEXT_ANCHOR_VALUES, SHAPE_TYPE_VALUES, STYLE_TYPE_VALUES, TABLE_CELL_TEXT_DIRECTION_VALUES, TABLE_CELL_VERTICAL_ALIGNMENT_VALUES, TABLE_JUSTIFICATION_VALUES, TABLE_ROW_HEIGHT_RULE_VALUES, TABLE_WIDTH_TYPE_VALUES, TAB_LEADER_VALUES, TAB_STOP_ALIGNMENT_VALUES, TEXT_EFFECT_VALUES, THEME_COLOR_SLOT_VALUES, UNDERLINE_STYLE_VALUES, normalizeShapeTextAnchor };
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
//#region src/types/documentEnumValues.ts
|
|
2
|
+
const SHAPE_TEXT_ANCHOR_VALUES = [
|
|
3
|
+
"top",
|
|
4
|
+
"middle",
|
|
5
|
+
"bottom",
|
|
6
|
+
"distributed",
|
|
7
|
+
"justified"
|
|
8
|
+
];
|
|
9
|
+
const normalizeShapeTextAnchor = (value) => {
|
|
10
|
+
if (value === "center") return "middle";
|
|
11
|
+
for (const anchor of SHAPE_TEXT_ANCHOR_VALUES) if (value === anchor) return anchor;
|
|
12
|
+
};
|
|
2
13
|
const THEME_COLOR_SLOT_VALUES = [
|
|
3
14
|
"dk1",
|
|
4
15
|
"lt1",
|
|
@@ -708,4 +719,4 @@ const LEVEL_SUFFIX_VALUES = [
|
|
|
708
719
|
"nothing"
|
|
709
720
|
];
|
|
710
721
|
//#endregion
|
|
711
|
-
export { BORDER_STYLE_VALUES, CONDITIONAL_STYLE_TYPE_VALUES, EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FLOATING_TABLE_X_SPEC_VALUES, FLOATING_TABLE_Y_SPEC_VALUES, FONT_HINT_VALUES, FONT_THEME_VALUES, FRAME_WRAP_VALUES, FRAME_X_ALIGN_VALUES, FRAME_Y_ALIGN_VALUES, HIGHLIGHT_COLOR_VALUES, IMAGE_HORIZONTAL_ALIGNMENT_VALUES, IMAGE_HORIZONTAL_RELATIVE_TO_VALUES, IMAGE_VERTICAL_ALIGNMENT_VALUES, IMAGE_VERTICAL_RELATIVE_TO_VALUES, IMAGE_WRAP_TEXT_VALUES, IMAGE_WRAP_TYPE_VALUES, LEVEL_SUFFIX_VALUES, LINE_SPACING_RULE_VALUES, NUMBER_FORMAT_VALUES, OUTLINE_STYLE_ATTR_VALUES, OUTLINE_STYLE_CSS_ALIASES, OUTLINE_STYLE_CSS_ALIAS_VALUES, PARAGRAPH_ALIGNMENT_VALUES, POSITIONAL_TAB_ALIGNMENT_VALUES, POSITIONAL_TAB_LEADER_VALUES, POSITIONAL_TAB_RELATIVE_TO_VALUES, SDT_LOCK_VALUES, SDT_TYPE_VALUES, SHADING_PATTERN_VALUES, SHAPE_OUTLINE_STYLE_VALUES, SHAPE_TYPE_VALUES, STYLE_TYPE_VALUES, TABLE_CELL_TEXT_DIRECTION_VALUES, TABLE_CELL_VERTICAL_ALIGNMENT_VALUES, TABLE_JUSTIFICATION_VALUES, TABLE_ROW_HEIGHT_RULE_VALUES, TABLE_WIDTH_TYPE_VALUES, TAB_LEADER_VALUES, TAB_STOP_ALIGNMENT_VALUES, TEXT_EFFECT_VALUES, THEME_COLOR_SLOT_VALUES, UNDERLINE_STYLE_VALUES };
|
|
722
|
+
export { BORDER_STYLE_VALUES, CONDITIONAL_STYLE_TYPE_VALUES, EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FLOATING_TABLE_X_SPEC_VALUES, FLOATING_TABLE_Y_SPEC_VALUES, FONT_HINT_VALUES, FONT_THEME_VALUES, FRAME_WRAP_VALUES, FRAME_X_ALIGN_VALUES, FRAME_Y_ALIGN_VALUES, HIGHLIGHT_COLOR_VALUES, IMAGE_HORIZONTAL_ALIGNMENT_VALUES, IMAGE_HORIZONTAL_RELATIVE_TO_VALUES, IMAGE_VERTICAL_ALIGNMENT_VALUES, IMAGE_VERTICAL_RELATIVE_TO_VALUES, IMAGE_WRAP_TEXT_VALUES, IMAGE_WRAP_TYPE_VALUES, LEVEL_SUFFIX_VALUES, LINE_SPACING_RULE_VALUES, NUMBER_FORMAT_VALUES, OUTLINE_STYLE_ATTR_VALUES, OUTLINE_STYLE_CSS_ALIASES, OUTLINE_STYLE_CSS_ALIAS_VALUES, PARAGRAPH_ALIGNMENT_VALUES, POSITIONAL_TAB_ALIGNMENT_VALUES, POSITIONAL_TAB_LEADER_VALUES, POSITIONAL_TAB_RELATIVE_TO_VALUES, SDT_LOCK_VALUES, SDT_TYPE_VALUES, SHADING_PATTERN_VALUES, SHAPE_OUTLINE_STYLE_VALUES, SHAPE_TEXT_ANCHOR_VALUES, SHAPE_TYPE_VALUES, STYLE_TYPE_VALUES, TABLE_CELL_TEXT_DIRECTION_VALUES, TABLE_CELL_VERTICAL_ALIGNMENT_VALUES, TABLE_JUSTIFICATION_VALUES, TABLE_ROW_HEIGHT_RULE_VALUES, TABLE_WIDTH_TYPE_VALUES, TAB_LEADER_VALUES, TAB_STOP_ALIGNMENT_VALUES, TEXT_EFFECT_VALUES, THEME_COLOR_SLOT_VALUES, UNDERLINE_STYLE_VALUES, normalizeShapeTextAnchor };
|
|
File without changes
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/types/remote-selection.d.ts
|
|
2
|
+
/** Serializable collaborator selection projected into ProseMirror positions. */
|
|
3
|
+
type RemoteSelection = {
|
|
4
|
+
anchor: number;
|
|
5
|
+
clientId: number;
|
|
6
|
+
color: string;
|
|
7
|
+
head: number;
|
|
8
|
+
name: string;
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
11
|
+
export { RemoteSelection };
|
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//#region src/types/wrap.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Wrap-type predicates shared across the layout pipeline.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors eigenpal docx-editor `wrapTypes` so that the new textBoxFlow
|
|
6
|
+
* predicates (eigenpal #474) and the existing image classifiers agree on
|
|
7
|
+
* which OOXML wrap variants are floating, which paint over body text, and
|
|
8
|
+
* which wrap around it.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* OOXML text-wrap variants for a drawing anchor (`wp:inline` vs the
|
|
12
|
+
* `wp:wrap*` elements). `inline` keeps the object in the text run; every other
|
|
13
|
+
* variant takes it out of inline flow. Mirrors the eigenpal docx-editor union
|
|
14
|
+
* so adapters share one wrap vocabulary.
|
|
15
|
+
*/
|
|
16
|
+
type WrapType = "inline" | "square" | "tight" | "through" | "topAndBottom" | "behind" | "inFront";
|
|
17
|
+
/**
|
|
18
|
+
* `true` for any wrap type that takes the object out of inline flow.
|
|
19
|
+
* Includes `topAndBottom` and the wrapNone variants (`behind`/`inFront`):
|
|
20
|
+
* those are anchored at absolute coordinates even though they don't shrink
|
|
21
|
+
* line widths.
|
|
22
|
+
*/
|
|
23
|
+
declare function isFloatingWrapType(wrapType: string | undefined): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* `true` for `behind` and `inFront` — wrapNone variants that don't shrink
|
|
26
|
+
* line widths; text paints over or under the object.
|
|
27
|
+
*/
|
|
28
|
+
declare function isWrapNone(wrapType: string | undefined): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* `true` for wrap types that actually divert lines around the object
|
|
31
|
+
* (`square` / `tight` / `through`). Excludes `topAndBottom` (block flow)
|
|
32
|
+
* and the wrapNone variants.
|
|
33
|
+
*/
|
|
34
|
+
declare function wrapsAroundText(wrapType: string | undefined): boolean;
|
|
35
|
+
//#endregion
|
|
36
|
+
export { WrapType, isFloatingWrapType, isWrapNone, wrapsAroundText };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//#region src/types/wrap.ts
|
|
2
|
+
const FLOATING_WRAP_TYPES = /* @__PURE__ */ new Set([
|
|
3
|
+
"square",
|
|
4
|
+
"tight",
|
|
5
|
+
"through",
|
|
6
|
+
"topAndBottom",
|
|
7
|
+
"behind",
|
|
8
|
+
"inFront"
|
|
9
|
+
]);
|
|
10
|
+
const TEXT_WRAPPING_TYPES = /* @__PURE__ */ new Set([
|
|
11
|
+
"square",
|
|
12
|
+
"tight",
|
|
13
|
+
"through"
|
|
14
|
+
]);
|
|
15
|
+
/**
|
|
16
|
+
* `true` for any wrap type that takes the object out of inline flow.
|
|
17
|
+
* Includes `topAndBottom` and the wrapNone variants (`behind`/`inFront`):
|
|
18
|
+
* those are anchored at absolute coordinates even though they don't shrink
|
|
19
|
+
* line widths.
|
|
20
|
+
*/
|
|
21
|
+
function isFloatingWrapType(wrapType) {
|
|
22
|
+
return wrapType !== void 0 && FLOATING_WRAP_TYPES.has(wrapType);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* `true` for `behind` and `inFront` — wrapNone variants that don't shrink
|
|
26
|
+
* line widths; text paints over or under the object.
|
|
27
|
+
*/
|
|
28
|
+
function isWrapNone(wrapType) {
|
|
29
|
+
return wrapType === "behind" || wrapType === "inFront";
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* `true` for wrap types that actually divert lines around the object
|
|
33
|
+
* (`square` / `tight` / `through`). Excludes `topAndBottom` (block flow)
|
|
34
|
+
* and the wrapNone variants.
|
|
35
|
+
*/
|
|
36
|
+
function wrapsAroundText(wrapType) {
|
|
37
|
+
return wrapType !== void 0 && TEXT_WRAPPING_TYPES.has(wrapType);
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { isFloatingWrapType, isWrapNone, wrapsAroundText };
|
|
@@ -47,7 +47,7 @@ declare const CJK_FALLBACK_FONT_FAMILY = "MS Mincho";
|
|
|
47
47
|
declare const setGoogleFontsEnabled: (enabled: boolean) => void;
|
|
48
48
|
declare const getGoogleFontsEnabled: () => boolean;
|
|
49
49
|
declare const setEmbeddedFontFamilyMap: (map: ReadonlyMap<string, string> | null) => void;
|
|
50
|
-
declare function resolveFontFamily(docxFontName: string): ResolvedFont;
|
|
50
|
+
declare function resolveFontFamily(docxFontName: string, alternateFontName?: string): ResolvedFont;
|
|
51
51
|
/**
|
|
52
52
|
* Split a CSS `font-family` list built by `quoteFontName` back into family
|
|
53
53
|
* names: the inverse of the quoting and escaping applied there. Quoted entries
|
|
@@ -813,7 +813,7 @@ let embeddedFontFamilyMap = null;
|
|
|
813
813
|
const setEmbeddedFontFamilyMap = (map) => {
|
|
814
814
|
embeddedFontFamilyMap = map;
|
|
815
815
|
};
|
|
816
|
-
function resolveFontFamily(docxFontName) {
|
|
816
|
+
function resolveFontFamily(docxFontName, alternateFontName) {
|
|
817
817
|
const normalizedName = docxFontName.trim().toLowerCase();
|
|
818
818
|
const scopedFamily = embeddedFontFamilyMap?.get(docxFontName) ?? null;
|
|
819
819
|
const aliasTarget = CJK_FONT_ALIASES[normalizedName];
|
|
@@ -821,13 +821,41 @@ function resolveFontFamily(docxFontName) {
|
|
|
821
821
|
if (mapping) {
|
|
822
822
|
const fallbackStack = aliasTarget && !mapping.fallbackStack.some((f) => f.toLowerCase() === normalizedName) ? [docxFontName, ...mapping.fallbackStack] : mapping.fallbackStack;
|
|
823
823
|
const primaryStack = scopedFamily ? [scopedFamily, ...fallbackStack.filter((f) => f.toLowerCase() !== normalizedName)] : fallbackStack;
|
|
824
|
-
return {
|
|
824
|
+
if (alternateFontName === void 0 || alternateFontName.trim().length === 0 || alternateFontName.trim().toLowerCase() === normalizedName) return {
|
|
825
825
|
googleFont: googleFontsEnabled ? mapping.googleFont : null,
|
|
826
826
|
cssFallback: withArabicFallback(primaryStack.map(quoteFontName).join(", ")),
|
|
827
827
|
originalFont: docxFontName,
|
|
828
828
|
hasGoogleEquivalent: googleFontsEnabled,
|
|
829
829
|
singleLineRatio: mapping.singleLineRatio
|
|
830
830
|
};
|
|
831
|
+
const alternate = resolveFontFamily(alternateFontName);
|
|
832
|
+
const category = mapping.category;
|
|
833
|
+
const concreteStack = dedupeFontFamilies([
|
|
834
|
+
scopedFamily ?? docxFontName,
|
|
835
|
+
...parseFontFamilyList(alternate.cssFallback),
|
|
836
|
+
...primaryStack
|
|
837
|
+
]).filter((family) => !isCssGenericFontFamily(family));
|
|
838
|
+
const cssFallback = withArabicFallback([...concreteStack, category].map(quoteFontName).join(", "));
|
|
839
|
+
return {
|
|
840
|
+
googleFont: googleFontsEnabled ? mapping.googleFont : null,
|
|
841
|
+
cssFallback,
|
|
842
|
+
originalFont: docxFontName,
|
|
843
|
+
hasGoogleEquivalent: googleFontsEnabled,
|
|
844
|
+
singleLineRatio: mapping.singleLineRatio
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
const alternateName = alternateFontName?.trim();
|
|
848
|
+
if (alternateName && alternateName.toLowerCase() !== normalizedName) {
|
|
849
|
+
const alternate = resolveFontFamily(alternateName);
|
|
850
|
+
const category = detectFontCategory(docxFontName);
|
|
851
|
+
const concreteStack = dedupeFontFamilies([scopedFamily ?? docxFontName, ...parseFontFamilyList(alternate.cssFallback)]).filter((family) => !isCssGenericFontFamily(family));
|
|
852
|
+
return {
|
|
853
|
+
googleFont: googleFontsEnabled ? alternate.googleFont : null,
|
|
854
|
+
cssFallback: withArabicFallback([...concreteStack, category].map(quoteFontName).join(", ")),
|
|
855
|
+
originalFont: docxFontName,
|
|
856
|
+
hasGoogleEquivalent: googleFontsEnabled && alternate.hasGoogleEquivalent,
|
|
857
|
+
singleLineRatio: alternate.singleLineRatio
|
|
858
|
+
};
|
|
831
859
|
}
|
|
832
860
|
const category = detectFontCategory(docxFontName);
|
|
833
861
|
const defaultFallback = DEFAULT_FALLBACKS[category];
|
|
@@ -839,6 +867,18 @@ function resolveFontFamily(docxFontName) {
|
|
|
839
867
|
singleLineRatio: DEFAULT_SINGLE_LINE_RATIO
|
|
840
868
|
};
|
|
841
869
|
}
|
|
870
|
+
const isCssGenericFontFamily = (family) => /^(?:sans-serif|serif|monospace|cursive|fantasy|system-ui)$/u.test(family.trim().toLowerCase());
|
|
871
|
+
const dedupeFontFamilies = (families) => {
|
|
872
|
+
const seen = /* @__PURE__ */ new Set();
|
|
873
|
+
const result = [];
|
|
874
|
+
for (const family of families) {
|
|
875
|
+
const normalized = family.trim().toLowerCase();
|
|
876
|
+
if (!normalized || seen.has(normalized)) continue;
|
|
877
|
+
seen.add(normalized);
|
|
878
|
+
result.push(family.trim());
|
|
879
|
+
}
|
|
880
|
+
return result;
|
|
881
|
+
};
|
|
842
882
|
const CSS_NEWLINE_ESCAPES = {
|
|
843
883
|
"\n": "\\a ",
|
|
844
884
|
"\r": "\\d ",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveColor, resolveHighlightToCss, resolveShadingColor } from "./colorResolver.js";
|
|
2
2
|
import { resolveFontFamily, resolveThemeFont } from "./fontResolver.js";
|
|
3
|
+
import { getHorizontalScaleFactor, normalizeHorizontalScalePercent } from "./horizontalScale.js";
|
|
3
4
|
import { AUTO_PARAGRAPH_SPACING_PX, eighthsToPixels, formatPx, halfPointsToPixels, halfPointsToPoints, twipsToPixels } from "./units.js";
|
|
4
5
|
//#region src/utils/formatToStyle.ts
|
|
5
6
|
/**
|
|
@@ -63,8 +64,9 @@ function textToStyle(formatting, theme) {
|
|
|
63
64
|
if (formatting.allCaps) style.textTransform = "uppercase";
|
|
64
65
|
else if (formatting.smallCaps) style.fontVariant = "small-caps";
|
|
65
66
|
if (formatting.spacing !== void 0 && formatting.spacing !== 0) style.letterSpacing = formatPx(twipsToPixels(formatting.spacing));
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
const horizontalScale = normalizeHorizontalScalePercent(formatting.scale);
|
|
68
|
+
if (horizontalScale !== void 0 && horizontalScale !== 100) {
|
|
69
|
+
style.transform = `scaleX(${getHorizontalScaleFactor(horizontalScale)})`;
|
|
68
70
|
style.display = "inline-block";
|
|
69
71
|
}
|
|
70
72
|
if (formatting.hidden) style.display = "none";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/utils/horizontalScale.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Normalize OOXML `w:w` horizontal text scale percentages.
|
|
4
|
+
*
|
|
5
|
+
* The OOXML value is bounded from 0% through 600%. Missing or malformed
|
|
6
|
+
* values use the document default (100%) and stay absent in the model.
|
|
7
|
+
*/
|
|
8
|
+
declare function normalizeHorizontalScalePercent(horizontalScale: number | null | undefined): number | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Parse the lexical forms accepted by OOXML `ST_TextScale`.
|
|
11
|
+
*
|
|
12
|
+
* Decimal values use the XML Schema integer grammar; percent values use the
|
|
13
|
+
* unsigned decimal grammar defined by `ST_TextScalePercent`. Surrounding
|
|
14
|
+
* whitespace is ignored, but an empty value or partial numeric prefix is not.
|
|
15
|
+
*/
|
|
16
|
+
declare function parseHorizontalScalePercent(horizontalScale: string | null | undefined): number | undefined;
|
|
17
|
+
declare function getHorizontalScaleFactor(horizontalScale: number | null | undefined): number;
|
|
18
|
+
/**
|
|
19
|
+
* Quantize an in-range model value for OOXML's integer-only representation.
|
|
20
|
+
*
|
|
21
|
+
* Imported documents use strict integer normalization. Serialization also
|
|
22
|
+
* tolerates finite floating-point residue produced by editing calculations,
|
|
23
|
+
* preserving the existing integer-attribute contract without emitting an
|
|
24
|
+
* invalid fractional value.
|
|
25
|
+
*/
|
|
26
|
+
declare function roundHorizontalScalePercentForSerialization(horizontalScale: number | null | undefined): number | undefined;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { getHorizontalScaleFactor, normalizeHorizontalScalePercent, parseHorizontalScalePercent, roundHorizontalScalePercentForSerialization };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//#region src/utils/horizontalScale.ts
|
|
2
|
+
/**
|
|
3
|
+
* Normalize OOXML `w:w` horizontal text scale percentages.
|
|
4
|
+
*
|
|
5
|
+
* The OOXML value is bounded from 0% through 600%. Missing or malformed
|
|
6
|
+
* values use the document default (100%) and stay absent in the model.
|
|
7
|
+
*/
|
|
8
|
+
function normalizeHorizontalScalePercent(horizontalScale) {
|
|
9
|
+
if (horizontalScale === null || horizontalScale === void 0 || !Number.isFinite(horizontalScale) || !Number.isInteger(horizontalScale) || horizontalScale < 0 || horizontalScale > 600) return;
|
|
10
|
+
return horizontalScale;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Parse the lexical forms accepted by OOXML `ST_TextScale`.
|
|
14
|
+
*
|
|
15
|
+
* Decimal values use the XML Schema integer grammar; percent values use the
|
|
16
|
+
* unsigned decimal grammar defined by `ST_TextScalePercent`. Surrounding
|
|
17
|
+
* whitespace is ignored, but an empty value or partial numeric prefix is not.
|
|
18
|
+
*/
|
|
19
|
+
function parseHorizontalScalePercent(horizontalScale) {
|
|
20
|
+
if (horizontalScale === null || horizontalScale === void 0) return;
|
|
21
|
+
const lexicalValue = horizontalScale.trim();
|
|
22
|
+
if (!/^(?:[+-]?\d+|\d+%)$/u.test(lexicalValue)) return;
|
|
23
|
+
const numericValue = lexicalValue.endsWith("%") ? lexicalValue.slice(0, -1) : lexicalValue;
|
|
24
|
+
return normalizeHorizontalScalePercent(Number(numericValue));
|
|
25
|
+
}
|
|
26
|
+
function getHorizontalScaleFactor(horizontalScale) {
|
|
27
|
+
return (normalizeHorizontalScalePercent(horizontalScale) ?? 100) / 100;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Quantize an in-range model value for OOXML's integer-only representation.
|
|
31
|
+
*
|
|
32
|
+
* Imported documents use strict integer normalization. Serialization also
|
|
33
|
+
* tolerates finite floating-point residue produced by editing calculations,
|
|
34
|
+
* preserving the existing integer-attribute contract without emitting an
|
|
35
|
+
* invalid fractional value.
|
|
36
|
+
*/
|
|
37
|
+
function roundHorizontalScalePercentForSerialization(horizontalScale) {
|
|
38
|
+
if (horizontalScale === null || horizontalScale === void 0 || !Number.isFinite(horizontalScale) || horizontalScale < 0 || horizontalScale > 600) return;
|
|
39
|
+
return Math.round(horizontalScale);
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { getHorizontalScaleFactor, normalizeHorizontalScalePercent, parseHorizontalScalePercent, roundHorizontalScalePercentForSerialization };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stll/folio-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "Headless, framework-neutral core of folio: the OOXML (.docx) parser, document model, ProseMirror integration, and page-layout engine. No React.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"document-model",
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
"perf": "bun scripts/profile-editor.ts"
|
|
114
114
|
},
|
|
115
115
|
"dependencies": {
|
|
116
|
-
"@stll/docx-core": "^0.17.
|
|
116
|
+
"@stll/docx-core": "^0.17.3",
|
|
117
117
|
"@stll/docx-utils": "^0.1.0",
|
|
118
118
|
"@stll/template-conditions": "^0.1.0",
|
|
119
119
|
"better-result": "3.0.1",
|