@stll/folio-core 0.23.1 → 0.24.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.
@@ -1,4 +1,4 @@
1
- import { expectFontFamilyMarkAttrs } from "../prosemirror/attrs/index.js";
1
+ import { expectFontFamilyMarkAttrs, expectParagraphAttrs } from "../prosemirror/attrs/index.js";
2
2
  import { parseFontFamilyList, resolveFontFamily } from "../utils/fontResolver.js";
3
3
  //#region src/controller/fontReadiness.ts
4
4
  function getDocumentFontSet() {
@@ -62,13 +62,15 @@ function collectInitialLayoutFontFaces(documentModel, pmDoc) {
62
62
  return Array.from(faces.values());
63
63
  }
64
64
  function addTextFormattingFontFaces(faces, formatting) {
65
- addLayoutFontFamilyFace(faces, formatting?.fontFamily, layoutDescriptorFromFormatting(formatting));
65
+ const standardDescriptor = layoutDescriptorFromFormatting(formatting);
66
+ const complexScriptDescriptor = layoutDescriptorFromEmphasis(formatting?.boldCs ?? formatting?.bold, formatting?.italicCs ?? formatting?.italic);
67
+ addLayoutFontFamilyFace(faces, formatting?.fontFamily, standardDescriptor, complexScriptDescriptor);
66
68
  }
67
69
  function collectProseMirrorFontFaces(faces, node, inheritedTextFormatting) {
68
70
  const paragraphDefaults = readParagraphDefaultTextFormatting(node);
69
71
  const textFormatting = paragraphDefaults ?? inheritedTextFormatting;
70
72
  if (paragraphDefaults) addTextFormattingFontFaces(faces, paragraphDefaults);
71
- if (node.attrs["listMarkerFontFamily"]) addLayoutFontFamilyFace(faces, node.attrs["listMarkerFontFamily"], REGULAR_LAYOUT_FONT_DESCRIPTOR);
73
+ if (node.type.name === "paragraph") addTextFormattingFontFaces(faces, expectParagraphAttrs(node).listMarkerFormatting);
72
74
  if (node.isText) {
73
75
  const descriptor = layoutDescriptorFromFormattingAndMarks(textFormatting, node.marks);
74
76
  addLayoutFontFamilyFace(faces, readFontFamilyMarkAttrs(node.marks) ?? textFormatting?.fontFamily ?? DEFAULT_LAYOUT_FONT_FAMILY, descriptor);
@@ -86,9 +88,12 @@ function readFontFamilyMarkAttrs(marks) {
86
88
  for (const mark of marks) if (mark.type.name === "fontFamily") return expectFontFamilyMarkAttrs(mark);
87
89
  }
88
90
  function layoutDescriptorFromFormatting(formatting) {
91
+ return layoutDescriptorFromEmphasis(formatting?.bold, formatting?.italic);
92
+ }
93
+ function layoutDescriptorFromEmphasis(bold, italic) {
89
94
  return {
90
- style: formatting?.italic ? "italic" : "normal",
91
- weight: formatting?.bold ? 700 : 400
95
+ style: italic ? "italic" : "normal",
96
+ weight: bold ? 700 : 400
92
97
  };
93
98
  }
94
99
  function layoutDescriptorFromFormattingAndMarks(formatting, marks) {
@@ -103,7 +108,7 @@ function layoutDescriptorFromFormattingAndMarks(formatting, marks) {
103
108
  weight: bold ? 700 : 400
104
109
  };
105
110
  }
106
- function addLayoutFontFamilyFace(faces, value, descriptor) {
111
+ function addLayoutFontFamilyFace(faces, value, descriptor, complexScriptDescriptor = descriptor) {
107
112
  if (typeof value === "string") {
108
113
  addLayoutFontFamilyNameFace(faces, value, descriptor);
109
114
  return;
@@ -112,7 +117,7 @@ function addLayoutFontFamilyFace(faces, value, descriptor) {
112
117
  const fontFamily = value;
113
118
  addLayoutFontFamilyFace(faces, fontFamily.ascii, descriptor);
114
119
  addLayoutFontFamilyFace(faces, fontFamily.hAnsi, descriptor);
115
- addLayoutFontFamilyFace(faces, fontFamily.cs, descriptor);
120
+ addLayoutFontFamilyFace(faces, fontFamily.cs, complexScriptDescriptor);
116
121
  addLayoutFontFamilyFace(faces, fontFamily.eastAsia, descriptor);
117
122
  }
118
123
  function addLayoutFontFamilyNameFace(faces, family, descriptor) {
@@ -24,6 +24,7 @@ type NumberingMap = {
24
24
  * @returns NumberingMap with definitions and helper functions
25
25
  */
26
26
  declare function parseNumbering(numberingXml: string | null): NumberingMap;
27
+ declare const markerFormattingFromLevel: (formatting: document_d_exports.TextFormatting | undefined) => document_d_exports.ListMarkerFormatting | undefined;
27
28
  declare function getCachedNumberingMap(definitions: document_d_exports.NumberingDefinitions): NumberingMap;
28
29
  /**
29
30
  * Create a NumberingMap with helper functions
@@ -80,4 +81,4 @@ declare function getBulletCharacter(level: document_d_exports.ListLevel): string
80
81
  */
81
82
  declare function isBulletLevel(level: document_d_exports.ListLevel): boolean;
82
83
  //#endregion
83
- export { NumberingMap, computeListRendering, createNumberingMap, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, numPrEqual, padDecimal, parseNumbering, renderListMarker };
84
+ export { NumberingMap, computeListRendering, createNumberingMap, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, markerFormattingFromLevel, numPrEqual, padDecimal, parseNumbering, renderListMarker };
@@ -1,5 +1,6 @@
1
1
  import { formatOoxmlCounter, padDecimal } from "./ooxmlCounterFormatter.js";
2
- import { FontHintSchema, LevelSuffixSchema, ThemeColorSlotSchema, narrowEnum } from "./parserEnums.js";
2
+ import { LevelSuffixSchema, narrowEnum } from "./parserEnums.js";
3
+ import { parseRunProperties } from "./runParser.js";
3
4
  import { findChild, findChildren, getAttribute, parseBooleanElement, parseNumericAttribute, parseXmlDocument } from "./xmlParser.js";
4
5
  //#region src/docx/numberingParser.ts
5
6
  const NUMBER_FORMAT_MAP = {
@@ -315,7 +316,10 @@ function parseListLevel(element) {
315
316
  };
316
317
  }
317
318
  if (pPrEl) level.pPr = parseLevelParagraphProps(pPrEl);
318
- if (rPrEl) level.rPr = parseLevelRunProps(rPrEl);
319
+ if (rPrEl) {
320
+ const runProperties = parseRunProperties(rPrEl, null);
321
+ if (runProperties) level.rPr = runProperties;
322
+ }
319
323
  return level;
320
324
  }
321
325
  /**
@@ -428,79 +432,21 @@ function parseTabLeader(val) {
428
432
  default: return;
429
433
  }
430
434
  }
431
- /**
432
- * Parse run properties for a list level (subset of full rPr)
433
- * Main concern: fonts for bullet characters
434
- */
435
- function parseLevelRunProps(rPr) {
436
- const formatting = {};
437
- let rFontsEl;
438
- let szEl;
439
- let colorEl;
440
- let bEl;
441
- let iEl;
442
- let vanishEl;
443
- for (const child of rPr.elements ?? []) {
444
- if (child.type !== "element") continue;
445
- switch (child.name) {
446
- case "w:rFonts":
447
- rFontsEl ??= child;
448
- break;
449
- case "w:sz":
450
- szEl ??= child;
451
- break;
452
- case "w:color":
453
- colorEl ??= child;
454
- break;
455
- case "w:b":
456
- bEl ??= child;
457
- break;
458
- case "w:i":
459
- iEl ??= child;
460
- break;
461
- case "w:vanish":
462
- vanishEl ??= child;
463
- break;
464
- default: break;
465
- }
466
- }
467
- if (rFontsEl) {
468
- const ascii = getAttribute(rFontsEl, "w", "ascii");
469
- const hAnsi = getAttribute(rFontsEl, "w", "hAnsi");
470
- const eastAsia = getAttribute(rFontsEl, "w", "eastAsia");
471
- const cs = getAttribute(rFontsEl, "w", "cs");
472
- const hint = narrowEnum(getAttribute(rFontsEl, "w", "hint"), FontHintSchema);
473
- formatting.fontFamily = {
474
- ...ascii != null ? { ascii } : {},
475
- ...hAnsi != null ? { hAnsi } : {},
476
- ...eastAsia != null ? { eastAsia } : {},
477
- ...cs != null ? { cs } : {},
478
- ...hint !== void 0 ? { hint } : {}
479
- };
480
- }
481
- if (szEl) {
482
- const size = parseNumericAttribute(szEl, "w", "val");
483
- if (size !== void 0) formatting.fontSize = size;
484
- }
485
- if (colorEl) {
486
- const val = getAttribute(colorEl, "w", "val");
487
- const validatedThemeColor = narrowEnum(getAttribute(colorEl, "w", "themeColor"), ThemeColorSlotSchema);
488
- if (val === "auto") formatting.color = { auto: true };
489
- else if (validatedThemeColor) {
490
- const themeTint = getAttribute(colorEl, "w", "themeTint");
491
- const themeShade = getAttribute(colorEl, "w", "themeShade");
492
- formatting.color = {
493
- themeColor: validatedThemeColor,
494
- ...themeTint != null ? { themeTint } : {},
495
- ...themeShade != null ? { themeShade } : {}
496
- };
497
- } else if (val) formatting.color = { rgb: val };
498
- }
499
- if (bEl) formatting.bold = parseBooleanElement(bEl);
500
- if (iEl) formatting.italic = parseBooleanElement(iEl);
501
- if (vanishEl) formatting.hidden = parseBooleanElement(vanishEl);
502
- return formatting;
503
- }
435
+ const markerFormattingFromLevel = (formatting) => {
436
+ if (!formatting) return;
437
+ const markerFormatting = {
438
+ ...formatting.fontFamily !== void 0 ? { fontFamily: formatting.fontFamily } : {},
439
+ ...formatting.fontSize !== void 0 ? { fontSize: formatting.fontSize } : {},
440
+ ...formatting.fontSizeCs !== void 0 ? { fontSizeCs: formatting.fontSizeCs } : {},
441
+ ...formatting.bold !== void 0 ? { bold: formatting.bold } : {},
442
+ ...formatting.boldCs !== void 0 ? { boldCs: formatting.boldCs } : {},
443
+ ...formatting.italic !== void 0 ? { italic: formatting.italic } : {},
444
+ ...formatting.italicCs !== void 0 ? { italicCs: formatting.italicCs } : {},
445
+ ...formatting.rtl !== void 0 ? { rtl: formatting.rtl } : {},
446
+ ...formatting.cs !== void 0 ? { cs: formatting.cs } : {}
447
+ };
448
+ return Object.keys(markerFormatting).length > 0 ? markerFormatting : void 0;
449
+ };
504
450
  /**
505
451
  * Per-definitions cache for `createNumberingMap`. Style application rebuilds
506
452
  * the lookup map on every picker click otherwise; the definitions object is
@@ -603,10 +549,8 @@ function computeListRendering(numPr, numbering) {
603
549
  };
604
550
  if (level.isLgl) rendering.isLegal = true;
605
551
  if (level.rPr?.hidden) rendering.markerHidden = true;
606
- const markerFont = level.rPr?.fontFamily?.ascii || level.rPr?.fontFamily?.hAnsi;
607
- if (markerFont) rendering.markerFontFamily = markerFont;
608
- if (level.rPr?.fontSize) rendering.markerFontSize = level.rPr.fontSize / 2;
609
- if (level.rPr?.bold !== void 0) rendering.markerBold = level.rPr.bold;
552
+ const markerFormatting = markerFormattingFromLevel(level.rPr);
553
+ if (markerFormatting) rendering.markerFormatting = markerFormatting;
610
554
  if (level.rPr?.allCaps) rendering.markerAllCaps = true;
611
555
  if (level.lvlJc) rendering.markerAlignment = level.lvlJc;
612
556
  if (level.suffix) rendering.markerSuffix = level.suffix;
@@ -666,4 +610,4 @@ function isBulletLevel(level) {
666
610
  return level.numFmt === "bullet" || level.numFmt === "none";
667
611
  }
668
612
  //#endregion
669
- export { computeListRendering, createNumberingMap, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, numPrEqual, padDecimal, parseNumbering, renderListMarker };
613
+ export { computeListRendering, createNumberingMap, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, markerFormattingFromLevel, numPrEqual, padDecimal, parseNumbering, renderListMarker };
@@ -2,6 +2,7 @@ import { isValidHexId } from "../utils/hexId.js";
2
2
  import { parseBookmarkEnd as parseBookmarkEnd$1, parseBookmarkStart as parseBookmarkStart$1 } from "./bookmarkParser.js";
3
3
  import { parseFieldType } from "./fieldParser.js";
4
4
  import { parseHyperlink as parseHyperlink$1 } from "./hyperlinkParser.js";
5
+ import { markerFormattingFromLevel } from "./numberingParser.js";
5
6
  import { BorderStyleSchema, FrameWrapSchema, FrameXAlignSchema, FrameYAlignSchema, LineSpacingRuleSchema, ParagraphAlignmentSchema, ShadingPatternSchema, TabLeaderSchema, TabStopAlignmentSchema, ThemeColorSlotSchema, narrowEnum } from "./parserEnums.js";
6
7
  import { consolidateParagraphContent } from "./runConsolidator.js";
7
8
  import { parseRun, parseRunProperties } from "./runParser.js";
@@ -1016,10 +1017,8 @@ function parseParagraph(node, styles, theme, numbering, rels = null, media = nul
1016
1017
  if (level.isLgl) listRendering.isLegal = true;
1017
1018
  listRendering.numFmt = level.isLgl ? "decimal" : level.numFmt;
1018
1019
  if (level.rPr?.hidden) listRendering.markerHidden = true;
1019
- const markerFont = level.rPr?.fontFamily?.ascii || level.rPr?.fontFamily?.hAnsi;
1020
- if (markerFont) listRendering.markerFontFamily = markerFont;
1021
- if (level.rPr?.fontSize) listRendering.markerFontSize = level.rPr.fontSize / 2;
1022
- if (level.rPr?.bold !== void 0) listRendering.markerBold = level.rPr.bold;
1020
+ const markerFormatting = markerFormattingFromLevel(level.rPr);
1021
+ if (markerFormatting) listRendering.markerFormatting = markerFormatting;
1023
1022
  if (level.rPr?.allCaps) listRendering.markerAllCaps = true;
1024
1023
  if (level.lvlJc) listRendering.markerAlignment = level.lvlJc;
1025
1024
  if (level.suffix) listRendering.markerSuffix = level.suffix;
@@ -442,7 +442,7 @@ function serializeParagraphAttrs(attrs) {
442
442
  "listMarker",
443
443
  "listIsBullet",
444
444
  "listMarkerHidden",
445
- "listMarkerBold",
445
+ "listMarkerFormatting",
446
446
  "listMarkerAlignment",
447
447
  "listMarkerSuffix",
448
448
  "tabs"
@@ -362,8 +362,7 @@ function applyRunFormattingOverrides(formatting, attrs) {
362
362
  if (attrs.fontSizeCs !== void 0) formatting.complexScriptFontSize = attrs.fontSizeCs / 2;
363
363
  if (attrs.cs !== void 0) formatting.forceComplexScript = attrs.cs;
364
364
  }
365
- function paragraphRunDefaults(pmAttrs, theme) {
366
- const defaultTextFormatting = pmAttrs.defaultTextFormatting;
365
+ function textFormattingToRunFormatting(defaultTextFormatting, theme) {
367
366
  if (!defaultTextFormatting) return {};
368
367
  const result = {};
369
368
  const fontFamily = defaultTextFormatting.fontFamily ? resolveWesternThemeFont(defaultTextFormatting.fontFamily, theme) : void 0;
@@ -379,6 +378,7 @@ function paragraphRunDefaults(pmAttrs, theme) {
379
378
  if (defaultTextFormatting.boldCs !== void 0) result.complexScriptBold = defaultTextFormatting.boldCs;
380
379
  if (defaultTextFormatting.italic !== void 0) result.italic = defaultTextFormatting.italic;
381
380
  if (defaultTextFormatting.italicCs !== void 0) result.complexScriptItalic = defaultTextFormatting.italicCs;
381
+ if (defaultTextFormatting.rtl !== void 0) result.rtl = defaultTextFormatting.rtl;
382
382
  if (defaultTextFormatting.cs !== void 0) result.forceComplexScript = defaultTextFormatting.cs;
383
383
  if (defaultTextFormatting.underline && defaultTextFormatting.underline.style !== "none") {
384
384
  result.underline = { style: defaultTextFormatting.underline.style };
@@ -408,6 +408,9 @@ function paragraphRunDefaults(pmAttrs, theme) {
408
408
  if (defaultTextFormatting.emphasisMark && defaultTextFormatting.emphasisMark !== "none") result.emphasisMark = defaultTextFormatting.emphasisMark;
409
409
  return result;
410
410
  }
411
+ function paragraphRunDefaults(pmAttrs, theme) {
412
+ return textFormattingToRunFormatting(pmAttrs.defaultTextFormatting, theme);
413
+ }
411
414
  /**
412
415
  * Build an ImageRun from ProseMirror node attrs, applying conditional property assignment
413
416
  * to satisfy exactOptionalPropertyTypes.
@@ -653,12 +656,8 @@ function toPreviousListAttrs(previousFormatting) {
653
656
  if (listStartOverride !== void 0) attrs.listStartOverride = listStartOverride;
654
657
  const listMarkerHidden = previousFormatting.listMarkerHidden;
655
658
  if (listMarkerHidden !== void 0) attrs.listMarkerHidden = listMarkerHidden;
656
- const listMarkerFontFamily = previousFormatting.listMarkerFontFamily;
657
- if (listMarkerFontFamily !== void 0) attrs.listMarkerFontFamily = listMarkerFontFamily;
658
- const listMarkerFontSize = previousFormatting.listMarkerFontSize;
659
- if (listMarkerFontSize !== void 0) attrs.listMarkerFontSize = listMarkerFontSize;
660
- const listMarkerBold = previousFormatting.listMarkerBold;
661
- if (listMarkerBold !== void 0) attrs.listMarkerBold = listMarkerBold;
659
+ const listMarkerFormatting = previousFormatting.listMarkerFormatting;
660
+ if (listMarkerFormatting !== void 0) attrs.listMarkerFormatting = listMarkerFormatting;
662
661
  const listMarkerAlignment = previousFormatting.listMarkerAlignment;
663
662
  if (listMarkerAlignment !== void 0) attrs.listMarkerAlignment = listMarkerAlignment;
664
663
  const listMarkerSuffix = previousFormatting.listMarkerSuffix;
@@ -674,7 +673,7 @@ function resolveDeletedListMarker(previousListAttrs, listCounters, listAbstractC
674
673
  if (previousListAttrs.listIsBullet) return "•";
675
674
  return null;
676
675
  }
677
- function applyDeletedListMarkerAttrs(attrs, change, listCounters, listAbstractCounters, listSeenNumIds) {
676
+ function applyDeletedListMarkerAttrs(attrs, change, listCounters, listAbstractCounters, listSeenNumIds, theme) {
678
677
  const previousListAttrs = toPreviousListAttrs(change.previousFormatting);
679
678
  const marker = resolveDeletedListMarker(previousListAttrs, listCounters, listAbstractCounters, listSeenNumIds);
680
679
  if (!marker) return;
@@ -682,9 +681,7 @@ function applyDeletedListMarkerAttrs(attrs, change, listCounters, listAbstractCo
682
681
  attrs.listMarkerRevision = toListMarkerRevision("del", change.info);
683
682
  if (previousListAttrs.listIsBullet !== void 0) attrs.listIsBullet = previousListAttrs.listIsBullet;
684
683
  if (previousListAttrs.listMarkerHidden !== void 0) attrs.listMarkerHidden = previousListAttrs.listMarkerHidden;
685
- if (previousListAttrs.listMarkerFontFamily) attrs.listMarkerFontFamily = previousListAttrs.listMarkerFontFamily;
686
- if (previousListAttrs.listMarkerFontSize) attrs.listMarkerFontSize = previousListAttrs.listMarkerFontSize;
687
- if (previousListAttrs.listMarkerBold !== void 0) attrs.listMarkerBold = previousListAttrs.listMarkerBold;
684
+ if (previousListAttrs.listMarkerFormatting) attrs.listMarkerFormatting = textFormattingToRunFormatting(previousListAttrs.listMarkerFormatting, theme);
688
685
  if (previousListAttrs.listMarkerAlignment) attrs.listMarkerAlignment = previousListAttrs.listMarkerAlignment;
689
686
  if (previousListAttrs.listMarkerSuffix) attrs.listMarkerSuffix = previousListAttrs.listMarkerSuffix;
690
687
  }
@@ -820,15 +817,13 @@ function convertParagraphAttrs(pmAttrs, theme, listCounters, listAbstractCounter
820
817
  else if (pmAttrs.listMarker) attrs.listMarker = pmAttrs.listIsBullet ? convertBulletToUnicode(pmAttrs.listMarker) : pmAttrs.listMarker;
821
818
  if (pmAttrs.listIsBullet !== void 0) attrs.listIsBullet = pmAttrs.listIsBullet;
822
819
  if (pmAttrs.listMarkerHidden) attrs.listMarkerHidden = true;
823
- if (pmAttrs.listMarkerFontFamily) attrs.listMarkerFontFamily = pmAttrs.listMarkerFontFamily;
824
- if (pmAttrs.listMarkerFontSize) attrs.listMarkerFontSize = pmAttrs.listMarkerFontSize;
825
- if (pmAttrs.listMarkerBold !== null && pmAttrs.listMarkerBold !== void 0) attrs.listMarkerBold = pmAttrs.listMarkerBold;
820
+ if (pmAttrs.listMarkerFormatting) attrs.listMarkerFormatting = textFormattingToRunFormatting(pmAttrs.listMarkerFormatting, theme);
826
821
  if (pmAttrs.listMarkerAlignment) attrs.listMarkerAlignment = pmAttrs.listMarkerAlignment;
827
822
  if (pmAttrs.listMarkerSuffix) attrs.listMarkerSuffix = pmAttrs.listMarkerSuffix;
828
823
  if (pmAttrs.listMarkerSecondSlotOffsetTwips !== void 0) attrs.listMarkerSecondSlotOffsetTwips = pmAttrs.listMarkerSecondSlotOffsetTwips;
829
824
  if (!pmAttrs.numPr) {
830
825
  const numberingRemovedChange = propertyChanges.find(isRemovedNumberingChange);
831
- if (numberingRemovedChange) applyDeletedListMarkerAttrs(attrs, numberingRemovedChange, originalListCounters, originalListAbstractCounters, originalListSeenNumIds);
826
+ if (numberingRemovedChange) applyDeletedListMarkerAttrs(attrs, numberingRemovedChange, originalListCounters, originalListAbstractCounters, originalListSeenNumIds, theme);
832
827
  }
833
828
  if (defaultTabStopTwips !== void 0) attrs.defaultTabStopTwips = defaultTabStopTwips;
834
829
  const dtf = pmAttrs.defaultTextFormatting;
@@ -1,4 +1,4 @@
1
- import { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, SectionPageNumbering, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned } from "./types.js";
1
+ import { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListMarkerFormatting, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, SectionPageNumbering, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned } from "./types.js";
2
2
  import { PageState, Paginator, PaginatorOptions, createPaginator } from "./paginator.js";
3
3
  import { KeepNextChain, calculateChainHeight, computeKeepNextChains, getMidChainIndices, hasKeepLines, hasPageBreakBefore } from "./keep-together.js";
4
4
  import { resolveSectionHeaderFooterRefs } from "./headerFooterRefs.js";
@@ -44,4 +44,4 @@ declare function layoutDocument(blocks: FlowBlock[], measures: Measure[], option
44
44
  */
45
45
  declare function getHeaderRowsHeight(measure: TableMeasure, headerRowCount: number): number;
46
46
  //#endregion
47
- export { BlockId, BorderStyle, type BreakDecision, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, type KeepNextChain, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, type PageState, type Paginator, type PaginatorOptions, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, SectionLayoutConfig, SectionPageNumbering, type SectionState, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, applyContextualSpacing, applyPendingToActive, assertExhaustiveFlowBlock, calculateChainHeight, collectSectionConfigs, computeKeepNextChains, createInitialSectionState, createPaginator, findPageIndexContainingPmPos, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getEffectiveColumns, getEffectiveMargins, getEffectivePageSize, getHeaderRowsHeight, getMidChainIndices, getTableRowLeadingWidth, hasKeepLines, hasPageBreakBefore, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, layoutDocument, resolveSectionHeaderFooterRefs, scheduleSectionBreak, tableColumnsArePinned };
47
+ export { BlockId, BorderStyle, type BreakDecision, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, type KeepNextChain, Layout, LayoutOptions, LineBreakRun, ListMarkerFormatting, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, type PageState, type Paginator, type PaginatorOptions, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, SectionLayoutConfig, SectionPageNumbering, type SectionState, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, applyContextualSpacing, applyPendingToActive, assertExhaustiveFlowBlock, calculateChainHeight, collectSectionConfigs, computeKeepNextChains, createInitialSectionState, createPaginator, findPageIndexContainingPmPos, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getEffectiveColumns, getEffectiveMargins, getEffectivePageSize, getHeaderRowsHeight, getMidChainIndices, getTableRowLeadingWidth, hasKeepLines, hasPageBreakBefore, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, layoutDocument, resolveSectionHeaderFooterRefs, scheduleSectionBreak, tableColumnsArePinned };
@@ -186,6 +186,10 @@ function hashParagraphBlock(block) {
186
186
  if (attrs.reserveEmptyOutlineHeight) parts.push("outline-empty-reserve");
187
187
  if (attrs.documentGridLinePitch !== void 0) parts.push(`documentGrid:${attrs.documentGridLinePitch}|${attrs.snapToGrid !== false}`);
188
188
  if (attrs.justificationCompatibility) parts.push(`justify-compat:${attrs.justificationCompatibility.type}`);
189
+ if (attrs.listMarker !== void 0) {
190
+ const marker = attrs.listMarkerFormatting;
191
+ parts.push(`marker:${attrs.listMarker}|${attrs.listMarkerHidden}|${attrs.listMarkerAlignment}|${attrs.listMarkerSuffix}|${marker?.fontFamily}|${marker?.eastAsiaFontFamily}|${marker?.complexScriptFontFamily}|${marker?.fontSize}|${marker?.complexScriptFontSize}|${marker?.bold}|${marker?.complexScriptBold}|${marker?.italic}|${marker?.complexScriptItalic}|${marker?.rtl}|${marker?.forceComplexScript}`);
192
+ }
189
193
  parts.push(...lineBreakPolicyCacheParts(attrs));
190
194
  const borders = attrs.borders;
191
195
  if (borders) {
@@ -8,7 +8,7 @@ import { ParagraphBlock } from "../types.js";
8
8
  declare const DEFAULT_TAB_STOP_TWIPS = 720;
9
9
  /**
10
10
  * Marker font resolution per ECMA-376 §17.9.6:
11
- * 1. explicit numbering-level rPr (`attrs.listMarkerFont*`),
11
+ * 1. explicit numbering-level rPr (`attrs.listMarkerFormatting`),
12
12
  * 2. first body text run's font,
13
13
  * 3. paragraph defaults, then document defaults.
14
14
  */
@@ -16,6 +16,8 @@ declare function resolveListMarkerFont(block: ParagraphBlock): {
16
16
  fontFamily: string;
17
17
  fontSize: number;
18
18
  bold?: boolean;
19
+ italic?: boolean;
20
+ rtl?: boolean;
19
21
  };
20
22
  /**
21
23
  * Compute the marker's inline-block width in pixels, or 0 if the paragraph
@@ -1,3 +1,5 @@
1
+ import { hasCjk, hasComplexScript } from "../../utils/scriptSegments.js";
2
+ import { hasComplexScriptFormatting, resolveComplexScriptFormatting } from "./complexScriptFormatting.js";
1
3
  import { ptToPx } from "./measureHelpers.js";
2
4
  import { measureTextWidth } from "./measureProvider.js";
3
5
  //#region src/layout-engine/measure/listMarkerWidth.ts
@@ -12,21 +14,33 @@ const DEFAULT_TAB_STOP_TWIPS = 720;
12
14
  const TWIPS_TO_PX = 96 / 1440;
13
15
  /**
14
16
  * Marker font resolution per ECMA-376 §17.9.6:
15
- * 1. explicit numbering-level rPr (`attrs.listMarkerFont*`),
17
+ * 1. explicit numbering-level rPr (`attrs.listMarkerFormatting`),
16
18
  * 2. first body text run's font,
17
19
  * 3. paragraph defaults, then document defaults.
18
20
  */
19
21
  function resolveListMarkerFont(block) {
20
22
  const attrs = block.attrs;
21
23
  const firstTextRun = block.runs.find((r) => r.kind === "text");
22
- const fontFamily = attrs?.listMarkerFontFamily ?? firstTextRun?.fontFamily ?? attrs?.defaultFontFamily ?? DEFAULT_FONT_FAMILY;
23
- const fontSize = attrs?.listMarkerFontSize ?? firstTextRun?.fontSize ?? attrs?.defaultFontSize ?? DEFAULT_FONT_SIZE;
24
- const bold = attrs?.listMarkerBold ?? firstTextRun?.bold;
25
- return {
26
- fontFamily,
27
- fontSize,
28
- ...bold !== void 0 ? { bold } : {}
24
+ const markerFormatting = attrs?.listMarkerFormatting;
25
+ const bold = markerFormatting?.bold ?? firstTextRun?.bold;
26
+ const italic = markerFormatting?.italic ?? firstTextRun?.italic;
27
+ const base = {
28
+ fontFamily: markerFormatting?.fontFamily ?? firstTextRun?.fontFamily ?? attrs?.defaultFontFamily ?? DEFAULT_FONT_FAMILY,
29
+ fontSize: markerFormatting?.fontSize ?? firstTextRun?.fontSize ?? attrs?.defaultFontSize ?? DEFAULT_FONT_SIZE,
30
+ ...bold !== void 0 ? { bold } : {},
31
+ ...italic !== void 0 ? { italic } : {},
32
+ ...markerFormatting?.rtl !== void 0 ? { rtl: markerFormatting.rtl } : {}
29
33
  };
34
+ const marker = attrs?.listMarker ?? "";
35
+ if (markerFormatting && hasComplexScriptFormatting(markerFormatting) && (markerFormatting.forceComplexScript || hasComplexScript(marker))) return {
36
+ ...base,
37
+ ...resolveComplexScriptFormatting(markerFormatting)
38
+ };
39
+ if (markerFormatting?.eastAsiaFontFamily && hasCjk(marker)) return {
40
+ ...base,
41
+ fontFamily: markerFormatting.eastAsiaFontFamily
42
+ };
43
+ return base;
30
44
  }
31
45
  /**
32
46
  * Compute the marker's inline-block width in pixels, or 0 if the paragraph
@@ -49,12 +63,7 @@ function resolveListMarkerFont(block) {
49
63
  function getListMarkerInlineWidth(block) {
50
64
  const attrs = block.attrs;
51
65
  if (!attrs?.listMarker || attrs.listMarkerHidden) return 0;
52
- const { fontFamily, fontSize, bold } = resolveListMarkerFont(block);
53
- const style = {
54
- fontFamily,
55
- fontSize,
56
- ...bold !== void 0 ? { bold } : {}
57
- };
66
+ const style = resolveListMarkerFont(block);
58
67
  const naturalWidth = measureTextWidth(attrs.listMarker, style);
59
68
  const markerEndOffset = getMarkerEndOffset(naturalWidth, attrs.listMarkerAlignment);
60
69
  const suffix = attrs.listMarkerSuffix ?? "tab";
@@ -75,19 +84,14 @@ function getListMarkerInlineWidth(block) {
75
84
  let bodyStart;
76
85
  if (firstCustomPast !== void 0 && firstGridPast !== void 0) bodyStart = Math.min(firstCustomPast, firstGridPast);
77
86
  else bodyStart = firstCustomPast ?? firstGridPast;
78
- if (bodyStart === void 0) return naturalWidth + ptToPx(fontSize) * .5;
87
+ if (bodyStart === void 0) return naturalWidth + ptToPx(style.fontSize ?? DEFAULT_FONT_SIZE) * .5;
79
88
  return bodyStart - markerStartPx;
80
89
  }
81
90
  /** Paint-only offset that places the marker around its authored list anchor. */
82
91
  function getListMarkerVisualOffset(block) {
83
92
  const attrs = block.attrs;
84
93
  if (!attrs?.listMarker || attrs.listMarkerHidden) return 0;
85
- const { fontFamily, fontSize, bold } = resolveListMarkerFont(block);
86
- const naturalWidth = measureTextWidth(attrs.listMarker, {
87
- fontFamily,
88
- fontSize,
89
- ...bold !== void 0 ? { bold } : {}
90
- });
94
+ const naturalWidth = measureTextWidth(attrs.listMarker, resolveListMarkerFont(block));
91
95
  if (attrs.listMarkerAlignment === "right") return -naturalWidth;
92
96
  if (attrs.listMarkerAlignment === "center") return -naturalWidth / 2;
93
97
  return 0;
@@ -1,7 +1,7 @@
1
1
  import { OutlineStyleAttr } from "../types/documentEnumValues.js";
2
2
  import { ImagePosition, ImageWrap, SdtProperties, SdtType, ShapeTextBody, TableCellFormatting, TableWidthType } from "@stll/docx-core/model";
3
3
  declare namespace types_d_exports {
4
- export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, SectionPageNumbering, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned };
4
+ export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListMarkerFormatting, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, SectionPageNumbering, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned };
5
5
  }
6
6
  /**
7
7
  * Unique identifier for a block in the document.
@@ -127,6 +127,7 @@ type RunFormatting = {
127
127
  /** Groups a suggestion's runs for accept/reject and scroll-to. */
128
128
  suggestionId?: string;
129
129
  };
130
+ type ListMarkerFormatting = Pick<RunFormatting, "fontFamily" | "eastAsiaFontFamily" | "complexScriptFontFamily" | "fontSize" | "complexScriptFontSize" | "bold" | "complexScriptBold" | "italic" | "complexScriptItalic" | "rtl" | "forceComplexScript">;
130
131
  /**
131
132
  * Hyperlink information for a run.
132
133
  */
@@ -461,15 +462,14 @@ type ParagraphAttrs = {
461
462
  listMarker?: string;
462
463
  listIsBullet?: boolean;
463
464
  listMarkerHidden?: boolean;
464
- listMarkerFontFamily?: string;
465
- listMarkerFontSize?: number;
465
+ /** Canonical numbering-level marker typography, resolved for layout. */
466
+ listMarkerFormatting?: ListMarkerFormatting;
466
467
  /**
467
468
  * Effective paragraph-mark size used only to measure a visible list
468
469
  * paragraph's final line. Word lets the inherited complex-script size raise
469
470
  * that line box without enlarging the marker or Western text glyphs.
470
471
  */
471
472
  listParagraphMarkFontSize?: number;
472
- listMarkerBold?: boolean;
473
473
  /** Horizontal alignment of the marker around the paragraph's list anchor. */
474
474
  listMarkerAlignment?: "left" | "center" | "right";
475
475
  /**
@@ -1380,4 +1380,4 @@ declare function tableColumnsArePinned(table: TableBlock): boolean;
1380
1380
  /** Return the leading visual offset for a row with omitted grid columns. */
1381
1381
  declare const getTableRowLeadingWidth: (row: TableRow, columnWidths: readonly number[]) => number;
1382
1382
  //#endregion
1383
- export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, SectionPageNumbering, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned, types_d_exports };
1383
+ export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListMarkerFormatting, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, SectionPageNumbering, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned, types_d_exports };
@@ -2,7 +2,7 @@ import { ommlToMathml } from "../docx/mathToMathml.js";
2
2
  import { parseXmlDocument } from "../docx/xmlParser.js";
3
3
  import { evaluateFieldInstruction } from "../fields/evaluateField.js";
4
4
  import { hasComplexScriptFormatting, resolveComplexScriptFormatting } from "../layout-engine/measure/complexScriptFormatting.js";
5
- import { getListMarkerInlineWidth, getListMarkerVisualOffset } from "../layout-engine/measure/listMarkerWidth.js";
5
+ import { getListMarkerInlineWidth, getListMarkerVisualOffset, resolveListMarkerFont } from "../layout-engine/measure/listMarkerWidth.js";
6
6
  import { DOCX_SCRIPT_FONT_SCALE } from "../layout-engine/measure/measureHelpers.js";
7
7
  import { FONT_KERNING_MODE, countCompressibleSpaces, getFontKerningMode, getRunFontKerningMode, toPaintedText } from "../layout-engine/measure/textMeasurementPolicy.js";
8
8
  import { isFloatingImageRun } from "../layout-engine/types.js";
@@ -1463,18 +1463,7 @@ function renderParagraphFragment(fragment, block, measure, context, options = {}
1463
1463
  const markerStart = hanging > 0 ? indentLeft - hanging : indentLeft + firstLine;
1464
1464
  lineEl.style.paddingLeft = `${Math.max(0, markerStart)}px`;
1465
1465
  lineEl.style.textIndent = "0";
1466
- let firstTextRun;
1467
- if (!block.attrs.listMarkerFontFamily || !block.attrs.listMarkerFontSize || block.attrs.listMarkerBold === void 0) for (let ri = line.fromRun; ri <= line.toRun; ri++) {
1468
- const r = block.runs[ri];
1469
- if (r && r.kind === "text") {
1470
- firstTextRun = r;
1471
- break;
1472
- }
1473
- }
1474
- const markerFontFamily = block.attrs.listMarkerFontFamily ?? firstTextRun?.fontFamily ?? block.attrs.defaultFontFamily;
1475
- const markerFontSize = block.attrs.listMarkerFontSize ?? firstTextRun?.fontSize ?? block.attrs.defaultFontSize;
1476
- const markerBold = block.attrs.listMarkerBold ?? firstTextRun?.bold;
1477
- const marker = renderListMarker(block.attrs.listMarker, getListMarkerInlineWidth(block), getListMarkerVisualOffset(block), doc, markerFontFamily, markerFontSize, markerBold, block.attrs.listMarkerRevision, block.attrs.listMarkerSecondSlotOffsetTwips);
1466
+ const marker = renderListMarker(block.attrs.listMarker, getListMarkerInlineWidth(block), getListMarkerVisualOffset(block), doc, resolveListMarkerFont(block), block.attrs.listMarkerRevision, block.attrs.listMarkerSecondSlotOffsetTwips);
1478
1467
  const markerMarginLeft = markerStart - Math.min(indentLeft, 0);
1479
1468
  if (markerMarginLeft < 0) marker.style.marginLeft = `${markerMarginLeft}px`;
1480
1469
  lineEl.prepend(marker);
@@ -1491,13 +1480,15 @@ function renderParagraphFragment(fragment, block, measure, context, options = {}
1491
1480
  * tab grid. Long markers like "1.1.1." therefore grow to the next stop
1492
1481
  * instead of butting against the body text.
1493
1482
  */
1494
- function renderListMarker(marker, inlineWidth, visualOffset, doc, fontFamily, fontSize, bold, revision, secondSlotOffsetTwips) {
1483
+ function renderListMarker(marker, inlineWidth, visualOffset, doc, formatting, revision, secondSlotOffsetTwips) {
1495
1484
  const span = doc.createElement("span");
1496
1485
  span.className = "layout-list-marker";
1497
1486
  span.style.display = "inline-block";
1498
- if (fontFamily) span.style.fontFamily = resolveFontFamily(fontFamily).cssFallback;
1499
- if (fontSize) span.style.fontSize = `${fontSize * 96 / 72}px`;
1500
- if (bold !== void 0) span.style.fontWeight = bold ? "700" : "normal";
1487
+ span.style.fontFamily = resolveFontFamily(formatting.fontFamily).cssFallback;
1488
+ if (formatting.fontSize) span.style.fontSize = `${formatting.fontSize * 96 / 72}px`;
1489
+ if (formatting.bold !== void 0) span.style.fontWeight = formatting.bold ? "700" : "normal";
1490
+ if (formatting.italic !== void 0) span.style.fontStyle = formatting.italic ? "italic" : "normal";
1491
+ if (formatting.rtl !== void 0) span.dir = formatting.rtl ? "rtl" : "ltr";
1501
1492
  span.style.textAlign = "left";
1502
1493
  span.style.textAlignLast = "left";
1503
1494
  span.style.boxSizing = "border-box";
@@ -162,9 +162,7 @@ const readParagraphAttrs = (node) => {
162
162
  optionalBoolean(attrs, "listIsLegal", "paragraph.attrs.listIsLegal", issues);
163
163
  optionalString(attrs, "listMarker", "paragraph.attrs.listMarker", issues);
164
164
  optionalBoolean(attrs, "listMarkerHidden", "paragraph.attrs.listMarkerHidden", issues);
165
- optionalString(attrs, "listMarkerFontFamily", "paragraph.attrs.listMarkerFontFamily", issues);
166
- optionalNumber(attrs, "listMarkerFontSize", "paragraph.attrs.listMarkerFontSize", issues);
167
- optionalBoolean(attrs, "listMarkerBold", "paragraph.attrs.listMarkerBold", issues);
165
+ optionalTextFormatting(attrs, "listMarkerFormatting", "paragraph.attrs.listMarkerFormatting", issues);
168
166
  optionalOneOf(attrs, "listMarkerAlignment", "paragraph.attrs.listMarkerAlignment", issues, [
169
167
  "left",
170
168
  "center",
@@ -1397,12 +1395,10 @@ const validateParagraphFormatting = (value, path, issues) => {
1397
1395
  "listIsBullet",
1398
1396
  "listIsLegal",
1399
1397
  "listMarkerHidden",
1400
- "listMarkerBold",
1401
1398
  "listMarkerAllCaps"
1402
1399
  ]) optionalBoolean(value, key, `${path}.${key}`, issues);
1403
1400
  for (const key of PARAGRAPH_FORMATTING_NUMBER_KEYS) optionalNumber(value, key, `${path}.${key}`, issues);
1404
1401
  for (const key of [
1405
- "listMarkerFontSize",
1406
1402
  "listImplicitChildLevelAdvances",
1407
1403
  "listMarkerSecondSlotOffsetTwips",
1408
1404
  "listAbstractNumId",
@@ -1412,7 +1408,7 @@ const validateParagraphFormatting = (value, path, issues) => {
1412
1408
  optionalString(value, "styleId", `${path}.styleId`, issues);
1413
1409
  optionalOneOf(value, "listNumFmt", `${path}.listNumFmt`, issues, NUMBER_FORMAT_VALUES);
1414
1410
  optionalString(value, "listMarker", `${path}.listMarker`, issues);
1415
- optionalString(value, "listMarkerFontFamily", `${path}.listMarkerFontFamily`, issues);
1411
+ optionalTextFormatting(value, "listMarkerFormatting", `${path}.listMarkerFormatting`, issues);
1416
1412
  optionalOneOf(value, "listMarkerAlignment", `${path}.listMarkerAlignment`, issues, [
1417
1413
  "left",
1418
1414
  "center",
@@ -421,9 +421,7 @@ function listRenderingFromAttrs(attrs) {
421
421
  ...attrs.listIsLegal != null && { isLegal: attrs.listIsLegal },
422
422
  ...attrs.listNumFmt != null && { numFmt: attrs.listNumFmt },
423
423
  ...attrs.listMarkerHidden != null && { markerHidden: attrs.listMarkerHidden },
424
- ...attrs.listMarkerFontFamily != null && { markerFontFamily: attrs.listMarkerFontFamily },
425
- ...attrs.listMarkerFontSize != null && { markerFontSize: attrs.listMarkerFontSize },
426
- ...attrs.listMarkerBold != null && { markerBold: attrs.listMarkerBold },
424
+ ...attrs.listMarkerFormatting != null && { markerFormatting: attrs.listMarkerFormatting },
427
425
  ...attrs.listMarkerAlignment != null && { markerAlignment: attrs.listMarkerAlignment },
428
426
  ...attrs.listMarkerSuffix != null && { markerSuffix: attrs.listMarkerSuffix },
429
427
  ...attrs.listMarkerAllCaps != null && { markerAllCaps: attrs.listMarkerAllCaps },
@@ -290,9 +290,7 @@ function paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOver
290
290
  if (paragraph.listRendering?.isLegal) attrs.listIsLegal = paragraph.listRendering.isLegal;
291
291
  if (paragraph.listRendering?.marker) attrs.listMarker = paragraph.listRendering.marker;
292
292
  if (paragraph.listRendering?.markerHidden) attrs.listMarkerHidden = paragraph.listRendering.markerHidden;
293
- if (paragraph.listRendering?.markerFontFamily) attrs.listMarkerFontFamily = paragraph.listRendering.markerFontFamily;
294
- if (paragraph.listRendering?.markerFontSize) attrs.listMarkerFontSize = paragraph.listRendering.markerFontSize;
295
- if (paragraph.listRendering?.markerBold !== void 0) attrs.listMarkerBold = paragraph.listRendering.markerBold;
293
+ if (paragraph.listRendering?.markerFormatting) attrs.listMarkerFormatting = paragraph.listRendering.markerFormatting;
296
294
  if (paragraph.listRendering?.markerAlignment) attrs.listMarkerAlignment = paragraph.listRendering.markerAlignment;
297
295
  if (paragraph.listRendering?.markerSuffix) attrs.listMarkerSuffix = paragraph.listRendering.markerSuffix;
298
296
  if (paragraph.listRendering?.markerAllCaps) attrs.listMarkerAllCaps = paragraph.listRendering.markerAllCaps;
@@ -204,9 +204,7 @@ const paragraphNodeSpec = {
204
204
  listIsLegal: { default: null },
205
205
  listMarker: { default: null },
206
206
  listMarkerHidden: { default: null },
207
- listMarkerFontFamily: { default: null },
208
- listMarkerFontSize: { default: null },
209
- listMarkerBold: { default: null },
207
+ listMarkerFormatting: { default: null },
210
208
  listMarkerAlignment: { default: null },
211
209
  listMarkerSuffix: { default: null },
212
210
  listMarkerAllCaps: { default: null },
@@ -32,9 +32,7 @@ const LIST_FORMATTING_ATTRS = [
32
32
  "listNumFmt",
33
33
  "listMarker",
34
34
  "listMarkerHidden",
35
- "listMarkerFontFamily",
36
- "listMarkerFontSize",
37
- "listMarkerBold",
35
+ "listMarkerFormatting",
38
36
  "listMarkerAlignment",
39
37
  "listMarkerSuffix",
40
38
  "listLevelNumFmts",
@@ -69,12 +69,8 @@ type ParagraphAttrs = {
69
69
  listMarker?: string;
70
70
  /** Whether the list marker is hidden (w:vanish on numbering level rPr) */
71
71
  listMarkerHidden?: boolean;
72
- /** Marker font family from numbering level rPr */
73
- listMarkerFontFamily?: string;
74
- /** Marker font size from numbering level rPr, in points */
75
- listMarkerFontSize?: number;
76
- /** Marker bold state from numbering level rPr */
77
- listMarkerBold?: boolean;
72
+ /** Canonical numbering-level marker typography, in OOXML units. */
73
+ listMarkerFormatting?: document_d_exports.ListMarkerFormatting;
78
74
  /** Horizontal alignment of the marker around the paragraph's list anchor. */
79
75
  listMarkerAlignment?: "left" | "center" | "right";
80
76
  /**
@@ -200,7 +196,7 @@ type ParagraphAttrs = {
200
196
  type ParagraphPropertyChangeAttrs = Omit<document_d_exports.ParagraphPropertyChange, "previousFormatting" | "currentFormatting"> & {
201
197
  previousFormatting?: Omit<document_d_exports.ParagraphFormatting, "numPr"> & {
202
198
  numPr?: document_d_exports.ParagraphFormatting["numPr"] | null;
203
- } & Partial<Pick<ParagraphAttrs, "listIsBullet" | "listIsLegal" | "listNumFmt" | "listMarker" | "listMarkerHidden" | "listMarkerFontFamily" | "listMarkerFontSize" | "listMarkerBold" | "listMarkerAlignment" | "listMarkerSuffix" | "listLevelNumFmts" | "listLevelStarts" | "listAbstractNumId" | "listStartOverride" | "lineSpacingExplicit" | "direction" | "_autospacingBase">>;
199
+ } & Partial<Pick<ParagraphAttrs, "listIsBullet" | "listIsLegal" | "listNumFmt" | "listMarker" | "listMarkerHidden" | "listMarkerFormatting" | "listMarkerAlignment" | "listMarkerSuffix" | "listLevelNumFmts" | "listLevelStarts" | "listAbstractNumId" | "listStartOverride" | "lineSpacingExplicit" | "direction" | "_autospacingBase">>;
204
200
  currentFormatting?: document_d_exports.ParagraphFormatting;
205
201
  };
206
202
  /**
@@ -86,9 +86,7 @@ function listAttrsFromResolvedStyle(resolved, numbering) {
86
86
  listIsLegal: rendering?.isLegal ?? null,
87
87
  listMarker: rendering?.marker ?? null,
88
88
  listMarkerHidden: rendering?.markerHidden ?? null,
89
- listMarkerFontFamily: rendering?.markerFontFamily ?? null,
90
- listMarkerFontSize: rendering?.markerFontSize ?? null,
91
- listMarkerBold: rendering?.markerBold ?? null,
89
+ listMarkerFormatting: rendering?.markerFormatting ?? null,
92
90
  listMarkerAlignment: rendering?.markerAlignment ?? null,
93
91
  listMarkerSuffix: rendering?.markerSuffix ?? null,
94
92
  listMarkerAllCaps: rendering?.markerAllCaps ?? null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/folio-core",
3
- "version": "0.23.1",
3
+ "version": "0.24.0",
4
4
  "description": "Headless, framework-neutral core of folio: the OOXML (.docx) parser, document model, ProseMirror integration, and page-layout engine. No React.",
5
5
  "keywords": [
6
6
  "document-model",
@@ -113,7 +113,7 @@
113
113
  "perf": "bun scripts/profile-editor.ts"
114
114
  },
115
115
  "dependencies": {
116
- "@stll/docx-core": "^0.15.2",
116
+ "@stll/docx-core": "^0.16.0",
117
117
  "@stll/docx-utils": "^0.1.0",
118
118
  "@stll/template-conditions": "^0.1.0",
119
119
  "better-result": "3.0.1",