@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
package/dist/index.js CHANGED
@@ -1810,15 +1810,15 @@ function convertLineSpacing(lineSpacing) {
1810
1810
  lineRule2 = "auto";
1811
1811
  break;
1812
1812
  case "atLeast":
1813
- line = value !== void 0 ? value * TWIPS_PER_POINT : void 0;
1813
+ line = value !== void 0 ? pointsToTwips(value) : void 0;
1814
1814
  lineRule2 = "atLeast";
1815
1815
  break;
1816
1816
  case "exactly":
1817
- line = value !== void 0 ? value * TWIPS_PER_POINT : void 0;
1817
+ line = value !== void 0 ? pointsToTwips(value) : void 0;
1818
1818
  lineRule2 = "exact";
1819
1819
  break;
1820
1820
  case "multiple":
1821
- line = value !== void 0 ? value * SINGLE_LINE_SPACING_TWIPS : void 0;
1821
+ line = value !== void 0 ? Math.round(value * SINGLE_LINE_SPACING_TWIPS) : void 0;
1822
1822
  lineRule2 = "auto";
1823
1823
  break;
1824
1824
  default:
@@ -2779,7 +2779,7 @@ var init_imageUtils = __esm({
2779
2779
  });
2780
2780
 
2781
2781
  // src/ir/types.ts
2782
- var TWIPS_PER_INCH, TWIPS_PER_POINT2, EMU_PER_INCH, EMU_PER_TWIP, DOCX_IR_SCHEMA_VERSION, DOCX_IR_BORDER_STYLES;
2782
+ var TWIPS_PER_INCH, TWIPS_PER_POINT2, EMU_PER_INCH, EMU_PER_TWIP, DOCX_IR_SCHEMA_VERSION, NOT_XML_CHAR, DOCX_IR_BORDER_STYLES;
2783
2783
  var init_types = __esm({
2784
2784
  "src/ir/types.ts"() {
2785
2785
  "use strict";
@@ -2788,6 +2788,7 @@ var init_types = __esm({
2788
2788
  EMU_PER_INCH = 914400;
2789
2789
  EMU_PER_TWIP = EMU_PER_INCH / TWIPS_PER_INCH;
2790
2790
  DOCX_IR_SCHEMA_VERSION = 1;
2791
+ NOT_XML_CHAR = /[^\t\n\r\u0020-\uD7FF\uE000-\uFFFD\u{10000}-\u{10FFFF}]/u;
2791
2792
  DOCX_IR_BORDER_STYLES = [
2792
2793
  "single",
2793
2794
  "dashDotStroked",
@@ -2825,6 +2826,9 @@ import { createHash as createHash2 } from "crypto";
2825
2826
  function pointsToTwips2(points) {
2826
2827
  return Math.round(points * TWIPS_PER_POINT2);
2827
2828
  }
2829
+ function wholeTwips(twips2) {
2830
+ return Math.round(twips2);
2831
+ }
2828
2832
  function pointsToHalfPoints(points) {
2829
2833
  return Math.round(points * 2);
2830
2834
  }
@@ -2840,8 +2844,8 @@ function inchesToEmu(inches) {
2840
2844
  function pointsToEmu(points) {
2841
2845
  return Math.round(points * EMU_PER_INCH / 72);
2842
2846
  }
2843
- function twipsToEmu(twips) {
2844
- return Math.round(twips * EMU_PER_TWIP);
2847
+ function twipsToEmu(twips2) {
2848
+ return Math.round(twips2 * EMU_PER_TWIP);
2845
2849
  }
2846
2850
  function pixelsToEmu(pixels) {
2847
2851
  return Math.round(pixels * 9525);
@@ -2849,8 +2853,8 @@ function pixelsToEmu(pixels) {
2849
2853
  function emuToPixels(emu) {
2850
2854
  return emu / 9525;
2851
2855
  }
2852
- function twipsToPixels(twips) {
2853
- return twips / TWIPS_PER_INCH * 96;
2856
+ function twipsToPixels(twips2) {
2857
+ return twips2 / TWIPS_PER_INCH * 96;
2854
2858
  }
2855
2859
  function irColor(hex) {
2856
2860
  return { hex: hex.startsWith("#") ? hex.slice(1) : hex };
@@ -2871,6 +2875,226 @@ var init_units = __esm({
2871
2875
  }
2872
2876
  });
2873
2877
 
2878
+ // src/ir/chartValues.ts
2879
+ function isNumericLabel(label) {
2880
+ return NUMERIC_LABEL.test(label.trim());
2881
+ }
2882
+ function scatterX(labels, index) {
2883
+ const label = labels[index];
2884
+ return label !== void 0 && isNumericLabel(label) ? Number(label) : index + 1;
2885
+ }
2886
+ var NUMERIC_LABEL;
2887
+ var init_chartValues = __esm({
2888
+ "src/ir/chartValues.ts"() {
2889
+ "use strict";
2890
+ NUMERIC_LABEL = /^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$/;
2891
+ }
2892
+ });
2893
+
2894
+ // src/renderers/chartText.ts
2895
+ function chartTextRoles(font) {
2896
+ const { bold, ...rest } = font;
2897
+ const text = rest;
2898
+ return {
2899
+ text,
2900
+ axisTitle: { ...text, bold },
2901
+ title: {
2902
+ ...text,
2903
+ fontSize: Math.round(text.fontSize * CHART_TITLE_SCALE),
2904
+ bold: true
2905
+ }
2906
+ };
2907
+ }
2908
+ var CHART_TITLE_SCALE;
2909
+ var init_chartText = __esm({
2910
+ "src/renderers/chartText.ts"() {
2911
+ "use strict";
2912
+ CHART_TITLE_SCALE = 1.4;
2913
+ }
2914
+ });
2915
+
2916
+ // src/renderers/docxjs/docxSubpath.ts
2917
+ function docxSubpath(specifier, component, importer) {
2918
+ let module;
2919
+ let failure;
2920
+ let loading;
2921
+ return {
2922
+ specifier,
2923
+ load() {
2924
+ loading ??= importer().then(
2925
+ (loadedModule) => {
2926
+ module = loadedModule;
2927
+ },
2928
+ (reason) => {
2929
+ failure = { reason };
2930
+ }
2931
+ );
2932
+ return loading;
2933
+ },
2934
+ loaded: () => module !== void 0,
2935
+ get() {
2936
+ if (module !== void 0) return module;
2937
+ if (failure !== void 0) {
2938
+ const { reason } = failure;
2939
+ const detail = reason instanceof Error ? reason.message : String(reason);
2940
+ throw new Error(
2941
+ `The docx \`${component}\` component needs docx 9.8.0 or later: the \`${specifier}\` entry could not be loaded (${detail})`
2942
+ );
2943
+ }
2944
+ throw new Error(
2945
+ `${specifier} is not loaded: await its load() before buildDocument() (render() does)`
2946
+ );
2947
+ }
2948
+ };
2949
+ }
2950
+ var init_docxSubpath = __esm({
2951
+ "src/renderers/docxjs/docxSubpath.ts"() {
2952
+ "use strict";
2953
+ }
2954
+ });
2955
+
2956
+ // src/renderers/docxjs/charts.ts
2957
+ import { assertNever } from "@json-to-office/shared/rendering";
2958
+ function loadDocxCharts() {
2959
+ return charts.load();
2960
+ }
2961
+ function chartFont(role) {
2962
+ return {
2963
+ name: role.fontFamily,
2964
+ size: role.fontSize,
2965
+ color: role.color,
2966
+ bold: role.bold ?? false
2967
+ };
2968
+ }
2969
+ function chartRunOptions(chart, floating) {
2970
+ const roles = chartTextRoles(chart.textFont);
2971
+ const text = chartFont(roles.text);
2972
+ const axisTitle = chartFont(roles.axisTitle);
2973
+ const palette = chart.colors;
2974
+ const colorAt = (index) => palette.length > 0 ? palette[index % palette.length] : void 0;
2975
+ const withColor = (index) => {
2976
+ const color = colorAt(index);
2977
+ return color ? { color } : {};
2978
+ };
2979
+ const seriesName = (index) => chart.series[index].name ?? `Series ${index + 1}`;
2980
+ const categories = chart.series[0]?.labels ?? [];
2981
+ const axis = (title) => ({
2982
+ ...title ? { title: { text: title, font: axisTitle } } : {},
2983
+ font: text
2984
+ });
2985
+ const common = {
2986
+ ...chart.title && chart.showTitle !== false ? { title: { text: chart.title, font: chartFont(roles.title) } } : {},
2987
+ legend: chart.showLegend === false ? false : {
2988
+ position: LEGEND_POSITION[chart.legendPosition ?? "b"],
2989
+ font: text
2990
+ },
2991
+ font: { name: roles.text.fontFamily, color: roles.text.color },
2992
+ // Office-open's chart space is unfilled and unbordered; `ChartRun`'s
2993
+ // default is Word's white fill and grey border.
2994
+ chartArea: { fill: "none", border: "none" },
2995
+ transformation: {
2996
+ width: emuToPixels(chart.widthEmu),
2997
+ height: emuToPixels(chart.heightEmu)
2998
+ },
2999
+ // Without an authored description `ChartRun` describes the chart from
3000
+ // its data, which is deterministic; the name stays empty, as it is on
3001
+ // every other drawing this renderer writes.
3002
+ ...chart.altText ? { altText: { name: "", description: chart.altText } } : {},
3003
+ ...floating ? { floating } : {}
3004
+ };
3005
+ switch (chart.chartType) {
3006
+ case "column":
3007
+ case "bar":
3008
+ case "area":
3009
+ case "line":
3010
+ return {
3011
+ ...common,
3012
+ type: chart.chartType,
3013
+ categories,
3014
+ ...chart.chartType === "line" ? { markers: true } : {},
3015
+ series: chart.series.map((entry, index) => ({
3016
+ name: seriesName(index),
3017
+ values: entry.values,
3018
+ ...withColor(index)
3019
+ })),
3020
+ categoryAxis: axis(chart.categoryAxisTitle),
3021
+ valueAxis: axis(chart.valueAxisTitle)
3022
+ };
3023
+ case "pie":
3024
+ case "doughnut":
3025
+ return {
3026
+ ...common,
3027
+ type: chart.chartType,
3028
+ categories,
3029
+ series: chart.series.map((entry, index) => ({
3030
+ name: seriesName(index),
3031
+ values: entry.values,
3032
+ ...palette.length > 0 ? { colors: entry.values.map((_, point) => colorAt(point)) } : {}
3033
+ }))
3034
+ };
3035
+ case "radar":
3036
+ return {
3037
+ ...common,
3038
+ type: "radar",
3039
+ categories,
3040
+ markers: true,
3041
+ series: chart.series.map((entry, index) => ({
3042
+ name: seriesName(index),
3043
+ values: entry.values,
3044
+ ...withColor(index)
3045
+ })),
3046
+ categoryAxis: axis(chart.categoryAxisTitle),
3047
+ valueAxis: axis(chart.valueAxisTitle)
3048
+ };
3049
+ case "scatter":
3050
+ return {
3051
+ ...common,
3052
+ type: "scatter",
3053
+ markers: true,
3054
+ lines: "straight",
3055
+ series: chart.series.map((entry, index) => ({
3056
+ name: seriesName(index),
3057
+ points: entry.values.map((y, point) => ({
3058
+ x: scatterX(entry.labels, point),
3059
+ y
3060
+ })),
3061
+ ...withColor(index)
3062
+ })),
3063
+ xAxis: { ...axis(chart.categoryAxisTitle), gridlines: false },
3064
+ yAxis: axis(chart.valueAxisTitle)
3065
+ };
3066
+ case "bubble":
3067
+ throw new Error(
3068
+ "the docxjs renderer has no emitter for a bubble chart; the compiler should have refused it"
3069
+ );
3070
+ default:
3071
+ return assertNever(chart.chartType, "DocxIrChartType");
3072
+ }
3073
+ }
3074
+ function emitChart(chart, floating) {
3075
+ return new (charts.get()).ChartRun(
3076
+ chartRunOptions(chart, floating)
3077
+ );
3078
+ }
3079
+ var charts, LEGEND_POSITION;
3080
+ var init_charts = __esm({
3081
+ "src/renderers/docxjs/charts.ts"() {
3082
+ "use strict";
3083
+ init_chartValues();
3084
+ init_units();
3085
+ init_chartText();
3086
+ init_docxSubpath();
3087
+ charts = docxSubpath("docx/charts", "chart", () => import("docx/charts"));
3088
+ LEGEND_POSITION = {
3089
+ b: "bottom",
3090
+ l: "left",
3091
+ r: "right",
3092
+ t: "top",
3093
+ tr: "topRight"
3094
+ };
3095
+ }
3096
+ });
3097
+
2874
3098
  // src/renderers/docxjs/emit.ts
2875
3099
  import {
2876
3100
  AlignmentType,
@@ -2906,7 +3130,7 @@ import {
2906
3130
  WidthType,
2907
3131
  WpsShapeRun
2908
3132
  } from "docx";
2909
- import { assertNever } from "@json-to-office/shared/rendering";
3133
+ import { assertNever as assertNever2 } from "@json-to-office/shared/rendering";
2910
3134
  function complexField(instruction, formatting, leading, cachedText) {
2911
3135
  const options = runOptions(formatting);
2912
3136
  const fieldChar = (type, extra = {}) => new TextRun({
@@ -2943,7 +3167,12 @@ function runOptions(formatting) {
2943
3167
  if (formatting.bold !== void 0) options.bold = formatting.bold;
2944
3168
  if (formatting.italic !== void 0) options.italics = formatting.italic;
2945
3169
  if (formatting.underline !== void 0) {
2946
- options.underline = formatting.underline ? { type: formatting.underline.type } : void 0;
3170
+ options.underline = formatting.underline ? {
3171
+ type: formatting.underline.type,
3172
+ // A native visual's text is the one place a run states its own
3173
+ // underline colour.
3174
+ ...formatting.underline.color ? { color: formatting.underline.color.hex } : {}
3175
+ } : void 0;
2947
3176
  }
2948
3177
  if (formatting.strike !== void 0) options.strike = formatting.strike;
2949
3178
  if (formatting.allCaps !== void 0) options.allCaps = formatting.allCaps;
@@ -3069,21 +3298,36 @@ function inlineChildren(children, resources = /* @__PURE__ */ new Map()) {
3069
3298
  case "shape":
3070
3299
  out.push(emitShape(child, resources));
3071
3300
  break;
3072
- case "drawingGroup":
3073
- throw new Error(
3074
- "the docxjs renderer has no emitter for a drawing group; this document should have been refused by the capability check"
3075
- );
3076
- case "chart":
3077
- throw new Error(
3078
- "the docxjs renderer has no emitter for a chart; this document should have been refused by the capability check"
3301
+ case "drawingGroup": {
3302
+ const build = resources.drawingGroup;
3303
+ if (!build) {
3304
+ throw new Error(
3305
+ "the docxjs renderer was handed a drawing group without loading docx/shapes; render() loads it whenever the IR holds one"
3306
+ );
3307
+ }
3308
+ const pending = breakOption();
3309
+ if (pending.break) out.push(new TextRun(pending));
3310
+ out.push(build(child, resources));
3311
+ break;
3312
+ }
3313
+ case "chart": {
3314
+ const pending = breakOption();
3315
+ if (pending.break) out.push(new TextRun(pending));
3316
+ out.push(
3317
+ emitChart(
3318
+ child,
3319
+ child.floating ? floatingOptions(child.floating) : void 0
3320
+ )
3079
3321
  );
3322
+ break;
3323
+ }
3080
3324
  case "noteReference":
3081
3325
  out.push(
3082
3326
  child.noteKind === "endnote" ? new EndnoteReferenceRun(child.id) : new FootnoteReferenceRun(child.id)
3083
3327
  );
3084
3328
  break;
3085
3329
  default:
3086
- assertNever(child, "DocxIrInline");
3330
+ assertNever2(child, "DocxIrInline");
3087
3331
  }
3088
3332
  }
3089
3333
  return out;
@@ -3288,7 +3532,7 @@ function emitBlock(block2, resources = /* @__PURE__ */ new Map()) {
3288
3532
  case "toc":
3289
3533
  return emitToc(block2);
3290
3534
  default:
3291
- return assertNever(block2, "DocxIrBlock");
3535
+ return assertNever2(block2, "DocxIrBlock");
3292
3536
  }
3293
3537
  }
3294
3538
  function emitToc(block2) {
@@ -3432,6 +3676,7 @@ var ALIGNMENT2, PAGE_FIELDS, isSequenceField, WRAP_TYPE, ThemedTableOfContents,
3432
3676
  var init_emit = __esm({
3433
3677
  "src/renderers/docxjs/emit.ts"() {
3434
3678
  "use strict";
3679
+ init_charts();
3435
3680
  ALIGNMENT2 = {
3436
3681
  left: AlignmentType.LEFT,
3437
3682
  center: AlignmentType.CENTER,
@@ -3730,10 +3975,13 @@ function canonicalizeRelationshipIds(zip) {
3730
3975
  const rename = new Map(
3731
3976
  ordered.map((id, index) => [id, `rId${highest + 1 + index}`])
3732
3977
  );
3733
- const rewrite = (xml) => xml.replace(/"([^"]+)"/g, (whole, value) => {
3734
- const replacement = rename.get(value);
3735
- return replacement === void 0 ? whole : `"${replacement}"`;
3736
- });
3978
+ const rewrite = (xml) => xml.replace(
3979
+ /(\s[\w:.-]+=)"([^"]*)"/g,
3980
+ (whole, prefix, value) => {
3981
+ const replacement = rename.get(value);
3982
+ return replacement === void 0 ? whole : `${prefix}"${replacement}"`;
3983
+ }
3984
+ );
3737
3985
  updates.push({ name: relsName, xml: rewrite(relsXml) });
3738
3986
  if (partEntry) {
3739
3987
  updates.push({ name: partName, xml: rewrite(partXml) });
@@ -3744,17 +3992,62 @@ function canonicalizeRelationshipIds(zip) {
3744
3992
  if (entry) zip.updateFile(entry, Buffer.from(update.xml, "utf8"));
3745
3993
  }
3746
3994
  }
3995
+ function canonicalizeEmbeddedPackages(zip, generatedAt, depth = 1) {
3996
+ const names = zip.getEntries().map((entry) => entry.entryName).filter((name) => EMBEDDED_OFFICE_PACKAGE.test(name));
3997
+ let changed = false;
3998
+ for (const name of names) {
3999
+ const entry = zip.getEntry(name);
4000
+ if (!entry) continue;
4001
+ let nested;
4002
+ try {
4003
+ nested = new AdmZip(entry.getData());
4004
+ nested.getEntries();
4005
+ } catch {
4006
+ continue;
4007
+ }
4008
+ if (canonicalizeNestedPackage(nested, generatedAt, depth)) {
4009
+ zip.updateFile(entry, nested.toBuffer());
4010
+ changed = true;
4011
+ }
4012
+ }
4013
+ return changed;
4014
+ }
4015
+ function canonicalizeNestedPackage(zip, generatedAt, depth) {
4016
+ let changed = false;
4017
+ const coreProperties3 = zip.getEntry("docProps/core.xml");
4018
+ if (coreProperties3) {
4019
+ const xml = coreProperties3.getData().toString("utf8");
4020
+ const normalized = xml.replace(
4021
+ CORE_TIMESTAMP,
4022
+ `$1${generatedAt.toISOString()}$2`
4023
+ );
4024
+ if (normalized !== xml) {
4025
+ zip.updateFile(coreProperties3, Buffer.from(normalized, "utf8"));
4026
+ changed = true;
4027
+ }
4028
+ }
4029
+ if (depth < MAX_EMBEDDED_DEPTH && canonicalizeEmbeddedPackages(zip, generatedAt, depth + 1)) {
4030
+ changed = true;
4031
+ }
4032
+ const zipTimestamp = toDosTime(generatedAt);
4033
+ for (const entry of zip.getEntries()) {
4034
+ const header = entry.header;
4035
+ if (header.timeval !== zipTimestamp) {
4036
+ header.timeval = zipTimestamp;
4037
+ changed = true;
4038
+ }
4039
+ }
4040
+ return changed;
4041
+ }
3747
4042
  function canonicalizeDocxBuffer(buffer, generatedAt = DEFAULT_GENERATION_DATE) {
3748
4043
  const zip = new AdmZip(buffer);
3749
4044
  normalizeTextCase(zip);
3750
4045
  canonicalizeRelationshipIds(zip);
4046
+ canonicalizeEmbeddedPackages(zip, generatedAt);
3751
4047
  const isoTimestamp = generatedAt.toISOString();
3752
4048
  const coreProperties3 = zip.getEntry("docProps/core.xml");
3753
4049
  if (coreProperties3) {
3754
- const normalized = coreProperties3.getData().toString("utf8").replace(
3755
- /(<dcterms:(?:created|modified)\b[^>]*>)[^<]*(<\/dcterms:(?:created|modified)>)/g,
3756
- `$1${isoTimestamp}$2`
3757
- );
4050
+ const normalized = coreProperties3.getData().toString("utf8").replace(CORE_TIMESTAMP, `$1${isoTimestamp}$2`);
3758
4051
  zip.updateFile(coreProperties3, Buffer.from(normalized, "utf8"));
3759
4052
  }
3760
4053
  const zipTimestamp = toDosTime(generatedAt);
@@ -3767,35 +4060,567 @@ function normalizeDocxCaseBuffer(buffer) {
3767
4060
  const zip = new AdmZip(buffer);
3768
4061
  return normalizeTextCase(zip) ? zip.toBuffer() : buffer;
3769
4062
  }
3770
- var DEFAULT_GENERATION_DATE, STABLE_RELATIONSHIP_ID;
4063
+ var DEFAULT_GENERATION_DATE, STABLE_RELATIONSHIP_ID, CORE_TIMESTAMP, EMBEDDED_OFFICE_PACKAGE, MAX_EMBEDDED_DEPTH;
3771
4064
  var init_packageDocument = __esm({
3772
4065
  "src/utils/packageDocument.ts"() {
3773
4066
  "use strict";
3774
4067
  init_normalizeTextCase();
3775
4068
  DEFAULT_GENERATION_DATE = /* @__PURE__ */ new Date("2000-01-01T00:00:00.000Z");
3776
4069
  STABLE_RELATIONSHIP_ID = /^rId\d+$/;
4070
+ CORE_TIMESTAMP = /(<dcterms:(?:created|modified)\b[^>]*>)[^<]*(<\/dcterms:(?:created|modified)>)/g;
4071
+ EMBEDDED_OFFICE_PACKAGE = /\.(?:docx|pptx|xlsx|xlsm)$/i;
4072
+ MAX_EMBEDDED_DEPTH = 3;
3777
4073
  }
3778
4074
  });
3779
4075
 
3780
4076
  // src/utils/fixFloatingImageIds.ts
3781
4077
  import AdmZip2 from "adm-zip";
4078
+ function compareDrawingParts(a, b) {
4079
+ const ma = DRAWING_PART.exec(a);
4080
+ const mb = DRAWING_PART.exec(b);
4081
+ const rank = RANK.indexOf(ma[1]) - RANK.indexOf(mb[1]);
4082
+ return rank !== 0 ? rank : Number(ma[2] || 0) - Number(mb[2] || 0);
4083
+ }
3782
4084
  function fixFloatingImageIdsInBuffer(buffer) {
3783
4085
  const zip = new AdmZip2(buffer);
3784
- const documentEntry = zip.getEntry("word/document.xml");
3785
- if (!documentEntry) {
4086
+ if (!zip.getEntry("word/document.xml")) {
3786
4087
  throw new Error("document.xml not found in DOCX");
3787
4088
  }
4089
+ const parts = zip.getEntries().filter((entry) => DRAWING_PART.test(entry.entryName)).sort((a, b) => compareDrawingParts(a.entryName, b.entryName));
3788
4090
  let idCounter = 1;
3789
- const documentXml = documentEntry.getData().toString("utf8").replace(/(<wp:docPr\b[^>]*?\s)id="\d+"/g, (_match, prefix) => {
3790
- const newId = idCounter++;
3791
- return `${prefix}id="${newId}"`;
3792
- });
3793
- zip.updateFile(documentEntry, Buffer.from(documentXml, "utf8"));
4091
+ for (const entry of parts) {
4092
+ const xml = entry.getData().toString("utf8");
4093
+ const renumbered = xml.replace(
4094
+ /(<wp:docPr\b[^>]*?\s)id="\d+"/g,
4095
+ (_match, prefix) => `${prefix}id="${idCounter++}"`
4096
+ );
4097
+ zip.updateFile(entry, Buffer.from(renumbered, "utf8"));
4098
+ }
3794
4099
  return zip.toBuffer();
3795
4100
  }
4101
+ var DRAWING_PART, RANK;
3796
4102
  var init_fixFloatingImageIds = __esm({
3797
4103
  "src/utils/fixFloatingImageIds.ts"() {
3798
4104
  "use strict";
4105
+ DRAWING_PART = /^word\/(document|header|footer|footnotes|endnotes|comments)(\d*)\.xml$/;
4106
+ RANK = [
4107
+ "document",
4108
+ "header",
4109
+ "footer",
4110
+ "footnotes",
4111
+ "endnotes",
4112
+ "comments"
4113
+ ];
4114
+ }
4115
+ });
4116
+
4117
+ // src/renderers/docxjs/presetShapes.ts
4118
+ function docxPresetShape(geometry) {
4119
+ const name = Object.hasOwn(OOXML_PRESET_TO_DOCX, geometry) ? OOXML_PRESET_TO_DOCX[geometry] : void 0;
4120
+ if (!name) {
4121
+ throw new Error(
4122
+ `the docxjs renderer has no preset for the OOXML geometry "${geometry}"`
4123
+ );
4124
+ }
4125
+ return name;
4126
+ }
4127
+ function docxLineDash(dash) {
4128
+ const name = Object.hasOwn(OOXML_DASH_TO_DOCX, dash) ? OOXML_DASH_TO_DOCX[dash] : void 0;
4129
+ if (!name) {
4130
+ throw new Error(
4131
+ `the docxjs renderer has no line dash for the OOXML value "${dash}"`
4132
+ );
4133
+ }
4134
+ return name;
4135
+ }
4136
+ var OOXML_PRESET_TO_DOCX, OOXML_DASH_TO_DOCX;
4137
+ var init_presetShapes = __esm({
4138
+ "src/renderers/docxjs/presetShapes.ts"() {
4139
+ "use strict";
4140
+ OOXML_PRESET_TO_DOCX = {
4141
+ line: "line",
4142
+ lineInv: "inverseLine",
4143
+ straightConnector1: "straightConnector",
4144
+ bentConnector2: "elbowConnectorOneBend",
4145
+ bentConnector3: "elbowConnector",
4146
+ bentConnector4: "elbowConnectorThreeBends",
4147
+ bentConnector5: "elbowConnectorFourBends",
4148
+ curvedConnector2: "curvedConnectorOneBend",
4149
+ curvedConnector3: "curvedConnector",
4150
+ curvedConnector4: "curvedConnectorThreeBends",
4151
+ curvedConnector5: "curvedConnectorFourBends",
4152
+ triangle: "triangle",
4153
+ rtTriangle: "rightTriangle",
4154
+ diamond: "diamond",
4155
+ parallelogram: "parallelogram",
4156
+ trapezoid: "trapezoid",
4157
+ nonIsoscelesTrapezoid: "nonIsoscelesTrapezoid",
4158
+ pentagon: "pentagon",
4159
+ hexagon: "hexagon",
4160
+ heptagon: "heptagon",
4161
+ octagon: "octagon",
4162
+ decagon: "decagon",
4163
+ dodecagon: "dodecagon",
4164
+ ellipse: "ellipse",
4165
+ teardrop: "teardrop",
4166
+ pieWedge: "pieWedge",
4167
+ pie: "pie",
4168
+ blockArc: "blockArc",
4169
+ donut: "donut",
4170
+ noSmoking: "noSymbol",
4171
+ chord: "chord",
4172
+ arc: "arc",
4173
+ frame: "frame",
4174
+ halfFrame: "halfFrame",
4175
+ corner: "lShape",
4176
+ diagStripe: "diagonalStripe",
4177
+ plus: "cross",
4178
+ plaque: "plaque",
4179
+ can: "cylinder",
4180
+ cube: "cube",
4181
+ bevel: "beveledRectangle",
4182
+ foldedCorner: "foldedCorner",
4183
+ smileyFace: "smileyFace",
4184
+ heart: "heart",
4185
+ lightningBolt: "lightningBolt",
4186
+ sun: "sun",
4187
+ moon: "moon",
4188
+ cloud: "cloud",
4189
+ leftBracket: "leftBracket",
4190
+ rightBracket: "rightBracket",
4191
+ leftBrace: "leftBrace",
4192
+ rightBrace: "rightBrace",
4193
+ bracketPair: "bracketPair",
4194
+ bracePair: "bracePair",
4195
+ rect: "rectangle",
4196
+ roundRect: "roundedRectangle",
4197
+ round1Rect: "roundedCornerRectangle",
4198
+ round2SameRect: "topRoundedCornersRectangle",
4199
+ round2DiagRect: "diagonalRoundedCornersRectangle",
4200
+ snip1Rect: "snippedCornerRectangle",
4201
+ snip2SameRect: "topSnippedCornersRectangle",
4202
+ snip2DiagRect: "diagonalSnippedCornersRectangle",
4203
+ snipRoundRect: "roundedAndSnippedCornersRectangle",
4204
+ rightArrow: "rightArrow",
4205
+ leftArrow: "leftArrow",
4206
+ upArrow: "upArrow",
4207
+ downArrow: "downArrow",
4208
+ leftRightArrow: "leftRightArrow",
4209
+ upDownArrow: "upDownArrow",
4210
+ quadArrow: "quadArrow",
4211
+ leftRightUpArrow: "leftRightUpArrow",
4212
+ bentArrow: "bentArrow",
4213
+ uturnArrow: "uTurnArrow",
4214
+ leftUpArrow: "leftUpArrow",
4215
+ bentUpArrow: "bentUpArrow",
4216
+ curvedRightArrow: "curvedRightArrow",
4217
+ curvedLeftArrow: "curvedLeftArrow",
4218
+ curvedUpArrow: "curvedUpArrow",
4219
+ curvedDownArrow: "curvedDownArrow",
4220
+ stripedRightArrow: "stripedRightArrow",
4221
+ notchedRightArrow: "notchedRightArrow",
4222
+ homePlate: "pentagonArrow",
4223
+ chevron: "chevron",
4224
+ rightArrowCallout: "rightArrowCallout",
4225
+ downArrowCallout: "downArrowCallout",
4226
+ leftArrowCallout: "leftArrowCallout",
4227
+ upArrowCallout: "upArrowCallout",
4228
+ leftRightArrowCallout: "leftRightArrowCallout",
4229
+ upDownArrowCallout: "upDownArrowCallout",
4230
+ quadArrowCallout: "quadArrowCallout",
4231
+ circularArrow: "circularArrow",
4232
+ leftCircularArrow: "leftCircularArrow",
4233
+ leftRightCircularArrow: "leftRightCircularArrow",
4234
+ swooshArrow: "swooshArrow",
4235
+ mathPlus: "mathPlus",
4236
+ mathMinus: "mathMinus",
4237
+ mathMultiply: "mathMultiply",
4238
+ mathDivide: "mathDivide",
4239
+ mathEqual: "mathEqual",
4240
+ mathNotEqual: "mathNotEqual",
4241
+ flowChartProcess: "flowChartProcess",
4242
+ flowChartAlternateProcess: "flowChartAlternateProcess",
4243
+ flowChartDecision: "flowChartDecision",
4244
+ flowChartInputOutput: "flowChartInputOutput",
4245
+ flowChartPredefinedProcess: "flowChartPredefinedProcess",
4246
+ flowChartInternalStorage: "flowChartInternalStorage",
4247
+ flowChartDocument: "flowChartDocument",
4248
+ flowChartMultidocument: "flowChartMultidocument",
4249
+ flowChartTerminator: "flowChartTerminator",
4250
+ flowChartPreparation: "flowChartPreparation",
4251
+ flowChartManualInput: "flowChartManualInput",
4252
+ flowChartManualOperation: "flowChartManualOperation",
4253
+ flowChartConnector: "flowChartConnector",
4254
+ flowChartOffpageConnector: "flowChartOffpageConnector",
4255
+ flowChartPunchedCard: "flowChartPunchedCard",
4256
+ flowChartPunchedTape: "flowChartPunchedTape",
4257
+ flowChartSummingJunction: "flowChartSummingJunction",
4258
+ flowChartOr: "flowChartOr",
4259
+ flowChartCollate: "flowChartCollate",
4260
+ flowChartSort: "flowChartSort",
4261
+ flowChartExtract: "flowChartExtract",
4262
+ flowChartMerge: "flowChartMerge",
4263
+ flowChartOfflineStorage: "flowChartOfflineStorage",
4264
+ flowChartOnlineStorage: "flowChartOnlineStorage",
4265
+ flowChartDelay: "flowChartDelay",
4266
+ flowChartMagneticTape: "flowChartMagneticTape",
4267
+ flowChartMagneticDisk: "flowChartMagneticDisk",
4268
+ flowChartMagneticDrum: "flowChartMagneticDrum",
4269
+ flowChartDisplay: "flowChartDisplay",
4270
+ irregularSeal1: "explosion12",
4271
+ irregularSeal2: "explosion14",
4272
+ star4: "star4",
4273
+ star5: "star5",
4274
+ star6: "star6",
4275
+ star7: "star7",
4276
+ star8: "star8",
4277
+ star10: "star10",
4278
+ star12: "star12",
4279
+ star16: "star16",
4280
+ star24: "star24",
4281
+ star32: "star32",
4282
+ ribbon2: "ribbonUp",
4283
+ ribbon: "ribbonDown",
4284
+ ellipseRibbon2: "curvedRibbonUp",
4285
+ ellipseRibbon: "curvedRibbonDown",
4286
+ leftRightRibbon: "leftRightRibbon",
4287
+ verticalScroll: "verticalScroll",
4288
+ horizontalScroll: "horizontalScroll",
4289
+ wave: "wave",
4290
+ doubleWave: "doubleWave",
4291
+ wedgeRectCallout: "rectangularCallout",
4292
+ wedgeRoundRectCallout: "roundedRectangularCallout",
4293
+ wedgeEllipseCallout: "ellipticalCallout",
4294
+ cloudCallout: "cloudCallout",
4295
+ borderCallout1: "lineCallout",
4296
+ borderCallout2: "bentLineCallout",
4297
+ borderCallout3: "doubleBentLineCallout",
4298
+ accentCallout1: "lineCalloutWithAccentBar",
4299
+ accentCallout2: "bentLineCalloutWithAccentBar",
4300
+ accentCallout3: "doubleBentLineCalloutWithAccentBar",
4301
+ callout1: "lineCalloutWithNoBorder",
4302
+ callout2: "bentLineCalloutWithNoBorder",
4303
+ callout3: "doubleBentLineCalloutWithNoBorder",
4304
+ accentBorderCallout1: "lineCalloutWithBorderAndAccentBar",
4305
+ accentBorderCallout2: "bentLineCalloutWithBorderAndAccentBar",
4306
+ accentBorderCallout3: "doubleBentLineCalloutWithBorderAndAccentBar",
4307
+ actionButtonBlank: "actionButtonBlank",
4308
+ actionButtonHome: "actionButtonHome",
4309
+ actionButtonHelp: "actionButtonHelp",
4310
+ actionButtonInformation: "actionButtonInformation",
4311
+ actionButtonForwardNext: "actionButtonForwardNext",
4312
+ actionButtonBackPrevious: "actionButtonBackPrevious",
4313
+ actionButtonEnd: "actionButtonEnd",
4314
+ actionButtonBeginning: "actionButtonBeginning",
4315
+ actionButtonReturn: "actionButtonReturn",
4316
+ actionButtonDocument: "actionButtonDocument",
4317
+ actionButtonSound: "actionButtonSound",
4318
+ actionButtonMovie: "actionButtonMovie",
4319
+ gear6: "gear6",
4320
+ gear9: "gear9",
4321
+ funnel: "funnel",
4322
+ cornerTabs: "cornerTabs",
4323
+ squareTabs: "squareTabs",
4324
+ plaqueTabs: "plaqueTabs",
4325
+ chartX: "chartX",
4326
+ chartStar: "chartStar",
4327
+ chartPlus: "chartPlus"
4328
+ };
4329
+ OOXML_DASH_TO_DOCX = {
4330
+ solid: "solid",
4331
+ dot: "dot",
4332
+ dash: "dash",
4333
+ lgDash: "longDash",
4334
+ dashDot: "dashDot",
4335
+ lgDashDot: "longDashDot",
4336
+ lgDashDotDot: "longDashDotDot",
4337
+ sysDash: "shortDash",
4338
+ sysDot: "shortDot",
4339
+ sysDashDot: "shortDashDot",
4340
+ sysDashDotDot: "shortDashDotDot"
4341
+ };
4342
+ }
4343
+ });
4344
+
4345
+ // src/renderers/docxjs/drawingGroup.ts
4346
+ function chooseMarkers(ir) {
4347
+ const strings = [];
4348
+ collectStrings(ir, strings, /* @__PURE__ */ new WeakSet());
4349
+ const unused = (base) => {
4350
+ for (let n = 0; ; n += 1) {
4351
+ const candidate = n === 0 ? base : `${base}-${n}`;
4352
+ if (!strings.some((value) => value.includes(candidate))) return candidate;
4353
+ }
4354
+ };
4355
+ return {
4356
+ noAltText: unused(DEFAULT_MARKERS.noAltText),
4357
+ textBox: unused(DEFAULT_MARKERS.textBox)
4358
+ };
4359
+ }
4360
+ function collectStrings(node, out, seen) {
4361
+ if (typeof node === "string") {
4362
+ out.push(node);
4363
+ return;
4364
+ }
4365
+ if (!node || typeof node !== "object") return;
4366
+ if (node instanceof ArrayBuffer || ArrayBuffer.isView(node)) return;
4367
+ if (seen.has(node)) return;
4368
+ seen.add(node);
4369
+ if (node instanceof Map) {
4370
+ for (const [key, value] of node) {
4371
+ collectStrings(key, out, seen);
4372
+ collectStrings(value, out, seen);
4373
+ }
4374
+ return;
4375
+ }
4376
+ if (node instanceof Set) {
4377
+ for (const value of node) collectStrings(value, out, seen);
4378
+ return;
4379
+ }
4380
+ for (const value of Object.values(node)) collectStrings(value, out, seen);
4381
+ }
4382
+ function createDrawingGroupFactory(shapes2, pictures, markers) {
4383
+ return (group, resources) => new shapes2.ShapeGroupRun(
4384
+ drawingGroupOptions(group, resources, pictures, markers)
4385
+ );
4386
+ }
4387
+ function drawingGroupOptions(group, resources, pictures, markers = DEFAULT_MARKERS) {
4388
+ const canvasWidth = group.canvasWidthEmu;
4389
+ const canvasHeight = group.canvasHeightEmu;
4390
+ const near = (value, edge) => Math.abs(value - edge) <= OVERFLOW_TOLERANCE_EMU;
4391
+ const bounds = unrotatedBounds(group.children);
4392
+ const fillsCanvas = bounds !== void 0 && near(bounds.left, 0) && near(bounds.top, 0) && near(bounds.right, canvasWidth) && near(bounds.bottom, canvasHeight);
4393
+ const union = {
4394
+ left: Math.min(0, bounds?.left ?? 0),
4395
+ top: Math.min(0, bounds?.top ?? 0),
4396
+ right: Math.max(canvasWidth, bounds?.right ?? 0),
4397
+ bottom: Math.max(canvasHeight, bounds?.bottom ?? 0)
4398
+ };
4399
+ const overflows = union.left < -OVERFLOW_TOLERANCE_EMU || union.top < -OVERFLOW_TOLERANCE_EMU || union.right > canvasWidth + OVERFLOW_TOLERANCE_EMU || union.bottom > canvasHeight + OVERFLOW_TOLERANCE_EMU;
4400
+ const widthEmu = overflows ? (union.right - union.left) * group.widthEmu / canvasWidth : group.widthEmu;
4401
+ const heightEmu = overflows ? (union.bottom - union.top) * group.heightEmu / canvasHeight : group.heightEmu;
4402
+ return {
4403
+ children: [
4404
+ ...fillsCanvas ? [] : [canvasPad(canvasWidth, canvasHeight)],
4405
+ ...group.children.map(
4406
+ (child, index) => groupChild(child, index, resources, pictures, markers)
4407
+ )
4408
+ ],
4409
+ transformation: {
4410
+ width: emuToPixels(widthEmu),
4411
+ height: emuToPixels(heightEmu)
4412
+ },
4413
+ // `||`, not `??`: docx treats '' as no description too, and would then
4414
+ // describe the diagram from the child names.
4415
+ altText: { name: "", description: group.altText || markers.noAltText },
4416
+ ...group.floating ? { floating: floatingOptions(group.floating) } : {}
4417
+ };
4418
+ }
4419
+ function groupChild(child, index, resources, pictures, markers) {
4420
+ return child.kind === "picture" ? pictureChild(child, index, pictures) : shapeChild(child, index, resources, markers);
4421
+ }
4422
+ function canvasPad(canvasWidthEmu, canvasHeightEmu) {
4423
+ return {
4424
+ type: "rectangle",
4425
+ transformation: {
4426
+ offset: { left: 0, top: 0 },
4427
+ width: emuToPixels(canvasWidthEmu),
4428
+ height: emuToPixels(canvasHeightEmu)
4429
+ },
4430
+ fill: "none",
4431
+ line: "none",
4432
+ decorative: true,
4433
+ altText: { name: "Canvas" }
4434
+ };
4435
+ }
4436
+ function childTransformation(frame) {
4437
+ const rotation2 = frame.rotationDegrees;
4438
+ return {
4439
+ offset: { left: emuToPixels(frame.xEmu), top: emuToPixels(frame.yEmu) },
4440
+ width: emuToPixels(frame.widthEmu),
4441
+ height: emuToPixels(frame.heightEmu),
4442
+ ...rotation2 !== void 0 && rotation2 % 360 !== 0 ? { rotation: rotation2 } : {},
4443
+ ...frame.flipHorizontal || frame.flipVertical ? {
4444
+ flip: {
4445
+ ...frame.flipHorizontal ? { horizontal: true } : {},
4446
+ ...frame.flipVertical ? { vertical: true } : {}
4447
+ }
4448
+ } : {}
4449
+ };
4450
+ }
4451
+ function shapeChild(shape, index, resources, markers) {
4452
+ const paragraphs = shape.text?.paragraphs ?? [];
4453
+ return {
4454
+ type: docxPresetShape(shape.geometry),
4455
+ transformation: childTransformation(shape.frame),
4456
+ fill: shapeFill(shape.fill),
4457
+ line: shapeLine(shape.outline),
4458
+ altText: {
4459
+ name: shape.name ?? `Shape ${index + 1}`,
4460
+ ...shape.isTextBox ? { title: markers.textBox } : {}
4461
+ },
4462
+ // Never `children: []`: docx would write an empty `w:txbxContent`, which
4463
+ // the schema does not allow.
4464
+ ...paragraphs.length > 0 ? {
4465
+ children: paragraphs.map(
4466
+ (paragraph2) => emitParagraph(paragraph2, resources)
4467
+ )
4468
+ } : {},
4469
+ ...shape.text ? { textOptions: textOptions(shape.text) } : {}
4470
+ };
4471
+ }
4472
+ function pictureChild(picture2, index, pictures) {
4473
+ const crop = picture2.crop;
4474
+ return {
4475
+ type: "picture",
4476
+ image: pictures(picture2.resourceId, {
4477
+ widthEmu: picture2.frame.widthEmu,
4478
+ heightEmu: picture2.frame.heightEmu
4479
+ }),
4480
+ transformation: childTransformation(picture2.frame),
4481
+ ...crop ? {
4482
+ crop: {
4483
+ ...crop.left !== void 0 ? { left: crop.left * 100 } : {},
4484
+ ...crop.top !== void 0 ? { top: crop.top * 100 } : {},
4485
+ ...crop.right !== void 0 ? { right: crop.right * 100 } : {},
4486
+ ...crop.bottom !== void 0 ? { bottom: crop.bottom * 100 } : {}
4487
+ }
4488
+ } : {},
4489
+ altText: {
4490
+ name: picture2.name ?? `Picture ${index + 1}`,
4491
+ ...picture2.altText ? { description: picture2.altText } : {}
4492
+ }
4493
+ };
4494
+ }
4495
+ function shapeFill(fill) {
4496
+ if (!fill || fill.kind === "none") return "none";
4497
+ return {
4498
+ type: "solid",
4499
+ color: fill.color.hex,
4500
+ ...fill.transparencyPercent !== void 0 ? { transparency: clamp(fill.transparencyPercent, 0, 100) } : {}
4501
+ };
4502
+ }
4503
+ function shapeLine(outline) {
4504
+ if (!outline || !outline.color) return "none";
4505
+ return {
4506
+ color: outline.color.hex,
4507
+ // An omitted width is office-open's omitted `w`, a hairline, where docx's
4508
+ // own default would be 1pt.
4509
+ width: outline.widthEmu !== void 0 ? clamp(outline.widthEmu / EMU_PER_POINT, 0, MAX_POINTS) : 0,
4510
+ ...outline.dash ? { dash: docxLineDash(outline.dash) } : {}
4511
+ };
4512
+ }
4513
+ function textOptions(text) {
4514
+ const insets = text.insetsEmu;
4515
+ const points = (emu) => clamp(emu / EMU_PER_POINT, 0, MAX_POINTS);
4516
+ const margins = insets ? {
4517
+ ...insets.top !== void 0 ? { top: points(insets.top) } : {},
4518
+ ...insets.right !== void 0 ? { right: points(insets.right) } : {},
4519
+ ...insets.bottom !== void 0 ? { bottom: points(insets.bottom) } : {},
4520
+ ...insets.left !== void 0 ? { left: points(insets.left) } : {}
4521
+ } : void 0;
4522
+ return {
4523
+ verticalAlignment: VERTICAL_ALIGNMENT[text.anchor ?? "top"],
4524
+ ...margins && Object.keys(margins).length > 0 ? { margins } : {}
4525
+ };
4526
+ }
4527
+ function unrotatedBounds(children) {
4528
+ if (children.length === 0) return void 0;
4529
+ let left = Infinity;
4530
+ let top = Infinity;
4531
+ let right = -Infinity;
4532
+ let bottom = -Infinity;
4533
+ for (const { frame } of children) {
4534
+ left = Math.min(left, frame.xEmu);
4535
+ top = Math.min(top, frame.yEmu);
4536
+ right = Math.max(right, frame.xEmu + frame.widthEmu);
4537
+ bottom = Math.max(bottom, frame.yEmu + frame.heightEmu);
4538
+ }
4539
+ return { left, top, right, bottom };
4540
+ }
4541
+ function clamp(value, low, high) {
4542
+ return Math.min(high, Math.max(low, value));
4543
+ }
4544
+ var DEFAULT_MARKERS, EMU_PER_POINT, MAX_POINTS, OVERFLOW_TOLERANCE_EMU, VERTICAL_ALIGNMENT;
4545
+ var init_drawingGroup = __esm({
4546
+ "src/renderers/docxjs/drawingGroup.ts"() {
4547
+ "use strict";
4548
+ init_units();
4549
+ init_emit();
4550
+ init_presetShapes();
4551
+ DEFAULT_MARKERS = {
4552
+ noAltText: "jto:no-alt-text",
4553
+ textBox: "jto:text-box"
4554
+ };
4555
+ EMU_PER_POINT = 12700;
4556
+ MAX_POINTS = 1584;
4557
+ OVERFLOW_TOLERANCE_EMU = 2;
4558
+ VERTICAL_ALIGNMENT = {
4559
+ top: "top",
4560
+ middle: "center",
4561
+ bottom: "bottom"
4562
+ };
4563
+ }
4564
+ });
4565
+
4566
+ // src/renderers/docxjs/drawingGroupRepair.ts
4567
+ import AdmZip3 from "adm-zip";
4568
+ function repairDrawingGroupsInBuffer(buffer, markers) {
4569
+ const textBox = new RegExp(
4570
+ `(<wps:cNvPr\\b[^>]*?) title="${escapeRegExp2(markers.textBox)}"(\\s*/>)<wps:cNvSpPr/>`,
4571
+ "g"
4572
+ );
4573
+ const noAltText = new RegExp(
4574
+ `(<wp:docPr\\b[^>]*?) descr="${escapeRegExp2(markers.noAltText)}"`,
4575
+ "g"
4576
+ );
4577
+ const zip = new AdmZip3(buffer);
4578
+ const parts = zip.getEntries().filter((entry) => DRAWING_PART.test(entry.entryName)).sort((a, b) => compareDrawingParts(a.entryName, b.entryName));
4579
+ const texts = new Map(
4580
+ parts.map((entry) => [entry, entry.getData().toString("utf8")])
4581
+ );
4582
+ let highest = 0;
4583
+ for (const xml of texts.values()) {
4584
+ for (const match of xml.matchAll(DOC_PR_ID)) {
4585
+ highest = Math.max(highest, Number(match[1]));
4586
+ }
4587
+ for (const match of xml.replace(GROUP, "").matchAll(CNVPR_ID)) {
4588
+ highest = Math.max(highest, Number(match[1]));
4589
+ }
4590
+ }
4591
+ let next = highest + 1;
4592
+ for (const [entry, xml] of texts) {
4593
+ if (!xml.includes("<wpg:wgp")) continue;
4594
+ const repaired = xml.replace(
4595
+ GROUP,
4596
+ (span) => span.replace(CHILD_ID, (_match, head) => `${head}id="${next++}"`).replace(
4597
+ ROTATION,
4598
+ (_match, head, value) => `${head}rot="${Math.round(Number(value))}"`
4599
+ ).replace(textBox, '$1$2<wps:cNvSpPr txBox="1"/>')
4600
+ ).replace(noAltText, "$1");
4601
+ texts.set(entry, repaired);
4602
+ zip.updateFile(entry, Buffer.from(repaired, "utf8"));
4603
+ }
4604
+ for (const [entry, xml] of texts) {
4605
+ if (xml.includes(markers.noAltText) || xml.includes(markers.textBox)) {
4606
+ throw new Error(
4607
+ `the drawing-group repair left a marker in ${entry.entryName}`
4608
+ );
4609
+ }
4610
+ }
4611
+ return zip.toBuffer();
4612
+ }
4613
+ var GROUP, DOC_PR_ID, CNVPR_ID, CHILD_ID, ROTATION, escapeRegExp2;
4614
+ var init_drawingGroupRepair = __esm({
4615
+ "src/renderers/docxjs/drawingGroupRepair.ts"() {
4616
+ "use strict";
4617
+ init_fixFloatingImageIds();
4618
+ GROUP = /<wpg:wgp\b[^>]*>[\s\S]*?<\/wpg:wgp>/g;
4619
+ DOC_PR_ID = /<wp:docPr\b[^>]*?\sid="(\d+)"/g;
4620
+ CNVPR_ID = /<(?:wps|pic|wpg):cNvPr\b[^>]*?\sid="(\d+)"/g;
4621
+ CHILD_ID = /(<(?:wps|pic|wpg):cNvPr\b[^>]*?\s)id="\d+"/g;
4622
+ ROTATION = /(<a:xfrm\b[^>]*?\s)rot="([^"]*)"/g;
4623
+ escapeRegExp2 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3799
4624
  }
3800
4625
  });
3801
4626
 
@@ -3824,18 +4649,32 @@ function createDocxJsRenderer() {
3824
4649
  format: "docx",
3825
4650
  capabilities: DOCXJS_CAPABILITIES,
3826
4651
  async render(ir, renderOptions) {
3827
- const resources = await prepareImages(
4652
+ await loadDocxCharts();
4653
+ const prepared = await prepareImages(
3828
4654
  ir,
3829
4655
  renderOptions?.svgRasterFallback
3830
4656
  );
4657
+ const markers = prepared.drawingGroups > 0 ? chooseMarkers(ir) : void 0;
4658
+ let resources = prepared.images;
4659
+ if (markers) {
4660
+ await shapes.load();
4661
+ resources = Object.assign(prepared.images, {
4662
+ drawingGroup: createDrawingGroupFactory(
4663
+ shapes.get(),
4664
+ prepared.pictureSource,
4665
+ markers
4666
+ )
4667
+ });
4668
+ }
3831
4669
  const document = buildDocument(ir, resources);
3832
4670
  const packed = await Packer.toBuffer(document);
3833
4671
  const fixed = fixFloatingImageIdsInBuffer(packed);
4672
+ const repaired = markers ? repairDrawingGroupsInBuffer(fixed, markers) : fixed;
3834
4673
  if (renderOptions?.deterministic === false)
3835
- return new Uint8Array(normalizeDocxCaseBuffer(fixed));
4674
+ return new Uint8Array(normalizeDocxCaseBuffer(repaired));
3836
4675
  return new Uint8Array(
3837
4676
  canonicalizeDocxBuffer(
3838
- fixed,
4677
+ repaired,
3839
4678
  resolveGenerationDate({
3840
4679
  deterministic: renderOptions?.deterministic,
3841
4680
  generatedAt: renderOptions?.generatedAt
@@ -3848,6 +4687,7 @@ function createDocxJsRenderer() {
3848
4687
  function buildDocument(ir, resources = /* @__PURE__ */ new Map()) {
3849
4688
  return new Document({
3850
4689
  styles: emitStyles(ir.styles),
4690
+ ...ir.theme ? { theme: themeOptions(ir.theme) } : {},
3851
4691
  sections: ir.sections.map(
3852
4692
  (section2, index) => sectionOptions(section2, resources, index === ir.sections.length - 1)
3853
4693
  ),
@@ -3879,6 +4719,21 @@ function buildDocument(ir, resources = /* @__PURE__ */ new Map()) {
3879
4719
  } : {}
3880
4720
  });
3881
4721
  }
4722
+ function themeOptions(theme) {
4723
+ const colors = {};
4724
+ for (const [slot, color] of Object.entries(theme.colors)) {
4725
+ if (color) colors[slot] = color.hex;
4726
+ }
4727
+ const fonts = {
4728
+ ...theme.headingFont ? { headings: theme.headingFont } : {},
4729
+ ...theme.bodyFont ? { body: theme.bodyFont } : {}
4730
+ };
4731
+ return {
4732
+ name: theme.name,
4733
+ ...Object.keys(colors).length > 0 ? { colors } : {},
4734
+ ...Object.keys(fonts).length > 0 ? { fonts } : {}
4735
+ };
4736
+ }
3882
4737
  function noteBodies(notes, resources) {
3883
4738
  const bodies = {};
3884
4739
  for (const note of notes) {
@@ -3916,7 +4771,7 @@ function numberingConfig(numbering) {
3916
4771
  };
3917
4772
  }
3918
4773
  async function prepareImages(ir, svgRasterFallback) {
3919
- const placements = collectImagePlacements(ir);
4774
+ const { placements, drawingGroups } = collectImagePlacements(ir);
3920
4775
  const jobs = [];
3921
4776
  for (const resource of ir.resources) {
3922
4777
  if (resource.kind !== "image" || resource.mediaType !== "svg") continue;
@@ -3964,16 +4819,55 @@ async function prepareImages(ir, svgRasterFallback) {
3964
4819
  });
3965
4820
  });
3966
4821
  }
3967
- return resources;
4822
+ const pictureSource = (resourceId, placement) => {
4823
+ const resource = ir.resources.find(
4824
+ (candidate) => candidate.kind === "image" && candidate.id === resourceId
4825
+ );
4826
+ if (!resource) {
4827
+ throw new Error(`no image was prepared for resource "${resourceId}"`);
4828
+ }
4829
+ const data = Buffer.from(resource.bytes);
4830
+ if (resource.mediaType !== "svg") {
4831
+ return {
4832
+ type: resource.mediaType,
4833
+ data
4834
+ };
4835
+ }
4836
+ const size = `${emuToPixels(placement.widthEmu)}x${emuToPixels(placement.heightEmu)}`;
4837
+ return {
4838
+ type: "svg",
4839
+ data,
4840
+ fallback: {
4841
+ type: "png",
4842
+ data: rasters.get(`${resourceId}:${size}`) ?? data
4843
+ }
4844
+ };
4845
+ };
4846
+ return { images: resources, pictureSource, drawingGroups };
3968
4847
  }
3969
4848
  function collectImagePlacements(ir) {
3970
4849
  const placements = /* @__PURE__ */ new Map();
4850
+ let drawingGroups = 0;
4851
+ const record = (resourceId, widthEmu, heightEmu) => {
4852
+ const key = `${emuToPixels(widthEmu)}x${emuToPixels(heightEmu)}`;
4853
+ const sizes = placements.get(resourceId) ?? /* @__PURE__ */ new Set();
4854
+ sizes.add(key);
4855
+ placements.set(resourceId, sizes);
4856
+ };
3971
4857
  const visitInline = (inline) => {
3972
4858
  if (inline.kind === "image") {
3973
- const key = `${emuToPixels(inline.widthEmu)}x${emuToPixels(inline.heightEmu)}`;
3974
- const sizes = placements.get(inline.resourceId) ?? /* @__PURE__ */ new Set();
3975
- sizes.add(key);
3976
- placements.set(inline.resourceId, sizes);
4859
+ record(inline.resourceId, inline.widthEmu, inline.heightEmu);
4860
+ return;
4861
+ }
4862
+ if (inline.kind === "drawingGroup") {
4863
+ drawingGroups += 1;
4864
+ for (const child of inline.children) {
4865
+ if (child.kind === "picture") {
4866
+ record(child.resourceId, child.frame.widthEmu, child.frame.heightEmu);
4867
+ } else {
4868
+ child.text?.paragraphs.forEach(visitBlock);
4869
+ }
4870
+ }
3977
4871
  return;
3978
4872
  }
3979
4873
  if (inline.kind === "hyperlink" || inline.kind === "revision") {
@@ -4004,7 +4898,7 @@ function collectImagePlacements(ir) {
4004
4898
  for (const note of [...ir.footnotes, ...ir.endnotes]) {
4005
4899
  note.children.forEach(visitBlock);
4006
4900
  }
4007
- return placements;
4901
+ return { placements, drawingGroups };
4008
4902
  }
4009
4903
  function sectionOptions(section2, resources, closesDocument = false) {
4010
4904
  const { page, columns } = section2.properties;
@@ -4117,17 +5011,26 @@ function coreProperties(ir) {
4117
5011
  ...metadata.custom?.length ? { customProperties: metadata.custom } : {}
4118
5012
  };
4119
5013
  }
4120
- var DOCXJS_RENDERER_ID, DOCXJS_CAPABILITIES;
5014
+ var DOCXJS_RENDERER_ID, shapes, DOCXJS_CAPABILITIES;
4121
5015
  var init_docxjs = __esm({
4122
5016
  "src/renderers/docxjs/index.ts"() {
4123
5017
  "use strict";
4124
5018
  init_styles2();
5019
+ init_charts();
4125
5020
  init_imageUtils();
4126
5021
  init_packageDocument();
4127
5022
  init_fixFloatingImageIds();
4128
5023
  init_emit();
5024
+ init_docxSubpath();
5025
+ init_drawingGroup();
5026
+ init_drawingGroupRepair();
4129
5027
  init_units();
4130
5028
  DOCXJS_RENDERER_ID = "docxjs";
5029
+ shapes = docxSubpath(
5030
+ "docx/shapes",
5031
+ "visual",
5032
+ () => import("docx/shapes")
5033
+ );
4131
5034
  DOCXJS_CAPABILITIES = /* @__PURE__ */ new Set([
4132
5035
  "paragraphs",
4133
5036
  "styles",
@@ -4140,8 +5043,10 @@ var init_docxjs = __esm({
4140
5043
  "images",
4141
5044
  "floating-images",
4142
5045
  "svg-images",
5046
+ "charts",
4143
5047
  "text-frames",
4144
5048
  "text-boxes",
5049
+ "drawing-groups",
4145
5050
  "toc",
4146
5051
  "cached-toc",
4147
5052
  "fields",
@@ -4162,124 +5067,332 @@ var init_docxjs = __esm({
4162
5067
  }
4163
5068
  });
4164
5069
 
4165
- // src/utils/chartWorkbook.ts
4166
- import AdmZip3 from "adm-zip";
4167
- import { chartWorkbookParts } from "@json-to-office/shared/rendering";
4168
- import {
4169
- CHART_WORKBOOK_SHEET_NAME,
4170
- categoryReference,
4171
- columnLetter,
4172
- seriesNameReference,
4173
- seriesValueReference
4174
- } from "@json-to-office/shared/rendering";
4175
- function buildChartWorkbook(series) {
4176
- const zip = new AdmZip3();
4177
- for (const [name, content] of chartWorkbookParts(series)) {
4178
- zip.addFile(name, Buffer.from(content, "utf8"));
5070
+ // src/renderers/office-open/chartLook.ts
5071
+ function seriesLook(chartType) {
5072
+ switch (chartType) {
5073
+ case "line":
5074
+ case "scatter":
5075
+ return { marker: MARKER, smooth: false };
5076
+ case "radar":
5077
+ return { marker: MARKER };
5078
+ case "column":
5079
+ case "bar":
5080
+ return { invertIfNegative: false };
5081
+ default:
5082
+ return {};
4179
5083
  }
4180
- for (const entry of zip.getEntries()) {
4181
- entry.header.timeval = WORKBOOK_TIMESTAMP;
5084
+ }
5085
+ function axesLook(chartType) {
5086
+ switch (chartType) {
5087
+ case "column":
5088
+ case "bar":
5089
+ case "line":
5090
+ case "area": {
5091
+ const horizontal = chartType === "bar";
5092
+ return {
5093
+ categoryAxis: {
5094
+ position: horizontal ? "l" : "b",
5095
+ ...NO_TICKS,
5096
+ line: AXIS_LINE
5097
+ },
5098
+ valueAxis: {
5099
+ position: horizontal ? "b" : "l",
5100
+ gridLine: GRIDLINES,
5101
+ ...NO_TICKS,
5102
+ lineVisible: false,
5103
+ crossBetween: chartType === "area" ? "midCat" : "between"
5104
+ }
5105
+ };
5106
+ }
5107
+ case "radar":
5108
+ return {
5109
+ categoryAxis: {
5110
+ position: "b",
5111
+ gridLine: GRIDLINES,
5112
+ ...NO_TICKS,
5113
+ majorTickMark: "cross",
5114
+ line: AXIS_LINE
5115
+ },
5116
+ valueAxis: {
5117
+ position: "l",
5118
+ gridLine: GRIDLINES,
5119
+ ...NO_TICKS,
5120
+ majorTickMark: "cross",
5121
+ line: AXIS_LINE,
5122
+ crossBetween: "between"
5123
+ }
5124
+ };
5125
+ case "scatter": {
5126
+ const line = { ...AXIS_LINE, color: SCATTER_AXIS_TINT };
5127
+ return {
5128
+ categoryAxis: {
5129
+ position: "b",
5130
+ ...NO_TICKS,
5131
+ line,
5132
+ crossBetween: "midCat"
5133
+ },
5134
+ valueAxis: {
5135
+ position: "l",
5136
+ gridLine: GRIDLINES,
5137
+ ...NO_TICKS,
5138
+ line,
5139
+ crossBetween: "midCat"
5140
+ }
5141
+ };
5142
+ }
5143
+ default:
5144
+ return {};
4182
5145
  }
4183
- return new Uint8Array(zip.toBuffer());
4184
5146
  }
4185
- var WORKBOOK_TIMESTAMP;
4186
- var init_chartWorkbook = __esm({
4187
- "src/utils/chartWorkbook.ts"() {
5147
+ function plotLook(chart) {
5148
+ const common = {
5149
+ ...axesLook(chart.chartType),
5150
+ autoTitleDeleted: !(chart.title && chart.showTitle !== false),
5151
+ plotAreaUnfilled: true
5152
+ };
5153
+ switch (chart.chartType) {
5154
+ case "column":
5155
+ return { ...common, dataBorder: "none", gapWidth: 219, overlap: -27 };
5156
+ case "bar":
5157
+ return { ...common, dataBorder: "none", gapWidth: 182, overlap: 0 };
5158
+ case "area":
5159
+ return { ...common, dataBorder: "none" };
5160
+ case "line":
5161
+ return { ...common, ...LINE_SERIES, lineMarkers: true };
5162
+ case "radar":
5163
+ return { ...common, ...LINE_SERIES, radarStyle: "marker" };
5164
+ case "scatter": {
5165
+ const labels = chart.series[0]?.labels ?? [];
5166
+ return {
5167
+ ...common,
5168
+ ...LINE_SERIES,
5169
+ lineWidthPoints: 1.5,
5170
+ scatterStyle: "lineMarker",
5171
+ scatterXValues: labels.map((_, index) => scatterX(labels, index))
5172
+ };
5173
+ }
5174
+ case "pie":
5175
+ case "doughnut":
5176
+ return {
5177
+ ...common,
5178
+ dataBorder: { widthPoints: 1.5, color: { scheme: "lt1" } },
5179
+ firstSliceAngle: 0,
5180
+ ...chart.chartType === "doughnut" ? { holeSize: 50 } : {}
5181
+ };
5182
+ default:
5183
+ return common;
5184
+ }
5185
+ }
5186
+ var TEXT_1_TINT, SCATTER_AXIS_TINT, HAIRLINE_POINTS, GRIDLINES, AXIS_LINE, NO_TICKS, LINE_SERIES, MARKER;
5187
+ var init_chartLook = __esm({
5188
+ "src/renderers/office-open/chartLook.ts"() {
4188
5189
  "use strict";
4189
- init_packageDocument();
4190
- WORKBOOK_TIMESTAMP = toDosTime(DEFAULT_GENERATION_DATE);
5190
+ init_chartValues();
5191
+ TEXT_1_TINT = { scheme: "tx1", lumMod: 15e3, lumOff: 85e3 };
5192
+ SCATTER_AXIS_TINT = {
5193
+ scheme: "tx1",
5194
+ lumMod: 25e3,
5195
+ lumOff: 75e3
5196
+ };
5197
+ HAIRLINE_POINTS = 0.75;
5198
+ GRIDLINES = {
5199
+ size: HAIRLINE_POINTS,
5200
+ color: TEXT_1_TINT
5201
+ };
5202
+ AXIS_LINE = {
5203
+ widthPoints: HAIRLINE_POINTS,
5204
+ color: TEXT_1_TINT
5205
+ };
5206
+ NO_TICKS = {
5207
+ majorTickMark: "none",
5208
+ minorTickMark: "none",
5209
+ tickLabelPosition: "nextTo"
5210
+ };
5211
+ LINE_SERIES = {
5212
+ lineWidthPoints: 2.25,
5213
+ lineCap: "rnd",
5214
+ lineJoin: "round",
5215
+ markerLineWidthPoints: HAIRLINE_POINTS
5216
+ };
5217
+ MARKER = { symbol: "circle", size: 5 };
4191
5218
  }
4192
5219
  });
4193
5220
 
4194
5221
  // src/renderers/office-open/chartParts.ts
4195
5222
  import {
4196
- CHART_WORKBOOK_CONTENT_TYPE,
4197
- chartWorkbookRelsXml,
4198
- matchChartParts,
4199
- spliceChartXml
5223
+ finishChartXml,
5224
+ matchChartParts
4200
5225
  } from "@json-to-office/shared/rendering";
4201
- function spliceInput(chart) {
4202
- const { bold, ...rest } = chart.textFont;
4203
- const textFont = rest;
4204
- const axisTitleFont = { ...rest, bold };
4205
- const chartTitleFont = {
4206
- ...rest,
4207
- fontSize: Math.round(rest.fontSize * CHART_TITLE_SCALE),
4208
- bold: true
5226
+ function chartInput(chart) {
5227
+ const roles = chartTextRoles(chart.textFont);
5228
+ const textFont = roles.text;
5229
+ const axisTitleFont = roles.axisTitle;
5230
+ const chartTitleFont = roles.title;
5231
+ const look = plotLook(chart);
5232
+ const categoryAxis = {
5233
+ ...look.categoryAxis,
5234
+ ...chart.categoryAxisTitle ? { title: chart.categoryAxisTitle, titleFont: axisTitleFont } : {}
5235
+ };
5236
+ const valueAxis = {
5237
+ ...look.valueAxis,
5238
+ ...chart.valueAxisTitle ? { title: chart.valueAxisTitle, titleFont: axisTitleFont } : {}
4209
5239
  };
4210
5240
  return {
5241
+ ...look,
4211
5242
  chartType: chart.chartType,
4212
5243
  series: chart.series,
4213
5244
  colors: chart.colors,
5245
+ ...chart.title && chart.showTitle !== false ? { title: chart.title } : {},
4214
5246
  textFont,
4215
5247
  titleFont: chartTitleFont,
4216
5248
  ...chart.legendPosition ? { legendPosition: chart.legendPosition } : {},
4217
- ...chart.categoryAxisTitle ? {
4218
- categoryAxis: {
4219
- title: chart.categoryAxisTitle,
4220
- titleFont: axisTitleFont
4221
- }
4222
- } : {},
4223
- ...chart.valueAxisTitle ? {
4224
- valueAxis: { title: chart.valueAxisTitle, titleFont: axisTitleFont }
4225
- } : {}
5249
+ ...Object.keys(categoryAxis).length > 0 ? { categoryAxis } : {},
5250
+ ...Object.keys(valueAxis).length > 0 ? { valueAxis } : {}
4226
5251
  };
4227
5252
  }
4228
- function declareWorkbookContentType(zip) {
4229
- const entry = zip.getEntry("[Content_Types].xml");
4230
- if (!entry) return;
4231
- const xml = entry.getData().toString("utf8");
4232
- if (xml.includes(`Extension="xlsx"`)) return;
4233
- zip.updateFile(
4234
- entry,
4235
- Buffer.from(
4236
- xml.replace(
4237
- '<Default Extension="xml"',
4238
- `<Default Extension="xlsx" ContentType="${CHART_WORKBOOK_CONTENT_TYPE}"/><Default Extension="xml"`
4239
- ),
4240
- "utf8"
4241
- )
4242
- );
4243
- }
4244
- function spliceChartParts(zip, charts) {
4245
- if (charts.length === 0) return;
5253
+ function finishChartParts(zip, charts2) {
5254
+ if (charts2.length === 0) return;
4246
5255
  const parts = zip.getEntries().map((entry) => entry.entryName).map((name) => name.match(/^word\/charts\/chart(\d+)\.xml$/)).filter((match) => match !== null).map(
4247
5256
  (match) => [
4248
5257
  Number(match[1]),
4249
5258
  zip.getEntry(match[0]).getData().toString("utf8")
4250
5259
  ]
4251
5260
  );
4252
- for (const { ordinal, xml, chart } of matchChartParts(parts, charts)) {
4253
- const workbookName = `chart${ordinal}.xlsx`;
5261
+ const inputs = charts2.map(chartInput);
5262
+ for (const { ordinal, xml, chart } of matchChartParts(parts, inputs)) {
5263
+ const finished = finishChartXml(xml, chart);
5264
+ if (finished === xml) continue;
4254
5265
  zip.updateFile(
4255
5266
  zip.getEntry(`word/charts/chart${ordinal}.xml`),
4256
- Buffer.from(spliceChartXml(xml, spliceInput(chart)), "utf8")
5267
+ Buffer.from(finished, "utf8")
4257
5268
  );
4258
- zip.addFile(
4259
- `word/embeddings/${workbookName}`,
4260
- Buffer.from(buildChartWorkbook(chart.series))
5269
+ }
5270
+ }
5271
+ var init_chartParts = __esm({
5272
+ "src/renderers/office-open/chartParts.ts"() {
5273
+ "use strict";
5274
+ init_chartText();
5275
+ init_chartLook();
5276
+ }
5277
+ });
5278
+
5279
+ // src/renderers/office-open/themePart.ts
5280
+ function replaceOnce(xml, pattern, replacer, what) {
5281
+ const count = [...xml.matchAll(pattern)].length;
5282
+ if (count !== 1) {
5283
+ throw new Error(
5284
+ `office-open's theme part has ${count} ${what}, expected one`
4261
5285
  );
4262
- zip.addFile(
4263
- `word/charts/_rels/chart${ordinal}.xml.rels`,
4264
- Buffer.from(chartWorkbookRelsXml(workbookName), "utf8")
5286
+ }
5287
+ return xml.replace(pattern, replacer);
5288
+ }
5289
+ function spliceTheme(zip, theme) {
5290
+ const entry = zip.getEntry(THEME_PART);
5291
+ if (!entry) throw new Error(`office-open wrote no ${THEME_PART}`);
5292
+ let xml = entry.getData().toString("utf8");
5293
+ const name = esc(theme.name);
5294
+ xml = renamed(xml, "theme", name);
5295
+ const colors = Object.entries(theme.colors);
5296
+ if (colors.some(([, color]) => color)) {
5297
+ xml = renamed(xml, "clrScheme", name);
5298
+ for (const [slot, color] of colors) {
5299
+ if (!color) continue;
5300
+ const element = SCHEME_ELEMENT[slot];
5301
+ xml = replaceOnce(
5302
+ xml,
5303
+ new RegExp(`<a:${element}>[\\s\\S]*?</a:${element}>`, "g"),
5304
+ () => `<a:${element}><a:srgbClr val="${color.hex}"/></a:${element}>`,
5305
+ `a:${element}`
5306
+ );
5307
+ }
5308
+ }
5309
+ if (theme.headingFont || theme.bodyFont) {
5310
+ xml = renamed(xml, "fontScheme", name);
5311
+ for (const [collection, key, officeFace] of OFFICE_FACES) {
5312
+ const family = theme[key];
5313
+ if (family === void 0 || family === officeFace) continue;
5314
+ xml = replaceOnce(
5315
+ xml,
5316
+ new RegExp(`(<a:${collection}>)<a:latin\\b[^>]*/>`, "g"),
5317
+ (_match, open) => `${open}<a:latin typeface="${esc(family)}"/>`,
5318
+ `a:${collection} a:latin`
5319
+ );
5320
+ }
5321
+ }
5322
+ zip.updateFile(THEME_PART, Buffer.from(xml, "utf8"));
5323
+ }
5324
+ var THEME_PART, SCHEME_ELEMENT, OFFICE_FACES, esc, renamed;
5325
+ var init_themePart = __esm({
5326
+ "src/renderers/office-open/themePart.ts"() {
5327
+ "use strict";
5328
+ THEME_PART = "word/theme/theme1.xml";
5329
+ SCHEME_ELEMENT = {
5330
+ dark1: "dk1",
5331
+ light1: "lt1",
5332
+ dark2: "dk2",
5333
+ light2: "lt2",
5334
+ accent1: "accent1",
5335
+ accent2: "accent2",
5336
+ accent3: "accent3",
5337
+ accent4: "accent4",
5338
+ accent5: "accent5",
5339
+ accent6: "accent6",
5340
+ hyperlink: "hlink",
5341
+ followedHyperlink: "folHlink"
5342
+ };
5343
+ OFFICE_FACES = [
5344
+ ["majorFont", "headingFont", "Calibri Light"],
5345
+ ["minorFont", "bodyFont", "Calibri"]
5346
+ ];
5347
+ esc = (value) => value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5348
+ renamed = (xml, element, name) => replaceOnce(
5349
+ xml,
5350
+ new RegExp(`(<a:${element}\\b[^>]*?\\bname=")[^"]*(")`, "g"),
5351
+ (_match, open, close) => `${open}${name}${close}`,
5352
+ `a:${element} name`
4265
5353
  );
4266
5354
  }
4267
- declareWorkbookContentType(zip);
5355
+ });
5356
+
5357
+ // src/utils/chartWorkbook.ts
5358
+ import AdmZip4 from "adm-zip";
5359
+ import { chartWorkbookParts } from "@json-to-office/shared/rendering";
5360
+ import {
5361
+ CHART_WORKBOOK_SHEET_NAME,
5362
+ categoryReference,
5363
+ columnLetter,
5364
+ seriesNameReference,
5365
+ seriesValueReference
5366
+ } from "@json-to-office/shared/rendering";
5367
+ function buildChartWorkbook(series, options = {}) {
5368
+ const zip = new AdmZip4();
5369
+ for (const [name, content] of chartWorkbookParts(series, options)) {
5370
+ zip.addFile(name, Buffer.from(content, "utf8"));
5371
+ }
5372
+ for (const entry of zip.getEntries()) {
5373
+ entry.header.timeval = WORKBOOK_TIMESTAMP;
5374
+ }
5375
+ return new Uint8Array(zip.toBuffer());
4268
5376
  }
4269
- var CHART_TITLE_SCALE;
4270
- var init_chartParts = __esm({
4271
- "src/renderers/office-open/chartParts.ts"() {
5377
+ var WORKBOOK_TIMESTAMP;
5378
+ var init_chartWorkbook = __esm({
5379
+ "src/utils/chartWorkbook.ts"() {
4272
5380
  "use strict";
4273
- init_chartWorkbook();
4274
- CHART_TITLE_SCALE = 1.4;
5381
+ init_packageDocument();
5382
+ WORKBOOK_TIMESTAMP = toDosTime(DEFAULT_GENERATION_DATE);
4275
5383
  }
4276
5384
  });
4277
5385
 
4278
5386
  // src/renderers/office-open/emit.ts
4279
- import { assertNever as assertNever2 } from "@json-to-office/shared/rendering";
5387
+ import { assertNever as assertNever3 } from "@json-to-office/shared/rendering";
5388
+ import { chartLook, withSeriesLook } from "@json-to-office/shared/rendering";
4280
5389
  function emptyContext() {
4281
5390
  let next = 1;
4282
- return { pictures: /* @__PURE__ */ new Map(), nextDrawingId: () => next++, charts: [] };
5391
+ return {
5392
+ pictures: /* @__PURE__ */ new Map(),
5393
+ nextDrawingId: () => next++,
5394
+ charts: []
5395
+ };
4283
5396
  }
4284
5397
  function simpleField(instruction, cachedText) {
4285
5398
  return {
@@ -4289,6 +5402,9 @@ function simpleField(instruction, cachedText) {
4289
5402
  }
4290
5403
  };
4291
5404
  }
5405
+ function twips(value) {
5406
+ return Math.floor(value);
5407
+ }
4292
5408
  function runProperties2(formatting) {
4293
5409
  if (!formatting) return {};
4294
5410
  const out = {};
@@ -4316,16 +5432,19 @@ function runProperties2(formatting) {
4316
5432
  if (formatting.doubleStrike !== void 0) {
4317
5433
  out.doubleStrike = formatting.doubleStrike;
4318
5434
  }
4319
- if (formatting.superScript) out.superScript = true;
4320
- if (formatting.subScript) out.subScript = true;
5435
+ if (formatting.superScript) out.verticalAlign = "superscript";
5436
+ if (formatting.subScript) out.verticalAlign = "subscript";
4321
5437
  if (formatting.smallCaps !== void 0) out.smallCaps = formatting.smallCaps;
4322
5438
  if (formatting.allCaps !== void 0) out.allCaps = formatting.allCaps;
4323
- if (formatting.highlight !== void 0) out.highlight = formatting.highlight;
5439
+ if (formatting.highlight !== void 0) {
5440
+ out.highlight = formatting.highlight;
5441
+ }
4324
5442
  if (formatting.shading) out.shading = shading(formatting.shading);
4325
5443
  if (formatting.scalePercent !== void 0)
4326
5444
  out.scale = formatting.scalePercent;
4327
- if (formatting.characterSpacingTwentieths !== void 0) {
4328
- out.characterSpacing = formatting.characterSpacingTwentieths;
5445
+ const stated = formatting.characterSpacingTwentieths;
5446
+ if (stated !== void 0 && stated !== 0) {
5447
+ out.characterSpacing = twips(stated);
4329
5448
  }
4330
5449
  if (formatting.language) out.language = { value: formatting.language };
4331
5450
  if (formatting.noProof !== void 0) out.noProof = formatting.noProof;
@@ -4355,7 +5474,9 @@ function inlineChildren2(children, ctx = emptyContext()) {
4355
5474
  switch (child.kind) {
4356
5475
  case "lineBreak":
4357
5476
  pendingBreaks += 1;
4358
- if (child.clear && child.clear !== "none") pendingClear = child.clear;
5477
+ if (child.clear && child.clear !== "none") {
5478
+ pendingClear = child.clear;
5479
+ }
4359
5480
  break;
4360
5481
  case "text":
4361
5482
  out.push({
@@ -4393,7 +5514,7 @@ function inlineChildren2(children, ctx = emptyContext()) {
4393
5514
  case "drawingGroup": {
4394
5515
  const pending = breakOption();
4395
5516
  if (pending.break) out.push(pending);
4396
- out.push({ wpgGroup: drawingGroupOptions(child, ctx) });
5517
+ out.push({ wpgGroup: drawingGroupOptions2(child, ctx) });
4397
5518
  break;
4398
5519
  }
4399
5520
  case "hyperlink":
@@ -4423,8 +5544,9 @@ function inlineChildren2(children, ctx = emptyContext()) {
4423
5544
  out.push({ wpsShape: shapeOptions(child, ctx) });
4424
5545
  break;
4425
5546
  case "chart":
4426
- ctx.charts?.push(child);
4427
- out.push({ chart: chartOptions(child, ctx) });
5547
+ out.push({
5548
+ chart: chartOptions(child, ctx, ctx.charts?.push(child) ?? 1)
5549
+ });
4428
5550
  break;
4429
5551
  case "noteReference":
4430
5552
  out.push(
@@ -4432,7 +5554,7 @@ function inlineChildren2(children, ctx = emptyContext()) {
4432
5554
  );
4433
5555
  break;
4434
5556
  default:
4435
- assertNever2(child, "DocxIrInline");
5557
+ assertNever3(child, "DocxIrInline");
4436
5558
  }
4437
5559
  }
4438
5560
  return out;
@@ -4473,8 +5595,11 @@ function floatingOptions2(floating) {
4473
5595
  ...floating.vertical ? { verticalPosition: position(floating.vertical) } : {},
4474
5596
  ...floating.wrap ? {
4475
5597
  wrap: {
4476
- type: WRAP_TYPE2[floating.wrap.type],
4477
- ...floating.wrap.side ? { side: floating.wrap.side } : {}
5598
+ // Named, as OOXML names them; 0.11 took docx.js's numbers.
5599
+ type: floating.wrap.type,
5600
+ ...floating.wrap.side ? {
5601
+ side: floating.wrap.side
5602
+ } : {}
4478
5603
  }
4479
5604
  } : {},
4480
5605
  ...floating.margins ? {
@@ -4501,66 +5626,92 @@ function imageMedia(ctx, resourceId, placement) {
4501
5626
  }
4502
5627
  function pictureOptions(image, ctx) {
4503
5628
  const media = imageMedia(ctx, image.resourceId, image);
4504
- return {
4505
- type: media.type,
5629
+ const source = media.type === "svg" ? {
5630
+ type: "svg",
4506
5631
  data: media.data,
4507
- // No `fileName`: at run level the backend allocates one, and stating our
4508
- // own would only fight it.
4509
- ...media.fallback ? { fallback: media.fallback } : {},
5632
+ // The SVG slot always carries a raster: `prepareImages` falls back
5633
+ // to the vector bytes when none could be made.
5634
+ fallback: media.fallback ?? {
5635
+ type: "png",
5636
+ data: media.data
5637
+ }
5638
+ } : { type: media.type, data: media.data };
5639
+ return {
5640
+ ...source,
4510
5641
  // Raw numbers are EMUs in @office-open/docx. Passing pixels here makes a
4511
5642
  // normal image only a few hundred EMUs wide, effectively a dot.
4512
5643
  transformation: { width: image.widthEmu, height: image.heightEmu },
4513
5644
  // The id is stated rather than left to the backend's process-global
4514
- // counter. `name` stays empty, which is what it was before and what the
4515
- // backend falls back to.
4516
- altText: { id: String(ctx.nextDrawingId()) },
5645
+ // counter. `name` stays empty, which is what the backend falls back to.
5646
+ altText: { id: String(ctx.nextDrawingId()), name: "" },
4517
5647
  ...image.floating ? { floating: floatingOptions2(image.floating) } : {}
4518
5648
  // No `description` or `title`: no DOCX this pipeline has produced carries
4519
5649
  // `wp:docPr` alt text, and the compiler warns so the gap is visible rather
4520
5650
  // than silent.
4521
5651
  };
4522
5652
  }
4523
- function drawingGroupOptions(group, ctx) {
5653
+ function drawingGroupOptions2(group, ctx) {
4524
5654
  const id = ctx.nextDrawingId();
4525
5655
  return {
4526
5656
  altText: {
4527
5657
  id: String(id),
5658
+ name: "",
4528
5659
  ...group.altText ? { description: group.altText } : {}
4529
5660
  },
4530
5661
  transformation: { width: group.widthEmu, height: group.heightEmu },
4531
- childOffset: { x: 0, y: 0 },
4532
- childExtent: { cx: group.canvasWidthEmu, cy: group.canvasHeightEmu },
4533
- children: group.children.map((child) => groupChild(child, ctx)),
5662
+ childOffsetX: 0,
5663
+ childOffsetY: 0,
5664
+ childExtentWidth: group.canvasWidthEmu,
5665
+ childExtentHeight: group.canvasHeightEmu,
5666
+ children: group.children.map((child) => groupChild2(child, ctx)),
4534
5667
  ...group.floating ? { floating: floatingOptions2(group.floating) } : {}
4535
5668
  };
4536
5669
  }
4537
- function chartOptions(chart, ctx) {
5670
+ function chartOptions(chart, ctx, ordinal) {
4538
5671
  const id = ctx.nextDrawingId();
5672
+ const input = chartInput(chart);
5673
+ const look = chartLook(input);
4539
5674
  return {
4540
5675
  type: chart.chartType,
4541
5676
  categories: chart.series[0]?.labels ?? [],
4542
- series: chart.series.map((entry, index) => ({
4543
- name: entry.name ?? `Series ${index + 1}`,
4544
- values: entry.values
4545
- })),
4546
- ...chart.title && chart.showTitle !== false ? { title: chart.title } : {},
5677
+ series: chart.series.map(
5678
+ (entry, index) => withSeriesLook(
5679
+ {
5680
+ name: entry.name ?? `Series ${index + 1}`,
5681
+ values: entry.values,
5682
+ ...seriesLook(chart.chartType)
5683
+ },
5684
+ look.series[index]
5685
+ )
5686
+ ),
5687
+ ...look.chart,
4547
5688
  ...chart.showLegend !== void 0 ? { showLegend: chart.showLegend } : {},
5689
+ externalData: {
5690
+ ...look.chart.externalData,
5691
+ fileName: `chart${ordinal}.xlsx`,
5692
+ // A scatter chart's column A holds the x values its `c:xVal` caches.
5693
+ data: buildChartWorkbook(
5694
+ chart.series,
5695
+ input.scatterXValues ? { categoryValues: input.scatterXValues } : {}
5696
+ )
5697
+ },
4548
5698
  transformation: { width: chart.widthEmu, height: chart.heightEmu },
4549
5699
  altText: {
4550
5700
  id: String(id),
5701
+ name: "",
4551
5702
  ...chart.altText ? { description: chart.altText } : {}
4552
5703
  },
4553
5704
  ...chart.floating ? { floating: floatingOptions2(chart.floating) } : {}
4554
5705
  };
4555
5706
  }
4556
- function groupChild(child, ctx) {
5707
+ function groupChild2(child, ctx) {
4557
5708
  return child.kind === "shape" ? groupShape(child, ctx) : groupPicture(child, ctx);
4558
5709
  }
4559
5710
  function groupShape(shape, ctx) {
4560
5711
  const id = ctx.nextDrawingId();
4561
5712
  return {
4562
5713
  type: "wps",
4563
- transformation: childTransformation(shape.frame),
5714
+ transformation: childTransformation2(shape.frame),
4564
5715
  data: {
4565
5716
  nonVisualProperties: {
4566
5717
  id,
@@ -4569,7 +5720,7 @@ function groupShape(shape, ctx) {
4569
5720
  // to hold text, and it changes how the object behaves on selection.
4570
5721
  ...shape.isTextBox ? { textBox: "1" } : {}
4571
5722
  },
4572
- presetGeometry: { preset: shape.geometry },
5723
+ geometry: shape.geometry,
4573
5724
  ...shape.fill ? { fill: drawingFill(shape.fill) } : {},
4574
5725
  ...shape.outline ? { outline: drawingOutline(shape.outline) } : {},
4575
5726
  children: (shape.text?.paragraphs ?? []).map(
@@ -4595,7 +5746,7 @@ function groupPicture(picture2, ctx) {
4595
5746
  fileName: media.fallbackFileName
4596
5747
  }
4597
5748
  } : {},
4598
- transformation: childTransformation(picture2.frame),
5749
+ transformation: childTransformation2(picture2.frame),
4599
5750
  nonVisualProperties: {
4600
5751
  id,
4601
5752
  ...picture2.name ? { name: picture2.name } : {},
@@ -4604,11 +5755,7 @@ function groupPicture(picture2, ctx) {
4604
5755
  ...picture2.crop ? { sourceRectangle: sourceRectangle(picture2.crop) } : {}
4605
5756
  };
4606
5757
  }
4607
- function childTransformation(frame) {
4608
- const flip = {
4609
- ...frame.flipHorizontal ? { horizontal: true } : {},
4610
- ...frame.flipVertical ? { vertical: true } : {}
4611
- };
5758
+ function childTransformation2(frame) {
4612
5759
  return {
4613
5760
  offset: {
4614
5761
  emus: { x: frame.xEmu, y: frame.yEmu },
@@ -4624,7 +5771,8 @@ function childTransformation(frame) {
4624
5771
  },
4625
5772
  // Degrees: the backend multiplies by 60000 on the way into `@rot`.
4626
5773
  ...frame.rotationDegrees !== void 0 ? { rotation: frame.rotationDegrees } : {},
4627
- ...Object.keys(flip).length > 0 ? { flip } : {}
5774
+ ...frame.flipHorizontal ? { flipHorizontal: true } : {},
5775
+ ...frame.flipVertical ? { flipVertical: true } : {}
4628
5776
  };
4629
5777
  }
4630
5778
  function drawingFill(fill) {
@@ -4632,7 +5780,6 @@ function drawingFill(fill) {
4632
5780
  return {
4633
5781
  type: "solid",
4634
5782
  color: {
4635
- type: "rgb",
4636
5783
  value: fill.color.hex,
4637
5784
  // Transparency becomes an alpha transform on the colour. DrawingML
4638
5785
  // states *opacity*, so the value is the complement of what the author
@@ -4674,7 +5821,7 @@ function sourceRectangle(crop) {
4674
5821
  function shapeOptions(shape, ctx) {
4675
5822
  const id = String(ctx.nextDrawingId());
4676
5823
  return {
4677
- altText: { id },
5824
+ altText: { id, name: "" },
4678
5825
  children: shape.children.map((child) => paragraph(child, ctx)),
4679
5826
  // Raw transformation numbers are EMUs in @office-open/docx, while the IR
4680
5827
  // deliberately stores native shape dimensions in pixels.
@@ -4685,7 +5832,7 @@ function shapeOptions(shape, ctx) {
4685
5832
  ...shape.fill ? {
4686
5833
  fill: {
4687
5834
  type: "solid",
4688
- color: { type: "rgb", value: shape.fill.hex }
5835
+ color: { value: shape.fill.hex }
4689
5836
  }
4690
5837
  } : {},
4691
5838
  ...shape.outline ? { outline: drawingOutline(shape.outline) } : {},
@@ -4716,16 +5863,16 @@ function paragraphProperties2(formatting) {
4716
5863
  if (formatting.indent) {
4717
5864
  const indent = {};
4718
5865
  if (formatting.indent.leftTwips !== void 0) {
4719
- indent.left = formatting.indent.leftTwips;
5866
+ indent.left = twips(formatting.indent.leftTwips);
4720
5867
  }
4721
5868
  if (formatting.indent.rightTwips !== void 0) {
4722
- indent.right = formatting.indent.rightTwips;
5869
+ indent.right = twips(formatting.indent.rightTwips);
4723
5870
  }
4724
5871
  if (formatting.indent.firstLineTwips !== void 0) {
4725
- indent.firstLine = formatting.indent.firstLineTwips;
5872
+ indent.firstLine = twips(formatting.indent.firstLineTwips);
4726
5873
  }
4727
5874
  if (formatting.indent.hangingTwips !== void 0) {
4728
- indent.hanging = formatting.indent.hangingTwips;
5875
+ indent.hanging = twips(formatting.indent.hangingTwips);
4729
5876
  }
4730
5877
  out.indent = indent;
4731
5878
  }
@@ -4790,13 +5937,19 @@ function frameOptions2(frame) {
4790
5937
  ...frame.anchorLock !== void 0 ? { anchorLock: frame.anchorLock } : {},
4791
5938
  ...frame.rule ? { rule: frame.rule } : {}
4792
5939
  };
4793
- return frame.xTwips !== void 0 || frame.yTwips !== void 0 ? {
4794
- type: "absolute",
4795
- position: { x: frame.xTwips ?? 0, y: frame.yTwips ?? 0 },
4796
- ...base
4797
- } : {
5940
+ if (frame.xTwips !== void 0 || frame.yTwips !== void 0) {
5941
+ return {
5942
+ type: "absolute",
5943
+ position: { x: frame.xTwips ?? 0, y: frame.yTwips ?? 0 },
5944
+ ...base
5945
+ };
5946
+ }
5947
+ return {
4798
5948
  type: "alignment",
4799
- alignment: { x: frame.xAlign, y: frame.yAlign },
5949
+ alignment: {
5950
+ x: frame.xAlign,
5951
+ y: frame.yAlign
5952
+ },
4800
5953
  ...base
4801
5954
  };
4802
5955
  }
@@ -4817,7 +5970,7 @@ function block(value, ctx = emptyContext()) {
4817
5970
  case "toc":
4818
5971
  return { toc: tableOfContents(value) };
4819
5972
  default:
4820
- return assertNever2(value, "DocxIrBlock");
5973
+ return assertNever3(value, "DocxIrBlock");
4821
5974
  }
4822
5975
  }
4823
5976
  function tocEntry(entry) {
@@ -4836,6 +5989,7 @@ function tableOfContents(value) {
4836
5989
  headingStyleRange: `${value.headingRange.from}-${value.headingRange.to}`
4837
5990
  } : {},
4838
5991
  ...value.styleLevels?.length ? {
5992
+ // `StyleLevel` is a class the backend only reads the two fields of.
4839
5993
  stylesWithLevels: value.styleLevels.map((style) => ({
4840
5994
  styleName: style.styleName,
4841
5995
  level: style.level
@@ -4856,15 +6010,17 @@ function table(value, ctx) {
4856
6010
  // `w:w` is an integer in OOXML. A grid that splits a remainder between
4857
6011
  // unstated columns comes out fractional, and the XML would carry it
4858
6012
  // verbatim; floored here, as the docxjs backend floors it.
4859
- size: value.width.kind === "auto" ? 0 : value.width.kind === "twips" ? Math.floor(value.width.value) : value.width.value,
4860
- type: value.width.kind === "twips" ? "dxa" : value.width.kind === "percent" ? "pct" : "auto"
6013
+ size: value.width.kind === "auto" ? 0 : value.width.kind === "twips" ? twips(value.width.value) : value.width.value,
6014
+ type: value.width.kind === "twips" ? "twips" : value.width.kind === "percent" ? "percent" : "auto"
4861
6015
  },
4862
6016
  layout: value.layout,
4863
6017
  // An empty grid is a table with nothing to say about its columns, which is
4864
6018
  // not the same as one whose columns are all zero wide. The compiler hands
4865
6019
  // over every grid in twips; floored for the same reason as the width.
4866
- ...value.columnGrid.values.length > 0 ? { columnWidths: value.columnGrid.values.map(Math.floor) } : {},
4867
- ...value.alignment ? { alignment: alignment(value.alignment) } : {},
6020
+ ...value.columnGrid.values.length > 0 ? { columnWidths: value.columnGrid.values.map(twips) } : {},
6021
+ ...value.alignment ? {
6022
+ alignment: alignment(value.alignment)
6023
+ } : {},
4868
6024
  ...value.borders ? { borders: borders2(value.borders) } : {},
4869
6025
  ...value.cellMargins ? { margins: cellMargins(value.cellMargins) } : {},
4870
6026
  ...value.floating ? { float: tableFloat2(value.floating) } : {}
@@ -4874,14 +6030,22 @@ function tableFloat2(floating) {
4874
6030
  return {
4875
6031
  ...floating.horizontalAnchor ? { horizontalAnchor: floating.horizontalAnchor } : {},
4876
6032
  ...floating.verticalAnchor ? { verticalAnchor: floating.verticalAnchor } : {},
4877
- ...floating.absoluteHorizontalPositionTwips !== void 0 ? { absoluteHorizontalPosition: floating.absoluteHorizontalPositionTwips } : {},
6033
+ ...floating.absoluteHorizontalPositionTwips !== void 0 ? {
6034
+ absoluteHorizontalPosition: twips(
6035
+ floating.absoluteHorizontalPositionTwips
6036
+ )
6037
+ } : {},
4878
6038
  ...floating.relativeHorizontalPosition ? { relativeHorizontalPosition: floating.relativeHorizontalPosition } : {},
4879
- ...floating.absoluteVerticalPositionTwips !== void 0 ? { absoluteVerticalPosition: floating.absoluteVerticalPositionTwips } : {},
6039
+ ...floating.absoluteVerticalPositionTwips !== void 0 ? {
6040
+ absoluteVerticalPosition: twips(
6041
+ floating.absoluteVerticalPositionTwips
6042
+ )
6043
+ } : {},
4880
6044
  ...floating.relativeVerticalPosition ? { relativeVerticalPosition: floating.relativeVerticalPosition } : {},
4881
- ...floating.topFromTextTwips !== void 0 ? { topFromText: floating.topFromTextTwips } : {},
4882
- ...floating.rightFromTextTwips !== void 0 ? { rightFromText: floating.rightFromTextTwips } : {},
4883
- ...floating.bottomFromTextTwips !== void 0 ? { bottomFromText: floating.bottomFromTextTwips } : {},
4884
- ...floating.leftFromTextTwips !== void 0 ? { leftFromText: floating.leftFromTextTwips } : {},
6045
+ ...floating.topFromTextTwips !== void 0 ? { topFromText: twips(floating.topFromTextTwips) } : {},
6046
+ ...floating.rightFromTextTwips !== void 0 ? { rightFromText: twips(floating.rightFromTextTwips) } : {},
6047
+ ...floating.bottomFromTextTwips !== void 0 ? { bottomFromText: twips(floating.bottomFromTextTwips) } : {},
6048
+ ...floating.leftFromTextTwips !== void 0 ? { leftFromText: twips(floating.leftFromTextTwips) } : {},
4885
6049
  ...floating.overlap ? { overlap: floating.overlap } : {}
4886
6050
  };
4887
6051
  }
@@ -4889,7 +6053,10 @@ function tableRow(row, ctx) {
4889
6053
  return {
4890
6054
  cells: row.cells.map((cell) => tableCell(cell, ctx)),
4891
6055
  ...row.heightTwips !== void 0 ? {
4892
- height: { value: row.heightTwips, rule: row.heightRule ?? "atLeast" }
6056
+ height: {
6057
+ value: twips(row.heightTwips),
6058
+ rule: row.heightRule ?? "atLeast"
6059
+ }
4893
6060
  } : {},
4894
6061
  ...row.isHeader !== void 0 ? { tableHeader: row.isHeader } : {},
4895
6062
  ...row.cantSplit !== void 0 ? { cantSplit: row.cantSplit } : {},
@@ -4897,20 +6064,30 @@ function tableRow(row, ctx) {
4897
6064
  };
4898
6065
  }
4899
6066
  function tableCell(cell, ctx) {
6067
+ const children = cellChildren2(cell, ctx);
6068
+ if (cell.children[cell.children.length - 1]?.kind === "toc")
6069
+ children.push({ paragraph: { children: [] } });
4900
6070
  return {
4901
- children: cell.children.map((child) => block(child, ctx)),
4902
- ...cell.widthTwips !== void 0 ? { width: { size: cell.widthTwips, type: "dxa" } } : {},
4903
- ...cell.verticalAlign ? { verticalAlign: cell.verticalAlign } : {},
6071
+ children,
6072
+ ...cell.widthTwips !== void 0 ? { width: { size: twips(cell.widthTwips), type: "twips" } } : {},
6073
+ ...cell.verticalAlign ? {
6074
+ verticalAlign: cell.verticalAlign
6075
+ } : {},
4904
6076
  ...cell.shading ? { shading: shading(cell.shading) } : {},
4905
6077
  ...cell.margins ? { margins: cellMargins(cell.margins) } : {},
4906
6078
  ...cell.borders ? { borders: borders2(cell.borders) } : {},
4907
6079
  ...cell.columnSpan !== void 0 ? { columnSpan: cell.columnSpan } : {},
4908
6080
  ...cell.rowSpan !== void 0 ? { verticalMerge: cell.rowSpan } : {},
4909
- ...cell.textDirection ? { textDirection: cell.textDirection } : {}
6081
+ ...cell.textDirection ? {
6082
+ textDirection: cell.textDirection
6083
+ } : {}
4910
6084
  };
4911
6085
  }
6086
+ function cellChildren2(cell, ctx) {
6087
+ return cell.children.map((child) => block(child, ctx));
6088
+ }
4912
6089
  function cellMargins(margins) {
4913
- const side = (value) => value === void 0 ? void 0 : { size: value, type: "dxa" };
6090
+ const side = (value) => value === void 0 ? void 0 : { size: twips(value), type: "twips" };
4914
6091
  const out = {};
4915
6092
  for (const [name, value] of [
4916
6093
  ["top", margins.topTwips],
@@ -4953,47 +6130,51 @@ function section(value, ctx, closesDocument = false) {
4953
6130
  properties: {
4954
6131
  ...value.properties.type ? { type: value.properties.type } : {},
4955
6132
  ...value.properties.titlePage !== void 0 ? { titlePage: value.properties.titlePage } : {},
4956
- page: {
4957
- // Orientation is implied by the width/height pair, which is how this
4958
- // pipeline has always expressed it; stating it as well changes `w:pgSz`.
4959
- size: {
4960
- width: page.widthTwips,
4961
- height: page.heightTwips,
4962
- ...page.code !== void 0 ? { code: page.code } : {}
4963
- },
4964
- margin: {
4965
- top: page.margins.topTwips,
4966
- right: page.margins.rightTwips,
4967
- bottom: page.margins.bottomTwips,
4968
- left: page.margins.leftTwips,
4969
- ...page.margins.headerTwips !== void 0 ? { header: page.margins.headerTwips } : {},
4970
- ...page.margins.footerTwips !== void 0 ? { footer: page.margins.footerTwips } : {},
4971
- ...page.margins.gutterTwips !== void 0 ? { gutter: page.margins.gutterTwips } : {}
4972
- },
4973
- ...value.properties.pageNumbers ? {
4974
- pageNumbers: {
4975
- ...value.properties.pageNumbers.start !== void 0 ? { start: value.properties.pageNumbers.start } : {},
4976
- ...value.properties.pageNumbers.formatType ? { formatType: value.properties.pageNumbers.formatType } : {}
4977
- }
4978
- } : {},
4979
- ...value.properties.borders ? {
4980
- borders: {
4981
- ...value.properties.borders.display ? { display: value.properties.borders.display } : {},
4982
- ...value.properties.borders.offsetFrom ? { offsetFrom: value.properties.borders.offsetFrom } : {},
4983
- ...value.properties.borders.borders ? borders2(value.properties.borders.borders) : {}
4984
- }
4985
- } : {}
6133
+ // Orientation is implied by the width/height pair, which is how this
6134
+ // pipeline has always expressed it; stating it as well changes `w:pgSz`.
6135
+ pageSize: {
6136
+ width: twips(page.widthTwips),
6137
+ height: twips(page.heightTwips),
6138
+ ...page.code !== void 0 ? { code: page.code } : {}
4986
6139
  },
6140
+ pageMargin: {
6141
+ top: twips(page.margins.topTwips),
6142
+ right: twips(page.margins.rightTwips),
6143
+ bottom: twips(page.margins.bottomTwips),
6144
+ left: twips(page.margins.leftTwips),
6145
+ ...page.margins.headerTwips !== void 0 ? { header: twips(page.margins.headerTwips) } : {},
6146
+ ...page.margins.footerTwips !== void 0 ? { footer: twips(page.margins.footerTwips) } : {},
6147
+ ...page.margins.gutterTwips !== void 0 ? { gutter: twips(page.margins.gutterTwips) } : {}
6148
+ },
6149
+ ...value.properties.pageNumbers ? {
6150
+ pageNumberType: {
6151
+ ...value.properties.pageNumbers.start !== void 0 ? { start: value.properties.pageNumbers.start } : {},
6152
+ ...value.properties.pageNumbers.formatType ? {
6153
+ format: value.properties.pageNumbers.formatType
6154
+ } : {}
6155
+ }
6156
+ } : {},
6157
+ ...value.properties.borders ? {
6158
+ pageBorders: {
6159
+ ...value.properties.borders.display ? {
6160
+ display: value.properties.borders.display
6161
+ } : {},
6162
+ ...value.properties.borders.offsetFrom ? {
6163
+ offsetFrom: value.properties.borders.offsetFrom
6164
+ } : {},
6165
+ ...value.properties.borders.borders ? borders2(value.properties.borders.borders) : {}
6166
+ }
6167
+ } : {},
4987
6168
  ...columns ? {
4988
- column: {
6169
+ columns: {
4989
6170
  count: columns.count,
4990
- ...columns.spaceTwips !== void 0 ? { space: columns.spaceTwips } : {},
6171
+ ...columns.spaceTwips !== void 0 ? { space: twips(columns.spaceTwips) } : {},
4991
6172
  ...columns.separator !== void 0 ? { separate: columns.separator } : {},
4992
6173
  ...columns.equalWidth !== void 0 ? { equalWidth: columns.equalWidth } : {},
4993
6174
  ...columns.widths ? {
4994
6175
  children: columns.widths.map((column) => ({
4995
- width: column.widthTwips,
4996
- ...column.spaceTwips !== void 0 ? { space: column.spaceTwips } : {}
6176
+ width: twips(column.widthTwips),
6177
+ ...column.spaceTwips !== void 0 ? { space: twips(column.spaceTwips) } : {}
4997
6178
  }))
4998
6179
  } : {}
4999
6180
  }
@@ -5021,14 +6202,16 @@ function headerFooterSet(set, ctx) {
5021
6202
  function sectionChildren2(value, ctx, closesDocument = false) {
5022
6203
  const blocks = value.children.map((child) => block(child, ctx));
5023
6204
  const last = value.children[value.children.length - 1];
5024
- if (closesDocument && last?.kind === "paragraph" && last.frame !== void 0)
6205
+ const bookmark = value.bookmark;
6206
+ if (bookmark?.closes && last?.kind !== "paragraph" || closesDocument && last?.kind === "paragraph" && last.frame !== void 0)
5025
6207
  blocks.push({
5026
6208
  paragraph: {
5027
6209
  children: [],
5028
6210
  spacing: { before: 0, after: 0, line: 20, lineRule: "exact" }
5029
6211
  }
5030
6212
  });
5031
- const bookmark = value.bookmark;
6213
+ if (!closesDocument && !bookmark?.closes && last?.kind === "toc")
6214
+ blocks.push({ paragraph: { children: [] } });
5032
6215
  if (!bookmark) return blocks;
5033
6216
  return [
5034
6217
  ...bookmark.opens ? [{ bookmarkStart: { id: bookmark.id, name: bookmark.name } }] : [],
@@ -5039,43 +6222,44 @@ function sectionChildren2(value, ctx, closesDocument = false) {
5039
6222
  function numberingConfig2(numbering) {
5040
6223
  return {
5041
6224
  reference: numbering.reference,
5042
- levels: numbering.levels.map((level) => ({
5043
- level: level.level,
5044
- format: level.format,
5045
- text: level.text,
5046
- ...level.alignment ? { alignment: alignment(level.alignment) } : {},
5047
- ...level.suffix ? { suffix: level.suffix } : {},
5048
- ...level.start !== void 0 ? { start: level.start } : {},
5049
- ...level.paragraphStyleId ? { paragraphStyle: level.paragraphStyleId } : {},
5050
- style: {
6225
+ levels: numbering.levels.map(
6226
+ (level) => ({
6227
+ level: level.level,
6228
+ format: level.format,
6229
+ text: level.text,
6230
+ ...level.alignment ? { alignment: alignment(level.alignment) } : {},
6231
+ ...level.suffix ? { suffix: level.suffix } : {},
6232
+ // Always stated. docx.js writes `w:start w:val="1"` for a level that
6233
+ // names none, and so did this backend until 0.14, which writes no
6234
+ // `w:start` at all when it is not given one; Word and LibreOffice then
6235
+ // count every such list from 0.
6236
+ start: level.start ?? 1,
6237
+ ...level.paragraphStyleId ? { paragraphStyle: level.paragraphStyleId } : {},
5051
6238
  ...level.indent ? {
5052
6239
  paragraph: {
5053
6240
  indent: {
5054
- ...level.indent.leftTwips !== void 0 ? { left: level.indent.leftTwips } : {},
5055
- ...level.indent.hangingTwips !== void 0 ? { hanging: level.indent.hangingTwips } : {}
6241
+ ...level.indent.leftTwips !== void 0 ? { left: twips(level.indent.leftTwips) } : {},
6242
+ ...level.indent.hangingTwips !== void 0 ? { hanging: twips(level.indent.hangingTwips) } : {}
5056
6243
  }
5057
6244
  }
5058
6245
  } : {},
5059
6246
  ...level.run ? { run: runProperties2(level.run) } : {}
5060
- }
5061
- }))
6247
+ })
6248
+ )
5062
6249
  };
5063
6250
  }
5064
- var WRAP_TYPE2, BODY_ANCHOR;
6251
+ var BODY_ANCHOR;
5065
6252
  var init_emit2 = __esm({
5066
6253
  "src/renderers/office-open/emit.ts"() {
5067
6254
  "use strict";
5068
6255
  init_units();
5069
- WRAP_TYPE2 = {
5070
- none: 0,
5071
- square: 1,
5072
- tight: 2,
5073
- topAndBottom: 3
5074
- };
6256
+ init_chartWorkbook();
6257
+ init_chartLook();
6258
+ init_chartParts();
5075
6259
  BODY_ANCHOR = {
5076
- top: "t",
5077
- middle: "ctr",
5078
- bottom: "b"
6260
+ top: "top",
6261
+ middle: "center",
6262
+ bottom: "bottom"
5079
6263
  };
5080
6264
  }
5081
6265
  });
@@ -5135,15 +6319,34 @@ var init_styles3 = __esm({
5135
6319
  var office_open_exports = {};
5136
6320
  __export(office_open_exports, {
5137
6321
  OFFICE_OPEN_DOCX_RENDERER_ID: () => OFFICE_OPEN_DOCX_RENDERER_ID,
6322
+ OFFICE_OPEN_VERSION: () => OFFICE_OPEN_VERSION,
5138
6323
  buildDocumentOptions: () => buildDocumentOptions,
5139
6324
  createOfficeOpenDocxRenderer: () => createOfficeOpenDocxRenderer
5140
6325
  });
5141
- import AdmZip4 from "adm-zip";
6326
+ import AdmZip5 from "adm-zip";
6327
+ import {
6328
+ assertBackendVersion,
6329
+ installedPackageVersion
6330
+ } from "@json-to-office/shared/rendering/node";
6331
+ function installedBackendVersion() {
6332
+ let resolved;
6333
+ try {
6334
+ resolved = import.meta.resolve?.(OFFICE_OPEN_DOCX);
6335
+ } catch {
6336
+ resolved = void 0;
6337
+ }
6338
+ return installedPackageVersion(OFFICE_OPEN_DOCX, import.meta.url, resolved);
6339
+ }
5142
6340
  async function createOfficeOpenDocxRenderer() {
5143
6341
  const backend = await import(
5144
6342
  /* @vite-ignore */
5145
6343
  OFFICE_OPEN_DOCX
5146
6344
  );
6345
+ assertBackendVersion(
6346
+ OFFICE_OPEN_DOCX,
6347
+ OFFICE_OPEN_VERSION,
6348
+ installedBackendVersion()
6349
+ );
5147
6350
  if (typeof backend.generateDocument !== "function") {
5148
6351
  throw new Error(
5149
6352
  `${OFFICE_OPEN_DOCX} does not export generateDocument(); the installed version is not compatible with this adapter.`
@@ -5154,10 +6357,10 @@ async function createOfficeOpenDocxRenderer() {
5154
6357
  format: "docx",
5155
6358
  capabilities: OFFICE_OPEN_CAPABILITIES,
5156
6359
  async render(ir, options) {
5157
- const charts = [];
6360
+ const charts2 = [];
5158
6361
  const document = await buildDocumentOptions(
5159
6362
  ir,
5160
- charts,
6363
+ charts2,
5161
6364
  options?.svgRasterFallback
5162
6365
  );
5163
6366
  const bytes = await backend.generateDocument(document, {
@@ -5166,9 +6369,10 @@ async function createOfficeOpenDocxRenderer() {
5166
6369
  let raw = Buffer.from(
5167
6370
  bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes)
5168
6371
  );
5169
- if (charts.length > 0) {
5170
- const zip = new AdmZip4(raw);
5171
- spliceChartParts(zip, charts);
6372
+ if (ir.theme || charts2.length > 0) {
6373
+ const zip = new AdmZip5(raw);
6374
+ if (ir.theme) spliceTheme(zip, ir.theme);
6375
+ finishChartParts(zip, charts2);
5172
6376
  raw = zip.toBuffer();
5173
6377
  }
5174
6378
  if (options?.deterministic === false)
@@ -5185,12 +6389,12 @@ async function createOfficeOpenDocxRenderer() {
5185
6389
  }
5186
6390
  };
5187
6391
  }
5188
- async function buildDocumentOptions(ir, charts = [], svgRasterFallback) {
6392
+ async function buildDocumentOptions(ir, charts2 = [], svgRasterFallback) {
5189
6393
  let nextDrawingId = 1;
5190
6394
  const ctx = {
5191
6395
  pictures: await prepareImages2(ir, svgRasterFallback),
5192
6396
  nextDrawingId: () => nextDrawingId++,
5193
- charts
6397
+ charts: charts2
5194
6398
  };
5195
6399
  return {
5196
6400
  styles: emitStyles2(ir.styles),
@@ -5198,16 +6402,20 @@ async function buildDocumentOptions(ir, charts = [], svgRasterFallback) {
5198
6402
  (value, index) => section(value, ctx, index === ir.sections.length - 1)
5199
6403
  ),
5200
6404
  ...coreProperties2(ir),
5201
- features: {
6405
+ settings: {
5202
6406
  updateFields: ir.settings.updateFields,
5203
6407
  ...ir.settings.trackRevisions ? { trackRevisions: true } : {}
5204
6408
  },
5205
- ...ir.numbering.length > 0 ? { numbering: { config: ir.numbering.map(numberingConfig2) } } : {},
6409
+ ...ir.numbering.length > 0 ? {
6410
+ numbering: {
6411
+ abstractNumberings: ir.numbering.map(numberingConfig2)
6412
+ }
6413
+ } : {},
5206
6414
  ...ir.footnotes.length > 0 ? { footnotes: noteBodies2(ir.footnotes, ctx) } : {},
5207
6415
  ...ir.endnotes.length > 0 ? { endnotes: noteBodies2(ir.endnotes, ctx) } : {},
5208
6416
  ...ir.comments.length > 0 ? {
5209
- comments: {
5210
- children: ir.comments.map((comment) => ({
6417
+ comments: ir.comments.map(
6418
+ (comment) => ({
5211
6419
  id: comment.id,
5212
6420
  author: comment.author,
5213
6421
  ...comment.initials ? { initials: comment.initials } : {},
@@ -5215,29 +6423,25 @@ async function buildDocumentOptions(ir, charts = [], svgRasterFallback) {
5215
6423
  children: comment.children.map(
5216
6424
  (child) => paragraphOf(child, ctx)
5217
6425
  )
5218
- }))
5219
- }
6426
+ })
6427
+ )
5220
6428
  } : {}
5221
6429
  };
5222
6430
  }
5223
6431
  function paragraphOf(value, ctx) {
5224
6432
  const emitted = block(value, ctx);
5225
- const asParagraph = emitted.paragraph;
5226
- if (!asParagraph) {
6433
+ if (!("paragraph" in emitted) || typeof emitted.paragraph === "string") {
5227
6434
  throw new Error(
5228
6435
  `the office-open renderer expected a paragraph, not a "${value.kind}"`
5229
6436
  );
5230
6437
  }
5231
- return asParagraph;
6438
+ return emitted.paragraph;
5232
6439
  }
5233
6440
  function noteBodies2(notes, ctx) {
5234
- const bodies = {};
5235
- for (const note of notes) {
5236
- bodies[String(note.id)] = {
5237
- children: note.children.map((child) => paragraphOf(child, ctx))
5238
- };
5239
- }
5240
- return bodies;
6441
+ return notes.map((note) => ({
6442
+ id: note.id,
6443
+ children: note.children.map((child) => paragraphOf(child, ctx))
6444
+ }));
5241
6445
  }
5242
6446
  async function prepareImages2(ir, svgRasterFallback) {
5243
6447
  const placements = collectImagePlacements2(ir);
@@ -5260,23 +6464,16 @@ async function prepareImages2(ir, svgRasterFallback) {
5260
6464
  if (resource.kind !== "image") continue;
5261
6465
  const type = resource.mediaType;
5262
6466
  const data = Buffer.from(resource.bytes);
5263
- const placementData = /* @__PURE__ */ new Map();
5264
- [...placements.get(resource.id) ?? []].forEach((size, index) => {
5265
- placementData.set(
5266
- size,
5267
- index === 0 ? data : distinguishImageBytes(data, type, size)
5268
- );
5269
- });
5270
6467
  resources.set(resource.id, (placement) => {
5271
6468
  const sizeKey = placementKey(placement);
5272
6469
  const stem = `${resource.id}-${sizeKey}`;
5273
6470
  return {
5274
6471
  type,
5275
- // @office-open/docx stores a drawing transformation on its deduplicated
5276
- // media entry. Equivalent bytes drawn at a second size would otherwise
5277
- // reuse the first size, so each distinct placement size gets equivalent
5278
- // image bytes carrying a harmless format-native marker.
5279
- data: placementData.get(sizeKey) ?? data,
6472
+ // The same bytes at every size: the backend keeps each drawing's
6473
+ // extent on the drawing. 0.11 kept it on its deduplicated media
6474
+ // entry, and an image drawn at a second size took the first one's,
6475
+ // unless its bytes were told apart.
6476
+ data,
5280
6477
  // Named after the resource and the size it is drawn at, so two
5281
6478
  // placements of one image at one size share a single part and a third
5282
6479
  // at another size gets its own.
@@ -5299,104 +6496,6 @@ async function prepareImages2(ir, svgRasterFallback) {
5299
6496
  function placementKey(placement) {
5300
6497
  return `${emuToPixels(placement.widthEmu)}x${emuToPixels(placement.heightEmu)}`;
5301
6498
  }
5302
- function distinguishImageBytes(data, mediaType, placement) {
5303
- const marker = Buffer.from(`json-to-office:${placement}`, "utf8");
5304
- switch (mediaType) {
5305
- case "png":
5306
- return addPngTextChunk(data, marker);
5307
- case "jpg":
5308
- return addJpegComment(data, marker);
5309
- case "gif":
5310
- return addGifComment(data, marker);
5311
- case "svg":
5312
- return Buffer.concat([
5313
- data,
5314
- Buffer.from(`
5315
- <!-- ${marker.toString("utf8")} -->`, "utf8")
5316
- ]);
5317
- case "bmp": {
5318
- const marked = Buffer.concat([data, marker]);
5319
- if (marked.length >= 14 && marked.subarray(0, 2).toString("ascii") === "BM") {
5320
- marked.writeUInt32LE(marked.length, 2);
5321
- }
5322
- return marked;
5323
- }
5324
- default:
5325
- return Buffer.concat([data, marker]);
5326
- }
5327
- }
5328
- function addPngTextChunk(data, marker) {
5329
- const signature = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
5330
- if (data.length < 8 || !data.subarray(0, 8).equals(signature)) {
5331
- return Buffer.concat([data, marker]);
5332
- }
5333
- let offset = 8;
5334
- while (offset + 12 <= data.length) {
5335
- const payloadLength = data.readUInt32BE(offset);
5336
- const chunkEnd = offset + 12 + payloadLength;
5337
- if (chunkEnd > data.length) break;
5338
- if (data.subarray(offset + 4, offset + 8).toString("ascii") === "IEND") {
5339
- const payload = Buffer.concat([
5340
- Buffer.from("json-to-office\0", "latin1"),
5341
- marker
5342
- ]);
5343
- const chunk = Buffer.alloc(12 + payload.length);
5344
- chunk.writeUInt32BE(payload.length, 0);
5345
- chunk.write("tEXt", 4, 4, "ascii");
5346
- payload.copy(chunk, 8);
5347
- chunk.writeUInt32BE(
5348
- crc32(chunk.subarray(4, 8 + payload.length)),
5349
- 8 + payload.length
5350
- );
5351
- return Buffer.concat([
5352
- data.subarray(0, offset),
5353
- chunk,
5354
- data.subarray(offset)
5355
- ]);
5356
- }
5357
- offset = chunkEnd;
5358
- }
5359
- throw new Error(
5360
- "Cannot mark a PNG that has no intact IEND chunk; its bytes were not checked to decode before rendering"
5361
- );
5362
- }
5363
- function addJpegComment(data, marker) {
5364
- if (data.length < 2 || data[0] !== 255 || data[1] !== 216) {
5365
- return Buffer.concat([data, marker]);
5366
- }
5367
- const comment = Buffer.alloc(4 + marker.length);
5368
- comment[0] = 255;
5369
- comment[1] = 254;
5370
- comment.writeUInt16BE(marker.length + 2, 2);
5371
- marker.copy(comment, 4);
5372
- return Buffer.concat([data.subarray(0, 2), comment, data.subarray(2)]);
5373
- }
5374
- function addGifComment(data, marker) {
5375
- const trailer = data.lastIndexOf(59);
5376
- if (trailer < 0 || marker.length > 255) {
5377
- return Buffer.concat([data, marker]);
5378
- }
5379
- const comment = Buffer.concat([
5380
- Buffer.from([33, 254, marker.length]),
5381
- marker,
5382
- Buffer.from([0])
5383
- ]);
5384
- return Buffer.concat([
5385
- data.subarray(0, trailer),
5386
- comment,
5387
- data.subarray(trailer)
5388
- ]);
5389
- }
5390
- function crc32(data) {
5391
- let crc = 4294967295;
5392
- for (const byte of data) {
5393
- crc ^= byte;
5394
- for (let bit = 0; bit < 8; bit += 1) {
5395
- crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
5396
- }
5397
- }
5398
- return (crc ^ 4294967295) >>> 0;
5399
- }
5400
6499
  function collectImagePlacements2(ir) {
5401
6500
  const placements = /* @__PURE__ */ new Map();
5402
6501
  const record = (resourceId, placement) => {
@@ -5464,17 +6563,19 @@ function coreProperties2(ir) {
5464
6563
  ...metadata.custom?.length ? { customProperties: metadata.custom } : {}
5465
6564
  };
5466
6565
  }
5467
- var OFFICE_OPEN_DOCX_RENDERER_ID, OFFICE_OPEN_DOCX, OFFICE_OPEN_CAPABILITIES;
6566
+ var OFFICE_OPEN_DOCX_RENDERER_ID, OFFICE_OPEN_DOCX, OFFICE_OPEN_VERSION, OFFICE_OPEN_CAPABILITIES;
5468
6567
  var init_office_open = __esm({
5469
6568
  "src/renderers/office-open/index.ts"() {
5470
6569
  "use strict";
5471
6570
  init_chartParts();
6571
+ init_themePart();
5472
6572
  init_imageUtils();
5473
6573
  init_packageDocument();
5474
6574
  init_emit2();
5475
6575
  init_styles3();
5476
6576
  OFFICE_OPEN_DOCX_RENDERER_ID = "office-open";
5477
6577
  OFFICE_OPEN_DOCX = "@office-open/docx";
6578
+ OFFICE_OPEN_VERSION = "0.14.6";
5478
6579
  OFFICE_OPEN_CAPABILITIES = /* @__PURE__ */ new Set([
5479
6580
  "paragraphs",
5480
6581
  "styles",
@@ -6431,6 +7532,7 @@ import {
6431
7532
  assetUnreadableError
6432
7533
  } from "@json-to-office/shared/rendering";
6433
7534
  import { capsFormatting as capsFormatting2, synthesizeFamilyName as synthesizeFamilyName2 } from "@json-to-office/shared";
7535
+ import { unrenderedComponentText } from "@json-to-office/quality";
6434
7536
  import {
6435
7537
  isNativeVisualProps as isNativeVisualProps4
6436
7538
  } from "@json-to-office/shared-docx";
@@ -7556,7 +8658,8 @@ function createDocumentStyles(themeNameOrObject = "consulting", language) {
7556
8658
  tabStops: effectiveTabStops.map(
7557
8659
  (ts) => ({
7558
8660
  type: ts.type,
7559
- positionTwips: ts.position === "max" ? RIGHT_MARGIN_TAB_TWIPS : ts.position,
8661
+ // Whole twips: `w:pos` is an integer no backend rounds.
8662
+ positionTwips: ts.position === "max" ? RIGHT_MARGIN_TAB_TWIPS : Math.round(ts.position),
7560
8663
  ...ts.leader && { leader: ts.leader }
7561
8664
  })
7562
8665
  )
@@ -9103,6 +10206,7 @@ function emptyToUndefined(formatting) {
9103
10206
  }
9104
10207
 
9105
10208
  // src/ir/compiler.ts
10209
+ init_chartValues();
9106
10210
  init_types();
9107
10211
 
9108
10212
  // src/ir/measure.ts
@@ -9135,6 +10239,49 @@ function tableWidthTwips(width, measureTwips) {
9135
10239
 
9136
10240
  // src/ir/compiler.ts
9137
10241
  init_units();
10242
+
10243
+ // src/ir/theme.ts
10244
+ init_colorUtils();
10245
+ init_styleHelpers();
10246
+ init_types();
10247
+ var THEME_COLOR_TOKENS = [
10248
+ ["dark1", "text"],
10249
+ ["light1", "background"],
10250
+ ["dark2", "textSecondary"],
10251
+ ["light2", "backgroundSecondary"],
10252
+ ["accent1", "primary"],
10253
+ ["accent2", "secondary"],
10254
+ ["accent3", "accent"],
10255
+ ["accent4", "accent4"],
10256
+ ["accent5", "accent5"],
10257
+ ["accent6", "accent6"]
10258
+ ];
10259
+ var NOT_XML_CHARS = new RegExp(NOT_XML_CHAR.source, "gu");
10260
+ var xmlText = (value) => (value ?? "").replace(NOT_XML_CHARS, "").trim();
10261
+ function compileTheme(theme, themeName) {
10262
+ const colors = {};
10263
+ for (const [slot, token] of THEME_COLOR_TOKENS) {
10264
+ const hex = schemeHex(token, theme);
10265
+ if (hex) colors[slot] = { hex };
10266
+ }
10267
+ const headingFont = xmlText(resolveFontFamily(theme, "heading"));
10268
+ const bodyFont = xmlText(resolveFontFamily(theme, "body"));
10269
+ return {
10270
+ name: xmlText(theme.name) || xmlText(themeName) || "Office Theme",
10271
+ colors,
10272
+ ...headingFont ? { headingFont } : {},
10273
+ ...bodyFont ? { bodyFont } : {}
10274
+ };
10275
+ }
10276
+ function schemeHex(token, theme) {
10277
+ try {
10278
+ return resolveColor(token, theme);
10279
+ } catch {
10280
+ return void 0;
10281
+ }
10282
+ }
10283
+
10284
+ // src/ir/compiler.ts
9138
10285
  var CONTENT_BOOKMARK_ID_BASE = 2e6;
9139
10286
  var SINGLE_LINE_TWIPS = 240;
9140
10287
  function compileDocument(structure, layout, warnings = [], images = /* @__PURE__ */ new Map(), options = {}) {
@@ -9200,6 +10347,7 @@ function compileDocument(structure, layout, warnings = [], images = /* @__PURE__
9200
10347
  trackRevisions: structure.trackRevisions === true,
9201
10348
  ...noProofWords(structure.theme) ? { noProofWords: noProofWords(structure.theme) } : {}
9202
10349
  },
10350
+ theme: compileTheme(structure.theme, structure.themeName),
9203
10351
  // The statistic styles are appended rather than built with the rest: a
9204
10352
  // document that uses no statistic must keep the exact style set it had
9205
10353
  // before the component had styles, or every recorded golden moves for a
@@ -9968,14 +11116,6 @@ function compileShape2(props, children, scope) {
9968
11116
  );
9969
11117
  }
9970
11118
  const fill = style?.shading?.fill;
9971
- const dropOutline = Boolean(fill) && Boolean(outline.outline);
9972
- if (dropOutline) {
9973
- warnOnce(
9974
- ctx,
9975
- "text-box",
9976
- '[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.'
9977
- );
9978
- }
9979
11119
  ctx.features.require("text-boxes", path4);
9980
11120
  if (props.floating) ctx.features.require("floating-images", path4);
9981
11121
  const insets = shapeInsets(
@@ -9994,7 +11134,7 @@ function compileShape2(props, children, scope) {
9994
11134
  heightPx: height.pixels,
9995
11135
  children,
9996
11136
  ...fill ? { fill: irColor(resolveColor(fill, ctx.theme)) } : {},
9997
- ...outline.outline && !dropOutline ? { outline: outline.outline } : {},
11137
+ ...outline.outline ? { outline: outline.outline } : {},
9998
11138
  ...insets ? { insetsEmu: insets } : {},
9999
11139
  ...props.floating ? { floating: compileFloating(props.floating, ctx, path4) } : {}
10000
11140
  }
@@ -10176,7 +11316,9 @@ function compileToc(component, scope) {
10176
11316
  formatting: {
10177
11317
  alignment: "left",
10178
11318
  spacing: {
10179
- beforeTwips: ctx.theme.componentDefaults?.heading?.spacing?.before ?? 240,
11319
+ beforeTwips: wholeTwips(
11320
+ ctx.theme.componentDefaults?.heading?.spacing?.before ?? 240
11321
+ ),
10180
11322
  // ~9pt, which separates the title from the list clearly enough to
10181
11323
  // read as a heading rather than a first entry.
10182
11324
  afterTwips: 180
@@ -10296,8 +11438,8 @@ function compileStatistic(component, scope) {
10296
11438
  const alignment2 = compileAlignment(props.alignment) ?? "center";
10297
11439
  ctx.usesStatistic = true;
10298
11440
  const spacing = props.spacing ? {
10299
- ...props.spacing.before !== void 0 ? { beforeTwips: props.spacing.before } : {},
10300
- ...props.spacing.after !== void 0 ? { afterTwips: props.spacing.after } : {}
11441
+ ...props.spacing.before !== void 0 ? { beforeTwips: wholeTwips(props.spacing.before) } : {},
11442
+ ...props.spacing.after !== void 0 ? { afterTwips: wholeTwips(props.spacing.after) } : {}
10301
11443
  } : void 0;
10302
11444
  if (props.format !== void 0) {
10303
11445
  warnOnce(
@@ -10922,7 +12064,7 @@ function paragraphFormatting(props, ctx, extra = {}) {
10922
12064
  if (Array.isArray(props.tabStops) && props.tabStops.length > 0) {
10923
12065
  formatting.tabStops = props.tabStops.map(
10924
12066
  (stop) => ({
10925
- positionTwips: stop.position,
12067
+ positionTwips: wholeTwips(stop.position),
10926
12068
  type: stop.type ?? "left",
10927
12069
  ...stop.leader ? { leader: stop.leader } : {}
10928
12070
  })
@@ -10971,7 +12113,10 @@ function compileSpacing(props, ctx) {
10971
12113
  }
10972
12114
  function compileLineSpacing(value) {
10973
12115
  if (typeof value === "number") {
10974
- return { lineTwips: value * SINGLE_LINE_TWIPS, lineRule: "auto" };
12116
+ return {
12117
+ lineTwips: wholeTwips(value * SINGLE_LINE_TWIPS),
12118
+ lineRule: "auto"
12119
+ };
10975
12120
  }
10976
12121
  if (typeof value !== "object" || value === null) return void 0;
10977
12122
  const { type, value: amount } = value;
@@ -10992,7 +12137,7 @@ function compileLineSpacing(value) {
10992
12137
  };
10993
12138
  case "multiple":
10994
12139
  return {
10995
- ...amount !== void 0 ? { lineTwips: amount * SINGLE_LINE_TWIPS } : {},
12140
+ ...amount !== void 0 ? { lineTwips: wholeTwips(amount * SINGLE_LINE_TWIPS) } : {},
10996
12141
  lineRule: "auto"
10997
12142
  };
10998
12143
  default:
@@ -11212,6 +12357,7 @@ function compileImage2(component, scope) {
11212
12357
  }
11213
12358
  var DEFAULT_CHART_HEIGHT_INCHES = 3;
11214
12359
  var MAX_CHART_TEXT_POINTS = 10;
12360
+ var MIN_CHART_TEXT_POINTS = 1;
11215
12361
  function chartTextFont(theme) {
11216
12362
  const bodySize = getThemeFonts(theme).body?.size;
11217
12363
  let color = "000000";
@@ -11221,9 +12367,12 @@ function chartTextFont(theme) {
11221
12367
  }
11222
12368
  return {
11223
12369
  fontFamily: resolveFontFamily(theme, "body"),
11224
- fontSize: Math.min(
11225
- typeof bodySize === "number" && bodySize > 0 ? bodySize : 11,
11226
- MAX_CHART_TEXT_POINTS
12370
+ fontSize: Math.max(
12371
+ MIN_CHART_TEXT_POINTS,
12372
+ Math.min(
12373
+ typeof bodySize === "number" && bodySize > 0 ? bodySize : 11,
12374
+ MAX_CHART_TEXT_POINTS
12375
+ )
11227
12376
  ),
11228
12377
  bold: false,
11229
12378
  color
@@ -11234,7 +12383,7 @@ function compileChart(component, scope) {
11234
12383
  const props = component.props ?? {};
11235
12384
  if (props.type === "bubble") {
11236
12385
  throw new Error(
11237
- `Chart at ${path4} is a bubble chart, which no docx renderer draws; use the pptx \`chart\` component on the pptxgenjs renderer for one.`
12386
+ `Chart at ${path4} is a bubble chart, but the docx \`chart\` component has no bubble sizes; use the pptx \`chart\` component on the pptxgenjs renderer.`
11238
12387
  );
11239
12388
  }
11240
12389
  const rawSeries = Array.isArray(props.data) ? props.data : [];
@@ -11256,10 +12405,22 @@ function compileChart(component, scope) {
11256
12405
  `Chart series "${label}" at ${path4} has ${labels.length} labels and ${values.length} values; they must be the same length.`
11257
12406
  );
11258
12407
  }
12408
+ if (labels.length === 0) {
12409
+ throw new Error(
12410
+ `Chart series "${label}" at ${path4} has no data points; give it at least one label and value.`
12411
+ );
12412
+ }
12413
+ const numbers = values.map((value) => Number(value));
12414
+ const bad = numbers.findIndex((value) => !Number.isFinite(value));
12415
+ if (bad !== -1) {
12416
+ throw new Error(
12417
+ `Chart series "${label}" at ${path4} has a value that is not a finite number (${JSON.stringify(values[bad])} at "${String(labels[bad])}").`
12418
+ );
12419
+ }
11259
12420
  return {
11260
12421
  ...typeof raw.name === "string" ? { name: raw.name } : {},
11261
12422
  labels: labels.map((value) => String(value)),
11262
- values: values.map((value) => Number(value))
12423
+ values: numbers
11263
12424
  };
11264
12425
  });
11265
12426
  const categories = series[0].labels;
@@ -11271,6 +12432,34 @@ function compileChart(component, scope) {
11271
12432
  );
11272
12433
  }
11273
12434
  }
12435
+ if (props.type === "pie" && series.length > 1) {
12436
+ throw new Error(
12437
+ `Chart at ${path4} 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.`
12438
+ );
12439
+ }
12440
+ if (props.type === "pie" || props.type === "doughnut") {
12441
+ for (const [index, entry] of series.entries()) {
12442
+ const at = entry.values.findIndex((value) => value < 0);
12443
+ if (at === -1) continue;
12444
+ throw new Error(
12445
+ `Chart series "${entry.name ?? `series ${index}`}" at ${path4} has a negative value (${entry.values[at]} at "${entry.labels[at]}"); a ${props.type} chart draws each value as a share of the whole.`
12446
+ );
12447
+ }
12448
+ }
12449
+ if (props.type === "scatter" && series.some((entry) => entry.labels.some((label) => !isNumericLabel(label)))) {
12450
+ warnOnce(
12451
+ ctx,
12452
+ "chart",
12453
+ `Chart at ${path4} 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.`
12454
+ );
12455
+ }
12456
+ if ((props.type === "pie" || props.type === "doughnut") && (typeof props.catAxisTitle === "string" || typeof props.valAxisTitle === "string")) {
12457
+ warnOnce(
12458
+ ctx,
12459
+ "chart",
12460
+ `Chart at ${path4} is a ${props.type} chart, which has no axes; its axis titles are not drawn.`
12461
+ );
12462
+ }
11274
12463
  const colors = Array.isArray(props.chartColors) ? props.chartColors.map(
11275
12464
  (color) => resolveColor(String(color), ctx.theme)
11276
12465
  ) : chartPaletteValues(ctx.theme).flatMap((value) => {
@@ -11283,13 +12472,21 @@ function compileChart(component, scope) {
11283
12472
  const contentWidthInches = scope.measureTwips / 1440;
11284
12473
  const widthInches = typeof props.width === "number" ? props.width : contentWidthInches;
11285
12474
  const heightInches = typeof props.height === "number" ? props.height : DEFAULT_CHART_HEIGHT_INCHES;
12475
+ const widthEmu = inchesToEmu(widthInches);
12476
+ const heightEmu = inchesToEmu(heightInches);
12477
+ if (!(widthEmu > 0 && heightEmu > 0)) {
12478
+ const inches = (value) => String(Number.isFinite(value) ? Math.round(value * 100) / 100 : value);
12479
+ throw new Error(
12480
+ `Chart at ${path4} has no room: it would be ${inches(widthInches)}\xD7${inches(heightInches)} in. Set \`width\`/\`height\`, or give it a container with space.`
12481
+ );
12482
+ }
11286
12483
  const chart = {
11287
12484
  kind: "chart",
11288
12485
  chartType: props.type,
11289
12486
  series,
11290
12487
  colors,
11291
- widthEmu: inchesToEmu(widthInches),
11292
- heightEmu: inchesToEmu(heightInches),
12488
+ widthEmu,
12489
+ heightEmu,
11293
12490
  ...typeof props.title === "string" ? { title: props.title } : {},
11294
12491
  ...props.showTitle !== void 0 ? { showTitle: props.showTitle } : {},
11295
12492
  ...props.showLegend !== void 0 ? { showLegend: props.showLegend } : {},
@@ -11453,8 +12650,8 @@ function compileFrame(floating, ctx, path4) {
11453
12650
  const useAbsolute = hasHorizontalOffset || hasVerticalOffset;
11454
12651
  const frame = {
11455
12652
  // 2in by 1in when the author gave no size.
11456
- widthTwips: floating.width || 2880,
11457
- heightTwips: floating.height || 1440,
12653
+ widthTwips: wholeTwips(floating.width || 2880),
12654
+ heightTwips: wholeTwips(floating.height || 1440),
11458
12655
  anchorHorizontal: floating.horizontalPosition?.relative || "page",
11459
12656
  anchorVertical: floating.verticalPosition?.relative || "page",
11460
12657
  ...floating.wrap?.type ? { wrap: floating.wrap.type } : {}
@@ -11467,17 +12664,21 @@ function compileFrame(floating, ctx, path4) {
11467
12664
  if (typeof rawX === "string" || typeof rawY === "string") {
11468
12665
  const hRelative = floating.horizontalPosition?.relative;
11469
12666
  const vRelative = floating.verticalPosition?.relative;
11470
- frame.xTwips = resolveOffsetTwips(
11471
- rawX,
11472
- hRelative && hRelative !== "page" ? getAvailableWidthTwips(ctx.theme, ctx.themeName) : getPageWidthTwips(ctx.theme, ctx.themeName)
12667
+ frame.xTwips = wholeTwips(
12668
+ resolveOffsetTwips(
12669
+ rawX,
12670
+ hRelative && hRelative !== "page" ? getAvailableWidthTwips(ctx.theme, ctx.themeName) : getPageWidthTwips(ctx.theme, ctx.themeName)
12671
+ )
11473
12672
  );
11474
- frame.yTwips = resolveOffsetTwips(
11475
- rawY,
11476
- vRelative && vRelative !== "page" ? getAvailableHeightTwips(ctx.theme, ctx.themeName) : getPageHeightTwips(ctx.theme, ctx.themeName)
12673
+ frame.yTwips = wholeTwips(
12674
+ resolveOffsetTwips(
12675
+ rawY,
12676
+ vRelative && vRelative !== "page" ? getAvailableHeightTwips(ctx.theme, ctx.themeName) : getPageHeightTwips(ctx.theme, ctx.themeName)
12677
+ )
11477
12678
  );
11478
12679
  } else {
11479
- frame.xTwips = rawX;
11480
- frame.yTwips = rawY;
12680
+ frame.xTwips = wholeTwips(rawX);
12681
+ frame.yTwips = wholeTwips(rawY);
11481
12682
  }
11482
12683
  return frame;
11483
12684
  }
@@ -11488,7 +12689,7 @@ function compileFrame(floating, ctx, path4) {
11488
12689
  function compileFloating(floating, ctx, path4) {
11489
12690
  if (floating.wrap?.type === "tight") {
11490
12691
  throw new Error(
11491
- "Image floating wrap.type 'tight' is not supported due to invalid OOXML emitted by docx. Use 'square', 'topAndBottom', or 'none'."
12692
+ "Image floating wrap.type 'tight' is not supported. Use 'square', 'topAndBottom', or 'none'."
11492
12693
  );
11493
12694
  }
11494
12695
  const hRelative = floating.horizontalPosition?.relative;
@@ -11811,10 +13012,17 @@ function cellChildren(cell, baseStyle, scope, rowRevision) {
11811
13012
  return wrap(cellVisual(content, scope));
11812
13013
  }
11813
13014
  if (content.name !== "paragraph") {
13015
+ const text2 = unrenderedComponentText(content.name);
13016
+ warnOnce(
13017
+ ctx,
13018
+ "table",
13019
+ `${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.`,
13020
+ "W_UNSUPPORTED_CELL_CONTENT"
13021
+ );
11814
13022
  return wrap([
11815
13023
  {
11816
13024
  kind: "text",
11817
- text: `[Unsupported component type: ${content.name}]`,
13025
+ text: text2,
11818
13026
  formatting: placeholderFormatting(base)
11819
13027
  }
11820
13028
  ]);
@@ -13183,6 +14391,11 @@ init_widthUtils();
13183
14391
  import probe2 from "probe-image-size";
13184
14392
 
13185
14393
  // src/quality/text-inventory.ts
14394
+ import {
14395
+ unrenderedComponentText as unrenderedComponentText2
14396
+ } from "@json-to-office/quality";
14397
+ import { TABLE_CELL_COMPONENTS } from "@json-to-office/shared-docx";
14398
+ var CELL_COMPONENTS = new Set(TABLE_CELL_COMPONENTS);
13186
14399
  function tocDepth(depth) {
13187
14400
  const range = asRecord(depth) ?? {};
13188
14401
  const from = typeof range.from === "number" ? range.from : 1;
@@ -13231,7 +14444,14 @@ function collectDocxTextInventory(children, basePath = "/children") {
13231
14444
  add(`${path4}/content`, rec.content, cellRole, extra);
13232
14445
  else {
13233
14446
  const inner = asRecord(rec.content);
13234
- if (inner)
14447
+ if (inner && typeof inner.name === "string" && !CELL_COMPONENTS.has(inner.name)) {
14448
+ add(
14449
+ `${path4}/content`,
14450
+ unrenderedComponentText2(inner.name),
14451
+ cellRole,
14452
+ { ...extra, optional: true, unrendered: inner.name }
14453
+ );
14454
+ } else if (inner)
13235
14455
  visitNode(inner, `${path4}/content`, { ...inherited, inCell: true });
13236
14456
  }
13237
14457
  return;
@@ -15631,18 +16851,18 @@ var docxExhibitRequiredRule = {
15631
16851
  1
15632
16852
  );
15633
16853
  if (minimum <= 0) return [];
15634
- const charts = facts.filter((fact) => fact.kind === "docx/chart").length;
16854
+ const charts2 = facts.filter((fact) => fact.kind === "docx/chart").length;
15635
16855
  const tables = facts.filter(
15636
16856
  (fact) => fact.kind === "docx/table" && fact.columns.length >= 2
15637
16857
  ).length;
15638
- const exhibits = charts + tables;
16858
+ const exhibits = charts2 + tables;
15639
16859
  if (exhibits >= minimum) return [];
15640
16860
  return [
15641
16861
  {
15642
16862
  path: themeFact(facts)?.path ?? "/props",
15643
16863
  message: exhibits === 0 ? `The document carries no data exhibit \u2014 no chart, no table of two or more columns; the ${profile?.id ?? "selected"} profile expects at least ${minimum}.` : `The document carries ${exhibits} data exhibit(s); the ${profile?.id ?? "selected"} profile expects at least ${minimum}.`,
15644
16864
  suggestion: "Put the numbers the argument leans on into a chart-figure or a data-table block, with a takeaway and a source, where the text discusses them. A kpi-row summarises; it does not count.",
15645
- context: { charts, tables, minimum }
16865
+ context: { charts: charts2, tables, minimum }
15646
16866
  }
15647
16867
  ];
15648
16868
  }