@json-to-office/core-docx 7.1.2 → 7.3.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.
Files changed (54) hide show
  1. package/dist/index.js +1647 -427
  2. package/dist/index.js.map +1 -1
  3. package/dist/ir/chartValues.d.ts +16 -0
  4. package/dist/ir/chartValues.d.ts.map +1 -0
  5. package/dist/ir/compiler.d.ts.map +1 -1
  6. package/dist/ir/features.d.ts.map +1 -1
  7. package/dist/ir/theme.d.ts +43 -0
  8. package/dist/ir/theme.d.ts.map +1 -0
  9. package/dist/ir/types.d.ts +45 -3
  10. package/dist/ir/types.d.ts.map +1 -1
  11. package/dist/ir/units.d.ts +12 -0
  12. package/dist/ir/units.d.ts.map +1 -1
  13. package/dist/ir/validation.d.ts +2 -1
  14. package/dist/ir/validation.d.ts.map +1 -1
  15. package/dist/plugin/example/index.js +1644 -424
  16. package/dist/plugin/example/index.js.map +1 -1
  17. package/dist/quality/text-inventory.d.ts +13 -3
  18. package/dist/quality/text-inventory.d.ts.map +1 -1
  19. package/dist/renderers/chartText.d.ts +43 -0
  20. package/dist/renderers/chartText.d.ts.map +1 -0
  21. package/dist/renderers/docxjs/charts.d.ts +45 -0
  22. package/dist/renderers/docxjs/charts.d.ts.map +1 -0
  23. package/dist/renderers/docxjs/docxSubpath.d.ts +34 -0
  24. package/dist/renderers/docxjs/docxSubpath.d.ts.map +1 -0
  25. package/dist/renderers/docxjs/drawingGroup.d.ts +79 -0
  26. package/dist/renderers/docxjs/drawingGroup.d.ts.map +1 -0
  27. package/dist/renderers/docxjs/drawingGroupRepair.d.ts +36 -0
  28. package/dist/renderers/docxjs/drawingGroupRepair.d.ts.map +1 -0
  29. package/dist/renderers/docxjs/emit.d.ts +10 -2
  30. package/dist/renderers/docxjs/emit.d.ts.map +1 -1
  31. package/dist/renderers/docxjs/index.d.ts +2 -1
  32. package/dist/renderers/docxjs/index.d.ts.map +1 -1
  33. package/dist/renderers/docxjs/presetShapes.d.ts +233 -0
  34. package/dist/renderers/docxjs/presetShapes.d.ts.map +1 -0
  35. package/dist/renderers/office-open/chartLook.d.ts +45 -0
  36. package/dist/renderers/office-open/chartLook.d.ts.map +1 -0
  37. package/dist/renderers/office-open/chartParts.d.ts +29 -15
  38. package/dist/renderers/office-open/chartParts.d.ts.map +1 -1
  39. package/dist/renderers/office-open/emit.d.ts +25 -17
  40. package/dist/renderers/office-open/emit.d.ts.map +1 -1
  41. package/dist/renderers/office-open/index.d.ts +16 -3
  42. package/dist/renderers/office-open/index.d.ts.map +1 -1
  43. package/dist/renderers/office-open/themePart.d.ts +28 -0
  44. package/dist/renderers/office-open/themePart.d.ts.map +1 -0
  45. package/dist/styles/themeToStyles.d.ts.map +1 -1
  46. package/dist/styles/utils/styleHelpers.d.ts +3 -1
  47. package/dist/styles/utils/styleHelpers.d.ts.map +1 -1
  48. package/dist/tsconfig.tsbuildinfo +1 -1
  49. package/dist/utils/chartWorkbook.d.ts +2 -1
  50. package/dist/utils/chartWorkbook.d.ts.map +1 -1
  51. package/dist/utils/fixFloatingImageIds.d.ts +17 -9
  52. package/dist/utils/fixFloatingImageIds.d.ts.map +1 -1
  53. package/dist/utils/packageDocument.d.ts.map +1 -1
  54. package/package.json +7 -7
@@ -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:
@@ -2751,7 +2751,7 @@ var init_imageUtils = __esm({
2751
2751
  });
2752
2752
 
2753
2753
  // src/ir/types.ts
2754
- var TWIPS_PER_INCH, TWIPS_PER_POINT2, EMU_PER_INCH, EMU_PER_TWIP, DOCX_IR_SCHEMA_VERSION, DOCX_IR_BORDER_STYLES;
2754
+ var TWIPS_PER_INCH, TWIPS_PER_POINT2, EMU_PER_INCH, EMU_PER_TWIP, DOCX_IR_SCHEMA_VERSION, NOT_XML_CHAR, DOCX_IR_BORDER_STYLES;
2755
2755
  var init_types = __esm({
2756
2756
  "src/ir/types.ts"() {
2757
2757
  "use strict";
@@ -2760,6 +2760,7 @@ var init_types = __esm({
2760
2760
  EMU_PER_INCH = 914400;
2761
2761
  EMU_PER_TWIP = EMU_PER_INCH / TWIPS_PER_INCH;
2762
2762
  DOCX_IR_SCHEMA_VERSION = 1;
2763
+ NOT_XML_CHAR = /[^\t\n\r\u0020-\uD7FF\uE000-\uFFFD\u{10000}-\u{10FFFF}]/u;
2763
2764
  DOCX_IR_BORDER_STYLES = [
2764
2765
  "single",
2765
2766
  "dashDotStroked",
@@ -2797,6 +2798,9 @@ import { createHash as createHash2 } from "crypto";
2797
2798
  function pointsToTwips2(points) {
2798
2799
  return Math.round(points * TWIPS_PER_POINT2);
2799
2800
  }
2801
+ function wholeTwips(twips2) {
2802
+ return Math.round(twips2);
2803
+ }
2800
2804
  function pointsToHalfPoints(points) {
2801
2805
  return Math.round(points * 2);
2802
2806
  }
@@ -2812,8 +2816,8 @@ function inchesToEmu(inches) {
2812
2816
  function pointsToEmu(points) {
2813
2817
  return Math.round(points * EMU_PER_INCH / 72);
2814
2818
  }
2815
- function twipsToEmu(twips) {
2816
- return Math.round(twips * EMU_PER_TWIP);
2819
+ function twipsToEmu(twips2) {
2820
+ return Math.round(twips2 * EMU_PER_TWIP);
2817
2821
  }
2818
2822
  function pixelsToEmu(pixels) {
2819
2823
  return Math.round(pixels * 9525);
@@ -2821,8 +2825,8 @@ function pixelsToEmu(pixels) {
2821
2825
  function emuToPixels(emu) {
2822
2826
  return emu / 9525;
2823
2827
  }
2824
- function twipsToPixels(twips) {
2825
- return twips / TWIPS_PER_INCH * 96;
2828
+ function twipsToPixels(twips2) {
2829
+ return twips2 / TWIPS_PER_INCH * 96;
2826
2830
  }
2827
2831
  function irColor(hex) {
2828
2832
  return { hex: hex.startsWith("#") ? hex.slice(1) : hex };
@@ -2843,6 +2847,226 @@ var init_units = __esm({
2843
2847
  }
2844
2848
  });
2845
2849
 
2850
+ // src/ir/chartValues.ts
2851
+ function isNumericLabel(label) {
2852
+ return NUMERIC_LABEL.test(label.trim());
2853
+ }
2854
+ function scatterX(labels, index) {
2855
+ const label = labels[index];
2856
+ return label !== void 0 && isNumericLabel(label) ? Number(label) : index + 1;
2857
+ }
2858
+ var NUMERIC_LABEL;
2859
+ var init_chartValues = __esm({
2860
+ "src/ir/chartValues.ts"() {
2861
+ "use strict";
2862
+ NUMERIC_LABEL = /^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$/;
2863
+ }
2864
+ });
2865
+
2866
+ // src/renderers/chartText.ts
2867
+ function chartTextRoles(font) {
2868
+ const { bold, ...rest } = font;
2869
+ const text = rest;
2870
+ return {
2871
+ text,
2872
+ axisTitle: { ...text, bold },
2873
+ title: {
2874
+ ...text,
2875
+ fontSize: Math.round(text.fontSize * CHART_TITLE_SCALE),
2876
+ bold: true
2877
+ }
2878
+ };
2879
+ }
2880
+ var CHART_TITLE_SCALE;
2881
+ var init_chartText = __esm({
2882
+ "src/renderers/chartText.ts"() {
2883
+ "use strict";
2884
+ CHART_TITLE_SCALE = 1.4;
2885
+ }
2886
+ });
2887
+
2888
+ // src/renderers/docxjs/docxSubpath.ts
2889
+ function docxSubpath(specifier, component, importer) {
2890
+ let module;
2891
+ let failure;
2892
+ let loading;
2893
+ return {
2894
+ specifier,
2895
+ load() {
2896
+ loading ??= importer().then(
2897
+ (loadedModule) => {
2898
+ module = loadedModule;
2899
+ },
2900
+ (reason) => {
2901
+ failure = { reason };
2902
+ }
2903
+ );
2904
+ return loading;
2905
+ },
2906
+ loaded: () => module !== void 0,
2907
+ get() {
2908
+ if (module !== void 0) return module;
2909
+ if (failure !== void 0) {
2910
+ const { reason } = failure;
2911
+ const detail = reason instanceof Error ? reason.message : String(reason);
2912
+ throw new Error(
2913
+ `The docx \`${component}\` component needs docx 9.8.0 or later: the \`${specifier}\` entry could not be loaded (${detail})`
2914
+ );
2915
+ }
2916
+ throw new Error(
2917
+ `${specifier} is not loaded: await its load() before buildDocument() (render() does)`
2918
+ );
2919
+ }
2920
+ };
2921
+ }
2922
+ var init_docxSubpath = __esm({
2923
+ "src/renderers/docxjs/docxSubpath.ts"() {
2924
+ "use strict";
2925
+ }
2926
+ });
2927
+
2928
+ // src/renderers/docxjs/charts.ts
2929
+ import { assertNever } from "@json-to-office/shared/rendering";
2930
+ function loadDocxCharts() {
2931
+ return charts.load();
2932
+ }
2933
+ function chartFont(role) {
2934
+ return {
2935
+ name: role.fontFamily,
2936
+ size: role.fontSize,
2937
+ color: role.color,
2938
+ bold: role.bold ?? false
2939
+ };
2940
+ }
2941
+ function chartRunOptions(chart, floating) {
2942
+ const roles = chartTextRoles(chart.textFont);
2943
+ const text = chartFont(roles.text);
2944
+ const axisTitle = chartFont(roles.axisTitle);
2945
+ const palette = chart.colors;
2946
+ const colorAt = (index) => palette.length > 0 ? palette[index % palette.length] : void 0;
2947
+ const withColor = (index) => {
2948
+ const color = colorAt(index);
2949
+ return color ? { color } : {};
2950
+ };
2951
+ const seriesName = (index) => chart.series[index].name ?? `Series ${index + 1}`;
2952
+ const categories = chart.series[0]?.labels ?? [];
2953
+ const axis = (title) => ({
2954
+ ...title ? { title: { text: title, font: axisTitle } } : {},
2955
+ font: text
2956
+ });
2957
+ const common = {
2958
+ ...chart.title && chart.showTitle !== false ? { title: { text: chart.title, font: chartFont(roles.title) } } : {},
2959
+ legend: chart.showLegend === false ? false : {
2960
+ position: LEGEND_POSITION[chart.legendPosition ?? "b"],
2961
+ font: text
2962
+ },
2963
+ font: { name: roles.text.fontFamily, color: roles.text.color },
2964
+ // Office-open's chart space is unfilled and unbordered; `ChartRun`'s
2965
+ // default is Word's white fill and grey border.
2966
+ chartArea: { fill: "none", border: "none" },
2967
+ transformation: {
2968
+ width: emuToPixels(chart.widthEmu),
2969
+ height: emuToPixels(chart.heightEmu)
2970
+ },
2971
+ // Without an authored description `ChartRun` describes the chart from
2972
+ // its data, which is deterministic; the name stays empty, as it is on
2973
+ // every other drawing this renderer writes.
2974
+ ...chart.altText ? { altText: { name: "", description: chart.altText } } : {},
2975
+ ...floating ? { floating } : {}
2976
+ };
2977
+ switch (chart.chartType) {
2978
+ case "column":
2979
+ case "bar":
2980
+ case "area":
2981
+ case "line":
2982
+ return {
2983
+ ...common,
2984
+ type: chart.chartType,
2985
+ categories,
2986
+ ...chart.chartType === "line" ? { markers: true } : {},
2987
+ series: chart.series.map((entry, index) => ({
2988
+ name: seriesName(index),
2989
+ values: entry.values,
2990
+ ...withColor(index)
2991
+ })),
2992
+ categoryAxis: axis(chart.categoryAxisTitle),
2993
+ valueAxis: axis(chart.valueAxisTitle)
2994
+ };
2995
+ case "pie":
2996
+ case "doughnut":
2997
+ return {
2998
+ ...common,
2999
+ type: chart.chartType,
3000
+ categories,
3001
+ series: chart.series.map((entry, index) => ({
3002
+ name: seriesName(index),
3003
+ values: entry.values,
3004
+ ...palette.length > 0 ? { colors: entry.values.map((_, point) => colorAt(point)) } : {}
3005
+ }))
3006
+ };
3007
+ case "radar":
3008
+ return {
3009
+ ...common,
3010
+ type: "radar",
3011
+ categories,
3012
+ markers: true,
3013
+ series: chart.series.map((entry, index) => ({
3014
+ name: seriesName(index),
3015
+ values: entry.values,
3016
+ ...withColor(index)
3017
+ })),
3018
+ categoryAxis: axis(chart.categoryAxisTitle),
3019
+ valueAxis: axis(chart.valueAxisTitle)
3020
+ };
3021
+ case "scatter":
3022
+ return {
3023
+ ...common,
3024
+ type: "scatter",
3025
+ markers: true,
3026
+ lines: "straight",
3027
+ series: chart.series.map((entry, index) => ({
3028
+ name: seriesName(index),
3029
+ points: entry.values.map((y, point) => ({
3030
+ x: scatterX(entry.labels, point),
3031
+ y
3032
+ })),
3033
+ ...withColor(index)
3034
+ })),
3035
+ xAxis: { ...axis(chart.categoryAxisTitle), gridlines: false },
3036
+ yAxis: axis(chart.valueAxisTitle)
3037
+ };
3038
+ case "bubble":
3039
+ throw new Error(
3040
+ "the docxjs renderer has no emitter for a bubble chart; the compiler should have refused it"
3041
+ );
3042
+ default:
3043
+ return assertNever(chart.chartType, "DocxIrChartType");
3044
+ }
3045
+ }
3046
+ function emitChart(chart, floating) {
3047
+ return new (charts.get()).ChartRun(
3048
+ chartRunOptions(chart, floating)
3049
+ );
3050
+ }
3051
+ var charts, LEGEND_POSITION;
3052
+ var init_charts = __esm({
3053
+ "src/renderers/docxjs/charts.ts"() {
3054
+ "use strict";
3055
+ init_chartValues();
3056
+ init_units();
3057
+ init_chartText();
3058
+ init_docxSubpath();
3059
+ charts = docxSubpath("docx/charts", "chart", () => import("docx/charts"));
3060
+ LEGEND_POSITION = {
3061
+ b: "bottom",
3062
+ l: "left",
3063
+ r: "right",
3064
+ t: "top",
3065
+ tr: "topRight"
3066
+ };
3067
+ }
3068
+ });
3069
+
2846
3070
  // src/renderers/docxjs/emit.ts
2847
3071
  import {
2848
3072
  AlignmentType,
@@ -2878,7 +3102,7 @@ import {
2878
3102
  WidthType,
2879
3103
  WpsShapeRun
2880
3104
  } from "docx";
2881
- import { assertNever } from "@json-to-office/shared/rendering";
3105
+ import { assertNever as assertNever2 } from "@json-to-office/shared/rendering";
2882
3106
  function complexField(instruction, formatting, leading, cachedText) {
2883
3107
  const options = runOptions(formatting);
2884
3108
  const fieldChar = (type, extra = {}) => new TextRun({
@@ -2915,7 +3139,12 @@ function runOptions(formatting) {
2915
3139
  if (formatting.bold !== void 0) options.bold = formatting.bold;
2916
3140
  if (formatting.italic !== void 0) options.italics = formatting.italic;
2917
3141
  if (formatting.underline !== void 0) {
2918
- options.underline = formatting.underline ? { type: formatting.underline.type } : void 0;
3142
+ options.underline = formatting.underline ? {
3143
+ type: formatting.underline.type,
3144
+ // A native visual's text is the one place a run states its own
3145
+ // underline colour.
3146
+ ...formatting.underline.color ? { color: formatting.underline.color.hex } : {}
3147
+ } : void 0;
2919
3148
  }
2920
3149
  if (formatting.strike !== void 0) options.strike = formatting.strike;
2921
3150
  if (formatting.allCaps !== void 0) options.allCaps = formatting.allCaps;
@@ -3041,21 +3270,36 @@ function inlineChildren(children, resources = /* @__PURE__ */ new Map()) {
3041
3270
  case "shape":
3042
3271
  out.push(emitShape(child, resources));
3043
3272
  break;
3044
- case "drawingGroup":
3045
- throw new Error(
3046
- "the docxjs renderer has no emitter for a drawing group; this document should have been refused by the capability check"
3047
- );
3048
- case "chart":
3049
- throw new Error(
3050
- "the docxjs renderer has no emitter for a chart; this document should have been refused by the capability check"
3273
+ case "drawingGroup": {
3274
+ const build = resources.drawingGroup;
3275
+ if (!build) {
3276
+ throw new Error(
3277
+ "the docxjs renderer was handed a drawing group without loading docx/shapes; render() loads it whenever the IR holds one"
3278
+ );
3279
+ }
3280
+ const pending = breakOption();
3281
+ if (pending.break) out.push(new TextRun(pending));
3282
+ out.push(build(child, resources));
3283
+ break;
3284
+ }
3285
+ case "chart": {
3286
+ const pending = breakOption();
3287
+ if (pending.break) out.push(new TextRun(pending));
3288
+ out.push(
3289
+ emitChart(
3290
+ child,
3291
+ child.floating ? floatingOptions(child.floating) : void 0
3292
+ )
3051
3293
  );
3294
+ break;
3295
+ }
3052
3296
  case "noteReference":
3053
3297
  out.push(
3054
3298
  child.noteKind === "endnote" ? new EndnoteReferenceRun(child.id) : new FootnoteReferenceRun(child.id)
3055
3299
  );
3056
3300
  break;
3057
3301
  default:
3058
- assertNever(child, "DocxIrInline");
3302
+ assertNever2(child, "DocxIrInline");
3059
3303
  }
3060
3304
  }
3061
3305
  return out;
@@ -3260,7 +3504,7 @@ function emitBlock(block2, resources = /* @__PURE__ */ new Map()) {
3260
3504
  case "toc":
3261
3505
  return emitToc(block2);
3262
3506
  default:
3263
- return assertNever(block2, "DocxIrBlock");
3507
+ return assertNever2(block2, "DocxIrBlock");
3264
3508
  }
3265
3509
  }
3266
3510
  function emitToc(block2) {
@@ -3404,6 +3648,7 @@ var ALIGNMENT2, PAGE_FIELDS, isSequenceField, WRAP_TYPE, ThemedTableOfContents,
3404
3648
  var init_emit = __esm({
3405
3649
  "src/renderers/docxjs/emit.ts"() {
3406
3650
  "use strict";
3651
+ init_charts();
3407
3652
  ALIGNMENT2 = {
3408
3653
  left: AlignmentType.LEFT,
3409
3654
  center: AlignmentType.CENTER,
@@ -3702,10 +3947,13 @@ function canonicalizeRelationshipIds(zip) {
3702
3947
  const rename = new Map(
3703
3948
  ordered.map((id, index) => [id, `rId${highest + 1 + index}`])
3704
3949
  );
3705
- const rewrite = (xml) => xml.replace(/"([^"]+)"/g, (whole, value) => {
3706
- const replacement = rename.get(value);
3707
- return replacement === void 0 ? whole : `"${replacement}"`;
3708
- });
3950
+ const rewrite = (xml) => xml.replace(
3951
+ /(\s[\w:.-]+=)"([^"]*)"/g,
3952
+ (whole, prefix, value) => {
3953
+ const replacement = rename.get(value);
3954
+ return replacement === void 0 ? whole : `${prefix}"${replacement}"`;
3955
+ }
3956
+ );
3709
3957
  updates.push({ name: relsName, xml: rewrite(relsXml) });
3710
3958
  if (partEntry) {
3711
3959
  updates.push({ name: partName, xml: rewrite(partXml) });
@@ -3716,17 +3964,62 @@ function canonicalizeRelationshipIds(zip) {
3716
3964
  if (entry) zip.updateFile(entry, Buffer.from(update.xml, "utf8"));
3717
3965
  }
3718
3966
  }
3967
+ function canonicalizeEmbeddedPackages(zip, generatedAt, depth = 1) {
3968
+ const names = zip.getEntries().map((entry) => entry.entryName).filter((name) => EMBEDDED_OFFICE_PACKAGE.test(name));
3969
+ let changed = false;
3970
+ for (const name of names) {
3971
+ const entry = zip.getEntry(name);
3972
+ if (!entry) continue;
3973
+ let nested;
3974
+ try {
3975
+ nested = new AdmZip(entry.getData());
3976
+ nested.getEntries();
3977
+ } catch {
3978
+ continue;
3979
+ }
3980
+ if (canonicalizeNestedPackage(nested, generatedAt, depth)) {
3981
+ zip.updateFile(entry, nested.toBuffer());
3982
+ changed = true;
3983
+ }
3984
+ }
3985
+ return changed;
3986
+ }
3987
+ function canonicalizeNestedPackage(zip, generatedAt, depth) {
3988
+ let changed = false;
3989
+ const coreProperties3 = zip.getEntry("docProps/core.xml");
3990
+ if (coreProperties3) {
3991
+ const xml = coreProperties3.getData().toString("utf8");
3992
+ const normalized = xml.replace(
3993
+ CORE_TIMESTAMP,
3994
+ `$1${generatedAt.toISOString()}$2`
3995
+ );
3996
+ if (normalized !== xml) {
3997
+ zip.updateFile(coreProperties3, Buffer.from(normalized, "utf8"));
3998
+ changed = true;
3999
+ }
4000
+ }
4001
+ if (depth < MAX_EMBEDDED_DEPTH && canonicalizeEmbeddedPackages(zip, generatedAt, depth + 1)) {
4002
+ changed = true;
4003
+ }
4004
+ const zipTimestamp = toDosTime(generatedAt);
4005
+ for (const entry of zip.getEntries()) {
4006
+ const header = entry.header;
4007
+ if (header.timeval !== zipTimestamp) {
4008
+ header.timeval = zipTimestamp;
4009
+ changed = true;
4010
+ }
4011
+ }
4012
+ return changed;
4013
+ }
3719
4014
  function canonicalizeDocxBuffer(buffer, generatedAt = DEFAULT_GENERATION_DATE) {
3720
4015
  const zip = new AdmZip(buffer);
3721
4016
  normalizeTextCase(zip);
3722
4017
  canonicalizeRelationshipIds(zip);
4018
+ canonicalizeEmbeddedPackages(zip, generatedAt);
3723
4019
  const isoTimestamp = generatedAt.toISOString();
3724
4020
  const coreProperties3 = zip.getEntry("docProps/core.xml");
3725
4021
  if (coreProperties3) {
3726
- const normalized = coreProperties3.getData().toString("utf8").replace(
3727
- /(<dcterms:(?:created|modified)\b[^>]*>)[^<]*(<\/dcterms:(?:created|modified)>)/g,
3728
- `$1${isoTimestamp}$2`
3729
- );
4022
+ const normalized = coreProperties3.getData().toString("utf8").replace(CORE_TIMESTAMP, `$1${isoTimestamp}$2`);
3730
4023
  zip.updateFile(coreProperties3, Buffer.from(normalized, "utf8"));
3731
4024
  }
3732
4025
  const zipTimestamp = toDosTime(generatedAt);
@@ -3739,35 +4032,567 @@ function normalizeDocxCaseBuffer(buffer) {
3739
4032
  const zip = new AdmZip(buffer);
3740
4033
  return normalizeTextCase(zip) ? zip.toBuffer() : buffer;
3741
4034
  }
3742
- var DEFAULT_GENERATION_DATE, STABLE_RELATIONSHIP_ID;
4035
+ var DEFAULT_GENERATION_DATE, STABLE_RELATIONSHIP_ID, CORE_TIMESTAMP, EMBEDDED_OFFICE_PACKAGE, MAX_EMBEDDED_DEPTH;
3743
4036
  var init_packageDocument = __esm({
3744
4037
  "src/utils/packageDocument.ts"() {
3745
4038
  "use strict";
3746
4039
  init_normalizeTextCase();
3747
4040
  DEFAULT_GENERATION_DATE = /* @__PURE__ */ new Date("2000-01-01T00:00:00.000Z");
3748
4041
  STABLE_RELATIONSHIP_ID = /^rId\d+$/;
4042
+ CORE_TIMESTAMP = /(<dcterms:(?:created|modified)\b[^>]*>)[^<]*(<\/dcterms:(?:created|modified)>)/g;
4043
+ EMBEDDED_OFFICE_PACKAGE = /\.(?:docx|pptx|xlsx|xlsm)$/i;
4044
+ MAX_EMBEDDED_DEPTH = 3;
3749
4045
  }
3750
4046
  });
3751
4047
 
3752
4048
  // src/utils/fixFloatingImageIds.ts
3753
4049
  import AdmZip2 from "adm-zip";
4050
+ function compareDrawingParts(a, b) {
4051
+ const ma = DRAWING_PART.exec(a);
4052
+ const mb = DRAWING_PART.exec(b);
4053
+ const rank = RANK.indexOf(ma[1]) - RANK.indexOf(mb[1]);
4054
+ return rank !== 0 ? rank : Number(ma[2] || 0) - Number(mb[2] || 0);
4055
+ }
3754
4056
  function fixFloatingImageIdsInBuffer(buffer) {
3755
4057
  const zip = new AdmZip2(buffer);
3756
- const documentEntry = zip.getEntry("word/document.xml");
3757
- if (!documentEntry) {
4058
+ if (!zip.getEntry("word/document.xml")) {
3758
4059
  throw new Error("document.xml not found in DOCX");
3759
4060
  }
4061
+ const parts = zip.getEntries().filter((entry) => DRAWING_PART.test(entry.entryName)).sort((a, b) => compareDrawingParts(a.entryName, b.entryName));
3760
4062
  let idCounter = 1;
3761
- const documentXml = documentEntry.getData().toString("utf8").replace(/(<wp:docPr\b[^>]*?\s)id="\d+"/g, (_match, prefix) => {
3762
- const newId = idCounter++;
3763
- return `${prefix}id="${newId}"`;
3764
- });
3765
- zip.updateFile(documentEntry, Buffer.from(documentXml, "utf8"));
4063
+ for (const entry of parts) {
4064
+ const xml = entry.getData().toString("utf8");
4065
+ const renumbered = xml.replace(
4066
+ /(<wp:docPr\b[^>]*?\s)id="\d+"/g,
4067
+ (_match, prefix) => `${prefix}id="${idCounter++}"`
4068
+ );
4069
+ zip.updateFile(entry, Buffer.from(renumbered, "utf8"));
4070
+ }
3766
4071
  return zip.toBuffer();
3767
4072
  }
4073
+ var DRAWING_PART, RANK;
3768
4074
  var init_fixFloatingImageIds = __esm({
3769
4075
  "src/utils/fixFloatingImageIds.ts"() {
3770
4076
  "use strict";
4077
+ DRAWING_PART = /^word\/(document|header|footer|footnotes|endnotes|comments)(\d*)\.xml$/;
4078
+ RANK = [
4079
+ "document",
4080
+ "header",
4081
+ "footer",
4082
+ "footnotes",
4083
+ "endnotes",
4084
+ "comments"
4085
+ ];
4086
+ }
4087
+ });
4088
+
4089
+ // src/renderers/docxjs/presetShapes.ts
4090
+ function docxPresetShape(geometry) {
4091
+ const name = Object.hasOwn(OOXML_PRESET_TO_DOCX, geometry) ? OOXML_PRESET_TO_DOCX[geometry] : void 0;
4092
+ if (!name) {
4093
+ throw new Error(
4094
+ `the docxjs renderer has no preset for the OOXML geometry "${geometry}"`
4095
+ );
4096
+ }
4097
+ return name;
4098
+ }
4099
+ function docxLineDash(dash) {
4100
+ const name = Object.hasOwn(OOXML_DASH_TO_DOCX, dash) ? OOXML_DASH_TO_DOCX[dash] : void 0;
4101
+ if (!name) {
4102
+ throw new Error(
4103
+ `the docxjs renderer has no line dash for the OOXML value "${dash}"`
4104
+ );
4105
+ }
4106
+ return name;
4107
+ }
4108
+ var OOXML_PRESET_TO_DOCX, OOXML_DASH_TO_DOCX;
4109
+ var init_presetShapes = __esm({
4110
+ "src/renderers/docxjs/presetShapes.ts"() {
4111
+ "use strict";
4112
+ OOXML_PRESET_TO_DOCX = {
4113
+ line: "line",
4114
+ lineInv: "inverseLine",
4115
+ straightConnector1: "straightConnector",
4116
+ bentConnector2: "elbowConnectorOneBend",
4117
+ bentConnector3: "elbowConnector",
4118
+ bentConnector4: "elbowConnectorThreeBends",
4119
+ bentConnector5: "elbowConnectorFourBends",
4120
+ curvedConnector2: "curvedConnectorOneBend",
4121
+ curvedConnector3: "curvedConnector",
4122
+ curvedConnector4: "curvedConnectorThreeBends",
4123
+ curvedConnector5: "curvedConnectorFourBends",
4124
+ triangle: "triangle",
4125
+ rtTriangle: "rightTriangle",
4126
+ diamond: "diamond",
4127
+ parallelogram: "parallelogram",
4128
+ trapezoid: "trapezoid",
4129
+ nonIsoscelesTrapezoid: "nonIsoscelesTrapezoid",
4130
+ pentagon: "pentagon",
4131
+ hexagon: "hexagon",
4132
+ heptagon: "heptagon",
4133
+ octagon: "octagon",
4134
+ decagon: "decagon",
4135
+ dodecagon: "dodecagon",
4136
+ ellipse: "ellipse",
4137
+ teardrop: "teardrop",
4138
+ pieWedge: "pieWedge",
4139
+ pie: "pie",
4140
+ blockArc: "blockArc",
4141
+ donut: "donut",
4142
+ noSmoking: "noSymbol",
4143
+ chord: "chord",
4144
+ arc: "arc",
4145
+ frame: "frame",
4146
+ halfFrame: "halfFrame",
4147
+ corner: "lShape",
4148
+ diagStripe: "diagonalStripe",
4149
+ plus: "cross",
4150
+ plaque: "plaque",
4151
+ can: "cylinder",
4152
+ cube: "cube",
4153
+ bevel: "beveledRectangle",
4154
+ foldedCorner: "foldedCorner",
4155
+ smileyFace: "smileyFace",
4156
+ heart: "heart",
4157
+ lightningBolt: "lightningBolt",
4158
+ sun: "sun",
4159
+ moon: "moon",
4160
+ cloud: "cloud",
4161
+ leftBracket: "leftBracket",
4162
+ rightBracket: "rightBracket",
4163
+ leftBrace: "leftBrace",
4164
+ rightBrace: "rightBrace",
4165
+ bracketPair: "bracketPair",
4166
+ bracePair: "bracePair",
4167
+ rect: "rectangle",
4168
+ roundRect: "roundedRectangle",
4169
+ round1Rect: "roundedCornerRectangle",
4170
+ round2SameRect: "topRoundedCornersRectangle",
4171
+ round2DiagRect: "diagonalRoundedCornersRectangle",
4172
+ snip1Rect: "snippedCornerRectangle",
4173
+ snip2SameRect: "topSnippedCornersRectangle",
4174
+ snip2DiagRect: "diagonalSnippedCornersRectangle",
4175
+ snipRoundRect: "roundedAndSnippedCornersRectangle",
4176
+ rightArrow: "rightArrow",
4177
+ leftArrow: "leftArrow",
4178
+ upArrow: "upArrow",
4179
+ downArrow: "downArrow",
4180
+ leftRightArrow: "leftRightArrow",
4181
+ upDownArrow: "upDownArrow",
4182
+ quadArrow: "quadArrow",
4183
+ leftRightUpArrow: "leftRightUpArrow",
4184
+ bentArrow: "bentArrow",
4185
+ uturnArrow: "uTurnArrow",
4186
+ leftUpArrow: "leftUpArrow",
4187
+ bentUpArrow: "bentUpArrow",
4188
+ curvedRightArrow: "curvedRightArrow",
4189
+ curvedLeftArrow: "curvedLeftArrow",
4190
+ curvedUpArrow: "curvedUpArrow",
4191
+ curvedDownArrow: "curvedDownArrow",
4192
+ stripedRightArrow: "stripedRightArrow",
4193
+ notchedRightArrow: "notchedRightArrow",
4194
+ homePlate: "pentagonArrow",
4195
+ chevron: "chevron",
4196
+ rightArrowCallout: "rightArrowCallout",
4197
+ downArrowCallout: "downArrowCallout",
4198
+ leftArrowCallout: "leftArrowCallout",
4199
+ upArrowCallout: "upArrowCallout",
4200
+ leftRightArrowCallout: "leftRightArrowCallout",
4201
+ upDownArrowCallout: "upDownArrowCallout",
4202
+ quadArrowCallout: "quadArrowCallout",
4203
+ circularArrow: "circularArrow",
4204
+ leftCircularArrow: "leftCircularArrow",
4205
+ leftRightCircularArrow: "leftRightCircularArrow",
4206
+ swooshArrow: "swooshArrow",
4207
+ mathPlus: "mathPlus",
4208
+ mathMinus: "mathMinus",
4209
+ mathMultiply: "mathMultiply",
4210
+ mathDivide: "mathDivide",
4211
+ mathEqual: "mathEqual",
4212
+ mathNotEqual: "mathNotEqual",
4213
+ flowChartProcess: "flowChartProcess",
4214
+ flowChartAlternateProcess: "flowChartAlternateProcess",
4215
+ flowChartDecision: "flowChartDecision",
4216
+ flowChartInputOutput: "flowChartInputOutput",
4217
+ flowChartPredefinedProcess: "flowChartPredefinedProcess",
4218
+ flowChartInternalStorage: "flowChartInternalStorage",
4219
+ flowChartDocument: "flowChartDocument",
4220
+ flowChartMultidocument: "flowChartMultidocument",
4221
+ flowChartTerminator: "flowChartTerminator",
4222
+ flowChartPreparation: "flowChartPreparation",
4223
+ flowChartManualInput: "flowChartManualInput",
4224
+ flowChartManualOperation: "flowChartManualOperation",
4225
+ flowChartConnector: "flowChartConnector",
4226
+ flowChartOffpageConnector: "flowChartOffpageConnector",
4227
+ flowChartPunchedCard: "flowChartPunchedCard",
4228
+ flowChartPunchedTape: "flowChartPunchedTape",
4229
+ flowChartSummingJunction: "flowChartSummingJunction",
4230
+ flowChartOr: "flowChartOr",
4231
+ flowChartCollate: "flowChartCollate",
4232
+ flowChartSort: "flowChartSort",
4233
+ flowChartExtract: "flowChartExtract",
4234
+ flowChartMerge: "flowChartMerge",
4235
+ flowChartOfflineStorage: "flowChartOfflineStorage",
4236
+ flowChartOnlineStorage: "flowChartOnlineStorage",
4237
+ flowChartDelay: "flowChartDelay",
4238
+ flowChartMagneticTape: "flowChartMagneticTape",
4239
+ flowChartMagneticDisk: "flowChartMagneticDisk",
4240
+ flowChartMagneticDrum: "flowChartMagneticDrum",
4241
+ flowChartDisplay: "flowChartDisplay",
4242
+ irregularSeal1: "explosion12",
4243
+ irregularSeal2: "explosion14",
4244
+ star4: "star4",
4245
+ star5: "star5",
4246
+ star6: "star6",
4247
+ star7: "star7",
4248
+ star8: "star8",
4249
+ star10: "star10",
4250
+ star12: "star12",
4251
+ star16: "star16",
4252
+ star24: "star24",
4253
+ star32: "star32",
4254
+ ribbon2: "ribbonUp",
4255
+ ribbon: "ribbonDown",
4256
+ ellipseRibbon2: "curvedRibbonUp",
4257
+ ellipseRibbon: "curvedRibbonDown",
4258
+ leftRightRibbon: "leftRightRibbon",
4259
+ verticalScroll: "verticalScroll",
4260
+ horizontalScroll: "horizontalScroll",
4261
+ wave: "wave",
4262
+ doubleWave: "doubleWave",
4263
+ wedgeRectCallout: "rectangularCallout",
4264
+ wedgeRoundRectCallout: "roundedRectangularCallout",
4265
+ wedgeEllipseCallout: "ellipticalCallout",
4266
+ cloudCallout: "cloudCallout",
4267
+ borderCallout1: "lineCallout",
4268
+ borderCallout2: "bentLineCallout",
4269
+ borderCallout3: "doubleBentLineCallout",
4270
+ accentCallout1: "lineCalloutWithAccentBar",
4271
+ accentCallout2: "bentLineCalloutWithAccentBar",
4272
+ accentCallout3: "doubleBentLineCalloutWithAccentBar",
4273
+ callout1: "lineCalloutWithNoBorder",
4274
+ callout2: "bentLineCalloutWithNoBorder",
4275
+ callout3: "doubleBentLineCalloutWithNoBorder",
4276
+ accentBorderCallout1: "lineCalloutWithBorderAndAccentBar",
4277
+ accentBorderCallout2: "bentLineCalloutWithBorderAndAccentBar",
4278
+ accentBorderCallout3: "doubleBentLineCalloutWithBorderAndAccentBar",
4279
+ actionButtonBlank: "actionButtonBlank",
4280
+ actionButtonHome: "actionButtonHome",
4281
+ actionButtonHelp: "actionButtonHelp",
4282
+ actionButtonInformation: "actionButtonInformation",
4283
+ actionButtonForwardNext: "actionButtonForwardNext",
4284
+ actionButtonBackPrevious: "actionButtonBackPrevious",
4285
+ actionButtonEnd: "actionButtonEnd",
4286
+ actionButtonBeginning: "actionButtonBeginning",
4287
+ actionButtonReturn: "actionButtonReturn",
4288
+ actionButtonDocument: "actionButtonDocument",
4289
+ actionButtonSound: "actionButtonSound",
4290
+ actionButtonMovie: "actionButtonMovie",
4291
+ gear6: "gear6",
4292
+ gear9: "gear9",
4293
+ funnel: "funnel",
4294
+ cornerTabs: "cornerTabs",
4295
+ squareTabs: "squareTabs",
4296
+ plaqueTabs: "plaqueTabs",
4297
+ chartX: "chartX",
4298
+ chartStar: "chartStar",
4299
+ chartPlus: "chartPlus"
4300
+ };
4301
+ OOXML_DASH_TO_DOCX = {
4302
+ solid: "solid",
4303
+ dot: "dot",
4304
+ dash: "dash",
4305
+ lgDash: "longDash",
4306
+ dashDot: "dashDot",
4307
+ lgDashDot: "longDashDot",
4308
+ lgDashDotDot: "longDashDotDot",
4309
+ sysDash: "shortDash",
4310
+ sysDot: "shortDot",
4311
+ sysDashDot: "shortDashDot",
4312
+ sysDashDotDot: "shortDashDotDot"
4313
+ };
4314
+ }
4315
+ });
4316
+
4317
+ // src/renderers/docxjs/drawingGroup.ts
4318
+ function chooseMarkers(ir) {
4319
+ const strings = [];
4320
+ collectStrings(ir, strings, /* @__PURE__ */ new WeakSet());
4321
+ const unused = (base) => {
4322
+ for (let n = 0; ; n += 1) {
4323
+ const candidate = n === 0 ? base : `${base}-${n}`;
4324
+ if (!strings.some((value) => value.includes(candidate))) return candidate;
4325
+ }
4326
+ };
4327
+ return {
4328
+ noAltText: unused(DEFAULT_MARKERS.noAltText),
4329
+ textBox: unused(DEFAULT_MARKERS.textBox)
4330
+ };
4331
+ }
4332
+ function collectStrings(node, out, seen) {
4333
+ if (typeof node === "string") {
4334
+ out.push(node);
4335
+ return;
4336
+ }
4337
+ if (!node || typeof node !== "object") return;
4338
+ if (node instanceof ArrayBuffer || ArrayBuffer.isView(node)) return;
4339
+ if (seen.has(node)) return;
4340
+ seen.add(node);
4341
+ if (node instanceof Map) {
4342
+ for (const [key, value] of node) {
4343
+ collectStrings(key, out, seen);
4344
+ collectStrings(value, out, seen);
4345
+ }
4346
+ return;
4347
+ }
4348
+ if (node instanceof Set) {
4349
+ for (const value of node) collectStrings(value, out, seen);
4350
+ return;
4351
+ }
4352
+ for (const value of Object.values(node)) collectStrings(value, out, seen);
4353
+ }
4354
+ function createDrawingGroupFactory(shapes2, pictures, markers) {
4355
+ return (group, resources) => new shapes2.ShapeGroupRun(
4356
+ drawingGroupOptions(group, resources, pictures, markers)
4357
+ );
4358
+ }
4359
+ function drawingGroupOptions(group, resources, pictures, markers = DEFAULT_MARKERS) {
4360
+ const canvasWidth = group.canvasWidthEmu;
4361
+ const canvasHeight = group.canvasHeightEmu;
4362
+ const near = (value, edge) => Math.abs(value - edge) <= OVERFLOW_TOLERANCE_EMU;
4363
+ const bounds = unrotatedBounds(group.children);
4364
+ const fillsCanvas = bounds !== void 0 && near(bounds.left, 0) && near(bounds.top, 0) && near(bounds.right, canvasWidth) && near(bounds.bottom, canvasHeight);
4365
+ const union = {
4366
+ left: Math.min(0, bounds?.left ?? 0),
4367
+ top: Math.min(0, bounds?.top ?? 0),
4368
+ right: Math.max(canvasWidth, bounds?.right ?? 0),
4369
+ bottom: Math.max(canvasHeight, bounds?.bottom ?? 0)
4370
+ };
4371
+ const overflows = union.left < -OVERFLOW_TOLERANCE_EMU || union.top < -OVERFLOW_TOLERANCE_EMU || union.right > canvasWidth + OVERFLOW_TOLERANCE_EMU || union.bottom > canvasHeight + OVERFLOW_TOLERANCE_EMU;
4372
+ const widthEmu = overflows ? (union.right - union.left) * group.widthEmu / canvasWidth : group.widthEmu;
4373
+ const heightEmu = overflows ? (union.bottom - union.top) * group.heightEmu / canvasHeight : group.heightEmu;
4374
+ return {
4375
+ children: [
4376
+ ...fillsCanvas ? [] : [canvasPad(canvasWidth, canvasHeight)],
4377
+ ...group.children.map(
4378
+ (child, index) => groupChild(child, index, resources, pictures, markers)
4379
+ )
4380
+ ],
4381
+ transformation: {
4382
+ width: emuToPixels(widthEmu),
4383
+ height: emuToPixels(heightEmu)
4384
+ },
4385
+ // `||`, not `??`: docx treats '' as no description too, and would then
4386
+ // describe the diagram from the child names.
4387
+ altText: { name: "", description: group.altText || markers.noAltText },
4388
+ ...group.floating ? { floating: floatingOptions(group.floating) } : {}
4389
+ };
4390
+ }
4391
+ function groupChild(child, index, resources, pictures, markers) {
4392
+ return child.kind === "picture" ? pictureChild(child, index, pictures) : shapeChild(child, index, resources, markers);
4393
+ }
4394
+ function canvasPad(canvasWidthEmu, canvasHeightEmu) {
4395
+ return {
4396
+ type: "rectangle",
4397
+ transformation: {
4398
+ offset: { left: 0, top: 0 },
4399
+ width: emuToPixels(canvasWidthEmu),
4400
+ height: emuToPixels(canvasHeightEmu)
4401
+ },
4402
+ fill: "none",
4403
+ line: "none",
4404
+ decorative: true,
4405
+ altText: { name: "Canvas" }
4406
+ };
4407
+ }
4408
+ function childTransformation(frame) {
4409
+ const rotation2 = frame.rotationDegrees;
4410
+ return {
4411
+ offset: { left: emuToPixels(frame.xEmu), top: emuToPixels(frame.yEmu) },
4412
+ width: emuToPixels(frame.widthEmu),
4413
+ height: emuToPixels(frame.heightEmu),
4414
+ ...rotation2 !== void 0 && rotation2 % 360 !== 0 ? { rotation: rotation2 } : {},
4415
+ ...frame.flipHorizontal || frame.flipVertical ? {
4416
+ flip: {
4417
+ ...frame.flipHorizontal ? { horizontal: true } : {},
4418
+ ...frame.flipVertical ? { vertical: true } : {}
4419
+ }
4420
+ } : {}
4421
+ };
4422
+ }
4423
+ function shapeChild(shape, index, resources, markers) {
4424
+ const paragraphs = shape.text?.paragraphs ?? [];
4425
+ return {
4426
+ type: docxPresetShape(shape.geometry),
4427
+ transformation: childTransformation(shape.frame),
4428
+ fill: shapeFill(shape.fill),
4429
+ line: shapeLine(shape.outline),
4430
+ altText: {
4431
+ name: shape.name ?? `Shape ${index + 1}`,
4432
+ ...shape.isTextBox ? { title: markers.textBox } : {}
4433
+ },
4434
+ // Never `children: []`: docx would write an empty `w:txbxContent`, which
4435
+ // the schema does not allow.
4436
+ ...paragraphs.length > 0 ? {
4437
+ children: paragraphs.map(
4438
+ (paragraph2) => emitParagraph(paragraph2, resources)
4439
+ )
4440
+ } : {},
4441
+ ...shape.text ? { textOptions: textOptions(shape.text) } : {}
4442
+ };
4443
+ }
4444
+ function pictureChild(picture2, index, pictures) {
4445
+ const crop = picture2.crop;
4446
+ return {
4447
+ type: "picture",
4448
+ image: pictures(picture2.resourceId, {
4449
+ widthEmu: picture2.frame.widthEmu,
4450
+ heightEmu: picture2.frame.heightEmu
4451
+ }),
4452
+ transformation: childTransformation(picture2.frame),
4453
+ ...crop ? {
4454
+ crop: {
4455
+ ...crop.left !== void 0 ? { left: crop.left * 100 } : {},
4456
+ ...crop.top !== void 0 ? { top: crop.top * 100 } : {},
4457
+ ...crop.right !== void 0 ? { right: crop.right * 100 } : {},
4458
+ ...crop.bottom !== void 0 ? { bottom: crop.bottom * 100 } : {}
4459
+ }
4460
+ } : {},
4461
+ altText: {
4462
+ name: picture2.name ?? `Picture ${index + 1}`,
4463
+ ...picture2.altText ? { description: picture2.altText } : {}
4464
+ }
4465
+ };
4466
+ }
4467
+ function shapeFill(fill) {
4468
+ if (!fill || fill.kind === "none") return "none";
4469
+ return {
4470
+ type: "solid",
4471
+ color: fill.color.hex,
4472
+ ...fill.transparencyPercent !== void 0 ? { transparency: clamp(fill.transparencyPercent, 0, 100) } : {}
4473
+ };
4474
+ }
4475
+ function shapeLine(outline) {
4476
+ if (!outline || !outline.color) return "none";
4477
+ return {
4478
+ color: outline.color.hex,
4479
+ // An omitted width is office-open's omitted `w`, a hairline, where docx's
4480
+ // own default would be 1pt.
4481
+ width: outline.widthEmu !== void 0 ? clamp(outline.widthEmu / EMU_PER_POINT, 0, MAX_POINTS) : 0,
4482
+ ...outline.dash ? { dash: docxLineDash(outline.dash) } : {}
4483
+ };
4484
+ }
4485
+ function textOptions(text) {
4486
+ const insets = text.insetsEmu;
4487
+ const points = (emu) => clamp(emu / EMU_PER_POINT, 0, MAX_POINTS);
4488
+ const margins = insets ? {
4489
+ ...insets.top !== void 0 ? { top: points(insets.top) } : {},
4490
+ ...insets.right !== void 0 ? { right: points(insets.right) } : {},
4491
+ ...insets.bottom !== void 0 ? { bottom: points(insets.bottom) } : {},
4492
+ ...insets.left !== void 0 ? { left: points(insets.left) } : {}
4493
+ } : void 0;
4494
+ return {
4495
+ verticalAlignment: VERTICAL_ALIGNMENT[text.anchor ?? "top"],
4496
+ ...margins && Object.keys(margins).length > 0 ? { margins } : {}
4497
+ };
4498
+ }
4499
+ function unrotatedBounds(children) {
4500
+ if (children.length === 0) return void 0;
4501
+ let left = Infinity;
4502
+ let top = Infinity;
4503
+ let right = -Infinity;
4504
+ let bottom = -Infinity;
4505
+ for (const { frame } of children) {
4506
+ left = Math.min(left, frame.xEmu);
4507
+ top = Math.min(top, frame.yEmu);
4508
+ right = Math.max(right, frame.xEmu + frame.widthEmu);
4509
+ bottom = Math.max(bottom, frame.yEmu + frame.heightEmu);
4510
+ }
4511
+ return { left, top, right, bottom };
4512
+ }
4513
+ function clamp(value, low, high) {
4514
+ return Math.min(high, Math.max(low, value));
4515
+ }
4516
+ var DEFAULT_MARKERS, EMU_PER_POINT, MAX_POINTS, OVERFLOW_TOLERANCE_EMU, VERTICAL_ALIGNMENT;
4517
+ var init_drawingGroup = __esm({
4518
+ "src/renderers/docxjs/drawingGroup.ts"() {
4519
+ "use strict";
4520
+ init_units();
4521
+ init_emit();
4522
+ init_presetShapes();
4523
+ DEFAULT_MARKERS = {
4524
+ noAltText: "jto:no-alt-text",
4525
+ textBox: "jto:text-box"
4526
+ };
4527
+ EMU_PER_POINT = 12700;
4528
+ MAX_POINTS = 1584;
4529
+ OVERFLOW_TOLERANCE_EMU = 2;
4530
+ VERTICAL_ALIGNMENT = {
4531
+ top: "top",
4532
+ middle: "center",
4533
+ bottom: "bottom"
4534
+ };
4535
+ }
4536
+ });
4537
+
4538
+ // src/renderers/docxjs/drawingGroupRepair.ts
4539
+ import AdmZip3 from "adm-zip";
4540
+ function repairDrawingGroupsInBuffer(buffer, markers) {
4541
+ const textBox = new RegExp(
4542
+ `(<wps:cNvPr\\b[^>]*?) title="${escapeRegExp2(markers.textBox)}"(\\s*/>)<wps:cNvSpPr/>`,
4543
+ "g"
4544
+ );
4545
+ const noAltText = new RegExp(
4546
+ `(<wp:docPr\\b[^>]*?) descr="${escapeRegExp2(markers.noAltText)}"`,
4547
+ "g"
4548
+ );
4549
+ const zip = new AdmZip3(buffer);
4550
+ const parts = zip.getEntries().filter((entry) => DRAWING_PART.test(entry.entryName)).sort((a, b) => compareDrawingParts(a.entryName, b.entryName));
4551
+ const texts = new Map(
4552
+ parts.map((entry) => [entry, entry.getData().toString("utf8")])
4553
+ );
4554
+ let highest = 0;
4555
+ for (const xml of texts.values()) {
4556
+ for (const match of xml.matchAll(DOC_PR_ID)) {
4557
+ highest = Math.max(highest, Number(match[1]));
4558
+ }
4559
+ for (const match of xml.replace(GROUP, "").matchAll(CNVPR_ID)) {
4560
+ highest = Math.max(highest, Number(match[1]));
4561
+ }
4562
+ }
4563
+ let next = highest + 1;
4564
+ for (const [entry, xml] of texts) {
4565
+ if (!xml.includes("<wpg:wgp")) continue;
4566
+ const repaired = xml.replace(
4567
+ GROUP,
4568
+ (span) => span.replace(CHILD_ID, (_match, head) => `${head}id="${next++}"`).replace(
4569
+ ROTATION,
4570
+ (_match, head, value) => `${head}rot="${Math.round(Number(value))}"`
4571
+ ).replace(textBox, '$1$2<wps:cNvSpPr txBox="1"/>')
4572
+ ).replace(noAltText, "$1");
4573
+ texts.set(entry, repaired);
4574
+ zip.updateFile(entry, Buffer.from(repaired, "utf8"));
4575
+ }
4576
+ for (const [entry, xml] of texts) {
4577
+ if (xml.includes(markers.noAltText) || xml.includes(markers.textBox)) {
4578
+ throw new Error(
4579
+ `the drawing-group repair left a marker in ${entry.entryName}`
4580
+ );
4581
+ }
4582
+ }
4583
+ return zip.toBuffer();
4584
+ }
4585
+ var GROUP, DOC_PR_ID, CNVPR_ID, CHILD_ID, ROTATION, escapeRegExp2;
4586
+ var init_drawingGroupRepair = __esm({
4587
+ "src/renderers/docxjs/drawingGroupRepair.ts"() {
4588
+ "use strict";
4589
+ init_fixFloatingImageIds();
4590
+ GROUP = /<wpg:wgp\b[^>]*>[\s\S]*?<\/wpg:wgp>/g;
4591
+ DOC_PR_ID = /<wp:docPr\b[^>]*?\sid="(\d+)"/g;
4592
+ CNVPR_ID = /<(?:wps|pic|wpg):cNvPr\b[^>]*?\sid="(\d+)"/g;
4593
+ CHILD_ID = /(<(?:wps|pic|wpg):cNvPr\b[^>]*?\s)id="\d+"/g;
4594
+ ROTATION = /(<a:xfrm\b[^>]*?\s)rot="([^"]*)"/g;
4595
+ escapeRegExp2 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3771
4596
  }
3772
4597
  });
3773
4598
 
@@ -3796,18 +4621,32 @@ function createDocxJsRenderer() {
3796
4621
  format: "docx",
3797
4622
  capabilities: DOCXJS_CAPABILITIES,
3798
4623
  async render(ir, renderOptions) {
3799
- const resources = await prepareImages(
4624
+ await loadDocxCharts();
4625
+ const prepared = await prepareImages(
3800
4626
  ir,
3801
4627
  renderOptions?.svgRasterFallback
3802
4628
  );
4629
+ const markers = prepared.drawingGroups > 0 ? chooseMarkers(ir) : void 0;
4630
+ let resources = prepared.images;
4631
+ if (markers) {
4632
+ await shapes.load();
4633
+ resources = Object.assign(prepared.images, {
4634
+ drawingGroup: createDrawingGroupFactory(
4635
+ shapes.get(),
4636
+ prepared.pictureSource,
4637
+ markers
4638
+ )
4639
+ });
4640
+ }
3803
4641
  const document = buildDocument(ir, resources);
3804
4642
  const packed = await Packer.toBuffer(document);
3805
4643
  const fixed = fixFloatingImageIdsInBuffer(packed);
4644
+ const repaired = markers ? repairDrawingGroupsInBuffer(fixed, markers) : fixed;
3806
4645
  if (renderOptions?.deterministic === false)
3807
- return new Uint8Array(normalizeDocxCaseBuffer(fixed));
4646
+ return new Uint8Array(normalizeDocxCaseBuffer(repaired));
3808
4647
  return new Uint8Array(
3809
4648
  canonicalizeDocxBuffer(
3810
- fixed,
4649
+ repaired,
3811
4650
  resolveGenerationDate({
3812
4651
  deterministic: renderOptions?.deterministic,
3813
4652
  generatedAt: renderOptions?.generatedAt
@@ -3820,6 +4659,7 @@ function createDocxJsRenderer() {
3820
4659
  function buildDocument(ir, resources = /* @__PURE__ */ new Map()) {
3821
4660
  return new Document({
3822
4661
  styles: emitStyles(ir.styles),
4662
+ ...ir.theme ? { theme: themeOptions(ir.theme) } : {},
3823
4663
  sections: ir.sections.map(
3824
4664
  (section2, index) => sectionOptions(section2, resources, index === ir.sections.length - 1)
3825
4665
  ),
@@ -3851,6 +4691,21 @@ function buildDocument(ir, resources = /* @__PURE__ */ new Map()) {
3851
4691
  } : {}
3852
4692
  });
3853
4693
  }
4694
+ function themeOptions(theme) {
4695
+ const colors = {};
4696
+ for (const [slot, color] of Object.entries(theme.colors)) {
4697
+ if (color) colors[slot] = color.hex;
4698
+ }
4699
+ const fonts = {
4700
+ ...theme.headingFont ? { headings: theme.headingFont } : {},
4701
+ ...theme.bodyFont ? { body: theme.bodyFont } : {}
4702
+ };
4703
+ return {
4704
+ name: theme.name,
4705
+ ...Object.keys(colors).length > 0 ? { colors } : {},
4706
+ ...Object.keys(fonts).length > 0 ? { fonts } : {}
4707
+ };
4708
+ }
3854
4709
  function noteBodies(notes, resources) {
3855
4710
  const bodies = {};
3856
4711
  for (const note of notes) {
@@ -3888,7 +4743,7 @@ function numberingConfig(numbering) {
3888
4743
  };
3889
4744
  }
3890
4745
  async function prepareImages(ir, svgRasterFallback) {
3891
- const placements = collectImagePlacements(ir);
4746
+ const { placements, drawingGroups } = collectImagePlacements(ir);
3892
4747
  const jobs = [];
3893
4748
  for (const resource of ir.resources) {
3894
4749
  if (resource.kind !== "image" || resource.mediaType !== "svg") continue;
@@ -3936,16 +4791,55 @@ async function prepareImages(ir, svgRasterFallback) {
3936
4791
  });
3937
4792
  });
3938
4793
  }
3939
- return resources;
4794
+ const pictureSource = (resourceId, placement) => {
4795
+ const resource = ir.resources.find(
4796
+ (candidate) => candidate.kind === "image" && candidate.id === resourceId
4797
+ );
4798
+ if (!resource) {
4799
+ throw new Error(`no image was prepared for resource "${resourceId}"`);
4800
+ }
4801
+ const data = Buffer.from(resource.bytes);
4802
+ if (resource.mediaType !== "svg") {
4803
+ return {
4804
+ type: resource.mediaType,
4805
+ data
4806
+ };
4807
+ }
4808
+ const size = `${emuToPixels(placement.widthEmu)}x${emuToPixels(placement.heightEmu)}`;
4809
+ return {
4810
+ type: "svg",
4811
+ data,
4812
+ fallback: {
4813
+ type: "png",
4814
+ data: rasters.get(`${resourceId}:${size}`) ?? data
4815
+ }
4816
+ };
4817
+ };
4818
+ return { images: resources, pictureSource, drawingGroups };
3940
4819
  }
3941
4820
  function collectImagePlacements(ir) {
3942
4821
  const placements = /* @__PURE__ */ new Map();
4822
+ let drawingGroups = 0;
4823
+ const record = (resourceId, widthEmu, heightEmu) => {
4824
+ const key = `${emuToPixels(widthEmu)}x${emuToPixels(heightEmu)}`;
4825
+ const sizes = placements.get(resourceId) ?? /* @__PURE__ */ new Set();
4826
+ sizes.add(key);
4827
+ placements.set(resourceId, sizes);
4828
+ };
3943
4829
  const visitInline = (inline) => {
3944
4830
  if (inline.kind === "image") {
3945
- const key = `${emuToPixels(inline.widthEmu)}x${emuToPixels(inline.heightEmu)}`;
3946
- const sizes = placements.get(inline.resourceId) ?? /* @__PURE__ */ new Set();
3947
- sizes.add(key);
3948
- placements.set(inline.resourceId, sizes);
4831
+ record(inline.resourceId, inline.widthEmu, inline.heightEmu);
4832
+ return;
4833
+ }
4834
+ if (inline.kind === "drawingGroup") {
4835
+ drawingGroups += 1;
4836
+ for (const child of inline.children) {
4837
+ if (child.kind === "picture") {
4838
+ record(child.resourceId, child.frame.widthEmu, child.frame.heightEmu);
4839
+ } else {
4840
+ child.text?.paragraphs.forEach(visitBlock);
4841
+ }
4842
+ }
3949
4843
  return;
3950
4844
  }
3951
4845
  if (inline.kind === "hyperlink" || inline.kind === "revision") {
@@ -3976,7 +4870,7 @@ function collectImagePlacements(ir) {
3976
4870
  for (const note of [...ir.footnotes, ...ir.endnotes]) {
3977
4871
  note.children.forEach(visitBlock);
3978
4872
  }
3979
- return placements;
4873
+ return { placements, drawingGroups };
3980
4874
  }
3981
4875
  function sectionOptions(section2, resources, closesDocument = false) {
3982
4876
  const { page, columns } = section2.properties;
@@ -4089,17 +4983,26 @@ function coreProperties(ir) {
4089
4983
  ...metadata.custom?.length ? { customProperties: metadata.custom } : {}
4090
4984
  };
4091
4985
  }
4092
- var DOCXJS_RENDERER_ID, DOCXJS_CAPABILITIES;
4986
+ var DOCXJS_RENDERER_ID, shapes, DOCXJS_CAPABILITIES;
4093
4987
  var init_docxjs = __esm({
4094
4988
  "src/renderers/docxjs/index.ts"() {
4095
4989
  "use strict";
4096
4990
  init_styles2();
4991
+ init_charts();
4097
4992
  init_imageUtils();
4098
4993
  init_packageDocument();
4099
4994
  init_fixFloatingImageIds();
4100
4995
  init_emit();
4996
+ init_docxSubpath();
4997
+ init_drawingGroup();
4998
+ init_drawingGroupRepair();
4101
4999
  init_units();
4102
5000
  DOCXJS_RENDERER_ID = "docxjs";
5001
+ shapes = docxSubpath(
5002
+ "docx/shapes",
5003
+ "visual",
5004
+ () => import("docx/shapes")
5005
+ );
4103
5006
  DOCXJS_CAPABILITIES = /* @__PURE__ */ new Set([
4104
5007
  "paragraphs",
4105
5008
  "styles",
@@ -4112,8 +5015,10 @@ var init_docxjs = __esm({
4112
5015
  "images",
4113
5016
  "floating-images",
4114
5017
  "svg-images",
5018
+ "charts",
4115
5019
  "text-frames",
4116
5020
  "text-boxes",
5021
+ "drawing-groups",
4117
5022
  "toc",
4118
5023
  "cached-toc",
4119
5024
  "fields",
@@ -4134,124 +5039,332 @@ var init_docxjs = __esm({
4134
5039
  }
4135
5040
  });
4136
5041
 
4137
- // src/utils/chartWorkbook.ts
4138
- import AdmZip3 from "adm-zip";
4139
- import { chartWorkbookParts } from "@json-to-office/shared/rendering";
4140
- import {
4141
- CHART_WORKBOOK_SHEET_NAME,
4142
- categoryReference,
4143
- columnLetter,
4144
- seriesNameReference,
4145
- seriesValueReference
4146
- } from "@json-to-office/shared/rendering";
4147
- function buildChartWorkbook(series) {
4148
- const zip = new AdmZip3();
4149
- for (const [name, content] of chartWorkbookParts(series)) {
4150
- zip.addFile(name, Buffer.from(content, "utf8"));
5042
+ // src/renderers/office-open/chartLook.ts
5043
+ function seriesLook(chartType) {
5044
+ switch (chartType) {
5045
+ case "line":
5046
+ case "scatter":
5047
+ return { marker: MARKER, smooth: false };
5048
+ case "radar":
5049
+ return { marker: MARKER };
5050
+ case "column":
5051
+ case "bar":
5052
+ return { invertIfNegative: false };
5053
+ default:
5054
+ return {};
4151
5055
  }
4152
- for (const entry of zip.getEntries()) {
4153
- entry.header.timeval = WORKBOOK_TIMESTAMP;
5056
+ }
5057
+ function axesLook(chartType) {
5058
+ switch (chartType) {
5059
+ case "column":
5060
+ case "bar":
5061
+ case "line":
5062
+ case "area": {
5063
+ const horizontal = chartType === "bar";
5064
+ return {
5065
+ categoryAxis: {
5066
+ position: horizontal ? "l" : "b",
5067
+ ...NO_TICKS,
5068
+ line: AXIS_LINE
5069
+ },
5070
+ valueAxis: {
5071
+ position: horizontal ? "b" : "l",
5072
+ gridLine: GRIDLINES,
5073
+ ...NO_TICKS,
5074
+ lineVisible: false,
5075
+ crossBetween: chartType === "area" ? "midCat" : "between"
5076
+ }
5077
+ };
5078
+ }
5079
+ case "radar":
5080
+ return {
5081
+ categoryAxis: {
5082
+ position: "b",
5083
+ gridLine: GRIDLINES,
5084
+ ...NO_TICKS,
5085
+ majorTickMark: "cross",
5086
+ line: AXIS_LINE
5087
+ },
5088
+ valueAxis: {
5089
+ position: "l",
5090
+ gridLine: GRIDLINES,
5091
+ ...NO_TICKS,
5092
+ majorTickMark: "cross",
5093
+ line: AXIS_LINE,
5094
+ crossBetween: "between"
5095
+ }
5096
+ };
5097
+ case "scatter": {
5098
+ const line = { ...AXIS_LINE, color: SCATTER_AXIS_TINT };
5099
+ return {
5100
+ categoryAxis: {
5101
+ position: "b",
5102
+ ...NO_TICKS,
5103
+ line,
5104
+ crossBetween: "midCat"
5105
+ },
5106
+ valueAxis: {
5107
+ position: "l",
5108
+ gridLine: GRIDLINES,
5109
+ ...NO_TICKS,
5110
+ line,
5111
+ crossBetween: "midCat"
5112
+ }
5113
+ };
5114
+ }
5115
+ default:
5116
+ return {};
4154
5117
  }
4155
- return new Uint8Array(zip.toBuffer());
4156
5118
  }
4157
- var WORKBOOK_TIMESTAMP;
4158
- var init_chartWorkbook = __esm({
4159
- "src/utils/chartWorkbook.ts"() {
5119
+ function plotLook(chart) {
5120
+ const common = {
5121
+ ...axesLook(chart.chartType),
5122
+ autoTitleDeleted: !(chart.title && chart.showTitle !== false),
5123
+ plotAreaUnfilled: true
5124
+ };
5125
+ switch (chart.chartType) {
5126
+ case "column":
5127
+ return { ...common, dataBorder: "none", gapWidth: 219, overlap: -27 };
5128
+ case "bar":
5129
+ return { ...common, dataBorder: "none", gapWidth: 182, overlap: 0 };
5130
+ case "area":
5131
+ return { ...common, dataBorder: "none" };
5132
+ case "line":
5133
+ return { ...common, ...LINE_SERIES, lineMarkers: true };
5134
+ case "radar":
5135
+ return { ...common, ...LINE_SERIES, radarStyle: "marker" };
5136
+ case "scatter": {
5137
+ const labels = chart.series[0]?.labels ?? [];
5138
+ return {
5139
+ ...common,
5140
+ ...LINE_SERIES,
5141
+ lineWidthPoints: 1.5,
5142
+ scatterStyle: "lineMarker",
5143
+ scatterXValues: labels.map((_, index) => scatterX(labels, index))
5144
+ };
5145
+ }
5146
+ case "pie":
5147
+ case "doughnut":
5148
+ return {
5149
+ ...common,
5150
+ dataBorder: { widthPoints: 1.5, color: { scheme: "lt1" } },
5151
+ firstSliceAngle: 0,
5152
+ ...chart.chartType === "doughnut" ? { holeSize: 50 } : {}
5153
+ };
5154
+ default:
5155
+ return common;
5156
+ }
5157
+ }
5158
+ var TEXT_1_TINT, SCATTER_AXIS_TINT, HAIRLINE_POINTS, GRIDLINES, AXIS_LINE, NO_TICKS, LINE_SERIES, MARKER;
5159
+ var init_chartLook = __esm({
5160
+ "src/renderers/office-open/chartLook.ts"() {
4160
5161
  "use strict";
4161
- init_packageDocument();
4162
- WORKBOOK_TIMESTAMP = toDosTime(DEFAULT_GENERATION_DATE);
5162
+ init_chartValues();
5163
+ TEXT_1_TINT = { scheme: "tx1", lumMod: 15e3, lumOff: 85e3 };
5164
+ SCATTER_AXIS_TINT = {
5165
+ scheme: "tx1",
5166
+ lumMod: 25e3,
5167
+ lumOff: 75e3
5168
+ };
5169
+ HAIRLINE_POINTS = 0.75;
5170
+ GRIDLINES = {
5171
+ size: HAIRLINE_POINTS,
5172
+ color: TEXT_1_TINT
5173
+ };
5174
+ AXIS_LINE = {
5175
+ widthPoints: HAIRLINE_POINTS,
5176
+ color: TEXT_1_TINT
5177
+ };
5178
+ NO_TICKS = {
5179
+ majorTickMark: "none",
5180
+ minorTickMark: "none",
5181
+ tickLabelPosition: "nextTo"
5182
+ };
5183
+ LINE_SERIES = {
5184
+ lineWidthPoints: 2.25,
5185
+ lineCap: "rnd",
5186
+ lineJoin: "round",
5187
+ markerLineWidthPoints: HAIRLINE_POINTS
5188
+ };
5189
+ MARKER = { symbol: "circle", size: 5 };
4163
5190
  }
4164
5191
  });
4165
5192
 
4166
5193
  // src/renderers/office-open/chartParts.ts
4167
5194
  import {
4168
- CHART_WORKBOOK_CONTENT_TYPE,
4169
- chartWorkbookRelsXml,
4170
- matchChartParts,
4171
- spliceChartXml
5195
+ finishChartXml,
5196
+ matchChartParts
4172
5197
  } from "@json-to-office/shared/rendering";
4173
- function spliceInput(chart) {
4174
- const { bold, ...rest } = chart.textFont;
4175
- const textFont = rest;
4176
- const axisTitleFont = { ...rest, bold };
4177
- const chartTitleFont = {
4178
- ...rest,
4179
- fontSize: Math.round(rest.fontSize * CHART_TITLE_SCALE),
4180
- bold: true
5198
+ function chartInput(chart) {
5199
+ const roles = chartTextRoles(chart.textFont);
5200
+ const textFont = roles.text;
5201
+ const axisTitleFont = roles.axisTitle;
5202
+ const chartTitleFont = roles.title;
5203
+ const look = plotLook(chart);
5204
+ const categoryAxis = {
5205
+ ...look.categoryAxis,
5206
+ ...chart.categoryAxisTitle ? { title: chart.categoryAxisTitle, titleFont: axisTitleFont } : {}
5207
+ };
5208
+ const valueAxis = {
5209
+ ...look.valueAxis,
5210
+ ...chart.valueAxisTitle ? { title: chart.valueAxisTitle, titleFont: axisTitleFont } : {}
4181
5211
  };
4182
5212
  return {
5213
+ ...look,
4183
5214
  chartType: chart.chartType,
4184
5215
  series: chart.series,
4185
5216
  colors: chart.colors,
5217
+ ...chart.title && chart.showTitle !== false ? { title: chart.title } : {},
4186
5218
  textFont,
4187
5219
  titleFont: chartTitleFont,
4188
5220
  ...chart.legendPosition ? { legendPosition: chart.legendPosition } : {},
4189
- ...chart.categoryAxisTitle ? {
4190
- categoryAxis: {
4191
- title: chart.categoryAxisTitle,
4192
- titleFont: axisTitleFont
4193
- }
4194
- } : {},
4195
- ...chart.valueAxisTitle ? {
4196
- valueAxis: { title: chart.valueAxisTitle, titleFont: axisTitleFont }
4197
- } : {}
5221
+ ...Object.keys(categoryAxis).length > 0 ? { categoryAxis } : {},
5222
+ ...Object.keys(valueAxis).length > 0 ? { valueAxis } : {}
4198
5223
  };
4199
5224
  }
4200
- function declareWorkbookContentType(zip) {
4201
- const entry = zip.getEntry("[Content_Types].xml");
4202
- if (!entry) return;
4203
- const xml = entry.getData().toString("utf8");
4204
- if (xml.includes(`Extension="xlsx"`)) return;
4205
- zip.updateFile(
4206
- entry,
4207
- Buffer.from(
4208
- xml.replace(
4209
- '<Default Extension="xml"',
4210
- `<Default Extension="xlsx" ContentType="${CHART_WORKBOOK_CONTENT_TYPE}"/><Default Extension="xml"`
4211
- ),
4212
- "utf8"
4213
- )
4214
- );
4215
- }
4216
- function spliceChartParts(zip, charts) {
4217
- if (charts.length === 0) return;
5225
+ function finishChartParts(zip, charts2) {
5226
+ if (charts2.length === 0) return;
4218
5227
  const parts = zip.getEntries().map((entry) => entry.entryName).map((name) => name.match(/^word\/charts\/chart(\d+)\.xml$/)).filter((match) => match !== null).map(
4219
5228
  (match) => [
4220
5229
  Number(match[1]),
4221
5230
  zip.getEntry(match[0]).getData().toString("utf8")
4222
5231
  ]
4223
5232
  );
4224
- for (const { ordinal, xml, chart } of matchChartParts(parts, charts)) {
4225
- const workbookName = `chart${ordinal}.xlsx`;
5233
+ const inputs = charts2.map(chartInput);
5234
+ for (const { ordinal, xml, chart } of matchChartParts(parts, inputs)) {
5235
+ const finished = finishChartXml(xml, chart);
5236
+ if (finished === xml) continue;
4226
5237
  zip.updateFile(
4227
5238
  zip.getEntry(`word/charts/chart${ordinal}.xml`),
4228
- Buffer.from(spliceChartXml(xml, spliceInput(chart)), "utf8")
5239
+ Buffer.from(finished, "utf8")
4229
5240
  );
4230
- zip.addFile(
4231
- `word/embeddings/${workbookName}`,
4232
- Buffer.from(buildChartWorkbook(chart.series))
5241
+ }
5242
+ }
5243
+ var init_chartParts = __esm({
5244
+ "src/renderers/office-open/chartParts.ts"() {
5245
+ "use strict";
5246
+ init_chartText();
5247
+ init_chartLook();
5248
+ }
5249
+ });
5250
+
5251
+ // src/renderers/office-open/themePart.ts
5252
+ function replaceOnce(xml, pattern, replacer, what) {
5253
+ const count = [...xml.matchAll(pattern)].length;
5254
+ if (count !== 1) {
5255
+ throw new Error(
5256
+ `office-open's theme part has ${count} ${what}, expected one`
4233
5257
  );
4234
- zip.addFile(
4235
- `word/charts/_rels/chart${ordinal}.xml.rels`,
4236
- Buffer.from(chartWorkbookRelsXml(workbookName), "utf8")
5258
+ }
5259
+ return xml.replace(pattern, replacer);
5260
+ }
5261
+ function spliceTheme(zip, theme) {
5262
+ const entry = zip.getEntry(THEME_PART);
5263
+ if (!entry) throw new Error(`office-open wrote no ${THEME_PART}`);
5264
+ let xml = entry.getData().toString("utf8");
5265
+ const name = esc(theme.name);
5266
+ xml = renamed(xml, "theme", name);
5267
+ const colors = Object.entries(theme.colors);
5268
+ if (colors.some(([, color]) => color)) {
5269
+ xml = renamed(xml, "clrScheme", name);
5270
+ for (const [slot, color] of colors) {
5271
+ if (!color) continue;
5272
+ const element = SCHEME_ELEMENT[slot];
5273
+ xml = replaceOnce(
5274
+ xml,
5275
+ new RegExp(`<a:${element}>[\\s\\S]*?</a:${element}>`, "g"),
5276
+ () => `<a:${element}><a:srgbClr val="${color.hex}"/></a:${element}>`,
5277
+ `a:${element}`
5278
+ );
5279
+ }
5280
+ }
5281
+ if (theme.headingFont || theme.bodyFont) {
5282
+ xml = renamed(xml, "fontScheme", name);
5283
+ for (const [collection, key, officeFace] of OFFICE_FACES) {
5284
+ const family = theme[key];
5285
+ if (family === void 0 || family === officeFace) continue;
5286
+ xml = replaceOnce(
5287
+ xml,
5288
+ new RegExp(`(<a:${collection}>)<a:latin\\b[^>]*/>`, "g"),
5289
+ (_match, open) => `${open}<a:latin typeface="${esc(family)}"/>`,
5290
+ `a:${collection} a:latin`
5291
+ );
5292
+ }
5293
+ }
5294
+ zip.updateFile(THEME_PART, Buffer.from(xml, "utf8"));
5295
+ }
5296
+ var THEME_PART, SCHEME_ELEMENT, OFFICE_FACES, esc, renamed;
5297
+ var init_themePart = __esm({
5298
+ "src/renderers/office-open/themePart.ts"() {
5299
+ "use strict";
5300
+ THEME_PART = "word/theme/theme1.xml";
5301
+ SCHEME_ELEMENT = {
5302
+ dark1: "dk1",
5303
+ light1: "lt1",
5304
+ dark2: "dk2",
5305
+ light2: "lt2",
5306
+ accent1: "accent1",
5307
+ accent2: "accent2",
5308
+ accent3: "accent3",
5309
+ accent4: "accent4",
5310
+ accent5: "accent5",
5311
+ accent6: "accent6",
5312
+ hyperlink: "hlink",
5313
+ followedHyperlink: "folHlink"
5314
+ };
5315
+ OFFICE_FACES = [
5316
+ ["majorFont", "headingFont", "Calibri Light"],
5317
+ ["minorFont", "bodyFont", "Calibri"]
5318
+ ];
5319
+ esc = (value) => value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5320
+ renamed = (xml, element, name) => replaceOnce(
5321
+ xml,
5322
+ new RegExp(`(<a:${element}\\b[^>]*?\\bname=")[^"]*(")`, "g"),
5323
+ (_match, open, close) => `${open}${name}${close}`,
5324
+ `a:${element} name`
4237
5325
  );
4238
5326
  }
4239
- declareWorkbookContentType(zip);
5327
+ });
5328
+
5329
+ // src/utils/chartWorkbook.ts
5330
+ import AdmZip4 from "adm-zip";
5331
+ import { chartWorkbookParts } from "@json-to-office/shared/rendering";
5332
+ import {
5333
+ CHART_WORKBOOK_SHEET_NAME,
5334
+ categoryReference,
5335
+ columnLetter,
5336
+ seriesNameReference,
5337
+ seriesValueReference
5338
+ } from "@json-to-office/shared/rendering";
5339
+ function buildChartWorkbook(series, options = {}) {
5340
+ const zip = new AdmZip4();
5341
+ for (const [name, content] of chartWorkbookParts(series, options)) {
5342
+ zip.addFile(name, Buffer.from(content, "utf8"));
5343
+ }
5344
+ for (const entry of zip.getEntries()) {
5345
+ entry.header.timeval = WORKBOOK_TIMESTAMP;
5346
+ }
5347
+ return new Uint8Array(zip.toBuffer());
4240
5348
  }
4241
- var CHART_TITLE_SCALE;
4242
- var init_chartParts = __esm({
4243
- "src/renderers/office-open/chartParts.ts"() {
5349
+ var WORKBOOK_TIMESTAMP;
5350
+ var init_chartWorkbook = __esm({
5351
+ "src/utils/chartWorkbook.ts"() {
4244
5352
  "use strict";
4245
- init_chartWorkbook();
4246
- CHART_TITLE_SCALE = 1.4;
5353
+ init_packageDocument();
5354
+ WORKBOOK_TIMESTAMP = toDosTime(DEFAULT_GENERATION_DATE);
4247
5355
  }
4248
5356
  });
4249
5357
 
4250
5358
  // src/renderers/office-open/emit.ts
4251
- import { assertNever as assertNever2 } from "@json-to-office/shared/rendering";
5359
+ import { assertNever as assertNever3 } from "@json-to-office/shared/rendering";
5360
+ import { chartLook, withSeriesLook } from "@json-to-office/shared/rendering";
4252
5361
  function emptyContext() {
4253
5362
  let next = 1;
4254
- return { pictures: /* @__PURE__ */ new Map(), nextDrawingId: () => next++, charts: [] };
5363
+ return {
5364
+ pictures: /* @__PURE__ */ new Map(),
5365
+ nextDrawingId: () => next++,
5366
+ charts: []
5367
+ };
4255
5368
  }
4256
5369
  function simpleField(instruction, cachedText) {
4257
5370
  return {
@@ -4261,6 +5374,9 @@ function simpleField(instruction, cachedText) {
4261
5374
  }
4262
5375
  };
4263
5376
  }
5377
+ function twips(value) {
5378
+ return Math.floor(value);
5379
+ }
4264
5380
  function runProperties2(formatting) {
4265
5381
  if (!formatting) return {};
4266
5382
  const out = {};
@@ -4288,16 +5404,19 @@ function runProperties2(formatting) {
4288
5404
  if (formatting.doubleStrike !== void 0) {
4289
5405
  out.doubleStrike = formatting.doubleStrike;
4290
5406
  }
4291
- if (formatting.superScript) out.superScript = true;
4292
- if (formatting.subScript) out.subScript = true;
5407
+ if (formatting.superScript) out.verticalAlign = "superscript";
5408
+ if (formatting.subScript) out.verticalAlign = "subscript";
4293
5409
  if (formatting.smallCaps !== void 0) out.smallCaps = formatting.smallCaps;
4294
5410
  if (formatting.allCaps !== void 0) out.allCaps = formatting.allCaps;
4295
- if (formatting.highlight !== void 0) out.highlight = formatting.highlight;
5411
+ if (formatting.highlight !== void 0) {
5412
+ out.highlight = formatting.highlight;
5413
+ }
4296
5414
  if (formatting.shading) out.shading = shading(formatting.shading);
4297
5415
  if (formatting.scalePercent !== void 0)
4298
5416
  out.scale = formatting.scalePercent;
4299
- if (formatting.characterSpacingTwentieths !== void 0) {
4300
- out.characterSpacing = formatting.characterSpacingTwentieths;
5417
+ const stated = formatting.characterSpacingTwentieths;
5418
+ if (stated !== void 0 && stated !== 0) {
5419
+ out.characterSpacing = twips(stated);
4301
5420
  }
4302
5421
  if (formatting.language) out.language = { value: formatting.language };
4303
5422
  if (formatting.noProof !== void 0) out.noProof = formatting.noProof;
@@ -4327,7 +5446,9 @@ function inlineChildren2(children, ctx = emptyContext()) {
4327
5446
  switch (child.kind) {
4328
5447
  case "lineBreak":
4329
5448
  pendingBreaks += 1;
4330
- if (child.clear && child.clear !== "none") pendingClear = child.clear;
5449
+ if (child.clear && child.clear !== "none") {
5450
+ pendingClear = child.clear;
5451
+ }
4331
5452
  break;
4332
5453
  case "text":
4333
5454
  out.push({
@@ -4365,7 +5486,7 @@ function inlineChildren2(children, ctx = emptyContext()) {
4365
5486
  case "drawingGroup": {
4366
5487
  const pending = breakOption();
4367
5488
  if (pending.break) out.push(pending);
4368
- out.push({ wpgGroup: drawingGroupOptions(child, ctx) });
5489
+ out.push({ wpgGroup: drawingGroupOptions2(child, ctx) });
4369
5490
  break;
4370
5491
  }
4371
5492
  case "hyperlink":
@@ -4395,8 +5516,9 @@ function inlineChildren2(children, ctx = emptyContext()) {
4395
5516
  out.push({ wpsShape: shapeOptions(child, ctx) });
4396
5517
  break;
4397
5518
  case "chart":
4398
- ctx.charts?.push(child);
4399
- out.push({ chart: chartOptions(child, ctx) });
5519
+ out.push({
5520
+ chart: chartOptions(child, ctx, ctx.charts?.push(child) ?? 1)
5521
+ });
4400
5522
  break;
4401
5523
  case "noteReference":
4402
5524
  out.push(
@@ -4404,7 +5526,7 @@ function inlineChildren2(children, ctx = emptyContext()) {
4404
5526
  );
4405
5527
  break;
4406
5528
  default:
4407
- assertNever2(child, "DocxIrInline");
5529
+ assertNever3(child, "DocxIrInline");
4408
5530
  }
4409
5531
  }
4410
5532
  return out;
@@ -4445,8 +5567,11 @@ function floatingOptions2(floating) {
4445
5567
  ...floating.vertical ? { verticalPosition: position(floating.vertical) } : {},
4446
5568
  ...floating.wrap ? {
4447
5569
  wrap: {
4448
- type: WRAP_TYPE2[floating.wrap.type],
4449
- ...floating.wrap.side ? { side: floating.wrap.side } : {}
5570
+ // Named, as OOXML names them; 0.11 took docx.js's numbers.
5571
+ type: floating.wrap.type,
5572
+ ...floating.wrap.side ? {
5573
+ side: floating.wrap.side
5574
+ } : {}
4450
5575
  }
4451
5576
  } : {},
4452
5577
  ...floating.margins ? {
@@ -4473,66 +5598,92 @@ function imageMedia(ctx, resourceId, placement) {
4473
5598
  }
4474
5599
  function pictureOptions(image, ctx) {
4475
5600
  const media = imageMedia(ctx, image.resourceId, image);
4476
- return {
4477
- type: media.type,
5601
+ const source = media.type === "svg" ? {
5602
+ type: "svg",
4478
5603
  data: media.data,
4479
- // No `fileName`: at run level the backend allocates one, and stating our
4480
- // own would only fight it.
4481
- ...media.fallback ? { fallback: media.fallback } : {},
5604
+ // The SVG slot always carries a raster: `prepareImages` falls back
5605
+ // to the vector bytes when none could be made.
5606
+ fallback: media.fallback ?? {
5607
+ type: "png",
5608
+ data: media.data
5609
+ }
5610
+ } : { type: media.type, data: media.data };
5611
+ return {
5612
+ ...source,
4482
5613
  // Raw numbers are EMUs in @office-open/docx. Passing pixels here makes a
4483
5614
  // normal image only a few hundred EMUs wide, effectively a dot.
4484
5615
  transformation: { width: image.widthEmu, height: image.heightEmu },
4485
5616
  // The id is stated rather than left to the backend's process-global
4486
- // counter. `name` stays empty, which is what it was before and what the
4487
- // backend falls back to.
4488
- altText: { id: String(ctx.nextDrawingId()) },
5617
+ // counter. `name` stays empty, which is what the backend falls back to.
5618
+ altText: { id: String(ctx.nextDrawingId()), name: "" },
4489
5619
  ...image.floating ? { floating: floatingOptions2(image.floating) } : {}
4490
5620
  // No `description` or `title`: no DOCX this pipeline has produced carries
4491
5621
  // `wp:docPr` alt text, and the compiler warns so the gap is visible rather
4492
5622
  // than silent.
4493
5623
  };
4494
5624
  }
4495
- function drawingGroupOptions(group, ctx) {
5625
+ function drawingGroupOptions2(group, ctx) {
4496
5626
  const id = ctx.nextDrawingId();
4497
5627
  return {
4498
5628
  altText: {
4499
5629
  id: String(id),
5630
+ name: "",
4500
5631
  ...group.altText ? { description: group.altText } : {}
4501
5632
  },
4502
5633
  transformation: { width: group.widthEmu, height: group.heightEmu },
4503
- childOffset: { x: 0, y: 0 },
4504
- childExtent: { cx: group.canvasWidthEmu, cy: group.canvasHeightEmu },
4505
- children: group.children.map((child) => groupChild(child, ctx)),
5634
+ childOffsetX: 0,
5635
+ childOffsetY: 0,
5636
+ childExtentWidth: group.canvasWidthEmu,
5637
+ childExtentHeight: group.canvasHeightEmu,
5638
+ children: group.children.map((child) => groupChild2(child, ctx)),
4506
5639
  ...group.floating ? { floating: floatingOptions2(group.floating) } : {}
4507
5640
  };
4508
5641
  }
4509
- function chartOptions(chart, ctx) {
5642
+ function chartOptions(chart, ctx, ordinal) {
4510
5643
  const id = ctx.nextDrawingId();
5644
+ const input = chartInput(chart);
5645
+ const look = chartLook(input);
4511
5646
  return {
4512
5647
  type: chart.chartType,
4513
5648
  categories: chart.series[0]?.labels ?? [],
4514
- series: chart.series.map((entry, index) => ({
4515
- name: entry.name ?? `Series ${index + 1}`,
4516
- values: entry.values
4517
- })),
4518
- ...chart.title && chart.showTitle !== false ? { title: chart.title } : {},
5649
+ series: chart.series.map(
5650
+ (entry, index) => withSeriesLook(
5651
+ {
5652
+ name: entry.name ?? `Series ${index + 1}`,
5653
+ values: entry.values,
5654
+ ...seriesLook(chart.chartType)
5655
+ },
5656
+ look.series[index]
5657
+ )
5658
+ ),
5659
+ ...look.chart,
4519
5660
  ...chart.showLegend !== void 0 ? { showLegend: chart.showLegend } : {},
5661
+ externalData: {
5662
+ ...look.chart.externalData,
5663
+ fileName: `chart${ordinal}.xlsx`,
5664
+ // A scatter chart's column A holds the x values its `c:xVal` caches.
5665
+ data: buildChartWorkbook(
5666
+ chart.series,
5667
+ input.scatterXValues ? { categoryValues: input.scatterXValues } : {}
5668
+ )
5669
+ },
4520
5670
  transformation: { width: chart.widthEmu, height: chart.heightEmu },
4521
5671
  altText: {
4522
5672
  id: String(id),
5673
+ name: "",
4523
5674
  ...chart.altText ? { description: chart.altText } : {}
4524
5675
  },
4525
5676
  ...chart.floating ? { floating: floatingOptions2(chart.floating) } : {}
4526
5677
  };
4527
5678
  }
4528
- function groupChild(child, ctx) {
5679
+ function groupChild2(child, ctx) {
4529
5680
  return child.kind === "shape" ? groupShape(child, ctx) : groupPicture(child, ctx);
4530
5681
  }
4531
5682
  function groupShape(shape, ctx) {
4532
5683
  const id = ctx.nextDrawingId();
4533
5684
  return {
4534
5685
  type: "wps",
4535
- transformation: childTransformation(shape.frame),
5686
+ transformation: childTransformation2(shape.frame),
4536
5687
  data: {
4537
5688
  nonVisualProperties: {
4538
5689
  id,
@@ -4541,7 +5692,7 @@ function groupShape(shape, ctx) {
4541
5692
  // to hold text, and it changes how the object behaves on selection.
4542
5693
  ...shape.isTextBox ? { textBox: "1" } : {}
4543
5694
  },
4544
- presetGeometry: { preset: shape.geometry },
5695
+ geometry: shape.geometry,
4545
5696
  ...shape.fill ? { fill: drawingFill(shape.fill) } : {},
4546
5697
  ...shape.outline ? { outline: drawingOutline(shape.outline) } : {},
4547
5698
  children: (shape.text?.paragraphs ?? []).map(
@@ -4567,7 +5718,7 @@ function groupPicture(picture2, ctx) {
4567
5718
  fileName: media.fallbackFileName
4568
5719
  }
4569
5720
  } : {},
4570
- transformation: childTransformation(picture2.frame),
5721
+ transformation: childTransformation2(picture2.frame),
4571
5722
  nonVisualProperties: {
4572
5723
  id,
4573
5724
  ...picture2.name ? { name: picture2.name } : {},
@@ -4576,11 +5727,7 @@ function groupPicture(picture2, ctx) {
4576
5727
  ...picture2.crop ? { sourceRectangle: sourceRectangle(picture2.crop) } : {}
4577
5728
  };
4578
5729
  }
4579
- function childTransformation(frame) {
4580
- const flip = {
4581
- ...frame.flipHorizontal ? { horizontal: true } : {},
4582
- ...frame.flipVertical ? { vertical: true } : {}
4583
- };
5730
+ function childTransformation2(frame) {
4584
5731
  return {
4585
5732
  offset: {
4586
5733
  emus: { x: frame.xEmu, y: frame.yEmu },
@@ -4596,7 +5743,8 @@ function childTransformation(frame) {
4596
5743
  },
4597
5744
  // Degrees: the backend multiplies by 60000 on the way into `@rot`.
4598
5745
  ...frame.rotationDegrees !== void 0 ? { rotation: frame.rotationDegrees } : {},
4599
- ...Object.keys(flip).length > 0 ? { flip } : {}
5746
+ ...frame.flipHorizontal ? { flipHorizontal: true } : {},
5747
+ ...frame.flipVertical ? { flipVertical: true } : {}
4600
5748
  };
4601
5749
  }
4602
5750
  function drawingFill(fill) {
@@ -4604,7 +5752,6 @@ function drawingFill(fill) {
4604
5752
  return {
4605
5753
  type: "solid",
4606
5754
  color: {
4607
- type: "rgb",
4608
5755
  value: fill.color.hex,
4609
5756
  // Transparency becomes an alpha transform on the colour. DrawingML
4610
5757
  // states *opacity*, so the value is the complement of what the author
@@ -4646,7 +5793,7 @@ function sourceRectangle(crop) {
4646
5793
  function shapeOptions(shape, ctx) {
4647
5794
  const id = String(ctx.nextDrawingId());
4648
5795
  return {
4649
- altText: { id },
5796
+ altText: { id, name: "" },
4650
5797
  children: shape.children.map((child) => paragraph(child, ctx)),
4651
5798
  // Raw transformation numbers are EMUs in @office-open/docx, while the IR
4652
5799
  // deliberately stores native shape dimensions in pixels.
@@ -4657,7 +5804,7 @@ function shapeOptions(shape, ctx) {
4657
5804
  ...shape.fill ? {
4658
5805
  fill: {
4659
5806
  type: "solid",
4660
- color: { type: "rgb", value: shape.fill.hex }
5807
+ color: { value: shape.fill.hex }
4661
5808
  }
4662
5809
  } : {},
4663
5810
  ...shape.outline ? { outline: drawingOutline(shape.outline) } : {},
@@ -4688,16 +5835,16 @@ function paragraphProperties2(formatting) {
4688
5835
  if (formatting.indent) {
4689
5836
  const indent = {};
4690
5837
  if (formatting.indent.leftTwips !== void 0) {
4691
- indent.left = formatting.indent.leftTwips;
5838
+ indent.left = twips(formatting.indent.leftTwips);
4692
5839
  }
4693
5840
  if (formatting.indent.rightTwips !== void 0) {
4694
- indent.right = formatting.indent.rightTwips;
5841
+ indent.right = twips(formatting.indent.rightTwips);
4695
5842
  }
4696
5843
  if (formatting.indent.firstLineTwips !== void 0) {
4697
- indent.firstLine = formatting.indent.firstLineTwips;
5844
+ indent.firstLine = twips(formatting.indent.firstLineTwips);
4698
5845
  }
4699
5846
  if (formatting.indent.hangingTwips !== void 0) {
4700
- indent.hanging = formatting.indent.hangingTwips;
5847
+ indent.hanging = twips(formatting.indent.hangingTwips);
4701
5848
  }
4702
5849
  out.indent = indent;
4703
5850
  }
@@ -4762,13 +5909,19 @@ function frameOptions2(frame) {
4762
5909
  ...frame.anchorLock !== void 0 ? { anchorLock: frame.anchorLock } : {},
4763
5910
  ...frame.rule ? { rule: frame.rule } : {}
4764
5911
  };
4765
- return frame.xTwips !== void 0 || frame.yTwips !== void 0 ? {
4766
- type: "absolute",
4767
- position: { x: frame.xTwips ?? 0, y: frame.yTwips ?? 0 },
4768
- ...base
4769
- } : {
5912
+ if (frame.xTwips !== void 0 || frame.yTwips !== void 0) {
5913
+ return {
5914
+ type: "absolute",
5915
+ position: { x: frame.xTwips ?? 0, y: frame.yTwips ?? 0 },
5916
+ ...base
5917
+ };
5918
+ }
5919
+ return {
4770
5920
  type: "alignment",
4771
- alignment: { x: frame.xAlign, y: frame.yAlign },
5921
+ alignment: {
5922
+ x: frame.xAlign,
5923
+ y: frame.yAlign
5924
+ },
4772
5925
  ...base
4773
5926
  };
4774
5927
  }
@@ -4789,7 +5942,7 @@ function block(value, ctx = emptyContext()) {
4789
5942
  case "toc":
4790
5943
  return { toc: tableOfContents(value) };
4791
5944
  default:
4792
- return assertNever2(value, "DocxIrBlock");
5945
+ return assertNever3(value, "DocxIrBlock");
4793
5946
  }
4794
5947
  }
4795
5948
  function tocEntry(entry) {
@@ -4808,6 +5961,7 @@ function tableOfContents(value) {
4808
5961
  headingStyleRange: `${value.headingRange.from}-${value.headingRange.to}`
4809
5962
  } : {},
4810
5963
  ...value.styleLevels?.length ? {
5964
+ // `StyleLevel` is a class the backend only reads the two fields of.
4811
5965
  stylesWithLevels: value.styleLevels.map((style) => ({
4812
5966
  styleName: style.styleName,
4813
5967
  level: style.level
@@ -4828,15 +5982,17 @@ function table(value, ctx) {
4828
5982
  // `w:w` is an integer in OOXML. A grid that splits a remainder between
4829
5983
  // unstated columns comes out fractional, and the XML would carry it
4830
5984
  // 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,
4832
- type: value.width.kind === "twips" ? "dxa" : value.width.kind === "percent" ? "pct" : "auto"
5985
+ size: value.width.kind === "auto" ? 0 : value.width.kind === "twips" ? twips(value.width.value) : value.width.value,
5986
+ type: value.width.kind === "twips" ? "twips" : value.width.kind === "percent" ? "percent" : "auto"
4833
5987
  },
4834
5988
  layout: value.layout,
4835
5989
  // An empty grid is a table with nothing to say about its columns, which is
4836
5990
  // not the same as one whose columns are all zero wide. The compiler hands
4837
5991
  // 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) } : {},
4839
- ...value.alignment ? { alignment: alignment(value.alignment) } : {},
5992
+ ...value.columnGrid.values.length > 0 ? { columnWidths: value.columnGrid.values.map(twips) } : {},
5993
+ ...value.alignment ? {
5994
+ alignment: alignment(value.alignment)
5995
+ } : {},
4840
5996
  ...value.borders ? { borders: borders2(value.borders) } : {},
4841
5997
  ...value.cellMargins ? { margins: cellMargins(value.cellMargins) } : {},
4842
5998
  ...value.floating ? { float: tableFloat2(value.floating) } : {}
@@ -4846,14 +6002,22 @@ function tableFloat2(floating) {
4846
6002
  return {
4847
6003
  ...floating.horizontalAnchor ? { horizontalAnchor: floating.horizontalAnchor } : {},
4848
6004
  ...floating.verticalAnchor ? { verticalAnchor: floating.verticalAnchor } : {},
4849
- ...floating.absoluteHorizontalPositionTwips !== void 0 ? { absoluteHorizontalPosition: floating.absoluteHorizontalPositionTwips } : {},
6005
+ ...floating.absoluteHorizontalPositionTwips !== void 0 ? {
6006
+ absoluteHorizontalPosition: twips(
6007
+ floating.absoluteHorizontalPositionTwips
6008
+ )
6009
+ } : {},
4850
6010
  ...floating.relativeHorizontalPosition ? { relativeHorizontalPosition: floating.relativeHorizontalPosition } : {},
4851
- ...floating.absoluteVerticalPositionTwips !== void 0 ? { absoluteVerticalPosition: floating.absoluteVerticalPositionTwips } : {},
6011
+ ...floating.absoluteVerticalPositionTwips !== void 0 ? {
6012
+ absoluteVerticalPosition: twips(
6013
+ floating.absoluteVerticalPositionTwips
6014
+ )
6015
+ } : {},
4852
6016
  ...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 } : {},
6017
+ ...floating.topFromTextTwips !== void 0 ? { topFromText: twips(floating.topFromTextTwips) } : {},
6018
+ ...floating.rightFromTextTwips !== void 0 ? { rightFromText: twips(floating.rightFromTextTwips) } : {},
6019
+ ...floating.bottomFromTextTwips !== void 0 ? { bottomFromText: twips(floating.bottomFromTextTwips) } : {},
6020
+ ...floating.leftFromTextTwips !== void 0 ? { leftFromText: twips(floating.leftFromTextTwips) } : {},
4857
6021
  ...floating.overlap ? { overlap: floating.overlap } : {}
4858
6022
  };
4859
6023
  }
@@ -4861,7 +6025,10 @@ function tableRow(row, ctx) {
4861
6025
  return {
4862
6026
  cells: row.cells.map((cell) => tableCell(cell, ctx)),
4863
6027
  ...row.heightTwips !== void 0 ? {
4864
- height: { value: row.heightTwips, rule: row.heightRule ?? "atLeast" }
6028
+ height: {
6029
+ value: twips(row.heightTwips),
6030
+ rule: row.heightRule ?? "atLeast"
6031
+ }
4865
6032
  } : {},
4866
6033
  ...row.isHeader !== void 0 ? { tableHeader: row.isHeader } : {},
4867
6034
  ...row.cantSplit !== void 0 ? { cantSplit: row.cantSplit } : {},
@@ -4869,20 +6036,30 @@ function tableRow(row, ctx) {
4869
6036
  };
4870
6037
  }
4871
6038
  function tableCell(cell, ctx) {
6039
+ const children = cellChildren2(cell, ctx);
6040
+ if (cell.children[cell.children.length - 1]?.kind === "toc")
6041
+ children.push({ paragraph: { children: [] } });
4872
6042
  return {
4873
- children: cell.children.map((child) => block(child, ctx)),
4874
- ...cell.widthTwips !== void 0 ? { width: { size: cell.widthTwips, type: "dxa" } } : {},
4875
- ...cell.verticalAlign ? { verticalAlign: cell.verticalAlign } : {},
6043
+ children,
6044
+ ...cell.widthTwips !== void 0 ? { width: { size: twips(cell.widthTwips), type: "twips" } } : {},
6045
+ ...cell.verticalAlign ? {
6046
+ verticalAlign: cell.verticalAlign
6047
+ } : {},
4876
6048
  ...cell.shading ? { shading: shading(cell.shading) } : {},
4877
6049
  ...cell.margins ? { margins: cellMargins(cell.margins) } : {},
4878
6050
  ...cell.borders ? { borders: borders2(cell.borders) } : {},
4879
6051
  ...cell.columnSpan !== void 0 ? { columnSpan: cell.columnSpan } : {},
4880
6052
  ...cell.rowSpan !== void 0 ? { verticalMerge: cell.rowSpan } : {},
4881
- ...cell.textDirection ? { textDirection: cell.textDirection } : {}
6053
+ ...cell.textDirection ? {
6054
+ textDirection: cell.textDirection
6055
+ } : {}
4882
6056
  };
4883
6057
  }
6058
+ function cellChildren2(cell, ctx) {
6059
+ return cell.children.map((child) => block(child, ctx));
6060
+ }
4884
6061
  function cellMargins(margins) {
4885
- const side = (value) => value === void 0 ? void 0 : { size: value, type: "dxa" };
6062
+ const side = (value) => value === void 0 ? void 0 : { size: twips(value), type: "twips" };
4886
6063
  const out = {};
4887
6064
  for (const [name, value] of [
4888
6065
  ["top", margins.topTwips],
@@ -4925,47 +6102,51 @@ function section(value, ctx, closesDocument = false) {
4925
6102
  properties: {
4926
6103
  ...value.properties.type ? { type: value.properties.type } : {},
4927
6104
  ...value.properties.titlePage !== void 0 ? { titlePage: value.properties.titlePage } : {},
4928
- page: {
4929
- // Orientation is implied by the width/height pair, which is how this
4930
- // pipeline has always expressed it; stating it as well changes `w:pgSz`.
4931
- size: {
4932
- width: page.widthTwips,
4933
- height: page.heightTwips,
4934
- ...page.code !== void 0 ? { code: page.code } : {}
4935
- },
4936
- 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 } : {}
4944
- },
4945
- ...value.properties.pageNumbers ? {
4946
- pageNumbers: {
4947
- ...value.properties.pageNumbers.start !== void 0 ? { start: value.properties.pageNumbers.start } : {},
4948
- ...value.properties.pageNumbers.formatType ? { formatType: value.properties.pageNumbers.formatType } : {}
4949
- }
4950
- } : {},
4951
- ...value.properties.borders ? {
4952
- borders: {
4953
- ...value.properties.borders.display ? { display: value.properties.borders.display } : {},
4954
- ...value.properties.borders.offsetFrom ? { offsetFrom: value.properties.borders.offsetFrom } : {},
4955
- ...value.properties.borders.borders ? borders2(value.properties.borders.borders) : {}
4956
- }
4957
- } : {}
6105
+ // Orientation is implied by the width/height pair, which is how this
6106
+ // pipeline has always expressed it; stating it as well changes `w:pgSz`.
6107
+ pageSize: {
6108
+ width: twips(page.widthTwips),
6109
+ height: twips(page.heightTwips),
6110
+ ...page.code !== void 0 ? { code: page.code } : {}
4958
6111
  },
6112
+ pageMargin: {
6113
+ top: twips(page.margins.topTwips),
6114
+ right: twips(page.margins.rightTwips),
6115
+ bottom: twips(page.margins.bottomTwips),
6116
+ left: twips(page.margins.leftTwips),
6117
+ ...page.margins.headerTwips !== void 0 ? { header: twips(page.margins.headerTwips) } : {},
6118
+ ...page.margins.footerTwips !== void 0 ? { footer: twips(page.margins.footerTwips) } : {},
6119
+ ...page.margins.gutterTwips !== void 0 ? { gutter: twips(page.margins.gutterTwips) } : {}
6120
+ },
6121
+ ...value.properties.pageNumbers ? {
6122
+ pageNumberType: {
6123
+ ...value.properties.pageNumbers.start !== void 0 ? { start: value.properties.pageNumbers.start } : {},
6124
+ ...value.properties.pageNumbers.formatType ? {
6125
+ format: value.properties.pageNumbers.formatType
6126
+ } : {}
6127
+ }
6128
+ } : {},
6129
+ ...value.properties.borders ? {
6130
+ pageBorders: {
6131
+ ...value.properties.borders.display ? {
6132
+ display: value.properties.borders.display
6133
+ } : {},
6134
+ ...value.properties.borders.offsetFrom ? {
6135
+ offsetFrom: value.properties.borders.offsetFrom
6136
+ } : {},
6137
+ ...value.properties.borders.borders ? borders2(value.properties.borders.borders) : {}
6138
+ }
6139
+ } : {},
4959
6140
  ...columns ? {
4960
- column: {
6141
+ columns: {
4961
6142
  count: columns.count,
4962
- ...columns.spaceTwips !== void 0 ? { space: columns.spaceTwips } : {},
6143
+ ...columns.spaceTwips !== void 0 ? { space: twips(columns.spaceTwips) } : {},
4963
6144
  ...columns.separator !== void 0 ? { separate: columns.separator } : {},
4964
6145
  ...columns.equalWidth !== void 0 ? { equalWidth: columns.equalWidth } : {},
4965
6146
  ...columns.widths ? {
4966
6147
  children: columns.widths.map((column) => ({
4967
- width: column.widthTwips,
4968
- ...column.spaceTwips !== void 0 ? { space: column.spaceTwips } : {}
6148
+ width: twips(column.widthTwips),
6149
+ ...column.spaceTwips !== void 0 ? { space: twips(column.spaceTwips) } : {}
4969
6150
  }))
4970
6151
  } : {}
4971
6152
  }
@@ -4993,14 +6174,16 @@ function headerFooterSet(set, ctx) {
4993
6174
  function sectionChildren2(value, ctx, closesDocument = false) {
4994
6175
  const blocks = value.children.map((child) => block(child, ctx));
4995
6176
  const last = value.children[value.children.length - 1];
4996
- if (closesDocument && last?.kind === "paragraph" && last.frame !== void 0)
6177
+ const bookmark = value.bookmark;
6178
+ if (bookmark?.closes && last?.kind !== "paragraph" || closesDocument && last?.kind === "paragraph" && last.frame !== void 0)
4997
6179
  blocks.push({
4998
6180
  paragraph: {
4999
6181
  children: [],
5000
6182
  spacing: { before: 0, after: 0, line: 20, lineRule: "exact" }
5001
6183
  }
5002
6184
  });
5003
- const bookmark = value.bookmark;
6185
+ if (!closesDocument && !bookmark?.closes && last?.kind === "toc")
6186
+ blocks.push({ paragraph: { children: [] } });
5004
6187
  if (!bookmark) return blocks;
5005
6188
  return [
5006
6189
  ...bookmark.opens ? [{ bookmarkStart: { id: bookmark.id, name: bookmark.name } }] : [],
@@ -5011,43 +6194,44 @@ function sectionChildren2(value, ctx, closesDocument = false) {
5011
6194
  function numberingConfig2(numbering) {
5012
6195
  return {
5013
6196
  reference: numbering.reference,
5014
- levels: numbering.levels.map((level) => ({
5015
- level: level.level,
5016
- format: level.format,
5017
- text: level.text,
5018
- ...level.alignment ? { alignment: alignment(level.alignment) } : {},
5019
- ...level.suffix ? { suffix: level.suffix } : {},
5020
- ...level.start !== void 0 ? { start: level.start } : {},
5021
- ...level.paragraphStyleId ? { paragraphStyle: level.paragraphStyleId } : {},
5022
- style: {
6197
+ levels: numbering.levels.map(
6198
+ (level) => ({
6199
+ level: level.level,
6200
+ format: level.format,
6201
+ text: level.text,
6202
+ ...level.alignment ? { alignment: alignment(level.alignment) } : {},
6203
+ ...level.suffix ? { suffix: level.suffix } : {},
6204
+ // Always stated. docx.js writes `w:start w:val="1"` for a level that
6205
+ // names none, and so did this backend until 0.14, which writes no
6206
+ // `w:start` at all when it is not given one; Word and LibreOffice then
6207
+ // count every such list from 0.
6208
+ start: level.start ?? 1,
6209
+ ...level.paragraphStyleId ? { paragraphStyle: level.paragraphStyleId } : {},
5023
6210
  ...level.indent ? {
5024
6211
  paragraph: {
5025
6212
  indent: {
5026
- ...level.indent.leftTwips !== void 0 ? { left: level.indent.leftTwips } : {},
5027
- ...level.indent.hangingTwips !== void 0 ? { hanging: level.indent.hangingTwips } : {}
6213
+ ...level.indent.leftTwips !== void 0 ? { left: twips(level.indent.leftTwips) } : {},
6214
+ ...level.indent.hangingTwips !== void 0 ? { hanging: twips(level.indent.hangingTwips) } : {}
5028
6215
  }
5029
6216
  }
5030
6217
  } : {},
5031
6218
  ...level.run ? { run: runProperties2(level.run) } : {}
5032
- }
5033
- }))
6219
+ })
6220
+ )
5034
6221
  };
5035
6222
  }
5036
- var WRAP_TYPE2, BODY_ANCHOR;
6223
+ var BODY_ANCHOR;
5037
6224
  var init_emit2 = __esm({
5038
6225
  "src/renderers/office-open/emit.ts"() {
5039
6226
  "use strict";
5040
6227
  init_units();
5041
- WRAP_TYPE2 = {
5042
- none: 0,
5043
- square: 1,
5044
- tight: 2,
5045
- topAndBottom: 3
5046
- };
6228
+ init_chartWorkbook();
6229
+ init_chartLook();
6230
+ init_chartParts();
5047
6231
  BODY_ANCHOR = {
5048
- top: "t",
5049
- middle: "ctr",
5050
- bottom: "b"
6232
+ top: "top",
6233
+ middle: "center",
6234
+ bottom: "bottom"
5051
6235
  };
5052
6236
  }
5053
6237
  });
@@ -5107,15 +6291,34 @@ var init_styles3 = __esm({
5107
6291
  var office_open_exports = {};
5108
6292
  __export(office_open_exports, {
5109
6293
  OFFICE_OPEN_DOCX_RENDERER_ID: () => OFFICE_OPEN_DOCX_RENDERER_ID,
6294
+ OFFICE_OPEN_VERSION: () => OFFICE_OPEN_VERSION,
5110
6295
  buildDocumentOptions: () => buildDocumentOptions,
5111
6296
  createOfficeOpenDocxRenderer: () => createOfficeOpenDocxRenderer
5112
6297
  });
5113
- import AdmZip4 from "adm-zip";
6298
+ import AdmZip5 from "adm-zip";
6299
+ import {
6300
+ assertBackendVersion,
6301
+ installedPackageVersion
6302
+ } from "@json-to-office/shared/rendering/node";
6303
+ function installedBackendVersion() {
6304
+ let resolved;
6305
+ try {
6306
+ resolved = import.meta.resolve?.(OFFICE_OPEN_DOCX);
6307
+ } catch {
6308
+ resolved = void 0;
6309
+ }
6310
+ return installedPackageVersion(OFFICE_OPEN_DOCX, import.meta.url, resolved);
6311
+ }
5114
6312
  async function createOfficeOpenDocxRenderer() {
5115
6313
  const backend = await import(
5116
6314
  /* @vite-ignore */
5117
6315
  OFFICE_OPEN_DOCX
5118
6316
  );
6317
+ assertBackendVersion(
6318
+ OFFICE_OPEN_DOCX,
6319
+ OFFICE_OPEN_VERSION,
6320
+ installedBackendVersion()
6321
+ );
5119
6322
  if (typeof backend.generateDocument !== "function") {
5120
6323
  throw new Error(
5121
6324
  `${OFFICE_OPEN_DOCX} does not export generateDocument(); the installed version is not compatible with this adapter.`
@@ -5126,10 +6329,10 @@ async function createOfficeOpenDocxRenderer() {
5126
6329
  format: "docx",
5127
6330
  capabilities: OFFICE_OPEN_CAPABILITIES,
5128
6331
  async render(ir, options) {
5129
- const charts = [];
6332
+ const charts2 = [];
5130
6333
  const document = await buildDocumentOptions(
5131
6334
  ir,
5132
- charts,
6335
+ charts2,
5133
6336
  options?.svgRasterFallback
5134
6337
  );
5135
6338
  const bytes = await backend.generateDocument(document, {
@@ -5138,9 +6341,10 @@ async function createOfficeOpenDocxRenderer() {
5138
6341
  let raw = Buffer.from(
5139
6342
  bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes)
5140
6343
  );
5141
- if (charts.length > 0) {
5142
- const zip = new AdmZip4(raw);
5143
- spliceChartParts(zip, charts);
6344
+ if (ir.theme || charts2.length > 0) {
6345
+ const zip = new AdmZip5(raw);
6346
+ if (ir.theme) spliceTheme(zip, ir.theme);
6347
+ finishChartParts(zip, charts2);
5144
6348
  raw = zip.toBuffer();
5145
6349
  }
5146
6350
  if (options?.deterministic === false)
@@ -5157,12 +6361,12 @@ async function createOfficeOpenDocxRenderer() {
5157
6361
  }
5158
6362
  };
5159
6363
  }
5160
- async function buildDocumentOptions(ir, charts = [], svgRasterFallback) {
6364
+ async function buildDocumentOptions(ir, charts2 = [], svgRasterFallback) {
5161
6365
  let nextDrawingId = 1;
5162
6366
  const ctx = {
5163
6367
  pictures: await prepareImages2(ir, svgRasterFallback),
5164
6368
  nextDrawingId: () => nextDrawingId++,
5165
- charts
6369
+ charts: charts2
5166
6370
  };
5167
6371
  return {
5168
6372
  styles: emitStyles2(ir.styles),
@@ -5170,16 +6374,20 @@ async function buildDocumentOptions(ir, charts = [], svgRasterFallback) {
5170
6374
  (value, index) => section(value, ctx, index === ir.sections.length - 1)
5171
6375
  ),
5172
6376
  ...coreProperties2(ir),
5173
- features: {
6377
+ settings: {
5174
6378
  updateFields: ir.settings.updateFields,
5175
6379
  ...ir.settings.trackRevisions ? { trackRevisions: true } : {}
5176
6380
  },
5177
- ...ir.numbering.length > 0 ? { numbering: { config: ir.numbering.map(numberingConfig2) } } : {},
6381
+ ...ir.numbering.length > 0 ? {
6382
+ numbering: {
6383
+ abstractNumberings: ir.numbering.map(numberingConfig2)
6384
+ }
6385
+ } : {},
5178
6386
  ...ir.footnotes.length > 0 ? { footnotes: noteBodies2(ir.footnotes, ctx) } : {},
5179
6387
  ...ir.endnotes.length > 0 ? { endnotes: noteBodies2(ir.endnotes, ctx) } : {},
5180
6388
  ...ir.comments.length > 0 ? {
5181
- comments: {
5182
- children: ir.comments.map((comment) => ({
6389
+ comments: ir.comments.map(
6390
+ (comment) => ({
5183
6391
  id: comment.id,
5184
6392
  author: comment.author,
5185
6393
  ...comment.initials ? { initials: comment.initials } : {},
@@ -5187,29 +6395,25 @@ async function buildDocumentOptions(ir, charts = [], svgRasterFallback) {
5187
6395
  children: comment.children.map(
5188
6396
  (child) => paragraphOf(child, ctx)
5189
6397
  )
5190
- }))
5191
- }
6398
+ })
6399
+ )
5192
6400
  } : {}
5193
6401
  };
5194
6402
  }
5195
6403
  function paragraphOf(value, ctx) {
5196
6404
  const emitted = block(value, ctx);
5197
- const asParagraph = emitted.paragraph;
5198
- if (!asParagraph) {
6405
+ if (!("paragraph" in emitted) || typeof emitted.paragraph === "string") {
5199
6406
  throw new Error(
5200
6407
  `the office-open renderer expected a paragraph, not a "${value.kind}"`
5201
6408
  );
5202
6409
  }
5203
- return asParagraph;
6410
+ return emitted.paragraph;
5204
6411
  }
5205
6412
  function noteBodies2(notes, ctx) {
5206
- const bodies = {};
5207
- for (const note of notes) {
5208
- bodies[String(note.id)] = {
5209
- children: note.children.map((child) => paragraphOf(child, ctx))
5210
- };
5211
- }
5212
- return bodies;
6413
+ return notes.map((note) => ({
6414
+ id: note.id,
6415
+ children: note.children.map((child) => paragraphOf(child, ctx))
6416
+ }));
5213
6417
  }
5214
6418
  async function prepareImages2(ir, svgRasterFallback) {
5215
6419
  const placements = collectImagePlacements2(ir);
@@ -5232,23 +6436,16 @@ async function prepareImages2(ir, svgRasterFallback) {
5232
6436
  if (resource.kind !== "image") continue;
5233
6437
  const type = resource.mediaType;
5234
6438
  const data = Buffer.from(resource.bytes);
5235
- const placementData = /* @__PURE__ */ new Map();
5236
- [...placements.get(resource.id) ?? []].forEach((size, index) => {
5237
- placementData.set(
5238
- size,
5239
- index === 0 ? data : distinguishImageBytes(data, type, size)
5240
- );
5241
- });
5242
6439
  resources.set(resource.id, (placement) => {
5243
6440
  const sizeKey = placementKey(placement);
5244
6441
  const stem = `${resource.id}-${sizeKey}`;
5245
6442
  return {
5246
6443
  type,
5247
- // @office-open/docx stores a drawing transformation on its deduplicated
5248
- // media entry. Equivalent bytes drawn at a second size would otherwise
5249
- // reuse the first size, so each distinct placement size gets equivalent
5250
- // image bytes carrying a harmless format-native marker.
5251
- data: placementData.get(sizeKey) ?? data,
6444
+ // The same bytes at every size: the backend keeps each drawing's
6445
+ // extent on the drawing. 0.11 kept it on its deduplicated media
6446
+ // entry, and an image drawn at a second size took the first one's,
6447
+ // unless its bytes were told apart.
6448
+ data,
5252
6449
  // Named after the resource and the size it is drawn at, so two
5253
6450
  // placements of one image at one size share a single part and a third
5254
6451
  // at another size gets its own.
@@ -5271,104 +6468,6 @@ async function prepareImages2(ir, svgRasterFallback) {
5271
6468
  function placementKey(placement) {
5272
6469
  return `${emuToPixels(placement.widthEmu)}x${emuToPixels(placement.heightEmu)}`;
5273
6470
  }
5274
- function distinguishImageBytes(data, mediaType, placement) {
5275
- const marker = Buffer.from(`json-to-office:${placement}`, "utf8");
5276
- switch (mediaType) {
5277
- case "png":
5278
- return addPngTextChunk(data, marker);
5279
- case "jpg":
5280
- return addJpegComment(data, marker);
5281
- case "gif":
5282
- return addGifComment(data, marker);
5283
- case "svg":
5284
- return Buffer.concat([
5285
- data,
5286
- Buffer.from(`
5287
- <!-- ${marker.toString("utf8")} -->`, "utf8")
5288
- ]);
5289
- case "bmp": {
5290
- const marked = Buffer.concat([data, marker]);
5291
- if (marked.length >= 14 && marked.subarray(0, 2).toString("ascii") === "BM") {
5292
- marked.writeUInt32LE(marked.length, 2);
5293
- }
5294
- return marked;
5295
- }
5296
- default:
5297
- return Buffer.concat([data, marker]);
5298
- }
5299
- }
5300
- function addPngTextChunk(data, marker) {
5301
- const signature = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
5302
- if (data.length < 8 || !data.subarray(0, 8).equals(signature)) {
5303
- return Buffer.concat([data, marker]);
5304
- }
5305
- let offset = 8;
5306
- while (offset + 12 <= data.length) {
5307
- const payloadLength = data.readUInt32BE(offset);
5308
- const chunkEnd = offset + 12 + payloadLength;
5309
- if (chunkEnd > data.length) break;
5310
- if (data.subarray(offset + 4, offset + 8).toString("ascii") === "IEND") {
5311
- const payload = Buffer.concat([
5312
- Buffer.from("json-to-office\0", "latin1"),
5313
- marker
5314
- ]);
5315
- const chunk = Buffer.alloc(12 + payload.length);
5316
- chunk.writeUInt32BE(payload.length, 0);
5317
- chunk.write("tEXt", 4, 4, "ascii");
5318
- payload.copy(chunk, 8);
5319
- chunk.writeUInt32BE(
5320
- crc32(chunk.subarray(4, 8 + payload.length)),
5321
- 8 + payload.length
5322
- );
5323
- return Buffer.concat([
5324
- data.subarray(0, offset),
5325
- chunk,
5326
- data.subarray(offset)
5327
- ]);
5328
- }
5329
- offset = chunkEnd;
5330
- }
5331
- throw new Error(
5332
- "Cannot mark a PNG that has no intact IEND chunk; its bytes were not checked to decode before rendering"
5333
- );
5334
- }
5335
- function addJpegComment(data, marker) {
5336
- if (data.length < 2 || data[0] !== 255 || data[1] !== 216) {
5337
- return Buffer.concat([data, marker]);
5338
- }
5339
- const comment = Buffer.alloc(4 + marker.length);
5340
- comment[0] = 255;
5341
- comment[1] = 254;
5342
- comment.writeUInt16BE(marker.length + 2, 2);
5343
- marker.copy(comment, 4);
5344
- return Buffer.concat([data.subarray(0, 2), comment, data.subarray(2)]);
5345
- }
5346
- function addGifComment(data, marker) {
5347
- const trailer = data.lastIndexOf(59);
5348
- if (trailer < 0 || marker.length > 255) {
5349
- return Buffer.concat([data, marker]);
5350
- }
5351
- const comment = Buffer.concat([
5352
- Buffer.from([33, 254, marker.length]),
5353
- marker,
5354
- Buffer.from([0])
5355
- ]);
5356
- return Buffer.concat([
5357
- data.subarray(0, trailer),
5358
- comment,
5359
- data.subarray(trailer)
5360
- ]);
5361
- }
5362
- function crc32(data) {
5363
- let crc = 4294967295;
5364
- for (const byte of data) {
5365
- crc ^= byte;
5366
- for (let bit = 0; bit < 8; bit += 1) {
5367
- crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
5368
- }
5369
- }
5370
- return (crc ^ 4294967295) >>> 0;
5371
- }
5372
6471
  function collectImagePlacements2(ir) {
5373
6472
  const placements = /* @__PURE__ */ new Map();
5374
6473
  const record = (resourceId, placement) => {
@@ -5436,17 +6535,19 @@ function coreProperties2(ir) {
5436
6535
  ...metadata.custom?.length ? { customProperties: metadata.custom } : {}
5437
6536
  };
5438
6537
  }
5439
- var OFFICE_OPEN_DOCX_RENDERER_ID, OFFICE_OPEN_DOCX, OFFICE_OPEN_CAPABILITIES;
6538
+ var OFFICE_OPEN_DOCX_RENDERER_ID, OFFICE_OPEN_DOCX, OFFICE_OPEN_VERSION, OFFICE_OPEN_CAPABILITIES;
5440
6539
  var init_office_open = __esm({
5441
6540
  "src/renderers/office-open/index.ts"() {
5442
6541
  "use strict";
5443
6542
  init_chartParts();
6543
+ init_themePart();
5444
6544
  init_imageUtils();
5445
6545
  init_packageDocument();
5446
6546
  init_emit2();
5447
6547
  init_styles3();
5448
6548
  OFFICE_OPEN_DOCX_RENDERER_ID = "office-open";
5449
6549
  OFFICE_OPEN_DOCX = "@office-open/docx";
6550
+ OFFICE_OPEN_VERSION = "0.14.6";
5450
6551
  OFFICE_OPEN_CAPABILITIES = /* @__PURE__ */ new Set([
5451
6552
  "paragraphs",
5452
6553
  "styles",
@@ -6688,6 +7789,7 @@ import {
6688
7789
  assetUnreadableError
6689
7790
  } from "@json-to-office/shared/rendering";
6690
7791
  import { capsFormatting as capsFormatting2, synthesizeFamilyName as synthesizeFamilyName2 } from "@json-to-office/shared";
7792
+ import { unrenderedComponentText } from "@json-to-office/quality";
6691
7793
  import {
6692
7794
  isNativeVisualProps as isNativeVisualProps4
6693
7795
  } from "@json-to-office/shared-docx";
@@ -7813,7 +8915,8 @@ function createDocumentStyles(themeNameOrObject = "consulting", language) {
7813
8915
  tabStops: effectiveTabStops.map(
7814
8916
  (ts) => ({
7815
8917
  type: ts.type,
7816
- positionTwips: ts.position === "max" ? RIGHT_MARGIN_TAB_TWIPS : ts.position,
8918
+ // Whole twips: `w:pos` is an integer no backend rounds.
8919
+ positionTwips: ts.position === "max" ? RIGHT_MARGIN_TAB_TWIPS : Math.round(ts.position),
7817
8920
  ...ts.leader && { leader: ts.leader }
7818
8921
  })
7819
8922
  )
@@ -9360,6 +10463,7 @@ function emptyToUndefined(formatting) {
9360
10463
  }
9361
10464
 
9362
10465
  // src/ir/compiler.ts
10466
+ init_chartValues();
9363
10467
  init_types();
9364
10468
 
9365
10469
  // src/ir/measure.ts
@@ -9392,6 +10496,49 @@ function tableWidthTwips(width, measureTwips) {
9392
10496
 
9393
10497
  // src/ir/compiler.ts
9394
10498
  init_units();
10499
+
10500
+ // src/ir/theme.ts
10501
+ init_colorUtils();
10502
+ init_styleHelpers();
10503
+ init_types();
10504
+ var THEME_COLOR_TOKENS = [
10505
+ ["dark1", "text"],
10506
+ ["light1", "background"],
10507
+ ["dark2", "textSecondary"],
10508
+ ["light2", "backgroundSecondary"],
10509
+ ["accent1", "primary"],
10510
+ ["accent2", "secondary"],
10511
+ ["accent3", "accent"],
10512
+ ["accent4", "accent4"],
10513
+ ["accent5", "accent5"],
10514
+ ["accent6", "accent6"]
10515
+ ];
10516
+ var NOT_XML_CHARS = new RegExp(NOT_XML_CHAR.source, "gu");
10517
+ var xmlText = (value) => (value ?? "").replace(NOT_XML_CHARS, "").trim();
10518
+ function compileTheme(theme, themeName) {
10519
+ const colors = {};
10520
+ for (const [slot, token] of THEME_COLOR_TOKENS) {
10521
+ const hex = schemeHex(token, theme);
10522
+ if (hex) colors[slot] = { hex };
10523
+ }
10524
+ const headingFont = xmlText(resolveFontFamily(theme, "heading"));
10525
+ const bodyFont = xmlText(resolveFontFamily(theme, "body"));
10526
+ return {
10527
+ name: xmlText(theme.name) || xmlText(themeName) || "Office Theme",
10528
+ colors,
10529
+ ...headingFont ? { headingFont } : {},
10530
+ ...bodyFont ? { bodyFont } : {}
10531
+ };
10532
+ }
10533
+ function schemeHex(token, theme) {
10534
+ try {
10535
+ return resolveColor(token, theme);
10536
+ } catch {
10537
+ return void 0;
10538
+ }
10539
+ }
10540
+
10541
+ // src/ir/compiler.ts
9395
10542
  var CONTENT_BOOKMARK_ID_BASE = 2e6;
9396
10543
  var SINGLE_LINE_TWIPS = 240;
9397
10544
  function compileDocument(structure, layout, warnings = [], images = /* @__PURE__ */ new Map(), options = {}) {
@@ -9457,6 +10604,7 @@ function compileDocument(structure, layout, warnings = [], images = /* @__PURE__
9457
10604
  trackRevisions: structure.trackRevisions === true,
9458
10605
  ...noProofWords(structure.theme) ? { noProofWords: noProofWords(structure.theme) } : {}
9459
10606
  },
10607
+ theme: compileTheme(structure.theme, structure.themeName),
9460
10608
  // The statistic styles are appended rather than built with the rest: a
9461
10609
  // document that uses no statistic must keep the exact style set it had
9462
10610
  // before the component had styles, or every recorded golden moves for a
@@ -10225,14 +11373,6 @@ function compileShape2(props, children, scope) {
10225
11373
  );
10226
11374
  }
10227
11375
  const fill = style?.shading?.fill;
10228
- const dropOutline = Boolean(fill) && Boolean(outline.outline);
10229
- if (dropOutline) {
10230
- warnOnce(
10231
- ctx,
10232
- "text-box",
10233
- '[core-docx] text-box renderAs "shape" cannot carry a fill and a border at once (docx emits invalid shape properties); keeping the fill and dropping the border.'
10234
- );
10235
- }
10236
11376
  ctx.features.require("text-boxes", path3);
10237
11377
  if (props.floating) ctx.features.require("floating-images", path3);
10238
11378
  const insets = shapeInsets(
@@ -10251,7 +11391,7 @@ function compileShape2(props, children, scope) {
10251
11391
  heightPx: height.pixels,
10252
11392
  children,
10253
11393
  ...fill ? { fill: irColor(resolveColor(fill, ctx.theme)) } : {},
10254
- ...outline.outline && !dropOutline ? { outline: outline.outline } : {},
11394
+ ...outline.outline ? { outline: outline.outline } : {},
10255
11395
  ...insets ? { insetsEmu: insets } : {},
10256
11396
  ...props.floating ? { floating: compileFloating(props.floating, ctx, path3) } : {}
10257
11397
  }
@@ -10433,7 +11573,9 @@ function compileToc(component, scope) {
10433
11573
  formatting: {
10434
11574
  alignment: "left",
10435
11575
  spacing: {
10436
- beforeTwips: ctx.theme.componentDefaults?.heading?.spacing?.before ?? 240,
11576
+ beforeTwips: wholeTwips(
11577
+ ctx.theme.componentDefaults?.heading?.spacing?.before ?? 240
11578
+ ),
10437
11579
  // ~9pt, which separates the title from the list clearly enough to
10438
11580
  // read as a heading rather than a first entry.
10439
11581
  afterTwips: 180
@@ -10553,8 +11695,8 @@ function compileStatistic(component, scope) {
10553
11695
  const alignment2 = compileAlignment(props.alignment) ?? "center";
10554
11696
  ctx.usesStatistic = true;
10555
11697
  const spacing = props.spacing ? {
10556
- ...props.spacing.before !== void 0 ? { beforeTwips: props.spacing.before } : {},
10557
- ...props.spacing.after !== void 0 ? { afterTwips: props.spacing.after } : {}
11698
+ ...props.spacing.before !== void 0 ? { beforeTwips: wholeTwips(props.spacing.before) } : {},
11699
+ ...props.spacing.after !== void 0 ? { afterTwips: wholeTwips(props.spacing.after) } : {}
10558
11700
  } : void 0;
10559
11701
  if (props.format !== void 0) {
10560
11702
  warnOnce(
@@ -11179,7 +12321,7 @@ function paragraphFormatting(props, ctx, extra = {}) {
11179
12321
  if (Array.isArray(props.tabStops) && props.tabStops.length > 0) {
11180
12322
  formatting.tabStops = props.tabStops.map(
11181
12323
  (stop) => ({
11182
- positionTwips: stop.position,
12324
+ positionTwips: wholeTwips(stop.position),
11183
12325
  type: stop.type ?? "left",
11184
12326
  ...stop.leader ? { leader: stop.leader } : {}
11185
12327
  })
@@ -11228,7 +12370,10 @@ function compileSpacing(props, ctx) {
11228
12370
  }
11229
12371
  function compileLineSpacing(value) {
11230
12372
  if (typeof value === "number") {
11231
- return { lineTwips: value * SINGLE_LINE_TWIPS, lineRule: "auto" };
12373
+ return {
12374
+ lineTwips: wholeTwips(value * SINGLE_LINE_TWIPS),
12375
+ lineRule: "auto"
12376
+ };
11232
12377
  }
11233
12378
  if (typeof value !== "object" || value === null) return void 0;
11234
12379
  const { type, value: amount } = value;
@@ -11249,7 +12394,7 @@ function compileLineSpacing(value) {
11249
12394
  };
11250
12395
  case "multiple":
11251
12396
  return {
11252
- ...amount !== void 0 ? { lineTwips: amount * SINGLE_LINE_TWIPS } : {},
12397
+ ...amount !== void 0 ? { lineTwips: wholeTwips(amount * SINGLE_LINE_TWIPS) } : {},
11253
12398
  lineRule: "auto"
11254
12399
  };
11255
12400
  default:
@@ -11469,6 +12614,7 @@ function compileImage2(component, scope) {
11469
12614
  }
11470
12615
  var DEFAULT_CHART_HEIGHT_INCHES = 3;
11471
12616
  var MAX_CHART_TEXT_POINTS = 10;
12617
+ var MIN_CHART_TEXT_POINTS = 1;
11472
12618
  function chartTextFont(theme) {
11473
12619
  const bodySize = getThemeFonts(theme).body?.size;
11474
12620
  let color = "000000";
@@ -11478,9 +12624,12 @@ function chartTextFont(theme) {
11478
12624
  }
11479
12625
  return {
11480
12626
  fontFamily: resolveFontFamily(theme, "body"),
11481
- fontSize: Math.min(
11482
- typeof bodySize === "number" && bodySize > 0 ? bodySize : 11,
11483
- MAX_CHART_TEXT_POINTS
12627
+ fontSize: Math.max(
12628
+ MIN_CHART_TEXT_POINTS,
12629
+ Math.min(
12630
+ typeof bodySize === "number" && bodySize > 0 ? bodySize : 11,
12631
+ MAX_CHART_TEXT_POINTS
12632
+ )
11484
12633
  ),
11485
12634
  bold: false,
11486
12635
  color
@@ -11491,7 +12640,7 @@ function compileChart(component, scope) {
11491
12640
  const props = component.props ?? {};
11492
12641
  if (props.type === "bubble") {
11493
12642
  throw new Error(
11494
- `Chart at ${path3} is a bubble chart, which no docx renderer draws; use the pptx \`chart\` component on the pptxgenjs renderer for one.`
12643
+ `Chart at ${path3} is a bubble chart, but the docx \`chart\` component has no bubble sizes; use the pptx \`chart\` component on the pptxgenjs renderer.`
11495
12644
  );
11496
12645
  }
11497
12646
  const rawSeries = Array.isArray(props.data) ? props.data : [];
@@ -11513,10 +12662,22 @@ function compileChart(component, scope) {
11513
12662
  `Chart series "${label}" at ${path3} has ${labels.length} labels and ${values.length} values; they must be the same length.`
11514
12663
  );
11515
12664
  }
12665
+ if (labels.length === 0) {
12666
+ throw new Error(
12667
+ `Chart series "${label}" at ${path3} has no data points; give it at least one label and value.`
12668
+ );
12669
+ }
12670
+ const numbers = values.map((value) => Number(value));
12671
+ const bad = numbers.findIndex((value) => !Number.isFinite(value));
12672
+ if (bad !== -1) {
12673
+ throw new Error(
12674
+ `Chart series "${label}" at ${path3} has a value that is not a finite number (${JSON.stringify(values[bad])} at "${String(labels[bad])}").`
12675
+ );
12676
+ }
11516
12677
  return {
11517
12678
  ...typeof raw.name === "string" ? { name: raw.name } : {},
11518
12679
  labels: labels.map((value) => String(value)),
11519
- values: values.map((value) => Number(value))
12680
+ values: numbers
11520
12681
  };
11521
12682
  });
11522
12683
  const categories = series[0].labels;
@@ -11528,6 +12689,34 @@ function compileChart(component, scope) {
11528
12689
  );
11529
12690
  }
11530
12691
  }
12692
+ if (props.type === "pie" && series.length > 1) {
12693
+ throw new Error(
12694
+ `Chart at ${path3} is a pie with ${series.length} series; a pie draws one series. Use a doughnut for one ring per series, or a column chart to compare them.`
12695
+ );
12696
+ }
12697
+ if (props.type === "pie" || props.type === "doughnut") {
12698
+ for (const [index, entry] of series.entries()) {
12699
+ const at = entry.values.findIndex((value) => value < 0);
12700
+ if (at === -1) continue;
12701
+ throw new Error(
12702
+ `Chart series "${entry.name ?? `series ${index}`}" at ${path3} has a negative value (${entry.values[at]} at "${entry.labels[at]}"); a ${props.type} chart draws each value as a share of the whole.`
12703
+ );
12704
+ }
12705
+ }
12706
+ if (props.type === "scatter" && series.some((entry) => entry.labels.some((label) => !isNumericLabel(label)))) {
12707
+ warnOnce(
12708
+ ctx,
12709
+ "chart",
12710
+ `Chart at ${path3} is a scatter chart whose labels are not all numbers; each label is its point's x, so these points are placed at 1, 2, 3\u2026 in order.`
12711
+ );
12712
+ }
12713
+ if ((props.type === "pie" || props.type === "doughnut") && (typeof props.catAxisTitle === "string" || typeof props.valAxisTitle === "string")) {
12714
+ warnOnce(
12715
+ ctx,
12716
+ "chart",
12717
+ `Chart at ${path3} is a ${props.type} chart, which has no axes; its axis titles are not drawn.`
12718
+ );
12719
+ }
11531
12720
  const colors = Array.isArray(props.chartColors) ? props.chartColors.map(
11532
12721
  (color) => resolveColor(String(color), ctx.theme)
11533
12722
  ) : chartPaletteValues(ctx.theme).flatMap((value) => {
@@ -11540,13 +12729,21 @@ function compileChart(component, scope) {
11540
12729
  const contentWidthInches = scope.measureTwips / 1440;
11541
12730
  const widthInches = typeof props.width === "number" ? props.width : contentWidthInches;
11542
12731
  const heightInches = typeof props.height === "number" ? props.height : DEFAULT_CHART_HEIGHT_INCHES;
12732
+ const widthEmu = inchesToEmu(widthInches);
12733
+ const heightEmu = inchesToEmu(heightInches);
12734
+ if (!(widthEmu > 0 && heightEmu > 0)) {
12735
+ const inches = (value) => String(Number.isFinite(value) ? Math.round(value * 100) / 100 : value);
12736
+ throw new Error(
12737
+ `Chart at ${path3} has no room: it would be ${inches(widthInches)}\xD7${inches(heightInches)} in. Set \`width\`/\`height\`, or give it a container with space.`
12738
+ );
12739
+ }
11543
12740
  const chart = {
11544
12741
  kind: "chart",
11545
12742
  chartType: props.type,
11546
12743
  series,
11547
12744
  colors,
11548
- widthEmu: inchesToEmu(widthInches),
11549
- heightEmu: inchesToEmu(heightInches),
12745
+ widthEmu,
12746
+ heightEmu,
11550
12747
  ...typeof props.title === "string" ? { title: props.title } : {},
11551
12748
  ...props.showTitle !== void 0 ? { showTitle: props.showTitle } : {},
11552
12749
  ...props.showLegend !== void 0 ? { showLegend: props.showLegend } : {},
@@ -11710,8 +12907,8 @@ function compileFrame(floating, ctx, path3) {
11710
12907
  const useAbsolute = hasHorizontalOffset || hasVerticalOffset;
11711
12908
  const frame = {
11712
12909
  // 2in by 1in when the author gave no size.
11713
- widthTwips: floating.width || 2880,
11714
- heightTwips: floating.height || 1440,
12910
+ widthTwips: wholeTwips(floating.width || 2880),
12911
+ heightTwips: wholeTwips(floating.height || 1440),
11715
12912
  anchorHorizontal: floating.horizontalPosition?.relative || "page",
11716
12913
  anchorVertical: floating.verticalPosition?.relative || "page",
11717
12914
  ...floating.wrap?.type ? { wrap: floating.wrap.type } : {}
@@ -11724,17 +12921,21 @@ function compileFrame(floating, ctx, path3) {
11724
12921
  if (typeof rawX === "string" || typeof rawY === "string") {
11725
12922
  const hRelative = floating.horizontalPosition?.relative;
11726
12923
  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)
12924
+ frame.xTwips = wholeTwips(
12925
+ resolveOffsetTwips(
12926
+ rawX,
12927
+ hRelative && hRelative !== "page" ? getAvailableWidthTwips(ctx.theme, ctx.themeName) : getPageWidthTwips(ctx.theme, ctx.themeName)
12928
+ )
11730
12929
  );
11731
- frame.yTwips = resolveOffsetTwips(
11732
- rawY,
11733
- vRelative && vRelative !== "page" ? getAvailableHeightTwips(ctx.theme, ctx.themeName) : getPageHeightTwips(ctx.theme, ctx.themeName)
12930
+ frame.yTwips = wholeTwips(
12931
+ resolveOffsetTwips(
12932
+ rawY,
12933
+ vRelative && vRelative !== "page" ? getAvailableHeightTwips(ctx.theme, ctx.themeName) : getPageHeightTwips(ctx.theme, ctx.themeName)
12934
+ )
11734
12935
  );
11735
12936
  } else {
11736
- frame.xTwips = rawX;
11737
- frame.yTwips = rawY;
12937
+ frame.xTwips = wholeTwips(rawX);
12938
+ frame.yTwips = wholeTwips(rawY);
11738
12939
  }
11739
12940
  return frame;
11740
12941
  }
@@ -11745,7 +12946,7 @@ function compileFrame(floating, ctx, path3) {
11745
12946
  function compileFloating(floating, ctx, path3) {
11746
12947
  if (floating.wrap?.type === "tight") {
11747
12948
  throw new Error(
11748
- "Image floating wrap.type 'tight' is not supported due to invalid OOXML emitted by docx. Use 'square', 'topAndBottom', or 'none'."
12949
+ "Image floating wrap.type 'tight' is not supported. Use 'square', 'topAndBottom', or 'none'."
11749
12950
  );
11750
12951
  }
11751
12952
  const hRelative = floating.horizontalPosition?.relative;
@@ -12068,10 +13269,17 @@ function cellChildren(cell, baseStyle, scope, rowRevision) {
12068
13269
  return wrap(cellVisual(content, scope));
12069
13270
  }
12070
13271
  if (content.name !== "paragraph") {
13272
+ const text2 = unrenderedComponentText(content.name);
13273
+ warnOnce(
13274
+ ctx,
13275
+ "table",
13276
+ `${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.`,
13277
+ "W_UNSUPPORTED_CELL_CONTENT"
13278
+ );
12071
13279
  return wrap([
12072
13280
  {
12073
13281
  kind: "text",
12074
- text: `[Unsupported component type: ${content.name}]`,
13282
+ text: text2,
12075
13283
  formatting: placeholderFormatting(base)
12076
13284
  }
12077
13285
  ]);
@@ -13269,6 +14477,11 @@ init_widthUtils();
13269
14477
  import probe2 from "probe-image-size";
13270
14478
 
13271
14479
  // src/quality/text-inventory.ts
14480
+ import {
14481
+ unrenderedComponentText as unrenderedComponentText2
14482
+ } from "@json-to-office/quality";
14483
+ import { TABLE_CELL_COMPONENTS } from "@json-to-office/shared-docx";
14484
+ var CELL_COMPONENTS = new Set(TABLE_CELL_COMPONENTS);
13272
14485
  function tocDepth(depth) {
13273
14486
  const range = asRecord(depth) ?? {};
13274
14487
  const from = typeof range.from === "number" ? range.from : 1;
@@ -13317,7 +14530,14 @@ function collectDocxTextInventory(children, basePath = "/children") {
13317
14530
  add(`${path3}/content`, rec.content, cellRole, extra);
13318
14531
  else {
13319
14532
  const inner = asRecord(rec.content);
13320
- if (inner)
14533
+ if (inner && typeof inner.name === "string" && !CELL_COMPONENTS.has(inner.name)) {
14534
+ add(
14535
+ `${path3}/content`,
14536
+ unrenderedComponentText2(inner.name),
14537
+ cellRole,
14538
+ { ...extra, optional: true, unrendered: inner.name }
14539
+ );
14540
+ } else if (inner)
13321
14541
  visitNode(inner, `${path3}/content`, { ...inherited, inCell: true });
13322
14542
  }
13323
14543
  return;