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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/index.js +1539 -471
  2. package/dist/index.js.map +1 -1
  3. package/dist/ir/chartValues.d.ts +16 -0
  4. package/dist/ir/chartValues.d.ts.map +1 -0
  5. package/dist/ir/compiler.d.ts.map +1 -1
  6. package/dist/ir/features.d.ts.map +1 -1
  7. package/dist/ir/theme.d.ts +43 -0
  8. package/dist/ir/theme.d.ts.map +1 -0
  9. package/dist/ir/types.d.ts +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 +1536 -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,226 @@ var init_units = __esm({
2874
2875
  }
2875
2876
  });
2876
2877
 
2878
+ // src/ir/chartValues.ts
2879
+ function isNumericLabel(label) {
2880
+ return NUMERIC_LABEL.test(label.trim());
2881
+ }
2882
+ function scatterX(labels, index) {
2883
+ const label = labels[index];
2884
+ return label !== void 0 && isNumericLabel(label) ? Number(label) : index + 1;
2885
+ }
2886
+ var NUMERIC_LABEL;
2887
+ var init_chartValues = __esm({
2888
+ "src/ir/chartValues.ts"() {
2889
+ "use strict";
2890
+ NUMERIC_LABEL = /^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$/;
2891
+ }
2892
+ });
2893
+
2894
+ // src/renderers/chartText.ts
2895
+ function chartTextRoles(font) {
2896
+ const { bold, ...rest } = font;
2897
+ const text = rest;
2898
+ return {
2899
+ text,
2900
+ axisTitle: { ...text, bold },
2901
+ title: {
2902
+ ...text,
2903
+ fontSize: Math.round(text.fontSize * CHART_TITLE_SCALE),
2904
+ bold: true
2905
+ }
2906
+ };
2907
+ }
2908
+ var CHART_TITLE_SCALE;
2909
+ var init_chartText = __esm({
2910
+ "src/renderers/chartText.ts"() {
2911
+ "use strict";
2912
+ CHART_TITLE_SCALE = 1.4;
2913
+ }
2914
+ });
2915
+
2916
+ // src/renderers/docxjs/docxSubpath.ts
2917
+ function docxSubpath(specifier, component, importer) {
2918
+ let module;
2919
+ let failure;
2920
+ let loading;
2921
+ return {
2922
+ specifier,
2923
+ load() {
2924
+ loading ??= importer().then(
2925
+ (loadedModule) => {
2926
+ module = loadedModule;
2927
+ },
2928
+ (reason) => {
2929
+ failure = { reason };
2930
+ }
2931
+ );
2932
+ return loading;
2933
+ },
2934
+ loaded: () => module !== void 0,
2935
+ get() {
2936
+ if (module !== void 0) return module;
2937
+ if (failure !== void 0) {
2938
+ const { reason } = failure;
2939
+ const detail = reason instanceof Error ? reason.message : String(reason);
2940
+ throw new Error(
2941
+ `The docx \`${component}\` component needs docx 9.8.0 or later: the \`${specifier}\` entry could not be loaded (${detail})`
2942
+ );
2943
+ }
2944
+ throw new Error(
2945
+ `${specifier} is not loaded: await its load() before buildDocument() (render() does)`
2946
+ );
2947
+ }
2948
+ };
2949
+ }
2950
+ var init_docxSubpath = __esm({
2951
+ "src/renderers/docxjs/docxSubpath.ts"() {
2952
+ "use strict";
2953
+ }
2954
+ });
2955
+
2956
+ // src/renderers/docxjs/charts.ts
2957
+ import { assertNever } from "@json-to-office/shared/rendering";
2958
+ function loadDocxCharts() {
2959
+ return charts.load();
2960
+ }
2961
+ function chartFont(role) {
2962
+ return {
2963
+ name: role.fontFamily,
2964
+ size: role.fontSize,
2965
+ color: role.color,
2966
+ bold: role.bold ?? false
2967
+ };
2968
+ }
2969
+ function chartRunOptions(chart, floating) {
2970
+ const roles = chartTextRoles(chart.textFont);
2971
+ const text = chartFont(roles.text);
2972
+ const axisTitle = chartFont(roles.axisTitle);
2973
+ const palette = chart.colors;
2974
+ const colorAt = (index) => palette.length > 0 ? palette[index % palette.length] : void 0;
2975
+ const withColor = (index) => {
2976
+ const color = colorAt(index);
2977
+ return color ? { color } : {};
2978
+ };
2979
+ const seriesName = (index) => chart.series[index].name ?? `Series ${index + 1}`;
2980
+ const categories = chart.series[0]?.labels ?? [];
2981
+ const axis = (title) => ({
2982
+ ...title ? { title: { text: title, font: axisTitle } } : {},
2983
+ font: text
2984
+ });
2985
+ const common = {
2986
+ ...chart.title && chart.showTitle !== false ? { title: { text: chart.title, font: chartFont(roles.title) } } : {},
2987
+ legend: chart.showLegend === false ? false : {
2988
+ position: LEGEND_POSITION[chart.legendPosition ?? "b"],
2989
+ font: text
2990
+ },
2991
+ font: { name: roles.text.fontFamily, color: roles.text.color },
2992
+ // Office-open's chart space is unfilled and unbordered; `ChartRun`'s
2993
+ // default is Word's white fill and grey border.
2994
+ chartArea: { fill: "none", border: "none" },
2995
+ transformation: {
2996
+ width: emuToPixels(chart.widthEmu),
2997
+ height: emuToPixels(chart.heightEmu)
2998
+ },
2999
+ // Without an authored description `ChartRun` describes the chart from
3000
+ // its data, which is deterministic; the name stays empty, as it is on
3001
+ // every other drawing this renderer writes.
3002
+ ...chart.altText ? { altText: { name: "", description: chart.altText } } : {},
3003
+ ...floating ? { floating } : {}
3004
+ };
3005
+ switch (chart.chartType) {
3006
+ case "column":
3007
+ case "bar":
3008
+ case "area":
3009
+ case "line":
3010
+ return {
3011
+ ...common,
3012
+ type: chart.chartType,
3013
+ categories,
3014
+ ...chart.chartType === "line" ? { markers: true } : {},
3015
+ series: chart.series.map((entry, index) => ({
3016
+ name: seriesName(index),
3017
+ values: entry.values,
3018
+ ...withColor(index)
3019
+ })),
3020
+ categoryAxis: axis(chart.categoryAxisTitle),
3021
+ valueAxis: axis(chart.valueAxisTitle)
3022
+ };
3023
+ case "pie":
3024
+ case "doughnut":
3025
+ return {
3026
+ ...common,
3027
+ type: chart.chartType,
3028
+ categories,
3029
+ series: chart.series.map((entry, index) => ({
3030
+ name: seriesName(index),
3031
+ values: entry.values,
3032
+ ...palette.length > 0 ? { colors: entry.values.map((_, point) => colorAt(point)) } : {}
3033
+ }))
3034
+ };
3035
+ case "radar":
3036
+ return {
3037
+ ...common,
3038
+ type: "radar",
3039
+ categories,
3040
+ markers: true,
3041
+ series: chart.series.map((entry, index) => ({
3042
+ name: seriesName(index),
3043
+ values: entry.values,
3044
+ ...withColor(index)
3045
+ })),
3046
+ categoryAxis: axis(chart.categoryAxisTitle),
3047
+ valueAxis: axis(chart.valueAxisTitle)
3048
+ };
3049
+ case "scatter":
3050
+ return {
3051
+ ...common,
3052
+ type: "scatter",
3053
+ markers: true,
3054
+ lines: "straight",
3055
+ series: chart.series.map((entry, index) => ({
3056
+ name: seriesName(index),
3057
+ points: entry.values.map((y, point) => ({
3058
+ x: scatterX(entry.labels, point),
3059
+ y
3060
+ })),
3061
+ ...withColor(index)
3062
+ })),
3063
+ xAxis: { ...axis(chart.categoryAxisTitle), gridlines: false },
3064
+ yAxis: axis(chart.valueAxisTitle)
3065
+ };
3066
+ case "bubble":
3067
+ throw new Error(
3068
+ "the docxjs renderer has no emitter for a bubble chart; the compiler should have refused it"
3069
+ );
3070
+ default:
3071
+ return assertNever(chart.chartType, "DocxIrChartType");
3072
+ }
3073
+ }
3074
+ function emitChart(chart, floating) {
3075
+ return new (charts.get()).ChartRun(
3076
+ chartRunOptions(chart, floating)
3077
+ );
3078
+ }
3079
+ var charts, LEGEND_POSITION;
3080
+ var init_charts = __esm({
3081
+ "src/renderers/docxjs/charts.ts"() {
3082
+ "use strict";
3083
+ init_chartValues();
3084
+ init_units();
3085
+ init_chartText();
3086
+ init_docxSubpath();
3087
+ charts = docxSubpath("docx/charts", "chart", () => import("docx/charts"));
3088
+ LEGEND_POSITION = {
3089
+ b: "bottom",
3090
+ l: "left",
3091
+ r: "right",
3092
+ t: "top",
3093
+ tr: "topRight"
3094
+ };
3095
+ }
3096
+ });
3097
+
2877
3098
  // src/renderers/docxjs/emit.ts
2878
3099
  import {
2879
3100
  AlignmentType,
@@ -2909,7 +3130,7 @@ import {
2909
3130
  WidthType,
2910
3131
  WpsShapeRun
2911
3132
  } from "docx";
2912
- import { assertNever } from "@json-to-office/shared/rendering";
3133
+ import { assertNever as assertNever2 } from "@json-to-office/shared/rendering";
2913
3134
  function complexField(instruction, formatting, leading, cachedText) {
2914
3135
  const options = runOptions(formatting);
2915
3136
  const fieldChar = (type, extra = {}) => new TextRun({
@@ -2946,7 +3167,12 @@ function runOptions(formatting) {
2946
3167
  if (formatting.bold !== void 0) options.bold = formatting.bold;
2947
3168
  if (formatting.italic !== void 0) options.italics = formatting.italic;
2948
3169
  if (formatting.underline !== void 0) {
2949
- options.underline = formatting.underline ? { type: formatting.underline.type } : void 0;
3170
+ options.underline = formatting.underline ? {
3171
+ type: formatting.underline.type,
3172
+ // A native visual's text is the one place a run states its own
3173
+ // underline colour.
3174
+ ...formatting.underline.color ? { color: formatting.underline.color.hex } : {}
3175
+ } : void 0;
2950
3176
  }
2951
3177
  if (formatting.strike !== void 0) options.strike = formatting.strike;
2952
3178
  if (formatting.allCaps !== void 0) options.allCaps = formatting.allCaps;
@@ -3072,21 +3298,36 @@ function inlineChildren(children, resources = /* @__PURE__ */ new Map()) {
3072
3298
  case "shape":
3073
3299
  out.push(emitShape(child, resources));
3074
3300
  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"
3301
+ case "drawingGroup": {
3302
+ const build = resources.drawingGroup;
3303
+ if (!build) {
3304
+ throw new Error(
3305
+ "the docxjs renderer was handed a drawing group without loading docx/shapes; render() loads it whenever the IR holds one"
3306
+ );
3307
+ }
3308
+ const pending = breakOption();
3309
+ if (pending.break) out.push(new TextRun(pending));
3310
+ out.push(build(child, resources));
3311
+ break;
3312
+ }
3313
+ case "chart": {
3314
+ const pending = breakOption();
3315
+ if (pending.break) out.push(new TextRun(pending));
3316
+ out.push(
3317
+ emitChart(
3318
+ child,
3319
+ child.floating ? floatingOptions(child.floating) : void 0
3320
+ )
3082
3321
  );
3322
+ break;
3323
+ }
3083
3324
  case "noteReference":
3084
3325
  out.push(
3085
3326
  child.noteKind === "endnote" ? new EndnoteReferenceRun(child.id) : new FootnoteReferenceRun(child.id)
3086
3327
  );
3087
3328
  break;
3088
3329
  default:
3089
- assertNever(child, "DocxIrInline");
3330
+ assertNever2(child, "DocxIrInline");
3090
3331
  }
3091
3332
  }
3092
3333
  return out;
@@ -3291,7 +3532,7 @@ function emitBlock(block2, resources = /* @__PURE__ */ new Map()) {
3291
3532
  case "toc":
3292
3533
  return emitToc(block2);
3293
3534
  default:
3294
- return assertNever(block2, "DocxIrBlock");
3535
+ return assertNever2(block2, "DocxIrBlock");
3295
3536
  }
3296
3537
  }
3297
3538
  function emitToc(block2) {
@@ -3435,6 +3676,7 @@ var ALIGNMENT2, PAGE_FIELDS, isSequenceField, WRAP_TYPE, ThemedTableOfContents,
3435
3676
  var init_emit = __esm({
3436
3677
  "src/renderers/docxjs/emit.ts"() {
3437
3678
  "use strict";
3679
+ init_charts();
3438
3680
  ALIGNMENT2 = {
3439
3681
  left: AlignmentType.LEFT,
3440
3682
  center: AlignmentType.CENTER,
@@ -3733,10 +3975,13 @@ function canonicalizeRelationshipIds(zip) {
3733
3975
  const rename = new Map(
3734
3976
  ordered.map((id, index) => [id, `rId${highest + 1 + index}`])
3735
3977
  );
3736
- const rewrite = (xml) => xml.replace(/"([^"]+)"/g, (whole, value) => {
3737
- const replacement = rename.get(value);
3738
- return replacement === void 0 ? whole : `"${replacement}"`;
3739
- });
3978
+ const rewrite = (xml) => xml.replace(
3979
+ /(\s[\w:.-]+=)"([^"]*)"/g,
3980
+ (whole, prefix, value) => {
3981
+ const replacement = rename.get(value);
3982
+ return replacement === void 0 ? whole : `${prefix}"${replacement}"`;
3983
+ }
3984
+ );
3740
3985
  updates.push({ name: relsName, xml: rewrite(relsXml) });
3741
3986
  if (partEntry) {
3742
3987
  updates.push({ name: partName, xml: rewrite(partXml) });
@@ -3747,17 +3992,62 @@ function canonicalizeRelationshipIds(zip) {
3747
3992
  if (entry) zip.updateFile(entry, Buffer.from(update.xml, "utf8"));
3748
3993
  }
3749
3994
  }
3995
+ function canonicalizeEmbeddedPackages(zip, generatedAt, depth = 1) {
3996
+ const names = zip.getEntries().map((entry) => entry.entryName).filter((name) => EMBEDDED_OFFICE_PACKAGE.test(name));
3997
+ let changed = false;
3998
+ for (const name of names) {
3999
+ const entry = zip.getEntry(name);
4000
+ if (!entry) continue;
4001
+ let nested;
4002
+ try {
4003
+ nested = new AdmZip(entry.getData());
4004
+ nested.getEntries();
4005
+ } catch {
4006
+ continue;
4007
+ }
4008
+ if (canonicalizeNestedPackage(nested, generatedAt, depth)) {
4009
+ zip.updateFile(entry, nested.toBuffer());
4010
+ changed = true;
4011
+ }
4012
+ }
4013
+ return changed;
4014
+ }
4015
+ function canonicalizeNestedPackage(zip, generatedAt, depth) {
4016
+ let changed = false;
4017
+ const coreProperties3 = zip.getEntry("docProps/core.xml");
4018
+ if (coreProperties3) {
4019
+ const xml = coreProperties3.getData().toString("utf8");
4020
+ const normalized = xml.replace(
4021
+ CORE_TIMESTAMP,
4022
+ `$1${generatedAt.toISOString()}$2`
4023
+ );
4024
+ if (normalized !== xml) {
4025
+ zip.updateFile(coreProperties3, Buffer.from(normalized, "utf8"));
4026
+ changed = true;
4027
+ }
4028
+ }
4029
+ if (depth < MAX_EMBEDDED_DEPTH && canonicalizeEmbeddedPackages(zip, generatedAt, depth + 1)) {
4030
+ changed = true;
4031
+ }
4032
+ const zipTimestamp = toDosTime(generatedAt);
4033
+ for (const entry of zip.getEntries()) {
4034
+ const header = entry.header;
4035
+ if (header.timeval !== zipTimestamp) {
4036
+ header.timeval = zipTimestamp;
4037
+ changed = true;
4038
+ }
4039
+ }
4040
+ return changed;
4041
+ }
3750
4042
  function canonicalizeDocxBuffer(buffer, generatedAt = DEFAULT_GENERATION_DATE) {
3751
4043
  const zip = new AdmZip(buffer);
3752
4044
  normalizeTextCase(zip);
3753
4045
  canonicalizeRelationshipIds(zip);
4046
+ canonicalizeEmbeddedPackages(zip, generatedAt);
3754
4047
  const isoTimestamp = generatedAt.toISOString();
3755
4048
  const coreProperties3 = zip.getEntry("docProps/core.xml");
3756
4049
  if (coreProperties3) {
3757
- const normalized = coreProperties3.getData().toString("utf8").replace(
3758
- /(<dcterms:(?:created|modified)\b[^>]*>)[^<]*(<\/dcterms:(?:created|modified)>)/g,
3759
- `$1${isoTimestamp}$2`
3760
- );
4050
+ const normalized = coreProperties3.getData().toString("utf8").replace(CORE_TIMESTAMP, `$1${isoTimestamp}$2`);
3761
4051
  zip.updateFile(coreProperties3, Buffer.from(normalized, "utf8"));
3762
4052
  }
3763
4053
  const zipTimestamp = toDosTime(generatedAt);
@@ -3770,35 +4060,567 @@ function normalizeDocxCaseBuffer(buffer) {
3770
4060
  const zip = new AdmZip(buffer);
3771
4061
  return normalizeTextCase(zip) ? zip.toBuffer() : buffer;
3772
4062
  }
3773
- var DEFAULT_GENERATION_DATE, STABLE_RELATIONSHIP_ID;
4063
+ var DEFAULT_GENERATION_DATE, STABLE_RELATIONSHIP_ID, CORE_TIMESTAMP, EMBEDDED_OFFICE_PACKAGE, MAX_EMBEDDED_DEPTH;
3774
4064
  var init_packageDocument = __esm({
3775
4065
  "src/utils/packageDocument.ts"() {
3776
4066
  "use strict";
3777
4067
  init_normalizeTextCase();
3778
4068
  DEFAULT_GENERATION_DATE = /* @__PURE__ */ new Date("2000-01-01T00:00:00.000Z");
3779
4069
  STABLE_RELATIONSHIP_ID = /^rId\d+$/;
4070
+ CORE_TIMESTAMP = /(<dcterms:(?:created|modified)\b[^>]*>)[^<]*(<\/dcterms:(?:created|modified)>)/g;
4071
+ EMBEDDED_OFFICE_PACKAGE = /\.(?:docx|pptx|xlsx|xlsm)$/i;
4072
+ MAX_EMBEDDED_DEPTH = 3;
3780
4073
  }
3781
4074
  });
3782
4075
 
3783
4076
  // src/utils/fixFloatingImageIds.ts
3784
4077
  import AdmZip2 from "adm-zip";
4078
+ function compareDrawingParts(a, b) {
4079
+ const ma = DRAWING_PART.exec(a);
4080
+ const mb = DRAWING_PART.exec(b);
4081
+ const rank = RANK.indexOf(ma[1]) - RANK.indexOf(mb[1]);
4082
+ return rank !== 0 ? rank : Number(ma[2] || 0) - Number(mb[2] || 0);
4083
+ }
3785
4084
  function fixFloatingImageIdsInBuffer(buffer) {
3786
4085
  const zip = new AdmZip2(buffer);
3787
- const documentEntry = zip.getEntry("word/document.xml");
3788
- if (!documentEntry) {
4086
+ if (!zip.getEntry("word/document.xml")) {
3789
4087
  throw new Error("document.xml not found in DOCX");
3790
4088
  }
4089
+ const parts = zip.getEntries().filter((entry) => DRAWING_PART.test(entry.entryName)).sort((a, b) => compareDrawingParts(a.entryName, b.entryName));
3791
4090
  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"));
4091
+ for (const entry of parts) {
4092
+ const xml = entry.getData().toString("utf8");
4093
+ const renumbered = xml.replace(
4094
+ /(<wp:docPr\b[^>]*?\s)id="\d+"/g,
4095
+ (_match, prefix) => `${prefix}id="${idCounter++}"`
4096
+ );
4097
+ zip.updateFile(entry, Buffer.from(renumbered, "utf8"));
4098
+ }
3797
4099
  return zip.toBuffer();
3798
4100
  }
4101
+ var DRAWING_PART, RANK;
3799
4102
  var init_fixFloatingImageIds = __esm({
3800
4103
  "src/utils/fixFloatingImageIds.ts"() {
3801
4104
  "use strict";
4105
+ DRAWING_PART = /^word\/(document|header|footer|footnotes|endnotes|comments)(\d*)\.xml$/;
4106
+ RANK = [
4107
+ "document",
4108
+ "header",
4109
+ "footer",
4110
+ "footnotes",
4111
+ "endnotes",
4112
+ "comments"
4113
+ ];
4114
+ }
4115
+ });
4116
+
4117
+ // src/renderers/docxjs/presetShapes.ts
4118
+ function docxPresetShape(geometry) {
4119
+ const name = Object.hasOwn(OOXML_PRESET_TO_DOCX, geometry) ? OOXML_PRESET_TO_DOCX[geometry] : void 0;
4120
+ if (!name) {
4121
+ throw new Error(
4122
+ `the docxjs renderer has no preset for the OOXML geometry "${geometry}"`
4123
+ );
4124
+ }
4125
+ return name;
4126
+ }
4127
+ function docxLineDash(dash) {
4128
+ const name = Object.hasOwn(OOXML_DASH_TO_DOCX, dash) ? OOXML_DASH_TO_DOCX[dash] : void 0;
4129
+ if (!name) {
4130
+ throw new Error(
4131
+ `the docxjs renderer has no line dash for the OOXML value "${dash}"`
4132
+ );
4133
+ }
4134
+ return name;
4135
+ }
4136
+ var OOXML_PRESET_TO_DOCX, OOXML_DASH_TO_DOCX;
4137
+ var init_presetShapes = __esm({
4138
+ "src/renderers/docxjs/presetShapes.ts"() {
4139
+ "use strict";
4140
+ OOXML_PRESET_TO_DOCX = {
4141
+ line: "line",
4142
+ lineInv: "inverseLine",
4143
+ straightConnector1: "straightConnector",
4144
+ bentConnector2: "elbowConnectorOneBend",
4145
+ bentConnector3: "elbowConnector",
4146
+ bentConnector4: "elbowConnectorThreeBends",
4147
+ bentConnector5: "elbowConnectorFourBends",
4148
+ curvedConnector2: "curvedConnectorOneBend",
4149
+ curvedConnector3: "curvedConnector",
4150
+ curvedConnector4: "curvedConnectorThreeBends",
4151
+ curvedConnector5: "curvedConnectorFourBends",
4152
+ triangle: "triangle",
4153
+ rtTriangle: "rightTriangle",
4154
+ diamond: "diamond",
4155
+ parallelogram: "parallelogram",
4156
+ trapezoid: "trapezoid",
4157
+ nonIsoscelesTrapezoid: "nonIsoscelesTrapezoid",
4158
+ pentagon: "pentagon",
4159
+ hexagon: "hexagon",
4160
+ heptagon: "heptagon",
4161
+ octagon: "octagon",
4162
+ decagon: "decagon",
4163
+ dodecagon: "dodecagon",
4164
+ ellipse: "ellipse",
4165
+ teardrop: "teardrop",
4166
+ pieWedge: "pieWedge",
4167
+ pie: "pie",
4168
+ blockArc: "blockArc",
4169
+ donut: "donut",
4170
+ noSmoking: "noSymbol",
4171
+ chord: "chord",
4172
+ arc: "arc",
4173
+ frame: "frame",
4174
+ halfFrame: "halfFrame",
4175
+ corner: "lShape",
4176
+ diagStripe: "diagonalStripe",
4177
+ plus: "cross",
4178
+ plaque: "plaque",
4179
+ can: "cylinder",
4180
+ cube: "cube",
4181
+ bevel: "beveledRectangle",
4182
+ foldedCorner: "foldedCorner",
4183
+ smileyFace: "smileyFace",
4184
+ heart: "heart",
4185
+ lightningBolt: "lightningBolt",
4186
+ sun: "sun",
4187
+ moon: "moon",
4188
+ cloud: "cloud",
4189
+ leftBracket: "leftBracket",
4190
+ rightBracket: "rightBracket",
4191
+ leftBrace: "leftBrace",
4192
+ rightBrace: "rightBrace",
4193
+ bracketPair: "bracketPair",
4194
+ bracePair: "bracePair",
4195
+ rect: "rectangle",
4196
+ roundRect: "roundedRectangle",
4197
+ round1Rect: "roundedCornerRectangle",
4198
+ round2SameRect: "topRoundedCornersRectangle",
4199
+ round2DiagRect: "diagonalRoundedCornersRectangle",
4200
+ snip1Rect: "snippedCornerRectangle",
4201
+ snip2SameRect: "topSnippedCornersRectangle",
4202
+ snip2DiagRect: "diagonalSnippedCornersRectangle",
4203
+ snipRoundRect: "roundedAndSnippedCornersRectangle",
4204
+ rightArrow: "rightArrow",
4205
+ leftArrow: "leftArrow",
4206
+ upArrow: "upArrow",
4207
+ downArrow: "downArrow",
4208
+ leftRightArrow: "leftRightArrow",
4209
+ upDownArrow: "upDownArrow",
4210
+ quadArrow: "quadArrow",
4211
+ leftRightUpArrow: "leftRightUpArrow",
4212
+ bentArrow: "bentArrow",
4213
+ uturnArrow: "uTurnArrow",
4214
+ leftUpArrow: "leftUpArrow",
4215
+ bentUpArrow: "bentUpArrow",
4216
+ curvedRightArrow: "curvedRightArrow",
4217
+ curvedLeftArrow: "curvedLeftArrow",
4218
+ curvedUpArrow: "curvedUpArrow",
4219
+ curvedDownArrow: "curvedDownArrow",
4220
+ stripedRightArrow: "stripedRightArrow",
4221
+ notchedRightArrow: "notchedRightArrow",
4222
+ homePlate: "pentagonArrow",
4223
+ chevron: "chevron",
4224
+ rightArrowCallout: "rightArrowCallout",
4225
+ downArrowCallout: "downArrowCallout",
4226
+ leftArrowCallout: "leftArrowCallout",
4227
+ upArrowCallout: "upArrowCallout",
4228
+ leftRightArrowCallout: "leftRightArrowCallout",
4229
+ upDownArrowCallout: "upDownArrowCallout",
4230
+ quadArrowCallout: "quadArrowCallout",
4231
+ circularArrow: "circularArrow",
4232
+ leftCircularArrow: "leftCircularArrow",
4233
+ leftRightCircularArrow: "leftRightCircularArrow",
4234
+ swooshArrow: "swooshArrow",
4235
+ mathPlus: "mathPlus",
4236
+ mathMinus: "mathMinus",
4237
+ mathMultiply: "mathMultiply",
4238
+ mathDivide: "mathDivide",
4239
+ mathEqual: "mathEqual",
4240
+ mathNotEqual: "mathNotEqual",
4241
+ flowChartProcess: "flowChartProcess",
4242
+ flowChartAlternateProcess: "flowChartAlternateProcess",
4243
+ flowChartDecision: "flowChartDecision",
4244
+ flowChartInputOutput: "flowChartInputOutput",
4245
+ flowChartPredefinedProcess: "flowChartPredefinedProcess",
4246
+ flowChartInternalStorage: "flowChartInternalStorage",
4247
+ flowChartDocument: "flowChartDocument",
4248
+ flowChartMultidocument: "flowChartMultidocument",
4249
+ flowChartTerminator: "flowChartTerminator",
4250
+ flowChartPreparation: "flowChartPreparation",
4251
+ flowChartManualInput: "flowChartManualInput",
4252
+ flowChartManualOperation: "flowChartManualOperation",
4253
+ flowChartConnector: "flowChartConnector",
4254
+ flowChartOffpageConnector: "flowChartOffpageConnector",
4255
+ flowChartPunchedCard: "flowChartPunchedCard",
4256
+ flowChartPunchedTape: "flowChartPunchedTape",
4257
+ flowChartSummingJunction: "flowChartSummingJunction",
4258
+ flowChartOr: "flowChartOr",
4259
+ flowChartCollate: "flowChartCollate",
4260
+ flowChartSort: "flowChartSort",
4261
+ flowChartExtract: "flowChartExtract",
4262
+ flowChartMerge: "flowChartMerge",
4263
+ flowChartOfflineStorage: "flowChartOfflineStorage",
4264
+ flowChartOnlineStorage: "flowChartOnlineStorage",
4265
+ flowChartDelay: "flowChartDelay",
4266
+ flowChartMagneticTape: "flowChartMagneticTape",
4267
+ flowChartMagneticDisk: "flowChartMagneticDisk",
4268
+ flowChartMagneticDrum: "flowChartMagneticDrum",
4269
+ flowChartDisplay: "flowChartDisplay",
4270
+ irregularSeal1: "explosion12",
4271
+ irregularSeal2: "explosion14",
4272
+ star4: "star4",
4273
+ star5: "star5",
4274
+ star6: "star6",
4275
+ star7: "star7",
4276
+ star8: "star8",
4277
+ star10: "star10",
4278
+ star12: "star12",
4279
+ star16: "star16",
4280
+ star24: "star24",
4281
+ star32: "star32",
4282
+ ribbon2: "ribbonUp",
4283
+ ribbon: "ribbonDown",
4284
+ ellipseRibbon2: "curvedRibbonUp",
4285
+ ellipseRibbon: "curvedRibbonDown",
4286
+ leftRightRibbon: "leftRightRibbon",
4287
+ verticalScroll: "verticalScroll",
4288
+ horizontalScroll: "horizontalScroll",
4289
+ wave: "wave",
4290
+ doubleWave: "doubleWave",
4291
+ wedgeRectCallout: "rectangularCallout",
4292
+ wedgeRoundRectCallout: "roundedRectangularCallout",
4293
+ wedgeEllipseCallout: "ellipticalCallout",
4294
+ cloudCallout: "cloudCallout",
4295
+ borderCallout1: "lineCallout",
4296
+ borderCallout2: "bentLineCallout",
4297
+ borderCallout3: "doubleBentLineCallout",
4298
+ accentCallout1: "lineCalloutWithAccentBar",
4299
+ accentCallout2: "bentLineCalloutWithAccentBar",
4300
+ accentCallout3: "doubleBentLineCalloutWithAccentBar",
4301
+ callout1: "lineCalloutWithNoBorder",
4302
+ callout2: "bentLineCalloutWithNoBorder",
4303
+ callout3: "doubleBentLineCalloutWithNoBorder",
4304
+ accentBorderCallout1: "lineCalloutWithBorderAndAccentBar",
4305
+ accentBorderCallout2: "bentLineCalloutWithBorderAndAccentBar",
4306
+ accentBorderCallout3: "doubleBentLineCalloutWithBorderAndAccentBar",
4307
+ actionButtonBlank: "actionButtonBlank",
4308
+ actionButtonHome: "actionButtonHome",
4309
+ actionButtonHelp: "actionButtonHelp",
4310
+ actionButtonInformation: "actionButtonInformation",
4311
+ actionButtonForwardNext: "actionButtonForwardNext",
4312
+ actionButtonBackPrevious: "actionButtonBackPrevious",
4313
+ actionButtonEnd: "actionButtonEnd",
4314
+ actionButtonBeginning: "actionButtonBeginning",
4315
+ actionButtonReturn: "actionButtonReturn",
4316
+ actionButtonDocument: "actionButtonDocument",
4317
+ actionButtonSound: "actionButtonSound",
4318
+ actionButtonMovie: "actionButtonMovie",
4319
+ gear6: "gear6",
4320
+ gear9: "gear9",
4321
+ funnel: "funnel",
4322
+ cornerTabs: "cornerTabs",
4323
+ squareTabs: "squareTabs",
4324
+ plaqueTabs: "plaqueTabs",
4325
+ chartX: "chartX",
4326
+ chartStar: "chartStar",
4327
+ chartPlus: "chartPlus"
4328
+ };
4329
+ OOXML_DASH_TO_DOCX = {
4330
+ solid: "solid",
4331
+ dot: "dot",
4332
+ dash: "dash",
4333
+ lgDash: "longDash",
4334
+ dashDot: "dashDot",
4335
+ lgDashDot: "longDashDot",
4336
+ lgDashDotDot: "longDashDotDot",
4337
+ sysDash: "shortDash",
4338
+ sysDot: "shortDot",
4339
+ sysDashDot: "shortDashDot",
4340
+ sysDashDotDot: "shortDashDotDot"
4341
+ };
4342
+ }
4343
+ });
4344
+
4345
+ // src/renderers/docxjs/drawingGroup.ts
4346
+ function chooseMarkers(ir) {
4347
+ const strings = [];
4348
+ collectStrings(ir, strings, /* @__PURE__ */ new WeakSet());
4349
+ const unused = (base) => {
4350
+ for (let n = 0; ; n += 1) {
4351
+ const candidate = n === 0 ? base : `${base}-${n}`;
4352
+ if (!strings.some((value) => value.includes(candidate))) return candidate;
4353
+ }
4354
+ };
4355
+ return {
4356
+ noAltText: unused(DEFAULT_MARKERS.noAltText),
4357
+ textBox: unused(DEFAULT_MARKERS.textBox)
4358
+ };
4359
+ }
4360
+ function collectStrings(node, out, seen) {
4361
+ if (typeof node === "string") {
4362
+ out.push(node);
4363
+ return;
4364
+ }
4365
+ if (!node || typeof node !== "object") return;
4366
+ if (node instanceof ArrayBuffer || ArrayBuffer.isView(node)) return;
4367
+ if (seen.has(node)) return;
4368
+ seen.add(node);
4369
+ if (node instanceof Map) {
4370
+ for (const [key, value] of node) {
4371
+ collectStrings(key, out, seen);
4372
+ collectStrings(value, out, seen);
4373
+ }
4374
+ return;
4375
+ }
4376
+ if (node instanceof Set) {
4377
+ for (const value of node) collectStrings(value, out, seen);
4378
+ return;
4379
+ }
4380
+ for (const value of Object.values(node)) collectStrings(value, out, seen);
4381
+ }
4382
+ function createDrawingGroupFactory(shapes2, pictures, markers) {
4383
+ return (group, resources) => new shapes2.ShapeGroupRun(
4384
+ drawingGroupOptions(group, resources, pictures, markers)
4385
+ );
4386
+ }
4387
+ function drawingGroupOptions(group, resources, pictures, markers = DEFAULT_MARKERS) {
4388
+ const canvasWidth = group.canvasWidthEmu;
4389
+ const canvasHeight = group.canvasHeightEmu;
4390
+ const near = (value, edge) => Math.abs(value - edge) <= OVERFLOW_TOLERANCE_EMU;
4391
+ const bounds = unrotatedBounds(group.children);
4392
+ const fillsCanvas = bounds !== void 0 && near(bounds.left, 0) && near(bounds.top, 0) && near(bounds.right, canvasWidth) && near(bounds.bottom, canvasHeight);
4393
+ const union = {
4394
+ left: Math.min(0, bounds?.left ?? 0),
4395
+ top: Math.min(0, bounds?.top ?? 0),
4396
+ right: Math.max(canvasWidth, bounds?.right ?? 0),
4397
+ bottom: Math.max(canvasHeight, bounds?.bottom ?? 0)
4398
+ };
4399
+ const overflows = union.left < -OVERFLOW_TOLERANCE_EMU || union.top < -OVERFLOW_TOLERANCE_EMU || union.right > canvasWidth + OVERFLOW_TOLERANCE_EMU || union.bottom > canvasHeight + OVERFLOW_TOLERANCE_EMU;
4400
+ const widthEmu = overflows ? (union.right - union.left) * group.widthEmu / canvasWidth : group.widthEmu;
4401
+ const heightEmu = overflows ? (union.bottom - union.top) * group.heightEmu / canvasHeight : group.heightEmu;
4402
+ return {
4403
+ children: [
4404
+ ...fillsCanvas ? [] : [canvasPad(canvasWidth, canvasHeight)],
4405
+ ...group.children.map(
4406
+ (child, index) => groupChild(child, index, resources, pictures, markers)
4407
+ )
4408
+ ],
4409
+ transformation: {
4410
+ width: emuToPixels(widthEmu),
4411
+ height: emuToPixels(heightEmu)
4412
+ },
4413
+ // `||`, not `??`: docx treats '' as no description too, and would then
4414
+ // describe the diagram from the child names.
4415
+ altText: { name: "", description: group.altText || markers.noAltText },
4416
+ ...group.floating ? { floating: floatingOptions(group.floating) } : {}
4417
+ };
4418
+ }
4419
+ function groupChild(child, index, resources, pictures, markers) {
4420
+ return child.kind === "picture" ? pictureChild(child, index, pictures) : shapeChild(child, index, resources, markers);
4421
+ }
4422
+ function canvasPad(canvasWidthEmu, canvasHeightEmu) {
4423
+ return {
4424
+ type: "rectangle",
4425
+ transformation: {
4426
+ offset: { left: 0, top: 0 },
4427
+ width: emuToPixels(canvasWidthEmu),
4428
+ height: emuToPixels(canvasHeightEmu)
4429
+ },
4430
+ fill: "none",
4431
+ line: "none",
4432
+ decorative: true,
4433
+ altText: { name: "Canvas" }
4434
+ };
4435
+ }
4436
+ function childTransformation(frame) {
4437
+ const rotation2 = frame.rotationDegrees;
4438
+ return {
4439
+ offset: { left: emuToPixels(frame.xEmu), top: emuToPixels(frame.yEmu) },
4440
+ width: emuToPixels(frame.widthEmu),
4441
+ height: emuToPixels(frame.heightEmu),
4442
+ ...rotation2 !== void 0 && rotation2 % 360 !== 0 ? { rotation: rotation2 } : {},
4443
+ ...frame.flipHorizontal || frame.flipVertical ? {
4444
+ flip: {
4445
+ ...frame.flipHorizontal ? { horizontal: true } : {},
4446
+ ...frame.flipVertical ? { vertical: true } : {}
4447
+ }
4448
+ } : {}
4449
+ };
4450
+ }
4451
+ function shapeChild(shape, index, resources, markers) {
4452
+ const paragraphs = shape.text?.paragraphs ?? [];
4453
+ return {
4454
+ type: docxPresetShape(shape.geometry),
4455
+ transformation: childTransformation(shape.frame),
4456
+ fill: shapeFill(shape.fill),
4457
+ line: shapeLine(shape.outline),
4458
+ altText: {
4459
+ name: shape.name ?? `Shape ${index + 1}`,
4460
+ ...shape.isTextBox ? { title: markers.textBox } : {}
4461
+ },
4462
+ // Never `children: []`: docx would write an empty `w:txbxContent`, which
4463
+ // the schema does not allow.
4464
+ ...paragraphs.length > 0 ? {
4465
+ children: paragraphs.map(
4466
+ (paragraph2) => emitParagraph(paragraph2, resources)
4467
+ )
4468
+ } : {},
4469
+ ...shape.text ? { textOptions: textOptions(shape.text) } : {}
4470
+ };
4471
+ }
4472
+ function pictureChild(picture2, index, pictures) {
4473
+ const crop = picture2.crop;
4474
+ return {
4475
+ type: "picture",
4476
+ image: pictures(picture2.resourceId, {
4477
+ widthEmu: picture2.frame.widthEmu,
4478
+ heightEmu: picture2.frame.heightEmu
4479
+ }),
4480
+ transformation: childTransformation(picture2.frame),
4481
+ ...crop ? {
4482
+ crop: {
4483
+ ...crop.left !== void 0 ? { left: crop.left * 100 } : {},
4484
+ ...crop.top !== void 0 ? { top: crop.top * 100 } : {},
4485
+ ...crop.right !== void 0 ? { right: crop.right * 100 } : {},
4486
+ ...crop.bottom !== void 0 ? { bottom: crop.bottom * 100 } : {}
4487
+ }
4488
+ } : {},
4489
+ altText: {
4490
+ name: picture2.name ?? `Picture ${index + 1}`,
4491
+ ...picture2.altText ? { description: picture2.altText } : {}
4492
+ }
4493
+ };
4494
+ }
4495
+ function shapeFill(fill) {
4496
+ if (!fill || fill.kind === "none") return "none";
4497
+ return {
4498
+ type: "solid",
4499
+ color: fill.color.hex,
4500
+ ...fill.transparencyPercent !== void 0 ? { transparency: clamp(fill.transparencyPercent, 0, 100) } : {}
4501
+ };
4502
+ }
4503
+ function shapeLine(outline) {
4504
+ if (!outline || !outline.color) return "none";
4505
+ return {
4506
+ color: outline.color.hex,
4507
+ // An omitted width is office-open's omitted `w`, a hairline, where docx's
4508
+ // own default would be 1pt.
4509
+ width: outline.widthEmu !== void 0 ? clamp(outline.widthEmu / EMU_PER_POINT, 0, MAX_POINTS) : 0,
4510
+ ...outline.dash ? { dash: docxLineDash(outline.dash) } : {}
4511
+ };
4512
+ }
4513
+ function textOptions(text) {
4514
+ const insets = text.insetsEmu;
4515
+ const points = (emu) => clamp(emu / EMU_PER_POINT, 0, MAX_POINTS);
4516
+ const margins = insets ? {
4517
+ ...insets.top !== void 0 ? { top: points(insets.top) } : {},
4518
+ ...insets.right !== void 0 ? { right: points(insets.right) } : {},
4519
+ ...insets.bottom !== void 0 ? { bottom: points(insets.bottom) } : {},
4520
+ ...insets.left !== void 0 ? { left: points(insets.left) } : {}
4521
+ } : void 0;
4522
+ return {
4523
+ verticalAlignment: VERTICAL_ALIGNMENT[text.anchor ?? "top"],
4524
+ ...margins && Object.keys(margins).length > 0 ? { margins } : {}
4525
+ };
4526
+ }
4527
+ function unrotatedBounds(children) {
4528
+ if (children.length === 0) return void 0;
4529
+ let left = Infinity;
4530
+ let top = Infinity;
4531
+ let right = -Infinity;
4532
+ let bottom = -Infinity;
4533
+ for (const { frame } of children) {
4534
+ left = Math.min(left, frame.xEmu);
4535
+ top = Math.min(top, frame.yEmu);
4536
+ right = Math.max(right, frame.xEmu + frame.widthEmu);
4537
+ bottom = Math.max(bottom, frame.yEmu + frame.heightEmu);
4538
+ }
4539
+ return { left, top, right, bottom };
4540
+ }
4541
+ function clamp(value, low, high) {
4542
+ return Math.min(high, Math.max(low, value));
4543
+ }
4544
+ var DEFAULT_MARKERS, EMU_PER_POINT, MAX_POINTS, OVERFLOW_TOLERANCE_EMU, VERTICAL_ALIGNMENT;
4545
+ var init_drawingGroup = __esm({
4546
+ "src/renderers/docxjs/drawingGroup.ts"() {
4547
+ "use strict";
4548
+ init_units();
4549
+ init_emit();
4550
+ init_presetShapes();
4551
+ DEFAULT_MARKERS = {
4552
+ noAltText: "jto:no-alt-text",
4553
+ textBox: "jto:text-box"
4554
+ };
4555
+ EMU_PER_POINT = 12700;
4556
+ MAX_POINTS = 1584;
4557
+ OVERFLOW_TOLERANCE_EMU = 2;
4558
+ VERTICAL_ALIGNMENT = {
4559
+ top: "top",
4560
+ middle: "center",
4561
+ bottom: "bottom"
4562
+ };
4563
+ }
4564
+ });
4565
+
4566
+ // src/renderers/docxjs/drawingGroupRepair.ts
4567
+ import AdmZip3 from "adm-zip";
4568
+ function repairDrawingGroupsInBuffer(buffer, markers) {
4569
+ const textBox = new RegExp(
4570
+ `(<wps:cNvPr\\b[^>]*?) title="${escapeRegExp2(markers.textBox)}"(\\s*/>)<wps:cNvSpPr/>`,
4571
+ "g"
4572
+ );
4573
+ const noAltText = new RegExp(
4574
+ `(<wp:docPr\\b[^>]*?) descr="${escapeRegExp2(markers.noAltText)}"`,
4575
+ "g"
4576
+ );
4577
+ const zip = new AdmZip3(buffer);
4578
+ const parts = zip.getEntries().filter((entry) => DRAWING_PART.test(entry.entryName)).sort((a, b) => compareDrawingParts(a.entryName, b.entryName));
4579
+ const texts = new Map(
4580
+ parts.map((entry) => [entry, entry.getData().toString("utf8")])
4581
+ );
4582
+ let highest = 0;
4583
+ for (const xml of texts.values()) {
4584
+ for (const match of xml.matchAll(DOC_PR_ID)) {
4585
+ highest = Math.max(highest, Number(match[1]));
4586
+ }
4587
+ for (const match of xml.replace(GROUP, "").matchAll(CNVPR_ID)) {
4588
+ highest = Math.max(highest, Number(match[1]));
4589
+ }
4590
+ }
4591
+ let next = highest + 1;
4592
+ for (const [entry, xml] of texts) {
4593
+ if (!xml.includes("<wpg:wgp")) continue;
4594
+ const repaired = xml.replace(
4595
+ GROUP,
4596
+ (span) => span.replace(CHILD_ID, (_match, head) => `${head}id="${next++}"`).replace(
4597
+ ROTATION,
4598
+ (_match, head, value) => `${head}rot="${Math.round(Number(value))}"`
4599
+ ).replace(textBox, '$1$2<wps:cNvSpPr txBox="1"/>')
4600
+ ).replace(noAltText, "$1");
4601
+ texts.set(entry, repaired);
4602
+ zip.updateFile(entry, Buffer.from(repaired, "utf8"));
4603
+ }
4604
+ for (const [entry, xml] of texts) {
4605
+ if (xml.includes(markers.noAltText) || xml.includes(markers.textBox)) {
4606
+ throw new Error(
4607
+ `the drawing-group repair left a marker in ${entry.entryName}`
4608
+ );
4609
+ }
4610
+ }
4611
+ return zip.toBuffer();
4612
+ }
4613
+ var GROUP, DOC_PR_ID, CNVPR_ID, CHILD_ID, ROTATION, escapeRegExp2;
4614
+ var init_drawingGroupRepair = __esm({
4615
+ "src/renderers/docxjs/drawingGroupRepair.ts"() {
4616
+ "use strict";
4617
+ init_fixFloatingImageIds();
4618
+ GROUP = /<wpg:wgp\b[^>]*>[\s\S]*?<\/wpg:wgp>/g;
4619
+ DOC_PR_ID = /<wp:docPr\b[^>]*?\sid="(\d+)"/g;
4620
+ CNVPR_ID = /<(?:wps|pic|wpg):cNvPr\b[^>]*?\sid="(\d+)"/g;
4621
+ CHILD_ID = /(<(?:wps|pic|wpg):cNvPr\b[^>]*?\s)id="\d+"/g;
4622
+ ROTATION = /(<a:xfrm\b[^>]*?\s)rot="([^"]*)"/g;
4623
+ escapeRegExp2 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3802
4624
  }
3803
4625
  });
3804
4626
 
@@ -3827,18 +4649,32 @@ function createDocxJsRenderer() {
3827
4649
  format: "docx",
3828
4650
  capabilities: DOCXJS_CAPABILITIES,
3829
4651
  async render(ir, renderOptions) {
3830
- const resources = await prepareImages(
4652
+ await loadDocxCharts();
4653
+ const prepared = await prepareImages(
3831
4654
  ir,
3832
4655
  renderOptions?.svgRasterFallback
3833
4656
  );
4657
+ const markers = prepared.drawingGroups > 0 ? chooseMarkers(ir) : void 0;
4658
+ let resources = prepared.images;
4659
+ if (markers) {
4660
+ await shapes.load();
4661
+ resources = Object.assign(prepared.images, {
4662
+ drawingGroup: createDrawingGroupFactory(
4663
+ shapes.get(),
4664
+ prepared.pictureSource,
4665
+ markers
4666
+ )
4667
+ });
4668
+ }
3834
4669
  const document = buildDocument(ir, resources);
3835
4670
  const packed = await Packer.toBuffer(document);
3836
4671
  const fixed = fixFloatingImageIdsInBuffer(packed);
4672
+ const repaired = markers ? repairDrawingGroupsInBuffer(fixed, markers) : fixed;
3837
4673
  if (renderOptions?.deterministic === false)
3838
- return new Uint8Array(normalizeDocxCaseBuffer(fixed));
4674
+ return new Uint8Array(normalizeDocxCaseBuffer(repaired));
3839
4675
  return new Uint8Array(
3840
4676
  canonicalizeDocxBuffer(
3841
- fixed,
4677
+ repaired,
3842
4678
  resolveGenerationDate({
3843
4679
  deterministic: renderOptions?.deterministic,
3844
4680
  generatedAt: renderOptions?.generatedAt
@@ -3851,6 +4687,7 @@ function createDocxJsRenderer() {
3851
4687
  function buildDocument(ir, resources = /* @__PURE__ */ new Map()) {
3852
4688
  return new Document({
3853
4689
  styles: emitStyles(ir.styles),
4690
+ ...ir.theme ? { theme: themeOptions(ir.theme) } : {},
3854
4691
  sections: ir.sections.map(
3855
4692
  (section2, index) => sectionOptions(section2, resources, index === ir.sections.length - 1)
3856
4693
  ),
@@ -3882,6 +4719,21 @@ function buildDocument(ir, resources = /* @__PURE__ */ new Map()) {
3882
4719
  } : {}
3883
4720
  });
3884
4721
  }
4722
+ function themeOptions(theme) {
4723
+ const colors = {};
4724
+ for (const [slot, color] of Object.entries(theme.colors)) {
4725
+ if (color) colors[slot] = color.hex;
4726
+ }
4727
+ const fonts = {
4728
+ ...theme.headingFont ? { headings: theme.headingFont } : {},
4729
+ ...theme.bodyFont ? { body: theme.bodyFont } : {}
4730
+ };
4731
+ return {
4732
+ name: theme.name,
4733
+ ...Object.keys(colors).length > 0 ? { colors } : {},
4734
+ ...Object.keys(fonts).length > 0 ? { fonts } : {}
4735
+ };
4736
+ }
3885
4737
  function noteBodies(notes, resources) {
3886
4738
  const bodies = {};
3887
4739
  for (const note of notes) {
@@ -3919,7 +4771,7 @@ function numberingConfig(numbering) {
3919
4771
  };
3920
4772
  }
3921
4773
  async function prepareImages(ir, svgRasterFallback) {
3922
- const placements = collectImagePlacements(ir);
4774
+ const { placements, drawingGroups } = collectImagePlacements(ir);
3923
4775
  const jobs = [];
3924
4776
  for (const resource of ir.resources) {
3925
4777
  if (resource.kind !== "image" || resource.mediaType !== "svg") continue;
@@ -3967,16 +4819,55 @@ async function prepareImages(ir, svgRasterFallback) {
3967
4819
  });
3968
4820
  });
3969
4821
  }
3970
- return resources;
4822
+ const pictureSource = (resourceId, placement) => {
4823
+ const resource = ir.resources.find(
4824
+ (candidate) => candidate.kind === "image" && candidate.id === resourceId
4825
+ );
4826
+ if (!resource) {
4827
+ throw new Error(`no image was prepared for resource "${resourceId}"`);
4828
+ }
4829
+ const data = Buffer.from(resource.bytes);
4830
+ if (resource.mediaType !== "svg") {
4831
+ return {
4832
+ type: resource.mediaType,
4833
+ data
4834
+ };
4835
+ }
4836
+ const size = `${emuToPixels(placement.widthEmu)}x${emuToPixels(placement.heightEmu)}`;
4837
+ return {
4838
+ type: "svg",
4839
+ data,
4840
+ fallback: {
4841
+ type: "png",
4842
+ data: rasters.get(`${resourceId}:${size}`) ?? data
4843
+ }
4844
+ };
4845
+ };
4846
+ return { images: resources, pictureSource, drawingGroups };
3971
4847
  }
3972
4848
  function collectImagePlacements(ir) {
3973
4849
  const placements = /* @__PURE__ */ new Map();
4850
+ let drawingGroups = 0;
4851
+ const record = (resourceId, widthEmu, heightEmu) => {
4852
+ const key = `${emuToPixels(widthEmu)}x${emuToPixels(heightEmu)}`;
4853
+ const sizes = placements.get(resourceId) ?? /* @__PURE__ */ new Set();
4854
+ sizes.add(key);
4855
+ placements.set(resourceId, sizes);
4856
+ };
3974
4857
  const visitInline = (inline) => {
3975
4858
  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);
4859
+ record(inline.resourceId, inline.widthEmu, inline.heightEmu);
4860
+ return;
4861
+ }
4862
+ if (inline.kind === "drawingGroup") {
4863
+ drawingGroups += 1;
4864
+ for (const child of inline.children) {
4865
+ if (child.kind === "picture") {
4866
+ record(child.resourceId, child.frame.widthEmu, child.frame.heightEmu);
4867
+ } else {
4868
+ child.text?.paragraphs.forEach(visitBlock);
4869
+ }
4870
+ }
3980
4871
  return;
3981
4872
  }
3982
4873
  if (inline.kind === "hyperlink" || inline.kind === "revision") {
@@ -4007,7 +4898,7 @@ function collectImagePlacements(ir) {
4007
4898
  for (const note of [...ir.footnotes, ...ir.endnotes]) {
4008
4899
  note.children.forEach(visitBlock);
4009
4900
  }
4010
- return placements;
4901
+ return { placements, drawingGroups };
4011
4902
  }
4012
4903
  function sectionOptions(section2, resources, closesDocument = false) {
4013
4904
  const { page, columns } = section2.properties;
@@ -4120,17 +5011,26 @@ function coreProperties(ir) {
4120
5011
  ...metadata.custom?.length ? { customProperties: metadata.custom } : {}
4121
5012
  };
4122
5013
  }
4123
- var DOCXJS_RENDERER_ID, DOCXJS_CAPABILITIES;
5014
+ var DOCXJS_RENDERER_ID, shapes, DOCXJS_CAPABILITIES;
4124
5015
  var init_docxjs = __esm({
4125
5016
  "src/renderers/docxjs/index.ts"() {
4126
5017
  "use strict";
4127
5018
  init_styles2();
5019
+ init_charts();
4128
5020
  init_imageUtils();
4129
5021
  init_packageDocument();
4130
5022
  init_fixFloatingImageIds();
4131
5023
  init_emit();
5024
+ init_docxSubpath();
5025
+ init_drawingGroup();
5026
+ init_drawingGroupRepair();
4132
5027
  init_units();
4133
5028
  DOCXJS_RENDERER_ID = "docxjs";
5029
+ shapes = docxSubpath(
5030
+ "docx/shapes",
5031
+ "visual",
5032
+ () => import("docx/shapes")
5033
+ );
4134
5034
  DOCXJS_CAPABILITIES = /* @__PURE__ */ new Set([
4135
5035
  "paragraphs",
4136
5036
  "styles",
@@ -4143,8 +5043,10 @@ var init_docxjs = __esm({
4143
5043
  "images",
4144
5044
  "floating-images",
4145
5045
  "svg-images",
5046
+ "charts",
4146
5047
  "text-frames",
4147
5048
  "text-boxes",
5049
+ "drawing-groups",
4148
5050
  "toc",
4149
5051
  "cached-toc",
4150
5052
  "fields",
@@ -4165,183 +5067,331 @@ var init_docxjs = __esm({
4165
5067
  }
4166
5068
  });
4167
5069
 
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"));
5070
+ // src/renderers/office-open/chartLook.ts
5071
+ function seriesLook(chartType) {
5072
+ switch (chartType) {
5073
+ case "line":
5074
+ case "scatter":
5075
+ return { marker: MARKER, smooth: false };
5076
+ case "radar":
5077
+ return { marker: MARKER };
5078
+ case "column":
5079
+ case "bar":
5080
+ return { invertIfNegative: false };
5081
+ default:
5082
+ return {};
4182
5083
  }
4183
- for (const entry of zip.getEntries()) {
4184
- entry.header.timeval = WORKBOOK_TIMESTAMP;
5084
+ }
5085
+ function axesLook(chartType) {
5086
+ switch (chartType) {
5087
+ case "column":
5088
+ case "bar":
5089
+ case "line":
5090
+ case "area": {
5091
+ const horizontal = chartType === "bar";
5092
+ return {
5093
+ categoryAxis: {
5094
+ position: horizontal ? "l" : "b",
5095
+ ...NO_TICKS,
5096
+ line: AXIS_LINE
5097
+ },
5098
+ valueAxis: {
5099
+ position: horizontal ? "b" : "l",
5100
+ gridLine: GRIDLINES,
5101
+ ...NO_TICKS,
5102
+ lineVisible: false,
5103
+ crossBetween: chartType === "area" ? "midCat" : "between"
5104
+ }
5105
+ };
5106
+ }
5107
+ case "radar":
5108
+ return {
5109
+ categoryAxis: {
5110
+ position: "b",
5111
+ gridLine: GRIDLINES,
5112
+ ...NO_TICKS,
5113
+ majorTickMark: "cross",
5114
+ line: AXIS_LINE
5115
+ },
5116
+ valueAxis: {
5117
+ position: "l",
5118
+ gridLine: GRIDLINES,
5119
+ ...NO_TICKS,
5120
+ majorTickMark: "cross",
5121
+ line: AXIS_LINE,
5122
+ crossBetween: "between"
5123
+ }
5124
+ };
5125
+ case "scatter": {
5126
+ const line = { ...AXIS_LINE, color: SCATTER_AXIS_TINT };
5127
+ return {
5128
+ categoryAxis: {
5129
+ position: "b",
5130
+ ...NO_TICKS,
5131
+ line,
5132
+ crossBetween: "midCat"
5133
+ },
5134
+ valueAxis: {
5135
+ position: "l",
5136
+ gridLine: GRIDLINES,
5137
+ ...NO_TICKS,
5138
+ line,
5139
+ crossBetween: "midCat"
5140
+ }
5141
+ };
5142
+ }
5143
+ default:
5144
+ return {};
5145
+ }
5146
+ }
5147
+ function plotLook(chart) {
5148
+ const common = {
5149
+ ...axesLook(chart.chartType),
5150
+ autoTitleDeleted: !(chart.title && chart.showTitle !== false),
5151
+ plotAreaUnfilled: true
5152
+ };
5153
+ switch (chart.chartType) {
5154
+ case "column":
5155
+ return { ...common, dataBorder: "none", gapWidth: 219, overlap: -27 };
5156
+ case "bar":
5157
+ return { ...common, dataBorder: "none", gapWidth: 182, overlap: 0 };
5158
+ case "area":
5159
+ return { ...common, dataBorder: "none" };
5160
+ case "line":
5161
+ return { ...common, ...LINE_SERIES, lineMarkers: true };
5162
+ case "radar":
5163
+ return { ...common, ...LINE_SERIES, radarStyle: "marker" };
5164
+ case "scatter": {
5165
+ const labels = chart.series[0]?.labels ?? [];
5166
+ return {
5167
+ ...common,
5168
+ ...LINE_SERIES,
5169
+ lineWidthPoints: 1.5,
5170
+ scatterStyle: "lineMarker",
5171
+ scatterXValues: labels.map((_, index) => scatterX(labels, index))
5172
+ };
5173
+ }
5174
+ case "pie":
5175
+ case "doughnut":
5176
+ return {
5177
+ ...common,
5178
+ dataBorder: { widthPoints: 1.5, color: { scheme: "lt1" } },
5179
+ firstSliceAngle: 0,
5180
+ ...chart.chartType === "doughnut" ? { holeSize: 50 } : {}
5181
+ };
5182
+ default:
5183
+ return common;
4185
5184
  }
4186
- return new Uint8Array(zip.toBuffer());
4187
5185
  }
4188
- var WORKBOOK_TIMESTAMP;
4189
- var init_chartWorkbook = __esm({
4190
- "src/utils/chartWorkbook.ts"() {
5186
+ var TEXT_1_TINT, SCATTER_AXIS_TINT, HAIRLINE_POINTS, GRIDLINES, AXIS_LINE, NO_TICKS, LINE_SERIES, MARKER;
5187
+ var init_chartLook = __esm({
5188
+ "src/renderers/office-open/chartLook.ts"() {
4191
5189
  "use strict";
4192
- init_packageDocument();
4193
- WORKBOOK_TIMESTAMP = toDosTime(DEFAULT_GENERATION_DATE);
5190
+ init_chartValues();
5191
+ TEXT_1_TINT = { scheme: "tx1", lumMod: 15e3, lumOff: 85e3 };
5192
+ SCATTER_AXIS_TINT = {
5193
+ scheme: "tx1",
5194
+ lumMod: 25e3,
5195
+ lumOff: 75e3
5196
+ };
5197
+ HAIRLINE_POINTS = 0.75;
5198
+ GRIDLINES = {
5199
+ size: HAIRLINE_POINTS,
5200
+ color: TEXT_1_TINT
5201
+ };
5202
+ AXIS_LINE = {
5203
+ widthPoints: HAIRLINE_POINTS,
5204
+ color: TEXT_1_TINT
5205
+ };
5206
+ NO_TICKS = {
5207
+ majorTickMark: "none",
5208
+ minorTickMark: "none",
5209
+ tickLabelPosition: "nextTo"
5210
+ };
5211
+ LINE_SERIES = {
5212
+ lineWidthPoints: 2.25,
5213
+ lineCap: "rnd",
5214
+ lineJoin: "round",
5215
+ markerLineWidthPoints: HAIRLINE_POINTS
5216
+ };
5217
+ MARKER = { symbol: "circle", size: 5 };
4194
5218
  }
4195
5219
  });
4196
5220
 
4197
5221
  // src/renderers/office-open/chartParts.ts
4198
5222
  import {
4199
- CHART_WORKBOOK_CONTENT_TYPE,
4200
- chartWorkbookRelsXml,
4201
- matchChartParts,
4202
- spliceChartXml
5223
+ finishChartXml,
5224
+ matchChartParts
4203
5225
  } 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
5226
+ function chartInput(chart) {
5227
+ const roles = chartTextRoles(chart.textFont);
5228
+ const textFont = roles.text;
5229
+ const axisTitleFont = roles.axisTitle;
5230
+ const chartTitleFont = roles.title;
5231
+ const look = plotLook(chart);
5232
+ const categoryAxis = {
5233
+ ...look.categoryAxis,
5234
+ ...chart.categoryAxisTitle ? { title: chart.categoryAxisTitle, titleFont: axisTitleFont } : {}
5235
+ };
5236
+ const valueAxis = {
5237
+ ...look.valueAxis,
5238
+ ...chart.valueAxisTitle ? { title: chart.valueAxisTitle, titleFont: axisTitleFont } : {}
4212
5239
  };
4213
5240
  return {
5241
+ ...look,
4214
5242
  chartType: chart.chartType,
4215
5243
  series: chart.series,
4216
5244
  colors: chart.colors,
5245
+ ...chart.title && chart.showTitle !== false ? { title: chart.title } : {},
4217
5246
  textFont,
4218
5247
  titleFont: chartTitleFont,
4219
5248
  ...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
- } : {}
5249
+ ...Object.keys(categoryAxis).length > 0 ? { categoryAxis } : {},
5250
+ ...Object.keys(valueAxis).length > 0 ? { valueAxis } : {}
4229
5251
  };
4230
5252
  }
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;
5253
+ function finishChartParts(zip, charts2) {
5254
+ if (charts2.length === 0) return;
4249
5255
  const parts = zip.getEntries().map((entry) => entry.entryName).map((name) => name.match(/^word\/charts\/chart(\d+)\.xml$/)).filter((match) => match !== null).map(
4250
5256
  (match) => [
4251
5257
  Number(match[1]),
4252
5258
  zip.getEntry(match[0]).getData().toString("utf8")
4253
5259
  ]
4254
5260
  );
4255
- for (const { ordinal, xml, chart } of matchChartParts(parts, charts)) {
4256
- const workbookName = `chart${ordinal}.xlsx`;
5261
+ const inputs = charts2.map(chartInput);
5262
+ for (const { ordinal, xml, chart } of matchChartParts(parts, inputs)) {
5263
+ const finished = finishChartXml(xml, chart);
5264
+ if (finished === xml) continue;
4257
5265
  zip.updateFile(
4258
5266
  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")
5267
+ Buffer.from(finished, "utf8")
4268
5268
  );
4269
5269
  }
4270
- declareWorkbookContentType(zip);
4271
5270
  }
4272
- var CHART_TITLE_SCALE;
4273
5271
  var init_chartParts = __esm({
4274
5272
  "src/renderers/office-open/chartParts.ts"() {
4275
5273
  "use strict";
4276
- init_chartWorkbook();
4277
- CHART_TITLE_SCALE = 1.4;
5274
+ init_chartText();
5275
+ init_chartLook();
4278
5276
  }
4279
5277
  });
4280
5278
 
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) {
5279
+ // src/renderers/office-open/themePart.ts
5280
+ function replaceOnce(xml, pattern, replacer, what) {
5281
+ const count = [...xml.matchAll(pattern)].length;
5282
+ if (count !== 1) {
4308
5283
  throw new Error(
4309
- `the office-open renderer placed ${tocs.length} table(s) of contents in table cells and found ${spliced.size}`
5284
+ `office-open's theme part has ${count} ${what}, expected one`
4310
5285
  );
4311
5286
  }
5287
+ return xml.replace(pattern, replacer);
5288
+ }
5289
+ function spliceTheme(zip, theme) {
5290
+ const entry = zip.getEntry(THEME_PART);
5291
+ if (!entry) throw new Error(`office-open wrote no ${THEME_PART}`);
5292
+ let xml = entry.getData().toString("utf8");
5293
+ const name = esc(theme.name);
5294
+ xml = renamed(xml, "theme", name);
5295
+ const colors = Object.entries(theme.colors);
5296
+ if (colors.some(([, color]) => color)) {
5297
+ xml = renamed(xml, "clrScheme", name);
5298
+ for (const [slot, color] of colors) {
5299
+ if (!color) continue;
5300
+ const element = SCHEME_ELEMENT[slot];
5301
+ xml = replaceOnce(
5302
+ xml,
5303
+ new RegExp(`<a:${element}>[\\s\\S]*?</a:${element}>`, "g"),
5304
+ () => `<a:${element}><a:srgbClr val="${color.hex}"/></a:${element}>`,
5305
+ `a:${element}`
5306
+ );
5307
+ }
5308
+ }
5309
+ if (theme.headingFont || theme.bodyFont) {
5310
+ xml = renamed(xml, "fontScheme", name);
5311
+ for (const [collection, key, officeFace] of OFFICE_FACES) {
5312
+ const family = theme[key];
5313
+ if (family === void 0 || family === officeFace) continue;
5314
+ xml = replaceOnce(
5315
+ xml,
5316
+ new RegExp(`(<a:${collection}>)<a:latin\\b[^>]*/>`, "g"),
5317
+ (_match, open) => `${open}<a:latin typeface="${esc(family)}"/>`,
5318
+ `a:${collection} a:latin`
5319
+ );
5320
+ }
5321
+ }
5322
+ zip.updateFile(THEME_PART, Buffer.from(xml, "utf8"));
4312
5323
  }
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"
5324
+ var THEME_PART, SCHEME_ELEMENT, OFFICE_FACES, esc, renamed;
5325
+ var init_themePart = __esm({
5326
+ "src/renderers/office-open/themePart.ts"() {
5327
+ "use strict";
5328
+ THEME_PART = "word/theme/theme1.xml";
5329
+ SCHEME_ELEMENT = {
5330
+ dark1: "dk1",
5331
+ light1: "lt1",
5332
+ dark2: "dk2",
5333
+ light2: "lt2",
5334
+ accent1: "accent1",
5335
+ accent2: "accent2",
5336
+ accent3: "accent3",
5337
+ accent4: "accent4",
5338
+ accent5: "accent5",
5339
+ accent6: "accent6",
5340
+ hyperlink: "hlink",
5341
+ followedHyperlink: "folHlink"
5342
+ };
5343
+ OFFICE_FACES = [
5344
+ ["majorFont", "headingFont", "Calibri Light"],
5345
+ ["minorFont", "bodyFont", "Calibri"]
5346
+ ];
5347
+ esc = (value) => value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5348
+ renamed = (xml, element, name) => replaceOnce(
5349
+ xml,
5350
+ new RegExp(`(<a:${element}\\b[^>]*?\\bname=")[^"]*(")`, "g"),
5351
+ (_match, open, close) => `${open}${name}${close}`,
5352
+ `a:${element} name`
4324
5353
  );
4325
5354
  }
4326
- return { from, to: close + "</w:p>".length };
5355
+ });
5356
+
5357
+ // src/utils/chartWorkbook.ts
5358
+ import AdmZip4 from "adm-zip";
5359
+ import { chartWorkbookParts } from "@json-to-office/shared/rendering";
5360
+ import {
5361
+ CHART_WORKBOOK_SHEET_NAME,
5362
+ categoryReference,
5363
+ columnLetter,
5364
+ seriesNameReference,
5365
+ seriesValueReference
5366
+ } from "@json-to-office/shared/rendering";
5367
+ function buildChartWorkbook(series, options = {}) {
5368
+ const zip = new AdmZip4();
5369
+ for (const [name, content] of chartWorkbookParts(series, options)) {
5370
+ zip.addFile(name, Buffer.from(content, "utf8"));
5371
+ }
5372
+ for (const entry of zip.getEntries()) {
5373
+ entry.header.timeval = WORKBOOK_TIMESTAMP;
5374
+ }
5375
+ return new Uint8Array(zip.toBuffer());
4327
5376
  }
4328
- var TABLE_PARTS;
4329
- var init_cellTocs = __esm({
4330
- "src/renderers/office-open/cellTocs.ts"() {
5377
+ var WORKBOOK_TIMESTAMP;
5378
+ var init_chartWorkbook = __esm({
5379
+ "src/utils/chartWorkbook.ts"() {
4331
5380
  "use strict";
4332
- TABLE_PARTS = /^word\/(?:document|header\d+|footer\d+)\.xml$/;
5381
+ init_packageDocument();
5382
+ WORKBOOK_TIMESTAMP = toDosTime(DEFAULT_GENERATION_DATE);
4333
5383
  }
4334
5384
  });
4335
5385
 
4336
5386
  // src/renderers/office-open/emit.ts
4337
- import { assertNever as assertNever2 } from "@json-to-office/shared/rendering";
5387
+ import { assertNever as assertNever3 } from "@json-to-office/shared/rendering";
5388
+ import { chartLook, withSeriesLook } from "@json-to-office/shared/rendering";
4338
5389
  function emptyContext() {
4339
5390
  let next = 1;
4340
5391
  return {
4341
5392
  pictures: /* @__PURE__ */ new Map(),
4342
5393
  nextDrawingId: () => next++,
4343
- charts: [],
4344
- cellTocs: []
5394
+ charts: []
4345
5395
  };
4346
5396
  }
4347
5397
  function simpleField(instruction, cachedText) {
@@ -4382,18 +5432,19 @@ function runProperties2(formatting) {
4382
5432
  if (formatting.doubleStrike !== void 0) {
4383
5433
  out.doubleStrike = formatting.doubleStrike;
4384
5434
  }
4385
- if (formatting.superScript) out.superScript = true;
4386
- if (formatting.subScript) out.subScript = true;
5435
+ if (formatting.superScript) out.verticalAlign = "superscript";
5436
+ if (formatting.subScript) out.verticalAlign = "subscript";
4387
5437
  if (formatting.smallCaps !== void 0) out.smallCaps = formatting.smallCaps;
4388
5438
  if (formatting.allCaps !== void 0) out.allCaps = formatting.allCaps;
4389
- if (formatting.highlight !== void 0) out.highlight = formatting.highlight;
5439
+ if (formatting.highlight !== void 0) {
5440
+ out.highlight = formatting.highlight;
5441
+ }
4390
5442
  if (formatting.shading) out.shading = shading(formatting.shading);
4391
5443
  if (formatting.scalePercent !== void 0)
4392
5444
  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;
5445
+ const stated = formatting.characterSpacingTwentieths;
5446
+ if (stated !== void 0 && stated !== 0) {
5447
+ out.characterSpacing = twips(stated);
4397
5448
  }
4398
5449
  if (formatting.language) out.language = { value: formatting.language };
4399
5450
  if (formatting.noProof !== void 0) out.noProof = formatting.noProof;
@@ -4423,7 +5474,9 @@ function inlineChildren2(children, ctx = emptyContext()) {
4423
5474
  switch (child.kind) {
4424
5475
  case "lineBreak":
4425
5476
  pendingBreaks += 1;
4426
- if (child.clear && child.clear !== "none") pendingClear = child.clear;
5477
+ if (child.clear && child.clear !== "none") {
5478
+ pendingClear = child.clear;
5479
+ }
4427
5480
  break;
4428
5481
  case "text":
4429
5482
  out.push({
@@ -4461,7 +5514,7 @@ function inlineChildren2(children, ctx = emptyContext()) {
4461
5514
  case "drawingGroup": {
4462
5515
  const pending = breakOption();
4463
5516
  if (pending.break) out.push(pending);
4464
- out.push({ wpgGroup: drawingGroupOptions(child, ctx) });
5517
+ out.push({ wpgGroup: drawingGroupOptions2(child, ctx) });
4465
5518
  break;
4466
5519
  }
4467
5520
  case "hyperlink":
@@ -4491,8 +5544,9 @@ function inlineChildren2(children, ctx = emptyContext()) {
4491
5544
  out.push({ wpsShape: shapeOptions(child, ctx) });
4492
5545
  break;
4493
5546
  case "chart":
4494
- ctx.charts?.push(child);
4495
- out.push({ chart: chartOptions(child, ctx) });
5547
+ out.push({
5548
+ chart: chartOptions(child, ctx, ctx.charts?.push(child) ?? 1)
5549
+ });
4496
5550
  break;
4497
5551
  case "noteReference":
4498
5552
  out.push(
@@ -4500,7 +5554,7 @@ function inlineChildren2(children, ctx = emptyContext()) {
4500
5554
  );
4501
5555
  break;
4502
5556
  default:
4503
- assertNever2(child, "DocxIrInline");
5557
+ assertNever3(child, "DocxIrInline");
4504
5558
  }
4505
5559
  }
4506
5560
  return out;
@@ -4541,8 +5595,11 @@ function floatingOptions2(floating) {
4541
5595
  ...floating.vertical ? { verticalPosition: position(floating.vertical) } : {},
4542
5596
  ...floating.wrap ? {
4543
5597
  wrap: {
4544
- type: WRAP_TYPE2[floating.wrap.type],
4545
- ...floating.wrap.side ? { side: floating.wrap.side } : {}
5598
+ // Named, as OOXML names them; 0.11 took docx.js's numbers.
5599
+ type: floating.wrap.type,
5600
+ ...floating.wrap.side ? {
5601
+ side: floating.wrap.side
5602
+ } : {}
4546
5603
  }
4547
5604
  } : {},
4548
5605
  ...floating.margins ? {
@@ -4569,66 +5626,92 @@ function imageMedia(ctx, resourceId, placement) {
4569
5626
  }
4570
5627
  function pictureOptions(image, ctx) {
4571
5628
  const media = imageMedia(ctx, image.resourceId, image);
4572
- return {
4573
- type: media.type,
5629
+ const source = media.type === "svg" ? {
5630
+ type: "svg",
4574
5631
  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 } : {},
5632
+ // The SVG slot always carries a raster: `prepareImages` falls back
5633
+ // to the vector bytes when none could be made.
5634
+ fallback: media.fallback ?? {
5635
+ type: "png",
5636
+ data: media.data
5637
+ }
5638
+ } : { type: media.type, data: media.data };
5639
+ return {
5640
+ ...source,
4578
5641
  // Raw numbers are EMUs in @office-open/docx. Passing pixels here makes a
4579
5642
  // normal image only a few hundred EMUs wide, effectively a dot.
4580
5643
  transformation: { width: image.widthEmu, height: image.heightEmu },
4581
5644
  // 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()) },
5645
+ // counter. `name` stays empty, which is what the backend falls back to.
5646
+ altText: { id: String(ctx.nextDrawingId()), name: "" },
4585
5647
  ...image.floating ? { floating: floatingOptions2(image.floating) } : {}
4586
5648
  // No `description` or `title`: no DOCX this pipeline has produced carries
4587
5649
  // `wp:docPr` alt text, and the compiler warns so the gap is visible rather
4588
5650
  // than silent.
4589
5651
  };
4590
5652
  }
4591
- function drawingGroupOptions(group, ctx) {
5653
+ function drawingGroupOptions2(group, ctx) {
4592
5654
  const id = ctx.nextDrawingId();
4593
5655
  return {
4594
5656
  altText: {
4595
5657
  id: String(id),
5658
+ name: "",
4596
5659
  ...group.altText ? { description: group.altText } : {}
4597
5660
  },
4598
5661
  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)),
5662
+ childOffsetX: 0,
5663
+ childOffsetY: 0,
5664
+ childExtentWidth: group.canvasWidthEmu,
5665
+ childExtentHeight: group.canvasHeightEmu,
5666
+ children: group.children.map((child) => groupChild2(child, ctx)),
4602
5667
  ...group.floating ? { floating: floatingOptions2(group.floating) } : {}
4603
5668
  };
4604
5669
  }
4605
- function chartOptions(chart, ctx) {
5670
+ function chartOptions(chart, ctx, ordinal) {
4606
5671
  const id = ctx.nextDrawingId();
5672
+ const input = chartInput(chart);
5673
+ const look = chartLook(input);
4607
5674
  return {
4608
5675
  type: chart.chartType,
4609
5676
  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 } : {},
5677
+ series: chart.series.map(
5678
+ (entry, index) => withSeriesLook(
5679
+ {
5680
+ name: entry.name ?? `Series ${index + 1}`,
5681
+ values: entry.values,
5682
+ ...seriesLook(chart.chartType)
5683
+ },
5684
+ look.series[index]
5685
+ )
5686
+ ),
5687
+ ...look.chart,
4615
5688
  ...chart.showLegend !== void 0 ? { showLegend: chart.showLegend } : {},
5689
+ externalData: {
5690
+ ...look.chart.externalData,
5691
+ fileName: `chart${ordinal}.xlsx`,
5692
+ // A scatter chart's column A holds the x values its `c:xVal` caches.
5693
+ data: buildChartWorkbook(
5694
+ chart.series,
5695
+ input.scatterXValues ? { categoryValues: input.scatterXValues } : {}
5696
+ )
5697
+ },
4616
5698
  transformation: { width: chart.widthEmu, height: chart.heightEmu },
4617
5699
  altText: {
4618
5700
  id: String(id),
5701
+ name: "",
4619
5702
  ...chart.altText ? { description: chart.altText } : {}
4620
5703
  },
4621
5704
  ...chart.floating ? { floating: floatingOptions2(chart.floating) } : {}
4622
5705
  };
4623
5706
  }
4624
- function groupChild(child, ctx) {
5707
+ function groupChild2(child, ctx) {
4625
5708
  return child.kind === "shape" ? groupShape(child, ctx) : groupPicture(child, ctx);
4626
5709
  }
4627
5710
  function groupShape(shape, ctx) {
4628
5711
  const id = ctx.nextDrawingId();
4629
5712
  return {
4630
5713
  type: "wps",
4631
- transformation: childTransformation(shape.frame),
5714
+ transformation: childTransformation2(shape.frame),
4632
5715
  data: {
4633
5716
  nonVisualProperties: {
4634
5717
  id,
@@ -4637,7 +5720,7 @@ function groupShape(shape, ctx) {
4637
5720
  // to hold text, and it changes how the object behaves on selection.
4638
5721
  ...shape.isTextBox ? { textBox: "1" } : {}
4639
5722
  },
4640
- presetGeometry: { preset: shape.geometry },
5723
+ geometry: shape.geometry,
4641
5724
  ...shape.fill ? { fill: drawingFill(shape.fill) } : {},
4642
5725
  ...shape.outline ? { outline: drawingOutline(shape.outline) } : {},
4643
5726
  children: (shape.text?.paragraphs ?? []).map(
@@ -4663,7 +5746,7 @@ function groupPicture(picture2, ctx) {
4663
5746
  fileName: media.fallbackFileName
4664
5747
  }
4665
5748
  } : {},
4666
- transformation: childTransformation(picture2.frame),
5749
+ transformation: childTransformation2(picture2.frame),
4667
5750
  nonVisualProperties: {
4668
5751
  id,
4669
5752
  ...picture2.name ? { name: picture2.name } : {},
@@ -4672,11 +5755,7 @@ function groupPicture(picture2, ctx) {
4672
5755
  ...picture2.crop ? { sourceRectangle: sourceRectangle(picture2.crop) } : {}
4673
5756
  };
4674
5757
  }
4675
- function childTransformation(frame) {
4676
- const flip = {
4677
- ...frame.flipHorizontal ? { horizontal: true } : {},
4678
- ...frame.flipVertical ? { vertical: true } : {}
4679
- };
5758
+ function childTransformation2(frame) {
4680
5759
  return {
4681
5760
  offset: {
4682
5761
  emus: { x: frame.xEmu, y: frame.yEmu },
@@ -4692,7 +5771,8 @@ function childTransformation(frame) {
4692
5771
  },
4693
5772
  // Degrees: the backend multiplies by 60000 on the way into `@rot`.
4694
5773
  ...frame.rotationDegrees !== void 0 ? { rotation: frame.rotationDegrees } : {},
4695
- ...Object.keys(flip).length > 0 ? { flip } : {}
5774
+ ...frame.flipHorizontal ? { flipHorizontal: true } : {},
5775
+ ...frame.flipVertical ? { flipVertical: true } : {}
4696
5776
  };
4697
5777
  }
4698
5778
  function drawingFill(fill) {
@@ -4700,7 +5780,6 @@ function drawingFill(fill) {
4700
5780
  return {
4701
5781
  type: "solid",
4702
5782
  color: {
4703
- type: "rgb",
4704
5783
  value: fill.color.hex,
4705
5784
  // Transparency becomes an alpha transform on the colour. DrawingML
4706
5785
  // states *opacity*, so the value is the complement of what the author
@@ -4742,7 +5821,7 @@ function sourceRectangle(crop) {
4742
5821
  function shapeOptions(shape, ctx) {
4743
5822
  const id = String(ctx.nextDrawingId());
4744
5823
  return {
4745
- altText: { id },
5824
+ altText: { id, name: "" },
4746
5825
  children: shape.children.map((child) => paragraph(child, ctx)),
4747
5826
  // Raw transformation numbers are EMUs in @office-open/docx, while the IR
4748
5827
  // deliberately stores native shape dimensions in pixels.
@@ -4753,7 +5832,7 @@ function shapeOptions(shape, ctx) {
4753
5832
  ...shape.fill ? {
4754
5833
  fill: {
4755
5834
  type: "solid",
4756
- color: { type: "rgb", value: shape.fill.hex }
5835
+ color: { value: shape.fill.hex }
4757
5836
  }
4758
5837
  } : {},
4759
5838
  ...shape.outline ? { outline: drawingOutline(shape.outline) } : {},
@@ -4858,13 +5937,19 @@ function frameOptions2(frame) {
4858
5937
  ...frame.anchorLock !== void 0 ? { anchorLock: frame.anchorLock } : {},
4859
5938
  ...frame.rule ? { rule: frame.rule } : {}
4860
5939
  };
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
- } : {
5940
+ if (frame.xTwips !== void 0 || frame.yTwips !== void 0) {
5941
+ return {
5942
+ type: "absolute",
5943
+ position: { x: frame.xTwips ?? 0, y: frame.yTwips ?? 0 },
5944
+ ...base
5945
+ };
5946
+ }
5947
+ return {
4866
5948
  type: "alignment",
4867
- alignment: { x: frame.xAlign, y: frame.yAlign },
5949
+ alignment: {
5950
+ x: frame.xAlign,
5951
+ y: frame.yAlign
5952
+ },
4868
5953
  ...base
4869
5954
  };
4870
5955
  }
@@ -4885,7 +5970,7 @@ function block(value, ctx = emptyContext()) {
4885
5970
  case "toc":
4886
5971
  return { toc: tableOfContents(value) };
4887
5972
  default:
4888
- return assertNever2(value, "DocxIrBlock");
5973
+ return assertNever3(value, "DocxIrBlock");
4889
5974
  }
4890
5975
  }
4891
5976
  function tocEntry(entry) {
@@ -4904,6 +5989,7 @@ function tableOfContents(value) {
4904
5989
  headingStyleRange: `${value.headingRange.from}-${value.headingRange.to}`
4905
5990
  } : {},
4906
5991
  ...value.styleLevels?.length ? {
5992
+ // `StyleLevel` is a class the backend only reads the two fields of.
4907
5993
  stylesWithLevels: value.styleLevels.map((style) => ({
4908
5994
  styleName: style.styleName,
4909
5995
  level: style.level
@@ -4925,14 +6011,16 @@ function table(value, ctx) {
4925
6011
  // unstated columns comes out fractional, and the XML would carry it
4926
6012
  // verbatim; floored here, as the docxjs backend floors it.
4927
6013
  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"
6014
+ type: value.width.kind === "twips" ? "twips" : value.width.kind === "percent" ? "percent" : "auto"
4929
6015
  },
4930
6016
  layout: value.layout,
4931
6017
  // An empty grid is a table with nothing to say about its columns, which is
4932
6018
  // not the same as one whose columns are all zero wide. The compiler hands
4933
6019
  // over every grid in twips; floored for the same reason as the width.
4934
6020
  ...value.columnGrid.values.length > 0 ? { columnWidths: value.columnGrid.values.map(twips) } : {},
4935
- ...value.alignment ? { alignment: alignment(value.alignment) } : {},
6021
+ ...value.alignment ? {
6022
+ alignment: alignment(value.alignment)
6023
+ } : {},
4936
6024
  ...value.borders ? { borders: borders2(value.borders) } : {},
4937
6025
  ...value.cellMargins ? { margins: cellMargins(value.cellMargins) } : {},
4938
6026
  ...value.floating ? { float: tableFloat2(value.floating) } : {}
@@ -4977,52 +6065,29 @@ function tableRow(row, ctx) {
4977
6065
  }
4978
6066
  function tableCell(cell, ctx) {
4979
6067
  const children = cellChildren2(cell, ctx);
4980
- if (cell.children[cell.children.length - 1]?.kind !== "paragraph")
6068
+ if (cell.children[cell.children.length - 1]?.kind === "toc")
4981
6069
  children.push({ paragraph: { children: [] } });
4982
6070
  return {
4983
6071
  children,
4984
- ...cell.widthTwips !== void 0 ? { width: { size: twips(cell.widthTwips), type: "dxa" } } : {},
4985
- ...cell.verticalAlign ? { verticalAlign: cell.verticalAlign } : {},
6072
+ ...cell.widthTwips !== void 0 ? { width: { size: twips(cell.widthTwips), type: "twips" } } : {},
6073
+ ...cell.verticalAlign ? {
6074
+ verticalAlign: cell.verticalAlign
6075
+ } : {},
4986
6076
  ...cell.shading ? { shading: shading(cell.shading) } : {},
4987
6077
  ...cell.margins ? { margins: cellMargins(cell.margins) } : {},
4988
6078
  ...cell.borders ? { borders: borders2(cell.borders) } : {},
4989
6079
  ...cell.columnSpan !== void 0 ? { columnSpan: cell.columnSpan } : {},
4990
6080
  ...cell.rowSpan !== void 0 ? { verticalMerge: cell.rowSpan } : {},
4991
- ...cell.textDirection ? { textDirection: cell.textDirection } : {}
6081
+ ...cell.textDirection ? {
6082
+ textDirection: cell.textDirection
6083
+ } : {}
4992
6084
  };
4993
6085
  }
4994
6086
  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 }] } };
6087
+ return cell.children.map((child) => block(child, ctx));
5023
6088
  }
5024
6089
  function cellMargins(margins) {
5025
- const side = (value) => value === void 0 ? void 0 : { size: twips(value), type: "dxa" };
6090
+ const side = (value) => value === void 0 ? void 0 : { size: twips(value), type: "twips" };
5026
6091
  const out = {};
5027
6092
  for (const [name, value] of [
5028
6093
  ["top", margins.topTwips],
@@ -5065,39 +6130,43 @@ function section(value, ctx, closesDocument = false) {
5065
6130
  properties: {
5066
6131
  ...value.properties.type ? { type: value.properties.type } : {},
5067
6132
  ...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
- } : {}
6133
+ // Orientation is implied by the width/height pair, which is how this
6134
+ // pipeline has always expressed it; stating it as well changes `w:pgSz`.
6135
+ pageSize: {
6136
+ width: twips(page.widthTwips),
6137
+ height: twips(page.heightTwips),
6138
+ ...page.code !== void 0 ? { code: page.code } : {}
5098
6139
  },
6140
+ pageMargin: {
6141
+ top: twips(page.margins.topTwips),
6142
+ right: twips(page.margins.rightTwips),
6143
+ bottom: twips(page.margins.bottomTwips),
6144
+ left: twips(page.margins.leftTwips),
6145
+ ...page.margins.headerTwips !== void 0 ? { header: twips(page.margins.headerTwips) } : {},
6146
+ ...page.margins.footerTwips !== void 0 ? { footer: twips(page.margins.footerTwips) } : {},
6147
+ ...page.margins.gutterTwips !== void 0 ? { gutter: twips(page.margins.gutterTwips) } : {}
6148
+ },
6149
+ ...value.properties.pageNumbers ? {
6150
+ pageNumberType: {
6151
+ ...value.properties.pageNumbers.start !== void 0 ? { start: value.properties.pageNumbers.start } : {},
6152
+ ...value.properties.pageNumbers.formatType ? {
6153
+ format: value.properties.pageNumbers.formatType
6154
+ } : {}
6155
+ }
6156
+ } : {},
6157
+ ...value.properties.borders ? {
6158
+ pageBorders: {
6159
+ ...value.properties.borders.display ? {
6160
+ display: value.properties.borders.display
6161
+ } : {},
6162
+ ...value.properties.borders.offsetFrom ? {
6163
+ offsetFrom: value.properties.borders.offsetFrom
6164
+ } : {},
6165
+ ...value.properties.borders.borders ? borders2(value.properties.borders.borders) : {}
6166
+ }
6167
+ } : {},
5099
6168
  ...columns ? {
5100
- column: {
6169
+ columns: {
5101
6170
  count: columns.count,
5102
6171
  ...columns.spaceTwips !== void 0 ? { space: twips(columns.spaceTwips) } : {},
5103
6172
  ...columns.separator !== void 0 ? { separate: columns.separator } : {},
@@ -5141,6 +6210,8 @@ function sectionChildren2(value, ctx, closesDocument = false) {
5141
6210
  spacing: { before: 0, after: 0, line: 20, lineRule: "exact" }
5142
6211
  }
5143
6212
  });
6213
+ if (!closesDocument && !bookmark?.closes && last?.kind === "toc")
6214
+ blocks.push({ paragraph: { children: [] } });
5144
6215
  if (!bookmark) return blocks;
5145
6216
  return [
5146
6217
  ...bookmark.opens ? [{ bookmarkStart: { id: bookmark.id, name: bookmark.name } }] : [],
@@ -5151,15 +6222,19 @@ function sectionChildren2(value, ctx, closesDocument = false) {
5151
6222
  function numberingConfig2(numbering) {
5152
6223
  return {
5153
6224
  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: {
6225
+ levels: numbering.levels.map(
6226
+ (level) => ({
6227
+ level: level.level,
6228
+ format: level.format,
6229
+ text: level.text,
6230
+ ...level.alignment ? { alignment: alignment(level.alignment) } : {},
6231
+ ...level.suffix ? { suffix: level.suffix } : {},
6232
+ // Always stated. docx.js writes `w:start w:val="1"` for a level that
6233
+ // names none, and so did this backend until 0.14, which writes no
6234
+ // `w:start` at all when it is not given one; Word and LibreOffice then
6235
+ // count every such list from 0.
6236
+ start: level.start ?? 1,
6237
+ ...level.paragraphStyleId ? { paragraphStyle: level.paragraphStyleId } : {},
5163
6238
  ...level.indent ? {
5164
6239
  paragraph: {
5165
6240
  indent: {
@@ -5169,25 +6244,22 @@ function numberingConfig2(numbering) {
5169
6244
  }
5170
6245
  } : {},
5171
6246
  ...level.run ? { run: runProperties2(level.run) } : {}
5172
- }
5173
- }))
6247
+ })
6248
+ )
5174
6249
  };
5175
6250
  }
5176
- var WRAP_TYPE2, BODY_ANCHOR;
6251
+ var BODY_ANCHOR;
5177
6252
  var init_emit2 = __esm({
5178
6253
  "src/renderers/office-open/emit.ts"() {
5179
6254
  "use strict";
5180
6255
  init_units();
5181
- WRAP_TYPE2 = {
5182
- none: 0,
5183
- square: 1,
5184
- tight: 2,
5185
- topAndBottom: 3
5186
- };
6256
+ init_chartWorkbook();
6257
+ init_chartLook();
6258
+ init_chartParts();
5187
6259
  BODY_ANCHOR = {
5188
- top: "t",
5189
- middle: "ctr",
5190
- bottom: "b"
6260
+ top: "top",
6261
+ middle: "center",
6262
+ bottom: "bottom"
5191
6263
  };
5192
6264
  }
5193
6265
  });
@@ -5247,37 +6319,49 @@ var init_styles3 = __esm({
5247
6319
  var office_open_exports = {};
5248
6320
  __export(office_open_exports, {
5249
6321
  OFFICE_OPEN_DOCX_RENDERER_ID: () => OFFICE_OPEN_DOCX_RENDERER_ID,
6322
+ OFFICE_OPEN_VERSION: () => OFFICE_OPEN_VERSION,
5250
6323
  buildDocumentOptions: () => buildDocumentOptions,
5251
6324
  createOfficeOpenDocxRenderer: () => createOfficeOpenDocxRenderer
5252
6325
  });
5253
- import AdmZip4 from "adm-zip";
6326
+ import AdmZip5 from "adm-zip";
6327
+ import {
6328
+ assertBackendVersion,
6329
+ installedPackageVersion
6330
+ } from "@json-to-office/shared/rendering/node";
6331
+ function installedBackendVersion() {
6332
+ let resolved;
6333
+ try {
6334
+ resolved = import.meta.resolve?.(OFFICE_OPEN_DOCX);
6335
+ } catch {
6336
+ resolved = void 0;
6337
+ }
6338
+ return installedPackageVersion(OFFICE_OPEN_DOCX, import.meta.url, resolved);
6339
+ }
5254
6340
  async function createOfficeOpenDocxRenderer() {
5255
6341
  const backend = await import(
5256
6342
  /* @vite-ignore */
5257
6343
  OFFICE_OPEN_DOCX
5258
6344
  );
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
- }
6345
+ assertBackendVersion(
6346
+ OFFICE_OPEN_DOCX,
6347
+ OFFICE_OPEN_VERSION,
6348
+ installedBackendVersion()
6349
+ );
6350
+ if (typeof backend.generateDocument !== "function") {
6351
+ throw new Error(
6352
+ `${OFFICE_OPEN_DOCX} does not export generateDocument(); the installed version is not compatible with this adapter.`
6353
+ );
5268
6354
  }
5269
6355
  return {
5270
6356
  id: OFFICE_OPEN_DOCX_RENDERER_ID,
5271
6357
  format: "docx",
5272
6358
  capabilities: OFFICE_OPEN_CAPABILITIES,
5273
6359
  async render(ir, options) {
5274
- const charts = [];
5275
- const cellTocs = [];
6360
+ const charts2 = [];
5276
6361
  const document = await buildDocumentOptions(
5277
6362
  ir,
5278
- charts,
5279
- options?.svgRasterFallback,
5280
- cellTocs
6363
+ charts2,
6364
+ options?.svgRasterFallback
5281
6365
  );
5282
6366
  const bytes = await backend.generateDocument(document, {
5283
6367
  type: "uint8array"
@@ -5285,10 +6369,10 @@ async function createOfficeOpenDocxRenderer() {
5285
6369
  let raw = Buffer.from(
5286
6370
  bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes)
5287
6371
  );
5288
- if (charts.length > 0 || cellTocs.length > 0) {
5289
- const zip = new AdmZip4(raw);
5290
- spliceChartParts(zip, charts);
5291
- spliceCellTocs(zip, cellTocs, backend.stringifyTableOfContents);
6372
+ if (ir.theme || charts2.length > 0) {
6373
+ const zip = new AdmZip5(raw);
6374
+ if (ir.theme) spliceTheme(zip, ir.theme);
6375
+ finishChartParts(zip, charts2);
5292
6376
  raw = zip.toBuffer();
5293
6377
  }
5294
6378
  if (options?.deterministic === false)
@@ -5305,13 +6389,12 @@ async function createOfficeOpenDocxRenderer() {
5305
6389
  }
5306
6390
  };
5307
6391
  }
5308
- async function buildDocumentOptions(ir, charts = [], svgRasterFallback, cellTocs = []) {
6392
+ async function buildDocumentOptions(ir, charts2 = [], svgRasterFallback) {
5309
6393
  let nextDrawingId = 1;
5310
6394
  const ctx = {
5311
6395
  pictures: await prepareImages2(ir, svgRasterFallback),
5312
6396
  nextDrawingId: () => nextDrawingId++,
5313
- charts,
5314
- cellTocs
6397
+ charts: charts2
5315
6398
  };
5316
6399
  return {
5317
6400
  styles: emitStyles2(ir.styles),
@@ -5319,16 +6402,20 @@ async function buildDocumentOptions(ir, charts = [], svgRasterFallback, cellTocs
5319
6402
  (value, index) => section(value, ctx, index === ir.sections.length - 1)
5320
6403
  ),
5321
6404
  ...coreProperties2(ir),
5322
- features: {
6405
+ settings: {
5323
6406
  updateFields: ir.settings.updateFields,
5324
6407
  ...ir.settings.trackRevisions ? { trackRevisions: true } : {}
5325
6408
  },
5326
- ...ir.numbering.length > 0 ? { numbering: { config: ir.numbering.map(numberingConfig2) } } : {},
6409
+ ...ir.numbering.length > 0 ? {
6410
+ numbering: {
6411
+ abstractNumberings: ir.numbering.map(numberingConfig2)
6412
+ }
6413
+ } : {},
5327
6414
  ...ir.footnotes.length > 0 ? { footnotes: noteBodies2(ir.footnotes, ctx) } : {},
5328
6415
  ...ir.endnotes.length > 0 ? { endnotes: noteBodies2(ir.endnotes, ctx) } : {},
5329
6416
  ...ir.comments.length > 0 ? {
5330
- comments: {
5331
- children: ir.comments.map((comment) => ({
6417
+ comments: ir.comments.map(
6418
+ (comment) => ({
5332
6419
  id: comment.id,
5333
6420
  author: comment.author,
5334
6421
  ...comment.initials ? { initials: comment.initials } : {},
@@ -5336,29 +6423,25 @@ async function buildDocumentOptions(ir, charts = [], svgRasterFallback, cellTocs
5336
6423
  children: comment.children.map(
5337
6424
  (child) => paragraphOf(child, ctx)
5338
6425
  )
5339
- }))
5340
- }
6426
+ })
6427
+ )
5341
6428
  } : {}
5342
6429
  };
5343
6430
  }
5344
6431
  function paragraphOf(value, ctx) {
5345
6432
  const emitted = block(value, ctx);
5346
- const asParagraph = emitted.paragraph;
5347
- if (!asParagraph) {
6433
+ if (!("paragraph" in emitted) || typeof emitted.paragraph === "string") {
5348
6434
  throw new Error(
5349
6435
  `the office-open renderer expected a paragraph, not a "${value.kind}"`
5350
6436
  );
5351
6437
  }
5352
- return asParagraph;
6438
+ return emitted.paragraph;
5353
6439
  }
5354
6440
  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;
6441
+ return notes.map((note) => ({
6442
+ id: note.id,
6443
+ children: note.children.map((child) => paragraphOf(child, ctx))
6444
+ }));
5362
6445
  }
5363
6446
  async function prepareImages2(ir, svgRasterFallback) {
5364
6447
  const placements = collectImagePlacements2(ir);
@@ -5381,23 +6464,16 @@ async function prepareImages2(ir, svgRasterFallback) {
5381
6464
  if (resource.kind !== "image") continue;
5382
6465
  const type = resource.mediaType;
5383
6466
  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
6467
  resources.set(resource.id, (placement) => {
5392
6468
  const sizeKey = placementKey(placement);
5393
6469
  const stem = `${resource.id}-${sizeKey}`;
5394
6470
  return {
5395
6471
  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,
6472
+ // The same bytes at every size: the backend keeps each drawing's
6473
+ // extent on the drawing. 0.11 kept it on its deduplicated media
6474
+ // entry, and an image drawn at a second size took the first one's,
6475
+ // unless its bytes were told apart.
6476
+ data,
5401
6477
  // Named after the resource and the size it is drawn at, so two
5402
6478
  // placements of one image at one size share a single part and a third
5403
6479
  // at another size gets its own.
@@ -5420,104 +6496,6 @@ async function prepareImages2(ir, svgRasterFallback) {
5420
6496
  function placementKey(placement) {
5421
6497
  return `${emuToPixels(placement.widthEmu)}x${emuToPixels(placement.heightEmu)}`;
5422
6498
  }
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
6499
  function collectImagePlacements2(ir) {
5522
6500
  const placements = /* @__PURE__ */ new Map();
5523
6501
  const record = (resourceId, placement) => {
@@ -5585,18 +6563,19 @@ function coreProperties2(ir) {
5585
6563
  ...metadata.custom?.length ? { customProperties: metadata.custom } : {}
5586
6564
  };
5587
6565
  }
5588
- var OFFICE_OPEN_DOCX_RENDERER_ID, OFFICE_OPEN_DOCX, OFFICE_OPEN_CAPABILITIES;
6566
+ var OFFICE_OPEN_DOCX_RENDERER_ID, OFFICE_OPEN_DOCX, OFFICE_OPEN_VERSION, OFFICE_OPEN_CAPABILITIES;
5589
6567
  var init_office_open = __esm({
5590
6568
  "src/renderers/office-open/index.ts"() {
5591
6569
  "use strict";
5592
6570
  init_chartParts();
5593
- init_cellTocs();
6571
+ init_themePart();
5594
6572
  init_imageUtils();
5595
6573
  init_packageDocument();
5596
6574
  init_emit2();
5597
6575
  init_styles3();
5598
6576
  OFFICE_OPEN_DOCX_RENDERER_ID = "office-open";
5599
6577
  OFFICE_OPEN_DOCX = "@office-open/docx";
6578
+ OFFICE_OPEN_VERSION = "0.14.6";
5600
6579
  OFFICE_OPEN_CAPABILITIES = /* @__PURE__ */ new Set([
5601
6580
  "paragraphs",
5602
6581
  "styles",
@@ -9227,6 +10206,7 @@ function emptyToUndefined(formatting) {
9227
10206
  }
9228
10207
 
9229
10208
  // src/ir/compiler.ts
10209
+ init_chartValues();
9230
10210
  init_types();
9231
10211
 
9232
10212
  // src/ir/measure.ts
@@ -9259,6 +10239,49 @@ function tableWidthTwips(width, measureTwips) {
9259
10239
 
9260
10240
  // src/ir/compiler.ts
9261
10241
  init_units();
10242
+
10243
+ // src/ir/theme.ts
10244
+ init_colorUtils();
10245
+ init_styleHelpers();
10246
+ init_types();
10247
+ var THEME_COLOR_TOKENS = [
10248
+ ["dark1", "text"],
10249
+ ["light1", "background"],
10250
+ ["dark2", "textSecondary"],
10251
+ ["light2", "backgroundSecondary"],
10252
+ ["accent1", "primary"],
10253
+ ["accent2", "secondary"],
10254
+ ["accent3", "accent"],
10255
+ ["accent4", "accent4"],
10256
+ ["accent5", "accent5"],
10257
+ ["accent6", "accent6"]
10258
+ ];
10259
+ var NOT_XML_CHARS = new RegExp(NOT_XML_CHAR.source, "gu");
10260
+ var xmlText = (value) => (value ?? "").replace(NOT_XML_CHARS, "").trim();
10261
+ function compileTheme(theme, themeName) {
10262
+ const colors = {};
10263
+ for (const [slot, token] of THEME_COLOR_TOKENS) {
10264
+ const hex = schemeHex(token, theme);
10265
+ if (hex) colors[slot] = { hex };
10266
+ }
10267
+ const headingFont = xmlText(resolveFontFamily(theme, "heading"));
10268
+ const bodyFont = xmlText(resolveFontFamily(theme, "body"));
10269
+ return {
10270
+ name: xmlText(theme.name) || xmlText(themeName) || "Office Theme",
10271
+ colors,
10272
+ ...headingFont ? { headingFont } : {},
10273
+ ...bodyFont ? { bodyFont } : {}
10274
+ };
10275
+ }
10276
+ function schemeHex(token, theme) {
10277
+ try {
10278
+ return resolveColor(token, theme);
10279
+ } catch {
10280
+ return void 0;
10281
+ }
10282
+ }
10283
+
10284
+ // src/ir/compiler.ts
9262
10285
  var CONTENT_BOOKMARK_ID_BASE = 2e6;
9263
10286
  var SINGLE_LINE_TWIPS = 240;
9264
10287
  function compileDocument(structure, layout, warnings = [], images = /* @__PURE__ */ new Map(), options = {}) {
@@ -9324,6 +10347,7 @@ function compileDocument(structure, layout, warnings = [], images = /* @__PURE__
9324
10347
  trackRevisions: structure.trackRevisions === true,
9325
10348
  ...noProofWords(structure.theme) ? { noProofWords: noProofWords(structure.theme) } : {}
9326
10349
  },
10350
+ theme: compileTheme(structure.theme, structure.themeName),
9327
10351
  // The statistic styles are appended rather than built with the rest: a
9328
10352
  // document that uses no statistic must keep the exact style set it had
9329
10353
  // before the component had styles, or every recorded golden moves for a
@@ -10092,14 +11116,6 @@ function compileShape2(props, children, scope) {
10092
11116
  );
10093
11117
  }
10094
11118
  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
11119
  ctx.features.require("text-boxes", path4);
10104
11120
  if (props.floating) ctx.features.require("floating-images", path4);
10105
11121
  const insets = shapeInsets(
@@ -10118,7 +11134,7 @@ function compileShape2(props, children, scope) {
10118
11134
  heightPx: height.pixels,
10119
11135
  children,
10120
11136
  ...fill ? { fill: irColor(resolveColor(fill, ctx.theme)) } : {},
10121
- ...outline.outline && !dropOutline ? { outline: outline.outline } : {},
11137
+ ...outline.outline ? { outline: outline.outline } : {},
10122
11138
  ...insets ? { insetsEmu: insets } : {},
10123
11139
  ...props.floating ? { floating: compileFloating(props.floating, ctx, path4) } : {}
10124
11140
  }
@@ -11341,6 +12357,7 @@ function compileImage2(component, scope) {
11341
12357
  }
11342
12358
  var DEFAULT_CHART_HEIGHT_INCHES = 3;
11343
12359
  var MAX_CHART_TEXT_POINTS = 10;
12360
+ var MIN_CHART_TEXT_POINTS = 1;
11344
12361
  function chartTextFont(theme) {
11345
12362
  const bodySize = getThemeFonts(theme).body?.size;
11346
12363
  let color = "000000";
@@ -11350,9 +12367,12 @@ function chartTextFont(theme) {
11350
12367
  }
11351
12368
  return {
11352
12369
  fontFamily: resolveFontFamily(theme, "body"),
11353
- fontSize: Math.min(
11354
- typeof bodySize === "number" && bodySize > 0 ? bodySize : 11,
11355
- MAX_CHART_TEXT_POINTS
12370
+ fontSize: Math.max(
12371
+ MIN_CHART_TEXT_POINTS,
12372
+ Math.min(
12373
+ typeof bodySize === "number" && bodySize > 0 ? bodySize : 11,
12374
+ MAX_CHART_TEXT_POINTS
12375
+ )
11356
12376
  ),
11357
12377
  bold: false,
11358
12378
  color
@@ -11363,7 +12383,7 @@ function compileChart(component, scope) {
11363
12383
  const props = component.props ?? {};
11364
12384
  if (props.type === "bubble") {
11365
12385
  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.`
12386
+ `Chart at ${path4} is a bubble chart, but the docx \`chart\` component has no bubble sizes; use the pptx \`chart\` component on the pptxgenjs renderer.`
11367
12387
  );
11368
12388
  }
11369
12389
  const rawSeries = Array.isArray(props.data) ? props.data : [];
@@ -11385,10 +12405,22 @@ function compileChart(component, scope) {
11385
12405
  `Chart series "${label}" at ${path4} has ${labels.length} labels and ${values.length} values; they must be the same length.`
11386
12406
  );
11387
12407
  }
12408
+ if (labels.length === 0) {
12409
+ throw new Error(
12410
+ `Chart series "${label}" at ${path4} has no data points; give it at least one label and value.`
12411
+ );
12412
+ }
12413
+ const numbers = values.map((value) => Number(value));
12414
+ const bad = numbers.findIndex((value) => !Number.isFinite(value));
12415
+ if (bad !== -1) {
12416
+ throw new Error(
12417
+ `Chart series "${label}" at ${path4} has a value that is not a finite number (${JSON.stringify(values[bad])} at "${String(labels[bad])}").`
12418
+ );
12419
+ }
11388
12420
  return {
11389
12421
  ...typeof raw.name === "string" ? { name: raw.name } : {},
11390
12422
  labels: labels.map((value) => String(value)),
11391
- values: values.map((value) => Number(value))
12423
+ values: numbers
11392
12424
  };
11393
12425
  });
11394
12426
  const categories = series[0].labels;
@@ -11400,6 +12432,34 @@ function compileChart(component, scope) {
11400
12432
  );
11401
12433
  }
11402
12434
  }
12435
+ if (props.type === "pie" && series.length > 1) {
12436
+ throw new Error(
12437
+ `Chart at ${path4} is a pie with ${series.length} series; a pie draws one series. Use a doughnut for one ring per series, or a column chart to compare them.`
12438
+ );
12439
+ }
12440
+ if (props.type === "pie" || props.type === "doughnut") {
12441
+ for (const [index, entry] of series.entries()) {
12442
+ const at = entry.values.findIndex((value) => value < 0);
12443
+ if (at === -1) continue;
12444
+ throw new Error(
12445
+ `Chart series "${entry.name ?? `series ${index}`}" at ${path4} has a negative value (${entry.values[at]} at "${entry.labels[at]}"); a ${props.type} chart draws each value as a share of the whole.`
12446
+ );
12447
+ }
12448
+ }
12449
+ if (props.type === "scatter" && series.some((entry) => entry.labels.some((label) => !isNumericLabel(label)))) {
12450
+ warnOnce(
12451
+ ctx,
12452
+ "chart",
12453
+ `Chart at ${path4} is a scatter chart whose labels are not all numbers; each label is its point's x, so these points are placed at 1, 2, 3\u2026 in order.`
12454
+ );
12455
+ }
12456
+ if ((props.type === "pie" || props.type === "doughnut") && (typeof props.catAxisTitle === "string" || typeof props.valAxisTitle === "string")) {
12457
+ warnOnce(
12458
+ ctx,
12459
+ "chart",
12460
+ `Chart at ${path4} is a ${props.type} chart, which has no axes; its axis titles are not drawn.`
12461
+ );
12462
+ }
11403
12463
  const colors = Array.isArray(props.chartColors) ? props.chartColors.map(
11404
12464
  (color) => resolveColor(String(color), ctx.theme)
11405
12465
  ) : chartPaletteValues(ctx.theme).flatMap((value) => {
@@ -11412,13 +12472,21 @@ function compileChart(component, scope) {
11412
12472
  const contentWidthInches = scope.measureTwips / 1440;
11413
12473
  const widthInches = typeof props.width === "number" ? props.width : contentWidthInches;
11414
12474
  const heightInches = typeof props.height === "number" ? props.height : DEFAULT_CHART_HEIGHT_INCHES;
12475
+ const widthEmu = inchesToEmu(widthInches);
12476
+ const heightEmu = inchesToEmu(heightInches);
12477
+ if (!(widthEmu > 0 && heightEmu > 0)) {
12478
+ const inches = (value) => String(Number.isFinite(value) ? Math.round(value * 100) / 100 : value);
12479
+ throw new Error(
12480
+ `Chart at ${path4} has no room: it would be ${inches(widthInches)}\xD7${inches(heightInches)} in. Set \`width\`/\`height\`, or give it a container with space.`
12481
+ );
12482
+ }
11415
12483
  const chart = {
11416
12484
  kind: "chart",
11417
12485
  chartType: props.type,
11418
12486
  series,
11419
12487
  colors,
11420
- widthEmu: inchesToEmu(widthInches),
11421
- heightEmu: inchesToEmu(heightInches),
12488
+ widthEmu,
12489
+ heightEmu,
11422
12490
  ...typeof props.title === "string" ? { title: props.title } : {},
11423
12491
  ...props.showTitle !== void 0 ? { showTitle: props.showTitle } : {},
11424
12492
  ...props.showLegend !== void 0 ? { showLegend: props.showLegend } : {},
@@ -11621,7 +12689,7 @@ function compileFrame(floating, ctx, path4) {
11621
12689
  function compileFloating(floating, ctx, path4) {
11622
12690
  if (floating.wrap?.type === "tight") {
11623
12691
  throw new Error(
11624
- "Image floating wrap.type 'tight' is not supported due to invalid OOXML emitted by docx. Use 'square', 'topAndBottom', or 'none'."
12692
+ "Image floating wrap.type 'tight' is not supported. Use 'square', 'topAndBottom', or 'none'."
11625
12693
  );
11626
12694
  }
11627
12695
  const hRelative = floating.horizontalPosition?.relative;
@@ -15783,18 +16851,18 @@ var docxExhibitRequiredRule = {
15783
16851
  1
15784
16852
  );
15785
16853
  if (minimum <= 0) return [];
15786
- const charts = facts.filter((fact) => fact.kind === "docx/chart").length;
16854
+ const charts2 = facts.filter((fact) => fact.kind === "docx/chart").length;
15787
16855
  const tables = facts.filter(
15788
16856
  (fact) => fact.kind === "docx/table" && fact.columns.length >= 2
15789
16857
  ).length;
15790
- const exhibits = charts + tables;
16858
+ const exhibits = charts2 + tables;
15791
16859
  if (exhibits >= minimum) return [];
15792
16860
  return [
15793
16861
  {
15794
16862
  path: themeFact(facts)?.path ?? "/props",
15795
16863
  message: exhibits === 0 ? `The document carries no data exhibit \u2014 no chart, no table of two or more columns; the ${profile?.id ?? "selected"} profile expects at least ${minimum}.` : `The document carries ${exhibits} data exhibit(s); the ${profile?.id ?? "selected"} profile expects at least ${minimum}.`,
15796
16864
  suggestion: "Put the numbers the argument leans on into a chart-figure or a data-table block, with a takeaway and a source, where the text discusses them. A kpi-row summarises; it does not count.",
15797
- context: { charts, tables, minimum }
16865
+ context: { charts: charts2, tables, minimum }
15798
16866
  }
15799
16867
  ];
15800
16868
  }