Archive for the ‘VSTO’ Category

Copying A Slide From One Presentation To Another

March 20, 2010

There are many ways to generate a PowerPoint presentation using Open XML.  The first way is to build it by hand strictly using the SDK.  Alternately you can modify a copy of a base presentation in place.  The third approach to generate a presentation is to build a new presentation from the parts of an existing presentation by copying slides as needed.  This post will focus on the third option.

In order to make this solution a little more elegant I am going to create a VSTO add-in as I did in my previous post.  This one is going to insert Tags to identify slides instead of NonVisualDrawingProperties which I used to identify charts, tables and images.  The code itself is fairly short.

SlideNameForm dialog = new SlideNameForm();

Selection selection = Globals.ThisAddIn.Application.ActiveWindow.Selection;

 

if(dialog.ShowDialog() == DialogResult.OK)

{

    selection.SlideRange.Tags.Add(dialog.slideName,dialog.slideName);

}

Zeyad Rajabi has a good post here on combining slides from two presentations.  The example he gives is great if you are doing a straight merge.  But what if you want to use your source file as almost a supermarket where you pick and chose slides and may even insert them repeatedly?  The following code uses the tags we created in the previous step to pick a particular slide an copy it to a destination file.

using (PresentationDocument newDocument = PresentationDocument.Open(OutputFileText.Text,true))

{

    PresentationDocument templateDocument = PresentationDocument.Open(FileNameText.Text, false);

 

    uniqueId = GetMaxIdFromChild(newDocument.PresentationPart.Presentation.SlideMasterIdList);

    uint maxId = GetMaxIdFromChild(newDocument.PresentationPart.Presentation.SlideIdList);

 

    SlidePart oldPart = GetSlidePartByTagName(templateDocument, SlideToCopyText.Text);

 

    SlidePart newPart = newDocument.PresentationPart.AddPart<SlidePart>(oldPart, "sourceId1");

 

    SlideMasterPart newMasterPart = newDocument.PresentationPart.AddPart(newPart.SlideLayoutPart.SlideMasterPart);

 

    SlideIdList idList = newDocument.PresentationPart.Presentation.SlideIdList;

 

    // create new slide ID

    maxId++;

    SlideId newId = new SlideId();

    newId.Id = maxId;

    newId.RelationshipId = "sourceId1";

    idList.Append(newId);

 

    // Create new master slide ID

    uniqueId++;

    SlideMasterId newMasterId = new SlideMasterId();

    newMasterId.Id = uniqueId;

    newMasterId.RelationshipId = newDocument.PresentationPart.GetIdOfPart(newMasterPart);

    newDocument.PresentationPart.Presentation.SlideMasterIdList.Append(newMasterId);

 

    // change slide layout ID

    FixSlideLayoutIds(newDocument.PresentationPart);

 

 

    //newPart.Slide.Save();

    newDocument.PresentationPart.Presentation.Save();

}

The GetMaxIDFromChild and FixSlideLayoutID methods are barrowed from Zeyad’s article.  The GetSlidePartByTagName method is listed below.  It is really one LINQ query that finds SlideParts with child Tags that have the requested Name.

private SlidePart GetSlidePartByTagName(PresentationDocument templateDocument, string tagName)

{

    return (from p in templateDocument.PresentationPart.SlideParts

            where

                p.UserDefinedTagsParts.First().TagList.Descendants

                    <DocumentFormat.OpenXml.Presentation.Tag>().First().Name ==

                tagName.ToUpper()

            select p).First();

}

This is what really makes the difference from what Zeyad posted.  The most powerful thing you can have when generating documents from templates is a consistent way of naming items to be manipulated.  I will be show more approaches like this in upcoming posts.

Advertisements