@json-to-office/core-docx 2.4.0 → 2.6.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 (50) hide show
  1. package/dist/blocks/index.d.ts +58 -0
  2. package/dist/blocks/index.d.ts.map +1 -0
  3. package/dist/blocks/keyTakeaways.d.ts +20 -0
  4. package/dist/blocks/keyTakeaways.d.ts.map +1 -0
  5. package/dist/blocks/types.d.ts +15 -0
  6. package/dist/blocks/types.d.ts.map +1 -0
  7. package/dist/components/highcharts.d.ts +2 -2
  8. package/dist/components/highcharts.d.ts.map +1 -1
  9. package/dist/core/componentTransform.d.ts +8 -0
  10. package/dist/core/componentTransform.d.ts.map +1 -1
  11. package/dist/core/desugarExternals.d.ts +2 -0
  12. package/dist/core/desugarExternals.d.ts.map +1 -1
  13. package/dist/core/generateFromIr.d.ts.map +1 -1
  14. package/dist/core/generationContext.d.ts.map +1 -1
  15. package/dist/index.d.ts +3 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +1027 -170
  18. package/dist/index.js.map +1 -1
  19. package/dist/ir/compiler.d.ts.map +1 -1
  20. package/dist/plugin/example/index.js +1010 -182
  21. package/dist/plugin/example/index.js.map +1 -1
  22. package/dist/quality/facts.d.ts +23 -1
  23. package/dist/quality/facts.d.ts.map +1 -1
  24. package/dist/quality/rules.d.ts +6 -0
  25. package/dist/quality/rules.d.ts.map +1 -1
  26. package/dist/renderers/docxjs/emit.d.ts.map +1 -1
  27. package/dist/renderers/docxjs/index.d.ts.map +1 -1
  28. package/dist/renderers/office-open/index.d.ts.map +1 -1
  29. package/dist/styles/index.d.ts +1 -1
  30. package/dist/styles/index.d.ts.map +1 -1
  31. package/dist/styles/themeToStyles.d.ts.map +1 -1
  32. package/dist/styles/utils/colorUtils.d.ts +12 -1
  33. package/dist/styles/utils/colorUtils.d.ts.map +1 -1
  34. package/dist/styles/utils/styleHelpers.d.ts +2 -0
  35. package/dist/styles/utils/styleHelpers.d.ts.map +1 -1
  36. package/dist/templates/themes/consulting.docx.theme.json +307 -0
  37. package/dist/templates/themes/index.d.ts +3649 -666
  38. package/dist/templates/themes/index.d.ts.map +1 -1
  39. package/dist/themes/defaults.d.ts +183 -180
  40. package/dist/themes/defaults.d.ts.map +1 -1
  41. package/dist/themes/design-system.d.ts +4 -0
  42. package/dist/themes/design-system.d.ts.map +1 -0
  43. package/dist/themes/overrides.d.ts +2 -1
  44. package/dist/themes/overrides.d.ts.map +1 -1
  45. package/dist/tsconfig.tsbuildinfo +1 -1
  46. package/dist/utils/normalizeTextCase.d.ts +15 -0
  47. package/dist/utils/normalizeTextCase.d.ts.map +1 -0
  48. package/dist/utils/packageDocument.d.ts +2 -0
  49. package/dist/utils/packageDocument.d.ts.map +1 -1
  50. package/package.json +4 -4
@@ -9,6 +9,7 @@ var __export = (target, all) => {
9
9
  };
10
10
 
11
11
  // src/themes/defaults.ts
12
+ import { designColors } from "@json-to-office/shared";
12
13
  function ensureThemeDefaults(theme) {
13
14
  return {
14
15
  ...theme,
@@ -41,10 +42,13 @@ function ensureThemeDefaults(theme) {
41
42
  };
42
43
  }
43
44
  function getThemeColors(theme) {
44
- return {
45
- ...DEFAULT_COLORS,
46
- ...theme.colors || {}
47
- };
45
+ return designColors(
46
+ {
47
+ ...DEFAULT_COLORS,
48
+ ...theme.colors || {}
49
+ },
50
+ theme.palette
51
+ );
48
52
  }
49
53
  function getThemeFonts(theme) {
50
54
  return {
@@ -786,6 +790,320 @@ var init_vermilion_docx_theme = __esm({
786
790
  }
787
791
  });
788
792
 
793
+ // src/templates/themes/consulting.docx.theme.json
794
+ var consulting_docx_theme_default;
795
+ var init_consulting_docx_theme = __esm({
796
+ "src/templates/themes/consulting.docx.theme.json"() {
797
+ consulting_docx_theme_default = {
798
+ name: "consulting",
799
+ displayName: "Consulting House",
800
+ description: "The house style for client and technical reports \u2014 near-black ink, three greys and one deep-blue accent, Calibri body under Arial headings, hairline rules, no fills behind body text, safe fonts only",
801
+ version: "1.0.0",
802
+ colors: {
803
+ primary: "#1A1F26",
804
+ secondary: "#4B5563",
805
+ accent: "#1B4F8A",
806
+ text: "#1A1F26",
807
+ background: "#FFFFFF",
808
+ border: "#C9CED6",
809
+ textPrimary: "#1A1F26",
810
+ textSecondary: "#4B5563",
811
+ textMuted: "#7B8794",
812
+ borderPrimary: "#C9CED6",
813
+ borderSecondary: "#E4E7EB",
814
+ backgroundPrimary: "#FFFFFF",
815
+ backgroundSecondary: "#F2F4F7",
816
+ accent4: "#5B8DC9",
817
+ accent5: "#A9C4E4",
818
+ accent6: "#7B8794"
819
+ },
820
+ palette: {
821
+ rule: "#C9CED6",
822
+ textMuted: "#7B8794",
823
+ onPrimary: "#FFFFFF",
824
+ surfaceInverse: "#1A1F26",
825
+ positive: "#1E7F4F",
826
+ negative: "#B42318",
827
+ chart: ["accent", "secondary", "accent4", "textMuted", "accent5", "rule"]
828
+ },
829
+ fonts: {
830
+ heading: { family: "Arial", size: 16 },
831
+ body: { family: "Calibri", size: 10.5 },
832
+ mono: { family: "Consolas", size: 9.5 },
833
+ light: { family: "Arial", size: 16 }
834
+ },
835
+ page: {
836
+ size: "A4",
837
+ margins: {
838
+ top: 1440,
839
+ bottom: 1440,
840
+ left: 1440,
841
+ right: 1440,
842
+ header: 680,
843
+ footer: 680,
844
+ gutter: 0
845
+ }
846
+ },
847
+ typography: {
848
+ roles: {
849
+ eyebrow: {
850
+ face: "heading",
851
+ size: 8,
852
+ weight: 700,
853
+ case: "upper",
854
+ tracking: 8,
855
+ color: "accent",
856
+ spaceAfter: 4
857
+ },
858
+ display: {
859
+ face: "heading",
860
+ weight: 700,
861
+ color: "primary",
862
+ lineHeight: 1.05,
863
+ spaceAfter: 8
864
+ },
865
+ stat: {
866
+ face: "heading",
867
+ weight: 700,
868
+ color: "accent",
869
+ lineHeight: 1
870
+ },
871
+ quote: {
872
+ face: "body",
873
+ color: "textSecondary",
874
+ lineHeight: 1.3
875
+ },
876
+ label: {
877
+ face: "body",
878
+ size: 9,
879
+ weight: 700,
880
+ color: "textSecondary"
881
+ },
882
+ footer: { face: "body", size: 8, color: "textMuted" },
883
+ tableHeader: {
884
+ face: "body",
885
+ size: 9,
886
+ weight: 700,
887
+ color: "primary"
888
+ },
889
+ tableCell: { face: "body", size: 9.5, color: "textPrimary" },
890
+ chartLabel: {
891
+ face: "body",
892
+ size: 9,
893
+ weight: 400,
894
+ color: "textSecondary"
895
+ },
896
+ tracker: {
897
+ face: "body",
898
+ size: 8,
899
+ case: "upper",
900
+ tracking: 6,
901
+ color: "textMuted"
902
+ },
903
+ source: { face: "body", size: 8, color: "textMuted" }
904
+ },
905
+ scale: {
906
+ a4: { base: 10.5, ratio: 1.2, baselinePt: 2 },
907
+ letter: { base: 10.5, ratio: 1.2, baselinePt: 2 }
908
+ }
909
+ },
910
+ spacing: {
911
+ basePt: 8,
912
+ blockGap: { tight: 4, normal: 8, loose: 16 },
913
+ canvas: {
914
+ a4: { safeAreaIn: 1, gutterIn: 0, columns: 12 },
915
+ letter: { safeAreaIn: 1, gutterIn: 0, columns: 12 }
916
+ }
917
+ },
918
+ chrome: {
919
+ runningHead: {
920
+ type: "tracker",
921
+ color: "textMuted",
922
+ rule: { weightPt: 0.5, color: "rule" },
923
+ alignment: "right"
924
+ },
925
+ tracker: {
926
+ type: "tracker",
927
+ color: "textMuted",
928
+ alignment: "right"
929
+ },
930
+ actionTitle: { type: "display", color: "primary" },
931
+ keyTakeaways: {
932
+ type: "label",
933
+ color: "primary",
934
+ rule: { weightPt: 2, color: "accent" },
935
+ padPt: 8
936
+ },
937
+ sourceLine: { type: "source", color: "textMuted" },
938
+ confidentialFooter: {
939
+ type: "footer",
940
+ color: "textMuted",
941
+ rule: { weightPt: 0.5, color: "rule" },
942
+ alignment: "left"
943
+ },
944
+ logoSlot: { alignment: "right" },
945
+ cover: {
946
+ type: "display",
947
+ color: "primary",
948
+ rule: { weightPt: 3, color: "accent" }
949
+ }
950
+ },
951
+ motif: {
952
+ kind: "rule",
953
+ color: "accent",
954
+ weightPt: 3,
955
+ placement: "top"
956
+ },
957
+ styles: {
958
+ normal: {
959
+ font: "body",
960
+ color: "textPrimary",
961
+ lineSpacing: { type: "multiple", value: 1.2 },
962
+ alignment: "left",
963
+ spacing: { after: 8 }
964
+ },
965
+ title: {
966
+ font: "heading",
967
+ size: 26,
968
+ bold: true,
969
+ color: "primary",
970
+ alignment: "left",
971
+ lineSpacing: { type: "multiple", value: 1.05 },
972
+ spacing: { before: 0, after: 6 }
973
+ },
974
+ subtitle: {
975
+ font: "body",
976
+ size: 13,
977
+ color: "textSecondary",
978
+ alignment: "left",
979
+ lineSpacing: { type: "multiple", value: 1.2 },
980
+ spacing: { before: 0, after: 18 }
981
+ },
982
+ heading1: {
983
+ font: "heading",
984
+ size: 16,
985
+ bold: true,
986
+ color: "primary",
987
+ lineSpacing: { type: "multiple", value: 1.1 },
988
+ spacing: { before: 24, after: 8 },
989
+ keepNext: true,
990
+ alignment: "left",
991
+ borders: {
992
+ bottom: { style: "single", size: 4, color: "rule", space: 4 }
993
+ }
994
+ },
995
+ heading2: {
996
+ font: "heading",
997
+ size: 12.5,
998
+ bold: true,
999
+ color: "primary",
1000
+ spacing: { before: 16, after: 6 },
1001
+ keepNext: true,
1002
+ alignment: "left"
1003
+ },
1004
+ heading3: {
1005
+ font: "body",
1006
+ size: 11,
1007
+ bold: true,
1008
+ color: "primary",
1009
+ spacing: { before: 12, after: 4 },
1010
+ keepNext: true,
1011
+ alignment: "left"
1012
+ },
1013
+ heading4: {
1014
+ font: "body",
1015
+ size: 10.5,
1016
+ bold: true,
1017
+ color: "textSecondary",
1018
+ spacing: { before: 10, after: 4 },
1019
+ keepNext: true,
1020
+ alignment: "left"
1021
+ },
1022
+ heading5: {
1023
+ font: "body",
1024
+ size: 10.5,
1025
+ bold: false,
1026
+ italic: true,
1027
+ color: "textSecondary",
1028
+ spacing: { before: 8, after: 4 },
1029
+ keepNext: true,
1030
+ alignment: "left"
1031
+ },
1032
+ heading6: {
1033
+ font: "body",
1034
+ size: 9,
1035
+ bold: true,
1036
+ case: "upper",
1037
+ color: "textMuted",
1038
+ characterSpacing: { type: "expanded", value: 20 },
1039
+ spacing: { before: 8, after: 4 },
1040
+ keepNext: true,
1041
+ alignment: "left"
1042
+ },
1043
+ TOC1: {
1044
+ font: "body",
1045
+ size: 10.5,
1046
+ bold: true,
1047
+ color: "textPrimary",
1048
+ spacing: { before: 4, after: 3 },
1049
+ tabStops: [{ type: "right", position: 9026, leader: "none" }]
1050
+ },
1051
+ TOC2: {
1052
+ font: "body",
1053
+ size: 10.5,
1054
+ color: "textSecondary",
1055
+ indent: { left: 240 },
1056
+ spacing: { after: 2 },
1057
+ tabStops: [{ type: "right", position: 9026, leader: "none" }]
1058
+ },
1059
+ TOC3: {
1060
+ font: "body",
1061
+ size: 10,
1062
+ color: "textMuted",
1063
+ indent: { left: 480 },
1064
+ spacing: { after: 2 },
1065
+ tabStops: [{ type: "right", position: 9026, leader: "none" }]
1066
+ }
1067
+ },
1068
+ componentDefaults: {
1069
+ table: {
1070
+ width: 100,
1071
+ borderColor: "#C9CED6",
1072
+ borderSize: 0.5,
1073
+ hideBorders: { left: true, right: true, insideVertical: true },
1074
+ cellDefaults: {
1075
+ font: { size: 9.5 },
1076
+ padding: { top: 4, bottom: 4 },
1077
+ verticalAlignment: "middle"
1078
+ },
1079
+ headerCellDefaults: {
1080
+ color: "#1A1F26",
1081
+ font: { size: 9, bold: true },
1082
+ padding: { top: 0, bottom: 5 },
1083
+ verticalAlignment: "bottom",
1084
+ borderColor: { bottom: "#1A1F26" },
1085
+ borderSize: { bottom: 0.75 }
1086
+ }
1087
+ },
1088
+ list: {
1089
+ format: "bullet",
1090
+ bullet: "\u2022",
1091
+ spacing: { item: 2 }
1092
+ },
1093
+ image: {
1094
+ alignment: "center"
1095
+ },
1096
+ statistic: {
1097
+ alignment: "left"
1098
+ },
1099
+ section: {
1100
+ pageBreak: false
1101
+ }
1102
+ }
1103
+ };
1104
+ }
1105
+ });
1106
+
789
1107
  // src/templates/themes/index.ts
790
1108
  import fs from "fs";
791
1109
  import path from "path";
@@ -800,7 +1118,8 @@ function loadThemesFromJson() {
800
1118
  const themes2 = {
801
1119
  minimal: ensureThemeDefaults(minimal_docx_theme_default),
802
1120
  devportal: ensureThemeDefaults(devportal_docx_theme_default),
803
- vermilion: ensureThemeDefaults(vermilion_docx_theme_default)
1121
+ vermilion: ensureThemeDefaults(vermilion_docx_theme_default),
1122
+ consulting: ensureThemeDefaults(consulting_docx_theme_default)
804
1123
  };
805
1124
  try {
806
1125
  const __filename2 = fileURLToPath(import.meta.url);
@@ -838,7 +1157,7 @@ function loadThemesFromJson() {
838
1157
  _themesCache = themes2;
839
1158
  return themes2;
840
1159
  }
841
- var _themesCache, themes, getTheme, getThemeWithFallback, hasTheme, getThemeNames, minimalTheme, devportalTheme, vermilionTheme;
1160
+ var _themesCache, themes, getTheme, getThemeWithFallback, hasTheme, getThemeNames, minimalTheme, devportalTheme, vermilionTheme, consultingTheme;
842
1161
  var init_themes = __esm({
843
1162
  "src/templates/themes/index.ts"() {
844
1163
  "use strict";
@@ -846,6 +1165,7 @@ var init_themes = __esm({
846
1165
  init_minimal_docx_theme();
847
1166
  init_devportal_docx_theme();
848
1167
  init_vermilion_docx_theme();
1168
+ init_consulting_docx_theme();
849
1169
  _themesCache = null;
850
1170
  themes = loadThemesFromJson();
851
1171
  getTheme = (themeName) => {
@@ -869,10 +1189,15 @@ var init_themes = __esm({
869
1189
  minimalTheme = themes["minimal"];
870
1190
  devportalTheme = themes["devportal"];
871
1191
  vermilionTheme = themes["vermilion"];
1192
+ consultingTheme = themes["consulting"];
872
1193
  }
873
1194
  });
874
1195
 
875
1196
  // src/styles/utils/colorUtils.ts
1197
+ import {
1198
+ DEFAULT_CHART_THEME_COLORS,
1199
+ resolveDesignColor
1200
+ } from "@json-to-office/shared";
876
1201
  function resolveColor(colorValue, theme) {
877
1202
  if (colorValue.startsWith("#")) {
878
1203
  const hexValue = colorValue.slice(1);
@@ -886,7 +1211,11 @@ function resolveColor(colorValue, theme) {
886
1211
  const colors = getThemeColors(theme);
887
1212
  const resolvedColor = colors[colorValue];
888
1213
  if (typeof resolvedColor === "string") {
889
- return resolveColor(resolvedColor, theme);
1214
+ const hex = resolveDesignColor(colorValue, colors);
1215
+ if (hex) return hex.toUpperCase();
1216
+ throw new Error(
1217
+ `Unresolvable theme color: "${colorValue}" (missing reference or cycle).`
1218
+ );
890
1219
  }
891
1220
  if (/^[0-9A-Fa-f]{6}$/.test(colorValue)) {
892
1221
  return colorValue.toUpperCase();
@@ -895,6 +1224,15 @@ function resolveColor(colorValue, theme) {
895
1224
  `Invalid color value: "${colorValue}". Must be a hex color with # prefix (e.g., "#000000") or a valid theme color name.`
896
1225
  );
897
1226
  }
1227
+ function chartPaletteValues(theme) {
1228
+ const explicit = theme.palette?.chart;
1229
+ if (explicit) return [...explicit];
1230
+ const colors = getThemeColors(theme);
1231
+ return DEFAULT_CHART_THEME_COLORS.flatMap((token) => {
1232
+ const value = colors[token];
1233
+ return typeof value === "string" && value.length > 0 ? [value] : [];
1234
+ });
1235
+ }
898
1236
  var init_colorUtils = __esm({
899
1237
  "src/styles/utils/colorUtils.ts"() {
900
1238
  "use strict";
@@ -902,43 +1240,6 @@ var init_colorUtils = __esm({
902
1240
  }
903
1241
  });
904
1242
 
905
- // src/utils/generationContext.ts
906
- import { AsyncLocalStorage } from "async_hooks";
907
- import { isAbsolute, resolve } from "path";
908
- function runWithBaseDir(baseDir, callback) {
909
- return baseDir === void 0 ? callback() : baseDirStorage.run(resolve(baseDir), callback);
910
- }
911
- function resolveFromBaseDir(filePath) {
912
- const base = baseDirStorage.getStore();
913
- if (!base || isAbsolute(filePath)) return filePath;
914
- return resolve(base, filePath);
915
- }
916
- function runWithWarnings(warnings, callback) {
917
- return warnings === void 0 ? callback() : warningsStorage.run(warnings, callback);
918
- }
919
- function reportWarning(component, code, message, context) {
920
- const warnings = warningsStorage.getStore();
921
- if (warnings) {
922
- warnings.push({
923
- component,
924
- message,
925
- severity: "warning",
926
- context: { code, ...context }
927
- });
928
- return;
929
- }
930
- console.warn(`[json-to-docx] [${code}] ${message}`);
931
- }
932
- var generationDateStorage, baseDirStorage, warningsStorage;
933
- var init_generationContext = __esm({
934
- "src/utils/generationContext.ts"() {
935
- "use strict";
936
- generationDateStorage = new AsyncLocalStorage();
937
- baseDirStorage = new AsyncLocalStorage();
938
- warningsStorage = new AsyncLocalStorage();
939
- }
940
- });
941
-
942
1243
  // src/styles/utils/styleHelpers.ts
943
1244
  function pointsToTwips(points) {
944
1245
  return Math.round(points * TWIPS_PER_POINT);
@@ -984,6 +1285,8 @@ function resolveFontProperties(theme, fontRef) {
984
1285
  size: fontDef.size,
985
1286
  color: fontDef.color,
986
1287
  bold: fontDef.bold,
1288
+ fontWeight: fontDef.fontWeight,
1289
+ case: fontDef.case,
987
1290
  italic: fontDef.italic,
988
1291
  underline: fontDef.underline,
989
1292
  alignment: fontDef.alignment,
@@ -1250,6 +1553,43 @@ var init_layoutUtils = __esm({
1250
1553
  }
1251
1554
  });
1252
1555
 
1556
+ // src/utils/generationContext.ts
1557
+ import { AsyncLocalStorage } from "async_hooks";
1558
+ import { isAbsolute, resolve } from "path";
1559
+ function runWithBaseDir(baseDir, callback) {
1560
+ return baseDir === void 0 ? callback() : baseDirStorage.run(resolve(baseDir), callback);
1561
+ }
1562
+ function resolveFromBaseDir(filePath) {
1563
+ const base = baseDirStorage.getStore();
1564
+ if (!base || isAbsolute(filePath)) return filePath;
1565
+ return resolve(base, filePath);
1566
+ }
1567
+ function runWithWarnings(warnings, callback) {
1568
+ return warnings === void 0 ? callback() : warningsStorage.run(warnings, callback);
1569
+ }
1570
+ function reportWarning(component, code, message, context) {
1571
+ const warnings = warningsStorage.getStore();
1572
+ if (warnings) {
1573
+ warnings.push({
1574
+ component,
1575
+ message,
1576
+ severity: "warning",
1577
+ context: { code, ...context }
1578
+ });
1579
+ return;
1580
+ }
1581
+ console.warn(`[json-to-docx] [${code}] ${message}`);
1582
+ }
1583
+ var generationDateStorage, baseDirStorage, warningsStorage;
1584
+ var init_generationContext = __esm({
1585
+ "src/utils/generationContext.ts"() {
1586
+ "use strict";
1587
+ generationDateStorage = new AsyncLocalStorage();
1588
+ baseDirStorage = new AsyncLocalStorage();
1589
+ warningsStorage = new AsyncLocalStorage();
1590
+ }
1591
+ });
1592
+
1253
1593
  // src/themes/json/parser.ts
1254
1594
  import { validateThemeJson } from "@json-to-office/shared-docx/validation/unified";
1255
1595
  var ThemeValidationError, ThemeParseError, ThemeParser;
@@ -2044,6 +2384,9 @@ function runOptions(formatting) {
2044
2384
  options.underline = formatting.underline ? { type: formatting.underline.type } : void 0;
2045
2385
  }
2046
2386
  if (formatting.strike !== void 0) options.strike = formatting.strike;
2387
+ if (formatting.allCaps !== void 0) options.allCaps = formatting.allCaps;
2388
+ if (formatting.smallCaps !== void 0)
2389
+ options.smallCaps = formatting.smallCaps;
2047
2390
  if (formatting.scalePercent !== void 0) {
2048
2391
  options.scale = formatting.scalePercent;
2049
2392
  }
@@ -2734,6 +3077,44 @@ var init_styles2 = __esm({
2734
3077
  }
2735
3078
  });
2736
3079
 
3080
+ // src/utils/normalizeTextCase.ts
3081
+ function normalizeTextCase(zip) {
3082
+ let changed = false;
3083
+ for (const entry of zip.getEntries()) {
3084
+ if (!WORD_XML.test(entry.entryName)) continue;
3085
+ const xml = entry.getData().toString("utf8");
3086
+ if (!ANY_CASE_FLAG.test(xml)) continue;
3087
+ const normalized = xml.replace(RUN_PROPERTIES, completeCaseFlags);
3088
+ if (normalized === xml) continue;
3089
+ zip.updateFile(entry, Buffer.from(normalized, "utf8"));
3090
+ changed = true;
3091
+ }
3092
+ return changed;
3093
+ }
3094
+ function completeCaseFlags(runProperties3) {
3095
+ const flag = CASE_FLAG.exec(runProperties3);
3096
+ if (!flag) return runProperties3;
3097
+ const isCaps = flag[1] === "caps";
3098
+ const opposite = `<w:${isCaps ? "smallCaps" : "caps"} w:val="false"/>`;
3099
+ if (runProperties3.includes(`<w:${isCaps ? "smallCaps" : "caps"}`)) {
3100
+ return runProperties3;
3101
+ }
3102
+ return runProperties3.replace(
3103
+ flag[0],
3104
+ isCaps ? `${flag[0]}${opposite}` : `${opposite}${flag[0]}`
3105
+ );
3106
+ }
3107
+ var WORD_XML, ANY_CASE_FLAG, RUN_PROPERTIES, CASE_FLAG;
3108
+ var init_normalizeTextCase = __esm({
3109
+ "src/utils/normalizeTextCase.ts"() {
3110
+ "use strict";
3111
+ WORD_XML = /^word\/.*\.xml$/;
3112
+ ANY_CASE_FLAG = /<w:(?:caps|smallCaps)\b/;
3113
+ RUN_PROPERTIES = /<w:rPr\b[^>]*>[\s\S]*?<\/w:rPr>/g;
3114
+ CASE_FLAG = /<w:(caps|smallCaps)\b[^>]*(?:\/>|>[\s\S]*?<\/w:\1>)/;
3115
+ }
3116
+ });
3117
+
2737
3118
  // src/utils/packageDocument.ts
2738
3119
  import AdmZip from "adm-zip";
2739
3120
  function resolveGenerationDate(options) {
@@ -2802,6 +3183,7 @@ function canonicalizeRelationshipIds(zip) {
2802
3183
  }
2803
3184
  function canonicalizeDocxBuffer(buffer, generatedAt = DEFAULT_GENERATION_DATE) {
2804
3185
  const zip = new AdmZip(buffer);
3186
+ normalizeTextCase(zip);
2805
3187
  canonicalizeRelationshipIds(zip);
2806
3188
  const isoTimestamp = generatedAt.toISOString();
2807
3189
  const coreProperties3 = zip.getEntry("docProps/core.xml");
@@ -2818,10 +3200,15 @@ function canonicalizeDocxBuffer(buffer, generatedAt = DEFAULT_GENERATION_DATE) {
2818
3200
  }
2819
3201
  return zip.toBuffer();
2820
3202
  }
3203
+ function normalizeDocxCaseBuffer(buffer) {
3204
+ const zip = new AdmZip(buffer);
3205
+ return normalizeTextCase(zip) ? zip.toBuffer() : buffer;
3206
+ }
2821
3207
  var DEFAULT_GENERATION_DATE, STABLE_RELATIONSHIP_ID;
2822
3208
  var init_packageDocument = __esm({
2823
3209
  "src/utils/packageDocument.ts"() {
2824
3210
  "use strict";
3211
+ init_normalizeTextCase();
2825
3212
  DEFAULT_GENERATION_DATE = /* @__PURE__ */ new Date("2000-01-01T00:00:00.000Z");
2826
3213
  STABLE_RELATIONSHIP_ID = /^rId\d+$/;
2827
3214
  }
@@ -2880,7 +3267,8 @@ function createDocxJsRenderer() {
2880
3267
  const document = buildDocument(ir, resources);
2881
3268
  const packed = await Packer.toBuffer(document);
2882
3269
  const fixed = fixFloatingImageIdsInBuffer(packed);
2883
- if (renderOptions?.deterministic === false) return new Uint8Array(fixed);
3270
+ if (renderOptions?.deterministic === false)
3271
+ return new Uint8Array(normalizeDocxCaseBuffer(fixed));
2884
3272
  return new Uint8Array(
2885
3273
  canonicalizeDocxBuffer(
2886
3274
  fixed,
@@ -4159,7 +4547,8 @@ async function createOfficeOpenDocxRenderer() {
4159
4547
  spliceChartParts(zip, charts);
4160
4548
  raw = zip.toBuffer();
4161
4549
  }
4162
- if (options?.deterministic === false) return new Uint8Array(raw);
4550
+ if (options?.deterministic === false)
4551
+ return new Uint8Array(normalizeDocxCaseBuffer(raw));
4163
4552
  return new Uint8Array(
4164
4553
  canonicalizeDocxBuffer(
4165
4554
  raw,
@@ -4518,6 +4907,7 @@ init_themes();
4518
4907
  import { applyExportMode } from "@json-to-office/shared";
4519
4908
 
4520
4909
  // src/themes/overrides.ts
4910
+ import { mergeWithDefaults } from "@json-to-office/shared";
4521
4911
  function applyThemeOverrides(theme, overrides) {
4522
4912
  if (!overrides) return theme;
4523
4913
  const fonts = { ...theme.fonts };
@@ -4531,12 +4921,87 @@ function applyThemeOverrides(theme, overrides) {
4531
4921
  }
4532
4922
  return {
4533
4923
  ...theme,
4924
+ ...Object.fromEntries(
4925
+ ["palette", "typography", "spacing", "chrome", "motif"].flatMap(
4926
+ (name) => {
4927
+ const key = name;
4928
+ return overrides[key] === void 0 ? [] : [[key, mergeWithDefaults(overrides[key], theme[key] ?? {})]];
4929
+ }
4930
+ )
4931
+ ),
4534
4932
  colors: { ...theme.colors, ...overrides.colors ?? {} },
4535
4933
  fonts,
4536
4934
  ...Object.keys(styles).length > 0 && { styles }
4537
4935
  };
4538
4936
  }
4539
4937
 
4938
+ // src/themes/design-system.ts
4939
+ init_defaults();
4940
+ import {
4941
+ designCanvas,
4942
+ resolveTypeRoles,
4943
+ validateDesignColors
4944
+ } from "@json-to-office/shared";
4945
+ function resolveDocxDesignSystem(theme) {
4946
+ validateDesignColors(theme, getThemeColors(theme));
4947
+ if (!theme.typography && !theme.spacing) return theme;
4948
+ const canvas = designCanvas("docx", theme.page.size);
4949
+ const roles = resolveTypeRoles(theme, canvas, theme.fonts.body.size ?? 11);
4950
+ const styles = { ...theme.styles };
4951
+ for (const [name, role] of Object.entries(roles)) {
4952
+ const spacing = {
4953
+ ...role.spaceBefore !== void 0 && { before: role.spaceBefore },
4954
+ ...role.spaceAfter !== void 0 && { after: role.spaceAfter }
4955
+ };
4956
+ styles[name] = {
4957
+ font: role.face ?? "body",
4958
+ size: role.size,
4959
+ ...role.weight !== void 0 && { fontWeight: role.weight },
4960
+ ...role.color !== void 0 && { color: role.color },
4961
+ ...role.case !== void 0 && { case: role.case },
4962
+ ...role.lineHeight !== void 0 && {
4963
+ lineSpacing: { type: "multiple", value: role.lineHeight }
4964
+ },
4965
+ ...role.tracking !== void 0 && {
4966
+ characterSpacing: {
4967
+ type: role.tracking < 0 ? "condensed" : "expanded",
4968
+ value: Math.abs(role.tracking) * role.size / 5
4969
+ }
4970
+ },
4971
+ ...Object.keys(spacing).length && { spacing },
4972
+ ...styles[name]
4973
+ };
4974
+ }
4975
+ const gap = theme.spacing?.blockGap?.normal ?? theme.spacing?.basePt;
4976
+ const scale = theme.typography?.scale?.[canvas];
4977
+ if (scale || gap !== void 0) {
4978
+ styles.normal = {
4979
+ ...scale && { size: scale.base },
4980
+ ...gap !== void 0 && { spacing: { after: gap } },
4981
+ ...styles.normal
4982
+ };
4983
+ }
4984
+ const space = theme.spacing?.canvas?.[canvas];
4985
+ const safe = space?.safeAreaIn;
4986
+ return {
4987
+ ...theme,
4988
+ styles,
4989
+ page: {
4990
+ ...theme.page,
4991
+ margins: {
4992
+ ...theme.page.margins,
4993
+ ...safe !== void 0 && {
4994
+ top: safe * 1440,
4995
+ bottom: safe * 1440,
4996
+ left: safe * 1440,
4997
+ right: safe * 1440
4998
+ },
4999
+ ...space?.gutterIn !== void 0 && { gutter: space.gutterIn * 1440 }
5000
+ }
5001
+ }
5002
+ };
5003
+ }
5004
+
4540
5005
  // src/core/generationContext.ts
4541
5006
  function defaultNamedThemeLookup(themeName, customThemes, warnings) {
4542
5007
  if (customThemes) {
@@ -4569,6 +5034,7 @@ function resolveThemeContext(documentIn, options = {}) {
4569
5034
  if (themeOverrides) {
4570
5035
  theme = applyThemeOverrides(theme, themeOverrides);
4571
5036
  }
5037
+ theme = resolveDocxDesignSystem(theme);
4572
5038
  const mode = applyExportMode({ doc: document, theme, fonts });
4573
5039
  for (const w of mode.warnings) {
4574
5040
  if (warnings) {
@@ -4802,7 +5268,10 @@ async function resolveDocumentFonts(document, theme, fonts, warnings, forceMater
4802
5268
  }
4803
5269
 
4804
5270
  // src/core/generateFromIr.ts
4805
- import { toRasterizeFontFaces } from "@json-to-office/shared/fonts/node";
5271
+ import {
5272
+ toChartFontFaces,
5273
+ toRasterizeFontFaces
5274
+ } from "@json-to-office/shared/fonts/node";
4806
5275
 
4807
5276
  // src/core/prerasterizeVisuals.ts
4808
5277
  import {
@@ -5200,9 +5669,90 @@ async function prerasterizeVisuals(root, serviceConfig, options = {}) {
5200
5669
  outcome = retry;
5201
5670
  }
5202
5671
  }
5203
- if (!outcome.applied) await rasterizeIndividually(chunk);
5672
+ if (!outcome.applied) await rasterizeIndividually(chunk);
5673
+ }
5674
+ return map;
5675
+ }
5676
+
5677
+ // src/core/componentTransform.ts
5678
+ async function transformComponents(doc, replace) {
5679
+ return await walk(doc, replace);
5680
+ }
5681
+ async function walk(node, replace) {
5682
+ if (Array.isArray(node)) {
5683
+ return Promise.all(node.map((child) => walk(child, replace)));
5684
+ }
5685
+ if (!node || typeof node !== "object") return node;
5686
+ const obj = node;
5687
+ if (typeof obj.name === "string" && obj.props) {
5688
+ const replacement = await replace(obj);
5689
+ if (replacement) return replacement;
5690
+ }
5691
+ const next = { ...obj };
5692
+ if (Array.isArray(obj.children)) {
5693
+ next.children = await walk(obj.children, replace);
5694
+ }
5695
+ if (obj.props && typeof obj.props === "object") {
5696
+ const props = obj.props;
5697
+ let changed = false;
5698
+ const nextProps = { ...props };
5699
+ for (const key of ["header", "footer"]) {
5700
+ if (Array.isArray(props[key])) {
5701
+ nextProps[key] = await walk(props[key], replace);
5702
+ changed = true;
5703
+ }
5704
+ }
5705
+ if (Array.isArray(props.columns)) {
5706
+ nextProps.columns = await Promise.all(
5707
+ props.columns.map(
5708
+ (column) => walkColumn(column, replace)
5709
+ )
5710
+ );
5711
+ changed = true;
5712
+ }
5713
+ if (changed) next.props = nextProps;
5204
5714
  }
5205
- return map;
5715
+ return next;
5716
+ }
5717
+ async function walkColumn(column, replace) {
5718
+ if (!column || typeof column !== "object") return column;
5719
+ const source = column;
5720
+ const next = { ...source };
5721
+ const header = source.header;
5722
+ if (header && typeof header === "object" && "content" in header) {
5723
+ next.header = { ...header, content: await walk(header.content, replace) };
5724
+ }
5725
+ if (Array.isArray(source.cells)) {
5726
+ next.cells = await Promise.all(
5727
+ source.cells.map(async (cell) => {
5728
+ if (cell && typeof cell === "object" && "content" in cell) {
5729
+ const source2 = cell;
5730
+ return { ...source2, content: await walk(source2.content, replace) };
5731
+ }
5732
+ return cell;
5733
+ })
5734
+ );
5735
+ }
5736
+ return next;
5737
+ }
5738
+ function withNodeIdentity(original, replacement) {
5739
+ const out = { ...replacement };
5740
+ if (original.id !== void 0) out.id = original.id;
5741
+ if (original.enabled !== void 0) out.enabled = original.enabled;
5742
+ return out;
5743
+ }
5744
+ function containsComponent(root, name) {
5745
+ const seen = /* @__PURE__ */ new WeakSet();
5746
+ const visit = (node) => {
5747
+ if (!node || typeof node !== "object" || seen.has(node)) return false;
5748
+ seen.add(node);
5749
+ if (Array.isArray(node)) return node.some(visit);
5750
+ const obj = node;
5751
+ if (typeof obj.name === "string" && obj.enabled === false) return false;
5752
+ if (obj.name === name && obj.props !== void 0) return true;
5753
+ return Object.values(obj).some(visit);
5754
+ };
5755
+ return visit(root);
5206
5756
  }
5207
5757
 
5208
5758
  // src/core/desugarExternals.ts
@@ -5216,7 +5766,14 @@ import {
5216
5766
 
5217
5767
  // src/components/highcharts.ts
5218
5768
  init_colorUtils();
5219
- import { DEFAULT_CHART_THEME_COLORS } from "@json-to-office/shared";
5769
+ init_layoutUtils();
5770
+ import {
5771
+ chartFamilyResolver,
5772
+ chartPointsPerPixel,
5773
+ POINTS_PER_PIXEL_96DPI,
5774
+ withChartFontFaceCss,
5775
+ withChartTypography
5776
+ } from "@json-to-office/shared";
5220
5777
  var DEFAULT_EXPORT_SERVER_URL = "http://localhost:7801";
5221
5778
  async function generateChart(config, servicesConfig) {
5222
5779
  if (!isNodeEnvironment()) {
@@ -5270,19 +5827,83 @@ function toChartColor(value, theme) {
5270
5827
  function withThemeColors(config, theme) {
5271
5828
  const options = config.options;
5272
5829
  if (!options || options.colors || !theme?.colors) return config;
5273
- const themeColors = theme.colors;
5274
- const palette = DEFAULT_CHART_THEME_COLORS.map((token) => {
5275
- const value = themeColors[token];
5276
- return typeof value === "string" && value.length > 0 ? toChartColor(value, theme) : void 0;
5277
- }).filter((c) => c !== void 0);
5830
+ const palette = chartPaletteValues(theme).flatMap((value) => {
5831
+ const color = toChartColor(value, theme);
5832
+ return color ? [color] : [];
5833
+ });
5278
5834
  if (palette.length === 0) return config;
5279
5835
  return {
5280
5836
  ...config,
5281
5837
  options: { ...config.options, colors: palette }
5282
5838
  };
5283
5839
  }
5284
- async function renderChartToImageProps(props, theme, servicesConfig) {
5285
- const config = withThemeColors(props, theme);
5840
+ var POINTS_PER_TWIP = 1 / 20;
5841
+ var POINTS_PER_PIXEL = POINTS_PER_PIXEL_96DPI;
5842
+ function placedWidthPt(config, theme) {
5843
+ const { width, height } = config;
5844
+ const chart = config.options.chart;
5845
+ if (typeof width === "number") return width * POINTS_PER_PIXEL;
5846
+ if (typeof width === "string") {
5847
+ const page = getPageSetup(theme);
5848
+ const contentWidthPt = (page.size.width - page.margin.left - page.margin.right) * POINTS_PER_TWIP;
5849
+ return parseFloat(width) / 100 * contentWidthPt;
5850
+ }
5851
+ if (typeof height === "number" && chart.height > 0) {
5852
+ return height / chart.height * chart.width * POINTS_PER_PIXEL;
5853
+ }
5854
+ return void 0;
5855
+ }
5856
+ function toHex(token, theme) {
5857
+ return `#${resolveColor(token, theme)}`;
5858
+ }
5859
+ function themeChartTypography(theme) {
5860
+ const styles = theme.styles ?? {};
5861
+ const family = chartFamilyResolver(theme);
5862
+ const bodyPt = styles.normal?.size ?? theme.fonts.body.size ?? 11;
5863
+ const label = styles.chartLabel;
5864
+ const heading = styles.heading3;
5865
+ const labelPt = label?.size ?? Math.max(bodyPt - 1, 6);
5866
+ return {
5867
+ bodyFamily: family(theme.fonts.body.family),
5868
+ headingFamily: family(theme.fonts.heading.family),
5869
+ textColor: toHex("textPrimary", theme),
5870
+ mutedColor: toHex("textSecondary", theme),
5871
+ labelPt,
5872
+ labelWeight: label?.fontWeight ?? (label?.bold ? 700 : void 0),
5873
+ titlePt: heading?.size ?? bodyPt + 2,
5874
+ titleWeight: heading?.fontWeight ?? (heading?.bold === false ? void 0 : 700),
5875
+ sourcePt: styles.source?.size ?? Math.max(labelPt - 1, 6)
5876
+ };
5877
+ }
5878
+ function withThemeTypography(config, theme) {
5879
+ if (!theme?.fonts?.body?.family || !theme.fonts.heading?.family) {
5880
+ return config;
5881
+ }
5882
+ return {
5883
+ ...config,
5884
+ options: withChartTypography(
5885
+ config.options,
5886
+ themeChartTypography(theme),
5887
+ chartPointsPerPixel(
5888
+ config.options.chart.width,
5889
+ placedWidthPt(config, theme)
5890
+ )
5891
+ )
5892
+ };
5893
+ }
5894
+ function withChartFontFaces(config, theme, faces) {
5895
+ if (!faces?.length || !theme?.fonts) return config;
5896
+ return withChartFontFaceCss(config, faces, [
5897
+ theme.fonts.body.family,
5898
+ theme.fonts.heading.family
5899
+ ]);
5900
+ }
5901
+ async function renderChartToImageProps(props, theme, servicesConfig, chartFonts) {
5902
+ const config = withChartFontFaces(
5903
+ withThemeTypography(withThemeColors(props, theme), theme),
5904
+ theme,
5905
+ chartFonts
5906
+ );
5286
5907
  const chart = await generateChart(config, servicesConfig);
5287
5908
  const hasConfigDimensions = config.width !== void 0 || config.height !== void 0;
5288
5909
  return {
@@ -5293,74 +5914,6 @@ async function renderChartToImageProps(props, theme, servicesConfig) {
5293
5914
  };
5294
5915
  }
5295
5916
 
5296
- // src/core/componentTransform.ts
5297
- async function transformComponents(doc, replace) {
5298
- return await walk(doc, replace);
5299
- }
5300
- async function walk(node, replace) {
5301
- if (Array.isArray(node)) {
5302
- return Promise.all(node.map((child) => walk(child, replace)));
5303
- }
5304
- if (!node || typeof node !== "object") return node;
5305
- const obj = node;
5306
- if (typeof obj.name === "string" && obj.props) {
5307
- const replacement = await replace(obj);
5308
- if (replacement) return replacement;
5309
- }
5310
- const next = { ...obj };
5311
- if (Array.isArray(obj.children)) {
5312
- next.children = await walk(obj.children, replace);
5313
- }
5314
- if (obj.props && typeof obj.props === "object") {
5315
- const props = obj.props;
5316
- let changed = false;
5317
- const nextProps = { ...props };
5318
- for (const key of ["header", "footer"]) {
5319
- if (Array.isArray(props[key])) {
5320
- nextProps[key] = await walk(props[key], replace);
5321
- changed = true;
5322
- }
5323
- }
5324
- if (Array.isArray(props.columns)) {
5325
- nextProps.columns = await Promise.all(
5326
- props.columns.map(
5327
- (column) => walkColumn(column, replace)
5328
- )
5329
- );
5330
- changed = true;
5331
- }
5332
- if (changed) next.props = nextProps;
5333
- }
5334
- return next;
5335
- }
5336
- async function walkColumn(column, replace) {
5337
- if (!column || typeof column !== "object") return column;
5338
- const source = column;
5339
- const next = { ...source };
5340
- const header = source.header;
5341
- if (header && typeof header === "object" && "content" in header) {
5342
- next.header = { ...header, content: await walk(header.content, replace) };
5343
- }
5344
- if (Array.isArray(source.cells)) {
5345
- next.cells = await Promise.all(
5346
- source.cells.map(async (cell) => {
5347
- if (cell && typeof cell === "object" && "content" in cell) {
5348
- const source2 = cell;
5349
- return { ...source2, content: await walk(source2.content, replace) };
5350
- }
5351
- return cell;
5352
- })
5353
- );
5354
- }
5355
- return next;
5356
- }
5357
- function withNodeIdentity(original, replacement) {
5358
- const out = { ...replacement };
5359
- if (original.id !== void 0) out.id = original.id;
5360
- if (original.enabled !== void 0) out.enabled = original.enabled;
5361
- return out;
5362
- }
5363
-
5364
5917
  // src/core/desugarExternals.ts
5365
5918
  async function desugarExternals(document, options) {
5366
5919
  const prerastered = await prerasterizeVisuals(
@@ -5390,7 +5943,8 @@ async function desugarExternals(document, options) {
5390
5943
  props: await renderChartToImageProps(
5391
5944
  node.props,
5392
5945
  options.theme,
5393
- options.services?.highcharts
5946
+ options.services?.highcharts,
5947
+ options.chartFonts
5394
5948
  )
5395
5949
  });
5396
5950
  }
@@ -5520,10 +6074,7 @@ init_colorUtils();
5520
6074
  import {
5521
6075
  FeatureRequirementCollector
5522
6076
  } from "@json-to-office/shared/rendering";
5523
- import {
5524
- synthesizeFamilyName,
5525
- DEFAULT_CHART_THEME_COLORS as DEFAULT_CHART_THEME_COLORS2
5526
- } from "@json-to-office/shared";
6077
+ import { capsFormatting as capsFormatting2, synthesizeFamilyName as synthesizeFamilyName2 } from "@json-to-office/shared";
5527
6078
  import {
5528
6079
  isNativeVisualProps as isNativeVisualProps4
5529
6080
  } from "@json-to-office/shared-docx";
@@ -6039,6 +6590,7 @@ function resolveColors(entries, at, deps) {
6039
6590
  init_themes();
6040
6591
  init_colorUtils();
6041
6592
  init_styleHelpers();
6593
+ import { capsFormatting, synthesizeFamilyName } from "@json-to-office/shared";
6042
6594
  var RIGHT_MARGIN_TAB_TWIPS = 9026 * 2;
6043
6595
  var STATISTIC_NUMBER_STYLE_ID = "StatisticNumber";
6044
6596
  var STATISTIC_DESCRIPTION_STYLE_ID = "StatisticDescription";
@@ -6047,25 +6599,8 @@ var STATISTIC_SIZE_POINTS = {
6047
6599
  medium: 28,
6048
6600
  large: 40
6049
6601
  };
6050
- function isValidStyleName(name) {
6051
- return [
6052
- "normal",
6053
- "heading1",
6054
- "heading2",
6055
- "heading3",
6056
- "heading4",
6057
- "heading5",
6058
- "heading6",
6059
- "title",
6060
- "subtitle"
6061
- ].includes(name);
6062
- }
6063
6602
  function getStyleSafe(theme, styleName) {
6064
- if (!isValidStyleName(styleName)) {
6065
- console.warn(`Invalid style name: ${styleName}`);
6066
- return void 0;
6067
- }
6068
- return theme.styles?.[styleName];
6603
+ return theme.styles && Object.hasOwn(theme.styles, styleName) ? theme.styles[styleName] : void 0;
6069
6604
  }
6070
6605
  function resolveStyleWithBaseStyle(theme, styleName, visited = /* @__PURE__ */ new Set()) {
6071
6606
  if (visited.has(styleName)) {
@@ -6122,9 +6657,17 @@ function resolveSpacing(spacing) {
6122
6657
  ...spacing?.after ? { afterTwips: pointsToTwips(spacing.after) } : {}
6123
6658
  };
6124
6659
  }
6660
+ function weightedFamily(props) {
6661
+ return props.fontWeight === void 0 ? void 0 : synthesizeFamilyName(
6662
+ props.family || "Arial",
6663
+ props.fontWeight,
6664
+ props.italic === true
6665
+ );
6666
+ }
6125
6667
  function convertRunProperties(merged, theme, defaultColor, defaultSize) {
6668
+ const weighted = weightedFamily(merged);
6126
6669
  return {
6127
- fontFamily: merged.family || "Arial",
6670
+ fontFamily: weighted?.family ?? merged.family ?? "Arial",
6128
6671
  sizeHalfPoints: (merged.size || defaultSize || 11) * 2,
6129
6672
  color: {
6130
6673
  hex: resolveColor(
@@ -6134,6 +6677,8 @@ function convertRunProperties(merged, theme, defaultColor, defaultSize) {
6134
6677
  },
6135
6678
  ...merged.bold !== void 0 && { bold: merged.bold },
6136
6679
  ...merged.italic !== void 0 && { italic: merged.italic },
6680
+ ...weighted && { bold: weighted.bold, italic: weighted.italic },
6681
+ ...merged.case !== void 0 && capsFormatting(merged.case),
6137
6682
  ...merged.underline !== void 0 && merged.underline && { underline: { type: "single" } },
6138
6683
  ...merged.characterSpacing && {
6139
6684
  characterSpacingTwentieths: merged.characterSpacing.type === "condensed" ? -merged.characterSpacing.value : merged.characterSpacing.value
@@ -6206,12 +6751,15 @@ function convertBorders(borders3, theme) {
6206
6751
  return Object.keys(converted).length > 0 ? converted : void 0;
6207
6752
  }
6208
6753
  function fallbackRun(fontProps, theme, defaultColor, defaultSize) {
6754
+ const weighted = weightedFamily(fontProps);
6209
6755
  return {
6210
- fontFamily: fontProps.family || "Arial",
6756
+ fontFamily: weighted?.family ?? fontProps.family ?? "Arial",
6211
6757
  sizeHalfPoints: (fontProps.size || defaultSize) * 2,
6212
6758
  color: { hex: resolveColor(fontProps.color || defaultColor, theme) },
6213
6759
  ...fontProps.bold !== void 0 && { bold: fontProps.bold },
6214
6760
  ...fontProps.italic !== void 0 && { italic: fontProps.italic },
6761
+ ...weighted && { bold: weighted.bold, italic: weighted.italic },
6762
+ ...fontProps.case !== void 0 && capsFormatting(fontProps.case),
6215
6763
  ...fontProps.underline !== void 0 && fontProps.underline && { underline: { type: "single" } }
6216
6764
  };
6217
6765
  }
@@ -6241,6 +6789,8 @@ function createDocumentStyles(themeNameOrObject = "minimal", language) {
6241
6789
  size: normalStyle?.size,
6242
6790
  color: normalStyle?.color,
6243
6791
  bold: normalStyle?.bold,
6792
+ fontWeight: normalStyle?.fontWeight,
6793
+ case: normalStyle?.case,
6244
6794
  italic: normalStyle?.italic,
6245
6795
  underline: normalStyle?.underline,
6246
6796
  characterSpacing: normalStyle?.characterSpacing,
@@ -6280,6 +6830,8 @@ function createDocumentStyles(themeNameOrObject = "minimal", language) {
6280
6830
  size: headingStyle.size,
6281
6831
  color: headingStyle.color,
6282
6832
  bold: headingStyle.bold,
6833
+ fontWeight: headingStyle.fontWeight,
6834
+ case: headingStyle.case,
6283
6835
  italic: headingStyle.italic,
6284
6836
  underline: headingStyle.underline,
6285
6837
  characterSpacing: headingStyle.characterSpacing,
@@ -6340,6 +6892,8 @@ function createDocumentStyles(themeNameOrObject = "minimal", language) {
6340
6892
  size: headingStyle.size,
6341
6893
  color: headingStyle.color,
6342
6894
  bold: headingStyle.bold,
6895
+ fontWeight: headingStyle.fontWeight,
6896
+ case: headingStyle.case,
6343
6897
  italic: headingStyle.italic,
6344
6898
  underline: headingStyle.underline,
6345
6899
  characterSpacing: headingStyle.characterSpacing,
@@ -6401,6 +6955,8 @@ function createDocumentStyles(themeNameOrObject = "minimal", language) {
6401
6955
  size: titleStyle.size,
6402
6956
  color: titleStyle.color,
6403
6957
  bold: titleStyle.bold,
6958
+ fontWeight: titleStyle.fontWeight,
6959
+ case: titleStyle.case,
6404
6960
  italic: titleStyle.italic,
6405
6961
  underline: titleStyle.underline,
6406
6962
  characterSpacing: titleStyle.characterSpacing,
@@ -6456,6 +7012,8 @@ function createDocumentStyles(themeNameOrObject = "minimal", language) {
6456
7012
  size: subtitleStyle.size,
6457
7013
  color: subtitleStyle.color,
6458
7014
  bold: subtitleStyle.bold,
7015
+ fontWeight: subtitleStyle.fontWeight,
7016
+ case: subtitleStyle.case,
6459
7017
  italic: subtitleStyle.italic,
6460
7018
  underline: subtitleStyle.underline,
6461
7019
  characterSpacing: subtitleStyle.characterSpacing,
@@ -6578,6 +7136,8 @@ function createDocumentStyles(themeNameOrObject = "minimal", language) {
6578
7136
  size: customStyle.size,
6579
7137
  color: customStyle.color,
6580
7138
  bold: customStyle.bold,
7139
+ fontWeight: customStyle.fontWeight,
7140
+ case: customStyle.case,
6581
7141
  italic: customStyle.italic,
6582
7142
  underline: customStyle.underline,
6583
7143
  characterSpacing: customStyle.characterSpacing,
@@ -8142,6 +8702,210 @@ function emptyToUndefined(formatting) {
8142
8702
  return entries.length === 0 ? void 0 : Object.fromEntries(entries);
8143
8703
  }
8144
8704
 
8705
+ // src/blocks/index.ts
8706
+ import { KEY_TAKEAWAYS_BUDGET } from "@json-to-office/shared-docx";
8707
+
8708
+ // src/blocks/keyTakeaways.ts
8709
+ var MIN_RULE_PT = 0.25;
8710
+ var MAX_RULE_PT = 12;
8711
+ var KEY_TAKEAWAYS_DEFAULT_LABEL = "Key takeaways";
8712
+ var FALLBACK_RECIPE = {
8713
+ ruleWeightPt: 1.5,
8714
+ ruleColor: "accent",
8715
+ padPt: 6,
8716
+ type: "label"
8717
+ };
8718
+ function compileKeyTakeaways(props, theme) {
8719
+ const recipe = theme.chrome?.keyTakeaways;
8720
+ const labelRole = recipe?.type ?? FALLBACK_RECIPE.type;
8721
+ const labelStyle = theme.styles?.[labelRole];
8722
+ const ruleWeightPt = Math.min(
8723
+ MAX_RULE_PT,
8724
+ Math.max(
8725
+ MIN_RULE_PT,
8726
+ recipe?.rule?.weightPt ?? FALLBACK_RECIPE.ruleWeightPt
8727
+ )
8728
+ );
8729
+ const ruleColor = recipe?.rule?.color ?? recipe?.color ?? FALLBACK_RECIPE.ruleColor;
8730
+ const padPt = recipe?.padPt ?? FALLBACK_RECIPE.padPt;
8731
+ const children = [
8732
+ {
8733
+ name: "divider",
8734
+ props: {
8735
+ thickness: ruleWeightPt,
8736
+ color: ruleColor,
8737
+ spacing: { before: 12, after: padPt }
8738
+ }
8739
+ },
8740
+ {
8741
+ name: "paragraph",
8742
+ props: {
8743
+ text: props.label ?? KEY_TAKEAWAYS_DEFAULT_LABEL,
8744
+ keepNext: true,
8745
+ spacing: { after: 4 },
8746
+ // The label role when the theme resolves one; a bold run in the
8747
+ // recipe's colour otherwise, so the label still reads as a label.
8748
+ ...labelStyle ? { themeStyle: labelRole } : {
8749
+ font: {
8750
+ bold: true,
8751
+ ...recipe?.color !== void 0 && { color: recipe.color }
8752
+ }
8753
+ }
8754
+ }
8755
+ },
8756
+ {
8757
+ name: "list",
8758
+ props: { items: [...props.items] }
8759
+ },
8760
+ {
8761
+ name: "divider",
8762
+ props: {
8763
+ thickness: 0.5,
8764
+ color: "borderPrimary",
8765
+ spacing: { before: padPt, after: 12 }
8766
+ }
8767
+ }
8768
+ ];
8769
+ return {
8770
+ children,
8771
+ sourceMap: {
8772
+ "/children/0": "",
8773
+ "/children/1": "",
8774
+ "/children/1/props/text": "/props/label",
8775
+ "/children/2": "",
8776
+ "/children/2/props/items": "/props/items",
8777
+ "/children/3": ""
8778
+ }
8779
+ };
8780
+ }
8781
+
8782
+ // src/blocks/index.ts
8783
+ var BLOCK_NAMES = ["key-takeaways"];
8784
+ var BLOCKS = {
8785
+ "key-takeaways": {
8786
+ compile: (props, theme) => compileKeyTakeaways(props, theme),
8787
+ budgets: (props, pointer) => (Array.isArray(props.items) ? props.items : []).flatMap(
8788
+ (item, index) => typeof item === "string" ? [
8789
+ {
8790
+ block: "key-takeaways",
8791
+ slot: "items",
8792
+ path: `${pointer}/props/items/${index}`,
8793
+ words: wordCount(item),
8794
+ maxWords: KEY_TAKEAWAYS_BUDGET.items.maxWords
8795
+ }
8796
+ ] : []
8797
+ )
8798
+ }
8799
+ };
8800
+ function isBlockName(name) {
8801
+ return BLOCK_NAMES.includes(name);
8802
+ }
8803
+ function isRecord(value) {
8804
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8805
+ }
8806
+ function expandBlocks(document, theme) {
8807
+ const sourceMap = {};
8808
+ const blocks = [];
8809
+ const walk2 = (node, pointer) => {
8810
+ if (Array.isArray(node)) {
8811
+ return node.map((child, index) => walk2(child, `${pointer}/${index}`));
8812
+ }
8813
+ if (!isRecord(node)) return node;
8814
+ if (typeof node.name === "string" && isRecord(node.props)) {
8815
+ if (node.enabled !== false && isBlockName(node.name)) {
8816
+ const compiled = BLOCKS[node.name].compile(node.props, theme);
8817
+ blocks.push(pointer);
8818
+ for (const [emitted, authored] of Object.entries(compiled.sourceMap)) {
8819
+ sourceMap[`${pointer}${emitted}`] = `${pointer}${authored}`;
8820
+ }
8821
+ return { ...node, children: compiled.children };
8822
+ }
8823
+ }
8824
+ const next = { ...node };
8825
+ if (isRecord(node.props)) {
8826
+ const props = { ...node.props };
8827
+ let changed = false;
8828
+ for (const key of ["header", "footer"]) {
8829
+ if (Array.isArray(props[key])) {
8830
+ props[key] = walk2(props[key], `${pointer}/props/${key}`);
8831
+ changed = true;
8832
+ }
8833
+ }
8834
+ if (Array.isArray(props.columns)) {
8835
+ props.columns = props.columns.map((column, index) => {
8836
+ if (!isRecord(column)) return column;
8837
+ const base = `${pointer}/props/columns/${index}`;
8838
+ const out = { ...column };
8839
+ if (isRecord(column.header) && "content" in column.header) {
8840
+ out.header = {
8841
+ ...column.header,
8842
+ content: walk2(column.header.content, `${base}/header/content`)
8843
+ };
8844
+ }
8845
+ if (Array.isArray(column.cells)) {
8846
+ out.cells = column.cells.map(
8847
+ (cell, cellIndex) => isRecord(cell) && "content" in cell ? {
8848
+ ...cell,
8849
+ content: walk2(
8850
+ cell.content,
8851
+ `${base}/cells/${cellIndex}/content`
8852
+ )
8853
+ } : cell
8854
+ );
8855
+ }
8856
+ return out;
8857
+ });
8858
+ changed = true;
8859
+ }
8860
+ if (changed) next.props = props;
8861
+ }
8862
+ if (Array.isArray(node.children)) {
8863
+ next.children = walk2(node.children, `${pointer}/children`);
8864
+ }
8865
+ return next;
8866
+ };
8867
+ const expanded = walk2(document, "");
8868
+ return {
8869
+ document: blocks.length > 0 ? expanded : document,
8870
+ sourceMap,
8871
+ blocks
8872
+ };
8873
+ }
8874
+ function toAuthoredPointer(sourceMap, pointer) {
8875
+ let best;
8876
+ for (const emitted of Object.keys(sourceMap)) {
8877
+ if ((pointer === emitted || pointer.startsWith(`${emitted}/`)) && (best === void 0 || emitted.length > best.length)) {
8878
+ best = emitted;
8879
+ }
8880
+ }
8881
+ if (best === void 0) return pointer;
8882
+ return `${sourceMap[best]}${pointer.slice(best.length)}`;
8883
+ }
8884
+ function blockSlotBudgets(document, blocks) {
8885
+ return blocks.flatMap((pointer) => {
8886
+ const node = nodeAt(document, pointer);
8887
+ if (!isRecord(node) || !isRecord(node.props) || !isBlockName(node.name)) {
8888
+ return [];
8889
+ }
8890
+ return BLOCKS[node.name].budgets(node.props, pointer);
8891
+ });
8892
+ }
8893
+ function wordCount(text) {
8894
+ const trimmed = text.trim();
8895
+ return trimmed === "" ? 0 : trimmed.split(/\s+/).length;
8896
+ }
8897
+ function nodeAt(root, pointer) {
8898
+ if (pointer === "") return root;
8899
+ let current = root;
8900
+ for (const segment of pointer.slice(1).split("/")) {
8901
+ const key = segment.replace(/~1/g, "/").replace(/~0/g, "~");
8902
+ if (Array.isArray(current)) current = current[Number(key)];
8903
+ else if (isRecord(current)) current = current[key];
8904
+ else return void 0;
8905
+ }
8906
+ return current;
8907
+ }
8908
+
8145
8909
  // src/ir/compiler.ts
8146
8910
  init_types();
8147
8911
  init_units();
@@ -8453,6 +9217,7 @@ function compileChromeParagraph(component, scope) {
8453
9217
  italic: font.italic ?? false,
8454
9218
  ...font.underline !== void 0 ? { underline: font.underline } : {},
8455
9219
  ...font.fontWeight !== void 0 ? { fontWeight: font.fontWeight } : {},
9220
+ ...font.case !== void 0 ? { case: font.case } : {},
8456
9221
  ...font.lineSpacing !== void 0 ? { lineSpacing: font.lineSpacing } : {}
8457
9222
  }
8458
9223
  };
@@ -8552,6 +9317,10 @@ function compileComponent(component, scope) {
8552
9317
  case "table":
8553
9318
  return compileTable(component, scope);
8554
9319
  default:
9320
+ if (isBlockName(component.name)) {
9321
+ if (component.enabled === false) return [];
9322
+ return compileBlock(component, scope);
9323
+ }
8555
9324
  scope.ctx.unsupported.push({
8556
9325
  name: component.name,
8557
9326
  path: scope.path
@@ -8559,6 +9328,20 @@ function compileComponent(component, scope) {
8559
9328
  return [];
8560
9329
  }
8561
9330
  }
9331
+ function compileBlock(component, scope) {
9332
+ const children = component.children ?? [];
9333
+ if (children.length === 0) {
9334
+ scope.ctx.unsupported.push({ name: component.name, path: scope.path });
9335
+ return [];
9336
+ }
9337
+ return children.flatMap(
9338
+ (child, index) => compileComponent(child, {
9339
+ ...scope,
9340
+ path: `${scope.path}.children[${index}]`,
9341
+ id: `${scope.id}:b${index}`
9342
+ })
9343
+ );
9344
+ }
8562
9345
  var MARKDOWN_LIST_LEVELS = {
8563
9346
  unordered: [
8564
9347
  { level: 0, format: "bullet", text: "\u2022", alignment: "left" },
@@ -9756,7 +10539,11 @@ function isoDateTime(date) {
9756
10539
  }
9757
10540
  function runFormatting3(font, props, ctx) {
9758
10541
  const hasWeightRequest = font.fontWeight != null || font.bold === true;
9759
- const effectiveFamily = font.family ?? (hasWeightRequest ? resolveBodyFamily(ctx.theme) : void 0);
10542
+ const roleName = props.themeStyle;
10543
+ const role = roleName ? ctx.theme.typography?.roles?.[roleName] : void 0;
10544
+ const roleStyles = ctx.theme.styles;
10545
+ const roleFamily = role && roleName ? resolveFontFamily(ctx.theme, roleStyles?.[roleName]?.font ?? role.face) : void 0;
10546
+ const effectiveFamily = font.family ?? (hasWeightRequest ? roleFamily ?? resolveBodyFamily(ctx.theme) : void 0);
9760
10547
  const weighted = applyFontWeightAlias({
9761
10548
  fontFamily: effectiveFamily,
9762
10549
  bold: font.bold,
@@ -9764,7 +10551,7 @@ function runFormatting3(font, props, ctx) {
9764
10551
  fontWeight: font.fontWeight
9765
10552
  });
9766
10553
  const formatting = {};
9767
- if (font.family || weighted.font && weighted.font !== effectiveFamily) {
10554
+ if (font.family || hasWeightRequest && roleFamily || weighted.font && weighted.font !== effectiveFamily) {
9768
10555
  formatting.fontFamily = weighted.font;
9769
10556
  }
9770
10557
  if (font.size) formatting.sizeHalfPoints = pointsToHalfPoints(font.size);
@@ -9776,6 +10563,8 @@ function runFormatting3(font, props, ctx) {
9776
10563
  formatting.underline = font.underline ? { type: "single" } : void 0;
9777
10564
  }
9778
10565
  if (font.scale) formatting.scalePercent = font.scale;
10566
+ if (font.case !== void 0)
10567
+ Object.assign(formatting, capsFormatting2(font.case));
9779
10568
  if (font.characterSpacing) {
9780
10569
  const { type, value } = font.characterSpacing;
9781
10570
  formatting.characterSpacingTwentieths = type === "condensed" ? -value : value;
@@ -9789,7 +10578,7 @@ function applyFontWeightAlias(opts) {
9789
10578
  return { bold: opts.bold, italic: opts.italic };
9790
10579
  }
9791
10580
  const weight = opts.fontWeight ?? (opts.bold === true ? 700 : void 0);
9792
- const synth = synthesizeFamilyName(
10581
+ const synth = synthesizeFamilyName2(
9793
10582
  opts.fontFamily,
9794
10583
  weight,
9795
10584
  opts.italic === true
@@ -10128,15 +10917,13 @@ function compileChart(component, scope) {
10128
10917
  }
10129
10918
  const colors = Array.isArray(props.chartColors) ? props.chartColors.map(
10130
10919
  (color) => resolveColor(String(color), ctx.theme)
10131
- ) : DEFAULT_CHART_THEME_COLORS2.map((token) => {
10132
- const value = ctx.theme?.colors?.[token];
10133
- if (typeof value !== "string" || value.length === 0) return void 0;
10920
+ ) : chartPaletteValues(ctx.theme).flatMap((value) => {
10134
10921
  try {
10135
- return resolveColor(token, ctx.theme);
10922
+ return [resolveColor(value, ctx.theme)];
10136
10923
  } catch {
10137
- return void 0;
10924
+ return [];
10138
10925
  }
10139
- }).filter((color) => color !== void 0);
10926
+ });
10140
10927
  const page = getPageSetup(ctx.theme);
10141
10928
  const contentWidthInches = (page.size.width - page.margin.left - page.margin.right) / 1440;
10142
10929
  const widthInches = typeof props.width === "number" ? props.width : contentWidthInches;
@@ -11203,8 +11990,8 @@ var formatDate = (date, formatString = "MMMM d, yyyy") => {
11203
11990
  };
11204
11991
 
11205
11992
  // src/styles/utils/componentDefaults.ts
11206
- import { mergeWithDefaults } from "@json-to-office/shared";
11207
11993
  import { mergeWithDefaults as mergeWithDefaults2 } from "@json-to-office/shared";
11994
+ import { mergeWithDefaults as mergeWithDefaults3 } from "@json-to-office/shared";
11208
11995
  function getComponentDefaults(theme) {
11209
11996
  return theme.componentDefaults || {};
11210
11997
  }
@@ -11253,35 +12040,35 @@ function getListDefaults(theme) {
11253
12040
  }
11254
12041
  function resolveHeadingProps(props, theme) {
11255
12042
  const defaults = getHeadingDefaults(theme);
11256
- return mergeWithDefaults(props, defaults);
12043
+ return mergeWithDefaults2(props, defaults);
11257
12044
  }
11258
12045
  function resolveParagraphProps(props, theme) {
11259
12046
  const defaults = getTextDefaults(theme);
11260
- return mergeWithDefaults(props, defaults);
12047
+ return mergeWithDefaults2(props, defaults);
11261
12048
  }
11262
12049
  function resolveImageProps(props, theme) {
11263
12050
  const defaults = getImageDefaults(theme);
11264
- return mergeWithDefaults(props, defaults);
12051
+ return mergeWithDefaults2(props, defaults);
11265
12052
  }
11266
12053
  function resolveStatisticProps(props, theme) {
11267
12054
  const defaults = getStatisticDefaults(theme);
11268
- return mergeWithDefaults(props, defaults);
12055
+ return mergeWithDefaults2(props, defaults);
11269
12056
  }
11270
12057
  function resolveTableProps(props, theme) {
11271
12058
  const defaults = getTableDefaults(theme);
11272
- return mergeWithDefaults(props, defaults);
12059
+ return mergeWithDefaults2(props, defaults);
11273
12060
  }
11274
12061
  function resolveSectionProps(props, theme) {
11275
12062
  const defaults = getSectionDefaults(theme);
11276
- return mergeWithDefaults(props, defaults);
12063
+ return mergeWithDefaults2(props, defaults);
11277
12064
  }
11278
12065
  function resolveColumnsProps(props, theme) {
11279
12066
  const defaults = getColumnsDefaults(theme);
11280
- return mergeWithDefaults(props, defaults);
12067
+ return mergeWithDefaults2(props, defaults);
11281
12068
  }
11282
12069
  function resolveListProps(props, theme) {
11283
12070
  const defaults = getListDefaults(theme);
11284
- return mergeWithDefaults(props, defaults);
12071
+ return mergeWithDefaults2(props, defaults);
11285
12072
  }
11286
12073
  function resolveHighchartsProps(props, _theme) {
11287
12074
  return props;
@@ -11295,7 +12082,7 @@ function getCustomComponentDefaults(theme, componentName) {
11295
12082
  }
11296
12083
  function resolveCustomComponentProps(props, theme, componentName) {
11297
12084
  const defaults = getCustomComponentDefaults(theme, componentName);
11298
- return mergeWithDefaults(props, defaults);
12085
+ return mergeWithDefaults2(props, defaults);
11299
12086
  }
11300
12087
 
11301
12088
  // src/styles/utils/resolveComponentTree.ts
@@ -11348,7 +12135,7 @@ function resolveComponentTree(components, theme) {
11348
12135
  // src/core/structure.ts
11349
12136
  function resolveDocumentTree(document, theme) {
11350
12137
  const docDefaults = document.props.componentDefaults;
11351
- const mergedComponentDefaults = docDefaults ? mergeWithDefaults2(docDefaults, theme.componentDefaults || {}) : void 0;
12138
+ const mergedComponentDefaults = docDefaults ? mergeWithDefaults3(docDefaults, theme.componentDefaults || {}) : void 0;
11352
12139
  const docNoProofWords = document.props.noProofWords;
11353
12140
  const mergedNoProofWords = docNoProofWords || theme.noProofWords ? Array.from(
11354
12141
  /* @__PURE__ */ new Set([...theme.noProofWords || [], ...docNoProofWords || []])
@@ -11489,6 +12276,7 @@ import {
11489
12276
  normalizeHighchartsChart
11490
12277
  } from "@json-to-office/quality";
11491
12278
  import { DEFAULT_DOCX_RENDERER_ID as DEFAULT_DOCX_RENDERER_ID2 } from "@json-to-office/shared-docx";
12279
+ import { designColors as designColors2, resolveDesignColor as resolveDesignColor2 } from "@json-to-office/shared";
11492
12280
 
11493
12281
  // src/json/normalizer.ts
11494
12282
  function normalizeComponent(component) {
@@ -11998,11 +12786,13 @@ function walkActive(node, path3, page, visit) {
11998
12786
  );
11999
12787
  }
12000
12788
  function prepareDocxQualityDocument(document, options = {}) {
12001
- const context = options.context ?? resolveThemeContext(normalizeDocument(document)[0], {
12789
+ const themed = options.context ?? resolveThemeContext(normalizeDocument(document)[0], {
12002
12790
  customThemes: options.customThemes,
12003
12791
  fonts: options.fonts,
12004
12792
  warnings: options.warnings
12005
12793
  });
12794
+ const expanded = expandBlocks(themed.document, themed.theme);
12795
+ const context = expanded.blocks.length > 0 ? { ...themed, document: expanded.document } : themed;
12006
12796
  const resolved = resolveDocumentTree(context.document, context.theme);
12007
12797
  const basePage = pageBox(resolved.theme, context.themeName);
12008
12798
  const typography = {
@@ -12012,24 +12802,50 @@ function prepareDocxQualityDocument(document, options = {}) {
12012
12802
  const facts = [];
12013
12803
  const provenance = {};
12014
12804
  let previousHeadingLevel;
12015
- const addFact = (fact) => {
12805
+ const authoredPath = (path3) => toAuthoredPointer(expanded.sourceMap, path3);
12806
+ const addFact = (raw) => {
12807
+ const fact = {
12808
+ ...raw,
12809
+ path: authoredPath(raw.path),
12810
+ ...raw.relatedPaths && {
12811
+ relatedPaths: raw.relatedPaths.map(authoredPath)
12812
+ }
12813
+ };
12016
12814
  facts.push(fact);
12017
12815
  provenance[fact.id] = {
12018
12816
  path: fact.path,
12019
12817
  ...fact.relatedPaths && { relatedPaths: fact.relatedPaths }
12020
12818
  };
12021
12819
  };
12820
+ for (const budget of blockSlotBudgets(context.document, expanded.blocks)) {
12821
+ addFact({
12822
+ id: `docx:block-slot:${budget.path}`,
12823
+ kind: "docx/block-slot",
12824
+ ...budget
12825
+ });
12826
+ }
12022
12827
  const paletteHexes = {};
12023
- for (const [token, value] of Object.entries(
12024
- resolved.theme.colors ?? {}
12025
- )) {
12828
+ const visualColors = designColors2(
12829
+ resolved.theme.colors,
12830
+ resolved.theme.palette
12831
+ );
12832
+ const entries = {
12833
+ ...visualColors,
12834
+ ...Object.fromEntries(
12835
+ (resolved.theme.palette?.chart ?? []).map((value) => {
12836
+ const hex = resolveDesignColor2(value, visualColors);
12837
+ return [hex ? `#${hex}` : value, value];
12838
+ })
12839
+ )
12840
+ };
12841
+ for (const [token, value] of Object.entries(entries)) {
12026
12842
  if (typeof value !== "string") continue;
12027
- const hex = normalizeHex(value);
12843
+ const hex = normalizeHex(resolveDesignColor2(value, visualColors) ?? value);
12028
12844
  if (hex) paletteHexes[token] = hex;
12029
12845
  }
12030
- const paletteTokens = SERIES_COLOR_TOKENS.filter(
12031
- (token) => paletteHexes[token] !== void 0
12032
- );
12846
+ const paletteTokens = resolved.theme.palette?.chart?.map(
12847
+ (value) => `#${resolveDesignColor2(value, visualColors)}`
12848
+ ) ?? SERIES_COLOR_TOKENS.filter((token) => paletteHexes[token] !== void 0);
12033
12849
  const authoredPropsAt = (pointer) => asRecord(asRecord(nodeAtPointer(context.document, pointer))?.props);
12034
12850
  addFact({
12035
12851
  id: "docx:theme",
@@ -12150,7 +12966,16 @@ function prepareDocxQualityDocument(document, options = {}) {
12150
12966
  },
12151
12967
  facts,
12152
12968
  provenance,
12153
- renderer: options.renderer ?? DEFAULT_DOCX_RENDERER_ID2
12969
+ renderer: options.renderer ?? DEFAULT_DOCX_RENDERER_ID2,
12970
+ ...expanded.blocks.length > 0 && {
12971
+ metadata: {
12972
+ blocks: {
12973
+ sourceMap: expanded.sourceMap,
12974
+ blocks: expanded.blocks,
12975
+ document: context.document
12976
+ }
12977
+ }
12978
+ }
12154
12979
  };
12155
12980
  }
12156
12981
 
@@ -12190,19 +13015,22 @@ async function compileDocumentScoped(document, options, warnings) {
12190
13015
  });
12191
13016
  const { context } = prepared.model;
12192
13017
  const hasVisual = collectVisualProps(context.document).length > 0;
13018
+ const hasHighcharts = containsComponent(context.document, "highcharts");
12193
13019
  const resolvedFonts = await resolveDocumentFonts(
12194
13020
  context.document,
12195
13021
  context.theme,
12196
13022
  options.fonts,
12197
13023
  warnings,
12198
- hasVisual
13024
+ hasVisual || hasHighcharts
12199
13025
  );
12200
13026
  const visualFonts = hasVisual ? toRasterizeFontFaces(resolvedFonts, warnings) : [];
13027
+ const chartFonts = hasHighcharts ? toChartFontFaces(resolvedFonts) : [];
12201
13028
  const desugared = await desugarExternals(context.document, {
12202
13029
  theme: context.theme,
12203
13030
  ...options.services ? { services: options.services } : {},
12204
13031
  ...options.baseDir !== void 0 ? { baseDir: options.baseDir } : {},
12205
- ...visualFonts.length > 0 ? { visualFonts } : {}
13032
+ ...visualFonts.length > 0 ? { visualFonts } : {},
13033
+ ...chartFonts.length > 0 ? { chartFonts } : {}
12206
13034
  });
12207
13035
  const resolved = desugared === context.document ? prepared.model.document : resolveDocumentTree(desugared, context.theme);
12208
13036
  const structure = await processResolvedDocument(