@stll/folio-core 0.22.2 → 0.23.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/ai-edits/apply.d.ts +1 -1
- package/dist/controller/layoutPipeline.js +28 -0
- package/dist/document-operations.d.ts +4 -4
- package/dist/document-operations.js +17 -1
- package/dist/docx/imageParser.js +6 -9
- package/dist/docx/rezip.d.ts +1 -1
- package/dist/docx/rezip.js +12 -31
- package/dist/docx/server/inspectDocxPackage.js +8 -7
- package/dist/docx/styleParser.js +16 -16
- package/dist/docx/xmlParser.d.ts +7 -3
- package/dist/docx/xmlParser.js +20 -7
- package/dist/layout-bridge/convert/toFlowBlocks.js +35 -61
- 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/imageLayout.d.ts +1 -1
- package/dist/layout-painter/renderPage.js +11 -1
- package/dist/layout-painter/renderParagraph.js +3 -4
- package/dist/managers/AutoSaveManager.js +8 -40
- package/dist/managers/TableSelectionManager.d.ts +1 -1
- package/dist/managers/autoSaveCodec.d.ts +17 -0
- package/dist/managers/autoSaveCodec.js +109 -0
- package/dist/prosemirror/attrs/index.js +278 -29
- package/dist/prosemirror/commands/comments.js +3 -3
- package/dist/prosemirror/commands/formatting.js +19 -19
- package/dist/prosemirror/commands/index.d.ts +2 -2
- package/dist/prosemirror/commands/paragraph.js +32 -32
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +5 -3
- package/dist/prosemirror/commands/propertyChangeScope.js +9 -8
- package/dist/prosemirror/commands/table.d.ts +10 -52
- package/dist/prosemirror/commands/table.js +34 -34
- package/dist/prosemirror/conversion/fromProseDoc.js +33 -20
- package/dist/prosemirror/conversion/sdtAttrs.d.ts +2 -1
- package/dist/prosemirror/conversion/sdtAttrs.js +9 -4
- package/dist/prosemirror/conversion/toProseDoc.js +1 -0
- package/dist/prosemirror/extensions/ExtensionManager.d.ts +6 -3
- package/dist/prosemirror/extensions/ExtensionManager.js +5 -3
- package/dist/prosemirror/extensions/core/ParagraphExtension.d.ts +1 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +30 -3
- package/dist/prosemirror/extensions/features/ListExtension.js +4 -4
- 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/extensions/nodes/ShapeExtension.d.ts +1 -1
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +1 -1
- package/dist/prosemirror/extensions/types.d.ts +134 -1
- package/dist/prosemirror/index.d.ts +2 -2
- package/dist/prosemirror/plugins/selectionTracker.js +25 -94
- package/dist/prosemirror/revisionCarriers.js +3 -1
- package/dist/prosemirror/schema/index.d.ts +2 -2
- package/dist/prosemirror/schema/index.js +87 -0
- package/dist/prosemirror/schema/nodes.d.ts +14 -2
- package/dist/prosemirror/selectionMarks.d.ts +11 -0
- package/dist/prosemirror/selectionMarks.js +19 -0
- package/dist/prosemirror/selectionState.d.ts +11 -5
- package/dist/prosemirror/selectionState.js +99 -74
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
- package/dist/utils/formatToStyle.js +1 -1
- package/dist/utils/tableOperations.d.ts +7 -6
- package/package.json +3 -3
package/dist/ai-edits/apply.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ type FolioAIEditView = {
|
|
|
17
17
|
type ApplyFolioAIEditOperationsOptions = {
|
|
18
18
|
view: FolioAIEditView;
|
|
19
19
|
snapshot: FolioAIEditSnapshot;
|
|
20
|
-
operations: FolioAIEditOperation[];
|
|
20
|
+
operations: readonly FolioAIEditOperation[];
|
|
21
21
|
mode?: FolioAIEditApplyMode;
|
|
22
22
|
author?: string;
|
|
23
23
|
/** Optional author initials (w:initials) stamped alongside the author. */
|
|
@@ -58,6 +58,29 @@ function bodyBlocksClearSectionHeaderFooter(blocks, { authoredMargins, sectionHe
|
|
|
58
58
|
});
|
|
59
59
|
return changed ? nextBlocks : blocks;
|
|
60
60
|
}
|
|
61
|
+
const arePageMarginsEqual = (left, right) => left.top === right.top && left.right === right.right && left.bottom === right.bottom && left.left === right.left && left.header === right.header && left.footer === right.footer;
|
|
62
|
+
const mirrorPageMarginsIfNeeded = (authoredMargins, pageNumber, mirrorMargins) => mirrorMargins && pageNumber % 2 === 0 ? {
|
|
63
|
+
...authoredMargins,
|
|
64
|
+
left: authoredMargins.right,
|
|
65
|
+
right: authoredMargins.left
|
|
66
|
+
} : authoredMargins;
|
|
67
|
+
function attachAuthoredMarginsToLayoutPages(layout, options) {
|
|
68
|
+
let changed = false;
|
|
69
|
+
const pages = layout.pages.map((page) => {
|
|
70
|
+
const sectionProperties = options.sectionPropertiesForMargins[page.sectionIndex ?? 0];
|
|
71
|
+
const authoredMargins = mirrorPageMarginsIfNeeded(sectionProperties ? getMargins(sectionProperties) : options.authoredMargins, page.number, options.mirrorMargins);
|
|
72
|
+
if (page.authoredMargins && arePageMarginsEqual(page.authoredMargins, authoredMargins)) return page;
|
|
73
|
+
changed = true;
|
|
74
|
+
return {
|
|
75
|
+
...page,
|
|
76
|
+
authoredMargins
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
return changed ? {
|
|
80
|
+
...layout,
|
|
81
|
+
pages
|
|
82
|
+
} : layout;
|
|
83
|
+
}
|
|
61
84
|
function runLayoutPipeline(deps, state, options = {}) {
|
|
62
85
|
const { contentWidth, columns, pageSize, margins, pageGap, showMarginGuides, marginGuideColor, syncCoordinator, headerContent, footerContent, firstPageHeaderContent, firstPageFooterContent, headerContentRId, footerContentRId, firstPageHeaderContentRId, firstPageFooterContentRId, sectionHeaderFooterRefs, theme: _theme, sectionProperties, document, defaultTabStop, mirrorMargins, styles, layout, hfPMs, painter, pagesContainer, session, renderHfFromContentOrPm, renderHeaderFooterContentByRId, documentFontsAreLoaded, buildFootnoteRenderItems, describeInvalidHighlightMarks, emptyTemplatePreviewEntries: EMPTY_TEMPLATE_PREVIEW_ENTRIES } = deps;
|
|
63
86
|
let outcome = {};
|
|
@@ -341,6 +364,11 @@ function runLayoutPipeline(deps, state, options = {}) {
|
|
|
341
364
|
};
|
|
342
365
|
let stabilizedFieldValues;
|
|
343
366
|
stabilizeFieldWidths();
|
|
367
|
+
newLayout = attachAuthoredMarginsToLayoutPages(newLayout, {
|
|
368
|
+
authoredMargins: margins,
|
|
369
|
+
mirrorMargins,
|
|
370
|
+
sectionPropertiesForMargins
|
|
371
|
+
});
|
|
344
372
|
const rebuildHeaderFooterForLayout = () => {
|
|
345
373
|
const seqValues = buildSeqValues(newBlocks);
|
|
346
374
|
const bookmarkTextInputs = stabilizedFieldValues === void 0 ? { seqValues } : {
|
|
@@ -56,10 +56,10 @@ declare class InvalidFolioDocumentOperationBatchError extends InvalidFolioDocume
|
|
|
56
56
|
declare const assertSupportedFolioDocumentOperationVersion: (value: unknown) => typeof FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION;
|
|
57
57
|
type FolioDocumentOperationBatch = {
|
|
58
58
|
readonly version: typeof FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION;
|
|
59
|
-
operations: FolioDocumentOperation[];
|
|
60
|
-
mode?: FolioDocumentOperationMode;
|
|
61
|
-
atomic?: boolean;
|
|
62
|
-
dryRun?: boolean;
|
|
59
|
+
readonly operations: readonly FolioDocumentOperation[];
|
|
60
|
+
readonly mode?: FolioDocumentOperationMode;
|
|
61
|
+
readonly atomic?: boolean;
|
|
62
|
+
readonly dryRun?: boolean;
|
|
63
63
|
};
|
|
64
64
|
declare const parseFolioDocumentOperationBatch: (value: unknown) => FolioDocumentOperationBatch;
|
|
65
65
|
type FolioDocumentOperationStatus = "committed" | "previewed" | "rejected";
|
|
@@ -34,6 +34,7 @@ const FOLIO_DOCUMENT_OPERATION_STORIES = Object.freeze([
|
|
|
34
34
|
]);
|
|
35
35
|
const FOLIO_DOCUMENT_OPERATION_PRECONDITIONS = Object.freeze(["blockTextHash"]);
|
|
36
36
|
const FOLIO_DOCUMENT_OPERATION_BATCH_MODES = Object.freeze(["best-effort", "atomic"]);
|
|
37
|
+
const parsedFolioDocumentOperationBatches = /* @__PURE__ */ new WeakSet();
|
|
37
38
|
const DIRECT_AND_TRACKED_MODES = Object.freeze(["direct", "tracked-changes"]);
|
|
38
39
|
const DIRECT_TRACKED_AND_SUGGESTED_MODES = FOLIO_DOCUMENT_OPERATION_MODES;
|
|
39
40
|
const DIRECT_AND_SUGGESTED_MODES = Object.freeze(["direct", "suggested"]);
|
|
@@ -83,6 +84,17 @@ const assertSupportedFolioDocumentOperationVersion = (value) => {
|
|
|
83
84
|
});
|
|
84
85
|
};
|
|
85
86
|
const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
87
|
+
const isParsedFolioDocumentOperationBatch = (value) => typeof value === "object" && value !== null && parsedFolioDocumentOperationBatches.has(value);
|
|
88
|
+
const freezeParsedValue = (value) => {
|
|
89
|
+
if (Array.isArray(value)) {
|
|
90
|
+
for (const entry of value) freezeParsedValue(entry);
|
|
91
|
+
Object.freeze(value);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (!isPlainObject(value)) return;
|
|
95
|
+
for (const entry of Object.values(value)) freezeParsedValue(entry);
|
|
96
|
+
Object.freeze(value);
|
|
97
|
+
};
|
|
86
98
|
const invalidBatch = (path, reason) => {
|
|
87
99
|
throw new InvalidFolioDocumentOperationBatchError({
|
|
88
100
|
message: `Invalid document operation batch at ${path}: ${reason}.`,
|
|
@@ -508,6 +520,7 @@ const parseDocumentOperation = (value, index) => {
|
|
|
508
520
|
return invalidBatch(`${path}.type`, `unsupported operation type "${type}"`);
|
|
509
521
|
};
|
|
510
522
|
const parseFolioDocumentOperationBatch = (value) => {
|
|
523
|
+
if (isParsedFolioDocumentOperationBatch(value)) return value;
|
|
511
524
|
if (!isPlainObject(value)) return invalidBatch("$", "expected an object");
|
|
512
525
|
assertAllowedKeys(value, "$", [
|
|
513
526
|
"version",
|
|
@@ -529,13 +542,16 @@ const parseFolioDocumentOperationBatch = (value) => {
|
|
|
529
542
|
if (operationIds.has(operation.id)) return invalidBatch(`$.operations[${index}].id`, "expected a unique operation id");
|
|
530
543
|
operationIds.add(operation.id);
|
|
531
544
|
}
|
|
532
|
-
|
|
545
|
+
const parsedBatch = {
|
|
533
546
|
version,
|
|
534
547
|
operations: parsedOperations,
|
|
535
548
|
...mode !== void 0 && { mode },
|
|
536
549
|
...atomic !== void 0 && { atomic },
|
|
537
550
|
...dryRun !== void 0 && { dryRun }
|
|
538
551
|
};
|
|
552
|
+
freezeParsedValue(parsedBatch);
|
|
553
|
+
parsedFolioDocumentOperationBatches.add(parsedBatch);
|
|
554
|
+
return parsedBatch;
|
|
539
555
|
};
|
|
540
556
|
const recoveryByReason = {
|
|
541
557
|
missingBlock: "refreshDocument",
|
package/dist/docx/imageParser.js
CHANGED
|
@@ -4,7 +4,7 @@ import { sanitizeExternalUrl } from "../utils/urlSecurity.js";
|
|
|
4
4
|
import { WRAP_ELEMENT_NAMES, parsePositionH, parsePositionV, parseWrapElement } from "./drawingUtils.js";
|
|
5
5
|
import { resolveTarget } from "./relsParser.js";
|
|
6
6
|
import { isTextBoxDrawing } from "./textBoxParser.js";
|
|
7
|
-
import { findByFullName, findChild, getAttribute, getChildElements, parseNumericAttribute } from "./xmlParser.js";
|
|
7
|
+
import { findByFullName, findChild, getAttribute, getChildElements, parseNumericAttribute, parseOnOffValue } from "./xmlParser.js";
|
|
8
8
|
//#region src/docx/imageParser.ts
|
|
9
9
|
/**
|
|
10
10
|
* Convert rotation value (1/60000 of a degree) to degrees
|
|
@@ -74,7 +74,7 @@ function parseDocProps(docPr) {
|
|
|
74
74
|
const name = getAttribute(docPr, null, "name");
|
|
75
75
|
const descr = getAttribute(docPr, null, "descr");
|
|
76
76
|
const title = getAttribute(docPr, null, "title");
|
|
77
|
-
const decorative = getAttribute(docPr, null, "decorative") ===
|
|
77
|
+
const decorative = parseOnOffValue(getAttribute(docPr, null, "decorative")) === true;
|
|
78
78
|
const hlinkClickEl = findChild(docPr, "a", "hlinkClick");
|
|
79
79
|
const hlinkRId = hlinkClickEl ? getAttribute(hlinkClickEl, "r", "id") : null;
|
|
80
80
|
return {
|
|
@@ -92,8 +92,8 @@ function parseDocProps(docPr) {
|
|
|
92
92
|
function parseTransform(xfrm) {
|
|
93
93
|
if (!xfrm) return;
|
|
94
94
|
const rot = getAttribute(xfrm, null, "rot");
|
|
95
|
-
const flipH = getAttribute(xfrm, null, "flipH") ===
|
|
96
|
-
const flipV = getAttribute(xfrm, null, "flipV") ===
|
|
95
|
+
const flipH = parseOnOffValue(getAttribute(xfrm, null, "flipH")) === true;
|
|
96
|
+
const flipV = parseOnOffValue(getAttribute(xfrm, null, "flipV")) === true;
|
|
97
97
|
const rotation = rotToDegrees(rot);
|
|
98
98
|
if (rotation === void 0 && !flipH && !flipV) return;
|
|
99
99
|
const transform = {};
|
|
@@ -155,10 +155,7 @@ function parseImageCrop(blipFill) {
|
|
|
155
155
|
* spec-defined default.
|
|
156
156
|
*/
|
|
157
157
|
function parseOnOffAttr(element, name) {
|
|
158
|
-
|
|
159
|
-
if (raw === null) return;
|
|
160
|
-
if (raw === "1" || raw === "true" || raw === "on") return true;
|
|
161
|
-
if (raw === "0" || raw === "false" || raw === "off") return false;
|
|
158
|
+
return parseOnOffValue(getAttribute(element, null, name));
|
|
162
159
|
}
|
|
163
160
|
/**
|
|
164
161
|
* Parse `<a:alphaModFix amt="..."/>` inside the `a:blip` element. The
|
|
@@ -358,7 +355,7 @@ function parseAnchor(anchorEl, rels, media) {
|
|
|
358
355
|
const size = parseExtent(findByFullName(anchorEl, "wp:extent"));
|
|
359
356
|
const padding = parseEffectExtent(findByFullName(anchorEl, "wp:effectExtent"));
|
|
360
357
|
const props = parseDocProps(findByFullName(anchorEl, "wp:docPr"));
|
|
361
|
-
const behindDoc = getAttribute(anchorEl, null, "behindDoc") ===
|
|
358
|
+
const behindDoc = parseOnOffValue(getAttribute(anchorEl, null, "behindDoc")) === true;
|
|
362
359
|
const layoutInCell = parseOnOffAttr(anchorEl, "layoutInCell");
|
|
363
360
|
const allowOverlap = parseOnOffAttr(anchorEl, "allowOverlap");
|
|
364
361
|
const anchorDistT = parseNumericAttribute(anchorEl, null, "distT");
|
package/dist/docx/rezip.d.ts
CHANGED
|
@@ -145,7 +145,7 @@ declare function updateCoreProperties(corePropsXml: string, { updateModifiedDate
|
|
|
145
145
|
* @param buffer - Buffer to validate
|
|
146
146
|
* @returns Promise resolving to validation result
|
|
147
147
|
*/
|
|
148
|
-
declare
|
|
148
|
+
declare const validateDocx: (buffer: ArrayBuffer) => Promise<{
|
|
149
149
|
valid: boolean;
|
|
150
150
|
errors: string[];
|
|
151
151
|
warnings: string[];
|
package/dist/docx/rezip.js
CHANGED
|
@@ -21,6 +21,7 @@ import { isPreservableDocxEntry } from "./unzip.js";
|
|
|
21
21
|
import { WORDPROCESSINGML_NAMESPACE_URIS, findChild, getChildElements, getLocalName, getNamespaceUri, matchesName, parseXml } from "./xmlParser.js";
|
|
22
22
|
import { assertXmlResourceLimits } from "./xmlResourceLimits.js";
|
|
23
23
|
import { panic } from "better-result";
|
|
24
|
+
import { validateDocxPackage } from "@stll/docx-core";
|
|
24
25
|
import JSZip from "jszip";
|
|
25
26
|
//#region src/docx/rezip.ts
|
|
26
27
|
/**
|
|
@@ -1223,38 +1224,18 @@ function getContentTypeForExtension(extension, mimeType) {
|
|
|
1223
1224
|
* @param buffer - Buffer to validate
|
|
1224
1225
|
* @returns Promise resolving to validation result
|
|
1225
1226
|
*/
|
|
1226
|
-
async
|
|
1227
|
-
const
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
]) if (!zip.file(file)) warnings.push(`Missing recommended file: ${file}`);
|
|
1237
|
-
const docFile = zip.file("word/document.xml");
|
|
1238
|
-
if (docFile) {
|
|
1239
|
-
const docXml = await docFile.async("text");
|
|
1240
|
-
if (!docXml.includes("<?xml")) warnings.push("document.xml missing XML declaration");
|
|
1241
|
-
if (!docXml.includes("<w:document")) errors.push("document.xml missing w:document element");
|
|
1242
|
-
if (!docXml.includes("<w:body>")) errors.push("document.xml missing w:body element");
|
|
1243
|
-
}
|
|
1244
|
-
const ctFile = zip.file("[Content_Types].xml");
|
|
1245
|
-
if (ctFile) {
|
|
1246
|
-
const ctXml = await ctFile.async("text");
|
|
1247
|
-
if (!ctXml.includes("word/document.xml") && !ctXml.includes("application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml")) warnings.push("Content_Types.xml may be missing document.xml type declaration");
|
|
1248
|
-
}
|
|
1249
|
-
} catch (error) {
|
|
1250
|
-
errors.push(`Failed to read as ZIP: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1251
|
-
}
|
|
1252
|
-
return {
|
|
1253
|
-
valid: errors.length === 0,
|
|
1254
|
-
errors,
|
|
1255
|
-
warnings
|
|
1227
|
+
const validateDocx = async (buffer) => {
|
|
1228
|
+
const result = await validateDocxPackage(buffer);
|
|
1229
|
+
return result.valid ? {
|
|
1230
|
+
valid: true,
|
|
1231
|
+
errors: [],
|
|
1232
|
+
warnings: []
|
|
1233
|
+
} : {
|
|
1234
|
+
valid: false,
|
|
1235
|
+
errors: [result.error],
|
|
1236
|
+
warnings: []
|
|
1256
1237
|
};
|
|
1257
|
-
}
|
|
1238
|
+
};
|
|
1258
1239
|
/**
|
|
1259
1240
|
* Check if buffer looks like a DOCX file (quick check)
|
|
1260
1241
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getAttributeAnyPrefix, getLocalName, parseXmlDocument } from "../xmlParser.js";
|
|
2
2
|
import { DocxArchiveError, loadDocxArchive } from "./boundedArchive.js";
|
|
3
|
-
import { TaggedError } from "better-result";
|
|
3
|
+
import { Result, TaggedError } from "better-result";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
5
|
//#region src/docx/server/inspectDocxPackage.ts
|
|
6
6
|
const FOLIO_DOCX_PACKAGE_INSPECTION_VERSION = 1;
|
|
@@ -32,16 +32,17 @@ const decodeXml = (bytes, part) => {
|
|
|
32
32
|
let encoding = "utf-8";
|
|
33
33
|
if (bytes.length >= 2 && (bytes[0] === 255 && bytes[1] === 254 || bytes[0] === 60 && bytes[1] === 0)) encoding = "utf-16le";
|
|
34
34
|
else if (bytes.length >= 2 && (bytes[0] === 254 && bytes[1] === 255 || bytes[0] === 0 && bytes[1] === 60)) encoding = "utf-16be";
|
|
35
|
-
try
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
throw new FolioDocxPackageInspectionError({
|
|
35
|
+
const decoded = Result.try({
|
|
36
|
+
try: () => new TextDecoder(encoding, { fatal: true }).decode(bytes),
|
|
37
|
+
catch: (cause) => new FolioDocxPackageInspectionError({
|
|
39
38
|
message: `Failed to decode XML package part "${part}"`,
|
|
40
39
|
code: "xml-decode-failed",
|
|
41
40
|
part,
|
|
42
41
|
cause
|
|
43
|
-
})
|
|
44
|
-
}
|
|
42
|
+
})
|
|
43
|
+
});
|
|
44
|
+
if (decoded.isOk()) return decoded.value;
|
|
45
|
+
throw decoded.error;
|
|
45
46
|
};
|
|
46
47
|
const parseContentTypes = (xml) => {
|
|
47
48
|
const declarations = {
|
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 };
|
|
@@ -9,7 +9,6 @@ import { autospacingMatchesBase } from "../../prosemirror/autospacingBase.js";
|
|
|
9
9
|
import { runShadingAttrsToShading } from "../../prosemirror/conversion/runShadingMark.js";
|
|
10
10
|
import { directionIsRtl } from "../../prosemirror/paragraphDirection.js";
|
|
11
11
|
import { assertValidProseMirrorDocument } from "../../prosemirror/validation.js";
|
|
12
|
-
import { NUMBER_FORMAT_VALUES } from "../../types/documentEnumValues.js";
|
|
13
12
|
import { resolveColor, resolveHighlightToCss } from "../../utils/colorResolver.js";
|
|
14
13
|
import { resolveThemeFont } from "../../utils/fontResolver.js";
|
|
15
14
|
import { resolveShadingFill } from "../../utils/formatToStyle.js";
|
|
@@ -364,7 +363,7 @@ const resolveWesternThemeFont = (fontFamily, theme) => {
|
|
|
364
363
|
return (themeRef ? resolveThemeFont(themeRef, theme?.fontScheme) : null) ?? fontFamily.ascii ?? fontFamily.hAnsi ?? void 0;
|
|
365
364
|
};
|
|
366
365
|
const resolveComplexScriptThemeFont = (fontFamily, theme) => {
|
|
367
|
-
return (fontFamily.
|
|
366
|
+
return (fontFamily.csTheme ? resolveThemeFont(fontFamily.csTheme, theme?.fontScheme) : null) ?? fontFamily.cs ?? void 0;
|
|
368
367
|
};
|
|
369
368
|
const resolveEastAsiaThemeFont = (fontFamily, theme) => {
|
|
370
369
|
return (fontFamily.eastAsiaTheme ? resolveThemeFont(fontFamily.eastAsiaTheme, theme?.fontScheme) : null) ?? fontFamily.eastAsia ?? void 0;
|
|
@@ -650,78 +649,54 @@ function toListMarkerRevision(kind, info) {
|
|
|
650
649
|
}
|
|
651
650
|
function isAddedNumberingChange(change) {
|
|
652
651
|
const previousFormatting = change.previousFormatting;
|
|
653
|
-
return previousFormatting != null && Object.hasOwn(previousFormatting, "numPr") && previousFormatting
|
|
652
|
+
return previousFormatting != null && Object.hasOwn(previousFormatting, "numPr") && previousFormatting.numPr == null;
|
|
654
653
|
}
|
|
655
654
|
function isRemovedNumberingChange(change) {
|
|
656
655
|
const previousFormatting = change.previousFormatting;
|
|
657
|
-
return previousFormatting != null && Object.hasOwn(previousFormatting, "numPr") && isListNumPr(previousFormatting
|
|
656
|
+
return previousFormatting != null && Object.hasOwn(previousFormatting, "numPr") && isListNumPr(previousFormatting.numPr);
|
|
658
657
|
}
|
|
659
658
|
function isChangedNumberingChange(currentNumPr, change) {
|
|
660
659
|
const previousFormatting = change.previousFormatting;
|
|
661
|
-
return previousFormatting != null && Object.hasOwn(previousFormatting, "numPr") && isListNumPr(previousFormatting
|
|
660
|
+
return previousFormatting != null && Object.hasOwn(previousFormatting, "numPr") && isListNumPr(previousFormatting.numPr) && !areListNumPrEqual(previousFormatting.numPr, currentNumPr);
|
|
662
661
|
}
|
|
663
662
|
function areListNumPrEqual(left, right) {
|
|
664
663
|
return left.numId === right.numId && left.ilvl === right.ilvl;
|
|
665
664
|
}
|
|
666
|
-
function isRecord(value) {
|
|
667
|
-
return typeof value === "object" && value !== null;
|
|
668
|
-
}
|
|
669
665
|
function isListNumPr(value) {
|
|
670
|
-
|
|
671
|
-
const numId = value["numId"];
|
|
672
|
-
const ilvl = value["ilvl"];
|
|
673
|
-
return (numId === void 0 || typeof numId === "number") && (ilvl === void 0 || typeof ilvl === "number");
|
|
674
|
-
}
|
|
675
|
-
function isNumberFormat(value) {
|
|
676
|
-
return NUMBER_FORMAT_VALUES.some((format) => format === value);
|
|
677
|
-
}
|
|
678
|
-
function readNumberFormats(value) {
|
|
679
|
-
if (!Array.isArray(value)) return;
|
|
680
|
-
const formats = [];
|
|
681
|
-
for (const item of value) {
|
|
682
|
-
if (!isNumberFormat(item)) return;
|
|
683
|
-
formats.push(item);
|
|
684
|
-
}
|
|
685
|
-
return formats;
|
|
686
|
-
}
|
|
687
|
-
function readListMarkerSuffix(value) {
|
|
688
|
-
if (value === "tab" || value === "space" || value === "nothing") return value;
|
|
689
|
-
}
|
|
690
|
-
function readListMarkerAlignment(value) {
|
|
691
|
-
if (value === "left" || value === "center" || value === "right") return value;
|
|
666
|
+
return value !== void 0 && value !== null;
|
|
692
667
|
}
|
|
693
668
|
function toPreviousListAttrs(previousFormatting) {
|
|
694
669
|
const attrs = {};
|
|
695
|
-
const numPr = previousFormatting
|
|
670
|
+
const numPr = previousFormatting.numPr;
|
|
696
671
|
if (isListNumPr(numPr)) attrs.numPr = numPr;
|
|
697
|
-
const listIsBullet = previousFormatting
|
|
698
|
-
if (
|
|
699
|
-
const listIsLegal = previousFormatting
|
|
700
|
-
if (
|
|
701
|
-
const listMarker = previousFormatting
|
|
702
|
-
if (
|
|
703
|
-
const listNumFmt = previousFormatting
|
|
704
|
-
if (
|
|
705
|
-
const listLevelNumFmts =
|
|
706
|
-
if (listLevelNumFmts) attrs.listLevelNumFmts = listLevelNumFmts;
|
|
707
|
-
const listLevelStarts = previousFormatting
|
|
708
|
-
if (
|
|
709
|
-
const listAbstractNumId = previousFormatting
|
|
710
|
-
if (
|
|
711
|
-
const listStartOverride = previousFormatting
|
|
712
|
-
if (
|
|
713
|
-
const listMarkerHidden = previousFormatting
|
|
714
|
-
if (
|
|
715
|
-
const listMarkerFontFamily = previousFormatting
|
|
716
|
-
if (
|
|
717
|
-
const listMarkerFontSize = previousFormatting
|
|
718
|
-
if (
|
|
719
|
-
const listMarkerBold = previousFormatting
|
|
720
|
-
if (
|
|
721
|
-
const listMarkerAlignment =
|
|
722
|
-
if (listMarkerAlignment) attrs.listMarkerAlignment = listMarkerAlignment;
|
|
723
|
-
const listMarkerSuffix =
|
|
724
|
-
if (listMarkerSuffix) attrs.listMarkerSuffix = listMarkerSuffix;
|
|
672
|
+
const listIsBullet = previousFormatting.listIsBullet;
|
|
673
|
+
if (listIsBullet !== void 0) attrs.listIsBullet = listIsBullet;
|
|
674
|
+
const listIsLegal = previousFormatting.listIsLegal;
|
|
675
|
+
if (listIsLegal !== void 0) attrs.listIsLegal = listIsLegal;
|
|
676
|
+
const listMarker = previousFormatting.listMarker;
|
|
677
|
+
if (listMarker !== void 0) attrs.listMarker = listMarker;
|
|
678
|
+
const listNumFmt = previousFormatting.listNumFmt;
|
|
679
|
+
if (listNumFmt !== void 0) attrs.listNumFmt = listNumFmt;
|
|
680
|
+
const listLevelNumFmts = previousFormatting.listLevelNumFmts;
|
|
681
|
+
if (listLevelNumFmts !== void 0) attrs.listLevelNumFmts = listLevelNumFmts;
|
|
682
|
+
const listLevelStarts = previousFormatting.listLevelStarts;
|
|
683
|
+
if (listLevelStarts !== void 0) attrs.listLevelStarts = listLevelStarts;
|
|
684
|
+
const listAbstractNumId = previousFormatting.listAbstractNumId;
|
|
685
|
+
if (listAbstractNumId !== void 0) attrs.listAbstractNumId = listAbstractNumId;
|
|
686
|
+
const listStartOverride = previousFormatting.listStartOverride;
|
|
687
|
+
if (listStartOverride !== void 0) attrs.listStartOverride = listStartOverride;
|
|
688
|
+
const listMarkerHidden = previousFormatting.listMarkerHidden;
|
|
689
|
+
if (listMarkerHidden !== void 0) attrs.listMarkerHidden = listMarkerHidden;
|
|
690
|
+
const listMarkerFontFamily = previousFormatting.listMarkerFontFamily;
|
|
691
|
+
if (listMarkerFontFamily !== void 0) attrs.listMarkerFontFamily = listMarkerFontFamily;
|
|
692
|
+
const listMarkerFontSize = previousFormatting.listMarkerFontSize;
|
|
693
|
+
if (listMarkerFontSize !== void 0) attrs.listMarkerFontSize = listMarkerFontSize;
|
|
694
|
+
const listMarkerBold = previousFormatting.listMarkerBold;
|
|
695
|
+
if (listMarkerBold !== void 0) attrs.listMarkerBold = listMarkerBold;
|
|
696
|
+
const listMarkerAlignment = previousFormatting.listMarkerAlignment;
|
|
697
|
+
if (listMarkerAlignment !== void 0) attrs.listMarkerAlignment = listMarkerAlignment;
|
|
698
|
+
const listMarkerSuffix = previousFormatting.listMarkerSuffix;
|
|
699
|
+
if (listMarkerSuffix !== void 0) attrs.listMarkerSuffix = listMarkerSuffix;
|
|
725
700
|
return attrs;
|
|
726
701
|
}
|
|
727
702
|
function resolveDeletedListMarker(previousListAttrs, listCounters, listAbstractCounters, listSeenNumIds) {
|
|
@@ -848,8 +823,7 @@ function convertParagraphAttrs(pmAttrs, theme, listCounters, listAbstractCounter
|
|
|
848
823
|
if (pmAttrs.runInWithNext) attrs.runInWithNext = true;
|
|
849
824
|
if (directionIsRtl(pmAttrs.direction)) attrs.bidi = true;
|
|
850
825
|
if (pmAttrs.styleId) attrs.styleId = pmAttrs.styleId;
|
|
851
|
-
const
|
|
852
|
-
const propertyChanges = Array.isArray(pPrChange) ? pPrChange : [];
|
|
826
|
+
const propertyChanges = pmAttrs._propertyChanges ?? [];
|
|
853
827
|
let changedNumberingChange;
|
|
854
828
|
if (pmAttrs.numPr) {
|
|
855
829
|
const numPr = {};
|
|
@@ -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;
|