Skip to content

Commit c84fecf

Browse files
authored
Merge pull request #94 from singerla/feature-modify-slide-backgrounds
Feature modify slide backgrounds
2 parents 473bd0e + 8bd3dbe commit c84fecf

File tree

11 files changed

+200
-19
lines changed

11 files changed

+200
-19
lines changed

__tests__/EXAMPLE-slide-bg.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// import * as ModifyBackground from '../src/modify/modify-background'; // Adjust
2+
// import Automizer from '../src/automizer';
3+
//
4+
// describe('ModifyMasterBackground', () => {
5+
// beforeEach(() => {
6+
// // Initialize the automizer instance
7+
// const automizer = new Automizer({
8+
// templateDir: `${__dirname}/pptx-templates`,
9+
// outputDir: `${__dirname}/pptx-output`,
10+
// });
11+
//
12+
// const pres = await automizer
13+
// .loadRoot(`EmptyTemplate.pptx`)
14+
// });
15+
//
16+
// test('should allow setting a background color on a master slide', async () => {
17+
// const masterId = 1; // Example master slide ID
18+
// const newColor = 'FF5733'; // Example new color
19+
//
20+
// // Modify the master slide by setting a new background color
21+
// await pres.addMaster(masterId, (master) => {
22+
// master.modifyBackground.setBackgroundColor(master, newColor);
23+
// });
24+
//
25+
// // Later on we could add a way to getMasterInfo or something that could either be the raw xml, or more helpfully a {background:{backgroundImage:"",backgroundColor:""}}
26+
// // Retrieve the modified master slide and verify the background color
27+
// const modifiedMaster = await pres.getMasterInfo(masterId);
28+
// expect(modifiedMaster.backgroundColor).toBe(newColor); // Assuming the master slide object has a backgroundColor property
29+
// });
30+
//
31+
// test('should allow setting a background image on a master slide', async () => {
32+
// const masterId = 1; // Example master slide ID
33+
// const newImage = 'path/to/new/image.jpg'; // Example new image path
34+
//
35+
// // Modify the master slide by setting a new background image
36+
// await automizer.addMaster(masterId, (master) => {
37+
// master.modifyBackground.setBackgroundImage(master, newImage);
38+
// });
39+
// // Later on we could add a way to getMasterInfo or something that could either be the raw xml, or more helpfully a {background:{backgroundImage:"",backgroundColor:""}}
40+
// // Retrieve the modified master slide and verify the background image
41+
// const modifiedMaster = await pres.getMasterInfo(masterId);
42+
// expect(modifiedMaster.backgroundImage).toBe(newImage); // Assuming the master slide object has a backgroundImage property
43+
// });
44+
//
45+
// // Additional tests for edge cases, error handling, etc.
46+
// });
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import Automizer from '../src/automizer';
2+
import { ModifyTextHelper } from '../src';
3+
import ModifyBackgroundHelper from '../src/helper/modify-background-helper';
4+
5+
test('Auto-import source slideLayout and -master and modify background color', async () => {
6+
const automizer = new Automizer({
7+
templateDir: `${__dirname}/pptx-templates`,
8+
outputDir: `${__dirname}/pptx-output`,
9+
autoImportSlideMasters: true,
10+
});
11+
12+
const pres = await automizer
13+
.loadRoot(`EmptyTemplate.pptx`)
14+
.load('SlideMasterBackgrounds.pptx')
15+
.addMaster(`SlideMasterBackgrounds.pptx`, 1, async (master) => {
16+
master.modify(
17+
ModifyBackgroundHelper.setSolidFill({
18+
type: 'srgbClr',
19+
value: 'aaccbb',
20+
}),
21+
);
22+
})
23+
.addSlide(`SlideMasterBackgrounds.pptx`, 3)
24+
.write(`modify-master-background-color.test.pptx`);
25+
26+
expect(pres.masters).toBe(2);
27+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import Automizer from '../src/automizer';
2+
import { ModifyImageHelper, ModifyTextHelper, XmlHelper } from '../src';
3+
import ModifyBackgroundHelper from '../src/helper/modify-background-helper';
4+
5+
test('Auto-import source slideLayout and -master and modify background image', async () => {
6+
const automizer = new Automizer({
7+
templateDir: `${__dirname}/pptx-templates`,
8+
outputDir: `${__dirname}/pptx-output`,
9+
autoImportSlideMasters: true,
10+
});
11+
12+
const pres = await automizer
13+
.loadRoot(`EmptyTemplate.pptx`)
14+
.load('SlideMasterBackgrounds.pptx')
15+
.loadMedia(`test.png`, `${__dirname}/../__tests__/media`, 'pre_')
16+
.addMaster(`SlideMasterBackgrounds.pptx`, 2, async (master) => {
17+
ModifyBackgroundHelper.setRelationTarget(master, 'pre_test.png');
18+
})
19+
.addSlide(`SlideMasterBackgrounds.pptx`, 1)
20+
.addSlide(`SlideMasterBackgrounds.pptx`, 1)
21+
.write(`modify-master-background-image.test.pptx`);
22+
23+
expect(pres.masters).toBe(2);
24+
});

src/classes/has-shapes.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,15 @@ export default class HasShapes {
205205
this.modifications.push(callback);
206206
}
207207

208+
/**
209+
* Push relations modifications list
210+
* @internal
211+
* @param callback
212+
*/
213+
modifyRelations(callback: SlideModificationCallback): void {
214+
this.relModifications.push(callback);
215+
}
216+
208217
/**
209218
* Select and modify a single element on an added slide.
210219
* @param {string} selector - Element's name on the slide.
@@ -839,7 +848,7 @@ export default class HasShapes {
839848
async applyRelModifications(): Promise<void> {
840849
await XmlHelper.modifyXmlInArchive(
841850
this.targetArchive,
842-
`ppt/slides/_rels/slide${this.targetNumber}.xml.rels`,
851+
`ppt/${this.targetType}s/_rels/${this.targetType}${this.targetNumber}.xml.rels`,
843852
this.relModifications,
844853
);
845854
}

src/classes/master.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { FileHelper } from '../helper/file-helper';
22
import { XmlHelper } from '../helper/xml-helper';
3-
import { ShapeTargetType, SourceIdentifier, Target } from '../types/types';
3+
import {
4+
ShapeTargetType,
5+
SlideModificationCallback,
6+
SourceIdentifier,
7+
Target,
8+
} from '../types/types';
49
import { IPresentationProps } from '../interfaces/ipresentation-props';
510
import { PresTemplate } from '../interfaces/pres-template';
611
import { RootPresTemplate } from '../interfaces/root-pres-template';
@@ -68,6 +73,7 @@ export class Master extends HasShapes implements IMaster {
6873
}
6974

7075
await this.applyModifications();
76+
await this.applyRelModifications();
7177

7278
const info = this.targetTemplate.automizer.params.showIntegrityInfo;
7379
const assert = this.targetTemplate.automizer.params.showIntegrityInfo;

src/classes/slide.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import { FileHelper } from '../helper/file-helper';
2-
import { XmlHelper } from '../helper/xml-helper';
3-
import { ShapeTargetType, SourceIdentifier } from '../types/types';
2+
import {
3+
ShapeTargetType,
4+
SlideModificationCallback,
5+
SourceIdentifier,
6+
} from '../types/types';
47
import { ISlide } from '../interfaces/islide';
58
import { IPresentationProps } from '../interfaces/ipresentation-props';
69
import { PresTemplate } from '../interfaces/pres-template';
710
import { RootPresTemplate } from '../interfaces/root-pres-template';
8-
import { last, vd } from '../helper/general-helper';
11+
import { last } from '../helper/general-helper';
912
import { XmlRelationshipHelper } from '../helper/xml-relationship-helper';
1013
import { IMaster } from '../interfaces/imaster';
1114
import HasShapes from './has-shapes';
@@ -82,7 +85,7 @@ export class Slide extends HasShapes implements ISlide {
8285
* @param targetLayoutId
8386
*/
8487
useSlideLayout(layoutId?: number | string): this {
85-
this.relModifications.push(async (slideRelXml) => {
88+
this.modifyRelations(async (slideRelXml) => {
8689
let targetLayoutId;
8790

8891
if (typeof layoutId === 'string') {
@@ -105,7 +108,6 @@ export class Slide extends HasShapes implements ISlide {
105108
slideLayouts[0].updateTargetIndex(targetLayoutId as number);
106109
}
107110
});
108-
109111
return this;
110112
}
111113

src/dev.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import Automizer from './index';
1+
import Automizer, { XmlHelper } from './index';
2+
import ModifyBackgroundHelper from './helper/modify-background-helper';
23
import { vd } from './helper/general-helper';
4+
import ModifyImageHelper from './helper/modify-image-helper';
35

46
const run = async () => {
57
const automizer = new Automizer({
@@ -10,15 +12,18 @@ const run = async () => {
1012

1113
let pres = automizer
1214
.loadRoot(`RootTemplate.pptx`)
13-
.load(`SlideMasterBackgrounds.pptx`);
15+
.load(`SlideMasterBackgrounds.pptx`)
1416

15-
pres.addSlide(`SlideMasterBackgrounds.pptx`, 2, async (slide) => {
16-
console.log('test');
17-
});
18-
19-
pres.write(`SlideMasterBackgroundsOutput.pptx`).then((summary) => {
20-
console.log(summary);
21-
});
17+
.load('SlideMasterBackgrounds.pptx')
18+
.loadMedia(`test.png`, `${__dirname}/../__tests__/media`, 'pre_')
19+
.addMaster(`SlideMasterBackgrounds.pptx`, 2, async (master) => {
20+
ModifyBackgroundHelper.setRelationTarget(master, 'pre_test.png');
21+
})
22+
.addSlide(`SlideMasterBackgrounds.pptx`, 1)
23+
.write(`SlideMasterBackgroundsOutput.pptx`)
24+
.then((summary) => {
25+
console.log(summary);
26+
});
2227
};
2328

2429
run().catch((error) => {
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { XmlDocument } from '../types/xml-types';
2+
import ModifyColorHelper from './modify-color-helper';
3+
import { Color } from '../types/modify-types';
4+
import { XmlHelper } from './xml-helper';
5+
import { vd } from './general-helper';
6+
import { IMaster, ModifyImageHelper } from '../index';
7+
8+
export default class ModifyBackgroundHelper {
9+
/**
10+
* Set solid fill of master background
11+
*/
12+
static setSolidFill =
13+
(color: Color) =>
14+
(slideMasterXml: XmlDocument): void => {
15+
const bgPr = slideMasterXml.getElementsByTagName('p:bgPr')?.item(0);
16+
if (bgPr) {
17+
ModifyColorHelper.solidFill(color)(bgPr);
18+
} else {
19+
throw 'No background properties for slideMaster';
20+
}
21+
};
22+
23+
/**
24+
* Modify a slideMaster background image's relation target
25+
* @param master
26+
* @param imageName
27+
*/
28+
static setRelationTarget = (master: IMaster, imageName: string) => {
29+
let targetRelation = '';
30+
master.modify((masterXml) => {
31+
targetRelation =
32+
ModifyBackgroundHelper.getBackgroundProperties(masterXml);
33+
});
34+
master.modifyRelations((relXml) => {
35+
const relations = XmlHelper.findByAttributeValue(
36+
relXml.getElementsByTagName('Relationship'),
37+
'Id',
38+
targetRelation,
39+
);
40+
if (relations[0]) {
41+
ModifyImageHelper.setRelationTarget(imageName)(undefined, relations[0]);
42+
}
43+
});
44+
};
45+
46+
/**
47+
* Extract background properties from slideMaster xml
48+
*/
49+
static getBackgroundProperties = (slideMasterXml: XmlDocument): string => {
50+
const bgPr = slideMasterXml.getElementsByTagName('p:bgPr')?.item(0);
51+
if (bgPr) {
52+
const blip = bgPr
53+
.getElementsByTagName('a:blip')
54+
?.item(0)
55+
.getAttribute('r:embed');
56+
return blip;
57+
} else {
58+
throw 'No background properties for slideMaster';
59+
}
60+
};
61+
}

src/helper/modify-shape-helper.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { ReplaceText, ReplaceTextOptions } from '../types/modify-types';
22
import { ShapeCoordinates } from '../types/shape-types';
3-
import { GeneralHelper, vd } from './general-helper';
3+
import { GeneralHelper } from './general-helper';
44
import TextReplaceHelper from './text-replace-helper';
55
import ModifyTextHelper from './modify-text-helper';
6-
import { HelperElement, XmlDocument, XmlElement } from '../types/xml-types';
7-
import { XmlHelper } from './xml-helper';
6+
import { XmlElement } from '../types/xml-types';
87

98
const map = {
109
x: { tag: 'a:off', attribute: 'x' },

src/interfaces/imaster.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export interface IMaster {
1212
sourceNumber: number;
1313
key: string;
1414
modify(callback: SlideModificationCallback): void;
15+
modifyRelations(callback: SlideModificationCallback): void;
1516
append(targetTemplate: RootPresTemplate): Promise<void>;
1617
addElement(
1718
presName: string,

0 commit comments

Comments
 (0)