Skip to content

Commit de0d21c

Browse files
authored
Merge pull request #42 from singerla/feature-content-tracker
Merge Feature content tracker
2 parents 298b909 + 3fd1b71 commit de0d21c

19 files changed

+794
-123
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ const automizer = new Automizer({
7272
// truncate root presentation and start with zero slides
7373
removeExistingSlides: true,
7474

75+
// activate `cleanup` to eventually remove unused files:
76+
cleanup: false,
77+
78+
// Set a value from 0-9 to specify the zip-compression level.
79+
// The lower the number, the faster your output file will be ready.
80+
// Higher compression levels produce smaller files.
81+
compression: 0,
82+
7583
// use a callback function to track pptx generation process.
7684
// statusTracker: myStatusTracker,
7785
})
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import Automizer, { modify } from '../src/index';
2+
import { ChartData } from '../dist';
3+
4+
test('create presentation, add and modify a scatter chart with embedded point images.', 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(`ChartScatter.pptx`, 'charts');
13+
14+
const dataScatter = <ChartData>(<unknown>{
15+
series: [{ label: 'series s1' }],
16+
categories: [
17+
{ label: 'r1', values: [{ x: 10, y: 20 }] },
18+
{ label: 'r2', values: [{ x: 21, y: 11 }] },
19+
{ label: 'r3', values: [{ x: 22, y: 28 }] },
20+
{ label: 'r4', values: [{ x: 13, y: 13 }] },
21+
],
22+
});
23+
24+
const result = await pres
25+
.addSlide('charts', 3, (slide) => {
26+
slide.modifyElement('ScatterPointImages', [
27+
modify.setChartScatter(dataScatter),
28+
]);
29+
})
30+
.write(`modify-chart-scatter-images.test.pptx`);
31+
32+
// expect(result.charts).toBe(2);
33+
});
42.9 KB
Binary file not shown.
64.5 KB
Binary file not shown.

src/automizer.ts

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import path from 'path';
1717
import * as fs from 'fs';
1818
import { XmlHelper } from './helper/xml-helper';
1919
import ModifyPresentationHelper from './helper/modify-presentation-helper';
20+
import { contentTracker, ContentTracker } from './helper/content-tracker';
21+
import JSZip from 'jszip';
2022

2123
/**
2224
* Automizer
@@ -42,15 +44,15 @@ export default class Automizer implements IPresentationProps {
4244
params: AutomizerParams;
4345
status: StatusTracker;
4446

45-
modifyPresentation: ModifyXmlCallback[];
47+
content: ContentTracker;
48+
modifyPresentation: ModifyXmlCallback[] = [];
4649

4750
/**
4851
* Creates an instance of `pptx-automizer`.
4952
* @param [params]
5053
*/
5154
constructor(params: AutomizerParams) {
5255
this.templates = [];
53-
this.modifyPresentation = [];
5456
this.params = params;
5557

5658
this.templateDir = params?.templateDir ? params.templateDir + '/' : '';
@@ -62,6 +64,8 @@ export default class Automizer implements IPresentationProps {
6264
this.timer = Date.now();
6365
this.setStatusTracker(params?.statusTracker);
6466

67+
this.content = new ContentTracker();
68+
6569
if (params.rootTemplate) {
6670
const location = this.getLocation(params.rootTemplate, 'template');
6771
this.rootTemplate = Template.import(location) as RootPresTemplate;
@@ -277,7 +281,19 @@ export default class Automizer implements IPresentationProps {
277281
await this.applyModifyPresentationCallbacks();
278282

279283
const rootArchive = await this.rootTemplate.archive;
280-
const content = await rootArchive.generateAsync({ type: 'nodebuffer' });
284+
285+
const options: JSZip.JSZipGeneratorOptions<'nodebuffer'> = {
286+
type: 'nodebuffer',
287+
};
288+
289+
if (this.params.compression > 0) {
290+
options.compression = 'DEFLATE';
291+
options.compressionOptions = {
292+
level: this.params.compression,
293+
};
294+
}
295+
296+
const content = await rootArchive.generateAsync(options);
281297

282298
return FileHelper.writeOutputFile(
283299
this.getLocation(location, 'output'),
@@ -318,12 +334,19 @@ export default class Automizer implements IPresentationProps {
318334
* Apply some callbacks to restore archive/xml structure
319335
* and prevent corrupted pptx files.
320336
*
321-
* TODO: Remove unused parts (slides, related items) from archive.
322337
* TODO: Use every imported image only once
323338
* TODO: Check for lost relations
324339
*/
325-
normalizePresentation(): void {
340+
async normalizePresentation(): Promise<void> {
326341
this.modify(ModifyPresentationHelper.normalizeSlideIds);
342+
343+
if (this.params.cleanup) {
344+
if (this.params.removeExistingSlides) {
345+
this.modify(ModifyPresentationHelper.removeUnusedFiles);
346+
}
347+
this.modify(ModifyPresentationHelper.removedUnusedImages);
348+
this.modify(ModifyPresentationHelper.removeUnusedContentTypes);
349+
}
327350
}
328351

329352
/**

src/classes/slide.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { Image } from '../shapes/image';
2525
import { Chart } from '../shapes/chart';
2626
import { GenericShape } from '../shapes/generic';
2727
import { vd } from '../helper/general-helper';
28+
import { ContentTracker } from '../helper/content-tracker';
2829

2930
export class Slide implements ISlide {
3031
/**
@@ -98,6 +99,7 @@ export class Slide implements ISlide {
9899
*/
99100
targetRelsPath: string;
100101
status: StatusTracker;
102+
content: ContentTracker;
101103
/**
102104
* List of unsupported tags in slide xml
103105
* @internal
@@ -126,6 +128,7 @@ export class Slide implements ISlide {
126128
this.importElements = [];
127129

128130
this.status = params.presentation.status;
131+
this.content = params.presentation.content;
129132
}
130133

131134
/**

src/classes/template.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import { XmlTemplateHelper } from '../helper/xml-template-helper';
1111
import { SlideInfo } from '../types/xml-types';
1212
import { XmlHelper } from '../helper/xml-helper';
1313
import { vd } from '../helper/general-helper';
14+
import { ContentTracker } from '../helper/content-tracker';
15+
import CacheHelper from '../helper/cache-helper';
1416

1517
export class Template implements ITemplate {
1618
/**
@@ -52,7 +54,7 @@ export class Template implements ITemplate {
5254
creationIds: SlideInfo[];
5355
existingSlides: number;
5456

55-
constructor(location: string) {
57+
constructor(location: string, cache?: CacheHelper) {
5658
this.location = location;
5759
const file = FileHelper.readFile(location);
5860
this.archive = FileHelper.extractFileContent(file as unknown as Buffer);
@@ -61,20 +63,21 @@ export class Template implements ITemplate {
6163
static import(
6264
location: string,
6365
name?: string,
66+
cache?: CacheHelper,
6467
): PresTemplate | RootPresTemplate {
6568
let newTemplate: PresTemplate | RootPresTemplate;
66-
6769
if (name) {
68-
newTemplate = new Template(location) as PresTemplate;
70+
newTemplate = new Template(location, cache) as PresTemplate;
6971
newTemplate.name = name;
7072
} else {
71-
newTemplate = new Template(location) as RootPresTemplate;
73+
newTemplate = new Template(location, cache) as RootPresTemplate;
7274
newTemplate.slides = [];
7375
newTemplate.counter = [
7476
new CountHelper('slides', newTemplate),
7577
new CountHelper('charts', newTemplate),
7678
new CountHelper('images', newTemplate),
7779
];
80+
newTemplate.content = new ContentTracker();
7881
}
7982

8083
return newTemplate;

src/constants/constants.ts

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { TargetByRelIdMapParam } from '../types/types';
1+
import {
2+
TargetByRelIdMapParam,
3+
TrackedRelation,
4+
TrackedRelationTag,
5+
} from '../types/types';
26

37
export const TargetByRelIdMap = {
48
chart: {
@@ -22,3 +26,93 @@ export const TargetByRelIdMap = {
2226
prefix: '../media/image',
2327
} as TargetByRelIdMapParam,
2428
};
29+
30+
export const imagesTrack: () => TrackedRelation[] = () => [
31+
{
32+
type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',
33+
tag: 'a:blip',
34+
role: 'image',
35+
attribute: 'r:embed',
36+
},
37+
{
38+
type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',
39+
tag: 'asvg:svgBlip',
40+
role: 'image',
41+
attribute: 'r:embed',
42+
},
43+
];
44+
45+
export const contentTrack: TrackedRelationTag[] = [
46+
{
47+
source: 'ppt/presentation.xml',
48+
relationsKey: 'ppt/_rels/presentation.xml.rels',
49+
tags: [
50+
{
51+
type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster',
52+
tag: 'p:sldMasterId',
53+
role: 'slideMaster',
54+
},
55+
{
56+
type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide',
57+
tag: 'p:sldId',
58+
role: 'slide',
59+
},
60+
],
61+
},
62+
{
63+
source: 'ppt/slides',
64+
relationsKey: 'ppt/slides/_rels',
65+
isDir: true,
66+
tags: [
67+
{
68+
type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart',
69+
tag: 'c:chart',
70+
role: 'chart',
71+
},
72+
{
73+
type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout',
74+
role: 'slideLayout',
75+
tag: null,
76+
},
77+
...imagesTrack(),
78+
],
79+
},
80+
{
81+
source: 'ppt/charts',
82+
relationsKey: 'ppt/charts/_rels',
83+
isDir: true,
84+
tags: [
85+
{
86+
type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/package',
87+
tag: 'c:externalData',
88+
role: 'externalData',
89+
},
90+
],
91+
},
92+
{
93+
source: 'ppt/slideMasters',
94+
relationsKey: 'ppt/slideMasters/_rels',
95+
isDir: true,
96+
tags: [
97+
{
98+
type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout',
99+
tag: 'p:sldLayoutId',
100+
role: 'slideLayout',
101+
},
102+
...imagesTrack(),
103+
],
104+
},
105+
{
106+
source: 'ppt/slideLayouts',
107+
relationsKey: 'ppt/slideLayouts/_rels',
108+
isDir: true,
109+
tags: [
110+
{
111+
type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster',
112+
role: 'slideMaster',
113+
tag: null,
114+
},
115+
...imagesTrack(),
116+
],
117+
},
118+
];

0 commit comments

Comments
 (0)