I need to create a presentation with quite recurring contents. So first created a template presentation using Powerpoint. Then I tried to unpack and modify with XSL. I did this with word before, and was quite happy. But for PPTX you have to create two new files per slide. That ended in a mess.
That brought me to the Open Xml SDK 2.0. Still a mess, but it is a little better.
I want to clone a template slide multiple times, and then “inject” my contents into the predefined shapes. That should be easy.
But it took me a while to find out how to correctly clone a slide. So I thought I’d share that with you.
var presentationFile = "SomeFile.pptx";
using (PresentationDocument presentationDocument
= PresentationDocument.Open(presentationFile, true))
{
var presentationPart = presentationDocument.PresentationPart;
var templatePart = presentationPart.GetSlidePartsInOrder().Last();
var newSlidePart = templatePart.CloneSlide();
presentationPart.AppendSlide(newSlidePart);
}
The real code…
… is in this extensions. I created something to find slide parts in the right order, because the order of slide parts stored can be different to the order they appear in. Then I created a utility that clones a slide, and another one that appends the new slide part to the presentation part, which will finally show it.
public static class OpenXmlUtils
{
public static IEnumerable<SlidePart> GetSlidePartsInOrder(this PresentationPart presentationPart)
{
SlideIdList slideIdList = presentationPart.Presentation.SlideIdList;
return slideIdList.ChildElements
.Cast<SlideId>()
.Select(x => presentationPart.GetPartById(x.RelationshipId))
.Cast<SlidePart>();
}
public static SlidePart CloneSlide(this SlidePart templatePart)
{
// find the presentationPart: makes the API more fluent
var presentationPart = templatePart.GetParentParts()
.OfType<PresentationPart>()
.Single();
// clone slide contents
Slide currentSlide = (Slide)templatePart.Slide.CloneNode(true);
var slidePartClone = presentationPart.AddNewPart<SlidePart>();
currentSlide.Save(slidePartClone);
// copy layout part
slidePartClone.AddPart(templatePart.SlideLayoutPart);
return slidePartClone;
}
public static void AppendSlide(this PresentationPart presentationPart, SlidePart newSlidePart)
{
SlideIdList slideIdList = presentationPart.Presentation.SlideIdList;
// find the highest id
uint maxSlideId = slideIdList.ChildElements
.Cast<SlideId>()
.Max(x => x.Id.Value);
// Insert the new slide into the slide list after the previous slide.
var id = maxSlideId + 1;
SlideId newSlideId = new SlideId();
slideIdList.Append(newSlideId);
newSlideId.Id = id;
newSlideId.RelationshipId = presentationPart.GetIdOfPart(newSlidePart);
}
}
