@json-to-office/core-docx 4.0.0 → 4.1.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.
@@ -12482,9 +12482,176 @@ async function expandBlocksWithPlugins(document, theme, plugins, render, preserv
12482
12482
  init_styleHelpers();
12483
12483
  init_defaults();
12484
12484
  init_widthUtils();
12485
+
12486
+ // src/quality/text-inventory.ts
12485
12487
  function asRecord(value) {
12486
12488
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
12487
12489
  }
12490
+ function twipsToPt(value) {
12491
+ return typeof value === "number" && Number.isFinite(value) ? value / 20 : void 0;
12492
+ }
12493
+ function frameOf(props) {
12494
+ const widthPt = twipsToPt(asRecord(props.floating)?.width);
12495
+ return widthPt === void 0 ? void 0 : { widthPt };
12496
+ }
12497
+ function collectDocxTextInventory(children, basePath = "/children") {
12498
+ const entries = [];
12499
+ const add = (path3, text, role, extra = {}) => {
12500
+ if (typeof text !== "string" || text.trim() === "") return;
12501
+ entries.push({
12502
+ id: `docx:text:${path3}`,
12503
+ kind: "docx/text",
12504
+ path: path3,
12505
+ text,
12506
+ role,
12507
+ order: entries.length,
12508
+ ...extra
12509
+ });
12510
+ };
12511
+ const visitCell = (cell, path3, role, inherited) => {
12512
+ const cellRole = inherited.repeats ? "chrome" : role;
12513
+ const extra = inherited.repeats ? { repeats: true } : {};
12514
+ if (typeof cell === "string") {
12515
+ add(path3, cell, cellRole, extra);
12516
+ return;
12517
+ }
12518
+ const rec = asRecord(cell);
12519
+ if (!rec) return;
12520
+ if ("content" in rec) {
12521
+ if (typeof rec.content === "string")
12522
+ add(`${path3}/content`, rec.content, cellRole, extra);
12523
+ else {
12524
+ const inner = asRecord(rec.content);
12525
+ if (inner) visitNode(inner, `${path3}/content`, inherited);
12526
+ }
12527
+ return;
12528
+ }
12529
+ if (typeof rec.name === "string") visitNode(rec, path3, inherited);
12530
+ };
12531
+ const visitNode = (node, path3, inherited) => {
12532
+ if (node.enabled === false) return;
12533
+ const props = asRecord(node.props) ?? {};
12534
+ const name = typeof node.name === "string" ? node.name : "";
12535
+ const extra = {
12536
+ ...inherited.repeats && { repeats: true }
12537
+ };
12538
+ switch (name) {
12539
+ case "heading": {
12540
+ if (inherited.repeats) {
12541
+ add(`${path3}/props/text`, props.text, "chrome", extra);
12542
+ break;
12543
+ }
12544
+ const level = typeof props.level === "number" && Number.isFinite(props.level) ? props.level : 1;
12545
+ add(`${path3}/props/text`, props.text, "heading", { ...extra, level });
12546
+ break;
12547
+ }
12548
+ case "paragraph": {
12549
+ const frame = frameOf(props);
12550
+ add(
12551
+ `${path3}/props/text`,
12552
+ props.text,
12553
+ inherited.repeats ? "chrome" : "body",
12554
+ {
12555
+ ...extra,
12556
+ ...frame && { frame }
12557
+ }
12558
+ );
12559
+ break;
12560
+ }
12561
+ case "list": {
12562
+ if (Array.isArray(props.items)) {
12563
+ props.items.forEach((item, index) => {
12564
+ const itemPath = `${path3}/props/items/${index}`;
12565
+ if (typeof item === "string")
12566
+ add(itemPath, item, "list-item", extra);
12567
+ else {
12568
+ const rec = asRecord(item);
12569
+ if (rec) add(`${itemPath}/text`, rec.text, "list-item", extra);
12570
+ }
12571
+ });
12572
+ }
12573
+ break;
12574
+ }
12575
+ case "table": {
12576
+ const columns = Array.isArray(props.columns) ? props.columns.map(asRecord) : [];
12577
+ columns.forEach((column, columnIndex) => {
12578
+ if (!column) return;
12579
+ visitCell(
12580
+ column.header,
12581
+ `${path3}/props/columns/${columnIndex}/header`,
12582
+ "table-header",
12583
+ inherited
12584
+ );
12585
+ });
12586
+ const rows = Math.max(
12587
+ 0,
12588
+ ...columns.map(
12589
+ (column) => Array.isArray(column?.cells) ? column.cells.length : 0
12590
+ )
12591
+ );
12592
+ for (let row = 0; row < rows; row++) {
12593
+ columns.forEach((column, columnIndex) => {
12594
+ if (!column || !Array.isArray(column.cells)) return;
12595
+ if (row >= column.cells.length) return;
12596
+ visitCell(
12597
+ column.cells[row],
12598
+ `${path3}/props/columns/${columnIndex}/cells/${row}`,
12599
+ "table-cell",
12600
+ inherited
12601
+ );
12602
+ });
12603
+ }
12604
+ break;
12605
+ }
12606
+ case "statistic": {
12607
+ add(`${path3}/props/number`, props.number, "statistic", extra);
12608
+ add(`${path3}/props/description`, props.description, "statistic", extra);
12609
+ break;
12610
+ }
12611
+ case "image":
12612
+ case "chart":
12613
+ case "highcharts":
12614
+ case "visual":
12615
+ case "visual-native": {
12616
+ add(`${path3}/props/caption`, props.caption, "caption", extra);
12617
+ break;
12618
+ }
12619
+ case "section": {
12620
+ for (const part of ["header", "footer"]) {
12621
+ const components = props[part];
12622
+ if (!Array.isArray(components)) continue;
12623
+ components.forEach((component, index) => {
12624
+ const rec = asRecord(component);
12625
+ if (rec) {
12626
+ visitNode(rec, `${path3}/props/${part}/${index}`, {
12627
+ repeats: true
12628
+ });
12629
+ }
12630
+ });
12631
+ }
12632
+ break;
12633
+ }
12634
+ default:
12635
+ break;
12636
+ }
12637
+ if (Array.isArray(node.children)) {
12638
+ node.children.forEach((child, index) => {
12639
+ const rec = asRecord(child);
12640
+ if (rec) visitNode(rec, `${path3}/children/${index}`, inherited);
12641
+ });
12642
+ }
12643
+ };
12644
+ children.forEach((child, index) => {
12645
+ const rec = asRecord(child);
12646
+ if (rec) visitNode(rec, `${basePath}/${index}`, {});
12647
+ });
12648
+ return entries;
12649
+ }
12650
+
12651
+ // src/quality/facts.ts
12652
+ function asRecord2(value) {
12653
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
12654
+ }
12488
12655
  function pageBox(theme, themeName, pageOverride) {
12489
12656
  const page = createSectionProperties(
12490
12657
  getColumnSettings("single"),
@@ -12510,7 +12677,7 @@ function tableFact(props, path3, availableWidthTwips) {
12510
12677
  let percentSum = 0;
12511
12678
  const explicitWidths = [];
12512
12679
  columns.forEach((column, index) => {
12513
- const width = asRecord(column)?.width;
12680
+ const width = asRecord2(column)?.width;
12514
12681
  if (typeof width === "number" && Number.isFinite(width)) {
12515
12682
  hasExplicitWidth = true;
12516
12683
  explicitWidths.push({ index, width });
@@ -12591,7 +12758,7 @@ function chartFact(node, props, path3, paletteTokens) {
12591
12758
  const categoryCount = Math.max(
12592
12759
  0,
12593
12760
  ...series.map((entry) => {
12594
- const record = asRecord(entry);
12761
+ const record = asRecord2(entry);
12595
12762
  const labels = Array.isArray(record?.labels) ? record.labels.length : 0;
12596
12763
  const values = Array.isArray(record?.values) ? record.values.length : 0;
12597
12764
  return Math.max(labels, values);
@@ -12633,8 +12800,8 @@ function statesOwnBorders(authored) {
12633
12800
  return true;
12634
12801
  }
12635
12802
  const layers = [
12636
- asRecord(authored.cellDefaults),
12637
- asRecord(authored.headerCellDefaults)
12803
+ asRecord2(authored.cellDefaults),
12804
+ asRecord2(authored.headerCellDefaults)
12638
12805
  ];
12639
12806
  if (layers.some(
12640
12807
  (layer) => layer?.borderSize !== void 0 || layer?.borderColor !== void 0
@@ -12643,8 +12810,8 @@ function statesOwnBorders(authored) {
12643
12810
  }
12644
12811
  const columns = Array.isArray(authored.columns) ? authored.columns : [];
12645
12812
  return columns.some((column) => {
12646
- const record = asRecord(column);
12647
- const defaults = asRecord(record?.cellDefaults);
12813
+ const record = asRecord2(column);
12814
+ const defaults = asRecord2(record?.cellDefaults);
12648
12815
  return defaults?.borderSize !== void 0 || defaults?.borderColor !== void 0;
12649
12816
  });
12650
12817
  }
@@ -12668,7 +12835,7 @@ function tableDesignFact(props, path3, theme, themeName, authored) {
12668
12835
  );
12669
12836
  const columns = authoredColumns.map(
12670
12837
  (column, index) => {
12671
- const authoredColumn = asRecord(column) ?? {};
12838
+ const authoredColumn = asRecord2(column) ?? {};
12672
12839
  const cells = Array.isArray(authoredColumn.cells) ? authoredColumn.cells : [];
12673
12840
  const alignments = /* @__PURE__ */ new Set();
12674
12841
  const values = [];
@@ -12687,11 +12854,11 @@ function tableDesignFact(props, path3, theme, themeName, authored) {
12687
12854
  },
12688
12855
  values,
12689
12856
  alignment: alignments.size === 1 ? [...alignments][0] : alignments.size === 0 ? "left" : "mixed",
12690
- hasCellDefaults: asRecord(authoredColumn.cellDefaults) !== void 0,
12691
- hasHeader: asRecord(authoredColumn.header) !== void 0,
12857
+ hasCellDefaults: asRecord2(authoredColumn.cellDefaults) !== void 0,
12858
+ hasHeader: asRecord2(authoredColumn.header) !== void 0,
12692
12859
  generated: false,
12693
12860
  cellsWithOwnAlignment: cells.flatMap((cell, cellIndex) => {
12694
- const alignment2 = asRecord(cell)?.horizontalAlignment;
12861
+ const alignment2 = asRecord2(cell)?.horizontalAlignment;
12695
12862
  return typeof alignment2 === "string" && DOCX_ALIGNMENTS.has(alignment2) && alignment2 !== "right" ? [cellIndex] : [];
12696
12863
  })
12697
12864
  };
@@ -12712,7 +12879,7 @@ function finiteNumber(value) {
12712
12879
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
12713
12880
  }
12714
12881
  function lineHeightPt(fontSizePt, spacing) {
12715
- const rule = asRecord(spacing);
12882
+ const rule = asRecord2(spacing);
12716
12883
  const value = finiteNumber(rule?.value);
12717
12884
  switch (rule?.type) {
12718
12885
  // `exactly` is an absolute line box, and display type routinely sets it
@@ -12729,7 +12896,7 @@ function lineHeightPt(fontSizePt, spacing) {
12729
12896
  }
12730
12897
  }
12731
12898
  function characterSpacingPt(spacing) {
12732
- const rule = asRecord(spacing);
12899
+ const rule = asRecord2(spacing);
12733
12900
  const value = finiteNumber(rule?.value);
12734
12901
  if (rule === void 0 || value === void 0) return 0;
12735
12902
  return rule.type === "condensed" ? -value / TWIPS_PER_POINT3 : value / TWIPS_PER_POINT3;
@@ -12743,7 +12910,7 @@ function nodeAtPointer(root, pointer) {
12743
12910
  current = current[index];
12744
12911
  continue;
12745
12912
  }
12746
- const record = asRecord(current);
12913
+ const record = asRecord2(current);
12747
12914
  if (!record) return void 0;
12748
12915
  current = record[token];
12749
12916
  }
@@ -12759,10 +12926,10 @@ function styleKey(node, props) {
12759
12926
  return typeof props.themeStyle === "string" && props.themeStyle !== "" ? props.themeStyle : "normal";
12760
12927
  }
12761
12928
  function effectiveFontSize(node, props, typography) {
12762
- const authored = finiteNumber(asRecord(props.font)?.size);
12929
+ const authored = finiteNumber(asRecord2(props.font)?.size);
12763
12930
  if (authored !== void 0) return { fontSizePt: authored, authored: true };
12764
12931
  const { styles, theme } = typography;
12765
- const style = asRecord(styles[styleKey(node, props)]) ?? asRecord(styles.normal);
12932
+ const style = asRecord2(styles[styleKey(node, props)]) ?? asRecord2(styles.normal);
12766
12933
  const stated = finiteNumber(style?.size);
12767
12934
  if (stated !== void 0) return { fontSizePt: stated, authored: false };
12768
12935
  const reference = style?.font === "heading" || style?.font === "body" || style?.font === "mono" || style?.font === "light" ? style.font : void 0;
@@ -12772,11 +12939,11 @@ function effectiveFontSize(node, props, typography) {
12772
12939
  function pinnedLineBox(props, path3) {
12773
12940
  const candidates = [
12774
12941
  [props.lineSpacing, `${path3}/props/lineSpacing`],
12775
- [asRecord(props.font)?.lineSpacing, `${path3}/props/font/lineSpacing`]
12942
+ [asRecord2(props.font)?.lineSpacing, `${path3}/props/font/lineSpacing`]
12776
12943
  ];
12777
12944
  for (const [spacing, pointer] of candidates) {
12778
12945
  if (spacing === void 0) continue;
12779
- const rule = asRecord(spacing);
12946
+ const rule = asRecord2(spacing);
12780
12947
  if (rule?.type !== "exactly") return void 0;
12781
12948
  const lineBoxPt = finiteNumber(rule.value);
12782
12949
  return lineBoxPt === void 0 ? void 0 : { lineBoxPt, pointer };
@@ -12802,8 +12969,8 @@ function lineBoxFact(node, props, path3, typography, authored) {
12802
12969
  };
12803
12970
  }
12804
12971
  function frameSignature(floating) {
12805
- const horizontal = asRecord(floating.horizontalPosition);
12806
- const vertical = asRecord(floating.verticalPosition);
12972
+ const horizontal = asRecord2(floating.horizontalPosition);
12973
+ const vertical = asRecord2(floating.verticalPosition);
12807
12974
  return JSON.stringify([
12808
12975
  floating.width ?? null,
12809
12976
  floating.height ?? null,
@@ -12813,24 +12980,24 @@ function frameSignature(floating) {
12813
12980
  vertical?.offset ?? null,
12814
12981
  vertical?.relative ?? null,
12815
12982
  vertical?.align ?? null,
12816
- asRecord(floating.wrap)?.type ?? null
12983
+ asRecord2(floating.wrap)?.type ?? null
12817
12984
  ]);
12818
12985
  }
12819
12986
  function frameTextFact(props, path3, page, frameChainId, flowIndex) {
12820
- const floating = asRecord(props.floating);
12987
+ const floating = asRecord2(props.floating);
12821
12988
  const frameWidthTwips = finiteNumber(floating?.width);
12822
12989
  if (frameWidthTwips === void 0) return void 0;
12823
12990
  const text = typeof props.text === "string" ? props.text : "";
12824
12991
  if (text.trim() === "") return void 0;
12825
- const font = asRecord(props.font);
12992
+ const font = asRecord2(props.font);
12826
12993
  const fontSizePt = finiteNumber(font?.size);
12827
12994
  if (fontSizePt === void 0) return void 0;
12828
12995
  const longestWord = text.split(/\s+/).reduce(
12829
12996
  (longest, word) => word.length > longest.length ? word : longest,
12830
12997
  ""
12831
12998
  );
12832
- const horizontal = asRecord(floating?.horizontalPosition);
12833
- const vertical = asRecord(floating?.verticalPosition);
12999
+ const horizontal = asRecord2(floating?.horizontalPosition);
13000
+ const vertical = asRecord2(floating?.verticalPosition);
12834
13001
  const offsetX = finiteNumber(horizontal?.offset);
12835
13002
  const offsetY = finiteNumber(vertical?.offset);
12836
13003
  const absolutelyPinned = horizontal?.offset !== void 0 || vertical?.offset !== void 0;
@@ -12901,7 +13068,7 @@ function svgTextFacts(props, path3) {
12901
13068
  return facts;
12902
13069
  }
12903
13070
  function walkActive(node, path3, page, visit) {
12904
- const rec = asRecord(node);
13071
+ const rec = asRecord2(node);
12905
13072
  if (!rec || rec.enabled === false) return;
12906
13073
  visit(rec, path3, page);
12907
13074
  const children = Array.isArray(rec.children) ? rec.children : [];
@@ -12924,7 +13091,7 @@ function textSizeFact(node, props, path3, typography, role) {
12924
13091
  }
12925
13092
  function tableTextSizeFacts(props, authored, path3, typography, page) {
12926
13093
  const facts = [];
12927
- const sizeOf = (holder) => finiteNumber(asRecord(asRecord(holder)?.font)?.size);
13094
+ const sizeOf = (holder) => finiteNumber(asRecord2(asRecord2(holder)?.font)?.size);
12928
13095
  const authoredSize = (holder, pointer, role) => {
12929
13096
  const size = sizeOf(holder);
12930
13097
  if (size === void 0) return;
@@ -12939,11 +13106,11 @@ function tableTextSizeFacts(props, authored, path3, typography, page) {
12939
13106
  });
12940
13107
  };
12941
13108
  const cellContent = (holder, pointer, role) => {
12942
- const content = asRecord(holder)?.content;
12943
- if (asRecord(content) === void 0) return;
13109
+ const content = asRecord2(holder)?.content;
13110
+ if (asRecord2(content) === void 0) return;
12944
13111
  walkActive(content, `${pointer}/content`, page, (node, nodePath) => {
12945
13112
  if (node.name !== "paragraph" && node.name !== "heading") return;
12946
- const nodeProps = asRecord(node.props) ?? {};
13113
+ const nodeProps = asRecord2(node.props) ?? {};
12947
13114
  if (typeof nodeProps.text !== "string" || nodeProps.text.trim() === "")
12948
13115
  return;
12949
13116
  const fact = textSizeFact(node, nodeProps, nodePath, typography, role);
@@ -12963,7 +13130,7 @@ function tableTextSizeFacts(props, authored, path3, typography, page) {
12963
13130
  );
12964
13131
  const columns = Array.isArray(authored.columns) ? authored.columns : [];
12965
13132
  columns.forEach((column, index) => {
12966
- const record = asRecord(column) ?? {};
13133
+ const record = asRecord2(column) ?? {};
12967
13134
  const columnPath = `${path3}/props/columns/${index}`;
12968
13135
  authoredSize(
12969
13136
  record.cellDefaults,
@@ -13060,7 +13227,7 @@ function prepareDocxQualityDocument(document, options = {}) {
13060
13227
  let inherited = {};
13061
13228
  topLevel.forEach((node, index) => {
13062
13229
  if (node?.name !== "section") return;
13063
- const props = asRecord(node.props) ?? {};
13230
+ const props = asRecord2(node.props) ?? {};
13064
13231
  const part = (kind) => props[kind] === "linkToPrevious" ? inherited[kind] : props[kind];
13065
13232
  const header = part("header");
13066
13233
  const footer = part("footer");
@@ -13068,8 +13235,8 @@ function prepareDocxQualityDocument(document, options = {}) {
13068
13235
  for (const kind of ["header", "footer"]) {
13069
13236
  if (!Array.isArray(props[kind])) continue;
13070
13237
  const drawnByBlock = !Array.isArray(
13071
- asRecord(
13072
- asRecord(
13238
+ asRecord2(
13239
+ asRecord2(
13073
13240
  (Array.isArray(themed.document.children) ? themed.document.children : [])[index]
13074
13241
  )?.props
13075
13242
  )?.[kind]
@@ -13077,7 +13244,7 @@ function prepareDocxQualityDocument(document, options = {}) {
13077
13244
  const part2 = props[kind];
13078
13245
  const visitChrome = (child, childPath) => {
13079
13246
  if (child.name !== "paragraph" && child.name !== "heading") return;
13080
- const childProps = asRecord(child.props) ?? {};
13247
+ const childProps = asRecord2(child.props) ?? {};
13081
13248
  if (typeof childProps.text !== "string" || childProps.text.trim() === "")
13082
13249
  return;
13083
13250
  const fact = textSizeFact(
@@ -13134,7 +13301,7 @@ function prepareDocxQualityDocument(document, options = {}) {
13134
13301
  const paletteTokens = resolved.theme.palette?.chart?.map(
13135
13302
  (value) => `#${resolveDesignColor2(value, visualColors)}`
13136
13303
  ) ?? SERIES_COLOR_TOKENS.filter((token) => paletteHexes[token] !== void 0);
13137
- const authoredPropsAt = (pointer) => asRecord(asRecord(nodeAtPointer(context.document, pointer))?.props);
13304
+ const authoredPropsAt = (pointer) => asRecord2(asRecord2(nodeAtPointer(context.document, pointer))?.props);
13138
13305
  const roleSizesPt = {};
13139
13306
  for (const key of Object.keys(typography.styles)) {
13140
13307
  const size = effectiveFontSize(
@@ -13170,7 +13337,7 @@ function prepareDocxQualityDocument(document, options = {}) {
13170
13337
  fontFamilies: [
13171
13338
  ...new Set(
13172
13339
  ["heading", "body"].flatMap((role) => {
13173
- const family = asRecord(
13340
+ const family = asRecord2(
13174
13341
  resolved.theme.fonts?.[role]
13175
13342
  )?.family;
13176
13343
  return typeof family === "string" && family.trim() !== "" ? [family] : [];
@@ -13206,12 +13373,15 @@ function prepareDocxQualityDocument(document, options = {}) {
13206
13373
  excerpt: occurrence.match.excerpt
13207
13374
  });
13208
13375
  });
13376
+ for (const entry of collectDocxTextInventory(resolved.children)) {
13377
+ addFact({ ...entry, generated: authoredPath(entry.path) !== entry.path });
13378
+ }
13209
13379
  let previousVisitPath;
13210
13380
  let lastFrame;
13211
13381
  let flowIndex = 0;
13212
13382
  const parentOf = (path3) => path3.slice(0, path3.lastIndexOf("/"));
13213
13383
  const visit = (node, path3, page) => {
13214
- const props = asRecord(node.props) ?? {};
13384
+ const props = asRecord2(node.props) ?? {};
13215
13385
  if (node.name === "table") {
13216
13386
  const fact = tableFact(props, path3, page.availableWidthTwips);
13217
13387
  if (fact) addFact(fact);
@@ -13240,7 +13410,7 @@ function prepareDocxQualityDocument(document, options = {}) {
13240
13410
  return {
13241
13411
  ...column,
13242
13412
  path: authored,
13243
- generated: table2 === void 0 || asRecord(nodeAtPointer(themed.document, table2))?.name !== "table"
13413
+ generated: table2 === void 0 || asRecord2(nodeAtPointer(themed.document, table2))?.name !== "table"
13244
13414
  };
13245
13415
  })
13246
13416
  });
@@ -13256,7 +13426,7 @@ function prepareDocxQualityDocument(document, options = {}) {
13256
13426
  addFact({
13257
13427
  ...fact,
13258
13428
  generated: !["chart", "highcharts"].includes(
13259
- String(asRecord(nodeAtPointer(themed.document, authored))?.name)
13429
+ String(asRecord2(nodeAtPointer(themed.document, authored))?.name)
13260
13430
  ),
13261
13431
  seriesColorsPath: authoredPath(fact.seriesColorsPath),
13262
13432
  ...fact.annotation && {
@@ -13270,7 +13440,7 @@ function prepareDocxQualityDocument(document, options = {}) {
13270
13440
  }
13271
13441
  }
13272
13442
  if (node.name === "paragraph" || node.name === "text-box") {
13273
- const floating = asRecord(props.floating);
13443
+ const floating = asRecord2(props.floating);
13274
13444
  if (floating) {
13275
13445
  const signature = frameSignature(floating);
13276
13446
  const chainId = lastFrame !== void 0 && previousVisitPath === lastFrame.path && parentOf(lastFrame.path) === parentOf(path3) && lastFrame.signature === signature ? lastFrame.chainId : `docx:frame-chain:${path3}`;