プレゼンテーションのすべてのスライドから特定の作成者のコメントをすべて削除する
このトピックでは、Open XML SDK for Office のクラスを使用して、プレゼンテーション内の特定の作成者によるすべてのコメントをプログラムで削除する方法について説明します。
注:
このサンプルは、最新のコメントPowerPoint用です。 クラシック コメントについては、 GitHub でアーカイブされたサンプルを参照してください。
PresentationDocument オブジェクトを取得する
Open XML SDK では、 PresentationDocument クラスはプレゼンテーション ドキュメント パッケージを表します。 プレゼンテーション ドキュメントを操作するには、まず PresentationDocument
クラスのインスタンスを作成してから、そのインスタンスを操作します。 ドキュメントからクラス インスタンスを作成するには、ファイル パスを使用する Open メソッドを呼び出し、2 番目のパラメーターとしてブール値を使用してドキュメントを編集可能にするかどうかを指定します。 読み取り/書き込みのためにドキュメントを開くには、次のusing
ステートメントに示すように、このパラメーターの値true
を指定します。
このコードの fileName パラメーターは、開く文書ファイルのパスを表す文字列であり、author は PowerPoint のオプションの [全般] タブに表示されるユーザー名です。
using (PresentationDocument doc = PresentationDocument.Open(fileName, true))
v3.0.0 以降では、using ステートメントに依存することを優先して、Close() メソッドが削除されました。
これにより、閉じかっこに達したときに、 Dispose() メソッドが自動的に呼び出されます。
using
ステートメントに続くブロックは、using
ステートメントで作成または名前付けされたオブジェクトのスコープを確立します(この場合はdoc
。
プレゼンテーション ドキュメントの基本構造
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
モダン コメント要素の構造
次の XML 要素は、1 つのコメントを指定します。
これには、コメント (t
) のテキストと、作成者 (authorId
)、作成日時 (created
)、コメント ID (id
) を参照する属性が含まれます。
<p188:cm id="{62A8A96D-E5A8-4BFC-B993-A6EAE3907CAD}" authorId="{CD37207E-7903-4ED4-8AE8-017538D2DF7E}" created="2024-12-30T20:26:06.503">
<p188:txBody>
<a:bodyPr/>
<a:lstStyle/>
<a:p>
<a:r>
<a:t>Needs more cowbell</a:t>
</a:r>
</a:p>
</p188:txBody>
</p188:cm>
次の表に、 cm
(コメント) 要素の可能な子要素と属性の定義を示します。 完全な定義については、「 MS-PPTX 2.16.3.3 CT_Comment
属性 | 定義 |
---|---|
id | コメントまたはコメント応答の ID を指定します。 |
authorId | コメントまたはコメント応答の作成者 ID を指定します。 |
status | コメントまたはコメント応答の状態を指定します。 |
作成済み | コメントまたはコメント応答が作成される日時を指定します。 |
startDate | コメントの開始日を指定します。 |
dueDate | コメントの期限を指定します。 |
assignedTo | コメントが割り当てられている作成者の一覧を指定します。 |
complete | コメントの完了率を指定します。 |
title | コメントのタイトルを指定します。 |
子の要素 | 定義 |
---|---|
pc:sldMkLst | コメントのアンカー先のスライドを識別するコンテンツ モニカーを指定します。 |
ac:deMkLst | コメントがアンカーされる描画要素を識別するコンテンツ モニカーを指定します。 |
ac:txMkLst | コメントがアンカーされるテキスト文字範囲を識別するコンテンツ モニカーを指定します。 |
unknownAnchor | コメントがアンカーされる不明なアンカーを指定します。 |
pos | コメントがアンカーされる最初のオブジェクトの左上隅を基準にして、コメントの位置を指定します。 |
replyLst | コメントへの応答の一覧を指定します。 |
txBody | コメントまたはコメント応答のテキストを指定します。 |
extLst | コメントまたはコメント応答の拡張機能の一覧を指定します。 |
次の XML スキーマの例では、必須属性と省略可能な属性に加えて、 cm
要素のメンバーを定義します。
<xsd:complexType name="CT_Comment">
<xsd:sequence>
<xsd:group ref="EG_CommentAnchor" minOccurs="1" maxOccurs="1"/>
<xsd:element name="pos" type="a:CT_Point2D" minOccurs="0" maxOccurs="1"/>
<xsd:element name="replyLst" type="CT_CommentReplyList" minOccurs="0" maxOccurs="1"/>
<xsd:group ref="EG_CommentProperties" minOccurs="1" maxOccurs="1"/>
</xsd:sequence>
<xsd:attributeGroup ref="AG_CommentProperties"/>
<xsd:attribute name="startDate" type="xsd:dateTime" use="optional"/>
<xsd:attribute name="dueDate" type="xsd:dateTime" use="optional"/>
<xsd:attribute name="assignedTo" type="ST_AuthorIdList" use="optional" default=""/>
<xsd:attribute name="complete" type="s:ST_PositiveFixedPercentage" default="0%" use="optional"/>
<xsd:attribute name="title" type="xsd:string" use="optional" default=""/>
</xsd:complexType>
サンプル コードの動作のしくみ
読み取り/書き込みアクセス用にプレゼンテーション ドキュメントを開き、 PresentationDocument
クラスをインスタンス化した後、コードはコメント作成者のリストから指定されたコメント作成者を取得します。
// Get the modern comments.
IEnumerable<Author>? commentAuthors = doc.PresentationPart?.authorsPart?.AuthorList.Elements<Author>()
.Where(x => x.Name is not null && x.Name.HasValue && x.Name.Value!.Equals(author));
一致する作成者とプレゼンテーションのすべてのスライドを反復処理することによって、すべてのスライド パーツと、各スライド パーツのコメント パーツを取得します。 その後、指定された作成者によるコメントのリストを取得し、それぞれを削除します。 また、コメント パーツに既存のコメントがないことを確認し、ない場合はそのパーツを削除します。 そして、コメント作成者パーツからコメント作成者を削除します。
// Iterate through all the matching authors.
foreach (Author commentAuthor in commentAuthors)
{
string? authorId = commentAuthor.Id;
IEnumerable<SlidePart>? slideParts = doc.PresentationPart?.SlideParts;
// If there's no author ID or slide parts or slide parts, return.
if (authorId is null || slideParts is null)
{
return;
}
// Iterate through all the slides and get the slide parts.
foreach (SlidePart slide in slideParts)
{
IEnumerable<PowerPointCommentPart>? slideCommentsParts = slide.commentParts;
// Get the list of comments.
if (slideCommentsParts is not null)
{
IEnumerable<Tuple<PowerPointCommentPart, Comment>> commentsTup = slideCommentsParts
.SelectMany(scp => scp.CommentList.Elements<Comment>()
.Where(comment => comment.AuthorId is not null && comment.AuthorId == authorId)
.Select(c => new Tuple<PowerPointCommentPart, Comment>(scp, c)));
foreach (Tuple<PowerPointCommentPart, Comment> comment in commentsTup)
{
// Delete all the comments by the specified author.
comment.Item1.CommentList.RemoveChild(comment.Item2);
// If the commentPart has no existing comment.
if (comment.Item1.CommentList.ChildElements.Count == 0)
{
// Delete this part.
slide.DeletePart(comment.Item1);
}
}
}
}
// Delete the comment author from the authors part.
doc.PresentationPart?.authorsPart?.AuthorList.RemoveChild(commentAuthor);
}
サンプル コード
次のメソッドは、パラメーターとして、ソース プレゼンテーション ファイル名とパス、および削除するコメントの作成者の名前を取ります。 プレゼンテーション内で、その作成者のすべてのコメントが検索され、削除されます。 次に、コメントの作成者のリストから、その作成者を削除します。
注:
[!メモ] 正確な作成者名を取得するには、プレゼンテーション ファイルを開いて [ ファイル ] メニュー項目をクリックし、[ オプション ] をクリックします。 [ PowerPoint オプション ] ウィンドウが開き、[ 全般 ] タブの内容が表示されます。 作成者の名前は、このタブの [ ユーザー名 ] と一致しなければなりません。
以下に、C# と Visual Basic による完全なサンプル コードを示します。
// Remove all the comments in the slides by a certain x.
static void DeleteCommentsByAuthorInPresentation(string fileName, string author)
{
using (PresentationDocument doc = PresentationDocument.Open(fileName, true))
{
// Get the modern comments.
IEnumerable<Author>? commentAuthors = doc.PresentationPart?.authorsPart?.AuthorList.Elements<Author>()
.Where(x => x.Name is not null && x.Name.HasValue && x.Name.Value!.Equals(author));
if (commentAuthors is null)
{
return;
}
// Iterate through all the matching authors.
foreach (Author commentAuthor in commentAuthors)
{
string? authorId = commentAuthor.Id;
IEnumerable<SlidePart>? slideParts = doc.PresentationPart?.SlideParts;
// If there's no author ID or slide parts or slide parts, return.
if (authorId is null || slideParts is null)
{
return;
}
// Iterate through all the slides and get the slide parts.
foreach (SlidePart slide in slideParts)
{
IEnumerable<PowerPointCommentPart>? slideCommentsParts = slide.commentParts;
// Get the list of comments.
if (slideCommentsParts is not null)
{
IEnumerable<Tuple<PowerPointCommentPart, Comment>> commentsTup = slideCommentsParts
.SelectMany(scp => scp.CommentList.Elements<Comment>()
.Where(comment => comment.AuthorId is not null && comment.AuthorId == authorId)
.Select(c => new Tuple<PowerPointCommentPart, Comment>(scp, c)));
foreach (Tuple<PowerPointCommentPart, Comment> comment in commentsTup)
{
// Delete all the comments by the specified author.
comment.Item1.CommentList.RemoveChild(comment.Item2);
// If the commentPart has no existing comment.
if (comment.Item1.CommentList.ChildElements.Count == 0)
{
// Delete this part.
slide.DeletePart(comment.Item1);
}
}
}
}
// Delete the comment author from the authors part.
doc.PresentationPart?.authorsPart?.AuthorList.RemoveChild(commentAuthor);
}
}
}