@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
package/dist/index.js CHANGED
@@ -2779,7 +2779,7 @@ var init_imageUtils = __esm({
2779
2779
  });
2780
2780
 
2781
2781
  // src/ir/types.ts
2782
- var TWIPS_PER_INCH, TWIPS_PER_POINT2, EMU_PER_INCH, EMU_PER_TWIP, DOCX_IR_SCHEMA_VERSION, DOCX_IR_BORDER_STYLES;
2782
+ var TWIPS_PER_INCH, TWIPS_PER_POINT2, EMU_PER_INCH, EMU_PER_TWIP, DOCX_IR_SCHEMA_VERSION, NOT_XML_CHAR, DOCX_IR_BORDER_STYLES;
2783
2783
  var init_types = __esm({
2784
2784
  "src/ir/types.ts"() {
2785
2785
  "use strict";
@@ -2788,6 +2788,7 @@ var init_types = __esm({
2788
2788
  EMU_PER_INCH = 914400;
2789
2789
  EMU_PER_TWIP = EMU_PER_INCH / TWIPS_PER_INCH;
2790
2790
  DOCX_IR_SCHEMA_VERSION = 1;
2791
+ NOT_XML_CHAR = /[^\t\n\r\u0020-\uD7FF\uE000-\uFFFD\u{10000}-\u{10FFFF}]/u;
2791
2792
  DOCX_IR_BORDER_STYLES = [
2792
2793
  "single",
2793
2794
  "dashDotStroked",
@@ -2874,6 +2875,242 @@ var init_units = __esm({
2874
2875
  }
2875
2876
  });
2876
2877
 
2878
+ // src/ir/chartValues.ts
2879
+ function numericLabelValue(label) {
2880
+ const value = label.trim();
2881
+ return NUMERIC_LABEL.test(value) ? Number(value) : void 0;
2882
+ }
2883
+ function isNumericLabel(label) {
2884
+ const value = numericLabelValue(label);
2885
+ return value !== void 0 && Number.isFinite(value);
2886
+ }
2887
+ function isOutOfRangeNumericLabel(label) {
2888
+ const value = numericLabelValue(label);
2889
+ return value !== void 0 && !Number.isFinite(value);
2890
+ }
2891
+ function scatterX(labels, index) {
2892
+ const label = labels[index];
2893
+ const value = label === void 0 ? void 0 : numericLabelValue(label);
2894
+ if (value === void 0) return index + 1;
2895
+ if (!Number.isFinite(value)) {
2896
+ throw new RangeError(
2897
+ `Scatter x label ${JSON.stringify(label)} is not finite.`
2898
+ );
2899
+ }
2900
+ return value;
2901
+ }
2902
+ var NUMERIC_LABEL;
2903
+ var init_chartValues = __esm({
2904
+ "src/ir/chartValues.ts"() {
2905
+ "use strict";
2906
+ NUMERIC_LABEL = /^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$/;
2907
+ }
2908
+ });
2909
+
2910
+ // src/renderers/chartText.ts
2911
+ function chartTextRoles(font) {
2912
+ const { bold, ...rest } = font;
2913
+ const text = rest;
2914
+ return {
2915
+ text,
2916
+ axisTitle: { ...text, bold },
2917
+ title: {
2918
+ ...text,
2919
+ fontSize: Math.round(text.fontSize * CHART_TITLE_SCALE),
2920
+ bold: true
2921
+ }
2922
+ };
2923
+ }
2924
+ var CHART_TITLE_SCALE;
2925
+ var init_chartText = __esm({
2926
+ "src/renderers/chartText.ts"() {
2927
+ "use strict";
2928
+ CHART_TITLE_SCALE = 1.4;
2929
+ }
2930
+ });
2931
+
2932
+ // src/renderers/docxjs/docxSubpath.ts
2933
+ function docxSubpath(specifier, component, importer) {
2934
+ let module;
2935
+ let failure;
2936
+ let loading;
2937
+ return {
2938
+ specifier,
2939
+ load() {
2940
+ loading ??= importer().then(
2941
+ (loadedModule) => {
2942
+ module = loadedModule;
2943
+ },
2944
+ (reason) => {
2945
+ failure = { reason };
2946
+ }
2947
+ );
2948
+ return loading;
2949
+ },
2950
+ loaded: () => module !== void 0,
2951
+ get() {
2952
+ if (module !== void 0) return module;
2953
+ if (failure !== void 0) {
2954
+ const { reason } = failure;
2955
+ const detail = reason instanceof Error ? reason.message : String(reason);
2956
+ throw new Error(
2957
+ `The docx \`${component}\` component needs docx 9.8.0 or later: the \`${specifier}\` entry could not be loaded (${detail})`
2958
+ );
2959
+ }
2960
+ throw new Error(
2961
+ `${specifier} is not loaded: await its load() before buildDocument() (render() does)`
2962
+ );
2963
+ }
2964
+ };
2965
+ }
2966
+ var init_docxSubpath = __esm({
2967
+ "src/renderers/docxjs/docxSubpath.ts"() {
2968
+ "use strict";
2969
+ }
2970
+ });
2971
+
2972
+ // src/renderers/docxjs/charts.ts
2973
+ import { assertNever } from "@json-to-office/shared/rendering";
2974
+ function loadDocxCharts() {
2975
+ return charts.load();
2976
+ }
2977
+ function chartFont(role) {
2978
+ return {
2979
+ name: role.fontFamily,
2980
+ size: role.fontSize,
2981
+ color: role.color,
2982
+ bold: role.bold ?? false
2983
+ };
2984
+ }
2985
+ function chartRunOptions(chart, floating) {
2986
+ const roles = chartTextRoles(chart.textFont);
2987
+ const text = chartFont(roles.text);
2988
+ const axisTitle = chartFont(roles.axisTitle);
2989
+ const palette = chart.colors;
2990
+ const colorAt = (index) => palette.length > 0 ? palette[index % palette.length] : void 0;
2991
+ const withColor = (index) => {
2992
+ const color = colorAt(index);
2993
+ return color ? { color } : {};
2994
+ };
2995
+ const seriesName = (index) => chart.series[index].name ?? `Series ${index + 1}`;
2996
+ const categories = chart.series[0]?.labels ?? [];
2997
+ const axis = (title) => ({
2998
+ ...title ? { title: { text: title, font: axisTitle } } : {},
2999
+ font: text
3000
+ });
3001
+ const common = {
3002
+ ...chart.title && chart.showTitle !== false ? { title: { text: chart.title, font: chartFont(roles.title) } } : {},
3003
+ legend: chart.showLegend === false ? false : {
3004
+ position: LEGEND_POSITION[chart.legendPosition ?? "b"],
3005
+ font: text
3006
+ },
3007
+ font: { name: roles.text.fontFamily, color: roles.text.color },
3008
+ // Office-open's chart space is unfilled and unbordered; `ChartRun`'s
3009
+ // default is Word's white fill and grey border.
3010
+ chartArea: { fill: "none", border: "none" },
3011
+ transformation: {
3012
+ width: emuToPixels(chart.widthEmu),
3013
+ height: emuToPixels(chart.heightEmu)
3014
+ },
3015
+ // Without an authored description `ChartRun` describes the chart from
3016
+ // its data, which is deterministic; the name stays empty, as it is on
3017
+ // every other drawing this renderer writes.
3018
+ ...chart.altText ? { altText: { name: "", description: chart.altText } } : {},
3019
+ ...floating ? { floating } : {}
3020
+ };
3021
+ switch (chart.chartType) {
3022
+ case "column":
3023
+ case "bar":
3024
+ case "area":
3025
+ case "line":
3026
+ return {
3027
+ ...common,
3028
+ type: chart.chartType,
3029
+ categories,
3030
+ ...chart.chartType === "line" ? { markers: true } : {},
3031
+ series: chart.series.map((entry, index) => ({
3032
+ name: seriesName(index),
3033
+ values: entry.values,
3034
+ ...withColor(index)
3035
+ })),
3036
+ categoryAxis: axis(chart.categoryAxisTitle),
3037
+ valueAxis: axis(chart.valueAxisTitle)
3038
+ };
3039
+ case "pie":
3040
+ case "doughnut":
3041
+ return {
3042
+ ...common,
3043
+ type: chart.chartType,
3044
+ categories,
3045
+ series: chart.series.map((entry, index) => ({
3046
+ name: seriesName(index),
3047
+ values: entry.values,
3048
+ ...palette.length > 0 ? { colors: entry.values.map((_, point) => colorAt(point)) } : {}
3049
+ }))
3050
+ };
3051
+ case "radar":
3052
+ return {
3053
+ ...common,
3054
+ type: "radar",
3055
+ categories,
3056
+ markers: true,
3057
+ series: chart.series.map((entry, index) => ({
3058
+ name: seriesName(index),
3059
+ values: entry.values,
3060
+ ...withColor(index)
3061
+ })),
3062
+ categoryAxis: axis(chart.categoryAxisTitle),
3063
+ valueAxis: axis(chart.valueAxisTitle)
3064
+ };
3065
+ case "scatter":
3066
+ return {
3067
+ ...common,
3068
+ type: "scatter",
3069
+ markers: true,
3070
+ lines: "straight",
3071
+ series: chart.series.map((entry, index) => ({
3072
+ name: seriesName(index),
3073
+ points: entry.values.map((y, point) => ({
3074
+ x: scatterX(entry.labels, point),
3075
+ y
3076
+ })),
3077
+ ...withColor(index)
3078
+ })),
3079
+ xAxis: { ...axis(chart.categoryAxisTitle), gridlines: false },
3080
+ yAxis: axis(chart.valueAxisTitle)
3081
+ };
3082
+ case "bubble":
3083
+ throw new Error(
3084
+ "the docxjs renderer has no emitter for a bubble chart; the compiler should have refused it"
3085
+ );
3086
+ default:
3087
+ return assertNever(chart.chartType, "DocxIrChartType");
3088
+ }
3089
+ }
3090
+ function emitChart(chart, floating) {
3091
+ return new (charts.get()).ChartRun(
3092
+ chartRunOptions(chart, floating)
3093
+ );
3094
+ }
3095
+ var charts, LEGEND_POSITION;
3096
+ var init_charts = __esm({
3097
+ "src/renderers/docxjs/charts.ts"() {
3098
+ "use strict";
3099
+ init_chartValues();
3100
+ init_units();
3101
+ init_chartText();
3102
+ init_docxSubpath();
3103
+ charts = docxSubpath("docx/charts", "chart", () => import("docx/charts"));
3104
+ LEGEND_POSITION = {
3105
+ b: "bottom",
3106
+ l: "left",
3107
+ r: "right",
3108
+ t: "top",
3109
+ tr: "topRight"
3110
+ };
3111
+ }
3112
+ });
3113
+
2877
3114
  // src/renderers/docxjs/emit.ts
2878
3115
  import {
2879
3116
  AlignmentType,
@@ -2909,7 +3146,7 @@ import {
2909
3146
  WidthType,
2910
3147
  WpsShapeRun
2911
3148
  } from "docx";
2912
- import { assertNever } from "@json-to-office/shared/rendering";
3149
+ import { assertNever as assertNever2 } from "@json-to-office/shared/rendering";
2913
3150
  function complexField(instruction, formatting, leading, cachedText) {
2914
3151
  const options = runOptions(formatting);
2915
3152
  const fieldChar = (type, extra = {}) => new TextRun({
@@ -2946,7 +3183,12 @@ function runOptions(formatting) {
2946
3183
  if (formatting.bold !== void 0) options.bold = formatting.bold;
2947
3184
  if (formatting.italic !== void 0) options.italics = formatting.italic;
2948
3185
  if (formatting.underline !== void 0) {
2949
- options.underline = formatting.underline ? { type: formatting.underline.type } : void 0;
3186
+ options.underline = formatting.underline ? {
3187
+ type: formatting.underline.type,
3188
+ // A native visual's text is the one place a run states its own
3189
+ // underline colour.
3190
+ ...formatting.underline.color ? { color: formatting.underline.color.hex } : {}
3191
+ } : void 0;
2950
3192
  }
2951
3193
  if (formatting.strike !== void 0) options.strike = formatting.strike;
2952
3194
  if (formatting.allCaps !== void 0) options.allCaps = formatting.allCaps;
@@ -3072,21 +3314,36 @@ function inlineChildren(children, resources = /* @__PURE__ */ new Map()) {
3072
3314
  case "shape":
3073
3315
  out.push(emitShape(child, resources));
3074
3316
  break;
3075
- case "drawingGroup":
3076
- throw new Error(
3077
- "the docxjs renderer has no emitter for a drawing group; this document should have been refused by the capability check"
3078
- );
3079
- case "chart":
3080
- throw new Error(
3081
- "the docxjs renderer has no emitter for a chart; this document should have been refused by the capability check"
3317
+ case "drawingGroup": {
3318
+ const build = resources.drawingGroup;
3319
+ if (!build) {
3320
+ throw new Error(
3321
+ "the docxjs renderer was handed a drawing group without loading docx/shapes; render() loads it whenever the IR holds one"
3322
+ );
3323
+ }
3324
+ const pending = breakOption();
3325
+ if (pending.break) out.push(new TextRun(pending));
3326
+ out.push(build(child, resources));
3327
+ break;
3328
+ }
3329
+ case "chart": {
3330
+ const pending = breakOption();
3331
+ if (pending.break) out.push(new TextRun(pending));
3332
+ out.push(
3333
+ emitChart(
3334
+ child,
3335
+ child.floating ? floatingOptions(child.floating) : void 0
3336
+ )
3082
3337
  );
3338
+ break;
3339
+ }
3083
3340
  case "noteReference":
3084
3341
  out.push(
3085
3342
  child.noteKind === "endnote" ? new EndnoteReferenceRun(child.id) : new FootnoteReferenceRun(child.id)
3086
3343
  );
3087
3344
  break;
3088
3345
  default:
3089
- assertNever(child, "DocxIrInline");
3346
+ assertNever2(child, "DocxIrInline");
3090
3347
  }
3091
3348
  }
3092
3349
  return out;
@@ -3291,7 +3548,7 @@ function emitBlock(block2, resources = /* @__PURE__ */ new Map()) {
3291
3548
  case "toc":
3292
3549
  return emitToc(block2);
3293
3550
  default:
3294
- return assertNever(block2, "DocxIrBlock");
3551
+ return assertNever2(block2, "DocxIrBlock");
3295
3552
  }
3296
3553
  }
3297
3554
  function emitToc(block2) {
@@ -3435,6 +3692,7 @@ var ALIGNMENT2, PAGE_FIELDS, isSequenceField, WRAP_TYPE, ThemedTableOfContents,
3435
3692
  var init_emit = __esm({
3436
3693
  "src/renderers/docxjs/emit.ts"() {
3437
3694
  "use strict";
3695
+ init_charts();
3438
3696
  ALIGNMENT2 = {
3439
3697
  left: AlignmentType.LEFT,
3440
3698
  center: AlignmentType.CENTER,
@@ -3733,10 +3991,13 @@ function canonicalizeRelationshipIds(zip) {
3733
3991
  const rename = new Map(
3734
3992
  ordered.map((id, index) => [id, `rId${highest + 1 + index}`])
3735
3993
  );
3736
- const rewrite = (xml) => xml.replace(/"([^"]+)"/g, (whole, value) => {
3737
- const replacement = rename.get(value);
3738
- return replacement === void 0 ? whole : `"${replacement}"`;
3739
- });
3994
+ const rewrite = (xml) => xml.replace(
3995
+ /(\s[\w:.-]+=)"([^"]*)"/g,
3996
+ (whole, prefix, value) => {
3997
+ const replacement = rename.get(value);
3998
+ return replacement === void 0 ? whole : `${prefix}"${replacement}"`;
3999
+ }
4000
+ );
3740
4001
  updates.push({ name: relsName, xml: rewrite(relsXml) });
3741
4002
  if (partEntry) {
3742
4003
  updates.push({ name: partName, xml: rewrite(partXml) });
@@ -3747,17 +4008,62 @@ function canonicalizeRelationshipIds(zip) {
3747
4008
  if (entry) zip.updateFile(entry, Buffer.from(update.xml, "utf8"));
3748
4009
  }
3749
4010
  }
4011
+ function canonicalizeEmbeddedPackages(zip, generatedAt, depth = 1) {
4012
+ const names = zip.getEntries().map((entry) => entry.entryName).filter((name) => EMBEDDED_OFFICE_PACKAGE.test(name));
4013
+ let changed = false;
4014
+ for (const name of names) {
4015
+ const entry = zip.getEntry(name);
4016
+ if (!entry) continue;
4017
+ let nested;
4018
+ try {
4019
+ nested = new AdmZip(entry.getData());
4020
+ nested.getEntries();
4021
+ } catch {
4022
+ continue;
4023
+ }
4024
+ if (canonicalizeNestedPackage(nested, generatedAt, depth)) {
4025
+ zip.updateFile(entry, nested.toBuffer());
4026
+ changed = true;
4027
+ }
4028
+ }
4029
+ return changed;
4030
+ }
4031
+ function canonicalizeNestedPackage(zip, generatedAt, depth) {
4032
+ let changed = false;
4033
+ const coreProperties3 = zip.getEntry("docProps/core.xml");
4034
+ if (coreProperties3) {
4035
+ const xml = coreProperties3.getData().toString("utf8");
4036
+ const normalized = xml.replace(
4037
+ CORE_TIMESTAMP,
4038
+ `$1${generatedAt.toISOString()}$2`
4039
+ );
4040
+ if (normalized !== xml) {
4041
+ zip.updateFile(coreProperties3, Buffer.from(normalized, "utf8"));
4042
+ changed = true;
4043
+ }
4044
+ }
4045
+ if (depth < MAX_EMBEDDED_DEPTH && canonicalizeEmbeddedPackages(zip, generatedAt, depth + 1)) {
4046
+ changed = true;
4047
+ }
4048
+ const zipTimestamp = toDosTime(generatedAt);
4049
+ for (const entry of zip.getEntries()) {
4050
+ const header = entry.header;
4051
+ if (header.timeval !== zipTimestamp) {
4052
+ header.timeval = zipTimestamp;
4053
+ changed = true;
4054
+ }
4055
+ }
4056
+ return changed;
4057
+ }
3750
4058
  function canonicalizeDocxBuffer(buffer, generatedAt = DEFAULT_GENERATION_DATE) {
3751
4059
  const zip = new AdmZip(buffer);
3752
4060
  normalizeTextCase(zip);
3753
4061
  canonicalizeRelationshipIds(zip);
4062
+ canonicalizeEmbeddedPackages(zip, generatedAt);
3754
4063
  const isoTimestamp = generatedAt.toISOString();
3755
4064
  const coreProperties3 = zip.getEntry("docProps/core.xml");
3756
4065
  if (coreProperties3) {
3757
- const normalized = coreProperties3.getData().toString("utf8").replace(
3758
- /(<dcterms:(?:created|modified)\b[^>]*>)[^<]*(<\/dcterms:(?:created|modified)>)/g,
3759
- `$1${isoTimestamp}$2`
3760
- );
4066
+ const normalized = coreProperties3.getData().toString("utf8").replace(CORE_TIMESTAMP, `$1${isoTimestamp}$2`);
3761
4067
  zip.updateFile(coreProperties3, Buffer.from(normalized, "utf8"));
3762
4068
  }
3763
4069
  const zipTimestamp = toDosTime(generatedAt);
@@ -3770,35 +4076,567 @@ function normalizeDocxCaseBuffer(buffer) {
3770
4076
  const zip = new AdmZip(buffer);
3771
4077
  return normalizeTextCase(zip) ? zip.toBuffer() : buffer;
3772
4078
  }
3773
- var DEFAULT_GENERATION_DATE, STABLE_RELATIONSHIP_ID;
4079
+ var DEFAULT_GENERATION_DATE, STABLE_RELATIONSHIP_ID, CORE_TIMESTAMP, EMBEDDED_OFFICE_PACKAGE, MAX_EMBEDDED_DEPTH;
3774
4080
  var init_packageDocument = __esm({
3775
4081
  "src/utils/packageDocument.ts"() {
3776
4082
  "use strict";
3777
4083
  init_normalizeTextCase();
3778
4084
  DEFAULT_GENERATION_DATE = /* @__PURE__ */ new Date("2000-01-01T00:00:00.000Z");
3779
4085
  STABLE_RELATIONSHIP_ID = /^rId\d+$/;
4086
+ CORE_TIMESTAMP = /(<dcterms:(?:created|modified)\b[^>]*>)[^<]*(<\/dcterms:(?:created|modified)>)/g;
4087
+ EMBEDDED_OFFICE_PACKAGE = /\.(?:docx|pptx|xlsx|xlsm)$/i;
4088
+ MAX_EMBEDDED_DEPTH = 3;
3780
4089
  }
3781
4090
  });
3782
4091
 
3783
4092
  // src/utils/fixFloatingImageIds.ts
3784
4093
  import AdmZip2 from "adm-zip";
4094
+ function compareDrawingParts(a, b) {
4095
+ const ma = DRAWING_PART.exec(a);
4096
+ const mb = DRAWING_PART.exec(b);
4097
+ const rank = RANK.indexOf(ma[1]) - RANK.indexOf(mb[1]);
4098
+ return rank !== 0 ? rank : Number(ma[2] || 0) - Number(mb[2] || 0);
4099
+ }
3785
4100
  function fixFloatingImageIdsInBuffer(buffer) {
3786
4101
  const zip = new AdmZip2(buffer);
3787
- const documentEntry = zip.getEntry("word/document.xml");
3788
- if (!documentEntry) {
4102
+ if (!zip.getEntry("word/document.xml")) {
3789
4103
  throw new Error("document.xml not found in DOCX");
3790
4104
  }
4105
+ const parts = zip.getEntries().filter((entry) => DRAWING_PART.test(entry.entryName)).sort((a, b) => compareDrawingParts(a.entryName, b.entryName));
3791
4106
  let idCounter = 1;
3792
- const documentXml = documentEntry.getData().toString("utf8").replace(/(<wp:docPr\b[^>]*?\s)id="\d+"/g, (_match, prefix) => {
3793
- const newId = idCounter++;
3794
- return `${prefix}id="${newId}"`;
3795
- });
3796
- zip.updateFile(documentEntry, Buffer.from(documentXml, "utf8"));
4107
+ for (const entry of parts) {
4108
+ const xml = entry.getData().toString("utf8");
4109
+ const renumbered = xml.replace(
4110
+ /(<wp:docPr\b[^>]*?\s)id="\d+"/g,
4111
+ (_match, prefix) => `${prefix}id="${idCounter++}"`
4112
+ );
4113
+ zip.updateFile(entry, Buffer.from(renumbered, "utf8"));
4114
+ }
3797
4115
  return zip.toBuffer();
3798
4116
  }
4117
+ var DRAWING_PART, RANK;
3799
4118
  var init_fixFloatingImageIds = __esm({
3800
4119
  "src/utils/fixFloatingImageIds.ts"() {
3801
4120
  "use strict";
4121
+ DRAWING_PART = /^word\/(document|header|footer|footnotes|endnotes|comments)(\d*)\.xml$/;
4122
+ RANK = [
4123
+ "document",
4124
+ "header",
4125
+ "footer",
4126
+ "footnotes",
4127
+ "endnotes",
4128
+ "comments"
4129
+ ];
4130
+ }
4131
+ });
4132
+
4133
+ // src/renderers/docxjs/presetShapes.ts
4134
+ function docxPresetShape(geometry) {
4135
+ const name = Object.hasOwn(OOXML_PRESET_TO_DOCX, geometry) ? OOXML_PRESET_TO_DOCX[geometry] : void 0;
4136
+ if (!name) {
4137
+ throw new Error(
4138
+ `the docxjs renderer has no preset for the OOXML geometry "${geometry}"`
4139
+ );
4140
+ }
4141
+ return name;
4142
+ }
4143
+ function docxLineDash(dash) {
4144
+ const name = Object.hasOwn(OOXML_DASH_TO_DOCX, dash) ? OOXML_DASH_TO_DOCX[dash] : void 0;
4145
+ if (!name) {
4146
+ throw new Error(
4147
+ `the docxjs renderer has no line dash for the OOXML value "${dash}"`
4148
+ );
4149
+ }
4150
+ return name;
4151
+ }
4152
+ var OOXML_PRESET_TO_DOCX, OOXML_DASH_TO_DOCX;
4153
+ var init_presetShapes = __esm({
4154
+ "src/renderers/docxjs/presetShapes.ts"() {
4155
+ "use strict";
4156
+ OOXML_PRESET_TO_DOCX = {
4157
+ line: "line",
4158
+ lineInv: "inverseLine",
4159
+ straightConnector1: "straightConnector",
4160
+ bentConnector2: "elbowConnectorOneBend",
4161
+ bentConnector3: "elbowConnector",
4162
+ bentConnector4: "elbowConnectorThreeBends",
4163
+ bentConnector5: "elbowConnectorFourBends",
4164
+ curvedConnector2: "curvedConnectorOneBend",
4165
+ curvedConnector3: "curvedConnector",
4166
+ curvedConnector4: "curvedConnectorThreeBends",
4167
+ curvedConnector5: "curvedConnectorFourBends",
4168
+ triangle: "triangle",
4169
+ rtTriangle: "rightTriangle",
4170
+ diamond: "diamond",
4171
+ parallelogram: "parallelogram",
4172
+ trapezoid: "trapezoid",
4173
+ nonIsoscelesTrapezoid: "nonIsoscelesTrapezoid",
4174
+ pentagon: "pentagon",
4175
+ hexagon: "hexagon",
4176
+ heptagon: "heptagon",
4177
+ octagon: "octagon",
4178
+ decagon: "decagon",
4179
+ dodecagon: "dodecagon",
4180
+ ellipse: "ellipse",
4181
+ teardrop: "teardrop",
4182
+ pieWedge: "pieWedge",
4183
+ pie: "pie",
4184
+ blockArc: "blockArc",
4185
+ donut: "donut",
4186
+ noSmoking: "noSymbol",
4187
+ chord: "chord",
4188
+ arc: "arc",
4189
+ frame: "frame",
4190
+ halfFrame: "halfFrame",
4191
+ corner: "lShape",
4192
+ diagStripe: "diagonalStripe",
4193
+ plus: "cross",
4194
+ plaque: "plaque",
4195
+ can: "cylinder",
4196
+ cube: "cube",
4197
+ bevel: "beveledRectangle",
4198
+ foldedCorner: "foldedCorner",
4199
+ smileyFace: "smileyFace",
4200
+ heart: "heart",
4201
+ lightningBolt: "lightningBolt",
4202
+ sun: "sun",
4203
+ moon: "moon",
4204
+ cloud: "cloud",
4205
+ leftBracket: "leftBracket",
4206
+ rightBracket: "rightBracket",
4207
+ leftBrace: "leftBrace",
4208
+ rightBrace: "rightBrace",
4209
+ bracketPair: "bracketPair",
4210
+ bracePair: "bracePair",
4211
+ rect: "rectangle",
4212
+ roundRect: "roundedRectangle",
4213
+ round1Rect: "roundedCornerRectangle",
4214
+ round2SameRect: "topRoundedCornersRectangle",
4215
+ round2DiagRect: "diagonalRoundedCornersRectangle",
4216
+ snip1Rect: "snippedCornerRectangle",
4217
+ snip2SameRect: "topSnippedCornersRectangle",
4218
+ snip2DiagRect: "diagonalSnippedCornersRectangle",
4219
+ snipRoundRect: "roundedAndSnippedCornersRectangle",
4220
+ rightArrow: "rightArrow",
4221
+ leftArrow: "leftArrow",
4222
+ upArrow: "upArrow",
4223
+ downArrow: "downArrow",
4224
+ leftRightArrow: "leftRightArrow",
4225
+ upDownArrow: "upDownArrow",
4226
+ quadArrow: "quadArrow",
4227
+ leftRightUpArrow: "leftRightUpArrow",
4228
+ bentArrow: "bentArrow",
4229
+ uturnArrow: "uTurnArrow",
4230
+ leftUpArrow: "leftUpArrow",
4231
+ bentUpArrow: "bentUpArrow",
4232
+ curvedRightArrow: "curvedRightArrow",
4233
+ curvedLeftArrow: "curvedLeftArrow",
4234
+ curvedUpArrow: "curvedUpArrow",
4235
+ curvedDownArrow: "curvedDownArrow",
4236
+ stripedRightArrow: "stripedRightArrow",
4237
+ notchedRightArrow: "notchedRightArrow",
4238
+ homePlate: "pentagonArrow",
4239
+ chevron: "chevron",
4240
+ rightArrowCallout: "rightArrowCallout",
4241
+ downArrowCallout: "downArrowCallout",
4242
+ leftArrowCallout: "leftArrowCallout",
4243
+ upArrowCallout: "upArrowCallout",
4244
+ leftRightArrowCallout: "leftRightArrowCallout",
4245
+ upDownArrowCallout: "upDownArrowCallout",
4246
+ quadArrowCallout: "quadArrowCallout",
4247
+ circularArrow: "circularArrow",
4248
+ leftCircularArrow: "leftCircularArrow",
4249
+ leftRightCircularArrow: "leftRightCircularArrow",
4250
+ swooshArrow: "swooshArrow",
4251
+ mathPlus: "mathPlus",
4252
+ mathMinus: "mathMinus",
4253
+ mathMultiply: "mathMultiply",
4254
+ mathDivide: "mathDivide",
4255
+ mathEqual: "mathEqual",
4256
+ mathNotEqual: "mathNotEqual",
4257
+ flowChartProcess: "flowChartProcess",
4258
+ flowChartAlternateProcess: "flowChartAlternateProcess",
4259
+ flowChartDecision: "flowChartDecision",
4260
+ flowChartInputOutput: "flowChartInputOutput",
4261
+ flowChartPredefinedProcess: "flowChartPredefinedProcess",
4262
+ flowChartInternalStorage: "flowChartInternalStorage",
4263
+ flowChartDocument: "flowChartDocument",
4264
+ flowChartMultidocument: "flowChartMultidocument",
4265
+ flowChartTerminator: "flowChartTerminator",
4266
+ flowChartPreparation: "flowChartPreparation",
4267
+ flowChartManualInput: "flowChartManualInput",
4268
+ flowChartManualOperation: "flowChartManualOperation",
4269
+ flowChartConnector: "flowChartConnector",
4270
+ flowChartOffpageConnector: "flowChartOffpageConnector",
4271
+ flowChartPunchedCard: "flowChartPunchedCard",
4272
+ flowChartPunchedTape: "flowChartPunchedTape",
4273
+ flowChartSummingJunction: "flowChartSummingJunction",
4274
+ flowChartOr: "flowChartOr",
4275
+ flowChartCollate: "flowChartCollate",
4276
+ flowChartSort: "flowChartSort",
4277
+ flowChartExtract: "flowChartExtract",
4278
+ flowChartMerge: "flowChartMerge",
4279
+ flowChartOfflineStorage: "flowChartOfflineStorage",
4280
+ flowChartOnlineStorage: "flowChartOnlineStorage",
4281
+ flowChartDelay: "flowChartDelay",
4282
+ flowChartMagneticTape: "flowChartMagneticTape",
4283
+ flowChartMagneticDisk: "flowChartMagneticDisk",
4284
+ flowChartMagneticDrum: "flowChartMagneticDrum",
4285
+ flowChartDisplay: "flowChartDisplay",
4286
+ irregularSeal1: "explosion12",
4287
+ irregularSeal2: "explosion14",
4288
+ star4: "star4",
4289
+ star5: "star5",
4290
+ star6: "star6",
4291
+ star7: "star7",
4292
+ star8: "star8",
4293
+ star10: "star10",
4294
+ star12: "star12",
4295
+ star16: "star16",
4296
+ star24: "star24",
4297
+ star32: "star32",
4298
+ ribbon2: "ribbonUp",
4299
+ ribbon: "ribbonDown",
4300
+ ellipseRibbon2: "curvedRibbonUp",
4301
+ ellipseRibbon: "curvedRibbonDown",
4302
+ leftRightRibbon: "leftRightRibbon",
4303
+ verticalScroll: "verticalScroll",
4304
+ horizontalScroll: "horizontalScroll",
4305
+ wave: "wave",
4306
+ doubleWave: "doubleWave",
4307
+ wedgeRectCallout: "rectangularCallout",
4308
+ wedgeRoundRectCallout: "roundedRectangularCallout",
4309
+ wedgeEllipseCallout: "ellipticalCallout",
4310
+ cloudCallout: "cloudCallout",
4311
+ borderCallout1: "lineCallout",
4312
+ borderCallout2: "bentLineCallout",
4313
+ borderCallout3: "doubleBentLineCallout",
4314
+ accentCallout1: "lineCalloutWithAccentBar",
4315
+ accentCallout2: "bentLineCalloutWithAccentBar",
4316
+ accentCallout3: "doubleBentLineCalloutWithAccentBar",
4317
+ callout1: "lineCalloutWithNoBorder",
4318
+ callout2: "bentLineCalloutWithNoBorder",
4319
+ callout3: "doubleBentLineCalloutWithNoBorder",
4320
+ accentBorderCallout1: "lineCalloutWithBorderAndAccentBar",
4321
+ accentBorderCallout2: "bentLineCalloutWithBorderAndAccentBar",
4322
+ accentBorderCallout3: "doubleBentLineCalloutWithBorderAndAccentBar",
4323
+ actionButtonBlank: "actionButtonBlank",
4324
+ actionButtonHome: "actionButtonHome",
4325
+ actionButtonHelp: "actionButtonHelp",
4326
+ actionButtonInformation: "actionButtonInformation",
4327
+ actionButtonForwardNext: "actionButtonForwardNext",
4328
+ actionButtonBackPrevious: "actionButtonBackPrevious",
4329
+ actionButtonEnd: "actionButtonEnd",
4330
+ actionButtonBeginning: "actionButtonBeginning",
4331
+ actionButtonReturn: "actionButtonReturn",
4332
+ actionButtonDocument: "actionButtonDocument",
4333
+ actionButtonSound: "actionButtonSound",
4334
+ actionButtonMovie: "actionButtonMovie",
4335
+ gear6: "gear6",
4336
+ gear9: "gear9",
4337
+ funnel: "funnel",
4338
+ cornerTabs: "cornerTabs",
4339
+ squareTabs: "squareTabs",
4340
+ plaqueTabs: "plaqueTabs",
4341
+ chartX: "chartX",
4342
+ chartStar: "chartStar",
4343
+ chartPlus: "chartPlus"
4344
+ };
4345
+ OOXML_DASH_TO_DOCX = {
4346
+ solid: "solid",
4347
+ dot: "dot",
4348
+ dash: "dash",
4349
+ lgDash: "longDash",
4350
+ dashDot: "dashDot",
4351
+ lgDashDot: "longDashDot",
4352
+ lgDashDotDot: "longDashDotDot",
4353
+ sysDash: "shortDash",
4354
+ sysDot: "shortDot",
4355
+ sysDashDot: "shortDashDot",
4356
+ sysDashDotDot: "shortDashDotDot"
4357
+ };
4358
+ }
4359
+ });
4360
+
4361
+ // src/renderers/docxjs/drawingGroup.ts
4362
+ function chooseMarkers(ir) {
4363
+ const strings = [];
4364
+ collectStrings(ir, strings, /* @__PURE__ */ new WeakSet());
4365
+ const unused = (base) => {
4366
+ for (let n = 0; ; n += 1) {
4367
+ const candidate = n === 0 ? base : `${base}-${n}`;
4368
+ if (!strings.some((value) => value.includes(candidate))) return candidate;
4369
+ }
4370
+ };
4371
+ return {
4372
+ noAltText: unused(DEFAULT_MARKERS.noAltText),
4373
+ textBox: unused(DEFAULT_MARKERS.textBox)
4374
+ };
4375
+ }
4376
+ function collectStrings(node, out, seen) {
4377
+ if (typeof node === "string") {
4378
+ out.push(node);
4379
+ return;
4380
+ }
4381
+ if (!node || typeof node !== "object") return;
4382
+ if (node instanceof ArrayBuffer || ArrayBuffer.isView(node)) return;
4383
+ if (seen.has(node)) return;
4384
+ seen.add(node);
4385
+ if (node instanceof Map) {
4386
+ for (const [key, value] of node) {
4387
+ collectStrings(key, out, seen);
4388
+ collectStrings(value, out, seen);
4389
+ }
4390
+ return;
4391
+ }
4392
+ if (node instanceof Set) {
4393
+ for (const value of node) collectStrings(value, out, seen);
4394
+ return;
4395
+ }
4396
+ for (const value of Object.values(node)) collectStrings(value, out, seen);
4397
+ }
4398
+ function createDrawingGroupFactory(shapes2, pictures, markers) {
4399
+ return (group, resources) => new shapes2.ShapeGroupRun(
4400
+ drawingGroupOptions(group, resources, pictures, markers)
4401
+ );
4402
+ }
4403
+ function drawingGroupOptions(group, resources, pictures, markers = DEFAULT_MARKERS) {
4404
+ const canvasWidth = group.canvasWidthEmu;
4405
+ const canvasHeight = group.canvasHeightEmu;
4406
+ const near = (value, edge) => Math.abs(value - edge) <= OVERFLOW_TOLERANCE_EMU;
4407
+ const bounds = unrotatedBounds(group.children);
4408
+ const fillsCanvas = bounds !== void 0 && near(bounds.left, 0) && near(bounds.top, 0) && near(bounds.right, canvasWidth) && near(bounds.bottom, canvasHeight);
4409
+ const union = {
4410
+ left: Math.min(0, bounds?.left ?? 0),
4411
+ top: Math.min(0, bounds?.top ?? 0),
4412
+ right: Math.max(canvasWidth, bounds?.right ?? 0),
4413
+ bottom: Math.max(canvasHeight, bounds?.bottom ?? 0)
4414
+ };
4415
+ const overflows = union.left < -OVERFLOW_TOLERANCE_EMU || union.top < -OVERFLOW_TOLERANCE_EMU || union.right > canvasWidth + OVERFLOW_TOLERANCE_EMU || union.bottom > canvasHeight + OVERFLOW_TOLERANCE_EMU;
4416
+ const widthEmu = overflows ? (union.right - union.left) * group.widthEmu / canvasWidth : group.widthEmu;
4417
+ const heightEmu = overflows ? (union.bottom - union.top) * group.heightEmu / canvasHeight : group.heightEmu;
4418
+ return {
4419
+ children: [
4420
+ ...fillsCanvas ? [] : [canvasPad(canvasWidth, canvasHeight)],
4421
+ ...group.children.map(
4422
+ (child, index) => groupChild(child, index, resources, pictures, markers)
4423
+ )
4424
+ ],
4425
+ transformation: {
4426
+ width: emuToPixels(widthEmu),
4427
+ height: emuToPixels(heightEmu)
4428
+ },
4429
+ // `||`, not `??`: docx treats '' as no description too, and would then
4430
+ // describe the diagram from the child names.
4431
+ altText: { name: "", description: group.altText || markers.noAltText },
4432
+ ...group.floating ? { floating: floatingOptions(group.floating) } : {}
4433
+ };
4434
+ }
4435
+ function groupChild(child, index, resources, pictures, markers) {
4436
+ return child.kind === "picture" ? pictureChild(child, index, pictures) : shapeChild(child, index, resources, markers);
4437
+ }
4438
+ function canvasPad(canvasWidthEmu, canvasHeightEmu) {
4439
+ return {
4440
+ type: "rectangle",
4441
+ transformation: {
4442
+ offset: { left: 0, top: 0 },
4443
+ width: emuToPixels(canvasWidthEmu),
4444
+ height: emuToPixels(canvasHeightEmu)
4445
+ },
4446
+ fill: "none",
4447
+ line: "none",
4448
+ decorative: true,
4449
+ altText: { name: "Canvas" }
4450
+ };
4451
+ }
4452
+ function childTransformation(frame) {
4453
+ const rotation2 = frame.rotationDegrees;
4454
+ return {
4455
+ offset: { left: emuToPixels(frame.xEmu), top: emuToPixels(frame.yEmu) },
4456
+ width: emuToPixels(frame.widthEmu),
4457
+ height: emuToPixels(frame.heightEmu),
4458
+ ...rotation2 !== void 0 && rotation2 % 360 !== 0 ? { rotation: rotation2 } : {},
4459
+ ...frame.flipHorizontal || frame.flipVertical ? {
4460
+ flip: {
4461
+ ...frame.flipHorizontal ? { horizontal: true } : {},
4462
+ ...frame.flipVertical ? { vertical: true } : {}
4463
+ }
4464
+ } : {}
4465
+ };
4466
+ }
4467
+ function shapeChild(shape, index, resources, markers) {
4468
+ const paragraphs = shape.text?.paragraphs ?? [];
4469
+ return {
4470
+ type: docxPresetShape(shape.geometry),
4471
+ transformation: childTransformation(shape.frame),
4472
+ fill: shapeFill(shape.fill),
4473
+ line: shapeLine(shape.outline),
4474
+ altText: {
4475
+ name: shape.name ?? `Shape ${index + 1}`,
4476
+ ...shape.isTextBox ? { title: markers.textBox } : {}
4477
+ },
4478
+ // Never `children: []`: docx would write an empty `w:txbxContent`, which
4479
+ // the schema does not allow.
4480
+ ...paragraphs.length > 0 ? {
4481
+ children: paragraphs.map(
4482
+ (paragraph2) => emitParagraph(paragraph2, resources)
4483
+ )
4484
+ } : {},
4485
+ ...shape.text ? { textOptions: textOptions(shape.text) } : {}
4486
+ };
4487
+ }
4488
+ function pictureChild(picture2, index, pictures) {
4489
+ const crop = picture2.crop;
4490
+ return {
4491
+ type: "picture",
4492
+ image: pictures(picture2.resourceId, {
4493
+ widthEmu: picture2.frame.widthEmu,
4494
+ heightEmu: picture2.frame.heightEmu
4495
+ }),
4496
+ transformation: childTransformation(picture2.frame),
4497
+ ...crop ? {
4498
+ crop: {
4499
+ ...crop.left !== void 0 ? { left: crop.left * 100 } : {},
4500
+ ...crop.top !== void 0 ? { top: crop.top * 100 } : {},
4501
+ ...crop.right !== void 0 ? { right: crop.right * 100 } : {},
4502
+ ...crop.bottom !== void 0 ? { bottom: crop.bottom * 100 } : {}
4503
+ }
4504
+ } : {},
4505
+ altText: {
4506
+ name: picture2.name ?? `Picture ${index + 1}`,
4507
+ ...picture2.altText ? { description: picture2.altText } : {}
4508
+ }
4509
+ };
4510
+ }
4511
+ function shapeFill(fill) {
4512
+ if (!fill || fill.kind === "none") return "none";
4513
+ return {
4514
+ type: "solid",
4515
+ color: fill.color.hex,
4516
+ ...fill.transparencyPercent !== void 0 ? { transparency: clamp(fill.transparencyPercent, 0, 100) } : {}
4517
+ };
4518
+ }
4519
+ function shapeLine(outline) {
4520
+ if (!outline || !outline.color) return "none";
4521
+ return {
4522
+ color: outline.color.hex,
4523
+ // An omitted width is office-open's omitted `w`, a hairline, where docx's
4524
+ // own default would be 1pt.
4525
+ width: outline.widthEmu !== void 0 ? clamp(outline.widthEmu / EMU_PER_POINT, 0, MAX_POINTS) : 0,
4526
+ ...outline.dash ? { dash: docxLineDash(outline.dash) } : {}
4527
+ };
4528
+ }
4529
+ function textOptions(text) {
4530
+ const insets = text.insetsEmu;
4531
+ const points = (emu) => clamp(emu / EMU_PER_POINT, 0, MAX_POINTS);
4532
+ const margins = insets ? {
4533
+ ...insets.top !== void 0 ? { top: points(insets.top) } : {},
4534
+ ...insets.right !== void 0 ? { right: points(insets.right) } : {},
4535
+ ...insets.bottom !== void 0 ? { bottom: points(insets.bottom) } : {},
4536
+ ...insets.left !== void 0 ? { left: points(insets.left) } : {}
4537
+ } : void 0;
4538
+ return {
4539
+ verticalAlignment: VERTICAL_ALIGNMENT[text.anchor ?? "top"],
4540
+ ...margins && Object.keys(margins).length > 0 ? { margins } : {}
4541
+ };
4542
+ }
4543
+ function unrotatedBounds(children) {
4544
+ if (children.length === 0) return void 0;
4545
+ let left = Infinity;
4546
+ let top = Infinity;
4547
+ let right = -Infinity;
4548
+ let bottom = -Infinity;
4549
+ for (const { frame } of children) {
4550
+ left = Math.min(left, frame.xEmu);
4551
+ top = Math.min(top, frame.yEmu);
4552
+ right = Math.max(right, frame.xEmu + frame.widthEmu);
4553
+ bottom = Math.max(bottom, frame.yEmu + frame.heightEmu);
4554
+ }
4555
+ return { left, top, right, bottom };
4556
+ }
4557
+ function clamp(value, low, high) {
4558
+ return Math.min(high, Math.max(low, value));
4559
+ }
4560
+ var DEFAULT_MARKERS, EMU_PER_POINT, MAX_POINTS, OVERFLOW_TOLERANCE_EMU, VERTICAL_ALIGNMENT;
4561
+ var init_drawingGroup = __esm({
4562
+ "src/renderers/docxjs/drawingGroup.ts"() {
4563
+ "use strict";
4564
+ init_units();
4565
+ init_emit();
4566
+ init_presetShapes();
4567
+ DEFAULT_MARKERS = {
4568
+ noAltText: "jto:no-alt-text",
4569
+ textBox: "jto:text-box"
4570
+ };
4571
+ EMU_PER_POINT = 12700;
4572
+ MAX_POINTS = 1584;
4573
+ OVERFLOW_TOLERANCE_EMU = 2;
4574
+ VERTICAL_ALIGNMENT = {
4575
+ top: "top",
4576
+ middle: "center",
4577
+ bottom: "bottom"
4578
+ };
4579
+ }
4580
+ });
4581
+
4582
+ // src/renderers/docxjs/drawingGroupRepair.ts
4583
+ import AdmZip3 from "adm-zip";
4584
+ function repairDrawingGroupsInBuffer(buffer, markers) {
4585
+ const textBox = new RegExp(
4586
+ `(<wps:cNvPr\\b[^>]*?) title="${escapeRegExp2(markers.textBox)}"(\\s*/>)<wps:cNvSpPr/>`,
4587
+ "g"
4588
+ );
4589
+ const noAltText = new RegExp(
4590
+ `(<wp:docPr\\b[^>]*?) descr="${escapeRegExp2(markers.noAltText)}"`,
4591
+ "g"
4592
+ );
4593
+ const zip = new AdmZip3(buffer);
4594
+ const parts = zip.getEntries().filter((entry) => DRAWING_PART.test(entry.entryName)).sort((a, b) => compareDrawingParts(a.entryName, b.entryName));
4595
+ const texts = new Map(
4596
+ parts.map((entry) => [entry, entry.getData().toString("utf8")])
4597
+ );
4598
+ let highest = 0;
4599
+ for (const xml of texts.values()) {
4600
+ for (const match of xml.matchAll(DOC_PR_ID)) {
4601
+ highest = Math.max(highest, Number(match[1]));
4602
+ }
4603
+ for (const match of xml.replace(GROUP, "").matchAll(CNVPR_ID)) {
4604
+ highest = Math.max(highest, Number(match[1]));
4605
+ }
4606
+ }
4607
+ let next = highest + 1;
4608
+ for (const [entry, xml] of texts) {
4609
+ if (!xml.includes("<wpg:wgp")) continue;
4610
+ const repaired = xml.replace(
4611
+ GROUP,
4612
+ (span) => span.replace(CHILD_ID, (_match, head) => `${head}id="${next++}"`).replace(
4613
+ ROTATION,
4614
+ (_match, head, value) => `${head}rot="${Math.round(Number(value))}"`
4615
+ ).replace(textBox, '$1$2<wps:cNvSpPr txBox="1"/>')
4616
+ ).replace(noAltText, "$1");
4617
+ texts.set(entry, repaired);
4618
+ zip.updateFile(entry, Buffer.from(repaired, "utf8"));
4619
+ }
4620
+ for (const [entry, xml] of texts) {
4621
+ if (xml.includes(markers.noAltText) || xml.includes(markers.textBox)) {
4622
+ throw new Error(
4623
+ `the drawing-group repair left a marker in ${entry.entryName}`
4624
+ );
4625
+ }
4626
+ }
4627
+ return zip.toBuffer();
4628
+ }
4629
+ var GROUP, DOC_PR_ID, CNVPR_ID, CHILD_ID, ROTATION, escapeRegExp2;
4630
+ var init_drawingGroupRepair = __esm({
4631
+ "src/renderers/docxjs/drawingGroupRepair.ts"() {
4632
+ "use strict";
4633
+ init_fixFloatingImageIds();
4634
+ GROUP = /<wpg:wgp\b[^>]*>[\s\S]*?<\/wpg:wgp>/g;
4635
+ DOC_PR_ID = /<wp:docPr\b[^>]*?\sid="(\d+)"/g;
4636
+ CNVPR_ID = /<(?:wps|pic|wpg):cNvPr\b[^>]*?\sid="(\d+)"/g;
4637
+ CHILD_ID = /(<(?:wps|pic|wpg):cNvPr\b[^>]*?\s)id="\d+"/g;
4638
+ ROTATION = /(<a:xfrm\b[^>]*?\s)rot="([^"]*)"/g;
4639
+ escapeRegExp2 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3802
4640
  }
3803
4641
  });
3804
4642
 
@@ -3827,18 +4665,32 @@ function createDocxJsRenderer() {
3827
4665
  format: "docx",
3828
4666
  capabilities: DOCXJS_CAPABILITIES,
3829
4667
  async render(ir, renderOptions) {
3830
- const resources = await prepareImages(
4668
+ await loadDocxCharts();
4669
+ const prepared = await prepareImages(
3831
4670
  ir,
3832
4671
  renderOptions?.svgRasterFallback
3833
4672
  );
4673
+ const markers = prepared.drawingGroups > 0 ? chooseMarkers(ir) : void 0;
4674
+ let resources = prepared.images;
4675
+ if (markers) {
4676
+ await shapes.load();
4677
+ resources = Object.assign(prepared.images, {
4678
+ drawingGroup: createDrawingGroupFactory(
4679
+ shapes.get(),
4680
+ prepared.pictureSource,
4681
+ markers
4682
+ )
4683
+ });
4684
+ }
3834
4685
  const document = buildDocument(ir, resources);
3835
4686
  const packed = await Packer.toBuffer(document);
3836
4687
  const fixed = fixFloatingImageIdsInBuffer(packed);
4688
+ const repaired = markers ? repairDrawingGroupsInBuffer(fixed, markers) : fixed;
3837
4689
  if (renderOptions?.deterministic === false)
3838
- return new Uint8Array(normalizeDocxCaseBuffer(fixed));
4690
+ return new Uint8Array(normalizeDocxCaseBuffer(repaired));
3839
4691
  return new Uint8Array(
3840
4692
  canonicalizeDocxBuffer(
3841
- fixed,
4693
+ repaired,
3842
4694
  resolveGenerationDate({
3843
4695
  deterministic: renderOptions?.deterministic,
3844
4696
  generatedAt: renderOptions?.generatedAt
@@ -3851,6 +4703,7 @@ function createDocxJsRenderer() {
3851
4703
  function buildDocument(ir, resources = /* @__PURE__ */ new Map()) {
3852
4704
  return new Document({
3853
4705
  styles: emitStyles(ir.styles),
4706
+ ...ir.theme ? { theme: themeOptions(ir.theme) } : {},
3854
4707
  sections: ir.sections.map(
3855
4708
  (section2, index) => sectionOptions(section2, resources, index === ir.sections.length - 1)
3856
4709
  ),
@@ -3882,6 +4735,21 @@ function buildDocument(ir, resources = /* @__PURE__ */ new Map()) {
3882
4735
  } : {}
3883
4736
  });
3884
4737
  }
4738
+ function themeOptions(theme) {
4739
+ const colors = {};
4740
+ for (const [slot, color] of Object.entries(theme.colors)) {
4741
+ if (color) colors[slot] = color.hex;
4742
+ }
4743
+ const fonts = {
4744
+ ...theme.headingFont ? { headings: theme.headingFont } : {},
4745
+ ...theme.bodyFont ? { body: theme.bodyFont } : {}
4746
+ };
4747
+ return {
4748
+ name: theme.name,
4749
+ ...Object.keys(colors).length > 0 ? { colors } : {},
4750
+ ...Object.keys(fonts).length > 0 ? { fonts } : {}
4751
+ };
4752
+ }
3885
4753
  function noteBodies(notes, resources) {
3886
4754
  const bodies = {};
3887
4755
  for (const note of notes) {
@@ -3919,7 +4787,7 @@ function numberingConfig(numbering) {
3919
4787
  };
3920
4788
  }
3921
4789
  async function prepareImages(ir, svgRasterFallback) {
3922
- const placements = collectImagePlacements(ir);
4790
+ const { placements, drawingGroups } = collectImagePlacements(ir);
3923
4791
  const jobs = [];
3924
4792
  for (const resource of ir.resources) {
3925
4793
  if (resource.kind !== "image" || resource.mediaType !== "svg") continue;
@@ -3967,16 +4835,55 @@ async function prepareImages(ir, svgRasterFallback) {
3967
4835
  });
3968
4836
  });
3969
4837
  }
3970
- return resources;
4838
+ const pictureSource = (resourceId, placement) => {
4839
+ const resource = ir.resources.find(
4840
+ (candidate) => candidate.kind === "image" && candidate.id === resourceId
4841
+ );
4842
+ if (!resource) {
4843
+ throw new Error(`no image was prepared for resource "${resourceId}"`);
4844
+ }
4845
+ const data = Buffer.from(resource.bytes);
4846
+ if (resource.mediaType !== "svg") {
4847
+ return {
4848
+ type: resource.mediaType,
4849
+ data
4850
+ };
4851
+ }
4852
+ const size = `${emuToPixels(placement.widthEmu)}x${emuToPixels(placement.heightEmu)}`;
4853
+ return {
4854
+ type: "svg",
4855
+ data,
4856
+ fallback: {
4857
+ type: "png",
4858
+ data: rasters.get(`${resourceId}:${size}`) ?? data
4859
+ }
4860
+ };
4861
+ };
4862
+ return { images: resources, pictureSource, drawingGroups };
3971
4863
  }
3972
4864
  function collectImagePlacements(ir) {
3973
4865
  const placements = /* @__PURE__ */ new Map();
4866
+ let drawingGroups = 0;
4867
+ const record = (resourceId, widthEmu, heightEmu) => {
4868
+ const key = `${emuToPixels(widthEmu)}x${emuToPixels(heightEmu)}`;
4869
+ const sizes = placements.get(resourceId) ?? /* @__PURE__ */ new Set();
4870
+ sizes.add(key);
4871
+ placements.set(resourceId, sizes);
4872
+ };
3974
4873
  const visitInline = (inline) => {
3975
4874
  if (inline.kind === "image") {
3976
- const key = `${emuToPixels(inline.widthEmu)}x${emuToPixels(inline.heightEmu)}`;
3977
- const sizes = placements.get(inline.resourceId) ?? /* @__PURE__ */ new Set();
3978
- sizes.add(key);
3979
- placements.set(inline.resourceId, sizes);
4875
+ record(inline.resourceId, inline.widthEmu, inline.heightEmu);
4876
+ return;
4877
+ }
4878
+ if (inline.kind === "drawingGroup") {
4879
+ drawingGroups += 1;
4880
+ for (const child of inline.children) {
4881
+ if (child.kind === "picture") {
4882
+ record(child.resourceId, child.frame.widthEmu, child.frame.heightEmu);
4883
+ } else {
4884
+ child.text?.paragraphs.forEach(visitBlock);
4885
+ }
4886
+ }
3980
4887
  return;
3981
4888
  }
3982
4889
  if (inline.kind === "hyperlink" || inline.kind === "revision") {
@@ -4007,7 +4914,7 @@ function collectImagePlacements(ir) {
4007
4914
  for (const note of [...ir.footnotes, ...ir.endnotes]) {
4008
4915
  note.children.forEach(visitBlock);
4009
4916
  }
4010
- return placements;
4917
+ return { placements, drawingGroups };
4011
4918
  }
4012
4919
  function sectionOptions(section2, resources, closesDocument = false) {
4013
4920
  const { page, columns } = section2.properties;
@@ -4120,17 +5027,26 @@ function coreProperties(ir) {
4120
5027
  ...metadata.custom?.length ? { customProperties: metadata.custom } : {}
4121
5028
  };
4122
5029
  }
4123
- var DOCXJS_RENDERER_ID, DOCXJS_CAPABILITIES;
5030
+ var DOCXJS_RENDERER_ID, shapes, DOCXJS_CAPABILITIES;
4124
5031
  var init_docxjs = __esm({
4125
5032
  "src/renderers/docxjs/index.ts"() {
4126
5033
  "use strict";
4127
5034
  init_styles2();
5035
+ init_charts();
4128
5036
  init_imageUtils();
4129
5037
  init_packageDocument();
4130
5038
  init_fixFloatingImageIds();
4131
5039
  init_emit();
5040
+ init_docxSubpath();
5041
+ init_drawingGroup();
5042
+ init_drawingGroupRepair();
4132
5043
  init_units();
4133
5044
  DOCXJS_RENDERER_ID = "docxjs";
5045
+ shapes = docxSubpath(
5046
+ "docx/shapes",
5047
+ "visual",
5048
+ () => import("docx/shapes")
5049
+ );
4134
5050
  DOCXJS_CAPABILITIES = /* @__PURE__ */ new Set([
4135
5051
  "paragraphs",
4136
5052
  "styles",
@@ -4143,8 +5059,10 @@ var init_docxjs = __esm({
4143
5059
  "images",
4144
5060
  "floating-images",
4145
5061
  "svg-images",
5062
+ "charts",
4146
5063
  "text-frames",
4147
5064
  "text-boxes",
5065
+ "drawing-groups",
4148
5066
  "toc",
4149
5067
  "cached-toc",
4150
5068
  "fields",
@@ -4165,183 +5083,331 @@ var init_docxjs = __esm({
4165
5083
  }
4166
5084
  });
4167
5085
 
4168
- // src/utils/chartWorkbook.ts
4169
- import AdmZip3 from "adm-zip";
4170
- import { chartWorkbookParts } from "@json-to-office/shared/rendering";
4171
- import {
4172
- CHART_WORKBOOK_SHEET_NAME,
4173
- categoryReference,
4174
- columnLetter,
4175
- seriesNameReference,
4176
- seriesValueReference
4177
- } from "@json-to-office/shared/rendering";
4178
- function buildChartWorkbook(series) {
4179
- const zip = new AdmZip3();
4180
- for (const [name, content] of chartWorkbookParts(series)) {
4181
- zip.addFile(name, Buffer.from(content, "utf8"));
5086
+ // src/renderers/office-open/chartLook.ts
5087
+ function seriesLook(chartType) {
5088
+ switch (chartType) {
5089
+ case "line":
5090
+ case "scatter":
5091
+ return { marker: MARKER, smooth: false };
5092
+ case "radar":
5093
+ return { marker: MARKER };
5094
+ case "column":
5095
+ case "bar":
5096
+ return { invertIfNegative: false };
5097
+ default:
5098
+ return {};
4182
5099
  }
4183
- for (const entry of zip.getEntries()) {
4184
- entry.header.timeval = WORKBOOK_TIMESTAMP;
5100
+ }
5101
+ function axesLook(chartType) {
5102
+ switch (chartType) {
5103
+ case "column":
5104
+ case "bar":
5105
+ case "line":
5106
+ case "area": {
5107
+ const horizontal = chartType === "bar";
5108
+ return {
5109
+ categoryAxis: {
5110
+ position: horizontal ? "l" : "b",
5111
+ ...NO_TICKS,
5112
+ line: AXIS_LINE
5113
+ },
5114
+ valueAxis: {
5115
+ position: horizontal ? "b" : "l",
5116
+ gridLine: GRIDLINES,
5117
+ ...NO_TICKS,
5118
+ lineVisible: false,
5119
+ crossBetween: chartType === "area" ? "midCat" : "between"
5120
+ }
5121
+ };
5122
+ }
5123
+ case "radar":
5124
+ return {
5125
+ categoryAxis: {
5126
+ position: "b",
5127
+ gridLine: GRIDLINES,
5128
+ ...NO_TICKS,
5129
+ majorTickMark: "cross",
5130
+ line: AXIS_LINE
5131
+ },
5132
+ valueAxis: {
5133
+ position: "l",
5134
+ gridLine: GRIDLINES,
5135
+ ...NO_TICKS,
5136
+ majorTickMark: "cross",
5137
+ line: AXIS_LINE,
5138
+ crossBetween: "between"
5139
+ }
5140
+ };
5141
+ case "scatter": {
5142
+ const line = { ...AXIS_LINE, color: SCATTER_AXIS_TINT };
5143
+ return {
5144
+ categoryAxis: {
5145
+ position: "b",
5146
+ ...NO_TICKS,
5147
+ line,
5148
+ crossBetween: "midCat"
5149
+ },
5150
+ valueAxis: {
5151
+ position: "l",
5152
+ gridLine: GRIDLINES,
5153
+ ...NO_TICKS,
5154
+ line,
5155
+ crossBetween: "midCat"
5156
+ }
5157
+ };
5158
+ }
5159
+ default:
5160
+ return {};
5161
+ }
5162
+ }
5163
+ function plotLook(chart) {
5164
+ const common = {
5165
+ ...axesLook(chart.chartType),
5166
+ autoTitleDeleted: !(chart.title && chart.showTitle !== false),
5167
+ plotAreaUnfilled: true
5168
+ };
5169
+ switch (chart.chartType) {
5170
+ case "column":
5171
+ return { ...common, dataBorder: "none", gapWidth: 219, overlap: -27 };
5172
+ case "bar":
5173
+ return { ...common, dataBorder: "none", gapWidth: 182, overlap: 0 };
5174
+ case "area":
5175
+ return { ...common, dataBorder: "none" };
5176
+ case "line":
5177
+ return { ...common, ...LINE_SERIES, lineMarkers: true };
5178
+ case "radar":
5179
+ return { ...common, ...LINE_SERIES, radarStyle: "marker" };
5180
+ case "scatter": {
5181
+ const labels = chart.series[0]?.labels ?? [];
5182
+ return {
5183
+ ...common,
5184
+ ...LINE_SERIES,
5185
+ lineWidthPoints: 1.5,
5186
+ scatterStyle: "lineMarker",
5187
+ scatterXValues: labels.map((_, index) => scatterX(labels, index))
5188
+ };
5189
+ }
5190
+ case "pie":
5191
+ case "doughnut":
5192
+ return {
5193
+ ...common,
5194
+ dataBorder: { widthPoints: 1.5, color: { scheme: "lt1" } },
5195
+ firstSliceAngle: 0,
5196
+ ...chart.chartType === "doughnut" ? { holeSize: 50 } : {}
5197
+ };
5198
+ default:
5199
+ return common;
4185
5200
  }
4186
- return new Uint8Array(zip.toBuffer());
4187
5201
  }
4188
- var WORKBOOK_TIMESTAMP;
4189
- var init_chartWorkbook = __esm({
4190
- "src/utils/chartWorkbook.ts"() {
5202
+ var TEXT_1_TINT, SCATTER_AXIS_TINT, HAIRLINE_POINTS, GRIDLINES, AXIS_LINE, NO_TICKS, LINE_SERIES, MARKER;
5203
+ var init_chartLook = __esm({
5204
+ "src/renderers/office-open/chartLook.ts"() {
4191
5205
  "use strict";
4192
- init_packageDocument();
4193
- WORKBOOK_TIMESTAMP = toDosTime(DEFAULT_GENERATION_DATE);
5206
+ init_chartValues();
5207
+ TEXT_1_TINT = { scheme: "tx1", lumMod: 15e3, lumOff: 85e3 };
5208
+ SCATTER_AXIS_TINT = {
5209
+ scheme: "tx1",
5210
+ lumMod: 25e3,
5211
+ lumOff: 75e3
5212
+ };
5213
+ HAIRLINE_POINTS = 0.75;
5214
+ GRIDLINES = {
5215
+ size: HAIRLINE_POINTS,
5216
+ color: TEXT_1_TINT
5217
+ };
5218
+ AXIS_LINE = {
5219
+ widthPoints: HAIRLINE_POINTS,
5220
+ color: TEXT_1_TINT
5221
+ };
5222
+ NO_TICKS = {
5223
+ majorTickMark: "none",
5224
+ minorTickMark: "none",
5225
+ tickLabelPosition: "nextTo"
5226
+ };
5227
+ LINE_SERIES = {
5228
+ lineWidthPoints: 2.25,
5229
+ lineCap: "rnd",
5230
+ lineJoin: "round",
5231
+ markerLineWidthPoints: HAIRLINE_POINTS
5232
+ };
5233
+ MARKER = { symbol: "circle", size: 5 };
4194
5234
  }
4195
5235
  });
4196
5236
 
4197
5237
  // src/renderers/office-open/chartParts.ts
4198
5238
  import {
4199
- CHART_WORKBOOK_CONTENT_TYPE,
4200
- chartWorkbookRelsXml,
4201
- matchChartParts,
4202
- spliceChartXml
5239
+ finishChartXml,
5240
+ matchChartParts
4203
5241
  } from "@json-to-office/shared/rendering";
4204
- function spliceInput(chart) {
4205
- const { bold, ...rest } = chart.textFont;
4206
- const textFont = rest;
4207
- const axisTitleFont = { ...rest, bold };
4208
- const chartTitleFont = {
4209
- ...rest,
4210
- fontSize: Math.round(rest.fontSize * CHART_TITLE_SCALE),
4211
- bold: true
5242
+ function chartInput(chart) {
5243
+ const roles = chartTextRoles(chart.textFont);
5244
+ const textFont = roles.text;
5245
+ const axisTitleFont = roles.axisTitle;
5246
+ const chartTitleFont = roles.title;
5247
+ const look = plotLook(chart);
5248
+ const categoryAxis = {
5249
+ ...look.categoryAxis,
5250
+ ...chart.categoryAxisTitle ? { title: chart.categoryAxisTitle, titleFont: axisTitleFont } : {}
5251
+ };
5252
+ const valueAxis = {
5253
+ ...look.valueAxis,
5254
+ ...chart.valueAxisTitle ? { title: chart.valueAxisTitle, titleFont: axisTitleFont } : {}
4212
5255
  };
4213
5256
  return {
5257
+ ...look,
4214
5258
  chartType: chart.chartType,
4215
5259
  series: chart.series,
4216
5260
  colors: chart.colors,
5261
+ ...chart.title && chart.showTitle !== false ? { title: chart.title } : {},
4217
5262
  textFont,
4218
5263
  titleFont: chartTitleFont,
4219
5264
  ...chart.legendPosition ? { legendPosition: chart.legendPosition } : {},
4220
- ...chart.categoryAxisTitle ? {
4221
- categoryAxis: {
4222
- title: chart.categoryAxisTitle,
4223
- titleFont: axisTitleFont
4224
- }
4225
- } : {},
4226
- ...chart.valueAxisTitle ? {
4227
- valueAxis: { title: chart.valueAxisTitle, titleFont: axisTitleFont }
4228
- } : {}
5265
+ ...Object.keys(categoryAxis).length > 0 ? { categoryAxis } : {},
5266
+ ...Object.keys(valueAxis).length > 0 ? { valueAxis } : {}
4229
5267
  };
4230
5268
  }
4231
- function declareWorkbookContentType(zip) {
4232
- const entry = zip.getEntry("[Content_Types].xml");
4233
- if (!entry) return;
4234
- const xml = entry.getData().toString("utf8");
4235
- if (xml.includes(`Extension="xlsx"`)) return;
4236
- zip.updateFile(
4237
- entry,
4238
- Buffer.from(
4239
- xml.replace(
4240
- '<Default Extension="xml"',
4241
- `<Default Extension="xlsx" ContentType="${CHART_WORKBOOK_CONTENT_TYPE}"/><Default Extension="xml"`
4242
- ),
4243
- "utf8"
4244
- )
4245
- );
4246
- }
4247
- function spliceChartParts(zip, charts) {
4248
- if (charts.length === 0) return;
5269
+ function finishChartParts(zip, charts2) {
5270
+ if (charts2.length === 0) return;
4249
5271
  const parts = zip.getEntries().map((entry) => entry.entryName).map((name) => name.match(/^word\/charts\/chart(\d+)\.xml$/)).filter((match) => match !== null).map(
4250
5272
  (match) => [
4251
5273
  Number(match[1]),
4252
5274
  zip.getEntry(match[0]).getData().toString("utf8")
4253
5275
  ]
4254
5276
  );
4255
- for (const { ordinal, xml, chart } of matchChartParts(parts, charts)) {
4256
- const workbookName = `chart${ordinal}.xlsx`;
5277
+ const inputs = charts2.map(chartInput);
5278
+ for (const { ordinal, xml, chart } of matchChartParts(parts, inputs)) {
5279
+ const finished = finishChartXml(xml, chart);
5280
+ if (finished === xml) continue;
4257
5281
  zip.updateFile(
4258
5282
  zip.getEntry(`word/charts/chart${ordinal}.xml`),
4259
- Buffer.from(spliceChartXml(xml, spliceInput(chart)), "utf8")
4260
- );
4261
- zip.addFile(
4262
- `word/embeddings/${workbookName}`,
4263
- Buffer.from(buildChartWorkbook(chart.series))
4264
- );
4265
- zip.addFile(
4266
- `word/charts/_rels/chart${ordinal}.xml.rels`,
4267
- Buffer.from(chartWorkbookRelsXml(workbookName), "utf8")
5283
+ Buffer.from(finished, "utf8")
4268
5284
  );
4269
5285
  }
4270
- declareWorkbookContentType(zip);
4271
5286
  }
4272
- var CHART_TITLE_SCALE;
4273
5287
  var init_chartParts = __esm({
4274
5288
  "src/renderers/office-open/chartParts.ts"() {
4275
5289
  "use strict";
4276
- init_chartWorkbook();
4277
- CHART_TITLE_SCALE = 1.4;
5290
+ init_chartText();
5291
+ init_chartLook();
4278
5292
  }
4279
5293
  });
4280
5294
 
4281
- // src/renderers/office-open/cellTocs.ts
4282
- function spliceCellTocs(zip, tocs, stringifyTableOfContents) {
4283
- if (tocs.length === 0) return;
4284
- const spliced = /* @__PURE__ */ new Set();
4285
- for (const entry of zip.getEntries()) {
4286
- if (!TABLE_PARTS.test(entry.entryName)) continue;
4287
- const original = entry.getData().toString("utf8");
4288
- let xml = original;
4289
- for (const toc of tocs) {
4290
- const start = markerParagraph(xml, toc.start);
4291
- if (!start) continue;
4292
- const end = markerParagraph(xml, toc.end);
4293
- if (spliced.has(toc) || !end || end.from < start.to) {
4294
- throw new Error(
4295
- `the office-open renderer lost track of a table of contents in a table cell (${entry.entryName})`
4296
- );
4297
- }
4298
- xml = xml.slice(0, start.from) + stringifyTableOfContents(
4299
- toc.alias,
4300
- toc.options,
4301
- xml.slice(start.to, end.from)
4302
- ) + xml.slice(end.to);
4303
- spliced.add(toc);
4304
- }
4305
- if (xml !== original) zip.updateFile(entry, Buffer.from(xml, "utf8"));
4306
- }
4307
- if (spliced.size !== tocs.length) {
5295
+ // src/renderers/office-open/themePart.ts
5296
+ function replaceOnce(xml, pattern, replacer, what) {
5297
+ const count = [...xml.matchAll(pattern)].length;
5298
+ if (count !== 1) {
4308
5299
  throw new Error(
4309
- `the office-open renderer placed ${tocs.length} table(s) of contents in table cells and found ${spliced.size}`
5300
+ `office-open's theme part has ${count} ${what}, expected one`
4310
5301
  );
4311
5302
  }
5303
+ return xml.replace(pattern, replacer);
5304
+ }
5305
+ function spliceTheme(zip, theme) {
5306
+ const entry = zip.getEntry(THEME_PART);
5307
+ if (!entry) throw new Error(`office-open wrote no ${THEME_PART}`);
5308
+ let xml = entry.getData().toString("utf8");
5309
+ const name = esc(theme.name);
5310
+ xml = renamed(xml, "theme", name);
5311
+ const colors = Object.entries(theme.colors);
5312
+ if (colors.some(([, color]) => color)) {
5313
+ xml = renamed(xml, "clrScheme", name);
5314
+ for (const [slot, color] of colors) {
5315
+ if (!color) continue;
5316
+ const element = SCHEME_ELEMENT[slot];
5317
+ xml = replaceOnce(
5318
+ xml,
5319
+ new RegExp(`<a:${element}>[\\s\\S]*?</a:${element}>`, "g"),
5320
+ () => `<a:${element}><a:srgbClr val="${color.hex}"/></a:${element}>`,
5321
+ `a:${element}`
5322
+ );
5323
+ }
5324
+ }
5325
+ if (theme.headingFont || theme.bodyFont) {
5326
+ xml = renamed(xml, "fontScheme", name);
5327
+ for (const [collection, key, officeFace] of OFFICE_FACES) {
5328
+ const family = theme[key];
5329
+ if (family === void 0 || family === officeFace) continue;
5330
+ xml = replaceOnce(
5331
+ xml,
5332
+ new RegExp(`(<a:${collection}>)<a:latin\\b[^>]*/>`, "g"),
5333
+ (_match, open) => `${open}<a:latin typeface="${esc(family)}"/>`,
5334
+ `a:${collection} a:latin`
5335
+ );
5336
+ }
5337
+ }
5338
+ zip.updateFile(THEME_PART, Buffer.from(xml, "utf8"));
4312
5339
  }
4313
- function markerParagraph(xml, marker) {
4314
- const at = xml.indexOf(marker);
4315
- if (at < 0) return void 0;
4316
- const from = Math.max(
4317
- xml.lastIndexOf("<w:p>", at),
4318
- xml.lastIndexOf("<w:p ", at)
4319
- );
4320
- const close = xml.indexOf("</w:p>", at);
4321
- if (xml.indexOf(marker, at + marker.length) >= 0 || from < 0 || close < 0 || xml.lastIndexOf("</w:p>", at) > from) {
4322
- throw new Error(
4323
- "the office-open renderer found a table-of-contents marker it did not write"
5340
+ var THEME_PART, SCHEME_ELEMENT, OFFICE_FACES, esc, renamed;
5341
+ var init_themePart = __esm({
5342
+ "src/renderers/office-open/themePart.ts"() {
5343
+ "use strict";
5344
+ THEME_PART = "word/theme/theme1.xml";
5345
+ SCHEME_ELEMENT = {
5346
+ dark1: "dk1",
5347
+ light1: "lt1",
5348
+ dark2: "dk2",
5349
+ light2: "lt2",
5350
+ accent1: "accent1",
5351
+ accent2: "accent2",
5352
+ accent3: "accent3",
5353
+ accent4: "accent4",
5354
+ accent5: "accent5",
5355
+ accent6: "accent6",
5356
+ hyperlink: "hlink",
5357
+ followedHyperlink: "folHlink"
5358
+ };
5359
+ OFFICE_FACES = [
5360
+ ["majorFont", "headingFont", "Calibri Light"],
5361
+ ["minorFont", "bodyFont", "Calibri"]
5362
+ ];
5363
+ esc = (value) => value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5364
+ renamed = (xml, element, name) => replaceOnce(
5365
+ xml,
5366
+ new RegExp(`(<a:${element}\\b[^>]*?\\bname=")[^"]*(")`, "g"),
5367
+ (_match, open, close) => `${open}${name}${close}`,
5368
+ `a:${element} name`
4324
5369
  );
4325
5370
  }
4326
- return { from, to: close + "</w:p>".length };
5371
+ });
5372
+
5373
+ // src/utils/chartWorkbook.ts
5374
+ import AdmZip4 from "adm-zip";
5375
+ import { chartWorkbookParts } from "@json-to-office/shared/rendering";
5376
+ import {
5377
+ CHART_WORKBOOK_SHEET_NAME,
5378
+ categoryReference,
5379
+ columnLetter,
5380
+ seriesNameReference,
5381
+ seriesValueReference
5382
+ } from "@json-to-office/shared/rendering";
5383
+ function buildChartWorkbook(series, options = {}) {
5384
+ const zip = new AdmZip4();
5385
+ for (const [name, content] of chartWorkbookParts(series, options)) {
5386
+ zip.addFile(name, Buffer.from(content, "utf8"));
5387
+ }
5388
+ for (const entry of zip.getEntries()) {
5389
+ entry.header.timeval = WORKBOOK_TIMESTAMP;
5390
+ }
5391
+ return new Uint8Array(zip.toBuffer());
4327
5392
  }
4328
- var TABLE_PARTS;
4329
- var init_cellTocs = __esm({
4330
- "src/renderers/office-open/cellTocs.ts"() {
5393
+ var WORKBOOK_TIMESTAMP;
5394
+ var init_chartWorkbook = __esm({
5395
+ "src/utils/chartWorkbook.ts"() {
4331
5396
  "use strict";
4332
- TABLE_PARTS = /^word\/(?:document|header\d+|footer\d+)\.xml$/;
5397
+ init_packageDocument();
5398
+ WORKBOOK_TIMESTAMP = toDosTime(DEFAULT_GENERATION_DATE);
4333
5399
  }
4334
5400
  });
4335
5401
 
4336
5402
  // src/renderers/office-open/emit.ts
4337
- import { assertNever as assertNever2 } from "@json-to-office/shared/rendering";
5403
+ import { assertNever as assertNever3 } from "@json-to-office/shared/rendering";
5404
+ import { chartLook, withSeriesLook } from "@json-to-office/shared/rendering";
4338
5405
  function emptyContext() {
4339
5406
  let next = 1;
4340
5407
  return {
4341
5408
  pictures: /* @__PURE__ */ new Map(),
4342
5409
  nextDrawingId: () => next++,
4343
- charts: [],
4344
- cellTocs: []
5410
+ charts: []
4345
5411
  };
4346
5412
  }
4347
5413
  function simpleField(instruction, cachedText) {
@@ -4382,18 +5448,19 @@ function runProperties2(formatting) {
4382
5448
  if (formatting.doubleStrike !== void 0) {
4383
5449
  out.doubleStrike = formatting.doubleStrike;
4384
5450
  }
4385
- if (formatting.superScript) out.superScript = true;
4386
- if (formatting.subScript) out.subScript = true;
5451
+ if (formatting.superScript) out.verticalAlign = "superscript";
5452
+ if (formatting.subScript) out.verticalAlign = "subscript";
4387
5453
  if (formatting.smallCaps !== void 0) out.smallCaps = formatting.smallCaps;
4388
5454
  if (formatting.allCaps !== void 0) out.allCaps = formatting.allCaps;
4389
- if (formatting.highlight !== void 0) out.highlight = formatting.highlight;
5455
+ if (formatting.highlight !== void 0) {
5456
+ out.highlight = formatting.highlight;
5457
+ }
4390
5458
  if (formatting.shading) out.shading = shading(formatting.shading);
4391
5459
  if (formatting.scalePercent !== void 0)
4392
5460
  out.scale = formatting.scalePercent;
4393
- if (formatting.characterSpacingTwentieths !== void 0) {
4394
- const stated = formatting.characterSpacingTwentieths;
4395
- const twentieths = twips(stated);
4396
- out.characterSpacing = twentieths === 0 && stated !== 0 ? "0pt" : twentieths;
5461
+ const stated = formatting.characterSpacingTwentieths;
5462
+ if (stated !== void 0 && stated !== 0) {
5463
+ out.characterSpacing = twips(stated);
4397
5464
  }
4398
5465
  if (formatting.language) out.language = { value: formatting.language };
4399
5466
  if (formatting.noProof !== void 0) out.noProof = formatting.noProof;
@@ -4423,7 +5490,9 @@ function inlineChildren2(children, ctx = emptyContext()) {
4423
5490
  switch (child.kind) {
4424
5491
  case "lineBreak":
4425
5492
  pendingBreaks += 1;
4426
- if (child.clear && child.clear !== "none") pendingClear = child.clear;
5493
+ if (child.clear && child.clear !== "none") {
5494
+ pendingClear = child.clear;
5495
+ }
4427
5496
  break;
4428
5497
  case "text":
4429
5498
  out.push({
@@ -4461,7 +5530,7 @@ function inlineChildren2(children, ctx = emptyContext()) {
4461
5530
  case "drawingGroup": {
4462
5531
  const pending = breakOption();
4463
5532
  if (pending.break) out.push(pending);
4464
- out.push({ wpgGroup: drawingGroupOptions(child, ctx) });
5533
+ out.push({ wpgGroup: drawingGroupOptions2(child, ctx) });
4465
5534
  break;
4466
5535
  }
4467
5536
  case "hyperlink":
@@ -4491,8 +5560,9 @@ function inlineChildren2(children, ctx = emptyContext()) {
4491
5560
  out.push({ wpsShape: shapeOptions(child, ctx) });
4492
5561
  break;
4493
5562
  case "chart":
4494
- ctx.charts?.push(child);
4495
- out.push({ chart: chartOptions(child, ctx) });
5563
+ out.push({
5564
+ chart: chartOptions(child, ctx, ctx.charts?.push(child) ?? 1)
5565
+ });
4496
5566
  break;
4497
5567
  case "noteReference":
4498
5568
  out.push(
@@ -4500,7 +5570,7 @@ function inlineChildren2(children, ctx = emptyContext()) {
4500
5570
  );
4501
5571
  break;
4502
5572
  default:
4503
- assertNever2(child, "DocxIrInline");
5573
+ assertNever3(child, "DocxIrInline");
4504
5574
  }
4505
5575
  }
4506
5576
  return out;
@@ -4541,8 +5611,11 @@ function floatingOptions2(floating) {
4541
5611
  ...floating.vertical ? { verticalPosition: position(floating.vertical) } : {},
4542
5612
  ...floating.wrap ? {
4543
5613
  wrap: {
4544
- type: WRAP_TYPE2[floating.wrap.type],
4545
- ...floating.wrap.side ? { side: floating.wrap.side } : {}
5614
+ // Named, as OOXML names them; 0.11 took docx.js's numbers.
5615
+ type: floating.wrap.type,
5616
+ ...floating.wrap.side ? {
5617
+ side: floating.wrap.side
5618
+ } : {}
4546
5619
  }
4547
5620
  } : {},
4548
5621
  ...floating.margins ? {
@@ -4569,66 +5642,92 @@ function imageMedia(ctx, resourceId, placement) {
4569
5642
  }
4570
5643
  function pictureOptions(image, ctx) {
4571
5644
  const media = imageMedia(ctx, image.resourceId, image);
4572
- return {
4573
- type: media.type,
5645
+ const source = media.type === "svg" ? {
5646
+ type: "svg",
4574
5647
  data: media.data,
4575
- // No `fileName`: at run level the backend allocates one, and stating our
4576
- // own would only fight it.
4577
- ...media.fallback ? { fallback: media.fallback } : {},
5648
+ // The SVG slot always carries a raster: `prepareImages` falls back
5649
+ // to the vector bytes when none could be made.
5650
+ fallback: media.fallback ?? {
5651
+ type: "png",
5652
+ data: media.data
5653
+ }
5654
+ } : { type: media.type, data: media.data };
5655
+ return {
5656
+ ...source,
4578
5657
  // Raw numbers are EMUs in @office-open/docx. Passing pixels here makes a
4579
5658
  // normal image only a few hundred EMUs wide, effectively a dot.
4580
5659
  transformation: { width: image.widthEmu, height: image.heightEmu },
4581
5660
  // The id is stated rather than left to the backend's process-global
4582
- // counter. `name` stays empty, which is what it was before and what the
4583
- // backend falls back to.
4584
- altText: { id: String(ctx.nextDrawingId()) },
5661
+ // counter. `name` stays empty, which is what the backend falls back to.
5662
+ altText: { id: String(ctx.nextDrawingId()), name: "" },
4585
5663
  ...image.floating ? { floating: floatingOptions2(image.floating) } : {}
4586
5664
  // No `description` or `title`: no DOCX this pipeline has produced carries
4587
5665
  // `wp:docPr` alt text, and the compiler warns so the gap is visible rather
4588
5666
  // than silent.
4589
5667
  };
4590
5668
  }
4591
- function drawingGroupOptions(group, ctx) {
5669
+ function drawingGroupOptions2(group, ctx) {
4592
5670
  const id = ctx.nextDrawingId();
4593
5671
  return {
4594
5672
  altText: {
4595
5673
  id: String(id),
5674
+ name: "",
4596
5675
  ...group.altText ? { description: group.altText } : {}
4597
5676
  },
4598
5677
  transformation: { width: group.widthEmu, height: group.heightEmu },
4599
- childOffset: { x: 0, y: 0 },
4600
- childExtent: { cx: group.canvasWidthEmu, cy: group.canvasHeightEmu },
4601
- children: group.children.map((child) => groupChild(child, ctx)),
5678
+ childOffsetX: 0,
5679
+ childOffsetY: 0,
5680
+ childExtentWidth: group.canvasWidthEmu,
5681
+ childExtentHeight: group.canvasHeightEmu,
5682
+ children: group.children.map((child) => groupChild2(child, ctx)),
4602
5683
  ...group.floating ? { floating: floatingOptions2(group.floating) } : {}
4603
5684
  };
4604
5685
  }
4605
- function chartOptions(chart, ctx) {
5686
+ function chartOptions(chart, ctx, ordinal) {
4606
5687
  const id = ctx.nextDrawingId();
5688
+ const input = chartInput(chart);
5689
+ const look = chartLook(input);
4607
5690
  return {
4608
5691
  type: chart.chartType,
4609
5692
  categories: chart.series[0]?.labels ?? [],
4610
- series: chart.series.map((entry, index) => ({
4611
- name: entry.name ?? `Series ${index + 1}`,
4612
- values: entry.values
4613
- })),
4614
- ...chart.title && chart.showTitle !== false ? { title: chart.title } : {},
5693
+ series: chart.series.map(
5694
+ (entry, index) => withSeriesLook(
5695
+ {
5696
+ name: entry.name ?? `Series ${index + 1}`,
5697
+ values: entry.values,
5698
+ ...seriesLook(chart.chartType)
5699
+ },
5700
+ look.series[index]
5701
+ )
5702
+ ),
5703
+ ...look.chart,
4615
5704
  ...chart.showLegend !== void 0 ? { showLegend: chart.showLegend } : {},
5705
+ externalData: {
5706
+ ...look.chart.externalData,
5707
+ fileName: `chart${ordinal}.xlsx`,
5708
+ // A scatter chart's column A holds the x values its `c:xVal` caches.
5709
+ data: buildChartWorkbook(
5710
+ chart.series,
5711
+ input.scatterXValues ? { categoryValues: input.scatterXValues } : {}
5712
+ )
5713
+ },
4616
5714
  transformation: { width: chart.widthEmu, height: chart.heightEmu },
4617
5715
  altText: {
4618
5716
  id: String(id),
5717
+ name: "",
4619
5718
  ...chart.altText ? { description: chart.altText } : {}
4620
5719
  },
4621
5720
  ...chart.floating ? { floating: floatingOptions2(chart.floating) } : {}
4622
5721
  };
4623
5722
  }
4624
- function groupChild(child, ctx) {
5723
+ function groupChild2(child, ctx) {
4625
5724
  return child.kind === "shape" ? groupShape(child, ctx) : groupPicture(child, ctx);
4626
5725
  }
4627
5726
  function groupShape(shape, ctx) {
4628
5727
  const id = ctx.nextDrawingId();
4629
5728
  return {
4630
5729
  type: "wps",
4631
- transformation: childTransformation(shape.frame),
5730
+ transformation: childTransformation2(shape.frame),
4632
5731
  data: {
4633
5732
  nonVisualProperties: {
4634
5733
  id,
@@ -4637,7 +5736,7 @@ function groupShape(shape, ctx) {
4637
5736
  // to hold text, and it changes how the object behaves on selection.
4638
5737
  ...shape.isTextBox ? { textBox: "1" } : {}
4639
5738
  },
4640
- presetGeometry: { preset: shape.geometry },
5739
+ geometry: shape.geometry,
4641
5740
  ...shape.fill ? { fill: drawingFill(shape.fill) } : {},
4642
5741
  ...shape.outline ? { outline: drawingOutline(shape.outline) } : {},
4643
5742
  children: (shape.text?.paragraphs ?? []).map(
@@ -4663,7 +5762,7 @@ function groupPicture(picture2, ctx) {
4663
5762
  fileName: media.fallbackFileName
4664
5763
  }
4665
5764
  } : {},
4666
- transformation: childTransformation(picture2.frame),
5765
+ transformation: childTransformation2(picture2.frame),
4667
5766
  nonVisualProperties: {
4668
5767
  id,
4669
5768
  ...picture2.name ? { name: picture2.name } : {},
@@ -4672,11 +5771,7 @@ function groupPicture(picture2, ctx) {
4672
5771
  ...picture2.crop ? { sourceRectangle: sourceRectangle(picture2.crop) } : {}
4673
5772
  };
4674
5773
  }
4675
- function childTransformation(frame) {
4676
- const flip = {
4677
- ...frame.flipHorizontal ? { horizontal: true } : {},
4678
- ...frame.flipVertical ? { vertical: true } : {}
4679
- };
5774
+ function childTransformation2(frame) {
4680
5775
  return {
4681
5776
  offset: {
4682
5777
  emus: { x: frame.xEmu, y: frame.yEmu },
@@ -4692,7 +5787,8 @@ function childTransformation(frame) {
4692
5787
  },
4693
5788
  // Degrees: the backend multiplies by 60000 on the way into `@rot`.
4694
5789
  ...frame.rotationDegrees !== void 0 ? { rotation: frame.rotationDegrees } : {},
4695
- ...Object.keys(flip).length > 0 ? { flip } : {}
5790
+ ...frame.flipHorizontal ? { flipHorizontal: true } : {},
5791
+ ...frame.flipVertical ? { flipVertical: true } : {}
4696
5792
  };
4697
5793
  }
4698
5794
  function drawingFill(fill) {
@@ -4700,7 +5796,6 @@ function drawingFill(fill) {
4700
5796
  return {
4701
5797
  type: "solid",
4702
5798
  color: {
4703
- type: "rgb",
4704
5799
  value: fill.color.hex,
4705
5800
  // Transparency becomes an alpha transform on the colour. DrawingML
4706
5801
  // states *opacity*, so the value is the complement of what the author
@@ -4742,7 +5837,7 @@ function sourceRectangle(crop) {
4742
5837
  function shapeOptions(shape, ctx) {
4743
5838
  const id = String(ctx.nextDrawingId());
4744
5839
  return {
4745
- altText: { id },
5840
+ altText: { id, name: "" },
4746
5841
  children: shape.children.map((child) => paragraph(child, ctx)),
4747
5842
  // Raw transformation numbers are EMUs in @office-open/docx, while the IR
4748
5843
  // deliberately stores native shape dimensions in pixels.
@@ -4753,7 +5848,7 @@ function shapeOptions(shape, ctx) {
4753
5848
  ...shape.fill ? {
4754
5849
  fill: {
4755
5850
  type: "solid",
4756
- color: { type: "rgb", value: shape.fill.hex }
5851
+ color: { value: shape.fill.hex }
4757
5852
  }
4758
5853
  } : {},
4759
5854
  ...shape.outline ? { outline: drawingOutline(shape.outline) } : {},
@@ -4858,13 +5953,19 @@ function frameOptions2(frame) {
4858
5953
  ...frame.anchorLock !== void 0 ? { anchorLock: frame.anchorLock } : {},
4859
5954
  ...frame.rule ? { rule: frame.rule } : {}
4860
5955
  };
4861
- return frame.xTwips !== void 0 || frame.yTwips !== void 0 ? {
4862
- type: "absolute",
4863
- position: { x: frame.xTwips ?? 0, y: frame.yTwips ?? 0 },
4864
- ...base
4865
- } : {
5956
+ if (frame.xTwips !== void 0 || frame.yTwips !== void 0) {
5957
+ return {
5958
+ type: "absolute",
5959
+ position: { x: frame.xTwips ?? 0, y: frame.yTwips ?? 0 },
5960
+ ...base
5961
+ };
5962
+ }
5963
+ return {
4866
5964
  type: "alignment",
4867
- alignment: { x: frame.xAlign, y: frame.yAlign },
5965
+ alignment: {
5966
+ x: frame.xAlign,
5967
+ y: frame.yAlign
5968
+ },
4868
5969
  ...base
4869
5970
  };
4870
5971
  }
@@ -4885,7 +5986,7 @@ function block(value, ctx = emptyContext()) {
4885
5986
  case "toc":
4886
5987
  return { toc: tableOfContents(value) };
4887
5988
  default:
4888
- return assertNever2(value, "DocxIrBlock");
5989
+ return assertNever3(value, "DocxIrBlock");
4889
5990
  }
4890
5991
  }
4891
5992
  function tocEntry(entry) {
@@ -4904,6 +6005,7 @@ function tableOfContents(value) {
4904
6005
  headingStyleRange: `${value.headingRange.from}-${value.headingRange.to}`
4905
6006
  } : {},
4906
6007
  ...value.styleLevels?.length ? {
6008
+ // `StyleLevel` is a class the backend only reads the two fields of.
4907
6009
  stylesWithLevels: value.styleLevels.map((style) => ({
4908
6010
  styleName: style.styleName,
4909
6011
  level: style.level
@@ -4925,14 +6027,16 @@ function table(value, ctx) {
4925
6027
  // unstated columns comes out fractional, and the XML would carry it
4926
6028
  // verbatim; floored here, as the docxjs backend floors it.
4927
6029
  size: value.width.kind === "auto" ? 0 : value.width.kind === "twips" ? twips(value.width.value) : value.width.value,
4928
- type: value.width.kind === "twips" ? "dxa" : value.width.kind === "percent" ? "pct" : "auto"
6030
+ type: value.width.kind === "twips" ? "twips" : value.width.kind === "percent" ? "percent" : "auto"
4929
6031
  },
4930
6032
  layout: value.layout,
4931
6033
  // An empty grid is a table with nothing to say about its columns, which is
4932
6034
  // not the same as one whose columns are all zero wide. The compiler hands
4933
6035
  // over every grid in twips; floored for the same reason as the width.
4934
6036
  ...value.columnGrid.values.length > 0 ? { columnWidths: value.columnGrid.values.map(twips) } : {},
4935
- ...value.alignment ? { alignment: alignment(value.alignment) } : {},
6037
+ ...value.alignment ? {
6038
+ alignment: alignment(value.alignment)
6039
+ } : {},
4936
6040
  ...value.borders ? { borders: borders2(value.borders) } : {},
4937
6041
  ...value.cellMargins ? { margins: cellMargins(value.cellMargins) } : {},
4938
6042
  ...value.floating ? { float: tableFloat2(value.floating) } : {}
@@ -4977,52 +6081,29 @@ function tableRow(row, ctx) {
4977
6081
  }
4978
6082
  function tableCell(cell, ctx) {
4979
6083
  const children = cellChildren2(cell, ctx);
4980
- if (cell.children[cell.children.length - 1]?.kind !== "paragraph")
6084
+ if (cell.children[cell.children.length - 1]?.kind === "toc")
4981
6085
  children.push({ paragraph: { children: [] } });
4982
6086
  return {
4983
6087
  children,
4984
- ...cell.widthTwips !== void 0 ? { width: { size: twips(cell.widthTwips), type: "dxa" } } : {},
4985
- ...cell.verticalAlign ? { verticalAlign: cell.verticalAlign } : {},
6088
+ ...cell.widthTwips !== void 0 ? { width: { size: twips(cell.widthTwips), type: "twips" } } : {},
6089
+ ...cell.verticalAlign ? {
6090
+ verticalAlign: cell.verticalAlign
6091
+ } : {},
4986
6092
  ...cell.shading ? { shading: shading(cell.shading) } : {},
4987
6093
  ...cell.margins ? { margins: cellMargins(cell.margins) } : {},
4988
6094
  ...cell.borders ? { borders: borders2(cell.borders) } : {},
4989
6095
  ...cell.columnSpan !== void 0 ? { columnSpan: cell.columnSpan } : {},
4990
6096
  ...cell.rowSpan !== void 0 ? { verticalMerge: cell.rowSpan } : {},
4991
- ...cell.textDirection ? { textDirection: cell.textDirection } : {}
6097
+ ...cell.textDirection ? {
6098
+ textDirection: cell.textDirection
6099
+ } : {}
4992
6100
  };
4993
6101
  }
4994
6102
  function cellChildren2(cell, ctx) {
4995
- const children = [];
4996
- for (const child of cell.children) {
4997
- if (child.kind !== "toc") {
4998
- children.push(block(child, ctx));
4999
- continue;
5000
- }
5001
- const { alias, entries, ...options } = tableOfContents(child);
5002
- const index = ctx.cellTocs.length;
5003
- const toc = {
5004
- start: cellTocMarker(index, "start"),
5005
- end: cellTocMarker(index, "end"),
5006
- alias: String(alias),
5007
- options
5008
- };
5009
- ctx.cellTocs.push(toc);
5010
- children.push(
5011
- markerParagraph2(toc.start),
5012
- ...entries ?? [],
5013
- markerParagraph2(toc.end)
5014
- );
5015
- }
5016
- return children;
5017
- }
5018
- function cellTocMarker(index, edge) {
5019
- return `\uE000jto-cell-toc-${index}-${edge}\uE000`;
5020
- }
5021
- function markerParagraph2(text) {
5022
- return { paragraph: { children: [{ text }] } };
6103
+ return cell.children.map((child) => block(child, ctx));
5023
6104
  }
5024
6105
  function cellMargins(margins) {
5025
- const side = (value) => value === void 0 ? void 0 : { size: twips(value), type: "dxa" };
6106
+ const side = (value) => value === void 0 ? void 0 : { size: twips(value), type: "twips" };
5026
6107
  const out = {};
5027
6108
  for (const [name, value] of [
5028
6109
  ["top", margins.topTwips],
@@ -5065,39 +6146,43 @@ function section(value, ctx, closesDocument = false) {
5065
6146
  properties: {
5066
6147
  ...value.properties.type ? { type: value.properties.type } : {},
5067
6148
  ...value.properties.titlePage !== void 0 ? { titlePage: value.properties.titlePage } : {},
5068
- page: {
5069
- // Orientation is implied by the width/height pair, which is how this
5070
- // pipeline has always expressed it; stating it as well changes `w:pgSz`.
5071
- size: {
5072
- width: twips(page.widthTwips),
5073
- height: twips(page.heightTwips),
5074
- ...page.code !== void 0 ? { code: page.code } : {}
5075
- },
5076
- margin: {
5077
- top: twips(page.margins.topTwips),
5078
- right: twips(page.margins.rightTwips),
5079
- bottom: twips(page.margins.bottomTwips),
5080
- left: twips(page.margins.leftTwips),
5081
- ...page.margins.headerTwips !== void 0 ? { header: twips(page.margins.headerTwips) } : {},
5082
- ...page.margins.footerTwips !== void 0 ? { footer: twips(page.margins.footerTwips) } : {},
5083
- ...page.margins.gutterTwips !== void 0 ? { gutter: twips(page.margins.gutterTwips) } : {}
5084
- },
5085
- ...value.properties.pageNumbers ? {
5086
- pageNumbers: {
5087
- ...value.properties.pageNumbers.start !== void 0 ? { start: value.properties.pageNumbers.start } : {},
5088
- ...value.properties.pageNumbers.formatType ? { formatType: value.properties.pageNumbers.formatType } : {}
5089
- }
5090
- } : {},
5091
- ...value.properties.borders ? {
5092
- borders: {
5093
- ...value.properties.borders.display ? { display: value.properties.borders.display } : {},
5094
- ...value.properties.borders.offsetFrom ? { offsetFrom: value.properties.borders.offsetFrom } : {},
5095
- ...value.properties.borders.borders ? borders2(value.properties.borders.borders) : {}
5096
- }
5097
- } : {}
6149
+ // Orientation is implied by the width/height pair, which is how this
6150
+ // pipeline has always expressed it; stating it as well changes `w:pgSz`.
6151
+ pageSize: {
6152
+ width: twips(page.widthTwips),
6153
+ height: twips(page.heightTwips),
6154
+ ...page.code !== void 0 ? { code: page.code } : {}
6155
+ },
6156
+ pageMargin: {
6157
+ top: twips(page.margins.topTwips),
6158
+ right: twips(page.margins.rightTwips),
6159
+ bottom: twips(page.margins.bottomTwips),
6160
+ left: twips(page.margins.leftTwips),
6161
+ ...page.margins.headerTwips !== void 0 ? { header: twips(page.margins.headerTwips) } : {},
6162
+ ...page.margins.footerTwips !== void 0 ? { footer: twips(page.margins.footerTwips) } : {},
6163
+ ...page.margins.gutterTwips !== void 0 ? { gutter: twips(page.margins.gutterTwips) } : {}
5098
6164
  },
6165
+ ...value.properties.pageNumbers ? {
6166
+ pageNumberType: {
6167
+ ...value.properties.pageNumbers.start !== void 0 ? { start: value.properties.pageNumbers.start } : {},
6168
+ ...value.properties.pageNumbers.formatType ? {
6169
+ format: value.properties.pageNumbers.formatType
6170
+ } : {}
6171
+ }
6172
+ } : {},
6173
+ ...value.properties.borders ? {
6174
+ pageBorders: {
6175
+ ...value.properties.borders.display ? {
6176
+ display: value.properties.borders.display
6177
+ } : {},
6178
+ ...value.properties.borders.offsetFrom ? {
6179
+ offsetFrom: value.properties.borders.offsetFrom
6180
+ } : {},
6181
+ ...value.properties.borders.borders ? borders2(value.properties.borders.borders) : {}
6182
+ }
6183
+ } : {},
5099
6184
  ...columns ? {
5100
- column: {
6185
+ columns: {
5101
6186
  count: columns.count,
5102
6187
  ...columns.spaceTwips !== void 0 ? { space: twips(columns.spaceTwips) } : {},
5103
6188
  ...columns.separator !== void 0 ? { separate: columns.separator } : {},
@@ -5141,6 +6226,8 @@ function sectionChildren2(value, ctx, closesDocument = false) {
5141
6226
  spacing: { before: 0, after: 0, line: 20, lineRule: "exact" }
5142
6227
  }
5143
6228
  });
6229
+ if (!closesDocument && !bookmark?.closes && last?.kind === "toc")
6230
+ blocks.push({ paragraph: { children: [] } });
5144
6231
  if (!bookmark) return blocks;
5145
6232
  return [
5146
6233
  ...bookmark.opens ? [{ bookmarkStart: { id: bookmark.id, name: bookmark.name } }] : [],
@@ -5151,15 +6238,19 @@ function sectionChildren2(value, ctx, closesDocument = false) {
5151
6238
  function numberingConfig2(numbering) {
5152
6239
  return {
5153
6240
  reference: numbering.reference,
5154
- levels: numbering.levels.map((level) => ({
5155
- level: level.level,
5156
- format: level.format,
5157
- text: level.text,
5158
- ...level.alignment ? { alignment: alignment(level.alignment) } : {},
5159
- ...level.suffix ? { suffix: level.suffix } : {},
5160
- ...level.start !== void 0 ? { start: level.start } : {},
5161
- ...level.paragraphStyleId ? { paragraphStyle: level.paragraphStyleId } : {},
5162
- style: {
6241
+ levels: numbering.levels.map(
6242
+ (level) => ({
6243
+ level: level.level,
6244
+ format: level.format,
6245
+ text: level.text,
6246
+ ...level.alignment ? { alignment: alignment(level.alignment) } : {},
6247
+ ...level.suffix ? { suffix: level.suffix } : {},
6248
+ // Always stated. docx.js writes `w:start w:val="1"` for a level that
6249
+ // names none, and so did this backend until 0.14, which writes no
6250
+ // `w:start` at all when it is not given one; Word and LibreOffice then
6251
+ // count every such list from 0.
6252
+ start: level.start ?? 1,
6253
+ ...level.paragraphStyleId ? { paragraphStyle: level.paragraphStyleId } : {},
5163
6254
  ...level.indent ? {
5164
6255
  paragraph: {
5165
6256
  indent: {
@@ -5169,25 +6260,22 @@ function numberingConfig2(numbering) {
5169
6260
  }
5170
6261
  } : {},
5171
6262
  ...level.run ? { run: runProperties2(level.run) } : {}
5172
- }
5173
- }))
6263
+ })
6264
+ )
5174
6265
  };
5175
6266
  }
5176
- var WRAP_TYPE2, BODY_ANCHOR;
6267
+ var BODY_ANCHOR;
5177
6268
  var init_emit2 = __esm({
5178
6269
  "src/renderers/office-open/emit.ts"() {
5179
6270
  "use strict";
5180
6271
  init_units();
5181
- WRAP_TYPE2 = {
5182
- none: 0,
5183
- square: 1,
5184
- tight: 2,
5185
- topAndBottom: 3
5186
- };
6272
+ init_chartWorkbook();
6273
+ init_chartLook();
6274
+ init_chartParts();
5187
6275
  BODY_ANCHOR = {
5188
- top: "t",
5189
- middle: "ctr",
5190
- bottom: "b"
6276
+ top: "top",
6277
+ middle: "center",
6278
+ bottom: "bottom"
5191
6279
  };
5192
6280
  }
5193
6281
  });
@@ -5247,37 +6335,49 @@ var init_styles3 = __esm({
5247
6335
  var office_open_exports = {};
5248
6336
  __export(office_open_exports, {
5249
6337
  OFFICE_OPEN_DOCX_RENDERER_ID: () => OFFICE_OPEN_DOCX_RENDERER_ID,
6338
+ OFFICE_OPEN_VERSION: () => OFFICE_OPEN_VERSION,
5250
6339
  buildDocumentOptions: () => buildDocumentOptions,
5251
6340
  createOfficeOpenDocxRenderer: () => createOfficeOpenDocxRenderer
5252
6341
  });
5253
- import AdmZip4 from "adm-zip";
6342
+ import AdmZip5 from "adm-zip";
6343
+ import {
6344
+ assertBackendVersion,
6345
+ installedPackageVersion
6346
+ } from "@json-to-office/shared/rendering/node";
6347
+ function installedBackendVersion() {
6348
+ let resolved;
6349
+ try {
6350
+ resolved = import.meta.resolve?.(OFFICE_OPEN_DOCX);
6351
+ } catch {
6352
+ resolved = void 0;
6353
+ }
6354
+ return installedPackageVersion(OFFICE_OPEN_DOCX, import.meta.url, resolved);
6355
+ }
5254
6356
  async function createOfficeOpenDocxRenderer() {
5255
6357
  const backend = await import(
5256
6358
  /* @vite-ignore */
5257
6359
  OFFICE_OPEN_DOCX
5258
6360
  );
5259
- for (const name of [
5260
- "generateDocument",
5261
- "stringifyTableOfContents"
5262
- ]) {
5263
- if (typeof backend[name] !== "function") {
5264
- throw new Error(
5265
- `${OFFICE_OPEN_DOCX} does not export ${name}(); the installed version is not compatible with this adapter.`
5266
- );
5267
- }
6361
+ assertBackendVersion(
6362
+ OFFICE_OPEN_DOCX,
6363
+ OFFICE_OPEN_VERSION,
6364
+ installedBackendVersion()
6365
+ );
6366
+ if (typeof backend.generateDocument !== "function") {
6367
+ throw new Error(
6368
+ `${OFFICE_OPEN_DOCX} does not export generateDocument(); the installed version is not compatible with this adapter.`
6369
+ );
5268
6370
  }
5269
6371
  return {
5270
6372
  id: OFFICE_OPEN_DOCX_RENDERER_ID,
5271
6373
  format: "docx",
5272
6374
  capabilities: OFFICE_OPEN_CAPABILITIES,
5273
6375
  async render(ir, options) {
5274
- const charts = [];
5275
- const cellTocs = [];
6376
+ const charts2 = [];
5276
6377
  const document = await buildDocumentOptions(
5277
6378
  ir,
5278
- charts,
5279
- options?.svgRasterFallback,
5280
- cellTocs
6379
+ charts2,
6380
+ options?.svgRasterFallback
5281
6381
  );
5282
6382
  const bytes = await backend.generateDocument(document, {
5283
6383
  type: "uint8array"
@@ -5285,10 +6385,10 @@ async function createOfficeOpenDocxRenderer() {
5285
6385
  let raw = Buffer.from(
5286
6386
  bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes)
5287
6387
  );
5288
- if (charts.length > 0 || cellTocs.length > 0) {
5289
- const zip = new AdmZip4(raw);
5290
- spliceChartParts(zip, charts);
5291
- spliceCellTocs(zip, cellTocs, backend.stringifyTableOfContents);
6388
+ if (ir.theme || charts2.length > 0) {
6389
+ const zip = new AdmZip5(raw);
6390
+ if (ir.theme) spliceTheme(zip, ir.theme);
6391
+ finishChartParts(zip, charts2);
5292
6392
  raw = zip.toBuffer();
5293
6393
  }
5294
6394
  if (options?.deterministic === false)
@@ -5305,13 +6405,12 @@ async function createOfficeOpenDocxRenderer() {
5305
6405
  }
5306
6406
  };
5307
6407
  }
5308
- async function buildDocumentOptions(ir, charts = [], svgRasterFallback, cellTocs = []) {
6408
+ async function buildDocumentOptions(ir, charts2 = [], svgRasterFallback) {
5309
6409
  let nextDrawingId = 1;
5310
6410
  const ctx = {
5311
6411
  pictures: await prepareImages2(ir, svgRasterFallback),
5312
6412
  nextDrawingId: () => nextDrawingId++,
5313
- charts,
5314
- cellTocs
6413
+ charts: charts2
5315
6414
  };
5316
6415
  return {
5317
6416
  styles: emitStyles2(ir.styles),
@@ -5319,16 +6418,20 @@ async function buildDocumentOptions(ir, charts = [], svgRasterFallback, cellTocs
5319
6418
  (value, index) => section(value, ctx, index === ir.sections.length - 1)
5320
6419
  ),
5321
6420
  ...coreProperties2(ir),
5322
- features: {
6421
+ settings: {
5323
6422
  updateFields: ir.settings.updateFields,
5324
6423
  ...ir.settings.trackRevisions ? { trackRevisions: true } : {}
5325
6424
  },
5326
- ...ir.numbering.length > 0 ? { numbering: { config: ir.numbering.map(numberingConfig2) } } : {},
6425
+ ...ir.numbering.length > 0 ? {
6426
+ numbering: {
6427
+ abstractNumberings: ir.numbering.map(numberingConfig2)
6428
+ }
6429
+ } : {},
5327
6430
  ...ir.footnotes.length > 0 ? { footnotes: noteBodies2(ir.footnotes, ctx) } : {},
5328
6431
  ...ir.endnotes.length > 0 ? { endnotes: noteBodies2(ir.endnotes, ctx) } : {},
5329
6432
  ...ir.comments.length > 0 ? {
5330
- comments: {
5331
- children: ir.comments.map((comment) => ({
6433
+ comments: ir.comments.map(
6434
+ (comment) => ({
5332
6435
  id: comment.id,
5333
6436
  author: comment.author,
5334
6437
  ...comment.initials ? { initials: comment.initials } : {},
@@ -5336,29 +6439,25 @@ async function buildDocumentOptions(ir, charts = [], svgRasterFallback, cellTocs
5336
6439
  children: comment.children.map(
5337
6440
  (child) => paragraphOf(child, ctx)
5338
6441
  )
5339
- }))
5340
- }
6442
+ })
6443
+ )
5341
6444
  } : {}
5342
6445
  };
5343
6446
  }
5344
6447
  function paragraphOf(value, ctx) {
5345
6448
  const emitted = block(value, ctx);
5346
- const asParagraph = emitted.paragraph;
5347
- if (!asParagraph) {
6449
+ if (!("paragraph" in emitted) || typeof emitted.paragraph === "string") {
5348
6450
  throw new Error(
5349
6451
  `the office-open renderer expected a paragraph, not a "${value.kind}"`
5350
6452
  );
5351
6453
  }
5352
- return asParagraph;
6454
+ return emitted.paragraph;
5353
6455
  }
5354
6456
  function noteBodies2(notes, ctx) {
5355
- const bodies = {};
5356
- for (const note of notes) {
5357
- bodies[String(note.id)] = {
5358
- children: note.children.map((child) => paragraphOf(child, ctx))
5359
- };
5360
- }
5361
- return bodies;
6457
+ return notes.map((note) => ({
6458
+ id: note.id,
6459
+ children: note.children.map((child) => paragraphOf(child, ctx))
6460
+ }));
5362
6461
  }
5363
6462
  async function prepareImages2(ir, svgRasterFallback) {
5364
6463
  const placements = collectImagePlacements2(ir);
@@ -5381,23 +6480,16 @@ async function prepareImages2(ir, svgRasterFallback) {
5381
6480
  if (resource.kind !== "image") continue;
5382
6481
  const type = resource.mediaType;
5383
6482
  const data = Buffer.from(resource.bytes);
5384
- const placementData = /* @__PURE__ */ new Map();
5385
- [...placements.get(resource.id) ?? []].forEach((size, index) => {
5386
- placementData.set(
5387
- size,
5388
- index === 0 ? data : distinguishImageBytes(data, type, size)
5389
- );
5390
- });
5391
6483
  resources.set(resource.id, (placement) => {
5392
6484
  const sizeKey = placementKey(placement);
5393
6485
  const stem = `${resource.id}-${sizeKey}`;
5394
6486
  return {
5395
6487
  type,
5396
- // @office-open/docx stores a drawing transformation on its deduplicated
5397
- // media entry. Equivalent bytes drawn at a second size would otherwise
5398
- // reuse the first size, so each distinct placement size gets equivalent
5399
- // image bytes carrying a harmless format-native marker.
5400
- data: placementData.get(sizeKey) ?? data,
6488
+ // The same bytes at every size: the backend keeps each drawing's
6489
+ // extent on the drawing. 0.11 kept it on its deduplicated media
6490
+ // entry, and an image drawn at a second size took the first one's,
6491
+ // unless its bytes were told apart.
6492
+ data,
5401
6493
  // Named after the resource and the size it is drawn at, so two
5402
6494
  // placements of one image at one size share a single part and a third
5403
6495
  // at another size gets its own.
@@ -5420,104 +6512,6 @@ async function prepareImages2(ir, svgRasterFallback) {
5420
6512
  function placementKey(placement) {
5421
6513
  return `${emuToPixels(placement.widthEmu)}x${emuToPixels(placement.heightEmu)}`;
5422
6514
  }
5423
- function distinguishImageBytes(data, mediaType, placement) {
5424
- const marker = Buffer.from(`json-to-office:${placement}`, "utf8");
5425
- switch (mediaType) {
5426
- case "png":
5427
- return addPngTextChunk(data, marker);
5428
- case "jpg":
5429
- return addJpegComment(data, marker);
5430
- case "gif":
5431
- return addGifComment(data, marker);
5432
- case "svg":
5433
- return Buffer.concat([
5434
- data,
5435
- Buffer.from(`
5436
- <!-- ${marker.toString("utf8")} -->`, "utf8")
5437
- ]);
5438
- case "bmp": {
5439
- const marked = Buffer.concat([data, marker]);
5440
- if (marked.length >= 14 && marked.subarray(0, 2).toString("ascii") === "BM") {
5441
- marked.writeUInt32LE(marked.length, 2);
5442
- }
5443
- return marked;
5444
- }
5445
- default:
5446
- return Buffer.concat([data, marker]);
5447
- }
5448
- }
5449
- function addPngTextChunk(data, marker) {
5450
- const signature = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
5451
- if (data.length < 8 || !data.subarray(0, 8).equals(signature)) {
5452
- return Buffer.concat([data, marker]);
5453
- }
5454
- let offset = 8;
5455
- while (offset + 12 <= data.length) {
5456
- const payloadLength = data.readUInt32BE(offset);
5457
- const chunkEnd = offset + 12 + payloadLength;
5458
- if (chunkEnd > data.length) break;
5459
- if (data.subarray(offset + 4, offset + 8).toString("ascii") === "IEND") {
5460
- const payload = Buffer.concat([
5461
- Buffer.from("json-to-office\0", "latin1"),
5462
- marker
5463
- ]);
5464
- const chunk = Buffer.alloc(12 + payload.length);
5465
- chunk.writeUInt32BE(payload.length, 0);
5466
- chunk.write("tEXt", 4, 4, "ascii");
5467
- payload.copy(chunk, 8);
5468
- chunk.writeUInt32BE(
5469
- crc32(chunk.subarray(4, 8 + payload.length)),
5470
- 8 + payload.length
5471
- );
5472
- return Buffer.concat([
5473
- data.subarray(0, offset),
5474
- chunk,
5475
- data.subarray(offset)
5476
- ]);
5477
- }
5478
- offset = chunkEnd;
5479
- }
5480
- throw new Error(
5481
- "Cannot mark a PNG that has no intact IEND chunk; its bytes were not checked to decode before rendering"
5482
- );
5483
- }
5484
- function addJpegComment(data, marker) {
5485
- if (data.length < 2 || data[0] !== 255 || data[1] !== 216) {
5486
- return Buffer.concat([data, marker]);
5487
- }
5488
- const comment = Buffer.alloc(4 + marker.length);
5489
- comment[0] = 255;
5490
- comment[1] = 254;
5491
- comment.writeUInt16BE(marker.length + 2, 2);
5492
- marker.copy(comment, 4);
5493
- return Buffer.concat([data.subarray(0, 2), comment, data.subarray(2)]);
5494
- }
5495
- function addGifComment(data, marker) {
5496
- const trailer = data.lastIndexOf(59);
5497
- if (trailer < 0 || marker.length > 255) {
5498
- return Buffer.concat([data, marker]);
5499
- }
5500
- const comment = Buffer.concat([
5501
- Buffer.from([33, 254, marker.length]),
5502
- marker,
5503
- Buffer.from([0])
5504
- ]);
5505
- return Buffer.concat([
5506
- data.subarray(0, trailer),
5507
- comment,
5508
- data.subarray(trailer)
5509
- ]);
5510
- }
5511
- function crc32(data) {
5512
- let crc = 4294967295;
5513
- for (const byte of data) {
5514
- crc ^= byte;
5515
- for (let bit = 0; bit < 8; bit += 1) {
5516
- crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
5517
- }
5518
- }
5519
- return (crc ^ 4294967295) >>> 0;
5520
- }
5521
6515
  function collectImagePlacements2(ir) {
5522
6516
  const placements = /* @__PURE__ */ new Map();
5523
6517
  const record = (resourceId, placement) => {
@@ -5585,18 +6579,19 @@ function coreProperties2(ir) {
5585
6579
  ...metadata.custom?.length ? { customProperties: metadata.custom } : {}
5586
6580
  };
5587
6581
  }
5588
- var OFFICE_OPEN_DOCX_RENDERER_ID, OFFICE_OPEN_DOCX, OFFICE_OPEN_CAPABILITIES;
6582
+ var OFFICE_OPEN_DOCX_RENDERER_ID, OFFICE_OPEN_DOCX, OFFICE_OPEN_VERSION, OFFICE_OPEN_CAPABILITIES;
5589
6583
  var init_office_open = __esm({
5590
6584
  "src/renderers/office-open/index.ts"() {
5591
6585
  "use strict";
5592
6586
  init_chartParts();
5593
- init_cellTocs();
6587
+ init_themePart();
5594
6588
  init_imageUtils();
5595
6589
  init_packageDocument();
5596
6590
  init_emit2();
5597
6591
  init_styles3();
5598
6592
  OFFICE_OPEN_DOCX_RENDERER_ID = "office-open";
5599
6593
  OFFICE_OPEN_DOCX = "@office-open/docx";
6594
+ OFFICE_OPEN_VERSION = "0.14.6";
5600
6595
  OFFICE_OPEN_CAPABILITIES = /* @__PURE__ */ new Set([
5601
6596
  "paragraphs",
5602
6597
  "styles",
@@ -9227,6 +10222,7 @@ function emptyToUndefined(formatting) {
9227
10222
  }
9228
10223
 
9229
10224
  // src/ir/compiler.ts
10225
+ init_chartValues();
9230
10226
  init_types();
9231
10227
 
9232
10228
  // src/ir/measure.ts
@@ -9259,6 +10255,49 @@ function tableWidthTwips(width, measureTwips) {
9259
10255
 
9260
10256
  // src/ir/compiler.ts
9261
10257
  init_units();
10258
+
10259
+ // src/ir/theme.ts
10260
+ init_colorUtils();
10261
+ init_styleHelpers();
10262
+ init_types();
10263
+ var THEME_COLOR_TOKENS = [
10264
+ ["dark1", "text"],
10265
+ ["light1", "background"],
10266
+ ["dark2", "textSecondary"],
10267
+ ["light2", "backgroundSecondary"],
10268
+ ["accent1", "primary"],
10269
+ ["accent2", "secondary"],
10270
+ ["accent3", "accent"],
10271
+ ["accent4", "accent4"],
10272
+ ["accent5", "accent5"],
10273
+ ["accent6", "accent6"]
10274
+ ];
10275
+ var NOT_XML_CHARS = new RegExp(NOT_XML_CHAR.source, "gu");
10276
+ var xmlText = (value) => (value ?? "").replace(NOT_XML_CHARS, "").trim();
10277
+ function compileTheme(theme, themeName) {
10278
+ const colors = {};
10279
+ for (const [slot, token] of THEME_COLOR_TOKENS) {
10280
+ const hex = schemeHex(token, theme);
10281
+ if (hex) colors[slot] = { hex };
10282
+ }
10283
+ const headingFont = xmlText(resolveFontFamily(theme, "heading"));
10284
+ const bodyFont = xmlText(resolveFontFamily(theme, "body"));
10285
+ return {
10286
+ name: xmlText(theme.name) || xmlText(themeName) || "Office Theme",
10287
+ colors,
10288
+ ...headingFont ? { headingFont } : {},
10289
+ ...bodyFont ? { bodyFont } : {}
10290
+ };
10291
+ }
10292
+ function schemeHex(token, theme) {
10293
+ try {
10294
+ return resolveColor(token, theme);
10295
+ } catch {
10296
+ return void 0;
10297
+ }
10298
+ }
10299
+
10300
+ // src/ir/compiler.ts
9262
10301
  var CONTENT_BOOKMARK_ID_BASE = 2e6;
9263
10302
  var SINGLE_LINE_TWIPS = 240;
9264
10303
  function compileDocument(structure, layout, warnings = [], images = /* @__PURE__ */ new Map(), options = {}) {
@@ -9324,6 +10363,7 @@ function compileDocument(structure, layout, warnings = [], images = /* @__PURE__
9324
10363
  trackRevisions: structure.trackRevisions === true,
9325
10364
  ...noProofWords(structure.theme) ? { noProofWords: noProofWords(structure.theme) } : {}
9326
10365
  },
10366
+ theme: compileTheme(structure.theme, structure.themeName),
9327
10367
  // The statistic styles are appended rather than built with the rest: a
9328
10368
  // document that uses no statistic must keep the exact style set it had
9329
10369
  // before the component had styles, or every recorded golden moves for a
@@ -10092,14 +11132,6 @@ function compileShape2(props, children, scope) {
10092
11132
  );
10093
11133
  }
10094
11134
  const fill = style?.shading?.fill;
10095
- const dropOutline = Boolean(fill) && Boolean(outline.outline);
10096
- if (dropOutline) {
10097
- warnOnce(
10098
- ctx,
10099
- "text-box",
10100
- '[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.'
10101
- );
10102
- }
10103
11135
  ctx.features.require("text-boxes", path4);
10104
11136
  if (props.floating) ctx.features.require("floating-images", path4);
10105
11137
  const insets = shapeInsets(
@@ -10118,7 +11150,7 @@ function compileShape2(props, children, scope) {
10118
11150
  heightPx: height.pixels,
10119
11151
  children,
10120
11152
  ...fill ? { fill: irColor(resolveColor(fill, ctx.theme)) } : {},
10121
- ...outline.outline && !dropOutline ? { outline: outline.outline } : {},
11153
+ ...outline.outline ? { outline: outline.outline } : {},
10122
11154
  ...insets ? { insetsEmu: insets } : {},
10123
11155
  ...props.floating ? { floating: compileFloating(props.floating, ctx, path4) } : {}
10124
11156
  }
@@ -11341,6 +12373,7 @@ function compileImage2(component, scope) {
11341
12373
  }
11342
12374
  var DEFAULT_CHART_HEIGHT_INCHES = 3;
11343
12375
  var MAX_CHART_TEXT_POINTS = 10;
12376
+ var MIN_CHART_TEXT_POINTS = 1;
11344
12377
  function chartTextFont(theme) {
11345
12378
  const bodySize = getThemeFonts(theme).body?.size;
11346
12379
  let color = "000000";
@@ -11350,9 +12383,12 @@ function chartTextFont(theme) {
11350
12383
  }
11351
12384
  return {
11352
12385
  fontFamily: resolveFontFamily(theme, "body"),
11353
- fontSize: Math.min(
11354
- typeof bodySize === "number" && bodySize > 0 ? bodySize : 11,
11355
- MAX_CHART_TEXT_POINTS
12386
+ fontSize: Math.max(
12387
+ MIN_CHART_TEXT_POINTS,
12388
+ Math.min(
12389
+ typeof bodySize === "number" && bodySize > 0 ? bodySize : 11,
12390
+ MAX_CHART_TEXT_POINTS
12391
+ )
11356
12392
  ),
11357
12393
  bold: false,
11358
12394
  color
@@ -11363,7 +12399,7 @@ function compileChart(component, scope) {
11363
12399
  const props = component.props ?? {};
11364
12400
  if (props.type === "bubble") {
11365
12401
  throw new Error(
11366
- `Chart at ${path4} is a bubble chart, which no docx renderer draws; use the pptx \`chart\` component on the pptxgenjs renderer for one.`
12402
+ `Chart at ${path4} is a bubble chart, but the docx \`chart\` component has no bubble sizes; use the pptx \`chart\` component on the pptxgenjs renderer.`
11367
12403
  );
11368
12404
  }
11369
12405
  const rawSeries = Array.isArray(props.data) ? props.data : [];
@@ -11385,10 +12421,22 @@ function compileChart(component, scope) {
11385
12421
  `Chart series "${label}" at ${path4} has ${labels.length} labels and ${values.length} values; they must be the same length.`
11386
12422
  );
11387
12423
  }
12424
+ if (labels.length === 0) {
12425
+ throw new Error(
12426
+ `Chart series "${label}" at ${path4} has no data points; give it at least one label and value.`
12427
+ );
12428
+ }
12429
+ const numbers = values.map((value) => Number(value));
12430
+ const bad = numbers.findIndex((value) => !Number.isFinite(value));
12431
+ if (bad !== -1) {
12432
+ throw new Error(
12433
+ `Chart series "${label}" at ${path4} has a value that is not a finite number (${JSON.stringify(values[bad])} at "${String(labels[bad])}").`
12434
+ );
12435
+ }
11388
12436
  return {
11389
12437
  ...typeof raw.name === "string" ? { name: raw.name } : {},
11390
12438
  labels: labels.map((value) => String(value)),
11391
- values: values.map((value) => Number(value))
12439
+ values: numbers
11392
12440
  };
11393
12441
  });
11394
12442
  const categories = series[0].labels;
@@ -11400,6 +12448,44 @@ function compileChart(component, scope) {
11400
12448
  );
11401
12449
  }
11402
12450
  }
12451
+ if (props.type === "pie" && series.length > 1) {
12452
+ throw new Error(
12453
+ `Chart at ${path4} is a pie with ${series.length} series; a pie draws one series. Use a doughnut for one ring per series, or a column chart to compare them.`
12454
+ );
12455
+ }
12456
+ if (props.type === "pie" || props.type === "doughnut") {
12457
+ for (const [index, entry] of series.entries()) {
12458
+ const at = entry.values.findIndex((value) => value < 0);
12459
+ if (at === -1) continue;
12460
+ throw new Error(
12461
+ `Chart series "${entry.name ?? `series ${index}`}" at ${path4} has a negative value (${entry.values[at]} at "${entry.labels[at]}"); a ${props.type} chart draws each value as a share of the whole.`
12462
+ );
12463
+ }
12464
+ }
12465
+ if (props.type === "scatter") {
12466
+ for (const [index, entry] of series.entries()) {
12467
+ const at = entry.labels.findIndex(isOutOfRangeNumericLabel);
12468
+ if (at !== -1) {
12469
+ throw new Error(
12470
+ `Chart series "${entry.name ?? `series ${index}`}" at ${path4} has an x label ${JSON.stringify(entry.labels[at])} outside the finite number range.`
12471
+ );
12472
+ }
12473
+ }
12474
+ }
12475
+ if (props.type === "scatter" && series.some((entry) => entry.labels.some((label) => !isNumericLabel(label)))) {
12476
+ warnOnce(
12477
+ ctx,
12478
+ "chart",
12479
+ `Chart at ${path4} 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.`
12480
+ );
12481
+ }
12482
+ if ((props.type === "pie" || props.type === "doughnut") && (typeof props.catAxisTitle === "string" || typeof props.valAxisTitle === "string")) {
12483
+ warnOnce(
12484
+ ctx,
12485
+ "chart",
12486
+ `Chart at ${path4} is a ${props.type} chart, which has no axes; its axis titles are not drawn.`
12487
+ );
12488
+ }
11403
12489
  const colors = Array.isArray(props.chartColors) ? props.chartColors.map(
11404
12490
  (color) => resolveColor(String(color), ctx.theme)
11405
12491
  ) : chartPaletteValues(ctx.theme).flatMap((value) => {
@@ -11412,13 +12498,21 @@ function compileChart(component, scope) {
11412
12498
  const contentWidthInches = scope.measureTwips / 1440;
11413
12499
  const widthInches = typeof props.width === "number" ? props.width : contentWidthInches;
11414
12500
  const heightInches = typeof props.height === "number" ? props.height : DEFAULT_CHART_HEIGHT_INCHES;
12501
+ const widthEmu = inchesToEmu(widthInches);
12502
+ const heightEmu = inchesToEmu(heightInches);
12503
+ if (!(widthEmu > 0 && heightEmu > 0)) {
12504
+ const inches = (value) => String(Number.isFinite(value) ? Math.round(value * 100) / 100 : value);
12505
+ throw new Error(
12506
+ `Chart at ${path4} has no room: it would be ${inches(widthInches)}\xD7${inches(heightInches)} in. Set \`width\`/\`height\`, or give it a container with space.`
12507
+ );
12508
+ }
11415
12509
  const chart = {
11416
12510
  kind: "chart",
11417
12511
  chartType: props.type,
11418
12512
  series,
11419
12513
  colors,
11420
- widthEmu: inchesToEmu(widthInches),
11421
- heightEmu: inchesToEmu(heightInches),
12514
+ widthEmu,
12515
+ heightEmu,
11422
12516
  ...typeof props.title === "string" ? { title: props.title } : {},
11423
12517
  ...props.showTitle !== void 0 ? { showTitle: props.showTitle } : {},
11424
12518
  ...props.showLegend !== void 0 ? { showLegend: props.showLegend } : {},
@@ -11621,7 +12715,7 @@ function compileFrame(floating, ctx, path4) {
11621
12715
  function compileFloating(floating, ctx, path4) {
11622
12716
  if (floating.wrap?.type === "tight") {
11623
12717
  throw new Error(
11624
- "Image floating wrap.type 'tight' is not supported due to invalid OOXML emitted by docx. Use 'square', 'topAndBottom', or 'none'."
12718
+ "Image floating wrap.type 'tight' is not supported. Use 'square', 'topAndBottom', or 'none'."
11625
12719
  );
11626
12720
  }
11627
12721
  const hRelative = floating.horizontalPosition?.relative;
@@ -15783,18 +16877,18 @@ var docxExhibitRequiredRule = {
15783
16877
  1
15784
16878
  );
15785
16879
  if (minimum <= 0) return [];
15786
- const charts = facts.filter((fact) => fact.kind === "docx/chart").length;
16880
+ const charts2 = facts.filter((fact) => fact.kind === "docx/chart").length;
15787
16881
  const tables = facts.filter(
15788
16882
  (fact) => fact.kind === "docx/table" && fact.columns.length >= 2
15789
16883
  ).length;
15790
- const exhibits = charts + tables;
16884
+ const exhibits = charts2 + tables;
15791
16885
  if (exhibits >= minimum) return [];
15792
16886
  return [
15793
16887
  {
15794
16888
  path: themeFact(facts)?.path ?? "/props",
15795
16889
  message: exhibits === 0 ? `The document carries no data exhibit \u2014 no chart, no table of two or more columns; the ${profile?.id ?? "selected"} profile expects at least ${minimum}.` : `The document carries ${exhibits} data exhibit(s); the ${profile?.id ?? "selected"} profile expects at least ${minimum}.`,
15796
16890
  suggestion: "Put the numbers the argument leans on into a chart-figure or a data-table block, with a takeaway and a source, where the text discusses them. A kpi-row summarises; it does not count.",
15797
- context: { charts, tables, minimum }
16891
+ context: { charts: charts2, tables, minimum }
15798
16892
  }
15799
16893
  ];
15800
16894
  }