@json-to-office/core-docx 7.2.0 → 7.3.1

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 (49) hide show
  1. package/dist/index.js +1565 -471
  2. package/dist/index.js.map +1 -1
  3. package/dist/ir/chartValues.d.ts +18 -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 +39 -3
  10. package/dist/ir/types.d.ts.map +1 -1
  11. package/dist/ir/validation.d.ts +2 -1
  12. package/dist/ir/validation.d.ts.map +1 -1
  13. package/dist/plugin/example/index.js +1562 -468
  14. package/dist/plugin/example/index.js.map +1 -1
  15. package/dist/renderers/chartText.d.ts +43 -0
  16. package/dist/renderers/chartText.d.ts.map +1 -0
  17. package/dist/renderers/docxjs/charts.d.ts +45 -0
  18. package/dist/renderers/docxjs/charts.d.ts.map +1 -0
  19. package/dist/renderers/docxjs/docxSubpath.d.ts +34 -0
  20. package/dist/renderers/docxjs/docxSubpath.d.ts.map +1 -0
  21. package/dist/renderers/docxjs/drawingGroup.d.ts +79 -0
  22. package/dist/renderers/docxjs/drawingGroup.d.ts.map +1 -0
  23. package/dist/renderers/docxjs/drawingGroupRepair.d.ts +36 -0
  24. package/dist/renderers/docxjs/drawingGroupRepair.d.ts.map +1 -0
  25. package/dist/renderers/docxjs/emit.d.ts +10 -2
  26. package/dist/renderers/docxjs/emit.d.ts.map +1 -1
  27. package/dist/renderers/docxjs/index.d.ts +2 -1
  28. package/dist/renderers/docxjs/index.d.ts.map +1 -1
  29. package/dist/renderers/docxjs/presetShapes.d.ts +233 -0
  30. package/dist/renderers/docxjs/presetShapes.d.ts.map +1 -0
  31. package/dist/renderers/office-open/chartLook.d.ts +45 -0
  32. package/dist/renderers/office-open/chartLook.d.ts.map +1 -0
  33. package/dist/renderers/office-open/chartParts.d.ts +29 -15
  34. package/dist/renderers/office-open/chartParts.d.ts.map +1 -1
  35. package/dist/renderers/office-open/emit.d.ts +25 -39
  36. package/dist/renderers/office-open/emit.d.ts.map +1 -1
  37. package/dist/renderers/office-open/index.d.ts +16 -4
  38. package/dist/renderers/office-open/index.d.ts.map +1 -1
  39. package/dist/renderers/office-open/themePart.d.ts +28 -0
  40. package/dist/renderers/office-open/themePart.d.ts.map +1 -0
  41. package/dist/tsconfig.tsbuildinfo +1 -1
  42. package/dist/utils/chartWorkbook.d.ts +2 -1
  43. package/dist/utils/chartWorkbook.d.ts.map +1 -1
  44. package/dist/utils/fixFloatingImageIds.d.ts +17 -9
  45. package/dist/utils/fixFloatingImageIds.d.ts.map +1 -1
  46. package/dist/utils/packageDocument.d.ts.map +1 -1
  47. package/package.json +6 -6
  48. package/dist/renderers/office-open/cellTocs.d.ts +0 -26
  49. package/dist/renderers/office-open/cellTocs.d.ts.map +0 -1
@@ -2751,7 +2751,7 @@ var init_imageUtils = __esm({
2751
2751
  });
2752
2752
 
2753
2753
  // src/ir/types.ts
2754
- var TWIPS_PER_INCH, TWIPS_PER_POINT2, EMU_PER_INCH, EMU_PER_TWIP, DOCX_IR_SCHEMA_VERSION, DOCX_IR_BORDER_STYLES;
2754
+ var TWIPS_PER_INCH, TWIPS_PER_POINT2, EMU_PER_INCH, EMU_PER_TWIP, DOCX_IR_SCHEMA_VERSION, NOT_XML_CHAR, DOCX_IR_BORDER_STYLES;
2755
2755
  var init_types = __esm({
2756
2756
  "src/ir/types.ts"() {
2757
2757
  "use strict";
@@ -2760,6 +2760,7 @@ var init_types = __esm({
2760
2760
  EMU_PER_INCH = 914400;
2761
2761
  EMU_PER_TWIP = EMU_PER_INCH / TWIPS_PER_INCH;
2762
2762
  DOCX_IR_SCHEMA_VERSION = 1;
2763
+ NOT_XML_CHAR = /[^\t\n\r\u0020-\uD7FF\uE000-\uFFFD\u{10000}-\u{10FFFF}]/u;
2763
2764
  DOCX_IR_BORDER_STYLES = [
2764
2765
  "single",
2765
2766
  "dashDotStroked",
@@ -2846,6 +2847,242 @@ var init_units = __esm({
2846
2847
  }
2847
2848
  });
2848
2849
 
2850
+ // src/ir/chartValues.ts
2851
+ function numericLabelValue(label) {
2852
+ const value = label.trim();
2853
+ return NUMERIC_LABEL.test(value) ? Number(value) : void 0;
2854
+ }
2855
+ function isNumericLabel(label) {
2856
+ const value = numericLabelValue(label);
2857
+ return value !== void 0 && Number.isFinite(value);
2858
+ }
2859
+ function isOutOfRangeNumericLabel(label) {
2860
+ const value = numericLabelValue(label);
2861
+ return value !== void 0 && !Number.isFinite(value);
2862
+ }
2863
+ function scatterX(labels, index) {
2864
+ const label = labels[index];
2865
+ const value = label === void 0 ? void 0 : numericLabelValue(label);
2866
+ if (value === void 0) return index + 1;
2867
+ if (!Number.isFinite(value)) {
2868
+ throw new RangeError(
2869
+ `Scatter x label ${JSON.stringify(label)} is not finite.`
2870
+ );
2871
+ }
2872
+ return value;
2873
+ }
2874
+ var NUMERIC_LABEL;
2875
+ var init_chartValues = __esm({
2876
+ "src/ir/chartValues.ts"() {
2877
+ "use strict";
2878
+ NUMERIC_LABEL = /^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$/;
2879
+ }
2880
+ });
2881
+
2882
+ // src/renderers/chartText.ts
2883
+ function chartTextRoles(font) {
2884
+ const { bold, ...rest } = font;
2885
+ const text = rest;
2886
+ return {
2887
+ text,
2888
+ axisTitle: { ...text, bold },
2889
+ title: {
2890
+ ...text,
2891
+ fontSize: Math.round(text.fontSize * CHART_TITLE_SCALE),
2892
+ bold: true
2893
+ }
2894
+ };
2895
+ }
2896
+ var CHART_TITLE_SCALE;
2897
+ var init_chartText = __esm({
2898
+ "src/renderers/chartText.ts"() {
2899
+ "use strict";
2900
+ CHART_TITLE_SCALE = 1.4;
2901
+ }
2902
+ });
2903
+
2904
+ // src/renderers/docxjs/docxSubpath.ts
2905
+ function docxSubpath(specifier, component, importer) {
2906
+ let module;
2907
+ let failure;
2908
+ let loading;
2909
+ return {
2910
+ specifier,
2911
+ load() {
2912
+ loading ??= importer().then(
2913
+ (loadedModule) => {
2914
+ module = loadedModule;
2915
+ },
2916
+ (reason) => {
2917
+ failure = { reason };
2918
+ }
2919
+ );
2920
+ return loading;
2921
+ },
2922
+ loaded: () => module !== void 0,
2923
+ get() {
2924
+ if (module !== void 0) return module;
2925
+ if (failure !== void 0) {
2926
+ const { reason } = failure;
2927
+ const detail = reason instanceof Error ? reason.message : String(reason);
2928
+ throw new Error(
2929
+ `The docx \`${component}\` component needs docx 9.8.0 or later: the \`${specifier}\` entry could not be loaded (${detail})`
2930
+ );
2931
+ }
2932
+ throw new Error(
2933
+ `${specifier} is not loaded: await its load() before buildDocument() (render() does)`
2934
+ );
2935
+ }
2936
+ };
2937
+ }
2938
+ var init_docxSubpath = __esm({
2939
+ "src/renderers/docxjs/docxSubpath.ts"() {
2940
+ "use strict";
2941
+ }
2942
+ });
2943
+
2944
+ // src/renderers/docxjs/charts.ts
2945
+ import { assertNever } from "@json-to-office/shared/rendering";
2946
+ function loadDocxCharts() {
2947
+ return charts.load();
2948
+ }
2949
+ function chartFont(role) {
2950
+ return {
2951
+ name: role.fontFamily,
2952
+ size: role.fontSize,
2953
+ color: role.color,
2954
+ bold: role.bold ?? false
2955
+ };
2956
+ }
2957
+ function chartRunOptions(chart, floating) {
2958
+ const roles = chartTextRoles(chart.textFont);
2959
+ const text = chartFont(roles.text);
2960
+ const axisTitle = chartFont(roles.axisTitle);
2961
+ const palette = chart.colors;
2962
+ const colorAt = (index) => palette.length > 0 ? palette[index % palette.length] : void 0;
2963
+ const withColor = (index) => {
2964
+ const color = colorAt(index);
2965
+ return color ? { color } : {};
2966
+ };
2967
+ const seriesName = (index) => chart.series[index].name ?? `Series ${index + 1}`;
2968
+ const categories = chart.series[0]?.labels ?? [];
2969
+ const axis = (title) => ({
2970
+ ...title ? { title: { text: title, font: axisTitle } } : {},
2971
+ font: text
2972
+ });
2973
+ const common = {
2974
+ ...chart.title && chart.showTitle !== false ? { title: { text: chart.title, font: chartFont(roles.title) } } : {},
2975
+ legend: chart.showLegend === false ? false : {
2976
+ position: LEGEND_POSITION[chart.legendPosition ?? "b"],
2977
+ font: text
2978
+ },
2979
+ font: { name: roles.text.fontFamily, color: roles.text.color },
2980
+ // Office-open's chart space is unfilled and unbordered; `ChartRun`'s
2981
+ // default is Word's white fill and grey border.
2982
+ chartArea: { fill: "none", border: "none" },
2983
+ transformation: {
2984
+ width: emuToPixels(chart.widthEmu),
2985
+ height: emuToPixels(chart.heightEmu)
2986
+ },
2987
+ // Without an authored description `ChartRun` describes the chart from
2988
+ // its data, which is deterministic; the name stays empty, as it is on
2989
+ // every other drawing this renderer writes.
2990
+ ...chart.altText ? { altText: { name: "", description: chart.altText } } : {},
2991
+ ...floating ? { floating } : {}
2992
+ };
2993
+ switch (chart.chartType) {
2994
+ case "column":
2995
+ case "bar":
2996
+ case "area":
2997
+ case "line":
2998
+ return {
2999
+ ...common,
3000
+ type: chart.chartType,
3001
+ categories,
3002
+ ...chart.chartType === "line" ? { markers: true } : {},
3003
+ series: chart.series.map((entry, index) => ({
3004
+ name: seriesName(index),
3005
+ values: entry.values,
3006
+ ...withColor(index)
3007
+ })),
3008
+ categoryAxis: axis(chart.categoryAxisTitle),
3009
+ valueAxis: axis(chart.valueAxisTitle)
3010
+ };
3011
+ case "pie":
3012
+ case "doughnut":
3013
+ return {
3014
+ ...common,
3015
+ type: chart.chartType,
3016
+ categories,
3017
+ series: chart.series.map((entry, index) => ({
3018
+ name: seriesName(index),
3019
+ values: entry.values,
3020
+ ...palette.length > 0 ? { colors: entry.values.map((_, point) => colorAt(point)) } : {}
3021
+ }))
3022
+ };
3023
+ case "radar":
3024
+ return {
3025
+ ...common,
3026
+ type: "radar",
3027
+ categories,
3028
+ markers: true,
3029
+ series: chart.series.map((entry, index) => ({
3030
+ name: seriesName(index),
3031
+ values: entry.values,
3032
+ ...withColor(index)
3033
+ })),
3034
+ categoryAxis: axis(chart.categoryAxisTitle),
3035
+ valueAxis: axis(chart.valueAxisTitle)
3036
+ };
3037
+ case "scatter":
3038
+ return {
3039
+ ...common,
3040
+ type: "scatter",
3041
+ markers: true,
3042
+ lines: "straight",
3043
+ series: chart.series.map((entry, index) => ({
3044
+ name: seriesName(index),
3045
+ points: entry.values.map((y, point) => ({
3046
+ x: scatterX(entry.labels, point),
3047
+ y
3048
+ })),
3049
+ ...withColor(index)
3050
+ })),
3051
+ xAxis: { ...axis(chart.categoryAxisTitle), gridlines: false },
3052
+ yAxis: axis(chart.valueAxisTitle)
3053
+ };
3054
+ case "bubble":
3055
+ throw new Error(
3056
+ "the docxjs renderer has no emitter for a bubble chart; the compiler should have refused it"
3057
+ );
3058
+ default:
3059
+ return assertNever(chart.chartType, "DocxIrChartType");
3060
+ }
3061
+ }
3062
+ function emitChart(chart, floating) {
3063
+ return new (charts.get()).ChartRun(
3064
+ chartRunOptions(chart, floating)
3065
+ );
3066
+ }
3067
+ var charts, LEGEND_POSITION;
3068
+ var init_charts = __esm({
3069
+ "src/renderers/docxjs/charts.ts"() {
3070
+ "use strict";
3071
+ init_chartValues();
3072
+ init_units();
3073
+ init_chartText();
3074
+ init_docxSubpath();
3075
+ charts = docxSubpath("docx/charts", "chart", () => import("docx/charts"));
3076
+ LEGEND_POSITION = {
3077
+ b: "bottom",
3078
+ l: "left",
3079
+ r: "right",
3080
+ t: "top",
3081
+ tr: "topRight"
3082
+ };
3083
+ }
3084
+ });
3085
+
2849
3086
  // src/renderers/docxjs/emit.ts
2850
3087
  import {
2851
3088
  AlignmentType,
@@ -2881,7 +3118,7 @@ import {
2881
3118
  WidthType,
2882
3119
  WpsShapeRun
2883
3120
  } from "docx";
2884
- import { assertNever } from "@json-to-office/shared/rendering";
3121
+ import { assertNever as assertNever2 } from "@json-to-office/shared/rendering";
2885
3122
  function complexField(instruction, formatting, leading, cachedText) {
2886
3123
  const options = runOptions(formatting);
2887
3124
  const fieldChar = (type, extra = {}) => new TextRun({
@@ -2918,7 +3155,12 @@ function runOptions(formatting) {
2918
3155
  if (formatting.bold !== void 0) options.bold = formatting.bold;
2919
3156
  if (formatting.italic !== void 0) options.italics = formatting.italic;
2920
3157
  if (formatting.underline !== void 0) {
2921
- options.underline = formatting.underline ? { type: formatting.underline.type } : void 0;
3158
+ options.underline = formatting.underline ? {
3159
+ type: formatting.underline.type,
3160
+ // A native visual's text is the one place a run states its own
3161
+ // underline colour.
3162
+ ...formatting.underline.color ? { color: formatting.underline.color.hex } : {}
3163
+ } : void 0;
2922
3164
  }
2923
3165
  if (formatting.strike !== void 0) options.strike = formatting.strike;
2924
3166
  if (formatting.allCaps !== void 0) options.allCaps = formatting.allCaps;
@@ -3044,21 +3286,36 @@ function inlineChildren(children, resources = /* @__PURE__ */ new Map()) {
3044
3286
  case "shape":
3045
3287
  out.push(emitShape(child, resources));
3046
3288
  break;
3047
- case "drawingGroup":
3048
- throw new Error(
3049
- "the docxjs renderer has no emitter for a drawing group; this document should have been refused by the capability check"
3050
- );
3051
- case "chart":
3052
- throw new Error(
3053
- "the docxjs renderer has no emitter for a chart; this document should have been refused by the capability check"
3289
+ case "drawingGroup": {
3290
+ const build = resources.drawingGroup;
3291
+ if (!build) {
3292
+ throw new Error(
3293
+ "the docxjs renderer was handed a drawing group without loading docx/shapes; render() loads it whenever the IR holds one"
3294
+ );
3295
+ }
3296
+ const pending = breakOption();
3297
+ if (pending.break) out.push(new TextRun(pending));
3298
+ out.push(build(child, resources));
3299
+ break;
3300
+ }
3301
+ case "chart": {
3302
+ const pending = breakOption();
3303
+ if (pending.break) out.push(new TextRun(pending));
3304
+ out.push(
3305
+ emitChart(
3306
+ child,
3307
+ child.floating ? floatingOptions(child.floating) : void 0
3308
+ )
3054
3309
  );
3310
+ break;
3311
+ }
3055
3312
  case "noteReference":
3056
3313
  out.push(
3057
3314
  child.noteKind === "endnote" ? new EndnoteReferenceRun(child.id) : new FootnoteReferenceRun(child.id)
3058
3315
  );
3059
3316
  break;
3060
3317
  default:
3061
- assertNever(child, "DocxIrInline");
3318
+ assertNever2(child, "DocxIrInline");
3062
3319
  }
3063
3320
  }
3064
3321
  return out;
@@ -3263,7 +3520,7 @@ function emitBlock(block2, resources = /* @__PURE__ */ new Map()) {
3263
3520
  case "toc":
3264
3521
  return emitToc(block2);
3265
3522
  default:
3266
- return assertNever(block2, "DocxIrBlock");
3523
+ return assertNever2(block2, "DocxIrBlock");
3267
3524
  }
3268
3525
  }
3269
3526
  function emitToc(block2) {
@@ -3407,6 +3664,7 @@ var ALIGNMENT2, PAGE_FIELDS, isSequenceField, WRAP_TYPE, ThemedTableOfContents,
3407
3664
  var init_emit = __esm({
3408
3665
  "src/renderers/docxjs/emit.ts"() {
3409
3666
  "use strict";
3667
+ init_charts();
3410
3668
  ALIGNMENT2 = {
3411
3669
  left: AlignmentType.LEFT,
3412
3670
  center: AlignmentType.CENTER,
@@ -3705,10 +3963,13 @@ function canonicalizeRelationshipIds(zip) {
3705
3963
  const rename = new Map(
3706
3964
  ordered.map((id, index) => [id, `rId${highest + 1 + index}`])
3707
3965
  );
3708
- const rewrite = (xml) => xml.replace(/"([^"]+)"/g, (whole, value) => {
3709
- const replacement = rename.get(value);
3710
- return replacement === void 0 ? whole : `"${replacement}"`;
3711
- });
3966
+ const rewrite = (xml) => xml.replace(
3967
+ /(\s[\w:.-]+=)"([^"]*)"/g,
3968
+ (whole, prefix, value) => {
3969
+ const replacement = rename.get(value);
3970
+ return replacement === void 0 ? whole : `${prefix}"${replacement}"`;
3971
+ }
3972
+ );
3712
3973
  updates.push({ name: relsName, xml: rewrite(relsXml) });
3713
3974
  if (partEntry) {
3714
3975
  updates.push({ name: partName, xml: rewrite(partXml) });
@@ -3719,17 +3980,62 @@ function canonicalizeRelationshipIds(zip) {
3719
3980
  if (entry) zip.updateFile(entry, Buffer.from(update.xml, "utf8"));
3720
3981
  }
3721
3982
  }
3983
+ function canonicalizeEmbeddedPackages(zip, generatedAt, depth = 1) {
3984
+ const names = zip.getEntries().map((entry) => entry.entryName).filter((name) => EMBEDDED_OFFICE_PACKAGE.test(name));
3985
+ let changed = false;
3986
+ for (const name of names) {
3987
+ const entry = zip.getEntry(name);
3988
+ if (!entry) continue;
3989
+ let nested;
3990
+ try {
3991
+ nested = new AdmZip(entry.getData());
3992
+ nested.getEntries();
3993
+ } catch {
3994
+ continue;
3995
+ }
3996
+ if (canonicalizeNestedPackage(nested, generatedAt, depth)) {
3997
+ zip.updateFile(entry, nested.toBuffer());
3998
+ changed = true;
3999
+ }
4000
+ }
4001
+ return changed;
4002
+ }
4003
+ function canonicalizeNestedPackage(zip, generatedAt, depth) {
4004
+ let changed = false;
4005
+ const coreProperties3 = zip.getEntry("docProps/core.xml");
4006
+ if (coreProperties3) {
4007
+ const xml = coreProperties3.getData().toString("utf8");
4008
+ const normalized = xml.replace(
4009
+ CORE_TIMESTAMP,
4010
+ `$1${generatedAt.toISOString()}$2`
4011
+ );
4012
+ if (normalized !== xml) {
4013
+ zip.updateFile(coreProperties3, Buffer.from(normalized, "utf8"));
4014
+ changed = true;
4015
+ }
4016
+ }
4017
+ if (depth < MAX_EMBEDDED_DEPTH && canonicalizeEmbeddedPackages(zip, generatedAt, depth + 1)) {
4018
+ changed = true;
4019
+ }
4020
+ const zipTimestamp = toDosTime(generatedAt);
4021
+ for (const entry of zip.getEntries()) {
4022
+ const header = entry.header;
4023
+ if (header.timeval !== zipTimestamp) {
4024
+ header.timeval = zipTimestamp;
4025
+ changed = true;
4026
+ }
4027
+ }
4028
+ return changed;
4029
+ }
3722
4030
  function canonicalizeDocxBuffer(buffer, generatedAt = DEFAULT_GENERATION_DATE) {
3723
4031
  const zip = new AdmZip(buffer);
3724
4032
  normalizeTextCase(zip);
3725
4033
  canonicalizeRelationshipIds(zip);
4034
+ canonicalizeEmbeddedPackages(zip, generatedAt);
3726
4035
  const isoTimestamp = generatedAt.toISOString();
3727
4036
  const coreProperties3 = zip.getEntry("docProps/core.xml");
3728
4037
  if (coreProperties3) {
3729
- const normalized = coreProperties3.getData().toString("utf8").replace(
3730
- /(<dcterms:(?:created|modified)\b[^>]*>)[^<]*(<\/dcterms:(?:created|modified)>)/g,
3731
- `$1${isoTimestamp}$2`
3732
- );
4038
+ const normalized = coreProperties3.getData().toString("utf8").replace(CORE_TIMESTAMP, `$1${isoTimestamp}$2`);
3733
4039
  zip.updateFile(coreProperties3, Buffer.from(normalized, "utf8"));
3734
4040
  }
3735
4041
  const zipTimestamp = toDosTime(generatedAt);
@@ -3742,35 +4048,567 @@ function normalizeDocxCaseBuffer(buffer) {
3742
4048
  const zip = new AdmZip(buffer);
3743
4049
  return normalizeTextCase(zip) ? zip.toBuffer() : buffer;
3744
4050
  }
3745
- var DEFAULT_GENERATION_DATE, STABLE_RELATIONSHIP_ID;
4051
+ var DEFAULT_GENERATION_DATE, STABLE_RELATIONSHIP_ID, CORE_TIMESTAMP, EMBEDDED_OFFICE_PACKAGE, MAX_EMBEDDED_DEPTH;
3746
4052
  var init_packageDocument = __esm({
3747
4053
  "src/utils/packageDocument.ts"() {
3748
4054
  "use strict";
3749
4055
  init_normalizeTextCase();
3750
4056
  DEFAULT_GENERATION_DATE = /* @__PURE__ */ new Date("2000-01-01T00:00:00.000Z");
3751
4057
  STABLE_RELATIONSHIP_ID = /^rId\d+$/;
4058
+ CORE_TIMESTAMP = /(<dcterms:(?:created|modified)\b[^>]*>)[^<]*(<\/dcterms:(?:created|modified)>)/g;
4059
+ EMBEDDED_OFFICE_PACKAGE = /\.(?:docx|pptx|xlsx|xlsm)$/i;
4060
+ MAX_EMBEDDED_DEPTH = 3;
3752
4061
  }
3753
4062
  });
3754
4063
 
3755
4064
  // src/utils/fixFloatingImageIds.ts
3756
4065
  import AdmZip2 from "adm-zip";
4066
+ function compareDrawingParts(a, b) {
4067
+ const ma = DRAWING_PART.exec(a);
4068
+ const mb = DRAWING_PART.exec(b);
4069
+ const rank = RANK.indexOf(ma[1]) - RANK.indexOf(mb[1]);
4070
+ return rank !== 0 ? rank : Number(ma[2] || 0) - Number(mb[2] || 0);
4071
+ }
3757
4072
  function fixFloatingImageIdsInBuffer(buffer) {
3758
4073
  const zip = new AdmZip2(buffer);
3759
- const documentEntry = zip.getEntry("word/document.xml");
3760
- if (!documentEntry) {
4074
+ if (!zip.getEntry("word/document.xml")) {
3761
4075
  throw new Error("document.xml not found in DOCX");
3762
4076
  }
4077
+ const parts = zip.getEntries().filter((entry) => DRAWING_PART.test(entry.entryName)).sort((a, b) => compareDrawingParts(a.entryName, b.entryName));
3763
4078
  let idCounter = 1;
3764
- const documentXml = documentEntry.getData().toString("utf8").replace(/(<wp:docPr\b[^>]*?\s)id="\d+"/g, (_match, prefix) => {
3765
- const newId = idCounter++;
3766
- return `${prefix}id="${newId}"`;
3767
- });
3768
- zip.updateFile(documentEntry, Buffer.from(documentXml, "utf8"));
4079
+ for (const entry of parts) {
4080
+ const xml = entry.getData().toString("utf8");
4081
+ const renumbered = xml.replace(
4082
+ /(<wp:docPr\b[^>]*?\s)id="\d+"/g,
4083
+ (_match, prefix) => `${prefix}id="${idCounter++}"`
4084
+ );
4085
+ zip.updateFile(entry, Buffer.from(renumbered, "utf8"));
4086
+ }
3769
4087
  return zip.toBuffer();
3770
4088
  }
4089
+ var DRAWING_PART, RANK;
3771
4090
  var init_fixFloatingImageIds = __esm({
3772
4091
  "src/utils/fixFloatingImageIds.ts"() {
3773
4092
  "use strict";
4093
+ DRAWING_PART = /^word\/(document|header|footer|footnotes|endnotes|comments)(\d*)\.xml$/;
4094
+ RANK = [
4095
+ "document",
4096
+ "header",
4097
+ "footer",
4098
+ "footnotes",
4099
+ "endnotes",
4100
+ "comments"
4101
+ ];
4102
+ }
4103
+ });
4104
+
4105
+ // src/renderers/docxjs/presetShapes.ts
4106
+ function docxPresetShape(geometry) {
4107
+ const name = Object.hasOwn(OOXML_PRESET_TO_DOCX, geometry) ? OOXML_PRESET_TO_DOCX[geometry] : void 0;
4108
+ if (!name) {
4109
+ throw new Error(
4110
+ `the docxjs renderer has no preset for the OOXML geometry "${geometry}"`
4111
+ );
4112
+ }
4113
+ return name;
4114
+ }
4115
+ function docxLineDash(dash) {
4116
+ const name = Object.hasOwn(OOXML_DASH_TO_DOCX, dash) ? OOXML_DASH_TO_DOCX[dash] : void 0;
4117
+ if (!name) {
4118
+ throw new Error(
4119
+ `the docxjs renderer has no line dash for the OOXML value "${dash}"`
4120
+ );
4121
+ }
4122
+ return name;
4123
+ }
4124
+ var OOXML_PRESET_TO_DOCX, OOXML_DASH_TO_DOCX;
4125
+ var init_presetShapes = __esm({
4126
+ "src/renderers/docxjs/presetShapes.ts"() {
4127
+ "use strict";
4128
+ OOXML_PRESET_TO_DOCX = {
4129
+ line: "line",
4130
+ lineInv: "inverseLine",
4131
+ straightConnector1: "straightConnector",
4132
+ bentConnector2: "elbowConnectorOneBend",
4133
+ bentConnector3: "elbowConnector",
4134
+ bentConnector4: "elbowConnectorThreeBends",
4135
+ bentConnector5: "elbowConnectorFourBends",
4136
+ curvedConnector2: "curvedConnectorOneBend",
4137
+ curvedConnector3: "curvedConnector",
4138
+ curvedConnector4: "curvedConnectorThreeBends",
4139
+ curvedConnector5: "curvedConnectorFourBends",
4140
+ triangle: "triangle",
4141
+ rtTriangle: "rightTriangle",
4142
+ diamond: "diamond",
4143
+ parallelogram: "parallelogram",
4144
+ trapezoid: "trapezoid",
4145
+ nonIsoscelesTrapezoid: "nonIsoscelesTrapezoid",
4146
+ pentagon: "pentagon",
4147
+ hexagon: "hexagon",
4148
+ heptagon: "heptagon",
4149
+ octagon: "octagon",
4150
+ decagon: "decagon",
4151
+ dodecagon: "dodecagon",
4152
+ ellipse: "ellipse",
4153
+ teardrop: "teardrop",
4154
+ pieWedge: "pieWedge",
4155
+ pie: "pie",
4156
+ blockArc: "blockArc",
4157
+ donut: "donut",
4158
+ noSmoking: "noSymbol",
4159
+ chord: "chord",
4160
+ arc: "arc",
4161
+ frame: "frame",
4162
+ halfFrame: "halfFrame",
4163
+ corner: "lShape",
4164
+ diagStripe: "diagonalStripe",
4165
+ plus: "cross",
4166
+ plaque: "plaque",
4167
+ can: "cylinder",
4168
+ cube: "cube",
4169
+ bevel: "beveledRectangle",
4170
+ foldedCorner: "foldedCorner",
4171
+ smileyFace: "smileyFace",
4172
+ heart: "heart",
4173
+ lightningBolt: "lightningBolt",
4174
+ sun: "sun",
4175
+ moon: "moon",
4176
+ cloud: "cloud",
4177
+ leftBracket: "leftBracket",
4178
+ rightBracket: "rightBracket",
4179
+ leftBrace: "leftBrace",
4180
+ rightBrace: "rightBrace",
4181
+ bracketPair: "bracketPair",
4182
+ bracePair: "bracePair",
4183
+ rect: "rectangle",
4184
+ roundRect: "roundedRectangle",
4185
+ round1Rect: "roundedCornerRectangle",
4186
+ round2SameRect: "topRoundedCornersRectangle",
4187
+ round2DiagRect: "diagonalRoundedCornersRectangle",
4188
+ snip1Rect: "snippedCornerRectangle",
4189
+ snip2SameRect: "topSnippedCornersRectangle",
4190
+ snip2DiagRect: "diagonalSnippedCornersRectangle",
4191
+ snipRoundRect: "roundedAndSnippedCornersRectangle",
4192
+ rightArrow: "rightArrow",
4193
+ leftArrow: "leftArrow",
4194
+ upArrow: "upArrow",
4195
+ downArrow: "downArrow",
4196
+ leftRightArrow: "leftRightArrow",
4197
+ upDownArrow: "upDownArrow",
4198
+ quadArrow: "quadArrow",
4199
+ leftRightUpArrow: "leftRightUpArrow",
4200
+ bentArrow: "bentArrow",
4201
+ uturnArrow: "uTurnArrow",
4202
+ leftUpArrow: "leftUpArrow",
4203
+ bentUpArrow: "bentUpArrow",
4204
+ curvedRightArrow: "curvedRightArrow",
4205
+ curvedLeftArrow: "curvedLeftArrow",
4206
+ curvedUpArrow: "curvedUpArrow",
4207
+ curvedDownArrow: "curvedDownArrow",
4208
+ stripedRightArrow: "stripedRightArrow",
4209
+ notchedRightArrow: "notchedRightArrow",
4210
+ homePlate: "pentagonArrow",
4211
+ chevron: "chevron",
4212
+ rightArrowCallout: "rightArrowCallout",
4213
+ downArrowCallout: "downArrowCallout",
4214
+ leftArrowCallout: "leftArrowCallout",
4215
+ upArrowCallout: "upArrowCallout",
4216
+ leftRightArrowCallout: "leftRightArrowCallout",
4217
+ upDownArrowCallout: "upDownArrowCallout",
4218
+ quadArrowCallout: "quadArrowCallout",
4219
+ circularArrow: "circularArrow",
4220
+ leftCircularArrow: "leftCircularArrow",
4221
+ leftRightCircularArrow: "leftRightCircularArrow",
4222
+ swooshArrow: "swooshArrow",
4223
+ mathPlus: "mathPlus",
4224
+ mathMinus: "mathMinus",
4225
+ mathMultiply: "mathMultiply",
4226
+ mathDivide: "mathDivide",
4227
+ mathEqual: "mathEqual",
4228
+ mathNotEqual: "mathNotEqual",
4229
+ flowChartProcess: "flowChartProcess",
4230
+ flowChartAlternateProcess: "flowChartAlternateProcess",
4231
+ flowChartDecision: "flowChartDecision",
4232
+ flowChartInputOutput: "flowChartInputOutput",
4233
+ flowChartPredefinedProcess: "flowChartPredefinedProcess",
4234
+ flowChartInternalStorage: "flowChartInternalStorage",
4235
+ flowChartDocument: "flowChartDocument",
4236
+ flowChartMultidocument: "flowChartMultidocument",
4237
+ flowChartTerminator: "flowChartTerminator",
4238
+ flowChartPreparation: "flowChartPreparation",
4239
+ flowChartManualInput: "flowChartManualInput",
4240
+ flowChartManualOperation: "flowChartManualOperation",
4241
+ flowChartConnector: "flowChartConnector",
4242
+ flowChartOffpageConnector: "flowChartOffpageConnector",
4243
+ flowChartPunchedCard: "flowChartPunchedCard",
4244
+ flowChartPunchedTape: "flowChartPunchedTape",
4245
+ flowChartSummingJunction: "flowChartSummingJunction",
4246
+ flowChartOr: "flowChartOr",
4247
+ flowChartCollate: "flowChartCollate",
4248
+ flowChartSort: "flowChartSort",
4249
+ flowChartExtract: "flowChartExtract",
4250
+ flowChartMerge: "flowChartMerge",
4251
+ flowChartOfflineStorage: "flowChartOfflineStorage",
4252
+ flowChartOnlineStorage: "flowChartOnlineStorage",
4253
+ flowChartDelay: "flowChartDelay",
4254
+ flowChartMagneticTape: "flowChartMagneticTape",
4255
+ flowChartMagneticDisk: "flowChartMagneticDisk",
4256
+ flowChartMagneticDrum: "flowChartMagneticDrum",
4257
+ flowChartDisplay: "flowChartDisplay",
4258
+ irregularSeal1: "explosion12",
4259
+ irregularSeal2: "explosion14",
4260
+ star4: "star4",
4261
+ star5: "star5",
4262
+ star6: "star6",
4263
+ star7: "star7",
4264
+ star8: "star8",
4265
+ star10: "star10",
4266
+ star12: "star12",
4267
+ star16: "star16",
4268
+ star24: "star24",
4269
+ star32: "star32",
4270
+ ribbon2: "ribbonUp",
4271
+ ribbon: "ribbonDown",
4272
+ ellipseRibbon2: "curvedRibbonUp",
4273
+ ellipseRibbon: "curvedRibbonDown",
4274
+ leftRightRibbon: "leftRightRibbon",
4275
+ verticalScroll: "verticalScroll",
4276
+ horizontalScroll: "horizontalScroll",
4277
+ wave: "wave",
4278
+ doubleWave: "doubleWave",
4279
+ wedgeRectCallout: "rectangularCallout",
4280
+ wedgeRoundRectCallout: "roundedRectangularCallout",
4281
+ wedgeEllipseCallout: "ellipticalCallout",
4282
+ cloudCallout: "cloudCallout",
4283
+ borderCallout1: "lineCallout",
4284
+ borderCallout2: "bentLineCallout",
4285
+ borderCallout3: "doubleBentLineCallout",
4286
+ accentCallout1: "lineCalloutWithAccentBar",
4287
+ accentCallout2: "bentLineCalloutWithAccentBar",
4288
+ accentCallout3: "doubleBentLineCalloutWithAccentBar",
4289
+ callout1: "lineCalloutWithNoBorder",
4290
+ callout2: "bentLineCalloutWithNoBorder",
4291
+ callout3: "doubleBentLineCalloutWithNoBorder",
4292
+ accentBorderCallout1: "lineCalloutWithBorderAndAccentBar",
4293
+ accentBorderCallout2: "bentLineCalloutWithBorderAndAccentBar",
4294
+ accentBorderCallout3: "doubleBentLineCalloutWithBorderAndAccentBar",
4295
+ actionButtonBlank: "actionButtonBlank",
4296
+ actionButtonHome: "actionButtonHome",
4297
+ actionButtonHelp: "actionButtonHelp",
4298
+ actionButtonInformation: "actionButtonInformation",
4299
+ actionButtonForwardNext: "actionButtonForwardNext",
4300
+ actionButtonBackPrevious: "actionButtonBackPrevious",
4301
+ actionButtonEnd: "actionButtonEnd",
4302
+ actionButtonBeginning: "actionButtonBeginning",
4303
+ actionButtonReturn: "actionButtonReturn",
4304
+ actionButtonDocument: "actionButtonDocument",
4305
+ actionButtonSound: "actionButtonSound",
4306
+ actionButtonMovie: "actionButtonMovie",
4307
+ gear6: "gear6",
4308
+ gear9: "gear9",
4309
+ funnel: "funnel",
4310
+ cornerTabs: "cornerTabs",
4311
+ squareTabs: "squareTabs",
4312
+ plaqueTabs: "plaqueTabs",
4313
+ chartX: "chartX",
4314
+ chartStar: "chartStar",
4315
+ chartPlus: "chartPlus"
4316
+ };
4317
+ OOXML_DASH_TO_DOCX = {
4318
+ solid: "solid",
4319
+ dot: "dot",
4320
+ dash: "dash",
4321
+ lgDash: "longDash",
4322
+ dashDot: "dashDot",
4323
+ lgDashDot: "longDashDot",
4324
+ lgDashDotDot: "longDashDotDot",
4325
+ sysDash: "shortDash",
4326
+ sysDot: "shortDot",
4327
+ sysDashDot: "shortDashDot",
4328
+ sysDashDotDot: "shortDashDotDot"
4329
+ };
4330
+ }
4331
+ });
4332
+
4333
+ // src/renderers/docxjs/drawingGroup.ts
4334
+ function chooseMarkers(ir) {
4335
+ const strings = [];
4336
+ collectStrings(ir, strings, /* @__PURE__ */ new WeakSet());
4337
+ const unused = (base) => {
4338
+ for (let n = 0; ; n += 1) {
4339
+ const candidate = n === 0 ? base : `${base}-${n}`;
4340
+ if (!strings.some((value) => value.includes(candidate))) return candidate;
4341
+ }
4342
+ };
4343
+ return {
4344
+ noAltText: unused(DEFAULT_MARKERS.noAltText),
4345
+ textBox: unused(DEFAULT_MARKERS.textBox)
4346
+ };
4347
+ }
4348
+ function collectStrings(node, out, seen) {
4349
+ if (typeof node === "string") {
4350
+ out.push(node);
4351
+ return;
4352
+ }
4353
+ if (!node || typeof node !== "object") return;
4354
+ if (node instanceof ArrayBuffer || ArrayBuffer.isView(node)) return;
4355
+ if (seen.has(node)) return;
4356
+ seen.add(node);
4357
+ if (node instanceof Map) {
4358
+ for (const [key, value] of node) {
4359
+ collectStrings(key, out, seen);
4360
+ collectStrings(value, out, seen);
4361
+ }
4362
+ return;
4363
+ }
4364
+ if (node instanceof Set) {
4365
+ for (const value of node) collectStrings(value, out, seen);
4366
+ return;
4367
+ }
4368
+ for (const value of Object.values(node)) collectStrings(value, out, seen);
4369
+ }
4370
+ function createDrawingGroupFactory(shapes2, pictures, markers) {
4371
+ return (group, resources) => new shapes2.ShapeGroupRun(
4372
+ drawingGroupOptions(group, resources, pictures, markers)
4373
+ );
4374
+ }
4375
+ function drawingGroupOptions(group, resources, pictures, markers = DEFAULT_MARKERS) {
4376
+ const canvasWidth = group.canvasWidthEmu;
4377
+ const canvasHeight = group.canvasHeightEmu;
4378
+ const near = (value, edge) => Math.abs(value - edge) <= OVERFLOW_TOLERANCE_EMU;
4379
+ const bounds = unrotatedBounds(group.children);
4380
+ const fillsCanvas = bounds !== void 0 && near(bounds.left, 0) && near(bounds.top, 0) && near(bounds.right, canvasWidth) && near(bounds.bottom, canvasHeight);
4381
+ const union = {
4382
+ left: Math.min(0, bounds?.left ?? 0),
4383
+ top: Math.min(0, bounds?.top ?? 0),
4384
+ right: Math.max(canvasWidth, bounds?.right ?? 0),
4385
+ bottom: Math.max(canvasHeight, bounds?.bottom ?? 0)
4386
+ };
4387
+ const overflows = union.left < -OVERFLOW_TOLERANCE_EMU || union.top < -OVERFLOW_TOLERANCE_EMU || union.right > canvasWidth + OVERFLOW_TOLERANCE_EMU || union.bottom > canvasHeight + OVERFLOW_TOLERANCE_EMU;
4388
+ const widthEmu = overflows ? (union.right - union.left) * group.widthEmu / canvasWidth : group.widthEmu;
4389
+ const heightEmu = overflows ? (union.bottom - union.top) * group.heightEmu / canvasHeight : group.heightEmu;
4390
+ return {
4391
+ children: [
4392
+ ...fillsCanvas ? [] : [canvasPad(canvasWidth, canvasHeight)],
4393
+ ...group.children.map(
4394
+ (child, index) => groupChild(child, index, resources, pictures, markers)
4395
+ )
4396
+ ],
4397
+ transformation: {
4398
+ width: emuToPixels(widthEmu),
4399
+ height: emuToPixels(heightEmu)
4400
+ },
4401
+ // `||`, not `??`: docx treats '' as no description too, and would then
4402
+ // describe the diagram from the child names.
4403
+ altText: { name: "", description: group.altText || markers.noAltText },
4404
+ ...group.floating ? { floating: floatingOptions(group.floating) } : {}
4405
+ };
4406
+ }
4407
+ function groupChild(child, index, resources, pictures, markers) {
4408
+ return child.kind === "picture" ? pictureChild(child, index, pictures) : shapeChild(child, index, resources, markers);
4409
+ }
4410
+ function canvasPad(canvasWidthEmu, canvasHeightEmu) {
4411
+ return {
4412
+ type: "rectangle",
4413
+ transformation: {
4414
+ offset: { left: 0, top: 0 },
4415
+ width: emuToPixels(canvasWidthEmu),
4416
+ height: emuToPixels(canvasHeightEmu)
4417
+ },
4418
+ fill: "none",
4419
+ line: "none",
4420
+ decorative: true,
4421
+ altText: { name: "Canvas" }
4422
+ };
4423
+ }
4424
+ function childTransformation(frame) {
4425
+ const rotation2 = frame.rotationDegrees;
4426
+ return {
4427
+ offset: { left: emuToPixels(frame.xEmu), top: emuToPixels(frame.yEmu) },
4428
+ width: emuToPixels(frame.widthEmu),
4429
+ height: emuToPixels(frame.heightEmu),
4430
+ ...rotation2 !== void 0 && rotation2 % 360 !== 0 ? { rotation: rotation2 } : {},
4431
+ ...frame.flipHorizontal || frame.flipVertical ? {
4432
+ flip: {
4433
+ ...frame.flipHorizontal ? { horizontal: true } : {},
4434
+ ...frame.flipVertical ? { vertical: true } : {}
4435
+ }
4436
+ } : {}
4437
+ };
4438
+ }
4439
+ function shapeChild(shape, index, resources, markers) {
4440
+ const paragraphs = shape.text?.paragraphs ?? [];
4441
+ return {
4442
+ type: docxPresetShape(shape.geometry),
4443
+ transformation: childTransformation(shape.frame),
4444
+ fill: shapeFill(shape.fill),
4445
+ line: shapeLine(shape.outline),
4446
+ altText: {
4447
+ name: shape.name ?? `Shape ${index + 1}`,
4448
+ ...shape.isTextBox ? { title: markers.textBox } : {}
4449
+ },
4450
+ // Never `children: []`: docx would write an empty `w:txbxContent`, which
4451
+ // the schema does not allow.
4452
+ ...paragraphs.length > 0 ? {
4453
+ children: paragraphs.map(
4454
+ (paragraph2) => emitParagraph(paragraph2, resources)
4455
+ )
4456
+ } : {},
4457
+ ...shape.text ? { textOptions: textOptions(shape.text) } : {}
4458
+ };
4459
+ }
4460
+ function pictureChild(picture2, index, pictures) {
4461
+ const crop = picture2.crop;
4462
+ return {
4463
+ type: "picture",
4464
+ image: pictures(picture2.resourceId, {
4465
+ widthEmu: picture2.frame.widthEmu,
4466
+ heightEmu: picture2.frame.heightEmu
4467
+ }),
4468
+ transformation: childTransformation(picture2.frame),
4469
+ ...crop ? {
4470
+ crop: {
4471
+ ...crop.left !== void 0 ? { left: crop.left * 100 } : {},
4472
+ ...crop.top !== void 0 ? { top: crop.top * 100 } : {},
4473
+ ...crop.right !== void 0 ? { right: crop.right * 100 } : {},
4474
+ ...crop.bottom !== void 0 ? { bottom: crop.bottom * 100 } : {}
4475
+ }
4476
+ } : {},
4477
+ altText: {
4478
+ name: picture2.name ?? `Picture ${index + 1}`,
4479
+ ...picture2.altText ? { description: picture2.altText } : {}
4480
+ }
4481
+ };
4482
+ }
4483
+ function shapeFill(fill) {
4484
+ if (!fill || fill.kind === "none") return "none";
4485
+ return {
4486
+ type: "solid",
4487
+ color: fill.color.hex,
4488
+ ...fill.transparencyPercent !== void 0 ? { transparency: clamp(fill.transparencyPercent, 0, 100) } : {}
4489
+ };
4490
+ }
4491
+ function shapeLine(outline) {
4492
+ if (!outline || !outline.color) return "none";
4493
+ return {
4494
+ color: outline.color.hex,
4495
+ // An omitted width is office-open's omitted `w`, a hairline, where docx's
4496
+ // own default would be 1pt.
4497
+ width: outline.widthEmu !== void 0 ? clamp(outline.widthEmu / EMU_PER_POINT, 0, MAX_POINTS) : 0,
4498
+ ...outline.dash ? { dash: docxLineDash(outline.dash) } : {}
4499
+ };
4500
+ }
4501
+ function textOptions(text) {
4502
+ const insets = text.insetsEmu;
4503
+ const points = (emu) => clamp(emu / EMU_PER_POINT, 0, MAX_POINTS);
4504
+ const margins = insets ? {
4505
+ ...insets.top !== void 0 ? { top: points(insets.top) } : {},
4506
+ ...insets.right !== void 0 ? { right: points(insets.right) } : {},
4507
+ ...insets.bottom !== void 0 ? { bottom: points(insets.bottom) } : {},
4508
+ ...insets.left !== void 0 ? { left: points(insets.left) } : {}
4509
+ } : void 0;
4510
+ return {
4511
+ verticalAlignment: VERTICAL_ALIGNMENT[text.anchor ?? "top"],
4512
+ ...margins && Object.keys(margins).length > 0 ? { margins } : {}
4513
+ };
4514
+ }
4515
+ function unrotatedBounds(children) {
4516
+ if (children.length === 0) return void 0;
4517
+ let left = Infinity;
4518
+ let top = Infinity;
4519
+ let right = -Infinity;
4520
+ let bottom = -Infinity;
4521
+ for (const { frame } of children) {
4522
+ left = Math.min(left, frame.xEmu);
4523
+ top = Math.min(top, frame.yEmu);
4524
+ right = Math.max(right, frame.xEmu + frame.widthEmu);
4525
+ bottom = Math.max(bottom, frame.yEmu + frame.heightEmu);
4526
+ }
4527
+ return { left, top, right, bottom };
4528
+ }
4529
+ function clamp(value, low, high) {
4530
+ return Math.min(high, Math.max(low, value));
4531
+ }
4532
+ var DEFAULT_MARKERS, EMU_PER_POINT, MAX_POINTS, OVERFLOW_TOLERANCE_EMU, VERTICAL_ALIGNMENT;
4533
+ var init_drawingGroup = __esm({
4534
+ "src/renderers/docxjs/drawingGroup.ts"() {
4535
+ "use strict";
4536
+ init_units();
4537
+ init_emit();
4538
+ init_presetShapes();
4539
+ DEFAULT_MARKERS = {
4540
+ noAltText: "jto:no-alt-text",
4541
+ textBox: "jto:text-box"
4542
+ };
4543
+ EMU_PER_POINT = 12700;
4544
+ MAX_POINTS = 1584;
4545
+ OVERFLOW_TOLERANCE_EMU = 2;
4546
+ VERTICAL_ALIGNMENT = {
4547
+ top: "top",
4548
+ middle: "center",
4549
+ bottom: "bottom"
4550
+ };
4551
+ }
4552
+ });
4553
+
4554
+ // src/renderers/docxjs/drawingGroupRepair.ts
4555
+ import AdmZip3 from "adm-zip";
4556
+ function repairDrawingGroupsInBuffer(buffer, markers) {
4557
+ const textBox = new RegExp(
4558
+ `(<wps:cNvPr\\b[^>]*?) title="${escapeRegExp2(markers.textBox)}"(\\s*/>)<wps:cNvSpPr/>`,
4559
+ "g"
4560
+ );
4561
+ const noAltText = new RegExp(
4562
+ `(<wp:docPr\\b[^>]*?) descr="${escapeRegExp2(markers.noAltText)}"`,
4563
+ "g"
4564
+ );
4565
+ const zip = new AdmZip3(buffer);
4566
+ const parts = zip.getEntries().filter((entry) => DRAWING_PART.test(entry.entryName)).sort((a, b) => compareDrawingParts(a.entryName, b.entryName));
4567
+ const texts = new Map(
4568
+ parts.map((entry) => [entry, entry.getData().toString("utf8")])
4569
+ );
4570
+ let highest = 0;
4571
+ for (const xml of texts.values()) {
4572
+ for (const match of xml.matchAll(DOC_PR_ID)) {
4573
+ highest = Math.max(highest, Number(match[1]));
4574
+ }
4575
+ for (const match of xml.replace(GROUP, "").matchAll(CNVPR_ID)) {
4576
+ highest = Math.max(highest, Number(match[1]));
4577
+ }
4578
+ }
4579
+ let next = highest + 1;
4580
+ for (const [entry, xml] of texts) {
4581
+ if (!xml.includes("<wpg:wgp")) continue;
4582
+ const repaired = xml.replace(
4583
+ GROUP,
4584
+ (span) => span.replace(CHILD_ID, (_match, head) => `${head}id="${next++}"`).replace(
4585
+ ROTATION,
4586
+ (_match, head, value) => `${head}rot="${Math.round(Number(value))}"`
4587
+ ).replace(textBox, '$1$2<wps:cNvSpPr txBox="1"/>')
4588
+ ).replace(noAltText, "$1");
4589
+ texts.set(entry, repaired);
4590
+ zip.updateFile(entry, Buffer.from(repaired, "utf8"));
4591
+ }
4592
+ for (const [entry, xml] of texts) {
4593
+ if (xml.includes(markers.noAltText) || xml.includes(markers.textBox)) {
4594
+ throw new Error(
4595
+ `the drawing-group repair left a marker in ${entry.entryName}`
4596
+ );
4597
+ }
4598
+ }
4599
+ return zip.toBuffer();
4600
+ }
4601
+ var GROUP, DOC_PR_ID, CNVPR_ID, CHILD_ID, ROTATION, escapeRegExp2;
4602
+ var init_drawingGroupRepair = __esm({
4603
+ "src/renderers/docxjs/drawingGroupRepair.ts"() {
4604
+ "use strict";
4605
+ init_fixFloatingImageIds();
4606
+ GROUP = /<wpg:wgp\b[^>]*>[\s\S]*?<\/wpg:wgp>/g;
4607
+ DOC_PR_ID = /<wp:docPr\b[^>]*?\sid="(\d+)"/g;
4608
+ CNVPR_ID = /<(?:wps|pic|wpg):cNvPr\b[^>]*?\sid="(\d+)"/g;
4609
+ CHILD_ID = /(<(?:wps|pic|wpg):cNvPr\b[^>]*?\s)id="\d+"/g;
4610
+ ROTATION = /(<a:xfrm\b[^>]*?\s)rot="([^"]*)"/g;
4611
+ escapeRegExp2 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3774
4612
  }
3775
4613
  });
3776
4614
 
@@ -3799,18 +4637,32 @@ function createDocxJsRenderer() {
3799
4637
  format: "docx",
3800
4638
  capabilities: DOCXJS_CAPABILITIES,
3801
4639
  async render(ir, renderOptions) {
3802
- const resources = await prepareImages(
4640
+ await loadDocxCharts();
4641
+ const prepared = await prepareImages(
3803
4642
  ir,
3804
4643
  renderOptions?.svgRasterFallback
3805
4644
  );
4645
+ const markers = prepared.drawingGroups > 0 ? chooseMarkers(ir) : void 0;
4646
+ let resources = prepared.images;
4647
+ if (markers) {
4648
+ await shapes.load();
4649
+ resources = Object.assign(prepared.images, {
4650
+ drawingGroup: createDrawingGroupFactory(
4651
+ shapes.get(),
4652
+ prepared.pictureSource,
4653
+ markers
4654
+ )
4655
+ });
4656
+ }
3806
4657
  const document = buildDocument(ir, resources);
3807
4658
  const packed = await Packer.toBuffer(document);
3808
4659
  const fixed = fixFloatingImageIdsInBuffer(packed);
4660
+ const repaired = markers ? repairDrawingGroupsInBuffer(fixed, markers) : fixed;
3809
4661
  if (renderOptions?.deterministic === false)
3810
- return new Uint8Array(normalizeDocxCaseBuffer(fixed));
4662
+ return new Uint8Array(normalizeDocxCaseBuffer(repaired));
3811
4663
  return new Uint8Array(
3812
4664
  canonicalizeDocxBuffer(
3813
- fixed,
4665
+ repaired,
3814
4666
  resolveGenerationDate({
3815
4667
  deterministic: renderOptions?.deterministic,
3816
4668
  generatedAt: renderOptions?.generatedAt
@@ -3823,6 +4675,7 @@ function createDocxJsRenderer() {
3823
4675
  function buildDocument(ir, resources = /* @__PURE__ */ new Map()) {
3824
4676
  return new Document({
3825
4677
  styles: emitStyles(ir.styles),
4678
+ ...ir.theme ? { theme: themeOptions(ir.theme) } : {},
3826
4679
  sections: ir.sections.map(
3827
4680
  (section2, index) => sectionOptions(section2, resources, index === ir.sections.length - 1)
3828
4681
  ),
@@ -3854,6 +4707,21 @@ function buildDocument(ir, resources = /* @__PURE__ */ new Map()) {
3854
4707
  } : {}
3855
4708
  });
3856
4709
  }
4710
+ function themeOptions(theme) {
4711
+ const colors = {};
4712
+ for (const [slot, color] of Object.entries(theme.colors)) {
4713
+ if (color) colors[slot] = color.hex;
4714
+ }
4715
+ const fonts = {
4716
+ ...theme.headingFont ? { headings: theme.headingFont } : {},
4717
+ ...theme.bodyFont ? { body: theme.bodyFont } : {}
4718
+ };
4719
+ return {
4720
+ name: theme.name,
4721
+ ...Object.keys(colors).length > 0 ? { colors } : {},
4722
+ ...Object.keys(fonts).length > 0 ? { fonts } : {}
4723
+ };
4724
+ }
3857
4725
  function noteBodies(notes, resources) {
3858
4726
  const bodies = {};
3859
4727
  for (const note of notes) {
@@ -3891,7 +4759,7 @@ function numberingConfig(numbering) {
3891
4759
  };
3892
4760
  }
3893
4761
  async function prepareImages(ir, svgRasterFallback) {
3894
- const placements = collectImagePlacements(ir);
4762
+ const { placements, drawingGroups } = collectImagePlacements(ir);
3895
4763
  const jobs = [];
3896
4764
  for (const resource of ir.resources) {
3897
4765
  if (resource.kind !== "image" || resource.mediaType !== "svg") continue;
@@ -3939,16 +4807,55 @@ async function prepareImages(ir, svgRasterFallback) {
3939
4807
  });
3940
4808
  });
3941
4809
  }
3942
- return resources;
4810
+ const pictureSource = (resourceId, placement) => {
4811
+ const resource = ir.resources.find(
4812
+ (candidate) => candidate.kind === "image" && candidate.id === resourceId
4813
+ );
4814
+ if (!resource) {
4815
+ throw new Error(`no image was prepared for resource "${resourceId}"`);
4816
+ }
4817
+ const data = Buffer.from(resource.bytes);
4818
+ if (resource.mediaType !== "svg") {
4819
+ return {
4820
+ type: resource.mediaType,
4821
+ data
4822
+ };
4823
+ }
4824
+ const size = `${emuToPixels(placement.widthEmu)}x${emuToPixels(placement.heightEmu)}`;
4825
+ return {
4826
+ type: "svg",
4827
+ data,
4828
+ fallback: {
4829
+ type: "png",
4830
+ data: rasters.get(`${resourceId}:${size}`) ?? data
4831
+ }
4832
+ };
4833
+ };
4834
+ return { images: resources, pictureSource, drawingGroups };
3943
4835
  }
3944
4836
  function collectImagePlacements(ir) {
3945
4837
  const placements = /* @__PURE__ */ new Map();
4838
+ let drawingGroups = 0;
4839
+ const record = (resourceId, widthEmu, heightEmu) => {
4840
+ const key = `${emuToPixels(widthEmu)}x${emuToPixels(heightEmu)}`;
4841
+ const sizes = placements.get(resourceId) ?? /* @__PURE__ */ new Set();
4842
+ sizes.add(key);
4843
+ placements.set(resourceId, sizes);
4844
+ };
3946
4845
  const visitInline = (inline) => {
3947
4846
  if (inline.kind === "image") {
3948
- const key = `${emuToPixels(inline.widthEmu)}x${emuToPixels(inline.heightEmu)}`;
3949
- const sizes = placements.get(inline.resourceId) ?? /* @__PURE__ */ new Set();
3950
- sizes.add(key);
3951
- placements.set(inline.resourceId, sizes);
4847
+ record(inline.resourceId, inline.widthEmu, inline.heightEmu);
4848
+ return;
4849
+ }
4850
+ if (inline.kind === "drawingGroup") {
4851
+ drawingGroups += 1;
4852
+ for (const child of inline.children) {
4853
+ if (child.kind === "picture") {
4854
+ record(child.resourceId, child.frame.widthEmu, child.frame.heightEmu);
4855
+ } else {
4856
+ child.text?.paragraphs.forEach(visitBlock);
4857
+ }
4858
+ }
3952
4859
  return;
3953
4860
  }
3954
4861
  if (inline.kind === "hyperlink" || inline.kind === "revision") {
@@ -3979,7 +4886,7 @@ function collectImagePlacements(ir) {
3979
4886
  for (const note of [...ir.footnotes, ...ir.endnotes]) {
3980
4887
  note.children.forEach(visitBlock);
3981
4888
  }
3982
- return placements;
4889
+ return { placements, drawingGroups };
3983
4890
  }
3984
4891
  function sectionOptions(section2, resources, closesDocument = false) {
3985
4892
  const { page, columns } = section2.properties;
@@ -4092,17 +4999,26 @@ function coreProperties(ir) {
4092
4999
  ...metadata.custom?.length ? { customProperties: metadata.custom } : {}
4093
5000
  };
4094
5001
  }
4095
- var DOCXJS_RENDERER_ID, DOCXJS_CAPABILITIES;
5002
+ var DOCXJS_RENDERER_ID, shapes, DOCXJS_CAPABILITIES;
4096
5003
  var init_docxjs = __esm({
4097
5004
  "src/renderers/docxjs/index.ts"() {
4098
5005
  "use strict";
4099
5006
  init_styles2();
5007
+ init_charts();
4100
5008
  init_imageUtils();
4101
5009
  init_packageDocument();
4102
5010
  init_fixFloatingImageIds();
4103
5011
  init_emit();
5012
+ init_docxSubpath();
5013
+ init_drawingGroup();
5014
+ init_drawingGroupRepair();
4104
5015
  init_units();
4105
5016
  DOCXJS_RENDERER_ID = "docxjs";
5017
+ shapes = docxSubpath(
5018
+ "docx/shapes",
5019
+ "visual",
5020
+ () => import("docx/shapes")
5021
+ );
4106
5022
  DOCXJS_CAPABILITIES = /* @__PURE__ */ new Set([
4107
5023
  "paragraphs",
4108
5024
  "styles",
@@ -4115,8 +5031,10 @@ var init_docxjs = __esm({
4115
5031
  "images",
4116
5032
  "floating-images",
4117
5033
  "svg-images",
5034
+ "charts",
4118
5035
  "text-frames",
4119
5036
  "text-boxes",
5037
+ "drawing-groups",
4120
5038
  "toc",
4121
5039
  "cached-toc",
4122
5040
  "fields",
@@ -4137,183 +5055,331 @@ var init_docxjs = __esm({
4137
5055
  }
4138
5056
  });
4139
5057
 
4140
- // src/utils/chartWorkbook.ts
4141
- import AdmZip3 from "adm-zip";
4142
- import { chartWorkbookParts } from "@json-to-office/shared/rendering";
4143
- import {
4144
- CHART_WORKBOOK_SHEET_NAME,
4145
- categoryReference,
4146
- columnLetter,
4147
- seriesNameReference,
4148
- seriesValueReference
4149
- } from "@json-to-office/shared/rendering";
4150
- function buildChartWorkbook(series) {
4151
- const zip = new AdmZip3();
4152
- for (const [name, content] of chartWorkbookParts(series)) {
4153
- zip.addFile(name, Buffer.from(content, "utf8"));
5058
+ // src/renderers/office-open/chartLook.ts
5059
+ function seriesLook(chartType) {
5060
+ switch (chartType) {
5061
+ case "line":
5062
+ case "scatter":
5063
+ return { marker: MARKER, smooth: false };
5064
+ case "radar":
5065
+ return { marker: MARKER };
5066
+ case "column":
5067
+ case "bar":
5068
+ return { invertIfNegative: false };
5069
+ default:
5070
+ return {};
4154
5071
  }
4155
- for (const entry of zip.getEntries()) {
4156
- entry.header.timeval = WORKBOOK_TIMESTAMP;
5072
+ }
5073
+ function axesLook(chartType) {
5074
+ switch (chartType) {
5075
+ case "column":
5076
+ case "bar":
5077
+ case "line":
5078
+ case "area": {
5079
+ const horizontal = chartType === "bar";
5080
+ return {
5081
+ categoryAxis: {
5082
+ position: horizontal ? "l" : "b",
5083
+ ...NO_TICKS,
5084
+ line: AXIS_LINE
5085
+ },
5086
+ valueAxis: {
5087
+ position: horizontal ? "b" : "l",
5088
+ gridLine: GRIDLINES,
5089
+ ...NO_TICKS,
5090
+ lineVisible: false,
5091
+ crossBetween: chartType === "area" ? "midCat" : "between"
5092
+ }
5093
+ };
5094
+ }
5095
+ case "radar":
5096
+ return {
5097
+ categoryAxis: {
5098
+ position: "b",
5099
+ gridLine: GRIDLINES,
5100
+ ...NO_TICKS,
5101
+ majorTickMark: "cross",
5102
+ line: AXIS_LINE
5103
+ },
5104
+ valueAxis: {
5105
+ position: "l",
5106
+ gridLine: GRIDLINES,
5107
+ ...NO_TICKS,
5108
+ majorTickMark: "cross",
5109
+ line: AXIS_LINE,
5110
+ crossBetween: "between"
5111
+ }
5112
+ };
5113
+ case "scatter": {
5114
+ const line = { ...AXIS_LINE, color: SCATTER_AXIS_TINT };
5115
+ return {
5116
+ categoryAxis: {
5117
+ position: "b",
5118
+ ...NO_TICKS,
5119
+ line,
5120
+ crossBetween: "midCat"
5121
+ },
5122
+ valueAxis: {
5123
+ position: "l",
5124
+ gridLine: GRIDLINES,
5125
+ ...NO_TICKS,
5126
+ line,
5127
+ crossBetween: "midCat"
5128
+ }
5129
+ };
5130
+ }
5131
+ default:
5132
+ return {};
5133
+ }
5134
+ }
5135
+ function plotLook(chart) {
5136
+ const common = {
5137
+ ...axesLook(chart.chartType),
5138
+ autoTitleDeleted: !(chart.title && chart.showTitle !== false),
5139
+ plotAreaUnfilled: true
5140
+ };
5141
+ switch (chart.chartType) {
5142
+ case "column":
5143
+ return { ...common, dataBorder: "none", gapWidth: 219, overlap: -27 };
5144
+ case "bar":
5145
+ return { ...common, dataBorder: "none", gapWidth: 182, overlap: 0 };
5146
+ case "area":
5147
+ return { ...common, dataBorder: "none" };
5148
+ case "line":
5149
+ return { ...common, ...LINE_SERIES, lineMarkers: true };
5150
+ case "radar":
5151
+ return { ...common, ...LINE_SERIES, radarStyle: "marker" };
5152
+ case "scatter": {
5153
+ const labels = chart.series[0]?.labels ?? [];
5154
+ return {
5155
+ ...common,
5156
+ ...LINE_SERIES,
5157
+ lineWidthPoints: 1.5,
5158
+ scatterStyle: "lineMarker",
5159
+ scatterXValues: labels.map((_, index) => scatterX(labels, index))
5160
+ };
5161
+ }
5162
+ case "pie":
5163
+ case "doughnut":
5164
+ return {
5165
+ ...common,
5166
+ dataBorder: { widthPoints: 1.5, color: { scheme: "lt1" } },
5167
+ firstSliceAngle: 0,
5168
+ ...chart.chartType === "doughnut" ? { holeSize: 50 } : {}
5169
+ };
5170
+ default:
5171
+ return common;
4157
5172
  }
4158
- return new Uint8Array(zip.toBuffer());
4159
5173
  }
4160
- var WORKBOOK_TIMESTAMP;
4161
- var init_chartWorkbook = __esm({
4162
- "src/utils/chartWorkbook.ts"() {
5174
+ var TEXT_1_TINT, SCATTER_AXIS_TINT, HAIRLINE_POINTS, GRIDLINES, AXIS_LINE, NO_TICKS, LINE_SERIES, MARKER;
5175
+ var init_chartLook = __esm({
5176
+ "src/renderers/office-open/chartLook.ts"() {
4163
5177
  "use strict";
4164
- init_packageDocument();
4165
- WORKBOOK_TIMESTAMP = toDosTime(DEFAULT_GENERATION_DATE);
5178
+ init_chartValues();
5179
+ TEXT_1_TINT = { scheme: "tx1", lumMod: 15e3, lumOff: 85e3 };
5180
+ SCATTER_AXIS_TINT = {
5181
+ scheme: "tx1",
5182
+ lumMod: 25e3,
5183
+ lumOff: 75e3
5184
+ };
5185
+ HAIRLINE_POINTS = 0.75;
5186
+ GRIDLINES = {
5187
+ size: HAIRLINE_POINTS,
5188
+ color: TEXT_1_TINT
5189
+ };
5190
+ AXIS_LINE = {
5191
+ widthPoints: HAIRLINE_POINTS,
5192
+ color: TEXT_1_TINT
5193
+ };
5194
+ NO_TICKS = {
5195
+ majorTickMark: "none",
5196
+ minorTickMark: "none",
5197
+ tickLabelPosition: "nextTo"
5198
+ };
5199
+ LINE_SERIES = {
5200
+ lineWidthPoints: 2.25,
5201
+ lineCap: "rnd",
5202
+ lineJoin: "round",
5203
+ markerLineWidthPoints: HAIRLINE_POINTS
5204
+ };
5205
+ MARKER = { symbol: "circle", size: 5 };
4166
5206
  }
4167
5207
  });
4168
5208
 
4169
5209
  // src/renderers/office-open/chartParts.ts
4170
5210
  import {
4171
- CHART_WORKBOOK_CONTENT_TYPE,
4172
- chartWorkbookRelsXml,
4173
- matchChartParts,
4174
- spliceChartXml
5211
+ finishChartXml,
5212
+ matchChartParts
4175
5213
  } from "@json-to-office/shared/rendering";
4176
- function spliceInput(chart) {
4177
- const { bold, ...rest } = chart.textFont;
4178
- const textFont = rest;
4179
- const axisTitleFont = { ...rest, bold };
4180
- const chartTitleFont = {
4181
- ...rest,
4182
- fontSize: Math.round(rest.fontSize * CHART_TITLE_SCALE),
4183
- bold: true
5214
+ function chartInput(chart) {
5215
+ const roles = chartTextRoles(chart.textFont);
5216
+ const textFont = roles.text;
5217
+ const axisTitleFont = roles.axisTitle;
5218
+ const chartTitleFont = roles.title;
5219
+ const look = plotLook(chart);
5220
+ const categoryAxis = {
5221
+ ...look.categoryAxis,
5222
+ ...chart.categoryAxisTitle ? { title: chart.categoryAxisTitle, titleFont: axisTitleFont } : {}
5223
+ };
5224
+ const valueAxis = {
5225
+ ...look.valueAxis,
5226
+ ...chart.valueAxisTitle ? { title: chart.valueAxisTitle, titleFont: axisTitleFont } : {}
4184
5227
  };
4185
5228
  return {
5229
+ ...look,
4186
5230
  chartType: chart.chartType,
4187
5231
  series: chart.series,
4188
5232
  colors: chart.colors,
5233
+ ...chart.title && chart.showTitle !== false ? { title: chart.title } : {},
4189
5234
  textFont,
4190
5235
  titleFont: chartTitleFont,
4191
5236
  ...chart.legendPosition ? { legendPosition: chart.legendPosition } : {},
4192
- ...chart.categoryAxisTitle ? {
4193
- categoryAxis: {
4194
- title: chart.categoryAxisTitle,
4195
- titleFont: axisTitleFont
4196
- }
4197
- } : {},
4198
- ...chart.valueAxisTitle ? {
4199
- valueAxis: { title: chart.valueAxisTitle, titleFont: axisTitleFont }
4200
- } : {}
5237
+ ...Object.keys(categoryAxis).length > 0 ? { categoryAxis } : {},
5238
+ ...Object.keys(valueAxis).length > 0 ? { valueAxis } : {}
4201
5239
  };
4202
5240
  }
4203
- function declareWorkbookContentType(zip) {
4204
- const entry = zip.getEntry("[Content_Types].xml");
4205
- if (!entry) return;
4206
- const xml = entry.getData().toString("utf8");
4207
- if (xml.includes(`Extension="xlsx"`)) return;
4208
- zip.updateFile(
4209
- entry,
4210
- Buffer.from(
4211
- xml.replace(
4212
- '<Default Extension="xml"',
4213
- `<Default Extension="xlsx" ContentType="${CHART_WORKBOOK_CONTENT_TYPE}"/><Default Extension="xml"`
4214
- ),
4215
- "utf8"
4216
- )
4217
- );
4218
- }
4219
- function spliceChartParts(zip, charts) {
4220
- if (charts.length === 0) return;
5241
+ function finishChartParts(zip, charts2) {
5242
+ if (charts2.length === 0) return;
4221
5243
  const parts = zip.getEntries().map((entry) => entry.entryName).map((name) => name.match(/^word\/charts\/chart(\d+)\.xml$/)).filter((match) => match !== null).map(
4222
5244
  (match) => [
4223
5245
  Number(match[1]),
4224
5246
  zip.getEntry(match[0]).getData().toString("utf8")
4225
5247
  ]
4226
5248
  );
4227
- for (const { ordinal, xml, chart } of matchChartParts(parts, charts)) {
4228
- const workbookName = `chart${ordinal}.xlsx`;
5249
+ const inputs = charts2.map(chartInput);
5250
+ for (const { ordinal, xml, chart } of matchChartParts(parts, inputs)) {
5251
+ const finished = finishChartXml(xml, chart);
5252
+ if (finished === xml) continue;
4229
5253
  zip.updateFile(
4230
5254
  zip.getEntry(`word/charts/chart${ordinal}.xml`),
4231
- Buffer.from(spliceChartXml(xml, spliceInput(chart)), "utf8")
4232
- );
4233
- zip.addFile(
4234
- `word/embeddings/${workbookName}`,
4235
- Buffer.from(buildChartWorkbook(chart.series))
4236
- );
4237
- zip.addFile(
4238
- `word/charts/_rels/chart${ordinal}.xml.rels`,
4239
- Buffer.from(chartWorkbookRelsXml(workbookName), "utf8")
5255
+ Buffer.from(finished, "utf8")
4240
5256
  );
4241
5257
  }
4242
- declareWorkbookContentType(zip);
4243
5258
  }
4244
- var CHART_TITLE_SCALE;
4245
5259
  var init_chartParts = __esm({
4246
5260
  "src/renderers/office-open/chartParts.ts"() {
4247
5261
  "use strict";
4248
- init_chartWorkbook();
4249
- CHART_TITLE_SCALE = 1.4;
5262
+ init_chartText();
5263
+ init_chartLook();
4250
5264
  }
4251
5265
  });
4252
5266
 
4253
- // src/renderers/office-open/cellTocs.ts
4254
- function spliceCellTocs(zip, tocs, stringifyTableOfContents) {
4255
- if (tocs.length === 0) return;
4256
- const spliced = /* @__PURE__ */ new Set();
4257
- for (const entry of zip.getEntries()) {
4258
- if (!TABLE_PARTS.test(entry.entryName)) continue;
4259
- const original = entry.getData().toString("utf8");
4260
- let xml = original;
4261
- for (const toc of tocs) {
4262
- const start = markerParagraph(xml, toc.start);
4263
- if (!start) continue;
4264
- const end = markerParagraph(xml, toc.end);
4265
- if (spliced.has(toc) || !end || end.from < start.to) {
4266
- throw new Error(
4267
- `the office-open renderer lost track of a table of contents in a table cell (${entry.entryName})`
4268
- );
4269
- }
4270
- xml = xml.slice(0, start.from) + stringifyTableOfContents(
4271
- toc.alias,
4272
- toc.options,
4273
- xml.slice(start.to, end.from)
4274
- ) + xml.slice(end.to);
4275
- spliced.add(toc);
4276
- }
4277
- if (xml !== original) zip.updateFile(entry, Buffer.from(xml, "utf8"));
4278
- }
4279
- if (spliced.size !== tocs.length) {
5267
+ // src/renderers/office-open/themePart.ts
5268
+ function replaceOnce(xml, pattern, replacer, what) {
5269
+ const count = [...xml.matchAll(pattern)].length;
5270
+ if (count !== 1) {
4280
5271
  throw new Error(
4281
- `the office-open renderer placed ${tocs.length} table(s) of contents in table cells and found ${spliced.size}`
5272
+ `office-open's theme part has ${count} ${what}, expected one`
4282
5273
  );
4283
5274
  }
5275
+ return xml.replace(pattern, replacer);
5276
+ }
5277
+ function spliceTheme(zip, theme) {
5278
+ const entry = zip.getEntry(THEME_PART);
5279
+ if (!entry) throw new Error(`office-open wrote no ${THEME_PART}`);
5280
+ let xml = entry.getData().toString("utf8");
5281
+ const name = esc(theme.name);
5282
+ xml = renamed(xml, "theme", name);
5283
+ const colors = Object.entries(theme.colors);
5284
+ if (colors.some(([, color]) => color)) {
5285
+ xml = renamed(xml, "clrScheme", name);
5286
+ for (const [slot, color] of colors) {
5287
+ if (!color) continue;
5288
+ const element = SCHEME_ELEMENT[slot];
5289
+ xml = replaceOnce(
5290
+ xml,
5291
+ new RegExp(`<a:${element}>[\\s\\S]*?</a:${element}>`, "g"),
5292
+ () => `<a:${element}><a:srgbClr val="${color.hex}"/></a:${element}>`,
5293
+ `a:${element}`
5294
+ );
5295
+ }
5296
+ }
5297
+ if (theme.headingFont || theme.bodyFont) {
5298
+ xml = renamed(xml, "fontScheme", name);
5299
+ for (const [collection, key, officeFace] of OFFICE_FACES) {
5300
+ const family = theme[key];
5301
+ if (family === void 0 || family === officeFace) continue;
5302
+ xml = replaceOnce(
5303
+ xml,
5304
+ new RegExp(`(<a:${collection}>)<a:latin\\b[^>]*/>`, "g"),
5305
+ (_match, open) => `${open}<a:latin typeface="${esc(family)}"/>`,
5306
+ `a:${collection} a:latin`
5307
+ );
5308
+ }
5309
+ }
5310
+ zip.updateFile(THEME_PART, Buffer.from(xml, "utf8"));
4284
5311
  }
4285
- function markerParagraph(xml, marker) {
4286
- const at = xml.indexOf(marker);
4287
- if (at < 0) return void 0;
4288
- const from = Math.max(
4289
- xml.lastIndexOf("<w:p>", at),
4290
- xml.lastIndexOf("<w:p ", at)
4291
- );
4292
- const close = xml.indexOf("</w:p>", at);
4293
- if (xml.indexOf(marker, at + marker.length) >= 0 || from < 0 || close < 0 || xml.lastIndexOf("</w:p>", at) > from) {
4294
- throw new Error(
4295
- "the office-open renderer found a table-of-contents marker it did not write"
5312
+ var THEME_PART, SCHEME_ELEMENT, OFFICE_FACES, esc, renamed;
5313
+ var init_themePart = __esm({
5314
+ "src/renderers/office-open/themePart.ts"() {
5315
+ "use strict";
5316
+ THEME_PART = "word/theme/theme1.xml";
5317
+ SCHEME_ELEMENT = {
5318
+ dark1: "dk1",
5319
+ light1: "lt1",
5320
+ dark2: "dk2",
5321
+ light2: "lt2",
5322
+ accent1: "accent1",
5323
+ accent2: "accent2",
5324
+ accent3: "accent3",
5325
+ accent4: "accent4",
5326
+ accent5: "accent5",
5327
+ accent6: "accent6",
5328
+ hyperlink: "hlink",
5329
+ followedHyperlink: "folHlink"
5330
+ };
5331
+ OFFICE_FACES = [
5332
+ ["majorFont", "headingFont", "Calibri Light"],
5333
+ ["minorFont", "bodyFont", "Calibri"]
5334
+ ];
5335
+ esc = (value) => value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5336
+ renamed = (xml, element, name) => replaceOnce(
5337
+ xml,
5338
+ new RegExp(`(<a:${element}\\b[^>]*?\\bname=")[^"]*(")`, "g"),
5339
+ (_match, open, close) => `${open}${name}${close}`,
5340
+ `a:${element} name`
4296
5341
  );
4297
5342
  }
4298
- return { from, to: close + "</w:p>".length };
5343
+ });
5344
+
5345
+ // src/utils/chartWorkbook.ts
5346
+ import AdmZip4 from "adm-zip";
5347
+ import { chartWorkbookParts } from "@json-to-office/shared/rendering";
5348
+ import {
5349
+ CHART_WORKBOOK_SHEET_NAME,
5350
+ categoryReference,
5351
+ columnLetter,
5352
+ seriesNameReference,
5353
+ seriesValueReference
5354
+ } from "@json-to-office/shared/rendering";
5355
+ function buildChartWorkbook(series, options = {}) {
5356
+ const zip = new AdmZip4();
5357
+ for (const [name, content] of chartWorkbookParts(series, options)) {
5358
+ zip.addFile(name, Buffer.from(content, "utf8"));
5359
+ }
5360
+ for (const entry of zip.getEntries()) {
5361
+ entry.header.timeval = WORKBOOK_TIMESTAMP;
5362
+ }
5363
+ return new Uint8Array(zip.toBuffer());
4299
5364
  }
4300
- var TABLE_PARTS;
4301
- var init_cellTocs = __esm({
4302
- "src/renderers/office-open/cellTocs.ts"() {
5365
+ var WORKBOOK_TIMESTAMP;
5366
+ var init_chartWorkbook = __esm({
5367
+ "src/utils/chartWorkbook.ts"() {
4303
5368
  "use strict";
4304
- TABLE_PARTS = /^word\/(?:document|header\d+|footer\d+)\.xml$/;
5369
+ init_packageDocument();
5370
+ WORKBOOK_TIMESTAMP = toDosTime(DEFAULT_GENERATION_DATE);
4305
5371
  }
4306
5372
  });
4307
5373
 
4308
5374
  // src/renderers/office-open/emit.ts
4309
- import { assertNever as assertNever2 } from "@json-to-office/shared/rendering";
5375
+ import { assertNever as assertNever3 } from "@json-to-office/shared/rendering";
5376
+ import { chartLook, withSeriesLook } from "@json-to-office/shared/rendering";
4310
5377
  function emptyContext() {
4311
5378
  let next = 1;
4312
5379
  return {
4313
5380
  pictures: /* @__PURE__ */ new Map(),
4314
5381
  nextDrawingId: () => next++,
4315
- charts: [],
4316
- cellTocs: []
5382
+ charts: []
4317
5383
  };
4318
5384
  }
4319
5385
  function simpleField(instruction, cachedText) {
@@ -4354,18 +5420,19 @@ function runProperties2(formatting) {
4354
5420
  if (formatting.doubleStrike !== void 0) {
4355
5421
  out.doubleStrike = formatting.doubleStrike;
4356
5422
  }
4357
- if (formatting.superScript) out.superScript = true;
4358
- if (formatting.subScript) out.subScript = true;
5423
+ if (formatting.superScript) out.verticalAlign = "superscript";
5424
+ if (formatting.subScript) out.verticalAlign = "subscript";
4359
5425
  if (formatting.smallCaps !== void 0) out.smallCaps = formatting.smallCaps;
4360
5426
  if (formatting.allCaps !== void 0) out.allCaps = formatting.allCaps;
4361
- if (formatting.highlight !== void 0) out.highlight = formatting.highlight;
5427
+ if (formatting.highlight !== void 0) {
5428
+ out.highlight = formatting.highlight;
5429
+ }
4362
5430
  if (formatting.shading) out.shading = shading(formatting.shading);
4363
5431
  if (formatting.scalePercent !== void 0)
4364
5432
  out.scale = formatting.scalePercent;
4365
- if (formatting.characterSpacingTwentieths !== void 0) {
4366
- const stated = formatting.characterSpacingTwentieths;
4367
- const twentieths = twips(stated);
4368
- out.characterSpacing = twentieths === 0 && stated !== 0 ? "0pt" : twentieths;
5433
+ const stated = formatting.characterSpacingTwentieths;
5434
+ if (stated !== void 0 && stated !== 0) {
5435
+ out.characterSpacing = twips(stated);
4369
5436
  }
4370
5437
  if (formatting.language) out.language = { value: formatting.language };
4371
5438
  if (formatting.noProof !== void 0) out.noProof = formatting.noProof;
@@ -4395,7 +5462,9 @@ function inlineChildren2(children, ctx = emptyContext()) {
4395
5462
  switch (child.kind) {
4396
5463
  case "lineBreak":
4397
5464
  pendingBreaks += 1;
4398
- if (child.clear && child.clear !== "none") pendingClear = child.clear;
5465
+ if (child.clear && child.clear !== "none") {
5466
+ pendingClear = child.clear;
5467
+ }
4399
5468
  break;
4400
5469
  case "text":
4401
5470
  out.push({
@@ -4433,7 +5502,7 @@ function inlineChildren2(children, ctx = emptyContext()) {
4433
5502
  case "drawingGroup": {
4434
5503
  const pending = breakOption();
4435
5504
  if (pending.break) out.push(pending);
4436
- out.push({ wpgGroup: drawingGroupOptions(child, ctx) });
5505
+ out.push({ wpgGroup: drawingGroupOptions2(child, ctx) });
4437
5506
  break;
4438
5507
  }
4439
5508
  case "hyperlink":
@@ -4463,8 +5532,9 @@ function inlineChildren2(children, ctx = emptyContext()) {
4463
5532
  out.push({ wpsShape: shapeOptions(child, ctx) });
4464
5533
  break;
4465
5534
  case "chart":
4466
- ctx.charts?.push(child);
4467
- out.push({ chart: chartOptions(child, ctx) });
5535
+ out.push({
5536
+ chart: chartOptions(child, ctx, ctx.charts?.push(child) ?? 1)
5537
+ });
4468
5538
  break;
4469
5539
  case "noteReference":
4470
5540
  out.push(
@@ -4472,7 +5542,7 @@ function inlineChildren2(children, ctx = emptyContext()) {
4472
5542
  );
4473
5543
  break;
4474
5544
  default:
4475
- assertNever2(child, "DocxIrInline");
5545
+ assertNever3(child, "DocxIrInline");
4476
5546
  }
4477
5547
  }
4478
5548
  return out;
@@ -4513,8 +5583,11 @@ function floatingOptions2(floating) {
4513
5583
  ...floating.vertical ? { verticalPosition: position(floating.vertical) } : {},
4514
5584
  ...floating.wrap ? {
4515
5585
  wrap: {
4516
- type: WRAP_TYPE2[floating.wrap.type],
4517
- ...floating.wrap.side ? { side: floating.wrap.side } : {}
5586
+ // Named, as OOXML names them; 0.11 took docx.js's numbers.
5587
+ type: floating.wrap.type,
5588
+ ...floating.wrap.side ? {
5589
+ side: floating.wrap.side
5590
+ } : {}
4518
5591
  }
4519
5592
  } : {},
4520
5593
  ...floating.margins ? {
@@ -4541,66 +5614,92 @@ function imageMedia(ctx, resourceId, placement) {
4541
5614
  }
4542
5615
  function pictureOptions(image, ctx) {
4543
5616
  const media = imageMedia(ctx, image.resourceId, image);
4544
- return {
4545
- type: media.type,
5617
+ const source = media.type === "svg" ? {
5618
+ type: "svg",
4546
5619
  data: media.data,
4547
- // No `fileName`: at run level the backend allocates one, and stating our
4548
- // own would only fight it.
4549
- ...media.fallback ? { fallback: media.fallback } : {},
5620
+ // The SVG slot always carries a raster: `prepareImages` falls back
5621
+ // to the vector bytes when none could be made.
5622
+ fallback: media.fallback ?? {
5623
+ type: "png",
5624
+ data: media.data
5625
+ }
5626
+ } : { type: media.type, data: media.data };
5627
+ return {
5628
+ ...source,
4550
5629
  // Raw numbers are EMUs in @office-open/docx. Passing pixels here makes a
4551
5630
  // normal image only a few hundred EMUs wide, effectively a dot.
4552
5631
  transformation: { width: image.widthEmu, height: image.heightEmu },
4553
5632
  // The id is stated rather than left to the backend's process-global
4554
- // counter. `name` stays empty, which is what it was before and what the
4555
- // backend falls back to.
4556
- altText: { id: String(ctx.nextDrawingId()) },
5633
+ // counter. `name` stays empty, which is what the backend falls back to.
5634
+ altText: { id: String(ctx.nextDrawingId()), name: "" },
4557
5635
  ...image.floating ? { floating: floatingOptions2(image.floating) } : {}
4558
5636
  // No `description` or `title`: no DOCX this pipeline has produced carries
4559
5637
  // `wp:docPr` alt text, and the compiler warns so the gap is visible rather
4560
5638
  // than silent.
4561
5639
  };
4562
5640
  }
4563
- function drawingGroupOptions(group, ctx) {
5641
+ function drawingGroupOptions2(group, ctx) {
4564
5642
  const id = ctx.nextDrawingId();
4565
5643
  return {
4566
5644
  altText: {
4567
5645
  id: String(id),
5646
+ name: "",
4568
5647
  ...group.altText ? { description: group.altText } : {}
4569
5648
  },
4570
5649
  transformation: { width: group.widthEmu, height: group.heightEmu },
4571
- childOffset: { x: 0, y: 0 },
4572
- childExtent: { cx: group.canvasWidthEmu, cy: group.canvasHeightEmu },
4573
- children: group.children.map((child) => groupChild(child, ctx)),
5650
+ childOffsetX: 0,
5651
+ childOffsetY: 0,
5652
+ childExtentWidth: group.canvasWidthEmu,
5653
+ childExtentHeight: group.canvasHeightEmu,
5654
+ children: group.children.map((child) => groupChild2(child, ctx)),
4574
5655
  ...group.floating ? { floating: floatingOptions2(group.floating) } : {}
4575
5656
  };
4576
5657
  }
4577
- function chartOptions(chart, ctx) {
5658
+ function chartOptions(chart, ctx, ordinal) {
4578
5659
  const id = ctx.nextDrawingId();
5660
+ const input = chartInput(chart);
5661
+ const look = chartLook(input);
4579
5662
  return {
4580
5663
  type: chart.chartType,
4581
5664
  categories: chart.series[0]?.labels ?? [],
4582
- series: chart.series.map((entry, index) => ({
4583
- name: entry.name ?? `Series ${index + 1}`,
4584
- values: entry.values
4585
- })),
4586
- ...chart.title && chart.showTitle !== false ? { title: chart.title } : {},
5665
+ series: chart.series.map(
5666
+ (entry, index) => withSeriesLook(
5667
+ {
5668
+ name: entry.name ?? `Series ${index + 1}`,
5669
+ values: entry.values,
5670
+ ...seriesLook(chart.chartType)
5671
+ },
5672
+ look.series[index]
5673
+ )
5674
+ ),
5675
+ ...look.chart,
4587
5676
  ...chart.showLegend !== void 0 ? { showLegend: chart.showLegend } : {},
5677
+ externalData: {
5678
+ ...look.chart.externalData,
5679
+ fileName: `chart${ordinal}.xlsx`,
5680
+ // A scatter chart's column A holds the x values its `c:xVal` caches.
5681
+ data: buildChartWorkbook(
5682
+ chart.series,
5683
+ input.scatterXValues ? { categoryValues: input.scatterXValues } : {}
5684
+ )
5685
+ },
4588
5686
  transformation: { width: chart.widthEmu, height: chart.heightEmu },
4589
5687
  altText: {
4590
5688
  id: String(id),
5689
+ name: "",
4591
5690
  ...chart.altText ? { description: chart.altText } : {}
4592
5691
  },
4593
5692
  ...chart.floating ? { floating: floatingOptions2(chart.floating) } : {}
4594
5693
  };
4595
5694
  }
4596
- function groupChild(child, ctx) {
5695
+ function groupChild2(child, ctx) {
4597
5696
  return child.kind === "shape" ? groupShape(child, ctx) : groupPicture(child, ctx);
4598
5697
  }
4599
5698
  function groupShape(shape, ctx) {
4600
5699
  const id = ctx.nextDrawingId();
4601
5700
  return {
4602
5701
  type: "wps",
4603
- transformation: childTransformation(shape.frame),
5702
+ transformation: childTransformation2(shape.frame),
4604
5703
  data: {
4605
5704
  nonVisualProperties: {
4606
5705
  id,
@@ -4609,7 +5708,7 @@ function groupShape(shape, ctx) {
4609
5708
  // to hold text, and it changes how the object behaves on selection.
4610
5709
  ...shape.isTextBox ? { textBox: "1" } : {}
4611
5710
  },
4612
- presetGeometry: { preset: shape.geometry },
5711
+ geometry: shape.geometry,
4613
5712
  ...shape.fill ? { fill: drawingFill(shape.fill) } : {},
4614
5713
  ...shape.outline ? { outline: drawingOutline(shape.outline) } : {},
4615
5714
  children: (shape.text?.paragraphs ?? []).map(
@@ -4635,7 +5734,7 @@ function groupPicture(picture2, ctx) {
4635
5734
  fileName: media.fallbackFileName
4636
5735
  }
4637
5736
  } : {},
4638
- transformation: childTransformation(picture2.frame),
5737
+ transformation: childTransformation2(picture2.frame),
4639
5738
  nonVisualProperties: {
4640
5739
  id,
4641
5740
  ...picture2.name ? { name: picture2.name } : {},
@@ -4644,11 +5743,7 @@ function groupPicture(picture2, ctx) {
4644
5743
  ...picture2.crop ? { sourceRectangle: sourceRectangle(picture2.crop) } : {}
4645
5744
  };
4646
5745
  }
4647
- function childTransformation(frame) {
4648
- const flip = {
4649
- ...frame.flipHorizontal ? { horizontal: true } : {},
4650
- ...frame.flipVertical ? { vertical: true } : {}
4651
- };
5746
+ function childTransformation2(frame) {
4652
5747
  return {
4653
5748
  offset: {
4654
5749
  emus: { x: frame.xEmu, y: frame.yEmu },
@@ -4664,7 +5759,8 @@ function childTransformation(frame) {
4664
5759
  },
4665
5760
  // Degrees: the backend multiplies by 60000 on the way into `@rot`.
4666
5761
  ...frame.rotationDegrees !== void 0 ? { rotation: frame.rotationDegrees } : {},
4667
- ...Object.keys(flip).length > 0 ? { flip } : {}
5762
+ ...frame.flipHorizontal ? { flipHorizontal: true } : {},
5763
+ ...frame.flipVertical ? { flipVertical: true } : {}
4668
5764
  };
4669
5765
  }
4670
5766
  function drawingFill(fill) {
@@ -4672,7 +5768,6 @@ function drawingFill(fill) {
4672
5768
  return {
4673
5769
  type: "solid",
4674
5770
  color: {
4675
- type: "rgb",
4676
5771
  value: fill.color.hex,
4677
5772
  // Transparency becomes an alpha transform on the colour. DrawingML
4678
5773
  // states *opacity*, so the value is the complement of what the author
@@ -4714,7 +5809,7 @@ function sourceRectangle(crop) {
4714
5809
  function shapeOptions(shape, ctx) {
4715
5810
  const id = String(ctx.nextDrawingId());
4716
5811
  return {
4717
- altText: { id },
5812
+ altText: { id, name: "" },
4718
5813
  children: shape.children.map((child) => paragraph(child, ctx)),
4719
5814
  // Raw transformation numbers are EMUs in @office-open/docx, while the IR
4720
5815
  // deliberately stores native shape dimensions in pixels.
@@ -4725,7 +5820,7 @@ function shapeOptions(shape, ctx) {
4725
5820
  ...shape.fill ? {
4726
5821
  fill: {
4727
5822
  type: "solid",
4728
- color: { type: "rgb", value: shape.fill.hex }
5823
+ color: { value: shape.fill.hex }
4729
5824
  }
4730
5825
  } : {},
4731
5826
  ...shape.outline ? { outline: drawingOutline(shape.outline) } : {},
@@ -4830,13 +5925,19 @@ function frameOptions2(frame) {
4830
5925
  ...frame.anchorLock !== void 0 ? { anchorLock: frame.anchorLock } : {},
4831
5926
  ...frame.rule ? { rule: frame.rule } : {}
4832
5927
  };
4833
- return frame.xTwips !== void 0 || frame.yTwips !== void 0 ? {
4834
- type: "absolute",
4835
- position: { x: frame.xTwips ?? 0, y: frame.yTwips ?? 0 },
4836
- ...base
4837
- } : {
5928
+ if (frame.xTwips !== void 0 || frame.yTwips !== void 0) {
5929
+ return {
5930
+ type: "absolute",
5931
+ position: { x: frame.xTwips ?? 0, y: frame.yTwips ?? 0 },
5932
+ ...base
5933
+ };
5934
+ }
5935
+ return {
4838
5936
  type: "alignment",
4839
- alignment: { x: frame.xAlign, y: frame.yAlign },
5937
+ alignment: {
5938
+ x: frame.xAlign,
5939
+ y: frame.yAlign
5940
+ },
4840
5941
  ...base
4841
5942
  };
4842
5943
  }
@@ -4857,7 +5958,7 @@ function block(value, ctx = emptyContext()) {
4857
5958
  case "toc":
4858
5959
  return { toc: tableOfContents(value) };
4859
5960
  default:
4860
- return assertNever2(value, "DocxIrBlock");
5961
+ return assertNever3(value, "DocxIrBlock");
4861
5962
  }
4862
5963
  }
4863
5964
  function tocEntry(entry) {
@@ -4876,6 +5977,7 @@ function tableOfContents(value) {
4876
5977
  headingStyleRange: `${value.headingRange.from}-${value.headingRange.to}`
4877
5978
  } : {},
4878
5979
  ...value.styleLevels?.length ? {
5980
+ // `StyleLevel` is a class the backend only reads the two fields of.
4879
5981
  stylesWithLevels: value.styleLevels.map((style) => ({
4880
5982
  styleName: style.styleName,
4881
5983
  level: style.level
@@ -4897,14 +5999,16 @@ function table(value, ctx) {
4897
5999
  // unstated columns comes out fractional, and the XML would carry it
4898
6000
  // verbatim; floored here, as the docxjs backend floors it.
4899
6001
  size: value.width.kind === "auto" ? 0 : value.width.kind === "twips" ? twips(value.width.value) : value.width.value,
4900
- type: value.width.kind === "twips" ? "dxa" : value.width.kind === "percent" ? "pct" : "auto"
6002
+ type: value.width.kind === "twips" ? "twips" : value.width.kind === "percent" ? "percent" : "auto"
4901
6003
  },
4902
6004
  layout: value.layout,
4903
6005
  // An empty grid is a table with nothing to say about its columns, which is
4904
6006
  // not the same as one whose columns are all zero wide. The compiler hands
4905
6007
  // over every grid in twips; floored for the same reason as the width.
4906
6008
  ...value.columnGrid.values.length > 0 ? { columnWidths: value.columnGrid.values.map(twips) } : {},
4907
- ...value.alignment ? { alignment: alignment(value.alignment) } : {},
6009
+ ...value.alignment ? {
6010
+ alignment: alignment(value.alignment)
6011
+ } : {},
4908
6012
  ...value.borders ? { borders: borders2(value.borders) } : {},
4909
6013
  ...value.cellMargins ? { margins: cellMargins(value.cellMargins) } : {},
4910
6014
  ...value.floating ? { float: tableFloat2(value.floating) } : {}
@@ -4949,52 +6053,29 @@ function tableRow(row, ctx) {
4949
6053
  }
4950
6054
  function tableCell(cell, ctx) {
4951
6055
  const children = cellChildren2(cell, ctx);
4952
- if (cell.children[cell.children.length - 1]?.kind !== "paragraph")
6056
+ if (cell.children[cell.children.length - 1]?.kind === "toc")
4953
6057
  children.push({ paragraph: { children: [] } });
4954
6058
  return {
4955
6059
  children,
4956
- ...cell.widthTwips !== void 0 ? { width: { size: twips(cell.widthTwips), type: "dxa" } } : {},
4957
- ...cell.verticalAlign ? { verticalAlign: cell.verticalAlign } : {},
6060
+ ...cell.widthTwips !== void 0 ? { width: { size: twips(cell.widthTwips), type: "twips" } } : {},
6061
+ ...cell.verticalAlign ? {
6062
+ verticalAlign: cell.verticalAlign
6063
+ } : {},
4958
6064
  ...cell.shading ? { shading: shading(cell.shading) } : {},
4959
6065
  ...cell.margins ? { margins: cellMargins(cell.margins) } : {},
4960
6066
  ...cell.borders ? { borders: borders2(cell.borders) } : {},
4961
6067
  ...cell.columnSpan !== void 0 ? { columnSpan: cell.columnSpan } : {},
4962
6068
  ...cell.rowSpan !== void 0 ? { verticalMerge: cell.rowSpan } : {},
4963
- ...cell.textDirection ? { textDirection: cell.textDirection } : {}
6069
+ ...cell.textDirection ? {
6070
+ textDirection: cell.textDirection
6071
+ } : {}
4964
6072
  };
4965
6073
  }
4966
6074
  function cellChildren2(cell, ctx) {
4967
- const children = [];
4968
- for (const child of cell.children) {
4969
- if (child.kind !== "toc") {
4970
- children.push(block(child, ctx));
4971
- continue;
4972
- }
4973
- const { alias, entries, ...options } = tableOfContents(child);
4974
- const index = ctx.cellTocs.length;
4975
- const toc = {
4976
- start: cellTocMarker(index, "start"),
4977
- end: cellTocMarker(index, "end"),
4978
- alias: String(alias),
4979
- options
4980
- };
4981
- ctx.cellTocs.push(toc);
4982
- children.push(
4983
- markerParagraph2(toc.start),
4984
- ...entries ?? [],
4985
- markerParagraph2(toc.end)
4986
- );
4987
- }
4988
- return children;
4989
- }
4990
- function cellTocMarker(index, edge) {
4991
- return `\uE000jto-cell-toc-${index}-${edge}\uE000`;
4992
- }
4993
- function markerParagraph2(text) {
4994
- return { paragraph: { children: [{ text }] } };
6075
+ return cell.children.map((child) => block(child, ctx));
4995
6076
  }
4996
6077
  function cellMargins(margins) {
4997
- const side = (value) => value === void 0 ? void 0 : { size: twips(value), type: "dxa" };
6078
+ const side = (value) => value === void 0 ? void 0 : { size: twips(value), type: "twips" };
4998
6079
  const out = {};
4999
6080
  for (const [name, value] of [
5000
6081
  ["top", margins.topTwips],
@@ -5037,39 +6118,43 @@ function section(value, ctx, closesDocument = false) {
5037
6118
  properties: {
5038
6119
  ...value.properties.type ? { type: value.properties.type } : {},
5039
6120
  ...value.properties.titlePage !== void 0 ? { titlePage: value.properties.titlePage } : {},
5040
- page: {
5041
- // Orientation is implied by the width/height pair, which is how this
5042
- // pipeline has always expressed it; stating it as well changes `w:pgSz`.
5043
- size: {
5044
- width: twips(page.widthTwips),
5045
- height: twips(page.heightTwips),
5046
- ...page.code !== void 0 ? { code: page.code } : {}
5047
- },
5048
- margin: {
5049
- top: twips(page.margins.topTwips),
5050
- right: twips(page.margins.rightTwips),
5051
- bottom: twips(page.margins.bottomTwips),
5052
- left: twips(page.margins.leftTwips),
5053
- ...page.margins.headerTwips !== void 0 ? { header: twips(page.margins.headerTwips) } : {},
5054
- ...page.margins.footerTwips !== void 0 ? { footer: twips(page.margins.footerTwips) } : {},
5055
- ...page.margins.gutterTwips !== void 0 ? { gutter: twips(page.margins.gutterTwips) } : {}
5056
- },
5057
- ...value.properties.pageNumbers ? {
5058
- pageNumbers: {
5059
- ...value.properties.pageNumbers.start !== void 0 ? { start: value.properties.pageNumbers.start } : {},
5060
- ...value.properties.pageNumbers.formatType ? { formatType: value.properties.pageNumbers.formatType } : {}
5061
- }
5062
- } : {},
5063
- ...value.properties.borders ? {
5064
- borders: {
5065
- ...value.properties.borders.display ? { display: value.properties.borders.display } : {},
5066
- ...value.properties.borders.offsetFrom ? { offsetFrom: value.properties.borders.offsetFrom } : {},
5067
- ...value.properties.borders.borders ? borders2(value.properties.borders.borders) : {}
5068
- }
5069
- } : {}
6121
+ // Orientation is implied by the width/height pair, which is how this
6122
+ // pipeline has always expressed it; stating it as well changes `w:pgSz`.
6123
+ pageSize: {
6124
+ width: twips(page.widthTwips),
6125
+ height: twips(page.heightTwips),
6126
+ ...page.code !== void 0 ? { code: page.code } : {}
6127
+ },
6128
+ pageMargin: {
6129
+ top: twips(page.margins.topTwips),
6130
+ right: twips(page.margins.rightTwips),
6131
+ bottom: twips(page.margins.bottomTwips),
6132
+ left: twips(page.margins.leftTwips),
6133
+ ...page.margins.headerTwips !== void 0 ? { header: twips(page.margins.headerTwips) } : {},
6134
+ ...page.margins.footerTwips !== void 0 ? { footer: twips(page.margins.footerTwips) } : {},
6135
+ ...page.margins.gutterTwips !== void 0 ? { gutter: twips(page.margins.gutterTwips) } : {}
5070
6136
  },
6137
+ ...value.properties.pageNumbers ? {
6138
+ pageNumberType: {
6139
+ ...value.properties.pageNumbers.start !== void 0 ? { start: value.properties.pageNumbers.start } : {},
6140
+ ...value.properties.pageNumbers.formatType ? {
6141
+ format: value.properties.pageNumbers.formatType
6142
+ } : {}
6143
+ }
6144
+ } : {},
6145
+ ...value.properties.borders ? {
6146
+ pageBorders: {
6147
+ ...value.properties.borders.display ? {
6148
+ display: value.properties.borders.display
6149
+ } : {},
6150
+ ...value.properties.borders.offsetFrom ? {
6151
+ offsetFrom: value.properties.borders.offsetFrom
6152
+ } : {},
6153
+ ...value.properties.borders.borders ? borders2(value.properties.borders.borders) : {}
6154
+ }
6155
+ } : {},
5071
6156
  ...columns ? {
5072
- column: {
6157
+ columns: {
5073
6158
  count: columns.count,
5074
6159
  ...columns.spaceTwips !== void 0 ? { space: twips(columns.spaceTwips) } : {},
5075
6160
  ...columns.separator !== void 0 ? { separate: columns.separator } : {},
@@ -5113,6 +6198,8 @@ function sectionChildren2(value, ctx, closesDocument = false) {
5113
6198
  spacing: { before: 0, after: 0, line: 20, lineRule: "exact" }
5114
6199
  }
5115
6200
  });
6201
+ if (!closesDocument && !bookmark?.closes && last?.kind === "toc")
6202
+ blocks.push({ paragraph: { children: [] } });
5116
6203
  if (!bookmark) return blocks;
5117
6204
  return [
5118
6205
  ...bookmark.opens ? [{ bookmarkStart: { id: bookmark.id, name: bookmark.name } }] : [],
@@ -5123,15 +6210,19 @@ function sectionChildren2(value, ctx, closesDocument = false) {
5123
6210
  function numberingConfig2(numbering) {
5124
6211
  return {
5125
6212
  reference: numbering.reference,
5126
- levels: numbering.levels.map((level) => ({
5127
- level: level.level,
5128
- format: level.format,
5129
- text: level.text,
5130
- ...level.alignment ? { alignment: alignment(level.alignment) } : {},
5131
- ...level.suffix ? { suffix: level.suffix } : {},
5132
- ...level.start !== void 0 ? { start: level.start } : {},
5133
- ...level.paragraphStyleId ? { paragraphStyle: level.paragraphStyleId } : {},
5134
- style: {
6213
+ levels: numbering.levels.map(
6214
+ (level) => ({
6215
+ level: level.level,
6216
+ format: level.format,
6217
+ text: level.text,
6218
+ ...level.alignment ? { alignment: alignment(level.alignment) } : {},
6219
+ ...level.suffix ? { suffix: level.suffix } : {},
6220
+ // Always stated. docx.js writes `w:start w:val="1"` for a level that
6221
+ // names none, and so did this backend until 0.14, which writes no
6222
+ // `w:start` at all when it is not given one; Word and LibreOffice then
6223
+ // count every such list from 0.
6224
+ start: level.start ?? 1,
6225
+ ...level.paragraphStyleId ? { paragraphStyle: level.paragraphStyleId } : {},
5135
6226
  ...level.indent ? {
5136
6227
  paragraph: {
5137
6228
  indent: {
@@ -5141,25 +6232,22 @@ function numberingConfig2(numbering) {
5141
6232
  }
5142
6233
  } : {},
5143
6234
  ...level.run ? { run: runProperties2(level.run) } : {}
5144
- }
5145
- }))
6235
+ })
6236
+ )
5146
6237
  };
5147
6238
  }
5148
- var WRAP_TYPE2, BODY_ANCHOR;
6239
+ var BODY_ANCHOR;
5149
6240
  var init_emit2 = __esm({
5150
6241
  "src/renderers/office-open/emit.ts"() {
5151
6242
  "use strict";
5152
6243
  init_units();
5153
- WRAP_TYPE2 = {
5154
- none: 0,
5155
- square: 1,
5156
- tight: 2,
5157
- topAndBottom: 3
5158
- };
6244
+ init_chartWorkbook();
6245
+ init_chartLook();
6246
+ init_chartParts();
5159
6247
  BODY_ANCHOR = {
5160
- top: "t",
5161
- middle: "ctr",
5162
- bottom: "b"
6248
+ top: "top",
6249
+ middle: "center",
6250
+ bottom: "bottom"
5163
6251
  };
5164
6252
  }
5165
6253
  });
@@ -5219,37 +6307,49 @@ var init_styles3 = __esm({
5219
6307
  var office_open_exports = {};
5220
6308
  __export(office_open_exports, {
5221
6309
  OFFICE_OPEN_DOCX_RENDERER_ID: () => OFFICE_OPEN_DOCX_RENDERER_ID,
6310
+ OFFICE_OPEN_VERSION: () => OFFICE_OPEN_VERSION,
5222
6311
  buildDocumentOptions: () => buildDocumentOptions,
5223
6312
  createOfficeOpenDocxRenderer: () => createOfficeOpenDocxRenderer
5224
6313
  });
5225
- import AdmZip4 from "adm-zip";
6314
+ import AdmZip5 from "adm-zip";
6315
+ import {
6316
+ assertBackendVersion,
6317
+ installedPackageVersion
6318
+ } from "@json-to-office/shared/rendering/node";
6319
+ function installedBackendVersion() {
6320
+ let resolved;
6321
+ try {
6322
+ resolved = import.meta.resolve?.(OFFICE_OPEN_DOCX);
6323
+ } catch {
6324
+ resolved = void 0;
6325
+ }
6326
+ return installedPackageVersion(OFFICE_OPEN_DOCX, import.meta.url, resolved);
6327
+ }
5226
6328
  async function createOfficeOpenDocxRenderer() {
5227
6329
  const backend = await import(
5228
6330
  /* @vite-ignore */
5229
6331
  OFFICE_OPEN_DOCX
5230
6332
  );
5231
- for (const name of [
5232
- "generateDocument",
5233
- "stringifyTableOfContents"
5234
- ]) {
5235
- if (typeof backend[name] !== "function") {
5236
- throw new Error(
5237
- `${OFFICE_OPEN_DOCX} does not export ${name}(); the installed version is not compatible with this adapter.`
5238
- );
5239
- }
6333
+ assertBackendVersion(
6334
+ OFFICE_OPEN_DOCX,
6335
+ OFFICE_OPEN_VERSION,
6336
+ installedBackendVersion()
6337
+ );
6338
+ if (typeof backend.generateDocument !== "function") {
6339
+ throw new Error(
6340
+ `${OFFICE_OPEN_DOCX} does not export generateDocument(); the installed version is not compatible with this adapter.`
6341
+ );
5240
6342
  }
5241
6343
  return {
5242
6344
  id: OFFICE_OPEN_DOCX_RENDERER_ID,
5243
6345
  format: "docx",
5244
6346
  capabilities: OFFICE_OPEN_CAPABILITIES,
5245
6347
  async render(ir, options) {
5246
- const charts = [];
5247
- const cellTocs = [];
6348
+ const charts2 = [];
5248
6349
  const document = await buildDocumentOptions(
5249
6350
  ir,
5250
- charts,
5251
- options?.svgRasterFallback,
5252
- cellTocs
6351
+ charts2,
6352
+ options?.svgRasterFallback
5253
6353
  );
5254
6354
  const bytes = await backend.generateDocument(document, {
5255
6355
  type: "uint8array"
@@ -5257,10 +6357,10 @@ async function createOfficeOpenDocxRenderer() {
5257
6357
  let raw = Buffer.from(
5258
6358
  bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes)
5259
6359
  );
5260
- if (charts.length > 0 || cellTocs.length > 0) {
5261
- const zip = new AdmZip4(raw);
5262
- spliceChartParts(zip, charts);
5263
- spliceCellTocs(zip, cellTocs, backend.stringifyTableOfContents);
6360
+ if (ir.theme || charts2.length > 0) {
6361
+ const zip = new AdmZip5(raw);
6362
+ if (ir.theme) spliceTheme(zip, ir.theme);
6363
+ finishChartParts(zip, charts2);
5264
6364
  raw = zip.toBuffer();
5265
6365
  }
5266
6366
  if (options?.deterministic === false)
@@ -5277,13 +6377,12 @@ async function createOfficeOpenDocxRenderer() {
5277
6377
  }
5278
6378
  };
5279
6379
  }
5280
- async function buildDocumentOptions(ir, charts = [], svgRasterFallback, cellTocs = []) {
6380
+ async function buildDocumentOptions(ir, charts2 = [], svgRasterFallback) {
5281
6381
  let nextDrawingId = 1;
5282
6382
  const ctx = {
5283
6383
  pictures: await prepareImages2(ir, svgRasterFallback),
5284
6384
  nextDrawingId: () => nextDrawingId++,
5285
- charts,
5286
- cellTocs
6385
+ charts: charts2
5287
6386
  };
5288
6387
  return {
5289
6388
  styles: emitStyles2(ir.styles),
@@ -5291,16 +6390,20 @@ async function buildDocumentOptions(ir, charts = [], svgRasterFallback, cellTocs
5291
6390
  (value, index) => section(value, ctx, index === ir.sections.length - 1)
5292
6391
  ),
5293
6392
  ...coreProperties2(ir),
5294
- features: {
6393
+ settings: {
5295
6394
  updateFields: ir.settings.updateFields,
5296
6395
  ...ir.settings.trackRevisions ? { trackRevisions: true } : {}
5297
6396
  },
5298
- ...ir.numbering.length > 0 ? { numbering: { config: ir.numbering.map(numberingConfig2) } } : {},
6397
+ ...ir.numbering.length > 0 ? {
6398
+ numbering: {
6399
+ abstractNumberings: ir.numbering.map(numberingConfig2)
6400
+ }
6401
+ } : {},
5299
6402
  ...ir.footnotes.length > 0 ? { footnotes: noteBodies2(ir.footnotes, ctx) } : {},
5300
6403
  ...ir.endnotes.length > 0 ? { endnotes: noteBodies2(ir.endnotes, ctx) } : {},
5301
6404
  ...ir.comments.length > 0 ? {
5302
- comments: {
5303
- children: ir.comments.map((comment) => ({
6405
+ comments: ir.comments.map(
6406
+ (comment) => ({
5304
6407
  id: comment.id,
5305
6408
  author: comment.author,
5306
6409
  ...comment.initials ? { initials: comment.initials } : {},
@@ -5308,29 +6411,25 @@ async function buildDocumentOptions(ir, charts = [], svgRasterFallback, cellTocs
5308
6411
  children: comment.children.map(
5309
6412
  (child) => paragraphOf(child, ctx)
5310
6413
  )
5311
- }))
5312
- }
6414
+ })
6415
+ )
5313
6416
  } : {}
5314
6417
  };
5315
6418
  }
5316
6419
  function paragraphOf(value, ctx) {
5317
6420
  const emitted = block(value, ctx);
5318
- const asParagraph = emitted.paragraph;
5319
- if (!asParagraph) {
6421
+ if (!("paragraph" in emitted) || typeof emitted.paragraph === "string") {
5320
6422
  throw new Error(
5321
6423
  `the office-open renderer expected a paragraph, not a "${value.kind}"`
5322
6424
  );
5323
6425
  }
5324
- return asParagraph;
6426
+ return emitted.paragraph;
5325
6427
  }
5326
6428
  function noteBodies2(notes, ctx) {
5327
- const bodies = {};
5328
- for (const note of notes) {
5329
- bodies[String(note.id)] = {
5330
- children: note.children.map((child) => paragraphOf(child, ctx))
5331
- };
5332
- }
5333
- return bodies;
6429
+ return notes.map((note) => ({
6430
+ id: note.id,
6431
+ children: note.children.map((child) => paragraphOf(child, ctx))
6432
+ }));
5334
6433
  }
5335
6434
  async function prepareImages2(ir, svgRasterFallback) {
5336
6435
  const placements = collectImagePlacements2(ir);
@@ -5353,23 +6452,16 @@ async function prepareImages2(ir, svgRasterFallback) {
5353
6452
  if (resource.kind !== "image") continue;
5354
6453
  const type = resource.mediaType;
5355
6454
  const data = Buffer.from(resource.bytes);
5356
- const placementData = /* @__PURE__ */ new Map();
5357
- [...placements.get(resource.id) ?? []].forEach((size, index) => {
5358
- placementData.set(
5359
- size,
5360
- index === 0 ? data : distinguishImageBytes(data, type, size)
5361
- );
5362
- });
5363
6455
  resources.set(resource.id, (placement) => {
5364
6456
  const sizeKey = placementKey(placement);
5365
6457
  const stem = `${resource.id}-${sizeKey}`;
5366
6458
  return {
5367
6459
  type,
5368
- // @office-open/docx stores a drawing transformation on its deduplicated
5369
- // media entry. Equivalent bytes drawn at a second size would otherwise
5370
- // reuse the first size, so each distinct placement size gets equivalent
5371
- // image bytes carrying a harmless format-native marker.
5372
- data: placementData.get(sizeKey) ?? data,
6460
+ // The same bytes at every size: the backend keeps each drawing's
6461
+ // extent on the drawing. 0.11 kept it on its deduplicated media
6462
+ // entry, and an image drawn at a second size took the first one's,
6463
+ // unless its bytes were told apart.
6464
+ data,
5373
6465
  // Named after the resource and the size it is drawn at, so two
5374
6466
  // placements of one image at one size share a single part and a third
5375
6467
  // at another size gets its own.
@@ -5392,104 +6484,6 @@ async function prepareImages2(ir, svgRasterFallback) {
5392
6484
  function placementKey(placement) {
5393
6485
  return `${emuToPixels(placement.widthEmu)}x${emuToPixels(placement.heightEmu)}`;
5394
6486
  }
5395
- function distinguishImageBytes(data, mediaType, placement) {
5396
- const marker = Buffer.from(`json-to-office:${placement}`, "utf8");
5397
- switch (mediaType) {
5398
- case "png":
5399
- return addPngTextChunk(data, marker);
5400
- case "jpg":
5401
- return addJpegComment(data, marker);
5402
- case "gif":
5403
- return addGifComment(data, marker);
5404
- case "svg":
5405
- return Buffer.concat([
5406
- data,
5407
- Buffer.from(`
5408
- <!-- ${marker.toString("utf8")} -->`, "utf8")
5409
- ]);
5410
- case "bmp": {
5411
- const marked = Buffer.concat([data, marker]);
5412
- if (marked.length >= 14 && marked.subarray(0, 2).toString("ascii") === "BM") {
5413
- marked.writeUInt32LE(marked.length, 2);
5414
- }
5415
- return marked;
5416
- }
5417
- default:
5418
- return Buffer.concat([data, marker]);
5419
- }
5420
- }
5421
- function addPngTextChunk(data, marker) {
5422
- const signature = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
5423
- if (data.length < 8 || !data.subarray(0, 8).equals(signature)) {
5424
- return Buffer.concat([data, marker]);
5425
- }
5426
- let offset = 8;
5427
- while (offset + 12 <= data.length) {
5428
- const payloadLength = data.readUInt32BE(offset);
5429
- const chunkEnd = offset + 12 + payloadLength;
5430
- if (chunkEnd > data.length) break;
5431
- if (data.subarray(offset + 4, offset + 8).toString("ascii") === "IEND") {
5432
- const payload = Buffer.concat([
5433
- Buffer.from("json-to-office\0", "latin1"),
5434
- marker
5435
- ]);
5436
- const chunk = Buffer.alloc(12 + payload.length);
5437
- chunk.writeUInt32BE(payload.length, 0);
5438
- chunk.write("tEXt", 4, 4, "ascii");
5439
- payload.copy(chunk, 8);
5440
- chunk.writeUInt32BE(
5441
- crc32(chunk.subarray(4, 8 + payload.length)),
5442
- 8 + payload.length
5443
- );
5444
- return Buffer.concat([
5445
- data.subarray(0, offset),
5446
- chunk,
5447
- data.subarray(offset)
5448
- ]);
5449
- }
5450
- offset = chunkEnd;
5451
- }
5452
- throw new Error(
5453
- "Cannot mark a PNG that has no intact IEND chunk; its bytes were not checked to decode before rendering"
5454
- );
5455
- }
5456
- function addJpegComment(data, marker) {
5457
- if (data.length < 2 || data[0] !== 255 || data[1] !== 216) {
5458
- return Buffer.concat([data, marker]);
5459
- }
5460
- const comment = Buffer.alloc(4 + marker.length);
5461
- comment[0] = 255;
5462
- comment[1] = 254;
5463
- comment.writeUInt16BE(marker.length + 2, 2);
5464
- marker.copy(comment, 4);
5465
- return Buffer.concat([data.subarray(0, 2), comment, data.subarray(2)]);
5466
- }
5467
- function addGifComment(data, marker) {
5468
- const trailer = data.lastIndexOf(59);
5469
- if (trailer < 0 || marker.length > 255) {
5470
- return Buffer.concat([data, marker]);
5471
- }
5472
- const comment = Buffer.concat([
5473
- Buffer.from([33, 254, marker.length]),
5474
- marker,
5475
- Buffer.from([0])
5476
- ]);
5477
- return Buffer.concat([
5478
- data.subarray(0, trailer),
5479
- comment,
5480
- data.subarray(trailer)
5481
- ]);
5482
- }
5483
- function crc32(data) {
5484
- let crc = 4294967295;
5485
- for (const byte of data) {
5486
- crc ^= byte;
5487
- for (let bit = 0; bit < 8; bit += 1) {
5488
- crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
5489
- }
5490
- }
5491
- return (crc ^ 4294967295) >>> 0;
5492
- }
5493
6487
  function collectImagePlacements2(ir) {
5494
6488
  const placements = /* @__PURE__ */ new Map();
5495
6489
  const record = (resourceId, placement) => {
@@ -5557,18 +6551,19 @@ function coreProperties2(ir) {
5557
6551
  ...metadata.custom?.length ? { customProperties: metadata.custom } : {}
5558
6552
  };
5559
6553
  }
5560
- var OFFICE_OPEN_DOCX_RENDERER_ID, OFFICE_OPEN_DOCX, OFFICE_OPEN_CAPABILITIES;
6554
+ var OFFICE_OPEN_DOCX_RENDERER_ID, OFFICE_OPEN_DOCX, OFFICE_OPEN_VERSION, OFFICE_OPEN_CAPABILITIES;
5561
6555
  var init_office_open = __esm({
5562
6556
  "src/renderers/office-open/index.ts"() {
5563
6557
  "use strict";
5564
6558
  init_chartParts();
5565
- init_cellTocs();
6559
+ init_themePart();
5566
6560
  init_imageUtils();
5567
6561
  init_packageDocument();
5568
6562
  init_emit2();
5569
6563
  init_styles3();
5570
6564
  OFFICE_OPEN_DOCX_RENDERER_ID = "office-open";
5571
6565
  OFFICE_OPEN_DOCX = "@office-open/docx";
6566
+ OFFICE_OPEN_VERSION = "0.14.6";
5572
6567
  OFFICE_OPEN_CAPABILITIES = /* @__PURE__ */ new Set([
5573
6568
  "paragraphs",
5574
6569
  "styles",
@@ -9484,6 +10479,7 @@ function emptyToUndefined(formatting) {
9484
10479
  }
9485
10480
 
9486
10481
  // src/ir/compiler.ts
10482
+ init_chartValues();
9487
10483
  init_types();
9488
10484
 
9489
10485
  // src/ir/measure.ts
@@ -9516,6 +10512,49 @@ function tableWidthTwips(width, measureTwips) {
9516
10512
 
9517
10513
  // src/ir/compiler.ts
9518
10514
  init_units();
10515
+
10516
+ // src/ir/theme.ts
10517
+ init_colorUtils();
10518
+ init_styleHelpers();
10519
+ init_types();
10520
+ var THEME_COLOR_TOKENS = [
10521
+ ["dark1", "text"],
10522
+ ["light1", "background"],
10523
+ ["dark2", "textSecondary"],
10524
+ ["light2", "backgroundSecondary"],
10525
+ ["accent1", "primary"],
10526
+ ["accent2", "secondary"],
10527
+ ["accent3", "accent"],
10528
+ ["accent4", "accent4"],
10529
+ ["accent5", "accent5"],
10530
+ ["accent6", "accent6"]
10531
+ ];
10532
+ var NOT_XML_CHARS = new RegExp(NOT_XML_CHAR.source, "gu");
10533
+ var xmlText = (value) => (value ?? "").replace(NOT_XML_CHARS, "").trim();
10534
+ function compileTheme(theme, themeName) {
10535
+ const colors = {};
10536
+ for (const [slot, token] of THEME_COLOR_TOKENS) {
10537
+ const hex = schemeHex(token, theme);
10538
+ if (hex) colors[slot] = { hex };
10539
+ }
10540
+ const headingFont = xmlText(resolveFontFamily(theme, "heading"));
10541
+ const bodyFont = xmlText(resolveFontFamily(theme, "body"));
10542
+ return {
10543
+ name: xmlText(theme.name) || xmlText(themeName) || "Office Theme",
10544
+ colors,
10545
+ ...headingFont ? { headingFont } : {},
10546
+ ...bodyFont ? { bodyFont } : {}
10547
+ };
10548
+ }
10549
+ function schemeHex(token, theme) {
10550
+ try {
10551
+ return resolveColor(token, theme);
10552
+ } catch {
10553
+ return void 0;
10554
+ }
10555
+ }
10556
+
10557
+ // src/ir/compiler.ts
9519
10558
  var CONTENT_BOOKMARK_ID_BASE = 2e6;
9520
10559
  var SINGLE_LINE_TWIPS = 240;
9521
10560
  function compileDocument(structure, layout, warnings = [], images = /* @__PURE__ */ new Map(), options = {}) {
@@ -9581,6 +10620,7 @@ function compileDocument(structure, layout, warnings = [], images = /* @__PURE__
9581
10620
  trackRevisions: structure.trackRevisions === true,
9582
10621
  ...noProofWords(structure.theme) ? { noProofWords: noProofWords(structure.theme) } : {}
9583
10622
  },
10623
+ theme: compileTheme(structure.theme, structure.themeName),
9584
10624
  // The statistic styles are appended rather than built with the rest: a
9585
10625
  // document that uses no statistic must keep the exact style set it had
9586
10626
  // before the component had styles, or every recorded golden moves for a
@@ -10349,14 +11389,6 @@ function compileShape2(props, children, scope) {
10349
11389
  );
10350
11390
  }
10351
11391
  const fill = style?.shading?.fill;
10352
- const dropOutline = Boolean(fill) && Boolean(outline.outline);
10353
- if (dropOutline) {
10354
- warnOnce(
10355
- ctx,
10356
- "text-box",
10357
- '[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.'
10358
- );
10359
- }
10360
11392
  ctx.features.require("text-boxes", path3);
10361
11393
  if (props.floating) ctx.features.require("floating-images", path3);
10362
11394
  const insets = shapeInsets(
@@ -10375,7 +11407,7 @@ function compileShape2(props, children, scope) {
10375
11407
  heightPx: height.pixels,
10376
11408
  children,
10377
11409
  ...fill ? { fill: irColor(resolveColor(fill, ctx.theme)) } : {},
10378
- ...outline.outline && !dropOutline ? { outline: outline.outline } : {},
11410
+ ...outline.outline ? { outline: outline.outline } : {},
10379
11411
  ...insets ? { insetsEmu: insets } : {},
10380
11412
  ...props.floating ? { floating: compileFloating(props.floating, ctx, path3) } : {}
10381
11413
  }
@@ -11598,6 +12630,7 @@ function compileImage2(component, scope) {
11598
12630
  }
11599
12631
  var DEFAULT_CHART_HEIGHT_INCHES = 3;
11600
12632
  var MAX_CHART_TEXT_POINTS = 10;
12633
+ var MIN_CHART_TEXT_POINTS = 1;
11601
12634
  function chartTextFont(theme) {
11602
12635
  const bodySize = getThemeFonts(theme).body?.size;
11603
12636
  let color = "000000";
@@ -11607,9 +12640,12 @@ function chartTextFont(theme) {
11607
12640
  }
11608
12641
  return {
11609
12642
  fontFamily: resolveFontFamily(theme, "body"),
11610
- fontSize: Math.min(
11611
- typeof bodySize === "number" && bodySize > 0 ? bodySize : 11,
11612
- MAX_CHART_TEXT_POINTS
12643
+ fontSize: Math.max(
12644
+ MIN_CHART_TEXT_POINTS,
12645
+ Math.min(
12646
+ typeof bodySize === "number" && bodySize > 0 ? bodySize : 11,
12647
+ MAX_CHART_TEXT_POINTS
12648
+ )
11613
12649
  ),
11614
12650
  bold: false,
11615
12651
  color
@@ -11620,7 +12656,7 @@ function compileChart(component, scope) {
11620
12656
  const props = component.props ?? {};
11621
12657
  if (props.type === "bubble") {
11622
12658
  throw new Error(
11623
- `Chart at ${path3} is a bubble chart, which no docx renderer draws; use the pptx \`chart\` component on the pptxgenjs renderer for one.`
12659
+ `Chart at ${path3} is a bubble chart, but the docx \`chart\` component has no bubble sizes; use the pptx \`chart\` component on the pptxgenjs renderer.`
11624
12660
  );
11625
12661
  }
11626
12662
  const rawSeries = Array.isArray(props.data) ? props.data : [];
@@ -11642,10 +12678,22 @@ function compileChart(component, scope) {
11642
12678
  `Chart series "${label}" at ${path3} has ${labels.length} labels and ${values.length} values; they must be the same length.`
11643
12679
  );
11644
12680
  }
12681
+ if (labels.length === 0) {
12682
+ throw new Error(
12683
+ `Chart series "${label}" at ${path3} has no data points; give it at least one label and value.`
12684
+ );
12685
+ }
12686
+ const numbers = values.map((value) => Number(value));
12687
+ const bad = numbers.findIndex((value) => !Number.isFinite(value));
12688
+ if (bad !== -1) {
12689
+ throw new Error(
12690
+ `Chart series "${label}" at ${path3} has a value that is not a finite number (${JSON.stringify(values[bad])} at "${String(labels[bad])}").`
12691
+ );
12692
+ }
11645
12693
  return {
11646
12694
  ...typeof raw.name === "string" ? { name: raw.name } : {},
11647
12695
  labels: labels.map((value) => String(value)),
11648
- values: values.map((value) => Number(value))
12696
+ values: numbers
11649
12697
  };
11650
12698
  });
11651
12699
  const categories = series[0].labels;
@@ -11657,6 +12705,44 @@ function compileChart(component, scope) {
11657
12705
  );
11658
12706
  }
11659
12707
  }
12708
+ if (props.type === "pie" && series.length > 1) {
12709
+ throw new Error(
12710
+ `Chart at ${path3} is a pie with ${series.length} series; a pie draws one series. Use a doughnut for one ring per series, or a column chart to compare them.`
12711
+ );
12712
+ }
12713
+ if (props.type === "pie" || props.type === "doughnut") {
12714
+ for (const [index, entry] of series.entries()) {
12715
+ const at = entry.values.findIndex((value) => value < 0);
12716
+ if (at === -1) continue;
12717
+ throw new Error(
12718
+ `Chart series "${entry.name ?? `series ${index}`}" at ${path3} has a negative value (${entry.values[at]} at "${entry.labels[at]}"); a ${props.type} chart draws each value as a share of the whole.`
12719
+ );
12720
+ }
12721
+ }
12722
+ if (props.type === "scatter") {
12723
+ for (const [index, entry] of series.entries()) {
12724
+ const at = entry.labels.findIndex(isOutOfRangeNumericLabel);
12725
+ if (at !== -1) {
12726
+ throw new Error(
12727
+ `Chart series "${entry.name ?? `series ${index}`}" at ${path3} has an x label ${JSON.stringify(entry.labels[at])} outside the finite number range.`
12728
+ );
12729
+ }
12730
+ }
12731
+ }
12732
+ if (props.type === "scatter" && series.some((entry) => entry.labels.some((label) => !isNumericLabel(label)))) {
12733
+ warnOnce(
12734
+ ctx,
12735
+ "chart",
12736
+ `Chart at ${path3} is a scatter chart whose labels are not all finite numbers; labels that do not read as finite numbers use their 1-based positions for x.`
12737
+ );
12738
+ }
12739
+ if ((props.type === "pie" || props.type === "doughnut") && (typeof props.catAxisTitle === "string" || typeof props.valAxisTitle === "string")) {
12740
+ warnOnce(
12741
+ ctx,
12742
+ "chart",
12743
+ `Chart at ${path3} is a ${props.type} chart, which has no axes; its axis titles are not drawn.`
12744
+ );
12745
+ }
11660
12746
  const colors = Array.isArray(props.chartColors) ? props.chartColors.map(
11661
12747
  (color) => resolveColor(String(color), ctx.theme)
11662
12748
  ) : chartPaletteValues(ctx.theme).flatMap((value) => {
@@ -11669,13 +12755,21 @@ function compileChart(component, scope) {
11669
12755
  const contentWidthInches = scope.measureTwips / 1440;
11670
12756
  const widthInches = typeof props.width === "number" ? props.width : contentWidthInches;
11671
12757
  const heightInches = typeof props.height === "number" ? props.height : DEFAULT_CHART_HEIGHT_INCHES;
12758
+ const widthEmu = inchesToEmu(widthInches);
12759
+ const heightEmu = inchesToEmu(heightInches);
12760
+ if (!(widthEmu > 0 && heightEmu > 0)) {
12761
+ const inches = (value) => String(Number.isFinite(value) ? Math.round(value * 100) / 100 : value);
12762
+ throw new Error(
12763
+ `Chart at ${path3} has no room: it would be ${inches(widthInches)}\xD7${inches(heightInches)} in. Set \`width\`/\`height\`, or give it a container with space.`
12764
+ );
12765
+ }
11672
12766
  const chart = {
11673
12767
  kind: "chart",
11674
12768
  chartType: props.type,
11675
12769
  series,
11676
12770
  colors,
11677
- widthEmu: inchesToEmu(widthInches),
11678
- heightEmu: inchesToEmu(heightInches),
12771
+ widthEmu,
12772
+ heightEmu,
11679
12773
  ...typeof props.title === "string" ? { title: props.title } : {},
11680
12774
  ...props.showTitle !== void 0 ? { showTitle: props.showTitle } : {},
11681
12775
  ...props.showLegend !== void 0 ? { showLegend: props.showLegend } : {},
@@ -11878,7 +12972,7 @@ function compileFrame(floating, ctx, path3) {
11878
12972
  function compileFloating(floating, ctx, path3) {
11879
12973
  if (floating.wrap?.type === "tight") {
11880
12974
  throw new Error(
11881
- "Image floating wrap.type 'tight' is not supported due to invalid OOXML emitted by docx. Use 'square', 'topAndBottom', or 'none'."
12975
+ "Image floating wrap.type 'tight' is not supported. Use 'square', 'topAndBottom', or 'none'."
11882
12976
  );
11883
12977
  }
11884
12978
  const hRelative = floating.horizontalPosition?.relative;