@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.
package/dist/index.js CHANGED
@@ -1810,15 +1810,15 @@ function convertLineSpacing(lineSpacing) {
1810
1810
  lineRule2 = "auto";
1811
1811
  break;
1812
1812
  case "atLeast":
1813
- line = value !== void 0 ? value * TWIPS_PER_POINT : void 0;
1813
+ line = value !== void 0 ? pointsToTwips(value) : void 0;
1814
1814
  lineRule2 = "atLeast";
1815
1815
  break;
1816
1816
  case "exactly":
1817
- line = value !== void 0 ? value * TWIPS_PER_POINT : void 0;
1817
+ line = value !== void 0 ? pointsToTwips(value) : void 0;
1818
1818
  lineRule2 = "exact";
1819
1819
  break;
1820
1820
  case "multiple":
1821
- line = value !== void 0 ? value * SINGLE_LINE_SPACING_TWIPS : void 0;
1821
+ line = value !== void 0 ? Math.round(value * SINGLE_LINE_SPACING_TWIPS) : void 0;
1822
1822
  lineRule2 = "auto";
1823
1823
  break;
1824
1824
  default:
@@ -2825,6 +2825,9 @@ import { createHash as createHash2 } from "crypto";
2825
2825
  function pointsToTwips2(points) {
2826
2826
  return Math.round(points * TWIPS_PER_POINT2);
2827
2827
  }
2828
+ function wholeTwips(twips2) {
2829
+ return Math.round(twips2);
2830
+ }
2828
2831
  function pointsToHalfPoints(points) {
2829
2832
  return Math.round(points * 2);
2830
2833
  }
@@ -2840,8 +2843,8 @@ function inchesToEmu(inches) {
2840
2843
  function pointsToEmu(points) {
2841
2844
  return Math.round(points * EMU_PER_INCH / 72);
2842
2845
  }
2843
- function twipsToEmu(twips) {
2844
- return Math.round(twips * EMU_PER_TWIP);
2846
+ function twipsToEmu(twips2) {
2847
+ return Math.round(twips2 * EMU_PER_TWIP);
2845
2848
  }
2846
2849
  function pixelsToEmu(pixels) {
2847
2850
  return Math.round(pixels * 9525);
@@ -2849,8 +2852,8 @@ function pixelsToEmu(pixels) {
2849
2852
  function emuToPixels(emu) {
2850
2853
  return emu / 9525;
2851
2854
  }
2852
- function twipsToPixels(twips) {
2853
- return twips / TWIPS_PER_INCH * 96;
2855
+ function twipsToPixels(twips2) {
2856
+ return twips2 / TWIPS_PER_INCH * 96;
2854
2857
  }
2855
2858
  function irColor(hex) {
2856
2859
  return { hex: hex.startsWith("#") ? hex.slice(1) : hex };
@@ -4275,11 +4278,71 @@ var init_chartParts = __esm({
4275
4278
  }
4276
4279
  });
4277
4280
 
4281
+ // src/renderers/office-open/cellTocs.ts
4282
+ function spliceCellTocs(zip, tocs, stringifyTableOfContents) {
4283
+ if (tocs.length === 0) return;
4284
+ const spliced = /* @__PURE__ */ new Set();
4285
+ for (const entry of zip.getEntries()) {
4286
+ if (!TABLE_PARTS.test(entry.entryName)) continue;
4287
+ const original = entry.getData().toString("utf8");
4288
+ let xml = original;
4289
+ for (const toc of tocs) {
4290
+ const start = markerParagraph(xml, toc.start);
4291
+ if (!start) continue;
4292
+ const end = markerParagraph(xml, toc.end);
4293
+ if (spliced.has(toc) || !end || end.from < start.to) {
4294
+ throw new Error(
4295
+ `the office-open renderer lost track of a table of contents in a table cell (${entry.entryName})`
4296
+ );
4297
+ }
4298
+ xml = xml.slice(0, start.from) + stringifyTableOfContents(
4299
+ toc.alias,
4300
+ toc.options,
4301
+ xml.slice(start.to, end.from)
4302
+ ) + xml.slice(end.to);
4303
+ spliced.add(toc);
4304
+ }
4305
+ if (xml !== original) zip.updateFile(entry, Buffer.from(xml, "utf8"));
4306
+ }
4307
+ if (spliced.size !== tocs.length) {
4308
+ throw new Error(
4309
+ `the office-open renderer placed ${tocs.length} table(s) of contents in table cells and found ${spliced.size}`
4310
+ );
4311
+ }
4312
+ }
4313
+ function markerParagraph(xml, marker) {
4314
+ const at = xml.indexOf(marker);
4315
+ if (at < 0) return void 0;
4316
+ const from = Math.max(
4317
+ xml.lastIndexOf("<w:p>", at),
4318
+ xml.lastIndexOf("<w:p ", at)
4319
+ );
4320
+ const close = xml.indexOf("</w:p>", at);
4321
+ if (xml.indexOf(marker, at + marker.length) >= 0 || from < 0 || close < 0 || xml.lastIndexOf("</w:p>", at) > from) {
4322
+ throw new Error(
4323
+ "the office-open renderer found a table-of-contents marker it did not write"
4324
+ );
4325
+ }
4326
+ return { from, to: close + "</w:p>".length };
4327
+ }
4328
+ var TABLE_PARTS;
4329
+ var init_cellTocs = __esm({
4330
+ "src/renderers/office-open/cellTocs.ts"() {
4331
+ "use strict";
4332
+ TABLE_PARTS = /^word\/(?:document|header\d+|footer\d+)\.xml$/;
4333
+ }
4334
+ });
4335
+
4278
4336
  // src/renderers/office-open/emit.ts
4279
4337
  import { assertNever as assertNever2 } from "@json-to-office/shared/rendering";
4280
4338
  function emptyContext() {
4281
4339
  let next = 1;
4282
- return { pictures: /* @__PURE__ */ new Map(), nextDrawingId: () => next++, charts: [] };
4340
+ return {
4341
+ pictures: /* @__PURE__ */ new Map(),
4342
+ nextDrawingId: () => next++,
4343
+ charts: [],
4344
+ cellTocs: []
4345
+ };
4283
4346
  }
4284
4347
  function simpleField(instruction, cachedText) {
4285
4348
  return {
@@ -4289,6 +4352,9 @@ function simpleField(instruction, cachedText) {
4289
4352
  }
4290
4353
  };
4291
4354
  }
4355
+ function twips(value) {
4356
+ return Math.floor(value);
4357
+ }
4292
4358
  function runProperties2(formatting) {
4293
4359
  if (!formatting) return {};
4294
4360
  const out = {};
@@ -4325,7 +4391,9 @@ function runProperties2(formatting) {
4325
4391
  if (formatting.scalePercent !== void 0)
4326
4392
  out.scale = formatting.scalePercent;
4327
4393
  if (formatting.characterSpacingTwentieths !== void 0) {
4328
- out.characterSpacing = formatting.characterSpacingTwentieths;
4394
+ const stated = formatting.characterSpacingTwentieths;
4395
+ const twentieths = twips(stated);
4396
+ out.characterSpacing = twentieths === 0 && stated !== 0 ? "0pt" : twentieths;
4329
4397
  }
4330
4398
  if (formatting.language) out.language = { value: formatting.language };
4331
4399
  if (formatting.noProof !== void 0) out.noProof = formatting.noProof;
@@ -4716,16 +4784,16 @@ function paragraphProperties2(formatting) {
4716
4784
  if (formatting.indent) {
4717
4785
  const indent = {};
4718
4786
  if (formatting.indent.leftTwips !== void 0) {
4719
- indent.left = formatting.indent.leftTwips;
4787
+ indent.left = twips(formatting.indent.leftTwips);
4720
4788
  }
4721
4789
  if (formatting.indent.rightTwips !== void 0) {
4722
- indent.right = formatting.indent.rightTwips;
4790
+ indent.right = twips(formatting.indent.rightTwips);
4723
4791
  }
4724
4792
  if (formatting.indent.firstLineTwips !== void 0) {
4725
- indent.firstLine = formatting.indent.firstLineTwips;
4793
+ indent.firstLine = twips(formatting.indent.firstLineTwips);
4726
4794
  }
4727
4795
  if (formatting.indent.hangingTwips !== void 0) {
4728
- indent.hanging = formatting.indent.hangingTwips;
4796
+ indent.hanging = twips(formatting.indent.hangingTwips);
4729
4797
  }
4730
4798
  out.indent = indent;
4731
4799
  }
@@ -4856,14 +4924,14 @@ function table(value, ctx) {
4856
4924
  // `w:w` is an integer in OOXML. A grid that splits a remainder between
4857
4925
  // unstated columns comes out fractional, and the XML would carry it
4858
4926
  // verbatim; floored here, as the docxjs backend floors it.
4859
- size: value.width.kind === "auto" ? 0 : value.width.kind === "twips" ? Math.floor(value.width.value) : value.width.value,
4927
+ size: value.width.kind === "auto" ? 0 : value.width.kind === "twips" ? twips(value.width.value) : value.width.value,
4860
4928
  type: value.width.kind === "twips" ? "dxa" : value.width.kind === "percent" ? "pct" : "auto"
4861
4929
  },
4862
4930
  layout: value.layout,
4863
4931
  // An empty grid is a table with nothing to say about its columns, which is
4864
4932
  // not the same as one whose columns are all zero wide. The compiler hands
4865
4933
  // over every grid in twips; floored for the same reason as the width.
4866
- ...value.columnGrid.values.length > 0 ? { columnWidths: value.columnGrid.values.map(Math.floor) } : {},
4934
+ ...value.columnGrid.values.length > 0 ? { columnWidths: value.columnGrid.values.map(twips) } : {},
4867
4935
  ...value.alignment ? { alignment: alignment(value.alignment) } : {},
4868
4936
  ...value.borders ? { borders: borders2(value.borders) } : {},
4869
4937
  ...value.cellMargins ? { margins: cellMargins(value.cellMargins) } : {},
@@ -4874,14 +4942,22 @@ function tableFloat2(floating) {
4874
4942
  return {
4875
4943
  ...floating.horizontalAnchor ? { horizontalAnchor: floating.horizontalAnchor } : {},
4876
4944
  ...floating.verticalAnchor ? { verticalAnchor: floating.verticalAnchor } : {},
4877
- ...floating.absoluteHorizontalPositionTwips !== void 0 ? { absoluteHorizontalPosition: floating.absoluteHorizontalPositionTwips } : {},
4945
+ ...floating.absoluteHorizontalPositionTwips !== void 0 ? {
4946
+ absoluteHorizontalPosition: twips(
4947
+ floating.absoluteHorizontalPositionTwips
4948
+ )
4949
+ } : {},
4878
4950
  ...floating.relativeHorizontalPosition ? { relativeHorizontalPosition: floating.relativeHorizontalPosition } : {},
4879
- ...floating.absoluteVerticalPositionTwips !== void 0 ? { absoluteVerticalPosition: floating.absoluteVerticalPositionTwips } : {},
4951
+ ...floating.absoluteVerticalPositionTwips !== void 0 ? {
4952
+ absoluteVerticalPosition: twips(
4953
+ floating.absoluteVerticalPositionTwips
4954
+ )
4955
+ } : {},
4880
4956
  ...floating.relativeVerticalPosition ? { relativeVerticalPosition: floating.relativeVerticalPosition } : {},
4881
- ...floating.topFromTextTwips !== void 0 ? { topFromText: floating.topFromTextTwips } : {},
4882
- ...floating.rightFromTextTwips !== void 0 ? { rightFromText: floating.rightFromTextTwips } : {},
4883
- ...floating.bottomFromTextTwips !== void 0 ? { bottomFromText: floating.bottomFromTextTwips } : {},
4884
- ...floating.leftFromTextTwips !== void 0 ? { leftFromText: floating.leftFromTextTwips } : {},
4957
+ ...floating.topFromTextTwips !== void 0 ? { topFromText: twips(floating.topFromTextTwips) } : {},
4958
+ ...floating.rightFromTextTwips !== void 0 ? { rightFromText: twips(floating.rightFromTextTwips) } : {},
4959
+ ...floating.bottomFromTextTwips !== void 0 ? { bottomFromText: twips(floating.bottomFromTextTwips) } : {},
4960
+ ...floating.leftFromTextTwips !== void 0 ? { leftFromText: twips(floating.leftFromTextTwips) } : {},
4885
4961
  ...floating.overlap ? { overlap: floating.overlap } : {}
4886
4962
  };
4887
4963
  }
@@ -4889,7 +4965,10 @@ function tableRow(row, ctx) {
4889
4965
  return {
4890
4966
  cells: row.cells.map((cell) => tableCell(cell, ctx)),
4891
4967
  ...row.heightTwips !== void 0 ? {
4892
- height: { value: row.heightTwips, rule: row.heightRule ?? "atLeast" }
4968
+ height: {
4969
+ value: twips(row.heightTwips),
4970
+ rule: row.heightRule ?? "atLeast"
4971
+ }
4893
4972
  } : {},
4894
4973
  ...row.isHeader !== void 0 ? { tableHeader: row.isHeader } : {},
4895
4974
  ...row.cantSplit !== void 0 ? { cantSplit: row.cantSplit } : {},
@@ -4897,9 +4976,12 @@ function tableRow(row, ctx) {
4897
4976
  };
4898
4977
  }
4899
4978
  function tableCell(cell, ctx) {
4979
+ const children = cellChildren2(cell, ctx);
4980
+ if (cell.children[cell.children.length - 1]?.kind !== "paragraph")
4981
+ children.push({ paragraph: { children: [] } });
4900
4982
  return {
4901
- children: cell.children.map((child) => block(child, ctx)),
4902
- ...cell.widthTwips !== void 0 ? { width: { size: cell.widthTwips, type: "dxa" } } : {},
4983
+ children,
4984
+ ...cell.widthTwips !== void 0 ? { width: { size: twips(cell.widthTwips), type: "dxa" } } : {},
4903
4985
  ...cell.verticalAlign ? { verticalAlign: cell.verticalAlign } : {},
4904
4986
  ...cell.shading ? { shading: shading(cell.shading) } : {},
4905
4987
  ...cell.margins ? { margins: cellMargins(cell.margins) } : {},
@@ -4909,8 +4991,38 @@ function tableCell(cell, ctx) {
4909
4991
  ...cell.textDirection ? { textDirection: cell.textDirection } : {}
4910
4992
  };
4911
4993
  }
4994
+ function cellChildren2(cell, ctx) {
4995
+ const children = [];
4996
+ for (const child of cell.children) {
4997
+ if (child.kind !== "toc") {
4998
+ children.push(block(child, ctx));
4999
+ continue;
5000
+ }
5001
+ const { alias, entries, ...options } = tableOfContents(child);
5002
+ const index = ctx.cellTocs.length;
5003
+ const toc = {
5004
+ start: cellTocMarker(index, "start"),
5005
+ end: cellTocMarker(index, "end"),
5006
+ alias: String(alias),
5007
+ options
5008
+ };
5009
+ ctx.cellTocs.push(toc);
5010
+ children.push(
5011
+ markerParagraph2(toc.start),
5012
+ ...entries ?? [],
5013
+ markerParagraph2(toc.end)
5014
+ );
5015
+ }
5016
+ return children;
5017
+ }
5018
+ function cellTocMarker(index, edge) {
5019
+ return `\uE000jto-cell-toc-${index}-${edge}\uE000`;
5020
+ }
5021
+ function markerParagraph2(text) {
5022
+ return { paragraph: { children: [{ text }] } };
5023
+ }
4912
5024
  function cellMargins(margins) {
4913
- const side = (value) => value === void 0 ? void 0 : { size: value, type: "dxa" };
5025
+ const side = (value) => value === void 0 ? void 0 : { size: twips(value), type: "dxa" };
4914
5026
  const out = {};
4915
5027
  for (const [name, value] of [
4916
5028
  ["top", margins.topTwips],
@@ -4957,18 +5069,18 @@ function section(value, ctx, closesDocument = false) {
4957
5069
  // Orientation is implied by the width/height pair, which is how this
4958
5070
  // pipeline has always expressed it; stating it as well changes `w:pgSz`.
4959
5071
  size: {
4960
- width: page.widthTwips,
4961
- height: page.heightTwips,
5072
+ width: twips(page.widthTwips),
5073
+ height: twips(page.heightTwips),
4962
5074
  ...page.code !== void 0 ? { code: page.code } : {}
4963
5075
  },
4964
5076
  margin: {
4965
- top: page.margins.topTwips,
4966
- right: page.margins.rightTwips,
4967
- bottom: page.margins.bottomTwips,
4968
- left: page.margins.leftTwips,
4969
- ...page.margins.headerTwips !== void 0 ? { header: page.margins.headerTwips } : {},
4970
- ...page.margins.footerTwips !== void 0 ? { footer: page.margins.footerTwips } : {},
4971
- ...page.margins.gutterTwips !== void 0 ? { gutter: page.margins.gutterTwips } : {}
5077
+ top: twips(page.margins.topTwips),
5078
+ right: twips(page.margins.rightTwips),
5079
+ bottom: twips(page.margins.bottomTwips),
5080
+ left: twips(page.margins.leftTwips),
5081
+ ...page.margins.headerTwips !== void 0 ? { header: twips(page.margins.headerTwips) } : {},
5082
+ ...page.margins.footerTwips !== void 0 ? { footer: twips(page.margins.footerTwips) } : {},
5083
+ ...page.margins.gutterTwips !== void 0 ? { gutter: twips(page.margins.gutterTwips) } : {}
4972
5084
  },
4973
5085
  ...value.properties.pageNumbers ? {
4974
5086
  pageNumbers: {
@@ -4987,13 +5099,13 @@ function section(value, ctx, closesDocument = false) {
4987
5099
  ...columns ? {
4988
5100
  column: {
4989
5101
  count: columns.count,
4990
- ...columns.spaceTwips !== void 0 ? { space: columns.spaceTwips } : {},
5102
+ ...columns.spaceTwips !== void 0 ? { space: twips(columns.spaceTwips) } : {},
4991
5103
  ...columns.separator !== void 0 ? { separate: columns.separator } : {},
4992
5104
  ...columns.equalWidth !== void 0 ? { equalWidth: columns.equalWidth } : {},
4993
5105
  ...columns.widths ? {
4994
5106
  children: columns.widths.map((column) => ({
4995
- width: column.widthTwips,
4996
- ...column.spaceTwips !== void 0 ? { space: column.spaceTwips } : {}
5107
+ width: twips(column.widthTwips),
5108
+ ...column.spaceTwips !== void 0 ? { space: twips(column.spaceTwips) } : {}
4997
5109
  }))
4998
5110
  } : {}
4999
5111
  }
@@ -5021,14 +5133,14 @@ function headerFooterSet(set, ctx) {
5021
5133
  function sectionChildren2(value, ctx, closesDocument = false) {
5022
5134
  const blocks = value.children.map((child) => block(child, ctx));
5023
5135
  const last = value.children[value.children.length - 1];
5024
- if (closesDocument && last?.kind === "paragraph" && last.frame !== void 0)
5136
+ const bookmark = value.bookmark;
5137
+ if (bookmark?.closes && last?.kind !== "paragraph" || closesDocument && last?.kind === "paragraph" && last.frame !== void 0)
5025
5138
  blocks.push({
5026
5139
  paragraph: {
5027
5140
  children: [],
5028
5141
  spacing: { before: 0, after: 0, line: 20, lineRule: "exact" }
5029
5142
  }
5030
5143
  });
5031
- const bookmark = value.bookmark;
5032
5144
  if (!bookmark) return blocks;
5033
5145
  return [
5034
5146
  ...bookmark.opens ? [{ bookmarkStart: { id: bookmark.id, name: bookmark.name } }] : [],
@@ -5051,8 +5163,8 @@ function numberingConfig2(numbering) {
5051
5163
  ...level.indent ? {
5052
5164
  paragraph: {
5053
5165
  indent: {
5054
- ...level.indent.leftTwips !== void 0 ? { left: level.indent.leftTwips } : {},
5055
- ...level.indent.hangingTwips !== void 0 ? { hanging: level.indent.hangingTwips } : {}
5166
+ ...level.indent.leftTwips !== void 0 ? { left: twips(level.indent.leftTwips) } : {},
5167
+ ...level.indent.hangingTwips !== void 0 ? { hanging: twips(level.indent.hangingTwips) } : {}
5056
5168
  }
5057
5169
  }
5058
5170
  } : {},
@@ -5144,10 +5256,15 @@ async function createOfficeOpenDocxRenderer() {
5144
5256
  /* @vite-ignore */
5145
5257
  OFFICE_OPEN_DOCX
5146
5258
  );
5147
- if (typeof backend.generateDocument !== "function") {
5148
- throw new Error(
5149
- `${OFFICE_OPEN_DOCX} does not export generateDocument(); the installed version is not compatible with this adapter.`
5150
- );
5259
+ for (const name of [
5260
+ "generateDocument",
5261
+ "stringifyTableOfContents"
5262
+ ]) {
5263
+ if (typeof backend[name] !== "function") {
5264
+ throw new Error(
5265
+ `${OFFICE_OPEN_DOCX} does not export ${name}(); the installed version is not compatible with this adapter.`
5266
+ );
5267
+ }
5151
5268
  }
5152
5269
  return {
5153
5270
  id: OFFICE_OPEN_DOCX_RENDERER_ID,
@@ -5155,10 +5272,12 @@ async function createOfficeOpenDocxRenderer() {
5155
5272
  capabilities: OFFICE_OPEN_CAPABILITIES,
5156
5273
  async render(ir, options) {
5157
5274
  const charts = [];
5275
+ const cellTocs = [];
5158
5276
  const document = await buildDocumentOptions(
5159
5277
  ir,
5160
5278
  charts,
5161
- options?.svgRasterFallback
5279
+ options?.svgRasterFallback,
5280
+ cellTocs
5162
5281
  );
5163
5282
  const bytes = await backend.generateDocument(document, {
5164
5283
  type: "uint8array"
@@ -5166,9 +5285,10 @@ async function createOfficeOpenDocxRenderer() {
5166
5285
  let raw = Buffer.from(
5167
5286
  bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes)
5168
5287
  );
5169
- if (charts.length > 0) {
5288
+ if (charts.length > 0 || cellTocs.length > 0) {
5170
5289
  const zip = new AdmZip4(raw);
5171
5290
  spliceChartParts(zip, charts);
5291
+ spliceCellTocs(zip, cellTocs, backend.stringifyTableOfContents);
5172
5292
  raw = zip.toBuffer();
5173
5293
  }
5174
5294
  if (options?.deterministic === false)
@@ -5185,12 +5305,13 @@ async function createOfficeOpenDocxRenderer() {
5185
5305
  }
5186
5306
  };
5187
5307
  }
5188
- async function buildDocumentOptions(ir, charts = [], svgRasterFallback) {
5308
+ async function buildDocumentOptions(ir, charts = [], svgRasterFallback, cellTocs = []) {
5189
5309
  let nextDrawingId = 1;
5190
5310
  const ctx = {
5191
5311
  pictures: await prepareImages2(ir, svgRasterFallback),
5192
5312
  nextDrawingId: () => nextDrawingId++,
5193
- charts
5313
+ charts,
5314
+ cellTocs
5194
5315
  };
5195
5316
  return {
5196
5317
  styles: emitStyles2(ir.styles),
@@ -5469,6 +5590,7 @@ var init_office_open = __esm({
5469
5590
  "src/renderers/office-open/index.ts"() {
5470
5591
  "use strict";
5471
5592
  init_chartParts();
5593
+ init_cellTocs();
5472
5594
  init_imageUtils();
5473
5595
  init_packageDocument();
5474
5596
  init_emit2();
@@ -6431,6 +6553,7 @@ import {
6431
6553
  assetUnreadableError
6432
6554
  } from "@json-to-office/shared/rendering";
6433
6555
  import { capsFormatting as capsFormatting2, synthesizeFamilyName as synthesizeFamilyName2 } from "@json-to-office/shared";
6556
+ import { unrenderedComponentText } from "@json-to-office/quality";
6434
6557
  import {
6435
6558
  isNativeVisualProps as isNativeVisualProps4
6436
6559
  } from "@json-to-office/shared-docx";
@@ -7556,7 +7679,8 @@ function createDocumentStyles(themeNameOrObject = "consulting", language) {
7556
7679
  tabStops: effectiveTabStops.map(
7557
7680
  (ts) => ({
7558
7681
  type: ts.type,
7559
- positionTwips: ts.position === "max" ? RIGHT_MARGIN_TAB_TWIPS : ts.position,
7682
+ // Whole twips: `w:pos` is an integer no backend rounds.
7683
+ positionTwips: ts.position === "max" ? RIGHT_MARGIN_TAB_TWIPS : Math.round(ts.position),
7560
7684
  ...ts.leader && { leader: ts.leader }
7561
7685
  })
7562
7686
  )
@@ -10176,7 +10300,9 @@ function compileToc(component, scope) {
10176
10300
  formatting: {
10177
10301
  alignment: "left",
10178
10302
  spacing: {
10179
- beforeTwips: ctx.theme.componentDefaults?.heading?.spacing?.before ?? 240,
10303
+ beforeTwips: wholeTwips(
10304
+ ctx.theme.componentDefaults?.heading?.spacing?.before ?? 240
10305
+ ),
10180
10306
  // ~9pt, which separates the title from the list clearly enough to
10181
10307
  // read as a heading rather than a first entry.
10182
10308
  afterTwips: 180
@@ -10296,8 +10422,8 @@ function compileStatistic(component, scope) {
10296
10422
  const alignment2 = compileAlignment(props.alignment) ?? "center";
10297
10423
  ctx.usesStatistic = true;
10298
10424
  const spacing = props.spacing ? {
10299
- ...props.spacing.before !== void 0 ? { beforeTwips: props.spacing.before } : {},
10300
- ...props.spacing.after !== void 0 ? { afterTwips: props.spacing.after } : {}
10425
+ ...props.spacing.before !== void 0 ? { beforeTwips: wholeTwips(props.spacing.before) } : {},
10426
+ ...props.spacing.after !== void 0 ? { afterTwips: wholeTwips(props.spacing.after) } : {}
10301
10427
  } : void 0;
10302
10428
  if (props.format !== void 0) {
10303
10429
  warnOnce(
@@ -10922,7 +11048,7 @@ function paragraphFormatting(props, ctx, extra = {}) {
10922
11048
  if (Array.isArray(props.tabStops) && props.tabStops.length > 0) {
10923
11049
  formatting.tabStops = props.tabStops.map(
10924
11050
  (stop) => ({
10925
- positionTwips: stop.position,
11051
+ positionTwips: wholeTwips(stop.position),
10926
11052
  type: stop.type ?? "left",
10927
11053
  ...stop.leader ? { leader: stop.leader } : {}
10928
11054
  })
@@ -10971,7 +11097,10 @@ function compileSpacing(props, ctx) {
10971
11097
  }
10972
11098
  function compileLineSpacing(value) {
10973
11099
  if (typeof value === "number") {
10974
- return { lineTwips: value * SINGLE_LINE_TWIPS, lineRule: "auto" };
11100
+ return {
11101
+ lineTwips: wholeTwips(value * SINGLE_LINE_TWIPS),
11102
+ lineRule: "auto"
11103
+ };
10975
11104
  }
10976
11105
  if (typeof value !== "object" || value === null) return void 0;
10977
11106
  const { type, value: amount } = value;
@@ -10992,7 +11121,7 @@ function compileLineSpacing(value) {
10992
11121
  };
10993
11122
  case "multiple":
10994
11123
  return {
10995
- ...amount !== void 0 ? { lineTwips: amount * SINGLE_LINE_TWIPS } : {},
11124
+ ...amount !== void 0 ? { lineTwips: wholeTwips(amount * SINGLE_LINE_TWIPS) } : {},
10996
11125
  lineRule: "auto"
10997
11126
  };
10998
11127
  default:
@@ -11453,8 +11582,8 @@ function compileFrame(floating, ctx, path4) {
11453
11582
  const useAbsolute = hasHorizontalOffset || hasVerticalOffset;
11454
11583
  const frame = {
11455
11584
  // 2in by 1in when the author gave no size.
11456
- widthTwips: floating.width || 2880,
11457
- heightTwips: floating.height || 1440,
11585
+ widthTwips: wholeTwips(floating.width || 2880),
11586
+ heightTwips: wholeTwips(floating.height || 1440),
11458
11587
  anchorHorizontal: floating.horizontalPosition?.relative || "page",
11459
11588
  anchorVertical: floating.verticalPosition?.relative || "page",
11460
11589
  ...floating.wrap?.type ? { wrap: floating.wrap.type } : {}
@@ -11467,17 +11596,21 @@ function compileFrame(floating, ctx, path4) {
11467
11596
  if (typeof rawX === "string" || typeof rawY === "string") {
11468
11597
  const hRelative = floating.horizontalPosition?.relative;
11469
11598
  const vRelative = floating.verticalPosition?.relative;
11470
- frame.xTwips = resolveOffsetTwips(
11471
- rawX,
11472
- hRelative && hRelative !== "page" ? getAvailableWidthTwips(ctx.theme, ctx.themeName) : getPageWidthTwips(ctx.theme, ctx.themeName)
11599
+ frame.xTwips = wholeTwips(
11600
+ resolveOffsetTwips(
11601
+ rawX,
11602
+ hRelative && hRelative !== "page" ? getAvailableWidthTwips(ctx.theme, ctx.themeName) : getPageWidthTwips(ctx.theme, ctx.themeName)
11603
+ )
11473
11604
  );
11474
- frame.yTwips = resolveOffsetTwips(
11475
- rawY,
11476
- vRelative && vRelative !== "page" ? getAvailableHeightTwips(ctx.theme, ctx.themeName) : getPageHeightTwips(ctx.theme, ctx.themeName)
11605
+ frame.yTwips = wholeTwips(
11606
+ resolveOffsetTwips(
11607
+ rawY,
11608
+ vRelative && vRelative !== "page" ? getAvailableHeightTwips(ctx.theme, ctx.themeName) : getPageHeightTwips(ctx.theme, ctx.themeName)
11609
+ )
11477
11610
  );
11478
11611
  } else {
11479
- frame.xTwips = rawX;
11480
- frame.yTwips = rawY;
11612
+ frame.xTwips = wholeTwips(rawX);
11613
+ frame.yTwips = wholeTwips(rawY);
11481
11614
  }
11482
11615
  return frame;
11483
11616
  }
@@ -11811,10 +11944,17 @@ function cellChildren(cell, baseStyle, scope, rowRevision) {
11811
11944
  return wrap(cellVisual(content, scope));
11812
11945
  }
11813
11946
  if (content.name !== "paragraph") {
11947
+ const text2 = unrenderedComponentText(content.name);
11948
+ warnOnce(
11949
+ ctx,
11950
+ "table",
11951
+ `${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.`,
11952
+ "W_UNSUPPORTED_CELL_CONTENT"
11953
+ );
11814
11954
  return wrap([
11815
11955
  {
11816
11956
  kind: "text",
11817
- text: `[Unsupported component type: ${content.name}]`,
11957
+ text: text2,
11818
11958
  formatting: placeholderFormatting(base)
11819
11959
  }
11820
11960
  ]);
@@ -13183,6 +13323,11 @@ init_widthUtils();
13183
13323
  import probe2 from "probe-image-size";
13184
13324
 
13185
13325
  // src/quality/text-inventory.ts
13326
+ import {
13327
+ unrenderedComponentText as unrenderedComponentText2
13328
+ } from "@json-to-office/quality";
13329
+ import { TABLE_CELL_COMPONENTS } from "@json-to-office/shared-docx";
13330
+ var CELL_COMPONENTS = new Set(TABLE_CELL_COMPONENTS);
13186
13331
  function tocDepth(depth) {
13187
13332
  const range = asRecord(depth) ?? {};
13188
13333
  const from = typeof range.from === "number" ? range.from : 1;
@@ -13231,7 +13376,14 @@ function collectDocxTextInventory(children, basePath = "/children") {
13231
13376
  add(`${path4}/content`, rec.content, cellRole, extra);
13232
13377
  else {
13233
13378
  const inner = asRecord(rec.content);
13234
- if (inner)
13379
+ if (inner && typeof inner.name === "string" && !CELL_COMPONENTS.has(inner.name)) {
13380
+ add(
13381
+ `${path4}/content`,
13382
+ unrenderedComponentText2(inner.name),
13383
+ cellRole,
13384
+ { ...extra, optional: true, unrendered: inner.name }
13385
+ );
13386
+ } else if (inner)
13235
13387
  visitNode(inner, `${path4}/content`, { ...inherited, inCell: true });
13236
13388
  }
13237
13389
  return;