@json-to-office/core-docx 0.5.0 → 0.7.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.
Files changed (38) hide show
  1. package/dist/components/highcharts.d.ts +2 -2
  2. package/dist/components/highcharts.d.ts.map +1 -1
  3. package/dist/components/image.d.ts +1 -1
  4. package/dist/components/image.d.ts.map +1 -1
  5. package/dist/components/index.d.ts +0 -2
  6. package/dist/components/index.d.ts.map +1 -1
  7. package/dist/components/text-box.d.ts.map +1 -1
  8. package/dist/core/content.d.ts +10 -10
  9. package/dist/core/content.d.ts.map +1 -1
  10. package/dist/core/generator.d.ts +2 -0
  11. package/dist/core/generator.d.ts.map +1 -1
  12. package/dist/core/render.d.ts +2 -0
  13. package/dist/core/render.d.ts.map +1 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +426 -437
  17. package/dist/index.js.map +1 -1
  18. package/dist/plugin/createDocumentGenerator.d.ts +3 -0
  19. package/dist/plugin/createDocumentGenerator.d.ts.map +1 -1
  20. package/dist/plugin/example/index.js +421 -431
  21. package/dist/plugin/example/index.js.map +1 -1
  22. package/dist/templates/documents/proposal.docx.json +3 -4
  23. package/dist/templates/documents/technical-guide.docx.json +3 -4
  24. package/dist/templates/themes/index.d.ts +32 -32
  25. package/dist/tsconfig.tsbuildinfo +1 -1
  26. package/dist/types/index.d.ts +5 -7
  27. package/dist/types/index.d.ts.map +1 -1
  28. package/dist/utils/docxImagePositioning.d.ts +2 -1
  29. package/dist/utils/docxImagePositioning.d.ts.map +1 -1
  30. package/dist/utils/imageUtils.d.ts +24 -4
  31. package/dist/utils/imageUtils.d.ts.map +1 -1
  32. package/dist/utils/widthUtils.d.ts +5 -0
  33. package/dist/utils/widthUtils.d.ts.map +1 -1
  34. package/package.json +3 -3
  35. package/dist/components/footer.d.ts +0 -12
  36. package/dist/components/footer.d.ts.map +0 -1
  37. package/dist/components/header.d.ts +0 -12
  38. package/dist/components/header.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -834,7 +834,7 @@ function convertLineSpacing(lineSpacing) {
834
834
  }
835
835
  return { line, lineRule };
836
836
  }
837
- var TWIPS_PER_POINT, SINGLE_LINE_SPACING_POINTS, SINGLE_LINE_SPACING_TWIPS, DOUBLE_LINE_SPACING_TWIPS, getBodyTextStyle;
837
+ var TWIPS_PER_POINT, SINGLE_LINE_SPACING_POINTS, SINGLE_LINE_SPACING_TWIPS, DOUBLE_LINE_SPACING_TWIPS;
838
838
  var init_styleHelpers = __esm({
839
839
  "src/styles/utils/styleHelpers.ts"() {
840
840
  "use strict";
@@ -845,22 +845,6 @@ var init_styleHelpers = __esm({
845
845
  SINGLE_LINE_SPACING_POINTS = 12;
846
846
  SINGLE_LINE_SPACING_TWIPS = SINGLE_LINE_SPACING_POINTS * TWIPS_PER_POINT;
847
847
  DOUBLE_LINE_SPACING_TWIPS = SINGLE_LINE_SPACING_TWIPS * 2;
848
- getBodyTextStyle = (theme, themeName) => {
849
- const themeConfig = resolveTheme(theme, themeName);
850
- if (themeConfig) {
851
- const normalStyle = getNormalStyle(themeConfig);
852
- return {
853
- size: (normalStyle.size || 11) * 2,
854
- font: resolveFontFamily(themeConfig, normalStyle.font),
855
- color: normalStyle.color ? resolveColor(normalStyle.color, themeConfig) : "000000"
856
- };
857
- }
858
- return {
859
- size: 20,
860
- font: "Arial",
861
- color: "000000"
862
- };
863
- };
864
848
  }
865
849
  });
866
850
 
@@ -1426,7 +1410,8 @@ __export(widthUtils_exports, {
1426
1410
  getPageHeightTwips: () => getPageHeightTwips,
1427
1411
  getPageWidthTwips: () => getPageWidthTwips,
1428
1412
  parsePercentageStringToFraction: () => parsePercentageStringToFraction,
1429
- relativeLengthToTwips: () => relativeLengthToTwips
1413
+ relativeLengthToTwips: () => relativeLengthToTwips,
1414
+ resolveOffsetTwips: () => resolveOffsetTwips
1430
1415
  });
1431
1416
  function getAvailableWidthTwips(theme, themeName) {
1432
1417
  const page = getPageSetup(theme, themeName);
@@ -1457,6 +1442,17 @@ function parsePercentageStringToFraction(value) {
1457
1442
  if (pct < 0 || pct > 100) return void 0;
1458
1443
  return pct / 100;
1459
1444
  }
1445
+ function resolveOffsetTwips(value, referenceTwips) {
1446
+ if (typeof value === "number") return value;
1447
+ const match = /^([0-9]+(?:\.[0-9]+)?)%$/.exec(value);
1448
+ if (!match) {
1449
+ console.warn(
1450
+ `resolveOffsetTwips: invalid percentage string "${value}", defaulting to 0`
1451
+ );
1452
+ return 0;
1453
+ }
1454
+ return Math.round(referenceTwips * parseFloat(match[1]) / 100);
1455
+ }
1460
1456
  function relativeLengthToTwips(value, availableWidthTwips) {
1461
1457
  if (typeof value === "number") {
1462
1458
  return pointsToTwips(value);
@@ -1473,240 +1469,6 @@ var init_widthUtils = __esm({
1473
1469
  }
1474
1470
  });
1475
1471
 
1476
- // src/utils/imageUtils.ts
1477
- var imageUtils_exports = {};
1478
- __export(imageUtils_exports, {
1479
- calculateImageDimensions: () => calculateImageDimensions,
1480
- calculateMissingDimension: () => calculateMissingDimension,
1481
- decodeBase64Image: () => decodeBase64Image,
1482
- detectImageType: () => detectImageType,
1483
- detectImageTypeFromExtension: () => detectImageTypeFromExtension,
1484
- detectImageTypeFromMimeType: () => detectImageTypeFromMimeType,
1485
- downloadImageFromUrl: () => downloadImageFromUrl,
1486
- extractMimeTypeFromDataUri: () => extractMimeTypeFromDataUri,
1487
- getImageBuffer: () => getImageBuffer,
1488
- getImageDimensions: () => getImageDimensions,
1489
- isBase64Image: () => isBase64Image,
1490
- isValidUrl: () => isValidUrl,
1491
- parseDimensionValue: () => parseDimensionValue,
1492
- parseWidthValue: () => parseWidthValue
1493
- });
1494
- import { readFileSync } from "fs";
1495
- import probe from "probe-image-size";
1496
- function parseWidthValue(width, availableWidthPx) {
1497
- if (typeof width === "number") {
1498
- return width;
1499
- }
1500
- const percentageMatch = width.match(/^(\d+(?:\.\d+)?)%$/);
1501
- if (percentageMatch) {
1502
- const pct = parseFloat(percentageMatch[1]);
1503
- if (pct < 0 || pct > 100) {
1504
- throw new Error(
1505
- `Invalid percentage value: ${width}. Must be between 0% and 100%`
1506
- );
1507
- }
1508
- const fraction = parsePercentageStringToFraction(width);
1509
- if (fraction !== void 0) {
1510
- return Math.round(availableWidthPx * fraction);
1511
- }
1512
- }
1513
- throw new Error(
1514
- `Invalid width value: ${width}. Expected number (pixels) or percentage string (e.g., "90%")`
1515
- );
1516
- }
1517
- function parseDimensionValue(value, availablePx) {
1518
- if (typeof value === "number") return value;
1519
- const percentageMatch = value.match(/^(\d+(?:\.\d+)?)%$/);
1520
- if (percentageMatch) {
1521
- const pct = parseFloat(percentageMatch[1]);
1522
- if (pct < 0 || pct > 100) {
1523
- throw new Error(
1524
- `Invalid percentage value: ${value}. Must be between 0% and 100%`
1525
- );
1526
- }
1527
- const fraction = parsePercentageStringToFraction(value);
1528
- if (fraction !== void 0) return Math.round(availablePx * fraction);
1529
- }
1530
- throw new Error(
1531
- `Invalid dimension value: ${value}. Expected number (pixels) or percentage string (e.g., "90%")`
1532
- );
1533
- }
1534
- function isValidUrl(string) {
1535
- try {
1536
- const url = new URL(string);
1537
- return url.protocol === "http:" || url.protocol === "https:";
1538
- } catch {
1539
- return false;
1540
- }
1541
- }
1542
- function isBase64Image(string) {
1543
- return /^data:image\/[a-zA-Z+]+;base64,/.test(string);
1544
- }
1545
- function decodeBase64Image(dataUri) {
1546
- try {
1547
- const base64Data = dataUri.split(",")[1];
1548
- if (!base64Data) {
1549
- throw new Error("Invalid base64 data URI format");
1550
- }
1551
- return Buffer.from(base64Data, "base64");
1552
- } catch (error) {
1553
- throw new Error(
1554
- `Failed to decode base64 image: ${error instanceof Error ? error.message : String(error)}`
1555
- );
1556
- }
1557
- }
1558
- function extractMimeTypeFromDataUri(dataUri) {
1559
- const match = dataUri.match(/^data:(image\/[a-zA-Z0-9+.-]+);base64,/);
1560
- return match ? match[1] : void 0;
1561
- }
1562
- function detectImageTypeFromExtension(path4) {
1563
- const extension = path4.toLowerCase().split(".").pop()?.split("?")[0];
1564
- switch (extension) {
1565
- case "jpg":
1566
- case "jpeg":
1567
- return "jpg";
1568
- case "png":
1569
- return "png";
1570
- case "gif":
1571
- return "gif";
1572
- case "bmp":
1573
- return "bmp";
1574
- case "svg":
1575
- return "svg";
1576
- default:
1577
- return void 0;
1578
- }
1579
- }
1580
- function detectImageTypeFromMimeType(mimeType) {
1581
- const normalized = mimeType.toLowerCase();
1582
- if (normalized.includes("svg")) return "svg";
1583
- if (normalized.includes("jpeg") || normalized.includes("jpg")) return "jpg";
1584
- if (normalized.includes("png")) return "png";
1585
- if (normalized.includes("gif")) return "gif";
1586
- if (normalized.includes("bmp")) return "bmp";
1587
- return void 0;
1588
- }
1589
- function detectImageType(imagePath) {
1590
- if (isBase64Image(imagePath)) {
1591
- const mimeType = extractMimeTypeFromDataUri(imagePath);
1592
- if (mimeType) {
1593
- const typeFromMime = detectImageTypeFromMimeType(mimeType);
1594
- if (typeFromMime) return typeFromMime;
1595
- }
1596
- }
1597
- const typeFromExtension = detectImageTypeFromExtension(imagePath);
1598
- if (typeFromExtension) return typeFromExtension;
1599
- return "png";
1600
- }
1601
- async function downloadImageFromUrl(url) {
1602
- try {
1603
- const controller = new AbortController();
1604
- const timeoutId = setTimeout(() => controller.abort(), 1e4);
1605
- const response = await fetch(url, {
1606
- signal: controller.signal,
1607
- headers: {
1608
- "User-Agent": "Mozilla/5.0 (compatible; json-to-docx/1.0)"
1609
- },
1610
- redirect: "follow"
1611
- // Automatically follow redirects (default behavior)
1612
- });
1613
- clearTimeout(timeoutId);
1614
- if (!response.ok) {
1615
- throw new Error(
1616
- `Failed to download image: HTTP ${response.status} ${response.statusText}`
1617
- );
1618
- }
1619
- const arrayBuffer = await response.arrayBuffer();
1620
- return Buffer.from(arrayBuffer);
1621
- } catch (error) {
1622
- if (error instanceof Error) {
1623
- if (error.name === "AbortError") {
1624
- throw new Error(
1625
- "Failed to download image: Request timeout after 10 seconds"
1626
- );
1627
- }
1628
- throw new Error(`Failed to download image from ${url}: ${error.message}`);
1629
- }
1630
- throw new Error(`Failed to download image from ${url}: Unknown error`);
1631
- }
1632
- }
1633
- async function getImageBuffer(imagePath) {
1634
- if (isBase64Image(imagePath)) {
1635
- return decodeBase64Image(imagePath);
1636
- }
1637
- if (isValidUrl(imagePath)) {
1638
- return await downloadImageFromUrl(imagePath);
1639
- }
1640
- return readFileSync(imagePath);
1641
- }
1642
- async function getImageDimensions(imagePath) {
1643
- try {
1644
- const imageBuffer = await getImageBuffer(imagePath);
1645
- const result = probe.sync(imageBuffer);
1646
- if (!result) {
1647
- throw new Error(`Unable to determine dimensions for image: ${imagePath}`);
1648
- }
1649
- return {
1650
- width: result.width,
1651
- height: result.height
1652
- };
1653
- } catch (error) {
1654
- throw new Error(
1655
- `Error reading image dimensions from ${imagePath}: ${error instanceof Error ? error.message : String(error)}`
1656
- );
1657
- }
1658
- }
1659
- function calculateMissingDimension(originalWidth, originalHeight, targetWidth, targetHeight) {
1660
- if (targetWidth && targetHeight) {
1661
- return { width: targetWidth, height: targetHeight };
1662
- }
1663
- const aspectRatio = originalWidth / originalHeight;
1664
- if (targetWidth && !targetHeight) {
1665
- return {
1666
- width: targetWidth,
1667
- height: Math.round(targetWidth / aspectRatio)
1668
- };
1669
- }
1670
- if (!targetWidth && targetHeight) {
1671
- return {
1672
- width: Math.round(targetHeight * aspectRatio),
1673
- height: targetHeight
1674
- };
1675
- }
1676
- return { width: originalWidth, height: originalHeight };
1677
- }
1678
- async function calculateImageDimensions(imagePath, targetWidth, targetHeight, fallbackWidth = 300, fallbackHeight = 180) {
1679
- try {
1680
- const originalDimensions = await getImageDimensions(imagePath);
1681
- return calculateMissingDimension(
1682
- originalDimensions.width,
1683
- originalDimensions.height,
1684
- targetWidth,
1685
- targetHeight
1686
- );
1687
- } catch (error) {
1688
- if (targetWidth && targetHeight) {
1689
- return { width: targetWidth, height: targetHeight };
1690
- }
1691
- if (targetWidth && !targetHeight) {
1692
- return { width: targetWidth, height: Math.round(targetWidth * 9 / 16) };
1693
- }
1694
- if (!targetWidth && targetHeight) {
1695
- return {
1696
- width: Math.round(targetHeight * 16 / 9),
1697
- height: targetHeight
1698
- };
1699
- }
1700
- return { width: fallbackWidth, height: fallbackHeight };
1701
- }
1702
- }
1703
- var init_imageUtils = __esm({
1704
- "src/utils/imageUtils.ts"() {
1705
- "use strict";
1706
- init_widthUtils();
1707
- }
1708
- });
1709
-
1710
1472
  // src/utils/docxImagePositioning.ts
1711
1473
  var docxImagePositioning_exports = {};
1712
1474
  __export(docxImagePositioning_exports, {
@@ -1815,13 +1577,17 @@ function mapWrapSide(side) {
1815
1577
  return void 0;
1816
1578
  }
1817
1579
  }
1818
- function mapFloatingOptions(floating) {
1580
+ function mapFloatingOptions(floating, theme, themeName) {
1819
1581
  if (!floating) return void 0;
1820
1582
  if (floating.wrap?.type === "tight") {
1821
1583
  throw new Error(
1822
1584
  "Image floating wrap.type 'tight' is not supported due to invalid OOXML emitted by docx. Use 'square', 'topAndBottom', or 'none'."
1823
1585
  );
1824
1586
  }
1587
+ const hRelative = floating.horizontalPosition?.relative;
1588
+ const vRelative = floating.verticalPosition?.relative;
1589
+ const hRef = hRelative && hRelative !== "page" ? getAvailableWidthTwips(theme, themeName) : getPageWidthTwips(theme, themeName);
1590
+ const vRef = vRelative && vRelative !== "page" ? getAvailableHeightTwips(theme, themeName) : getPageHeightTwips(theme, themeName);
1825
1591
  const hasHorizontal = Boolean(floating.horizontalPosition);
1826
1592
  const hasVertical = Boolean(floating.verticalPosition);
1827
1593
  const horizontalPosition = floating.horizontalPosition ? {
@@ -1832,7 +1598,7 @@ function mapFloatingOptions(floating) {
1832
1598
  align: mapHorizontalAlign(floating.horizontalPosition.align)
1833
1599
  },
1834
1600
  ...floating.horizontalPosition.offset !== void 0 && {
1835
- offset: floating.horizontalPosition.offset * TWIPS_TO_EMU
1601
+ offset: resolveOffsetTwips(floating.horizontalPosition.offset, hRef) * TWIPS_TO_EMU
1836
1602
  }
1837
1603
  } : hasVertical ? {
1838
1604
  // Default horizontal: align left relative to margin
@@ -1847,7 +1613,7 @@ function mapFloatingOptions(floating) {
1847
1613
  align: mapVerticalAlign(floating.verticalPosition.align)
1848
1614
  },
1849
1615
  ...floating.verticalPosition.offset !== void 0 && {
1850
- offset: floating.verticalPosition.offset * TWIPS_TO_EMU
1616
+ offset: resolveOffsetTwips(floating.verticalPosition.offset, vRef) * TWIPS_TO_EMU
1851
1617
  }
1852
1618
  } : hasHorizontal ? {
1853
1619
  // Default vertical: align top relative to paragraph
@@ -1858,19 +1624,21 @@ function mapFloatingOptions(floating) {
1858
1624
  ...floating.wrap.type && { type: mapWrapType(floating.wrap.type) },
1859
1625
  ...floating.wrap.side && { side: mapWrapSide(floating.wrap.side) }
1860
1626
  } : void 0;
1627
+ const pageW = getPageWidthTwips(theme, themeName);
1628
+ const pageH = getPageHeightTwips(theme, themeName);
1861
1629
  const rawMargins = floating.wrap?.margins || floating.margins;
1862
1630
  const margins = rawMargins ? {
1863
1631
  ...rawMargins.top !== void 0 && {
1864
- top: rawMargins.top * TWIPS_TO_EMU
1632
+ top: resolveOffsetTwips(rawMargins.top, pageH) * TWIPS_TO_EMU
1865
1633
  },
1866
1634
  ...rawMargins.bottom !== void 0 && {
1867
- bottom: rawMargins.bottom * TWIPS_TO_EMU
1635
+ bottom: resolveOffsetTwips(rawMargins.bottom, pageH) * TWIPS_TO_EMU
1868
1636
  },
1869
1637
  ...rawMargins.left !== void 0 && {
1870
- left: rawMargins.left * TWIPS_TO_EMU
1638
+ left: resolveOffsetTwips(rawMargins.left, pageW) * TWIPS_TO_EMU
1871
1639
  },
1872
1640
  ...rawMargins.right !== void 0 && {
1873
- right: rawMargins.right * TWIPS_TO_EMU
1641
+ right: resolveOffsetTwips(rawMargins.right, pageW) * TWIPS_TO_EMU
1874
1642
  }
1875
1643
  } : void 0;
1876
1644
  let zIndex = floating.zIndex !== void 0 ? floating.zIndex : 0;
@@ -1906,6 +1674,7 @@ var TWIPS_TO_EMU;
1906
1674
  var init_docxImagePositioning = __esm({
1907
1675
  "src/utils/docxImagePositioning.ts"() {
1908
1676
  "use strict";
1677
+ init_widthUtils();
1909
1678
  TWIPS_TO_EMU = 635;
1910
1679
  }
1911
1680
  });
@@ -2172,8 +1941,6 @@ import {
2172
1941
  isTableComponent,
2173
1942
  isListComponent,
2174
1943
  isTocComponent,
2175
- isHeaderComponent,
2176
- isFooterComponent,
2177
1944
  isHighchartsComponent
2178
1945
  } from "@json-to-office/shared-docx";
2179
1946
 
@@ -2650,18 +2417,251 @@ Suggestion: ${suggestion}`
2650
2417
  }
2651
2418
 
2652
2419
  // src/core/render.ts
2653
- init_imageUtils();
2654
- init_defaults();
2655
2420
  import {
2656
2421
  Document,
2657
- Paragraph as Paragraph9,
2658
- TextRun as TextRun7,
2659
- ImageRun as ImageRun2,
2660
- AlignmentType as AlignmentType6,
2422
+ Paragraph as Paragraph7,
2423
+ TextRun as TextRun5,
2424
+ AlignmentType as AlignmentType5,
2661
2425
  BookmarkStart as BookmarkStart2,
2662
2426
  BookmarkEnd as BookmarkEnd2
2663
2427
  } from "docx";
2664
2428
 
2429
+ // src/utils/imageUtils.ts
2430
+ init_widthUtils();
2431
+ import { readFileSync } from "fs";
2432
+ import probe from "probe-image-size";
2433
+ import { ImageRun } from "docx";
2434
+ function parseWidthValue(width, availableWidthPx) {
2435
+ if (typeof width === "number") {
2436
+ return width;
2437
+ }
2438
+ const percentageMatch = width.match(/^(\d+(?:\.\d+)?)%$/);
2439
+ if (percentageMatch) {
2440
+ const pct = parseFloat(percentageMatch[1]);
2441
+ if (pct < 0 || pct > 100) {
2442
+ throw new Error(
2443
+ `Invalid percentage value: ${width}. Must be between 0% and 100%`
2444
+ );
2445
+ }
2446
+ const fraction = parsePercentageStringToFraction(width);
2447
+ if (fraction !== void 0) {
2448
+ return Math.round(availableWidthPx * fraction);
2449
+ }
2450
+ }
2451
+ throw new Error(
2452
+ `Invalid width value: ${width}. Expected number (pixels) or percentage string (e.g., "90%")`
2453
+ );
2454
+ }
2455
+ function parseDimensionValue(value, availablePx) {
2456
+ if (typeof value === "number") return value;
2457
+ const percentageMatch = value.match(/^(\d+(?:\.\d+)?)%$/);
2458
+ if (percentageMatch) {
2459
+ const pct = parseFloat(percentageMatch[1]);
2460
+ if (pct < 0 || pct > 100) {
2461
+ throw new Error(
2462
+ `Invalid percentage value: ${value}. Must be between 0% and 100%`
2463
+ );
2464
+ }
2465
+ const fraction = parsePercentageStringToFraction(value);
2466
+ if (fraction !== void 0) return Math.round(availablePx * fraction);
2467
+ }
2468
+ throw new Error(
2469
+ `Invalid dimension value: ${value}. Expected number (pixels) or percentage string (e.g., "90%")`
2470
+ );
2471
+ }
2472
+ function isValidUrl(string) {
2473
+ try {
2474
+ const url = new URL(string);
2475
+ return url.protocol === "http:" || url.protocol === "https:";
2476
+ } catch {
2477
+ return false;
2478
+ }
2479
+ }
2480
+ function isBase64Image(string) {
2481
+ return /^data:image\/[a-zA-Z+]+;base64,/.test(string);
2482
+ }
2483
+ function decodeBase64Image(dataUri) {
2484
+ try {
2485
+ const base64Data = dataUri.split(",")[1];
2486
+ if (!base64Data) {
2487
+ throw new Error("Invalid base64 data URI format");
2488
+ }
2489
+ return Buffer.from(base64Data, "base64");
2490
+ } catch (error) {
2491
+ throw new Error(
2492
+ `Failed to decode base64 image: ${error instanceof Error ? error.message : String(error)}`
2493
+ );
2494
+ }
2495
+ }
2496
+ function extractMimeTypeFromDataUri(dataUri) {
2497
+ const match = dataUri.match(/^data:(image\/[a-zA-Z0-9+.-]+);base64,/);
2498
+ return match ? match[1] : void 0;
2499
+ }
2500
+ function detectImageTypeFromExtension(path4) {
2501
+ const extension = path4.toLowerCase().split(".").pop()?.split("?")[0];
2502
+ switch (extension) {
2503
+ case "jpg":
2504
+ case "jpeg":
2505
+ return "jpg";
2506
+ case "png":
2507
+ return "png";
2508
+ case "gif":
2509
+ return "gif";
2510
+ case "bmp":
2511
+ return "bmp";
2512
+ case "svg":
2513
+ return "svg";
2514
+ default:
2515
+ return void 0;
2516
+ }
2517
+ }
2518
+ function detectImageTypeFromMimeType(mimeType) {
2519
+ const normalized = mimeType.toLowerCase();
2520
+ if (normalized.includes("svg")) return "svg";
2521
+ if (normalized.includes("jpeg") || normalized.includes("jpg")) return "jpg";
2522
+ if (normalized.includes("png")) return "png";
2523
+ if (normalized.includes("gif")) return "gif";
2524
+ if (normalized.includes("bmp")) return "bmp";
2525
+ return void 0;
2526
+ }
2527
+ function detectImageType(imagePath, responseContentType) {
2528
+ if (responseContentType) {
2529
+ const typeFromResponse = detectImageTypeFromMimeType(responseContentType);
2530
+ if (typeFromResponse) return typeFromResponse;
2531
+ }
2532
+ if (isBase64Image(imagePath)) {
2533
+ const mimeType = extractMimeTypeFromDataUri(imagePath);
2534
+ if (mimeType) {
2535
+ const typeFromMime = detectImageTypeFromMimeType(mimeType);
2536
+ if (typeFromMime) return typeFromMime;
2537
+ }
2538
+ }
2539
+ const typeFromExtension = detectImageTypeFromExtension(imagePath);
2540
+ if (typeFromExtension) return typeFromExtension;
2541
+ return "png";
2542
+ }
2543
+ function createTypedImageRun(opts) {
2544
+ const base = {
2545
+ data: opts.data,
2546
+ transformation: opts.transformation,
2547
+ ...opts.floating && { floating: opts.floating }
2548
+ };
2549
+ if (opts.type === "svg") {
2550
+ return new ImageRun({
2551
+ type: "svg",
2552
+ ...base,
2553
+ fallback: { type: "png", data: opts.data }
2554
+ });
2555
+ }
2556
+ return new ImageRun({ type: opts.type, ...base });
2557
+ }
2558
+ async function downloadImageFromUrl(url) {
2559
+ try {
2560
+ const controller = new AbortController();
2561
+ const timeoutId = setTimeout(() => controller.abort(), 1e4);
2562
+ const response = await fetch(url, {
2563
+ signal: controller.signal,
2564
+ headers: {
2565
+ "User-Agent": "Mozilla/5.0 (compatible; json-to-docx/1.0)"
2566
+ },
2567
+ redirect: "follow"
2568
+ // Automatically follow redirects (default behavior)
2569
+ });
2570
+ clearTimeout(timeoutId);
2571
+ if (!response.ok) {
2572
+ throw new Error(
2573
+ `Failed to download image: HTTP ${response.status} ${response.statusText}`
2574
+ );
2575
+ }
2576
+ const contentType = response.headers.get("content-type") || void 0;
2577
+ const arrayBuffer = await response.arrayBuffer();
2578
+ return { buffer: Buffer.from(arrayBuffer), contentType };
2579
+ } catch (error) {
2580
+ if (error instanceof Error) {
2581
+ if (error.name === "AbortError") {
2582
+ throw new Error(
2583
+ "Failed to download image: Request timeout after 10 seconds"
2584
+ );
2585
+ }
2586
+ throw new Error(`Failed to download image from ${url}: ${error.message}`);
2587
+ }
2588
+ throw new Error(`Failed to download image from ${url}: Unknown error`);
2589
+ }
2590
+ }
2591
+ async function getImageBuffer(imagePath) {
2592
+ if (isBase64Image(imagePath)) {
2593
+ return { buffer: decodeBase64Image(imagePath) };
2594
+ }
2595
+ if (isValidUrl(imagePath)) {
2596
+ return await downloadImageFromUrl(imagePath);
2597
+ }
2598
+ return { buffer: readFileSync(imagePath) };
2599
+ }
2600
+ async function getImageDimensions(imagePath) {
2601
+ try {
2602
+ const { buffer: imageBuffer } = await getImageBuffer(imagePath);
2603
+ const result = probe.sync(imageBuffer);
2604
+ if (!result) {
2605
+ throw new Error(`Unable to determine dimensions for image: ${imagePath}`);
2606
+ }
2607
+ return {
2608
+ width: result.width,
2609
+ height: result.height
2610
+ };
2611
+ } catch (error) {
2612
+ throw new Error(
2613
+ `Error reading image dimensions from ${imagePath}: ${error instanceof Error ? error.message : String(error)}`
2614
+ );
2615
+ }
2616
+ }
2617
+ function calculateMissingDimension(originalWidth, originalHeight, targetWidth, targetHeight) {
2618
+ if (targetWidth && targetHeight) {
2619
+ return { width: targetWidth, height: targetHeight };
2620
+ }
2621
+ const aspectRatio = originalWidth / originalHeight;
2622
+ if (targetWidth && !targetHeight) {
2623
+ return {
2624
+ width: targetWidth,
2625
+ height: Math.round(targetWidth / aspectRatio)
2626
+ };
2627
+ }
2628
+ if (!targetWidth && targetHeight) {
2629
+ return {
2630
+ width: Math.round(targetHeight * aspectRatio),
2631
+ height: targetHeight
2632
+ };
2633
+ }
2634
+ return { width: originalWidth, height: originalHeight };
2635
+ }
2636
+ async function calculateImageDimensions(imagePath, targetWidth, targetHeight, fallbackWidth = 300, fallbackHeight = 180) {
2637
+ try {
2638
+ const originalDimensions = await getImageDimensions(imagePath);
2639
+ return calculateMissingDimension(
2640
+ originalDimensions.width,
2641
+ originalDimensions.height,
2642
+ targetWidth,
2643
+ targetHeight
2644
+ );
2645
+ } catch (error) {
2646
+ if (targetWidth && targetHeight) {
2647
+ return { width: targetWidth, height: targetHeight };
2648
+ }
2649
+ if (targetWidth && !targetHeight) {
2650
+ return { width: targetWidth, height: Math.round(targetWidth * 9 / 16) };
2651
+ }
2652
+ if (!targetWidth && targetHeight) {
2653
+ return {
2654
+ width: Math.round(targetHeight * 16 / 9),
2655
+ height: targetHeight
2656
+ };
2657
+ }
2658
+ return { width: fallbackWidth, height: fallbackHeight };
2659
+ }
2660
+ }
2661
+
2662
+ // src/core/render.ts
2663
+ init_defaults();
2664
+
2665
2665
  // src/styles/themeToDocxAdapter.ts
2666
2666
  init_themes();
2667
2667
  init_colorUtils();
@@ -3941,9 +3941,6 @@ function resolveListProps(props, theme) {
3941
3941
  }
3942
3942
 
3943
3943
  // src/core/content.ts
3944
- init_imageUtils();
3945
- init_styles();
3946
- init_defaults();
3947
3944
  import {
3948
3945
  Paragraph,
3949
3946
  TextRun as TextRun3,
@@ -3951,7 +3948,6 @@ import {
3951
3948
  TableRow,
3952
3949
  TableCell,
3953
3950
  AlignmentType as AlignmentType2,
3954
- ImageRun,
3955
3951
  PageBreak,
3956
3952
  WidthType,
3957
3953
  BorderStyle as BorderStyle2,
@@ -3963,6 +3959,8 @@ import {
3963
3959
  VerticalAlign,
3964
3960
  Bookmark
3965
3961
  } from "docx";
3962
+ init_styles();
3963
+ init_defaults();
3966
3964
 
3967
3965
  // src/utils/bookmarkRegistry.ts
3968
3966
  var BookmarkRegistry = class {
@@ -4037,7 +4035,8 @@ var globalBookmarkRegistry = new BookmarkRegistry();
4037
4035
  init_styleHelpers();
4038
4036
  init_colorUtils();
4039
4037
  init_styleHelpers();
4040
- function createText(content, theme, _themeName, options = {}) {
4038
+ init_widthUtils();
4039
+ function createText(content, theme, themeName, options = {}) {
4041
4040
  const normalizedContent = normalizeUnicodeText(content);
4042
4041
  const style = options.style || "Normal";
4043
4042
  const spacing = {};
@@ -4089,7 +4088,7 @@ function createText(content, theme, _themeName, options = {}) {
4089
4088
  children.push(...textRuns);
4090
4089
  }
4091
4090
  const isFloating = !!options.floating;
4092
- const frameOptions = isFloating && options.floating ? mapFrameOptions(options.floating) : void 0;
4091
+ const frameOptions = isFloating && options.floating ? mapFrameOptions(options.floating, theme, themeName) : void 0;
4093
4092
  return new Paragraph({
4094
4093
  children,
4095
4094
  style,
@@ -4103,7 +4102,7 @@ function createText(content, theme, _themeName, options = {}) {
4103
4102
  ...options.keepLines !== void 0 && { keepLines: options.keepLines }
4104
4103
  });
4105
4104
  }
4106
- function mapFrameOptions(floating) {
4105
+ function mapFrameOptions(floating, theme, themeName) {
4107
4106
  const hasHorizontalOffset = floating.horizontalPosition?.offset !== void 0;
4108
4107
  const hasVerticalOffset = floating.verticalPosition?.offset !== void 0;
4109
4108
  const hasHorizontalAlign = floating.horizontalPosition?.align !== void 0;
@@ -4128,8 +4127,21 @@ function mapFrameOptions(floating) {
4128
4127
  baseOptions.anchorLock = floating.anchorLock;
4129
4128
  }
4130
4129
  if (useAbsolute) {
4131
- const x = floating.horizontalPosition?.offset ?? 0;
4132
- const y = floating.verticalPosition?.offset ?? 0;
4130
+ const rawX = floating.horizontalPosition?.offset ?? 0;
4131
+ const rawY = floating.verticalPosition?.offset ?? 0;
4132
+ let x;
4133
+ let y;
4134
+ if (typeof rawX === "string" || typeof rawY === "string") {
4135
+ const hRelative = floating.horizontalPosition?.relative;
4136
+ const vRelative = floating.verticalPosition?.relative;
4137
+ const hRef = hRelative && hRelative !== "page" ? getAvailableWidthTwips(theme, themeName) : getPageWidthTwips(theme, themeName);
4138
+ const vRef = vRelative && vRelative !== "page" ? getAvailableHeightTwips(theme, themeName) : getPageHeightTwips(theme, themeName);
4139
+ x = resolveOffsetTwips(rawX, hRef);
4140
+ y = resolveOffsetTwips(rawY, vRef);
4141
+ } else {
4142
+ x = rawX;
4143
+ y = rawY;
4144
+ }
4133
4145
  return {
4134
4146
  type: "absolute",
4135
4147
  position: { x, y },
@@ -4227,14 +4239,17 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4227
4239
  ...options.keepLines !== void 0 && { keepLines: options.keepLines }
4228
4240
  });
4229
4241
  }
4230
- async function createImage(path4, theme, options = {}) {
4242
+ async function createImage(path4, theme, themeName, options = {}) {
4231
4243
  const elements = [];
4232
4244
  const isFloating = !!options.floating;
4233
4245
  const alignment = isFloating ? void 0 : getAlignment(options.alignment || "center");
4234
4246
  let imagePath = path4;
4235
4247
  let imageBuffer;
4248
+ let responseContentType;
4236
4249
  try {
4237
- imageBuffer = await getImageBuffer(imagePath);
4250
+ const imageResult = await getImageBuffer(imagePath);
4251
+ imageBuffer = imageResult.buffer;
4252
+ responseContentType = imageResult.contentType;
4238
4253
  const {
4239
4254
  getAvailableWidthTwips: getAvailableWidthTwips2,
4240
4255
  getPageWidthTwips: getPageWidthTwips2,
@@ -4264,29 +4279,12 @@ async function createImage(path4, theme, options = {}) {
4264
4279
  fallbackHeight
4265
4280
  );
4266
4281
  const { mapFloatingOptions: mapFloatingOptions2 } = await Promise.resolve().then(() => (init_docxImagePositioning(), docxImagePositioning_exports));
4267
- const floating = isFloating ? mapFloatingOptions2(options.floating) : void 0;
4268
- const { detectImageType: detectImageType2 } = await Promise.resolve().then(() => (init_imageUtils(), imageUtils_exports));
4269
- const imageType = detectImageType2(imagePath);
4270
- const imageRun = imageType === "svg" ? new ImageRun({
4271
- type: "svg",
4272
- data: imageBuffer,
4273
- fallback: {
4274
- type: "png",
4275
- data: imageBuffer
4276
- // Use the same buffer as fallback for now
4277
- },
4278
- transformation: {
4279
- width: dimensions.width,
4280
- height: dimensions.height
4281
- },
4282
- ...floating && { floating }
4283
- }) : new ImageRun({
4282
+ const floating = isFloating ? mapFloatingOptions2(options.floating, theme, themeName) : void 0;
4283
+ const imageType = detectImageType(imagePath, responseContentType);
4284
+ const imageRun = createTypedImageRun({
4284
4285
  type: imageType,
4285
4286
  data: imageBuffer,
4286
- transformation: {
4287
- width: dimensions.width,
4288
- height: dimensions.height
4289
- },
4287
+ transformation: { width: dimensions.width, height: dimensions.height },
4290
4288
  ...floating && { floating }
4291
4289
  });
4292
4290
  const spacing = {};
@@ -4410,7 +4408,7 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
4410
4408
  const numRows = columns[0]?.cells?.length || 0;
4411
4409
  const getDefaultCellDefaults = () => ({
4412
4410
  color: "000000",
4413
- backgroundColor: "FFFFFF",
4411
+ backgroundColor: "transparent",
4414
4412
  horizontalAlignment: "left",
4415
4413
  verticalAlignment: "top",
4416
4414
  font: {
@@ -4715,9 +4713,27 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
4715
4713
  if (typeof cell === "object" && "name" in cell && "props" in cell) {
4716
4714
  if (isParagraphComponent(cell)) {
4717
4715
  const textComp = cell;
4716
+ const paragraphFont = textComp.props.font;
4717
+ const paragraphStyle = {
4718
+ ...mergedStyle,
4719
+ ...paragraphFont?.family && { font: paragraphFont.family },
4720
+ ...paragraphFont?.size && { size: paragraphFont.size * 2 },
4721
+ ...paragraphFont?.bold !== void 0 && {
4722
+ bold: paragraphFont.bold
4723
+ },
4724
+ ...paragraphFont?.italic !== void 0 && {
4725
+ italics: paragraphFont.italic
4726
+ },
4727
+ ...paragraphFont?.underline !== void 0 && {
4728
+ underline: paragraphFont.underline ? { type: "single" } : void 0
4729
+ },
4730
+ ...paragraphFont?.color && {
4731
+ color: resolveColor(paragraphFont.color, theme)
4732
+ }
4733
+ };
4718
4734
  cellChildren = parseTextWithDecorators(
4719
4735
  textComp.props.text,
4720
- mergedStyle,
4736
+ paragraphStyle,
4721
4737
  { enableHyperlinks: true }
4722
4738
  );
4723
4739
  } else if (isImageComponent(cell)) {
@@ -4729,7 +4745,7 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
4729
4745
  'Image component requires either "path" or "base64" property'
4730
4746
  );
4731
4747
  }
4732
- const imageBuffer = await getImageBuffer(imageSource);
4748
+ const imageResult = await getImageBuffer(imageSource);
4733
4749
  const parsedWidth = typeof imageComp.props.width === "string" ? parseWidthValue(imageComp.props.width, 300) : imageComp.props.width;
4734
4750
  const parsedHeight = typeof imageComp.props.height === "string" ? parseWidthValue(imageComp.props.height, 200) : imageComp.props.height;
4735
4751
  const dimensions = await calculateImageDimensions(
@@ -4741,9 +4757,10 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
4741
4757
  20
4742
4758
  // fallback height
4743
4759
  );
4744
- const imageRun = new ImageRun({
4745
- type: "png",
4746
- data: imageBuffer,
4760
+ const imgType = detectImageType(imageSource, imageResult.contentType);
4761
+ const imageRun = createTypedImageRun({
4762
+ type: imgType,
4763
+ data: imageResult.buffer,
4747
4764
  transformation: {
4748
4765
  width: dimensions.width,
4749
4766
  height: dimensions.height
@@ -4847,8 +4864,10 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
4847
4864
  })
4848
4865
  ],
4849
4866
  verticalAlign: verticalAlignment,
4850
- shading: {
4851
- fill: mergedDefaults.backgroundColor || getThemeColors(theme).background
4867
+ ...mergedDefaults.backgroundColor !== "transparent" && {
4868
+ shading: {
4869
+ fill: mergedDefaults.backgroundColor
4870
+ }
4852
4871
  },
4853
4872
  margins: createMarginsFromPadding(mergedDefaults.padding),
4854
4873
  borders: {
@@ -5014,8 +5033,10 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
5014
5033
  })
5015
5034
  ],
5016
5035
  verticalAlign: verticalAlignment,
5017
- shading: {
5018
- fill: mergedDefaults.backgroundColor || getThemeColors(theme).background
5036
+ ...mergedDefaults.backgroundColor !== "transparent" && {
5037
+ shading: {
5038
+ fill: mergedDefaults.backgroundColor
5039
+ }
5019
5040
  },
5020
5041
  margins: createMarginsFromPadding(mergedDefaults.padding),
5021
5042
  borders: {
@@ -5419,7 +5440,7 @@ function renderListComponent(component, theme, themeName) {
5419
5440
  }
5420
5441
 
5421
5442
  // src/components/image.ts
5422
- async function renderImageComponent(component, theme) {
5443
+ async function renderImageComponent(component, theme, themeName) {
5423
5444
  if (!isImageComponent(component)) return [];
5424
5445
  const resolvedConfig = resolveImageProps(component.props, theme);
5425
5446
  const imageSource = resolvedConfig.base64 || resolvedConfig.path;
@@ -5428,7 +5449,7 @@ async function renderImageComponent(component, theme) {
5428
5449
  'Image component requires either "path" or "base64" property'
5429
5450
  );
5430
5451
  }
5431
- return await createImage(imageSource, theme, {
5452
+ return await createImage(imageSource, theme, themeName, {
5432
5453
  caption: resolvedConfig.caption,
5433
5454
  width: resolvedConfig.width,
5434
5455
  height: resolvedConfig.height,
@@ -5529,15 +5550,20 @@ function buildCellOptions(children, styleCfg, theme) {
5529
5550
  }
5530
5551
 
5531
5552
  // src/components/text-box.ts
5532
- function mapTableFloatOptions(floating) {
5553
+ init_widthUtils();
5554
+ function mapTableFloatOptions(floating, theme, themeName) {
5533
5555
  if (!floating) return void 0;
5534
5556
  const opt = {};
5557
+ const hRelative = floating.horizontalPosition?.relative;
5558
+ const vRelative = floating.verticalPosition?.relative;
5559
+ const hRef = hRelative && hRelative !== "page" ? getAvailableWidthTwips(theme, themeName) : getPageWidthTwips(theme, themeName);
5560
+ const vRef = vRelative && vRelative !== "page" ? getAvailableHeightTwips(theme, themeName) : getPageHeightTwips(theme, themeName);
5535
5561
  const hp = floating.horizontalPosition;
5536
5562
  if (hp?.relative) {
5537
5563
  opt.horizontalAnchor = hp.relative === "margin" ? TableAnchorType.MARGIN : hp.relative === "page" ? TableAnchorType.PAGE : TableAnchorType.TEXT;
5538
5564
  }
5539
5565
  if (hp?.offset !== void 0) {
5540
- opt.absoluteHorizontalPosition = hp.offset;
5566
+ opt.absoluteHorizontalPosition = resolveOffsetTwips(hp.offset, hRef);
5541
5567
  } else if (hp?.align) {
5542
5568
  const map = {
5543
5569
  left: RelativeHorizontalPosition.LEFT,
@@ -5553,7 +5579,7 @@ function mapTableFloatOptions(floating) {
5553
5579
  opt.verticalAnchor = vp.relative === "margin" ? TableAnchorType.MARGIN : vp.relative === "page" ? TableAnchorType.PAGE : TableAnchorType.TEXT;
5554
5580
  }
5555
5581
  if (vp?.offset !== void 0) {
5556
- opt.absoluteVerticalPosition = vp.offset;
5582
+ opt.absoluteVerticalPosition = resolveOffsetTwips(vp.offset, vRef);
5557
5583
  } else if (vp?.align) {
5558
5584
  const mapV = {
5559
5585
  top: RelativeVerticalPosition.TOP,
@@ -5564,12 +5590,17 @@ function mapTableFloatOptions(floating) {
5564
5590
  };
5565
5591
  opt.relativeVerticalPosition = mapV[vp.align];
5566
5592
  }
5593
+ const pageW = getPageWidthTwips(theme, themeName);
5594
+ const pageH = getPageHeightTwips(theme, themeName);
5567
5595
  const m = floating.wrap?.margins;
5568
5596
  if (m) {
5569
- if (m.top !== void 0) opt.topFromText = m.top;
5570
- if (m.right !== void 0) opt.rightFromText = m.right;
5571
- if (m.bottom !== void 0) opt.bottomFromText = m.bottom;
5572
- if (m.left !== void 0) opt.leftFromText = m.left;
5597
+ if (m.top !== void 0) opt.topFromText = resolveOffsetTwips(m.top, pageH);
5598
+ if (m.right !== void 0)
5599
+ opt.rightFromText = resolveOffsetTwips(m.right, pageW);
5600
+ if (m.bottom !== void 0)
5601
+ opt.bottomFromText = resolveOffsetTwips(m.bottom, pageH);
5602
+ if (m.left !== void 0)
5603
+ opt.leftFromText = resolveOffsetTwips(m.left, pageW);
5573
5604
  }
5574
5605
  opt.overlap = OverlapType.OVERLAP;
5575
5606
  return opt;
@@ -5625,7 +5656,7 @@ async function renderTextBoxComponent(component, theme, themeName, _context) {
5625
5656
  const row = new TableRow2({
5626
5657
  children: [new TableCell2(cellOpts)]
5627
5658
  });
5628
- const float = mapTableFloatOptions(tb.props.floating);
5659
+ const float = mapTableFloatOptions(tb.props.floating, theme, themeName);
5629
5660
  const PIXELS_TO_TWIPS = 15;
5630
5661
  const DEFAULT_WIDTH_TWIPS = 5e3;
5631
5662
  const rawWidth = tb.props.width ?? tb.props.floating?.width;
@@ -5662,7 +5693,7 @@ async function renderTableComponent(component, theme, themeName) {
5662
5693
  const rows = rawConfig.rows;
5663
5694
  const defaultCellDefaults = {
5664
5695
  color: "000000",
5665
- backgroundColor: "FFFFFF",
5696
+ backgroundColor: "transparent",
5666
5697
  horizontalAlignment: "left",
5667
5698
  verticalAlignment: "top",
5668
5699
  font: {
@@ -5898,77 +5929,12 @@ function renderStatisticComponent(component, theme) {
5898
5929
  );
5899
5930
  }
5900
5931
 
5901
- // src/components/header.ts
5902
- import { Paragraph as Paragraph6, TextRun as TextRun4 } from "docx";
5903
- init_styles();
5904
- init_defaults();
5905
-
5906
- // src/utils/alignmentUtils.ts
5907
- import { AlignmentType as AlignmentType4 } from "docx";
5908
- function getAlignment3(alignment) {
5909
- switch (alignment) {
5910
- case "center":
5911
- return AlignmentType4.CENTER;
5912
- case "right":
5913
- return AlignmentType4.RIGHT;
5914
- case "justify":
5915
- return AlignmentType4.JUSTIFIED;
5916
- default:
5917
- return AlignmentType4.LEFT;
5918
- }
5919
- }
5920
-
5921
- // src/components/header.ts
5922
- function renderHeaderComponent(component, theme, themeName) {
5923
- if (!isHeaderComponent(component)) return [];
5924
- const headerComp = component;
5925
- const bodyStyle = getBodyTextStyle(theme, themeName);
5926
- return [
5927
- new Paragraph6({
5928
- children: [
5929
- new TextRun4({
5930
- text: `[Header: ${headerComp.props.alignment || "center"}]`,
5931
- font: bodyStyle.font,
5932
- size: bodyStyle.size,
5933
- color: getThemeColors(theme).secondary
5934
- })
5935
- ],
5936
- alignment: getAlignment3(headerComp.props.alignment || "center"),
5937
- style: "Normal"
5938
- })
5939
- ];
5940
- }
5941
-
5942
- // src/components/footer.ts
5943
- import { Paragraph as Paragraph7, TextRun as TextRun5 } from "docx";
5944
- init_styles();
5945
- init_defaults();
5946
- function renderFooterComponent(component, theme, themeName) {
5947
- if (!isFooterComponent(component)) return [];
5948
- const footerComp = component;
5949
- const bodyStyle = getBodyTextStyle(theme, themeName);
5950
- return [
5951
- new Paragraph7({
5952
- children: [
5953
- new TextRun5({
5954
- text: `[Footer: ${footerComp.props.alignment || "center"}]`,
5955
- font: bodyStyle.font,
5956
- size: bodyStyle.size,
5957
- color: getThemeColors(theme).secondary
5958
- })
5959
- ],
5960
- alignment: getAlignment3(footerComp.props.alignment || "center"),
5961
- style: "Normal"
5962
- })
5963
- ];
5964
- }
5965
-
5966
5932
  // src/components/toc/index.ts
5967
5933
  import {
5968
- Paragraph as Paragraph8,
5934
+ Paragraph as Paragraph6,
5969
5935
  TableOfContents,
5970
- AlignmentType as AlignmentType5,
5971
- TextRun as TextRun6,
5936
+ AlignmentType as AlignmentType4,
5937
+ TextRun as TextRun4,
5972
5938
  StyleLevel
5973
5939
  } from "docx";
5974
5940
  function parseDepthRange(rawDepth, fieldName, defaultFrom = 1, defaultTo = 3) {
@@ -6021,9 +5987,9 @@ function renderTocComponent(component, theme, context) {
6021
5987
  const paragraphs = [];
6022
5988
  if (componentProps.title) {
6023
5989
  paragraphs.push(
6024
- new Paragraph8({
5990
+ new Paragraph6({
6025
5991
  children: [
6026
- new TextRun6({
5992
+ new TextRun4({
6027
5993
  text: componentProps.title,
6028
5994
  bold: true,
6029
5995
  size: 28
@@ -6036,7 +6002,7 @@ function renderTocComponent(component, theme, context) {
6036
6002
  // 180 ~= 9pt, provides clearer separation by default
6037
6003
  after: 180
6038
6004
  },
6039
- alignment: AlignmentType5.LEFT
6005
+ alignment: AlignmentType4.LEFT
6040
6006
  })
6041
6007
  );
6042
6008
  }
@@ -6112,27 +6078,33 @@ function hasNodeBuiltins() {
6112
6078
 
6113
6079
  // src/components/highcharts.ts
6114
6080
  var DEFAULT_EXPORT_SERVER_URL = "http://localhost:7801";
6115
- function getExportServerUrl(propsUrl) {
6116
- return propsUrl || process.env.HIGHCHARTS_SERVER_URL || DEFAULT_EXPORT_SERVER_URL;
6081
+ function getExportServerUrl(propsUrl, servicesUrl) {
6082
+ const raw = propsUrl || servicesUrl || DEFAULT_EXPORT_SERVER_URL;
6083
+ return raw.startsWith("http") ? raw : `http://${raw}`;
6117
6084
  }
6118
- async function generateChart(config) {
6085
+ async function generateChart(config, servicesConfig) {
6119
6086
  if (!isNodeEnvironment()) {
6120
6087
  throw new Error(
6121
6088
  "Highcharts export server requires a Node.js environment. Chart generation is not available in browser environments."
6122
6089
  );
6123
6090
  }
6124
- const serverUrl = getExportServerUrl(config.serverUrl);
6091
+ const serverUrl = getExportServerUrl(
6092
+ config.serverUrl,
6093
+ servicesConfig?.serverUrl
6094
+ );
6125
6095
  const requestBody = {
6126
6096
  infile: config.options,
6127
6097
  type: "png",
6128
6098
  b64: true,
6129
6099
  scale: config.scale
6130
6100
  };
6101
+ const headers = {
6102
+ "Content-Type": "application/json",
6103
+ ...servicesConfig?.headers
6104
+ };
6131
6105
  const response = await fetch(`${serverUrl}/export`, {
6132
6106
  method: "POST",
6133
- headers: {
6134
- "Content-Type": "application/json"
6135
- },
6107
+ headers,
6136
6108
  body: JSON.stringify(requestBody)
6137
6109
  }).catch((error) => {
6138
6110
  throw new Error(
@@ -6155,16 +6127,20 @@ Cause: ${error instanceof Error ? error.message : String(error)}`
6155
6127
  height
6156
6128
  };
6157
6129
  }
6158
- async function renderHighchartsComponent(component, theme, _themeName) {
6130
+ async function renderHighchartsComponent(component, theme, themeName, context) {
6159
6131
  if (!isHighchartsComponent(component)) return [];
6160
6132
  const config = component.props;
6161
- const chartResult = await generateChart(config);
6133
+ const chartResult = await generateChart(
6134
+ config,
6135
+ context?.services?.highcharts
6136
+ );
6162
6137
  const hasConfigDimensions = config.width !== void 0 || config.height !== void 0;
6163
6138
  const renderWidth = hasConfigDimensions ? config.width : chartResult.width;
6164
6139
  const renderHeight = hasConfigDimensions ? config.height : chartResult.height;
6165
6140
  const imageParagraphs = await createImage(
6166
6141
  chartResult.base64DataUri,
6167
6142
  theme,
6143
+ themeName,
6168
6144
  {
6169
6145
  width: renderWidth,
6170
6146
  height: renderHeight,
@@ -6227,16 +6203,16 @@ var textSpaceAfterComponent = createComponent({
6227
6203
 
6228
6204
  // src/core/render.ts
6229
6205
  init_docxImagePositioning();
6230
- function getAlignment4(alignment) {
6206
+ function getAlignment3(alignment) {
6231
6207
  switch (alignment) {
6232
6208
  case "center":
6233
- return AlignmentType6.CENTER;
6209
+ return AlignmentType5.CENTER;
6234
6210
  case "right":
6235
- return AlignmentType6.RIGHT;
6211
+ return AlignmentType5.RIGHT;
6236
6212
  case "justify":
6237
- return AlignmentType6.JUSTIFIED;
6213
+ return AlignmentType5.JUSTIFIED;
6238
6214
  default:
6239
- return AlignmentType6.LEFT;
6215
+ return AlignmentType5.LEFT;
6240
6216
  }
6241
6217
  }
6242
6218
  async function renderDocument(structure, layout, options) {
@@ -6253,6 +6229,7 @@ async function renderDocument(structure, layout, options) {
6253
6229
  structure.theme,
6254
6230
  structure.themeName
6255
6231
  );
6232
+ context.services = options?.services;
6256
6233
  let sectionBookmarkCounter = 0;
6257
6234
  let previousHeader = void 0;
6258
6235
  let previousFooter = void 0;
@@ -6327,7 +6304,7 @@ async function renderDocument(structure, layout, options) {
6327
6304
  }
6328
6305
  });
6329
6306
  }
6330
- async function renderHeaderFooterComponents(components, theme, _themeName, _context) {
6307
+ async function renderHeaderFooterComponents(components, theme, themeName, _context) {
6331
6308
  if (!components || components.length === 0) {
6332
6309
  return [];
6333
6310
  }
@@ -6349,9 +6326,9 @@ async function renderHeaderFooterComponents(components, theme, _themeName, _cont
6349
6326
  };
6350
6327
  const textRuns = parseTextWithDecorators(textComp.props.text, textStyle);
6351
6328
  elements.push(
6352
- new Paragraph9({
6329
+ new Paragraph7({
6353
6330
  children: textRuns,
6354
- alignment: textComp.props.alignment ? getAlignment4(textComp.props.alignment) : void 0,
6331
+ alignment: textComp.props.alignment ? getAlignment3(textComp.props.alignment) : void 0,
6355
6332
  style: "Normal"
6356
6333
  })
6357
6334
  );
@@ -6360,9 +6337,9 @@ async function renderHeaderFooterComponents(components, theme, _themeName, _cont
6360
6337
  let imageSource = imageComp.props.base64 || imageComp.props.path;
6361
6338
  if (!imageSource) {
6362
6339
  elements.push(
6363
- new Paragraph9({
6340
+ new Paragraph7({
6364
6341
  children: [
6365
- new TextRun7({
6342
+ new TextRun5({
6366
6343
  text: "[IMAGE: Missing path or base64 property]",
6367
6344
  font: getThemeFonts(theme).body.family,
6368
6345
  size: 20,
@@ -6370,15 +6347,18 @@ async function renderHeaderFooterComponents(components, theme, _themeName, _cont
6370
6347
  color: "#FF0000"
6371
6348
  })
6372
6349
  ],
6373
- alignment: imageComp.props.alignment ? getAlignment4(imageComp.props.alignment) : void 0,
6350
+ alignment: imageComp.props.alignment ? getAlignment3(imageComp.props.alignment) : void 0,
6374
6351
  style: "Normal"
6375
6352
  })
6376
6353
  );
6377
6354
  continue;
6378
6355
  }
6379
6356
  let imageBuffer;
6357
+ let responseContentType;
6380
6358
  try {
6381
- imageBuffer = await getImageBuffer(imageSource);
6359
+ const imageResult = await getImageBuffer(imageSource);
6360
+ imageBuffer = imageResult.buffer;
6361
+ responseContentType = imageResult.contentType;
6382
6362
  } catch (error) {
6383
6363
  throw new Error(
6384
6364
  `Failed to load image from ${imageSource.substring(0, 50)}`
@@ -6411,9 +6391,14 @@ async function renderHeaderFooterComponents(components, theme, _themeName, _cont
6411
6391
  referenceWidthPx,
6412
6392
  fallbackHeight
6413
6393
  );
6414
- const floatingOptions = mapFloatingOptions(imageComp.props.floating);
6415
- const imageRun = new ImageRun2({
6416
- type: "png",
6394
+ const floatingOptions = mapFloatingOptions(
6395
+ imageComp.props.floating,
6396
+ theme,
6397
+ themeName
6398
+ );
6399
+ const imageType = detectImageType(imageSource, responseContentType);
6400
+ const imageRun = createTypedImageRun({
6401
+ type: imageType,
6417
6402
  data: imageBuffer,
6418
6403
  transformation: {
6419
6404
  width: dimensions.width,
@@ -6422,9 +6407,9 @@ async function renderHeaderFooterComponents(components, theme, _themeName, _cont
6422
6407
  ...floatingOptions && { floating: floatingOptions }
6423
6408
  });
6424
6409
  elements.push(
6425
- new Paragraph9({
6410
+ new Paragraph7({
6426
6411
  children: [imageRun],
6427
- alignment: imageComp.props.alignment ? getAlignment4(imageComp.props.alignment) : void 0,
6412
+ alignment: imageComp.props.alignment ? getAlignment3(imageComp.props.alignment) : void 0,
6428
6413
  style: "Normal"
6429
6414
  })
6430
6415
  );
@@ -6434,9 +6419,9 @@ async function renderHeaderFooterComponents(components, theme, _themeName, _cont
6434
6419
  error instanceof Error ? error.message : error
6435
6420
  );
6436
6421
  elements.push(
6437
- new Paragraph9({
6422
+ new Paragraph7({
6438
6423
  children: [
6439
- new TextRun7({
6424
+ new TextRun5({
6440
6425
  text: `[IMAGE: ${imageComp.props.path}]`,
6441
6426
  font: getThemeFonts(theme).body.family,
6442
6427
  size: 20,
@@ -6449,7 +6434,7 @@ async function renderHeaderFooterComponents(components, theme, _themeName, _cont
6449
6434
  );
6450
6435
  }
6451
6436
  } else if (isTableComponent(component)) {
6452
- const tables = await renderTableComponent(component, theme, _themeName);
6437
+ const tables = await renderTableComponent(component, theme, themeName);
6453
6438
  elements.push(...tables);
6454
6439
  }
6455
6440
  }
@@ -6474,7 +6459,7 @@ async function renderSection(section, theme, themeName, context, sectionOrdinal,
6474
6459
  };
6475
6460
  if (sectionBookmarkId && isFirstLayoutOfUserSection && sharedLinkId !== void 0) {
6476
6461
  elements.push(
6477
- new Paragraph9({
6462
+ new Paragraph7({
6478
6463
  children: [new BookmarkStart2(sectionBookmarkId, sharedLinkId)],
6479
6464
  spacing: {
6480
6465
  before: 0,
@@ -6500,7 +6485,7 @@ async function renderSection(section, theme, themeName, context, sectionOrdinal,
6500
6485
  }
6501
6486
  if (closeBookmark && sharedLinkId !== void 0) {
6502
6487
  elements.push(
6503
- new Paragraph9({
6488
+ new Paragraph7({
6504
6489
  children: [new BookmarkEnd2(sharedLinkId)]
6505
6490
  })
6506
6491
  );
@@ -6553,23 +6538,24 @@ async function renderComponent(component, theme, themeName, context) {
6553
6538
  } else if (isColumnsComponent(component)) {
6554
6539
  return await renderColumnsComponent(component, theme, themeName, context);
6555
6540
  } else if (isImageComponent(component)) {
6556
- return await renderImageComponent(component, theme);
6541
+ return await renderImageComponent(component, theme, themeName);
6557
6542
  } else if (isTextBoxComponent(component)) {
6558
6543
  return await renderTextBoxComponent(component, theme, themeName, context);
6559
6544
  } else if (isStatisticComponent(component)) {
6560
6545
  return renderStatisticComponent(component, theme);
6561
6546
  } else if (isTableComponent(component)) {
6562
6547
  return await renderTableComponent(component, theme, themeName);
6563
- } else if (isHeaderComponent(component)) {
6564
- return renderHeaderComponent(component, theme, themeName);
6565
- } else if (isFooterComponent(component)) {
6566
- return renderFooterComponent(component, theme, themeName);
6567
6548
  } else if (isListComponent(component)) {
6568
6549
  return renderListComponent(component, theme, themeName);
6569
6550
  } else if (isTocComponent(component)) {
6570
6551
  return renderTocComponent(component, theme, context);
6571
6552
  } else if (isHighchartsComponent(component)) {
6572
- return await renderHighchartsComponent(component, theme, themeName);
6553
+ return await renderHighchartsComponent(
6554
+ component,
6555
+ theme,
6556
+ themeName,
6557
+ context
6558
+ );
6573
6559
  } else if (isSectionComponent(component)) {
6574
6560
  return await renderSectionComponent(component, theme, themeName, context);
6575
6561
  }
@@ -6762,7 +6748,7 @@ async function generateFromConfig(props, components) {
6762
6748
  };
6763
6749
  return await generateDocument(reportComponent);
6764
6750
  }
6765
- async function generateDocumentWithCustomThemes(document, customThemes) {
6751
+ async function generateDocumentWithCustomThemes(document, customThemes, services) {
6766
6752
  const themeName = document.props.theme || "minimal";
6767
6753
  let theme;
6768
6754
  if (customThemes) {
@@ -6785,8 +6771,8 @@ async function generateDocumentWithCustomThemes(document, customThemes) {
6785
6771
  const structure = await processDocument(document, theme, themeName);
6786
6772
  const layout = applyLayout(structure.sections, theme, themeName);
6787
6773
  const renderedDocument = await renderDocument(structure, layout, {
6788
- bypassCache: false
6789
- // Enable component caching
6774
+ bypassCache: false,
6775
+ services
6790
6776
  });
6791
6777
  return renderedDocument;
6792
6778
  }
@@ -6804,7 +6790,8 @@ async function generateDocumentFromJson(jsonConfig, options) {
6804
6790
  const [reportComponent] = normalizeDocument(componentToConvert);
6805
6791
  return await generateDocumentWithCustomThemes(
6806
6792
  reportComponent,
6807
- options?.customThemes
6793
+ options?.customThemes,
6794
+ options?.services
6808
6795
  );
6809
6796
  }
6810
6797
  function validateJsonSchema(jsonConfig) {
@@ -7434,7 +7421,8 @@ function createBuilderImpl(state) {
7434
7421
  theme: state.theme,
7435
7422
  customThemes: state.customThemes,
7436
7423
  debug: state.debug,
7437
- enableCache: state.enableCache
7424
+ enableCache: state.enableCache,
7425
+ services: state.services
7438
7426
  };
7439
7427
  return createBuilderImpl(
7440
7428
  newState
@@ -7466,7 +7454,9 @@ function createBuilderImpl(state) {
7466
7454
  themeName
7467
7455
  );
7468
7456
  const layout = applyLayout(structure.sections, docTheme, themeName);
7469
- const generatedDocument = await renderDocument(structure, layout);
7457
+ const generatedDocument = await renderDocument(structure, layout, {
7458
+ services: state.services
7459
+ });
7470
7460
  return {
7471
7461
  document: generatedDocument,
7472
7462
  warnings: warnings.length > 0 ? warnings : null
@@ -7581,7 +7571,8 @@ function createDocumentGenerator(options) {
7581
7571
  theme: options.theme,
7582
7572
  customThemes: options.customThemes,
7583
7573
  debug: options.debug ?? false,
7584
- enableCache: options.enableCache ?? false
7574
+ enableCache: options.enableCache ?? false,
7575
+ services: options.services
7585
7576
  };
7586
7577
  return createBuilderImpl(initialState);
7587
7578
  }
@@ -7638,8 +7629,6 @@ export {
7638
7629
  hasNodeBuiltins,
7639
7630
  initializeComponentCache,
7640
7631
  isColumnsComponent,
7641
- isFooterComponent,
7642
- isHeaderComponent,
7643
7632
  isHeadingComponent,
7644
7633
  isHighchartsComponent,
7645
7634
  isImageComponent,