dom-to-pptx 1.1.0 → 1.1.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/CHANGELOG.md +12 -2
- package/README.md +295 -323
- package/dist/dom-to-pptx.bundle.js +247 -95
- package/dist/dom-to-pptx.cjs +247 -95
- package/dist/dom-to-pptx.cjs.map +1 -1
- package/dist/dom-to-pptx.mjs +247 -95
- package/dist/dom-to-pptx.mjs.map +1 -1
- package/package.json +83 -83
- package/rollup.config.js +9 -14
- package/src/font-embedder.js +163 -159
- package/src/font-utils.js +32 -35
- package/src/image-processor.js +58 -19
- package/src/index.js +971 -905
- package/src/utils.js +711 -674
- package/dist/dom-to-pptx.min.js +0 -64284
package/package.json
CHANGED
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "dom-to-pptx",
|
|
3
|
-
"version": "1.1.
|
|
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
|
-
"main": "dist/dom-to-pptx.cjs",
|
|
6
|
-
"module": "dist/dom-to-pptx.mjs",
|
|
7
|
-
"browser": "dist/dom-to-pptx.bundle.js",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"import": "./dist/dom-to-pptx.mjs",
|
|
11
|
-
"require": "./dist/dom-to-pptx.cjs",
|
|
12
|
-
"default": "./dist/dom-to-pptx.cjs"
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
"scripts": {
|
|
16
|
-
"test": "echo \"Error: no test specified\" && exit 1",
|
|
17
|
-
"build": "npx rollup -c",
|
|
18
|
-
"lint": "eslint .",
|
|
19
|
-
"format": "prettier --write ."
|
|
20
|
-
},
|
|
21
|
-
"repository": {
|
|
22
|
-
"type": "git",
|
|
23
|
-
"url": "git+https://github.com/atharva9167j/dom-to-pptx.git"
|
|
24
|
-
},
|
|
25
|
-
"keywords": [
|
|
26
|
-
"pptx",
|
|
27
|
-
"html-to-pptx",
|
|
28
|
-
"powerpoint",
|
|
29
|
-
"pptx-generator",
|
|
30
|
-
"slide-generator",
|
|
31
|
-
"presentation-generator",
|
|
32
|
-
"dom",
|
|
33
|
-
"dom-to-pptx",
|
|
34
|
-
"export",
|
|
35
|
-
"browser-export",
|
|
36
|
-
"client-side-pptx",
|
|
37
|
-
"html-to-powerpoint",
|
|
38
|
-
"convert-html",
|
|
39
|
-
"css-to-pptx",
|
|
40
|
-
"css-to-powerpoint",
|
|
41
|
-
"pptx-creator",
|
|
42
|
-
"presentation-tools",
|
|
43
|
-
"slides",
|
|
44
|
-
"slide-creation",
|
|
45
|
-
"web-to-pptx",
|
|
46
|
-
"web-to-powerpoint",
|
|
47
|
-
"frontend-pptx",
|
|
48
|
-
"html-rendering",
|
|
49
|
-
"layout-engine",
|
|
50
|
-
"gradient-support",
|
|
51
|
-
"flexbox",
|
|
52
|
-
"css-grid",
|
|
53
|
-
"typography"
|
|
54
|
-
],
|
|
55
|
-
"author": "Atharva Dharmendra Jagtap <atharvaj321@gmail.com>",
|
|
56
|
-
"license": "MIT",
|
|
57
|
-
"bugs": {
|
|
58
|
-
"url": "https://github.com/atharva9167j/dom-to-pptx/issues"
|
|
59
|
-
},
|
|
60
|
-
"devDependencies": {
|
|
61
|
-
"@rollup/plugin-commonjs": "^29.0.0",
|
|
62
|
-
"@rollup/plugin-json": "^6.1.0",
|
|
63
|
-
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
64
|
-
"buffer": "^6.0.3",
|
|
65
|
-
"eslint": "^9.39.1",
|
|
66
|
-
"eslint-config-prettier": "^10.1.8",
|
|
67
|
-
"eslint-plugin-prettier": "^5.5.4",
|
|
68
|
-
"events": "^3.3.0",
|
|
69
|
-
"prettier": "^3.7.3",
|
|
70
|
-
"process": "^0.11.10",
|
|
71
|
-
"rollup-plugin-polyfill-node": "^0.10.1",
|
|
72
|
-
"stream-browserify": "^3.0.0",
|
|
73
|
-
"util": "^0.12.5"
|
|
74
|
-
},
|
|
75
|
-
"dependencies": {
|
|
76
|
-
"fonteditor-core": "^2.6.3",
|
|
77
|
-
"html2canvas": "^1.4.1",
|
|
78
|
-
"jszip": "^3.10.1",
|
|
79
|
-
"opentype.js": "^1.3.4",
|
|
80
|
-
"pako": "^2.1.0",
|
|
81
|
-
"pptxgenjs": "^3.12.0"
|
|
82
|
-
}
|
|
83
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "dom-to-pptx",
|
|
3
|
+
"version": "1.1.1",
|
|
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
|
+
"main": "dist/dom-to-pptx.cjs",
|
|
6
|
+
"module": "dist/dom-to-pptx.mjs",
|
|
7
|
+
"browser": "dist/dom-to-pptx.bundle.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/dom-to-pptx.mjs",
|
|
11
|
+
"require": "./dist/dom-to-pptx.cjs",
|
|
12
|
+
"default": "./dist/dom-to-pptx.cjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
17
|
+
"build": "npx rollup -c",
|
|
18
|
+
"lint": "eslint .",
|
|
19
|
+
"format": "prettier --write ."
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/atharva9167j/dom-to-pptx.git"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"pptx",
|
|
27
|
+
"html-to-pptx",
|
|
28
|
+
"powerpoint",
|
|
29
|
+
"pptx-generator",
|
|
30
|
+
"slide-generator",
|
|
31
|
+
"presentation-generator",
|
|
32
|
+
"dom",
|
|
33
|
+
"dom-to-pptx",
|
|
34
|
+
"export",
|
|
35
|
+
"browser-export",
|
|
36
|
+
"client-side-pptx",
|
|
37
|
+
"html-to-powerpoint",
|
|
38
|
+
"convert-html",
|
|
39
|
+
"css-to-pptx",
|
|
40
|
+
"css-to-powerpoint",
|
|
41
|
+
"pptx-creator",
|
|
42
|
+
"presentation-tools",
|
|
43
|
+
"slides",
|
|
44
|
+
"slide-creation",
|
|
45
|
+
"web-to-pptx",
|
|
46
|
+
"web-to-powerpoint",
|
|
47
|
+
"frontend-pptx",
|
|
48
|
+
"html-rendering",
|
|
49
|
+
"layout-engine",
|
|
50
|
+
"gradient-support",
|
|
51
|
+
"flexbox",
|
|
52
|
+
"css-grid",
|
|
53
|
+
"typography"
|
|
54
|
+
],
|
|
55
|
+
"author": "Atharva Dharmendra Jagtap <atharvaj321@gmail.com>",
|
|
56
|
+
"license": "MIT",
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/atharva9167j/dom-to-pptx/issues"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@rollup/plugin-commonjs": "^29.0.0",
|
|
62
|
+
"@rollup/plugin-json": "^6.1.0",
|
|
63
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
64
|
+
"buffer": "^6.0.3",
|
|
65
|
+
"eslint": "^9.39.1",
|
|
66
|
+
"eslint-config-prettier": "^10.1.8",
|
|
67
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
68
|
+
"events": "^3.3.0",
|
|
69
|
+
"prettier": "^3.7.3",
|
|
70
|
+
"process": "^0.11.10",
|
|
71
|
+
"rollup-plugin-polyfill-node": "^0.10.1",
|
|
72
|
+
"stream-browserify": "^3.0.0",
|
|
73
|
+
"util": "^0.12.5"
|
|
74
|
+
},
|
|
75
|
+
"dependencies": {
|
|
76
|
+
"fonteditor-core": "^2.6.3",
|
|
77
|
+
"html2canvas": "^1.4.1",
|
|
78
|
+
"jszip": "^3.10.1",
|
|
79
|
+
"opentype.js": "^1.3.4",
|
|
80
|
+
"pako": "^2.1.0",
|
|
81
|
+
"pptxgenjs": "^3.12.0"
|
|
82
|
+
}
|
|
83
|
+
}
|
package/rollup.config.js
CHANGED
|
@@ -10,9 +10,11 @@ const input = 'src/index.js';
|
|
|
10
10
|
const onwarn = (warning, warn) => {
|
|
11
11
|
if (warning.code === 'CIRCULAR_DEPENDENCY') {
|
|
12
12
|
// Ignore circular dependencies in these known packages
|
|
13
|
-
if (
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
if (
|
|
14
|
+
warning.message.includes('node_modules/readable-stream') ||
|
|
15
|
+
warning.message.includes('node_modules/jszip') ||
|
|
16
|
+
warning.message.includes('node_modules/semver')
|
|
17
|
+
) {
|
|
16
18
|
return;
|
|
17
19
|
}
|
|
18
20
|
}
|
|
@@ -42,14 +44,7 @@ const configLibrary = {
|
|
|
42
44
|
json(),
|
|
43
45
|
],
|
|
44
46
|
// Mark all dependencies as external so they aren't bundled into the .mjs/.cjs files
|
|
45
|
-
external: [
|
|
46
|
-
'pptxgenjs',
|
|
47
|
-
'html2canvas',
|
|
48
|
-
'jszip',
|
|
49
|
-
'fonteditor-core',
|
|
50
|
-
'opentype.js',
|
|
51
|
-
'pako'
|
|
52
|
-
],
|
|
47
|
+
external: ['pptxgenjs', 'html2canvas', 'jszip', 'fonteditor-core', 'opentype.js', 'pako'],
|
|
53
48
|
onwarn,
|
|
54
49
|
};
|
|
55
50
|
|
|
@@ -73,12 +68,12 @@ const configBundle = {
|
|
|
73
68
|
// If you want to bundle PptxGenJS inside, remove it from external/globals.
|
|
74
69
|
// Usually for "bundle.js", we bundle everything except maybe very large libs.
|
|
75
70
|
// Based on your previous config, we are bundling everything.
|
|
76
|
-
}
|
|
71
|
+
},
|
|
77
72
|
},
|
|
78
73
|
plugins: [
|
|
79
74
|
// 1. JSON plugin (needed for some deps)
|
|
80
75
|
json(),
|
|
81
|
-
|
|
76
|
+
|
|
82
77
|
// 2. Resolve browser versions of modules
|
|
83
78
|
resolve({
|
|
84
79
|
browser: true,
|
|
@@ -98,4 +93,4 @@ const configBundle = {
|
|
|
98
93
|
onwarn,
|
|
99
94
|
};
|
|
100
95
|
|
|
101
|
-
export default [configLibrary, configBundle];
|
|
96
|
+
export default [configLibrary, configBundle];
|
package/src/font-embedder.js
CHANGED
|
@@ -1,159 +1,163 @@
|
|
|
1
|
-
// src/font-embedder.js
|
|
2
|
-
import opentype from 'opentype.js';
|
|
3
|
-
import { fontToEot } from './font-utils.js';
|
|
4
|
-
|
|
5
|
-
const START_RID = 201314;
|
|
6
|
-
|
|
7
|
-
export class PPTXEmbedFonts {
|
|
8
|
-
constructor() {
|
|
9
|
-
this.zip = null;
|
|
10
|
-
this.rId = START_RID;
|
|
11
|
-
this.fonts = []; // { name, data, rid }
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async loadZip(zip) {
|
|
15
|
-
this.zip = zip;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Reads the font name from the buffer using opentype.js
|
|
20
|
-
*/
|
|
21
|
-
getFontInfo(fontBuffer) {
|
|
22
|
-
try {
|
|
23
|
-
const font = opentype.parse(fontBuffer);
|
|
24
|
-
const names = font.names;
|
|
25
|
-
// Prefer English name, fallback to others
|
|
26
|
-
const fontFamily = names.fontFamily.en || Object.values(names.fontFamily)[0];
|
|
27
|
-
return { name: fontFamily };
|
|
28
|
-
} catch (e) {
|
|
29
|
-
console.warn('Could not parse font info', e);
|
|
30
|
-
return { name: 'Unknown' };
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async addFont(fontFace, fontBuffer, type) {
|
|
35
|
-
// Convert to EOT/fntdata for PPTX compatibility
|
|
36
|
-
const eotData = await fontToEot(type, fontBuffer);
|
|
37
|
-
const rid = this.rId++;
|
|
38
|
-
this.fonts.push({ name: fontFace, data: eotData, rid });
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async updateFiles() {
|
|
42
|
-
await this.updateContentTypesXML();
|
|
43
|
-
await this.updatePresentationXML();
|
|
44
|
-
await this.updateRelsPresentationXML();
|
|
45
|
-
this.updateFontFiles();
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async generateBlob() {
|
|
49
|
-
if (!this.zip) throw new Error('Zip not loaded');
|
|
50
|
-
return this.zip.generateAsync({
|
|
51
|
-
type: 'blob',
|
|
52
|
-
compression: 'DEFLATE',
|
|
53
|
-
compressionOptions: { level: 6 },
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// --- XML Manipulation Methods ---
|
|
58
|
-
|
|
59
|
-
async updateContentTypesXML() {
|
|
60
|
-
const file = this.zip.file('[Content_Types].xml');
|
|
61
|
-
if (!file) throw new Error('[Content_Types].xml not found');
|
|
62
|
-
|
|
63
|
-
const xmlStr = await file.async('string');
|
|
64
|
-
const parser = new DOMParser();
|
|
65
|
-
const doc = parser.parseFromString(xmlStr, 'text/xml');
|
|
66
|
-
|
|
67
|
-
const types = doc.getElementsByTagName('Types')[0];
|
|
68
|
-
const defaults = Array.from(doc.getElementsByTagName('Default'));
|
|
69
|
-
|
|
70
|
-
const hasFntData = defaults.some(el => el.getAttribute('Extension') === 'fntdata');
|
|
71
|
-
|
|
72
|
-
if (!hasFntData) {
|
|
73
|
-
const el = doc.createElement('Default');
|
|
74
|
-
el.setAttribute('Extension', 'fntdata');
|
|
75
|
-
el.setAttribute('ContentType', 'application/x-fontdata');
|
|
76
|
-
types.insertBefore(el, types.firstChild);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
this.zip.file('[Content_Types].xml', new XMLSerializer().serializeToString(doc));
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async updatePresentationXML() {
|
|
83
|
-
const file = this.zip.file('ppt/presentation.xml');
|
|
84
|
-
if (!file) throw new Error('ppt/presentation.xml not found');
|
|
85
|
-
|
|
86
|
-
const xmlStr = await file.async('string');
|
|
87
|
-
const parser = new DOMParser();
|
|
88
|
-
const doc = parser.parseFromString(xmlStr, 'text/xml');
|
|
89
|
-
const presentation = doc.getElementsByTagName('p:presentation')[0];
|
|
90
|
-
|
|
91
|
-
// Enable embedding flags
|
|
92
|
-
presentation.setAttribute('saveSubsetFonts', 'true');
|
|
93
|
-
presentation.setAttribute('embedTrueTypeFonts', 'true');
|
|
94
|
-
|
|
95
|
-
// Find or create embeddedFontLst
|
|
96
|
-
let embeddedFontLst = presentation.getElementsByTagName('p:embeddedFontLst')[0];
|
|
97
|
-
|
|
98
|
-
if (!embeddedFontLst) {
|
|
99
|
-
embeddedFontLst = doc.createElement('p:embeddedFontLst');
|
|
100
|
-
|
|
101
|
-
// Insert before defaultTextStyle or at end
|
|
102
|
-
const defaultTextStyle = presentation.getElementsByTagName('p:defaultTextStyle')[0];
|
|
103
|
-
if (defaultTextStyle) {
|
|
104
|
-
presentation.insertBefore(embeddedFontLst, defaultTextStyle);
|
|
105
|
-
} else {
|
|
106
|
-
presentation.appendChild(embeddedFontLst);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Add font references
|
|
111
|
-
this.fonts.forEach(font => {
|
|
112
|
-
// Check if already exists
|
|
113
|
-
const existing = Array.from(embeddedFontLst.getElementsByTagName('p:font'))
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
fontNode.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
regular.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
rel.
|
|
146
|
-
rel.setAttribute('
|
|
147
|
-
rel.setAttribute('
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
this.
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
1
|
+
// src/font-embedder.js
|
|
2
|
+
import opentype from 'opentype.js';
|
|
3
|
+
import { fontToEot } from './font-utils.js';
|
|
4
|
+
|
|
5
|
+
const START_RID = 201314;
|
|
6
|
+
|
|
7
|
+
export class PPTXEmbedFonts {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.zip = null;
|
|
10
|
+
this.rId = START_RID;
|
|
11
|
+
this.fonts = []; // { name, data, rid }
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async loadZip(zip) {
|
|
15
|
+
this.zip = zip;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Reads the font name from the buffer using opentype.js
|
|
20
|
+
*/
|
|
21
|
+
getFontInfo(fontBuffer) {
|
|
22
|
+
try {
|
|
23
|
+
const font = opentype.parse(fontBuffer);
|
|
24
|
+
const names = font.names;
|
|
25
|
+
// Prefer English name, fallback to others
|
|
26
|
+
const fontFamily = names.fontFamily.en || Object.values(names.fontFamily)[0];
|
|
27
|
+
return { name: fontFamily };
|
|
28
|
+
} catch (e) {
|
|
29
|
+
console.warn('Could not parse font info', e);
|
|
30
|
+
return { name: 'Unknown' };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async addFont(fontFace, fontBuffer, type) {
|
|
35
|
+
// Convert to EOT/fntdata for PPTX compatibility
|
|
36
|
+
const eotData = await fontToEot(type, fontBuffer);
|
|
37
|
+
const rid = this.rId++;
|
|
38
|
+
this.fonts.push({ name: fontFace, data: eotData, rid });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async updateFiles() {
|
|
42
|
+
await this.updateContentTypesXML();
|
|
43
|
+
await this.updatePresentationXML();
|
|
44
|
+
await this.updateRelsPresentationXML();
|
|
45
|
+
this.updateFontFiles();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async generateBlob() {
|
|
49
|
+
if (!this.zip) throw new Error('Zip not loaded');
|
|
50
|
+
return this.zip.generateAsync({
|
|
51
|
+
type: 'blob',
|
|
52
|
+
compression: 'DEFLATE',
|
|
53
|
+
compressionOptions: { level: 6 },
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// --- XML Manipulation Methods ---
|
|
58
|
+
|
|
59
|
+
async updateContentTypesXML() {
|
|
60
|
+
const file = this.zip.file('[Content_Types].xml');
|
|
61
|
+
if (!file) throw new Error('[Content_Types].xml not found');
|
|
62
|
+
|
|
63
|
+
const xmlStr = await file.async('string');
|
|
64
|
+
const parser = new DOMParser();
|
|
65
|
+
const doc = parser.parseFromString(xmlStr, 'text/xml');
|
|
66
|
+
|
|
67
|
+
const types = doc.getElementsByTagName('Types')[0];
|
|
68
|
+
const defaults = Array.from(doc.getElementsByTagName('Default'));
|
|
69
|
+
|
|
70
|
+
const hasFntData = defaults.some((el) => el.getAttribute('Extension') === 'fntdata');
|
|
71
|
+
|
|
72
|
+
if (!hasFntData) {
|
|
73
|
+
const el = doc.createElement('Default');
|
|
74
|
+
el.setAttribute('Extension', 'fntdata');
|
|
75
|
+
el.setAttribute('ContentType', 'application/x-fontdata');
|
|
76
|
+
types.insertBefore(el, types.firstChild);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
this.zip.file('[Content_Types].xml', new XMLSerializer().serializeToString(doc));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async updatePresentationXML() {
|
|
83
|
+
const file = this.zip.file('ppt/presentation.xml');
|
|
84
|
+
if (!file) throw new Error('ppt/presentation.xml not found');
|
|
85
|
+
|
|
86
|
+
const xmlStr = await file.async('string');
|
|
87
|
+
const parser = new DOMParser();
|
|
88
|
+
const doc = parser.parseFromString(xmlStr, 'text/xml');
|
|
89
|
+
const presentation = doc.getElementsByTagName('p:presentation')[0];
|
|
90
|
+
|
|
91
|
+
// Enable embedding flags
|
|
92
|
+
presentation.setAttribute('saveSubsetFonts', 'true');
|
|
93
|
+
presentation.setAttribute('embedTrueTypeFonts', 'true');
|
|
94
|
+
|
|
95
|
+
// Find or create embeddedFontLst
|
|
96
|
+
let embeddedFontLst = presentation.getElementsByTagName('p:embeddedFontLst')[0];
|
|
97
|
+
|
|
98
|
+
if (!embeddedFontLst) {
|
|
99
|
+
embeddedFontLst = doc.createElement('p:embeddedFontLst');
|
|
100
|
+
|
|
101
|
+
// Insert before defaultTextStyle or at end
|
|
102
|
+
const defaultTextStyle = presentation.getElementsByTagName('p:defaultTextStyle')[0];
|
|
103
|
+
if (defaultTextStyle) {
|
|
104
|
+
presentation.insertBefore(embeddedFontLst, defaultTextStyle);
|
|
105
|
+
} else {
|
|
106
|
+
presentation.appendChild(embeddedFontLst);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Add font references
|
|
111
|
+
this.fonts.forEach((font) => {
|
|
112
|
+
// Check if already exists
|
|
113
|
+
const existing = Array.from(embeddedFontLst.getElementsByTagName('p:font')).find(
|
|
114
|
+
(node) => node.getAttribute('typeface') === font.name
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
if (!existing) {
|
|
118
|
+
const embedFont = doc.createElement('p:embeddedFont');
|
|
119
|
+
|
|
120
|
+
const fontNode = doc.createElement('p:font');
|
|
121
|
+
fontNode.setAttribute('typeface', font.name);
|
|
122
|
+
embedFont.appendChild(fontNode);
|
|
123
|
+
|
|
124
|
+
const regular = doc.createElement('p:regular');
|
|
125
|
+
regular.setAttribute('r:id', `rId${font.rid}`);
|
|
126
|
+
embedFont.appendChild(regular);
|
|
127
|
+
|
|
128
|
+
embeddedFontLst.appendChild(embedFont);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
this.zip.file('ppt/presentation.xml', new XMLSerializer().serializeToString(doc));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async updateRelsPresentationXML() {
|
|
136
|
+
const file = this.zip.file('ppt/_rels/presentation.xml.rels');
|
|
137
|
+
if (!file) throw new Error('presentation.xml.rels not found');
|
|
138
|
+
|
|
139
|
+
const xmlStr = await file.async('string');
|
|
140
|
+
const parser = new DOMParser();
|
|
141
|
+
const doc = parser.parseFromString(xmlStr, 'text/xml');
|
|
142
|
+
const relationships = doc.getElementsByTagName('Relationships')[0];
|
|
143
|
+
|
|
144
|
+
this.fonts.forEach((font) => {
|
|
145
|
+
const rel = doc.createElement('Relationship');
|
|
146
|
+
rel.setAttribute('Id', `rId${font.rid}`);
|
|
147
|
+
rel.setAttribute('Target', `fonts/${font.rid}.fntdata`);
|
|
148
|
+
rel.setAttribute(
|
|
149
|
+
'Type',
|
|
150
|
+
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/font'
|
|
151
|
+
);
|
|
152
|
+
relationships.appendChild(rel);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
this.zip.file('ppt/_rels/presentation.xml.rels', new XMLSerializer().serializeToString(doc));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
updateFontFiles() {
|
|
159
|
+
this.fonts.forEach((font) => {
|
|
160
|
+
this.zip.file(`ppt/fonts/${font.rid}.fntdata`, font.data);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
package/src/font-utils.js
CHANGED
|
@@ -1,35 +1,32 @@
|
|
|
1
|
-
// src/font-utils.js
|
|
2
|
-
import { Font } from 'fonteditor-core';
|
|
3
|
-
import pako from 'pako';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Converts various font formats to EOT (Embedded OpenType),
|
|
7
|
-
* which is highly compatible with PowerPoint embedding.
|
|
8
|
-
* @param {string} type - 'ttf', 'woff', or 'otf'
|
|
9
|
-
* @param {ArrayBuffer} fontBuffer - The raw font data
|
|
10
|
-
*/
|
|
11
|
-
export async function fontToEot(type, fontBuffer) {
|
|
12
|
-
const options = {
|
|
13
|
-
type,
|
|
14
|
-
hinting: true,
|
|
15
|
-
// inflate is required for WOFF decoding
|
|
16
|
-
inflate: type === 'woff' ? pako.inflate : undefined,
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const font = Font.create(fontBuffer, options);
|
|
20
|
-
|
|
21
|
-
const eotBuffer = font.write({
|
|
22
|
-
type: 'eot',
|
|
23
|
-
toBuffer: true,
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
if (eotBuffer instanceof ArrayBuffer) {
|
|
27
|
-
return eotBuffer;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Ensure we return an ArrayBuffer
|
|
31
|
-
return eotBuffer.buffer.slice(
|
|
32
|
-
|
|
33
|
-
eotBuffer.byteOffset + eotBuffer.byteLength
|
|
34
|
-
);
|
|
35
|
-
}
|
|
1
|
+
// src/font-utils.js
|
|
2
|
+
import { Font } from 'fonteditor-core';
|
|
3
|
+
import pako from 'pako';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Converts various font formats to EOT (Embedded OpenType),
|
|
7
|
+
* which is highly compatible with PowerPoint embedding.
|
|
8
|
+
* @param {string} type - 'ttf', 'woff', or 'otf'
|
|
9
|
+
* @param {ArrayBuffer} fontBuffer - The raw font data
|
|
10
|
+
*/
|
|
11
|
+
export async function fontToEot(type, fontBuffer) {
|
|
12
|
+
const options = {
|
|
13
|
+
type,
|
|
14
|
+
hinting: true,
|
|
15
|
+
// inflate is required for WOFF decoding
|
|
16
|
+
inflate: type === 'woff' ? pako.inflate : undefined,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const font = Font.create(fontBuffer, options);
|
|
20
|
+
|
|
21
|
+
const eotBuffer = font.write({
|
|
22
|
+
type: 'eot',
|
|
23
|
+
toBuffer: true,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
if (eotBuffer instanceof ArrayBuffer) {
|
|
27
|
+
return eotBuffer;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Ensure we return an ArrayBuffer
|
|
31
|
+
return eotBuffer.buffer.slice(eotBuffer.byteOffset, eotBuffer.byteOffset + eotBuffer.byteLength);
|
|
32
|
+
}
|