Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge Feature content tracker #42

Merged
merged 11 commits into from
Jan 13, 2023
Merged
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ const automizer = new Automizer({
// truncate root presentation and start with zero slides
removeExistingSlides: true,

// activate `cleanup` to eventually remove unused files:
cleanup: false,

// Set a value from 0-9 to specify the zip-compression level.
// The lower the number, the faster your output file will be ready.
// Higher compression levels produce smaller files.
compression: 0,

// use a callback function to track pptx generation process.
// statusTracker: myStatusTracker,
})
Expand Down
33 changes: 33 additions & 0 deletions __tests__/modify-chart-scatter-images.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Automizer, { modify } from '../src/index';
import { ChartData } from '../dist';

test('create presentation, add and modify a scatter chart with embedded point images.', async () => {
const automizer = new Automizer({
templateDir: `${__dirname}/pptx-templates`,
outputDir: `${__dirname}/pptx-output`,
});

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

const dataScatter = <ChartData>(<unknown>{
series: [{ label: 'series s1' }],
categories: [
{ label: 'r1', values: [{ x: 10, y: 20 }] },
{ label: 'r2', values: [{ x: 21, y: 11 }] },
{ label: 'r3', values: [{ x: 22, y: 28 }] },
{ label: 'r4', values: [{ x: 13, y: 13 }] },
],
});

const result = await pres
.addSlide('charts', 3, (slide) => {
slide.modifyElement('ScatterPointImages', [
modify.setChartScatter(dataScatter),
]);
})
.write(`modify-chart-scatter-images.test.pptx`);

// expect(result.charts).toBe(2);
});
Binary file modified __tests__/pptx-templates/ChartScatter.pptx
Binary file not shown.
Binary file not shown.
33 changes: 28 additions & 5 deletions src/automizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import path from 'path';
import * as fs from 'fs';
import { XmlHelper } from './helper/xml-helper';
import ModifyPresentationHelper from './helper/modify-presentation-helper';
import { contentTracker, ContentTracker } from './helper/content-tracker';
import JSZip from 'jszip';

/**
* Automizer
Expand All @@ -42,15 +44,15 @@ export default class Automizer implements IPresentationProps {
params: AutomizerParams;
status: StatusTracker;

modifyPresentation: ModifyXmlCallback[];
content: ContentTracker;
modifyPresentation: ModifyXmlCallback[] = [];

/**
* Creates an instance of `pptx-automizer`.
* @param [params]
*/
constructor(params: AutomizerParams) {
this.templates = [];
this.modifyPresentation = [];
this.params = params;

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

this.content = new ContentTracker();

if (params.rootTemplate) {
const location = this.getLocation(params.rootTemplate, 'template');
this.rootTemplate = Template.import(location) as RootPresTemplate;
Expand Down Expand Up @@ -277,7 +281,19 @@ export default class Automizer implements IPresentationProps {
await this.applyModifyPresentationCallbacks();

const rootArchive = await this.rootTemplate.archive;
const content = await rootArchive.generateAsync({ type: 'nodebuffer' });

const options: JSZip.JSZipGeneratorOptions<'nodebuffer'> = {
type: 'nodebuffer',
};

if (this.params.compression > 0) {
options.compression = 'DEFLATE';
options.compressionOptions = {
level: this.params.compression,
};
}

const content = await rootArchive.generateAsync(options);

return FileHelper.writeOutputFile(
this.getLocation(location, 'output'),
Expand Down Expand Up @@ -318,12 +334,19 @@ export default class Automizer implements IPresentationProps {
* Apply some callbacks to restore archive/xml structure
* and prevent corrupted pptx files.
*
* TODO: Remove unused parts (slides, related items) from archive.
* TODO: Use every imported image only once
* TODO: Check for lost relations
*/
normalizePresentation(): void {
async normalizePresentation(): Promise<void> {
this.modify(ModifyPresentationHelper.normalizeSlideIds);

if (this.params.cleanup) {
if (this.params.removeExistingSlides) {
this.modify(ModifyPresentationHelper.removeUnusedFiles);
}
this.modify(ModifyPresentationHelper.removedUnusedImages);
this.modify(ModifyPresentationHelper.removeUnusedContentTypes);
}
}

/**
Expand Down
3 changes: 3 additions & 0 deletions src/classes/slide.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { Image } from '../shapes/image';
import { Chart } from '../shapes/chart';
import { GenericShape } from '../shapes/generic';
import { vd } from '../helper/general-helper';
import { ContentTracker } from '../helper/content-tracker';

export class Slide implements ISlide {
/**
Expand Down Expand Up @@ -98,6 +99,7 @@ export class Slide implements ISlide {
*/
targetRelsPath: string;
status: StatusTracker;
content: ContentTracker;
/**
* List of unsupported tags in slide xml
* @internal
Expand Down Expand Up @@ -126,6 +128,7 @@ export class Slide implements ISlide {
this.importElements = [];

this.status = params.presentation.status;
this.content = params.presentation.content;
}

/**
Expand Down
11 changes: 7 additions & 4 deletions src/classes/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { XmlTemplateHelper } from '../helper/xml-template-helper';
import { SlideInfo } from '../types/xml-types';
import { XmlHelper } from '../helper/xml-helper';
import { vd } from '../helper/general-helper';
import { ContentTracker } from '../helper/content-tracker';
import CacheHelper from '../helper/cache-helper';

export class Template implements ITemplate {
/**
Expand Down Expand Up @@ -52,7 +54,7 @@ export class Template implements ITemplate {
creationIds: SlideInfo[];
existingSlides: number;

constructor(location: string) {
constructor(location: string, cache?: CacheHelper) {
this.location = location;
const file = FileHelper.readFile(location);
this.archive = FileHelper.extractFileContent(file as unknown as Buffer);
Expand All @@ -61,20 +63,21 @@ export class Template implements ITemplate {
static import(
location: string,
name?: string,
cache?: CacheHelper,
): PresTemplate | RootPresTemplate {
let newTemplate: PresTemplate | RootPresTemplate;

if (name) {
newTemplate = new Template(location) as PresTemplate;
newTemplate = new Template(location, cache) as PresTemplate;
newTemplate.name = name;
} else {
newTemplate = new Template(location) as RootPresTemplate;
newTemplate = new Template(location, cache) as RootPresTemplate;
newTemplate.slides = [];
newTemplate.counter = [
new CountHelper('slides', newTemplate),
new CountHelper('charts', newTemplate),
new CountHelper('images', newTemplate),
];
newTemplate.content = new ContentTracker();
}

return newTemplate;
Expand Down
96 changes: 95 additions & 1 deletion src/constants/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { TargetByRelIdMapParam } from '../types/types';
import {
TargetByRelIdMapParam,
TrackedRelation,
TrackedRelationTag,
} from '../types/types';

export const TargetByRelIdMap = {
chart: {
Expand All @@ -22,3 +26,93 @@ export const TargetByRelIdMap = {
prefix: '../media/image',
} as TargetByRelIdMapParam,
};

export const imagesTrack: () => TrackedRelation[] = () => [
{
type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',
tag: 'a:blip',
role: 'image',
attribute: 'r:embed',
},
{
type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',
tag: 'asvg:svgBlip',
role: 'image',
attribute: 'r:embed',
},
];

export const contentTrack: TrackedRelationTag[] = [
{
source: 'ppt/presentation.xml',
relationsKey: 'ppt/_rels/presentation.xml.rels',
tags: [
{
type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster',
tag: 'p:sldMasterId',
role: 'slideMaster',
},
{
type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide',
tag: 'p:sldId',
role: 'slide',
},
],
},
{
source: 'ppt/slides',
relationsKey: 'ppt/slides/_rels',
isDir: true,
tags: [
{
type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart',
tag: 'c:chart',
role: 'chart',
},
{
type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout',
role: 'slideLayout',
tag: null,
},
...imagesTrack(),
],
},
{
source: 'ppt/charts',
relationsKey: 'ppt/charts/_rels',
isDir: true,
tags: [
{
type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/package',
tag: 'c:externalData',
role: 'externalData',
},
],
},
{
source: 'ppt/slideMasters',
relationsKey: 'ppt/slideMasters/_rels',
isDir: true,
tags: [
{
type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout',
tag: 'p:sldLayoutId',
role: 'slideLayout',
},
...imagesTrack(),
],
},
{
source: 'ppt/slideLayouts',
relationsKey: 'ppt/slideLayouts/_rels',
isDir: true,
tags: [
{
type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster',
role: 'slideMaster',
tag: null,
},
...imagesTrack(),
],
},
];
Loading