Skip to content

Commit 87e8ecb

Browse files
committed
feat(pres): get info about loaded templates with pres.getInfo()
1 parent aca40be commit 87e8ecb

File tree

4 files changed

+213
-36
lines changed

4 files changed

+213
-36
lines changed

README.md

Lines changed: 104 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
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 merge templates, customize slide content, and maintain your library of .pptx templates. `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 merge templates, customize slide content, and maintain your library of .pptx templates. `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).
44

55
`pptx-automizer` will fit best to users who try to maintain their own library of pptx template files. This is perfect to anyone who uses complex and well-styled customized layouts. Any existing slide and even a single element can be a data driven template for output pptx files.
66

@@ -130,23 +130,31 @@ const automizer = new Automizer({
130130
// any existing slide in RootTemplate.pptx. Otherwise, we are going to start
131131
// with a truncated root template.
132132
let pres = automizer
133-
.loadRoot(`RootTemplate.pptx`)
133+
.loadRoot('RootTemplate.pptx')
134134
// We want to make some more files available and give them a handy label.
135-
.load(`SlideWithShapes.pptx`, 'shapes')
136-
.load(`SlideWithGraph.pptx`, 'graph')
135+
.load('SlideWithShapes.pptx', 'shapes')
136+
.load('SlideWithGraph.pptx', 'graph')
137137
// Skipping the second argument will not set a label.
138-
.load(`SlideWithImages.pptx`);
138+
.load('SlideWithImages.pptx');
139+
140+
// Get useful information about loaded templates:
141+
/*
142+
const presInfo = await pres.getInfo();
143+
const mySlides = presInfo.slidesByTemplate('shapes');
144+
const mySlide = presInfo.slideByNumber('shapes', 2);
145+
const myShape = presInfo.elementByName('shapes', 2, 'Cloud');
146+
*/
139147

140148
// addSlide takes two arguments: The first will specify the source
141149
// presentation's label to get the template from, the second will set the
142150
// slide number to require.
143151
pres
144152
.addSlide('graph', 1)
145153
.addSlide('shapes', 1)
146-
.addSlide(`SlideWithImages.pptx`, 2);
154+
.addSlide('SlideWithImages.pptx', 2);
147155

148156
// Finally, we want to write the output file.
149-
pres.write(`myPresentation.pptx`).then((summary) => {
157+
pres.write('myPresentation.pptx').then((summary) => {
150158
console.log(summary);
151159
});
152160

@@ -478,12 +486,12 @@ pres.addSlide('charts', 2, (slide) => {
478486
```
479487

480488
Find out more about modifying charts:
489+
481490
- [Modify chart axis](https://github.com/singerla/pptx-automizer/blob/main/__tests__/modify-chart-axis.test.ts)
482491
- [Dealing with bubble charts](https://github.com/singerla/pptx-automizer/blob/main/__tests__/modify-chart-bubbles.test.ts)
483492
- [Vertical line charts](https://github.com/singerla/pptx-automizer/blob/main/__tests__/modify-chart-vertical-lines.test.ts)
484493
- [Style chart series and data points](https://github.com/singerla/pptx-automizer/blob/main/__tests__/modify-existing-chart-styled.test.ts)
485494

486-
487495
## Modify extended charts
488496

489497
If you need to modify extended chart types, such like waterfall or map charts, you need to use `modify.setExtendedChartData`.
@@ -526,6 +534,94 @@ pres
526534
});
527535
```
528536

537+
# Examples
538+
539+
## Loop through an existing presentation
540+
541+
If you would like to modify some elements in a single .pptx file, it is important to that `pptx-automizer` is not able to directly "jump" to a shape and modify.
542+
543+
This is how it works internally:
544+
545+
- Load a root template to append slides to
546+
- (Probably) load root template again to modify slides
547+
- Load other templates
548+
- Append a loaded slide to (probably truncated) root template
549+
- Modify the recently added slide
550+
- Write root template and appended slides as output presentation.
551+
552+
In case you need to apply modifications to the root template, you need to load it as a normal template:
553+
554+
```ts
555+
import Automizer, {
556+
CmToDxa,
557+
ISlide,
558+
ModifyColorHelper,
559+
ModifyShapeHelper,
560+
ModifyTextHelper,
561+
} from 'pptx-automizer';
562+
563+
const run = async () => {
564+
const automizer = new Automizer({
565+
templateDir: `path/to/pptx-templates`,
566+
outputDir: `path/to/pptx-output`,
567+
// this is required to start with no slides:
568+
removeExistingSlides: true,
569+
});
570+
571+
let pres = automizer
572+
.loadRoot(`SlideWithShapes.pptx`)
573+
// We load it twice to make it available for modifying slides.
574+
// Defining a "name" as second params makes it a little easier
575+
.load(`SlideWithShapes.pptx`, 'myTemplate');
576+
577+
// This is brandnew: get useful information about loaded templates:
578+
const myTemplates = await pres.getInfo();
579+
const mySlides = myTemplates.slidesByTemplate(`myTemplate`);
580+
581+
// Feel free to create some functions to pre-define all modifications
582+
// you need to apply to your slides.
583+
type CallbackBySlideNumber = {
584+
slideNumber: number;
585+
callback: (slide: ISlide) => void;
586+
};
587+
const callbacks: CallbackBySlideNumber[] = [
588+
{
589+
slideNumber: 2,
590+
callback: (slide: ISlide) => {
591+
slide.modifyElement('Cloud', [
592+
ModifyTextHelper.setText('My content'),
593+
ModifyShapeHelper.setPosition({
594+
h: CmToDxa(5),
595+
}),
596+
ModifyColorHelper.solidFill({
597+
type: 'srgbClr',
598+
value: 'cccccc',
599+
}),
600+
]);
601+
},
602+
},
603+
];
604+
const getCallbacks = (slideNumber: number) => {
605+
return callbacks.find((callback) => callback.slideNumber === slideNumber)
606+
?.callback;
607+
};
608+
609+
// We can loop all slides an apply the callbacks if defined
610+
mySlides.forEach((mySlide) => {
611+
pres.addSlide('myTemplate', mySlide.number, getCallbacks(mySlide.number));
612+
});
613+
614+
// This will result to an output presentation containing all slides of "SlideWithShapes.pptx"
615+
pres.write(`myOutputPresentation.pptx`).then((summary) => {
616+
console.log(summary);
617+
});
618+
};
619+
620+
run().catch((error) => {
621+
console.error(error);
622+
});
623+
```
624+
529625
## Sort output slides
530626

531627
There are three ways to arrange slides in an output presentation.
@@ -701,7 +797,6 @@ This project is deeply inspired by:
701797
- [node-pptx](https://github.com/heavysixer/node-pptx)
702798
- [docxtemplater](https://github.com/open-xml-templating/docxtemplater)
703799

704-
705800
### Commercial Support
706801

707802
If you need commercial support on complex .pptx automation, please take a look at [ensembl.io](https://ensembl.io).

src/automizer.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,20 @@ import {
33
ArchiveParams,
44
AutomizerParams,
55
AutomizerSummary,
6+
PresentationInfo,
67
SourceIdentifier,
78
StatusTracker,
89
} from './types/types';
910
import { IPresentationProps } from './interfaces/ipresentation-props';
1011
import { PresTemplate } from './interfaces/pres-template';
1112
import { RootPresTemplate } from './interfaces/root-pres-template';
1213
import { Template } from './classes/template';
13-
import { ModifyXmlCallback, TemplateInfo, XmlElement } from './types/xml-types';
14+
import {
15+
ModifyXmlCallback,
16+
SlideInfo,
17+
TemplateInfo,
18+
XmlElement,
19+
} from './types/xml-types';
1420
import { GeneralHelper, vd } from './helper/general-helper';
1521
import { Master } from './classes/master';
1622
import path from 'path';
@@ -242,6 +248,37 @@ export default class Automizer implements IPresentationProps {
242248
return templateCreationId;
243249
}
244250

251+
/**
252+
* Get some info about the imported templates
253+
* @returns Promise<PresentationInfo>
254+
*/
255+
public async getInfo(): Promise<PresentationInfo> {
256+
const creationIds = await this.setCreationIds();
257+
const info: PresentationInfo = {
258+
templateByName: (tplName: string) => {
259+
return creationIds.find((template) => template.name === tplName);
260+
},
261+
slidesByTemplate: (tplName: string) => {
262+
return info.templateByName(tplName)?.slides || [];
263+
},
264+
slideByNumber: (tplName: string, slideNumber: number) => {
265+
return info
266+
.slidesByTemplate(tplName)
267+
.find((slide) => slide.number === slideNumber);
268+
},
269+
elementByName: (
270+
tplName: string,
271+
slideNumber: number,
272+
elementName: string,
273+
) => {
274+
return info
275+
.slideByNumber(tplName, slideNumber)
276+
?.elements.find((element) => elementName === element.name);
277+
},
278+
};
279+
return info;
280+
}
281+
245282
/**
246283
* Determines whether template is root or default template.
247284
* @param template

src/dev.ts

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,65 @@
1-
import Automizer, { ModifyImageHelper, ModifyShapeHelper } from './index';
2-
import { CmToDxa } from './helper/modify-helper';
1+
import Automizer, {
2+
CmToDxa,
3+
ISlide,
4+
ModifyColorHelper,
5+
ModifyShapeHelper,
6+
ModifyTextHelper,
7+
} from './index';
38

49
const run = async () => {
510
const automizer = new Automizer({
611
templateDir: `${__dirname}/../__tests__/pptx-templates`,
712
outputDir: `${__dirname}/../__tests__/pptx-output`,
8-
mediaDir: `${__dirname}/../__tests__/media`,
13+
removeExistingSlides: true,
914
});
1015

11-
const pres = automizer
12-
.loadRoot(`RootTemplate.pptx`)
13-
.loadMedia(`feather.png`)
14-
.load(`SlideWithShapes.pptx`, 'shapes')
15-
.load(`SlideWithImages.pptx`, 'images');
16+
let pres = automizer
17+
.loadRoot(`SlideWithShapes.pptx`)
18+
// We load it twice to make it available for modifying slides
19+
// Defining a "name" as second params makes it a little easier
20+
.load(`SlideWithShapes.pptx`, 'myTemplate');
1621

17-
await pres
18-
.addSlide('images', 2, (slide) => {
19-
slide.modifyElement('imagePNG', [
20-
ModifyShapeHelper.setPosition({
21-
w: CmToDxa(5),
22-
h: CmToDxa(5),
23-
}),
24-
ModifyImageHelper.setRelationTarget('feather.png'),
25-
ModifyImageHelper.setDuotoneFill({
26-
tint: 100000,
27-
color: {
22+
// This is brandnew: get useful information about loaded templates:
23+
const myTemplates = await pres.getInfo();
24+
const mySlides = myTemplates.slidesByTemplate(`myTemplate`);
25+
26+
// Feel free to create some functions to pre-define all modifications
27+
// you need to apply to your slides.
28+
type CallbackBySlideNumber = {
29+
slideNumber: number;
30+
callback: (slide: ISlide) => void;
31+
};
32+
const callbacks: CallbackBySlideNumber[] = [
33+
{
34+
slideNumber: 2,
35+
callback: (slide: ISlide) => {
36+
slide.modifyElement('Cloud', [
37+
ModifyTextHelper.setText('My content'),
38+
ModifyShapeHelper.setPosition({
39+
h: CmToDxa(5),
40+
}),
41+
ModifyColorHelper.solidFill({
2842
type: 'srgbClr',
29-
value: 'ff850c',
30-
},
31-
}),
32-
]);
33-
})
34-
.write(`modify-shapes.test.pptx`);
43+
value: 'cccccc',
44+
}),
45+
]);
46+
},
47+
},
48+
];
49+
const getCallbacks = (slideNumber: number) => {
50+
return callbacks.find((callback) => callback.slideNumber === slideNumber)
51+
?.callback;
52+
};
53+
54+
// We can loop all slides an apply the callbacks if defined
55+
mySlides.forEach((mySlide) => {
56+
pres.addSlide('myTemplate', mySlide.number, getCallbacks(mySlide.number));
57+
});
58+
59+
// This will result to an output presentation containing all slides of "SlideWithShapes.pptx"
60+
pres.write(`myOutputPresentation.pptx`).then((summary) => {
61+
console.log(summary);
62+
});
3563
};
3664

3765
run().catch((error) => {

src/types/types.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import { ElementSubtype, ElementType } from '../enums/element-type';
2-
import { RelationshipAttribute, XmlDocument, XmlElement } from './xml-types';
2+
import {
3+
ElementInfo,
4+
RelationshipAttribute,
5+
SlideInfo,
6+
TemplateInfo,
7+
XmlDocument,
8+
XmlElement,
9+
} from './xml-types';
310
import IArchive, { ArchiveMode } from '../interfaces/iarchive';
411
import { ContentTypeExtension } from '../enums/content-type-map';
512

@@ -100,6 +107,16 @@ export type AutomizerSummary = {
100107
images: number;
101108
masters: number;
102109
};
110+
export type PresentationInfo = {
111+
templateByName: (tplName: string) => TemplateInfo;
112+
slidesByTemplate: (tplName: string) => SlideInfo[];
113+
slideByNumber: (tplName: string, slideNumber: number) => SlideInfo;
114+
elementByName: (
115+
tplName: string,
116+
slideNumber: number,
117+
elementName: string,
118+
) => ElementInfo;
119+
};
103120
export type Target = {
104121
file: string;
105122
type: string;

0 commit comments

Comments
 (0)