Move a paragraph from one presentation to another
This topic shows how to use the classes in the Open XML SDK for Office to move a paragraph from one presentation to another presentation programmatically.
Getting a PresentationDocument Object
In the Open XML SDK, the PresentationDocument class represents a
presentation document package. To work with a presentation document,
first create an instance of the PresentationDocument
class, and then work with
that instance. To create the class instance from the document call the
Open method that uses a
file path, and a Boolean value as the second parameter to specify
whether a document is editable. To open a document for read/write,
specify the value true
for this parameter
as shown in the following using
statement.
In this code, the sourceFile
parameter is a string that represents the path
for the file from which you want to open the document.
With v3.0.0+ the Close() method
has been removed in favor of relying on the using statement.
This ensures that the Dispose() method is automatically called
when the closing brace is reached. The block that follows the using
statement establishes a scope for the
object that is created or named in the using
statement, in this case sourceDoc
.
Basic Presentation Document Structure
The basic document structure of a PresentationML
document consists of a number of
parts, among which is the main part that contains the presentation
definition. The following text from the ISO/IEC 29500 specification
introduces the overall form of a PresentationML
package.
The main part of a
PresentationML
package starts with a presentation root element. That element contains a presentation, which, in turn, refers to a slide list, a slide master list, a notes master list, and a handout master list. The slide list refers to all of the slides in the presentation; the slide master list refers to the entire slide masters used in the presentation; the notes master contains information about the formatting of notes pages; and the handout master describes how a handout looks.A handout is a printed set of slides that can be provided to an audience.
As well as text and graphics, each slide can contain comments and notes, can have a layout, and can be part of one or more custom presentations. A comment is an annotation intended for the person maintaining the presentation slide deck. A note is a reminder or piece of text intended for the presenter or the audience.
Other features that a
PresentationML
document can include the following: animation, audio, video, and transitions between slides.A
PresentationML
document is not stored as one large body in a single part. Instead, the elements that implement certain groupings of functionality are stored in separate parts. For example, all authors in a document are stored in one authors part while each slide has its own part.ISO/IEC 29500: 2016
The following XML code example represents a presentation that contains two slides denoted by the IDs 267 and 256.
<p:presentation xmlns:p="…" … >
<p:sldMasterIdLst>
<p:sldMasterId
xmlns:rel="https://…/relationships" rel:id="rId1"/>
</p:sldMasterIdLst>
<p:notesMasterIdLst>
<p:notesMasterId
xmlns:rel="https://…/relationships" rel:id="rId4"/>
</p:notesMasterIdLst>
<p:handoutMasterIdLst>
<p:handoutMasterId
xmlns:rel="https://…/relationships" rel:id="rId5"/>
</p:handoutMasterIdLst>
<p:sldIdLst>
<p:sldId id="267"
xmlns:rel="https://…/relationships" rel:id="rId2"/>
<p:sldId id="256"
xmlns:rel="https://…/relationships" rel:id="rId3"/>
</p:sldIdLst>
<p:sldSz cx="9144000" cy="6858000"/>
<p:notesSz cx="6858000" cy="9144000"/>
</p:presentation>
Using the Open XML SDK, you can create document structure and
content using strongly-typed classes that correspond to PresentationML
elements. You can find these classes in the
namespace. The following table lists the class names of the classes that
correspond to the sld
, sldLayout
, sldMaster
, and notesMaster
elements.
PresentationML Element | Open XML SDK Class | Description |
---|---|---|
<sld/> |
Slide | Presentation Slide. It is the root element of SlidePart. |
<sldLayout/> |
SlideLayout | Slide Layout. It is the root element of SlideLayoutPart. |
<sldMaster/> |
SlideMaster | Slide Master. It is the root element of SlideMasterPart. |
<notesMaster/> |
NotesMaster | Notes Master (or handoutMaster). It is the root element of NotesMasterPart. |
Structure of the Shape Text Body
The following text from the ISO/IEC 29500 specification introduces the structure of this element.
This element specifies the existence of text to be contained within the corresponding shape. All visible text and visible text related properties are contained within this element. There can be multiple paragraphs and within paragraphs multiple runs of text.
© ISO/IEC 29500: 2016
The following table lists the child elements of the shape text body and the description of each.
Child Element | Description |
---|---|
bodyPr | Body Properties |
lstStyle | Text List Styles |
p | Text Paragraphs |
The following XML Schema fragment defines the contents of this element:
<complexType name="CT_TextBody">
<sequence>
<element name="bodyPr" type="CT_TextBodyProperties" minOccurs="1" maxOccurs="1"/>
<element name="lstStyle" type="CT_TextListStyle" minOccurs="0" maxOccurs="1"/>
<element name="p" type="CT_TextParagraph" minOccurs="1" maxOccurs="unbounded"/>
</sequence>
</complexType>
How the Sample Code Works
The code in this topic consists of two methods, MoveParagraphToPresentation
and GetFirstSlide
. The first method takes two string
parameters: one that represents the source file, which contains the
paragraph to move, and one that represents the target file, to which the
paragraph is moved. The method opens both presentation files and then
calls the GetFirstSlide
method to get the
first slide in each file. It then gets the first TextBody
shape in each slide and the first
paragraph in the source shape. It performs a deep clone
of the source paragraph,
copying not only the source Paragraph
object itself, but also everything
contained in that object, including its text. It then inserts the cloned
paragraph in the target file and removes the source paragraph from the
source file, replacing it with a placeholder paragraph. Finally, it
saves the modified slides in both presentations.
// Moves a paragraph range in a TextBody shape in the source document
// to another TextBody shape in the target document.
static void MoveParagraphToPresentation(string sourceFile, string targetFile)
{
// Open the source file as read/write.
using (PresentationDocument sourceDoc = PresentationDocument.Open(sourceFile, true))
// </Snippet1
// Open the target file as read/write.
using (PresentationDocument targetDoc = PresentationDocument.Open(targetFile, true))
{
// Get the first slide in the source presentation.
SlidePart slide1 = GetFirstSlide(sourceDoc);
// Get the first TextBody shape in it.
TextBody textBody1 = slide1.Slide.Descendants<TextBody>().First();
// Get the first paragraph in the TextBody shape.
// Note: "Drawing" is the alias of namespace DocumentFormat.OpenXml.Drawing
Drawing.Paragraph p1 = textBody1.Elements<Drawing.Paragraph>().First();
// Get the first slide in the target presentation.
SlidePart slide2 = GetFirstSlide(targetDoc);
// Get the first TextBody shape in it.
TextBody textBody2 = slide2.Slide.Descendants<TextBody>().First();
// Clone the source paragraph and insert the cloned. paragraph into the target TextBody shape.
// Passing "true" creates a deep clone, which creates a copy of the
// Paragraph object and everything directly or indirectly referenced by that object.
textBody2.Append(p1.CloneNode(true));
// Remove the source paragraph from the source file.
textBody1.RemoveChild(p1);
// Replace the removed paragraph with a placeholder.
textBody1.AppendChild(new Drawing.Paragraph());
}
}
The GetFirstSlide
method takes the PresentationDocument
object passed in, gets its
presentation part, and then gets the ID of the first slide in its slide
list. It then gets the relationship ID of the slide, gets the slide part
from the relationship ID, and returns the slide part to the calling
method.
// Get the slide part of the first slide in the presentation document.
static SlidePart GetFirstSlide(PresentationDocument presentationDocument)
{
// Get relationship ID of the first slide
PresentationPart part = presentationDocument.PresentationPart ?? presentationDocument.AddPresentationPart();
SlideIdList slideIdList = part.Presentation.SlideIdList ?? part.Presentation.AppendChild(new SlideIdList());
SlideId slideId = part.Presentation.SlideIdList?.GetFirstChild<SlideId>() ?? slideIdList.AppendChild<SlideId>(new SlideId());
string? relId = slideId.RelationshipId;
if (relId is null)
{
throw new ArgumentNullException(nameof(relId));
}
// Get the slide part by the relationship ID.
SlidePart slidePart = (SlidePart)part.GetPartById(relId);
return slidePart;
}
Sample Code
The following is the complete sample code in both C# and Visual Basic.
// Moves a paragraph range in a TextBody shape in the source document
// to another TextBody shape in the target document.
static void MoveParagraphToPresentation(string sourceFile, string targetFile)
{
// Open the source file as read/write.
using (PresentationDocument sourceDoc = PresentationDocument.Open(sourceFile, true))
// </Snippet1
// Open the target file as read/write.
using (PresentationDocument targetDoc = PresentationDocument.Open(targetFile, true))
{
// Get the first slide in the source presentation.
SlidePart slide1 = GetFirstSlide(sourceDoc);
// Get the first TextBody shape in it.
TextBody textBody1 = slide1.Slide.Descendants<TextBody>().First();
// Get the first paragraph in the TextBody shape.
// Note: "Drawing" is the alias of namespace DocumentFormat.OpenXml.Drawing
Drawing.Paragraph p1 = textBody1.Elements<Drawing.Paragraph>().First();
// Get the first slide in the target presentation.
SlidePart slide2 = GetFirstSlide(targetDoc);
// Get the first TextBody shape in it.
TextBody textBody2 = slide2.Slide.Descendants<TextBody>().First();
// Clone the source paragraph and insert the cloned. paragraph into the target TextBody shape.
// Passing "true" creates a deep clone, which creates a copy of the
// Paragraph object and everything directly or indirectly referenced by that object.
textBody2.Append(p1.CloneNode(true));
// Remove the source paragraph from the source file.
textBody1.RemoveChild(p1);
// Replace the removed paragraph with a placeholder.
textBody1.AppendChild(new Drawing.Paragraph());
}
}
// Get the slide part of the first slide in the presentation document.
static SlidePart GetFirstSlide(PresentationDocument presentationDocument)
{
// Get relationship ID of the first slide
PresentationPart part = presentationDocument.PresentationPart ?? presentationDocument.AddPresentationPart();
SlideIdList slideIdList = part.Presentation.SlideIdList ?? part.Presentation.AppendChild(new SlideIdList());
SlideId slideId = part.Presentation.SlideIdList?.GetFirstChild<SlideId>() ?? slideIdList.AppendChild<SlideId>(new SlideId());
string? relId = slideId.RelationshipId;
if (relId is null)
{
throw new ArgumentNullException(nameof(relId));
}
// Get the slide part by the relationship ID.
SlidePart slidePart = (SlidePart)part.GetPartById(relId);
return slidePart;
}