I have code that copies the content of one PowerPoint slide into another. Below is an example of how images are processed.
foreach (OpenXmlElement element in sourceSlide.CommonSlideData.ShapeTree.ChildElements.ToList())
{
string elementType = element.GetType().ToString();
if (elementType.EndsWith(".Picture"))
{
// Deep clone the element.
elementClone = element.CloneNode(true);
var picture = (Picture)elementClone;
// Get the picture's original rId
var blip = picture.BlipFill.Blip;
string rId = blip.Embed.Value;
// Retrieve the ImagePart from the original slide by rId
ImagePart sourceImagePart = (ImagePart)sourceSlide.SlidePart.GetPartById(rId);
// Add the image part to the new slide, letting OpenXml generate the new rId
ImagePart targetImagePart = targetSlidePart.AddImagePart(sourceImagePart.ContentType);
// And copy the image data.
targetImagePart.FeedData(sourceImagePart.GetStream());
// Retrieve the new ID from the target image part,
string id = targetSlidePart.GetIdOfPart(targetImagePart);
// and assign it to the picture.
blip.Embed.Value = id;
// Get the shape tree that we're adding the clone to and append to it.
ShapeTree shapeTree = targetSlide.CommonSlideData.ShapeTree;
shapeTree.Append(elementClone);
}
This code works fine. For other scenarios like Graphic Frames, it looks a bit different, because each graphic frame can contain multiple picture objects.
// Go thru all the Picture objects in this GraphicFrame.
foreach (var sourcePicture in element.Descendants<Picture>())
{
string rId = sourcePicture.BlipFill.Blip.Embed.Value;
ImagePart sourceImagePart = (ImagePart)sourceSlide.SlidePart.GetPartById(rId);
var contentType = sourceImagePart.ContentType;
var targetPicture = elementClone.Descendants<Picture>().First(x => x.BlipFill.Blip.Embed.Value == rId);
var targetBlip = targetPicture.BlipFill.Blip;
ImagePart targetImagePart = targetSlidePart.AddImagePart(contentType);
targetImagePart.FeedData(sourceImagePart.GetStream());
string id = targetSlidePart.GetIdOfPart(targetImagePart);
targetBlip.Embed.Value = id;
}
Now I need to do the same thing with OLE objects.
// Go thru all the embedded objects in this GraphicFrame.
foreach (var oleObject in element.Descendants<OleObject>())
{
// Get the rId of the embedded OLE object.
string rId = oleObject.Id;
// Get the EmbeddedPart from the source slide.
var embeddedOleObj = sourceSlide.SlidePart.GetPartById(rId);
// Get the content type.
var contentType = embeddedOleObj.ContentType;
// Create the Target Part. Let OpenXML assign an rId.
var targetObjectPart = targetSlide.SlidePart.AddNewPart<EmbeddedObjectPart>(contentType, null);
// Get the embedded OLE object data from the original object.
var objectStream = embeddedOleObj.GetStream();
// And give it to the ObjectPart.
targetObjectPart.FeedData(objectStream);
// Get the new rId and assign it to the OLE Object.
string id = targetSlidePart.GetIdOfPart(targetObjectPart);
oleObject.Id = id;
}
But it didn't work. The resulting PowerPoint is corrupted.
What am I doing wrong?
NOTE: All of the code works except for the rId
handling in the OLE Object. I know it works because if I simply pass the original rId
from the source object to the target Object Part, like this:
var targetObjectPart = targetSlide.SlidePart
.AddNewPart<EmbeddedObjectPart>(contentType, rId);
it will function properly, so long as that rId
doesn't already exist in the target slide, which will obviously not work every time like I need it to.
The source slide and target slide are coming from different PPTX files. We're using OpenXML, not Office Interop.
Right-click the selected slide(s), and then click Copy. Right-click the thumbnail you want your copied slides to follow in the second presentation, and under Paste Options, do one of the following: To take the theme of the presentation you're pasting into, click Use Destination Theme.
What happens when copying slides. When we copy slides onto a new PowerPoint presentation deck the slides will default to the new theme style. This means that backgrounds, fonts and colours on your slides can change to the new theme style.
Copy and Paste Method in PowerPoint A quick way to use slides from one presentation in another presentation is to copy the slides that you want to use and paste those slides into the new presentation. Open both presentations to show them at the same time on the screen.
For us to isolate this issue, we suggest that you create a new PowerPoint file and copy the slides that you have in presentation A. Once done, check if you will still experience the same issue. In addition, you can check the articles below on how to copy your slides. Please post back with the result for further assistance. Was this reply helpful?
If you have a PowerPoint presentation already made and this is what you want to manipulate, then open the presentation. If you don’t have a ready presentation, just open a new presentation and insert any object. In our example, we will create a rectangular shape and position it anywhere in the slide.
I found that slides with review comments are the culprit. If a slide contains a review comment, it allows you to copy it, and paste options are available, but nothing happens. If only one slide in a group of 10 slides I want to copy has a comment, the entire copy job fails.
Since you did not provide the full code, it is difficult to tell what's wrong.
My guess would be that you are not modifying the correct object.
In your code example for Pictures, you are creating and modifying elementClone
.
In your code example for ole objects, you are working with and modifying oleObject
(which is a descendant of element
) and it is not exacly clear from the context, whether it is a part of the source document or of the target document.
You can try this minimal example:
c:\testdata\input.pptx
c:\testdata\output.pptx
After running the code, I was able to open the embedded ole object in the output document.
using DocumentFormat.OpenXml.Presentation;
using DocumentFormat.OpenXml.Packaging;
using System.Linq;
namespace ooxml
{
class Program
{
static void Main(string[] args)
{
CopyOle("c:\\testdata\\input.pptx", "c:\\testdata\\output.pptx");
}
private static void CopyOle(string inputFile, string outputFile)
{
using (PresentationDocument sourceDocument = PresentationDocument.Open(inputFile, true))
{
using (PresentationDocument targetDocument = PresentationDocument.Open(outputFile, true))
{
var sourceSlidePart = sourceDocument.PresentationPart.SlideParts.First();
var targetSlidePart = targetDocument.PresentationPart.SlideParts.First();
foreach (var element in sourceSlidePart.Slide.CommonSlideData.ShapeTree.ChildElements)
{
//clones an element, does not copy the actual relationship target (e.g. ppt\embeddings\oleObject1.bin)
var elementClone = element.CloneNode(true);
//for each cloned OleObject, fix its relationship
foreach(var clonedOleObject in elementClone.Descendants<OleObject>())
{
//find the original EmbeddedObjectPart in the source document
//(we can use the id from the clonedOleObject to do that, since it contained the same id
// as the source ole object)
var sourceObjectPart = sourceSlidePart.GetPartById(clonedOleObject.Id);
//create a new EmbeddedObjectPart in the target document and copy the data from the original EmbeddedObjectPart
var targetObjectPart = targetSlidePart.AddEmbeddedObjectPart(sourceObjectPart.ContentType);
targetObjectPart.FeedData(sourceObjectPart.GetStream());
//update the relationship target on the clonedOleObject to point to the newly created EmbeddedObjectPath
clonedOleObject.Id = targetSlidePart.GetIdOfPart(targetObjectPart);
}
//add cloned element to the document
targetSlidePart.Slide.CommonSlideData.ShapeTree.Append(elementClone);
}
targetDocument.PresentationPart.Presentation.Save();
}
}
}
}
}
As for troubleshooting, the OOXML Tools chrome extension was helpful.
It allows to compare the structure of two documents, so it is way easier to analyze what went wrong.
Examples:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With