プレゼンテーションにテーマを適用する
このトピックでは、Open XML SDK for Office のクラスを使用して、あるプレゼンテーションから別のプレゼンテーションにプログラムでテーマを適用する方法について説明します。
PresentationDocument オブジェクトを取得する
Open XML SDK では、 PresentationDocument クラスはプレゼンテーション ドキュメント パッケージを表します。 To work with a presentation document, first create an instance of the PresentationDocument class, and then work with that instance. ドキュメントからクラス インスタンスを作成するには、ファイル パスを使用する Open メソッドを呼び出し、2 番目のパラメーターとしてブール値を使用してドキュメントを編集可能にするかどうかを指定します。 ドキュメントを読み取り専用アクセスで開くには、このパラメーターに値 false を指定します。 ドキュメントを読み取り/書き込みアクセスで開くには、このパラメーターに値 true を指定します。 次の using ステートメントでは、テーマの適用先となるプレゼンテーションと、既にそのテーマが適用されている適用元のプレゼンテーションの 2 つのプレゼンテーション ファイルを開いています。 適用元のプレゼンテーション ファイルは読み取り専用アクセスで開き、適用先のプレゼンテーション ファイルは読み取り/書き込みアクセスで開きます。 このコードでは、 themePresentation パラメーターが適用元のプレゼンテーション ドキュメントのパスを表す文字列で、 presentationFile パラメーターが適用先のプレゼンテーション ドキュメントのパスを表す文字列です。
using (PresentationDocument themeDocument = PresentationDocument.Open(themePresentation, false))
using (PresentationDocument presentationDocument = PresentationDocument.Open(presentationFile, true))
{
v3.0.0 以降では、using ステートメントに依存することを優先して、Close() メソッドが削除されました。
これにより、閉じかっこに達したときに、 Dispose() メソッドが自動的に呼び出されます。
using
ステートメントに続くブロックは、using
ステートメントで作成または名前付けされたオブジェクトのスコープを確立します。この場合は、themeDocument
とpresentationDocument
。
プレゼンテーション ドキュメントの基本構造
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 要素に対応する厳密に型指定されたクラスを使用して、ドキュメント構造とコンテンツを作成できます。 これらのクラスは、 名前空間にあります。 次の表に、 sld
、 sldLayout
、 sldMaster
、および 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
次の表に、テーマ クラスで使用できる子の型の一覧を示します。
PresentationML の要素 | Open XML SDK クラス | 説明 |
---|---|---|
custClrLst | CustomColorList | ユーザー設定色リスト |
extLst | ExtensionList | 拡張リスト |
extraClrSchemeLst | ExtraColorSchemeList | 付加的な配色リスト |
objectDefaults | ObjectDefaults | オブジェクトの既定値 |
themeElements | ThemeElements | テーマの要素 |
次の XML スキーマ フラグメントでは、テーマ要素の 4 つのパーツを定義しています。 themeElements 要素には、テーマ内で定義された主要な書式設定が含まれます。 その他のパーツは、 themeElements に含まれている情報のオーバーライド、既定値、および追加情報を提供します。 テーマを定義する複合型の CT_OfficeStyleSheet は、次のような方法で定義されます。
<complexType name="CT_OfficeStyleSheet">
<sequence>
<element name="themeElements" type="CT_BaseStyles" minOccurs="1" maxOccurs="1"/>
<element name="objectDefaults" type="CT_ObjectStyleDefaults" minOccurs="0" maxOccurs="1"/>
<element name="extraClrSchemeLst" type="CT_ColorSchemeList" minOccurs="0" maxOccurs="1"/>
<element name="custClrLst" type="CT_CustomColorList" minOccurs="0" maxOccurs="1"/>
<element name="extLst" type="CT_OfficeArtExtensionList" minOccurs="0" maxOccurs="1"/>
</sequence>
<attribute name="name" type="xsd:string" use="optional" default=""/>
</complexType>
この複合型には CT_OfficeArtExtensionList も含まれており、この複合型の将来の拡張のために使用されます。
サンプル コードの動作のしくみ
サンプル コードは、 ApplyThemeToPresentation メソッドの 2 つのオーバーロード、および GetSlideLayoutType メソッドで構成されます。 次のコードは、1 つ目のオーバーロードされたメソッドを示しており、 themePresentation と presentationFile の 2 つのプレゼンテーション ファイルを開いて、2 つ目のオーバーロードされたメソッドにパラメーターとして渡しています。
// Apply a new theme to the presentation.
static void ApplyThemeToPresentation(string presentationFile, string themePresentation)
{
using (PresentationDocument themeDocument = PresentationDocument.Open(themePresentation, false))
using (PresentationDocument presentationDocument = PresentationDocument.Open(presentationFile, true))
{
ApplyThemeToPresentationDocument(presentationDocument, themeDocument);
}
}
2 つ目のオーバーロードされたメソッドでは、まずどちらかのプレゼンテーション ファイルが空かどうかを確認し、ファイルが空の場合は例外をスローします。 次に、 PresentationPart オブジェクトを宣言し、渡された適用先の PresentationDocument オブジェクトのプレゼンテーション パーツを割り当てて設定することによって、プレゼンテーション ドキュメントのプレゼンテーション パーツを取得します。 その後、渡された両方のオブジェクトのプレゼンテーション パーツからスライド マスター パーツを取得し、適用先のプレゼンテーションのスライド マスター パーツのリレーションシップ ID を取得します。
// Apply a new theme to the presentation.
static void ApplyThemeToPresentationDocument(PresentationDocument presentationDocument, PresentationDocument themeDocument)
{
// Get the presentation part of the presentation document.
PresentationPart presentationPart = presentationDocument.PresentationPart ?? presentationDocument.AddPresentationPart();
// Get the existing slide master part.
SlideMasterPart slideMasterPart = presentationPart.SlideMasterParts.ElementAt(0);
string relationshipId = presentationPart.GetIdOfPart(slideMasterPart);
// Get the new slide master part.
PresentationPart themeDocPresentationPart = themeDocument.PresentationPart ?? themeDocument.AddPresentationPart();
SlideMasterPart newSlideMasterPart = themeDocPresentationPart.SlideMasterParts.ElementAt(0);
適用先のプレゼンテーションから既存のテーマ パーツとスライド マスター パーツを削除します。 以前のリレーションシップ ID を再利用して、適用元のプレゼンテーションからの新しいスライド マスター パーツを適用先のプレゼンテーションに追加します。 また、適用先のプレゼンテーションにテーマ パーツも追加します。
if (presentationPart.ThemePart is not null)
{
// Remove the existing theme part.
presentationPart.DeletePart(presentationPart.ThemePart);
}
// Remove the old slide master part.
presentationPart.DeletePart(slideMasterPart);
// Import the new slide master part, and reuse the old relationship ID.
newSlideMasterPart = presentationPart.AddPart(newSlideMasterPart, relationshipId);
if (newSlideMasterPart.ThemePart is not null)
{
// Change to the new theme part.
presentationPart.AddPart(newSlideMasterPart.ThemePart);
}
スライド マスター パーツのすべてのスライド レイアウト パーツを反復処理して、新しいスライド レイアウトのリストに追加します。 既定のレイアウトの種類を指定します。 たとえば、既定のレイアウトの種類のコードは "Title and Content" です。
Dictionary<string, SlideLayoutPart> newSlideLayouts = new Dictionary<string, SlideLayoutPart>();
foreach (var slideLayoutPart in newSlideMasterPart.SlideLayoutParts)
{
string? newLayoutType = GetSlideLayoutType(slideLayoutPart);
if (newLayoutType is not null)
{
newSlideLayouts.Add(newLayoutType, slideLayoutPart);
}
}
string? layoutType = null;
SlideLayoutPart? newLayoutPart = null;
// Insert the code for the layout for this example.
string defaultLayoutType = "Title and Content";
適用先のプレゼンテーションのすべてのスライド パーツを反復処理し、すべてのスライドのスライド レイアウト リレーションシップを削除します。 GetSlideLayoutType メソッドを使用して、スライド レイアウト パーツのレイアウトの種類を調べます。 既存のスライド レイアウト パーツがあるスライドについては、以前と同じ種類の新しいスライド レイアウト パーツを追加します。 既存のスライド レイアウト パーツがないスライドについては、既定の種類の新しいスライド レイアウト パーツを追加します。
// Remove the slide layout relationship on all slides.
foreach (var slidePart in presentationPart.SlideParts)
{
layoutType = null;
if (slidePart.SlideLayoutPart is not null)
{
// Determine the slide layout type for each slide.
layoutType = GetSlideLayoutType(slidePart.SlideLayoutPart);
// Delete the old layout part.
slidePart.DeletePart(slidePart.SlideLayoutPart);
}
if (layoutType is not null && newSlideLayouts.TryGetValue(layoutType, out newLayoutPart))
{
// Apply the new layout part.
slidePart.AddPart(newLayoutPart);
}
else
{
newLayoutPart = newSlideLayouts[defaultLayoutType];
// Apply the new default layout part.
slidePart.AddPart(newLayoutPart);
}
}
スライド レイアウトの種類を取得するために、 GetSlideLayoutType メソッドを使用します。このメソッドはパラメーターとしてスライド レイアウト パーツを受け取り、スライド レイアウトの種類の名前を表す文字列を 2 つ目のオーバーロードされた ApplyThemeToPresentation メソッドに返します。
// Get the slide layout type.
static string? GetSlideLayoutType(SlideLayoutPart slideLayoutPart)
{
CommonSlideData? slideData = slideLayoutPart.SlideLayout?.CommonSlideData;
return slideData?.Name;
}
サンプル コード
以下は、プレゼンテーションのテーマを別のプレゼンテーションにコピーする完全なサンプル コードです。 このプログラムを使用するには、コピーするテーマが設定された適用元のプレゼンテーション (たとえば Myppt9-theme.pptx) と、適用先のプレゼンテーション (たとえば Myppt9.pptx) の 2 種類のプレゼンテーションを作成する必要があります。 コピーを行うには、プログラムで次の呼び出しを使用します。
ApplyThemeToPresentation(args[0], args[1]);
この呼び出しを実行した後で Myppt2.pptx ファイルを調べると、Myppt9-theme.pptx ファイルと同じテーマが適用されていることがわかります。
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Presentation;
using System.Collections.Generic;
using System.Linq;
ApplyThemeToPresentation(args[0], args[1]);
// Apply a new theme to the presentation.
static void ApplyThemeToPresentation(string presentationFile, string themePresentation)
{
using (PresentationDocument themeDocument = PresentationDocument.Open(themePresentation, false))
using (PresentationDocument presentationDocument = PresentationDocument.Open(presentationFile, true))
{
ApplyThemeToPresentationDocument(presentationDocument, themeDocument);
}
}
// Apply a new theme to the presentation.
static void ApplyThemeToPresentationDocument(PresentationDocument presentationDocument, PresentationDocument themeDocument)
{
// Get the presentation part of the presentation document.
PresentationPart presentationPart = presentationDocument.PresentationPart ?? presentationDocument.AddPresentationPart();
// Get the existing slide master part.
SlideMasterPart slideMasterPart = presentationPart.SlideMasterParts.ElementAt(0);
string relationshipId = presentationPart.GetIdOfPart(slideMasterPart);
// Get the new slide master part.
PresentationPart themeDocPresentationPart = themeDocument.PresentationPart ?? themeDocument.AddPresentationPart();
SlideMasterPart newSlideMasterPart = themeDocPresentationPart.SlideMasterParts.ElementAt(0);
if (presentationPart.ThemePart is not null)
{
// Remove the existing theme part.
presentationPart.DeletePart(presentationPart.ThemePart);
}
// Remove the old slide master part.
presentationPart.DeletePart(slideMasterPart);
// Import the new slide master part, and reuse the old relationship ID.
newSlideMasterPart = presentationPart.AddPart(newSlideMasterPart, relationshipId);
if (newSlideMasterPart.ThemePart is not null)
{
// Change to the new theme part.
presentationPart.AddPart(newSlideMasterPart.ThemePart);
}
Dictionary<string, SlideLayoutPart> newSlideLayouts = new Dictionary<string, SlideLayoutPart>();
foreach (var slideLayoutPart in newSlideMasterPart.SlideLayoutParts)
{
string? newLayoutType = GetSlideLayoutType(slideLayoutPart);
if (newLayoutType is not null)
{
newSlideLayouts.Add(newLayoutType, slideLayoutPart);
}
}
string? layoutType = null;
SlideLayoutPart? newLayoutPart = null;
// Insert the code for the layout for this example.
string defaultLayoutType = "Title and Content";
// Remove the slide layout relationship on all slides.
foreach (var slidePart in presentationPart.SlideParts)
{
layoutType = null;
if (slidePart.SlideLayoutPart is not null)
{
// Determine the slide layout type for each slide.
layoutType = GetSlideLayoutType(slidePart.SlideLayoutPart);
// Delete the old layout part.
slidePart.DeletePart(slidePart.SlideLayoutPart);
}
if (layoutType is not null && newSlideLayouts.TryGetValue(layoutType, out newLayoutPart))
{
// Apply the new layout part.
slidePart.AddPart(newLayoutPart);
}
else
{
newLayoutPart = newSlideLayouts[defaultLayoutType];
// Apply the new default layout part.
slidePart.AddPart(newLayoutPart);
}
}
}
// Get the slide layout type.
static string? GetSlideLayoutType(SlideLayoutPart slideLayoutPart)
{
CommonSlideData? slideData = slideLayoutPart.SlideLayout?.CommonSlideData;
return slideData?.Name;
}