From c931dcfbca2df06888c5cd1178f9571d2f6bb9cd Mon Sep 17 00:00:00 2001 From: Niranjan Uma Shankar Date: Tue, 23 Dec 2025 10:15:24 +0400 Subject: [PATCH 1/3] Add support for rounded rect cropping of images --- src/core-interfaces.ts | 8 ++++++++ src/gen-objects.ts | 1 + src/gen-xml.ts | 14 +++++++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/core-interfaces.ts b/src/core-interfaces.ts index 52cb967d9..f7bd01e3f 100644 --- a/src/core-interfaces.ts +++ b/src/core-interfaces.ts @@ -513,6 +513,14 @@ export interface ImageProps extends PositionProps, DataOrPathProps, ObjectNamePr * @default false */ rounding?: boolean + /** + * Rounded rectangle corner radius (only when rounding is true) + * - values: 0.0 to 1.0 (0.0 = no rounding, 1.0 = maximum rounding) + * @default undefined (uses default PowerPoint rounding) + * @example 0.2 // 20% corner radius + * @example 0.5 // 50% corner radius + */ + rectRadius?: number /** * Shadow Props * - MS-PPT > Format Picture > Shadow diff --git a/src/gen-objects.ts b/src/gen-objects.ts index 6543cc052..a6a3356f9 100644 --- a/src/gen-objects.ts +++ b/src/gen-objects.ts @@ -451,6 +451,7 @@ export function addImageDefinition(target: PresSlide, opt: ImageProps): void { h: intHeight || 1, altText: opt.altText || '', rounding: typeof opt.rounding === 'boolean' ? opt.rounding : false, + rectRadius: opt.rectRadius, sizing, placeholder: opt.placeholder, rotate: opt.rotate || 0, diff --git a/src/gen-xml.ts b/src/gen-xml.ts index f1e3ecd9f..3b36e619e 100644 --- a/src/gen-xml.ts +++ b/src/gen-xml.ts @@ -608,7 +608,19 @@ function slideObjectToXml (slide: PresSlide | SlideLayout): string { strSlideXml += ` ` strSlideXml += ` ` strSlideXml += ' ' - strSlideXml += ` ` + if (rounding) { + strSlideXml += ` ` + if (slideItemObj.options.rectRadius !== undefined) { + // Calculate adjustment value: rectRadius is 0.0-1.0 (ratio), convert to Office Open XML adjustment value + // Formula matches shape calculation: (rectRadius * EMU * 100000) / min(width, height) + // For images, rectRadius is treated as a ratio (0.0 = no rounding, 1.0 = max rounding) + const adjValue = Math.round((slideItemObj.options.rectRadius * EMU * 100000) / Math.min(imgWidth, imgHeight)) + strSlideXml += `` + } + strSlideXml += `` + } else { + strSlideXml += ` ` + } // EFFECTS > SHADOW: REF: @see http://officeopenxml.com/drwSp-effects.php if (slideItemObj.options.shadow && slideItemObj.options.shadow.type !== 'none') { From c8f6035e88cfa4d8f8bbda363627abb57e07a551 Mon Sep 17 00:00:00 2001 From: Niranjan Uma Shankar Date: Tue, 23 Dec 2025 10:16:06 +0400 Subject: [PATCH 2/3] Script to generate a Chronicle card in pptx --- acorn.svg | 1 + add_image_with_rounding.js | 158 +++++++++++++++++++++++++++++++++++++ atom.svg | 1 + 3 files changed, 160 insertions(+) create mode 100644 acorn.svg create mode 100644 add_image_with_rounding.js create mode 100644 atom.svg diff --git a/acorn.svg b/acorn.svg new file mode 100644 index 000000000..705ed3ada --- /dev/null +++ b/acorn.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/add_image_with_rounding.js b/add_image_with_rounding.js new file mode 100644 index 000000000..e060888e2 --- /dev/null +++ b/add_image_with_rounding.js @@ -0,0 +1,158 @@ +/* + * NAME: add_image_with_rounding.js + * DESC: Adds an image to a slide with rounding enabled + * USAGE: node add_image_with_rounding.js + */ + +import pptxgen from "pptxgenjs"; + +const pptx = new pptxgen(); + +// Create a new slide +const slide = pptx.addSlide(); + + +// Add the image with rounding enabled +slide.addImage({ + path: "https://files.chroniclehq.com/card-background-v2/thumbnail/v2-gradient-01-light.jpg", + x: 0.528, + y: 1.051, + w: 2.479, + h: 1.76, + rounding: true, + rectRadius: 0.2 // Optional: Set corner radius (0.0 = no rounding, 1.0 = maximum rounding) +}); + +slide.addImage({ + path: "https://files.chroniclehq.com/card-background-v2/thumbnail/v2-gradient-13-light.jpg", + x: 3.87, + y: 1.051, + w: 2.479, + h: 1.76, + rounding: true, + rectRadius: 0.2 // Optional: Set corner radius (0.0 = no rounding, 1.0 = maximum rounding) +}); + +slide.addImage({ + path: "atom.svg", // Replace with your SVG file path + x: 0.628, // Position after "card" text (adjust as needed) + y: 1.295, + w: 0.15, // Icon width (adjust size as needed) + h: 0.15 // Icon height (adjust size as needed) +}); + +slide.addText("Card 1", { + x: 0.528, + y: 1.555, + w: 0.751, + h: 0.208, + fontSize: 14, + color: "050505", + align: "left" +}); + +slide.addText("Lorem ipsum dolor sit amet", { + x: 0.528, + y: 1.759, + w: 2.114, + h: 0.208, + fontSize: 12, + color: "050505", + align: "left" +}); + +slide.addText("Ut enim ad minim", { + x: 0.528, + y: 2.295, + w: 1.48, + h: 0.188, + fontSize: 12, + color: "050505", + align: "left" +}); + +slide.addText("Lorem ipsum dolor sit amet, consectetur adipiscing elit.", { + x: 4.051, + y: 1.862, + w: 2.114, + h: 0.188, + fontSize: 12, + color: "050505", + align: "left" +}); + +slide.addImage({ + path: "https://files.chroniclehq.com/card-background-v2/thumbnail/v2-image-architecture-01.jpg", + x: 6.893, + y: 1.169, + w: 1.992, + h: 2.755, + sizing: { + type: "crop", + w: 1.992, + h: 2.755 + }, + rounding: true, + rectRadius: 0.2 // Optional: Set corner radius (0.0 = no rounding, 1.0 = maximum rounding) +}); + +slide.addShape(pptx.ShapeType.roundRect, { + x: 0.528, // Horizontal position in inches + y: 3.212, // Vertical position in inches + w: 2.5, // Width in inches + h: 2.0, // Height in inches + fill: { color: "000000", transparency: 92 }, // rgba(0,0,0,0.04) = black with 96% transparency + rectRadius: 0.2 // Corner radius: 0 = sharp corners, 1 = fully rounded + }); + + slide.addImage({ + path: "acorn.svg", // Replace with your SVG file path + x: 0.628, // Position after "card" text (adjust as needed) + y: 3.3, + w: 0.15, // Icon width (adjust size as needed) + h: 0.15 // Icon height (adjust size as needed) +}); + +slide.addText("Card 3", { + x: 0.528, + y: 3.669, + w: 0.751, + h: 0.208, + fontSize: 14, + color: "050505", + align: "left" +}); + +slide.addText("Lorem ipsum dolor sit amet", { + x: 0.528, + y: 3.925, + w: 2.114, + h: 0.208, + fontSize: 12, + color: "050505", + align: "left" +}); + +slide.addText("Ut enim ad minim", { + x: 0.528, + y: 4.751, + w: 1.48, + h: 0.188, + fontSize: 12, + color: "050505", + align: "left" +}); + + + + +// Save the presentation +const exportName = "Image_With_Rounding"; +pptx.writeFile({ fileName: exportName }) + .then((fileName) => { + console.log(`Presentation exported: ${fileName}`); + }) + .catch((err) => { + console.error(`ERROR: ${err}`); + }); + diff --git a/atom.svg b/atom.svg new file mode 100644 index 000000000..a73826218 --- /dev/null +++ b/atom.svg @@ -0,0 +1 @@ + \ No newline at end of file From e469bdd0f23d5de23e0f8a5ef45b75a0ba6ce1b2 Mon Sep 17 00:00:00 2001 From: Niranjan Uma Shankar Date: Tue, 23 Dec 2025 10:57:46 +0400 Subject: [PATCH 3/3] Update --- add_image_with_rounding.js | 34 +++++++++++++--------------------- src/gen-xml.ts | 3 +-- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/add_image_with_rounding.js b/add_image_with_rounding.js index e060888e2..47f692f23 100644 --- a/add_image_with_rounding.js +++ b/add_image_with_rounding.js @@ -1,18 +1,11 @@ -/* - * NAME: add_image_with_rounding.js - * DESC: Adds an image to a slide with rounding enabled - * USAGE: node add_image_with_rounding.js - */ import pptxgen from "pptxgenjs"; const pptx = new pptxgen(); -// Create a new slide const slide = pptx.addSlide(); -// Add the image with rounding enabled slide.addImage({ path: "https://files.chroniclehq.com/card-background-v2/thumbnail/v2-gradient-01-light.jpg", x: 0.528, @@ -20,7 +13,7 @@ slide.addImage({ w: 2.479, h: 1.76, rounding: true, - rectRadius: 0.2 // Optional: Set corner radius (0.0 = no rounding, 1.0 = maximum rounding) + rectRadius: 0.2 }); slide.addImage({ @@ -30,15 +23,15 @@ slide.addImage({ w: 2.479, h: 1.76, rounding: true, - rectRadius: 0.2 // Optional: Set corner radius (0.0 = no rounding, 1.0 = maximum rounding) + rectRadius: 0.2 }); slide.addImage({ - path: "atom.svg", // Replace with your SVG file path - x: 0.628, // Position after "card" text (adjust as needed) + path: "atom.svg", + x: 0.628, y: 1.295, - w: 0.15, // Icon width (adjust size as needed) - h: 0.15 // Icon height (adjust size as needed) + w: 0.15, + h: 0.15 }); slide.addText("Card 1", { @@ -93,7 +86,7 @@ slide.addImage({ h: 2.755 }, rounding: true, - rectRadius: 0.2 // Optional: Set corner radius (0.0 = no rounding, 1.0 = maximum rounding) + rectRadius: 0.2 }); slide.addShape(pptx.ShapeType.roundRect, { @@ -101,16 +94,16 @@ slide.addShape(pptx.ShapeType.roundRect, { y: 3.212, // Vertical position in inches w: 2.5, // Width in inches h: 2.0, // Height in inches - fill: { color: "000000", transparency: 92 }, // rgba(0,0,0,0.04) = black with 96% transparency - rectRadius: 0.2 // Corner radius: 0 = sharp corners, 1 = fully rounded + fill: { color: "000000", transparency: 92 }, + rectRadius: 0.2 }); slide.addImage({ - path: "acorn.svg", // Replace with your SVG file path - x: 0.628, // Position after "card" text (adjust as needed) + path: "acorn.svg", + x: 0.628, y: 3.3, - w: 0.15, // Icon width (adjust size as needed) - h: 0.15 // Icon height (adjust size as needed) + w: 0.15, + h: 0.15 }); slide.addText("Card 3", { @@ -146,7 +139,6 @@ slide.addText("Ut enim ad minim", { -// Save the presentation const exportName = "Image_With_Rounding"; pptx.writeFile({ fileName: exportName }) .then((fileName) => { diff --git a/src/gen-xml.ts b/src/gen-xml.ts index 3b36e619e..0d3307e43 100644 --- a/src/gen-xml.ts +++ b/src/gen-xml.ts @@ -612,8 +612,7 @@ function slideObjectToXml (slide: PresSlide | SlideLayout): string { strSlideXml += ` ` if (slideItemObj.options.rectRadius !== undefined) { // Calculate adjustment value: rectRadius is 0.0-1.0 (ratio), convert to Office Open XML adjustment value - // Formula matches shape calculation: (rectRadius * EMU * 100000) / min(width, height) - // For images, rectRadius is treated as a ratio (0.0 = no rounding, 1.0 = max rounding) + // (rectRadius * EMU * 100000) / min(width, height) const adjValue = Math.round((slideItemObj.options.rectRadius * EMU * 100000) / Math.min(imgWidth, imgHeight)) strSlideXml += `` }