@json-to-office/core-docx 0.17.1 → 0.18.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.
@@ -2849,6 +2849,16 @@ async function downloadImageFromUrl(url) {
2849
2849
  throw new Error(`Failed to download image from ${url}: Unknown error`);
2850
2850
  }
2851
2851
  }
2852
+ function resolveImageSource(props) {
2853
+ const hasValue = (v) => typeof v === "string" && v.trim().length > 0;
2854
+ if (hasValue(props.svg)) {
2855
+ const encoded = Buffer.from(props.svg, "utf-8").toString("base64");
2856
+ return `data:image/svg+xml;base64,${encoded}`;
2857
+ }
2858
+ if (hasValue(props.base64)) return props.base64;
2859
+ if (hasValue(props.path)) return props.path;
2860
+ return void 0;
2861
+ }
2852
2862
  async function getImageBuffer(imagePath) {
2853
2863
  if (isBase64Image(imagePath)) {
2854
2864
  return { buffer: decodeBase64Image(imagePath) };
@@ -3550,216 +3560,116 @@ function getStyleIdForLevel(level) {
3550
3560
  return styleMap[level] || "Heading1";
3551
3561
  }
3552
3562
 
3553
- // src/utils/textParser.ts
3554
- import { TextRun as TextRun2, ExternalHyperlink as ExternalHyperlink2, InternalHyperlink as InternalHyperlink2 } from "docx";
3555
-
3556
- // src/utils/placeholderProcessor.ts
3557
- import {
3558
- TextRun,
3559
- PageNumber
3560
- } from "docx";
3563
+ // src/core/render.ts
3564
+ init_styleHelpers();
3565
+ init_layoutUtils();
3561
3566
 
3562
- // src/utils/unicode.ts
3563
- function normalizeUnicodeText(text) {
3564
- return (text ?? "").normalize("NFC");
3565
- }
3567
+ // src/cache/index.ts
3568
+ var cache_exports = {};
3569
+ __export(cache_exports, {
3570
+ CacheKeyGenerator: () => CacheKeyGenerator
3571
+ });
3572
+ __reExport(cache_exports, cache_star);
3573
+ import * as cache_star from "@json-to-office/shared/cache";
3566
3574
 
3567
- // src/utils/placeholderProcessor.ts
3568
- var PlaceholderRegistry = class {
3569
- static handlers = /* @__PURE__ */ new Map();
3570
- /**
3571
- * Register a placeholder handler
3572
- */
3573
- static register(name, handler) {
3574
- this.handlers.set(name.toUpperCase(), handler);
3575
+ // src/cache/key-generator.ts
3576
+ import { createHash } from "crypto";
3577
+ var CacheKeyGenerator = class {
3578
+ version;
3579
+ constructor(version = "1.0") {
3580
+ this.version = version;
3575
3581
  }
3576
3582
  /**
3577
- * Get a placeholder handler
3583
+ * Generate cache key for a component
3578
3584
  */
3579
- static get(name) {
3580
- return this.handlers.get(name.toUpperCase());
3585
+ generateKey(component, context, options = {}) {
3586
+ const parts = [
3587
+ this.version,
3588
+ component.name,
3589
+ this.hashProps(component.props)
3590
+ ];
3591
+ if (options.includeTheme !== false) {
3592
+ parts.push(context.theme.name);
3593
+ }
3594
+ if (options.includeContext) {
3595
+ parts.push(this.hashContext(context));
3596
+ }
3597
+ if (options.additionalKeys) {
3598
+ parts.push(...options.additionalKeys);
3599
+ }
3600
+ if (options.version) {
3601
+ parts.push(options.version);
3602
+ }
3603
+ return parts.join(":");
3581
3604
  }
3582
3605
  /**
3583
- * Check if a placeholder is registered
3606
+ * Hash component props
3584
3607
  */
3585
- static has(name) {
3586
- return this.handlers.has(name.toUpperCase());
3608
+ hashProps(props) {
3609
+ if (!props) return "null";
3610
+ const normalized = this.normalizeObject(props);
3611
+ const json = JSON.stringify(normalized);
3612
+ return this.hash(json);
3587
3613
  }
3588
3614
  /**
3589
- * Get all registered placeholder names
3615
+ * Hash render context
3590
3616
  */
3591
- static getRegisteredNames() {
3592
- return Array.from(this.handlers.keys());
3617
+ hashContext(context) {
3618
+ const relevant = {
3619
+ theme: context.theme.name,
3620
+ document: context.document
3621
+ // Exclude runtime properties like sectionIndex, componentIndex
3622
+ };
3623
+ return this.hash(JSON.stringify(relevant));
3593
3624
  }
3594
3625
  /**
3595
- * Clear all registered placeholders
3626
+ * Normalize object for consistent hashing
3596
3627
  */
3597
- static clear() {
3598
- this.handlers.clear();
3599
- }
3600
- };
3601
- function processTextWithPlaceholders(text, baseStyle = {}, context = {}) {
3602
- const normalizedText = normalizeUnicodeText(text);
3603
- const combinedRegex = /(\*\*\*|___)([\s\S]*?)\1|(\*\*|__)([\s\S]*?)\3|(\*|_)([\s\S]*?)\5|\{([^}]+)\}/g;
3604
- const result = [];
3605
- let lastIndex = 0;
3606
- let match;
3607
- while ((match = combinedRegex.exec(normalizedText)) !== null) {
3608
- if (match.index > lastIndex) {
3609
- const beforeText = normalizedText.substring(lastIndex, match.index);
3610
- if (beforeText) {
3611
- result.push(...createTextRunsWithNewlines(beforeText, baseStyle));
3612
- }
3628
+ normalizeObject(obj) {
3629
+ if (obj === null || obj === void 0) return obj;
3630
+ if (Array.isArray(obj)) {
3631
+ return obj.map((item) => this.normalizeObject(item));
3613
3632
  }
3614
- if (match[7]) {
3615
- const placeholderName = match[7];
3616
- const handler = PlaceholderRegistry.get(placeholderName);
3617
- if (handler) {
3618
- const placeholderResult = handler({ ...context, style: baseStyle });
3619
- if (Array.isArray(placeholderResult)) {
3620
- result.push(...placeholderResult);
3621
- } else if (placeholderResult instanceof TextRun) {
3622
- result.push(placeholderResult);
3623
- } else if (typeof placeholderResult === "string") {
3624
- result.push(
3625
- ...createTextRunsWithNewlines(placeholderResult, baseStyle)
3626
- );
3627
- }
3628
- } else {
3629
- result.push(...createTextRunsWithNewlines(match[0], baseStyle));
3630
- }
3631
- } else {
3632
- let decoratedText;
3633
- let bold = baseStyle.bold || false;
3634
- let italics = baseStyle.italics || false;
3635
- if (match[1] === "***" || match[1] === "___") {
3636
- decoratedText = match[2];
3637
- bold = true;
3638
- italics = true;
3639
- } else if (match[3] === "**" || match[3] === "__") {
3640
- decoratedText = match[4];
3641
- bold = true;
3642
- } else if (match[5] === "*" || match[5] === "_") {
3643
- decoratedText = match[6];
3644
- italics = true;
3645
- } else {
3646
- decoratedText = match[0];
3647
- }
3648
- const decoratedTextRuns = processTextWithPlaceholders(
3649
- decoratedText,
3650
- {
3651
- ...baseStyle,
3652
- bold,
3653
- italics
3654
- },
3655
- context
3656
- );
3657
- result.push(...decoratedTextRuns);
3633
+ if (obj instanceof Date) {
3634
+ return obj.toISOString();
3658
3635
  }
3659
- lastIndex = match.index + match[0].length;
3660
- }
3661
- if (lastIndex < normalizedText.length) {
3662
- const remainingText = normalizedText.substring(lastIndex);
3663
- if (remainingText) {
3664
- result.push(...createTextRunsWithNewlines(remainingText, baseStyle));
3636
+ if (typeof obj === "object") {
3637
+ const sorted = {};
3638
+ const keys = Object.keys(obj).sort();
3639
+ for (const key of keys) {
3640
+ sorted[key] = this.normalizeObject(obj[key]);
3641
+ }
3642
+ return sorted;
3665
3643
  }
3644
+ return obj;
3666
3645
  }
3667
- if (result.length === 0 && normalizedText) {
3668
- result.push(...createTextRunsWithNewlines(normalizedText, baseStyle));
3669
- }
3670
- return result;
3671
- }
3672
- function createTextRunsWithNewlines(text, baseStyle) {
3673
- const runs = [];
3674
- const lines = text.split("\n");
3675
- for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
3676
- const line = lines[lineIndex];
3677
- const needsLineBreak = lineIndex > 0;
3678
- if (line || needsLineBreak) {
3679
- runs.push(
3680
- new TextRun({
3681
- text: line,
3682
- font: baseStyle.font,
3683
- size: baseStyle.size,
3684
- color: baseStyle.color,
3685
- bold: baseStyle.bold,
3686
- italics: baseStyle.italics,
3687
- underline: baseStyle.underline,
3688
- break: needsLineBreak ? 1 : void 0
3689
- })
3690
- );
3691
- }
3646
+ /**
3647
+ * Create hash
3648
+ */
3649
+ hash(input) {
3650
+ return createHash("sha256").update(input).digest("hex").substring(0, 16);
3692
3651
  }
3693
- return runs;
3694
- }
3695
- function initializeBuiltinPlaceholders() {
3696
- PlaceholderRegistry.register("PAGE", (context) => {
3697
- return new TextRun({
3698
- children: [PageNumber.CURRENT],
3699
- font: context?.style?.font,
3700
- size: context?.style?.size,
3701
- color: context?.style?.color,
3702
- bold: context?.style?.bold,
3703
- italics: context?.style?.italics,
3704
- underline: context?.style?.underline
3705
- });
3706
- });
3707
- PlaceholderRegistry.register("TOTAL_PAGES", (context) => {
3708
- return new TextRun({
3709
- children: [PageNumber.TOTAL_PAGES],
3710
- font: context?.style?.font,
3711
- size: context?.style?.size,
3712
- color: context?.style?.color,
3713
- bold: context?.style?.bold,
3714
- italics: context?.style?.italics,
3715
- underline: context?.style?.underline
3716
- });
3717
- });
3718
- PlaceholderRegistry.register("DATE", (context) => {
3719
- const today = /* @__PURE__ */ new Date();
3720
- const dateString = today.toLocaleDateString();
3721
- return new TextRun({
3722
- text: dateString,
3723
- font: context?.style?.font,
3724
- size: context?.style?.size,
3725
- color: context?.style?.color,
3726
- bold: context?.style?.bold,
3727
- italics: context?.style?.italics,
3728
- underline: context?.style?.underline
3729
- });
3730
- });
3731
- PlaceholderRegistry.register("DATETIME", (context) => {
3732
- const now = /* @__PURE__ */ new Date();
3733
- const dateTimeString = now.toLocaleString();
3734
- return new TextRun({
3735
- text: dateTimeString,
3736
- font: context?.style?.font,
3737
- size: context?.style?.size,
3738
- color: context?.style?.color,
3739
- bold: context?.style?.bold,
3740
- italics: context?.style?.italics,
3741
- underline: context?.style?.underline
3742
- });
3743
- });
3744
- PlaceholderRegistry.register("YEAR", (context) => {
3745
- const year = (/* @__PURE__ */ new Date()).getFullYear().toString();
3746
- return new TextRun({
3747
- text: year,
3748
- font: context?.style?.font,
3749
- size: context?.style?.size,
3750
- color: context?.style?.color,
3751
- bold: context?.style?.bold,
3752
- italics: context?.style?.italics,
3753
- underline: context?.style?.underline
3754
- });
3755
- });
3652
+ };
3653
+
3654
+ // src/utils/revisionUtils.ts
3655
+ import { TextRun as TextRun3, InsertedTextRun, DeletedTextRun } from "docx";
3656
+
3657
+ // src/utils/unicode.ts
3658
+ function normalizeUnicodeText(text) {
3659
+ return (text ?? "").normalize("NFC");
3756
3660
  }
3757
- initializeBuiltinPlaceholders();
3661
+
3662
+ // src/utils/placeholderProcessor.ts
3663
+ import {
3664
+ TextRun as TextRun2,
3665
+ PageNumber
3666
+ } from "docx";
3758
3667
 
3759
3668
  // src/utils/textParser.ts
3669
+ import { TextRun, ExternalHyperlink, InternalHyperlink } from "docx";
3760
3670
  function parseTextWithDecorators(text, baseStyle = {}, options = {}) {
3761
3671
  if (!text) {
3762
- return [new TextRun2({ text: "", ...baseStyle })];
3672
+ return [new TextRun({ text: "", ...baseStyle })];
3763
3673
  }
3764
3674
  const normalizedText = normalizeUnicodeText(text);
3765
3675
  const hasPlaceholders = /\{[^}]+\}/.test(normalizedText);
@@ -3781,7 +3691,7 @@ function parseTextWithDecorators(text, baseStyle = {}, options = {}) {
3781
3691
  if (match.index > lastIndex) {
3782
3692
  const plainText = normalizedText.substring(lastIndex, match.index);
3783
3693
  if (plainText) {
3784
- runs.push(...createTextRunsWithNewlines2(plainText, baseStyle, options));
3694
+ runs.push(...createTextRunsWithNewlines(plainText, baseStyle, options));
3785
3695
  }
3786
3696
  }
3787
3697
  let decoratedText;
@@ -3800,7 +3710,7 @@ function parseTextWithDecorators(text, baseStyle = {}, options = {}) {
3800
3710
  } else {
3801
3711
  decoratedText = match[0];
3802
3712
  }
3803
- const decoratedRuns = createTextRunsWithNewlines2(
3713
+ const decoratedRuns = createTextRunsWithNewlines(
3804
3714
  decoratedText,
3805
3715
  baseStyle,
3806
3716
  options,
@@ -3816,18 +3726,18 @@ function parseTextWithDecorators(text, baseStyle = {}, options = {}) {
3816
3726
  const remainingText = normalizedText.substring(lastIndex);
3817
3727
  if (remainingText) {
3818
3728
  runs.push(
3819
- ...createTextRunsWithNewlines2(remainingText, baseStyle, options)
3729
+ ...createTextRunsWithNewlines(remainingText, baseStyle, options)
3820
3730
  );
3821
3731
  }
3822
3732
  }
3823
3733
  if (runs.length === 0 && normalizedText) {
3824
3734
  runs.push(
3825
- ...createTextRunsWithNewlines2(normalizedText, baseStyle, options)
3735
+ ...createTextRunsWithNewlines(normalizedText, baseStyle, options)
3826
3736
  );
3827
3737
  }
3828
3738
  return runs;
3829
3739
  }
3830
- function createTextRunsWithNewlines2(text, baseStyle, options, overrideStyle) {
3740
+ function createTextRunsWithNewlines(text, baseStyle, options, overrideStyle) {
3831
3741
  const runs = [];
3832
3742
  const lines = text.split("\n");
3833
3743
  for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
@@ -3839,7 +3749,7 @@ function createTextRunsWithNewlines2(text, baseStyle, options, overrideStyle) {
3839
3749
  italics: overrideStyle?.italics ?? baseStyle.italics
3840
3750
  };
3841
3751
  runs.push(
3842
- new TextRun2({
3752
+ new TextRun({
3843
3753
  text: line,
3844
3754
  ...baseStyle.font && { font: baseStyle.font },
3845
3755
  ...baseStyle.size && { size: baseStyle.size },
@@ -3885,7 +3795,7 @@ function parseTextWithHyperlinks(text, baseStyle = {}, options = {}) {
3885
3795
  if (isInternal) {
3886
3796
  const bookmarkId = linkUrl.substring(1);
3887
3797
  runs.push(
3888
- new InternalHyperlink2({
3798
+ new InternalHyperlink({
3889
3799
  children: linkTextRuns,
3890
3800
  // Cast needed for docx types
3891
3801
  anchor: bookmarkId
@@ -3893,7 +3803,7 @@ function parseTextWithHyperlinks(text, baseStyle = {}, options = {}) {
3893
3803
  );
3894
3804
  } else {
3895
3805
  runs.push(
3896
- new ExternalHyperlink2({
3806
+ new ExternalHyperlink({
3897
3807
  children: linkTextRuns,
3898
3808
  // Cast needed for docx types
3899
3809
  link: linkUrl
@@ -3922,100 +3832,199 @@ function parseTextWithHyperlinks(text, baseStyle = {}, options = {}) {
3922
3832
  return runs;
3923
3833
  }
3924
3834
 
3925
- // src/core/render.ts
3926
- init_colorUtils();
3927
- init_styleHelpers();
3928
- init_layoutUtils();
3929
-
3930
- // src/cache/index.ts
3931
- var cache_exports = {};
3932
- __export(cache_exports, {
3933
- CacheKeyGenerator: () => CacheKeyGenerator
3934
- });
3935
- __reExport(cache_exports, cache_star);
3936
- import * as cache_star from "@json-to-office/shared/cache";
3937
-
3938
- // src/cache/key-generator.ts
3939
- import { createHash } from "crypto";
3940
- var CacheKeyGenerator = class {
3941
- version;
3942
- constructor(version = "1.0") {
3943
- this.version = version;
3835
+ // src/utils/placeholderProcessor.ts
3836
+ var PlaceholderRegistry = class {
3837
+ static handlers = /* @__PURE__ */ new Map();
3838
+ /**
3839
+ * Register a placeholder handler
3840
+ */
3841
+ static register(name, handler) {
3842
+ this.handlers.set(name.toUpperCase(), handler);
3944
3843
  }
3945
3844
  /**
3946
- * Generate cache key for a component
3845
+ * Get a placeholder handler
3947
3846
  */
3948
- generateKey(component, context, options = {}) {
3949
- const parts = [
3950
- this.version,
3951
- component.name,
3952
- this.hashProps(component.props)
3953
- ];
3954
- if (options.includeTheme !== false) {
3955
- parts.push(context.theme.name);
3956
- }
3957
- if (options.includeContext) {
3958
- parts.push(this.hashContext(context));
3959
- }
3960
- if (options.additionalKeys) {
3961
- parts.push(...options.additionalKeys);
3962
- }
3963
- if (options.version) {
3964
- parts.push(options.version);
3965
- }
3966
- return parts.join(":");
3847
+ static get(name) {
3848
+ return this.handlers.get(name.toUpperCase());
3967
3849
  }
3968
3850
  /**
3969
- * Hash component props
3851
+ * Check if a placeholder is registered
3970
3852
  */
3971
- hashProps(props) {
3972
- if (!props) return "null";
3973
- const normalized = this.normalizeObject(props);
3974
- const json = JSON.stringify(normalized);
3975
- return this.hash(json);
3853
+ static has(name) {
3854
+ return this.handlers.has(name.toUpperCase());
3976
3855
  }
3977
3856
  /**
3978
- * Hash render context
3857
+ * Get all registered placeholder names
3979
3858
  */
3980
- hashContext(context) {
3981
- const relevant = {
3982
- theme: context.theme.name,
3983
- document: context.document
3984
- // Exclude runtime properties like sectionIndex, componentIndex
3985
- };
3986
- return this.hash(JSON.stringify(relevant));
3859
+ static getRegisteredNames() {
3860
+ return Array.from(this.handlers.keys());
3987
3861
  }
3988
3862
  /**
3989
- * Normalize object for consistent hashing
3863
+ * Clear all registered placeholders
3990
3864
  */
3991
- normalizeObject(obj) {
3992
- if (obj === null || obj === void 0) return obj;
3993
- if (Array.isArray(obj)) {
3994
- return obj.map((item) => this.normalizeObject(item));
3995
- }
3996
- if (obj instanceof Date) {
3997
- return obj.toISOString();
3865
+ static clear() {
3866
+ this.handlers.clear();
3867
+ }
3868
+ };
3869
+ function processTextWithPlaceholders(text, baseStyle = {}, context = {}) {
3870
+ const normalizedText = normalizeUnicodeText(text);
3871
+ const combinedRegex = /(\*\*\*|___)([\s\S]*?)\1|(\*\*|__)([\s\S]*?)\3|(\*|_)([\s\S]*?)\5|\{([^}]+)\}/g;
3872
+ const result = [];
3873
+ let lastIndex = 0;
3874
+ let match;
3875
+ while ((match = combinedRegex.exec(normalizedText)) !== null) {
3876
+ if (match.index > lastIndex) {
3877
+ const beforeText = normalizedText.substring(lastIndex, match.index);
3878
+ if (beforeText) {
3879
+ result.push(...createTextRunsWithNewlines2(beforeText, baseStyle));
3880
+ }
3998
3881
  }
3999
- if (typeof obj === "object") {
4000
- const sorted = {};
4001
- const keys = Object.keys(obj).sort();
4002
- for (const key of keys) {
4003
- sorted[key] = this.normalizeObject(obj[key]);
3882
+ if (match[7]) {
3883
+ const placeholderName = match[7];
3884
+ const handler = PlaceholderRegistry.get(placeholderName);
3885
+ if (handler) {
3886
+ const placeholderResult = handler({ ...context, style: baseStyle });
3887
+ if (Array.isArray(placeholderResult)) {
3888
+ result.push(...placeholderResult);
3889
+ } else if (placeholderResult instanceof TextRun2) {
3890
+ result.push(placeholderResult);
3891
+ } else if (typeof placeholderResult === "string") {
3892
+ result.push(
3893
+ ...createTextRunsWithNewlines2(placeholderResult, baseStyle)
3894
+ );
3895
+ }
3896
+ } else {
3897
+ result.push(...createTextRunsWithNewlines2(match[0], baseStyle));
4004
3898
  }
4005
- return sorted;
3899
+ } else {
3900
+ let decoratedText;
3901
+ let bold = baseStyle.bold || false;
3902
+ let italics = baseStyle.italics || false;
3903
+ if (match[1] === "***" || match[1] === "___") {
3904
+ decoratedText = match[2];
3905
+ bold = true;
3906
+ italics = true;
3907
+ } else if (match[3] === "**" || match[3] === "__") {
3908
+ decoratedText = match[4];
3909
+ bold = true;
3910
+ } else if (match[5] === "*" || match[5] === "_") {
3911
+ decoratedText = match[6];
3912
+ italics = true;
3913
+ } else {
3914
+ decoratedText = match[0];
3915
+ }
3916
+ const decoratedTextRuns = processTextWithPlaceholders(
3917
+ decoratedText,
3918
+ {
3919
+ ...baseStyle,
3920
+ bold,
3921
+ italics
3922
+ },
3923
+ context
3924
+ );
3925
+ result.push(...decoratedTextRuns);
4006
3926
  }
4007
- return obj;
3927
+ lastIndex = match.index + match[0].length;
4008
3928
  }
4009
- /**
4010
- * Create hash
4011
- */
4012
- hash(input) {
4013
- return createHash("sha256").update(input).digest("hex").substring(0, 16);
3929
+ if (lastIndex < normalizedText.length) {
3930
+ const remainingText = normalizedText.substring(lastIndex);
3931
+ if (remainingText) {
3932
+ result.push(...createTextRunsWithNewlines2(remainingText, baseStyle));
3933
+ }
4014
3934
  }
4015
- };
3935
+ if (result.length === 0 && normalizedText) {
3936
+ result.push(...createTextRunsWithNewlines2(normalizedText, baseStyle));
3937
+ }
3938
+ return result;
3939
+ }
3940
+ function createTextRunsWithNewlines2(text, baseStyle) {
3941
+ const runs = [];
3942
+ const lines = text.split("\n");
3943
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
3944
+ const line = lines[lineIndex];
3945
+ const needsLineBreak = lineIndex > 0;
3946
+ if (line || needsLineBreak) {
3947
+ runs.push(
3948
+ new TextRun2({
3949
+ text: line,
3950
+ font: baseStyle.font,
3951
+ size: baseStyle.size,
3952
+ color: baseStyle.color,
3953
+ bold: baseStyle.bold,
3954
+ italics: baseStyle.italics,
3955
+ underline: baseStyle.underline,
3956
+ break: needsLineBreak ? 1 : void 0
3957
+ })
3958
+ );
3959
+ }
3960
+ }
3961
+ return runs;
3962
+ }
3963
+ function initializeBuiltinPlaceholders() {
3964
+ PlaceholderRegistry.register("PAGE", (context) => {
3965
+ return new TextRun2({
3966
+ children: [PageNumber.CURRENT],
3967
+ font: context?.style?.font,
3968
+ size: context?.style?.size,
3969
+ color: context?.style?.color,
3970
+ bold: context?.style?.bold,
3971
+ italics: context?.style?.italics,
3972
+ underline: context?.style?.underline
3973
+ });
3974
+ });
3975
+ PlaceholderRegistry.register("TOTAL_PAGES", (context) => {
3976
+ return new TextRun2({
3977
+ children: [PageNumber.TOTAL_PAGES],
3978
+ font: context?.style?.font,
3979
+ size: context?.style?.size,
3980
+ color: context?.style?.color,
3981
+ bold: context?.style?.bold,
3982
+ italics: context?.style?.italics,
3983
+ underline: context?.style?.underline
3984
+ });
3985
+ });
3986
+ PlaceholderRegistry.register("DATE", (context) => {
3987
+ const today = /* @__PURE__ */ new Date();
3988
+ const dateString = today.toLocaleDateString();
3989
+ return new TextRun2({
3990
+ text: dateString,
3991
+ font: context?.style?.font,
3992
+ size: context?.style?.size,
3993
+ color: context?.style?.color,
3994
+ bold: context?.style?.bold,
3995
+ italics: context?.style?.italics,
3996
+ underline: context?.style?.underline
3997
+ });
3998
+ });
3999
+ PlaceholderRegistry.register("DATETIME", (context) => {
4000
+ const now = /* @__PURE__ */ new Date();
4001
+ const dateTimeString = now.toLocaleString();
4002
+ return new TextRun2({
4003
+ text: dateTimeString,
4004
+ font: context?.style?.font,
4005
+ size: context?.style?.size,
4006
+ color: context?.style?.color,
4007
+ bold: context?.style?.bold,
4008
+ italics: context?.style?.italics,
4009
+ underline: context?.style?.underline
4010
+ });
4011
+ });
4012
+ PlaceholderRegistry.register("YEAR", (context) => {
4013
+ const year = (/* @__PURE__ */ new Date()).getFullYear().toString();
4014
+ return new TextRun2({
4015
+ text: year,
4016
+ font: context?.style?.font,
4017
+ size: context?.style?.size,
4018
+ color: context?.style?.color,
4019
+ bold: context?.style?.bold,
4020
+ italics: context?.style?.italics,
4021
+ underline: context?.style?.underline
4022
+ });
4023
+ });
4024
+ }
4025
+ initializeBuiltinPlaceholders();
4016
4026
 
4017
4027
  // src/utils/revisionUtils.ts
4018
- import { TextRun as TextRun3, InsertedTextRun, DeletedTextRun } from "docx";
4019
4028
  var DEFAULT_REVISION_AUTHOR = "json-to-office";
4020
4029
  var DEFAULT_REVISION_DATE = "1970-01-01T00:00:00Z";
4021
4030
  var RevisionIdRegistry = class {
@@ -5067,10 +5076,10 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
5067
5076
  } else if (isImageComponent(cell)) {
5068
5077
  const imageComp = cell;
5069
5078
  try {
5070
- const imageSource = imageComp.props.base64 || imageComp.props.path;
5079
+ const imageSource = resolveImageSource(imageComp.props);
5071
5080
  if (!imageSource) {
5072
5081
  throw new Error(
5073
- 'Image component requires either "path" or "base64" property'
5082
+ 'Image component requires one of "path", "base64", or "svg" property'
5074
5083
  );
5075
5084
  }
5076
5085
  const imageResult = await getImageBuffer(imageSource);
@@ -5096,7 +5105,7 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
5096
5105
  });
5097
5106
  cellChildren = [imageRun];
5098
5107
  } catch (error) {
5099
- const imageSource = imageComp.props.base64 || imageComp.props.path || "unknown";
5108
+ const imageSource = imageComp.props.svg?.trim() ? "inline-svg" : imageComp.props.base64 || imageComp.props.path || "unknown";
5100
5109
  cellChildren = [
5101
5110
  new TextRun4({
5102
5111
  text: `[IMAGE: ${imageSource.substring(0, 50)}${imageSource.length > 50 ? "..." : ""}]`,
@@ -5773,10 +5782,10 @@ function renderListComponent(component, theme, themeName) {
5773
5782
  async function renderImageComponent(component, theme, themeName) {
5774
5783
  if (!isImageComponent(component)) return [];
5775
5784
  const resolvedConfig = component.props;
5776
- const imageSource = resolvedConfig.base64 || resolvedConfig.path;
5785
+ const imageSource = resolveImageSource(resolvedConfig);
5777
5786
  if (!imageSource) {
5778
5787
  throw new Error(
5779
- 'Image component requires either "path" or "base64" property'
5788
+ 'Image component requires one of "path", "base64", or "svg" property'
5780
5789
  );
5781
5790
  }
5782
5791
  return await createImage(imageSource, theme, themeName, {
@@ -6458,7 +6467,10 @@ async function generateChart(config, servicesConfig) {
6458
6467
  infile: config.options,
6459
6468
  type: "png",
6460
6469
  b64: true,
6461
- scale: config.scale
6470
+ scale: config.scale,
6471
+ // Forward resources verbatim only when present so the payload stays
6472
+ // byte-identical to before for callers that omit it.
6473
+ ...config.resources ? { resources: config.resources } : {}
6462
6474
  };
6463
6475
  const response = await postJsonToService({
6464
6476
  url: serverUrl,
@@ -6772,32 +6784,37 @@ async function renderHeaderFooterComponents(components, theme, themeName, contex
6772
6784
  for (const component of activeComponents) {
6773
6785
  if (isParagraphComponent(component)) {
6774
6786
  const textComp = component;
6787
+ const font = textComp.props.font;
6775
6788
  const normalStyle = getNormalStyle(theme);
6776
- const textStyle = {
6777
- font: textComp.props.font?.family || resolveFontFamily(theme, normalStyle.font) || getThemeFonts(theme).body.family,
6778
- size: (textComp.props.font?.size ?? normalStyle.size ?? 11) * 2,
6779
- // Convert to half-points
6780
- bold: textComp.props.font?.bold ?? false,
6781
- italics: textComp.props.font?.italic ?? false,
6782
- color: textComp.props.font?.color && resolveColor(textComp.props.font.color, theme) || normalStyle.color && resolveColor(normalStyle.color, theme) || getThemeColors(theme).textPrimary
6783
- };
6784
- const textRuns = parseTextWithDecorators(textComp.props.text, textStyle);
6785
6789
  elements.push(
6786
- new Paragraph7({
6787
- children: textRuns,
6788
- alignment: textComp.props.alignment ? getAlignment3(textComp.props.alignment) : void 0,
6789
- style: "Normal"
6790
+ createText(textComp.props.text, theme, themeName, {
6791
+ style: "Normal",
6792
+ alignment: textComp.props.alignment,
6793
+ // Resolve explicit run styling against the Normal style, preserving
6794
+ // prior header/footer rendering for these properties.
6795
+ fontFamily: font?.family || resolveFontFamily(theme, normalStyle.font) || getThemeFonts(theme).body.family,
6796
+ fontSize: font?.size ?? normalStyle.size ?? 11,
6797
+ // Pass the raw color/token; createText resolves it. Fall back to the
6798
+ // Normal style color, then the theme's primary text color.
6799
+ fontColor: font?.color || normalStyle.color || "textPrimary",
6800
+ bold: font?.bold ?? false,
6801
+ italic: font?.italic ?? false,
6802
+ underline: font?.underline,
6803
+ fontWeight: font?.fontWeight,
6804
+ boldColor: textComp.props.boldColor,
6805
+ spacing: textComp.props.spacing,
6806
+ lineSpacing: font?.lineSpacing
6790
6807
  })
6791
6808
  );
6792
6809
  } else if (isImageComponent(component)) {
6793
6810
  const imageComp = component;
6794
- let imageSource = imageComp.props.base64 || imageComp.props.path;
6811
+ let imageSource = resolveImageSource(imageComp.props);
6795
6812
  if (!imageSource) {
6796
6813
  elements.push(
6797
6814
  new Paragraph7({
6798
6815
  children: [
6799
6816
  new TextRun6({
6800
- text: "[IMAGE: Missing path or base64 property]",
6817
+ text: "[IMAGE: Missing path, base64, or svg property]",
6801
6818
  font: getThemeFonts(theme).body.family,
6802
6819
  size: 20,
6803
6820
  bold: true,
@@ -6871,15 +6888,16 @@ async function renderHeaderFooterComponents(components, theme, themeName, contex
6871
6888
  })
6872
6889
  );
6873
6890
  } catch (error) {
6891
+ const sourcePreview = imageSource.substring(0, 50);
6874
6892
  console.error(
6875
- `[Header/Footer Image Error] Failed to render image: ${imageComp.props.path?.substring(0, 50)}...`,
6893
+ `[Header/Footer Image Error] Failed to render image: ${sourcePreview}...`,
6876
6894
  error instanceof Error ? error.message : error
6877
6895
  );
6878
6896
  elements.push(
6879
6897
  new Paragraph7({
6880
6898
  children: [
6881
6899
  new TextRun6({
6882
- text: `[IMAGE: ${imageComp.props.path}]`,
6900
+ text: `[IMAGE: ${sourcePreview}]`,
6883
6901
  font: getThemeFonts(theme).body.family,
6884
6902
  size: 20,
6885
6903
  color: getThemeColors(theme).secondary,