@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.
package/dist/index.js CHANGED
@@ -2768,6 +2768,16 @@ async function downloadImageFromUrl(url) {
2768
2768
  throw new Error(`Failed to download image from ${url}: Unknown error`);
2769
2769
  }
2770
2770
  }
2771
+ function resolveImageSource(props) {
2772
+ const hasValue = (v) => typeof v === "string" && v.trim().length > 0;
2773
+ if (hasValue(props.svg)) {
2774
+ const encoded = Buffer.from(props.svg, "utf-8").toString("base64");
2775
+ return `data:image/svg+xml;base64,${encoded}`;
2776
+ }
2777
+ if (hasValue(props.base64)) return props.base64;
2778
+ if (hasValue(props.path)) return props.path;
2779
+ return void 0;
2780
+ }
2771
2781
  async function getImageBuffer(imagePath) {
2772
2782
  if (isBase64Image(imagePath)) {
2773
2783
  return { buffer: decodeBase64Image(imagePath) };
@@ -3469,216 +3479,116 @@ function getStyleIdForLevel(level) {
3469
3479
  return styleMap[level] || "Heading1";
3470
3480
  }
3471
3481
 
3472
- // src/utils/textParser.ts
3473
- import { TextRun as TextRun2, ExternalHyperlink as ExternalHyperlink2, InternalHyperlink as InternalHyperlink2 } from "docx";
3474
-
3475
- // src/utils/placeholderProcessor.ts
3476
- import {
3477
- TextRun,
3478
- PageNumber
3479
- } from "docx";
3482
+ // src/core/render.ts
3483
+ init_styleHelpers();
3484
+ init_layoutUtils();
3480
3485
 
3481
- // src/utils/unicode.ts
3482
- function normalizeUnicodeText(text) {
3483
- return (text ?? "").normalize("NFC");
3484
- }
3486
+ // src/cache/index.ts
3487
+ var cache_exports = {};
3488
+ __export(cache_exports, {
3489
+ CacheKeyGenerator: () => CacheKeyGenerator
3490
+ });
3491
+ __reExport(cache_exports, cache_star);
3492
+ import * as cache_star from "@json-to-office/shared/cache";
3485
3493
 
3486
- // src/utils/placeholderProcessor.ts
3487
- var PlaceholderRegistry = class {
3488
- static handlers = /* @__PURE__ */ new Map();
3489
- /**
3490
- * Register a placeholder handler
3491
- */
3492
- static register(name, handler) {
3493
- this.handlers.set(name.toUpperCase(), handler);
3494
+ // src/cache/key-generator.ts
3495
+ import { createHash } from "crypto";
3496
+ var CacheKeyGenerator = class {
3497
+ version;
3498
+ constructor(version = "1.0") {
3499
+ this.version = version;
3494
3500
  }
3495
3501
  /**
3496
- * Get a placeholder handler
3502
+ * Generate cache key for a component
3497
3503
  */
3498
- static get(name) {
3499
- return this.handlers.get(name.toUpperCase());
3504
+ generateKey(component, context, options = {}) {
3505
+ const parts = [
3506
+ this.version,
3507
+ component.name,
3508
+ this.hashProps(component.props)
3509
+ ];
3510
+ if (options.includeTheme !== false) {
3511
+ parts.push(context.theme.name);
3512
+ }
3513
+ if (options.includeContext) {
3514
+ parts.push(this.hashContext(context));
3515
+ }
3516
+ if (options.additionalKeys) {
3517
+ parts.push(...options.additionalKeys);
3518
+ }
3519
+ if (options.version) {
3520
+ parts.push(options.version);
3521
+ }
3522
+ return parts.join(":");
3500
3523
  }
3501
3524
  /**
3502
- * Check if a placeholder is registered
3525
+ * Hash component props
3503
3526
  */
3504
- static has(name) {
3505
- return this.handlers.has(name.toUpperCase());
3527
+ hashProps(props) {
3528
+ if (!props) return "null";
3529
+ const normalized = this.normalizeObject(props);
3530
+ const json = JSON.stringify(normalized);
3531
+ return this.hash(json);
3506
3532
  }
3507
3533
  /**
3508
- * Get all registered placeholder names
3534
+ * Hash render context
3509
3535
  */
3510
- static getRegisteredNames() {
3511
- return Array.from(this.handlers.keys());
3536
+ hashContext(context) {
3537
+ const relevant = {
3538
+ theme: context.theme.name,
3539
+ document: context.document
3540
+ // Exclude runtime properties like sectionIndex, componentIndex
3541
+ };
3542
+ return this.hash(JSON.stringify(relevant));
3512
3543
  }
3513
3544
  /**
3514
- * Clear all registered placeholders
3545
+ * Normalize object for consistent hashing
3515
3546
  */
3516
- static clear() {
3517
- this.handlers.clear();
3518
- }
3519
- };
3520
- function processTextWithPlaceholders(text, baseStyle = {}, context = {}) {
3521
- const normalizedText = normalizeUnicodeText(text);
3522
- const combinedRegex = /(\*\*\*|___)([\s\S]*?)\1|(\*\*|__)([\s\S]*?)\3|(\*|_)([\s\S]*?)\5|\{([^}]+)\}/g;
3523
- const result = [];
3524
- let lastIndex = 0;
3525
- let match;
3526
- while ((match = combinedRegex.exec(normalizedText)) !== null) {
3527
- if (match.index > lastIndex) {
3528
- const beforeText = normalizedText.substring(lastIndex, match.index);
3529
- if (beforeText) {
3530
- result.push(...createTextRunsWithNewlines(beforeText, baseStyle));
3531
- }
3547
+ normalizeObject(obj) {
3548
+ if (obj === null || obj === void 0) return obj;
3549
+ if (Array.isArray(obj)) {
3550
+ return obj.map((item) => this.normalizeObject(item));
3532
3551
  }
3533
- if (match[7]) {
3534
- const placeholderName = match[7];
3535
- const handler = PlaceholderRegistry.get(placeholderName);
3536
- if (handler) {
3537
- const placeholderResult = handler({ ...context, style: baseStyle });
3538
- if (Array.isArray(placeholderResult)) {
3539
- result.push(...placeholderResult);
3540
- } else if (placeholderResult instanceof TextRun) {
3541
- result.push(placeholderResult);
3542
- } else if (typeof placeholderResult === "string") {
3543
- result.push(
3544
- ...createTextRunsWithNewlines(placeholderResult, baseStyle)
3545
- );
3546
- }
3547
- } else {
3548
- result.push(...createTextRunsWithNewlines(match[0], baseStyle));
3549
- }
3550
- } else {
3551
- let decoratedText;
3552
- let bold = baseStyle.bold || false;
3553
- let italics = baseStyle.italics || false;
3554
- if (match[1] === "***" || match[1] === "___") {
3555
- decoratedText = match[2];
3556
- bold = true;
3557
- italics = true;
3558
- } else if (match[3] === "**" || match[3] === "__") {
3559
- decoratedText = match[4];
3560
- bold = true;
3561
- } else if (match[5] === "*" || match[5] === "_") {
3562
- decoratedText = match[6];
3563
- italics = true;
3564
- } else {
3565
- decoratedText = match[0];
3566
- }
3567
- const decoratedTextRuns = processTextWithPlaceholders(
3568
- decoratedText,
3569
- {
3570
- ...baseStyle,
3571
- bold,
3572
- italics
3573
- },
3574
- context
3575
- );
3576
- result.push(...decoratedTextRuns);
3552
+ if (obj instanceof Date) {
3553
+ return obj.toISOString();
3577
3554
  }
3578
- lastIndex = match.index + match[0].length;
3579
- }
3580
- if (lastIndex < normalizedText.length) {
3581
- const remainingText = normalizedText.substring(lastIndex);
3582
- if (remainingText) {
3583
- result.push(...createTextRunsWithNewlines(remainingText, baseStyle));
3555
+ if (typeof obj === "object") {
3556
+ const sorted = {};
3557
+ const keys = Object.keys(obj).sort();
3558
+ for (const key of keys) {
3559
+ sorted[key] = this.normalizeObject(obj[key]);
3560
+ }
3561
+ return sorted;
3584
3562
  }
3563
+ return obj;
3585
3564
  }
3586
- if (result.length === 0 && normalizedText) {
3587
- result.push(...createTextRunsWithNewlines(normalizedText, baseStyle));
3588
- }
3589
- return result;
3590
- }
3591
- function createTextRunsWithNewlines(text, baseStyle) {
3592
- const runs = [];
3593
- const lines = text.split("\n");
3594
- for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
3595
- const line = lines[lineIndex];
3596
- const needsLineBreak = lineIndex > 0;
3597
- if (line || needsLineBreak) {
3598
- runs.push(
3599
- new TextRun({
3600
- text: line,
3601
- font: baseStyle.font,
3602
- size: baseStyle.size,
3603
- color: baseStyle.color,
3604
- bold: baseStyle.bold,
3605
- italics: baseStyle.italics,
3606
- underline: baseStyle.underline,
3607
- break: needsLineBreak ? 1 : void 0
3608
- })
3609
- );
3610
- }
3565
+ /**
3566
+ * Create hash
3567
+ */
3568
+ hash(input) {
3569
+ return createHash("sha256").update(input).digest("hex").substring(0, 16);
3611
3570
  }
3612
- return runs;
3613
- }
3614
- function initializeBuiltinPlaceholders() {
3615
- PlaceholderRegistry.register("PAGE", (context) => {
3616
- return new TextRun({
3617
- children: [PageNumber.CURRENT],
3618
- font: context?.style?.font,
3619
- size: context?.style?.size,
3620
- color: context?.style?.color,
3621
- bold: context?.style?.bold,
3622
- italics: context?.style?.italics,
3623
- underline: context?.style?.underline
3624
- });
3625
- });
3626
- PlaceholderRegistry.register("TOTAL_PAGES", (context) => {
3627
- return new TextRun({
3628
- children: [PageNumber.TOTAL_PAGES],
3629
- font: context?.style?.font,
3630
- size: context?.style?.size,
3631
- color: context?.style?.color,
3632
- bold: context?.style?.bold,
3633
- italics: context?.style?.italics,
3634
- underline: context?.style?.underline
3635
- });
3636
- });
3637
- PlaceholderRegistry.register("DATE", (context) => {
3638
- const today = /* @__PURE__ */ new Date();
3639
- const dateString = today.toLocaleDateString();
3640
- return new TextRun({
3641
- text: dateString,
3642
- font: context?.style?.font,
3643
- size: context?.style?.size,
3644
- color: context?.style?.color,
3645
- bold: context?.style?.bold,
3646
- italics: context?.style?.italics,
3647
- underline: context?.style?.underline
3648
- });
3649
- });
3650
- PlaceholderRegistry.register("DATETIME", (context) => {
3651
- const now = /* @__PURE__ */ new Date();
3652
- const dateTimeString = now.toLocaleString();
3653
- return new TextRun({
3654
- text: dateTimeString,
3655
- font: context?.style?.font,
3656
- size: context?.style?.size,
3657
- color: context?.style?.color,
3658
- bold: context?.style?.bold,
3659
- italics: context?.style?.italics,
3660
- underline: context?.style?.underline
3661
- });
3662
- });
3663
- PlaceholderRegistry.register("YEAR", (context) => {
3664
- const year = (/* @__PURE__ */ new Date()).getFullYear().toString();
3665
- return new TextRun({
3666
- text: year,
3667
- font: context?.style?.font,
3668
- size: context?.style?.size,
3669
- color: context?.style?.color,
3670
- bold: context?.style?.bold,
3671
- italics: context?.style?.italics,
3672
- underline: context?.style?.underline
3673
- });
3674
- });
3571
+ };
3572
+
3573
+ // src/utils/revisionUtils.ts
3574
+ import { TextRun as TextRun3, InsertedTextRun, DeletedTextRun } from "docx";
3575
+
3576
+ // src/utils/unicode.ts
3577
+ function normalizeUnicodeText(text) {
3578
+ return (text ?? "").normalize("NFC");
3675
3579
  }
3676
- initializeBuiltinPlaceholders();
3580
+
3581
+ // src/utils/placeholderProcessor.ts
3582
+ import {
3583
+ TextRun as TextRun2,
3584
+ PageNumber
3585
+ } from "docx";
3677
3586
 
3678
3587
  // src/utils/textParser.ts
3588
+ import { TextRun, ExternalHyperlink, InternalHyperlink } from "docx";
3679
3589
  function parseTextWithDecorators(text, baseStyle = {}, options = {}) {
3680
3590
  if (!text) {
3681
- return [new TextRun2({ text: "", ...baseStyle })];
3591
+ return [new TextRun({ text: "", ...baseStyle })];
3682
3592
  }
3683
3593
  const normalizedText = normalizeUnicodeText(text);
3684
3594
  const hasPlaceholders = /\{[^}]+\}/.test(normalizedText);
@@ -3700,7 +3610,7 @@ function parseTextWithDecorators(text, baseStyle = {}, options = {}) {
3700
3610
  if (match.index > lastIndex) {
3701
3611
  const plainText = normalizedText.substring(lastIndex, match.index);
3702
3612
  if (plainText) {
3703
- runs.push(...createTextRunsWithNewlines2(plainText, baseStyle, options));
3613
+ runs.push(...createTextRunsWithNewlines(plainText, baseStyle, options));
3704
3614
  }
3705
3615
  }
3706
3616
  let decoratedText;
@@ -3719,7 +3629,7 @@ function parseTextWithDecorators(text, baseStyle = {}, options = {}) {
3719
3629
  } else {
3720
3630
  decoratedText = match[0];
3721
3631
  }
3722
- const decoratedRuns = createTextRunsWithNewlines2(
3632
+ const decoratedRuns = createTextRunsWithNewlines(
3723
3633
  decoratedText,
3724
3634
  baseStyle,
3725
3635
  options,
@@ -3735,18 +3645,18 @@ function parseTextWithDecorators(text, baseStyle = {}, options = {}) {
3735
3645
  const remainingText = normalizedText.substring(lastIndex);
3736
3646
  if (remainingText) {
3737
3647
  runs.push(
3738
- ...createTextRunsWithNewlines2(remainingText, baseStyle, options)
3648
+ ...createTextRunsWithNewlines(remainingText, baseStyle, options)
3739
3649
  );
3740
3650
  }
3741
3651
  }
3742
3652
  if (runs.length === 0 && normalizedText) {
3743
3653
  runs.push(
3744
- ...createTextRunsWithNewlines2(normalizedText, baseStyle, options)
3654
+ ...createTextRunsWithNewlines(normalizedText, baseStyle, options)
3745
3655
  );
3746
3656
  }
3747
3657
  return runs;
3748
3658
  }
3749
- function createTextRunsWithNewlines2(text, baseStyle, options, overrideStyle) {
3659
+ function createTextRunsWithNewlines(text, baseStyle, options, overrideStyle) {
3750
3660
  const runs = [];
3751
3661
  const lines = text.split("\n");
3752
3662
  for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
@@ -3758,7 +3668,7 @@ function createTextRunsWithNewlines2(text, baseStyle, options, overrideStyle) {
3758
3668
  italics: overrideStyle?.italics ?? baseStyle.italics
3759
3669
  };
3760
3670
  runs.push(
3761
- new TextRun2({
3671
+ new TextRun({
3762
3672
  text: line,
3763
3673
  ...baseStyle.font && { font: baseStyle.font },
3764
3674
  ...baseStyle.size && { size: baseStyle.size },
@@ -3804,7 +3714,7 @@ function parseTextWithHyperlinks(text, baseStyle = {}, options = {}) {
3804
3714
  if (isInternal) {
3805
3715
  const bookmarkId = linkUrl.substring(1);
3806
3716
  runs.push(
3807
- new InternalHyperlink2({
3717
+ new InternalHyperlink({
3808
3718
  children: linkTextRuns,
3809
3719
  // Cast needed for docx types
3810
3720
  anchor: bookmarkId
@@ -3812,7 +3722,7 @@ function parseTextWithHyperlinks(text, baseStyle = {}, options = {}) {
3812
3722
  );
3813
3723
  } else {
3814
3724
  runs.push(
3815
- new ExternalHyperlink2({
3725
+ new ExternalHyperlink({
3816
3726
  children: linkTextRuns,
3817
3727
  // Cast needed for docx types
3818
3728
  link: linkUrl
@@ -3841,100 +3751,199 @@ function parseTextWithHyperlinks(text, baseStyle = {}, options = {}) {
3841
3751
  return runs;
3842
3752
  }
3843
3753
 
3844
- // src/core/render.ts
3845
- init_colorUtils();
3846
- init_styleHelpers();
3847
- init_layoutUtils();
3848
-
3849
- // src/cache/index.ts
3850
- var cache_exports = {};
3851
- __export(cache_exports, {
3852
- CacheKeyGenerator: () => CacheKeyGenerator
3853
- });
3854
- __reExport(cache_exports, cache_star);
3855
- import * as cache_star from "@json-to-office/shared/cache";
3856
-
3857
- // src/cache/key-generator.ts
3858
- import { createHash } from "crypto";
3859
- var CacheKeyGenerator = class {
3860
- version;
3861
- constructor(version = "1.0") {
3862
- this.version = version;
3754
+ // src/utils/placeholderProcessor.ts
3755
+ var PlaceholderRegistry = class {
3756
+ static handlers = /* @__PURE__ */ new Map();
3757
+ /**
3758
+ * Register a placeholder handler
3759
+ */
3760
+ static register(name, handler) {
3761
+ this.handlers.set(name.toUpperCase(), handler);
3863
3762
  }
3864
3763
  /**
3865
- * Generate cache key for a component
3764
+ * Get a placeholder handler
3866
3765
  */
3867
- generateKey(component, context, options = {}) {
3868
- const parts = [
3869
- this.version,
3870
- component.name,
3871
- this.hashProps(component.props)
3872
- ];
3873
- if (options.includeTheme !== false) {
3874
- parts.push(context.theme.name);
3875
- }
3876
- if (options.includeContext) {
3877
- parts.push(this.hashContext(context));
3878
- }
3879
- if (options.additionalKeys) {
3880
- parts.push(...options.additionalKeys);
3881
- }
3882
- if (options.version) {
3883
- parts.push(options.version);
3884
- }
3885
- return parts.join(":");
3766
+ static get(name) {
3767
+ return this.handlers.get(name.toUpperCase());
3886
3768
  }
3887
3769
  /**
3888
- * Hash component props
3770
+ * Check if a placeholder is registered
3889
3771
  */
3890
- hashProps(props) {
3891
- if (!props) return "null";
3892
- const normalized = this.normalizeObject(props);
3893
- const json = JSON.stringify(normalized);
3894
- return this.hash(json);
3772
+ static has(name) {
3773
+ return this.handlers.has(name.toUpperCase());
3895
3774
  }
3896
3775
  /**
3897
- * Hash render context
3776
+ * Get all registered placeholder names
3898
3777
  */
3899
- hashContext(context) {
3900
- const relevant = {
3901
- theme: context.theme.name,
3902
- document: context.document
3903
- // Exclude runtime properties like sectionIndex, componentIndex
3904
- };
3905
- return this.hash(JSON.stringify(relevant));
3778
+ static getRegisteredNames() {
3779
+ return Array.from(this.handlers.keys());
3906
3780
  }
3907
3781
  /**
3908
- * Normalize object for consistent hashing
3782
+ * Clear all registered placeholders
3909
3783
  */
3910
- normalizeObject(obj) {
3911
- if (obj === null || obj === void 0) return obj;
3912
- if (Array.isArray(obj)) {
3913
- return obj.map((item) => this.normalizeObject(item));
3914
- }
3915
- if (obj instanceof Date) {
3916
- return obj.toISOString();
3784
+ static clear() {
3785
+ this.handlers.clear();
3786
+ }
3787
+ };
3788
+ function processTextWithPlaceholders(text, baseStyle = {}, context = {}) {
3789
+ const normalizedText = normalizeUnicodeText(text);
3790
+ const combinedRegex = /(\*\*\*|___)([\s\S]*?)\1|(\*\*|__)([\s\S]*?)\3|(\*|_)([\s\S]*?)\5|\{([^}]+)\}/g;
3791
+ const result = [];
3792
+ let lastIndex = 0;
3793
+ let match;
3794
+ while ((match = combinedRegex.exec(normalizedText)) !== null) {
3795
+ if (match.index > lastIndex) {
3796
+ const beforeText = normalizedText.substring(lastIndex, match.index);
3797
+ if (beforeText) {
3798
+ result.push(...createTextRunsWithNewlines2(beforeText, baseStyle));
3799
+ }
3917
3800
  }
3918
- if (typeof obj === "object") {
3919
- const sorted = {};
3920
- const keys = Object.keys(obj).sort();
3921
- for (const key of keys) {
3922
- sorted[key] = this.normalizeObject(obj[key]);
3801
+ if (match[7]) {
3802
+ const placeholderName = match[7];
3803
+ const handler = PlaceholderRegistry.get(placeholderName);
3804
+ if (handler) {
3805
+ const placeholderResult = handler({ ...context, style: baseStyle });
3806
+ if (Array.isArray(placeholderResult)) {
3807
+ result.push(...placeholderResult);
3808
+ } else if (placeholderResult instanceof TextRun2) {
3809
+ result.push(placeholderResult);
3810
+ } else if (typeof placeholderResult === "string") {
3811
+ result.push(
3812
+ ...createTextRunsWithNewlines2(placeholderResult, baseStyle)
3813
+ );
3814
+ }
3815
+ } else {
3816
+ result.push(...createTextRunsWithNewlines2(match[0], baseStyle));
3923
3817
  }
3924
- return sorted;
3818
+ } else {
3819
+ let decoratedText;
3820
+ let bold = baseStyle.bold || false;
3821
+ let italics = baseStyle.italics || false;
3822
+ if (match[1] === "***" || match[1] === "___") {
3823
+ decoratedText = match[2];
3824
+ bold = true;
3825
+ italics = true;
3826
+ } else if (match[3] === "**" || match[3] === "__") {
3827
+ decoratedText = match[4];
3828
+ bold = true;
3829
+ } else if (match[5] === "*" || match[5] === "_") {
3830
+ decoratedText = match[6];
3831
+ italics = true;
3832
+ } else {
3833
+ decoratedText = match[0];
3834
+ }
3835
+ const decoratedTextRuns = processTextWithPlaceholders(
3836
+ decoratedText,
3837
+ {
3838
+ ...baseStyle,
3839
+ bold,
3840
+ italics
3841
+ },
3842
+ context
3843
+ );
3844
+ result.push(...decoratedTextRuns);
3925
3845
  }
3926
- return obj;
3846
+ lastIndex = match.index + match[0].length;
3927
3847
  }
3928
- /**
3929
- * Create hash
3930
- */
3931
- hash(input) {
3932
- return createHash("sha256").update(input).digest("hex").substring(0, 16);
3848
+ if (lastIndex < normalizedText.length) {
3849
+ const remainingText = normalizedText.substring(lastIndex);
3850
+ if (remainingText) {
3851
+ result.push(...createTextRunsWithNewlines2(remainingText, baseStyle));
3852
+ }
3933
3853
  }
3934
- };
3854
+ if (result.length === 0 && normalizedText) {
3855
+ result.push(...createTextRunsWithNewlines2(normalizedText, baseStyle));
3856
+ }
3857
+ return result;
3858
+ }
3859
+ function createTextRunsWithNewlines2(text, baseStyle) {
3860
+ const runs = [];
3861
+ const lines = text.split("\n");
3862
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
3863
+ const line = lines[lineIndex];
3864
+ const needsLineBreak = lineIndex > 0;
3865
+ if (line || needsLineBreak) {
3866
+ runs.push(
3867
+ new TextRun2({
3868
+ text: line,
3869
+ font: baseStyle.font,
3870
+ size: baseStyle.size,
3871
+ color: baseStyle.color,
3872
+ bold: baseStyle.bold,
3873
+ italics: baseStyle.italics,
3874
+ underline: baseStyle.underline,
3875
+ break: needsLineBreak ? 1 : void 0
3876
+ })
3877
+ );
3878
+ }
3879
+ }
3880
+ return runs;
3881
+ }
3882
+ function initializeBuiltinPlaceholders() {
3883
+ PlaceholderRegistry.register("PAGE", (context) => {
3884
+ return new TextRun2({
3885
+ children: [PageNumber.CURRENT],
3886
+ font: context?.style?.font,
3887
+ size: context?.style?.size,
3888
+ color: context?.style?.color,
3889
+ bold: context?.style?.bold,
3890
+ italics: context?.style?.italics,
3891
+ underline: context?.style?.underline
3892
+ });
3893
+ });
3894
+ PlaceholderRegistry.register("TOTAL_PAGES", (context) => {
3895
+ return new TextRun2({
3896
+ children: [PageNumber.TOTAL_PAGES],
3897
+ font: context?.style?.font,
3898
+ size: context?.style?.size,
3899
+ color: context?.style?.color,
3900
+ bold: context?.style?.bold,
3901
+ italics: context?.style?.italics,
3902
+ underline: context?.style?.underline
3903
+ });
3904
+ });
3905
+ PlaceholderRegistry.register("DATE", (context) => {
3906
+ const today = /* @__PURE__ */ new Date();
3907
+ const dateString = today.toLocaleDateString();
3908
+ return new TextRun2({
3909
+ text: dateString,
3910
+ font: context?.style?.font,
3911
+ size: context?.style?.size,
3912
+ color: context?.style?.color,
3913
+ bold: context?.style?.bold,
3914
+ italics: context?.style?.italics,
3915
+ underline: context?.style?.underline
3916
+ });
3917
+ });
3918
+ PlaceholderRegistry.register("DATETIME", (context) => {
3919
+ const now = /* @__PURE__ */ new Date();
3920
+ const dateTimeString = now.toLocaleString();
3921
+ return new TextRun2({
3922
+ text: dateTimeString,
3923
+ font: context?.style?.font,
3924
+ size: context?.style?.size,
3925
+ color: context?.style?.color,
3926
+ bold: context?.style?.bold,
3927
+ italics: context?.style?.italics,
3928
+ underline: context?.style?.underline
3929
+ });
3930
+ });
3931
+ PlaceholderRegistry.register("YEAR", (context) => {
3932
+ const year = (/* @__PURE__ */ new Date()).getFullYear().toString();
3933
+ return new TextRun2({
3934
+ text: year,
3935
+ font: context?.style?.font,
3936
+ size: context?.style?.size,
3937
+ color: context?.style?.color,
3938
+ bold: context?.style?.bold,
3939
+ italics: context?.style?.italics,
3940
+ underline: context?.style?.underline
3941
+ });
3942
+ });
3943
+ }
3944
+ initializeBuiltinPlaceholders();
3935
3945
 
3936
3946
  // src/utils/revisionUtils.ts
3937
- import { TextRun as TextRun3, InsertedTextRun, DeletedTextRun } from "docx";
3938
3947
  var DEFAULT_REVISION_AUTHOR = "json-to-office";
3939
3948
  var DEFAULT_REVISION_DATE = "1970-01-01T00:00:00Z";
3940
3949
  var RevisionIdRegistry = class {
@@ -5017,10 +5026,10 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
5017
5026
  } else if (isImageComponent(cell)) {
5018
5027
  const imageComp = cell;
5019
5028
  try {
5020
- const imageSource = imageComp.props.base64 || imageComp.props.path;
5029
+ const imageSource = resolveImageSource(imageComp.props);
5021
5030
  if (!imageSource) {
5022
5031
  throw new Error(
5023
- 'Image component requires either "path" or "base64" property'
5032
+ 'Image component requires one of "path", "base64", or "svg" property'
5024
5033
  );
5025
5034
  }
5026
5035
  const imageResult = await getImageBuffer(imageSource);
@@ -5046,7 +5055,7 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
5046
5055
  });
5047
5056
  cellChildren = [imageRun];
5048
5057
  } catch (error) {
5049
- const imageSource = imageComp.props.base64 || imageComp.props.path || "unknown";
5058
+ const imageSource = imageComp.props.svg?.trim() ? "inline-svg" : imageComp.props.base64 || imageComp.props.path || "unknown";
5050
5059
  cellChildren = [
5051
5060
  new TextRun4({
5052
5061
  text: `[IMAGE: ${imageSource.substring(0, 50)}${imageSource.length > 50 ? "..." : ""}]`,
@@ -5723,10 +5732,10 @@ function renderListComponent(component, theme, themeName) {
5723
5732
  async function renderImageComponent(component, theme, themeName) {
5724
5733
  if (!isImageComponent(component)) return [];
5725
5734
  const resolvedConfig = component.props;
5726
- const imageSource = resolvedConfig.base64 || resolvedConfig.path;
5735
+ const imageSource = resolveImageSource(resolvedConfig);
5727
5736
  if (!imageSource) {
5728
5737
  throw new Error(
5729
- 'Image component requires either "path" or "base64" property'
5738
+ 'Image component requires one of "path", "base64", or "svg" property'
5730
5739
  );
5731
5740
  }
5732
5741
  return await createImage(imageSource, theme, themeName, {
@@ -6415,7 +6424,10 @@ async function generateChart(config, servicesConfig) {
6415
6424
  infile: config.options,
6416
6425
  type: "png",
6417
6426
  b64: true,
6418
- scale: config.scale
6427
+ scale: config.scale,
6428
+ // Forward resources verbatim only when present so the payload stays
6429
+ // byte-identical to before for callers that omit it.
6430
+ ...config.resources ? { resources: config.resources } : {}
6419
6431
  };
6420
6432
  const response = await postJsonToService({
6421
6433
  url: serverUrl,
@@ -6736,32 +6748,37 @@ async function renderHeaderFooterComponents(components, theme, themeName, contex
6736
6748
  for (const component of activeComponents) {
6737
6749
  if (isParagraphComponent(component)) {
6738
6750
  const textComp = component;
6751
+ const font = textComp.props.font;
6739
6752
  const normalStyle = getNormalStyle(theme);
6740
- const textStyle = {
6741
- font: textComp.props.font?.family || resolveFontFamily(theme, normalStyle.font) || getThemeFonts(theme).body.family,
6742
- size: (textComp.props.font?.size ?? normalStyle.size ?? 11) * 2,
6743
- // Convert to half-points
6744
- bold: textComp.props.font?.bold ?? false,
6745
- italics: textComp.props.font?.italic ?? false,
6746
- color: textComp.props.font?.color && resolveColor(textComp.props.font.color, theme) || normalStyle.color && resolveColor(normalStyle.color, theme) || getThemeColors(theme).textPrimary
6747
- };
6748
- const textRuns = parseTextWithDecorators(textComp.props.text, textStyle);
6749
6753
  elements.push(
6750
- new Paragraph7({
6751
- children: textRuns,
6752
- alignment: textComp.props.alignment ? getAlignment3(textComp.props.alignment) : void 0,
6753
- style: "Normal"
6754
+ createText(textComp.props.text, theme, themeName, {
6755
+ style: "Normal",
6756
+ alignment: textComp.props.alignment,
6757
+ // Resolve explicit run styling against the Normal style, preserving
6758
+ // prior header/footer rendering for these properties.
6759
+ fontFamily: font?.family || resolveFontFamily(theme, normalStyle.font) || getThemeFonts(theme).body.family,
6760
+ fontSize: font?.size ?? normalStyle.size ?? 11,
6761
+ // Pass the raw color/token; createText resolves it. Fall back to the
6762
+ // Normal style color, then the theme's primary text color.
6763
+ fontColor: font?.color || normalStyle.color || "textPrimary",
6764
+ bold: font?.bold ?? false,
6765
+ italic: font?.italic ?? false,
6766
+ underline: font?.underline,
6767
+ fontWeight: font?.fontWeight,
6768
+ boldColor: textComp.props.boldColor,
6769
+ spacing: textComp.props.spacing,
6770
+ lineSpacing: font?.lineSpacing
6754
6771
  })
6755
6772
  );
6756
6773
  } else if (isImageComponent(component)) {
6757
6774
  const imageComp = component;
6758
- let imageSource = imageComp.props.base64 || imageComp.props.path;
6775
+ let imageSource = resolveImageSource(imageComp.props);
6759
6776
  if (!imageSource) {
6760
6777
  elements.push(
6761
6778
  new Paragraph7({
6762
6779
  children: [
6763
6780
  new TextRun6({
6764
- text: "[IMAGE: Missing path or base64 property]",
6781
+ text: "[IMAGE: Missing path, base64, or svg property]",
6765
6782
  font: getThemeFonts(theme).body.family,
6766
6783
  size: 20,
6767
6784
  bold: true,
@@ -6835,15 +6852,16 @@ async function renderHeaderFooterComponents(components, theme, themeName, contex
6835
6852
  })
6836
6853
  );
6837
6854
  } catch (error) {
6855
+ const sourcePreview = imageSource.substring(0, 50);
6838
6856
  console.error(
6839
- `[Header/Footer Image Error] Failed to render image: ${imageComp.props.path?.substring(0, 50)}...`,
6857
+ `[Header/Footer Image Error] Failed to render image: ${sourcePreview}...`,
6840
6858
  error instanceof Error ? error.message : error
6841
6859
  );
6842
6860
  elements.push(
6843
6861
  new Paragraph7({
6844
6862
  children: [
6845
6863
  new TextRun6({
6846
- text: `[IMAGE: ${imageComp.props.path}]`,
6864
+ text: `[IMAGE: ${sourcePreview}]`,
6847
6865
  font: getThemeFonts(theme).body.family,
6848
6866
  size: 20,
6849
6867
  color: getThemeColors(theme).secondary,