次の方法で共有


段落を別のプレゼンテーションに移動する

このトピックでは、Open XML SDK for Office のクラスを使用して、あるプレゼンテーションから別のプレゼンテーションにプログラムで段落を移動する方法について説明します。

PresentationDocument オブジェクトを取得する

Open XML SDK では、 PresentationDocument クラスはプレゼンテーション ドキュメント パッケージを表します。 プレゼンテーション ドキュメントを操作するには、まず PresentationDocument クラスのインスタンスを作成してから、そのインスタンスを操作します。 ドキュメントからクラス インスタンスを作成するには、ファイル パスを使用する Open メソッドを呼び出し、2 番目のパラメーターとしてブール値を使用してドキュメントを編集可能にするかどうかを指定します。 読み取り/書き込みのためにドキュメントを開くには、次のusingステートメントに示すように、このパラメーターの値trueを指定します。 このコードでは、 sourceFile パラメーターは、ドキュメントを開くファイルのパスを表す文字列です。

v3.0.0 以降では、using ステートメントに依存することを優先して、Close() メソッドが削除されました。 これにより、閉じかっこに達したときに、 Dispose() メソッドが自動的に呼び出されます。 using ステートメントに続くブロックは、using ステートメントで作成または名前付けされたオブジェクトのスコープを確立します(この場合はsourceDoc

プレゼンテーション ドキュメントの基本構造

PresentationMLドキュメントの基本的なドキュメント構造は、多数のパーツで構成され、その中にはプレゼンテーション定義を含むメインパーツがあります。 ISO/IEC 29500 仕様の次のテキストでは、PresentationML パッケージの全体的な形式について説明します。

PresentationML パッケージのメイン部分は、プレゼンテーション ルート要素で始まります。 この要素にはプレゼンテーションが含まれており、プレゼンテーションは スライド リスト、スライド マスター リスト、ノート マスター リスト、配布資料マスター リストを参照します。 スライド リストはプレゼンテーション内のすべてのスライドを参照します。スライド マスター リストはプレゼンテーションで使用されるスライド マスター全体を参照します。ノート マスターにはノート ページの書式設定に関する情報が含まれます。配布資料マスターは配布資料がどのように表示されるかを示します。

配布資料とは、聴衆に提供できるように一連のスライドを印刷したものです。

テキストやグラフィックのように、各スライドにはコメントノートを含めることができ、レイアウトを指定したり、1 つ以上のカスタム プレゼンテーションに組み込んだりできます。 コメントは、プレゼンテーション スライド デッキをメンテナンスする人向けの注釈です。 ノートは、プレゼンテーションの発表者または参加者向けのリマインダーやメモです。

PresentationMLドキュメントには、アニメーションオーディオビデオ、スライド間の切り替えなどのその他の機能があります。

PresentationMLドキュメントは、1 つのパーツに 1 つの大きな本文として格納されません。 その代わりに、特定のグループの機能を実現する要素が別個のパーツに格納されます。 たとえば、ドキュメント内のすべての作成者は 1 つの作成者パーツに格納され、各スライドには独自のパーツがあります。

ISO/IEC 29500: 2016

次の XML コードの例は、267 と 256 という ID で示される 2 つのスライドを含むプレゼンテーションを表します。

    <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>

Open XML SDK を使用すると、PresentationML 要素に対応する厳密に型指定されたクラスを使用して、ドキュメント構造とコンテンツを作成できます。 これらのクラスは、 名前空間にあります。 次の表に、 sldsldLayoutsldMaster、および notesMaster の各要素に対応するクラスのクラス名を示します。

PresentationML 要素 Open XML SDK クラス 説明
<sld/> Slide プレゼンテーション スライド。 SlidePart のルート要素
<sldLayout/> SlideLayout スライド レイアウト。 SlideLayoutPart のルート要素
<sldMaster/> SlideMaster スライド マスター。 SlideMasterPart のルート要素
<notesMaster/> NotesMaster ノート マスター (または handoutMaster)。 NotesMasterPart のルート要素

図形テキスト本体の構造

ISO/IEC 29500 仕様で、この要素の構造は次のように記述されています。

この要素は、対応する図形の内側に含めるテキストが存在することを指定するものです。 目に見えるすべてのテキストと、目に見えるすべてのテキスト関連プロパティがこの要素に含まれます。 複数の段落と、段落内に複数のテキスト セクションを含めることもできます。

© ISO/IEC 29500: 2016

次の表に、図形テキスト本体の子要素と、各子要素の説明を示します。

子の要素 説明
bodyPr 本体のプロパティ
lstStyle テキスト リストのプロパティ
p テキスト段落

次の XML スキーマ フラグメントが、この要素のコンテンツを定義します。

    <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>

サンプル コードの動作のしくみ

このトピックのコードは、 MoveParagraphToPresentationGetFirstSlideの 2 つのメソッドで構成されています。 最初のメソッドは、2 つの文字列パラメーターを受け取ります。1 つは、移動する段落を含むソース ファイルを表すパラメーターと、段落の移動先のターゲット ファイルを表すパラメーターです。 メソッドは両方のプレゼンテーション ファイルを開き、 GetFirstSlide メソッドを呼び出して各ファイルの最初のスライドを取得します。 次に、各スライドの最初の TextBody 図形とソース図形の最初の段落を取得します。 ソース段落の deep clone を実行し、ソース Paragraph オブジェクト自体だけでなく、そのオブジェクトに含まれるすべてのテキストもコピーします。 次に、複製した段落をターゲット ファイルに挿入し、ソース ファイルからソース 段落を削除し、プレースホルダー 段落に置き換えます。 最後に、変更したスライドを両方のプレゼンテーションに保存します。

// 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());
    }
}

GetFirstSlide メソッドは、渡されたPresentationDocument オブジェクトを取得し、そのプレゼンテーション パーツを取得し、スライド リストの最初のスライドの ID を取得します。 その後、スライドのリレーションシップ ID を取得し、リレーションシップ ID からスライド パーツを取得して、呼び出し元のメソッドにスライド パーツを返します。

// 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;
}

サンプル コード

以下に、C# と 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;
}

関連項目