@stll/folio-core 0.23.0 → 0.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/controller/layoutPipeline.js +7 -3
  2. package/dist/docx/blockContentParser.js +2 -47
  3. package/dist/docx/fieldParser.d.ts +1 -1
  4. package/dist/docx/fieldParser.js +12 -4
  5. package/dist/docx/numberingParser.d.ts +2 -11
  6. package/dist/docx/numberingParser.js +3 -79
  7. package/dist/docx/ooxmlCounterFormatter.d.ts +8 -0
  8. package/dist/docx/ooxmlCounterFormatter.js +134 -0
  9. package/dist/docx/serializer/tableSerializer.js +1 -1
  10. package/dist/docx/tableParser.js +2 -1
  11. package/dist/fields/bookmarkPages.js +1 -1
  12. package/dist/fields/evaluateField.js +1 -1
  13. package/dist/fields/fieldContext.d.ts +2 -0
  14. package/dist/fields/resolveFieldValues.js +5 -2
  15. package/dist/layout-bridge/convert/headerFooterLayout.js +6 -0
  16. package/dist/layout-bridge/convert/toFlowBlocks.js +16 -48
  17. package/dist/layout-engine/index.d.ts +3 -2
  18. package/dist/layout-engine/index.js +40 -16
  19. package/dist/layout-engine/measure/cache.js +1 -1
  20. package/dist/layout-engine/measure/complexScriptFormatting.d.ts +18 -0
  21. package/dist/layout-engine/measure/complexScriptFormatting.js +11 -0
  22. package/dist/layout-engine/measure/measureContainer.js +31 -16
  23. package/dist/layout-engine/measure/measureHelpers.js +4 -0
  24. package/dist/layout-engine/measure/measureParagraph.js +13 -3
  25. package/dist/layout-engine/measure/measureTypes.d.ts +4 -0
  26. package/dist/layout-engine/paginator.d.ts +5 -3
  27. package/dist/layout-engine/paginator.js +34 -13
  28. package/dist/layout-engine/types.d.ts +31 -4
  29. package/dist/layout-painter/index.js +2 -1
  30. package/dist/layout-painter/renderPage.js +9 -3
  31. package/dist/layout-painter/renderParagraph.js +42 -25
  32. package/dist/layout-painter/renderUtils.d.ts +2 -0
  33. package/dist/paged-layout/sectionGeometry.d.ts +3 -2
  34. package/dist/paged-layout/sectionGeometry.js +17 -1
  35. package/dist/prosemirror/attrs/index.js +5 -0
  36. package/dist/prosemirror/conversion/fromProseDoc.js +4 -16
  37. package/dist/prosemirror/conversion/toProseDoc.js +4 -1
  38. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.d.ts +3 -2
  39. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +31 -17
  40. package/dist/prosemirror/extensions/nodes/TableExtension.js +1 -0
  41. package/dist/prosemirror/schema/marks.d.ts +8 -0
  42. package/dist/prosemirror/schema/nodes.d.ts +2 -0
  43. package/package.json +1 -1
@@ -601,6 +601,7 @@ function renderFootnoteContent(footnote, contentWidth, doc, context) {
601
601
  wrapper.style.height = `${content.height}px`;
602
602
  const renderContext = {
603
603
  pageNumber: context?.pageNumber ?? 0,
604
+ ...context?.pageNumberFormat === void 0 ? {} : { pageNumberFormat: context.pageNumberFormat },
604
605
  totalPages: context?.totalPages ?? 0,
605
606
  section: context?.section ?? "body",
606
607
  contentWidth
@@ -1133,8 +1134,7 @@ function applySectionHeaderFooterOptions(page, pageOptions, options) {
1133
1134
  if (!refs) return false;
1134
1135
  const isFirstSectionPage = page.sectionPageNumber === 1;
1135
1136
  const useFirst = refs.titlePg === true && isFirstSectionPage;
1136
- const sectionPageNumber = page.sectionPageNumber ?? page.number;
1137
- const useEven = refs.evenAndOddHeaders === true && sectionPageNumber % 2 === 0;
1137
+ const useEven = refs.evenAndOddHeaders === true && page.logicalNumber % 2 === 0;
1138
1138
  const headerRId = (() => {
1139
1139
  if (useFirst) return refs.headerFirst;
1140
1140
  if (useEven) return refs.headerEven;
@@ -1176,7 +1176,8 @@ function sectionPagesContext(page, counts) {
1176
1176
  */
1177
1177
  function buildPageRenderArgs(page, totalPages, options) {
1178
1178
  const context = {
1179
- pageNumber: page.number,
1179
+ pageNumber: page.logicalNumber,
1180
+ ...page.logicalNumberFormat === void 0 ? {} : { pageNumberFormat: page.logicalNumberFormat },
1180
1181
  totalPages,
1181
1182
  section: "body",
1182
1183
  ...options.bookmarkPages ? { bookmarkPages: options.bookmarkPages } : {},
@@ -1224,6 +1225,7 @@ function computePageFingerprintInternal(page, blockLookup, options) {
1224
1225
  parts.push(`m:${pageMarginsFingerprint(page.margins)}`);
1225
1226
  if (page.authoredMargins) parts.push(`am:${pageMarginsFingerprint(page.authoredMargins)}`);
1226
1227
  parts.push(`n:${page.number}`);
1228
+ parts.push(`ln:${page.logicalNumber},${page.logicalNumberFormat ?? ""}`);
1227
1229
  if (page.sectionIndex !== void 0) parts.push(`si:${page.sectionIndex}`);
1228
1230
  if (page.sectionPageNumber !== void 0) parts.push(`sp:${page.sectionPageNumber}`);
1229
1231
  if (page.headerFooterRefs) parts.push(`hf:${JSON.stringify(page.headerFooterRefs)}`);
@@ -1334,6 +1336,10 @@ function runContentKey(run) {
1334
1336
  if (run.highlight) parts.push(`hi:${run.highlight}`);
1335
1337
  if (run.fontFamily) parts.push(`ff:${run.fontFamily}`);
1336
1338
  if (run.complexScriptFontFamily) parts.push(`cs:${run.complexScriptFontFamily}`);
1339
+ if (run.complexScriptFontSize !== void 0) parts.push(`cs-size:${run.complexScriptFontSize}`);
1340
+ if (run.complexScriptBold !== void 0) parts.push(`cs-bold:${run.complexScriptBold}`);
1341
+ if (run.complexScriptItalic !== void 0) parts.push(`cs-italic:${run.complexScriptItalic}`);
1342
+ if (run.forceComplexScript !== void 0) parts.push(`cs-force:${run.forceComplexScript}`);
1337
1343
  if (run.eastAsiaFontFamily) parts.push(`ea:${run.eastAsiaFontFamily}`);
1338
1344
  if (run.fontSize !== void 0) parts.push(`fs:${run.fontSize}`);
1339
1345
  if (run.letterSpacing !== void 0) parts.push(`ls:${run.letterSpacing}`);
@@ -1,6 +1,7 @@
1
1
  import { ommlToMathml } from "../docx/mathToMathml.js";
2
2
  import { parseXmlDocument } from "../docx/xmlParser.js";
3
3
  import { evaluateFieldInstruction } from "../fields/evaluateField.js";
4
+ import { hasComplexScriptFormatting, resolveComplexScriptFormatting } from "../layout-engine/measure/complexScriptFormatting.js";
4
5
  import { getListMarkerInlineWidth, getListMarkerVisualOffset } from "../layout-engine/measure/listMarkerWidth.js";
5
6
  import { DOCX_SCRIPT_FONT_SCALE } from "../layout-engine/measure/measureHelpers.js";
6
7
  import { FONT_KERNING_MODE, countCompressibleSpaces, getFontKerningMode, getRunFontKerningMode, toPaintedText } from "../layout-engine/measure/textMeasurementPolicy.js";
@@ -571,6 +572,7 @@ function resolveFieldText(run, context) {
571
572
  if (!context) return fallback;
572
573
  const fieldContext = {
573
574
  pageNumber: context.pageNumber,
575
+ ...context.pageNumberFormat === void 0 ? {} : { pageNumberFormat: context.pageNumberFormat },
574
576
  totalPages: context.totalPages,
575
577
  bookmarkPages: context.bookmarkPages ?? EMPTY_BOOKMARK_PAGES,
576
578
  bookmarkText: context.bookmarkText ?? EMPTY_BOOKMARK_TEXT,
@@ -591,6 +593,10 @@ function renderFieldRun(run, doc, context) {
591
593
  kind: "text",
592
594
  text
593
595
  };
596
+ if (resolvedRun.forceComplexScript && hasComplexScriptFormatting(resolvedRun)) return renderTextRun({
597
+ ...resolvedRun,
598
+ ...resolveComplexScriptFormatting(resolvedRun)
599
+ }, doc);
594
600
  if (needsPerScriptSpans({
595
601
  ...resolvedRun,
596
602
  text
@@ -607,7 +613,7 @@ function renderFieldRun(run, doc, context) {
607
613
  const segmentRun = {
608
614
  ...segmentBase,
609
615
  text: segment.text,
610
- ...scriptFontOverride(resolvedRun, segment.script)
616
+ ...scriptFormattingOverride(resolvedRun, segment.script)
611
617
  };
612
618
  wrapper.append(renderTextRun(segmentRun, doc));
613
619
  }
@@ -725,9 +731,9 @@ function sliceRunsForLine(block, line) {
725
731
  * The font a script segment paints with, mirroring the measurer's
726
732
  * `scriptFontFamily`. Returning undefined leaves the run's own `fontFamily`.
727
733
  */
728
- const scriptFontOverride = (run, script) => {
734
+ const scriptFormattingOverride = (run, script) => {
735
+ if (run.forceComplexScript || script === SCRIPT_CLASS.complex) return resolveComplexScriptFormatting(run);
729
736
  if (script === SCRIPT_CLASS.eastAsia && run.eastAsiaFontFamily !== void 0) return { fontFamily: run.eastAsiaFontFamily };
730
- if (script === SCRIPT_CLASS.complex && run.complexScriptFontFamily !== void 0) return { fontFamily: run.complexScriptFontFamily };
731
737
  return {};
732
738
  };
733
739
  /**
@@ -739,7 +745,7 @@ const scriptFontOverride = (run, script) => {
739
745
  const needsPerScriptSpans = (run) => {
740
746
  if (run.letterSpacing) return false;
741
747
  if (run.eastAsiaFontFamily !== void 0 && hasCjk(run.text)) return true;
742
- return run.complexScriptFontFamily !== void 0 && hasComplexScript(run.text);
748
+ return hasComplexScriptFormatting(run) && hasComplexScript(run.text);
743
749
  };
744
750
  /**
745
751
  * Split each text run carrying an East-Asian font into per-script sub-runs, so
@@ -753,6 +759,13 @@ const needsPerScriptSpans = (run) => {
753
759
  function splitTextRunsByEastAsia(runs) {
754
760
  const result = [];
755
761
  for (const run of runs) {
762
+ if (isTextRun(run) && run.forceComplexScript && hasComplexScriptFormatting(run)) {
763
+ result.push({
764
+ ...run,
765
+ ...resolveComplexScriptFormatting(run)
766
+ });
767
+ continue;
768
+ }
756
769
  if (!isTextRun(run) || !needsPerScriptSpans(run)) {
757
770
  result.push(run);
758
771
  continue;
@@ -762,7 +775,7 @@ function splitTextRunsByEastAsia(runs) {
762
775
  result.push({
763
776
  ...run,
764
777
  text: segment.text,
765
- ...scriptFontOverride(run, segment.script),
778
+ ...scriptFormattingOverride(run, segment.script),
766
779
  ...run.pmStart !== void 0 ? {
767
780
  pmStart: run.pmStart + offset,
768
781
  pmEnd: run.pmStart + offset + segment.text.length
@@ -866,6 +879,10 @@ function runMeasureStyle(run) {
866
879
  ...run.smallCaps !== void 0 ? { smallCaps: run.smallCaps } : {},
867
880
  ...run.eastAsiaFontFamily !== void 0 ? { eastAsiaFontFamily: run.eastAsiaFontFamily } : {},
868
881
  ...run.complexScriptFontFamily !== void 0 ? { complexScriptFontFamily: run.complexScriptFontFamily } : {},
882
+ ...run.complexScriptFontSize !== void 0 ? { complexScriptFontSize: run.complexScriptFontSize } : {},
883
+ ...run.complexScriptBold !== void 0 ? { complexScriptBold: run.complexScriptBold } : {},
884
+ ...run.complexScriptItalic !== void 0 ? { complexScriptItalic: run.complexScriptItalic } : {},
885
+ ...run.forceComplexScript !== void 0 ? { forceComplexScript: run.forceComplexScript } : {},
869
886
  kerning: getRunFontKerningMode(run, 11) === FONT_KERNING_MODE.enabled
870
887
  };
871
888
  }
@@ -972,27 +989,32 @@ function createTextMeasurer(doc) {
972
989
  return (text, fontSize = 11, fontFamily = "Calibri", style = {}) => {
973
990
  if (!ctx) return applyLetterSpacingToMeasuredWidth(text.length * 7, text, style.letterSpacing);
974
991
  ctx.fontKerning = getFontKerningMode(style);
975
- const cssFallback = resolveFontFamily(fontFamily).cssFallback;
976
- const fontSizePx = fontSize * 96 / 72;
977
- const fontPrefixParts = [];
978
- if (style.italic) fontPrefixParts.push("italic");
979
- if (style.smallCaps) fontPrefixParts.push("small-caps");
980
- if (style.bold) fontPrefixParts.push("700");
981
- fontPrefixParts.push(`${fontSizePx}px`);
982
- const fontPrefix = fontPrefixParts.join(" ");
983
- const perScriptFallback = {
984
- ...style.eastAsiaFontFamily ? { [SCRIPT_CLASS.eastAsia]: resolveFontFamily(style.eastAsiaFontFamily).cssFallback } : {},
985
- ...style.complexScriptFontFamily ? { [SCRIPT_CLASS.complex]: resolveFontFamily(style.complexScriptFontFamily).cssFallback } : {}
992
+ const fontString = (family, size, bold, italic) => {
993
+ const fontPrefixParts = [];
994
+ if (italic) fontPrefixParts.push("italic");
995
+ if (style.smallCaps) fontPrefixParts.push("small-caps");
996
+ if (bold) fontPrefixParts.push("700");
997
+ fontPrefixParts.push(`${size * 96 / 72}px`);
998
+ return `${fontPrefixParts.join(" ")} ${resolveFontFamily(family).cssFallback}`;
986
999
  };
987
- if (!style.letterSpacing && (style.eastAsiaFontFamily && hasCjk(text) || style.complexScriptFontFamily && hasComplexScript(text))) {
1000
+ const baseFont = fontString(fontFamily, fontSize, style.bold, style.italic);
1001
+ const complex = resolveComplexScriptFormatting(style);
1002
+ const complexFont = fontString(complex.fontFamily ?? fontFamily, complex.fontSize ?? fontSize, complex.bold ?? style.bold, complex.italic ?? style.italic);
1003
+ if (style.forceComplexScript) {
1004
+ ctx.font = complexFont;
1005
+ return applyLetterSpacingToMeasuredWidth(ctx.measureText(text).width, text, style.letterSpacing);
1006
+ }
1007
+ if (!style.letterSpacing && (style.eastAsiaFontFamily && hasCjk(text) || hasComplexScriptFormatting(style) && hasComplexScript(text))) {
988
1008
  let segmentedWidth = 0;
989
1009
  for (const segment of segmentByScript(text)) {
990
- ctx.font = `${fontPrefix} ${perScriptFallback[segment.script] ?? cssFallback}`;
1010
+ if (segment.script === SCRIPT_CLASS.eastAsia && style.eastAsiaFontFamily) ctx.font = fontString(style.eastAsiaFontFamily, fontSize, style.bold, style.italic);
1011
+ else if (segment.script === SCRIPT_CLASS.complex) ctx.font = complexFont;
1012
+ else ctx.font = baseFont;
991
1013
  segmentedWidth += ctx.measureText(segment.text).width;
992
1014
  }
993
1015
  return segmentedWidth;
994
1016
  }
995
- ctx.font = `${fontPrefix} ${cssFallback}`;
1017
+ ctx.font = baseFont;
996
1018
  return applyLetterSpacingToMeasuredWidth(ctx.measureText(text).width, text, style.letterSpacing);
997
1019
  };
998
1020
  }
@@ -1180,12 +1202,7 @@ function renderLine(block, line, alignment, doc, options) {
1180
1202
  if (!measureText) continue;
1181
1203
  const fontSize = run.fontSize || 11;
1182
1204
  const fontFamily = run.fontFamily || "Calibri";
1183
- const measuredWidth = measureText(run.allCaps ? run.text.toLocaleUpperCase() : run.text, fontSize, fontFamily, {
1184
- ...run.bold !== void 0 ? { bold: run.bold } : {},
1185
- ...run.italic !== void 0 ? { italic: run.italic } : {},
1186
- ...run.letterSpacing !== void 0 ? { letterSpacing: run.letterSpacing } : {},
1187
- ...run.smallCaps !== void 0 ? { smallCaps: run.smallCaps } : {}
1188
- });
1205
+ const measuredWidth = measureText(run.allCaps ? run.text.toLocaleUpperCase() : run.text, fontSize, fontFamily, runMeasureStyle(run));
1189
1206
  reserveScaledAdvance(runEl, measuredWidth, run.horizontalScale);
1190
1207
  currentX += measuredWidth * ((run.horizontalScale ?? 100) / 100);
1191
1208
  } else if (isImageRun(run)) {
@@ -16,6 +16,8 @@ declare function resolveImageLineAlign(imageRun: ImageRun, paragraphAlignment: "
16
16
  type RenderContext = {
17
17
  /** Current page number (1-indexed) */
18
18
  pageNumber: number;
19
+ /** OOXML section-level format for the current logical page number. */
20
+ pageNumberFormat?: string;
19
21
  /** Total number of pages */
20
22
  totalPages: number;
21
23
  /** Which section is being rendered */
@@ -1,5 +1,5 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
- import { PageMargins } from "../layout-engine/types.js";
2
+ import { PageMargins, SectionPageNumbering } from "../layout-engine/types.js";
3
3
  //#region src/paged-layout/sectionGeometry.d.ts
4
4
  declare const DEFAULT_PAGE_WIDTH_PX = 816;
5
5
  declare const DEFAULT_PAGE_HEIGHT_PX = 1056;
@@ -16,5 +16,6 @@ declare const getPageSize: (sectionProps: document_d_exports.SectionProperties |
16
16
  h: number;
17
17
  };
18
18
  declare const getMargins: (sectionProps: document_d_exports.SectionProperties | null | undefined) => PageMargins;
19
+ declare const getPageNumbering: (sectionProps: document_d_exports.SectionProperties | null | undefined) => SectionPageNumbering;
19
20
  //#endregion
20
- export { DEFAULT_BODY_MARGIN_PX, DEFAULT_HEADER_FOOTER_DISTANCE_PX, DEFAULT_PAGE_HEIGHT_PX, DEFAULT_PAGE_WIDTH_PX, getMargins, getPageSize, twipsToPixels, twipsToPxOr };
21
+ export { DEFAULT_BODY_MARGIN_PX, DEFAULT_HEADER_FOOTER_DISTANCE_PX, DEFAULT_PAGE_HEIGHT_PX, DEFAULT_PAGE_WIDTH_PX, getMargins, getPageNumbering, getPageSize, twipsToPixels, twipsToPxOr };
@@ -32,5 +32,21 @@ const getMargins = (sectionProps) => ({
32
32
  header: twipsToPxOr(sectionProps?.headerDistance, 48),
33
33
  footer: twipsToPxOr(sectionProps?.footerDistance, 48)
34
34
  });
35
+ const getPageNumbering = (sectionProps) => {
36
+ const pageNumbering = sectionProps?.pageNumbering;
37
+ const format = pageNumbering?.format;
38
+ if (pageNumbering?.start === void 0) return format === void 0 ? { type: "continue" } : {
39
+ type: "continue",
40
+ format
41
+ };
42
+ return format === void 0 ? {
43
+ type: "restart",
44
+ start: pageNumbering.start
45
+ } : {
46
+ type: "restart",
47
+ start: pageNumbering.start,
48
+ format
49
+ };
50
+ };
35
51
  //#endregion
36
- export { DEFAULT_BODY_MARGIN_PX, DEFAULT_HEADER_FOOTER_DISTANCE_PX, DEFAULT_PAGE_HEIGHT_PX, DEFAULT_PAGE_WIDTH_PX, getMargins, getPageSize, twipsToPixels, twipsToPxOr };
52
+ export { DEFAULT_BODY_MARGIN_PX, DEFAULT_HEADER_FOOTER_DISTANCE_PX, DEFAULT_PAGE_HEIGHT_PX, DEFAULT_PAGE_WIDTH_PX, getMargins, getPageNumbering, getPageSize, twipsToPixels, twipsToPxOr };
@@ -276,6 +276,7 @@ const readTableAttrs = (node) => {
276
276
  "insideH",
277
277
  "insideV"
278
278
  ]);
279
+ optionalBoolean(attrs, "_resolvedBidi", "table.attrs._resolvedBidi", issues);
279
280
  optionalRecord(attrs, "_originalFormatting", "table.attrs._originalFormatting", issues);
280
281
  return attrsResult(attrs, issues);
281
282
  };
@@ -651,6 +652,10 @@ const readRunFormattingOverrideMarkAttrs = (mark) => {
651
652
  const issues = [];
652
653
  expectMarkType(mark, "runFormattingOverride", issues);
653
654
  for (const key of RUN_FORMATTING_OVERRIDE_FALSE_KEYS) optionalFalse(attrs, key, `runFormattingOverride.attrs.${key}`, issues);
655
+ optionalBoolean(attrs, "boldCs", "runFormattingOverride.attrs.boldCs", issues);
656
+ optionalBoolean(attrs, "italicCs", "runFormattingOverride.attrs.italicCs", issues);
657
+ optionalNumber(attrs, "fontSizeCs", "runFormattingOverride.attrs.fontSizeCs", issues);
658
+ optionalBoolean(attrs, "cs", "runFormattingOverride.attrs.cs", issues);
654
659
  optionalOneOf(attrs, "underline", "runFormattingOverride.attrs.underline", issues, ["none"]);
655
660
  return attrsResult(attrs, issues);
656
661
  };
@@ -4,6 +4,7 @@ import { OUTLINE_STYLE_CSS_ALIASES } from "../../types/documentEnumValues.js";
4
4
  import { pixelsToEmu } from "../../utils/units.js";
5
5
  import { expectBlockSdtAttrs, expectCharacterSpacingMarkAttrs, expectCharacterStyleMarkAttrs, expectCommentMarkAttrs, expectEmphasisMarkAttrs, expectFieldAttrs, expectFontFamilyMarkAttrs, expectFontSizeMarkAttrs, expectFootnoteRefMarkAttrs, expectHardBreakAttrs, expectHighlightMarkAttrs, expectHyperlinkMarkAttrs, expectImageAttrs, expectLanguageMarkAttrs, expectMathAttrs, expectParagraphAttrs, expectRunFormattingOverrideMarkAttrs, expectRunPropertyChangeMarkAttrs, expectRunShadingMarkAttrs, expectSdtAttrs, expectShapeAttrs, expectStrikeMarkAttrs, expectSymbolAttrs, expectTabAttrs, expectTableAttrs, expectTableCellAttrs, expectTableRowAttrs, expectTextBoxAttrs, expectTextColorMarkAttrs, expectTextEffectMarkAttrs, expectTrackedChangeMarkAttrs, expectUnderlineMarkAttrs } from "../attrs/index.js";
6
6
  import { autospacingMatchesBase, hasAutospacingBaseSide } from "../autospacingBase.js";
7
+ import { applyRunFormattingOverrideAttrs } from "../extensions/marks/RunFormattingOverrideExtension.js";
7
8
  import { directionToBidi } from "../paragraphDirection.js";
8
9
  import { RUN_FORMATTING_MARK_NAMES } from "../runFormattingMarkNames.js";
9
10
  import { expectTextBoxAnchorAttrs } from "../textBoxAnchorAttrs.js";
@@ -1341,6 +1342,7 @@ function createShapeRun(node) {
1341
1342
  function marksToTextFormatting(marks) {
1342
1343
  const formatting = {};
1343
1344
  let characterStyleRPr;
1345
+ let runFormattingOverrideMark;
1344
1346
  for (const mark of marks) switch (mark.type.name) {
1345
1347
  case "bold":
1346
1348
  formatting.bold = true;
@@ -1453,7 +1455,7 @@ function marksToTextFormatting(marks) {
1453
1455
  formatting.effect = expectTextEffectMarkAttrs(mark).effect;
1454
1456
  break;
1455
1457
  case "runFormattingOverride":
1456
- applyRunFormattingOverrideAttrs(formatting, expectRunFormattingOverrideMarkAttrs(mark));
1458
+ runFormattingOverrideMark = mark;
1457
1459
  break;
1458
1460
  case "characterStyle": {
1459
1461
  const attrs = expectCharacterStyleMarkAttrs(mark);
@@ -1463,6 +1465,7 @@ function marksToTextFormatting(marks) {
1463
1465
  }
1464
1466
  default: break;
1465
1467
  }
1468
+ if (runFormattingOverrideMark) applyRunFormattingOverrideAttrs(formatting, expectRunFormattingOverrideMarkAttrs(runFormattingOverrideMark));
1466
1469
  if (characterStyleRPr) return subtractCharacterStyleFormatting(formatting, characterStyleRPr);
1467
1470
  return formatting;
1468
1471
  }
@@ -1522,21 +1525,6 @@ function subtractCharacterStyleFormatting(formatting, styleRPr) {
1522
1525
  }
1523
1526
  return result;
1524
1527
  }
1525
- function applyRunFormattingOverrideAttrs(formatting, attrs) {
1526
- if (attrs.bold === false) formatting.bold = false;
1527
- if (attrs.italic === false) formatting.italic = false;
1528
- if (attrs.underline === "none") formatting.underline = { style: "none" };
1529
- if (attrs.strike === false) formatting.strike = false;
1530
- if (attrs.doubleStrike === false) formatting.doubleStrike = false;
1531
- if (attrs.allCaps === false) formatting.allCaps = false;
1532
- if (attrs.smallCaps === false) formatting.smallCaps = false;
1533
- if (attrs.hidden === false) formatting.hidden = false;
1534
- if (attrs.emboss === false) formatting.emboss = false;
1535
- if (attrs.imprint === false) formatting.imprint = false;
1536
- if (attrs.shadow === false) formatting.shadow = false;
1537
- if (attrs.outline === false) formatting.outline = false;
1538
- if (attrs.rtl === false) formatting.rtl = false;
1539
- }
1540
1528
  /**
1541
1529
  * Convert a ProseMirror table node to our Table type
1542
1530
  */
@@ -645,7 +645,9 @@ function convertTable(table, styleResolver, context) {
645
645
  const fallbackTableStyle = tableStyleId ? void 0 : defaultTableStyle;
646
646
  const conditionalTableStyleId = tableStyle?.styleId ?? fallbackTableStyle?.styleId;
647
647
  const resolvedTableBorders = table.formatting?.borders ?? tableStyle?.tblPr?.borders ?? fallbackTableStyle?.tblPr?.borders;
648
- const resolvedTableIndent = table.formatting?.indent ?? tableStyle?.tblPr?.indent ?? fallbackTableStyle?.tblPr?.indent;
648
+ const fallbackTableIndent = fallbackTableStyle?.tblPr?.indent;
649
+ const resolvedTableIndent = table.formatting?.indent ?? tableStyle?.tblPr?.indent ?? (fallbackTableIndent?.value === 0 ? void 0 : fallbackTableIndent);
650
+ const resolvedTableBidi = table.formatting?.bidi ?? tableStyle?.tblPr?.bidi ?? fallbackTableStyle?.tblPr?.bidi;
649
651
  const tableCellMargins = table.formatting?.cellMargins ?? tableStyle?.tblPr?.cellMargins ?? fallbackTableStyle?.tblPr?.cellMargins;
650
652
  let cellMarginsAttr;
651
653
  if (tableCellMargins) {
@@ -667,6 +669,7 @@ function convertTable(table, styleResolver, context) {
667
669
  if (table.formatting?.look) attrs.look = table.formatting.look;
668
670
  if (table.formatting?.borders) attrs.borders = table.formatting.borders;
669
671
  if (resolvedTableIndent) attrs._resolvedIndent = resolvedTableIndent;
672
+ if (resolvedTableBidi !== void 0) attrs._resolvedBidi = resolvedTableBidi;
670
673
  if (table.formatting) attrs._originalFormatting = table.formatting;
671
674
  if (table.propertyChanges && table.propertyChanges.length > 0) attrs.tblPrChange = [...table.propertyChanges];
672
675
  const conditionalStyles = {};
@@ -1,10 +1,11 @@
1
1
  import { document_d_exports } from "../../../types/document.js";
2
+ import { RunFormattingOverrideAttrs } from "../../schema/marks.js";
2
3
  import { MarkExtension } from "../types.js";
3
4
  import { Mark } from "prosemirror-model";
4
5
  //#region src/prosemirror/extensions/marks/RunFormattingOverrideExtension.d.ts
5
- type RunFormattingOverrideAttrs = Record<string, false | "none">;
6
6
  declare function buildRunFormattingOverrideAttrs(formatting: document_d_exports.TextFormatting | undefined): RunFormattingOverrideAttrs | undefined;
7
+ declare function applyRunFormattingOverrideAttrs(formatting: document_d_exports.TextFormatting, attrs: RunFormattingOverrideAttrs): void;
7
8
  declare function applyRunFormattingOverrideMark(formatting: document_d_exports.TextFormatting, mark: Mark): void;
8
9
  declare const RunFormattingOverrideExtension: (options?: Partial<Record<string, unknown>> | undefined) => MarkExtension;
9
10
  //#endregion
10
- export { RunFormattingOverrideExtension, applyRunFormattingOverrideMark, buildRunFormattingOverrideAttrs };
11
+ export { RunFormattingOverrideExtension, applyRunFormattingOverrideAttrs, applyRunFormattingOverrideMark, buildRunFormattingOverrideAttrs };
@@ -4,23 +4,26 @@ import { createMarkExtension } from "../create.js";
4
4
  function buildRunFormattingOverrideAttrs(formatting) {
5
5
  if (!formatting) return;
6
6
  const attrs = {};
7
- if (formatting.bold === false) attrs["bold"] = false;
8
- if (formatting.italic === false) attrs["italic"] = false;
9
- if (formatting.underline?.style === "none") attrs["underline"] = "none";
10
- if (formatting.strike === false) attrs["strike"] = false;
11
- if (formatting.doubleStrike === false) attrs["doubleStrike"] = false;
12
- if (formatting.allCaps === false) attrs["allCaps"] = false;
13
- if (formatting.smallCaps === false) attrs["smallCaps"] = false;
14
- if (formatting.hidden === false) attrs["hidden"] = false;
15
- if (formatting.emboss === false) attrs["emboss"] = false;
16
- if (formatting.imprint === false) attrs["imprint"] = false;
17
- if (formatting.shadow === false) attrs["shadow"] = false;
18
- if (formatting.outline === false) attrs["outline"] = false;
19
- if (formatting.rtl === false) attrs["rtl"] = false;
7
+ if (formatting.bold === false) attrs.bold = false;
8
+ if (formatting.italic === false) attrs.italic = false;
9
+ if (formatting.underline?.style === "none") attrs.underline = "none";
10
+ if (formatting.strike === false) attrs.strike = false;
11
+ if (formatting.doubleStrike === false) attrs.doubleStrike = false;
12
+ if (formatting.allCaps === false) attrs.allCaps = false;
13
+ if (formatting.smallCaps === false) attrs.smallCaps = false;
14
+ if (formatting.hidden === false) attrs.hidden = false;
15
+ if (formatting.emboss === false) attrs.emboss = false;
16
+ if (formatting.imprint === false) attrs.imprint = false;
17
+ if (formatting.shadow === false) attrs.shadow = false;
18
+ if (formatting.outline === false) attrs.outline = false;
19
+ if (formatting.rtl === false) attrs.rtl = false;
20
+ if (formatting.boldCs !== void 0 && formatting.boldCs !== formatting.bold) attrs.boldCs = formatting.boldCs;
21
+ if (formatting.italicCs !== void 0 && formatting.italicCs !== formatting.italic) attrs.italicCs = formatting.italicCs;
22
+ if (formatting.fontSizeCs !== void 0 && formatting.fontSizeCs !== formatting.fontSize) attrs.fontSizeCs = formatting.fontSizeCs;
23
+ if (formatting.cs !== void 0) attrs.cs = formatting.cs;
20
24
  return Object.keys(attrs).length > 0 ? attrs : void 0;
21
25
  }
22
- function applyRunFormattingOverrideMark(formatting, mark) {
23
- const attrs = expectRunFormattingOverrideMarkAttrs(mark);
26
+ function applyRunFormattingOverrideAttrs(formatting, attrs) {
24
27
  if (attrs.bold === false) formatting.bold = false;
25
28
  if (attrs.italic === false) formatting.italic = false;
26
29
  if (attrs.underline === "none") formatting.underline = { style: "none" };
@@ -34,6 +37,13 @@ function applyRunFormattingOverrideMark(formatting, mark) {
34
37
  if (attrs.shadow === false) formatting.shadow = false;
35
38
  if (attrs.outline === false) formatting.outline = false;
36
39
  if (attrs.rtl === false) formatting.rtl = false;
40
+ if (attrs.boldCs !== void 0) formatting.boldCs = attrs.boldCs;
41
+ if (attrs.italicCs !== void 0) formatting.italicCs = attrs.italicCs;
42
+ if (attrs.fontSizeCs !== void 0) formatting.fontSizeCs = attrs.fontSizeCs;
43
+ if (attrs.cs !== void 0) formatting.cs = attrs.cs;
44
+ }
45
+ function applyRunFormattingOverrideMark(formatting, mark) {
46
+ applyRunFormattingOverrideAttrs(formatting, expectRunFormattingOverrideMarkAttrs(mark));
37
47
  }
38
48
  const RunFormattingOverrideExtension = createMarkExtension({
39
49
  name: "runFormattingOverride",
@@ -52,7 +62,11 @@ const RunFormattingOverrideExtension = createMarkExtension({
52
62
  imprint: { default: null },
53
63
  shadow: { default: null },
54
64
  outline: { default: null },
55
- rtl: { default: null }
65
+ rtl: { default: null },
66
+ boldCs: { default: null },
67
+ italicCs: { default: null },
68
+ fontSizeCs: { default: null },
69
+ cs: { default: null }
56
70
  },
57
71
  toDOM(mark) {
58
72
  const attrs = expectRunFormattingOverrideMarkAttrs(mark);
@@ -72,4 +86,4 @@ const RunFormattingOverrideExtension = createMarkExtension({
72
86
  }
73
87
  });
74
88
  //#endregion
75
- export { RunFormattingOverrideExtension, applyRunFormattingOverrideMark, buildRunFormattingOverrideAttrs };
89
+ export { RunFormattingOverrideExtension, applyRunFormattingOverrideAttrs, applyRunFormattingOverrideMark, buildRunFormattingOverrideAttrs };
@@ -154,6 +154,7 @@ const tableSpec = {
154
154
  look: { default: null },
155
155
  borders: { default: null },
156
156
  _resolvedIndent: { default: null },
157
+ _resolvedBidi: { default: null },
157
158
  _originalFormatting: { default: null },
158
159
  tblPrChange: { default: null },
159
160
  _suggestedInsert: { default: null }
@@ -122,6 +122,14 @@ type RunPropertyChangeMarkAttrs = {
122
122
  suggestionId?: string;
123
123
  };
124
124
  type RunFormattingOverrideAttrs = { [K in keyof Pick<document_d_exports.TextFormatting, "bold" | "italic" | "strike" | "doubleStrike" | "allCaps" | "smallCaps" | "hidden" | "emboss" | "imprint" | "shadow" | "outline" | "rtl">]?: false; } & {
125
+ /** Independent complex-script weight (`w:bCs`). */
126
+ boldCs?: boolean;
127
+ /** Force complex-script formatting for the full run (`w:cs`). */
128
+ cs?: boolean;
129
+ /** Independent complex-script size in half-points (`w:szCs`). */
130
+ fontSizeCs?: number;
131
+ /** Independent complex-script slant (`w:iCs`). */
132
+ italicCs?: boolean;
125
133
  underline?: "none";
126
134
  };
127
135
  /**
@@ -555,6 +555,8 @@ type TableAttrs = {
555
555
  borders?: document_d_exports.TableBorders;
556
556
  /** Effective table indent after style resolution. PM-only; never serialized. */
557
557
  _resolvedIndent?: NonNullable<document_d_exports.TableFormatting["indent"]>;
558
+ /** Effective table direction after style resolution. PM-only; never serialized. */
559
+ _resolvedBidi?: boolean;
558
560
  /** Original table formatting from DOCX for lossless round-trip serialization */
559
561
  _originalFormatting?: document_d_exports.TableFormatting;
560
562
  /** Tracked table property changes (w:tblPrChange) for round-trip + accept/reject */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/folio-core",
3
- "version": "0.23.0",
3
+ "version": "0.23.1",
4
4
  "description": "Headless, framework-neutral core of folio: the OOXML (.docx) parser, document model, ProseMirror integration, and page-layout engine. No React.",
5
5
  "keywords": [
6
6
  "document-model",