Replacing Image in one Shape can modify other Shapes

Hello,

We are seeing an issue where an image that is replaced one Shape can affect other Shapes in the Presentation. This happens when the same image is used by multiple Shapes. When we replace the image in one Shape, it affects used of all other Shapes.

We understand that even in Microsoft PowerPoint, when you use the same image in two or more Shapes, the same image file is recycled within the PPTX. So when “Shape 1” and “Shape 2” have the same image, the PPTX will be saved with only 1 file in the “media” folder. However, when the image is replaced in one of the Shapes (which uses the “shared image”), all other Shapes that share the image are also updated with the new image. Our expectation is that when the image is replaced in one Shape, it applies to that Shape only and does not affect any other Shape.

This behavior can be seen in the latest Aspose Slides for Java version 20.9, the attached PNG files and the following Java code:

final String pptFile  = [PATH] + "ReplaceImageIssue.pptx";
final String imgFile1 = [PATH] + "chart1.png";
final String imgFile2 = [PATH] + "chart2.png";

IPresentation ppt = new Presentation();

// load the image bytes
final byte[] imgBytes = Files.readAllBytes(Paths.get(imgFile1));

// Add 4 new empty Slides
ILayoutSlide layout = ppt.getMasters().get_Item(0).getLayoutSlides()
                         .getByType(SlideLayoutType.Blank);
for (int i = 0; i < 4; i++) {
    ISlide slide = ppt.getSlides().insertEmptySlide(0, layout);

    // add the image to the new slide
    IPPImage img = ppt.getImages().addImage(imgBytes);
    slide.getShapes().addPictureFrame(ShapeType.Rectangle, 10, 20, 
                                      img.getWidth(), img.getHeight(), img);
}

// Save the new PPTX
Files.deleteIfExists(Paths.get(pptFile));
ppt.save(pptFile, SaveFormat.Pptx);
System.out.println("Saved ORIGINAL PPTX file: " + pptFile);

// Now reload the PPTX and replace the image in Slide #3 (only)
ppt = new Presentation(pptFile);

// Get the only Shape that exists in Slide #3
IShape shape = ppt.getSlides().get_Item(2).getShapes().get_Item(0);
IPPImage existingImg = ((IPictureFrame)shape).getPictureFormat().getPicture().getImage();

// Replace the existing image with Chart2
final byte[] newImgBytes = Files.readAllBytes(Paths.get(imgFile2));
existingImg.replaceImage(newImgBytes);

// Save the modified PPTX
final String newPptx = pptFile.replace(".pptx", "_mod.pptx");
Files.deleteIfExists(Paths.get(newPptx));
ppt.save(newPptx, SaveFormat.Pptx);
System.out.println("Saved MODIFIED PPTX file: " + newPptx);

Running the above code should produce:

  1. A new PPTX file similar to the attached ReplaceImageIssue.pptx file. If you open the PPTX in PowerPoint, you should see there are 4 Slides and each has one Shape displaying the same image - derived from the “Chart1.png” file.
  2. A new PPTX file similar to the attached ReplaceImageIssue_mod.pptx file. If you open the PPTX in PowerPoint, you should see all 4 Shapes are displaying the same new image - derived from the “Chart2.png” file. However, the code replaced the Shape from Slide #3 only. The other Shapes should not have been modified.

Environment Details:

  • Aspose Slides for Java 20.9
  • Java version 1.8.0_211
  • Windows 10 OS (but also reproducible under Linux).

File description in the ReplaceImage.zip (70.9 KB) attachment contains:

  • chart1.png: PNG file that is initially used as the “shared image” on the 4 Shapes added by the code above.
  • chart2.png: PNG file that is to replace the image for the Shape in Slide #3 per the code above.
  • ReplaceImageIssue.pptx: PPTX file produced from the code above on our environment and shows “chart1.png” as the shared image.
  • ReplaceImageIssue_mod.pptx: PPTX file produced from the code above on our environment after the image from Slide #3 is replaced.

Thank you!

@oraspose

Actually, this is not an issue but implementation inside API that needs to be understood. When you add images to presentation images collection, you then only refer the image added inside the images collection for shapes where that image is to be used. So, when you will change the source image, its reference in all shapes will get changed. This implementation is useful in many aspects. Say, you have watermark or a logo shape on all slides which share same image. When you change one image in collection, it will be reflected on all other shapes inside slides.

In your case, what I would suggest you is to add a new image in collection and then update the desired shape reference image with that one.

Thanks for your response Mudassir. What you’ve described makes sense, but can you please clarify the following items:

  1. You say that we should “add a new image in collection”. Isn’t that what is being done in the example code per:
    IPPImage img = ppt.getImages().addImage(imgBytes);
    In other words, are we not creating a new IPPImage when we invoke the addImage API? While we understand that the image has the same bytes that are assigned to other Shapes, doesn’t the invocation to addImage add a new image to the collection?

  2. Can you clarify what you mean by “… and then update the desired shape reference image”? Other than replaceImage, what API can we use to change the image associated with a Shape? It would be great if you could provide sample code.

Thanks again.

@oraspose

You need to use the setImage() - method, instead of replaceImage() for your scenario if you are requiring to change the image that is referred in many shapes but you ought to change that in one shape only. Please use the following code snippet:

final String pptFile  = path + "ReplaceImageIssue.pptx";
final String imgFile1 = path + "chart1.png";
final String imgFile2 = path + "chart2.png";

IPresentation ppt = new Presentation();

// load the image bytes
final byte[] imgBytes = Files.readAllBytes(Paths.get(imgFile1));

// Add 4 new empty Slides
ILayoutSlide layout = ppt.getMasters().get_Item(0).getLayoutSlides()
		.getByType(SlideLayoutType.Blank);
for (int i = 0; i < 4; i++) {
	ISlide slide = ppt.getSlides().insertEmptySlide(0, layout);

	// add the image to the new slide
	IPPImage img = ppt.getImages().addImage(imgBytes);
	slide.getShapes().addPictureFrame(ShapeType.Rectangle, 10, 20,
			img.getWidth(), img.getHeight(), img);
}

// Save the new PPTX
Files.deleteIfExists(Paths.get(pptFile));
ppt.save(pptFile, SaveFormat.Pptx);
System.out.println("Saved ORIGINAL PPTX file: " + pptFile);

// Now reload the PPTX and replace the image in Slide #3 (only)
ppt = new Presentation(pptFile);

// Get the only Shape that exists in Slide #3
IShape shape = ppt.getSlides().get_Item(2).getShapes().get_Item(0);
IPPImage existingImg = ((IPictureFrame)shape).getPictureFormat().getPicture().getImage();

// Replace the existing image with Chart2
final byte[] newImgBytes = Files.readAllBytes(Paths.get(imgFile2));
IPPImage replaceImage = ppt.getImages().addImage(newImgBytes);
((IPictureFrame)shape).getPictureFormat().getPicture().setImage(replaceImage);

// Save the modified PPTX
final String newPptx = pptFile.replace(".pptx", "_mod.pptx");
Files.deleteIfExists(Paths.get(newPptx));
ppt.save(newPptx, SaveFormat.Pptx);
System.out.println("Saved MODIFIED PPTX file: " + newPptx);

Thanks again Mudassir, that is very helpful.

@oraspose

You are always welcome.