Skip to content

Commit 122b7fc

Browse files
committed
test(generate): text, charts, images with pptxgenjs #60
1 parent 4851f30 commit 122b7fc

File tree

13 files changed

+367
-132
lines changed

13 files changed

+367
-132
lines changed

README.md

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# pptx-automizer: A Powerful .pptx Modifier for Node.js
22

3-
`pptx-automizer` is a Node.js-based PowerPoint (.pptx) generator that automates the manipulation of existing .pptx files. With `pptx-automizer`, you can import your library of .pptx templates, merge templates, and customize slide content. `pptx-automizer` will not write files from scratch, but edit and merge existing pptx files. You can style template slides within PowerPoint, and these templates will be seamlessly integrated into the output presentation. Most of the content can be modified by using callbacks with [xmldom](https://github.com/xmldom/xmldom).
3+
`pptx-automizer` is a Node.js-based PowerPoint (.pptx) generator that automates the manipulation of existing .pptx files. With `pptx-automizer`, you can import your library of .pptx templates, merge templates, and customize slide content. You can style template slides within PowerPoint, and these templates will be seamlessly integrated into the output presentation. Most of the content can be modified by using callbacks with [xmldom](https://github.com/xmldom/xmldom).
4+
5+
If you require to create elements from scratch, `pptx-automizer` wraps around [PptxGenJS](https://github.com/gitbrent/PptxGenJS). Use the powerful syntax of `PptxGenJS` to add dynamic content to your existing .pptx template files.
46

57
`pptx-automizer` is particularly well-suited for users who aim to manage their own library of .pptx template files, making it an ideal choice for those who work with intricate, well-designed customized layouts. With this tool, any existing slide or even a single element can serve as a data-driven template for generating output .pptx files.
68

@@ -561,6 +563,32 @@ pres.addSlide('charts', 2, (slide) => {
561563
});
562564
```
563565

566+
567+
## Add elements with PptxGenJs
568+
569+
If you require to add an element from scratch, you can use [PptxGenJS](https://github.com/gitbrent/PptxGenJS) by calling `slide.generate()`.
570+
571+
```ts
572+
pres.addSlide('empty', 1, (slide) => {
573+
// Use pptxgenjs to add text from scratch:
574+
slide.generate((pptxGenJSSlide, objectName) => {
575+
pptxGenJSSlide.addText('Test', {
576+
x: 1,
577+
y: 1,
578+
color: '363636',
579+
// if you did not set a custom object name, a random id
580+
// needs to be passed for objectName.
581+
objectName,
582+
});
583+
}, 'custom object name');
584+
});
585+
```
586+
587+
Find out more about adding elements with `PptxGenJs`:
588+
589+
- [Add generated charts](https://github.com/singerla/pptx-automizer/blob/main/__tests__/generate-pptx-genjs-charts.test.ts)
590+
- [Add arbitrary images](https://github.com/singerla/pptx-automizer/blob/main/__tests__/generate-pptx-genjs-image.test.ts)
591+
564592
## Remove elements from a slide
565593

566594
You can as well remove elements from slides.
@@ -885,4 +913,4 @@ This project was inspired by:
885913
# Commercial Support
886914

887915
If you need commercial support on complex .pptx automation, please take a look at [ensemblio.com](https://ensemblio.com).
888-
![ensemblio](https://ensemblio.com/ensemblio-lg.png)
916+
![ensemblio](https://ensembl.io/ensemblio-lg.png)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import Automizer from '../src/automizer';
2+
import { ChartData, modify } from '../src';
3+
4+
const dataChartAreaLine = [
5+
{
6+
name: 'Actual Sales',
7+
labels: ['Jan', 'Feb', 'Mar'],
8+
values: [1500, 4600, 5156],
9+
},
10+
{
11+
name: 'Projected Sales',
12+
labels: ['Jan', 'Feb', 'Mar'],
13+
values: [1000, 2600, 3456],
14+
},
15+
];
16+
17+
test('generate a chart with pptxgenjs and add it to a template slide', async () => {
18+
const automizer = new Automizer({
19+
templateDir: `${__dirname}/pptx-templates`,
20+
outputDir: `${__dirname}/pptx-output`,
21+
});
22+
23+
const pres = automizer
24+
.loadRoot(`RootTemplate.pptx`)
25+
.load(`EmptySlide.pptx`, 'empty')
26+
.load(`SlideWithCharts.pptx`, 'charts');
27+
28+
pres.addSlide('empty', 1, (slide) => {
29+
// Use pptxgenjs to add generated contents from scratch:
30+
slide.generate((pSlide, objectName, pptxGenJs) => {
31+
pSlide.addChart(pptxGenJs.ChartType.line, dataChartAreaLine, {
32+
x: 1,
33+
y: 1,
34+
w: 8,
35+
h: 4,
36+
objectName,
37+
});
38+
});
39+
});
40+
41+
// Mix the created chart with modified existing chart
42+
pres.addSlide('charts', 2, (slide) => {
43+
slide.modifyElement('ColumnChart', [
44+
modify.setChartData(<ChartData>{
45+
series: [{ label: 'series 1' }, { label: 'series 3' }],
46+
categories: [
47+
{ label: 'cat 2-1', values: [50, 50] },
48+
{ label: 'cat 2-2', values: [14, 50] },
49+
],
50+
}),
51+
]);
52+
});
53+
54+
const result = await pres.write(`generate-pptxgenjs-charts.test.pptx`);
55+
56+
expect(result.charts).toBe(4);
57+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import Automizer from '../src/automizer';
2+
import { ChartData, modify } from '../src';
3+
4+
test('insert an image with pptxgenjs on a template slide', async () => {
5+
const automizer = new Automizer({
6+
templateDir: `${__dirname}/pptx-templates`,
7+
outputDir: `${__dirname}/pptx-output`,
8+
});
9+
10+
const pres = automizer
11+
.loadRoot(`RootTemplate.pptx`)
12+
.load(`EmptySlide.pptx`, 'empty');
13+
14+
pres.addSlide('empty', 1, (slide) => {
15+
// Use pptxgenjs to add image from file:
16+
slide.generate((pptxGenJSSlide, objectName) => {
17+
pptxGenJSSlide.addImage({
18+
path: `${__dirname}/images/test.png`,
19+
x: 1,
20+
y: 2,
21+
objectName,
22+
});
23+
});
24+
});
25+
26+
const result = await pres.write(`generate-pptxgenjs-image.test.pptx`);
27+
28+
expect(result.images).toBe(1);
29+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import Automizer from '../src/automizer';
2+
import { ChartData, modify } from '../src';
3+
4+
test('insert a textbox with pptxgenjs on a template slide', async () => {
5+
const automizer = new Automizer({
6+
templateDir: `${__dirname}/pptx-templates`,
7+
outputDir: `${__dirname}/pptx-output`,
8+
});
9+
10+
const pres = automizer
11+
.loadRoot(`RootTemplate.pptx`)
12+
.load(`EmptySlide.pptx`, 'empty');
13+
14+
pres.addSlide('empty', 1, (slide) => {
15+
// Use pptxgenjs to add text from scratch:
16+
slide.generate((pptxGenJSSlide, objectName) => {
17+
pptxGenJSSlide.addText('Test', {
18+
x: 1,
19+
y: 1,
20+
color: '363636',
21+
objectName,
22+
});
23+
}, 'custom object name');
24+
});
25+
26+
const result = await pres.write(`generate-pptxgenjs-text.test.pptx`);
27+
28+
expect(result.slides).toBe(2);
29+
});

src/automizer.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -437,16 +437,12 @@ export default class Automizer implements IPresentationProps {
437437
}
438438

439439
async finalizePresentation() {
440-
await this.rootTemplate.injectPptxGenJS();
441-
442440
await this.writeMasterSlides();
443441
await this.writeSlides();
444442
await this.writeMediaFiles();
445443
await this.normalizePresentation();
446444
await this.applyModifyPresentationCallbacks();
447445

448-
// await this.rootTemplate.cleanupPptxGenJS();
449-
450446
// TODO: refactor content tracker, move this to root template
451447
Tracker.reset();
452448
}
@@ -467,9 +463,11 @@ export default class Automizer implements IPresentationProps {
467463
await this.rootTemplate.countExistingSlides();
468464
this.status.max = this.rootTemplate.slides.length;
469465

466+
await this.rootTemplate.runExternalGenerators();
470467
for (const slide of this.rootTemplate.slides) {
471468
await this.rootTemplate.appendSlide(slide);
472469
}
470+
await this.rootTemplate.cleanupExternalGenerators();
473471

474472
if (this.params.removeExistingSlides) {
475473
await this.rootTemplate.truncate();

src/classes/has-shapes.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,6 @@ export default class HasShapes {
728728
*/
729729
async copyRelatedContent(): Promise<void> {
730730
const charts = await Chart.getAllOnSlide(this.sourceArchive, this.relsPath);
731-
vd(charts.length);
732731
for (const chart of charts) {
733732
await new Chart(
734733
{

src/classes/template.ts

Lines changed: 19 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@ import { ArchiveParams, MediaFile } from '../types/types';
1414
import Automizer from '../automizer';
1515
import { IMaster } from '../interfaces/imaster';
1616
import { ILayout } from '../interfaces/ilayout';
17-
import PptxGenJS from 'pptxgenjs';
18-
import { randomUUID } from 'crypto';
19-
import fs from 'fs';
17+
import { IGenerator } from '../interfaces/igenerator';
18+
import GeneratePptxGenJs from '../helper/generate/generate-pptxgenjs';
2019

2120
export class Template implements ITemplate {
2221
/**
@@ -69,8 +68,7 @@ export class Template implements ITemplate {
6968
mediaFiles: MediaFile[] = [];
7069

7170
automizer: Automizer;
72-
pptxGenJS: PptxGenJS;
73-
pptxGenJSTmpFile: string;
71+
generators: IGenerator[] = [];
7472

7573
constructor(location: string, params: ArchiveParams) {
7674
this.location = location;
@@ -196,54 +194,6 @@ export class Template implements ITemplate {
196194
});
197195
}
198196

199-
async injectPptxGenJS(): Promise<void> {
200-
this.createPptxGenJSInstance();
201-
this.pptxGenJSTmpFile = randomUUID() + '.pptx';
202-
203-
let generatedSlidesCount = 0;
204-
for (const slide of this.slides) {
205-
const generate = slide.getGeneratedElements();
206-
if (generate.length) {
207-
generatedSlidesCount++;
208-
const pgenSlide = this.appendPptxGenSlide();
209-
210-
generate.forEach((generateElement) => {
211-
generateElement.objectName =
212-
generateElement.objectName || randomUUID();
213-
generateElement.tmpSlideNumber = generatedSlidesCount;
214-
generateElement.callback(
215-
pgenSlide,
216-
generateElement.objectName,
217-
this.pptxGenJS,
218-
);
219-
220-
slide.addElement(
221-
this.pptxGenJSTmpFile,
222-
generatedSlidesCount,
223-
generateElement.objectName,
224-
);
225-
});
226-
}
227-
}
228-
229-
if (generatedSlidesCount > 0) {
230-
await this.pptxGenJS.writeFile({
231-
fileName: this.automizer.templateDir + '/' + this.pptxGenJSTmpFile,
232-
});
233-
this.automizer.load(this.pptxGenJSTmpFile);
234-
}
235-
}
236-
237-
createPptxGenJSInstance(): void {
238-
this.pptxGenJS = new PptxGenJS();
239-
}
240-
appendPptxGenSlide(): PptxGenJS.Slide {
241-
return this.pptxGenJS.addSlide();
242-
}
243-
async cleanupPptxGenJS() {
244-
fs.unlinkSync(this.automizer.templateDir + '/' + this.pptxGenJSTmpFile);
245-
}
246-
247197
async countExistingSlides(): Promise<void> {
248198
const xml = await this.getSlideIdList();
249199
const sldIdLst = xml.getElementsByTagName('p:sldIdLst');
@@ -288,4 +238,20 @@ export class Template implements ITemplate {
288238
count(name: string): number {
289239
return CountHelper.count(name, this.counter);
290240
}
241+
242+
async runExternalGenerators() {
243+
this.generators.push(
244+
new GeneratePptxGenJs(this.automizer, this.slides).create(),
245+
);
246+
247+
for (const generator of this.generators) {
248+
await generator.generateSlides();
249+
}
250+
}
251+
252+
async cleanupExternalGenerators() {
253+
for (const generator of this.generators) {
254+
await generator.cleanup();
255+
}
256+
}
291257
}

0 commit comments

Comments
 (0)