@json-to-office/core-docx 2.3.0 → 2.5.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 (33) hide show
  1. package/dist/components/highcharts.d.ts.map +1 -1
  2. package/dist/core/generationContext.d.ts.map +1 -1
  3. package/dist/index.js +653 -52
  4. package/dist/index.js.map +1 -1
  5. package/dist/ir/compiler.d.ts.map +1 -1
  6. package/dist/plugin/example/index.js +476 -61
  7. package/dist/plugin/example/index.js.map +1 -1
  8. package/dist/quality/facts.d.ts +56 -2
  9. package/dist/quality/facts.d.ts.map +1 -1
  10. package/dist/quality/rules.d.ts +16 -0
  11. package/dist/quality/rules.d.ts.map +1 -1
  12. package/dist/renderers/docxjs/emit.d.ts.map +1 -1
  13. package/dist/renderers/docxjs/index.d.ts.map +1 -1
  14. package/dist/renderers/office-open/index.d.ts.map +1 -1
  15. package/dist/styles/themeToStyles.d.ts.map +1 -1
  16. package/dist/styles/utils/colorUtils.d.ts +12 -1
  17. package/dist/styles/utils/colorUtils.d.ts.map +1 -1
  18. package/dist/styles/utils/styleHelpers.d.ts +2 -0
  19. package/dist/styles/utils/styleHelpers.d.ts.map +1 -1
  20. package/dist/templates/themes/index.d.ts +1896 -668
  21. package/dist/templates/themes/index.d.ts.map +1 -1
  22. package/dist/themes/defaults.d.ts +196 -181
  23. package/dist/themes/defaults.d.ts.map +1 -1
  24. package/dist/themes/design-system.d.ts +4 -0
  25. package/dist/themes/design-system.d.ts.map +1 -0
  26. package/dist/themes/overrides.d.ts +2 -1
  27. package/dist/themes/overrides.d.ts.map +1 -1
  28. package/dist/tsconfig.tsbuildinfo +1 -1
  29. package/dist/utils/normalizeTextCase.d.ts +15 -0
  30. package/dist/utils/normalizeTextCase.d.ts.map +1 -0
  31. package/dist/utils/packageDocument.d.ts +2 -0
  32. package/dist/utils/packageDocument.d.ts.map +1 -1
  33. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -15,8 +15,10 @@ var __export = (target, all) => {
15
15
  };
16
16
 
17
17
  // src/themes/defaults.ts
18
+ import { designColors } from "@json-to-office/shared";
18
19
  function ensureThemeDefaults(theme) {
19
20
  return {
21
+ ...theme,
20
22
  $schema: theme.$schema,
21
23
  name: theme.name || "default",
22
24
  displayName: theme.displayName || "Default Theme",
@@ -27,6 +29,7 @@ function ensureThemeDefaults(theme) {
27
29
  ...theme.colors || {}
28
30
  },
29
31
  fonts: {
32
+ ...theme.fonts || {},
30
33
  heading: { ...DEFAULT_FONTS.heading, ...theme.fonts?.heading || {} },
31
34
  body: { ...DEFAULT_FONTS.body, ...theme.fonts?.body || {} },
32
35
  mono: { ...DEFAULT_FONTS.mono, ...theme.fonts?.mono || {} },
@@ -45,10 +48,13 @@ function ensureThemeDefaults(theme) {
45
48
  };
46
49
  }
47
50
  function getThemeColors(theme) {
48
- return {
49
- ...DEFAULT_COLORS,
50
- ...theme.colors || {}
51
- };
51
+ return designColors(
52
+ {
53
+ ...DEFAULT_COLORS,
54
+ ...theme.colors || {}
55
+ },
56
+ theme.palette
57
+ );
52
58
  }
53
59
  function getThemeFonts(theme) {
54
60
  return {
@@ -186,6 +192,10 @@ var init_defaults = __esm({
186
192
  });
187
193
 
188
194
  // src/styles/utils/colorUtils.ts
195
+ import {
196
+ DEFAULT_CHART_THEME_COLORS,
197
+ resolveDesignColor
198
+ } from "@json-to-office/shared";
189
199
  function resolveColor(colorValue, theme) {
190
200
  if (colorValue.startsWith("#")) {
191
201
  const hexValue = colorValue.slice(1);
@@ -199,7 +209,11 @@ function resolveColor(colorValue, theme) {
199
209
  const colors = getThemeColors(theme);
200
210
  const resolvedColor = colors[colorValue];
201
211
  if (typeof resolvedColor === "string") {
202
- return resolveColor(resolvedColor, theme);
212
+ const hex = resolveDesignColor(colorValue, colors);
213
+ if (hex) return hex.toUpperCase();
214
+ throw new Error(
215
+ `Unresolvable theme color: "${colorValue}" (missing reference or cycle).`
216
+ );
203
217
  }
204
218
  if (/^[0-9A-Fa-f]{6}$/.test(colorValue)) {
205
219
  return colorValue.toUpperCase();
@@ -208,6 +222,15 @@ function resolveColor(colorValue, theme) {
208
222
  `Invalid color value: "${colorValue}". Must be a hex color with # prefix (e.g., "#000000") or a valid theme color name.`
209
223
  );
210
224
  }
225
+ function chartPaletteValues(theme) {
226
+ const explicit = theme.palette?.chart;
227
+ if (explicit) return [...explicit];
228
+ const colors = getThemeColors(theme);
229
+ return DEFAULT_CHART_THEME_COLORS.flatMap((token) => {
230
+ const value = colors[token];
231
+ return typeof value === "string" && value.length > 0 ? [value] : [];
232
+ });
233
+ }
211
234
  var init_colorUtils = __esm({
212
235
  "src/styles/utils/colorUtils.ts"() {
213
236
  "use strict";
@@ -988,6 +1011,8 @@ function resolveFontProperties(theme, fontRef) {
988
1011
  size: fontDef.size,
989
1012
  color: fontDef.color,
990
1013
  bold: fontDef.bold,
1014
+ fontWeight: fontDef.fontWeight,
1015
+ case: fontDef.case,
991
1016
  italic: fontDef.italic,
992
1017
  underline: fontDef.underline,
993
1018
  alignment: fontDef.alignment,
@@ -2070,6 +2095,9 @@ function runOptions(formatting) {
2070
2095
  options.underline = formatting.underline ? { type: formatting.underline.type } : void 0;
2071
2096
  }
2072
2097
  if (formatting.strike !== void 0) options.strike = formatting.strike;
2098
+ if (formatting.allCaps !== void 0) options.allCaps = formatting.allCaps;
2099
+ if (formatting.smallCaps !== void 0)
2100
+ options.smallCaps = formatting.smallCaps;
2073
2101
  if (formatting.scalePercent !== void 0) {
2074
2102
  options.scale = formatting.scalePercent;
2075
2103
  }
@@ -2760,6 +2788,44 @@ var init_styles2 = __esm({
2760
2788
  }
2761
2789
  });
2762
2790
 
2791
+ // src/utils/normalizeTextCase.ts
2792
+ function normalizeTextCase(zip) {
2793
+ let changed = false;
2794
+ for (const entry of zip.getEntries()) {
2795
+ if (!WORD_XML.test(entry.entryName)) continue;
2796
+ const xml = entry.getData().toString("utf8");
2797
+ if (!ANY_CASE_FLAG.test(xml)) continue;
2798
+ const normalized = xml.replace(RUN_PROPERTIES, completeCaseFlags);
2799
+ if (normalized === xml) continue;
2800
+ zip.updateFile(entry, Buffer.from(normalized, "utf8"));
2801
+ changed = true;
2802
+ }
2803
+ return changed;
2804
+ }
2805
+ function completeCaseFlags(runProperties3) {
2806
+ const flag = CASE_FLAG.exec(runProperties3);
2807
+ if (!flag) return runProperties3;
2808
+ const isCaps = flag[1] === "caps";
2809
+ const opposite = `<w:${isCaps ? "smallCaps" : "caps"} w:val="false"/>`;
2810
+ if (runProperties3.includes(`<w:${isCaps ? "smallCaps" : "caps"}`)) {
2811
+ return runProperties3;
2812
+ }
2813
+ return runProperties3.replace(
2814
+ flag[0],
2815
+ isCaps ? `${flag[0]}${opposite}` : `${opposite}${flag[0]}`
2816
+ );
2817
+ }
2818
+ var WORD_XML, ANY_CASE_FLAG, RUN_PROPERTIES, CASE_FLAG;
2819
+ var init_normalizeTextCase = __esm({
2820
+ "src/utils/normalizeTextCase.ts"() {
2821
+ "use strict";
2822
+ WORD_XML = /^word\/.*\.xml$/;
2823
+ ANY_CASE_FLAG = /<w:(?:caps|smallCaps)\b/;
2824
+ RUN_PROPERTIES = /<w:rPr\b[^>]*>[\s\S]*?<\/w:rPr>/g;
2825
+ CASE_FLAG = /<w:(caps|smallCaps)\b[^>]*(?:\/>|>[\s\S]*?<\/w:\1>)/;
2826
+ }
2827
+ });
2828
+
2763
2829
  // src/utils/packageDocument.ts
2764
2830
  import AdmZip from "adm-zip";
2765
2831
  function resolveGenerationDate(options) {
@@ -2828,6 +2894,7 @@ function canonicalizeRelationshipIds(zip) {
2828
2894
  }
2829
2895
  function canonicalizeDocxBuffer(buffer, generatedAt = DEFAULT_GENERATION_DATE) {
2830
2896
  const zip = new AdmZip(buffer);
2897
+ normalizeTextCase(zip);
2831
2898
  canonicalizeRelationshipIds(zip);
2832
2899
  const isoTimestamp = generatedAt.toISOString();
2833
2900
  const coreProperties3 = zip.getEntry("docProps/core.xml");
@@ -2844,10 +2911,15 @@ function canonicalizeDocxBuffer(buffer, generatedAt = DEFAULT_GENERATION_DATE) {
2844
2911
  }
2845
2912
  return zip.toBuffer();
2846
2913
  }
2914
+ function normalizeDocxCaseBuffer(buffer) {
2915
+ const zip = new AdmZip(buffer);
2916
+ return normalizeTextCase(zip) ? zip.toBuffer() : buffer;
2917
+ }
2847
2918
  var DEFAULT_GENERATION_DATE, STABLE_RELATIONSHIP_ID;
2848
2919
  var init_packageDocument = __esm({
2849
2920
  "src/utils/packageDocument.ts"() {
2850
2921
  "use strict";
2922
+ init_normalizeTextCase();
2851
2923
  DEFAULT_GENERATION_DATE = /* @__PURE__ */ new Date("2000-01-01T00:00:00.000Z");
2852
2924
  STABLE_RELATIONSHIP_ID = /^rId\d+$/;
2853
2925
  }
@@ -2906,7 +2978,8 @@ function createDocxJsRenderer() {
2906
2978
  const document = buildDocument(ir, resources);
2907
2979
  const packed = await Packer.toBuffer(document);
2908
2980
  const fixed = fixFloatingImageIdsInBuffer(packed);
2909
- if (renderOptions?.deterministic === false) return new Uint8Array(fixed);
2981
+ if (renderOptions?.deterministic === false)
2982
+ return new Uint8Array(normalizeDocxCaseBuffer(fixed));
2910
2983
  return new Uint8Array(
2911
2984
  canonicalizeDocxBuffer(
2912
2985
  fixed,
@@ -4185,7 +4258,8 @@ async function createOfficeOpenDocxRenderer() {
4185
4258
  spliceChartParts(zip, charts);
4186
4259
  raw = zip.toBuffer();
4187
4260
  }
4188
- if (options?.deterministic === false) return new Uint8Array(raw);
4261
+ if (options?.deterministic === false)
4262
+ return new Uint8Array(normalizeDocxCaseBuffer(raw));
4189
4263
  return new Uint8Array(
4190
4264
  canonicalizeDocxBuffer(
4191
4265
  raw,
@@ -5043,7 +5117,6 @@ import {
5043
5117
 
5044
5118
  // src/components/highcharts.ts
5045
5119
  init_colorUtils();
5046
- import { DEFAULT_CHART_THEME_COLORS } from "@json-to-office/shared";
5047
5120
  var DEFAULT_EXPORT_SERVER_URL = "http://localhost:7801";
5048
5121
  async function generateChart(config, servicesConfig) {
5049
5122
  if (!isNodeEnvironment()) {
@@ -5097,11 +5170,10 @@ function toChartColor(value, theme) {
5097
5170
  function withThemeColors(config, theme) {
5098
5171
  const options = config.options;
5099
5172
  if (!options || options.colors || !theme?.colors) return config;
5100
- const themeColors = theme.colors;
5101
- const palette = DEFAULT_CHART_THEME_COLORS.map((token) => {
5102
- const value = themeColors[token];
5103
- return typeof value === "string" && value.length > 0 ? toChartColor(value, theme) : void 0;
5104
- }).filter((c) => c !== void 0);
5173
+ const palette = chartPaletteValues(theme).flatMap((value) => {
5174
+ const color = toChartColor(value, theme);
5175
+ return color ? [color] : [];
5176
+ });
5105
5177
  if (palette.length === 0) return config;
5106
5178
  return {
5107
5179
  ...config,
@@ -5347,10 +5419,7 @@ init_colorUtils();
5347
5419
  import {
5348
5420
  FeatureRequirementCollector
5349
5421
  } from "@json-to-office/shared/rendering";
5350
- import {
5351
- synthesizeFamilyName,
5352
- DEFAULT_CHART_THEME_COLORS as DEFAULT_CHART_THEME_COLORS2
5353
- } from "@json-to-office/shared";
5422
+ import { capsFormatting as capsFormatting2, synthesizeFamilyName as synthesizeFamilyName2 } from "@json-to-office/shared";
5354
5423
  import {
5355
5424
  isNativeVisualProps as isNativeVisualProps4
5356
5425
  } from "@json-to-office/shared-docx";
@@ -5866,6 +5935,7 @@ function resolveColors(entries, at, deps) {
5866
5935
  init_themes();
5867
5936
  init_colorUtils();
5868
5937
  init_styleHelpers();
5938
+ import { capsFormatting, synthesizeFamilyName } from "@json-to-office/shared";
5869
5939
  var RIGHT_MARGIN_TAB_TWIPS = 9026 * 2;
5870
5940
  var STATISTIC_NUMBER_STYLE_ID = "StatisticNumber";
5871
5941
  var STATISTIC_DESCRIPTION_STYLE_ID = "StatisticDescription";
@@ -5874,25 +5944,8 @@ var STATISTIC_SIZE_POINTS = {
5874
5944
  medium: 28,
5875
5945
  large: 40
5876
5946
  };
5877
- function isValidStyleName(name) {
5878
- return [
5879
- "normal",
5880
- "heading1",
5881
- "heading2",
5882
- "heading3",
5883
- "heading4",
5884
- "heading5",
5885
- "heading6",
5886
- "title",
5887
- "subtitle"
5888
- ].includes(name);
5889
- }
5890
5947
  function getStyleSafe(theme, styleName) {
5891
- if (!isValidStyleName(styleName)) {
5892
- console.warn(`Invalid style name: ${styleName}`);
5893
- return void 0;
5894
- }
5895
- return theme.styles?.[styleName];
5948
+ return theme.styles && Object.hasOwn(theme.styles, styleName) ? theme.styles[styleName] : void 0;
5896
5949
  }
5897
5950
  function resolveStyleWithBaseStyle(theme, styleName, visited = /* @__PURE__ */ new Set()) {
5898
5951
  if (visited.has(styleName)) {
@@ -5949,9 +6002,17 @@ function resolveSpacing(spacing) {
5949
6002
  ...spacing?.after ? { afterTwips: pointsToTwips(spacing.after) } : {}
5950
6003
  };
5951
6004
  }
6005
+ function weightedFamily(props) {
6006
+ return props.fontWeight === void 0 ? void 0 : synthesizeFamilyName(
6007
+ props.family || "Arial",
6008
+ props.fontWeight,
6009
+ props.italic === true
6010
+ );
6011
+ }
5952
6012
  function convertRunProperties(merged, theme, defaultColor, defaultSize) {
6013
+ const weighted = weightedFamily(merged);
5953
6014
  return {
5954
- fontFamily: merged.family || "Arial",
6015
+ fontFamily: weighted?.family ?? merged.family ?? "Arial",
5955
6016
  sizeHalfPoints: (merged.size || defaultSize || 11) * 2,
5956
6017
  color: {
5957
6018
  hex: resolveColor(
@@ -5961,6 +6022,8 @@ function convertRunProperties(merged, theme, defaultColor, defaultSize) {
5961
6022
  },
5962
6023
  ...merged.bold !== void 0 && { bold: merged.bold },
5963
6024
  ...merged.italic !== void 0 && { italic: merged.italic },
6025
+ ...weighted && { bold: weighted.bold, italic: weighted.italic },
6026
+ ...merged.case !== void 0 && capsFormatting(merged.case),
5964
6027
  ...merged.underline !== void 0 && merged.underline && { underline: { type: "single" } },
5965
6028
  ...merged.characterSpacing && {
5966
6029
  characterSpacingTwentieths: merged.characterSpacing.type === "condensed" ? -merged.characterSpacing.value : merged.characterSpacing.value
@@ -6033,12 +6096,15 @@ function convertBorders(borders3, theme) {
6033
6096
  return Object.keys(converted).length > 0 ? converted : void 0;
6034
6097
  }
6035
6098
  function fallbackRun(fontProps, theme, defaultColor, defaultSize) {
6099
+ const weighted = weightedFamily(fontProps);
6036
6100
  return {
6037
- fontFamily: fontProps.family || "Arial",
6101
+ fontFamily: weighted?.family ?? fontProps.family ?? "Arial",
6038
6102
  sizeHalfPoints: (fontProps.size || defaultSize) * 2,
6039
6103
  color: { hex: resolveColor(fontProps.color || defaultColor, theme) },
6040
6104
  ...fontProps.bold !== void 0 && { bold: fontProps.bold },
6041
6105
  ...fontProps.italic !== void 0 && { italic: fontProps.italic },
6106
+ ...weighted && { bold: weighted.bold, italic: weighted.italic },
6107
+ ...fontProps.case !== void 0 && capsFormatting(fontProps.case),
6042
6108
  ...fontProps.underline !== void 0 && fontProps.underline && { underline: { type: "single" } }
6043
6109
  };
6044
6110
  }
@@ -6068,6 +6134,8 @@ function createDocumentStyles(themeNameOrObject = "minimal", language) {
6068
6134
  size: normalStyle?.size,
6069
6135
  color: normalStyle?.color,
6070
6136
  bold: normalStyle?.bold,
6137
+ fontWeight: normalStyle?.fontWeight,
6138
+ case: normalStyle?.case,
6071
6139
  italic: normalStyle?.italic,
6072
6140
  underline: normalStyle?.underline,
6073
6141
  characterSpacing: normalStyle?.characterSpacing,
@@ -6107,6 +6175,8 @@ function createDocumentStyles(themeNameOrObject = "minimal", language) {
6107
6175
  size: headingStyle.size,
6108
6176
  color: headingStyle.color,
6109
6177
  bold: headingStyle.bold,
6178
+ fontWeight: headingStyle.fontWeight,
6179
+ case: headingStyle.case,
6110
6180
  italic: headingStyle.italic,
6111
6181
  underline: headingStyle.underline,
6112
6182
  characterSpacing: headingStyle.characterSpacing,
@@ -6167,6 +6237,8 @@ function createDocumentStyles(themeNameOrObject = "minimal", language) {
6167
6237
  size: headingStyle.size,
6168
6238
  color: headingStyle.color,
6169
6239
  bold: headingStyle.bold,
6240
+ fontWeight: headingStyle.fontWeight,
6241
+ case: headingStyle.case,
6170
6242
  italic: headingStyle.italic,
6171
6243
  underline: headingStyle.underline,
6172
6244
  characterSpacing: headingStyle.characterSpacing,
@@ -6228,6 +6300,8 @@ function createDocumentStyles(themeNameOrObject = "minimal", language) {
6228
6300
  size: titleStyle.size,
6229
6301
  color: titleStyle.color,
6230
6302
  bold: titleStyle.bold,
6303
+ fontWeight: titleStyle.fontWeight,
6304
+ case: titleStyle.case,
6231
6305
  italic: titleStyle.italic,
6232
6306
  underline: titleStyle.underline,
6233
6307
  characterSpacing: titleStyle.characterSpacing,
@@ -6283,6 +6357,8 @@ function createDocumentStyles(themeNameOrObject = "minimal", language) {
6283
6357
  size: subtitleStyle.size,
6284
6358
  color: subtitleStyle.color,
6285
6359
  bold: subtitleStyle.bold,
6360
+ fontWeight: subtitleStyle.fontWeight,
6361
+ case: subtitleStyle.case,
6286
6362
  italic: subtitleStyle.italic,
6287
6363
  underline: subtitleStyle.underline,
6288
6364
  characterSpacing: subtitleStyle.characterSpacing,
@@ -6405,6 +6481,8 @@ function createDocumentStyles(themeNameOrObject = "minimal", language) {
6405
6481
  size: customStyle.size,
6406
6482
  color: customStyle.color,
6407
6483
  bold: customStyle.bold,
6484
+ fontWeight: customStyle.fontWeight,
6485
+ case: customStyle.case,
6408
6486
  italic: customStyle.italic,
6409
6487
  underline: customStyle.underline,
6410
6488
  characterSpacing: customStyle.characterSpacing,
@@ -8280,6 +8358,7 @@ function compileChromeParagraph(component, scope) {
8280
8358
  italic: font.italic ?? false,
8281
8359
  ...font.underline !== void 0 ? { underline: font.underline } : {},
8282
8360
  ...font.fontWeight !== void 0 ? { fontWeight: font.fontWeight } : {},
8361
+ ...font.case !== void 0 ? { case: font.case } : {},
8283
8362
  ...font.lineSpacing !== void 0 ? { lineSpacing: font.lineSpacing } : {}
8284
8363
  }
8285
8364
  };
@@ -9583,7 +9662,11 @@ function isoDateTime(date) {
9583
9662
  }
9584
9663
  function runFormatting3(font, props, ctx) {
9585
9664
  const hasWeightRequest = font.fontWeight != null || font.bold === true;
9586
- const effectiveFamily = font.family ?? (hasWeightRequest ? resolveBodyFamily(ctx.theme) : void 0);
9665
+ const roleName = props.themeStyle;
9666
+ const role = roleName ? ctx.theme.typography?.roles?.[roleName] : void 0;
9667
+ const roleStyles = ctx.theme.styles;
9668
+ const roleFamily = role && roleName ? resolveFontFamily(ctx.theme, roleStyles?.[roleName]?.font ?? role.face) : void 0;
9669
+ const effectiveFamily = font.family ?? (hasWeightRequest ? roleFamily ?? resolveBodyFamily(ctx.theme) : void 0);
9587
9670
  const weighted = applyFontWeightAlias({
9588
9671
  fontFamily: effectiveFamily,
9589
9672
  bold: font.bold,
@@ -9591,7 +9674,7 @@ function runFormatting3(font, props, ctx) {
9591
9674
  fontWeight: font.fontWeight
9592
9675
  });
9593
9676
  const formatting = {};
9594
- if (font.family || weighted.font && weighted.font !== effectiveFamily) {
9677
+ if (font.family || hasWeightRequest && roleFamily || weighted.font && weighted.font !== effectiveFamily) {
9595
9678
  formatting.fontFamily = weighted.font;
9596
9679
  }
9597
9680
  if (font.size) formatting.sizeHalfPoints = pointsToHalfPoints(font.size);
@@ -9603,6 +9686,8 @@ function runFormatting3(font, props, ctx) {
9603
9686
  formatting.underline = font.underline ? { type: "single" } : void 0;
9604
9687
  }
9605
9688
  if (font.scale) formatting.scalePercent = font.scale;
9689
+ if (font.case !== void 0)
9690
+ Object.assign(formatting, capsFormatting2(font.case));
9606
9691
  if (font.characterSpacing) {
9607
9692
  const { type, value } = font.characterSpacing;
9608
9693
  formatting.characterSpacingTwentieths = type === "condensed" ? -value : value;
@@ -9616,7 +9701,7 @@ function applyFontWeightAlias(opts) {
9616
9701
  return { bold: opts.bold, italic: opts.italic };
9617
9702
  }
9618
9703
  const weight = opts.fontWeight ?? (opts.bold === true ? 700 : void 0);
9619
- const synth = synthesizeFamilyName(
9704
+ const synth = synthesizeFamilyName2(
9620
9705
  opts.fontFamily,
9621
9706
  weight,
9622
9707
  opts.italic === true
@@ -9955,15 +10040,13 @@ function compileChart(component, scope) {
9955
10040
  }
9956
10041
  const colors = Array.isArray(props.chartColors) ? props.chartColors.map(
9957
10042
  (color) => resolveColor(String(color), ctx.theme)
9958
- ) : DEFAULT_CHART_THEME_COLORS2.map((token) => {
9959
- const value = ctx.theme?.colors?.[token];
9960
- if (typeof value !== "string" || value.length === 0) return void 0;
10043
+ ) : chartPaletteValues(ctx.theme).flatMap((value) => {
9961
10044
  try {
9962
- return resolveColor(token, ctx.theme);
10045
+ return [resolveColor(value, ctx.theme)];
9963
10046
  } catch {
9964
- return void 0;
10047
+ return [];
9965
10048
  }
9966
- }).filter((color) => color !== void 0);
10049
+ });
9967
10050
  const page = getPageSetup(ctx.theme);
9968
10051
  const contentWidthInches = (page.size.width - page.margin.left - page.margin.right) / 1440;
9969
10052
  const widthInches = typeof props.width === "number" ? props.width : contentWidthInches;
@@ -11298,7 +11381,17 @@ function createRenderContext(document, theme, themeName) {
11298
11381
  }
11299
11382
 
11300
11383
  // src/quality/facts.ts
11384
+ import {
11385
+ chartEncodingFor,
11386
+ collectColorLiterals,
11387
+ collectFontFamilies,
11388
+ collectPlaceholders,
11389
+ hasUnitMarker,
11390
+ normalizeHex,
11391
+ normalizeHighchartsChart
11392
+ } from "@json-to-office/quality";
11301
11393
  import { DEFAULT_DOCX_RENDERER_ID as DEFAULT_DOCX_RENDERER_ID2 } from "@json-to-office/shared-docx";
11394
+ import { designColors as designColors2, resolveDesignColor as resolveDesignColor2 } from "@json-to-office/shared";
11302
11395
 
11303
11396
  // src/core/generationContext.ts
11304
11397
  import { applyExportMode } from "@json-to-office/shared";
@@ -11322,6 +11415,7 @@ function resolveBuiltInTheme(themeName, options = {}) {
11322
11415
  }
11323
11416
 
11324
11417
  // src/themes/overrides.ts
11418
+ import { mergeWithDefaults as mergeWithDefaults3 } from "@json-to-office/shared";
11325
11419
  function applyThemeOverrides(theme, overrides) {
11326
11420
  if (!overrides) return theme;
11327
11421
  const fonts = { ...theme.fonts };
@@ -11335,12 +11429,87 @@ function applyThemeOverrides(theme, overrides) {
11335
11429
  }
11336
11430
  return {
11337
11431
  ...theme,
11432
+ ...Object.fromEntries(
11433
+ ["palette", "typography", "spacing", "chrome", "motif"].flatMap(
11434
+ (name) => {
11435
+ const key = name;
11436
+ return overrides[key] === void 0 ? [] : [[key, mergeWithDefaults3(overrides[key], theme[key] ?? {})]];
11437
+ }
11438
+ )
11439
+ ),
11338
11440
  colors: { ...theme.colors, ...overrides.colors ?? {} },
11339
11441
  fonts,
11340
11442
  ...Object.keys(styles).length > 0 && { styles }
11341
11443
  };
11342
11444
  }
11343
11445
 
11446
+ // src/themes/design-system.ts
11447
+ init_defaults();
11448
+ import {
11449
+ designCanvas,
11450
+ resolveTypeRoles,
11451
+ validateDesignColors
11452
+ } from "@json-to-office/shared";
11453
+ function resolveDocxDesignSystem(theme) {
11454
+ validateDesignColors(theme, getThemeColors(theme));
11455
+ if (!theme.typography && !theme.spacing) return theme;
11456
+ const canvas = designCanvas("docx", theme.page.size);
11457
+ const roles = resolveTypeRoles(theme, canvas, theme.fonts.body.size ?? 11);
11458
+ const styles = { ...theme.styles };
11459
+ for (const [name, role] of Object.entries(roles)) {
11460
+ const spacing = {
11461
+ ...role.spaceBefore !== void 0 && { before: role.spaceBefore },
11462
+ ...role.spaceAfter !== void 0 && { after: role.spaceAfter }
11463
+ };
11464
+ styles[name] = {
11465
+ font: role.face ?? "body",
11466
+ size: role.size,
11467
+ ...role.weight !== void 0 && { fontWeight: role.weight },
11468
+ ...role.color !== void 0 && { color: role.color },
11469
+ ...role.case !== void 0 && { case: role.case },
11470
+ ...role.lineHeight !== void 0 && {
11471
+ lineSpacing: { type: "multiple", value: role.lineHeight }
11472
+ },
11473
+ ...role.tracking !== void 0 && {
11474
+ characterSpacing: {
11475
+ type: role.tracking < 0 ? "condensed" : "expanded",
11476
+ value: Math.abs(role.tracking) * role.size / 5
11477
+ }
11478
+ },
11479
+ ...Object.keys(spacing).length && { spacing },
11480
+ ...styles[name]
11481
+ };
11482
+ }
11483
+ const gap = theme.spacing?.blockGap?.normal ?? theme.spacing?.basePt;
11484
+ const scale = theme.typography?.scale?.[canvas];
11485
+ if (scale || gap !== void 0) {
11486
+ styles.normal = {
11487
+ ...scale && { size: scale.base },
11488
+ ...gap !== void 0 && { spacing: { after: gap } },
11489
+ ...styles.normal
11490
+ };
11491
+ }
11492
+ const space = theme.spacing?.canvas?.[canvas];
11493
+ const safe = space?.safeAreaIn;
11494
+ return {
11495
+ ...theme,
11496
+ styles,
11497
+ page: {
11498
+ ...theme.page,
11499
+ margins: {
11500
+ ...theme.page.margins,
11501
+ ...safe !== void 0 && {
11502
+ top: safe * 1440,
11503
+ bottom: safe * 1440,
11504
+ left: safe * 1440,
11505
+ right: safe * 1440
11506
+ },
11507
+ ...space?.gutterIn !== void 0 && { gutter: space.gutterIn * 1440 }
11508
+ }
11509
+ }
11510
+ };
11511
+ }
11512
+
11344
11513
  // src/core/generationContext.ts
11345
11514
  function defaultNamedThemeLookup(themeName, customThemes, warnings) {
11346
11515
  if (customThemes) {
@@ -11373,6 +11542,7 @@ function resolveThemeContext(documentIn, options = {}) {
11373
11542
  if (themeOverrides) {
11374
11543
  theme = applyThemeOverrides(theme, themeOverrides);
11375
11544
  }
11545
+ theme = resolveDocxDesignSystem(theme);
11376
11546
  const mode = applyExportMode({ doc: document, theme, fonts });
11377
11547
  for (const w of mode.warnings) {
11378
11548
  if (warnings) {
@@ -11539,6 +11709,164 @@ function tableFact(props, path4, availableWidthTwips) {
11539
11709
  explicitWidths
11540
11710
  };
11541
11711
  }
11712
+ var SERIES_COLOR_TOKENS = [
11713
+ "primary",
11714
+ "accent",
11715
+ "secondary",
11716
+ "accent4",
11717
+ "accent5",
11718
+ "accent6"
11719
+ ];
11720
+ function chartFact(node, props, path4, paletteTokens) {
11721
+ const base = {
11722
+ id: `docx:chart:${path4}`,
11723
+ kind: "docx/chart",
11724
+ path: path4,
11725
+ componentName: typeof node.name === "string" ? node.name : "",
11726
+ paletteTokens
11727
+ };
11728
+ if (node.name === "highcharts") {
11729
+ const shape = normalizeHighchartsChart(props.options);
11730
+ return {
11731
+ ...base,
11732
+ chartType: shape.chartType,
11733
+ encoding: shape.encoding,
11734
+ threeD: shape.threeD,
11735
+ seriesCount: shape.seriesCount,
11736
+ categoryCount: shape.categoryCount,
11737
+ seriesColorsStated: shape.seriesColorsStated,
11738
+ seriesColorsPath: `${path4}/props/options`,
11739
+ ...shape.valueAxisMin !== void 0 && {
11740
+ valueAxisMin: shape.valueAxisMin
11741
+ },
11742
+ unitStated: shape.unitStated,
11743
+ annotation: {
11744
+ stated: shape.annotationStated,
11745
+ path: path4,
11746
+ slot: "props.options.caption.text"
11747
+ }
11748
+ };
11749
+ }
11750
+ const chartType = typeof props.type === "string" ? props.type : "";
11751
+ if (chartType === "") return void 0;
11752
+ const series = Array.isArray(props.data) ? props.data : [];
11753
+ const categoryCount = Math.max(
11754
+ 0,
11755
+ ...series.map((entry) => {
11756
+ const record = asRecord(entry);
11757
+ const labels = Array.isArray(record?.labels) ? record.labels.length : 0;
11758
+ const values = Array.isArray(record?.values) ? record.values.length : 0;
11759
+ return Math.max(labels, values);
11760
+ })
11761
+ );
11762
+ const caption = typeof props.caption === "string" ? props.caption : "";
11763
+ const title = typeof props.title === "string" ? props.title : "";
11764
+ return {
11765
+ ...base,
11766
+ chartType,
11767
+ encoding: chartEncodingFor(chartType),
11768
+ // No 3D type exists in the DOCX chart schema — `office-open` draws none —
11769
+ // so this is always false here, and stays a field rather than an omission
11770
+ // so both formats answer the same questions.
11771
+ threeD: false,
11772
+ seriesCount: series.length,
11773
+ categoryCount,
11774
+ seriesColorsStated: Array.isArray(props.chartColors) && props.chartColors.length > 0,
11775
+ seriesColorsPath: `${path4}/props/chartColors`,
11776
+ // No `valueAxisMin`: a DOCX chart has no axis-floor property, so the
11777
+ // baseline is always the renderer's, which starts a bar at zero.
11778
+ unitStated: hasUnitMarker(
11779
+ typeof props.valAxisTitle === "string" ? props.valAxisTitle : ""
11780
+ ) || hasUnitMarker(title) || hasUnitMarker(caption),
11781
+ annotation: {
11782
+ stated: caption.trim() !== "",
11783
+ path: path4,
11784
+ slot: "props.caption"
11785
+ }
11786
+ };
11787
+ }
11788
+ var DOCX_ALIGNMENTS = /* @__PURE__ */ new Set(["left", "center", "right", "justify"]);
11789
+ function resolvedCellText(content) {
11790
+ return typeof content === "string" ? content : "[component]";
11791
+ }
11792
+ function statesOwnBorders(authored) {
11793
+ if (!authored) return false;
11794
+ if (authored.borderSize !== void 0 || authored.borderColor !== void 0 || authored.hideBorders !== void 0) {
11795
+ return true;
11796
+ }
11797
+ const layers = [
11798
+ asRecord(authored.cellDefaults),
11799
+ asRecord(authored.headerCellDefaults)
11800
+ ];
11801
+ if (layers.some(
11802
+ (layer) => layer?.borderSize !== void 0 || layer?.borderColor !== void 0
11803
+ )) {
11804
+ return true;
11805
+ }
11806
+ const columns = Array.isArray(authored.columns) ? authored.columns : [];
11807
+ return columns.some((column) => {
11808
+ const record = asRecord(column);
11809
+ const defaults = asRecord(record?.cellDefaults);
11810
+ return defaults?.borderSize !== void 0 || defaults?.borderColor !== void 0;
11811
+ });
11812
+ }
11813
+ function tableDesignFact(props, path4, theme, themeName, authored) {
11814
+ const authoredColumns = Array.isArray(props.columns) ? props.columns : [];
11815
+ if (authoredColumns.length === 0) return void 0;
11816
+ const model = resolveTableModel(
11817
+ props,
11818
+ theme,
11819
+ themeName
11820
+ );
11821
+ const bodyRows = model.rows;
11822
+ const drawnRows = bodyRows.length + (model.header ? 1 : 0);
11823
+ if (drawnRows === 0) return void 0;
11824
+ const fullGrid = statesOwnBorders(authored) && [...model.header ? [model.header] : [], ...bodyRows].every(
11825
+ (row) => row.cells.every(
11826
+ (cell) => ["top", "right", "bottom", "left"].every(
11827
+ (side) => !cell.borders[side].hidden && cell.borders[side].size > 0
11828
+ )
11829
+ )
11830
+ );
11831
+ const columns = authoredColumns.map(
11832
+ (column, index) => {
11833
+ const authoredColumn = asRecord(column) ?? {};
11834
+ const cells = Array.isArray(authoredColumn.cells) ? authoredColumn.cells : [];
11835
+ const alignments = /* @__PURE__ */ new Set();
11836
+ const values = [];
11837
+ bodyRows.forEach((row) => {
11838
+ const cell = row.cells[index];
11839
+ if (!cell || cell.missing) return;
11840
+ values.push(resolvedCellText(cell.content));
11841
+ alignments.add(cell.horizontalAlignment);
11842
+ });
11843
+ const header = model.header?.cells[index];
11844
+ return {
11845
+ index,
11846
+ path: `${path4}/props/columns/${index}`,
11847
+ ...header?.content !== void 0 && {
11848
+ header: resolvedCellText(header.content)
11849
+ },
11850
+ values,
11851
+ alignment: alignments.size === 1 ? [...alignments][0] : alignments.size === 0 ? "left" : "mixed",
11852
+ hasCellDefaults: asRecord(authoredColumn.cellDefaults) !== void 0,
11853
+ hasHeader: asRecord(authoredColumn.header) !== void 0,
11854
+ cellsWithOwnAlignment: cells.flatMap((cell, cellIndex) => {
11855
+ const alignment2 = asRecord(cell)?.horizontalAlignment;
11856
+ return typeof alignment2 === "string" && DOCX_ALIGNMENTS.has(alignment2) && alignment2 !== "right" ? [cellIndex] : [];
11857
+ })
11858
+ };
11859
+ }
11860
+ );
11861
+ return {
11862
+ id: `docx:table-design:${path4}`,
11863
+ kind: "docx/table",
11864
+ path: path4,
11865
+ columns,
11866
+ rowCount: drawnRows,
11867
+ fullGrid
11868
+ };
11869
+ }
11542
11870
  var TWIPS_PER_POINT3 = 20;
11543
11871
  var SINGLE_LINE_RATIO = 1.2;
11544
11872
  function finiteNumber(value) {
@@ -11567,20 +11895,23 @@ function characterSpacingPt(spacing) {
11567
11895
  if (rule === void 0 || value === void 0) return 0;
11568
11896
  return rule.type === "condensed" ? -value / TWIPS_PER_POINT3 : value / TWIPS_PER_POINT3;
11569
11897
  }
11570
- function pointerExists(root, pointer) {
11898
+ function nodeAtPointer(root, pointer) {
11571
11899
  let current = root;
11572
11900
  for (const token of pointer.split("/").slice(1)) {
11573
11901
  if (Array.isArray(current)) {
11574
11902
  const index = Number(token);
11575
- if (!Number.isInteger(index)) return false;
11903
+ if (!Number.isInteger(index)) return void 0;
11576
11904
  current = current[index];
11577
11905
  continue;
11578
11906
  }
11579
11907
  const record = asRecord(current);
11580
- if (!record) return false;
11908
+ if (!record) return void 0;
11581
11909
  current = record[token];
11582
11910
  }
11583
- return current !== void 0;
11911
+ return current;
11912
+ }
11913
+ function pointerExists(root, pointer) {
11914
+ return nodeAtPointer(root, pointer) !== void 0;
11584
11915
  }
11585
11916
  function styleKey(node, props) {
11586
11917
  if (node.name === "heading") {
@@ -11761,6 +12092,78 @@ function prepareDocxQualityDocument(document, options = {}) {
11761
12092
  ...fact.relatedPaths && { relatedPaths: fact.relatedPaths }
11762
12093
  };
11763
12094
  };
12095
+ const paletteHexes = {};
12096
+ const visualColors = designColors2(
12097
+ resolved.theme.colors,
12098
+ resolved.theme.palette
12099
+ );
12100
+ const entries = {
12101
+ ...visualColors,
12102
+ ...Object.fromEntries(
12103
+ (resolved.theme.palette?.chart ?? []).map((value) => {
12104
+ const hex = resolveDesignColor2(value, visualColors);
12105
+ return [hex ? `#${hex}` : value, value];
12106
+ })
12107
+ )
12108
+ };
12109
+ for (const [token, value] of Object.entries(entries)) {
12110
+ if (typeof value !== "string") continue;
12111
+ const hex = normalizeHex(resolveDesignColor2(value, visualColors) ?? value);
12112
+ if (hex) paletteHexes[token] = hex;
12113
+ }
12114
+ const paletteTokens = resolved.theme.palette?.chart?.map(
12115
+ (value) => `#${resolveDesignColor2(value, visualColors)}`
12116
+ ) ?? SERIES_COLOR_TOKENS.filter((token) => paletteHexes[token] !== void 0);
12117
+ const authoredPropsAt = (pointer) => asRecord(asRecord(nodeAtPointer(context.document, pointer))?.props);
12118
+ addFact({
12119
+ id: "docx:theme",
12120
+ kind: "docx/theme",
12121
+ path: "/props",
12122
+ themeName: context.themeName,
12123
+ paletteHexes,
12124
+ // `heading` and `body` only. A theme also names `mono` and `light`, but
12125
+ // those paint nothing until a component asks for them — counting an
12126
+ // unused `Courier New` against a document's family budget would flag a
12127
+ // report that only ever uses one typeface.
12128
+ fontFamilies: [
12129
+ ...new Set(
12130
+ ["heading", "body"].flatMap((role) => {
12131
+ const family = asRecord(
12132
+ resolved.theme.fonts?.[role]
12133
+ )?.family;
12134
+ return typeof family === "string" && family.trim() !== "" ? [family] : [];
12135
+ })
12136
+ )
12137
+ ]
12138
+ });
12139
+ collectColorLiterals(document).forEach((literal, index) => {
12140
+ addFact({
12141
+ id: `docx:color:${index}:${literal.path}`,
12142
+ kind: "docx/color",
12143
+ path: literal.path,
12144
+ raw: literal.raw,
12145
+ hex: literal.hex
12146
+ });
12147
+ });
12148
+ collectFontFamilies(document).forEach((use, index) => {
12149
+ addFact({
12150
+ id: `docx:font:${index}:${use.path}`,
12151
+ kind: "docx/font-family",
12152
+ path: use.path,
12153
+ family: use.family
12154
+ });
12155
+ });
12156
+ collectPlaceholders(document).forEach((occurrence, index) => {
12157
+ addFact({
12158
+ id: `docx:placeholder:${index}:${occurrence.path}`,
12159
+ kind: "docx/placeholder",
12160
+ path: occurrence.path,
12161
+ text: occurrence.text,
12162
+ placeholderKind: occurrence.match.kind,
12163
+ pattern: occurrence.match.pattern,
12164
+ excerpt: occurrence.match.excerpt
12165
+ });
12166
+ });
11764
12167
  let previousVisitPath;
11765
12168
  let lastFrame;
11766
12169
  let flowIndex = 0;
@@ -11770,6 +12173,18 @@ function prepareDocxQualityDocument(document, options = {}) {
11770
12173
  if (node.name === "table") {
11771
12174
  const fact = tableFact(props, path4, page.availableWidthTwips);
11772
12175
  if (fact) addFact(fact);
12176
+ const design = tableDesignFact(
12177
+ props,
12178
+ path4,
12179
+ resolved.theme,
12180
+ context.themeName,
12181
+ authoredPropsAt(path4)
12182
+ );
12183
+ if (design) addFact(design);
12184
+ }
12185
+ if (node.name === "chart" || node.name === "highcharts") {
12186
+ const fact = chartFact(node, props, path4, paletteTokens);
12187
+ if (fact) addFact(fact);
11773
12188
  }
11774
12189
  if (node.name === "paragraph" || node.name === "text-box") {
11775
12190
  const floating = asRecord(props.floating);
@@ -12193,9 +12608,17 @@ import {
12193
12608
 
12194
12609
  // src/quality/rules.ts
12195
12610
  import {
12611
+ chartInfoDesignFindings,
12612
+ DEFAULT_MAXIMUM_CHART_SERIES,
12613
+ DEFAULT_MAXIMUM_PIE_SLICES,
12614
+ fontCountFinding,
12196
12615
  mergeQualityProfiles,
12616
+ nearestPaletteToken,
12617
+ offPaletteFinding,
12618
+ placeholderFinding,
12197
12619
  QUALITY_CODES,
12198
- QualityEngine
12620
+ QualityEngine,
12621
+ tableInfoDesignFindings
12199
12622
  } from "@json-to-office/quality";
12200
12623
 
12201
12624
  // src/quality/text-metrics.ts
@@ -12661,6 +13084,179 @@ var docxLineBoxRule = {
12661
13084
  });
12662
13085
  }
12663
13086
  };
13087
+ var docxPlaceholderRule = {
13088
+ id: "docx/placeholder-text",
13089
+ code: QUALITY_CODES.PLACEHOLDER_TEXT,
13090
+ category: "integrity",
13091
+ defaultSeverity: "warning",
13092
+ defaultCertainty: "deterministic",
13093
+ formats: ["docx"],
13094
+ evaluate: ({ facts }) => facts.filter(
13095
+ (fact) => fact.kind === "docx/placeholder"
13096
+ ).map(
13097
+ (fact) => placeholderFinding({
13098
+ path: fact.path,
13099
+ kind: fact.placeholderKind,
13100
+ pattern: fact.pattern,
13101
+ excerpt: fact.excerpt
13102
+ })
13103
+ )
13104
+ };
13105
+ var DEFAULT_MAX_FONT_FAMILIES = 3;
13106
+ var docxFontCountRule = {
13107
+ id: "docx/font-count",
13108
+ code: QUALITY_CODES.FONT_COUNT,
13109
+ category: "brand",
13110
+ defaultSeverity: "warning",
13111
+ defaultCertainty: "deterministic",
13112
+ formats: ["docx"],
13113
+ defaultParameters: { maximumFamilies: DEFAULT_MAX_FONT_FAMILIES },
13114
+ evaluate: ({ facts, configuration }) => {
13115
+ const maximum = numberParameter(
13116
+ configuration.parameters,
13117
+ "maximumFamilies",
13118
+ DEFAULT_MAX_FONT_FAMILIES
13119
+ );
13120
+ const theme = facts.find(
13121
+ (fact) => fact.kind === "docx/theme"
13122
+ );
13123
+ const families = new Set(theme?.fontFamilies ?? []);
13124
+ const extraPaths = [];
13125
+ for (const fact of facts) {
13126
+ if (fact.kind !== "docx/font-family") continue;
13127
+ const use = fact;
13128
+ if (!families.has(use.family)) extraPaths.push(use.path);
13129
+ families.add(use.family);
13130
+ }
13131
+ if (families.size <= maximum) return [];
13132
+ return [
13133
+ fontCountFinding({
13134
+ path: theme?.path ?? "/props",
13135
+ families: [...families].sort(),
13136
+ maximum,
13137
+ relatedPaths: [...new Set(extraPaths)]
13138
+ })
13139
+ ];
13140
+ }
13141
+ };
13142
+ var docxPaletteRule = {
13143
+ id: "docx/palette-adherence",
13144
+ code: QUALITY_CODES.OFF_PALETTE,
13145
+ category: "brand",
13146
+ defaultSeverity: "info",
13147
+ defaultCertainty: "deterministic",
13148
+ formats: ["docx"],
13149
+ evaluate: ({ facts }) => {
13150
+ const theme = facts.find(
13151
+ (fact) => fact.kind === "docx/theme"
13152
+ );
13153
+ const palette = theme?.paletteHexes ?? {};
13154
+ const known = new Set(Object.values(palette));
13155
+ return facts.filter((fact) => fact.kind === "docx/color").filter((fact) => !known.has(fact.hex)).map((fact) => {
13156
+ const nearest = nearestPaletteToken(fact.hex, palette);
13157
+ return offPaletteFinding({
13158
+ path: fact.path,
13159
+ raw: fact.raw,
13160
+ hex: fact.hex,
13161
+ ...nearest && { nearest }
13162
+ });
13163
+ });
13164
+ }
13165
+ };
13166
+ var DEFAULT_MAX_TABLE_ROWS_PER_PAGE = 25;
13167
+ var docxChartRule = {
13168
+ id: "docx/chart-design",
13169
+ code: QUALITY_CODES.CHART_OVERLOADED,
13170
+ category: "information-design",
13171
+ defaultSeverity: "warning",
13172
+ defaultCertainty: "deterministic",
13173
+ formats: ["docx"],
13174
+ defaultParameters: {
13175
+ maximumSeries: DEFAULT_MAXIMUM_CHART_SERIES,
13176
+ maximumSlices: DEFAULT_MAXIMUM_PIE_SLICES
13177
+ },
13178
+ evaluate: ({ facts, configuration }) => {
13179
+ const maximumSeries = numberParameter(
13180
+ configuration.parameters,
13181
+ "maximumSeries",
13182
+ DEFAULT_MAXIMUM_CHART_SERIES
13183
+ );
13184
+ const maximumSlices = numberParameter(
13185
+ configuration.parameters,
13186
+ "maximumSlices",
13187
+ DEFAULT_MAXIMUM_PIE_SLICES
13188
+ );
13189
+ return facts.filter((fact) => fact.kind === "docx/chart").flatMap((fact) => {
13190
+ const fix = seriesColorFix(fact);
13191
+ return chartInfoDesignFindings(fact, {
13192
+ maximumSeries,
13193
+ maximumSlices,
13194
+ ...fix && { seriesColorFix: fix }
13195
+ });
13196
+ });
13197
+ }
13198
+ };
13199
+ function seriesColorFix(fact) {
13200
+ if (fact.componentName !== "chart") return void 0;
13201
+ if (fact.seriesCount < 1 || fact.paletteTokens.length === 0) return void 0;
13202
+ const tokens = Array.from(
13203
+ { length: fact.seriesCount },
13204
+ (_, index) => fact.paletteTokens[index % fact.paletteTokens.length]
13205
+ );
13206
+ return [{ op: "add", path: fact.seriesColorsPath, value: tokens }];
13207
+ }
13208
+ var docxTableDesignRule = {
13209
+ id: "docx/table-design",
13210
+ code: QUALITY_CODES.TABLE_NUMERIC_ALIGN,
13211
+ category: "information-design",
13212
+ defaultSeverity: "warning",
13213
+ defaultCertainty: "deterministic",
13214
+ formats: ["docx"],
13215
+ defaultParameters: { maximumRows: DEFAULT_MAX_TABLE_ROWS_PER_PAGE },
13216
+ evaluate: ({ facts, configuration }) => {
13217
+ const maximumRows = numberParameter(
13218
+ configuration.parameters,
13219
+ "maximumRows",
13220
+ DEFAULT_MAX_TABLE_ROWS_PER_PAGE
13221
+ );
13222
+ return facts.filter((fact) => fact.kind === "docx/table").flatMap(
13223
+ (fact) => tableInfoDesignFindings(fact, {
13224
+ maximumRows,
13225
+ rowSurface: "page",
13226
+ rowSeverity: "info",
13227
+ alignFix: alignColumnRight
13228
+ })
13229
+ );
13230
+ }
13231
+ };
13232
+ function alignColumnRight(column) {
13233
+ const operations = [
13234
+ column.hasCellDefaults ? {
13235
+ op: "add",
13236
+ path: `${column.path}/cellDefaults/horizontalAlignment`,
13237
+ value: "right"
13238
+ } : {
13239
+ op: "add",
13240
+ path: `${column.path}/cellDefaults`,
13241
+ value: { horizontalAlignment: "right" }
13242
+ }
13243
+ ];
13244
+ if (column.hasHeader) {
13245
+ operations.push({
13246
+ op: "add",
13247
+ path: `${column.path}/header/horizontalAlignment`,
13248
+ value: "right"
13249
+ });
13250
+ }
13251
+ for (const index of column.cellsWithOwnAlignment) {
13252
+ operations.push({
13253
+ op: "add",
13254
+ path: `${column.path}/cells/${index}/horizontalAlignment`,
13255
+ value: "right"
13256
+ });
13257
+ }
13258
+ return operations;
13259
+ }
12664
13260
  var DOCX_QUALITY_RULES = {
12665
13261
  id: "docx/default",
12666
13262
  rules: [
@@ -12669,7 +13265,12 @@ var DOCX_QUALITY_RULES = {
12669
13265
  docxTextFitRule,
12670
13266
  docxFrameCollisionRule,
12671
13267
  docxSvgTextBoundsRule,
12672
- docxLineBoxRule
13268
+ docxLineBoxRule,
13269
+ docxPlaceholderRule,
13270
+ docxChartRule,
13271
+ docxTableDesignRule,
13272
+ docxFontCountRule,
13273
+ docxPaletteRule
12673
13274
  ]
12674
13275
  };
12675
13276
  var DOCX_QUALITY_PROFILES = {