@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
@@ -828,7 +828,7 @@ function convertLineSpacing(lineSpacing) {
828
828
  }
829
829
  return { line, lineRule };
830
830
  }
831
- var TWIPS_PER_POINT, SINGLE_LINE_SPACING_POINTS, SINGLE_LINE_SPACING_TWIPS, DOUBLE_LINE_SPACING_TWIPS, getBodyTextStyle;
831
+ var TWIPS_PER_POINT, SINGLE_LINE_SPACING_POINTS, SINGLE_LINE_SPACING_TWIPS, DOUBLE_LINE_SPACING_TWIPS;
832
832
  var init_styleHelpers = __esm({
833
833
  "src/styles/utils/styleHelpers.ts"() {
834
834
  "use strict";
@@ -839,22 +839,6 @@ var init_styleHelpers = __esm({
839
839
  SINGLE_LINE_SPACING_POINTS = 12;
840
840
  SINGLE_LINE_SPACING_TWIPS = SINGLE_LINE_SPACING_POINTS * TWIPS_PER_POINT;
841
841
  DOUBLE_LINE_SPACING_TWIPS = SINGLE_LINE_SPACING_TWIPS * 2;
842
- getBodyTextStyle = (theme, themeName) => {
843
- const themeConfig = resolveTheme(theme, themeName);
844
- if (themeConfig) {
845
- const normalStyle = getNormalStyle(themeConfig);
846
- return {
847
- size: (normalStyle.size || 11) * 2,
848
- font: resolveFontFamily(themeConfig, normalStyle.font),
849
- color: normalStyle.color ? resolveColor(normalStyle.color, themeConfig) : "000000"
850
- };
851
- }
852
- return {
853
- size: 20,
854
- font: "Arial",
855
- color: "000000"
856
- };
857
- };
858
842
  }
859
843
  });
860
844
 
@@ -1347,7 +1331,8 @@ __export(widthUtils_exports, {
1347
1331
  getPageHeightTwips: () => getPageHeightTwips,
1348
1332
  getPageWidthTwips: () => getPageWidthTwips,
1349
1333
  parsePercentageStringToFraction: () => parsePercentageStringToFraction,
1350
- relativeLengthToTwips: () => relativeLengthToTwips
1334
+ relativeLengthToTwips: () => relativeLengthToTwips,
1335
+ resolveOffsetTwips: () => resolveOffsetTwips
1351
1336
  });
1352
1337
  function getAvailableWidthTwips(theme, themeName) {
1353
1338
  const page = getPageSetup(theme, themeName);
@@ -1378,6 +1363,17 @@ function parsePercentageStringToFraction(value) {
1378
1363
  if (pct < 0 || pct > 100) return void 0;
1379
1364
  return pct / 100;
1380
1365
  }
1366
+ function resolveOffsetTwips(value, referenceTwips) {
1367
+ if (typeof value === "number") return value;
1368
+ const match = /^([0-9]+(?:\.[0-9]+)?)%$/.exec(value);
1369
+ if (!match) {
1370
+ console.warn(
1371
+ `resolveOffsetTwips: invalid percentage string "${value}", defaulting to 0`
1372
+ );
1373
+ return 0;
1374
+ }
1375
+ return Math.round(referenceTwips * parseFloat(match[1]) / 100);
1376
+ }
1381
1377
  function relativeLengthToTwips(value, availableWidthTwips) {
1382
1378
  if (typeof value === "number") {
1383
1379
  return pointsToTwips(value);
@@ -1394,240 +1390,6 @@ var init_widthUtils = __esm({
1394
1390
  }
1395
1391
  });
1396
1392
 
1397
- // src/utils/imageUtils.ts
1398
- var imageUtils_exports = {};
1399
- __export(imageUtils_exports, {
1400
- calculateImageDimensions: () => calculateImageDimensions,
1401
- calculateMissingDimension: () => calculateMissingDimension,
1402
- decodeBase64Image: () => decodeBase64Image,
1403
- detectImageType: () => detectImageType,
1404
- detectImageTypeFromExtension: () => detectImageTypeFromExtension,
1405
- detectImageTypeFromMimeType: () => detectImageTypeFromMimeType,
1406
- downloadImageFromUrl: () => downloadImageFromUrl,
1407
- extractMimeTypeFromDataUri: () => extractMimeTypeFromDataUri,
1408
- getImageBuffer: () => getImageBuffer,
1409
- getImageDimensions: () => getImageDimensions,
1410
- isBase64Image: () => isBase64Image,
1411
- isValidUrl: () => isValidUrl,
1412
- parseDimensionValue: () => parseDimensionValue,
1413
- parseWidthValue: () => parseWidthValue
1414
- });
1415
- import { readFileSync } from "fs";
1416
- import probe from "probe-image-size";
1417
- function parseWidthValue(width, availableWidthPx) {
1418
- if (typeof width === "number") {
1419
- return width;
1420
- }
1421
- const percentageMatch = width.match(/^(\d+(?:\.\d+)?)%$/);
1422
- if (percentageMatch) {
1423
- const pct = parseFloat(percentageMatch[1]);
1424
- if (pct < 0 || pct > 100) {
1425
- throw new Error(
1426
- `Invalid percentage value: ${width}. Must be between 0% and 100%`
1427
- );
1428
- }
1429
- const fraction = parsePercentageStringToFraction(width);
1430
- if (fraction !== void 0) {
1431
- return Math.round(availableWidthPx * fraction);
1432
- }
1433
- }
1434
- throw new Error(
1435
- `Invalid width value: ${width}. Expected number (pixels) or percentage string (e.g., "90%")`
1436
- );
1437
- }
1438
- function parseDimensionValue(value, availablePx) {
1439
- if (typeof value === "number") return value;
1440
- const percentageMatch = value.match(/^(\d+(?:\.\d+)?)%$/);
1441
- if (percentageMatch) {
1442
- const pct = parseFloat(percentageMatch[1]);
1443
- if (pct < 0 || pct > 100) {
1444
- throw new Error(
1445
- `Invalid percentage value: ${value}. Must be between 0% and 100%`
1446
- );
1447
- }
1448
- const fraction = parsePercentageStringToFraction(value);
1449
- if (fraction !== void 0) return Math.round(availablePx * fraction);
1450
- }
1451
- throw new Error(
1452
- `Invalid dimension value: ${value}. Expected number (pixels) or percentage string (e.g., "90%")`
1453
- );
1454
- }
1455
- function isValidUrl(string) {
1456
- try {
1457
- const url = new URL(string);
1458
- return url.protocol === "http:" || url.protocol === "https:";
1459
- } catch {
1460
- return false;
1461
- }
1462
- }
1463
- function isBase64Image(string) {
1464
- return /^data:image\/[a-zA-Z+]+;base64,/.test(string);
1465
- }
1466
- function decodeBase64Image(dataUri) {
1467
- try {
1468
- const base64Data = dataUri.split(",")[1];
1469
- if (!base64Data) {
1470
- throw new Error("Invalid base64 data URI format");
1471
- }
1472
- return Buffer.from(base64Data, "base64");
1473
- } catch (error) {
1474
- throw new Error(
1475
- `Failed to decode base64 image: ${error instanceof Error ? error.message : String(error)}`
1476
- );
1477
- }
1478
- }
1479
- function extractMimeTypeFromDataUri(dataUri) {
1480
- const match = dataUri.match(/^data:(image\/[a-zA-Z0-9+.-]+);base64,/);
1481
- return match ? match[1] : void 0;
1482
- }
1483
- function detectImageTypeFromExtension(path3) {
1484
- const extension = path3.toLowerCase().split(".").pop()?.split("?")[0];
1485
- switch (extension) {
1486
- case "jpg":
1487
- case "jpeg":
1488
- return "jpg";
1489
- case "png":
1490
- return "png";
1491
- case "gif":
1492
- return "gif";
1493
- case "bmp":
1494
- return "bmp";
1495
- case "svg":
1496
- return "svg";
1497
- default:
1498
- return void 0;
1499
- }
1500
- }
1501
- function detectImageTypeFromMimeType(mimeType) {
1502
- const normalized = mimeType.toLowerCase();
1503
- if (normalized.includes("svg")) return "svg";
1504
- if (normalized.includes("jpeg") || normalized.includes("jpg")) return "jpg";
1505
- if (normalized.includes("png")) return "png";
1506
- if (normalized.includes("gif")) return "gif";
1507
- if (normalized.includes("bmp")) return "bmp";
1508
- return void 0;
1509
- }
1510
- function detectImageType(imagePath) {
1511
- if (isBase64Image(imagePath)) {
1512
- const mimeType = extractMimeTypeFromDataUri(imagePath);
1513
- if (mimeType) {
1514
- const typeFromMime = detectImageTypeFromMimeType(mimeType);
1515
- if (typeFromMime) return typeFromMime;
1516
- }
1517
- }
1518
- const typeFromExtension = detectImageTypeFromExtension(imagePath);
1519
- if (typeFromExtension) return typeFromExtension;
1520
- return "png";
1521
- }
1522
- async function downloadImageFromUrl(url) {
1523
- try {
1524
- const controller = new AbortController();
1525
- const timeoutId = setTimeout(() => controller.abort(), 1e4);
1526
- const response = await fetch(url, {
1527
- signal: controller.signal,
1528
- headers: {
1529
- "User-Agent": "Mozilla/5.0 (compatible; json-to-docx/1.0)"
1530
- },
1531
- redirect: "follow"
1532
- // Automatically follow redirects (default behavior)
1533
- });
1534
- clearTimeout(timeoutId);
1535
- if (!response.ok) {
1536
- throw new Error(
1537
- `Failed to download image: HTTP ${response.status} ${response.statusText}`
1538
- );
1539
- }
1540
- const arrayBuffer = await response.arrayBuffer();
1541
- return Buffer.from(arrayBuffer);
1542
- } catch (error) {
1543
- if (error instanceof Error) {
1544
- if (error.name === "AbortError") {
1545
- throw new Error(
1546
- "Failed to download image: Request timeout after 10 seconds"
1547
- );
1548
- }
1549
- throw new Error(`Failed to download image from ${url}: ${error.message}`);
1550
- }
1551
- throw new Error(`Failed to download image from ${url}: Unknown error`);
1552
- }
1553
- }
1554
- async function getImageBuffer(imagePath) {
1555
- if (isBase64Image(imagePath)) {
1556
- return decodeBase64Image(imagePath);
1557
- }
1558
- if (isValidUrl(imagePath)) {
1559
- return await downloadImageFromUrl(imagePath);
1560
- }
1561
- return readFileSync(imagePath);
1562
- }
1563
- async function getImageDimensions(imagePath) {
1564
- try {
1565
- const imageBuffer = await getImageBuffer(imagePath);
1566
- const result = probe.sync(imageBuffer);
1567
- if (!result) {
1568
- throw new Error(`Unable to determine dimensions for image: ${imagePath}`);
1569
- }
1570
- return {
1571
- width: result.width,
1572
- height: result.height
1573
- };
1574
- } catch (error) {
1575
- throw new Error(
1576
- `Error reading image dimensions from ${imagePath}: ${error instanceof Error ? error.message : String(error)}`
1577
- );
1578
- }
1579
- }
1580
- function calculateMissingDimension(originalWidth, originalHeight, targetWidth, targetHeight) {
1581
- if (targetWidth && targetHeight) {
1582
- return { width: targetWidth, height: targetHeight };
1583
- }
1584
- const aspectRatio = originalWidth / originalHeight;
1585
- if (targetWidth && !targetHeight) {
1586
- return {
1587
- width: targetWidth,
1588
- height: Math.round(targetWidth / aspectRatio)
1589
- };
1590
- }
1591
- if (!targetWidth && targetHeight) {
1592
- return {
1593
- width: Math.round(targetHeight * aspectRatio),
1594
- height: targetHeight
1595
- };
1596
- }
1597
- return { width: originalWidth, height: originalHeight };
1598
- }
1599
- async function calculateImageDimensions(imagePath, targetWidth, targetHeight, fallbackWidth = 300, fallbackHeight = 180) {
1600
- try {
1601
- const originalDimensions = await getImageDimensions(imagePath);
1602
- return calculateMissingDimension(
1603
- originalDimensions.width,
1604
- originalDimensions.height,
1605
- targetWidth,
1606
- targetHeight
1607
- );
1608
- } catch (error) {
1609
- if (targetWidth && targetHeight) {
1610
- return { width: targetWidth, height: targetHeight };
1611
- }
1612
- if (targetWidth && !targetHeight) {
1613
- return { width: targetWidth, height: Math.round(targetWidth * 9 / 16) };
1614
- }
1615
- if (!targetWidth && targetHeight) {
1616
- return {
1617
- width: Math.round(targetHeight * 16 / 9),
1618
- height: targetHeight
1619
- };
1620
- }
1621
- return { width: fallbackWidth, height: fallbackHeight };
1622
- }
1623
- }
1624
- var init_imageUtils = __esm({
1625
- "src/utils/imageUtils.ts"() {
1626
- "use strict";
1627
- init_widthUtils();
1628
- }
1629
- });
1630
-
1631
1393
  // src/utils/docxImagePositioning.ts
1632
1394
  var docxImagePositioning_exports = {};
1633
1395
  __export(docxImagePositioning_exports, {
@@ -1736,13 +1498,17 @@ function mapWrapSide(side) {
1736
1498
  return void 0;
1737
1499
  }
1738
1500
  }
1739
- function mapFloatingOptions(floating) {
1501
+ function mapFloatingOptions(floating, theme, themeName) {
1740
1502
  if (!floating) return void 0;
1741
1503
  if (floating.wrap?.type === "tight") {
1742
1504
  throw new Error(
1743
1505
  "Image floating wrap.type 'tight' is not supported due to invalid OOXML emitted by docx. Use 'square', 'topAndBottom', or 'none'."
1744
1506
  );
1745
1507
  }
1508
+ const hRelative = floating.horizontalPosition?.relative;
1509
+ const vRelative = floating.verticalPosition?.relative;
1510
+ const hRef = hRelative && hRelative !== "page" ? getAvailableWidthTwips(theme, themeName) : getPageWidthTwips(theme, themeName);
1511
+ const vRef = vRelative && vRelative !== "page" ? getAvailableHeightTwips(theme, themeName) : getPageHeightTwips(theme, themeName);
1746
1512
  const hasHorizontal = Boolean(floating.horizontalPosition);
1747
1513
  const hasVertical = Boolean(floating.verticalPosition);
1748
1514
  const horizontalPosition = floating.horizontalPosition ? {
@@ -1753,7 +1519,7 @@ function mapFloatingOptions(floating) {
1753
1519
  align: mapHorizontalAlign(floating.horizontalPosition.align)
1754
1520
  },
1755
1521
  ...floating.horizontalPosition.offset !== void 0 && {
1756
- offset: floating.horizontalPosition.offset * TWIPS_TO_EMU
1522
+ offset: resolveOffsetTwips(floating.horizontalPosition.offset, hRef) * TWIPS_TO_EMU
1757
1523
  }
1758
1524
  } : hasVertical ? {
1759
1525
  // Default horizontal: align left relative to margin
@@ -1768,7 +1534,7 @@ function mapFloatingOptions(floating) {
1768
1534
  align: mapVerticalAlign(floating.verticalPosition.align)
1769
1535
  },
1770
1536
  ...floating.verticalPosition.offset !== void 0 && {
1771
- offset: floating.verticalPosition.offset * TWIPS_TO_EMU
1537
+ offset: resolveOffsetTwips(floating.verticalPosition.offset, vRef) * TWIPS_TO_EMU
1772
1538
  }
1773
1539
  } : hasHorizontal ? {
1774
1540
  // Default vertical: align top relative to paragraph
@@ -1779,19 +1545,21 @@ function mapFloatingOptions(floating) {
1779
1545
  ...floating.wrap.type && { type: mapWrapType(floating.wrap.type) },
1780
1546
  ...floating.wrap.side && { side: mapWrapSide(floating.wrap.side) }
1781
1547
  } : void 0;
1548
+ const pageW = getPageWidthTwips(theme, themeName);
1549
+ const pageH = getPageHeightTwips(theme, themeName);
1782
1550
  const rawMargins = floating.wrap?.margins || floating.margins;
1783
1551
  const margins = rawMargins ? {
1784
1552
  ...rawMargins.top !== void 0 && {
1785
- top: rawMargins.top * TWIPS_TO_EMU
1553
+ top: resolveOffsetTwips(rawMargins.top, pageH) * TWIPS_TO_EMU
1786
1554
  },
1787
1555
  ...rawMargins.bottom !== void 0 && {
1788
- bottom: rawMargins.bottom * TWIPS_TO_EMU
1556
+ bottom: resolveOffsetTwips(rawMargins.bottom, pageH) * TWIPS_TO_EMU
1789
1557
  },
1790
1558
  ...rawMargins.left !== void 0 && {
1791
- left: rawMargins.left * TWIPS_TO_EMU
1559
+ left: resolveOffsetTwips(rawMargins.left, pageW) * TWIPS_TO_EMU
1792
1560
  },
1793
1561
  ...rawMargins.right !== void 0 && {
1794
- right: rawMargins.right * TWIPS_TO_EMU
1562
+ right: resolveOffsetTwips(rawMargins.right, pageW) * TWIPS_TO_EMU
1795
1563
  }
1796
1564
  } : void 0;
1797
1565
  let zIndex = floating.zIndex !== void 0 ? floating.zIndex : 0;
@@ -1827,6 +1595,7 @@ var TWIPS_TO_EMU;
1827
1595
  var init_docxImagePositioning = __esm({
1828
1596
  "src/utils/docxImagePositioning.ts"() {
1829
1597
  "use strict";
1598
+ init_widthUtils();
1830
1599
  TWIPS_TO_EMU = 635;
1831
1600
  }
1832
1601
  });
@@ -2191,8 +1960,6 @@ import {
2191
1960
  isTableComponent,
2192
1961
  isListComponent,
2193
1962
  isTocComponent,
2194
- isHeaderComponent,
2195
- isFooterComponent,
2196
1963
  isHighchartsComponent
2197
1964
  } from "@json-to-office/shared-docx";
2198
1965
 
@@ -2666,18 +2433,251 @@ Suggestion: ${suggestion}`
2666
2433
  }
2667
2434
 
2668
2435
  // src/core/render.ts
2669
- init_imageUtils();
2670
- init_defaults();
2671
2436
  import {
2672
2437
  Document,
2673
- Paragraph as Paragraph9,
2674
- TextRun as TextRun7,
2675
- ImageRun as ImageRun2,
2676
- AlignmentType as AlignmentType6,
2438
+ Paragraph as Paragraph7,
2439
+ TextRun as TextRun5,
2440
+ AlignmentType as AlignmentType5,
2677
2441
  BookmarkStart as BookmarkStart2,
2678
2442
  BookmarkEnd as BookmarkEnd2
2679
2443
  } from "docx";
2680
2444
 
2445
+ // src/utils/imageUtils.ts
2446
+ init_widthUtils();
2447
+ import { readFileSync } from "fs";
2448
+ import probe from "probe-image-size";
2449
+ import { ImageRun } from "docx";
2450
+ function parseWidthValue(width, availableWidthPx) {
2451
+ if (typeof width === "number") {
2452
+ return width;
2453
+ }
2454
+ const percentageMatch = width.match(/^(\d+(?:\.\d+)?)%$/);
2455
+ if (percentageMatch) {
2456
+ const pct = parseFloat(percentageMatch[1]);
2457
+ if (pct < 0 || pct > 100) {
2458
+ throw new Error(
2459
+ `Invalid percentage value: ${width}. Must be between 0% and 100%`
2460
+ );
2461
+ }
2462
+ const fraction = parsePercentageStringToFraction(width);
2463
+ if (fraction !== void 0) {
2464
+ return Math.round(availableWidthPx * fraction);
2465
+ }
2466
+ }
2467
+ throw new Error(
2468
+ `Invalid width value: ${width}. Expected number (pixels) or percentage string (e.g., "90%")`
2469
+ );
2470
+ }
2471
+ function parseDimensionValue(value, availablePx) {
2472
+ if (typeof value === "number") return value;
2473
+ const percentageMatch = value.match(/^(\d+(?:\.\d+)?)%$/);
2474
+ if (percentageMatch) {
2475
+ const pct = parseFloat(percentageMatch[1]);
2476
+ if (pct < 0 || pct > 100) {
2477
+ throw new Error(
2478
+ `Invalid percentage value: ${value}. Must be between 0% and 100%`
2479
+ );
2480
+ }
2481
+ const fraction = parsePercentageStringToFraction(value);
2482
+ if (fraction !== void 0) return Math.round(availablePx * fraction);
2483
+ }
2484
+ throw new Error(
2485
+ `Invalid dimension value: ${value}. Expected number (pixels) or percentage string (e.g., "90%")`
2486
+ );
2487
+ }
2488
+ function isValidUrl(string) {
2489
+ try {
2490
+ const url = new URL(string);
2491
+ return url.protocol === "http:" || url.protocol === "https:";
2492
+ } catch {
2493
+ return false;
2494
+ }
2495
+ }
2496
+ function isBase64Image(string) {
2497
+ return /^data:image\/[a-zA-Z+]+;base64,/.test(string);
2498
+ }
2499
+ function decodeBase64Image(dataUri) {
2500
+ try {
2501
+ const base64Data = dataUri.split(",")[1];
2502
+ if (!base64Data) {
2503
+ throw new Error("Invalid base64 data URI format");
2504
+ }
2505
+ return Buffer.from(base64Data, "base64");
2506
+ } catch (error) {
2507
+ throw new Error(
2508
+ `Failed to decode base64 image: ${error instanceof Error ? error.message : String(error)}`
2509
+ );
2510
+ }
2511
+ }
2512
+ function extractMimeTypeFromDataUri(dataUri) {
2513
+ const match = dataUri.match(/^data:(image\/[a-zA-Z0-9+.-]+);base64,/);
2514
+ return match ? match[1] : void 0;
2515
+ }
2516
+ function detectImageTypeFromExtension(path3) {
2517
+ const extension = path3.toLowerCase().split(".").pop()?.split("?")[0];
2518
+ switch (extension) {
2519
+ case "jpg":
2520
+ case "jpeg":
2521
+ return "jpg";
2522
+ case "png":
2523
+ return "png";
2524
+ case "gif":
2525
+ return "gif";
2526
+ case "bmp":
2527
+ return "bmp";
2528
+ case "svg":
2529
+ return "svg";
2530
+ default:
2531
+ return void 0;
2532
+ }
2533
+ }
2534
+ function detectImageTypeFromMimeType(mimeType) {
2535
+ const normalized = mimeType.toLowerCase();
2536
+ if (normalized.includes("svg")) return "svg";
2537
+ if (normalized.includes("jpeg") || normalized.includes("jpg")) return "jpg";
2538
+ if (normalized.includes("png")) return "png";
2539
+ if (normalized.includes("gif")) return "gif";
2540
+ if (normalized.includes("bmp")) return "bmp";
2541
+ return void 0;
2542
+ }
2543
+ function detectImageType(imagePath, responseContentType) {
2544
+ if (responseContentType) {
2545
+ const typeFromResponse = detectImageTypeFromMimeType(responseContentType);
2546
+ if (typeFromResponse) return typeFromResponse;
2547
+ }
2548
+ if (isBase64Image(imagePath)) {
2549
+ const mimeType = extractMimeTypeFromDataUri(imagePath);
2550
+ if (mimeType) {
2551
+ const typeFromMime = detectImageTypeFromMimeType(mimeType);
2552
+ if (typeFromMime) return typeFromMime;
2553
+ }
2554
+ }
2555
+ const typeFromExtension = detectImageTypeFromExtension(imagePath);
2556
+ if (typeFromExtension) return typeFromExtension;
2557
+ return "png";
2558
+ }
2559
+ function createTypedImageRun(opts) {
2560
+ const base = {
2561
+ data: opts.data,
2562
+ transformation: opts.transformation,
2563
+ ...opts.floating && { floating: opts.floating }
2564
+ };
2565
+ if (opts.type === "svg") {
2566
+ return new ImageRun({
2567
+ type: "svg",
2568
+ ...base,
2569
+ fallback: { type: "png", data: opts.data }
2570
+ });
2571
+ }
2572
+ return new ImageRun({ type: opts.type, ...base });
2573
+ }
2574
+ async function downloadImageFromUrl(url) {
2575
+ try {
2576
+ const controller = new AbortController();
2577
+ const timeoutId = setTimeout(() => controller.abort(), 1e4);
2578
+ const response = await fetch(url, {
2579
+ signal: controller.signal,
2580
+ headers: {
2581
+ "User-Agent": "Mozilla/5.0 (compatible; json-to-docx/1.0)"
2582
+ },
2583
+ redirect: "follow"
2584
+ // Automatically follow redirects (default behavior)
2585
+ });
2586
+ clearTimeout(timeoutId);
2587
+ if (!response.ok) {
2588
+ throw new Error(
2589
+ `Failed to download image: HTTP ${response.status} ${response.statusText}`
2590
+ );
2591
+ }
2592
+ const contentType = response.headers.get("content-type") || void 0;
2593
+ const arrayBuffer = await response.arrayBuffer();
2594
+ return { buffer: Buffer.from(arrayBuffer), contentType };
2595
+ } catch (error) {
2596
+ if (error instanceof Error) {
2597
+ if (error.name === "AbortError") {
2598
+ throw new Error(
2599
+ "Failed to download image: Request timeout after 10 seconds"
2600
+ );
2601
+ }
2602
+ throw new Error(`Failed to download image from ${url}: ${error.message}`);
2603
+ }
2604
+ throw new Error(`Failed to download image from ${url}: Unknown error`);
2605
+ }
2606
+ }
2607
+ async function getImageBuffer(imagePath) {
2608
+ if (isBase64Image(imagePath)) {
2609
+ return { buffer: decodeBase64Image(imagePath) };
2610
+ }
2611
+ if (isValidUrl(imagePath)) {
2612
+ return await downloadImageFromUrl(imagePath);
2613
+ }
2614
+ return { buffer: readFileSync(imagePath) };
2615
+ }
2616
+ async function getImageDimensions(imagePath) {
2617
+ try {
2618
+ const { buffer: imageBuffer } = await getImageBuffer(imagePath);
2619
+ const result = probe.sync(imageBuffer);
2620
+ if (!result) {
2621
+ throw new Error(`Unable to determine dimensions for image: ${imagePath}`);
2622
+ }
2623
+ return {
2624
+ width: result.width,
2625
+ height: result.height
2626
+ };
2627
+ } catch (error) {
2628
+ throw new Error(
2629
+ `Error reading image dimensions from ${imagePath}: ${error instanceof Error ? error.message : String(error)}`
2630
+ );
2631
+ }
2632
+ }
2633
+ function calculateMissingDimension(originalWidth, originalHeight, targetWidth, targetHeight) {
2634
+ if (targetWidth && targetHeight) {
2635
+ return { width: targetWidth, height: targetHeight };
2636
+ }
2637
+ const aspectRatio = originalWidth / originalHeight;
2638
+ if (targetWidth && !targetHeight) {
2639
+ return {
2640
+ width: targetWidth,
2641
+ height: Math.round(targetWidth / aspectRatio)
2642
+ };
2643
+ }
2644
+ if (!targetWidth && targetHeight) {
2645
+ return {
2646
+ width: Math.round(targetHeight * aspectRatio),
2647
+ height: targetHeight
2648
+ };
2649
+ }
2650
+ return { width: originalWidth, height: originalHeight };
2651
+ }
2652
+ async function calculateImageDimensions(imagePath, targetWidth, targetHeight, fallbackWidth = 300, fallbackHeight = 180) {
2653
+ try {
2654
+ const originalDimensions = await getImageDimensions(imagePath);
2655
+ return calculateMissingDimension(
2656
+ originalDimensions.width,
2657
+ originalDimensions.height,
2658
+ targetWidth,
2659
+ targetHeight
2660
+ );
2661
+ } catch (error) {
2662
+ if (targetWidth && targetHeight) {
2663
+ return { width: targetWidth, height: targetHeight };
2664
+ }
2665
+ if (targetWidth && !targetHeight) {
2666
+ return { width: targetWidth, height: Math.round(targetWidth * 9 / 16) };
2667
+ }
2668
+ if (!targetWidth && targetHeight) {
2669
+ return {
2670
+ width: Math.round(targetHeight * 16 / 9),
2671
+ height: targetHeight
2672
+ };
2673
+ }
2674
+ return { width: fallbackWidth, height: fallbackHeight };
2675
+ }
2676
+ }
2677
+
2678
+ // src/core/render.ts
2679
+ init_defaults();
2680
+
2681
2681
  // src/styles/themeToDocxAdapter.ts
2682
2682
  init_themes();
2683
2683
  init_colorUtils();
@@ -3932,9 +3932,6 @@ function resolveListProps(props, theme) {
3932
3932
  }
3933
3933
 
3934
3934
  // src/core/content.ts
3935
- init_imageUtils();
3936
- init_styles();
3937
- init_defaults();
3938
3935
  import {
3939
3936
  Paragraph,
3940
3937
  TextRun as TextRun3,
@@ -3942,7 +3939,6 @@ import {
3942
3939
  TableRow,
3943
3940
  TableCell,
3944
3941
  AlignmentType as AlignmentType2,
3945
- ImageRun,
3946
3942
  PageBreak,
3947
3943
  WidthType,
3948
3944
  BorderStyle as BorderStyle2,
@@ -3954,6 +3950,8 @@ import {
3954
3950
  VerticalAlign,
3955
3951
  Bookmark
3956
3952
  } from "docx";
3953
+ init_styles();
3954
+ init_defaults();
3957
3955
 
3958
3956
  // src/utils/bookmarkRegistry.ts
3959
3957
  var BookmarkRegistry = class {
@@ -4028,7 +4026,8 @@ var globalBookmarkRegistry = new BookmarkRegistry();
4028
4026
  init_styleHelpers();
4029
4027
  init_colorUtils();
4030
4028
  init_styleHelpers();
4031
- function createText(content, theme, _themeName, options = {}) {
4029
+ init_widthUtils();
4030
+ function createText(content, theme, themeName, options = {}) {
4032
4031
  const normalizedContent = normalizeUnicodeText(content);
4033
4032
  const style = options.style || "Normal";
4034
4033
  const spacing = {};
@@ -4080,7 +4079,7 @@ function createText(content, theme, _themeName, options = {}) {
4080
4079
  children.push(...textRuns);
4081
4080
  }
4082
4081
  const isFloating = !!options.floating;
4083
- const frameOptions = isFloating && options.floating ? mapFrameOptions(options.floating) : void 0;
4082
+ const frameOptions = isFloating && options.floating ? mapFrameOptions(options.floating, theme, themeName) : void 0;
4084
4083
  return new Paragraph({
4085
4084
  children,
4086
4085
  style,
@@ -4094,7 +4093,7 @@ function createText(content, theme, _themeName, options = {}) {
4094
4093
  ...options.keepLines !== void 0 && { keepLines: options.keepLines }
4095
4094
  });
4096
4095
  }
4097
- function mapFrameOptions(floating) {
4096
+ function mapFrameOptions(floating, theme, themeName) {
4098
4097
  const hasHorizontalOffset = floating.horizontalPosition?.offset !== void 0;
4099
4098
  const hasVerticalOffset = floating.verticalPosition?.offset !== void 0;
4100
4099
  const hasHorizontalAlign = floating.horizontalPosition?.align !== void 0;
@@ -4119,8 +4118,21 @@ function mapFrameOptions(floating) {
4119
4118
  baseOptions.anchorLock = floating.anchorLock;
4120
4119
  }
4121
4120
  if (useAbsolute) {
4122
- const x = floating.horizontalPosition?.offset ?? 0;
4123
- const y = floating.verticalPosition?.offset ?? 0;
4121
+ const rawX = floating.horizontalPosition?.offset ?? 0;
4122
+ const rawY = floating.verticalPosition?.offset ?? 0;
4123
+ let x;
4124
+ let y;
4125
+ if (typeof rawX === "string" || typeof rawY === "string") {
4126
+ const hRelative = floating.horizontalPosition?.relative;
4127
+ const vRelative = floating.verticalPosition?.relative;
4128
+ const hRef = hRelative && hRelative !== "page" ? getAvailableWidthTwips(theme, themeName) : getPageWidthTwips(theme, themeName);
4129
+ const vRef = vRelative && vRelative !== "page" ? getAvailableHeightTwips(theme, themeName) : getPageHeightTwips(theme, themeName);
4130
+ x = resolveOffsetTwips(rawX, hRef);
4131
+ y = resolveOffsetTwips(rawY, vRef);
4132
+ } else {
4133
+ x = rawX;
4134
+ y = rawY;
4135
+ }
4124
4136
  return {
4125
4137
  type: "absolute",
4126
4138
  position: { x, y },
@@ -4218,14 +4230,17 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4218
4230
  ...options.keepLines !== void 0 && { keepLines: options.keepLines }
4219
4231
  });
4220
4232
  }
4221
- async function createImage(path3, theme, options = {}) {
4233
+ async function createImage(path3, theme, themeName, options = {}) {
4222
4234
  const elements = [];
4223
4235
  const isFloating = !!options.floating;
4224
4236
  const alignment = isFloating ? void 0 : getAlignment(options.alignment || "center");
4225
4237
  let imagePath = path3;
4226
4238
  let imageBuffer;
4239
+ let responseContentType;
4227
4240
  try {
4228
- imageBuffer = await getImageBuffer(imagePath);
4241
+ const imageResult = await getImageBuffer(imagePath);
4242
+ imageBuffer = imageResult.buffer;
4243
+ responseContentType = imageResult.contentType;
4229
4244
  const {
4230
4245
  getAvailableWidthTwips: getAvailableWidthTwips2,
4231
4246
  getPageWidthTwips: getPageWidthTwips2,
@@ -4255,29 +4270,12 @@ async function createImage(path3, theme, options = {}) {
4255
4270
  fallbackHeight
4256
4271
  );
4257
4272
  const { mapFloatingOptions: mapFloatingOptions2 } = await Promise.resolve().then(() => (init_docxImagePositioning(), docxImagePositioning_exports));
4258
- const floating = isFloating ? mapFloatingOptions2(options.floating) : void 0;
4259
- const { detectImageType: detectImageType2 } = await Promise.resolve().then(() => (init_imageUtils(), imageUtils_exports));
4260
- const imageType = detectImageType2(imagePath);
4261
- const imageRun = imageType === "svg" ? new ImageRun({
4262
- type: "svg",
4263
- data: imageBuffer,
4264
- fallback: {
4265
- type: "png",
4266
- data: imageBuffer
4267
- // Use the same buffer as fallback for now
4268
- },
4269
- transformation: {
4270
- width: dimensions.width,
4271
- height: dimensions.height
4272
- },
4273
- ...floating && { floating }
4274
- }) : new ImageRun({
4273
+ const floating = isFloating ? mapFloatingOptions2(options.floating, theme, themeName) : void 0;
4274
+ const imageType = detectImageType(imagePath, responseContentType);
4275
+ const imageRun = createTypedImageRun({
4275
4276
  type: imageType,
4276
4277
  data: imageBuffer,
4277
- transformation: {
4278
- width: dimensions.width,
4279
- height: dimensions.height
4280
- },
4278
+ transformation: { width: dimensions.width, height: dimensions.height },
4281
4279
  ...floating && { floating }
4282
4280
  });
4283
4281
  const spacing = {};
@@ -4401,7 +4399,7 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
4401
4399
  const numRows = columns[0]?.cells?.length || 0;
4402
4400
  const getDefaultCellDefaults = () => ({
4403
4401
  color: "000000",
4404
- backgroundColor: "FFFFFF",
4402
+ backgroundColor: "transparent",
4405
4403
  horizontalAlignment: "left",
4406
4404
  verticalAlignment: "top",
4407
4405
  font: {
@@ -4706,9 +4704,27 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
4706
4704
  if (typeof cell === "object" && "name" in cell && "props" in cell) {
4707
4705
  if (isParagraphComponent(cell)) {
4708
4706
  const textComp = cell;
4707
+ const paragraphFont = textComp.props.font;
4708
+ const paragraphStyle = {
4709
+ ...mergedStyle,
4710
+ ...paragraphFont?.family && { font: paragraphFont.family },
4711
+ ...paragraphFont?.size && { size: paragraphFont.size * 2 },
4712
+ ...paragraphFont?.bold !== void 0 && {
4713
+ bold: paragraphFont.bold
4714
+ },
4715
+ ...paragraphFont?.italic !== void 0 && {
4716
+ italics: paragraphFont.italic
4717
+ },
4718
+ ...paragraphFont?.underline !== void 0 && {
4719
+ underline: paragraphFont.underline ? { type: "single" } : void 0
4720
+ },
4721
+ ...paragraphFont?.color && {
4722
+ color: resolveColor(paragraphFont.color, theme)
4723
+ }
4724
+ };
4709
4725
  cellChildren = parseTextWithDecorators(
4710
4726
  textComp.props.text,
4711
- mergedStyle,
4727
+ paragraphStyle,
4712
4728
  { enableHyperlinks: true }
4713
4729
  );
4714
4730
  } else if (isImageComponent(cell)) {
@@ -4720,7 +4736,7 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
4720
4736
  'Image component requires either "path" or "base64" property'
4721
4737
  );
4722
4738
  }
4723
- const imageBuffer = await getImageBuffer(imageSource);
4739
+ const imageResult = await getImageBuffer(imageSource);
4724
4740
  const parsedWidth = typeof imageComp.props.width === "string" ? parseWidthValue(imageComp.props.width, 300) : imageComp.props.width;
4725
4741
  const parsedHeight = typeof imageComp.props.height === "string" ? parseWidthValue(imageComp.props.height, 200) : imageComp.props.height;
4726
4742
  const dimensions = await calculateImageDimensions(
@@ -4732,9 +4748,10 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
4732
4748
  20
4733
4749
  // fallback height
4734
4750
  );
4735
- const imageRun = new ImageRun({
4736
- type: "png",
4737
- data: imageBuffer,
4751
+ const imgType = detectImageType(imageSource, imageResult.contentType);
4752
+ const imageRun = createTypedImageRun({
4753
+ type: imgType,
4754
+ data: imageResult.buffer,
4738
4755
  transformation: {
4739
4756
  width: dimensions.width,
4740
4757
  height: dimensions.height
@@ -4838,8 +4855,10 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
4838
4855
  })
4839
4856
  ],
4840
4857
  verticalAlign: verticalAlignment,
4841
- shading: {
4842
- fill: mergedDefaults.backgroundColor || getThemeColors(theme).background
4858
+ ...mergedDefaults.backgroundColor !== "transparent" && {
4859
+ shading: {
4860
+ fill: mergedDefaults.backgroundColor
4861
+ }
4843
4862
  },
4844
4863
  margins: createMarginsFromPadding(mergedDefaults.padding),
4845
4864
  borders: {
@@ -5005,8 +5024,10 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
5005
5024
  })
5006
5025
  ],
5007
5026
  verticalAlign: verticalAlignment,
5008
- shading: {
5009
- fill: mergedDefaults.backgroundColor || getThemeColors(theme).background
5027
+ ...mergedDefaults.backgroundColor !== "transparent" && {
5028
+ shading: {
5029
+ fill: mergedDefaults.backgroundColor
5030
+ }
5010
5031
  },
5011
5032
  margins: createMarginsFromPadding(mergedDefaults.padding),
5012
5033
  borders: {
@@ -5410,7 +5431,7 @@ function renderListComponent(component, theme, themeName) {
5410
5431
  }
5411
5432
 
5412
5433
  // src/components/image.ts
5413
- async function renderImageComponent(component, theme) {
5434
+ async function renderImageComponent(component, theme, themeName) {
5414
5435
  if (!isImageComponent(component)) return [];
5415
5436
  const resolvedConfig = resolveImageProps(component.props, theme);
5416
5437
  const imageSource = resolvedConfig.base64 || resolvedConfig.path;
@@ -5419,7 +5440,7 @@ async function renderImageComponent(component, theme) {
5419
5440
  'Image component requires either "path" or "base64" property'
5420
5441
  );
5421
5442
  }
5422
- return await createImage(imageSource, theme, {
5443
+ return await createImage(imageSource, theme, themeName, {
5423
5444
  caption: resolvedConfig.caption,
5424
5445
  width: resolvedConfig.width,
5425
5446
  height: resolvedConfig.height,
@@ -5520,15 +5541,20 @@ function buildCellOptions(children, styleCfg, theme) {
5520
5541
  }
5521
5542
 
5522
5543
  // src/components/text-box.ts
5523
- function mapTableFloatOptions(floating) {
5544
+ init_widthUtils();
5545
+ function mapTableFloatOptions(floating, theme, themeName) {
5524
5546
  if (!floating) return void 0;
5525
5547
  const opt = {};
5548
+ const hRelative = floating.horizontalPosition?.relative;
5549
+ const vRelative = floating.verticalPosition?.relative;
5550
+ const hRef = hRelative && hRelative !== "page" ? getAvailableWidthTwips(theme, themeName) : getPageWidthTwips(theme, themeName);
5551
+ const vRef = vRelative && vRelative !== "page" ? getAvailableHeightTwips(theme, themeName) : getPageHeightTwips(theme, themeName);
5526
5552
  const hp = floating.horizontalPosition;
5527
5553
  if (hp?.relative) {
5528
5554
  opt.horizontalAnchor = hp.relative === "margin" ? TableAnchorType.MARGIN : hp.relative === "page" ? TableAnchorType.PAGE : TableAnchorType.TEXT;
5529
5555
  }
5530
5556
  if (hp?.offset !== void 0) {
5531
- opt.absoluteHorizontalPosition = hp.offset;
5557
+ opt.absoluteHorizontalPosition = resolveOffsetTwips(hp.offset, hRef);
5532
5558
  } else if (hp?.align) {
5533
5559
  const map = {
5534
5560
  left: RelativeHorizontalPosition.LEFT,
@@ -5544,7 +5570,7 @@ function mapTableFloatOptions(floating) {
5544
5570
  opt.verticalAnchor = vp.relative === "margin" ? TableAnchorType.MARGIN : vp.relative === "page" ? TableAnchorType.PAGE : TableAnchorType.TEXT;
5545
5571
  }
5546
5572
  if (vp?.offset !== void 0) {
5547
- opt.absoluteVerticalPosition = vp.offset;
5573
+ opt.absoluteVerticalPosition = resolveOffsetTwips(vp.offset, vRef);
5548
5574
  } else if (vp?.align) {
5549
5575
  const mapV = {
5550
5576
  top: RelativeVerticalPosition.TOP,
@@ -5555,12 +5581,17 @@ function mapTableFloatOptions(floating) {
5555
5581
  };
5556
5582
  opt.relativeVerticalPosition = mapV[vp.align];
5557
5583
  }
5584
+ const pageW = getPageWidthTwips(theme, themeName);
5585
+ const pageH = getPageHeightTwips(theme, themeName);
5558
5586
  const m = floating.wrap?.margins;
5559
5587
  if (m) {
5560
- if (m.top !== void 0) opt.topFromText = m.top;
5561
- if (m.right !== void 0) opt.rightFromText = m.right;
5562
- if (m.bottom !== void 0) opt.bottomFromText = m.bottom;
5563
- if (m.left !== void 0) opt.leftFromText = m.left;
5588
+ if (m.top !== void 0) opt.topFromText = resolveOffsetTwips(m.top, pageH);
5589
+ if (m.right !== void 0)
5590
+ opt.rightFromText = resolveOffsetTwips(m.right, pageW);
5591
+ if (m.bottom !== void 0)
5592
+ opt.bottomFromText = resolveOffsetTwips(m.bottom, pageH);
5593
+ if (m.left !== void 0)
5594
+ opt.leftFromText = resolveOffsetTwips(m.left, pageW);
5564
5595
  }
5565
5596
  opt.overlap = OverlapType.OVERLAP;
5566
5597
  return opt;
@@ -5616,7 +5647,7 @@ async function renderTextBoxComponent(component, theme, themeName, _context) {
5616
5647
  const row = new TableRow2({
5617
5648
  children: [new TableCell2(cellOpts)]
5618
5649
  });
5619
- const float = mapTableFloatOptions(tb.props.floating);
5650
+ const float = mapTableFloatOptions(tb.props.floating, theme, themeName);
5620
5651
  const PIXELS_TO_TWIPS = 15;
5621
5652
  const DEFAULT_WIDTH_TWIPS = 5e3;
5622
5653
  const rawWidth = tb.props.width ?? tb.props.floating?.width;
@@ -5653,7 +5684,7 @@ async function renderTableComponent(component, theme, themeName) {
5653
5684
  const rows = rawConfig.rows;
5654
5685
  const defaultCellDefaults = {
5655
5686
  color: "000000",
5656
- backgroundColor: "FFFFFF",
5687
+ backgroundColor: "transparent",
5657
5688
  horizontalAlignment: "left",
5658
5689
  verticalAlignment: "top",
5659
5690
  font: {
@@ -5889,77 +5920,12 @@ function renderStatisticComponent(component, theme) {
5889
5920
  );
5890
5921
  }
5891
5922
 
5892
- // src/components/header.ts
5893
- import { Paragraph as Paragraph6, TextRun as TextRun4 } from "docx";
5894
- init_styles();
5895
- init_defaults();
5896
-
5897
- // src/utils/alignmentUtils.ts
5898
- import { AlignmentType as AlignmentType4 } from "docx";
5899
- function getAlignment3(alignment) {
5900
- switch (alignment) {
5901
- case "center":
5902
- return AlignmentType4.CENTER;
5903
- case "right":
5904
- return AlignmentType4.RIGHT;
5905
- case "justify":
5906
- return AlignmentType4.JUSTIFIED;
5907
- default:
5908
- return AlignmentType4.LEFT;
5909
- }
5910
- }
5911
-
5912
- // src/components/header.ts
5913
- function renderHeaderComponent(component, theme, themeName) {
5914
- if (!isHeaderComponent(component)) return [];
5915
- const headerComp = component;
5916
- const bodyStyle = getBodyTextStyle(theme, themeName);
5917
- return [
5918
- new Paragraph6({
5919
- children: [
5920
- new TextRun4({
5921
- text: `[Header: ${headerComp.props.alignment || "center"}]`,
5922
- font: bodyStyle.font,
5923
- size: bodyStyle.size,
5924
- color: getThemeColors(theme).secondary
5925
- })
5926
- ],
5927
- alignment: getAlignment3(headerComp.props.alignment || "center"),
5928
- style: "Normal"
5929
- })
5930
- ];
5931
- }
5932
-
5933
- // src/components/footer.ts
5934
- import { Paragraph as Paragraph7, TextRun as TextRun5 } from "docx";
5935
- init_styles();
5936
- init_defaults();
5937
- function renderFooterComponent(component, theme, themeName) {
5938
- if (!isFooterComponent(component)) return [];
5939
- const footerComp = component;
5940
- const bodyStyle = getBodyTextStyle(theme, themeName);
5941
- return [
5942
- new Paragraph7({
5943
- children: [
5944
- new TextRun5({
5945
- text: `[Footer: ${footerComp.props.alignment || "center"}]`,
5946
- font: bodyStyle.font,
5947
- size: bodyStyle.size,
5948
- color: getThemeColors(theme).secondary
5949
- })
5950
- ],
5951
- alignment: getAlignment3(footerComp.props.alignment || "center"),
5952
- style: "Normal"
5953
- })
5954
- ];
5955
- }
5956
-
5957
5923
  // src/components/toc/index.ts
5958
5924
  import {
5959
- Paragraph as Paragraph8,
5925
+ Paragraph as Paragraph6,
5960
5926
  TableOfContents,
5961
- AlignmentType as AlignmentType5,
5962
- TextRun as TextRun6,
5927
+ AlignmentType as AlignmentType4,
5928
+ TextRun as TextRun4,
5963
5929
  StyleLevel
5964
5930
  } from "docx";
5965
5931
  function parseDepthRange(rawDepth, fieldName, defaultFrom = 1, defaultTo = 3) {
@@ -6012,9 +5978,9 @@ function renderTocComponent(component, theme, context) {
6012
5978
  const paragraphs = [];
6013
5979
  if (componentProps.title) {
6014
5980
  paragraphs.push(
6015
- new Paragraph8({
5981
+ new Paragraph6({
6016
5982
  children: [
6017
- new TextRun6({
5983
+ new TextRun4({
6018
5984
  text: componentProps.title,
6019
5985
  bold: true,
6020
5986
  size: 28
@@ -6027,7 +5993,7 @@ function renderTocComponent(component, theme, context) {
6027
5993
  // 180 ~= 9pt, provides clearer separation by default
6028
5994
  after: 180
6029
5995
  },
6030
- alignment: AlignmentType5.LEFT
5996
+ alignment: AlignmentType4.LEFT
6031
5997
  })
6032
5998
  );
6033
5999
  }
@@ -6096,27 +6062,33 @@ function isNodeEnvironment() {
6096
6062
 
6097
6063
  // src/components/highcharts.ts
6098
6064
  var DEFAULT_EXPORT_SERVER_URL = "http://localhost:7801";
6099
- function getExportServerUrl(propsUrl) {
6100
- return propsUrl || process.env.HIGHCHARTS_SERVER_URL || DEFAULT_EXPORT_SERVER_URL;
6065
+ function getExportServerUrl(propsUrl, servicesUrl) {
6066
+ const raw = propsUrl || servicesUrl || DEFAULT_EXPORT_SERVER_URL;
6067
+ return raw.startsWith("http") ? raw : `http://${raw}`;
6101
6068
  }
6102
- async function generateChart(config) {
6069
+ async function generateChart(config, servicesConfig) {
6103
6070
  if (!isNodeEnvironment()) {
6104
6071
  throw new Error(
6105
6072
  "Highcharts export server requires a Node.js environment. Chart generation is not available in browser environments."
6106
6073
  );
6107
6074
  }
6108
- const serverUrl = getExportServerUrl(config.serverUrl);
6075
+ const serverUrl = getExportServerUrl(
6076
+ config.serverUrl,
6077
+ servicesConfig?.serverUrl
6078
+ );
6109
6079
  const requestBody = {
6110
6080
  infile: config.options,
6111
6081
  type: "png",
6112
6082
  b64: true,
6113
6083
  scale: config.scale
6114
6084
  };
6085
+ const headers = {
6086
+ "Content-Type": "application/json",
6087
+ ...servicesConfig?.headers
6088
+ };
6115
6089
  const response = await fetch(`${serverUrl}/export`, {
6116
6090
  method: "POST",
6117
- headers: {
6118
- "Content-Type": "application/json"
6119
- },
6091
+ headers,
6120
6092
  body: JSON.stringify(requestBody)
6121
6093
  }).catch((error) => {
6122
6094
  throw new Error(
@@ -6139,16 +6111,20 @@ Cause: ${error instanceof Error ? error.message : String(error)}`
6139
6111
  height
6140
6112
  };
6141
6113
  }
6142
- async function renderHighchartsComponent(component, theme, _themeName) {
6114
+ async function renderHighchartsComponent(component, theme, themeName, context) {
6143
6115
  if (!isHighchartsComponent(component)) return [];
6144
6116
  const config = component.props;
6145
- const chartResult = await generateChart(config);
6117
+ const chartResult = await generateChart(
6118
+ config,
6119
+ context?.services?.highcharts
6120
+ );
6146
6121
  const hasConfigDimensions = config.width !== void 0 || config.height !== void 0;
6147
6122
  const renderWidth = hasConfigDimensions ? config.width : chartResult.width;
6148
6123
  const renderHeight = hasConfigDimensions ? config.height : chartResult.height;
6149
6124
  const imageParagraphs = await createImage(
6150
6125
  chartResult.base64DataUri,
6151
6126
  theme,
6127
+ themeName,
6152
6128
  {
6153
6129
  width: renderWidth,
6154
6130
  height: renderHeight,
@@ -6211,16 +6187,16 @@ var textSpaceAfterComponent = createComponent({
6211
6187
 
6212
6188
  // src/core/render.ts
6213
6189
  init_docxImagePositioning();
6214
- function getAlignment4(alignment) {
6190
+ function getAlignment3(alignment) {
6215
6191
  switch (alignment) {
6216
6192
  case "center":
6217
- return AlignmentType6.CENTER;
6193
+ return AlignmentType5.CENTER;
6218
6194
  case "right":
6219
- return AlignmentType6.RIGHT;
6195
+ return AlignmentType5.RIGHT;
6220
6196
  case "justify":
6221
- return AlignmentType6.JUSTIFIED;
6197
+ return AlignmentType5.JUSTIFIED;
6222
6198
  default:
6223
- return AlignmentType6.LEFT;
6199
+ return AlignmentType5.LEFT;
6224
6200
  }
6225
6201
  }
6226
6202
  async function renderDocument(structure, layout, options) {
@@ -6237,6 +6213,7 @@ async function renderDocument(structure, layout, options) {
6237
6213
  structure.theme,
6238
6214
  structure.themeName
6239
6215
  );
6216
+ context.services = options?.services;
6240
6217
  let sectionBookmarkCounter = 0;
6241
6218
  let previousHeader = void 0;
6242
6219
  let previousFooter = void 0;
@@ -6311,7 +6288,7 @@ async function renderDocument(structure, layout, options) {
6311
6288
  }
6312
6289
  });
6313
6290
  }
6314
- async function renderHeaderFooterComponents(components, theme, _themeName, _context) {
6291
+ async function renderHeaderFooterComponents(components, theme, themeName, _context) {
6315
6292
  if (!components || components.length === 0) {
6316
6293
  return [];
6317
6294
  }
@@ -6333,9 +6310,9 @@ async function renderHeaderFooterComponents(components, theme, _themeName, _cont
6333
6310
  };
6334
6311
  const textRuns = parseTextWithDecorators(textComp.props.text, textStyle);
6335
6312
  elements.push(
6336
- new Paragraph9({
6313
+ new Paragraph7({
6337
6314
  children: textRuns,
6338
- alignment: textComp.props.alignment ? getAlignment4(textComp.props.alignment) : void 0,
6315
+ alignment: textComp.props.alignment ? getAlignment3(textComp.props.alignment) : void 0,
6339
6316
  style: "Normal"
6340
6317
  })
6341
6318
  );
@@ -6344,9 +6321,9 @@ async function renderHeaderFooterComponents(components, theme, _themeName, _cont
6344
6321
  let imageSource = imageComp.props.base64 || imageComp.props.path;
6345
6322
  if (!imageSource) {
6346
6323
  elements.push(
6347
- new Paragraph9({
6324
+ new Paragraph7({
6348
6325
  children: [
6349
- new TextRun7({
6326
+ new TextRun5({
6350
6327
  text: "[IMAGE: Missing path or base64 property]",
6351
6328
  font: getThemeFonts(theme).body.family,
6352
6329
  size: 20,
@@ -6354,15 +6331,18 @@ async function renderHeaderFooterComponents(components, theme, _themeName, _cont
6354
6331
  color: "#FF0000"
6355
6332
  })
6356
6333
  ],
6357
- alignment: imageComp.props.alignment ? getAlignment4(imageComp.props.alignment) : void 0,
6334
+ alignment: imageComp.props.alignment ? getAlignment3(imageComp.props.alignment) : void 0,
6358
6335
  style: "Normal"
6359
6336
  })
6360
6337
  );
6361
6338
  continue;
6362
6339
  }
6363
6340
  let imageBuffer;
6341
+ let responseContentType;
6364
6342
  try {
6365
- imageBuffer = await getImageBuffer(imageSource);
6343
+ const imageResult = await getImageBuffer(imageSource);
6344
+ imageBuffer = imageResult.buffer;
6345
+ responseContentType = imageResult.contentType;
6366
6346
  } catch (error) {
6367
6347
  throw new Error(
6368
6348
  `Failed to load image from ${imageSource.substring(0, 50)}`
@@ -6395,9 +6375,14 @@ async function renderHeaderFooterComponents(components, theme, _themeName, _cont
6395
6375
  referenceWidthPx,
6396
6376
  fallbackHeight
6397
6377
  );
6398
- const floatingOptions = mapFloatingOptions(imageComp.props.floating);
6399
- const imageRun = new ImageRun2({
6400
- type: "png",
6378
+ const floatingOptions = mapFloatingOptions(
6379
+ imageComp.props.floating,
6380
+ theme,
6381
+ themeName
6382
+ );
6383
+ const imageType = detectImageType(imageSource, responseContentType);
6384
+ const imageRun = createTypedImageRun({
6385
+ type: imageType,
6401
6386
  data: imageBuffer,
6402
6387
  transformation: {
6403
6388
  width: dimensions.width,
@@ -6406,9 +6391,9 @@ async function renderHeaderFooterComponents(components, theme, _themeName, _cont
6406
6391
  ...floatingOptions && { floating: floatingOptions }
6407
6392
  });
6408
6393
  elements.push(
6409
- new Paragraph9({
6394
+ new Paragraph7({
6410
6395
  children: [imageRun],
6411
- alignment: imageComp.props.alignment ? getAlignment4(imageComp.props.alignment) : void 0,
6396
+ alignment: imageComp.props.alignment ? getAlignment3(imageComp.props.alignment) : void 0,
6412
6397
  style: "Normal"
6413
6398
  })
6414
6399
  );
@@ -6418,9 +6403,9 @@ async function renderHeaderFooterComponents(components, theme, _themeName, _cont
6418
6403
  error instanceof Error ? error.message : error
6419
6404
  );
6420
6405
  elements.push(
6421
- new Paragraph9({
6406
+ new Paragraph7({
6422
6407
  children: [
6423
- new TextRun7({
6408
+ new TextRun5({
6424
6409
  text: `[IMAGE: ${imageComp.props.path}]`,
6425
6410
  font: getThemeFonts(theme).body.family,
6426
6411
  size: 20,
@@ -6433,7 +6418,7 @@ async function renderHeaderFooterComponents(components, theme, _themeName, _cont
6433
6418
  );
6434
6419
  }
6435
6420
  } else if (isTableComponent(component)) {
6436
- const tables = await renderTableComponent(component, theme, _themeName);
6421
+ const tables = await renderTableComponent(component, theme, themeName);
6437
6422
  elements.push(...tables);
6438
6423
  }
6439
6424
  }
@@ -6458,7 +6443,7 @@ async function renderSection(section, theme, themeName, context, sectionOrdinal,
6458
6443
  };
6459
6444
  if (sectionBookmarkId && isFirstLayoutOfUserSection && sharedLinkId !== void 0) {
6460
6445
  elements.push(
6461
- new Paragraph9({
6446
+ new Paragraph7({
6462
6447
  children: [new BookmarkStart2(sectionBookmarkId, sharedLinkId)],
6463
6448
  spacing: {
6464
6449
  before: 0,
@@ -6484,7 +6469,7 @@ async function renderSection(section, theme, themeName, context, sectionOrdinal,
6484
6469
  }
6485
6470
  if (closeBookmark && sharedLinkId !== void 0) {
6486
6471
  elements.push(
6487
- new Paragraph9({
6472
+ new Paragraph7({
6488
6473
  children: [new BookmarkEnd2(sharedLinkId)]
6489
6474
  })
6490
6475
  );
@@ -6537,23 +6522,24 @@ async function renderComponent(component, theme, themeName, context) {
6537
6522
  } else if (isColumnsComponent(component)) {
6538
6523
  return await renderColumnsComponent(component, theme, themeName, context);
6539
6524
  } else if (isImageComponent(component)) {
6540
- return await renderImageComponent(component, theme);
6525
+ return await renderImageComponent(component, theme, themeName);
6541
6526
  } else if (isTextBoxComponent(component)) {
6542
6527
  return await renderTextBoxComponent(component, theme, themeName, context);
6543
6528
  } else if (isStatisticComponent(component)) {
6544
6529
  return renderStatisticComponent(component, theme);
6545
6530
  } else if (isTableComponent(component)) {
6546
6531
  return await renderTableComponent(component, theme, themeName);
6547
- } else if (isHeaderComponent(component)) {
6548
- return renderHeaderComponent(component, theme, themeName);
6549
- } else if (isFooterComponent(component)) {
6550
- return renderFooterComponent(component, theme, themeName);
6551
6532
  } else if (isListComponent(component)) {
6552
6533
  return renderListComponent(component, theme, themeName);
6553
6534
  } else if (isTocComponent(component)) {
6554
6535
  return renderTocComponent(component, theme, context);
6555
6536
  } else if (isHighchartsComponent(component)) {
6556
- return await renderHighchartsComponent(component, theme, themeName);
6537
+ return await renderHighchartsComponent(
6538
+ component,
6539
+ theme,
6540
+ themeName,
6541
+ context
6542
+ );
6557
6543
  } else if (isSectionComponent(component)) {
6558
6544
  return await renderSectionComponent(component, theme, themeName, context);
6559
6545
  }
@@ -6775,7 +6761,8 @@ function createBuilderImpl(state) {
6775
6761
  theme: state.theme,
6776
6762
  customThemes: state.customThemes,
6777
6763
  debug: state.debug,
6778
- enableCache: state.enableCache
6764
+ enableCache: state.enableCache,
6765
+ services: state.services
6779
6766
  };
6780
6767
  return createBuilderImpl(
6781
6768
  newState
@@ -6807,7 +6794,9 @@ function createBuilderImpl(state) {
6807
6794
  themeName
6808
6795
  );
6809
6796
  const layout = applyLayout(structure.sections, docTheme, themeName);
6810
- const generatedDocument = await renderDocument(structure, layout);
6797
+ const generatedDocument = await renderDocument(structure, layout, {
6798
+ services: state.services
6799
+ });
6811
6800
  return {
6812
6801
  document: generatedDocument,
6813
6802
  warnings: warnings.length > 0 ? warnings : null
@@ -6922,7 +6911,8 @@ function createDocumentGenerator(options) {
6922
6911
  theme: options.theme,
6923
6912
  customThemes: options.customThemes,
6924
6913
  debug: options.debug ?? false,
6925
- enableCache: options.enableCache ?? false
6914
+ enableCache: options.enableCache ?? false,
6915
+ services: options.services
6926
6916
  };
6927
6917
  return createBuilderImpl(initialState);
6928
6918
  }