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