@json-to-office/core-docx 0.24.0 → 0.25.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.
package/dist/index.js CHANGED
@@ -1059,7 +1059,8 @@ function resolveFontProperties(theme, fontRef) {
1059
1059
  alignment: fontDef.alignment,
1060
1060
  lineSpacing: fontDef.lineSpacing,
1061
1061
  spacing: fontDef.spacing,
1062
- characterSpacing: fontDef.characterSpacing
1062
+ characterSpacing: fontDef.characterSpacing,
1063
+ scale: fontDef.scale
1063
1064
  };
1064
1065
  }
1065
1066
  function mergeFontAndStyleProperties(fontProps, styleOverrides) {
@@ -1941,6 +1942,9 @@ import {
1941
1942
  isVisualComponent
1942
1943
  } from "@json-to-office/shared-docx";
1943
1944
 
1945
+ // src/core/generationContext.ts
1946
+ import { applyExportMode, scopedThemeName } from "@json-to-office/shared";
1947
+
1944
1948
  // src/styles/theme-resolver.ts
1945
1949
  init_themes();
1946
1950
  function resolveBuiltInTheme(themeName, options = {}) {
@@ -1959,6 +1963,81 @@ function resolveBuiltInTheme(themeName, options = {}) {
1959
1963
  return getThemeWithFallback(themeName);
1960
1964
  }
1961
1965
 
1966
+ // src/themes/overrides.ts
1967
+ import { createHash } from "crypto";
1968
+ function applyThemeOverrides(theme, overrides) {
1969
+ if (!overrides) return theme;
1970
+ const fonts = { ...theme.fonts };
1971
+ for (const [role, def] of Object.entries(overrides.fonts ?? {})) {
1972
+ const key = role;
1973
+ fonts[key] = { ...theme.fonts[key], ...def };
1974
+ }
1975
+ const styles = { ...theme.styles ?? {} };
1976
+ for (const [name, def] of Object.entries(overrides.styles ?? {})) {
1977
+ styles[name] = { ...styles[name], ...def };
1978
+ }
1979
+ return {
1980
+ ...theme,
1981
+ colors: { ...theme.colors, ...overrides.colors ?? {} },
1982
+ fonts,
1983
+ ...Object.keys(styles).length > 0 && { styles }
1984
+ };
1985
+ }
1986
+ function themeOverridesDigest(overrides) {
1987
+ return createHash("sha256").update(JSON.stringify(overrides)).digest("hex").slice(0, 8);
1988
+ }
1989
+
1990
+ // src/core/generationContext.ts
1991
+ function defaultNamedThemeLookup(themeName, customThemes, warnings) {
1992
+ if (customThemes) {
1993
+ if (customThemes[themeName]) return customThemes[themeName];
1994
+ const themeNameLower = themeName.toLowerCase();
1995
+ const matchingThemeKey = Object.keys(customThemes).find(
1996
+ (key) => key.toLowerCase() === themeNameLower
1997
+ );
1998
+ if (matchingThemeKey) return customThemes[matchingThemeKey];
1999
+ return resolveBuiltInTheme(themeName, { customThemes, warnings });
2000
+ }
2001
+ return resolveBuiltInTheme(themeName, { warnings });
2002
+ }
2003
+ function resolveThemeContext(documentIn, options = {}) {
2004
+ const { customThemes, fonts, warnings, resolveNamedTheme } = options;
2005
+ if (documentIn.props === null) {
2006
+ throw new Error(
2007
+ "Document `props` is null. Omit it, or provide an object \u2014 a null props cannot carry a theme."
2008
+ );
2009
+ }
2010
+ const document = documentIn.props === void 0 ? { ...documentIn, props: {} } : documentIn;
2011
+ const baseThemeName = document.props.theme || "minimal";
2012
+ let theme = resolveNamedTheme ? resolveNamedTheme(baseThemeName, warnings) : defaultNamedThemeLookup(baseThemeName, customThemes, warnings);
2013
+ const themeOverrides = document.props.themeOverrides;
2014
+ let themeName = baseThemeName;
2015
+ if (themeOverrides) {
2016
+ theme = applyThemeOverrides(theme, themeOverrides);
2017
+ themeName = `${themeName}+ov:${themeOverridesDigest(themeOverrides)}`;
2018
+ }
2019
+ const mode = applyExportMode({ doc: document, theme, fonts });
2020
+ for (const w of mode.warnings) {
2021
+ if (warnings) {
2022
+ warnings.push({
2023
+ component: "fontRegistry",
2024
+ message: w.message,
2025
+ severity: "warning",
2026
+ context: { code: w.code }
2027
+ });
2028
+ } else {
2029
+ console.warn(`[json-to-docx] [${w.code}] ${w.message}`);
2030
+ }
2031
+ }
2032
+ return {
2033
+ document: mode.doc,
2034
+ theme: mode.theme,
2035
+ // Scope by mode too: substitute rewrites the theme in place, so caches
2036
+ // must not share slots with custom-mode runs keyed on the same name.
2037
+ themeName: scopedThemeName(themeName, fonts?.mode)
2038
+ };
2039
+ }
2040
+
1962
2041
  // src/utils/formatters.ts
1963
2042
  import { format } from "date-fns";
1964
2043
  var formatDate = (date, formatString = "MMMM d, yyyy") => {
@@ -2955,7 +3034,8 @@ function convertRunProperties(merged, theme, defaultColor, defaultSize) {
2955
3034
  ...merged.underline !== void 0 && merged.underline && { underline: { type: "single" } },
2956
3035
  ...merged.characterSpacing && {
2957
3036
  characterSpacing: merged.characterSpacing.type === "condensed" ? -merged.characterSpacing.value : merged.characterSpacing.value
2958
- }
3037
+ },
3038
+ ...merged.scale && { scale: merged.scale }
2959
3039
  };
2960
3040
  }
2961
3041
  function convertParagraphProperties(merged, styleProps, theme) {
@@ -3029,7 +3109,8 @@ function createWordStyles(themeNameOrObject = "minimal", language) {
3029
3109
  bold: normalStyle?.bold,
3030
3110
  italic: normalStyle?.italic,
3031
3111
  underline: normalStyle?.underline,
3032
- characterSpacing: normalStyle?.characterSpacing
3112
+ characterSpacing: normalStyle?.characterSpacing,
3113
+ scale: normalStyle?.scale
3033
3114
  });
3034
3115
  return convertRunProperties(merged, theme, theme.colors.text);
3035
3116
  })(),
@@ -3067,7 +3148,8 @@ function createWordStyles(themeNameOrObject = "minimal", language) {
3067
3148
  bold: headingStyle.bold,
3068
3149
  italic: headingStyle.italic,
3069
3150
  underline: headingStyle.underline,
3070
- characterSpacing: headingStyle.characterSpacing
3151
+ characterSpacing: headingStyle.characterSpacing,
3152
+ scale: headingStyle.scale
3071
3153
  });
3072
3154
  return convertRunProperties(merged, theme, theme.colors.primary, 20);
3073
3155
  })(),
@@ -3133,7 +3215,8 @@ function createWordStyles(themeNameOrObject = "minimal", language) {
3133
3215
  bold: headingStyle.bold,
3134
3216
  italic: headingStyle.italic,
3135
3217
  underline: headingStyle.underline,
3136
- characterSpacing: headingStyle.characterSpacing
3218
+ characterSpacing: headingStyle.characterSpacing,
3219
+ scale: headingStyle.scale
3137
3220
  });
3138
3221
  return convertRunProperties(merged, theme, theme.colors.primary, 20);
3139
3222
  })(),
@@ -3200,7 +3283,8 @@ function createWordStyles(themeNameOrObject = "minimal", language) {
3200
3283
  bold: titleStyle.bold,
3201
3284
  italic: titleStyle.italic,
3202
3285
  underline: titleStyle.underline,
3203
- characterSpacing: titleStyle.characterSpacing
3286
+ characterSpacing: titleStyle.characterSpacing,
3287
+ scale: titleStyle.scale
3204
3288
  });
3205
3289
  return convertRunProperties(merged, theme, theme.colors.primary, 20);
3206
3290
  })(),
@@ -3261,7 +3345,8 @@ function createWordStyles(themeNameOrObject = "minimal", language) {
3261
3345
  bold: subtitleStyle.bold,
3262
3346
  italic: subtitleStyle.italic,
3263
3347
  underline: subtitleStyle.underline,
3264
- characterSpacing: subtitleStyle.characterSpacing
3348
+ characterSpacing: subtitleStyle.characterSpacing,
3349
+ scale: subtitleStyle.scale
3265
3350
  });
3266
3351
  return convertRunProperties(merged, theme, theme.colors.secondary);
3267
3352
  })(),
@@ -3403,7 +3488,8 @@ function createWordStyles(themeNameOrObject = "minimal", language) {
3403
3488
  bold: customStyle.bold,
3404
3489
  italic: customStyle.italic,
3405
3490
  underline: customStyle.underline,
3406
- characterSpacing: customStyle.characterSpacing
3491
+ characterSpacing: customStyle.characterSpacing,
3492
+ scale: customStyle.scale
3407
3493
  });
3408
3494
  return convertRunProperties(merged, theme, theme.colors.text);
3409
3495
  })(),
@@ -3511,7 +3597,7 @@ __reExport(cache_exports, cache_star);
3511
3597
  import * as cache_star from "@json-to-office/shared/cache";
3512
3598
 
3513
3599
  // src/cache/key-generator.ts
3514
- import { createHash } from "crypto";
3600
+ import { createHash as createHash2 } from "crypto";
3515
3601
  var CacheKeyGenerator = class {
3516
3602
  version;
3517
3603
  constructor(version = "1.0") {
@@ -3585,7 +3671,7 @@ var CacheKeyGenerator = class {
3585
3671
  * Create hash
3586
3672
  */
3587
3673
  hash(input) {
3588
- return createHash("sha256").update(input).digest("hex").substring(0, 16);
3674
+ return createHash2("sha256").update(input).digest("hex").substring(0, 16);
3589
3675
  }
3590
3676
  };
3591
3677
 
@@ -3600,11 +3686,12 @@ function normalizeUnicodeText(text) {
3600
3686
  // src/utils/placeholderProcessor.ts
3601
3687
  import {
3602
3688
  TextRun as TextRun2,
3603
- PageNumber
3689
+ PageNumber,
3690
+ Tab as Tab2
3604
3691
  } from "docx";
3605
3692
 
3606
3693
  // src/utils/textParser.ts
3607
- import { TextRun, ExternalHyperlink, InternalHyperlink } from "docx";
3694
+ import { TextRun, ExternalHyperlink, InternalHyperlink, Tab } from "docx";
3608
3695
  function escapeRegExp(s) {
3609
3696
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3610
3697
  }
@@ -3727,28 +3814,49 @@ function createTextRunsWithNewlines(text, baseStyle, options, overrideStyle) {
3727
3814
  ...runStyle.bold !== void 0 && { bold: runStyle.bold },
3728
3815
  ...runStyle.italics !== void 0 && { italics: runStyle.italics },
3729
3816
  ...baseStyle.underline && { underline: baseStyle.underline },
3817
+ ...baseStyle.scale && { scale: baseStyle.scale },
3818
+ ...baseStyle.characterSpacing && {
3819
+ characterSpacing: baseStyle.characterSpacing
3820
+ },
3730
3821
  ...baseStyle.language && { language: baseStyle.language }
3731
3822
  };
3732
3823
  const wholeRunNoProof = baseStyle.noProof === true;
3733
3824
  const wordsForLine = wholeRunNoProof ? void 0 : options.noProofWords;
3734
3825
  let firstSegment = true;
3735
- const lineRuns = splitByNoProofWords(
3736
- line,
3737
- (segment, matched) => {
3738
- const segNoProof = matched || wholeRunNoProof;
3739
- const run = new TextRun({
3740
- text: segment,
3741
- ...commonProps,
3742
- ...(matched || baseStyle.noProof !== void 0) && {
3743
- noProof: segNoProof
3744
- },
3745
- ...needsLineBreak && firstSegment && { break: 1 }
3746
- });
3826
+ const lineRuns = [];
3827
+ const tabSegments = line.split(" ");
3828
+ tabSegments.forEach((tabSegment, tabIndex) => {
3829
+ if (tabIndex > 0) {
3830
+ lineRuns.push(
3831
+ new TextRun({
3832
+ children: [new Tab()],
3833
+ ...commonProps,
3834
+ ...needsLineBreak && firstSegment && { break: 1 }
3835
+ })
3836
+ );
3747
3837
  firstSegment = false;
3748
- return run;
3749
- },
3750
- wordsForLine
3751
- );
3838
+ }
3839
+ if (!tabSegment && tabSegments.length > 1) return;
3840
+ lineRuns.push(
3841
+ ...splitByNoProofWords(
3842
+ tabSegment,
3843
+ (segment, matched) => {
3844
+ const segNoProof = matched || wholeRunNoProof;
3845
+ const run = new TextRun({
3846
+ text: segment,
3847
+ ...commonProps,
3848
+ ...(matched || baseStyle.noProof !== void 0) && {
3849
+ noProof: segNoProof
3850
+ },
3851
+ ...needsLineBreak && firstSegment && { break: 1 }
3852
+ });
3853
+ firstSegment = false;
3854
+ return run;
3855
+ },
3856
+ wordsForLine
3857
+ )
3858
+ );
3859
+ });
3752
3860
  runs.push(...lineRuns);
3753
3861
  }
3754
3862
  }
@@ -3983,27 +4091,48 @@ function createTextRunsWithNewlines2(text, baseStyle, noProofWords) {
3983
4091
  bold: baseStyle.bold,
3984
4092
  italics: baseStyle.italics,
3985
4093
  underline: baseStyle.underline,
4094
+ ...baseStyle.scale && { scale: baseStyle.scale },
4095
+ ...baseStyle.characterSpacing && {
4096
+ characterSpacing: baseStyle.characterSpacing
4097
+ },
3986
4098
  ...baseStyle.language && { language: baseStyle.language }
3987
4099
  };
3988
4100
  const wholeRunNoProof = baseStyle.noProof === true;
3989
4101
  const wordsForLine = wholeRunNoProof ? void 0 : noProofWords;
3990
4102
  let firstSegment = true;
3991
- const lineRuns = splitByNoProofWords(
3992
- line,
3993
- (segment, matched) => {
3994
- const run = new TextRun2({
3995
- text: segment,
3996
- ...commonProps,
3997
- ...(matched || baseStyle.noProof !== void 0) && {
3998
- noProof: matched || wholeRunNoProof
3999
- },
4000
- break: needsLineBreak && firstSegment ? 1 : void 0
4001
- });
4103
+ const lineRuns = [];
4104
+ const tabSegments = line.split(" ");
4105
+ tabSegments.forEach((tabSegment, tabIndex) => {
4106
+ if (tabIndex > 0) {
4107
+ lineRuns.push(
4108
+ new TextRun2({
4109
+ children: [new Tab2()],
4110
+ ...commonProps,
4111
+ break: needsLineBreak && firstSegment ? 1 : void 0
4112
+ })
4113
+ );
4002
4114
  firstSegment = false;
4003
- return run;
4004
- },
4005
- wordsForLine
4006
- );
4115
+ }
4116
+ if (!tabSegment && tabSegments.length > 1) return;
4117
+ lineRuns.push(
4118
+ ...splitByNoProofWords(
4119
+ tabSegment,
4120
+ (segment, matched) => {
4121
+ const run = new TextRun2({
4122
+ text: segment,
4123
+ ...commonProps,
4124
+ ...(matched || baseStyle.noProof !== void 0) && {
4125
+ noProof: matched || wholeRunNoProof
4126
+ },
4127
+ break: needsLineBreak && firstSegment ? 1 : void 0
4128
+ });
4129
+ firstSegment = false;
4130
+ return run;
4131
+ },
4132
+ wordsForLine
4133
+ )
4134
+ );
4135
+ });
4007
4136
  runs.push(...lineRuns);
4008
4137
  }
4009
4138
  }
@@ -4158,12 +4287,12 @@ function componentHasRevision(component) {
4158
4287
  }
4159
4288
 
4160
4289
  // src/core/cached-render.ts
4161
- import { createHash as createHash2 } from "crypto";
4290
+ import { createHash as createHash3 } from "crypto";
4162
4291
  var componentCache = null;
4163
4292
  var cacheKeyGen = null;
4164
4293
  function createThemeHash(theme) {
4165
4294
  const themeString = JSON.stringify(theme);
4166
- return createHash2("sha256").update(themeString).digest("hex").substring(0, 8);
4295
+ return createHash3("sha256").update(themeString).digest("hex").substring(0, 8);
4167
4296
  }
4168
4297
  function initializeComponentCache(cache) {
4169
4298
  if (cache) {
@@ -4431,6 +4560,12 @@ function createText(content, theme, themeName, options = {}) {
4431
4560
  ...options.underline !== void 0 && {
4432
4561
  underline: options.underline ? { type: "single" } : void 0
4433
4562
  },
4563
+ // Character width scaling in percent (w:w)
4564
+ ...options.scale && { scale: options.scale },
4565
+ // Letter tracking (w:spacing, twentieths of a point; negative = condensed)
4566
+ ...options.characterSpacing && {
4567
+ characterSpacing: options.characterSpacing.type === "condensed" ? -options.characterSpacing.value : options.characterSpacing.value
4568
+ },
4434
4569
  // Proofing: per-run language and/or no-proof. Omitting language lets the
4435
4570
  // run inherit the document default set on docDefaults.
4436
4571
  ...options.language && { language: { value: options.language } },
@@ -4455,7 +4590,7 @@ function createText(content, theme, themeName, options = {}) {
4455
4590
  }
4456
4591
  } else {
4457
4592
  const textRuns = parseTextWithDecorators(normalizedContent, baseTextStyle, {
4458
- boldColor: options.boldColor,
4593
+ boldColor: options.boldColor ? resolveColor(options.boldColor, theme) : void 0,
4459
4594
  enableHyperlinks: true,
4460
4595
  noProofWords: resolveNoProofWords(theme, options.noProofWords)
4461
4596
  });
@@ -4487,7 +4622,9 @@ function createText(content, theme, themeName, options = {}) {
4487
4622
  },
4488
4623
  ...frameOptions && { frame: frameOptions },
4489
4624
  ...options.keepNext !== void 0 && { keepNext: options.keepNext },
4490
- ...options.keepLines !== void 0 && { keepLines: options.keepLines }
4625
+ ...options.keepLines !== void 0 && { keepLines: options.keepLines },
4626
+ ...options.indent && { indent: options.indent },
4627
+ ...options.tabStops && options.tabStops.length > 0 && { tabStops: options.tabStops }
4491
4628
  });
4492
4629
  }
4493
4630
  function mapFrameOptions(floating, theme, themeName) {
@@ -4593,6 +4730,12 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4593
4730
  ...options.underline !== void 0 && {
4594
4731
  underline: options.underline ? { type: "single" } : void 0
4595
4732
  },
4733
+ // Character width scaling in percent (w:w)
4734
+ ...options.scale && { scale: options.scale },
4735
+ // Letter tracking (w:spacing, twentieths of a point; negative = condensed)
4736
+ ...options.characterSpacing && {
4737
+ characterSpacing: options.characterSpacing.type === "condensed" ? -options.characterSpacing.value : options.characterSpacing.value
4738
+ },
4596
4739
  // Proofing: per-run language and/or no-proof (see createText).
4597
4740
  ...options.language && { language: { value: options.language } },
4598
4741
  ...options.noProof !== void 0 && { noProof: options.noProof }
@@ -4633,7 +4776,7 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4633
4776
  const headingTextChildren = [];
4634
4777
  if (hasDecorators) {
4635
4778
  const textRuns = parseTextWithDecorators(normalizedText, baseTextStyle, {
4636
- boldColor: options.boldColor,
4779
+ boldColor: options.boldColor ? resolveColor(options.boldColor, theme) : void 0,
4637
4780
  enableHyperlinks: true,
4638
4781
  noProofWords: headingNoProofWords
4639
4782
  });
@@ -4650,7 +4793,7 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4650
4793
  } else {
4651
4794
  if (hasDecorators) {
4652
4795
  const textRuns = parseTextWithDecorators(normalizedText, baseTextStyle, {
4653
- boldColor: options.boldColor,
4796
+ boldColor: options.boldColor ? resolveColor(options.boldColor, theme) : void 0,
4654
4797
  enableHyperlinks: true,
4655
4798
  noProofWords: headingNoProofWords
4656
4799
  });
@@ -4666,7 +4809,8 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4666
4809
  // Only override spacing if explicitly provided
4667
4810
  spacing: hasExplicitSpacing ? spacing : void 0,
4668
4811
  ...options.keepNext !== void 0 && { keepNext: options.keepNext },
4669
- ...options.keepLines !== void 0 && { keepLines: options.keepLines }
4812
+ ...options.keepLines !== void 0 && { keepLines: options.keepLines },
4813
+ ...options.indent && { indent: options.indent }
4670
4814
  });
4671
4815
  }
4672
4816
  async function createImage(path4, theme, themeName, options = {}) {
@@ -5658,6 +5802,8 @@ function renderHeadingComponent(component, theme, themeName) {
5658
5802
  fontWeight: config.font?.fontWeight,
5659
5803
  italic: config.font?.italic,
5660
5804
  underline: config.font?.underline,
5805
+ scale: config.font?.scale,
5806
+ characterSpacing: config.font?.characterSpacing,
5661
5807
  // Proofing: local language override + no-proof toggle + known-words list
5662
5808
  language: config.language,
5663
5809
  noProof: config.noProof,
@@ -5665,6 +5811,8 @@ function renderHeadingComponent(component, theme, themeName) {
5665
5811
  // Pagination control
5666
5812
  keepNext: config.keepNext,
5667
5813
  keepLines: config.keepLines,
5814
+ // Paragraph indentation (w:ind) in twips
5815
+ indent: config.indent,
5668
5816
  // Bookmark ID for internal linking
5669
5817
  bookmarkId,
5670
5818
  // Tracked-change segments (rendered as native Word revisions)
@@ -5945,6 +6093,8 @@ function renderParagraphComponent(component, theme, themeName) {
5945
6093
  fontWeight: resolvedConfig.font?.fontWeight,
5946
6094
  italic: resolvedConfig.font?.italic,
5947
6095
  underline: resolvedConfig.font?.underline,
6096
+ scale: resolvedConfig.font?.scale,
6097
+ characterSpacing: resolvedConfig.font?.characterSpacing,
5948
6098
  // Proofing: local language override + no-proof toggle + known-words list
5949
6099
  language: resolvedConfig.language,
5950
6100
  noProof: resolvedConfig.noProof,
@@ -5957,6 +6107,10 @@ function renderParagraphComponent(component, theme, themeName) {
5957
6107
  keepNext: resolvedConfig.keepNext,
5958
6108
  // Pass keepLines property
5959
6109
  keepLines: resolvedConfig.keepLines,
6110
+ // Paragraph indentation (w:ind) in twips
6111
+ indent: resolvedConfig.indent,
6112
+ // Tab stops (w:tabs); \t characters in text jump to these positions
6113
+ tabStops: resolvedConfig.tabStops,
5960
6114
  // Pass bookmark ID for internal linking
5961
6115
  bookmarkId: resolvedConfig.id,
5962
6116
  // Tracked-change segments (rendered as native Word revisions)
@@ -7505,9 +7659,6 @@ async function resolveDocumentFonts(document, theme, fonts, warnings) {
7505
7659
  return resolved;
7506
7660
  }
7507
7661
 
7508
- // src/core/generator.ts
7509
- import { applyExportMode, scopedThemeName } from "@json-to-office/shared";
7510
-
7511
7662
  // src/utils/packageDocument.ts
7512
7663
  import AdmZip2 from "adm-zip";
7513
7664
  import { Packer } from "docx";
@@ -7761,42 +7912,11 @@ async function generateFromConfig(props, components, options) {
7761
7912
  return await generateDocument(reportComponent, options);
7762
7913
  }
7763
7914
  async function generateDocumentWithCustomThemes(documentIn, customThemes, services, fonts, warnings, generationDate) {
7764
- let document = documentIn.props === void 0 ? { ...documentIn, props: {} } : documentIn;
7765
- let themeName = document.props.theme || "minimal";
7766
- let theme;
7767
- if (customThemes) {
7768
- if (customThemes[themeName]) {
7769
- theme = customThemes[themeName];
7770
- } else {
7771
- const themeNameLower = themeName.toLowerCase();
7772
- const matchingThemeKey = Object.keys(customThemes).find(
7773
- (key) => key.toLowerCase() === themeNameLower
7774
- );
7775
- if (matchingThemeKey) {
7776
- theme = customThemes[matchingThemeKey];
7777
- } else {
7778
- theme = resolveBuiltInTheme(themeName, { customThemes, warnings });
7779
- }
7780
- }
7781
- } else {
7782
- theme = resolveBuiltInTheme(themeName, { warnings });
7783
- }
7784
- const mode = applyExportMode({ doc: document, theme, fonts });
7785
- document = mode.doc;
7786
- theme = mode.theme;
7787
- themeName = scopedThemeName(themeName, fonts?.mode);
7788
- for (const w of mode.warnings) {
7789
- if (warnings) {
7790
- warnings.push({
7791
- component: "fontRegistry",
7792
- message: w.message,
7793
- severity: "warning",
7794
- context: { code: w.code }
7795
- });
7796
- } else {
7797
- console.warn(`[json-to-docx] [${w.code}] ${w.message}`);
7798
- }
7799
- }
7915
+ const { document, theme, themeName } = resolveThemeContext(documentIn, {
7916
+ customThemes,
7917
+ fonts,
7918
+ warnings
7919
+ });
7800
7920
  await resolveDocumentFonts(document, theme, fonts, warnings);
7801
7921
  const structure = await processDocument(
7802
7922
  document,
@@ -8212,9 +8332,6 @@ async function runExample(example, options = {}) {
8212
8332
  }
8213
8333
  }
8214
8334
 
8215
- // src/plugin/createDocumentGenerator.ts
8216
- import { applyExportMode as applyExportMode2, scopedThemeName as scopedThemeName2 } from "@json-to-office/shared";
8217
-
8218
8335
  // src/plugin/version-resolver.ts
8219
8336
  import { resolveComponentVersion } from "@json-to-office/shared/plugin";
8220
8337
 
@@ -8678,32 +8795,25 @@ function createBuilderImpl(state) {
8678
8795
  }
8679
8796
  };
8680
8797
  const warnings = [];
8681
- const baseThemeName = internalDocument.props.theme || "minimal";
8682
- const docTheme = resolveDocumentTheme(baseThemeName, warnings);
8683
- const mode = applyExportMode2({
8684
- doc: internalDocument,
8685
- theme: docTheme,
8686
- fonts: state.fonts
8798
+ const {
8799
+ document: modedRoot,
8800
+ theme: modedTheme,
8801
+ themeName
8802
+ } = resolveThemeContext(internalDocument, {
8803
+ customThemes: state.customThemes,
8804
+ fonts: state.fonts,
8805
+ warnings,
8806
+ resolveNamedTheme: resolveDocumentTheme
8687
8807
  });
8688
- const modedTheme = mode.theme;
8689
- const themeName = scopedThemeName2(baseThemeName, state.fonts?.mode);
8690
- for (const w of mode.warnings) {
8691
- warnings.push({
8692
- component: "fontRegistry",
8693
- message: w.message,
8694
- severity: "warning",
8695
- context: { code: w.code }
8696
- });
8697
- }
8698
8808
  const processed = await processDocumentComponents(
8699
- mode.doc.children || [],
8809
+ modedRoot.children || [],
8700
8810
  preserveSet,
8701
8811
  warnings,
8702
8812
  modedTheme,
8703
8813
  validateEmitted
8704
8814
  );
8705
8815
  const processedDocument = {
8706
- ...mode.doc,
8816
+ ...modedRoot,
8707
8817
  children: processed.standard
8708
8818
  };
8709
8819
  const [modedDoc] = normalizeDocument(processedDocument);
@@ -8724,7 +8834,7 @@ function createBuilderImpl(state) {
8724
8834
  bypassCache: !state.enableCache
8725
8835
  });
8726
8836
  const preservedDefinition = preserveSet ? {
8727
- ...mode.doc,
8837
+ ...modedRoot,
8728
8838
  children: processed.preserved
8729
8839
  } : void 0;
8730
8840
  return {