dom-to-pptx 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.0.8] - 2025-12-12 (Hot-Patch)
6
+
7
+ ### Fixed
8
+
9
+ - **Fixed SVGs not getting converted**: Seperated the logic to handle SVGs and Web Components/Icons.
10
+
11
+
5
12
  ## [1.0.7] - 2025-12-12
6
13
 
7
14
  ### Fixed
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # dom-to-pptx
2
2
 
3
- **The High-Fidelity HTML to PowerPoint Converter (v1.0.7).**
3
+ **The High-Fidelity HTML to PowerPoint Converter (v1.0.8).**
4
4
 
5
5
  Most HTML-to-PPTX libraries fail when faced with modern web design. They break on gradients, misalign text, ignore rounded corners, or simply take a screenshot (which isn't editable).
6
6
 
@@ -17808,6 +17808,69 @@
17808
17808
  return Math.round(Math.atan2(b, a) * (180 / Math.PI));
17809
17809
  }
17810
17810
 
17811
+ function svgToPng(node) {
17812
+ return new Promise((resolve) => {
17813
+ const clone = node.cloneNode(true);
17814
+ const rect = node.getBoundingClientRect();
17815
+ const width = rect.width || 300;
17816
+ const height = rect.height || 150;
17817
+
17818
+ function inlineStyles(source, target) {
17819
+ const computed = window.getComputedStyle(source);
17820
+ const properties = [
17821
+ 'fill',
17822
+ 'stroke',
17823
+ 'stroke-width',
17824
+ 'stroke-linecap',
17825
+ 'stroke-linejoin',
17826
+ 'opacity',
17827
+ 'font-family',
17828
+ 'font-size',
17829
+ 'font-weight',
17830
+ ];
17831
+
17832
+ if (computed.fill === 'none') target.setAttribute('fill', 'none');
17833
+ else if (computed.fill) target.style.fill = computed.fill;
17834
+
17835
+ if (computed.stroke === 'none') target.setAttribute('stroke', 'none');
17836
+ else if (computed.stroke) target.style.stroke = computed.stroke;
17837
+
17838
+ properties.forEach((prop) => {
17839
+ if (prop !== 'fill' && prop !== 'stroke') {
17840
+ const val = computed[prop];
17841
+ if (val && val !== 'auto') target.style[prop] = val;
17842
+ }
17843
+ });
17844
+
17845
+ for (let i = 0; i < source.children.length; i++) {
17846
+ if (target.children[i]) inlineStyles(source.children[i], target.children[i]);
17847
+ }
17848
+ }
17849
+
17850
+ inlineStyles(node, clone);
17851
+ clone.setAttribute('width', width);
17852
+ clone.setAttribute('height', height);
17853
+ clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
17854
+
17855
+ const xml = new XMLSerializer().serializeToString(clone);
17856
+ const svgUrl = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(xml)}`;
17857
+ const img = new Image();
17858
+ img.crossOrigin = 'Anonymous';
17859
+ img.onload = () => {
17860
+ const canvas = document.createElement('canvas');
17861
+ const scale = 3;
17862
+ canvas.width = width * scale;
17863
+ canvas.height = height * scale;
17864
+ const ctx = canvas.getContext('2d');
17865
+ ctx.scale(scale, scale);
17866
+ ctx.drawImage(img, 0, 0, width, height);
17867
+ resolve(canvas.toDataURL('image/png'));
17868
+ };
17869
+ img.onerror = () => resolve(null);
17870
+ img.src = svgUrl;
17871
+ });
17872
+ }
17873
+
17811
17874
  function getVisibleShadow(shadowStr, scale) {
17812
17875
  if (!shadowStr || shadowStr === 'none') return null;
17813
17876
  const shadows = shadowStr.split(/,(?![^()]*\))/);
@@ -18318,23 +18381,18 @@
18318
18381
 
18319
18382
  const items = [];
18320
18383
 
18321
- // --- ASYNC JOB: SVGs / Icons ---
18322
- if (
18323
- node.nodeName.toUpperCase() === 'SVG' ||
18324
- node.tagName.includes('-') ||
18325
- node.tagName === 'ION-ICON'
18326
- ) {
18384
+ // --- ASYNC JOB: SVG Tags ---
18385
+ if (node.nodeName.toUpperCase() === 'SVG') {
18327
18386
  const item = {
18328
18387
  type: 'image',
18329
18388
  zIndex,
18330
18389
  domOrder,
18331
- options: { x, y, w, h, rotate: rotation, data: null }, // Data null initially
18390
+ options: { data: null, x, y, w, h, rotate: rotation },
18332
18391
  };
18333
18392
 
18334
- // Create Job
18335
18393
  const job = async () => {
18336
- const pngData = await elementToCanvasImage(node, widthPx, heightPx);
18337
- if (pngData) item.options.data = pngData;
18394
+ const processed = await svgToPng(node);
18395
+ if (processed) item.options.data = processed;
18338
18396
  else item.skip = true;
18339
18397
  };
18340
18398
 
@@ -18384,6 +18442,34 @@
18384
18442
  return { items: [item], job, stopRecursion: true };
18385
18443
  }
18386
18444
 
18445
+ // --- ASYNC JOB: Icons and Other Elements ---
18446
+ if (
18447
+ node.tagName.toUpperCase() === 'MATERIAL-ICON' ||
18448
+ node.tagName.toUpperCase() === 'ICONIFY-ICON' ||
18449
+ node.tagName.toUpperCase() === 'REMIX-ICON' ||
18450
+ node.tagName.toUpperCase() === 'ION-ICON' ||
18451
+ node.tagName.toUpperCase() === 'EVA-ICON' ||
18452
+ node.tagName.toUpperCase() === 'BOX-ICON' ||
18453
+ node.tagName.toUpperCase() === 'FA-ICON' ||
18454
+ node.tagName.includes('-')
18455
+ ) {
18456
+ const item = {
18457
+ type: 'image',
18458
+ zIndex,
18459
+ domOrder,
18460
+ options: { x, y, w, h, rotate: rotation, data: null }, // Data null initially
18461
+ };
18462
+
18463
+ // Create Job
18464
+ const job = async () => {
18465
+ const pngData = await elementToCanvasImage(node, widthPx, heightPx);
18466
+ if (pngData) item.options.data = pngData;
18467
+ else item.skip = true;
18468
+ };
18469
+
18470
+ return { items: [item], job, stopRecursion: true };
18471
+ }
18472
+
18387
18473
  // Radii logic
18388
18474
  const borderRadiusValue = parseFloat(style.borderRadius) || 0;
18389
18475
  const borderBottomLeftRadius = parseFloat(style.borderBottomLeftRadius) || 0;
@@ -8135,6 +8135,69 @@ function getRotation(transformStr) {
8135
8135
  return Math.round(Math.atan2(b, a) * (180 / Math.PI));
8136
8136
  }
8137
8137
 
8138
+ function svgToPng(node) {
8139
+ return new Promise((resolve) => {
8140
+ const clone = node.cloneNode(true);
8141
+ const rect = node.getBoundingClientRect();
8142
+ const width = rect.width || 300;
8143
+ const height = rect.height || 150;
8144
+
8145
+ function inlineStyles(source, target) {
8146
+ const computed = window.getComputedStyle(source);
8147
+ const properties = [
8148
+ 'fill',
8149
+ 'stroke',
8150
+ 'stroke-width',
8151
+ 'stroke-linecap',
8152
+ 'stroke-linejoin',
8153
+ 'opacity',
8154
+ 'font-family',
8155
+ 'font-size',
8156
+ 'font-weight',
8157
+ ];
8158
+
8159
+ if (computed.fill === 'none') target.setAttribute('fill', 'none');
8160
+ else if (computed.fill) target.style.fill = computed.fill;
8161
+
8162
+ if (computed.stroke === 'none') target.setAttribute('stroke', 'none');
8163
+ else if (computed.stroke) target.style.stroke = computed.stroke;
8164
+
8165
+ properties.forEach((prop) => {
8166
+ if (prop !== 'fill' && prop !== 'stroke') {
8167
+ const val = computed[prop];
8168
+ if (val && val !== 'auto') target.style[prop] = val;
8169
+ }
8170
+ });
8171
+
8172
+ for (let i = 0; i < source.children.length; i++) {
8173
+ if (target.children[i]) inlineStyles(source.children[i], target.children[i]);
8174
+ }
8175
+ }
8176
+
8177
+ inlineStyles(node, clone);
8178
+ clone.setAttribute('width', width);
8179
+ clone.setAttribute('height', height);
8180
+ clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
8181
+
8182
+ const xml = new XMLSerializer().serializeToString(clone);
8183
+ const svgUrl = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(xml)}`;
8184
+ const img = new Image();
8185
+ img.crossOrigin = 'Anonymous';
8186
+ img.onload = () => {
8187
+ const canvas = document.createElement('canvas');
8188
+ const scale = 3;
8189
+ canvas.width = width * scale;
8190
+ canvas.height = height * scale;
8191
+ const ctx = canvas.getContext('2d');
8192
+ ctx.scale(scale, scale);
8193
+ ctx.drawImage(img, 0, 0, width, height);
8194
+ resolve(canvas.toDataURL('image/png'));
8195
+ };
8196
+ img.onerror = () => resolve(null);
8197
+ img.src = svgUrl;
8198
+ });
8199
+ }
8200
+
8138
8201
  function getVisibleShadow(shadowStr, scale) {
8139
8202
  if (!shadowStr || shadowStr === 'none') return null;
8140
8203
  const shadows = shadowStr.split(/,(?![^()]*\))/);
@@ -8645,23 +8708,18 @@ function prepareRenderItem(node, config, domOrder, pptx, effectiveZIndex, comput
8645
8708
 
8646
8709
  const items = [];
8647
8710
 
8648
- // --- ASYNC JOB: SVGs / Icons ---
8649
- if (
8650
- node.nodeName.toUpperCase() === 'SVG' ||
8651
- node.tagName.includes('-') ||
8652
- node.tagName === 'ION-ICON'
8653
- ) {
8711
+ // --- ASYNC JOB: SVG Tags ---
8712
+ if (node.nodeName.toUpperCase() === 'SVG') {
8654
8713
  const item = {
8655
8714
  type: 'image',
8656
8715
  zIndex,
8657
8716
  domOrder,
8658
- options: { x, y, w, h, rotate: rotation, data: null }, // Data null initially
8717
+ options: { data: null, x, y, w, h, rotate: rotation },
8659
8718
  };
8660
8719
 
8661
- // Create Job
8662
8720
  const job = async () => {
8663
- const pngData = await elementToCanvasImage(node, widthPx, heightPx);
8664
- if (pngData) item.options.data = pngData;
8721
+ const processed = await svgToPng(node);
8722
+ if (processed) item.options.data = processed;
8665
8723
  else item.skip = true;
8666
8724
  };
8667
8725
 
@@ -8711,6 +8769,34 @@ function prepareRenderItem(node, config, domOrder, pptx, effectiveZIndex, comput
8711
8769
  return { items: [item], job, stopRecursion: true };
8712
8770
  }
8713
8771
 
8772
+ // --- ASYNC JOB: Icons and Other Elements ---
8773
+ if (
8774
+ node.tagName.toUpperCase() === 'MATERIAL-ICON' ||
8775
+ node.tagName.toUpperCase() === 'ICONIFY-ICON' ||
8776
+ node.tagName.toUpperCase() === 'REMIX-ICON' ||
8777
+ node.tagName.toUpperCase() === 'ION-ICON' ||
8778
+ node.tagName.toUpperCase() === 'EVA-ICON' ||
8779
+ node.tagName.toUpperCase() === 'BOX-ICON' ||
8780
+ node.tagName.toUpperCase() === 'FA-ICON' ||
8781
+ node.tagName.includes('-')
8782
+ ) {
8783
+ const item = {
8784
+ type: 'image',
8785
+ zIndex,
8786
+ domOrder,
8787
+ options: { x, y, w, h, rotate: rotation, data: null }, // Data null initially
8788
+ };
8789
+
8790
+ // Create Job
8791
+ const job = async () => {
8792
+ const pngData = await elementToCanvasImage(node, widthPx, heightPx);
8793
+ if (pngData) item.options.data = pngData;
8794
+ else item.skip = true;
8795
+ };
8796
+
8797
+ return { items: [item], job, stopRecursion: true };
8798
+ }
8799
+
8714
8800
  // Radii logic
8715
8801
  const borderRadiusValue = parseFloat(style.borderRadius) || 0;
8716
8802
  const borderBottomLeftRadius = parseFloat(style.borderBottomLeftRadius) || 0;
@@ -8135,6 +8135,69 @@
8135
8135
  return Math.round(Math.atan2(b, a) * (180 / Math.PI));
8136
8136
  }
8137
8137
 
8138
+ function svgToPng(node) {
8139
+ return new Promise((resolve) => {
8140
+ const clone = node.cloneNode(true);
8141
+ const rect = node.getBoundingClientRect();
8142
+ const width = rect.width || 300;
8143
+ const height = rect.height || 150;
8144
+
8145
+ function inlineStyles(source, target) {
8146
+ const computed = window.getComputedStyle(source);
8147
+ const properties = [
8148
+ 'fill',
8149
+ 'stroke',
8150
+ 'stroke-width',
8151
+ 'stroke-linecap',
8152
+ 'stroke-linejoin',
8153
+ 'opacity',
8154
+ 'font-family',
8155
+ 'font-size',
8156
+ 'font-weight',
8157
+ ];
8158
+
8159
+ if (computed.fill === 'none') target.setAttribute('fill', 'none');
8160
+ else if (computed.fill) target.style.fill = computed.fill;
8161
+
8162
+ if (computed.stroke === 'none') target.setAttribute('stroke', 'none');
8163
+ else if (computed.stroke) target.style.stroke = computed.stroke;
8164
+
8165
+ properties.forEach((prop) => {
8166
+ if (prop !== 'fill' && prop !== 'stroke') {
8167
+ const val = computed[prop];
8168
+ if (val && val !== 'auto') target.style[prop] = val;
8169
+ }
8170
+ });
8171
+
8172
+ for (let i = 0; i < source.children.length; i++) {
8173
+ if (target.children[i]) inlineStyles(source.children[i], target.children[i]);
8174
+ }
8175
+ }
8176
+
8177
+ inlineStyles(node, clone);
8178
+ clone.setAttribute('width', width);
8179
+ clone.setAttribute('height', height);
8180
+ clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
8181
+
8182
+ const xml = new XMLSerializer().serializeToString(clone);
8183
+ const svgUrl = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(xml)}`;
8184
+ const img = new Image();
8185
+ img.crossOrigin = 'Anonymous';
8186
+ img.onload = () => {
8187
+ const canvas = document.createElement('canvas');
8188
+ const scale = 3;
8189
+ canvas.width = width * scale;
8190
+ canvas.height = height * scale;
8191
+ const ctx = canvas.getContext('2d');
8192
+ ctx.scale(scale, scale);
8193
+ ctx.drawImage(img, 0, 0, width, height);
8194
+ resolve(canvas.toDataURL('image/png'));
8195
+ };
8196
+ img.onerror = () => resolve(null);
8197
+ img.src = svgUrl;
8198
+ });
8199
+ }
8200
+
8138
8201
  function getVisibleShadow(shadowStr, scale) {
8139
8202
  if (!shadowStr || shadowStr === 'none') return null;
8140
8203
  const shadows = shadowStr.split(/,(?![^()]*\))/);
@@ -8645,23 +8708,18 @@
8645
8708
 
8646
8709
  const items = [];
8647
8710
 
8648
- // --- ASYNC JOB: SVGs / Icons ---
8649
- if (
8650
- node.nodeName.toUpperCase() === 'SVG' ||
8651
- node.tagName.includes('-') ||
8652
- node.tagName === 'ION-ICON'
8653
- ) {
8711
+ // --- ASYNC JOB: SVG Tags ---
8712
+ if (node.nodeName.toUpperCase() === 'SVG') {
8654
8713
  const item = {
8655
8714
  type: 'image',
8656
8715
  zIndex,
8657
8716
  domOrder,
8658
- options: { x, y, w, h, rotate: rotation, data: null }, // Data null initially
8717
+ options: { data: null, x, y, w, h, rotate: rotation },
8659
8718
  };
8660
8719
 
8661
- // Create Job
8662
8720
  const job = async () => {
8663
- const pngData = await elementToCanvasImage(node, widthPx, heightPx);
8664
- if (pngData) item.options.data = pngData;
8721
+ const processed = await svgToPng(node);
8722
+ if (processed) item.options.data = processed;
8665
8723
  else item.skip = true;
8666
8724
  };
8667
8725
 
@@ -8711,6 +8769,34 @@
8711
8769
  return { items: [item], job, stopRecursion: true };
8712
8770
  }
8713
8771
 
8772
+ // --- ASYNC JOB: Icons and Other Elements ---
8773
+ if (
8774
+ node.tagName.toUpperCase() === 'MATERIAL-ICON' ||
8775
+ node.tagName.toUpperCase() === 'ICONIFY-ICON' ||
8776
+ node.tagName.toUpperCase() === 'REMIX-ICON' ||
8777
+ node.tagName.toUpperCase() === 'ION-ICON' ||
8778
+ node.tagName.toUpperCase() === 'EVA-ICON' ||
8779
+ node.tagName.toUpperCase() === 'BOX-ICON' ||
8780
+ node.tagName.toUpperCase() === 'FA-ICON' ||
8781
+ node.tagName.includes('-')
8782
+ ) {
8783
+ const item = {
8784
+ type: 'image',
8785
+ zIndex,
8786
+ domOrder,
8787
+ options: { x, y, w, h, rotate: rotation, data: null }, // Data null initially
8788
+ };
8789
+
8790
+ // Create Job
8791
+ const job = async () => {
8792
+ const pngData = await elementToCanvasImage(node, widthPx, heightPx);
8793
+ if (pngData) item.options.data = pngData;
8794
+ else item.skip = true;
8795
+ };
8796
+
8797
+ return { items: [item], job, stopRecursion: true };
8798
+ }
8799
+
8714
8800
  // Radii logic
8715
8801
  const borderRadiusValue = parseFloat(style.borderRadius) || 0;
8716
8802
  const borderBottomLeftRadius = parseFloat(style.borderBottomLeftRadius) || 0;
@@ -8111,6 +8111,69 @@ function getRotation(transformStr) {
8111
8111
  return Math.round(Math.atan2(b, a) * (180 / Math.PI));
8112
8112
  }
8113
8113
 
8114
+ function svgToPng(node) {
8115
+ return new Promise((resolve) => {
8116
+ const clone = node.cloneNode(true);
8117
+ const rect = node.getBoundingClientRect();
8118
+ const width = rect.width || 300;
8119
+ const height = rect.height || 150;
8120
+
8121
+ function inlineStyles(source, target) {
8122
+ const computed = window.getComputedStyle(source);
8123
+ const properties = [
8124
+ 'fill',
8125
+ 'stroke',
8126
+ 'stroke-width',
8127
+ 'stroke-linecap',
8128
+ 'stroke-linejoin',
8129
+ 'opacity',
8130
+ 'font-family',
8131
+ 'font-size',
8132
+ 'font-weight',
8133
+ ];
8134
+
8135
+ if (computed.fill === 'none') target.setAttribute('fill', 'none');
8136
+ else if (computed.fill) target.style.fill = computed.fill;
8137
+
8138
+ if (computed.stroke === 'none') target.setAttribute('stroke', 'none');
8139
+ else if (computed.stroke) target.style.stroke = computed.stroke;
8140
+
8141
+ properties.forEach((prop) => {
8142
+ if (prop !== 'fill' && prop !== 'stroke') {
8143
+ const val = computed[prop];
8144
+ if (val && val !== 'auto') target.style[prop] = val;
8145
+ }
8146
+ });
8147
+
8148
+ for (let i = 0; i < source.children.length; i++) {
8149
+ if (target.children[i]) inlineStyles(source.children[i], target.children[i]);
8150
+ }
8151
+ }
8152
+
8153
+ inlineStyles(node, clone);
8154
+ clone.setAttribute('width', width);
8155
+ clone.setAttribute('height', height);
8156
+ clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
8157
+
8158
+ const xml = new XMLSerializer().serializeToString(clone);
8159
+ const svgUrl = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(xml)}`;
8160
+ const img = new Image();
8161
+ img.crossOrigin = 'Anonymous';
8162
+ img.onload = () => {
8163
+ const canvas = document.createElement('canvas');
8164
+ const scale = 3;
8165
+ canvas.width = width * scale;
8166
+ canvas.height = height * scale;
8167
+ const ctx = canvas.getContext('2d');
8168
+ ctx.scale(scale, scale);
8169
+ ctx.drawImage(img, 0, 0, width, height);
8170
+ resolve(canvas.toDataURL('image/png'));
8171
+ };
8172
+ img.onerror = () => resolve(null);
8173
+ img.src = svgUrl;
8174
+ });
8175
+ }
8176
+
8114
8177
  function getVisibleShadow(shadowStr, scale) {
8115
8178
  if (!shadowStr || shadowStr === 'none') return null;
8116
8179
  const shadows = shadowStr.split(/,(?![^()]*\))/);
@@ -8621,23 +8684,18 @@ function prepareRenderItem(node, config, domOrder, pptx, effectiveZIndex, comput
8621
8684
 
8622
8685
  const items = [];
8623
8686
 
8624
- // --- ASYNC JOB: SVGs / Icons ---
8625
- if (
8626
- node.nodeName.toUpperCase() === 'SVG' ||
8627
- node.tagName.includes('-') ||
8628
- node.tagName === 'ION-ICON'
8629
- ) {
8687
+ // --- ASYNC JOB: SVG Tags ---
8688
+ if (node.nodeName.toUpperCase() === 'SVG') {
8630
8689
  const item = {
8631
8690
  type: 'image',
8632
8691
  zIndex,
8633
8692
  domOrder,
8634
- options: { x, y, w, h, rotate: rotation, data: null }, // Data null initially
8693
+ options: { data: null, x, y, w, h, rotate: rotation },
8635
8694
  };
8636
8695
 
8637
- // Create Job
8638
8696
  const job = async () => {
8639
- const pngData = await elementToCanvasImage(node, widthPx, heightPx);
8640
- if (pngData) item.options.data = pngData;
8697
+ const processed = await svgToPng(node);
8698
+ if (processed) item.options.data = processed;
8641
8699
  else item.skip = true;
8642
8700
  };
8643
8701
 
@@ -8687,6 +8745,34 @@ function prepareRenderItem(node, config, domOrder, pptx, effectiveZIndex, comput
8687
8745
  return { items: [item], job, stopRecursion: true };
8688
8746
  }
8689
8747
 
8748
+ // --- ASYNC JOB: Icons and Other Elements ---
8749
+ if (
8750
+ node.tagName.toUpperCase() === 'MATERIAL-ICON' ||
8751
+ node.tagName.toUpperCase() === 'ICONIFY-ICON' ||
8752
+ node.tagName.toUpperCase() === 'REMIX-ICON' ||
8753
+ node.tagName.toUpperCase() === 'ION-ICON' ||
8754
+ node.tagName.toUpperCase() === 'EVA-ICON' ||
8755
+ node.tagName.toUpperCase() === 'BOX-ICON' ||
8756
+ node.tagName.toUpperCase() === 'FA-ICON' ||
8757
+ node.tagName.includes('-')
8758
+ ) {
8759
+ const item = {
8760
+ type: 'image',
8761
+ zIndex,
8762
+ domOrder,
8763
+ options: { x, y, w, h, rotate: rotation, data: null }, // Data null initially
8764
+ };
8765
+
8766
+ // Create Job
8767
+ const job = async () => {
8768
+ const pngData = await elementToCanvasImage(node, widthPx, heightPx);
8769
+ if (pngData) item.options.data = pngData;
8770
+ else item.skip = true;
8771
+ };
8772
+
8773
+ return { items: [item], job, stopRecursion: true };
8774
+ }
8775
+
8690
8776
  // Radii logic
8691
8777
  const borderRadiusValue = parseFloat(style.borderRadius) || 0;
8692
8778
  const borderBottomLeftRadius = parseFloat(style.borderBottomLeftRadius) || 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dom-to-pptx",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "A client-side library that converts any HTML element into a fully editable PowerPoint slide. **dom-to-pptx** transforms DOM structures into pixel-accurate `.pptx` content, preserving gradients, shadows, rounded images, and responsive layouts. It translates CSS Flexbox/Grid, linear-gradients, box-shadows, and typography into native PowerPoint shapes, enabling precise, design-faithful slide generation directly from the browser.",
5
5
  "main": "dist/dom-to-pptx.cjs",
6
6
  "module": "dist/dom-to-pptx.mjs",
package/src/index.js CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  getVisibleShadow,
13
13
  generateGradientSVG,
14
14
  getRotation,
15
+ svgToPng,
15
16
  getPadding,
16
17
  getSoftEdges,
17
18
  generateBlurredSVG,
@@ -312,23 +313,18 @@ function prepareRenderItem(node, config, domOrder, pptx, effectiveZIndex, comput
312
313
 
313
314
  const items = [];
314
315
 
315
- // --- ASYNC JOB: SVGs / Icons ---
316
- if (
317
- node.nodeName.toUpperCase() === 'SVG' ||
318
- node.tagName.includes('-') ||
319
- node.tagName === 'ION-ICON'
320
- ) {
316
+ // --- ASYNC JOB: SVG Tags ---
317
+ if (node.nodeName.toUpperCase() === 'SVG') {
321
318
  const item = {
322
319
  type: 'image',
323
320
  zIndex,
324
321
  domOrder,
325
- options: { x, y, w, h, rotate: rotation, data: null }, // Data null initially
322
+ options: { data: null, x, y, w, h, rotate: rotation },
326
323
  };
327
324
 
328
- // Create Job
329
325
  const job = async () => {
330
- const pngData = await elementToCanvasImage(node, widthPx, heightPx);
331
- if (pngData) item.options.data = pngData;
326
+ const processed = await svgToPng(node);
327
+ if (processed) item.options.data = processed;
332
328
  else item.skip = true;
333
329
  };
334
330
 
@@ -378,6 +374,34 @@ function prepareRenderItem(node, config, domOrder, pptx, effectiveZIndex, comput
378
374
  return { items: [item], job, stopRecursion: true };
379
375
  }
380
376
 
377
+ // --- ASYNC JOB: Icons and Other Elements ---
378
+ if (
379
+ node.tagName.toUpperCase() === 'MATERIAL-ICON' ||
380
+ node.tagName.toUpperCase() === 'ICONIFY-ICON' ||
381
+ node.tagName.toUpperCase() === 'REMIX-ICON' ||
382
+ node.tagName.toUpperCase() === 'ION-ICON' ||
383
+ node.tagName.toUpperCase() === 'EVA-ICON' ||
384
+ node.tagName.toUpperCase() === 'BOX-ICON' ||
385
+ node.tagName.toUpperCase() === 'FA-ICON' ||
386
+ node.tagName.includes('-')
387
+ ) {
388
+ const item = {
389
+ type: 'image',
390
+ zIndex,
391
+ domOrder,
392
+ options: { x, y, w, h, rotate: rotation, data: null }, // Data null initially
393
+ };
394
+
395
+ // Create Job
396
+ const job = async () => {
397
+ const pngData = await elementToCanvasImage(node, widthPx, heightPx);
398
+ if (pngData) item.options.data = pngData;
399
+ else item.skip = true;
400
+ };
401
+
402
+ return { items: [item], job, stopRecursion: true };
403
+ }
404
+
381
405
  // Radii logic
382
406
  const borderRadiusValue = parseFloat(style.borderRadius) || 0;
383
407
  const borderBottomLeftRadius = parseFloat(style.borderBottomLeftRadius) || 0;