@8btc/ppt-generator-mcp 0.0.1
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/README.md +129 -0
- package/dist/export-pptx.d.ts +6 -0
- package/dist/export-pptx.js +876 -0
- package/dist/generate-ppt-slides.d.ts +10 -0
- package/dist/generate-ppt-slides.js +581 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +161 -0
- package/dist/ppt-generator.d.ts +39 -0
- package/dist/ppt-generator.js +110 -0
- package/dist/tpls/imgs.json +482 -0
- package/dist/tpls/tpl-1.json +7649 -0
- package/dist/tpls/tpl-2.json +7455 -0
- package/dist/tpls/tpl-3.json +8184 -0
- package/dist/tpls/tpl-4.json +8352 -0
- package/dist/types/outline.d.ts +36 -0
- package/dist/types/outline.js +2 -0
- package/dist/types/slide.d.ts +696 -0
- package/dist/types/slide.js +2 -0
- package/dist/utils/element.d.ts +94 -0
- package/dist/utils/element.js +239 -0
- package/dist/utils/htmlParser/format.d.ts +3 -0
- package/dist/utils/htmlParser/format.js +47 -0
- package/dist/utils/htmlParser/index.d.ts +4 -0
- package/dist/utils/htmlParser/index.js +15 -0
- package/dist/utils/htmlParser/lexer.d.ts +2 -0
- package/dist/utils/htmlParser/lexer.js +245 -0
- package/dist/utils/htmlParser/parser.d.ts +15 -0
- package/dist/utils/htmlParser/parser.js +117 -0
- package/dist/utils/htmlParser/stringify.d.ts +3 -0
- package/dist/utils/htmlParser/stringify.js +32 -0
- package/dist/utils/htmlParser/tags.d.ts +8 -0
- package/dist/utils/htmlParser/tags.js +50 -0
- package/dist/utils/htmlParser/types.d.ts +55 -0
- package/dist/utils/htmlParser/types.js +2 -0
- package/dist/utils/svg2Base64.d.ts +1 -0
- package/dist/utils/svg2Base64.js +58 -0
- package/dist/utils/svgPathParser.d.ts +120 -0
- package/dist/utils/svgPathParser.js +145 -0
- package/package.json +59 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parse = exports.rewindStack = exports.hasTerminalParent = exports.parser = void 0;
|
|
4
|
+
const tags_1 = require("./tags");
|
|
5
|
+
const parser = (tokens) => {
|
|
6
|
+
const root = { tagName: null, children: [] };
|
|
7
|
+
const state = { tokens, cursor: 0, stack: [root] };
|
|
8
|
+
(0, exports.parse)(state);
|
|
9
|
+
return root.children;
|
|
10
|
+
};
|
|
11
|
+
exports.parser = parser;
|
|
12
|
+
const hasTerminalParent = (tagName, stack) => {
|
|
13
|
+
const tagParents = tags_1.closingTagAncestorBreakers[tagName];
|
|
14
|
+
if (tagParents) {
|
|
15
|
+
let currentIndex = stack.length - 1;
|
|
16
|
+
while (currentIndex >= 0) {
|
|
17
|
+
const parentTagName = stack[currentIndex].tagName;
|
|
18
|
+
if (parentTagName === tagName)
|
|
19
|
+
break;
|
|
20
|
+
if (parentTagName && tagParents.includes(parentTagName))
|
|
21
|
+
return true;
|
|
22
|
+
currentIndex--;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
};
|
|
27
|
+
exports.hasTerminalParent = hasTerminalParent;
|
|
28
|
+
const rewindStack = (stack, newLength) => {
|
|
29
|
+
stack.splice(newLength);
|
|
30
|
+
};
|
|
31
|
+
exports.rewindStack = rewindStack;
|
|
32
|
+
const parse = (state) => {
|
|
33
|
+
const { stack, tokens } = state;
|
|
34
|
+
let { cursor } = state;
|
|
35
|
+
let nodes = stack[stack.length - 1].children;
|
|
36
|
+
const len = tokens.length;
|
|
37
|
+
while (cursor < len) {
|
|
38
|
+
const token = tokens[cursor];
|
|
39
|
+
if (token.type !== "tag-start") {
|
|
40
|
+
nodes.push(token);
|
|
41
|
+
cursor++;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const tagToken = tokens[++cursor];
|
|
45
|
+
cursor++;
|
|
46
|
+
const tagName = tagToken.content.toLowerCase();
|
|
47
|
+
if (token.close) {
|
|
48
|
+
let index = stack.length;
|
|
49
|
+
let shouldRewind = false;
|
|
50
|
+
while (--index > -1) {
|
|
51
|
+
if (stack[index].tagName === tagName) {
|
|
52
|
+
shouldRewind = true;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
while (cursor < len) {
|
|
57
|
+
if (tokens[cursor].type !== "tag-end")
|
|
58
|
+
break;
|
|
59
|
+
cursor++;
|
|
60
|
+
}
|
|
61
|
+
if (shouldRewind) {
|
|
62
|
+
(0, exports.rewindStack)(stack, index);
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
else
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const isClosingTag = tags_1.closingTags.includes(tagName);
|
|
69
|
+
let shouldRewindToAutoClose = isClosingTag;
|
|
70
|
+
if (shouldRewindToAutoClose) {
|
|
71
|
+
shouldRewindToAutoClose = !(0, exports.hasTerminalParent)(tagName, stack);
|
|
72
|
+
}
|
|
73
|
+
if (shouldRewindToAutoClose) {
|
|
74
|
+
let currentIndex = stack.length - 1;
|
|
75
|
+
while (currentIndex > 0) {
|
|
76
|
+
if (tagName === stack[currentIndex].tagName) {
|
|
77
|
+
(0, exports.rewindStack)(stack, currentIndex);
|
|
78
|
+
const previousIndex = currentIndex - 1;
|
|
79
|
+
nodes = stack[previousIndex].children;
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
currentIndex = currentIndex - 1;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const attributes = [];
|
|
86
|
+
let tagEndToken;
|
|
87
|
+
while (cursor < len) {
|
|
88
|
+
const _token = tokens[cursor];
|
|
89
|
+
if (_token.type === "tag-end") {
|
|
90
|
+
tagEndToken = _token;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
attributes.push(_token.content);
|
|
94
|
+
cursor++;
|
|
95
|
+
}
|
|
96
|
+
if (!tagEndToken)
|
|
97
|
+
break;
|
|
98
|
+
cursor++;
|
|
99
|
+
const children = [];
|
|
100
|
+
const elementNode = {
|
|
101
|
+
type: "element",
|
|
102
|
+
tagName: tagToken.content,
|
|
103
|
+
attributes,
|
|
104
|
+
children,
|
|
105
|
+
};
|
|
106
|
+
nodes.push(elementNode);
|
|
107
|
+
const hasChildren = !(tagEndToken.close || tags_1.voidTags.includes(tagName));
|
|
108
|
+
if (hasChildren) {
|
|
109
|
+
stack.push({ tagName, children });
|
|
110
|
+
const innerState = { tokens, cursor, stack };
|
|
111
|
+
(0, exports.parse)(innerState);
|
|
112
|
+
cursor = innerState.cursor;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
state.cursor = cursor;
|
|
116
|
+
};
|
|
117
|
+
exports.parse = parse;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toHTML = exports.formatAttributes = void 0;
|
|
4
|
+
const tags_1 = require("./tags");
|
|
5
|
+
const formatAttributes = (attributes) => {
|
|
6
|
+
return attributes.reduce((attrs, attribute) => {
|
|
7
|
+
const { key, value } = attribute;
|
|
8
|
+
if (value === null)
|
|
9
|
+
return `${attrs} ${key}`;
|
|
10
|
+
if (key === "style" && !value)
|
|
11
|
+
return "";
|
|
12
|
+
const quoteEscape = value.indexOf("'") !== -1;
|
|
13
|
+
const quote = quoteEscape ? '"' : "'";
|
|
14
|
+
return `${attrs} ${key}=${quote}${value}${quote}`;
|
|
15
|
+
}, "");
|
|
16
|
+
};
|
|
17
|
+
exports.formatAttributes = formatAttributes;
|
|
18
|
+
const toHTML = (tree) => {
|
|
19
|
+
const htmlStrings = tree.map((node) => {
|
|
20
|
+
if (node.type === "text")
|
|
21
|
+
return node.content;
|
|
22
|
+
if (node.type === "comment")
|
|
23
|
+
return `<!--${node.content}-->`;
|
|
24
|
+
const { tagName, attributes, children } = node;
|
|
25
|
+
const isSelfClosing = tags_1.voidTags.includes(tagName.toLowerCase());
|
|
26
|
+
if (isSelfClosing)
|
|
27
|
+
return `<${tagName}${(0, exports.formatAttributes)(attributes)}>`;
|
|
28
|
+
return `<${tagName}${(0, exports.formatAttributes)(attributes)}>${(0, exports.toHTML)(children)}</${tagName}>`;
|
|
29
|
+
});
|
|
30
|
+
return htmlStrings.join("");
|
|
31
|
+
};
|
|
32
|
+
exports.toHTML = toHTML;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const childlessTags: string[];
|
|
2
|
+
export declare const closingTags: string[];
|
|
3
|
+
interface ClosingTagAncestorBreakers {
|
|
4
|
+
[key: string]: string[];
|
|
5
|
+
}
|
|
6
|
+
export declare const closingTagAncestorBreakers: ClosingTagAncestorBreakers;
|
|
7
|
+
export declare const voidTags: string[];
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.voidTags = exports.closingTagAncestorBreakers = exports.closingTags = exports.childlessTags = void 0;
|
|
4
|
+
exports.childlessTags = ["style", "script", "template"];
|
|
5
|
+
exports.closingTags = [
|
|
6
|
+
"html",
|
|
7
|
+
"head",
|
|
8
|
+
"body",
|
|
9
|
+
"p",
|
|
10
|
+
"dt",
|
|
11
|
+
"dd",
|
|
12
|
+
"li",
|
|
13
|
+
"option",
|
|
14
|
+
"thead",
|
|
15
|
+
"th",
|
|
16
|
+
"tbody",
|
|
17
|
+
"tr",
|
|
18
|
+
"td",
|
|
19
|
+
"tfoot",
|
|
20
|
+
"colgroup",
|
|
21
|
+
];
|
|
22
|
+
exports.closingTagAncestorBreakers = {
|
|
23
|
+
li: ["ul", "ol", "menu"],
|
|
24
|
+
dt: ["dl"],
|
|
25
|
+
dd: ["dl"],
|
|
26
|
+
tbody: ["table"],
|
|
27
|
+
thead: ["table"],
|
|
28
|
+
tfoot: ["table"],
|
|
29
|
+
tr: ["table"],
|
|
30
|
+
td: ["table"],
|
|
31
|
+
};
|
|
32
|
+
exports.voidTags = [
|
|
33
|
+
"!doctype",
|
|
34
|
+
"area",
|
|
35
|
+
"base",
|
|
36
|
+
"br",
|
|
37
|
+
"col",
|
|
38
|
+
"command",
|
|
39
|
+
"embed",
|
|
40
|
+
"hr",
|
|
41
|
+
"img",
|
|
42
|
+
"input",
|
|
43
|
+
"keygen",
|
|
44
|
+
"link",
|
|
45
|
+
"meta",
|
|
46
|
+
"param",
|
|
47
|
+
"source",
|
|
48
|
+
"track",
|
|
49
|
+
"wbr",
|
|
50
|
+
];
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export interface ElementAttribute {
|
|
2
|
+
key: string;
|
|
3
|
+
value: string | null;
|
|
4
|
+
}
|
|
5
|
+
export interface CommentElement {
|
|
6
|
+
type: "comment";
|
|
7
|
+
content: string;
|
|
8
|
+
}
|
|
9
|
+
export interface TextElement {
|
|
10
|
+
type: "text";
|
|
11
|
+
content: string;
|
|
12
|
+
}
|
|
13
|
+
export interface NormalElement {
|
|
14
|
+
type: "element";
|
|
15
|
+
tagName: string;
|
|
16
|
+
children: HTMLNode[];
|
|
17
|
+
attributes: string[];
|
|
18
|
+
}
|
|
19
|
+
export type HTMLNode = CommentElement | TextElement | NormalElement;
|
|
20
|
+
export interface ElementAST {
|
|
21
|
+
type: "element";
|
|
22
|
+
tagName: string;
|
|
23
|
+
children: AST[];
|
|
24
|
+
attributes: ElementAttribute[];
|
|
25
|
+
}
|
|
26
|
+
export interface CommentOrTextAST {
|
|
27
|
+
type: "comment" | "text";
|
|
28
|
+
content: string;
|
|
29
|
+
}
|
|
30
|
+
export type AST = CommentOrTextAST | ElementAST;
|
|
31
|
+
export interface TagStartToken {
|
|
32
|
+
type: "tag-start";
|
|
33
|
+
close: boolean;
|
|
34
|
+
}
|
|
35
|
+
export interface TagEndToken {
|
|
36
|
+
type: "tag-end";
|
|
37
|
+
close: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface TagToken {
|
|
40
|
+
type: "tag";
|
|
41
|
+
content: string;
|
|
42
|
+
}
|
|
43
|
+
export interface TextToken {
|
|
44
|
+
type: "text";
|
|
45
|
+
content: string;
|
|
46
|
+
}
|
|
47
|
+
export interface CommentToken {
|
|
48
|
+
type: "comment";
|
|
49
|
+
content: string;
|
|
50
|
+
}
|
|
51
|
+
export interface AttributeToken {
|
|
52
|
+
type: "attribute";
|
|
53
|
+
content: string;
|
|
54
|
+
}
|
|
55
|
+
export type Token = TagStartToken | TagEndToken | TagToken | TextToken | CommentToken | AttributeToken;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const svg2Base64: (element: Element) => string;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// svg转base64图片,参考:https://github.com/scriptex/svg64
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.svg2Base64 = void 0;
|
|
5
|
+
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
|
6
|
+
const PREFIX = "data:image/svg+xml;base64,";
|
|
7
|
+
const utf8Encode = (string) => {
|
|
8
|
+
string = string.replace(/\r\n/g, "\n");
|
|
9
|
+
let utftext = "";
|
|
10
|
+
for (let n = 0; n < string.length; n++) {
|
|
11
|
+
const c = string.charCodeAt(n);
|
|
12
|
+
if (c < 128) {
|
|
13
|
+
utftext += String.fromCharCode(c);
|
|
14
|
+
}
|
|
15
|
+
else if (c > 127 && c < 2048) {
|
|
16
|
+
utftext += String.fromCharCode((c >> 6) | 192);
|
|
17
|
+
utftext += String.fromCharCode((c & 63) | 128);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
utftext += String.fromCharCode((c >> 12) | 224);
|
|
21
|
+
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
|
|
22
|
+
utftext += String.fromCharCode((c & 63) | 128);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return utftext;
|
|
26
|
+
};
|
|
27
|
+
const encode = (input) => {
|
|
28
|
+
let output = "";
|
|
29
|
+
let chr1, chr2, chr3, enc1, enc2, enc3, enc4;
|
|
30
|
+
let i = 0;
|
|
31
|
+
input = utf8Encode(input);
|
|
32
|
+
while (i < input.length) {
|
|
33
|
+
chr1 = input.charCodeAt(i++);
|
|
34
|
+
chr2 = input.charCodeAt(i++);
|
|
35
|
+
chr3 = input.charCodeAt(i++);
|
|
36
|
+
enc1 = chr1 >> 2;
|
|
37
|
+
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
|
|
38
|
+
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
|
|
39
|
+
enc4 = chr3 & 63;
|
|
40
|
+
if (isNaN(chr2))
|
|
41
|
+
enc3 = enc4 = 64;
|
|
42
|
+
else if (isNaN(chr3))
|
|
43
|
+
enc4 = 64;
|
|
44
|
+
output =
|
|
45
|
+
output +
|
|
46
|
+
characters.charAt(enc1) +
|
|
47
|
+
characters.charAt(enc2) +
|
|
48
|
+
characters.charAt(enc3) +
|
|
49
|
+
characters.charAt(enc4);
|
|
50
|
+
}
|
|
51
|
+
return output;
|
|
52
|
+
};
|
|
53
|
+
const svg2Base64 = (element) => {
|
|
54
|
+
const XMLS = new XMLSerializer();
|
|
55
|
+
const svg = XMLS.serializeToString(element);
|
|
56
|
+
return PREFIX + encode(svg);
|
|
57
|
+
};
|
|
58
|
+
exports.svg2Base64 = svg2Base64;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 简单解析SVG路径
|
|
3
|
+
* @param d SVG path d属性
|
|
4
|
+
*/
|
|
5
|
+
export declare const parseSvgPath: (d: string) => ({
|
|
6
|
+
type: string;
|
|
7
|
+
relative: boolean;
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
} | {
|
|
11
|
+
type: string;
|
|
12
|
+
relative: boolean;
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
} | {
|
|
16
|
+
type: string;
|
|
17
|
+
relative: boolean;
|
|
18
|
+
x: number;
|
|
19
|
+
} | {
|
|
20
|
+
type: string;
|
|
21
|
+
relative: boolean;
|
|
22
|
+
y: number;
|
|
23
|
+
} | {
|
|
24
|
+
type: string;
|
|
25
|
+
} | {
|
|
26
|
+
type: string;
|
|
27
|
+
relative: boolean;
|
|
28
|
+
x1: number;
|
|
29
|
+
y1: number;
|
|
30
|
+
x: number;
|
|
31
|
+
y: number;
|
|
32
|
+
} | {
|
|
33
|
+
type: string;
|
|
34
|
+
relative: boolean;
|
|
35
|
+
x: number;
|
|
36
|
+
y: number;
|
|
37
|
+
} | {
|
|
38
|
+
type: string;
|
|
39
|
+
relative: boolean;
|
|
40
|
+
x1: number;
|
|
41
|
+
y1: number;
|
|
42
|
+
x2: number;
|
|
43
|
+
y2: number;
|
|
44
|
+
x: number;
|
|
45
|
+
y: number;
|
|
46
|
+
} | {
|
|
47
|
+
type: string;
|
|
48
|
+
relative: boolean;
|
|
49
|
+
x2: number;
|
|
50
|
+
y2: number;
|
|
51
|
+
x: number;
|
|
52
|
+
y: number;
|
|
53
|
+
} | {
|
|
54
|
+
type: string;
|
|
55
|
+
relative: boolean;
|
|
56
|
+
rX: number;
|
|
57
|
+
rY: number;
|
|
58
|
+
xRot: number;
|
|
59
|
+
sweepFlag: 0 | 1;
|
|
60
|
+
lArcFlag: 0 | 1;
|
|
61
|
+
x: number;
|
|
62
|
+
y: number;
|
|
63
|
+
cX?: number;
|
|
64
|
+
cY?: number;
|
|
65
|
+
phi1?: number;
|
|
66
|
+
phi2?: number;
|
|
67
|
+
})[];
|
|
68
|
+
export type SvgPath = ReturnType<typeof parseSvgPath>;
|
|
69
|
+
/**
|
|
70
|
+
* 解析SVG路径,并将圆弧(A)类型的路径转为三次贝塞尔(C)类型的路径
|
|
71
|
+
* @param d SVG path d属性
|
|
72
|
+
*/
|
|
73
|
+
export declare const toPoints: (d: string) => ({
|
|
74
|
+
x: number;
|
|
75
|
+
y: number;
|
|
76
|
+
relative: boolean;
|
|
77
|
+
type: string;
|
|
78
|
+
curve?: undefined;
|
|
79
|
+
close?: undefined;
|
|
80
|
+
} | {
|
|
81
|
+
x: number;
|
|
82
|
+
y: number;
|
|
83
|
+
curve: {
|
|
84
|
+
type: string;
|
|
85
|
+
x1: number;
|
|
86
|
+
y1: number;
|
|
87
|
+
x2: number;
|
|
88
|
+
y2: number;
|
|
89
|
+
};
|
|
90
|
+
relative: boolean;
|
|
91
|
+
type: string;
|
|
92
|
+
close?: undefined;
|
|
93
|
+
} | {
|
|
94
|
+
x: number;
|
|
95
|
+
y: number;
|
|
96
|
+
curve: {
|
|
97
|
+
type: string;
|
|
98
|
+
x1: number;
|
|
99
|
+
y1: number;
|
|
100
|
+
x2?: undefined;
|
|
101
|
+
y2?: undefined;
|
|
102
|
+
};
|
|
103
|
+
relative: boolean;
|
|
104
|
+
type: string;
|
|
105
|
+
close?: undefined;
|
|
106
|
+
} | {
|
|
107
|
+
close: boolean;
|
|
108
|
+
type: string;
|
|
109
|
+
x?: undefined;
|
|
110
|
+
y?: undefined;
|
|
111
|
+
relative?: undefined;
|
|
112
|
+
curve?: undefined;
|
|
113
|
+
})[];
|
|
114
|
+
export declare const getSvgPathRange: (path: string) => {
|
|
115
|
+
minX: number;
|
|
116
|
+
minY: number;
|
|
117
|
+
maxX: number;
|
|
118
|
+
maxY: number;
|
|
119
|
+
};
|
|
120
|
+
export type SvgPoints = ReturnType<typeof toPoints>;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getSvgPathRange = exports.toPoints = exports.parseSvgPath = void 0;
|
|
7
|
+
const svg_pathdata_1 = require("svg-pathdata");
|
|
8
|
+
const svg_arc_to_cubic_bezier_1 = __importDefault(require("svg-arc-to-cubic-bezier"));
|
|
9
|
+
const typeMap = {
|
|
10
|
+
1: "Z",
|
|
11
|
+
2: "M",
|
|
12
|
+
4: "H",
|
|
13
|
+
8: "V",
|
|
14
|
+
16: "L",
|
|
15
|
+
32: "C",
|
|
16
|
+
64: "S",
|
|
17
|
+
128: "Q",
|
|
18
|
+
256: "T",
|
|
19
|
+
512: "A",
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* 简单解析SVG路径
|
|
23
|
+
* @param d SVG path d属性
|
|
24
|
+
*/
|
|
25
|
+
const parseSvgPath = (d) => {
|
|
26
|
+
const pathData = new svg_pathdata_1.SVGPathData(d);
|
|
27
|
+
const ret = pathData.commands.map((item) => {
|
|
28
|
+
return { ...item, type: typeMap[item.type] };
|
|
29
|
+
});
|
|
30
|
+
return ret;
|
|
31
|
+
};
|
|
32
|
+
exports.parseSvgPath = parseSvgPath;
|
|
33
|
+
/**
|
|
34
|
+
* 解析SVG路径,并将圆弧(A)类型的路径转为三次贝塞尔(C)类型的路径
|
|
35
|
+
* @param d SVG path d属性
|
|
36
|
+
*/
|
|
37
|
+
const toPoints = (d) => {
|
|
38
|
+
const pathData = new svg_pathdata_1.SVGPathData(d);
|
|
39
|
+
const points = [];
|
|
40
|
+
for (const item of pathData.commands) {
|
|
41
|
+
const type = typeMap[item.type];
|
|
42
|
+
if (item.type === 2 || item.type === 16) {
|
|
43
|
+
points.push({
|
|
44
|
+
x: item.x,
|
|
45
|
+
y: item.y,
|
|
46
|
+
relative: item.relative,
|
|
47
|
+
type,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
if (item.type === 32) {
|
|
51
|
+
points.push({
|
|
52
|
+
x: item.x,
|
|
53
|
+
y: item.y,
|
|
54
|
+
curve: {
|
|
55
|
+
type: "cubic",
|
|
56
|
+
x1: item.x1,
|
|
57
|
+
y1: item.y1,
|
|
58
|
+
x2: item.x2,
|
|
59
|
+
y2: item.y2,
|
|
60
|
+
},
|
|
61
|
+
relative: item.relative,
|
|
62
|
+
type,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
else if (item.type === 128) {
|
|
66
|
+
points.push({
|
|
67
|
+
x: item.x,
|
|
68
|
+
y: item.y,
|
|
69
|
+
curve: {
|
|
70
|
+
type: "quadratic",
|
|
71
|
+
x1: item.x1,
|
|
72
|
+
y1: item.y1,
|
|
73
|
+
},
|
|
74
|
+
relative: item.relative,
|
|
75
|
+
type,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
else if (item.type === 512) {
|
|
79
|
+
const lastPoint = points[points.length - 1];
|
|
80
|
+
if (!["M", "L", "Q", "C"].includes(lastPoint.type))
|
|
81
|
+
continue;
|
|
82
|
+
const cubicBezierPoints = (0, svg_arc_to_cubic_bezier_1.default)({
|
|
83
|
+
px: lastPoint.x,
|
|
84
|
+
py: lastPoint.y,
|
|
85
|
+
cx: item.x,
|
|
86
|
+
cy: item.y,
|
|
87
|
+
rx: item.rX,
|
|
88
|
+
ry: item.rY,
|
|
89
|
+
xAxisRotation: item.xRot,
|
|
90
|
+
largeArcFlag: item.lArcFlag,
|
|
91
|
+
sweepFlag: item.sweepFlag,
|
|
92
|
+
});
|
|
93
|
+
for (const cbPoint of cubicBezierPoints) {
|
|
94
|
+
points.push({
|
|
95
|
+
x: cbPoint.x,
|
|
96
|
+
y: cbPoint.y,
|
|
97
|
+
curve: {
|
|
98
|
+
type: "cubic",
|
|
99
|
+
x1: cbPoint.x1,
|
|
100
|
+
y1: cbPoint.y1,
|
|
101
|
+
x2: cbPoint.x2,
|
|
102
|
+
y2: cbPoint.y2,
|
|
103
|
+
},
|
|
104
|
+
relative: false,
|
|
105
|
+
type: "C",
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else if (item.type === 1) {
|
|
110
|
+
points.push({ close: true, type });
|
|
111
|
+
}
|
|
112
|
+
else
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
return points;
|
|
116
|
+
};
|
|
117
|
+
exports.toPoints = toPoints;
|
|
118
|
+
const getSvgPathRange = (path) => {
|
|
119
|
+
try {
|
|
120
|
+
const pathData = new svg_pathdata_1.SVGPathData(path);
|
|
121
|
+
const xList = [];
|
|
122
|
+
const yList = [];
|
|
123
|
+
for (const item of pathData.commands) {
|
|
124
|
+
const x = "x" in item ? item.x : 0;
|
|
125
|
+
const y = "y" in item ? item.y : 0;
|
|
126
|
+
xList.push(x);
|
|
127
|
+
yList.push(y);
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
minX: Math.min(...xList),
|
|
131
|
+
minY: Math.min(...yList),
|
|
132
|
+
maxX: Math.max(...xList),
|
|
133
|
+
maxY: Math.max(...yList),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return {
|
|
138
|
+
minX: 0,
|
|
139
|
+
minY: 0,
|
|
140
|
+
maxX: 0,
|
|
141
|
+
maxY: 0,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
exports.getSvgPathRange = getSvgPathRange;
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@8btc/ppt-generator-mcp",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "MCP service for generating PPT files from AI-generated outlines and templates",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc && cp -r src/tpls dist",
|
|
8
|
+
"start": "node dist/index.js",
|
|
9
|
+
"dev": "tsx src/index.ts",
|
|
10
|
+
"test": "npm run build && node test/test-gen-ppt.cjs",
|
|
11
|
+
"release": "npm run build && npm publish --access public"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"bin": {
|
|
17
|
+
"@8btc/ppt-generator-mcp": "dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"mcp",
|
|
21
|
+
"ppt",
|
|
22
|
+
"powerpoint",
|
|
23
|
+
"generator",
|
|
24
|
+
"ai"
|
|
25
|
+
],
|
|
26
|
+
"author": "PPT Agent",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@modelcontextprotocol/sdk": "^1.18.1",
|
|
30
|
+
"@napi-rs/canvas": "^0.1.80",
|
|
31
|
+
"@types/jsdom": "^21.1.7",
|
|
32
|
+
"fs-extra": "^11.2.0",
|
|
33
|
+
"himalaya": "^1.1.1",
|
|
34
|
+
"jsdom": "^27.0.0",
|
|
35
|
+
"lodash": "^4.17.21",
|
|
36
|
+
"nanoid": "^5.1.6",
|
|
37
|
+
"path": "^0.12.7",
|
|
38
|
+
"pptxgenjs": "^3.12.0",
|
|
39
|
+
"svg-arc-to-cubic-bezier": "^3.2.0",
|
|
40
|
+
"svg-pathdata": "^8.0.0",
|
|
41
|
+
"tinycolor2": "^1.6.0",
|
|
42
|
+
"uuid": "^9.0.1"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/fs-extra": "^11.0.4",
|
|
46
|
+
"@types/jest": "^29.0.0",
|
|
47
|
+
"@types/lodash": "^4.17.20",
|
|
48
|
+
"@types/node": "^20.0.0",
|
|
49
|
+
"@types/svg-arc-to-cubic-bezier": "^3.2.3",
|
|
50
|
+
"@types/tinycolor2": "^1.4.6",
|
|
51
|
+
"@types/uuid": "^9.0.0",
|
|
52
|
+
"jest": "^29.0.0",
|
|
53
|
+
"tsx": "^4.0.0",
|
|
54
|
+
"typescript": "^5.0.0"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=18.0.0"
|
|
58
|
+
}
|
|
59
|
+
}
|