How to Assemble Multiple PowerPoint Decks
I just want to let you guys know we are working on some server issues here, which is why some of the previous posts are not showing images or links to source code. This issue will hopefully be resolved soon.
In my previous post, I showed you the easy way to assemble multiple Word documents. Today, I am going to show you how to assemble multiple PowerPoint decks together.
Solution
To merge two decks, a source deck and a destination deck, together we need to take the following actions:
- Open up the destination deck via the Open XML SDK and access its main presentation part
- Open up the source deck and access its main presentation part
- Add every slide in the source deck, in order, to the destination deck
- For every slide added to the destination deck, make sure that there is a relationship between the main destination presentation part and the copied master slide layout part
- For every added master slide layout part make sure there is a reference to that part within the main destination part
- For every added slide part make sure there is a reference to that part within the main destination part
- Perform cleanup work to ensure that all references to slide layout parts have unique ids
- Save changes made to the destination deck
My post will talk about using version 2 of the SDK.
For the sake of this post, let's say I am starting with the following two decks, each with three slides:
Destination Deck |
Source Deck |
If you just want to jump straight into the code, feel free to download this solution here.
The Code
As described in the solution section above, the first three steps require us to open both the destination and source decks in order to add every slide in the source deck to the destination deck. Below is the code snippet necessary to accomplish those tasks:
static void MergeDecks(string sourceDeck, string destDeck) { int id = 1; //Open up the destination deck using (PresentationDocument myDestDeck = PresentationDocument.Open(destDeck, true)) { PresentationPart destPresPart = myDestDeck.PresentationPart; //Open up the source deck using (PresentationDocument mySourceDeck = PresentationDocument.Open(sourceDeck, true)) { PresentationPart sourcePresPart = mySourceDeck.PresentationPart; //Need to get a unique ids for slide master and slide lists //(will use these later) uniqueId = GetMaxIdFromChild(destPresPart.Presentation.SlideMasterIdList); uint maxSlideId = GetMaxIdFromChild(destPresPart.Presentation.SlideIdList); //Copy each slide in my source deck in order to my destination deck foreach (SlideId slideId in sourcePresPart.Presentation.SlideIdList) { SlidePart sp; SlidePart destSp; SlideMasterPart destMasterPart; string relId; SlideMasterId newSlideMasterId; SlideId newSlideId; //come up with a unique relationship id id++; sp = (SlidePart)sourcePresPart .GetPartById(slideId.RelationshipId); relId = sourceDeck.Remove(sourceDeck.IndexOf('.')) + id; destSp = destPresPart.AddPart<SlidePart>(sp, relId); //Add the slide part to the destination deck SlidePart destSp = destPresPart.AddPart<SlidePart>(sp, relId); ... } ... } ... } } |
We needed to go through the slide id list in order to ensure that we added all the slides from the source deck to the destination deck in order. In addition, note that we also taking advantage of AddPart, which allows us to not only add a slide part, but all parts referenced by that slide part.
At this point we have just imported the slide part to the destination deck. Our next task is to fix up the relationship between the main destination presentation part to the added slide master part. Again, we are going to take advantage of AddPart to add this relationship with the following code (AddPart will fix up relationships if the part already exists in the package):
//Master part was added, but now we need to make sure the relationship is in place destMasterPart = destSp.SlideLayoutPart.SlideMasterPart; destPresPart.AddPart(destMasterPart); |
The next steps are to add the list of slide master part ids and slide ids to the appropriate lists in the main destination presentation part. The ids referenced in each of these lists need to be unique. An additional constraint is that the id values across the slide master id and the slide layout id lists need to be unique and are required to have id values that are greater than or equal to 2147483684. To help deal with the uniqueness factor we need a method that is able to return the current max id value in the list. So anytime we add a new item to the list we simply use the max id value and add 1. Below is a generic method that is able to retrieve the max id value in a set of children elements:
static uint GetMaxIdFromChild(OpenXmlElement el) { uint max = 1; //Get max id value from set of children foreach (OpenXmlElement child in el.ChildElements) { OpenXmlAttribute attribute = child.GetAttribute("id", ""); uint id = uint.Parse(attribute.Value); if (id > max) max = id; } return max; } |
This method is called before the initial for loop in order to get the appropriate unique ids for the slide master part id and slide id lists. Once we have the unique ids we can add the slide master part id and slide id to the appropriate lists in the main destination presentation part. Below is the necessary code needed to add to these two lists:
//Add slide master to slide master list uniqueId++; newSlideMasterId = new SlideMasterId(); newSlideMasterId.RelationshipId = destPresPart.GetIdOfPart(destMasterPart); newSlideMasterId.Id = uniqueId; //Add slide to slide list maxSlideId++; newSlideId = new SlideId(); newSlideId.RelationshipId = relId; newSlideId.Id = maxSlideId; destPresPart.Presentation.SlideMasterIdList.Append(newSlideMasterId); destPresPart.Presentation.SlideIdList.Append(newSlideId); |
Almost done! The last bit of work we need to do is ensure that all slide layout ids are unique. As mentioned above these id values cannot conflict with the id values of the slide master part id list. In addition we need to make sure that these ids are greater than 2147483684. Below is a method that will go through all slide master parts and will fix up all referenced slide layout ids by simply incrementing id values based on the maximum seen uint value seen thus far:
static void FixSlideLayoutIds(PresentationPart presPart) { //Need to make sure all slide layouts have unique ids foreach (SlideMasterPart slideMasterPart in presPart.SlideMasterParts) { foreach (SlideLayoutId slideLayoutId in slideMasterPart.SlideMaster.SlideLayoutIdList) { uniqueId++; slideLayoutId.Id = (uint)uniqueId; } slideMasterPart.SlideMaster.Save(); } } |
End Result
Putting everything together and running this code, we end up with a presentation that has six slides, all in the proper order.
Here is a screenshot of the final presentation:
Zeyad Rajabi
Comments
Anonymous
March 06, 2009
Ces dernières semaines furent assez complètes et complexes, et le temps m’a manqué pour partager avecAnonymous
March 11, 2009
Great article Zeyad. Although it's a bit too difficult for me I a going to try it.Anonymous
March 18, 2009
It’s been a couple of weeks since I posted, and I’ve come across several interesting blog posts and articles