@stll/folio-core 0.22.1 → 0.22.3
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/ai-edits/headless.d.ts +6 -6
- package/dist/ai-edits/headless.js +2 -2
- package/dist/content-controls/errors.d.ts +17 -17
- package/dist/content-controls/errors.js +3 -3
- package/dist/controller/layoutPipeline.js +28 -0
- package/dist/document-operations.d.ts +6 -6
- package/dist/document-operations.js +2 -2
- package/dist/docx/encryption/errors.d.ts +3 -3
- package/dist/docx/encryption/errors.js +1 -1
- package/dist/docx/ensureParaIds.d.ts +4 -4
- package/dist/docx/ensureParaIds.js +1 -1
- package/dist/docx/imageParser.js +6 -9
- package/dist/docx/metadataPrivacy.d.ts +6 -6
- package/dist/docx/metadataPrivacy.js +2 -2
- package/dist/docx/parser.d.ts +5 -5
- package/dist/docx/parser.js +1 -1
- package/dist/docx/server/applyDocxXmlPatchProposal.d.ts +6 -6
- package/dist/docx/server/applyDocxXmlPatchProposal.js +2 -2
- package/dist/docx/server/boundedArchive.d.ts +4 -4
- package/dist/docx/server/boundedArchive.js +1 -1
- package/dist/docx/server/build.d.ts +5 -5
- package/dist/docx/server/build.js +1 -1
- package/dist/docx/server/createBilingualDocument.d.ts +3 -3
- package/dist/docx/server/createBilingualDocument.js +1 -1
- package/dist/docx/server/evaluateDocxXmlPatchProposal.d.ts +6 -6
- package/dist/docx/server/evaluateDocxXmlPatchProposal.js +2 -2
- package/dist/docx/server/inspectDocxPackage.d.ts +3 -3
- package/dist/docx/server/inspectDocxPackage.js +9 -8
- package/dist/docx/styleParser.js +16 -16
- package/dist/docx/xmlParser.d.ts +7 -3
- package/dist/docx/xmlParser.js +20 -7
- package/dist/docx/xmlResourceLimits.d.ts +4 -4
- package/dist/docx/xmlResourceLimits.js +1 -1
- package/dist/layout-engine/measure/font-metrics.worker.js +1 -1
- package/dist/layout-engine/measure/measureHelpers.d.ts +2 -1
- package/dist/layout-engine/measure/measureHelpers.js +5 -2
- package/dist/layout-engine/types.d.ts +6 -0
- package/dist/layout-painter/anchoredImagePosition.d.ts +2 -1
- package/dist/layout-painter/anchoredImagePosition.js +14 -6
- package/dist/layout-painter/renderPage.js +11 -1
- package/dist/layout-painter/renderParagraph.js +3 -4
- package/dist/prosemirror/attrs/index.js +6 -5
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +1 -1
- package/dist/prosemirror/commands/propertyChangeScope.js +1 -0
- package/dist/prosemirror/conversion/fromProseDoc.js +13 -3
- package/dist/prosemirror/conversion/toProseDoc.js +1 -0
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +30 -3
- package/dist/prosemirror/extensions/marks/UnderlineExtension.js +9 -7
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +2 -1
- package/dist/prosemirror/extensions/marks/markUtils.js +92 -16
- package/dist/prosemirror/plugins/selectionTracker.js +10 -5
- package/dist/prosemirror/schema/nodes.d.ts +1 -0
- package/dist/prosemirror/selectionMarks.d.ts +11 -0
- package/dist/prosemirror/selectionMarks.js +19 -0
- package/dist/prosemirror/selectionState.js +8 -20
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
- package/dist/redline.d.ts +4 -4
- package/dist/redline.js +1 -1
- package/dist/utils/formatToStyle.js +1 -1
- package/dist/version-comparison.d.ts +3 -3
- package/dist/version-comparison.js +1 -1
- package/package.json +3 -3
package/dist/docx/styleParser.js
CHANGED
|
@@ -2,7 +2,7 @@ import { mergeParagraphFormatting } from "../utils/paragraphFormattingMerge.js";
|
|
|
2
2
|
import { mergeTextFormatting } from "../utils/textFormattingMerge.js";
|
|
3
3
|
import { BorderStyleSchema, ConditionalStyleTypeSchema, EmphasisMarkSchema, FontHintSchema, FontThemeSchema, HighlightColorSchema, LineSpacingRuleSchema, ParagraphAlignmentSchema, ShadingPatternSchema, StyleTypeSchema, TabLeaderSchema, TabStopAlignmentSchema, TableCellTextDirectionSchema, TableRowHeightRuleSchema, TableWidthTypeSchema, TextEffectSchema, ThemeColorSlotSchema, UnderlineStyleSchema, narrowEnum } from "./parserEnums.js";
|
|
4
4
|
import { resolveThemeFontRef } from "./themeParser.js";
|
|
5
|
-
import { findChild, findChildren, getAttribute, getLocalName, parseBooleanElement, parseNumberingLevelAttribute, parseNumericAttribute, parseTableMeasurementValue, parseXmlDocument } from "./xmlParser.js";
|
|
5
|
+
import { findChild, findChildren, getAttribute, getLocalName, parseBooleanElement, parseNumberingLevelAttribute, parseNumericAttribute, parseOnOffValue, parseTableMeasurementValue, parseXmlDocument } from "./xmlParser.js";
|
|
6
6
|
//#region src/docx/styleParser.ts
|
|
7
7
|
/**
|
|
8
8
|
* Parse text formatting properties (w:rPr)
|
|
@@ -224,9 +224,9 @@ function parseBorderSpec(border) {
|
|
|
224
224
|
const space = parseNumericAttribute(border, "w", "space");
|
|
225
225
|
if (space !== void 0) spec.space = space;
|
|
226
226
|
const shadowAttr = getAttribute(border, "w", "shadow");
|
|
227
|
-
if (shadowAttr) spec.shadow = shadowAttr
|
|
227
|
+
if (shadowAttr) spec.shadow = parseOnOffValue(shadowAttr) ?? false;
|
|
228
228
|
const frame = getAttribute(border, "w", "frame");
|
|
229
|
-
if (frame) spec.frame = frame
|
|
229
|
+
if (frame) spec.frame = parseOnOffValue(frame) ?? false;
|
|
230
230
|
return spec;
|
|
231
231
|
}
|
|
232
232
|
/**
|
|
@@ -278,9 +278,9 @@ function parseParagraphProperties(pPr, theme) {
|
|
|
278
278
|
const lineRule = narrowEnum(getAttribute(spacing, "w", "lineRule"), LineSpacingRuleSchema);
|
|
279
279
|
if (lineRule) formatting.lineSpacingRule = lineRule;
|
|
280
280
|
const beforeAuto = getAttribute(spacing, "w", "beforeAutospacing");
|
|
281
|
-
if (beforeAuto) formatting.beforeAutospacing = beforeAuto
|
|
281
|
+
if (beforeAuto) formatting.beforeAutospacing = parseOnOffValue(beforeAuto) ?? false;
|
|
282
282
|
const afterAuto = getAttribute(spacing, "w", "afterAutospacing");
|
|
283
|
-
if (afterAuto) formatting.afterAutospacing = afterAuto
|
|
283
|
+
if (afterAuto) formatting.afterAutospacing = parseOnOffValue(afterAuto) ?? false;
|
|
284
284
|
}
|
|
285
285
|
const ind = findChild(pPr, "w", "ind");
|
|
286
286
|
if (ind) {
|
|
@@ -452,17 +452,17 @@ function parseTableLook(tblLook) {
|
|
|
452
452
|
}
|
|
453
453
|
}
|
|
454
454
|
const firstColumn = getAttribute(tblLook, "w", "firstColumn");
|
|
455
|
-
if (firstColumn) look.firstColumn = firstColumn
|
|
455
|
+
if (firstColumn) look.firstColumn = parseOnOffValue(firstColumn) ?? false;
|
|
456
456
|
const firstRow = getAttribute(tblLook, "w", "firstRow");
|
|
457
|
-
if (firstRow) look.firstRow = firstRow
|
|
457
|
+
if (firstRow) look.firstRow = parseOnOffValue(firstRow) ?? false;
|
|
458
458
|
const lastColumn = getAttribute(tblLook, "w", "lastColumn");
|
|
459
|
-
if (lastColumn) look.lastColumn = lastColumn
|
|
459
|
+
if (lastColumn) look.lastColumn = parseOnOffValue(lastColumn) ?? false;
|
|
460
460
|
const lastRow = getAttribute(tblLook, "w", "lastRow");
|
|
461
|
-
if (lastRow) look.lastRow = lastRow
|
|
461
|
+
if (lastRow) look.lastRow = parseOnOffValue(lastRow) ?? false;
|
|
462
462
|
const noHBand = getAttribute(tblLook, "w", "noHBand");
|
|
463
|
-
if (noHBand) look.noHBand = noHBand
|
|
463
|
+
if (noHBand) look.noHBand = parseOnOffValue(noHBand) ?? false;
|
|
464
464
|
const noVBand = getAttribute(tblLook, "w", "noVBand");
|
|
465
|
-
if (noVBand) look.noVBand = noVBand
|
|
465
|
+
if (noVBand) look.noVBand = parseOnOffValue(noVBand) ?? false;
|
|
466
466
|
return Object.keys(look).length > 0 ? look : void 0;
|
|
467
467
|
}
|
|
468
468
|
/**
|
|
@@ -669,7 +669,7 @@ function parseStyle(styleEl, theme) {
|
|
|
669
669
|
type: narrowEnum(rawType, StyleTypeSchema) ?? "paragraph"
|
|
670
670
|
};
|
|
671
671
|
const defaultAttr = getAttribute(styleEl, "w", "default");
|
|
672
|
-
if (defaultAttr) style.default = defaultAttr
|
|
672
|
+
if (defaultAttr) style.default = parseOnOffValue(defaultAttr) ?? false;
|
|
673
673
|
const children = collectStyleChildren(styleEl);
|
|
674
674
|
const nameEl = children.name;
|
|
675
675
|
if (nameEl) {
|
|
@@ -871,10 +871,10 @@ function parseStyleDefinitionsFromDocument(doc, theme, resolvedStyles) {
|
|
|
871
871
|
const latentStylesEl = findChild(doc, "w", "latentStyles");
|
|
872
872
|
if (latentStylesEl) {
|
|
873
873
|
const latentStyles = {
|
|
874
|
-
defLockedState: getAttribute(latentStylesEl, "w", "defLockedState")
|
|
875
|
-
defSemiHidden: getAttribute(latentStylesEl, "w", "defSemiHidden")
|
|
876
|
-
defUnhideWhenUsed: getAttribute(latentStylesEl, "w", "defUnhideWhenUsed")
|
|
877
|
-
defQFormat: getAttribute(latentStylesEl, "w", "defQFormat")
|
|
874
|
+
defLockedState: parseOnOffValue(getAttribute(latentStylesEl, "w", "defLockedState")) ?? false,
|
|
875
|
+
defSemiHidden: parseOnOffValue(getAttribute(latentStylesEl, "w", "defSemiHidden")) ?? false,
|
|
876
|
+
defUnhideWhenUsed: parseOnOffValue(getAttribute(latentStylesEl, "w", "defUnhideWhenUsed")) ?? false,
|
|
877
|
+
defQFormat: parseOnOffValue(getAttribute(latentStylesEl, "w", "defQFormat")) ?? false
|
|
878
878
|
};
|
|
879
879
|
const defUIPriority = parseNumericAttribute(latentStylesEl, "w", "defUIPriority");
|
|
880
880
|
if (defUIPriority !== void 0) latentStyles.defUIPriority = defUIPriority;
|
package/dist/docx/xmlParser.d.ts
CHANGED
|
@@ -272,13 +272,17 @@ declare function parseNumberingLevelAttribute(element: XmlElement | null | undef
|
|
|
272
272
|
* (`5000`); normalize those to the ECMA-376 unit the layout engine expects.
|
|
273
273
|
*/
|
|
274
274
|
declare function parseTableMeasurementValue(element: XmlElement | null | undefined, widthType: string): number | undefined;
|
|
275
|
+
/**
|
|
276
|
+
* Parse an OOXML `ST_OnOff` lexical value.
|
|
277
|
+
*/
|
|
278
|
+
declare function parseOnOffValue(value: string | null | undefined): boolean | undefined;
|
|
275
279
|
/**
|
|
276
280
|
* Parse a boolean value from an attribute or element presence
|
|
277
281
|
*
|
|
278
282
|
* OOXML boolean conventions:
|
|
279
283
|
* - Element presence with no val attribute = true
|
|
280
|
-
* - w:val="true" or w:val="
|
|
281
|
-
* - w:val="false" or w:val="
|
|
284
|
+
* - w:val="true", w:val="1", or w:val="on" = true
|
|
285
|
+
* - w:val="false", w:val="0", or w:val="off" = false
|
|
282
286
|
*
|
|
283
287
|
* @param element - Element to check
|
|
284
288
|
* @param namespace - Namespace for val attribute
|
|
@@ -334,4 +338,4 @@ declare function mergeXmlnsDeclarations(inherited: Record<string, string>, eleme
|
|
|
334
338
|
*/
|
|
335
339
|
declare function cloneWithXmlnsDeclarations(element: XmlElement, xmlnsDecls: Record<string, string>): XmlElement;
|
|
336
340
|
//#endregion
|
|
337
|
-
export { NAMESPACES, WORDPROCESSINGML_NAMESPACE_URIS, XmlElement, XmlNamespaceScope, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findByFullName, findChild, findChildByLocalName, findChildByNamespaceUri, findChildren, findChildrenByLocalName, findDeep, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributeByNamespaceUri, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, getTextContent, hasChild, hasFlag, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumberingLevelAttribute, parseNumericAttribute, parseTableMeasurementValue, parseXml, parseXmlDocument };
|
|
341
|
+
export { NAMESPACES, WORDPROCESSINGML_NAMESPACE_URIS, XmlElement, XmlNamespaceScope, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findByFullName, findChild, findChildByLocalName, findChildByNamespaceUri, findChildren, findChildrenByLocalName, findDeep, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributeByNamespaceUri, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, getTextContent, hasChild, hasFlag, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumberingLevelAttribute, parseNumericAttribute, parseOnOffValue, parseTableMeasurementValue, parseXml, parseXmlDocument };
|
package/dist/docx/xmlParser.js
CHANGED
|
@@ -438,8 +438,7 @@ function getTextContent(element) {
|
|
|
438
438
|
function hasFlag(element, namespace, name) {
|
|
439
439
|
const value = getAttribute(element, namespace, name);
|
|
440
440
|
if (value === null) return false;
|
|
441
|
-
|
|
442
|
-
return true;
|
|
441
|
+
return parseOnOffValue(value) ?? true;
|
|
443
442
|
}
|
|
444
443
|
/**
|
|
445
444
|
* Check if a child element exists (used for boolean flags in OOXML)
|
|
@@ -515,12 +514,27 @@ function parseTableMeasurementValue(element, widthType) {
|
|
|
515
514
|
return Number.isNaN(num) ? void 0 : num;
|
|
516
515
|
}
|
|
517
516
|
/**
|
|
517
|
+
* Parse an OOXML `ST_OnOff` lexical value.
|
|
518
|
+
*/
|
|
519
|
+
function parseOnOffValue(value) {
|
|
520
|
+
if (value === null || value === void 0) return;
|
|
521
|
+
switch (value) {
|
|
522
|
+
case "1":
|
|
523
|
+
case "true":
|
|
524
|
+
case "on": return true;
|
|
525
|
+
case "0":
|
|
526
|
+
case "false":
|
|
527
|
+
case "off": return false;
|
|
528
|
+
default: return;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
518
532
|
* Parse a boolean value from an attribute or element presence
|
|
519
533
|
*
|
|
520
534
|
* OOXML boolean conventions:
|
|
521
535
|
* - Element presence with no val attribute = true
|
|
522
|
-
* - w:val="true" or w:val="
|
|
523
|
-
* - w:val="false" or w:val="
|
|
536
|
+
* - w:val="true", w:val="1", or w:val="on" = true
|
|
537
|
+
* - w:val="false", w:val="0", or w:val="off" = false
|
|
524
538
|
*
|
|
525
539
|
* @param element - Element to check
|
|
526
540
|
* @param namespace - Namespace for val attribute
|
|
@@ -541,8 +555,7 @@ function parseBooleanElement(element, namespace = "w") {
|
|
|
541
555
|
}
|
|
542
556
|
}
|
|
543
557
|
if (val === null) return true;
|
|
544
|
-
|
|
545
|
-
return true;
|
|
558
|
+
return parseOnOffValue(val) ?? true;
|
|
546
559
|
}
|
|
547
560
|
/**
|
|
548
561
|
* Deep find - search recursively for an element
|
|
@@ -699,4 +712,4 @@ function cloneWithXmlnsDeclarations(element, xmlnsDecls) {
|
|
|
699
712
|
return element;
|
|
700
713
|
}
|
|
701
714
|
//#endregion
|
|
702
|
-
export { NAMESPACES, WORDPROCESSINGML_NAMESPACE_URIS, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findByFullName, findChild, findChildByLocalName, findChildByNamespaceUri, findChildren, findChildrenByLocalName, findDeep, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributeByNamespaceUri, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, getTextContent, hasChild, hasFlag, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumberingLevelAttribute, parseNumericAttribute, parseTableMeasurementValue, parseXml, parseXmlDocument };
|
|
715
|
+
export { NAMESPACES, WORDPROCESSINGML_NAMESPACE_URIS, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findByFullName, findChild, findChildByLocalName, findChildByNamespaceUri, findChildren, findChildrenByLocalName, findDeep, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributeByNamespaceUri, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, getTextContent, hasChild, hasFlag, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumberingLevelAttribute, parseNumericAttribute, parseOnOffValue, parseTableMeasurementValue, parseXml, parseXmlDocument };
|
|
@@ -11,12 +11,12 @@ type XmlResourceLimits = {
|
|
|
11
11
|
maxNodes: number;
|
|
12
12
|
};
|
|
13
13
|
type XmlResourceLimitKind = "bytes" | "depth" | "nodes" | "syntax";
|
|
14
|
-
declare const XmlResourceLimitError_base: import("better-result").TaggedErrorClass<"XmlResourceLimitError"
|
|
14
|
+
declare const XmlResourceLimitError_base: import("better-result").TaggedErrorClass<"XmlResourceLimitError">;
|
|
15
|
+
/** XML input exceeded a parser resource bound or could not be scanned safely. */
|
|
16
|
+
declare class XmlResourceLimitError extends XmlResourceLimitError_base<{
|
|
15
17
|
message: string;
|
|
16
18
|
limit: XmlResourceLimitKind;
|
|
17
|
-
}
|
|
18
|
-
/** XML input exceeded a parser resource bound or could not be scanned safely. */
|
|
19
|
-
declare class XmlResourceLimitError extends XmlResourceLimitError_base {}
|
|
19
|
+
}> {}
|
|
20
20
|
/**
|
|
21
21
|
* Bound XML bytes, element count, and nesting before building an object tree.
|
|
22
22
|
* The lexical scan is iterative, so deeply nested input cannot consume the JS
|
|
@@ -6,7 +6,7 @@ const FOLIO_XML_RESOURCE_LIMITS = {
|
|
|
6
6
|
maxNodes: 1e6
|
|
7
7
|
};
|
|
8
8
|
/** XML input exceeded a parser resource bound or could not be scanned safely. */
|
|
9
|
-
var XmlResourceLimitError = class extends TaggedError("XmlResourceLimitError")
|
|
9
|
+
var XmlResourceLimitError = class extends TaggedError("XmlResourceLimitError") {};
|
|
10
10
|
const exceedsUtf8ByteLimit = (value, maxBytes) => {
|
|
11
11
|
let bytes = 0;
|
|
12
12
|
for (let index = 0; index < value.length; index += 1) {
|
|
@@ -17,7 +17,7 @@ import { TaggedError } from "better-result";
|
|
|
17
17
|
* that touches `document`, `window`, or React. The protocol module is
|
|
18
18
|
* deliberately dependency-free.
|
|
19
19
|
*/
|
|
20
|
-
var WorkerInitError = class extends TaggedError("WorkerInitError")
|
|
20
|
+
var WorkerInitError = class extends TaggedError("WorkerInitError") {};
|
|
21
21
|
let ctx = null;
|
|
22
22
|
let initError = null;
|
|
23
23
|
const FONT_FINGERPRINT_EPSILON = .01;
|
|
@@ -3,6 +3,7 @@ import { FontStyle } from "./measureTypes.js";
|
|
|
3
3
|
//#region src/layout-engine/measure/measureHelpers.d.ts
|
|
4
4
|
declare const DEFAULT_FONT_SIZE = 11;
|
|
5
5
|
declare const DEFAULT_FONT_FAMILY = "Calibri";
|
|
6
|
+
declare const DOCX_SCRIPT_FONT_SCALE = 0.75;
|
|
6
7
|
/**
|
|
7
8
|
* Build a measurement `FontStyle` from a run's formatting. Single source of
|
|
8
9
|
* truth for run → FontStyle so every measurement path (layout line-breaking,
|
|
@@ -87,4 +88,4 @@ declare function halfPtToPx(halfPt: number): number;
|
|
|
87
88
|
*/
|
|
88
89
|
declare function pxToHalfPt(px: number): number;
|
|
89
90
|
//#endregion
|
|
90
|
-
export { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, buildFontString, buildRunFontStyle, clearFontResolvedCache, findCharacterAtX, getResolvedData, getXForCharacter, halfPtToPx, ptToPx, pxToHalfPt, pxToPt, pxToTwips, twipsToPx };
|
|
91
|
+
export { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, DOCX_SCRIPT_FONT_SCALE, buildFontString, buildRunFontStyle, clearFontResolvedCache, findCharacterAtX, getResolvedData, getXForCharacter, halfPtToPx, ptToPx, pxToHalfPt, pxToPt, pxToTwips, twipsToPx };
|
|
@@ -9,12 +9,14 @@ import { FONT_KERNING_MODE, getRunFontKerningMode } from "./textMeasurementPolic
|
|
|
9
9
|
* char-offset geometry. The layout engine imports these directly so it never
|
|
10
10
|
* transitively pulls the canvas measurement backend; the canvas implementation
|
|
11
11
|
* (`measureContainer.ts`) consumes them too.
|
|
12
|
+
* Script sizing: eigenpal/docx-editor@585413d0 (Apache-2.0), modified for Folio.
|
|
12
13
|
*/
|
|
13
14
|
const TWIPS_PER_INCH = 1440;
|
|
14
15
|
const PX_PER_INCH = 96;
|
|
15
16
|
const TWIPS_PER_PX = TWIPS_PER_INCH / PX_PER_INCH;
|
|
16
17
|
const DEFAULT_FONT_SIZE = 11;
|
|
17
18
|
const DEFAULT_FONT_FAMILY = "Calibri";
|
|
19
|
+
const DOCX_SCRIPT_FONT_SCALE = .75;
|
|
18
20
|
/**
|
|
19
21
|
* Build a measurement `FontStyle` from a run's formatting. Single source of
|
|
20
22
|
* truth for run → FontStyle so every measurement path (layout line-breaking,
|
|
@@ -24,7 +26,8 @@ const DEFAULT_FONT_FAMILY = "Calibri";
|
|
|
24
26
|
* and size fallbacks for runs that declare neither.
|
|
25
27
|
*/
|
|
26
28
|
function buildRunFontStyle(run, fallbackFontFamily, fallbackFontSize) {
|
|
27
|
-
const
|
|
29
|
+
const baseFontSize = run.fontSize ?? fallbackFontSize;
|
|
30
|
+
const fontSize = run.superscript || run.subscript ? baseFontSize * DOCX_SCRIPT_FONT_SCALE : baseFontSize;
|
|
28
31
|
return {
|
|
29
32
|
fontFamily: run.fontFamily ?? fallbackFontFamily,
|
|
30
33
|
...run.eastAsiaFontFamily !== void 0 ? { eastAsiaFontFamily: run.eastAsiaFontFamily } : {},
|
|
@@ -168,4 +171,4 @@ function pxToHalfPt(px) {
|
|
|
168
171
|
return pxToPt(px) * 2;
|
|
169
172
|
}
|
|
170
173
|
//#endregion
|
|
171
|
-
export { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, buildFontString, buildRunFontStyle, clearFontResolvedCache, findCharacterAtX, getResolvedData, getXForCharacter, halfPtToPx, ptToPx, pxToHalfPt, pxToPt, pxToTwips, twipsToPx };
|
|
174
|
+
export { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, DOCX_SCRIPT_FONT_SCALE, buildFontString, buildRunFontStyle, clearFontResolvedCache, findCharacterAtX, getResolvedData, getXForCharacter, halfPtToPx, ptToPx, pxToHalfPt, pxToPt, pxToTwips, twipsToPx };
|
|
@@ -1063,6 +1063,12 @@ type Page = {
|
|
|
1063
1063
|
fragments: Fragment[];
|
|
1064
1064
|
/** Page margins. */
|
|
1065
1065
|
margins: PageMargins;
|
|
1066
|
+
/**
|
|
1067
|
+
* Authored section margins before header/footer clearance expands the body
|
|
1068
|
+
* content box insets. Page-margin-relative anchors use these page-setup
|
|
1069
|
+
* landmarks instead of furniture-expanded content insets.
|
|
1070
|
+
*/
|
|
1071
|
+
authoredMargins?: PageMargins;
|
|
1066
1072
|
/** Page size (width, height). */
|
|
1067
1073
|
size: {
|
|
1068
1074
|
w: number;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ImageRun } from "../layout-engine/types.js";
|
|
1
|
+
import { ImageRun, PageMargins } from "../layout-engine/types.js";
|
|
2
2
|
//#region src/layout-painter/anchoredImagePosition.d.ts
|
|
3
3
|
type PageGeometry = {
|
|
4
4
|
pageWidth: number;
|
|
@@ -7,6 +7,7 @@ type PageGeometry = {
|
|
|
7
7
|
marginTop: number;
|
|
8
8
|
marginRight: number;
|
|
9
9
|
marginBottom: number;
|
|
10
|
+
authoredMargins?: PageMargins;
|
|
10
11
|
contentWidth: number;
|
|
11
12
|
contentHeight: number;
|
|
12
13
|
};
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { emuToPixels } from "./renderUtils.js";
|
|
2
2
|
//#region src/layout-painter/anchoredImagePosition.ts
|
|
3
|
+
const authoredMargins = (geometry) => geometry.authoredMargins ?? {
|
|
4
|
+
top: geometry.marginTop,
|
|
5
|
+
right: geometry.marginRight,
|
|
6
|
+
bottom: geometry.marginBottom,
|
|
7
|
+
left: geometry.marginLeft
|
|
8
|
+
};
|
|
3
9
|
function resolveHorizontalBand(relativeTo, geometry) {
|
|
10
|
+
const margins = authoredMargins(geometry);
|
|
4
11
|
switch (relativeTo) {
|
|
5
12
|
case "page": return {
|
|
6
13
|
baseX: -geometry.marginLeft,
|
|
@@ -9,12 +16,12 @@ function resolveHorizontalBand(relativeTo, geometry) {
|
|
|
9
16
|
case "leftMargin":
|
|
10
17
|
case "insideMargin": return {
|
|
11
18
|
baseX: -geometry.marginLeft,
|
|
12
|
-
bandWidth:
|
|
19
|
+
bandWidth: margins.left
|
|
13
20
|
};
|
|
14
21
|
case "rightMargin":
|
|
15
22
|
case "outsideMargin": return {
|
|
16
|
-
baseX: geometry.
|
|
17
|
-
bandWidth:
|
|
23
|
+
baseX: geometry.pageWidth - geometry.marginLeft - margins.right,
|
|
24
|
+
bandWidth: margins.right
|
|
18
25
|
};
|
|
19
26
|
case "character": return {
|
|
20
27
|
baseX: 0,
|
|
@@ -27,6 +34,7 @@ function resolveHorizontalBand(relativeTo, geometry) {
|
|
|
27
34
|
}
|
|
28
35
|
}
|
|
29
36
|
function resolveVerticalBand(relativeTo, fragmentY, geometry) {
|
|
37
|
+
const margins = authoredMargins(geometry);
|
|
30
38
|
switch (relativeTo) {
|
|
31
39
|
case "page": return {
|
|
32
40
|
baseY: -geometry.marginTop,
|
|
@@ -34,11 +42,11 @@ function resolveVerticalBand(relativeTo, fragmentY, geometry) {
|
|
|
34
42
|
};
|
|
35
43
|
case "topMargin": return {
|
|
36
44
|
baseY: -geometry.marginTop,
|
|
37
|
-
bandHeight:
|
|
45
|
+
bandHeight: margins.top
|
|
38
46
|
};
|
|
39
47
|
case "bottomMargin": return {
|
|
40
|
-
baseY: geometry.
|
|
41
|
-
bandHeight:
|
|
48
|
+
baseY: geometry.pageHeight - geometry.marginTop - margins.bottom,
|
|
49
|
+
bandHeight: margins.bottom
|
|
42
50
|
};
|
|
43
51
|
case "paragraph":
|
|
44
52
|
case "line": return {
|
|
@@ -797,6 +797,7 @@ function renderPage(page, context, options = {}) {
|
|
|
797
797
|
marginTop: page.margins.top,
|
|
798
798
|
marginRight: page.margins.right,
|
|
799
799
|
marginBottom: page.margins.bottom,
|
|
800
|
+
...page.authoredMargins ? { authoredMargins: page.authoredMargins } : {},
|
|
800
801
|
contentWidth,
|
|
801
802
|
contentHeight: page.size.h - page.margins.top - page.margins.bottom
|
|
802
803
|
};
|
|
@@ -1209,10 +1210,19 @@ function computePageFingerprint(page, blockLookup) {
|
|
|
1209
1210
|
function computePageRenderFingerprint(page, blockLookup) {
|
|
1210
1211
|
return computePageFingerprintInternal(page, blockLookup, { includePmPositions: false });
|
|
1211
1212
|
}
|
|
1213
|
+
const pageMarginsFingerprint = (margins) => [
|
|
1214
|
+
margins.top,
|
|
1215
|
+
margins.right,
|
|
1216
|
+
margins.bottom,
|
|
1217
|
+
margins.left,
|
|
1218
|
+
margins.header ?? "",
|
|
1219
|
+
margins.footer ?? ""
|
|
1220
|
+
].join(",");
|
|
1212
1221
|
function computePageFingerprintInternal(page, blockLookup, options) {
|
|
1213
1222
|
const parts = [];
|
|
1214
1223
|
parts.push(`s:${page.size.w},${page.size.h}`);
|
|
1215
|
-
parts.push(`m:${page.margins
|
|
1224
|
+
parts.push(`m:${pageMarginsFingerprint(page.margins)}`);
|
|
1225
|
+
if (page.authoredMargins) parts.push(`am:${pageMarginsFingerprint(page.authoredMargins)}`);
|
|
1216
1226
|
parts.push(`n:${page.number}`);
|
|
1217
1227
|
if (page.sectionIndex !== void 0) parts.push(`si:${page.sectionIndex}`);
|
|
1218
1228
|
if (page.sectionPageNumber !== void 0) parts.push(`sp:${page.sectionPageNumber}`);
|
|
@@ -2,7 +2,7 @@ import { ommlToMathml } from "../docx/mathToMathml.js";
|
|
|
2
2
|
import { parseXmlDocument } from "../docx/xmlParser.js";
|
|
3
3
|
import { evaluateFieldInstruction } from "../fields/evaluateField.js";
|
|
4
4
|
import { getListMarkerInlineWidth, getListMarkerVisualOffset } from "../layout-engine/measure/listMarkerWidth.js";
|
|
5
|
-
import "../layout-engine/measure/measureHelpers.js";
|
|
5
|
+
import { DOCX_SCRIPT_FONT_SCALE } from "../layout-engine/measure/measureHelpers.js";
|
|
6
6
|
import { FONT_KERNING_MODE, countCompressibleSpaces, getFontKerningMode, getRunFontKerningMode, toPaintedText } from "../layout-engine/measure/textMeasurementPolicy.js";
|
|
7
7
|
import { isFloatingImageRun } from "../layout-engine/types.js";
|
|
8
8
|
import { calculateTabWidth } from "../prosemirror/utils/tabCalculator.js";
|
|
@@ -76,7 +76,6 @@ function isMathRun(run) {
|
|
|
76
76
|
}
|
|
77
77
|
const AUTOMATIC_TEXT_COLOR_VALUES = /* @__PURE__ */ new Set(["auto", "windowtext"]);
|
|
78
78
|
const DEFAULT_BLACK_TEXT_COLOR_VALUES = /* @__PURE__ */ new Set(["000000", "000"]);
|
|
79
|
-
const DOCX_SUPERSCRIPT_SCALE = .75;
|
|
80
79
|
const SUGGESTION_COLOR_CSS = "var(--suggestion-color, #6d3bd6)";
|
|
81
80
|
const SUGGESTION_TINT_CSS = "var(--suggestion-bg, color-mix(in oklch, #6d3bd6 12%, transparent))";
|
|
82
81
|
const SUGGESTION_TINT_LAYER_CSS = `linear-gradient(${SUGGESTION_TINT_CSS}, ${SUGGESTION_TINT_CSS})`;
|
|
@@ -109,8 +108,8 @@ function fontSizePtToPx(fontSizePt) {
|
|
|
109
108
|
return fontSizePt * 96 / 72;
|
|
110
109
|
}
|
|
111
110
|
function getRaisedRunFontSize(run) {
|
|
112
|
-
if (run.fontSize) return `${fontSizePtToPx(run.fontSize) *
|
|
113
|
-
return `${
|
|
111
|
+
if (run.fontSize) return `${fontSizePtToPx(run.fontSize) * DOCX_SCRIPT_FONT_SCALE}px`;
|
|
112
|
+
return `${DOCX_SCRIPT_FONT_SCALE}em`;
|
|
114
113
|
}
|
|
115
114
|
/**
|
|
116
115
|
* Apply text run styles to an element
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FIELD_TYPE_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, LINE_SPACING_RULE_VALUES, OUTLINE_STYLE_ATTR_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, 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 } from "../../types/documentEnumValues.js";
|
|
1
|
+
import { FIELD_TYPE_VALUES, FONT_THEME_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, LINE_SPACING_RULE_VALUES, OUTLINE_STYLE_ATTR_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, 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 } from "../../types/documentEnumValues.js";
|
|
2
2
|
import { isParagraphDirection } from "../paragraphDirection.js";
|
|
3
3
|
import { TRACKED_CHANGE_PROVENANCE_VALUES } from "../schema/marks.js";
|
|
4
4
|
import { panic } from "better-result";
|
|
@@ -163,6 +163,7 @@ const readParagraphAttrs = (node) => {
|
|
|
163
163
|
optionalNumber(attrs, "spaceBefore", "paragraph.attrs.spaceBefore", issues);
|
|
164
164
|
optionalNumber(attrs, "spaceAfter", "paragraph.attrs.spaceAfter", issues);
|
|
165
165
|
optionalNumber(attrs, "lineSpacing", "paragraph.attrs.lineSpacing", issues);
|
|
166
|
+
optionalBoolean(attrs, "lineSpacingExplicit", "paragraph.attrs.lineSpacingExplicit", issues);
|
|
166
167
|
optionalBoolean(attrs, "snapToGrid", "paragraph.attrs.snapToGrid", issues);
|
|
167
168
|
optionalOneOf(attrs, "lineSpacingRule", "paragraph.attrs.lineSpacingRule", issues, LINE_SPACING_RULE_VALUES);
|
|
168
169
|
optionalNumber(attrs, "indentLeft", "paragraph.attrs.indentLeft", issues);
|
|
@@ -571,10 +572,10 @@ const readFontFamilyMarkAttrs = (mark) => {
|
|
|
571
572
|
optionalString(attrs, "eastAsia", "fontFamily.attrs.eastAsia", issues);
|
|
572
573
|
optionalString(attrs, "cs", "fontFamily.attrs.cs", issues);
|
|
573
574
|
optionalString(attrs, "hint", "fontFamily.attrs.hint", issues);
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
575
|
+
optionalOneOf(attrs, "asciiTheme", "fontFamily.attrs.asciiTheme", issues, FONT_THEME_VALUES);
|
|
576
|
+
optionalOneOf(attrs, "hAnsiTheme", "fontFamily.attrs.hAnsiTheme", issues, FONT_THEME_VALUES);
|
|
577
|
+
optionalOneOf(attrs, "eastAsiaTheme", "fontFamily.attrs.eastAsiaTheme", issues, FONT_THEME_VALUES);
|
|
578
|
+
optionalOneOf(attrs, "csTheme", "fontFamily.attrs.csTheme", issues, FONT_THEME_VALUES);
|
|
578
579
|
return attrsResult(attrs, issues);
|
|
579
580
|
};
|
|
580
581
|
const expectFontFamilyMarkAttrs = (mark) => expectCachedMarkAttrs(mark, fontFamilyAttrsCache, readFontFamilyMarkAttrs, "font family attrs");
|
|
@@ -19,7 +19,7 @@ type AttrPatch = Record<string, unknown>;
|
|
|
19
19
|
* without a style resolver: `numPrFromStyle`, the `list*` rendering attrs,
|
|
20
20
|
* `spacingFromDocDefaults`, `spacingFromImplicitDefaultStyle`
|
|
21
21
|
*/
|
|
22
|
-
declare const PPR_CHANGE_SCOPED_ATTR_KEYS: readonly ["styleId", "numPr", "alignment", "spaceBefore", "spaceAfter", "lineSpacing", "lineSpacingRule", "snapToGrid", "spacingExplicit", "indentLeft", "indentRight", "indentFirstLine", "hangingIndent", "borders", "shading", "tabs", "pageBreakBefore", "keepNext", "keepLines", "widowControl", "contextualSpacing", "outlineLevel", "direction", "_autospacingBase"];
|
|
22
|
+
declare const PPR_CHANGE_SCOPED_ATTR_KEYS: readonly ["styleId", "numPr", "alignment", "spaceBefore", "spaceAfter", "lineSpacing", "lineSpacingRule", "lineSpacingExplicit", "snapToGrid", "spacingExplicit", "indentLeft", "indentRight", "indentFirstLine", "hangingIndent", "borders", "shading", "tabs", "pageBreakBefore", "keepNext", "keepLines", "widowControl", "contextualSpacing", "outlineLevel", "direction", "_autospacingBase"];
|
|
23
23
|
/**
|
|
24
24
|
* Build the attr patch that rejecting one pPrChange applies to a paragraph:
|
|
25
25
|
* every in-scope key set to the stored previous value, or reset to `null`
|
|
@@ -543,6 +543,7 @@ function paragraphAttrsToFormatting(attrs) {
|
|
|
543
543
|
const afterAutospacingEdited = afterHasAutospacingBase ? !autospacingMatchesBase(attrs._autospacingBase, "after", spaceAfter) : afterOriginalAutospacing && attrs._autospacingBase == null;
|
|
544
544
|
const shouldSerializeSpaceBefore = typeof spaceBefore === "number" && (!beforeHasAutospacingBase || beforeAutospacingEdited);
|
|
545
545
|
const shouldSerializeSpaceAfter = typeof spaceAfter === "number" && (!afterHasAutospacingBase || afterAutospacingEdited);
|
|
546
|
+
const hasDirectLineSpacing = attrs.lineSpacingExplicit === true;
|
|
546
547
|
if (attrs._originalFormatting) {
|
|
547
548
|
const orig = attrs._originalFormatting;
|
|
548
549
|
const result = { ...orig };
|
|
@@ -556,6 +557,15 @@ function paragraphAttrsToFormatting(attrs) {
|
|
|
556
557
|
if (typeof spaceAfter === "number") result.spaceAfter = spaceAfter;
|
|
557
558
|
else delete result.spaceAfter;
|
|
558
559
|
}
|
|
560
|
+
if (attrs.spacingExplicit?.before && typeof spaceBefore === "number") result.spaceBefore = spaceBefore;
|
|
561
|
+
if (attrs.spacingExplicit?.after && typeof spaceAfter === "number") result.spaceAfter = spaceAfter;
|
|
562
|
+
const originalHasDirectLineSpacing = orig.lineSpacing !== void 0 || orig.lineSpacingRule !== void 0;
|
|
563
|
+
if (hasDirectLineSpacing || originalHasDirectLineSpacing) {
|
|
564
|
+
if (typeof attrs.lineSpacing === "number") result.lineSpacing = attrs.lineSpacing;
|
|
565
|
+
else Reflect.deleteProperty(result, "lineSpacing");
|
|
566
|
+
if (attrs.lineSpacingRule) result.lineSpacingRule = attrs.lineSpacingRule;
|
|
567
|
+
else Reflect.deleteProperty(result, "lineSpacingRule");
|
|
568
|
+
}
|
|
559
569
|
if (attrs.alignment !== (orig.alignment ?? void 0)) if (attrs.alignment) result.alignment = attrs.alignment;
|
|
560
570
|
else delete result.alignment;
|
|
561
571
|
if (isStyleSourcedNumPr(attrs)) {
|
|
@@ -583,15 +593,15 @@ function paragraphAttrsToFormatting(attrs) {
|
|
|
583
593
|
}
|
|
584
594
|
const outlineLevel = Reflect.get(attrs, "outlineLevel");
|
|
585
595
|
const bidi = directionToBidi(attrs.direction);
|
|
586
|
-
if (!(attrs.alignment || shouldSerializeSpaceBefore || shouldSerializeSpaceAfter || beforeAutospacingEdited || afterAutospacingEdited ||
|
|
596
|
+
if (!(attrs.alignment || shouldSerializeSpaceBefore || shouldSerializeSpaceAfter || beforeAutospacingEdited || afterAutospacingEdited || hasDirectLineSpacing || attrs.snapToGrid != null || attrs.indentLeft || attrs.indentRight || attrs.indentFirstLine || attrs.numPr || attrs.styleId || attrs.borders || attrs.shading || attrs.tabs || typeof outlineLevel === "number" || attrs.contextualSpacing || attrs.spacingExplicit || bidi != null || attrs.pageBreakBefore != null || attrs.widowControl != null || attrs.kinsoku != null || attrs.overflowPunctuation != null || attrs.suppressAutoHyphens != null)) return;
|
|
587
597
|
const f = {};
|
|
588
598
|
if (attrs.alignment) f.alignment = attrs.alignment;
|
|
589
599
|
if (shouldSerializeSpaceBefore) f.spaceBefore = spaceBefore;
|
|
590
600
|
if (beforeAutospacingEdited) f.beforeAutospacing = false;
|
|
591
601
|
if (shouldSerializeSpaceAfter) f.spaceAfter = spaceAfter;
|
|
592
602
|
if (afterAutospacingEdited) f.afterAutospacing = false;
|
|
593
|
-
if (attrs.lineSpacing) f.lineSpacing = attrs.lineSpacing;
|
|
594
|
-
if (attrs.lineSpacingRule) f.lineSpacingRule = attrs.lineSpacingRule;
|
|
603
|
+
if (hasDirectLineSpacing && typeof attrs.lineSpacing === "number") f.lineSpacing = attrs.lineSpacing;
|
|
604
|
+
if (hasDirectLineSpacing && attrs.lineSpacingRule) f.lineSpacingRule = attrs.lineSpacingRule;
|
|
595
605
|
if (attrs.snapToGrid != null) f.snapToGrid = attrs.snapToGrid;
|
|
596
606
|
if (attrs.spacingExplicit) f.spacingExplicit = attrs.spacingExplicit;
|
|
597
607
|
if (attrs.indentLeft) f.indentLeft = attrs.indentLeft;
|
|
@@ -319,6 +319,7 @@ function paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOver
|
|
|
319
319
|
set("spaceAfter", formatting?.spaceAfter ?? stylePpr?.spaceAfter);
|
|
320
320
|
set("lineSpacing", formatting?.lineSpacing ?? stylePpr?.lineSpacing);
|
|
321
321
|
set("lineSpacingRule", formatting?.lineSpacingRule ?? stylePpr?.lineSpacingRule);
|
|
322
|
+
set("lineSpacingExplicit", formatting?.lineSpacing !== void 0 || formatting?.lineSpacingRule !== void 0 ? true : void 0);
|
|
322
323
|
set("snapToGrid", formatting?.snapToGrid ?? stylePpr?.snapToGrid);
|
|
323
324
|
set("spacingExplicit", formatting?.spacingExplicit);
|
|
324
325
|
const paragraphStyle = styleId ? styleResolver.getStyle(styleId) ?? styleResolver.getDefaultParagraphStyle() : styleResolver.getDefaultParagraphStyle();
|
|
@@ -161,6 +161,7 @@ function extractParagraphAttrsFromStyle(element) {
|
|
|
161
161
|
if (spacing) {
|
|
162
162
|
attrs.lineSpacing = spacing.lineSpacing;
|
|
163
163
|
attrs.lineSpacingRule = spacing.lineSpacingRule;
|
|
164
|
+
attrs.lineSpacingExplicit = true;
|
|
164
165
|
}
|
|
165
166
|
}
|
|
166
167
|
if (style.marginTop) {
|
|
@@ -187,6 +188,7 @@ const paragraphNodeSpec = {
|
|
|
187
188
|
spaceAfter: { default: null },
|
|
188
189
|
lineSpacing: { default: null },
|
|
189
190
|
lineSpacingRule: { default: null },
|
|
191
|
+
lineSpacingExplicit: { default: null },
|
|
190
192
|
snapToGrid: { default: null },
|
|
191
193
|
spacingExplicit: { default: null },
|
|
192
194
|
spacingFromDocDefaults: { default: null },
|
|
@@ -323,6 +325,30 @@ function setParagraphAttr(attr, value) {
|
|
|
323
325
|
return true;
|
|
324
326
|
};
|
|
325
327
|
}
|
|
328
|
+
function setParagraphSpacingAttr(side, twips) {
|
|
329
|
+
return (state, dispatch) => {
|
|
330
|
+
const { $from, $to } = state.selection;
|
|
331
|
+
if (!dispatch) return true;
|
|
332
|
+
let tr = state.tr;
|
|
333
|
+
const seen = /* @__PURE__ */ new Set();
|
|
334
|
+
state.doc.nodesBetween($from.pos, $to.pos, (node, pos) => {
|
|
335
|
+
if (node.type.name === "paragraph" && !seen.has(pos)) {
|
|
336
|
+
seen.add(pos);
|
|
337
|
+
const spacingExplicit = {
|
|
338
|
+
...node.attrs["spacingExplicit"],
|
|
339
|
+
[side]: true
|
|
340
|
+
};
|
|
341
|
+
tr = tr.setNodeMarkup(pos, void 0, {
|
|
342
|
+
...node.attrs,
|
|
343
|
+
[side === "before" ? "spaceBefore" : "spaceAfter"]: twips,
|
|
344
|
+
spacingExplicit
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
dispatch(tr.scrollIntoView());
|
|
349
|
+
return true;
|
|
350
|
+
};
|
|
351
|
+
}
|
|
326
352
|
function setParagraphAttrsCmd(attrs) {
|
|
327
353
|
return (state, dispatch) => {
|
|
328
354
|
const { $from, $to } = state.selection;
|
|
@@ -348,7 +374,8 @@ function makeSetAlignment(alignment) {
|
|
|
348
374
|
function makeSetLineSpacing(value, rule = "auto") {
|
|
349
375
|
return (state, dispatch) => setParagraphAttrsCmd({
|
|
350
376
|
lineSpacing: value,
|
|
351
|
-
lineSpacingRule: rule
|
|
377
|
+
lineSpacingRule: rule,
|
|
378
|
+
lineSpacingExplicit: true
|
|
352
379
|
})(state, dispatch);
|
|
353
380
|
}
|
|
354
381
|
function makeIncreaseIndent(amount = 720) {
|
|
@@ -498,8 +525,8 @@ const ParagraphExtension = createNodeExtension({
|
|
|
498
525
|
singleSpacing: () => makeSetLineSpacing(240),
|
|
499
526
|
oneAndHalfSpacing: () => makeSetLineSpacing(360),
|
|
500
527
|
doubleSpacing: () => makeSetLineSpacing(480),
|
|
501
|
-
setSpaceBefore: (twips) =>
|
|
502
|
-
setSpaceAfter: (twips) =>
|
|
528
|
+
setSpaceBefore: (twips) => setParagraphSpacingAttr("before", twips),
|
|
529
|
+
setSpaceAfter: (twips) => setParagraphSpacingAttr("after", twips),
|
|
503
530
|
increaseIndent: (amount) => makeIncreaseIndent(amount),
|
|
504
531
|
decreaseIndent: (amount) => makeDecreaseIndent(amount),
|
|
505
532
|
setIndentLeft: (twips) => setParagraphAttr("indentLeft", twips > 0 ? twips : null),
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { expectUnderlineMarkAttrs } from "../../attrs/index.js";
|
|
2
2
|
import { createMarkExtension } from "../create.js";
|
|
3
|
-
import { setMark } from "./markUtils.js";
|
|
3
|
+
import { setMark, toggleUnderlineMark } from "./markUtils.js";
|
|
4
4
|
import { panic } from "better-result";
|
|
5
|
-
import { toggleMark } from "prosemirror-commands";
|
|
6
5
|
//#region src/prosemirror/extensions/marks/UnderlineExtension.ts
|
|
7
6
|
/**
|
|
8
7
|
* Underline Mark Extension
|
|
@@ -17,14 +16,17 @@ const UnderlineExtension = createMarkExtension({
|
|
|
17
16
|
},
|
|
18
17
|
parseDOM: [{ tag: "u" }, {
|
|
19
18
|
style: "text-decoration",
|
|
20
|
-
getAttrs: (value) =>
|
|
19
|
+
getAttrs: (value) => {
|
|
20
|
+
if (value.includes("underline")) return {};
|
|
21
|
+
return value.includes("none") ? { style: "none" } : false;
|
|
22
|
+
}
|
|
21
23
|
}],
|
|
22
24
|
toDOM(mark) {
|
|
23
25
|
const attrs = expectUnderlineMarkAttrs(mark);
|
|
24
26
|
const style = attrs.style;
|
|
25
27
|
const colorRgb = attrs.color?.rgb;
|
|
26
|
-
const cssStyle = [
|
|
27
|
-
if (style && style !== "single") {
|
|
28
|
+
const cssStyle = [`text-decoration: ${style === "none" ? "none" : "underline"}`];
|
|
29
|
+
if (style && style !== "single" && style !== "none") {
|
|
28
30
|
const cssDecorationStyle = {
|
|
29
31
|
double: "double",
|
|
30
32
|
dotted: "dotted",
|
|
@@ -46,13 +48,13 @@ const UnderlineExtension = createMarkExtension({
|
|
|
46
48
|
if (!underlineType) panic("Missing mark type: underline");
|
|
47
49
|
return {
|
|
48
50
|
commands: {
|
|
49
|
-
toggleUnderline: () =>
|
|
51
|
+
toggleUnderline: () => toggleUnderlineMark(underlineType),
|
|
50
52
|
setUnderlineStyle: (style, color) => setMark(underlineType, {
|
|
51
53
|
style,
|
|
52
54
|
color
|
|
53
55
|
})
|
|
54
56
|
},
|
|
55
|
-
keyboardShortcuts: { "Mod-u":
|
|
57
|
+
keyboardShortcuts: { "Mod-u": toggleUnderlineMark(underlineType) }
|
|
56
58
|
};
|
|
57
59
|
}
|
|
58
60
|
});
|
|
@@ -4,6 +4,7 @@ import { Mark, MarkType, Schema } from "prosemirror-model";
|
|
|
4
4
|
//#region src/prosemirror/extensions/marks/markUtils.d.ts
|
|
5
5
|
type MarkAttrs = Record<string, unknown>;
|
|
6
6
|
declare function setMark(markType: MarkType, attrs: MarkAttrs): Command;
|
|
7
|
+
declare function toggleUnderlineMark(markType: MarkType): Command;
|
|
7
8
|
declare function removeMark(markType: MarkType): Command;
|
|
8
9
|
/**
|
|
9
10
|
* Check if a mark is active in the current selection
|
|
@@ -30,4 +31,4 @@ declare function createSetMarkCommand(markType: MarkType, attrs?: Record<string,
|
|
|
30
31
|
*/
|
|
31
32
|
declare function createRemoveMarkCommand(markType: MarkType): Command;
|
|
32
33
|
//#endregion
|
|
33
|
-
export { clearFormatting, createRemoveMarkCommand, createSetMarkCommand, getMarkAttr, isMarkActive, removeMark, setMark, textFormattingToMarks };
|
|
34
|
+
export { clearFormatting, createRemoveMarkCommand, createSetMarkCommand, getMarkAttr, isMarkActive, removeMark, setMark, textFormattingToMarks, toggleUnderlineMark };
|