@json-to-office/core-docx 0.17.3 → 0.19.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.
@@ -0,0 +1,32 @@
1
+ import type { ComponentDefinition } from '@json-to-office/shared-docx';
2
+ /**
3
+ * @example
4
+ * ```json
5
+ * {
6
+ * "name": "eldermoor-census",
7
+ * "props": { "title": "Regno di Valebrook", "manaPct": "51,2%", "castles": "3.120" }
8
+ * }
9
+ * ```
10
+ */
11
+ export declare const eldermoorCensusComponent: import("../createComponent").CustomComponent<ComponentDefinition, {
12
+ '1.0.0': import("../createComponent").ComponentVersion<ComponentDefinition, import("@sinclair/typebox").TObject<{
13
+ title: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
14
+ manaPct: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
15
+ faithfulSubjects: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
16
+ swornGuilds: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
17
+ mappedTerritoriesPct: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
18
+ castles: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
19
+ outposts: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
20
+ watchtowers: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
21
+ sovereignTreasury: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
22
+ warChest: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
23
+ marketMint: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
24
+ royalServants: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
25
+ servantsLeft: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
26
+ servantsRight: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
27
+ width: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TNumber, import("@sinclair/typebox").TString]>>;
28
+ alignment: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"left">, import("@sinclair/typebox").TLiteral<"center">, import("@sinclair/typebox").TLiteral<"right">]>>;
29
+ caption: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
30
+ }>>;
31
+ }, "eldermoor-census">;
32
+ //# sourceMappingURL=eldermoor-census.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eldermoor-census.component.d.ts","sourceRoot":"","sources":["../../../src/plugin/example/eldermoor-census.component.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAgNvE;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;sBAwBnC,CAAC"}
@@ -2195,12 +2195,17 @@ function resolveComponentTree(components, theme) {
2195
2195
  async function processDocument(document, theme, themeName) {
2196
2196
  const metadata = createDocumentMetadata(document.props);
2197
2197
  const docDefaults = document.props.componentDefaults;
2198
- const effectiveTheme = docDefaults ? {
2198
+ const mergedComponentDefaults = docDefaults ? mergeWithDefaults2(docDefaults, theme.componentDefaults || {}) : void 0;
2199
+ const docNoProofWords = document.props.noProofWords;
2200
+ const mergedNoProofWords = docNoProofWords || theme.noProofWords ? Array.from(
2201
+ /* @__PURE__ */ new Set([...theme.noProofWords || [], ...docNoProofWords || []])
2202
+ ) : void 0;
2203
+ const effectiveTheme = mergedComponentDefaults || mergedNoProofWords ? {
2199
2204
  ...theme,
2200
- componentDefaults: mergeWithDefaults2(
2201
- docDefaults,
2202
- theme.componentDefaults || {}
2203
- )
2205
+ ...mergedComponentDefaults && {
2206
+ componentDefaults: mergedComponentDefaults
2207
+ },
2208
+ ...mergedNoProofWords && { noProofWords: mergedNoProofWords }
2204
2209
  } : theme;
2205
2210
  const context = createRenderContext(
2206
2211
  {
@@ -2222,7 +2227,8 @@ async function processDocument(document, theme, themeName) {
2222
2227
  sections,
2223
2228
  theme: effectiveTheme,
2224
2229
  themeName,
2225
- trackRevisions: document.props.trackRevisions
2230
+ trackRevisions: document.props.trackRevisions,
2231
+ language: document.props.language
2226
2232
  };
2227
2233
  }
2228
2234
  function createDocumentMetadata(props) {
@@ -2849,6 +2855,16 @@ async function downloadImageFromUrl(url) {
2849
2855
  throw new Error(`Failed to download image from ${url}: Unknown error`);
2850
2856
  }
2851
2857
  }
2858
+ function resolveImageSource(props) {
2859
+ const hasValue = (v) => typeof v === "string" && v.trim().length > 0;
2860
+ if (hasValue(props.svg)) {
2861
+ const encoded = Buffer.from(props.svg, "utf-8").toString("base64");
2862
+ return `data:image/svg+xml;base64,${encoded}`;
2863
+ }
2864
+ if (hasValue(props.base64)) return props.base64;
2865
+ if (hasValue(props.path)) return props.path;
2866
+ return void 0;
2867
+ }
2852
2868
  async function getImageBuffer(imagePath) {
2853
2869
  if (isBase64Image(imagePath)) {
2854
2870
  return { buffer: decodeBase64Image(imagePath) };
@@ -3076,7 +3092,7 @@ function convertBorders(borders, theme) {
3076
3092
  ...right && { right }
3077
3093
  } : void 0;
3078
3094
  }
3079
- function createWordStyles(themeNameOrObject = "minimal") {
3095
+ function createWordStyles(themeNameOrObject = "minimal", language) {
3080
3096
  const theme = typeof themeNameOrObject === "string" ? getTheme(themeNameOrObject) || getTheme("minimal") : themeNameOrObject;
3081
3097
  const paragraphStyles = [
3082
3098
  // Normal body text style
@@ -3534,8 +3550,20 @@ function createWordStyles(themeNameOrObject = "minimal") {
3534
3550
  });
3535
3551
  }
3536
3552
  return {
3537
- paragraphStyles
3553
+ paragraphStyles,
3538
3554
  // Cast needed due to docx typing
3555
+ // Document-default proofing language (w:docDefaults/w:rPrDefault). Runs that
3556
+ // don't set their own w:lang inherit this, so Word spell-checks the whole
3557
+ // document in the requested language unless a component overrides it.
3558
+ ...language && {
3559
+ default: {
3560
+ document: {
3561
+ run: {
3562
+ language: { value: language }
3563
+ }
3564
+ }
3565
+ }
3566
+ }
3539
3567
  };
3540
3568
  }
3541
3569
  function getStyleIdForLevel(level) {
@@ -3657,6 +3685,37 @@ import {
3657
3685
 
3658
3686
  // src/utils/textParser.ts
3659
3687
  import { TextRun, ExternalHyperlink, InternalHyperlink } from "docx";
3688
+ function escapeRegExp(s) {
3689
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3690
+ }
3691
+ function buildNoProofWordsRegex(noProofWords) {
3692
+ const words = (noProofWords || []).filter((w) => w && w.trim().length > 0);
3693
+ if (words.length === 0) return null;
3694
+ const escaped = words.slice().sort((a, b) => b.length - a.length).map(escapeRegExp);
3695
+ return new RegExp(
3696
+ `(?<![\\p{L}\\p{N}])(?:${escaped.join("|")})(?![\\p{L}\\p{N}])`,
3697
+ "giu"
3698
+ );
3699
+ }
3700
+ function splitByNoProofWords(text, makeRun, noProofWords) {
3701
+ const regex = buildNoProofWordsRegex(noProofWords);
3702
+ if (!regex || !text) return [makeRun(text, false)];
3703
+ const runs = [];
3704
+ let lastIndex = 0;
3705
+ let match;
3706
+ while ((match = regex.exec(text)) !== null) {
3707
+ if (match.index > lastIndex) {
3708
+ runs.push(makeRun(text.slice(lastIndex, match.index), false));
3709
+ }
3710
+ runs.push(makeRun(match[0], true));
3711
+ lastIndex = match.index + match[0].length;
3712
+ if (regex.lastIndex === match.index) regex.lastIndex++;
3713
+ }
3714
+ if (lastIndex < text.length) {
3715
+ runs.push(makeRun(text.slice(lastIndex), false));
3716
+ }
3717
+ return runs.length > 0 ? runs : [makeRun(text, false)];
3718
+ }
3660
3719
  function parseTextWithDecorators(text, baseStyle = {}, options = {}) {
3661
3720
  if (!text) {
3662
3721
  return [new TextRun({ text: "", ...baseStyle })];
@@ -3667,7 +3726,8 @@ function parseTextWithDecorators(text, baseStyle = {}, options = {}) {
3667
3726
  return processTextWithPlaceholders(
3668
3727
  normalizedText,
3669
3728
  baseStyle,
3670
- options.placeholderContext || {}
3729
+ options.placeholderContext || {},
3730
+ options.noProofWords
3671
3731
  );
3672
3732
  }
3673
3733
  if (options.enableHyperlinks) {
@@ -3738,20 +3798,38 @@ function createTextRunsWithNewlines(text, baseStyle, options, overrideStyle) {
3738
3798
  bold: overrideStyle?.bold ?? baseStyle.bold,
3739
3799
  italics: overrideStyle?.italics ?? baseStyle.italics
3740
3800
  };
3741
- runs.push(
3742
- new TextRun({
3743
- text: line,
3744
- ...baseStyle.font && { font: baseStyle.font },
3745
- ...baseStyle.size && { size: baseStyle.size },
3746
- ...baseStyle.color && {
3747
- color: runStyle.bold && options.boldColor ? options.boldColor : baseStyle.color
3748
- },
3749
- ...runStyle.bold !== void 0 && { bold: runStyle.bold },
3750
- ...runStyle.italics !== void 0 && { italics: runStyle.italics },
3751
- ...baseStyle.underline && { underline: baseStyle.underline },
3752
- ...needsLineBreak && { break: 1 }
3753
- })
3801
+ const commonProps = {
3802
+ ...baseStyle.font && { font: baseStyle.font },
3803
+ ...baseStyle.size && { size: baseStyle.size },
3804
+ ...baseStyle.color && {
3805
+ color: runStyle.bold && options.boldColor ? options.boldColor : baseStyle.color
3806
+ },
3807
+ ...runStyle.bold !== void 0 && { bold: runStyle.bold },
3808
+ ...runStyle.italics !== void 0 && { italics: runStyle.italics },
3809
+ ...baseStyle.underline && { underline: baseStyle.underline },
3810
+ ...baseStyle.language && { language: baseStyle.language }
3811
+ };
3812
+ const wholeRunNoProof = baseStyle.noProof === true;
3813
+ const wordsForLine = wholeRunNoProof ? void 0 : options.noProofWords;
3814
+ let firstSegment = true;
3815
+ const lineRuns = splitByNoProofWords(
3816
+ line,
3817
+ (segment, matched) => {
3818
+ const segNoProof = matched || wholeRunNoProof;
3819
+ const run = new TextRun({
3820
+ text: segment,
3821
+ ...commonProps,
3822
+ ...(matched || baseStyle.noProof !== void 0) && {
3823
+ noProof: segNoProof
3824
+ },
3825
+ ...needsLineBreak && firstSegment && { break: 1 }
3826
+ });
3827
+ firstSegment = false;
3828
+ return run;
3829
+ },
3830
+ wordsForLine
3754
3831
  );
3832
+ runs.push(...lineRuns);
3755
3833
  }
3756
3834
  }
3757
3835
  return runs;
@@ -3856,7 +3934,7 @@ var PlaceholderRegistry = class {
3856
3934
  this.handlers.clear();
3857
3935
  }
3858
3936
  };
3859
- function processTextWithPlaceholders(text, baseStyle = {}, context = {}) {
3937
+ function processTextWithPlaceholders(text, baseStyle = {}, context = {}, noProofWords) {
3860
3938
  const normalizedText = normalizeUnicodeText(text);
3861
3939
  const combinedRegex = /(\*\*\*|___)([\s\S]*?)\1|(\*\*|__)([\s\S]*?)\3|(\*|_)([\s\S]*?)\5|\{([^}]+)\}/g;
3862
3940
  const result = [];
@@ -3866,7 +3944,9 @@ function processTextWithPlaceholders(text, baseStyle = {}, context = {}) {
3866
3944
  if (match.index > lastIndex) {
3867
3945
  const beforeText = normalizedText.substring(lastIndex, match.index);
3868
3946
  if (beforeText) {
3869
- result.push(...createTextRunsWithNewlines2(beforeText, baseStyle));
3947
+ result.push(
3948
+ ...createTextRunsWithNewlines2(beforeText, baseStyle, noProofWords)
3949
+ );
3870
3950
  }
3871
3951
  }
3872
3952
  if (match[7]) {
@@ -3880,11 +3960,17 @@ function processTextWithPlaceholders(text, baseStyle = {}, context = {}) {
3880
3960
  result.push(placeholderResult);
3881
3961
  } else if (typeof placeholderResult === "string") {
3882
3962
  result.push(
3883
- ...createTextRunsWithNewlines2(placeholderResult, baseStyle)
3963
+ ...createTextRunsWithNewlines2(
3964
+ placeholderResult,
3965
+ baseStyle,
3966
+ noProofWords
3967
+ )
3884
3968
  );
3885
3969
  }
3886
3970
  } else {
3887
- result.push(...createTextRunsWithNewlines2(match[0], baseStyle));
3971
+ result.push(
3972
+ ...createTextRunsWithNewlines2(match[0], baseStyle, noProofWords)
3973
+ );
3888
3974
  }
3889
3975
  } else {
3890
3976
  let decoratedText;
@@ -3919,33 +4005,54 @@ function processTextWithPlaceholders(text, baseStyle = {}, context = {}) {
3919
4005
  if (lastIndex < normalizedText.length) {
3920
4006
  const remainingText = normalizedText.substring(lastIndex);
3921
4007
  if (remainingText) {
3922
- result.push(...createTextRunsWithNewlines2(remainingText, baseStyle));
4008
+ result.push(
4009
+ ...createTextRunsWithNewlines2(remainingText, baseStyle, noProofWords)
4010
+ );
3923
4011
  }
3924
4012
  }
3925
4013
  if (result.length === 0 && normalizedText) {
3926
- result.push(...createTextRunsWithNewlines2(normalizedText, baseStyle));
4014
+ result.push(
4015
+ ...createTextRunsWithNewlines2(normalizedText, baseStyle, noProofWords)
4016
+ );
3927
4017
  }
3928
4018
  return result;
3929
4019
  }
3930
- function createTextRunsWithNewlines2(text, baseStyle) {
4020
+ function createTextRunsWithNewlines2(text, baseStyle, noProofWords) {
3931
4021
  const runs = [];
3932
4022
  const lines = text.split("\n");
3933
4023
  for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
3934
4024
  const line = lines[lineIndex];
3935
4025
  const needsLineBreak = lineIndex > 0;
3936
4026
  if (line || needsLineBreak) {
3937
- runs.push(
3938
- new TextRun2({
3939
- text: line,
3940
- font: baseStyle.font,
3941
- size: baseStyle.size,
3942
- color: baseStyle.color,
3943
- bold: baseStyle.bold,
3944
- italics: baseStyle.italics,
3945
- underline: baseStyle.underline,
3946
- break: needsLineBreak ? 1 : void 0
3947
- })
4027
+ const commonProps = {
4028
+ font: baseStyle.font,
4029
+ size: baseStyle.size,
4030
+ color: baseStyle.color,
4031
+ bold: baseStyle.bold,
4032
+ italics: baseStyle.italics,
4033
+ underline: baseStyle.underline,
4034
+ ...baseStyle.language && { language: baseStyle.language }
4035
+ };
4036
+ const wholeRunNoProof = baseStyle.noProof === true;
4037
+ const wordsForLine = wholeRunNoProof ? void 0 : noProofWords;
4038
+ let firstSegment = true;
4039
+ const lineRuns = splitByNoProofWords(
4040
+ line,
4041
+ (segment, matched) => {
4042
+ const run = new TextRun2({
4043
+ text: segment,
4044
+ ...commonProps,
4045
+ ...(matched || baseStyle.noProof !== void 0) && {
4046
+ noProof: matched || wholeRunNoProof
4047
+ },
4048
+ break: needsLineBreak && firstSegment ? 1 : void 0
4049
+ });
4050
+ firstSegment = false;
4051
+ return run;
4052
+ },
4053
+ wordsForLine
3948
4054
  );
4055
+ runs.push(...lineRuns);
3949
4056
  }
3950
4057
  }
3951
4058
  return runs;
@@ -4290,6 +4397,11 @@ function applyFontWeightAlias(opts) {
4290
4397
  );
4291
4398
  return { font: synth.family, bold: synth.bold, italics: synth.italic };
4292
4399
  }
4400
+ function resolveNoProofWords(theme, optionWords) {
4401
+ const themeWords = theme.noProofWords;
4402
+ const merged = [...themeWords || [], ...optionWords || []];
4403
+ return merged.length > 0 ? Array.from(new Set(merged)) : void 0;
4404
+ }
4293
4405
  function createText(content, theme, themeName, options = {}) {
4294
4406
  const normalizedContent = normalizeUnicodeText(content);
4295
4407
  const style = options.style || "Normal";
@@ -4332,7 +4444,11 @@ function createText(content, theme, themeName, options = {}) {
4332
4444
  ...weighted.italics !== void 0 && { italics: weighted.italics },
4333
4445
  ...options.underline !== void 0 && {
4334
4446
  underline: options.underline ? { type: "single" } : void 0
4335
- }
4447
+ },
4448
+ // Proofing: per-run language and/or no-proof. Omitting language lets the
4449
+ // run inherit the document default set on docDefaults.
4450
+ ...options.language && { language: { value: options.language } },
4451
+ ...options.noProof !== void 0 && { noProof: options.noProof }
4336
4452
  };
4337
4453
  if (options.revision) {
4338
4454
  const revisionRuns = createRevisionRuns(options.revision, baseTextStyle);
@@ -4354,7 +4470,8 @@ function createText(content, theme, themeName, options = {}) {
4354
4470
  } else {
4355
4471
  const textRuns = parseTextWithDecorators(normalizedContent, baseTextStyle, {
4356
4472
  boldColor: options.boldColor,
4357
- enableHyperlinks: true
4473
+ enableHyperlinks: true,
4474
+ noProofWords: resolveNoProofWords(theme, options.noProofWords)
4358
4475
  });
4359
4476
  if (options.bookmarkId) {
4360
4477
  globalBookmarkRegistry.register(
@@ -4489,8 +4606,21 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4489
4606
  },
4490
4607
  ...options.underline !== void 0 && {
4491
4608
  underline: options.underline ? { type: "single" } : void 0
4492
- }
4609
+ },
4610
+ // Proofing: per-run language and/or no-proof (see createText).
4611
+ ...options.language && { language: { value: options.language } },
4612
+ ...options.noProof !== void 0 && { noProof: options.noProof }
4493
4613
  };
4614
+ const headingNoProofWords = resolveNoProofWords(theme, options.noProofWords);
4615
+ const makeHeadingRuns = (value) => splitByNoProofWords(
4616
+ value,
4617
+ (segment, matched) => new TextRun4({
4618
+ text: segment,
4619
+ ...baseTextStyle,
4620
+ ...matched && { noProof: true }
4621
+ }),
4622
+ headingNoProofWords
4623
+ );
4494
4624
  if (options.revision) {
4495
4625
  const revisionRuns = createRevisionRuns(options.revision, baseTextStyle);
4496
4626
  if (options.bookmarkId) {
@@ -4518,13 +4648,12 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4518
4648
  if (hasDecorators) {
4519
4649
  const textRuns = parseTextWithDecorators(normalizedText, baseTextStyle, {
4520
4650
  boldColor: options.boldColor,
4521
- enableHyperlinks: true
4651
+ enableHyperlinks: true,
4652
+ noProofWords: headingNoProofWords
4522
4653
  });
4523
4654
  headingTextChildren.push(...textRuns);
4524
4655
  } else {
4525
- headingTextChildren.push(
4526
- new TextRun4({ text: normalizedText, ...baseTextStyle })
4527
- );
4656
+ headingTextChildren.push(...makeHeadingRuns(normalizedText));
4528
4657
  }
4529
4658
  children.push(
4530
4659
  new Bookmark({
@@ -4536,11 +4665,12 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4536
4665
  if (hasDecorators) {
4537
4666
  const textRuns = parseTextWithDecorators(normalizedText, baseTextStyle, {
4538
4667
  boldColor: options.boldColor,
4539
- enableHyperlinks: true
4668
+ enableHyperlinks: true,
4669
+ noProofWords: headingNoProofWords
4540
4670
  });
4541
4671
  children.push(...textRuns);
4542
4672
  } else {
4543
- children.push(new TextRun4({ text: normalizedText, ...baseTextStyle }));
4673
+ children.push(...makeHeadingRuns(normalizedText));
4544
4674
  }
4545
4675
  }
4546
4676
  return new Paragraph({
@@ -5066,10 +5196,10 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
5066
5196
  } else if (isImageComponent(cell)) {
5067
5197
  const imageComp = cell;
5068
5198
  try {
5069
- const imageSource = imageComp.props.base64 || imageComp.props.path;
5199
+ const imageSource = resolveImageSource(imageComp.props);
5070
5200
  if (!imageSource) {
5071
5201
  throw new Error(
5072
- 'Image component requires either "path" or "base64" property'
5202
+ 'Image component requires one of "path", "base64", or "svg" property'
5073
5203
  );
5074
5204
  }
5075
5205
  const imageResult = await getImageBuffer(imageSource);
@@ -5095,7 +5225,7 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
5095
5225
  });
5096
5226
  cellChildren = [imageRun];
5097
5227
  } catch (error) {
5098
- const imageSource = imageComp.props.base64 || imageComp.props.path || "unknown";
5228
+ const imageSource = imageComp.props.svg?.trim() ? "inline-svg" : imageComp.props.base64 || imageComp.props.path || "unknown";
5099
5229
  cellChildren = [
5100
5230
  new TextRun4({
5101
5231
  text: `[IMAGE: ${imageSource.substring(0, 50)}${imageSource.length > 50 ? "..." : ""}]`,
@@ -5503,6 +5633,10 @@ function renderHeadingComponent(component, theme, themeName) {
5503
5633
  fontWeight: config.font?.fontWeight,
5504
5634
  italic: config.font?.italic,
5505
5635
  underline: config.font?.underline,
5636
+ // Proofing: local language override + no-proof toggle + known-words list
5637
+ language: config.language,
5638
+ noProof: config.noProof,
5639
+ noProofWords: config.noProofWords,
5506
5640
  // Pagination control
5507
5641
  keepNext: config.keepNext,
5508
5642
  keepLines: config.keepLines,
@@ -5621,6 +5755,10 @@ function renderParagraphComponent(component, theme, themeName) {
5621
5755
  fontWeight: resolvedConfig.font?.fontWeight,
5622
5756
  italic: resolvedConfig.font?.italic,
5623
5757
  underline: resolvedConfig.font?.underline,
5758
+ // Proofing: local language override + no-proof toggle + known-words list
5759
+ language: resolvedConfig.language,
5760
+ noProof: resolvedConfig.noProof,
5761
+ noProofWords: resolvedConfig.noProofWords,
5624
5762
  // Pass outline level for TOC support
5625
5763
  outlineLevel,
5626
5764
  // Pass floating positioning properties
@@ -5772,10 +5910,10 @@ function renderListComponent(component, theme, themeName) {
5772
5910
  async function renderImageComponent(component, theme, themeName) {
5773
5911
  if (!isImageComponent(component)) return [];
5774
5912
  const resolvedConfig = component.props;
5775
- const imageSource = resolvedConfig.base64 || resolvedConfig.path;
5913
+ const imageSource = resolveImageSource(resolvedConfig);
5776
5914
  if (!imageSource) {
5777
5915
  throw new Error(
5778
- 'Image component requires either "path" or "base64" property'
5916
+ 'Image component requires one of "path", "base64", or "svg" property'
5779
5917
  );
5780
5918
  }
5781
5919
  return await createImage(imageSource, theme, themeName, {
@@ -6457,7 +6595,10 @@ async function generateChart(config, servicesConfig) {
6457
6595
  infile: config.options,
6458
6596
  type: "png",
6459
6597
  b64: true,
6460
- scale: config.scale
6598
+ scale: config.scale,
6599
+ // Forward resources verbatim only when present so the payload stays
6600
+ // byte-identical to before for callers that omit it.
6601
+ ...config.resources ? { resources: config.resources } : {}
6461
6602
  };
6462
6603
  const response = await postJsonToService({
6463
6604
  url: serverUrl,
@@ -6744,7 +6885,7 @@ async function renderDocument(structure, layout, options) {
6744
6885
  }
6745
6886
  const numberingConfigs = globalNumberingRegistry2.getAll();
6746
6887
  return new Document({
6747
- styles: createWordStyles(structure.theme),
6888
+ styles: createWordStyles(structure.theme, structure.language),
6748
6889
  sections,
6749
6890
  features: {
6750
6891
  updateFields: true,
@@ -6795,13 +6936,13 @@ async function renderHeaderFooterComponents(components, theme, themeName, contex
6795
6936
  );
6796
6937
  } else if (isImageComponent(component)) {
6797
6938
  const imageComp = component;
6798
- let imageSource = imageComp.props.base64 || imageComp.props.path;
6939
+ let imageSource = resolveImageSource(imageComp.props);
6799
6940
  if (!imageSource) {
6800
6941
  elements.push(
6801
6942
  new Paragraph7({
6802
6943
  children: [
6803
6944
  new TextRun6({
6804
- text: "[IMAGE: Missing path or base64 property]",
6945
+ text: "[IMAGE: Missing path, base64, or svg property]",
6805
6946
  font: getThemeFonts(theme).body.family,
6806
6947
  size: 20,
6807
6948
  bold: true,
@@ -6875,15 +7016,16 @@ async function renderHeaderFooterComponents(components, theme, themeName, contex
6875
7016
  })
6876
7017
  );
6877
7018
  } catch (error) {
7019
+ const sourcePreview = imageSource.substring(0, 50);
6878
7020
  console.error(
6879
- `[Header/Footer Image Error] Failed to render image: ${imageComp.props.path?.substring(0, 50)}...`,
7021
+ `[Header/Footer Image Error] Failed to render image: ${sourcePreview}...`,
6880
7022
  error instanceof Error ? error.message : error
6881
7023
  );
6882
7024
  elements.push(
6883
7025
  new Paragraph7({
6884
7026
  children: [
6885
7027
  new TextRun6({
6886
- text: `[IMAGE: ${imageComp.props.path}]`,
7028
+ text: `[IMAGE: ${sourcePreview}]`,
6887
7029
  font: getThemeFonts(theme).body.family,
6888
7030
  size: 20,
6889
7031
  color: getThemeColors(theme).secondary,