Skip to content

Commit

Permalink
test(generate): text, charts, images with pptxgenjs #60
Browse files Browse the repository at this point in the history
  • Loading branch information
singerla committed Nov 27, 2023
1 parent 4851f30 commit 122b7fc
Show file tree
Hide file tree
Showing 13 changed files with 367 additions and 132 deletions.
32 changes: 30 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# pptx-automizer: A Powerful .pptx Modifier for Node.js

`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).
`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).

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.

`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.

Expand Down Expand Up @@ -561,6 +563,32 @@ pres.addSlide('charts', 2, (slide) => {
});
```


## Add elements with PptxGenJs

If you require to add an element from scratch, you can use [PptxGenJS](https://github.com/gitbrent/PptxGenJS) by calling `slide.generate()`.

```ts
pres.addSlide('empty', 1, (slide) => {
// Use pptxgenjs to add text from scratch:
slide.generate((pptxGenJSSlide, objectName) => {
pptxGenJSSlide.addText('Test', {
x: 1,
y: 1,
color: '363636',
// if you did not set a custom object name, a random id
// needs to be passed for objectName.
objectName,
});
}, 'custom object name');
});
```

Find out more about adding elements with `PptxGenJs`:

- [Add generated charts](https://github.com/singerla/pptx-automizer/blob/main/__tests__/generate-pptx-genjs-charts.test.ts)
- [Add arbitrary images](https://github.com/singerla/pptx-automizer/blob/main/__tests__/generate-pptx-genjs-image.test.ts)

## Remove elements from a slide

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

If you need commercial support on complex .pptx automation, please take a look at [ensemblio.com](https://ensemblio.com).
![ensemblio](https://ensemblio.com/ensemblio-lg.png)
![ensemblio](https://ensembl.io/ensemblio-lg.png)
57 changes: 57 additions & 0 deletions __tests__/generate-pptxgenjs-charts.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import Automizer from '../src/automizer';
import { ChartData, modify } from '../src';

const dataChartAreaLine = [
{
name: 'Actual Sales',
labels: ['Jan', 'Feb', 'Mar'],
values: [1500, 4600, 5156],
},
{
name: 'Projected Sales',
labels: ['Jan', 'Feb', 'Mar'],
values: [1000, 2600, 3456],
},
];

test('generate a chart with pptxgenjs and add it to a template slide', async () => {
const automizer = new Automizer({
templateDir: `${__dirname}/pptx-templates`,
outputDir: `${__dirname}/pptx-output`,
});

const pres = automizer
.loadRoot(`RootTemplate.pptx`)
.load(`EmptySlide.pptx`, 'empty')
.load(`SlideWithCharts.pptx`, 'charts');

pres.addSlide('empty', 1, (slide) => {
// Use pptxgenjs to add generated contents from scratch:
slide.generate((pSlide, objectName, pptxGenJs) => {
pSlide.addChart(pptxGenJs.ChartType.line, dataChartAreaLine, {
x: 1,
y: 1,
w: 8,
h: 4,
objectName,
});
});
});

// Mix the created chart with modified existing chart
pres.addSlide('charts', 2, (slide) => {
slide.modifyElement('ColumnChart', [
modify.setChartData(<ChartData>{
series: [{ label: 'series 1' }, { label: 'series 3' }],
categories: [
{ label: 'cat 2-1', values: [50, 50] },
{ label: 'cat 2-2', values: [14, 50] },
],
}),
]);
});

const result = await pres.write(`generate-pptxgenjs-charts.test.pptx`);

expect(result.charts).toBe(4);
});
29 changes: 29 additions & 0 deletions __tests__/generate-pptxgenjs-image.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Automizer from '../src/automizer';
import { ChartData, modify } from '../src';

test('insert an image with pptxgenjs on a template slide', async () => {
const automizer = new Automizer({
templateDir: `${__dirname}/pptx-templates`,
outputDir: `${__dirname}/pptx-output`,
});

const pres = automizer
.loadRoot(`RootTemplate.pptx`)
.load(`EmptySlide.pptx`, 'empty');

pres.addSlide('empty', 1, (slide) => {
// Use pptxgenjs to add image from file:
slide.generate((pptxGenJSSlide, objectName) => {
pptxGenJSSlide.addImage({
path: `${__dirname}/images/test.png`,
x: 1,
y: 2,
objectName,
});
});
});

const result = await pres.write(`generate-pptxgenjs-image.test.pptx`);

expect(result.images).toBe(1);
});
29 changes: 29 additions & 0 deletions __tests__/generate-pptxgenjs-text.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Automizer from '../src/automizer';
import { ChartData, modify } from '../src';

test('insert a textbox with pptxgenjs on a template slide', async () => {
const automizer = new Automizer({
templateDir: `${__dirname}/pptx-templates`,
outputDir: `${__dirname}/pptx-output`,
});

const pres = automizer
.loadRoot(`RootTemplate.pptx`)
.load(`EmptySlide.pptx`, 'empty');

pres.addSlide('empty', 1, (slide) => {
// Use pptxgenjs to add text from scratch:
slide.generate((pptxGenJSSlide, objectName) => {
pptxGenJSSlide.addText('Test', {
x: 1,
y: 1,
color: '363636',
objectName,
});
}, 'custom object name');
});

const result = await pres.write(`generate-pptxgenjs-text.test.pptx`);

expect(result.slides).toBe(2);
});
6 changes: 2 additions & 4 deletions src/automizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,16 +437,12 @@ export default class Automizer implements IPresentationProps {
}

async finalizePresentation() {
await this.rootTemplate.injectPptxGenJS();

await this.writeMasterSlides();
await this.writeSlides();
await this.writeMediaFiles();
await this.normalizePresentation();
await this.applyModifyPresentationCallbacks();

// await this.rootTemplate.cleanupPptxGenJS();

// TODO: refactor content tracker, move this to root template
Tracker.reset();
}
Expand All @@ -467,9 +463,11 @@ export default class Automizer implements IPresentationProps {
await this.rootTemplate.countExistingSlides();
this.status.max = this.rootTemplate.slides.length;

await this.rootTemplate.runExternalGenerators();
for (const slide of this.rootTemplate.slides) {
await this.rootTemplate.appendSlide(slide);
}
await this.rootTemplate.cleanupExternalGenerators();

if (this.params.removeExistingSlides) {
await this.rootTemplate.truncate();
Expand Down
1 change: 0 additions & 1 deletion src/classes/has-shapes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,6 @@ export default class HasShapes {
*/
async copyRelatedContent(): Promise<void> {
const charts = await Chart.getAllOnSlide(this.sourceArchive, this.relsPath);
vd(charts.length);
for (const chart of charts) {
await new Chart(
{
Expand Down
72 changes: 19 additions & 53 deletions src/classes/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ import { ArchiveParams, MediaFile } from '../types/types';
import Automizer from '../automizer';
import { IMaster } from '../interfaces/imaster';
import { ILayout } from '../interfaces/ilayout';
import PptxGenJS from 'pptxgenjs';
import { randomUUID } from 'crypto';
import fs from 'fs';
import { IGenerator } from '../interfaces/igenerator';
import GeneratePptxGenJs from '../helper/generate/generate-pptxgenjs';

export class Template implements ITemplate {
/**
Expand Down Expand Up @@ -69,8 +68,7 @@ export class Template implements ITemplate {
mediaFiles: MediaFile[] = [];

automizer: Automizer;
pptxGenJS: PptxGenJS;
pptxGenJSTmpFile: string;
generators: IGenerator[] = [];

constructor(location: string, params: ArchiveParams) {
this.location = location;
Expand Down Expand Up @@ -196,54 +194,6 @@ export class Template implements ITemplate {
});
}

async injectPptxGenJS(): Promise<void> {
this.createPptxGenJSInstance();
this.pptxGenJSTmpFile = randomUUID() + '.pptx';

let generatedSlidesCount = 0;
for (const slide of this.slides) {
const generate = slide.getGeneratedElements();
if (generate.length) {
generatedSlidesCount++;
const pgenSlide = this.appendPptxGenSlide();

generate.forEach((generateElement) => {
generateElement.objectName =
generateElement.objectName || randomUUID();
generateElement.tmpSlideNumber = generatedSlidesCount;
generateElement.callback(
pgenSlide,
generateElement.objectName,
this.pptxGenJS,
);

slide.addElement(
this.pptxGenJSTmpFile,
generatedSlidesCount,
generateElement.objectName,
);
});
}
}

if (generatedSlidesCount > 0) {
await this.pptxGenJS.writeFile({
fileName: this.automizer.templateDir + '/' + this.pptxGenJSTmpFile,
});
this.automizer.load(this.pptxGenJSTmpFile);
}
}

createPptxGenJSInstance(): void {
this.pptxGenJS = new PptxGenJS();
}
appendPptxGenSlide(): PptxGenJS.Slide {
return this.pptxGenJS.addSlide();
}
async cleanupPptxGenJS() {
fs.unlinkSync(this.automizer.templateDir + '/' + this.pptxGenJSTmpFile);
}

async countExistingSlides(): Promise<void> {
const xml = await this.getSlideIdList();
const sldIdLst = xml.getElementsByTagName('p:sldIdLst');
Expand Down Expand Up @@ -288,4 +238,20 @@ export class Template implements ITemplate {
count(name: string): number {
return CountHelper.count(name, this.counter);
}

async runExternalGenerators() {
this.generators.push(
new GeneratePptxGenJs(this.automizer, this.slides).create(),
);

for (const generator of this.generators) {
await generator.generateSlides();
}
}

async cleanupExternalGenerators() {
for (const generator of this.generators) {
await generator.cleanup();
}
}
}
Loading

0 comments on commit 122b7fc

Please sign in to comment.