dom-to-pptx 1.0.5 → 1.0.7
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 +31 -18
- package/{Readme.md → README.md} +165 -85
- package/SUPPORTED.md +50 -50
- package/dist/dom-to-pptx.bundle.js +18565 -30968
- package/dist/dom-to-pptx.cjs +358 -304
- package/dist/dom-to-pptx.min.js +356 -304
- package/dist/dom-to-pptx.mjs +352 -301
- package/package.json +73 -73
- package/rollup.config.js +62 -53
- package/src/image-processor.js +79 -76
- package/src/index.js +222 -148
- package/src/utils.js +59 -23
package/dist/dom-to-pptx.cjs
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
3
5
|
var PptxGenJSImport = require('pptxgenjs');
|
|
4
6
|
|
|
5
|
-
function
|
|
7
|
+
function _interopNamespace(e) {
|
|
8
|
+
if (e && e.__esModule) return e;
|
|
6
9
|
var n = Object.create(null);
|
|
7
10
|
if (e) {
|
|
8
11
|
Object.keys(e).forEach(function (k) {
|
|
@@ -15,11 +18,11 @@ function _interopNamespaceDefault(e) {
|
|
|
15
18
|
}
|
|
16
19
|
});
|
|
17
20
|
}
|
|
18
|
-
n
|
|
21
|
+
n["default"] = e;
|
|
19
22
|
return Object.freeze(n);
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
var PptxGenJSImport__namespace = /*#__PURE__*/
|
|
25
|
+
var PptxGenJSImport__namespace = /*#__PURE__*/_interopNamespace(PptxGenJSImport);
|
|
23
26
|
|
|
24
27
|
/*!
|
|
25
28
|
* html2canvas 1.4.1 <https://html2canvas.hertzen.com>
|
|
@@ -74,7 +77,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
|
|
|
74
77
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
75
78
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
76
79
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
77
|
-
step((generator = generator.apply(thisArg, [])).next());
|
|
80
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
78
81
|
});
|
|
79
82
|
}
|
|
80
83
|
|
|
@@ -107,7 +110,7 @@ function __generator(thisArg, body) {
|
|
|
107
110
|
}
|
|
108
111
|
|
|
109
112
|
function __spreadArray(to, from, pack) {
|
|
110
|
-
if (arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
113
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
111
114
|
if (ar || !(i in from)) {
|
|
112
115
|
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
113
116
|
ar[i] = from[i];
|
|
@@ -7976,17 +7979,20 @@ function generateCompositeBorderSVG(w, h, radius, sides) {
|
|
|
7976
7979
|
*/
|
|
7977
7980
|
function generateCustomShapeSVG(w, h, color, opacity, radii) {
|
|
7978
7981
|
let { tl, tr, br, bl } = radii;
|
|
7979
|
-
|
|
7982
|
+
|
|
7980
7983
|
// Clamp radii using CSS spec logic (avoid overlap)
|
|
7981
7984
|
const factor = Math.min(
|
|
7982
|
-
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
|
|
7985
|
+
w / (tl + tr) || Infinity,
|
|
7986
|
+
h / (tr + br) || Infinity,
|
|
7987
|
+
w / (br + bl) || Infinity,
|
|
7988
|
+
h / (bl + tl) || Infinity
|
|
7986
7989
|
);
|
|
7987
|
-
|
|
7990
|
+
|
|
7988
7991
|
if (factor < 1) {
|
|
7989
|
-
tl *= factor;
|
|
7992
|
+
tl *= factor;
|
|
7993
|
+
tr *= factor;
|
|
7994
|
+
br *= factor;
|
|
7995
|
+
bl *= factor;
|
|
7990
7996
|
}
|
|
7991
7997
|
|
|
7992
7998
|
const path = `
|
|
@@ -8017,7 +8023,10 @@ function parseColor(str) {
|
|
|
8017
8023
|
if (str.startsWith('#')) {
|
|
8018
8024
|
let hex = str.slice(1);
|
|
8019
8025
|
if (hex.length === 3)
|
|
8020
|
-
hex = hex
|
|
8026
|
+
hex = hex
|
|
8027
|
+
.split('')
|
|
8028
|
+
.map((c) => c + c)
|
|
8029
|
+
.join('');
|
|
8021
8030
|
return { hex: hex.toUpperCase(), opacity: 1 };
|
|
8022
8031
|
}
|
|
8023
8032
|
const match = str.match(/[\d.]+/g);
|
|
@@ -8080,25 +8089,35 @@ function isTextContainer(node) {
|
|
|
8080
8089
|
|
|
8081
8090
|
// Check if children are purely inline text formatting or visual shapes
|
|
8082
8091
|
const isSafeInline = (el) => {
|
|
8092
|
+
// 1. Reject Web Components / Icons / Images
|
|
8093
|
+
if (el.tagName.includes('-')) return false;
|
|
8094
|
+
if (el.tagName === 'IMG' || el.tagName === 'SVG') return false;
|
|
8095
|
+
|
|
8083
8096
|
const style = window.getComputedStyle(el);
|
|
8084
8097
|
const display = style.display;
|
|
8085
|
-
|
|
8086
|
-
//
|
|
8087
|
-
const isInlineTag = ['SPAN', 'B', 'STRONG', 'EM', 'I', 'A', 'SMALL'].includes(el.tagName);
|
|
8098
|
+
|
|
8099
|
+
// 2. Initial check: Must be a standard inline tag OR display:inline
|
|
8100
|
+
const isInlineTag = ['SPAN', 'B', 'STRONG', 'EM', 'I', 'A', 'SMALL', 'MARK'].includes(el.tagName);
|
|
8088
8101
|
const isInlineDisplay = display.includes('inline');
|
|
8089
8102
|
|
|
8090
8103
|
if (!isInlineTag && !isInlineDisplay) return false;
|
|
8091
8104
|
|
|
8092
|
-
//
|
|
8093
|
-
//
|
|
8094
|
-
//
|
|
8095
|
-
|
|
8105
|
+
// 3. CRITICAL FIX: Check for Structural Styling
|
|
8106
|
+
// PPTX Text Runs (parts of a text line) CANNOT have backgrounds, borders, or padding.
|
|
8107
|
+
// If a child element has these, the parent is NOT a simple text container;
|
|
8108
|
+
// it is a layout container composed of styled blocks.
|
|
8096
8109
|
const bgColor = parseColor(style.backgroundColor);
|
|
8097
8110
|
const hasVisibleBg = bgColor.hex && bgColor.opacity > 0;
|
|
8098
8111
|
const hasBorder = parseFloat(style.borderWidth) > 0 && parseColor(style.borderColor).opacity > 0;
|
|
8099
8112
|
|
|
8113
|
+
if (hasVisibleBg || hasBorder) {
|
|
8114
|
+
return false;
|
|
8115
|
+
}
|
|
8116
|
+
|
|
8117
|
+
// 4. Check for empty shapes (visual objects without text, like dots)
|
|
8118
|
+
const hasContent = el.textContent.trim().length > 0;
|
|
8100
8119
|
if (!hasContent && (hasVisibleBg || hasBorder)) {
|
|
8101
|
-
return false;
|
|
8120
|
+
return false;
|
|
8102
8121
|
}
|
|
8103
8122
|
|
|
8104
8123
|
return true;
|
|
@@ -8116,62 +8135,6 @@ function getRotation(transformStr) {
|
|
|
8116
8135
|
return Math.round(Math.atan2(b, a) * (180 / Math.PI));
|
|
8117
8136
|
}
|
|
8118
8137
|
|
|
8119
|
-
function svgToPng(node) {
|
|
8120
|
-
return new Promise((resolve) => {
|
|
8121
|
-
const clone = node.cloneNode(true);
|
|
8122
|
-
const rect = node.getBoundingClientRect();
|
|
8123
|
-
const width = rect.width || 300;
|
|
8124
|
-
const height = rect.height || 150;
|
|
8125
|
-
|
|
8126
|
-
function inlineStyles(source, target) {
|
|
8127
|
-
const computed = window.getComputedStyle(source);
|
|
8128
|
-
const properties = [
|
|
8129
|
-
'fill', 'stroke', 'stroke-width', 'stroke-linecap',
|
|
8130
|
-
'stroke-linejoin', 'opacity', 'font-family', 'font-size', 'font-weight',
|
|
8131
|
-
];
|
|
8132
|
-
|
|
8133
|
-
if (computed.fill === 'none') target.setAttribute('fill', 'none');
|
|
8134
|
-
else if (computed.fill) target.style.fill = computed.fill;
|
|
8135
|
-
|
|
8136
|
-
if (computed.stroke === 'none') target.setAttribute('stroke', 'none');
|
|
8137
|
-
else if (computed.stroke) target.style.stroke = computed.stroke;
|
|
8138
|
-
|
|
8139
|
-
properties.forEach((prop) => {
|
|
8140
|
-
if (prop !== 'fill' && prop !== 'stroke') {
|
|
8141
|
-
const val = computed[prop];
|
|
8142
|
-
if (val && val !== 'auto') target.style[prop] = val;
|
|
8143
|
-
}
|
|
8144
|
-
});
|
|
8145
|
-
|
|
8146
|
-
for (let i = 0; i < source.children.length; i++) {
|
|
8147
|
-
if (target.children[i]) inlineStyles(source.children[i], target.children[i]);
|
|
8148
|
-
}
|
|
8149
|
-
}
|
|
8150
|
-
|
|
8151
|
-
inlineStyles(node, clone);
|
|
8152
|
-
clone.setAttribute('width', width);
|
|
8153
|
-
clone.setAttribute('height', height);
|
|
8154
|
-
clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
|
|
8155
|
-
|
|
8156
|
-
const xml = new XMLSerializer().serializeToString(clone);
|
|
8157
|
-
const svgUrl = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(xml)}`;
|
|
8158
|
-
const img = new Image();
|
|
8159
|
-
img.crossOrigin = 'Anonymous';
|
|
8160
|
-
img.onload = () => {
|
|
8161
|
-
const canvas = document.createElement('canvas');
|
|
8162
|
-
const scale = 3;
|
|
8163
|
-
canvas.width = width * scale;
|
|
8164
|
-
canvas.height = height * scale;
|
|
8165
|
-
const ctx = canvas.getContext('2d');
|
|
8166
|
-
ctx.scale(scale, scale);
|
|
8167
|
-
ctx.drawImage(img, 0, 0, width, height);
|
|
8168
|
-
resolve(canvas.toDataURL('image/png'));
|
|
8169
|
-
};
|
|
8170
|
-
img.onerror = () => resolve(null);
|
|
8171
|
-
img.src = svgUrl;
|
|
8172
|
-
});
|
|
8173
|
-
}
|
|
8174
|
-
|
|
8175
8138
|
function getVisibleShadow(shadowStr, scale) {
|
|
8176
8139
|
if (!shadowStr || shadowStr === 'none') return null;
|
|
8177
8140
|
const shadows = shadowStr.split(/,(?![^()]*\))/);
|
|
@@ -8210,16 +8173,29 @@ function generateGradientSVG(w, h, bgString, radius, border) {
|
|
|
8210
8173
|
const content = match[1];
|
|
8211
8174
|
const parts = content.split(/,(?![^()]*\))/).map((p) => p.trim());
|
|
8212
8175
|
|
|
8213
|
-
let x1 = '0%',
|
|
8176
|
+
let x1 = '0%',
|
|
8177
|
+
y1 = '0%',
|
|
8178
|
+
x2 = '0%',
|
|
8179
|
+
y2 = '100%';
|
|
8214
8180
|
let stopsStartIdx = 0;
|
|
8215
8181
|
if (parts[0].includes('to right')) {
|
|
8216
|
-
x1 = '0%';
|
|
8182
|
+
x1 = '0%';
|
|
8183
|
+
x2 = '100%';
|
|
8184
|
+
y2 = '0%';
|
|
8185
|
+
stopsStartIdx = 1;
|
|
8217
8186
|
} else if (parts[0].includes('to left')) {
|
|
8218
|
-
x1 = '100%';
|
|
8187
|
+
x1 = '100%';
|
|
8188
|
+
x2 = '0%';
|
|
8189
|
+
y2 = '0%';
|
|
8190
|
+
stopsStartIdx = 1;
|
|
8219
8191
|
} else if (parts[0].includes('to top')) {
|
|
8220
|
-
y1 = '100%';
|
|
8192
|
+
y1 = '100%';
|
|
8193
|
+
y2 = '0%';
|
|
8194
|
+
stopsStartIdx = 1;
|
|
8221
8195
|
} else if (parts[0].includes('to bottom')) {
|
|
8222
|
-
y1 = '0%';
|
|
8196
|
+
y1 = '0%';
|
|
8197
|
+
y2 = '100%';
|
|
8198
|
+
stopsStartIdx = 1;
|
|
8223
8199
|
}
|
|
8224
8200
|
|
|
8225
8201
|
let stopsXML = '';
|
|
@@ -8294,81 +8270,84 @@ function generateBlurredSVG(w, h, color, radius, blurPx) {
|
|
|
8294
8270
|
};
|
|
8295
8271
|
}
|
|
8296
8272
|
|
|
8297
|
-
// src/image-processor.js
|
|
8298
|
-
|
|
8299
|
-
async function getProcessedImage(src, targetW, targetH, radius) {
|
|
8300
|
-
return new Promise((resolve) => {
|
|
8301
|
-
const img = new Image();
|
|
8302
|
-
img.crossOrigin = 'Anonymous'; // Critical for canvas manipulation
|
|
8303
|
-
|
|
8304
|
-
img.onload = () => {
|
|
8305
|
-
const canvas = document.createElement('canvas');
|
|
8306
|
-
// Double resolution for better quality
|
|
8307
|
-
const scale = 2;
|
|
8308
|
-
canvas.width = targetW * scale;
|
|
8309
|
-
canvas.height = targetH * scale;
|
|
8310
|
-
const ctx = canvas.getContext('2d');
|
|
8311
|
-
ctx.scale(scale, scale);
|
|
8312
|
-
|
|
8313
|
-
// Normalize radius input to an object { tl, tr, br, bl }
|
|
8314
|
-
let r = { tl: 0, tr: 0, br: 0, bl: 0 };
|
|
8315
|
-
if (typeof radius === 'number') {
|
|
8316
|
-
r = { tl: radius, tr: radius, br: radius, bl: radius };
|
|
8317
|
-
} else if (typeof radius === 'object' && radius !== null) {
|
|
8318
|
-
r = { ...r, ...radius }; // Merge with defaults
|
|
8319
|
-
}
|
|
8320
|
-
|
|
8321
|
-
// 1. Draw the Mask (Custom Shape with specific corners)
|
|
8322
|
-
ctx.beginPath();
|
|
8323
|
-
|
|
8324
|
-
// Border Radius Clamping Logic (CSS Spec)
|
|
8325
|
-
// Prevents corners from overlapping if radii are too large for the container
|
|
8326
|
-
const factor = Math.min(
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
8331
|
-
);
|
|
8332
|
-
|
|
8333
|
-
if (factor < 1) {
|
|
8334
|
-
r.tl *= factor;
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
|
|
8341
|
-
ctx.
|
|
8342
|
-
ctx.
|
|
8343
|
-
ctx.
|
|
8344
|
-
ctx.
|
|
8345
|
-
ctx.
|
|
8346
|
-
ctx.
|
|
8347
|
-
|
|
8348
|
-
ctx.
|
|
8349
|
-
ctx.
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
|
|
8353
|
-
ctx.
|
|
8354
|
-
|
|
8355
|
-
//
|
|
8356
|
-
|
|
8357
|
-
|
|
8358
|
-
|
|
8359
|
-
const
|
|
8360
|
-
const
|
|
8361
|
-
const
|
|
8362
|
-
const
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
|
|
8273
|
+
// src/image-processor.js
|
|
8274
|
+
|
|
8275
|
+
async function getProcessedImage(src, targetW, targetH, radius) {
|
|
8276
|
+
return new Promise((resolve) => {
|
|
8277
|
+
const img = new Image();
|
|
8278
|
+
img.crossOrigin = 'Anonymous'; // Critical for canvas manipulation
|
|
8279
|
+
|
|
8280
|
+
img.onload = () => {
|
|
8281
|
+
const canvas = document.createElement('canvas');
|
|
8282
|
+
// Double resolution for better quality
|
|
8283
|
+
const scale = 2;
|
|
8284
|
+
canvas.width = targetW * scale;
|
|
8285
|
+
canvas.height = targetH * scale;
|
|
8286
|
+
const ctx = canvas.getContext('2d');
|
|
8287
|
+
ctx.scale(scale, scale);
|
|
8288
|
+
|
|
8289
|
+
// Normalize radius input to an object { tl, tr, br, bl }
|
|
8290
|
+
let r = { tl: 0, tr: 0, br: 0, bl: 0 };
|
|
8291
|
+
if (typeof radius === 'number') {
|
|
8292
|
+
r = { tl: radius, tr: radius, br: radius, bl: radius };
|
|
8293
|
+
} else if (typeof radius === 'object' && radius !== null) {
|
|
8294
|
+
r = { ...r, ...radius }; // Merge with defaults
|
|
8295
|
+
}
|
|
8296
|
+
|
|
8297
|
+
// 1. Draw the Mask (Custom Shape with specific corners)
|
|
8298
|
+
ctx.beginPath();
|
|
8299
|
+
|
|
8300
|
+
// Border Radius Clamping Logic (CSS Spec)
|
|
8301
|
+
// Prevents corners from overlapping if radii are too large for the container
|
|
8302
|
+
const factor = Math.min(
|
|
8303
|
+
targetW / (r.tl + r.tr) || Infinity,
|
|
8304
|
+
targetH / (r.tr + r.br) || Infinity,
|
|
8305
|
+
targetW / (r.br + r.bl) || Infinity,
|
|
8306
|
+
targetH / (r.bl + r.tl) || Infinity
|
|
8307
|
+
);
|
|
8308
|
+
|
|
8309
|
+
if (factor < 1) {
|
|
8310
|
+
r.tl *= factor;
|
|
8311
|
+
r.tr *= factor;
|
|
8312
|
+
r.br *= factor;
|
|
8313
|
+
r.bl *= factor;
|
|
8314
|
+
}
|
|
8315
|
+
|
|
8316
|
+
// Draw path: Top-Left -> Top-Right -> Bottom-Right -> Bottom-Left
|
|
8317
|
+
ctx.moveTo(r.tl, 0);
|
|
8318
|
+
ctx.lineTo(targetW - r.tr, 0);
|
|
8319
|
+
ctx.arcTo(targetW, 0, targetW, r.tr, r.tr);
|
|
8320
|
+
ctx.lineTo(targetW, targetH - r.br);
|
|
8321
|
+
ctx.arcTo(targetW, targetH, targetW - r.br, targetH, r.br);
|
|
8322
|
+
ctx.lineTo(r.bl, targetH);
|
|
8323
|
+
ctx.arcTo(0, targetH, 0, targetH - r.bl, r.bl);
|
|
8324
|
+
ctx.lineTo(0, r.tl);
|
|
8325
|
+
ctx.arcTo(0, 0, r.tl, 0, r.tl);
|
|
8326
|
+
|
|
8327
|
+
ctx.closePath();
|
|
8328
|
+
ctx.fillStyle = '#000';
|
|
8329
|
+
ctx.fill();
|
|
8330
|
+
|
|
8331
|
+
// 2. Composite Source-In (Crops the next image draw to the mask)
|
|
8332
|
+
ctx.globalCompositeOperation = 'source-in';
|
|
8333
|
+
|
|
8334
|
+
// 3. Draw Image (Object Cover Logic)
|
|
8335
|
+
const wRatio = targetW / img.width;
|
|
8336
|
+
const hRatio = targetH / img.height;
|
|
8337
|
+
const maxRatio = Math.max(wRatio, hRatio);
|
|
8338
|
+
const renderW = img.width * maxRatio;
|
|
8339
|
+
const renderH = img.height * maxRatio;
|
|
8340
|
+
const renderX = (targetW - renderW) / 2;
|
|
8341
|
+
const renderY = (targetH - renderH) / 2;
|
|
8342
|
+
|
|
8343
|
+
ctx.drawImage(img, renderX, renderY, renderW, renderH);
|
|
8344
|
+
|
|
8345
|
+
resolve(canvas.toDataURL('image/png'));
|
|
8346
|
+
};
|
|
8347
|
+
|
|
8348
|
+
img.onerror = () => resolve(null);
|
|
8349
|
+
img.src = src;
|
|
8350
|
+
});
|
|
8372
8351
|
}
|
|
8373
8352
|
|
|
8374
8353
|
// src/index.js
|
|
@@ -8440,66 +8419,115 @@ async function processSlide(root, slide, pptx) {
|
|
|
8440
8419
|
};
|
|
8441
8420
|
|
|
8442
8421
|
const renderQueue = [];
|
|
8422
|
+
const asyncTasks = []; // Queue for heavy operations (Images, Canvas)
|
|
8443
8423
|
let domOrderCounter = 0;
|
|
8444
8424
|
|
|
8445
|
-
|
|
8425
|
+
// Sync Traversal Function
|
|
8426
|
+
function collect(node, parentZIndex) {
|
|
8446
8427
|
const order = domOrderCounter++;
|
|
8447
|
-
|
|
8428
|
+
|
|
8429
|
+
let currentZ = parentZIndex;
|
|
8430
|
+
let nodeStyle = null;
|
|
8431
|
+
const nodeType = node.nodeType;
|
|
8432
|
+
|
|
8433
|
+
if (nodeType === 1) {
|
|
8434
|
+
nodeStyle = window.getComputedStyle(node);
|
|
8435
|
+
// Optimization: Skip completely hidden elements immediately
|
|
8436
|
+
if (
|
|
8437
|
+
nodeStyle.display === 'none' ||
|
|
8438
|
+
nodeStyle.visibility === 'hidden' ||
|
|
8439
|
+
nodeStyle.opacity === '0'
|
|
8440
|
+
) {
|
|
8441
|
+
return;
|
|
8442
|
+
}
|
|
8443
|
+
if (nodeStyle.zIndex !== 'auto') {
|
|
8444
|
+
currentZ = parseInt(nodeStyle.zIndex);
|
|
8445
|
+
}
|
|
8446
|
+
}
|
|
8447
|
+
|
|
8448
|
+
// Prepare the item. If it needs async work, it returns a 'job'
|
|
8449
|
+
const result = prepareRenderItem(
|
|
8450
|
+
node,
|
|
8451
|
+
{ ...layoutConfig, root },
|
|
8452
|
+
order,
|
|
8453
|
+
pptx,
|
|
8454
|
+
currentZ,
|
|
8455
|
+
nodeStyle
|
|
8456
|
+
);
|
|
8457
|
+
|
|
8448
8458
|
if (result) {
|
|
8449
|
-
if (result.items)
|
|
8459
|
+
if (result.items) {
|
|
8460
|
+
// Push items immediately to queue (data might be missing but filled later)
|
|
8461
|
+
renderQueue.push(...result.items);
|
|
8462
|
+
}
|
|
8463
|
+
if (result.job) {
|
|
8464
|
+
// Push the promise-returning function to the task list
|
|
8465
|
+
asyncTasks.push(result.job);
|
|
8466
|
+
}
|
|
8450
8467
|
if (result.stopRecursion) return;
|
|
8451
8468
|
}
|
|
8452
|
-
|
|
8469
|
+
|
|
8470
|
+
// Recurse children synchronously
|
|
8471
|
+
const childNodes = node.childNodes;
|
|
8472
|
+
for (let i = 0; i < childNodes.length; i++) {
|
|
8473
|
+
collect(childNodes[i], currentZ);
|
|
8474
|
+
}
|
|
8453
8475
|
}
|
|
8454
8476
|
|
|
8455
|
-
|
|
8477
|
+
// 1. Traverse and build the structure (Fast)
|
|
8478
|
+
collect(root, 0);
|
|
8479
|
+
|
|
8480
|
+
// 2. Execute all heavy tasks in parallel (Fast)
|
|
8481
|
+
if (asyncTasks.length > 0) {
|
|
8482
|
+
await Promise.all(asyncTasks.map((task) => task()));
|
|
8483
|
+
}
|
|
8484
|
+
|
|
8485
|
+
// 3. Cleanup and Sort
|
|
8486
|
+
// Remove items that failed to generate data (marked with skip)
|
|
8487
|
+
const finalQueue = renderQueue.filter(
|
|
8488
|
+
(item) => !item.skip && (item.type !== 'image' || item.options.data)
|
|
8489
|
+
);
|
|
8456
8490
|
|
|
8457
|
-
|
|
8491
|
+
finalQueue.sort((a, b) => {
|
|
8458
8492
|
if (a.zIndex !== b.zIndex) return a.zIndex - b.zIndex;
|
|
8459
8493
|
return a.domOrder - b.domOrder;
|
|
8460
8494
|
});
|
|
8461
8495
|
|
|
8462
|
-
|
|
8496
|
+
// 4. Add to Slide
|
|
8497
|
+
for (const item of finalQueue) {
|
|
8463
8498
|
if (item.type === 'shape') slide.addShape(item.shapeType, item.options);
|
|
8464
8499
|
if (item.type === 'image') slide.addImage(item.options);
|
|
8465
8500
|
if (item.type === 'text') slide.addText(item.textParts, item.options);
|
|
8466
8501
|
}
|
|
8467
8502
|
}
|
|
8468
8503
|
|
|
8469
|
-
|
|
8504
|
+
/**
|
|
8505
|
+
* Optimized html2canvas wrapper
|
|
8506
|
+
* Now strictly captures the node itself, not the root.
|
|
8507
|
+
*/
|
|
8508
|
+
async function elementToCanvasImage(node, widthPx, heightPx) {
|
|
8470
8509
|
return new Promise((resolve) => {
|
|
8471
|
-
const width = Math.ceil(widthPx);
|
|
8472
|
-
const height = Math.ceil(heightPx);
|
|
8473
|
-
|
|
8474
|
-
if (width <= 0 || height <= 0) {
|
|
8475
|
-
resolve(null);
|
|
8476
|
-
return;
|
|
8477
|
-
}
|
|
8478
|
-
|
|
8510
|
+
const width = Math.max(Math.ceil(widthPx), 1);
|
|
8511
|
+
const height = Math.max(Math.ceil(heightPx), 1);
|
|
8479
8512
|
const style = window.getComputedStyle(node);
|
|
8480
8513
|
|
|
8481
|
-
|
|
8482
|
-
|
|
8483
|
-
height: root.scrollHeight,
|
|
8484
|
-
useCORS: true,
|
|
8485
|
-
allowTaint: true,
|
|
8514
|
+
// Optimized: Capture ONLY the specific node
|
|
8515
|
+
html2canvas(node, {
|
|
8486
8516
|
backgroundColor: null,
|
|
8517
|
+
logging: false,
|
|
8518
|
+
scale: 2, // Slight quality boost
|
|
8487
8519
|
})
|
|
8488
8520
|
.then((canvas) => {
|
|
8489
|
-
const rootCanvas = canvas;
|
|
8490
|
-
const nodeRect = node.getBoundingClientRect();
|
|
8491
|
-
const rootRect = root.getBoundingClientRect();
|
|
8492
|
-
const sourceX = nodeRect.left - rootRect.left;
|
|
8493
|
-
const sourceY = nodeRect.top - rootRect.top;
|
|
8494
|
-
|
|
8495
8521
|
const destCanvas = document.createElement('canvas');
|
|
8496
8522
|
destCanvas.width = width;
|
|
8497
8523
|
destCanvas.height = height;
|
|
8498
8524
|
const ctx = destCanvas.getContext('2d');
|
|
8499
8525
|
|
|
8500
|
-
|
|
8526
|
+
// Draw the captured canvas into our sized canvas
|
|
8527
|
+
// html2canvas might return a larger canvas if scale > 1, so we fit it
|
|
8528
|
+
ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height, 0, 0, width, height);
|
|
8501
8529
|
|
|
8502
|
-
//
|
|
8530
|
+
// Apply border radius clipping
|
|
8503
8531
|
let tl = parseFloat(style.borderTopLeftRadius) || 0;
|
|
8504
8532
|
let tr = parseFloat(style.borderTopRightRadius) || 0;
|
|
8505
8533
|
let br = parseFloat(style.borderBottomRightRadius) || 0;
|
|
@@ -8519,38 +8547,89 @@ async function elementToCanvasImage(node, widthPx, heightPx, root) {
|
|
|
8519
8547
|
bl *= f;
|
|
8520
8548
|
}
|
|
8521
8549
|
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8534
|
-
|
|
8550
|
+
if (tl + tr + br + bl > 0) {
|
|
8551
|
+
ctx.globalCompositeOperation = 'destination-in';
|
|
8552
|
+
ctx.beginPath();
|
|
8553
|
+
ctx.moveTo(tl, 0);
|
|
8554
|
+
ctx.lineTo(width - tr, 0);
|
|
8555
|
+
ctx.arcTo(width, 0, width, tr, tr);
|
|
8556
|
+
ctx.lineTo(width, height - br);
|
|
8557
|
+
ctx.arcTo(width, height, width - br, height, br);
|
|
8558
|
+
ctx.lineTo(bl, height);
|
|
8559
|
+
ctx.arcTo(0, height, 0, height - bl, bl);
|
|
8560
|
+
ctx.lineTo(0, tl);
|
|
8561
|
+
ctx.arcTo(0, 0, tl, 0, tl);
|
|
8562
|
+
ctx.closePath();
|
|
8563
|
+
ctx.fill();
|
|
8564
|
+
}
|
|
8535
8565
|
|
|
8536
8566
|
resolve(destCanvas.toDataURL('image/png'));
|
|
8537
8567
|
})
|
|
8538
|
-
.catch(() =>
|
|
8568
|
+
.catch((e) => {
|
|
8569
|
+
console.warn('Canvas capture failed for node', node, e);
|
|
8570
|
+
resolve(null);
|
|
8571
|
+
});
|
|
8539
8572
|
});
|
|
8540
8573
|
}
|
|
8541
8574
|
|
|
8542
|
-
|
|
8575
|
+
/**
|
|
8576
|
+
* Replaces createRenderItem.
|
|
8577
|
+
* Returns { items: [], job: () => Promise, stopRecursion: boolean }
|
|
8578
|
+
*/
|
|
8579
|
+
function prepareRenderItem(node, config, domOrder, pptx, effectiveZIndex, computedStyle) {
|
|
8580
|
+
// 1. Text Node Handling
|
|
8581
|
+
if (node.nodeType === 3) {
|
|
8582
|
+
const textContent = node.nodeValue.trim();
|
|
8583
|
+
if (!textContent) return null;
|
|
8584
|
+
|
|
8585
|
+
const parent = node.parentElement;
|
|
8586
|
+
if (!parent) return null;
|
|
8587
|
+
|
|
8588
|
+
if (isTextContainer(parent)) return null; // Parent handles it
|
|
8589
|
+
|
|
8590
|
+
const range = document.createRange();
|
|
8591
|
+
range.selectNode(node);
|
|
8592
|
+
const rect = range.getBoundingClientRect();
|
|
8593
|
+
range.detach();
|
|
8594
|
+
|
|
8595
|
+
const style = window.getComputedStyle(parent);
|
|
8596
|
+
const widthPx = rect.width;
|
|
8597
|
+
const heightPx = rect.height;
|
|
8598
|
+
const unrotatedW = widthPx * PX_TO_INCH * config.scale;
|
|
8599
|
+
const unrotatedH = heightPx * PX_TO_INCH * config.scale;
|
|
8600
|
+
|
|
8601
|
+
const x = config.offX + (rect.left - config.rootX) * PX_TO_INCH * config.scale;
|
|
8602
|
+
const y = config.offY + (rect.top - config.rootY) * PX_TO_INCH * config.scale;
|
|
8603
|
+
|
|
8604
|
+
return {
|
|
8605
|
+
items: [
|
|
8606
|
+
{
|
|
8607
|
+
type: 'text',
|
|
8608
|
+
zIndex: effectiveZIndex,
|
|
8609
|
+
domOrder,
|
|
8610
|
+
textParts: [
|
|
8611
|
+
{
|
|
8612
|
+
text: textContent,
|
|
8613
|
+
options: getTextStyle(style, config.scale),
|
|
8614
|
+
},
|
|
8615
|
+
],
|
|
8616
|
+
options: { x, y, w: unrotatedW, h: unrotatedH, margin: 0, autoFit: false },
|
|
8617
|
+
},
|
|
8618
|
+
],
|
|
8619
|
+
stopRecursion: false,
|
|
8620
|
+
};
|
|
8621
|
+
}
|
|
8622
|
+
|
|
8543
8623
|
if (node.nodeType !== 1) return null;
|
|
8544
|
-
const style =
|
|
8545
|
-
if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0')
|
|
8546
|
-
return null;
|
|
8624
|
+
const style = computedStyle; // Use pre-computed style
|
|
8547
8625
|
|
|
8548
8626
|
const rect = node.getBoundingClientRect();
|
|
8549
8627
|
if (rect.width < 0.5 || rect.height < 0.5) return null;
|
|
8550
8628
|
|
|
8551
|
-
const zIndex =
|
|
8629
|
+
const zIndex = effectiveZIndex;
|
|
8552
8630
|
const rotation = getRotation(style.transform);
|
|
8553
8631
|
const elementOpacity = parseFloat(style.opacity);
|
|
8632
|
+
const safeOpacity = isNaN(elementOpacity) ? 1 : elementOpacity;
|
|
8554
8633
|
|
|
8555
8634
|
const widthPx = node.offsetWidth || rect.width;
|
|
8556
8635
|
const heightPx = node.offsetHeight || rect.height;
|
|
@@ -8566,21 +8645,31 @@ async function createRenderItem(node, config, domOrder, pptx) {
|
|
|
8566
8645
|
|
|
8567
8646
|
const items = [];
|
|
8568
8647
|
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
|
|
8573
|
-
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
8648
|
+
// --- ASYNC JOB: SVGs / Icons ---
|
|
8649
|
+
if (
|
|
8650
|
+
node.nodeName.toUpperCase() === 'SVG' ||
|
|
8651
|
+
node.tagName.includes('-') ||
|
|
8652
|
+
node.tagName === 'ION-ICON'
|
|
8653
|
+
) {
|
|
8654
|
+
const item = {
|
|
8655
|
+
type: 'image',
|
|
8656
|
+
zIndex,
|
|
8657
|
+
domOrder,
|
|
8658
|
+
options: { x, y, w, h, rotate: rotation, data: null }, // Data null initially
|
|
8659
|
+
};
|
|
8660
|
+
|
|
8661
|
+
// Create Job
|
|
8662
|
+
const job = async () => {
|
|
8663
|
+
const pngData = await elementToCanvasImage(node, widthPx, heightPx);
|
|
8664
|
+
if (pngData) item.options.data = pngData;
|
|
8665
|
+
else item.skip = true;
|
|
8666
|
+
};
|
|
8667
|
+
|
|
8668
|
+
return { items: [item], job, stopRecursion: true };
|
|
8579
8669
|
}
|
|
8580
8670
|
|
|
8581
|
-
// ---
|
|
8671
|
+
// --- ASYNC JOB: IMG Tags ---
|
|
8582
8672
|
if (node.tagName === 'IMG') {
|
|
8583
|
-
// Extract individual corner radii
|
|
8584
8673
|
let radii = {
|
|
8585
8674
|
tl: parseFloat(style.borderTopLeftRadius) || 0,
|
|
8586
8675
|
tr: parseFloat(style.borderTopRightRadius) || 0,
|
|
@@ -8589,8 +8678,6 @@ async function createRenderItem(node, config, domOrder, pptx) {
|
|
|
8589
8678
|
};
|
|
8590
8679
|
|
|
8591
8680
|
const hasAnyRadius = radii.tl > 0 || radii.tr > 0 || radii.br > 0 || radii.bl > 0;
|
|
8592
|
-
|
|
8593
|
-
// Fallback: Check parent if image has no specific radius but parent clips it
|
|
8594
8681
|
if (!hasAnyRadius) {
|
|
8595
8682
|
const parent = node.parentElement;
|
|
8596
8683
|
const parentStyle = window.getComputedStyle(parent);
|
|
@@ -8601,10 +8688,6 @@ async function createRenderItem(node, config, domOrder, pptx) {
|
|
|
8601
8688
|
br: parseFloat(parentStyle.borderBottomRightRadius) || 0,
|
|
8602
8689
|
bl: parseFloat(parentStyle.borderBottomLeftRadius) || 0,
|
|
8603
8690
|
};
|
|
8604
|
-
// Simple heuristic: If image takes up full size of parent, inherit radii.
|
|
8605
|
-
// For complex grids (like slide-1), this blindly applies parent radius.
|
|
8606
|
-
// In a perfect world, we'd calculate intersection, but for now we apply parent radius
|
|
8607
|
-
// if the image is close to the parent's size, effectively masking it.
|
|
8608
8691
|
const pRect = parent.getBoundingClientRect();
|
|
8609
8692
|
if (Math.abs(pRect.width - rect.width) < 5 && Math.abs(pRect.height - rect.height) < 5) {
|
|
8610
8693
|
radii = pRadii;
|
|
@@ -8612,19 +8695,23 @@ async function createRenderItem(node, config, domOrder, pptx) {
|
|
|
8612
8695
|
}
|
|
8613
8696
|
}
|
|
8614
8697
|
|
|
8615
|
-
const
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
|
|
8698
|
+
const item = {
|
|
8699
|
+
type: 'image',
|
|
8700
|
+
zIndex,
|
|
8701
|
+
domOrder,
|
|
8702
|
+
options: { x, y, w, h, rotate: rotation, data: null },
|
|
8703
|
+
};
|
|
8704
|
+
|
|
8705
|
+
const job = async () => {
|
|
8706
|
+
const processed = await getProcessedImage(node.src, widthPx, heightPx, radii);
|
|
8707
|
+
if (processed) item.options.data = processed;
|
|
8708
|
+
else item.skip = true;
|
|
8709
|
+
};
|
|
8710
|
+
|
|
8711
|
+
return { items: [item], job, stopRecursion: true };
|
|
8624
8712
|
}
|
|
8625
|
-
// --- UPDATED IMG BLOCK END ---
|
|
8626
8713
|
|
|
8627
|
-
// Radii
|
|
8714
|
+
// Radii logic
|
|
8628
8715
|
const borderRadiusValue = parseFloat(style.borderRadius) || 0;
|
|
8629
8716
|
const borderBottomLeftRadius = parseFloat(style.borderBottomLeftRadius) || 0;
|
|
8630
8717
|
const borderBottomRightRadius = parseFloat(style.borderBottomRightRadius) || 0;
|
|
@@ -8642,25 +8729,30 @@ async function createRenderItem(node, config, domOrder, pptx) {
|
|
|
8642
8729
|
borderTopLeftRadius ||
|
|
8643
8730
|
borderTopRightRadius));
|
|
8644
8731
|
|
|
8645
|
-
//
|
|
8732
|
+
// --- ASYNC JOB: Clipped Divs via Canvas ---
|
|
8646
8733
|
if (hasPartialBorderRadius && isClippedByParent(node)) {
|
|
8647
8734
|
const marginLeft = parseFloat(style.marginLeft) || 0;
|
|
8648
8735
|
const marginTop = parseFloat(style.marginTop) || 0;
|
|
8649
8736
|
x += marginLeft * PX_TO_INCH * config.scale;
|
|
8650
8737
|
y += marginTop * PX_TO_INCH * config.scale;
|
|
8651
8738
|
|
|
8652
|
-
const
|
|
8653
|
-
|
|
8654
|
-
|
|
8655
|
-
|
|
8656
|
-
|
|
8657
|
-
|
|
8658
|
-
|
|
8659
|
-
|
|
8660
|
-
|
|
8661
|
-
|
|
8739
|
+
const item = {
|
|
8740
|
+
type: 'image',
|
|
8741
|
+
zIndex,
|
|
8742
|
+
domOrder,
|
|
8743
|
+
options: { x, y, w, h, rotate: rotation, data: null },
|
|
8744
|
+
};
|
|
8745
|
+
|
|
8746
|
+
const job = async () => {
|
|
8747
|
+
const canvasImageData = await elementToCanvasImage(node, widthPx, heightPx);
|
|
8748
|
+
if (canvasImageData) item.options.data = canvasImageData;
|
|
8749
|
+
else item.skip = true;
|
|
8750
|
+
};
|
|
8751
|
+
|
|
8752
|
+
return { items: [item], job, stopRecursion: true };
|
|
8662
8753
|
}
|
|
8663
8754
|
|
|
8755
|
+
// --- SYNC: Standard CSS Extraction ---
|
|
8664
8756
|
const bgColorObj = parseColor(style.backgroundColor);
|
|
8665
8757
|
const bgClip = style.webkitBackgroundClip || style.backgroundClip;
|
|
8666
8758
|
const isBgClipText = bgClip === 'text';
|
|
@@ -8699,7 +8791,7 @@ async function createRenderItem(node, config, domOrder, pptx) {
|
|
|
8699
8791
|
x -= bulletShift;
|
|
8700
8792
|
w += bulletShift;
|
|
8701
8793
|
textParts.push({
|
|
8702
|
-
text: '
|
|
8794
|
+
text: ' ',
|
|
8703
8795
|
options: {
|
|
8704
8796
|
color: parseColor(style.color).hex || '000000',
|
|
8705
8797
|
fontSize: fontSizePt,
|
|
@@ -8805,7 +8897,6 @@ async function createRenderItem(node, config, domOrder, pptx) {
|
|
|
8805
8897
|
});
|
|
8806
8898
|
}
|
|
8807
8899
|
if (hasCompositeBorder) {
|
|
8808
|
-
// Add border shapes after the main background
|
|
8809
8900
|
const borderItems = createCompositeBorderItems(
|
|
8810
8901
|
borderInfo.sides,
|
|
8811
8902
|
x,
|
|
@@ -8825,7 +8916,7 @@ async function createRenderItem(node, config, domOrder, pptx) {
|
|
|
8825
8916
|
hasShadow ||
|
|
8826
8917
|
textPayload
|
|
8827
8918
|
) {
|
|
8828
|
-
const finalAlpha =
|
|
8919
|
+
const finalAlpha = safeOpacity * bgColorObj.opacity;
|
|
8829
8920
|
const transparency = (1 - finalAlpha) * 100;
|
|
8830
8921
|
const useSolidFill = bgColorObj.hex && !isImageWrapper;
|
|
8831
8922
|
|
|
@@ -8847,14 +8938,7 @@ async function createRenderItem(node, config, domOrder, pptx) {
|
|
|
8847
8938
|
type: 'image',
|
|
8848
8939
|
zIndex,
|
|
8849
8940
|
domOrder,
|
|
8850
|
-
options: {
|
|
8851
|
-
data: shapeSvg,
|
|
8852
|
-
x,
|
|
8853
|
-
y,
|
|
8854
|
-
w,
|
|
8855
|
-
h,
|
|
8856
|
-
rotate: rotation,
|
|
8857
|
-
},
|
|
8941
|
+
options: { data: shapeSvg, x, y, w, h, rotate: rotation },
|
|
8858
8942
|
});
|
|
8859
8943
|
} else {
|
|
8860
8944
|
const shapeOpts = {
|
|
@@ -8869,9 +8953,7 @@ async function createRenderItem(node, config, domOrder, pptx) {
|
|
|
8869
8953
|
line: hasUniformBorder ? borderInfo.options : null,
|
|
8870
8954
|
};
|
|
8871
8955
|
|
|
8872
|
-
if (hasShadow)
|
|
8873
|
-
shapeOpts.shadow = getVisibleShadow(shadowStr, config.scale);
|
|
8874
|
-
}
|
|
8956
|
+
if (hasShadow) shapeOpts.shadow = getVisibleShadow(shadowStr, config.scale);
|
|
8875
8957
|
|
|
8876
8958
|
const borderRadius = parseFloat(style.borderRadius) || 0;
|
|
8877
8959
|
const aspectRatio = Math.max(widthPx, heightPx) / Math.min(widthPx, heightPx);
|
|
@@ -8934,77 +9016,49 @@ async function createRenderItem(node, config, domOrder, pptx) {
|
|
|
8934
9016
|
return { items, stopRecursion: !!textPayload };
|
|
8935
9017
|
}
|
|
8936
9018
|
|
|
8937
|
-
/**
|
|
8938
|
-
* Helper function to create individual border shapes
|
|
8939
|
-
*/
|
|
8940
9019
|
function createCompositeBorderItems(sides, x, y, w, h, scale, zIndex, domOrder) {
|
|
8941
9020
|
const items = [];
|
|
8942
9021
|
const pxToInch = 1 / 96;
|
|
9022
|
+
const common = { zIndex: zIndex + 1, domOrder, shapeType: 'rect' };
|
|
8943
9023
|
|
|
8944
|
-
|
|
8945
|
-
if (sides.top.width > 0) {
|
|
9024
|
+
if (sides.top.width > 0)
|
|
8946
9025
|
items.push({
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
domOrder,
|
|
8950
|
-
shapeType: 'rect',
|
|
8951
|
-
options: {
|
|
8952
|
-
x: x,
|
|
8953
|
-
y: y,
|
|
8954
|
-
w: w,
|
|
8955
|
-
h: sides.top.width * pxToInch * scale,
|
|
8956
|
-
fill: { color: sides.top.color },
|
|
8957
|
-
},
|
|
9026
|
+
...common,
|
|
9027
|
+
options: { x, y, w, h: sides.top.width * pxToInch * scale, fill: { color: sides.top.color } },
|
|
8958
9028
|
});
|
|
8959
|
-
|
|
8960
|
-
// RIGHT BORDER
|
|
8961
|
-
if (sides.right.width > 0) {
|
|
9029
|
+
if (sides.right.width > 0)
|
|
8962
9030
|
items.push({
|
|
8963
|
-
|
|
8964
|
-
zIndex: zIndex + 1,
|
|
8965
|
-
domOrder,
|
|
8966
|
-
shapeType: 'rect',
|
|
9031
|
+
...common,
|
|
8967
9032
|
options: {
|
|
8968
9033
|
x: x + w - sides.right.width * pxToInch * scale,
|
|
8969
|
-
y
|
|
9034
|
+
y,
|
|
8970
9035
|
w: sides.right.width * pxToInch * scale,
|
|
8971
|
-
h
|
|
9036
|
+
h,
|
|
8972
9037
|
fill: { color: sides.right.color },
|
|
8973
9038
|
},
|
|
8974
9039
|
});
|
|
8975
|
-
|
|
8976
|
-
// BOTTOM BORDER
|
|
8977
|
-
if (sides.bottom.width > 0) {
|
|
9040
|
+
if (sides.bottom.width > 0)
|
|
8978
9041
|
items.push({
|
|
8979
|
-
|
|
8980
|
-
zIndex: zIndex + 1,
|
|
8981
|
-
domOrder,
|
|
8982
|
-
shapeType: 'rect',
|
|
9042
|
+
...common,
|
|
8983
9043
|
options: {
|
|
8984
|
-
x
|
|
9044
|
+
x,
|
|
8985
9045
|
y: y + h - sides.bottom.width * pxToInch * scale,
|
|
8986
|
-
w
|
|
9046
|
+
w,
|
|
8987
9047
|
h: sides.bottom.width * pxToInch * scale,
|
|
8988
9048
|
fill: { color: sides.bottom.color },
|
|
8989
9049
|
},
|
|
8990
9050
|
});
|
|
8991
|
-
|
|
8992
|
-
// LEFT BORDER
|
|
8993
|
-
if (sides.left.width > 0) {
|
|
9051
|
+
if (sides.left.width > 0)
|
|
8994
9052
|
items.push({
|
|
8995
|
-
|
|
8996
|
-
zIndex: zIndex + 1,
|
|
8997
|
-
domOrder,
|
|
8998
|
-
shapeType: 'rect',
|
|
9053
|
+
...common,
|
|
8999
9054
|
options: {
|
|
9000
|
-
x
|
|
9001
|
-
y
|
|
9055
|
+
x,
|
|
9056
|
+
y,
|
|
9002
9057
|
w: sides.left.width * pxToInch * scale,
|
|
9003
|
-
h
|
|
9058
|
+
h,
|
|
9004
9059
|
fill: { color: sides.left.color },
|
|
9005
9060
|
},
|
|
9006
9061
|
});
|
|
9007
|
-
}
|
|
9008
9062
|
|
|
9009
9063
|
return items;
|
|
9010
9064
|
}
|