@json-to-office/core-docx 7.1.2 → 7.2.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.
@@ -1804,15 +1804,15 @@ function convertLineSpacing(lineSpacing) {
1804
1804
  lineRule2 = "auto";
1805
1805
  break;
1806
1806
  case "atLeast":
1807
- line = value !== void 0 ? value * TWIPS_PER_POINT : void 0;
1807
+ line = value !== void 0 ? pointsToTwips(value) : void 0;
1808
1808
  lineRule2 = "atLeast";
1809
1809
  break;
1810
1810
  case "exactly":
1811
- line = value !== void 0 ? value * TWIPS_PER_POINT : void 0;
1811
+ line = value !== void 0 ? pointsToTwips(value) : void 0;
1812
1812
  lineRule2 = "exact";
1813
1813
  break;
1814
1814
  case "multiple":
1815
- line = value !== void 0 ? value * SINGLE_LINE_SPACING_TWIPS : void 0;
1815
+ line = value !== void 0 ? Math.round(value * SINGLE_LINE_SPACING_TWIPS) : void 0;
1816
1816
  lineRule2 = "auto";
1817
1817
  break;
1818
1818
  default:
@@ -2797,6 +2797,9 @@ import { createHash as createHash2 } from "crypto";
2797
2797
  function pointsToTwips2(points) {
2798
2798
  return Math.round(points * TWIPS_PER_POINT2);
2799
2799
  }
2800
+ function wholeTwips(twips2) {
2801
+ return Math.round(twips2);
2802
+ }
2800
2803
  function pointsToHalfPoints(points) {
2801
2804
  return Math.round(points * 2);
2802
2805
  }
@@ -2812,8 +2815,8 @@ function inchesToEmu(inches) {
2812
2815
  function pointsToEmu(points) {
2813
2816
  return Math.round(points * EMU_PER_INCH / 72);
2814
2817
  }
2815
- function twipsToEmu(twips) {
2816
- return Math.round(twips * EMU_PER_TWIP);
2818
+ function twipsToEmu(twips2) {
2819
+ return Math.round(twips2 * EMU_PER_TWIP);
2817
2820
  }
2818
2821
  function pixelsToEmu(pixels) {
2819
2822
  return Math.round(pixels * 9525);
@@ -2821,8 +2824,8 @@ function pixelsToEmu(pixels) {
2821
2824
  function emuToPixels(emu) {
2822
2825
  return emu / 9525;
2823
2826
  }
2824
- function twipsToPixels(twips) {
2825
- return twips / TWIPS_PER_INCH * 96;
2827
+ function twipsToPixels(twips2) {
2828
+ return twips2 / TWIPS_PER_INCH * 96;
2826
2829
  }
2827
2830
  function irColor(hex) {
2828
2831
  return { hex: hex.startsWith("#") ? hex.slice(1) : hex };
@@ -4247,11 +4250,71 @@ var init_chartParts = __esm({
4247
4250
  }
4248
4251
  });
4249
4252
 
4253
+ // src/renderers/office-open/cellTocs.ts
4254
+ function spliceCellTocs(zip, tocs, stringifyTableOfContents) {
4255
+ if (tocs.length === 0) return;
4256
+ const spliced = /* @__PURE__ */ new Set();
4257
+ for (const entry of zip.getEntries()) {
4258
+ if (!TABLE_PARTS.test(entry.entryName)) continue;
4259
+ const original = entry.getData().toString("utf8");
4260
+ let xml = original;
4261
+ for (const toc of tocs) {
4262
+ const start = markerParagraph(xml, toc.start);
4263
+ if (!start) continue;
4264
+ const end = markerParagraph(xml, toc.end);
4265
+ if (spliced.has(toc) || !end || end.from < start.to) {
4266
+ throw new Error(
4267
+ `the office-open renderer lost track of a table of contents in a table cell (${entry.entryName})`
4268
+ );
4269
+ }
4270
+ xml = xml.slice(0, start.from) + stringifyTableOfContents(
4271
+ toc.alias,
4272
+ toc.options,
4273
+ xml.slice(start.to, end.from)
4274
+ ) + xml.slice(end.to);
4275
+ spliced.add(toc);
4276
+ }
4277
+ if (xml !== original) zip.updateFile(entry, Buffer.from(xml, "utf8"));
4278
+ }
4279
+ if (spliced.size !== tocs.length) {
4280
+ throw new Error(
4281
+ `the office-open renderer placed ${tocs.length} table(s) of contents in table cells and found ${spliced.size}`
4282
+ );
4283
+ }
4284
+ }
4285
+ function markerParagraph(xml, marker) {
4286
+ const at = xml.indexOf(marker);
4287
+ if (at < 0) return void 0;
4288
+ const from = Math.max(
4289
+ xml.lastIndexOf("<w:p>", at),
4290
+ xml.lastIndexOf("<w:p ", at)
4291
+ );
4292
+ const close = xml.indexOf("</w:p>", at);
4293
+ if (xml.indexOf(marker, at + marker.length) >= 0 || from < 0 || close < 0 || xml.lastIndexOf("</w:p>", at) > from) {
4294
+ throw new Error(
4295
+ "the office-open renderer found a table-of-contents marker it did not write"
4296
+ );
4297
+ }
4298
+ return { from, to: close + "</w:p>".length };
4299
+ }
4300
+ var TABLE_PARTS;
4301
+ var init_cellTocs = __esm({
4302
+ "src/renderers/office-open/cellTocs.ts"() {
4303
+ "use strict";
4304
+ TABLE_PARTS = /^word\/(?:document|header\d+|footer\d+)\.xml$/;
4305
+ }
4306
+ });
4307
+
4250
4308
  // src/renderers/office-open/emit.ts
4251
4309
  import { assertNever as assertNever2 } from "@json-to-office/shared/rendering";
4252
4310
  function emptyContext() {
4253
4311
  let next = 1;
4254
- return { pictures: /* @__PURE__ */ new Map(), nextDrawingId: () => next++, charts: [] };
4312
+ return {
4313
+ pictures: /* @__PURE__ */ new Map(),
4314
+ nextDrawingId: () => next++,
4315
+ charts: [],
4316
+ cellTocs: []
4317
+ };
4255
4318
  }
4256
4319
  function simpleField(instruction, cachedText) {
4257
4320
  return {
@@ -4261,6 +4324,9 @@ function simpleField(instruction, cachedText) {
4261
4324
  }
4262
4325
  };
4263
4326
  }
4327
+ function twips(value) {
4328
+ return Math.floor(value);
4329
+ }
4264
4330
  function runProperties2(formatting) {
4265
4331
  if (!formatting) return {};
4266
4332
  const out = {};
@@ -4297,7 +4363,9 @@ function runProperties2(formatting) {
4297
4363
  if (formatting.scalePercent !== void 0)
4298
4364
  out.scale = formatting.scalePercent;
4299
4365
  if (formatting.characterSpacingTwentieths !== void 0) {
4300
- out.characterSpacing = formatting.characterSpacingTwentieths;
4366
+ const stated = formatting.characterSpacingTwentieths;
4367
+ const twentieths = twips(stated);
4368
+ out.characterSpacing = twentieths === 0 && stated !== 0 ? "0pt" : twentieths;
4301
4369
  }
4302
4370
  if (formatting.language) out.language = { value: formatting.language };
4303
4371
  if (formatting.noProof !== void 0) out.noProof = formatting.noProof;
@@ -4688,16 +4756,16 @@ function paragraphProperties2(formatting) {
4688
4756
  if (formatting.indent) {
4689
4757
  const indent = {};
4690
4758
  if (formatting.indent.leftTwips !== void 0) {
4691
- indent.left = formatting.indent.leftTwips;
4759
+ indent.left = twips(formatting.indent.leftTwips);
4692
4760
  }
4693
4761
  if (formatting.indent.rightTwips !== void 0) {
4694
- indent.right = formatting.indent.rightTwips;
4762
+ indent.right = twips(formatting.indent.rightTwips);
4695
4763
  }
4696
4764
  if (formatting.indent.firstLineTwips !== void 0) {
4697
- indent.firstLine = formatting.indent.firstLineTwips;
4765
+ indent.firstLine = twips(formatting.indent.firstLineTwips);
4698
4766
  }
4699
4767
  if (formatting.indent.hangingTwips !== void 0) {
4700
- indent.hanging = formatting.indent.hangingTwips;
4768
+ indent.hanging = twips(formatting.indent.hangingTwips);
4701
4769
  }
4702
4770
  out.indent = indent;
4703
4771
  }
@@ -4828,14 +4896,14 @@ function table(value, ctx) {
4828
4896
  // `w:w` is an integer in OOXML. A grid that splits a remainder between
4829
4897
  // unstated columns comes out fractional, and the XML would carry it
4830
4898
  // verbatim; floored here, as the docxjs backend floors it.
4831
- size: value.width.kind === "auto" ? 0 : value.width.kind === "twips" ? Math.floor(value.width.value) : value.width.value,
4899
+ size: value.width.kind === "auto" ? 0 : value.width.kind === "twips" ? twips(value.width.value) : value.width.value,
4832
4900
  type: value.width.kind === "twips" ? "dxa" : value.width.kind === "percent" ? "pct" : "auto"
4833
4901
  },
4834
4902
  layout: value.layout,
4835
4903
  // An empty grid is a table with nothing to say about its columns, which is
4836
4904
  // not the same as one whose columns are all zero wide. The compiler hands
4837
4905
  // over every grid in twips; floored for the same reason as the width.
4838
- ...value.columnGrid.values.length > 0 ? { columnWidths: value.columnGrid.values.map(Math.floor) } : {},
4906
+ ...value.columnGrid.values.length > 0 ? { columnWidths: value.columnGrid.values.map(twips) } : {},
4839
4907
  ...value.alignment ? { alignment: alignment(value.alignment) } : {},
4840
4908
  ...value.borders ? { borders: borders2(value.borders) } : {},
4841
4909
  ...value.cellMargins ? { margins: cellMargins(value.cellMargins) } : {},
@@ -4846,14 +4914,22 @@ function tableFloat2(floating) {
4846
4914
  return {
4847
4915
  ...floating.horizontalAnchor ? { horizontalAnchor: floating.horizontalAnchor } : {},
4848
4916
  ...floating.verticalAnchor ? { verticalAnchor: floating.verticalAnchor } : {},
4849
- ...floating.absoluteHorizontalPositionTwips !== void 0 ? { absoluteHorizontalPosition: floating.absoluteHorizontalPositionTwips } : {},
4917
+ ...floating.absoluteHorizontalPositionTwips !== void 0 ? {
4918
+ absoluteHorizontalPosition: twips(
4919
+ floating.absoluteHorizontalPositionTwips
4920
+ )
4921
+ } : {},
4850
4922
  ...floating.relativeHorizontalPosition ? { relativeHorizontalPosition: floating.relativeHorizontalPosition } : {},
4851
- ...floating.absoluteVerticalPositionTwips !== void 0 ? { absoluteVerticalPosition: floating.absoluteVerticalPositionTwips } : {},
4923
+ ...floating.absoluteVerticalPositionTwips !== void 0 ? {
4924
+ absoluteVerticalPosition: twips(
4925
+ floating.absoluteVerticalPositionTwips
4926
+ )
4927
+ } : {},
4852
4928
  ...floating.relativeVerticalPosition ? { relativeVerticalPosition: floating.relativeVerticalPosition } : {},
4853
- ...floating.topFromTextTwips !== void 0 ? { topFromText: floating.topFromTextTwips } : {},
4854
- ...floating.rightFromTextTwips !== void 0 ? { rightFromText: floating.rightFromTextTwips } : {},
4855
- ...floating.bottomFromTextTwips !== void 0 ? { bottomFromText: floating.bottomFromTextTwips } : {},
4856
- ...floating.leftFromTextTwips !== void 0 ? { leftFromText: floating.leftFromTextTwips } : {},
4929
+ ...floating.topFromTextTwips !== void 0 ? { topFromText: twips(floating.topFromTextTwips) } : {},
4930
+ ...floating.rightFromTextTwips !== void 0 ? { rightFromText: twips(floating.rightFromTextTwips) } : {},
4931
+ ...floating.bottomFromTextTwips !== void 0 ? { bottomFromText: twips(floating.bottomFromTextTwips) } : {},
4932
+ ...floating.leftFromTextTwips !== void 0 ? { leftFromText: twips(floating.leftFromTextTwips) } : {},
4857
4933
  ...floating.overlap ? { overlap: floating.overlap } : {}
4858
4934
  };
4859
4935
  }
@@ -4861,7 +4937,10 @@ function tableRow(row, ctx) {
4861
4937
  return {
4862
4938
  cells: row.cells.map((cell) => tableCell(cell, ctx)),
4863
4939
  ...row.heightTwips !== void 0 ? {
4864
- height: { value: row.heightTwips, rule: row.heightRule ?? "atLeast" }
4940
+ height: {
4941
+ value: twips(row.heightTwips),
4942
+ rule: row.heightRule ?? "atLeast"
4943
+ }
4865
4944
  } : {},
4866
4945
  ...row.isHeader !== void 0 ? { tableHeader: row.isHeader } : {},
4867
4946
  ...row.cantSplit !== void 0 ? { cantSplit: row.cantSplit } : {},
@@ -4869,9 +4948,12 @@ function tableRow(row, ctx) {
4869
4948
  };
4870
4949
  }
4871
4950
  function tableCell(cell, ctx) {
4951
+ const children = cellChildren2(cell, ctx);
4952
+ if (cell.children[cell.children.length - 1]?.kind !== "paragraph")
4953
+ children.push({ paragraph: { children: [] } });
4872
4954
  return {
4873
- children: cell.children.map((child) => block(child, ctx)),
4874
- ...cell.widthTwips !== void 0 ? { width: { size: cell.widthTwips, type: "dxa" } } : {},
4955
+ children,
4956
+ ...cell.widthTwips !== void 0 ? { width: { size: twips(cell.widthTwips), type: "dxa" } } : {},
4875
4957
  ...cell.verticalAlign ? { verticalAlign: cell.verticalAlign } : {},
4876
4958
  ...cell.shading ? { shading: shading(cell.shading) } : {},
4877
4959
  ...cell.margins ? { margins: cellMargins(cell.margins) } : {},
@@ -4881,8 +4963,38 @@ function tableCell(cell, ctx) {
4881
4963
  ...cell.textDirection ? { textDirection: cell.textDirection } : {}
4882
4964
  };
4883
4965
  }
4966
+ function cellChildren2(cell, ctx) {
4967
+ const children = [];
4968
+ for (const child of cell.children) {
4969
+ if (child.kind !== "toc") {
4970
+ children.push(block(child, ctx));
4971
+ continue;
4972
+ }
4973
+ const { alias, entries, ...options } = tableOfContents(child);
4974
+ const index = ctx.cellTocs.length;
4975
+ const toc = {
4976
+ start: cellTocMarker(index, "start"),
4977
+ end: cellTocMarker(index, "end"),
4978
+ alias: String(alias),
4979
+ options
4980
+ };
4981
+ ctx.cellTocs.push(toc);
4982
+ children.push(
4983
+ markerParagraph2(toc.start),
4984
+ ...entries ?? [],
4985
+ markerParagraph2(toc.end)
4986
+ );
4987
+ }
4988
+ return children;
4989
+ }
4990
+ function cellTocMarker(index, edge) {
4991
+ return `\uE000jto-cell-toc-${index}-${edge}\uE000`;
4992
+ }
4993
+ function markerParagraph2(text) {
4994
+ return { paragraph: { children: [{ text }] } };
4995
+ }
4884
4996
  function cellMargins(margins) {
4885
- const side = (value) => value === void 0 ? void 0 : { size: value, type: "dxa" };
4997
+ const side = (value) => value === void 0 ? void 0 : { size: twips(value), type: "dxa" };
4886
4998
  const out = {};
4887
4999
  for (const [name, value] of [
4888
5000
  ["top", margins.topTwips],
@@ -4929,18 +5041,18 @@ function section(value, ctx, closesDocument = false) {
4929
5041
  // Orientation is implied by the width/height pair, which is how this
4930
5042
  // pipeline has always expressed it; stating it as well changes `w:pgSz`.
4931
5043
  size: {
4932
- width: page.widthTwips,
4933
- height: page.heightTwips,
5044
+ width: twips(page.widthTwips),
5045
+ height: twips(page.heightTwips),
4934
5046
  ...page.code !== void 0 ? { code: page.code } : {}
4935
5047
  },
4936
5048
  margin: {
4937
- top: page.margins.topTwips,
4938
- right: page.margins.rightTwips,
4939
- bottom: page.margins.bottomTwips,
4940
- left: page.margins.leftTwips,
4941
- ...page.margins.headerTwips !== void 0 ? { header: page.margins.headerTwips } : {},
4942
- ...page.margins.footerTwips !== void 0 ? { footer: page.margins.footerTwips } : {},
4943
- ...page.margins.gutterTwips !== void 0 ? { gutter: page.margins.gutterTwips } : {}
5049
+ top: twips(page.margins.topTwips),
5050
+ right: twips(page.margins.rightTwips),
5051
+ bottom: twips(page.margins.bottomTwips),
5052
+ left: twips(page.margins.leftTwips),
5053
+ ...page.margins.headerTwips !== void 0 ? { header: twips(page.margins.headerTwips) } : {},
5054
+ ...page.margins.footerTwips !== void 0 ? { footer: twips(page.margins.footerTwips) } : {},
5055
+ ...page.margins.gutterTwips !== void 0 ? { gutter: twips(page.margins.gutterTwips) } : {}
4944
5056
  },
4945
5057
  ...value.properties.pageNumbers ? {
4946
5058
  pageNumbers: {
@@ -4959,13 +5071,13 @@ function section(value, ctx, closesDocument = false) {
4959
5071
  ...columns ? {
4960
5072
  column: {
4961
5073
  count: columns.count,
4962
- ...columns.spaceTwips !== void 0 ? { space: columns.spaceTwips } : {},
5074
+ ...columns.spaceTwips !== void 0 ? { space: twips(columns.spaceTwips) } : {},
4963
5075
  ...columns.separator !== void 0 ? { separate: columns.separator } : {},
4964
5076
  ...columns.equalWidth !== void 0 ? { equalWidth: columns.equalWidth } : {},
4965
5077
  ...columns.widths ? {
4966
5078
  children: columns.widths.map((column) => ({
4967
- width: column.widthTwips,
4968
- ...column.spaceTwips !== void 0 ? { space: column.spaceTwips } : {}
5079
+ width: twips(column.widthTwips),
5080
+ ...column.spaceTwips !== void 0 ? { space: twips(column.spaceTwips) } : {}
4969
5081
  }))
4970
5082
  } : {}
4971
5083
  }
@@ -4993,14 +5105,14 @@ function headerFooterSet(set, ctx) {
4993
5105
  function sectionChildren2(value, ctx, closesDocument = false) {
4994
5106
  const blocks = value.children.map((child) => block(child, ctx));
4995
5107
  const last = value.children[value.children.length - 1];
4996
- if (closesDocument && last?.kind === "paragraph" && last.frame !== void 0)
5108
+ const bookmark = value.bookmark;
5109
+ if (bookmark?.closes && last?.kind !== "paragraph" || closesDocument && last?.kind === "paragraph" && last.frame !== void 0)
4997
5110
  blocks.push({
4998
5111
  paragraph: {
4999
5112
  children: [],
5000
5113
  spacing: { before: 0, after: 0, line: 20, lineRule: "exact" }
5001
5114
  }
5002
5115
  });
5003
- const bookmark = value.bookmark;
5004
5116
  if (!bookmark) return blocks;
5005
5117
  return [
5006
5118
  ...bookmark.opens ? [{ bookmarkStart: { id: bookmark.id, name: bookmark.name } }] : [],
@@ -5023,8 +5135,8 @@ function numberingConfig2(numbering) {
5023
5135
  ...level.indent ? {
5024
5136
  paragraph: {
5025
5137
  indent: {
5026
- ...level.indent.leftTwips !== void 0 ? { left: level.indent.leftTwips } : {},
5027
- ...level.indent.hangingTwips !== void 0 ? { hanging: level.indent.hangingTwips } : {}
5138
+ ...level.indent.leftTwips !== void 0 ? { left: twips(level.indent.leftTwips) } : {},
5139
+ ...level.indent.hangingTwips !== void 0 ? { hanging: twips(level.indent.hangingTwips) } : {}
5028
5140
  }
5029
5141
  }
5030
5142
  } : {},
@@ -5116,10 +5228,15 @@ async function createOfficeOpenDocxRenderer() {
5116
5228
  /* @vite-ignore */
5117
5229
  OFFICE_OPEN_DOCX
5118
5230
  );
5119
- if (typeof backend.generateDocument !== "function") {
5120
- throw new Error(
5121
- `${OFFICE_OPEN_DOCX} does not export generateDocument(); the installed version is not compatible with this adapter.`
5122
- );
5231
+ for (const name of [
5232
+ "generateDocument",
5233
+ "stringifyTableOfContents"
5234
+ ]) {
5235
+ if (typeof backend[name] !== "function") {
5236
+ throw new Error(
5237
+ `${OFFICE_OPEN_DOCX} does not export ${name}(); the installed version is not compatible with this adapter.`
5238
+ );
5239
+ }
5123
5240
  }
5124
5241
  return {
5125
5242
  id: OFFICE_OPEN_DOCX_RENDERER_ID,
@@ -5127,10 +5244,12 @@ async function createOfficeOpenDocxRenderer() {
5127
5244
  capabilities: OFFICE_OPEN_CAPABILITIES,
5128
5245
  async render(ir, options) {
5129
5246
  const charts = [];
5247
+ const cellTocs = [];
5130
5248
  const document = await buildDocumentOptions(
5131
5249
  ir,
5132
5250
  charts,
5133
- options?.svgRasterFallback
5251
+ options?.svgRasterFallback,
5252
+ cellTocs
5134
5253
  );
5135
5254
  const bytes = await backend.generateDocument(document, {
5136
5255
  type: "uint8array"
@@ -5138,9 +5257,10 @@ async function createOfficeOpenDocxRenderer() {
5138
5257
  let raw = Buffer.from(
5139
5258
  bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes)
5140
5259
  );
5141
- if (charts.length > 0) {
5260
+ if (charts.length > 0 || cellTocs.length > 0) {
5142
5261
  const zip = new AdmZip4(raw);
5143
5262
  spliceChartParts(zip, charts);
5263
+ spliceCellTocs(zip, cellTocs, backend.stringifyTableOfContents);
5144
5264
  raw = zip.toBuffer();
5145
5265
  }
5146
5266
  if (options?.deterministic === false)
@@ -5157,12 +5277,13 @@ async function createOfficeOpenDocxRenderer() {
5157
5277
  }
5158
5278
  };
5159
5279
  }
5160
- async function buildDocumentOptions(ir, charts = [], svgRasterFallback) {
5280
+ async function buildDocumentOptions(ir, charts = [], svgRasterFallback, cellTocs = []) {
5161
5281
  let nextDrawingId = 1;
5162
5282
  const ctx = {
5163
5283
  pictures: await prepareImages2(ir, svgRasterFallback),
5164
5284
  nextDrawingId: () => nextDrawingId++,
5165
- charts
5285
+ charts,
5286
+ cellTocs
5166
5287
  };
5167
5288
  return {
5168
5289
  styles: emitStyles2(ir.styles),
@@ -5441,6 +5562,7 @@ var init_office_open = __esm({
5441
5562
  "src/renderers/office-open/index.ts"() {
5442
5563
  "use strict";
5443
5564
  init_chartParts();
5565
+ init_cellTocs();
5444
5566
  init_imageUtils();
5445
5567
  init_packageDocument();
5446
5568
  init_emit2();
@@ -6688,6 +6810,7 @@ import {
6688
6810
  assetUnreadableError
6689
6811
  } from "@json-to-office/shared/rendering";
6690
6812
  import { capsFormatting as capsFormatting2, synthesizeFamilyName as synthesizeFamilyName2 } from "@json-to-office/shared";
6813
+ import { unrenderedComponentText } from "@json-to-office/quality";
6691
6814
  import {
6692
6815
  isNativeVisualProps as isNativeVisualProps4
6693
6816
  } from "@json-to-office/shared-docx";
@@ -7813,7 +7936,8 @@ function createDocumentStyles(themeNameOrObject = "consulting", language) {
7813
7936
  tabStops: effectiveTabStops.map(
7814
7937
  (ts) => ({
7815
7938
  type: ts.type,
7816
- positionTwips: ts.position === "max" ? RIGHT_MARGIN_TAB_TWIPS : ts.position,
7939
+ // Whole twips: `w:pos` is an integer no backend rounds.
7940
+ positionTwips: ts.position === "max" ? RIGHT_MARGIN_TAB_TWIPS : Math.round(ts.position),
7817
7941
  ...ts.leader && { leader: ts.leader }
7818
7942
  })
7819
7943
  )
@@ -10433,7 +10557,9 @@ function compileToc(component, scope) {
10433
10557
  formatting: {
10434
10558
  alignment: "left",
10435
10559
  spacing: {
10436
- beforeTwips: ctx.theme.componentDefaults?.heading?.spacing?.before ?? 240,
10560
+ beforeTwips: wholeTwips(
10561
+ ctx.theme.componentDefaults?.heading?.spacing?.before ?? 240
10562
+ ),
10437
10563
  // ~9pt, which separates the title from the list clearly enough to
10438
10564
  // read as a heading rather than a first entry.
10439
10565
  afterTwips: 180
@@ -10553,8 +10679,8 @@ function compileStatistic(component, scope) {
10553
10679
  const alignment2 = compileAlignment(props.alignment) ?? "center";
10554
10680
  ctx.usesStatistic = true;
10555
10681
  const spacing = props.spacing ? {
10556
- ...props.spacing.before !== void 0 ? { beforeTwips: props.spacing.before } : {},
10557
- ...props.spacing.after !== void 0 ? { afterTwips: props.spacing.after } : {}
10682
+ ...props.spacing.before !== void 0 ? { beforeTwips: wholeTwips(props.spacing.before) } : {},
10683
+ ...props.spacing.after !== void 0 ? { afterTwips: wholeTwips(props.spacing.after) } : {}
10558
10684
  } : void 0;
10559
10685
  if (props.format !== void 0) {
10560
10686
  warnOnce(
@@ -11179,7 +11305,7 @@ function paragraphFormatting(props, ctx, extra = {}) {
11179
11305
  if (Array.isArray(props.tabStops) && props.tabStops.length > 0) {
11180
11306
  formatting.tabStops = props.tabStops.map(
11181
11307
  (stop) => ({
11182
- positionTwips: stop.position,
11308
+ positionTwips: wholeTwips(stop.position),
11183
11309
  type: stop.type ?? "left",
11184
11310
  ...stop.leader ? { leader: stop.leader } : {}
11185
11311
  })
@@ -11228,7 +11354,10 @@ function compileSpacing(props, ctx) {
11228
11354
  }
11229
11355
  function compileLineSpacing(value) {
11230
11356
  if (typeof value === "number") {
11231
- return { lineTwips: value * SINGLE_LINE_TWIPS, lineRule: "auto" };
11357
+ return {
11358
+ lineTwips: wholeTwips(value * SINGLE_LINE_TWIPS),
11359
+ lineRule: "auto"
11360
+ };
11232
11361
  }
11233
11362
  if (typeof value !== "object" || value === null) return void 0;
11234
11363
  const { type, value: amount } = value;
@@ -11249,7 +11378,7 @@ function compileLineSpacing(value) {
11249
11378
  };
11250
11379
  case "multiple":
11251
11380
  return {
11252
- ...amount !== void 0 ? { lineTwips: amount * SINGLE_LINE_TWIPS } : {},
11381
+ ...amount !== void 0 ? { lineTwips: wholeTwips(amount * SINGLE_LINE_TWIPS) } : {},
11253
11382
  lineRule: "auto"
11254
11383
  };
11255
11384
  default:
@@ -11710,8 +11839,8 @@ function compileFrame(floating, ctx, path3) {
11710
11839
  const useAbsolute = hasHorizontalOffset || hasVerticalOffset;
11711
11840
  const frame = {
11712
11841
  // 2in by 1in when the author gave no size.
11713
- widthTwips: floating.width || 2880,
11714
- heightTwips: floating.height || 1440,
11842
+ widthTwips: wholeTwips(floating.width || 2880),
11843
+ heightTwips: wholeTwips(floating.height || 1440),
11715
11844
  anchorHorizontal: floating.horizontalPosition?.relative || "page",
11716
11845
  anchorVertical: floating.verticalPosition?.relative || "page",
11717
11846
  ...floating.wrap?.type ? { wrap: floating.wrap.type } : {}
@@ -11724,17 +11853,21 @@ function compileFrame(floating, ctx, path3) {
11724
11853
  if (typeof rawX === "string" || typeof rawY === "string") {
11725
11854
  const hRelative = floating.horizontalPosition?.relative;
11726
11855
  const vRelative = floating.verticalPosition?.relative;
11727
- frame.xTwips = resolveOffsetTwips(
11728
- rawX,
11729
- hRelative && hRelative !== "page" ? getAvailableWidthTwips(ctx.theme, ctx.themeName) : getPageWidthTwips(ctx.theme, ctx.themeName)
11856
+ frame.xTwips = wholeTwips(
11857
+ resolveOffsetTwips(
11858
+ rawX,
11859
+ hRelative && hRelative !== "page" ? getAvailableWidthTwips(ctx.theme, ctx.themeName) : getPageWidthTwips(ctx.theme, ctx.themeName)
11860
+ )
11730
11861
  );
11731
- frame.yTwips = resolveOffsetTwips(
11732
- rawY,
11733
- vRelative && vRelative !== "page" ? getAvailableHeightTwips(ctx.theme, ctx.themeName) : getPageHeightTwips(ctx.theme, ctx.themeName)
11862
+ frame.yTwips = wholeTwips(
11863
+ resolveOffsetTwips(
11864
+ rawY,
11865
+ vRelative && vRelative !== "page" ? getAvailableHeightTwips(ctx.theme, ctx.themeName) : getPageHeightTwips(ctx.theme, ctx.themeName)
11866
+ )
11734
11867
  );
11735
11868
  } else {
11736
- frame.xTwips = rawX;
11737
- frame.yTwips = rawY;
11869
+ frame.xTwips = wholeTwips(rawX);
11870
+ frame.yTwips = wholeTwips(rawY);
11738
11871
  }
11739
11872
  return frame;
11740
11873
  }
@@ -12068,10 +12201,17 @@ function cellChildren(cell, baseStyle, scope, rowRevision) {
12068
12201
  return wrap(cellVisual(content, scope));
12069
12202
  }
12070
12203
  if (content.name !== "paragraph") {
12204
+ const text2 = unrenderedComponentText(content.name);
12205
+ warnOnce(
12206
+ ctx,
12207
+ "table",
12208
+ `${scope.path} holds a "${content.name}", which a table cell cannot render, so it shows "${text2}" instead. A cell takes a string, a paragraph, an image, a visual or a highcharts chart; set statistics side by side with a columns component.`,
12209
+ "W_UNSUPPORTED_CELL_CONTENT"
12210
+ );
12071
12211
  return wrap([
12072
12212
  {
12073
12213
  kind: "text",
12074
- text: `[Unsupported component type: ${content.name}]`,
12214
+ text: text2,
12075
12215
  formatting: placeholderFormatting(base)
12076
12216
  }
12077
12217
  ]);
@@ -13269,6 +13409,11 @@ init_widthUtils();
13269
13409
  import probe2 from "probe-image-size";
13270
13410
 
13271
13411
  // src/quality/text-inventory.ts
13412
+ import {
13413
+ unrenderedComponentText as unrenderedComponentText2
13414
+ } from "@json-to-office/quality";
13415
+ import { TABLE_CELL_COMPONENTS } from "@json-to-office/shared-docx";
13416
+ var CELL_COMPONENTS = new Set(TABLE_CELL_COMPONENTS);
13272
13417
  function tocDepth(depth) {
13273
13418
  const range = asRecord(depth) ?? {};
13274
13419
  const from = typeof range.from === "number" ? range.from : 1;
@@ -13317,7 +13462,14 @@ function collectDocxTextInventory(children, basePath = "/children") {
13317
13462
  add(`${path3}/content`, rec.content, cellRole, extra);
13318
13463
  else {
13319
13464
  const inner = asRecord(rec.content);
13320
- if (inner)
13465
+ if (inner && typeof inner.name === "string" && !CELL_COMPONENTS.has(inner.name)) {
13466
+ add(
13467
+ `${path3}/content`,
13468
+ unrenderedComponentText2(inner.name),
13469
+ cellRole,
13470
+ { ...extra, optional: true, unrendered: inner.name }
13471
+ );
13472
+ } else if (inner)
13321
13473
  visitNode(inner, `${path3}/content`, { ...inherited, inCell: true });
13322
13474
  }
13323
13475
  return;