@json-to-office/core-docx 0.24.0 → 0.26.1

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
 
@@ -3604,7 +3690,7 @@ import {
3604
3690
  } from "docx";
3605
3691
 
3606
3692
  // src/utils/textParser.ts
3607
- import { TextRun, ExternalHyperlink, InternalHyperlink } from "docx";
3693
+ import { TextRun, ExternalHyperlink, InternalHyperlink, Tab } from "docx";
3608
3694
  function escapeRegExp(s) {
3609
3695
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3610
3696
  }
@@ -3707,40 +3793,53 @@ function parseTextWithDecorators(text, baseStyle = {}, options = {}) {
3707
3793
  }
3708
3794
  return runs;
3709
3795
  }
3710
- function createTextRunsWithNewlines(text, baseStyle, options, overrideStyle) {
3711
- const runs = [];
3712
- const lines = text.split("\n");
3713
- for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
3714
- const line = lines[lineIndex];
3715
- const needsLineBreak = lineIndex > 0;
3716
- if (line || needsLineBreak) {
3717
- const runStyle = {
3718
- bold: overrideStyle?.bold ?? baseStyle.bold,
3719
- italics: overrideStyle?.italics ?? baseStyle.italics
3720
- };
3721
- const commonProps = {
3722
- ...baseStyle.font && { font: baseStyle.font },
3723
- ...baseStyle.size && { size: baseStyle.size },
3724
- ...baseStyle.color && {
3725
- color: runStyle.bold && options.boldColor ? options.boldColor : baseStyle.color
3726
- },
3727
- ...runStyle.bold !== void 0 && { bold: runStyle.bold },
3728
- ...runStyle.italics !== void 0 && { italics: runStyle.italics },
3729
- ...baseStyle.underline && { underline: baseStyle.underline },
3730
- ...baseStyle.language && { language: baseStyle.language }
3731
- };
3732
- const wholeRunNoProof = baseStyle.noProof === true;
3733
- const wordsForLine = wholeRunNoProof ? void 0 : options.noProofWords;
3734
- let firstSegment = true;
3735
- const lineRuns = splitByNoProofWords(
3736
- line,
3796
+ function buildRunCommonProps(baseStyle, overrides) {
3797
+ const bold = overrides?.bold ?? baseStyle.bold;
3798
+ const italics = overrides?.italics ?? baseStyle.italics;
3799
+ return {
3800
+ ...baseStyle.font && { font: baseStyle.font },
3801
+ ...baseStyle.size && { size: baseStyle.size },
3802
+ ...baseStyle.color && {
3803
+ color: bold && overrides?.boldColor ? overrides.boldColor : baseStyle.color
3804
+ },
3805
+ ...bold !== void 0 && { bold },
3806
+ ...italics !== void 0 && { italics },
3807
+ ...baseStyle.underline && { underline: baseStyle.underline },
3808
+ ...baseStyle.scale && { scale: baseStyle.scale },
3809
+ ...baseStyle.characterSpacing && {
3810
+ characterSpacing: baseStyle.characterSpacing
3811
+ },
3812
+ ...baseStyle.language && { language: baseStyle.language }
3813
+ };
3814
+ }
3815
+ function buildLineRuns(line, commonProps, opts) {
3816
+ const { needsLineBreak, noProof } = opts;
3817
+ const wholeRunNoProof = noProof === true;
3818
+ const wordsForLine = wholeRunNoProof ? void 0 : opts.noProofWords;
3819
+ let firstSegment = true;
3820
+ const lineRuns = [];
3821
+ const tabSegments = line.split(" ");
3822
+ tabSegments.forEach((tabSegment, tabIndex) => {
3823
+ if (tabIndex > 0) {
3824
+ lineRuns.push(
3825
+ new TextRun({
3826
+ children: [new Tab()],
3827
+ ...commonProps,
3828
+ ...needsLineBreak && firstSegment && { break: 1 }
3829
+ })
3830
+ );
3831
+ firstSegment = false;
3832
+ }
3833
+ if (!tabSegment && tabSegments.length > 1) return;
3834
+ lineRuns.push(
3835
+ ...splitByNoProofWords(
3836
+ tabSegment,
3737
3837
  (segment, matched) => {
3738
- const segNoProof = matched || wholeRunNoProof;
3739
3838
  const run = new TextRun({
3740
3839
  text: segment,
3741
3840
  ...commonProps,
3742
- ...(matched || baseStyle.noProof !== void 0) && {
3743
- noProof: segNoProof
3841
+ ...(matched || noProof !== void 0) && {
3842
+ noProof: matched || wholeRunNoProof
3744
3843
  },
3745
3844
  ...needsLineBreak && firstSegment && { break: 1 }
3746
3845
  });
@@ -3748,12 +3847,36 @@ function createTextRunsWithNewlines(text, baseStyle, options, overrideStyle) {
3748
3847
  return run;
3749
3848
  },
3750
3849
  wordsForLine
3850
+ )
3851
+ );
3852
+ });
3853
+ return lineRuns;
3854
+ }
3855
+ function buildTextRuns(text, commonProps, opts = {}) {
3856
+ const runs = [];
3857
+ const lines = text.split("\n");
3858
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
3859
+ const line = lines[lineIndex];
3860
+ const needsLineBreak = lineIndex > 0;
3861
+ if (line || needsLineBreak) {
3862
+ runs.push(
3863
+ ...buildLineRuns(line, commonProps, { needsLineBreak, ...opts })
3751
3864
  );
3752
- runs.push(...lineRuns);
3753
3865
  }
3754
3866
  }
3755
3867
  return runs;
3756
3868
  }
3869
+ function createTextRunsWithNewlines(text, baseStyle, options, overrideStyle) {
3870
+ return buildTextRuns(
3871
+ text,
3872
+ buildRunCommonProps(baseStyle, {
3873
+ bold: overrideStyle?.bold,
3874
+ italics: overrideStyle?.italics,
3875
+ boldColor: options.boldColor
3876
+ }),
3877
+ { noProof: baseStyle.noProof, noProofWords: options.noProofWords }
3878
+ );
3879
+ }
3757
3880
  function parseTextWithHyperlinks(text, baseStyle = {}, options = {}) {
3758
3881
  const normalizedText = normalizeUnicodeText(text);
3759
3882
  const runs = [];
@@ -3970,44 +4093,10 @@ function processTextWithPlaceholders(text, baseStyle = {}, context = {}, noProof
3970
4093
  return result;
3971
4094
  }
3972
4095
  function createTextRunsWithNewlines2(text, baseStyle, noProofWords) {
3973
- const runs = [];
3974
- const lines = text.split("\n");
3975
- for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
3976
- const line = lines[lineIndex];
3977
- const needsLineBreak = lineIndex > 0;
3978
- if (line || needsLineBreak) {
3979
- const commonProps = {
3980
- font: baseStyle.font,
3981
- size: baseStyle.size,
3982
- color: baseStyle.color,
3983
- bold: baseStyle.bold,
3984
- italics: baseStyle.italics,
3985
- underline: baseStyle.underline,
3986
- ...baseStyle.language && { language: baseStyle.language }
3987
- };
3988
- const wholeRunNoProof = baseStyle.noProof === true;
3989
- const wordsForLine = wholeRunNoProof ? void 0 : noProofWords;
3990
- 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
- });
4002
- firstSegment = false;
4003
- return run;
4004
- },
4005
- wordsForLine
4006
- );
4007
- runs.push(...lineRuns);
4008
- }
4009
- }
4010
- return runs;
4096
+ return buildTextRuns(text, buildRunCommonProps(baseStyle), {
4097
+ noProof: baseStyle.noProof,
4098
+ noProofWords
4099
+ });
4011
4100
  }
4012
4101
  function initializeBuiltinPlaceholders() {
4013
4102
  PlaceholderRegistry.register("PAGE", (context) => {
@@ -4158,12 +4247,12 @@ function componentHasRevision(component) {
4158
4247
  }
4159
4248
 
4160
4249
  // src/core/cached-render.ts
4161
- import { createHash as createHash2 } from "crypto";
4250
+ import { createHash as createHash3 } from "crypto";
4162
4251
  var componentCache = null;
4163
4252
  var cacheKeyGen = null;
4164
4253
  function createThemeHash(theme) {
4165
4254
  const themeString = JSON.stringify(theme);
4166
- return createHash2("sha256").update(themeString).digest("hex").substring(0, 8);
4255
+ return createHash3("sha256").update(themeString).digest("hex").substring(0, 8);
4167
4256
  }
4168
4257
  function initializeComponentCache(cache) {
4169
4258
  if (cache) {
@@ -4431,6 +4520,12 @@ function createText(content, theme, themeName, options = {}) {
4431
4520
  ...options.underline !== void 0 && {
4432
4521
  underline: options.underline ? { type: "single" } : void 0
4433
4522
  },
4523
+ // Character width scaling in percent (w:w)
4524
+ ...options.scale && { scale: options.scale },
4525
+ // Letter tracking (w:spacing, twentieths of a point; negative = condensed)
4526
+ ...options.characterSpacing && {
4527
+ characterSpacing: options.characterSpacing.type === "condensed" ? -options.characterSpacing.value : options.characterSpacing.value
4528
+ },
4434
4529
  // Proofing: per-run language and/or no-proof. Omitting language lets the
4435
4530
  // run inherit the document default set on docDefaults.
4436
4531
  ...options.language && { language: { value: options.language } },
@@ -4455,7 +4550,7 @@ function createText(content, theme, themeName, options = {}) {
4455
4550
  }
4456
4551
  } else {
4457
4552
  const textRuns = parseTextWithDecorators(normalizedContent, baseTextStyle, {
4458
- boldColor: options.boldColor,
4553
+ boldColor: options.boldColor ? resolveColor(options.boldColor, theme) : void 0,
4459
4554
  enableHyperlinks: true,
4460
4555
  noProofWords: resolveNoProofWords(theme, options.noProofWords)
4461
4556
  });
@@ -4487,7 +4582,9 @@ function createText(content, theme, themeName, options = {}) {
4487
4582
  },
4488
4583
  ...frameOptions && { frame: frameOptions },
4489
4584
  ...options.keepNext !== void 0 && { keepNext: options.keepNext },
4490
- ...options.keepLines !== void 0 && { keepLines: options.keepLines }
4585
+ ...options.keepLines !== void 0 && { keepLines: options.keepLines },
4586
+ ...options.indent && { indent: options.indent },
4587
+ ...options.tabStops && options.tabStops.length > 0 && { tabStops: options.tabStops }
4491
4588
  });
4492
4589
  }
4493
4590
  function mapFrameOptions(floating, theme, themeName) {
@@ -4593,6 +4690,12 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4593
4690
  ...options.underline !== void 0 && {
4594
4691
  underline: options.underline ? { type: "single" } : void 0
4595
4692
  },
4693
+ // Character width scaling in percent (w:w)
4694
+ ...options.scale && { scale: options.scale },
4695
+ // Letter tracking (w:spacing, twentieths of a point; negative = condensed)
4696
+ ...options.characterSpacing && {
4697
+ characterSpacing: options.characterSpacing.type === "condensed" ? -options.characterSpacing.value : options.characterSpacing.value
4698
+ },
4596
4699
  // Proofing: per-run language and/or no-proof (see createText).
4597
4700
  ...options.language && { language: { value: options.language } },
4598
4701
  ...options.noProof !== void 0 && { noProof: options.noProof }
@@ -4633,7 +4736,7 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4633
4736
  const headingTextChildren = [];
4634
4737
  if (hasDecorators) {
4635
4738
  const textRuns = parseTextWithDecorators(normalizedText, baseTextStyle, {
4636
- boldColor: options.boldColor,
4739
+ boldColor: options.boldColor ? resolveColor(options.boldColor, theme) : void 0,
4637
4740
  enableHyperlinks: true,
4638
4741
  noProofWords: headingNoProofWords
4639
4742
  });
@@ -4650,7 +4753,7 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4650
4753
  } else {
4651
4754
  if (hasDecorators) {
4652
4755
  const textRuns = parseTextWithDecorators(normalizedText, baseTextStyle, {
4653
- boldColor: options.boldColor,
4756
+ boldColor: options.boldColor ? resolveColor(options.boldColor, theme) : void 0,
4654
4757
  enableHyperlinks: true,
4655
4758
  noProofWords: headingNoProofWords
4656
4759
  });
@@ -4666,7 +4769,8 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4666
4769
  // Only override spacing if explicitly provided
4667
4770
  spacing: hasExplicitSpacing ? spacing : void 0,
4668
4771
  ...options.keepNext !== void 0 && { keepNext: options.keepNext },
4669
- ...options.keepLines !== void 0 && { keepLines: options.keepLines }
4772
+ ...options.keepLines !== void 0 && { keepLines: options.keepLines },
4773
+ ...options.indent && { indent: options.indent }
4670
4774
  });
4671
4775
  }
4672
4776
  async function createImage(path4, theme, themeName, options = {}) {
@@ -5658,6 +5762,8 @@ function renderHeadingComponent(component, theme, themeName) {
5658
5762
  fontWeight: config.font?.fontWeight,
5659
5763
  italic: config.font?.italic,
5660
5764
  underline: config.font?.underline,
5765
+ scale: config.font?.scale,
5766
+ characterSpacing: config.font?.characterSpacing,
5661
5767
  // Proofing: local language override + no-proof toggle + known-words list
5662
5768
  language: config.language,
5663
5769
  noProof: config.noProof,
@@ -5665,6 +5771,8 @@ function renderHeadingComponent(component, theme, themeName) {
5665
5771
  // Pagination control
5666
5772
  keepNext: config.keepNext,
5667
5773
  keepLines: config.keepLines,
5774
+ // Paragraph indentation (w:ind) in twips
5775
+ indent: config.indent,
5668
5776
  // Bookmark ID for internal linking
5669
5777
  bookmarkId,
5670
5778
  // Tracked-change segments (rendered as native Word revisions)
@@ -5945,6 +6053,8 @@ function renderParagraphComponent(component, theme, themeName) {
5945
6053
  fontWeight: resolvedConfig.font?.fontWeight,
5946
6054
  italic: resolvedConfig.font?.italic,
5947
6055
  underline: resolvedConfig.font?.underline,
6056
+ scale: resolvedConfig.font?.scale,
6057
+ characterSpacing: resolvedConfig.font?.characterSpacing,
5948
6058
  // Proofing: local language override + no-proof toggle + known-words list
5949
6059
  language: resolvedConfig.language,
5950
6060
  noProof: resolvedConfig.noProof,
@@ -5957,6 +6067,10 @@ function renderParagraphComponent(component, theme, themeName) {
5957
6067
  keepNext: resolvedConfig.keepNext,
5958
6068
  // Pass keepLines property
5959
6069
  keepLines: resolvedConfig.keepLines,
6070
+ // Paragraph indentation (w:ind) in twips
6071
+ indent: resolvedConfig.indent,
6072
+ // Tab stops (w:tabs); \t characters in text jump to these positions
6073
+ tabStops: resolvedConfig.tabStops,
5960
6074
  // Pass bookmark ID for internal linking
5961
6075
  bookmarkId: resolvedConfig.id,
5962
6076
  // Tracked-change segments (rendered as native Word revisions)
@@ -7505,9 +7619,6 @@ async function resolveDocumentFonts(document, theme, fonts, warnings) {
7505
7619
  return resolved;
7506
7620
  }
7507
7621
 
7508
- // src/core/generator.ts
7509
- import { applyExportMode, scopedThemeName } from "@json-to-office/shared";
7510
-
7511
7622
  // src/utils/packageDocument.ts
7512
7623
  import AdmZip2 from "adm-zip";
7513
7624
  import { Packer } from "docx";
@@ -7761,42 +7872,11 @@ async function generateFromConfig(props, components, options) {
7761
7872
  return await generateDocument(reportComponent, options);
7762
7873
  }
7763
7874
  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
- }
7875
+ const { document, theme, themeName } = resolveThemeContext(documentIn, {
7876
+ customThemes,
7877
+ fonts,
7878
+ warnings
7879
+ });
7800
7880
  await resolveDocumentFonts(document, theme, fonts, warnings);
7801
7881
  const structure = await processDocument(
7802
7882
  document,
@@ -8212,9 +8292,6 @@ async function runExample(example, options = {}) {
8212
8292
  }
8213
8293
  }
8214
8294
 
8215
- // src/plugin/createDocumentGenerator.ts
8216
- import { applyExportMode as applyExportMode2, scopedThemeName as scopedThemeName2 } from "@json-to-office/shared";
8217
-
8218
8295
  // src/plugin/version-resolver.ts
8219
8296
  import { resolveComponentVersion } from "@json-to-office/shared/plugin";
8220
8297
 
@@ -8678,32 +8755,25 @@ function createBuilderImpl(state) {
8678
8755
  }
8679
8756
  };
8680
8757
  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
8758
+ const {
8759
+ document: modedRoot,
8760
+ theme: modedTheme,
8761
+ themeName
8762
+ } = resolveThemeContext(internalDocument, {
8763
+ customThemes: state.customThemes,
8764
+ fonts: state.fonts,
8765
+ warnings,
8766
+ resolveNamedTheme: resolveDocumentTheme
8687
8767
  });
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
8768
  const processed = await processDocumentComponents(
8699
- mode.doc.children || [],
8769
+ modedRoot.children || [],
8700
8770
  preserveSet,
8701
8771
  warnings,
8702
8772
  modedTheme,
8703
8773
  validateEmitted
8704
8774
  );
8705
8775
  const processedDocument = {
8706
- ...mode.doc,
8776
+ ...modedRoot,
8707
8777
  children: processed.standard
8708
8778
  };
8709
8779
  const [modedDoc] = normalizeDocument(processedDocument);
@@ -8724,7 +8794,7 @@ function createBuilderImpl(state) {
8724
8794
  bypassCache: !state.enableCache
8725
8795
  });
8726
8796
  const preservedDefinition = preserveSet ? {
8727
- ...mode.doc,
8797
+ ...modedRoot,
8728
8798
  children: processed.preserved
8729
8799
  } : void 0;
8730
8800
  return {