Introduction
This
article will show you how to programmatically merge PowerPoint 2010
presentations into one and save the merged presentation in PDF format using
Open XML, Open XML SDK 2, Microsoft Office Interop, Microsoft Office Object
Library and Visual Studio 2013.
Overview
We
have three PowerPoint 2010 presentations:
- SamplePresentation1.pptx
- SamplePresentation2.pptx
- SamplePresentation3.pptx
We’re going to merge
them in to one presentation using a template TemplatePresentation.pptx. Let’s
first manually merge these presentations using Microsoft PowerPoint. Following images
show the input presentations and the merged presentation generated after
manually copying the slides:
TemplatePresentation.pptx
SamplePresentation1.pptx
SamplePresentation2.pptx
SamplePresentation3.pptx
Manually Merged Presentation
Merge Presentations using Open XML SDK
Below topics list the steps to merge presentations in a Windows Forms Application.
Create a Windows Forms Application Project
- Open
Visual Studio 2013. Click on File->New->Project
Select
Visual C#-> Windows and select
the Windows Forms Application
template
- Set
the name of the presentation to PresentationMerger
and select a desired location for the solution
Design the Windows Form
- Drag six buttons on
the form as seen in the image below
Drag 2 openFileDialog
and 2 saveFileDialog on the form
Set the properties of the controls:
openFileDialog1:
Name: openFileDialogSelectPresentations
MultiSelect: true
Filter: PowerPoint Presentation|*.pptx
openFileDialog2:
Name: openFileDialogSelectTemplate
Filter: PowerPoint Presentation|*.pptx
saveFileDialog1:
Name: saveFileDialogMerge
saveFileDialog2:
Name: saveFileDialogSavePdf
button1:
Name: btnSelectTemplate
Text: Select Template to Use
button2:
Name: btnSelectPresentations
Text: Select Presentations to Merge
button3:
Name: btnMergedPresentation
Text: Merged Presentation
button4:
Name: btnConvertedPdf
Text: Converted PDF
button5:
Name: btnMerge
Text: Merge
button6:
Name: btnSaveAsPdf
Text: Save as PDF
Create Events for the Controls
Double click btnSelectPresentations and enter the code below
private void btnSelectPresentations_Click(object sender, EventArgs e)
{
openFileDialogSelectPresentations.ShowDialog();
}
Double click btnSelectTemplate and enter the code below
private void btnSelectTemplate_Click(object sender, EventArgs e)
{
openFileDialogSelectTemplate.ShowDialog();
}
Double click buttonMergedOutput and enter the code below
private void btnMergedPresentation_Click(object sender, EventArgs e)
{
saveFileDialogMerge.ShowDialog();
}
Double click btnConvertedPdf and enter the code below
private void btnConvertedPdf_Click(object sender, EventArgs e)
{
saveFileDialogSavePdf.ShowDialog();
}
Add Reference to Open XML Assemblies
Right click References
in PresentationMerger project and
select Add Reference
Select DocumentFormat.OpenXml,
WindowsBase, Microsoft.Office.Interop.PowerPoint and Microsoft Office 14.0 Object Library and then click OK
Add the Code for Open XML
Add the additional using statements to reference Open XML related namespaces.
using System.IO;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Presentation;
using Microsoft.Office.Interop.PowerPoint;
Create a variable to hold the unique ID for both the slide master and the slide layout. Name the variable as uniqueId.
public partial class Form1 : Form
{
static uint uniqueId;
Create the GetMaxSlideMasterId() method. This method will return the Slide Master ID. Note that Slide Master IDs must be greater than or equal to 2147483648 because IDs greater than 2147483648 represent Slide Master IDs. This method loops through the elements of the Slide Master ID List and checks whether the child ID is greater than the defined max ID or not.
private uint GetMaxSlideMasterId(SlideMasterIdList slideMasterIdList)
{
uint max = 2147483648;
if (slideMasterIdList != null)
foreach (SlideMasterId child in
slideMasterIdList.Elements<slidemasterid>())
{
uint id = child.Id;
if (id > max)
max = id;
}
return max;
}
</slidemasterid>
Create the GetMaxSlideId() method. This method will return the Slide ID. Note that Slide IDs must have a minimum value of greater than or equal to 256 and a maximum value of less than 2147483648. IDs greater than 2147483648 represent Slide Master IDs. This method loops through the elements of the Slide ID List and checks whether the child ID is greater than the defined max ID.
private uint GetMaxSlideId(SlideIdList slideIdList)
{
uint max = 256;
if (slideIdList != null)
foreach (SlideId child in slideIdList.Elements<slideid>())
{
uint id = child.Id;
if (id > max)
max = id;
}
return max;
}
</slideid>
Create the FixSlideLayoutIds() method. This method will ensure that each slide layout inside the Slide Master Part in each Presentation Part will have a unique ID. These IDs must be unique to distinguish each layout from one another.
private void FixSlideLayoutIds(PresentationPart presPart)
{
foreach (SlideMasterPart slideMasterPart in presPart.SlideMasterParts)
{
foreach (SlideLayoutId slideLayoutId in slideMasterPart.SlideMaster.SlideLayoutIdList)
{
uniqueId++;
slideLayoutId.Id = (uint)uniqueId;
}
slideMasterPart.SlideMaster.Save();
}
Create the MergePresentation() method. This method merges one PowerPoint presentation to another. It does so by opening the destination PowerPoint as editable and checks if it has an existing Slide ID List. If it doesn’t, the code will create one.
private void MergePresentation(string sourcePresentation, string mergedPresentation)
{
int id = 0;
using (PresentationDocument myDestDeck = PresentationDocument.Open(mergedPresentation, true))
{
PresentationPart destPresPart = myDestDeck.PresentationPart;
if (destPresPart.Presentation.SlideIdList == null)
destPresPart.Presentation.SlideIdList = new SlideIdList();
While the destination PowerPoint presentation is open, we open the source PowerPoint presentation.
using (PresentationDocument mySourceDeck = PresentationDocument.Open(sourcePresentation, false))
{
PresentationPart sourcePresPart =
mySourceDeck.PresentationPart;
With both PowerPoint presentations open, we loop through each through each Slide ID list of the presentation in the Presentation part of the Source PowerPoint presentation. We then copy these parts and create new relations in to the destination PowerPoint presentation. Note that we have to get the Max Slide Master ID and the Max Slide ID before the foreach statement as this will be used to generate the incremental IDs for the new slide master ID and slide ID.
The GetMaxSlideMasterId() and GetMaxSlideId() methods are called to ensure that we always get the last ID and incrementing it by one to ensure that all the IDs for the Master Slide ID and Slide ID are unique.
uniqueId = GetMaxSlideMasterId(destPresPart.Presentation.SlideMasterIdList);
uint maxSlideId = GetMaxSlideId(destPresPart.Presentation.SlideIdList);
foreach (SlideId slideId in
sourcePresPart.Presentation.SlideIdList)
{
SlidePart sp;
SlidePart destSp;
SlideMasterPart destMasterPart;
string relId;
SlideMasterId newSlideMasterId;
SlideId newSlideId;
id++;
sp =
(SlidePart)sourcePresPart.GetPartById(
slideId.RelationshipId);
relId = Path.GetFileNameWithoutExtension(sourcePresentation).Replace(" ", "_") + id;
destSp = destPresPart.AddPart<slidepart>(sp, relId);
destMasterPart = destSp.SlideLayoutPart.SlideMasterPart;
destPresPart.AddPart(destMasterPart);
uniqueId++;
newSlideMasterId = new SlideMasterId();
newSlideMasterId.RelationshipId =
destPresPart.GetIdOfPart(destMasterPart);
newSlideMasterId.Id = uniqueId;
destPresPart.Presentation.SlideMasterIdList.Append(
newSlideMasterId);
maxSlideId++;
newSlideId = new SlideId();
newSlideId.RelationshipId = relId;
newSlideId.Id = maxSlideId;
destPresPart.Presentation.SlideIdList.Append(newSlideId);
}
</slidepart>
Next, we make sure that all Slide IDs are unique in the destination PowerPoint presentation by calling the FixSlideLayoutIds() method. Once everything is hunky-dory, we can then save the destination PowerPoint Presentation.
FixSlideLayoutIds(destPresPart);
}
destPresPart.Presentation.Save();
Create click event for buttonMerge. Double click buttonMerge. Add the variables below to store selected template, selected source presentations and target merged PowerPoint presentation.
private void btnMerge_Click(object sender, EventArgs e)
{
string presentationTemplate = openFileDialogSelectTemplate.FileName;
string[] presentationsToBeMerged = openFileDialogSelectPresentations.FileNames;
string mergedPresentation = saveFileDialogMerge.FileName;
Create a copy of the template presentation to generate the new presentation.
File.Copy(presentationTemplate, mergedPresentation, true);
Loop through each selected source PowerPoint presentations and call the MergePresentationSlides() method
foreach (string sourcePresentation in presentationsToBeMerged)
MergePresentation(sourcePresentation, mergedPresentation);
Convert the Merged Presentation to PDF
OpenXML SDK can’t convert any Office document to another format, it can
only be used to edit and manipulate the document and it does an excellent job
with it as it is based on SDK and is suitable for server side operations.
To
convert an Office document to PDF, I used Microsoft
Office Interop. Double click btnSaveAsPdf
button and use the following code to call ConvertToPdf() method and
convert the merged presentation to PDF.
private void btnSaveAsPdf_Click(object sender, EventArgs e)
{
String pptxFile = saveFileDialogMerge.FileName;
String pdfFile = saveFileDialogSavePdf.FileName;
ConvertToPdf(pptxFile, pdfFile);
}
Below is the ConverToPdf() method which creates an instance of PowerPoint application, loads the merged PPTX and
converts it to PDF:
public void ConvertToPdf(String pptxFile, String pdfFile)
{
try
{
Microsoft.Office.Interop.PowerPoint.Application ppApp = new
Microsoft.Office.Interop.PowerPoint.Application();
Microsoft.Office.Interop.PowerPoint.Presentation presentation = ppApp.Presentations.Open
(pptxFile, Microsoft.Office.Core.MsoTriState.msoTrue,
Microsoft.Office.Core.MsoTriState.msoFalse,
Microsoft.Office.Core.MsoTriState.msoFalse);
presentation.ExportAsFixedFormat(pdfFile,
PpFixedFormatType.ppFixedFormatTypePDF,
PpFixedFormatIntent.ppFixedFormatIntentPrint,
Microsoft.Office.Core.MsoTriState.msoFalse,
PpPrintHandoutOrder.ppPrintHandoutHorizontalFirst,
PpPrintOutputType.ppPrintOutputSlides,
Microsoft.Office.Core.MsoTriState.msoFalse,
null,
PpPrintRangeType.ppPrintAll,
"",
false,
false,
false,
true,
true,
System.Reflection.Missing.Value);
presentation.Close();
presentation = null;
ppApp = null;
GC.Collect();
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
throw ex;
}
}
Running the Code
Hit F5
Click on Select Template to Merge button
Inside the Presentations folder which is found in the solution directory, select TemplatePresentation.pptx and click Open
Click on Select Presentations to Merge button
Inside the Presentations folder which is found in the solution directory, select "SamplePresentation1.pptx" "SamplePresentation2.pptx" "SamplePresentation3.pptx" and click Open
Click on the Merged Presentation button
Select a location for the merged presentation and give it a name that ends with .pptx and click on the Save button
Click on the Merge button. Now the presentations are merged
Click on the Converted PDF button
Select a location for the converted PDF and give it a name that ends with .pdf and click on the Save button
Click on the Save as PDF button. Now the merged presentation has been converted to PDF. Following images show the merged presentation and converted PDF
Merged Presentation
Merged PPDF
Alternate Approach
The
above solution does the job using open source and free components but this
solution is for PPTX only. What if you want to do the same with PPT and other
presentation formats? Also, you can see from the source code that the number of
lines required just doing the merging of presentations is more than expected.
In real world, developers also have to handle the business requirements and integrate other
complex logic like editing, replacing some words, copy different amount of
slides etc. The complexity increases with the increasing number of operations.
Below
is the code using a commercial .net powerpoint component for your review that does
the same job:
private void btnMerge_Click(object sender, EventArgs e)
{
string[] presentationsToBeMerged = openFileDialogSelectPresentations.FileNames;
string mergedPresentation = saveFileDialogMerge.FileName;
Aspose.Slides.Presentation destinationPresentation = new Aspose.Slides.Presentation();
foreach (string presentation in presentationsToBeMerged)
{
Aspose.Slides.Presentation sourcePresentation = new Aspose.Slides.Presentation(presentation);
foreach (Aspose.Slides.Slide slide in sourcePresentation.Slides)
{
destinationPresentation.Slides.AddClone(slide);
}
}
destinationPresentation.Save(mergedPresentation, Aspose.Slides.Export.SaveFormat.Pptx);
}
private void btnSaveAsPdf_Click(object sender, EventArgs e)
{
String pptxFile = saveFileDialogMerge.FileName;
String pdfFile = saveFileDialogSavePdf.FileName;
Aspose.Slides.Presentation mergedPresentation = new Aspose.Slides.Presentation(pptxFile);
mergedPresentation.Save(pdfFile, Aspose.Slides.Export.SaveFormat.Pdf);
}
You
can notice that you just need to call Presentation.Slides.AddClone()
method to merge presentations and Save()
method to save the presentation as PPTX, PPT, PDF and many other formats. So
just one line of code merges the presentations and a single line of code is
enough to convert.
The code is very clean, but
there is a price factor associated with third party libraries. You have to
evaluate for yourself whether a third party library is suitable for your requirements or free/open source components are enough.