@stll/folio-core 0.33.2 → 0.34.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.js +137 -23
- package/dist/ai-edits/snapshot.js +24 -2
- package/dist/ai-edits/types.d.ts +6 -6
- package/dist/compare/formatting.d.ts +1 -1
- package/dist/compare/formatting.js +38 -9
- package/dist/compare/verification.js +18 -1
- package/dist/controller/headerFooterEditorManager.js +11 -9
- package/dist/controller/layoutPipeline.js +24 -3
- package/dist/display-list/build/watermarkPrimitives.js +15 -3
- package/dist/document-operations.js +30 -3
- package/dist/docx/headerFooterParser.js +8 -14
- package/dist/docx/paragraphParser.js +45 -0
- package/dist/docx/serializer/headerFooterSerializer.js +21 -2
- package/dist/docx/serializer/paragraphSerializer.d.ts +1 -1
- package/dist/docx/serializer/paragraphSerializer.js +19 -9
- package/dist/docx/settingsParser.js +3 -0
- package/dist/docx/watermarkParser.d.ts +2 -4
- package/dist/docx/watermarkParser.js +4 -6
- package/dist/headless-layout.js +14 -2
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +2 -2
- package/dist/layout-bridge/convert/footnoteLayout.js +23 -10
- package/dist/layout-bridge/convert/headerFooterLayout.js +13 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +96 -13
- package/dist/layout-engine/index.js +11 -4
- package/dist/layout-engine/justifiedLineFit.d.ts +4 -4
- package/dist/layout-engine/justifiedLineFit.js +4 -4
- package/dist/layout-engine/measure/lineBreakProvider.js +1 -0
- package/dist/layout-engine/measure/measureBlocks.js +1 -1
- package/dist/layout-engine/measure/measureParagraph.js +29 -29
- package/dist/layout-painter/renderPage.js +6 -1
- package/dist/layout-painter/renderParagraph.js +18 -8
- package/dist/layout-painter/renderWatermark.js +11 -4
- package/dist/prosemirror/attrs/index.js +11 -0
- package/dist/prosemirror/commands/comments.js +22 -2
- package/dist/prosemirror/conversion/fromProseDoc.js +32 -5
- package/dist/prosemirror/conversion/toProseDoc.js +49 -11
- package/dist/prosemirror/extensions/core/DocExtension.js +7 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +1 -0
- package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +1 -0
- package/dist/prosemirror/schema/marks.d.ts +2 -8
- package/dist/utils/fontResolver.js +51 -0
- package/dist/utils/formatToStyle.js +41 -1
- package/dist/watermark/index.js +7 -0
- package/package.json +2 -2
|
@@ -500,12 +500,15 @@ function getHeaderRowsHeight(measure, headerRowCount) {
|
|
|
500
500
|
return height;
|
|
501
501
|
}
|
|
502
502
|
const tableRowStartsWithRenderedPageBreak = (block, rowIndex) => {
|
|
503
|
-
const
|
|
503
|
+
const row = block.rows[rowIndex];
|
|
504
|
+
const visibleCells = row?.cells.filter((cell) => cell.blocks.some((cellBlock) => cellBlock.kind !== "paragraph" || !isEmptyParagraph(cellBlock)));
|
|
504
505
|
if (!visibleCells || visibleCells.length === 0) return false;
|
|
505
|
-
|
|
506
|
+
const startsWithRenderedPageBreak = (cell) => {
|
|
506
507
|
const firstVisibleBlock = cell.blocks.find((cellBlock) => cellBlock.kind !== "paragraph" || !isEmptyParagraph(cellBlock));
|
|
507
508
|
return firstVisibleBlock?.kind === "paragraph" && firstVisibleBlock.attrs?.renderedPageBreakBefore === true;
|
|
508
|
-
}
|
|
509
|
+
};
|
|
510
|
+
if (row?.heightRule === "exact") return startsWithRenderedPageBreak(visibleCells[0]);
|
|
511
|
+
return visibleCells.every(startsWithRenderedPageBreak);
|
|
509
512
|
};
|
|
510
513
|
const getVerticallyMergedRows = (block) => {
|
|
511
514
|
const mergedRows = /* @__PURE__ */ new Set();
|
|
@@ -866,6 +869,8 @@ function layoutAnchoredImage(block, measure, paginator) {
|
|
|
866
869
|
};
|
|
867
870
|
paginator.addUnflowedFragment(fragment);
|
|
868
871
|
}
|
|
872
|
+
const TEXT_BOX_ANCHOR_BLOCK_ID = Symbol.for("stll.textBoxAnchorBlockId");
|
|
873
|
+
const readTextBoxAnchorBlockId = (block) => Reflect.get(block, TEXT_BOX_ANCHOR_BLOCK_ID);
|
|
869
874
|
/**
|
|
870
875
|
* Layout a text box block onto pages.
|
|
871
876
|
*/
|
|
@@ -909,12 +914,14 @@ function layoutTextBox(block, measure, { paginator, sectionMarginTop, sectionPag
|
|
|
909
914
|
boxWidth: measure.width
|
|
910
915
|
}) : paginator.getColumnX(state.columnIndex);
|
|
911
916
|
const vertical = block.position.vertical;
|
|
917
|
+
const anchorBlockId = readTextBoxAnchorBlockId(block);
|
|
918
|
+
const anchorParagraph = vertical?.relativeTo === "paragraph" && typeof anchorBlockId === "string" ? state.page.fragments.find((fragment) => fragment.kind === "paragraph" && fragment.blockId === anchorBlockId) : void 0;
|
|
912
919
|
const y = isPageFrameRelativeAnchor(vertical?.relativeTo) ? state.topMargin + bandTopContentY(vertical, {
|
|
913
920
|
pageHeight: sectionPageHeight,
|
|
914
921
|
marginTop: sectionMarginTop,
|
|
915
922
|
marginBottom: sectionMarginBottom,
|
|
916
923
|
boxHeight: measure.height
|
|
917
|
-
}) : state.cursorY + emuToPixels(vertical?.posOffset ?? 0);
|
|
924
|
+
}) : (anchorParagraph?.y ?? state.cursorY) + emuToPixels(vertical?.posOffset ?? 0);
|
|
918
925
|
const fragment = {
|
|
919
926
|
kind: "textBox",
|
|
920
927
|
blockId: block.id,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParagraphBlock } from "./types.js";
|
|
2
2
|
//#region src/layout-engine/justifiedLineFit.d.ts
|
|
3
|
-
declare const
|
|
4
|
-
declare const
|
|
5
|
-
declare const
|
|
3
|
+
declare const JUSTIFIED_FINAL_LINE_MAX_SHRINK_RATIO = 0.025;
|
|
4
|
+
declare const JUSTIFIED_FINAL_LINE_SPACE_CONTRACTION_RATIO = 0.32;
|
|
5
|
+
declare const supportsJustifiedFinalLineContraction: (block: ParagraphBlock) => boolean;
|
|
6
6
|
//#endregion
|
|
7
|
-
export {
|
|
7
|
+
export { JUSTIFIED_FINAL_LINE_MAX_SHRINK_RATIO, JUSTIFIED_FINAL_LINE_SPACE_CONTRACTION_RATIO, supportsJustifiedFinalLineContraction };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//#region src/layout-engine/justifiedLineFit.ts
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
const
|
|
2
|
+
const JUSTIFIED_FINAL_LINE_MAX_SHRINK_RATIO = .025;
|
|
3
|
+
const JUSTIFIED_FINAL_LINE_SPACE_CONTRACTION_RATIO = .32;
|
|
4
|
+
const supportsJustifiedFinalLineContraction = (block) => block.attrs?.alignment === "justify" && block.attrs.justificationCompatibility?.type !== "legacy" && (block.attrs.listMarker !== void 0 || (block.attrs.indent?.left ?? 0) > 0);
|
|
5
5
|
//#endregion
|
|
6
|
-
export {
|
|
6
|
+
export { JUSTIFIED_FINAL_LINE_MAX_SHRINK_RATIO, JUSTIFIED_FINAL_LINE_SPACE_CONTRACTION_RATIO, supportsJustifiedFinalLineContraction };
|
|
@@ -169,6 +169,7 @@ const usesEastAsianLineBreaking = (text, locale) => localeUsesEastAsianLineBreak
|
|
|
169
169
|
const allowsBreak = (text, index, policy, usesEastAsianRules, nextLineStart) => {
|
|
170
170
|
const previous = previousCodePoint(text, index);
|
|
171
171
|
const next = nextLineStart ?? firstCodePoint(text, index);
|
|
172
|
+
if (!(usesEastAsianRules || policy?.kinsoku === true || policy?.noLineBreaksBefore !== void 0 || policy?.noLineBreaksAfter !== void 0)) return true;
|
|
172
173
|
if (previous === " " && next === "%" && policy?.kinsoku !== true && policy?.noLineBreaksBefore === void 0 && !usesEastAsianRules) return !isProhibitedLineEnd(previous, policy);
|
|
173
174
|
return !isProhibitedLineEnd(previous, policy) && !isProhibitedLineStart(next, policy);
|
|
174
175
|
};
|
|
@@ -587,7 +587,7 @@ function measureTextBoxBlock(tb, fieldValues) {
|
|
|
587
587
|
return {
|
|
588
588
|
kind: "textBox",
|
|
589
589
|
width: totalWidth,
|
|
590
|
-
height: tb.autoFit === "shape" ?
|
|
590
|
+
height: tb.autoFit === "shape" ? contentBoxHeight : tb.height ?? contentBoxHeight,
|
|
591
591
|
innerMeasures
|
|
592
592
|
};
|
|
593
593
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { CJK_FALLBACK_FONT_FAMILY, isCjkFont } from "../../utils/fontResolver.js";
|
|
2
2
|
import { getHorizontalScaleFactor } from "../../utils/horizontalScale.js";
|
|
3
|
-
import { isRtlParagraph } from "../../utils/paragraphBaseDirection.js";
|
|
4
3
|
import { inlineImageBoundingBox } from "../../utils/rotationBoundingBox.js";
|
|
5
4
|
import { hasCjk, hasComplexScript } from "../../utils/scriptSegments.js";
|
|
6
|
-
import {
|
|
5
|
+
import { JUSTIFIED_FINAL_LINE_MAX_SHRINK_RATIO, JUSTIFIED_FINAL_LINE_SPACE_CONTRACTION_RATIO, supportsJustifiedFinalLineContraction } from "../justifiedLineFit.js";
|
|
7
6
|
import { measuredLineAdvance } from "../lineFlow.js";
|
|
8
7
|
import { isFloatingImageRun } from "../types.js";
|
|
9
8
|
import { clampFloatingWrapMargins } from "./clampFloatingWrapMargins.js";
|
|
@@ -382,7 +381,7 @@ function resolveJustifyFitStrategy(block, isFirstLine, profile) {
|
|
|
382
381
|
};
|
|
383
382
|
return {
|
|
384
383
|
type: "space",
|
|
385
|
-
ratio:
|
|
384
|
+
ratio: JUSTIFIED_FINAL_LINE_SPACE_CONTRACTION_RATIO,
|
|
386
385
|
maxWidthRatio: JUSTIFY_SHRINK_TOLERANCE_RATIO
|
|
387
386
|
};
|
|
388
387
|
}
|
|
@@ -409,10 +408,10 @@ function resolveJustifyFitStrategy(block, isFirstLine, profile) {
|
|
|
409
408
|
};
|
|
410
409
|
}
|
|
411
410
|
function resolveFinalLineJustifyFitStrategy(block, profile) {
|
|
412
|
-
if (
|
|
411
|
+
if (supportsJustifiedFinalLineContraction(block)) return {
|
|
413
412
|
type: "space",
|
|
414
|
-
ratio:
|
|
415
|
-
maxWidthRatio:
|
|
413
|
+
ratio: JUSTIFIED_FINAL_LINE_SPACE_CONTRACTION_RATIO,
|
|
414
|
+
maxWidthRatio: JUSTIFIED_FINAL_LINE_MAX_SHRINK_RATIO
|
|
416
415
|
};
|
|
417
416
|
return resolveJustifyFitStrategy(block, false, profile);
|
|
418
417
|
}
|
|
@@ -433,11 +432,11 @@ function isFinalTextCandidate(block, runIndex, nextBreak) {
|
|
|
433
432
|
return true;
|
|
434
433
|
}
|
|
435
434
|
function resolveTextCandidateFit({ block, line, isFirstLine, isFinalCandidate, candidateWidth, candidateSpaceWidth, fallbackTolerancePx, continuationStrategy, finalStrategy }) {
|
|
436
|
-
if (
|
|
435
|
+
if (!isFinalCandidate || !supportsJustifiedFinalLineContraction(block)) return {
|
|
437
436
|
type: "ordinary",
|
|
438
437
|
tolerancePx: fallbackTolerancePx
|
|
439
438
|
};
|
|
440
|
-
const ordinaryTolerancePx = justifyFitTolerance(line, continuationStrategy, candidateSpaceWidth);
|
|
439
|
+
const ordinaryTolerancePx = isFirstLine ? fallbackTolerancePx : justifyFitTolerance(line, continuationStrategy, candidateSpaceWidth);
|
|
441
440
|
if (candidateWidth <= line.availableWidth + ordinaryTolerancePx) return {
|
|
442
441
|
type: "ordinary",
|
|
443
442
|
tolerancePx: ordinaryTolerancePx
|
|
@@ -713,7 +712,6 @@ function findClearLineY(startY, lineHeight, zones, contentWidth, minWidth) {
|
|
|
713
712
|
function measureParagraph(block, maxWidth, options) {
|
|
714
713
|
const runs = block.runs;
|
|
715
714
|
const attrs = block.attrs;
|
|
716
|
-
const isRtl = isRtlParagraph(block);
|
|
717
715
|
const spacing = attrs?.spacing;
|
|
718
716
|
const isJustifiedParagraph = attrs?.alignment === "justify";
|
|
719
717
|
const justificationProfile = {
|
|
@@ -754,24 +752,26 @@ function measureParagraph(block, maxWidth, options) {
|
|
|
754
752
|
const firstLineWidth = Math.max(1, getFloatingAvailableWidth(firstLineFloatingMargins, baseFirstLineWidth));
|
|
755
753
|
const lines = [];
|
|
756
754
|
let consecutiveHyphenatedLines = 0;
|
|
755
|
+
if (attrs?.suppressEmptyParagraphHeight) {
|
|
756
|
+
const finalRunIndex = Math.max(0, runs.length - 1);
|
|
757
|
+
const finalRun = runs.at(-1);
|
|
758
|
+
lines.push({
|
|
759
|
+
fromRun: 0,
|
|
760
|
+
fromChar: 0,
|
|
761
|
+
toRun: finalRunIndex,
|
|
762
|
+
toChar: finalRun?.kind === "text" ? finalRun.text.length : 0,
|
|
763
|
+
width: 0,
|
|
764
|
+
ascent: 0,
|
|
765
|
+
descent: 0,
|
|
766
|
+
lineHeight: 0
|
|
767
|
+
});
|
|
768
|
+
return {
|
|
769
|
+
kind: "paragraph",
|
|
770
|
+
lines,
|
|
771
|
+
totalHeight: 0
|
|
772
|
+
};
|
|
773
|
+
}
|
|
757
774
|
if (runs.length === 0) {
|
|
758
|
-
if (attrs?.suppressEmptyParagraphHeight) {
|
|
759
|
-
lines.push({
|
|
760
|
-
fromRun: 0,
|
|
761
|
-
fromChar: 0,
|
|
762
|
-
toRun: 0,
|
|
763
|
-
toChar: 0,
|
|
764
|
-
width: 0,
|
|
765
|
-
ascent: 0,
|
|
766
|
-
descent: 0,
|
|
767
|
-
lineHeight: 0
|
|
768
|
-
});
|
|
769
|
-
return {
|
|
770
|
-
kind: "paragraph",
|
|
771
|
-
lines,
|
|
772
|
-
totalHeight: 0
|
|
773
|
-
};
|
|
774
|
-
}
|
|
775
775
|
const emptyMetrics = calculateEmptyParagraphMetrics(attrs?.defaultFontSize ?? DEFAULT_FONT_SIZE, spacing, attrs?.defaultFontFamily ?? DEFAULT_FONT_FAMILY, attrs);
|
|
776
776
|
const outlineLineHeight = attrs?.reserveEmptyOutlineHeight ? emptyMetrics.lineHeight * 2 : emptyMetrics.lineHeight;
|
|
777
777
|
lines.push({
|
|
@@ -987,11 +987,11 @@ function measureParagraph(block, maxWidth, options) {
|
|
|
987
987
|
let tabWidth = tabResult.width;
|
|
988
988
|
const authoredEndpoint = contentX + tabWidth + followingWidth;
|
|
989
989
|
const activeContentRightEdge = maxWidth - currentLine.rightOffset;
|
|
990
|
-
const
|
|
990
|
+
const preservesAuthoredEndStop = tabResult.alignment === "end" && authoredEndpoint <= activeContentRightEdge + WIDTH_TOLERANCE;
|
|
991
991
|
const landsOnLeftIndent = tabResult.alignment === "start" && indentLeft > 0 && Math.abs(contentX + tabWidth - indentLeft) <= WIDTH_TOLERANCE;
|
|
992
992
|
const lineRightEdgeX = indentLeft + (isFirstLine ? firstLineOffset + markerInlineWidth : 0) + currentLine.availableWidth + currentLine.leftOffset;
|
|
993
|
-
if (!
|
|
994
|
-
if (
|
|
993
|
+
if (!preservesAuthoredEndStop && !landsOnLeftIndent && !hasFollowingTabOnLine(runs, runIndex) && canClampTabToRightEdge(tabResult.alignment, currentLine.width, hasPriorTabOnLine(runs, runIndex), followingWidth, currentLine.availableWidth) && (tabWidth > 0 || followingWidth > 0) && contentX + tabWidth + followingWidth > lineRightEdgeX + WIDTH_TOLERANCE) tabWidth = Math.max(1, lineRightEdgeX - contentX - followingWidth);
|
|
994
|
+
if (preservesAuthoredEndStop) currentLine.availableWidth = Math.max(currentLine.availableWidth, currentLine.width + tabWidth + followingWidth);
|
|
995
995
|
if (currentLine.width + tabWidth > currentLine.availableWidth + WIDTH_TOLERANCE) {
|
|
996
996
|
startNewLine(runIndex, 0);
|
|
997
997
|
updateMaxFont(style);
|
|
@@ -24,6 +24,7 @@ import { panic } from "better-result";
|
|
|
24
24
|
* Renders a single page from Layout data to DOM elements.
|
|
25
25
|
* Each page contains positioned fragments within a content area.
|
|
26
26
|
*/
|
|
27
|
+
const TEXT_BOX_ANCHOR_BLOCK_ID = Symbol.for("stll.textBoxAnchorBlockId");
|
|
27
28
|
const floatingTableReservesBand = ({ contentX, tableWidth, contentWidth, distLeft, distRight }) => {
|
|
28
29
|
const leftClearance = Math.max(0, contentX - distLeft);
|
|
29
30
|
const rightClearance = Math.max(0, contentWidth - contentX - tableWidth - distRight);
|
|
@@ -442,6 +443,7 @@ function renderHeaderFooterContent(content, context, options, layout) {
|
|
|
442
443
|
...context,
|
|
443
444
|
positioning: "absolute"
|
|
444
445
|
};
|
|
446
|
+
const paragraphStartYByBlockId = /* @__PURE__ */ new Map();
|
|
445
447
|
for (let i = 0; i < content.blocks.length; i++) {
|
|
446
448
|
const block = content.blocks[i];
|
|
447
449
|
const measure = content.measures[i];
|
|
@@ -450,6 +452,7 @@ function renderHeaderFooterContent(content, context, options, layout) {
|
|
|
450
452
|
const paragraphBlock = block;
|
|
451
453
|
const paragraphMeasure = measure;
|
|
452
454
|
const paragraphStartY = cursorY;
|
|
455
|
+
paragraphStartYByBlockId.set(paragraphBlock.id, paragraphStartY);
|
|
453
456
|
const inlineRuns = [];
|
|
454
457
|
for (const run of paragraphBlock.runs) if (run.kind === "image" && (isFloatingImageRun(run) || run.position)) floatingImages.push({
|
|
455
458
|
src: run.src,
|
|
@@ -530,9 +533,11 @@ function renderHeaderFooterContent(content, context, options, layout) {
|
|
|
530
533
|
document: doc,
|
|
531
534
|
renderTable: renderNestedTable
|
|
532
535
|
});
|
|
536
|
+
const anchorBlockId = Reflect.get(block, TEXT_BOX_ANCHOR_BLOCK_ID);
|
|
537
|
+
const paragraphY = block.position?.vertical?.relativeTo === "paragraph" && typeof anchorBlockId === "string" ? paragraphStartYByBlockId.get(anchorBlockId) ?? cursorY : cursorY;
|
|
533
538
|
const textBoxTop = block.position ? resolveHeaderFooterFloatTop({
|
|
534
539
|
height: measure.height,
|
|
535
|
-
paragraphY
|
|
540
|
+
paragraphY,
|
|
536
541
|
position: block.position
|
|
537
542
|
}, layout) : cursorY;
|
|
538
543
|
fragEl.style.top = `${textBoxTop}px`;
|
|
@@ -14,7 +14,7 @@ import "../utils/fontWeights.js";
|
|
|
14
14
|
import { getHorizontalScaleFactor } from "../utils/horizontalScale.js";
|
|
15
15
|
import { resolvePhysicalParagraphInlineLayout } from "../utils/paragraphInlineLayout.js";
|
|
16
16
|
import { inlineImageBoundingBox, parseRotationDegrees, rotatedBoundingBox } from "../utils/rotationBoundingBox.js";
|
|
17
|
-
import {
|
|
17
|
+
import { sanitizeImageSrc } from "../utils/sanitizeImageSrc.js";
|
|
18
18
|
import { SCRIPT_CLASS, hasCjk, hasComplexScript, segmentByScript } from "../utils/scriptSegments.js";
|
|
19
19
|
import { sanitizeExternalUrl } from "../utils/urlSecurity.js";
|
|
20
20
|
import { borderStrokeToCss, resolveParagraphBorderHorizontalOutsets } from "./borderStroke.js";
|
|
@@ -42,6 +42,14 @@ const PARAGRAPH_CLASS_NAMES = {
|
|
|
42
42
|
image: "layout-run-image",
|
|
43
43
|
lineBreak: "layout-run-linebreak"
|
|
44
44
|
};
|
|
45
|
+
const applyImageRunSource = (img, src) => {
|
|
46
|
+
const safeSrc = sanitizeImageSrc(src);
|
|
47
|
+
if (safeSrc === void 0) {
|
|
48
|
+
img.style.visibility = "hidden";
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
img.src = safeSrc;
|
|
52
|
+
};
|
|
45
53
|
const LEFT_TO_RIGHT_DIRECTION = "ltr";
|
|
46
54
|
const RIGHT_TO_LEFT_DIRECTION = "rtl";
|
|
47
55
|
const DISPLAYED_URL_PATTERN = /^(?:https?:\/\/|www\.)\S+$/iu;
|
|
@@ -488,7 +496,7 @@ function getLeaderChar(leader) {
|
|
|
488
496
|
function renderInlineImageRun(run, doc) {
|
|
489
497
|
const img = doc.createElement("img");
|
|
490
498
|
img.className = `${PARAGRAPH_CLASS_NAMES.run} ${PARAGRAPH_CLASS_NAMES.image}`;
|
|
491
|
-
|
|
499
|
+
applyImageRunSource(img, run.src);
|
|
492
500
|
img.width = run.width;
|
|
493
501
|
img.height = run.height;
|
|
494
502
|
img.style.width = `${run.width}px`;
|
|
@@ -556,7 +564,7 @@ function renderBlockImage(run, doc) {
|
|
|
556
564
|
container.style.marginTop = `${run.distTop ?? 6}px`;
|
|
557
565
|
container.style.marginBottom = `${run.distBottom ?? 6}px`;
|
|
558
566
|
const img = doc.createElement("img");
|
|
559
|
-
|
|
567
|
+
applyImageRunSource(img, run.src);
|
|
560
568
|
img.width = run.width;
|
|
561
569
|
img.height = run.height;
|
|
562
570
|
if (run.alt) img.alt = run.alt;
|
|
@@ -1241,7 +1249,7 @@ function renderLine(block, line, alignment, doc, options) {
|
|
|
1241
1249
|
}
|
|
1242
1250
|
}
|
|
1243
1251
|
lineEl.style.whiteSpace = "pre";
|
|
1244
|
-
lineEl.style.overflow = "visible";
|
|
1252
|
+
lineEl.style.overflow = block.attrs?.suppressEmptyParagraphHeight ? "hidden" : "visible";
|
|
1245
1253
|
let tabContext;
|
|
1246
1254
|
const hasScaledTextRun = runsForLine.some((run) => (isTextRun(run) || isFieldRun(run) || isMathRun(run)) && getHorizontalScaleFactor(run.horizontalScale) !== 1);
|
|
1247
1255
|
const measureText = collapsedSpaceMeasureText ?? (hasTabRuns || hasScaledTextRun ? createTextMeasurer(doc) : void 0);
|
|
@@ -1285,7 +1293,11 @@ function renderLine(block, line, alignment, doc, options) {
|
|
|
1285
1293
|
break;
|
|
1286
1294
|
}
|
|
1287
1295
|
}
|
|
1288
|
-
|
|
1296
|
+
const authoredEndpoint = currentX + tabResult.width + followingWidthForCheck;
|
|
1297
|
+
const activeContentRightEdge = options?.contentWidthPx === void 0 ? void 0 : options.contentWidthPx - (options.floatingMargins?.rightMargin ?? 0);
|
|
1298
|
+
const preservesAuthoredEndStop = activeContentRightEdge !== void 0 && tabResult.alignment === "end" && authoredEndpoint <= activeContentRightEdge + RIGHT_EDGE_EPSILON_PX;
|
|
1299
|
+
const preservesAuthoredEndStopPastIndent = lineRightEdgeX !== void 0 && preservesAuthoredEndStop && authoredEndpoint > lineRightEdgeX + RIGHT_EDGE_EPSILON_PX;
|
|
1300
|
+
if (lineRightEdgeX !== void 0 && options?.isRtl !== true && tabResult.alignment === "end" && !hasFollowingTab && !preservesAuthoredEndStopPastIndent && authoredEndpoint >= lineRightEdgeX - RIGHT_EDGE_EPSILON_PX) {
|
|
1289
1301
|
lineEl.style.display = "flex";
|
|
1290
1302
|
lineEl.style.alignItems = "baseline";
|
|
1291
1303
|
lineEl.style.whiteSpace = "pre";
|
|
@@ -1320,10 +1332,8 @@ function renderLine(block, line, alignment, doc, options) {
|
|
|
1320
1332
|
break;
|
|
1321
1333
|
}
|
|
1322
1334
|
let tabWidth = tabResult.width;
|
|
1323
|
-
const activeContentRightEdge = options?.contentWidthPx === void 0 ? void 0 : options.contentWidthPx - (options.floatingMargins?.rightMargin ?? 0);
|
|
1324
|
-
const preservesLogicalRtlEndStop = options?.isRtl === true && tabResult.alignment === "end" && activeContentRightEdge !== void 0 && currentX + tabWidth + followingWidthForCheck <= activeContentRightEdge + RIGHT_EDGE_EPSILON_PX;
|
|
1325
1335
|
const landsOnLeftIndent = tabResult.alignment === "start" && tabLeftIndentPx > 0 && Math.abs(currentX + tabWidth - tabLeftIndentPx) <= RIGHT_EDGE_EPSILON_PX;
|
|
1326
|
-
if (!
|
|
1336
|
+
if (!preservesAuthoredEndStop && !landsOnLeftIndent && lineRightEdgeX !== void 0 && canClampTabToRightEdge(tabResult.alignment, i > 0, runsForLine.slice(0, i).some(isTabRun), options?.isLastLine === true) && currentX + tabWidth + followingWidthForCheck > lineRightEdgeX) tabWidth = Math.max(1, lineRightEdgeX - currentX - followingWidthForCheck);
|
|
1327
1337
|
const tabEl = renderTabRun(run, doc, tabWidth, tabResult.leader);
|
|
1328
1338
|
lineEl.append(tabEl);
|
|
1329
1339
|
currentX += tabWidth;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { resolveFontFamily } from "../utils/fontResolver.js";
|
|
2
2
|
import { applySanitizedImageSrc } from "../utils/sanitizeImageSrc.js";
|
|
3
|
+
import { pointsToPixels } from "../utils/units.js";
|
|
3
4
|
//#region src/layout-painter/renderWatermark.ts
|
|
4
5
|
const WATERMARK_CLASS = "layout-page-watermark";
|
|
6
|
+
const PICTURE_WASHOUT_OPACITY = .18;
|
|
5
7
|
/**
|
|
6
8
|
* Build the watermark overlay element for a page. Returns `null` when
|
|
7
9
|
* the watermark is a picture without a resolved `imageSrc` (the
|
|
@@ -51,10 +53,15 @@ function renderPictureWatermark(watermark, imageSrc, doc) {
|
|
|
51
53
|
applySanitizedImageSrc(img, imageSrc);
|
|
52
54
|
img.alt = "";
|
|
53
55
|
img.setAttribute("aria-hidden", "true");
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
if (watermark.widthPt !== void 0 && watermark.heightPt !== void 0) {
|
|
57
|
+
img.style.width = `${pointsToPixels(watermark.widthPt)}px`;
|
|
58
|
+
img.style.height = `${pointsToPixels(watermark.heightPt)}px`;
|
|
59
|
+
} else {
|
|
60
|
+
const scalePct = (watermark.scale ?? 1) * 100;
|
|
61
|
+
img.style.maxWidth = `${scalePct}%`;
|
|
62
|
+
img.style.maxHeight = `${scalePct}%`;
|
|
63
|
+
}
|
|
64
|
+
img.style.opacity = String(watermark.washout === false ? 1 : PICTURE_WASHOUT_OPACITY);
|
|
58
65
|
img.style.objectFit = "contain";
|
|
59
66
|
return img;
|
|
60
67
|
}
|
|
@@ -91,6 +91,11 @@ const RUN_FORMATTING_OVERRIDE_BOOLEAN_KEYS = [
|
|
|
91
91
|
"outline"
|
|
92
92
|
];
|
|
93
93
|
const RUN_FORMATTING_OVERRIDE_FALSE_KEYS = ["doubleStrike", "rtl"];
|
|
94
|
+
const RUN_FORMATTING_OVERRIDE_DIRECT_FONT_PROPERTIES = [
|
|
95
|
+
"fontFamily",
|
|
96
|
+
"fontSize",
|
|
97
|
+
"color"
|
|
98
|
+
];
|
|
94
99
|
const SECTION_ORIENTATIONS = ["portrait", "landscape"];
|
|
95
100
|
const SECTION_START_TYPES = [
|
|
96
101
|
"continuous",
|
|
@@ -678,6 +683,12 @@ const readRunFormattingOverrideMarkAttrs = (mark) => {
|
|
|
678
683
|
optionalNumber(attrs, "fontSizeCs", "runFormattingOverride.attrs.fontSizeCs", issues);
|
|
679
684
|
optionalBoolean(attrs, "cs", "runFormattingOverride.attrs.cs", issues);
|
|
680
685
|
optionalOneOf(attrs, "underline", "runFormattingOverride.attrs.underline", issues, ["none"]);
|
|
686
|
+
optionalOneOfArray(attrs, "directFontProperties", "runFormattingOverride.attrs.directFontProperties", issues, RUN_FORMATTING_OVERRIDE_DIRECT_FONT_PROPERTIES);
|
|
687
|
+
const directFontProperties = attrs["directFontProperties"];
|
|
688
|
+
if (Array.isArray(directFontProperties) && new Set(directFontProperties).size !== directFontProperties.length) issues.push({
|
|
689
|
+
path: "runFormattingOverride.attrs.directFontProperties",
|
|
690
|
+
message: "Expected unique direct font properties."
|
|
691
|
+
});
|
|
681
692
|
return attrsResult(attrs, issues);
|
|
682
693
|
};
|
|
683
694
|
const expectRunFormattingOverrideMarkAttrs = (mark) => expectCachedMarkAttrs(mark, runFormattingOverrideAttrsCache, readRunFormattingOverrideMarkAttrs, "run formatting override attrs");
|
|
@@ -62,6 +62,7 @@ function resolveChange(from, to, mode, revisionIds) {
|
|
|
62
62
|
const keepType = mode === "accept" ? insertionType : deletionType;
|
|
63
63
|
const removeType = mode === "accept" ? deletionType : insertionType;
|
|
64
64
|
const revisionSet = revisionIds === void 0 ? null : new Set(revisionIds);
|
|
65
|
+
const removeKeptMarksInBulk = revisionSet === null && keepType !== void 0;
|
|
65
66
|
const matchesRevision = (mark) => revisionSet === null || typeof mark.attrs["revisionId"] === "number" && revisionSet.has(mark.attrs["revisionId"]);
|
|
66
67
|
if (dispatch) {
|
|
67
68
|
const tr = state.tr;
|
|
@@ -139,10 +140,29 @@ function resolveChange(from, to, mode, revisionIds) {
|
|
|
139
140
|
from: rangeFrom,
|
|
140
141
|
to: rangeTo
|
|
141
142
|
});
|
|
142
|
-
|
|
143
|
+
if (!removeKeptMarksInBulk) {
|
|
144
|
+
for (const mark of node.marks) if (keepType && mark.type === keepType && matchesRevision(mark)) tr.removeMark(rangeFrom, rangeTo, mark);
|
|
145
|
+
}
|
|
143
146
|
return true;
|
|
144
147
|
});
|
|
145
|
-
|
|
148
|
+
if (removeKeptMarksInBulk) tr.removeMark(from, to, keepType);
|
|
149
|
+
let rangesToDelete = deleteRanges;
|
|
150
|
+
if (revisionSet === null) {
|
|
151
|
+
const coalescedDeleteRanges = [];
|
|
152
|
+
for (const range of deleteRanges) {
|
|
153
|
+
const previous = coalescedDeleteRanges.at(-1);
|
|
154
|
+
if (previous && range.from <= previous.to) {
|
|
155
|
+
previous.to = Math.max(previous.to, range.to);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
coalescedDeleteRanges.push({
|
|
159
|
+
from: range.from,
|
|
160
|
+
to: range.to
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
rangesToDelete = coalescedDeleteRanges;
|
|
164
|
+
}
|
|
165
|
+
for (const range of rangesToDelete.toReversed()) tr.delete(range.from, range.to);
|
|
146
166
|
pPrMarkOps.sort((a, b) => b.paragraphPos - a.paragraphPos);
|
|
147
167
|
for (const op of pPrMarkOps) {
|
|
148
168
|
const mappedPos = tr.mapping.map(op.paragraphPos);
|
|
@@ -689,9 +689,9 @@ function createTrackedRunWrapper(type, info, child) {
|
|
|
689
689
|
content
|
|
690
690
|
};
|
|
691
691
|
}
|
|
692
|
-
function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, textBoxAnchorMarkers, skipLeadingRenderedPageBreak = false) {
|
|
692
|
+
function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, textBoxAnchorMarkers, skipLeadingRenderedPageBreak = false, inheritedFormattingOverride) {
|
|
693
693
|
const content = [];
|
|
694
|
-
const inheritedFormatting = paragraph.type.name === "paragraph" ? expectParagraphAttrs(paragraph).defaultTextFormatting ?? void 0 : void 0;
|
|
694
|
+
const inheritedFormatting = inheritedFormattingOverride ?? (paragraph.type.name === "paragraph" ? expectParagraphAttrs(paragraph).defaultTextFormatting ?? void 0 : void 0);
|
|
695
695
|
const sortedEmptyHyperlinks = (emptyHyperlinks ?? []).map((attrs, order) => ({
|
|
696
696
|
attrs,
|
|
697
697
|
order
|
|
@@ -966,7 +966,7 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
|
|
|
966
966
|
}));
|
|
967
967
|
} else if (node.type.name === "sdt") {
|
|
968
968
|
flushCurrentInline();
|
|
969
|
-
content.push(createInlineSdtFromNode(node, textBoxAnchorMarkers));
|
|
969
|
+
content.push(createInlineSdtFromNode(node, textBoxAnchorMarkers, inheritedFormatting));
|
|
970
970
|
} else if (node.type.name === "math") {
|
|
971
971
|
flushCurrentInline();
|
|
972
972
|
content.push(createMathFromNode(node));
|
|
@@ -1369,11 +1369,11 @@ function createMathFromNode(node) {
|
|
|
1369
1369
|
/**
|
|
1370
1370
|
* Create an InlineSdt from a PM sdt node
|
|
1371
1371
|
*/
|
|
1372
|
-
function createInlineSdtFromNode(node, textBoxAnchorMarkers) {
|
|
1372
|
+
function createInlineSdtFromNode(node, textBoxAnchorMarkers, inheritedFormatting) {
|
|
1373
1373
|
return {
|
|
1374
1374
|
type: "inlineSdt",
|
|
1375
1375
|
properties: sdtPropertiesFromAttrs(expectSdtAttrs(node)),
|
|
1376
|
-
content: extractParagraphContent(node, void 0, void 0, textBoxAnchorMarkers).filter((c) => c.type === "run" || c.type === "hyperlink" || c.type === "simpleField" || c.type === "complexField" || c.type === "inlineSdt" || c.type === "insertion" || c.type === "deletion" || c.type === "moveFrom" || c.type === "moveTo" || c.type === "mathEquation")
|
|
1376
|
+
content: extractParagraphContent(node, void 0, void 0, textBoxAnchorMarkers, false, inheritedFormatting).filter((c) => c.type === "run" || c.type === "hyperlink" || c.type === "simpleField" || c.type === "complexField" || c.type === "inlineSdt" || c.type === "insertion" || c.type === "deletion" || c.type === "moveFrom" || c.type === "moveTo" || c.type === "mathEquation")
|
|
1377
1377
|
};
|
|
1378
1378
|
}
|
|
1379
1379
|
/**
|
|
@@ -1524,6 +1524,7 @@ function createShapeRun(node) {
|
|
|
1524
1524
|
function marksToTextFormatting(marks, options) {
|
|
1525
1525
|
const formatting = {};
|
|
1526
1526
|
let directOverrideFormatting;
|
|
1527
|
+
let directFontProperties;
|
|
1527
1528
|
let characterStyleRPr;
|
|
1528
1529
|
let runFormattingOverrideMark;
|
|
1529
1530
|
for (const mark of marks) switch (mark.type.name) {
|
|
@@ -1651,9 +1652,14 @@ function marksToTextFormatting(marks, options) {
|
|
|
1651
1652
|
}
|
|
1652
1653
|
if (runFormattingOverrideMark) {
|
|
1653
1654
|
const overrideAttrs = expectRunFormattingOverrideMarkAttrs(runFormattingOverrideMark);
|
|
1655
|
+
directFontProperties = overrideAttrs.directFontProperties;
|
|
1654
1656
|
applyRunFormattingOverrideAttrs(formatting, overrideAttrs);
|
|
1655
1657
|
directOverrideFormatting = {};
|
|
1656
1658
|
applyRunFormattingOverrideAttrs(directOverrideFormatting, overrideAttrs);
|
|
1659
|
+
for (const property of directFontProperties ?? []) {
|
|
1660
|
+
const value = formatting[property];
|
|
1661
|
+
if (value !== void 0) Reflect.set(directOverrideFormatting, property, value);
|
|
1662
|
+
}
|
|
1657
1663
|
}
|
|
1658
1664
|
if (characterStyleRPr) return subtractCharacterStyleFormatting({
|
|
1659
1665
|
directOverrideFormatting,
|
|
@@ -1661,8 +1667,29 @@ function marksToTextFormatting(marks, options) {
|
|
|
1661
1667
|
inheritedFormatting: options?.inheritedFormatting,
|
|
1662
1668
|
styleRPr: characterStyleRPr
|
|
1663
1669
|
});
|
|
1670
|
+
for (const property of [
|
|
1671
|
+
"fontFamily",
|
|
1672
|
+
"fontSize",
|
|
1673
|
+
"color"
|
|
1674
|
+
]) {
|
|
1675
|
+
if (directFontProperties?.includes(property)) continue;
|
|
1676
|
+
const value = formatting[property];
|
|
1677
|
+
const inheritedValue = options?.inheritedFormatting?.[property];
|
|
1678
|
+
const matchesInherited = property === "fontFamily" ? sameFontFamily(formatting.fontFamily, options?.inheritedFormatting?.fontFamily) : JSON.stringify(value) === JSON.stringify(inheritedValue);
|
|
1679
|
+
if (inheritedValue !== void 0 && matchesInherited) Reflect.deleteProperty(formatting, property);
|
|
1680
|
+
}
|
|
1664
1681
|
return formatting;
|
|
1665
1682
|
}
|
|
1683
|
+
const sameFontFamily = (left, right) => [
|
|
1684
|
+
"ascii",
|
|
1685
|
+
"hAnsi",
|
|
1686
|
+
"eastAsia",
|
|
1687
|
+
"hint",
|
|
1688
|
+
"asciiTheme",
|
|
1689
|
+
"hAnsiTheme",
|
|
1690
|
+
"eastAsiaTheme",
|
|
1691
|
+
"csTheme"
|
|
1692
|
+
].every((property) => left?.[property] === right?.[property]) && (left?.cs ?? left?.ascii) === (right?.cs ?? right?.ascii);
|
|
1666
1693
|
/**
|
|
1667
1694
|
* Negatable boolean run-property keys whose serializer emits an explicit
|
|
1668
1695
|
* `w:val="0"` override when the value is `false` (see `serializeTextFormatting`
|
|
@@ -18,6 +18,7 @@ import { shadingToRunShadingAttrs } from "./runShadingMark.js";
|
|
|
18
18
|
import { sdtAttrsFromProperties } from "./sdtAttrs.js";
|
|
19
19
|
import { panic } from "better-result";
|
|
20
20
|
//#region src/prosemirror/conversion/toProseDoc.ts
|
|
21
|
+
const DETACHED_WATERMARK_HOST = Symbol.for("stll.detachedWatermarkHost");
|
|
21
22
|
/**
|
|
22
23
|
* Build a `nextTextBoxGroupId()` generator salted with a random per-load
|
|
23
24
|
* nonce, so minted text-box anchor ids (`<salt>:<group>:<index>`) are unique
|
|
@@ -139,7 +140,12 @@ function toProseDoc(document, options) {
|
|
|
139
140
|
};
|
|
140
141
|
nodes.push(...convertBodyBlocks(paragraphs));
|
|
141
142
|
if (nodes.length === 0) nodes.push(schema.node("paragraph", {}, []));
|
|
142
|
-
const
|
|
143
|
+
const finalSectionStart = document.package.document.sections?.at(-1)?.properties.sectionStart ?? null;
|
|
144
|
+
const adjustLineHeightInTable = document.package.settings?.adjustLineHeightInTable === true;
|
|
145
|
+
const pmDoc = stampNumberedRefFieldBaselines(schema.node("doc", {
|
|
146
|
+
_finalSectionStart: finalSectionStart,
|
|
147
|
+
_adjustLineHeightInTable: adjustLineHeightInTable
|
|
148
|
+
}, nodes));
|
|
143
149
|
assertValidProseMirrorDocument(pmDoc, "Document conversion produced an invalid ProseMirror document");
|
|
144
150
|
return pmDoc;
|
|
145
151
|
}
|
|
@@ -210,6 +216,7 @@ function convertParagraph(paragraph, styleResolver, nextHyperlinkInstanceIndex,
|
|
|
210
216
|
const paragraphRunFormatting = resolveRunFormattingWithoutDefaults(paragraph.formatting?.runProperties, styleResolver);
|
|
211
217
|
let inheritableParagraphRunFormatting;
|
|
212
218
|
if (paragraphRunFormatting && !isTocParagraph) inheritableParagraphRunFormatting = stripParagraphMarkFormattingForBodyRuns(paragraphRunFormatting);
|
|
219
|
+
const ordinaryStyleFormatting = paragraph.formatting?.styleId === void 0 ? mergeTextFormatting(styleRunFormatting, extraRunFormatting) : mergeTextFormatting(extraRunFormatting, styleRunFormatting);
|
|
213
220
|
const orderedToggleFormatting = cascadeStyleTextFormatting([
|
|
214
221
|
{
|
|
215
222
|
formatting: styleResolver?.getDocDefaults()?.rPr,
|
|
@@ -223,7 +230,7 @@ function convertParagraph(paragraph, styleResolver, nextHyperlinkInstanceIndex,
|
|
|
223
230
|
formatting: paragraphStyleRunFormatting,
|
|
224
231
|
type: "style"
|
|
225
232
|
}
|
|
226
|
-
], { ordinaryFormatting:
|
|
233
|
+
], { ordinaryFormatting: ordinaryStyleFormatting });
|
|
227
234
|
let baseRunFormatting = orderedToggleFormatting.formatting;
|
|
228
235
|
if (paragraphStyleFontFamily) baseRunFormatting = mergeTextFormatting(baseRunFormatting, { fontFamily: paragraphStyleFontFamily });
|
|
229
236
|
const paragraphMarkPrecedesStyle = paragraph.formatting?.styleId !== void 0;
|
|
@@ -1000,10 +1007,10 @@ function convertTableRow(row, styleResolver, context, isHeaderRow, columnWidths,
|
|
|
1000
1007
|
let cellConditionalStyle = conditionalStyles?.wholeTable;
|
|
1001
1008
|
cellConditionalStyle = mergeConditionalStyles(cellConditionalStyle, effectiveRowBandStyle);
|
|
1002
1009
|
cellConditionalStyle = mergeConditionalStyles(cellConditionalStyle, vertBandStyle);
|
|
1003
|
-
if (cellIsFirstRow && (tableLook?.firstRow || rowCnf?.firstRow || cellCnf?.firstRow)) cellConditionalStyle = mergeConditionalStyles(cellConditionalStyle, conditionalStyles?.firstRow);
|
|
1004
|
-
if (cellIsLastRow && (tableLook?.lastRow || rowCnf?.lastRow || cellCnf?.lastRow)) cellConditionalStyle = mergeConditionalStyles(cellConditionalStyle, conditionalStyles?.lastRow);
|
|
1005
1010
|
if (cellIsFirstCol && (tableLook?.firstColumn || rowCnf?.firstColumn || cellCnf?.firstColumn)) cellConditionalStyle = mergeConditionalStyles(cellConditionalStyle, conditionalStyles?.firstCol);
|
|
1006
1011
|
if (cellIsLastCol && (tableLook?.lastColumn || rowCnf?.lastColumn || cellCnf?.lastColumn)) cellConditionalStyle = mergeConditionalStyles(cellConditionalStyle, conditionalStyles?.lastCol);
|
|
1012
|
+
if (cellIsFirstRow && (tableLook?.firstRow || rowCnf?.firstRow || cellCnf?.firstRow)) cellConditionalStyle = mergeConditionalStyles(cellConditionalStyle, conditionalStyles?.firstRow);
|
|
1013
|
+
if (cellIsLastRow && (tableLook?.lastRow || rowCnf?.lastRow || cellCnf?.lastRow)) cellConditionalStyle = mergeConditionalStyles(cellConditionalStyle, conditionalStyles?.lastRow);
|
|
1007
1014
|
if (cellIsFirstRow && cellIsFirstCol && (tableLook?.firstRow || rowCnf?.firstRow || cellCnf?.firstRow) && (tableLook?.firstColumn || rowCnf?.firstColumn || cellCnf?.firstColumn)) cellConditionalStyle = mergeConditionalStyles(cellConditionalStyle, conditionalStyles?.nwCell);
|
|
1008
1015
|
if (cellIsFirstRow && cellIsLastCol && (tableLook?.firstRow || rowCnf?.firstRow || cellCnf?.firstRow) && (tableLook?.lastColumn || rowCnf?.lastColumn || cellCnf?.lastColumn)) cellConditionalStyle = mergeConditionalStyles(cellConditionalStyle, conditionalStyles?.neCell);
|
|
1009
1016
|
if (cellIsLastRow && cellIsFirstCol && (tableLook?.lastRow || rowCnf?.lastRow || cellCnf?.lastRow) && (tableLook?.firstColumn || rowCnf?.firstColumn || cellCnf?.firstColumn)) cellConditionalStyle = mergeConditionalStyles(cellConditionalStyle, conditionalStyles?.swCell);
|
|
@@ -1265,6 +1272,7 @@ function buildRunMarks(runFormatting, inherited, styleResolver) {
|
|
|
1265
1272
|
hasCharacterStyle: styleId !== void 0,
|
|
1266
1273
|
paragraphMarkOverrides: inherited.paragraphMarkOverrides
|
|
1267
1274
|
}) });
|
|
1275
|
+
addDirectFontProvenance(marks, runFormatting);
|
|
1268
1276
|
if (styleId) {
|
|
1269
1277
|
const styleRPr = characterStyleFormatting ? marksToTextFormatting(textFormattingToMarks(characterStyleFormatting)) : void 0;
|
|
1270
1278
|
marks.push(schema.mark("characterStyle", {
|
|
@@ -1277,6 +1285,24 @@ function buildRunMarks(runFormatting, inherited, styleResolver) {
|
|
|
1277
1285
|
mergedFormatting
|
|
1278
1286
|
};
|
|
1279
1287
|
}
|
|
1288
|
+
const addDirectFontProvenance = (marks, directFormatting) => {
|
|
1289
|
+
const directFontProperties = [];
|
|
1290
|
+
if (directFormatting?.fontFamily !== void 0) directFontProperties.push("fontFamily");
|
|
1291
|
+
if (directFormatting?.fontSize !== void 0) directFontProperties.push("fontSize");
|
|
1292
|
+
if (directFormatting?.color !== void 0) directFontProperties.push("color");
|
|
1293
|
+
if (!directFormatting || !Object.keys(directFormatting).some((property) => property !== "styleId")) return;
|
|
1294
|
+
const index = marks.findIndex(({ type }) => type.name === "runFormattingOverride");
|
|
1295
|
+
const existing = index >= 0 ? marks.at(index) : void 0;
|
|
1296
|
+
const override = schema.mark("runFormattingOverride", {
|
|
1297
|
+
...existing?.attrs,
|
|
1298
|
+
...directFontProperties.length > 0 && { directFontProperties }
|
|
1299
|
+
});
|
|
1300
|
+
if (index >= 0) {
|
|
1301
|
+
marks[index] = override;
|
|
1302
|
+
return;
|
|
1303
|
+
}
|
|
1304
|
+
marks.push(override);
|
|
1305
|
+
};
|
|
1280
1306
|
const ORDINARY_STYLE_TOGGLE_KEYS = [
|
|
1281
1307
|
"bold",
|
|
1282
1308
|
"italic",
|
|
@@ -1658,12 +1684,12 @@ function convertShape(shape) {
|
|
|
1658
1684
|
position
|
|
1659
1685
|
});
|
|
1660
1686
|
}
|
|
1661
|
-
function convertParagraphWithTextBoxes(block, styleResolver, { textBoxGroupId, context, extraRunFormatting, tableParagraphOverlay }) {
|
|
1687
|
+
function convertParagraphWithTextBoxes(block, styleResolver, { textBoxGroupId, context, extraRunFormatting, preserveEmptyWrapper, tableParagraphOverlay }) {
|
|
1662
1688
|
const { textBoxes, textBoxAnchors } = extractTextBoxesFromParagraph(block, textBoxGroupId);
|
|
1663
1689
|
const pmParagraph = convertParagraph(block, styleResolver, context.nextHyperlinkInstanceIndex, context.pairedBookmarkIds, void 0, extraRunFormatting, tableParagraphOverlay, textBoxAnchors);
|
|
1664
1690
|
const nodes = [];
|
|
1665
1691
|
const isEmptyAfterExtraction = textBoxes.length > 0 && !hasContentBesidesTextBoxAnchors(pmParagraph);
|
|
1666
|
-
const keepWrapperParagraph = isEmptyAfterExtraction && hasParagraphBoundaryPayload(block, pmParagraph);
|
|
1692
|
+
const keepWrapperParagraph = isEmptyAfterExtraction && (preserveEmptyWrapper === true || hasParagraphBoundaryPayload(block, pmParagraph));
|
|
1667
1693
|
if (!isEmptyAfterExtraction || keepWrapperParagraph) nodes.push(pmParagraph);
|
|
1668
1694
|
for (const { textBox, anchorId, trackedChange, inlineSdts } of textBoxes) nodes.push(convertTextBox(textBox, styleResolver, {
|
|
1669
1695
|
placement: isEmptyAfterExtraction && !keepWrapperParagraph ? "standalone" : "inlineWithPrevious",
|
|
@@ -1929,11 +1955,23 @@ function headerFooterToProseDoc(content, options) {
|
|
|
1929
1955
|
};
|
|
1930
1956
|
const convertBlocks = (blocks) => {
|
|
1931
1957
|
const out = [];
|
|
1932
|
-
for (const block of blocks) if (block.type === "paragraph")
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1958
|
+
for (const block of blocks) if (block.type === "paragraph") {
|
|
1959
|
+
const isDetachedWatermarkHost = Reflect.get(block, DETACHED_WATERMARK_HOST) === true;
|
|
1960
|
+
const paragraphNodes = convertParagraphWithTextBoxes(block, styleResolver, {
|
|
1961
|
+
textBoxGroupId: nextTextBoxGroupId(),
|
|
1962
|
+
context: conversionContext,
|
|
1963
|
+
preserveEmptyWrapper: isDetachedWatermarkHost
|
|
1964
|
+
});
|
|
1965
|
+
if (isDetachedWatermarkHost) {
|
|
1966
|
+
const paragraphNodeIndex = paragraphNodes.findIndex(({ type }) => type.name === "paragraph");
|
|
1967
|
+
const paragraphNode = paragraphNodes[paragraphNodeIndex];
|
|
1968
|
+
if (paragraphNode) paragraphNodes[paragraphNodeIndex] = paragraphNode.type.create({
|
|
1969
|
+
...paragraphNode.attrs,
|
|
1970
|
+
_detachedWatermarkHost: true
|
|
1971
|
+
}, paragraphNode.content, paragraphNode.marks);
|
|
1972
|
+
}
|
|
1973
|
+
out.push(...paragraphNodes);
|
|
1974
|
+
} else if (block.type === "table") out.push(convertTable(block, styleResolver, conversionContext));
|
|
1937
1975
|
else out.push(convertBlockSdt(block, convertBlocks));
|
|
1938
1976
|
return out;
|
|
1939
1977
|
};
|
|
@@ -6,7 +6,13 @@ import { createNodeExtension } from "../create.js";
|
|
|
6
6
|
const DocExtension = createNodeExtension({
|
|
7
7
|
name: "doc",
|
|
8
8
|
schemaNodeName: "doc",
|
|
9
|
-
nodeSpec: {
|
|
9
|
+
nodeSpec: {
|
|
10
|
+
attrs: {
|
|
11
|
+
_finalSectionStart: { default: null },
|
|
12
|
+
_adjustLineHeightInTable: { default: false }
|
|
13
|
+
},
|
|
14
|
+
content: "(paragraph | horizontalRule | pageBreak | table | textBox | blockSdt)+"
|
|
15
|
+
}
|
|
10
16
|
});
|
|
11
17
|
//#endregion
|
|
12
18
|
export { DocExtension };
|