Skip to content

Commit d645480

Browse files
committed
chore(doc): add new functions to readme
1 parent b45082e commit d645480

File tree

3 files changed

+90
-31
lines changed

3 files changed

+90
-31
lines changed

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,22 @@ pres.addSlide('RootTemplate.pptx', 1, (slide) => {
268268
/* ... */
269269
]);
270270
});
271+
272+
// In case you have two or more shapes on your template slide with the same name,
273+
// you can address the target by nameIdx:
274+
pres.addSlide('SlideWithGraph.pptx', 1, (slide) => {
275+
// Starting from 0, this will find the second shape named 'ColumnChart'
276+
// on the slide.
277+
slide.modifyElement(
278+
{
279+
name: 'ColumnChart',
280+
nameIdx: 1,
281+
},
282+
[
283+
/* ... */
284+
],
285+
);
286+
});
271287
```
272288

273289
> You can display and manage shape names directly in PowerPoint by opening the "Selection"-pane for your current slide. Hit `ALT+F10` and PowerPoint will give you a (nested) list including all (grouped) shapes. You can edit a shape name by double-click or by hitting `F2` after selecting a shape from the list. [See MS-docs for more info.](https://support.microsoft.com/en-us/office/use-the-selection-pane-to-manage-objects-in-documents-a6b2fd3e-d769-46c1-9b9c-b94e04a72550)
@@ -544,6 +560,19 @@ pres.addSlide('images', 2, (slide) => {
544560
});
545561
```
546562

563+
This will also auto-crop the image to the new width and height,
564+
based on the container aspect ratio, derived from the original image
565+
and using the new image width and height based on the files loaded into
566+
the presentation media folder using .loadMedia():
567+
568+
```ts
569+
pres.addSlide('images', 2, (slide) => {
570+
slide.modifyElement('Image Placeholder', [
571+
ModifyImageHelper.setRelationTargetCover('feather.png', pres),
572+
]);
573+
});
574+
```
575+
547576
Find more examples on image manipulation:
548577

549578
- [Add external image](https://github.com/singerla/pptx-automizer/blob/main/__tests__/add-external-image.test.ts)

src/helper/compute-src-rect.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ type SrcRect = { l?: number; t?: number; r?: number; b?: number }; // thousandth
22
const K = 100000;
33

44
// Convert the source rectangle to fractions of the image width and height for easier calculation
5-
function toFractions(src: SrcRect): { l: number; t: number; r: number; b: number } {
5+
function toFractions(src: SrcRect): {
6+
l: number;
7+
t: number;
8+
r: number;
9+
b: number;
10+
} {
611
return {
712
l: (src.l ?? 0) / K,
813
t: (src.t ?? 0) / K,
@@ -15,7 +20,7 @@ function toFractions(src: SrcRect): { l: number; t: number; r: number; b: number
1520
export function inferContainerAr(
1621
oldImageWidth: number,
1722
oldImageHeight: number,
18-
currentSrcRect: SrcRect // read from the slide; missing attrs mean 0
23+
currentSrcRect: SrcRect, // read from the slide; missing attrs mean 0
1924
): number {
2025
const { l, t, r, b } = toFractions(currentSrcRect);
2126
const wf = 1 - (l + r);
@@ -28,10 +33,15 @@ export function inferContainerAr(
2833
export function computeSrcRectForNewImage(
2934
containerAr: number,
3035
newImageWidth: number,
31-
newImageHeight: number
36+
newImageHeight: number,
3237
): SrcRect {
3338
const newAr = newImageWidth / newImageHeight;
34-
if (!isFinite(containerAr) || !isFinite(newAr) || containerAr <= 0 || newAr <= 0) {
39+
if (
40+
!isFinite(containerAr) ||
41+
!isFinite(newAr) ||
42+
containerAr <= 0 ||
43+
newAr <= 0
44+
) {
3545
return { l: 0, t: 0, r: 0, b: 0 };
3646
}
3747

@@ -51,4 +61,4 @@ export function computeSrcRectForNewImage(
5161
// equal AR -> no crop needed
5262
return { l: 0, t: 0, r: 0, b: 0 };
5363
}
54-
}
64+
}

src/helper/modify-image-helper.ts

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { XmlElement } from '../types/xml-types';
22
import { ImageStyle } from '../types/modify-types';
33
import slugify from 'slugify';
4-
import {imageSize } from 'image-size';
4+
import { imageSize } from 'image-size';
55
import fs from 'fs';
66
import { IPresentationProps } from '../interfaces/ipresentation-props';
7-
import { computeSrcRectForNewImage, inferContainerAr } from './compute-src-rect';
7+
import {
8+
computeSrcRectForNewImage,
9+
inferContainerAr,
10+
} from './compute-src-rect';
811

912
export default class ModifyImageHelper {
1013
/**
@@ -27,29 +30,29 @@ export default class ModifyImageHelper {
2730
* the presentation media folder using .loadMedia()
2831
* @param filename name of target image in root template media folder.
2932
* @param pres presentation properties (to access the root template archive)
30-
* @param newImageWidth width of the new image
31-
* @param newImageHeight height of the new image
3233
*/
3334
static setRelationTargetCover = (
3435
filename: string,
3536
pres: IPresentationProps,
3637
) => {
3738
return async (element: XmlElement, arg1: XmlElement): Promise<void> => {
38-
3939
const newTarget = '../media/' + slugify(filename);
4040
const originalTarget = arg1.getAttribute('Target');
4141
const originalTargetPath = originalTarget.replace('../', 'ppt/');
4242
const originalImageDimensions = { width: 100, height: 100 };
4343
const newImageDimensions = { width: 100, height: 100 };
4444

45-
4645
// Get the new image dimensions, using the rootTemplate mediafiles array,
4746
// since we have it loaded into the presentation media folder using .loadMedia()
4847
// If we don't find the media file, we warn, but continue
4948
try {
50-
const mediaFile = pres.rootTemplate.mediaFiles.find(file => file.file === filename);
51-
if(!mediaFile) {
52-
throw new Error("Media file not found in template archive in path: " + filename);
49+
const mediaFile = pres.rootTemplate.mediaFiles.find(
50+
(file) => file.file === filename,
51+
);
52+
if (!mediaFile) {
53+
throw new Error(
54+
'Media file not found in template archive in path: ' + filename,
55+
);
5356
}
5457
const buffer = fs.readFileSync(mediaFile.filepath);
5558
const _dimensions = imageSize(buffer);
@@ -58,53 +61,70 @@ export default class ModifyImageHelper {
5861
} catch (error) {
5962
console.warn("Couldn't find media file in template archive in path.");
6063
}
61-
64+
6265
// Find the original image dimensions using the original target path from the original slide
6366
// using the rootTemplate archive file system and get the image dimensions.
6467
// If we don't find the original image, we warn, but continue
6568
// If we find the original image, we get the image dimensions and use this to reverse calculate
6669
// the aspect ratio and then use the new image dimensions to calculate and set the new crop on srcRect.
6770
// This results in the image being cropped in the image container to match the aspect ratio of the new image.
6871
try {
69-
if(pres.rootTemplate.archive.fileExists(originalTargetPath)) {
70-
const originalImage = await pres.rootTemplate.archive.read(originalTargetPath, "nodebuffer");
72+
if (pres.rootTemplate.archive.fileExists(originalTargetPath)) {
73+
const originalImage = await pres.rootTemplate.archive.read(
74+
originalTargetPath,
75+
'nodebuffer',
76+
);
7177
const _dimensions = imageSize(originalImage);
7278
originalImageDimensions.width = _dimensions.width;
7379
originalImageDimensions.height = _dimensions.height;
7480
} else {
75-
throw new Error("Original image not found from template archive in path: " + originalTargetPath);
81+
throw new Error(
82+
'Original image not found from template archive in path: ' +
83+
originalTargetPath,
84+
);
7685
}
77-
86+
7887
const srcRect = element.getElementsByTagName('a:srcRect')[0];
7988
const srcRectLeft = srcRect.getAttribute('l');
8089
const srcRectTop = srcRect.getAttribute('t');
8190
const srcRectRight = srcRect.getAttribute('r');
8291
const srcRectBottom = srcRect.getAttribute('b');
83-
92+
8493
const currentSrcRect = {
8594
l: srcRectLeft ? Number(srcRectLeft) : 0,
8695
t: srcRectTop ? Number(srcRectTop) : 0,
8796
r: srcRectRight ? Number(srcRectRight) : 0,
8897
b: srcRectBottom ? Number(srcRectBottom) : 0,
89-
}
90-
91-
const containerAr = inferContainerAr(originalImageDimensions.width, originalImageDimensions.height, currentSrcRect);
92-
93-
const newSrcRect = computeSrcRectForNewImage(containerAr, newImageDimensions.width, newImageDimensions.height);
94-
98+
};
99+
100+
const containerAr = inferContainerAr(
101+
originalImageDimensions.width,
102+
originalImageDimensions.height,
103+
currentSrcRect,
104+
);
105+
106+
const newSrcRect = computeSrcRectForNewImage(
107+
containerAr,
108+
newImageDimensions.width,
109+
newImageDimensions.height,
110+
);
111+
95112
srcRect.setAttribute('l', String(newSrcRect.l));
96113
srcRect.setAttribute('t', String(newSrcRect.t));
97114
srcRect.setAttribute('r', String(newSrcRect.r));
98115
srcRect.setAttribute('b', String(newSrcRect.b));
99116
} catch (error) {
100-
const errorMessage = error instanceof Error ? error.message : String(error);
101-
console.warn("Skipped setting relation target cropped due to an error: " + errorMessage);
117+
const errorMessage =
118+
error instanceof Error ? error.message : String(error);
119+
console.warn(
120+
'Skipped setting relation target cropped due to an error: ' +
121+
errorMessage,
122+
);
102123
}
103-
104-
arg1.setAttribute('Target', newTarget);
105124

125+
arg1.setAttribute('Target', newTarget);
106126
};
107-
}
127+
};
108128

109129
/*
110130
Update an existing duotone image overlay element (WIP)

0 commit comments

Comments
 (0)