json2pptx 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +22 -1
- package/dist/index.d.ts +22 -1
- package/dist/index.js +279 -44
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +279 -44
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -58,6 +58,7 @@ type ShapeElement = BaseElement<'shape'> & {
|
|
|
58
58
|
path?: string;
|
|
59
59
|
viewBox?: [number, number];
|
|
60
60
|
fill?: string;
|
|
61
|
+
pattern?: string;
|
|
61
62
|
text?: TextContent;
|
|
62
63
|
};
|
|
63
64
|
type LineElement = BaseElement<'line'> & {
|
|
@@ -72,7 +73,27 @@ type LineElement = BaseElement<'line'> & {
|
|
|
72
73
|
style?: 'solid' | 'dashed' | 'dotted';
|
|
73
74
|
points?: [unknown, unknown];
|
|
74
75
|
};
|
|
75
|
-
type
|
|
76
|
+
type TableCellStyle = {
|
|
77
|
+
fontname?: string;
|
|
78
|
+
color?: string;
|
|
79
|
+
align?: string;
|
|
80
|
+
fontsize?: string;
|
|
81
|
+
backcolor?: string;
|
|
82
|
+
};
|
|
83
|
+
type TableCellData = {
|
|
84
|
+
id?: string;
|
|
85
|
+
colspan?: number;
|
|
86
|
+
rowspan?: number;
|
|
87
|
+
text?: string;
|
|
88
|
+
style?: TableCellStyle;
|
|
89
|
+
};
|
|
90
|
+
type TableElement = BaseElement<'table'> & {
|
|
91
|
+
type: 'table';
|
|
92
|
+
colWidths?: number[];
|
|
93
|
+
data?: TableCellData[][];
|
|
94
|
+
cellMinHeight?: number;
|
|
95
|
+
};
|
|
96
|
+
type SlideElement = TextElement | ImageElement | ShapeElement | LineElement | TableElement;
|
|
76
97
|
type SlideBackground = {
|
|
77
98
|
color?: string;
|
|
78
99
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -58,6 +58,7 @@ type ShapeElement = BaseElement<'shape'> & {
|
|
|
58
58
|
path?: string;
|
|
59
59
|
viewBox?: [number, number];
|
|
60
60
|
fill?: string;
|
|
61
|
+
pattern?: string;
|
|
61
62
|
text?: TextContent;
|
|
62
63
|
};
|
|
63
64
|
type LineElement = BaseElement<'line'> & {
|
|
@@ -72,7 +73,27 @@ type LineElement = BaseElement<'line'> & {
|
|
|
72
73
|
style?: 'solid' | 'dashed' | 'dotted';
|
|
73
74
|
points?: [unknown, unknown];
|
|
74
75
|
};
|
|
75
|
-
type
|
|
76
|
+
type TableCellStyle = {
|
|
77
|
+
fontname?: string;
|
|
78
|
+
color?: string;
|
|
79
|
+
align?: string;
|
|
80
|
+
fontsize?: string;
|
|
81
|
+
backcolor?: string;
|
|
82
|
+
};
|
|
83
|
+
type TableCellData = {
|
|
84
|
+
id?: string;
|
|
85
|
+
colspan?: number;
|
|
86
|
+
rowspan?: number;
|
|
87
|
+
text?: string;
|
|
88
|
+
style?: TableCellStyle;
|
|
89
|
+
};
|
|
90
|
+
type TableElement = BaseElement<'table'> & {
|
|
91
|
+
type: 'table';
|
|
92
|
+
colWidths?: number[];
|
|
93
|
+
data?: TableCellData[][];
|
|
94
|
+
cellMinHeight?: number;
|
|
95
|
+
};
|
|
96
|
+
type SlideElement = TextElement | ImageElement | ShapeElement | LineElement | TableElement;
|
|
76
97
|
type SlideBackground = {
|
|
77
98
|
color?: string;
|
|
78
99
|
};
|
package/dist/index.js
CHANGED
|
@@ -27,7 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
27
|
));
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
29
|
|
|
30
|
-
//
|
|
30
|
+
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
ENABLE_DECK_JSON: () => ENABLE_DECK_JSON,
|
|
@@ -43,7 +43,7 @@ var import_pptxgenjs = __toESM(require("pptxgenjs"));
|
|
|
43
43
|
var import_jszip = __toESM(require("jszip"));
|
|
44
44
|
var import_tinycolor2 = __toESM(require("tinycolor2"));
|
|
45
45
|
|
|
46
|
-
//
|
|
46
|
+
// src/resolveImageData.ts
|
|
47
47
|
var dataUrlRegex = /^data:image\/[^;]+;base64,/;
|
|
48
48
|
var mimeFromPath = (path) => {
|
|
49
49
|
const lower = path.toLowerCase();
|
|
@@ -97,7 +97,7 @@ var resolveImageData = async (src) => {
|
|
|
97
97
|
return `data:${contentType};base64,${base64}`;
|
|
98
98
|
};
|
|
99
99
|
|
|
100
|
-
//
|
|
100
|
+
// src/htmlParser/tags.ts
|
|
101
101
|
var childlessTags = ["style", "script", "template"];
|
|
102
102
|
var closingTags = [
|
|
103
103
|
"html",
|
|
@@ -146,7 +146,7 @@ var voidTags = [
|
|
|
146
146
|
"wbr"
|
|
147
147
|
];
|
|
148
148
|
|
|
149
|
-
//
|
|
149
|
+
// src/htmlParser/lexer.ts
|
|
150
150
|
var jumpPosition = (state, end) => {
|
|
151
151
|
const len = end - state.position;
|
|
152
152
|
movePosition(state, len);
|
|
@@ -379,7 +379,7 @@ var lexer = (str) => {
|
|
|
379
379
|
return state.tokens;
|
|
380
380
|
};
|
|
381
381
|
|
|
382
|
-
//
|
|
382
|
+
// src/htmlParser/parser.ts
|
|
383
383
|
var parser = (tokens) => {
|
|
384
384
|
const root = { tagName: null, children: [] };
|
|
385
385
|
const state = { tokens, cursor: 0, stack: [root] };
|
|
@@ -484,7 +484,7 @@ var parse = (state) => {
|
|
|
484
484
|
state.cursor = cursor;
|
|
485
485
|
};
|
|
486
486
|
|
|
487
|
-
//
|
|
487
|
+
// src/htmlParser/format.ts
|
|
488
488
|
var splitHead = (str, sep) => {
|
|
489
489
|
const idx = str.indexOf(sep);
|
|
490
490
|
if (idx === -1) return [str];
|
|
@@ -527,14 +527,14 @@ var format = (nodes) => {
|
|
|
527
527
|
});
|
|
528
528
|
};
|
|
529
529
|
|
|
530
|
-
//
|
|
530
|
+
// src/htmlParser/index.ts
|
|
531
531
|
var toAST = (str) => {
|
|
532
532
|
const tokens = lexer(str);
|
|
533
533
|
const nodes = parser(tokens);
|
|
534
534
|
return format(nodes);
|
|
535
535
|
};
|
|
536
536
|
|
|
537
|
-
//
|
|
537
|
+
// src/svgPathParser.ts
|
|
538
538
|
var import_svg_pathdata = require("svg-pathdata");
|
|
539
539
|
var import_svg_arc_to_cubic_bezier = __toESM(require("svg-arc-to-cubic-bezier"));
|
|
540
540
|
var typeMap = {
|
|
@@ -624,7 +624,7 @@ var toPoints = (d) => {
|
|
|
624
624
|
return points;
|
|
625
625
|
};
|
|
626
626
|
|
|
627
|
-
//
|
|
627
|
+
// src/element.ts
|
|
628
628
|
var getElementRange = (element) => {
|
|
629
629
|
var _a, _b, _c, _d, _e, _f;
|
|
630
630
|
let minX = 0;
|
|
@@ -677,7 +677,7 @@ var getLineElementPath = (element) => {
|
|
|
677
677
|
return `M${start} L${end}`;
|
|
678
678
|
};
|
|
679
679
|
|
|
680
|
-
//
|
|
680
|
+
// src/index.ts
|
|
681
681
|
var DEFAULT_WIDTH = 1e3;
|
|
682
682
|
var DEFAULT_HEIGHT = 562.5;
|
|
683
683
|
var DEFAULT_FONT_SIZE = 16;
|
|
@@ -876,6 +876,148 @@ var dashTypeMap = {
|
|
|
876
876
|
dashed: "dash",
|
|
877
877
|
dotted: "sysDot"
|
|
878
878
|
};
|
|
879
|
+
var stripFillTags = (value) => value.replace(/<a:solidFill>[\s\S]*?<\/a:solidFill>/g, "").replace(/<a:gradFill>[\s\S]*?<\/a:gradFill>/g, "").replace(/<a:blipFill>[\s\S]*?<\/a:blipFill>/g, "").replace(/<a:noFill\s*\/>/g, "").replace(/<a:noFill><\/a:noFill>/g, "");
|
|
880
|
+
var applyPatternFill = (slideXml, objectName, relId) => {
|
|
881
|
+
const nameToken = `name="${objectName}"`;
|
|
882
|
+
let cursor = 0;
|
|
883
|
+
let result = slideXml;
|
|
884
|
+
while (true) {
|
|
885
|
+
const nameIndex = result.indexOf(nameToken, cursor);
|
|
886
|
+
if (nameIndex === -1) break;
|
|
887
|
+
const spStart = result.lastIndexOf("<p:sp", nameIndex);
|
|
888
|
+
const spEnd = result.indexOf("</p:sp>", nameIndex);
|
|
889
|
+
if (spStart === -1 || spEnd === -1) break;
|
|
890
|
+
const spXml = result.slice(spStart, spEnd + "</p:sp>".length);
|
|
891
|
+
const spPrStart = spXml.indexOf("<p:spPr>");
|
|
892
|
+
const spPrEnd = spXml.indexOf("</p:spPr>");
|
|
893
|
+
if (spPrStart === -1 || spPrEnd === -1) {
|
|
894
|
+
cursor = spEnd + 1;
|
|
895
|
+
continue;
|
|
896
|
+
}
|
|
897
|
+
const spPrOpenEnd = spXml.indexOf(">", spPrStart);
|
|
898
|
+
const spPrInner = spXml.slice(spPrOpenEnd + 1, spPrEnd);
|
|
899
|
+
const cleanedInner = stripFillTags(spPrInner);
|
|
900
|
+
const blipFill = `<a:blipFill><a:blip r:embed="${relId}"/><a:srcRect/><a:stretch><a:fillRect/></a:stretch></a:blipFill>`;
|
|
901
|
+
let nextInner = cleanedInner;
|
|
902
|
+
if (cleanedInner.includes("</a:custGeom>")) {
|
|
903
|
+
nextInner = cleanedInner.replace("</a:custGeom>", `</a:custGeom>${blipFill}`);
|
|
904
|
+
} else {
|
|
905
|
+
const lnIndex = cleanedInner.indexOf("<a:ln");
|
|
906
|
+
nextInner = lnIndex === -1 ? `${cleanedInner}${blipFill}` : `${cleanedInner.slice(0, lnIndex)}${blipFill}${cleanedInner.slice(lnIndex)}`;
|
|
907
|
+
}
|
|
908
|
+
const updatedSpXml = spXml.slice(0, spPrOpenEnd + 1) + nextInner + spXml.slice(spPrEnd);
|
|
909
|
+
result = result.slice(0, spStart) + updatedSpXml + result.slice(spEnd + "</p:sp>".length);
|
|
910
|
+
cursor = spStart + updatedSpXml.length;
|
|
911
|
+
}
|
|
912
|
+
return result;
|
|
913
|
+
};
|
|
914
|
+
var parseDataUrlImage = (dataUrl) => {
|
|
915
|
+
var _a;
|
|
916
|
+
const match = dataUrl.match(/^data:(image\/[^;]+);base64,(.+)$/);
|
|
917
|
+
if (!match) return null;
|
|
918
|
+
const mime = match[1];
|
|
919
|
+
const data = match[2];
|
|
920
|
+
const extMap = {
|
|
921
|
+
"image/jpeg": "jpeg",
|
|
922
|
+
"image/jpg": "jpg",
|
|
923
|
+
"image/png": "png",
|
|
924
|
+
"image/gif": "gif",
|
|
925
|
+
"image/svg+xml": "svg",
|
|
926
|
+
"image/webp": "webp",
|
|
927
|
+
"image/bmp": "bmp"
|
|
928
|
+
};
|
|
929
|
+
return { mime, data, ext: (_a = extMap[mime]) != null ? _a : "png" };
|
|
930
|
+
};
|
|
931
|
+
var normalizeFontName = (value) => value ? value.replace(/^"+|"+$/g, "") : void 0;
|
|
932
|
+
var parseFontSize = (value) => {
|
|
933
|
+
if (!value) return void 0;
|
|
934
|
+
const size = Number.parseFloat(value);
|
|
935
|
+
return Number.isFinite(size) ? size : void 0;
|
|
936
|
+
};
|
|
937
|
+
var parseTableColor = (value) => {
|
|
938
|
+
if (!value) return void 0;
|
|
939
|
+
const normalized = value.trim();
|
|
940
|
+
if (!normalized) return void 0;
|
|
941
|
+
const c = formatColor(normalized);
|
|
942
|
+
return {
|
|
943
|
+
color: c.color.replace("#", ""),
|
|
944
|
+
transparency: (1 - c.alpha) * 100
|
|
945
|
+
};
|
|
946
|
+
};
|
|
947
|
+
var isPlaceholderCell = (cell) => {
|
|
948
|
+
var _a, _b, _c;
|
|
949
|
+
if (!cell) return false;
|
|
950
|
+
const colspan = (_a = cell.colspan) != null ? _a : 1;
|
|
951
|
+
const rowspan = (_b = cell.rowspan) != null ? _b : 1;
|
|
952
|
+
if (colspan !== 1 || rowspan !== 1) return false;
|
|
953
|
+
const text = (_c = cell.text) != null ? _c : "";
|
|
954
|
+
const style = cell.style;
|
|
955
|
+
const hasStyle = Boolean(style == null ? void 0 : style.fontname) || Boolean(style == null ? void 0 : style.fontsize) || Boolean(style == null ? void 0 : style.color) || Boolean(style == null ? void 0 : style.backcolor);
|
|
956
|
+
return text.trim() === "" && !hasStyle;
|
|
957
|
+
};
|
|
958
|
+
var buildTableRows = (element, ratioPx2Pt) => {
|
|
959
|
+
var _a, _b;
|
|
960
|
+
const data = element.data;
|
|
961
|
+
if (!data || !data.length) return [];
|
|
962
|
+
const colCount = (_b = (_a = element.colWidths) == null ? void 0 : _a.length) != null ? _b : Math.max(...data.map((row) => row.length));
|
|
963
|
+
const rows = [];
|
|
964
|
+
const skip = new Array(colCount).fill(0);
|
|
965
|
+
data.forEach((row, rowIndex) => {
|
|
966
|
+
var _a2, _b2, _c, _d;
|
|
967
|
+
const cells = [];
|
|
968
|
+
let colIndex = 0;
|
|
969
|
+
let cellIndex = 0;
|
|
970
|
+
while (colIndex < colCount) {
|
|
971
|
+
if (skip[colIndex] > 0) {
|
|
972
|
+
skip[colIndex] -= 1;
|
|
973
|
+
if (isPlaceholderCell(row[cellIndex])) {
|
|
974
|
+
cellIndex += 1;
|
|
975
|
+
}
|
|
976
|
+
colIndex += 1;
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
const cell = row[cellIndex];
|
|
980
|
+
if (!cell) break;
|
|
981
|
+
cellIndex += 1;
|
|
982
|
+
const colSpan = (_a2 = cell.colspan) != null ? _a2 : 1;
|
|
983
|
+
const rowSpan = (_b2 = cell.rowspan) != null ? _b2 : 1;
|
|
984
|
+
if (rowSpan > 1) {
|
|
985
|
+
for (let i = 0; i < colSpan; i += 1) {
|
|
986
|
+
skip[colIndex + i] = rowSpan - 1;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
if (colSpan > 1) {
|
|
990
|
+
for (let i = 0; i < colSpan - 1; i += 1) {
|
|
991
|
+
if (isPlaceholderCell(row[cellIndex])) {
|
|
992
|
+
cellIndex += 1;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
const style = (_c = cell.style) != null ? _c : {};
|
|
997
|
+
const fontSize = parseFontSize(style.fontsize);
|
|
998
|
+
const fill = parseTableColor(style.backcolor);
|
|
999
|
+
const color = parseTableColor(style.color);
|
|
1000
|
+
const options = {
|
|
1001
|
+
colspan: colSpan > 1 ? colSpan : void 0,
|
|
1002
|
+
rowspan: rowSpan > 1 ? rowSpan : void 0,
|
|
1003
|
+
align: style.align,
|
|
1004
|
+
valign: "middle",
|
|
1005
|
+
fontFace: normalizeFontName(style.fontname),
|
|
1006
|
+
fontSize: fontSize ? fontSize / ratioPx2Pt : void 0,
|
|
1007
|
+
color: color == null ? void 0 : color.color,
|
|
1008
|
+
fill: fill ? { color: fill.color, transparency: fill.transparency } : void 0,
|
|
1009
|
+
margin: 0
|
|
1010
|
+
};
|
|
1011
|
+
cells.push({
|
|
1012
|
+
text: (_d = cell.text) != null ? _d : "",
|
|
1013
|
+
options
|
|
1014
|
+
});
|
|
1015
|
+
colIndex += colSpan;
|
|
1016
|
+
}
|
|
1017
|
+
rows.push(cells);
|
|
1018
|
+
});
|
|
1019
|
+
return rows;
|
|
1020
|
+
};
|
|
879
1021
|
var getShadowOption = (shadow, ratioPx2Pt) => {
|
|
880
1022
|
var _a, _b;
|
|
881
1023
|
const c = formatColor((_a = shadow.color) != null ? _a : "#000000");
|
|
@@ -937,8 +1079,9 @@ var isBase64Image = (url) => {
|
|
|
937
1079
|
return regex.test(url);
|
|
938
1080
|
};
|
|
939
1081
|
async function buildPptxBlob(template) {
|
|
940
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K;
|
|
1082
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T;
|
|
941
1083
|
const pptx = new import_pptxgenjs.default();
|
|
1084
|
+
const patternShapes = [];
|
|
942
1085
|
const width = (_a = template.width) != null ? _a : DEFAULT_WIDTH;
|
|
943
1086
|
const height = (_b = template.height) != null ? _b : DEFAULT_HEIGHT;
|
|
944
1087
|
const viewportRatio = height / width;
|
|
@@ -953,14 +1096,14 @@ async function buildPptxBlob(template) {
|
|
|
953
1096
|
pptx.defineLayout({ name: "A3_V", width: 10, height: 14.1421356 });
|
|
954
1097
|
pptx.layout = "A3_V";
|
|
955
1098
|
} else pptx.layout = "LAYOUT_16x9";
|
|
956
|
-
for (const slideJson of (_c = template.slides) != null ? _c : []) {
|
|
1099
|
+
for (const [slideIndex, slideJson] of ((_c = template.slides) != null ? _c : []).entries()) {
|
|
957
1100
|
const slide = pptx.addSlide();
|
|
958
1101
|
const backgroundColor = (_d = slideJson.background) == null ? void 0 : _d.color;
|
|
959
1102
|
if (backgroundColor) {
|
|
960
1103
|
const c = formatColor(backgroundColor);
|
|
961
1104
|
slide.background = { color: c.color, transparency: (1 - c.alpha) * 100 };
|
|
962
1105
|
}
|
|
963
|
-
for (const element of (_e = slideJson.elements) != null ? _e : []) {
|
|
1106
|
+
for (const [elementIndex, element] of ((_e = slideJson.elements) != null ? _e : []).entries()) {
|
|
964
1107
|
if (element.type === "text" && element.content) {
|
|
965
1108
|
const textProps = formatHTML(element.content, ratioPx2Pt);
|
|
966
1109
|
const options = {
|
|
@@ -972,7 +1115,7 @@ async function buildPptxBlob(template) {
|
|
|
972
1115
|
fontFace: element.defaultFontName || ((_j = template.theme) == null ? void 0 : _j.fontName) || DEFAULT_FONT_FACE,
|
|
973
1116
|
color: "#000000",
|
|
974
1117
|
valign: "top",
|
|
975
|
-
margin:
|
|
1118
|
+
margin: 0,
|
|
976
1119
|
paraSpaceBefore: 5 / ratioPx2Pt,
|
|
977
1120
|
lineSpacingMultiple: 1.5 / 1.25,
|
|
978
1121
|
autoFit: true
|
|
@@ -987,6 +1130,8 @@ async function buildPptxBlob(template) {
|
|
|
987
1130
|
color: c.color,
|
|
988
1131
|
transparency: (1 - c.alpha * opacity) * 100
|
|
989
1132
|
};
|
|
1133
|
+
} else {
|
|
1134
|
+
options.fill = { color: "FFFFFF", transparency: 100 };
|
|
990
1135
|
}
|
|
991
1136
|
if (element.defaultColor) options.color = formatColor(element.defaultColor).color;
|
|
992
1137
|
if (element.shadow) options.shadow = getShadowOption(element.shadow, ratioPx2Pt);
|
|
@@ -1040,38 +1185,51 @@ async function buildPptxBlob(template) {
|
|
|
1040
1185
|
y: ((_u = element.height) != null ? _u : 0) / element.viewBox[1]
|
|
1041
1186
|
};
|
|
1042
1187
|
const points = formatPoints(toPoints(element.path), ratioPx2Inch, scale);
|
|
1043
|
-
|
|
1188
|
+
const pattern = element.pattern;
|
|
1189
|
+
const hasFill = typeof element.fill === "string" ? element.fill.trim().length > 0 : false;
|
|
1044
1190
|
const opacity = element.opacity === void 0 ? 1 : element.opacity;
|
|
1045
1191
|
const options = {
|
|
1046
1192
|
x: ((_v = element.left) != null ? _v : 0) / ratioPx2Inch,
|
|
1047
1193
|
y: ((_w = element.top) != null ? _w : 0) / ratioPx2Inch,
|
|
1048
1194
|
w: ((_x = element.width) != null ? _x : 0) / ratioPx2Inch,
|
|
1049
1195
|
h: ((_y = element.height) != null ? _y : 0) / ratioPx2Inch,
|
|
1050
|
-
fill: {
|
|
1051
|
-
color: fillColor.color,
|
|
1052
|
-
transparency: (1 - fillColor.alpha * opacity) * 100
|
|
1053
|
-
},
|
|
1054
1196
|
points
|
|
1055
1197
|
};
|
|
1198
|
+
if (pattern) {
|
|
1199
|
+
const objectName = `pattern-${slideIndex}-${(_z = element.id) != null ? _z : elementIndex}`;
|
|
1200
|
+
options.objectName = objectName;
|
|
1201
|
+
options.fill = { color: "FFFFFF", transparency: 100 };
|
|
1202
|
+
patternShapes.push({ slideIndex, objectName, dataUrl: pattern });
|
|
1203
|
+
} else if (hasFill) {
|
|
1204
|
+
const fillColor = formatColor(element.fill || "#000000");
|
|
1205
|
+
options.fill = {
|
|
1206
|
+
color: fillColor.color,
|
|
1207
|
+
transparency: (1 - fillColor.alpha * opacity) * 100
|
|
1208
|
+
};
|
|
1209
|
+
} else {
|
|
1210
|
+
options.fill = { color: "FFFFFF", transparency: 100 };
|
|
1211
|
+
}
|
|
1056
1212
|
if (element.flipH) options.flipH = element.flipH;
|
|
1057
1213
|
if (element.flipV) options.flipV = element.flipV;
|
|
1058
1214
|
if (element.shadow) options.shadow = getShadowOption(element.shadow, ratioPx2Pt);
|
|
1059
|
-
if ((
|
|
1215
|
+
if ((_A = element.outline) == null ? void 0 : _A.width) options.line = getOutlineOption(element.outline, ratioPx2Pt);
|
|
1060
1216
|
if (element.rotate) options.rotate = element.rotate;
|
|
1061
1217
|
slide.addShape("custGeom", options);
|
|
1062
|
-
if ((
|
|
1218
|
+
if ((_B = element.text) == null ? void 0 : _B.content) {
|
|
1063
1219
|
const textProps = formatHTML(element.text.content, ratioPx2Pt);
|
|
1064
1220
|
const textOptions = {
|
|
1065
|
-
x: ((
|
|
1066
|
-
y: ((
|
|
1067
|
-
w: ((
|
|
1068
|
-
h: ((
|
|
1221
|
+
x: ((_C = element.left) != null ? _C : 0) / ratioPx2Inch,
|
|
1222
|
+
y: ((_D = element.top) != null ? _D : 0) / ratioPx2Inch,
|
|
1223
|
+
w: ((_E = element.width) != null ? _E : 0) / ratioPx2Inch,
|
|
1224
|
+
h: ((_F = element.height) != null ? _F : 0) / ratioPx2Inch,
|
|
1069
1225
|
fontSize: DEFAULT_FONT_SIZE / ratioPx2Pt,
|
|
1070
1226
|
fontFace: element.text.defaultFontName || DEFAULT_FONT_FACE,
|
|
1071
1227
|
color: "#000000",
|
|
1072
1228
|
paraSpaceBefore: 5 / ratioPx2Pt,
|
|
1073
|
-
valign: element.text.align
|
|
1229
|
+
valign: element.text.align,
|
|
1230
|
+
fill: { color: "FFFFFF", transparency: 100 }
|
|
1074
1231
|
};
|
|
1232
|
+
textOptions.margin = 0;
|
|
1075
1233
|
if (element.rotate) textOptions.rotate = element.rotate;
|
|
1076
1234
|
if (element.text.defaultColor) {
|
|
1077
1235
|
textOptions.color = formatColor(element.text.defaultColor).color;
|
|
@@ -1086,45 +1244,122 @@ async function buildPptxBlob(template) {
|
|
|
1086
1244
|
const { minX, maxX, minY, maxY } = getElementRange(element);
|
|
1087
1245
|
const c = formatColor(element.color || "#000000");
|
|
1088
1246
|
const options = {
|
|
1089
|
-
x: ((
|
|
1090
|
-
y: ((
|
|
1247
|
+
x: ((_G = element.left) != null ? _G : 0) / ratioPx2Inch,
|
|
1248
|
+
y: ((_H = element.top) != null ? _H : 0) / ratioPx2Inch,
|
|
1091
1249
|
w: (maxX - minX) / ratioPx2Inch,
|
|
1092
1250
|
h: (maxY - minY) / ratioPx2Inch,
|
|
1093
1251
|
line: {
|
|
1094
1252
|
color: c.color,
|
|
1095
1253
|
transparency: (1 - c.alpha) * 100,
|
|
1096
|
-
width: ((
|
|
1254
|
+
width: ((_I = element.width) != null ? _I : 1) / ratioPx2Pt,
|
|
1097
1255
|
dashType: element.style ? dashTypeMap[element.style] : "solid",
|
|
1098
|
-
beginArrowType: ((
|
|
1099
|
-
endArrowType: ((
|
|
1256
|
+
beginArrowType: ((_J = element.points) == null ? void 0 : _J[0]) ? "arrow" : "none",
|
|
1257
|
+
endArrowType: ((_K = element.points) == null ? void 0 : _K[1]) ? "arrow" : "none"
|
|
1100
1258
|
},
|
|
1101
1259
|
points
|
|
1102
1260
|
};
|
|
1103
1261
|
if (element.shadow) options.shadow = getShadowOption(element.shadow, ratioPx2Pt);
|
|
1104
1262
|
slide.addShape("custGeom", options);
|
|
1105
1263
|
}
|
|
1264
|
+
if (element.type === "table") {
|
|
1265
|
+
const rows = buildTableRows(element, ratioPx2Pt);
|
|
1266
|
+
if (!rows.length) continue;
|
|
1267
|
+
const colWidths = element.colWidths;
|
|
1268
|
+
const colW = colWidths ? colWidths.map((ratio) => {
|
|
1269
|
+
var _a2;
|
|
1270
|
+
return ((_a2 = element.width) != null ? _a2 : 0) * ratio / ratioPx2Inch;
|
|
1271
|
+
}) : void 0;
|
|
1272
|
+
const rowCount = rows.length || 1;
|
|
1273
|
+
const baseRowHeight = ((_L = element.height) != null ? _L : 0) / rowCount / ratioPx2Inch;
|
|
1274
|
+
const minRowHeight = element.cellMinHeight ? element.cellMinHeight / ratioPx2Inch : void 0;
|
|
1275
|
+
const rowH = minRowHeight ? new Array(rowCount).fill(Math.max(minRowHeight, baseRowHeight)) : void 0;
|
|
1276
|
+
const outline = element.outline;
|
|
1277
|
+
const border = (outline == null ? void 0 : outline.width) || (outline == null ? void 0 : outline.color) ? {
|
|
1278
|
+
pt: ((_M = outline.width) != null ? _M : 1) / ratioPx2Pt,
|
|
1279
|
+
color: formatColor(outline.color || "#000000").color.replace("#", "")
|
|
1280
|
+
} : void 0;
|
|
1281
|
+
slide.addTable(rows, {
|
|
1282
|
+
x: ((_N = element.left) != null ? _N : 0) / ratioPx2Inch,
|
|
1283
|
+
y: ((_O = element.top) != null ? _O : 0) / ratioPx2Inch,
|
|
1284
|
+
w: ((_P = element.width) != null ? _P : 0) / ratioPx2Inch,
|
|
1285
|
+
h: ((_Q = element.height) != null ? _Q : 0) / ratioPx2Inch,
|
|
1286
|
+
colW,
|
|
1287
|
+
rowH,
|
|
1288
|
+
border,
|
|
1289
|
+
margin: 0
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1106
1292
|
}
|
|
1107
1293
|
}
|
|
1108
|
-
const fileName = `${sanitizeFileName((
|
|
1294
|
+
const fileName = `${sanitizeFileName((_R = template.title) != null ? _R : "presentation")}.pptx`;
|
|
1109
1295
|
const pptxBuffer = await pptx.write({
|
|
1110
1296
|
outputType: "arraybuffer",
|
|
1111
1297
|
compression: true
|
|
1112
1298
|
});
|
|
1113
|
-
|
|
1299
|
+
const needsZip = ENABLE_DECK_JSON || patternShapes.length > 0;
|
|
1300
|
+
if (!needsZip) {
|
|
1114
1301
|
return { blob: new Blob([pptxBuffer]), fileName };
|
|
1115
1302
|
}
|
|
1116
1303
|
const zip = await import_jszip.default.loadAsync(pptxBuffer);
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1304
|
+
if (patternShapes.length) {
|
|
1305
|
+
const mediaFiles = Object.keys(zip.files).filter((name) => name.startsWith("ppt/media/"));
|
|
1306
|
+
let maxImageIndex = 0;
|
|
1307
|
+
for (const name of mediaFiles) {
|
|
1308
|
+
const match = name.match(/ppt\/media\/image(\d+)/);
|
|
1309
|
+
if (match) {
|
|
1310
|
+
const index = Number.parseInt(match[1], 10);
|
|
1311
|
+
if (Number.isFinite(index)) maxImageIndex = Math.max(maxImageIndex, index);
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
const slideCache = /* @__PURE__ */ new Map();
|
|
1315
|
+
const relsCache = /* @__PURE__ */ new Map();
|
|
1316
|
+
for (const pattern of patternShapes) {
|
|
1317
|
+
const parsed = parseDataUrlImage(pattern.dataUrl);
|
|
1318
|
+
if (!parsed) continue;
|
|
1319
|
+
maxImageIndex += 1;
|
|
1320
|
+
const imageName = `image${maxImageIndex}.${parsed.ext}`;
|
|
1321
|
+
zip.file(`ppt/media/${imageName}`, parsed.data, { base64: true });
|
|
1322
|
+
const slideNumber = pattern.slideIndex + 1;
|
|
1323
|
+
const slidePath = `ppt/slides/slide${slideNumber}.xml`;
|
|
1324
|
+
const relsPath = `ppt/slides/_rels/slide${slideNumber}.xml.rels`;
|
|
1325
|
+
const relsXml = (_S = relsCache.get(slideNumber)) != null ? _S : zip.file(relsPath) ? await zip.file(relsPath).async("string") : `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"></Relationships>`;
|
|
1326
|
+
let maxRelId = 0;
|
|
1327
|
+
relsXml.replace(/Id="rId(\d+)"/g, (_, id) => {
|
|
1328
|
+
const value = Number.parseInt(id, 10);
|
|
1329
|
+
if (Number.isFinite(value)) maxRelId = Math.max(maxRelId, value);
|
|
1330
|
+
return "";
|
|
1331
|
+
});
|
|
1332
|
+
const relId = `rId${maxRelId + 1}`;
|
|
1333
|
+
const relEntry = `<Relationship Id="${relId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="../media/${imageName}"/>`;
|
|
1334
|
+
const nextRelsXml = relsXml.replace(
|
|
1335
|
+
"</Relationships>",
|
|
1336
|
+
`${relEntry}</Relationships>`
|
|
1337
|
+
);
|
|
1338
|
+
relsCache.set(slideNumber, nextRelsXml);
|
|
1339
|
+
const slideXml = (_T = slideCache.get(slideNumber)) != null ? _T : zip.file(slidePath) ? await zip.file(slidePath).async("string") : "";
|
|
1340
|
+
const nextSlideXml = slideXml ? applyPatternFill(slideXml, pattern.objectName, relId) : slideXml;
|
|
1341
|
+
slideCache.set(slideNumber, nextSlideXml);
|
|
1342
|
+
}
|
|
1343
|
+
for (const [slideNumber, xml] of slideCache.entries()) {
|
|
1344
|
+
zip.file(`ppt/slides/slide${slideNumber}.xml`, xml);
|
|
1345
|
+
}
|
|
1346
|
+
for (const [slideNumber, xml] of relsCache.entries()) {
|
|
1347
|
+
zip.file(`ppt/slides/_rels/slide${slideNumber}.xml.rels`, xml);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
if (ENABLE_DECK_JSON) {
|
|
1351
|
+
zip.file(
|
|
1352
|
+
PPTX_JSON_PAYLOAD_PATH,
|
|
1353
|
+
JSON.stringify(
|
|
1354
|
+
{
|
|
1355
|
+
version: PPTX_JSON_PAYLOAD_VERSION,
|
|
1356
|
+
deck: template
|
|
1357
|
+
},
|
|
1358
|
+
null,
|
|
1359
|
+
2
|
|
1360
|
+
)
|
|
1361
|
+
);
|
|
1362
|
+
}
|
|
1128
1363
|
const blob = await zip.generateAsync({ type: "blob" });
|
|
1129
1364
|
return { blob, fileName };
|
|
1130
1365
|
}
|