Adicionar um comentário a um slide de uma apresentação
Este tópico mostra como utilizar as classes no SDK Open XML para o Office para adicionar um comentário ao primeiro diapositivo numa apresentação através de programação.
Observação
Este exemplo destina-se a comentários modernos do PowerPoint. Para comentários clássicos, veja o exemplo arquivado no GitHub.
Estrutura básica do documento de apresentação
A estrutura de documentos básica de um PresentationML
documento consiste em várias partes, entre as quais a main parte que contém a definição da apresentação. O texto seguinte da especificação ISO/IEC 29500 apresenta a forma geral de um PresentationML
pacote.
O main parte de um
PresentationML
pacote começa com um elemento raiz de apresentação. Esse elemento contém uma apresentação que, por sua vez, se refere a uma lista de diapositivos, a uma lista de master de diapositivos, a uma lista de notas master e a um folheto master lista. A lista de diapositivos refere-se a todos os diapositivos na apresentação; A lista de master de diapositivos refere-se a todos os modelos globais de diapositivos utilizados na apresentação; as notas master contêm informações sobre a formatação das páginas de notas; e o master de folheto descreve o aspeto de um folheto.Um folheto é um conjunto impresso de diapositivos que podem ser fornecidos a uma audiência.
Além de texto e gráficos, cada diapositivo pode conter comentários e notas, pode ter um esquema e pode fazer parte de uma ou mais apresentações personalizadas. Um comentário é uma anotação destinada à pessoa que mantém o conjunto de diapositivos da apresentação. Uma nota é um lembrete ou texto destinado ao apresentador ou à audiência.
Outras funcionalidades que um
PresentationML
documento pode incluir: animação, áudio, vídeo e transições entre diapositivos.Um
PresentationML
documento não é armazenado como um corpo grande numa única parte. Em vez disso, os elementos que implementam determinados agrupamentos de funcionalidades são armazenados em partes separadas. Por exemplo, todos os autores num documento são armazenados numa parte dos autores, enquanto cada diapositivo tem a sua própria parte.ISO/IEC 29500: 2016
O seguinte exemplo de código XML representa uma apresentação que contém dois diapositivos indicados pelos IDs 267 e 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>
Com o SDK Open XML, pode criar a estrutura e o conteúdo do documento com classes com tipos fortes que correspondem a elementos PresentationML. Pode encontrar estas classes no espaço de nomes. A tabela seguinte lista os nomes das classes que correspondem aos sld
elementos , sldLayout
, sldMaster
e notesMaster
.
Elemento PresentationML | Abrir Classe SDK XML | Descrição |
---|---|---|
<sld/> |
Slide | Diapositivo de Apresentação. É o elemento raiz de SlidePart. |
<sldLayout/> |
SlideLayout | Esquema de Diapositivo. É o elemento raiz de SlideLayoutPart. |
<sldMaster/> |
SlideMaster | Modelo Global de Diapositivos. É o elemento raiz de SlideMasterPart. |
<notesMaster/> |
NotesMaster | Modelo Global de Notas (ou handoutMaster). É o elemento raiz de NotesMasterPart. |
A Estrutura do Elemento de Comentário Moderno
O seguinte elemento XML especifica um único comentário.
Contém o texto do comentário (t
) e os atributos que se referem ao respetivo autor (authorId
), data e hora de criação (created
) e id de comentário (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>
As tabelas seguintes listam as definições dos possíveis elementos subordinados e atributos do cm
elemento (comentário). Para obter a definição completa, consulte MS-PPTX 2.16.3.3 CT_Comment
Atributo | Definição |
---|---|
id | Especifica o ID de um comentário ou uma resposta de comentário. |
authorId | Especifica o ID de autor de um comentário ou uma resposta de comentário. |
status | Especifica a status de um comentário ou uma resposta de comentário. |
criadas | Especifica a data/hora em que o comentário ou a resposta ao comentário são criados. |
startDate | Especifica a data de início do comentário. |
dueDate | Especifica a data para conclusão do comentário. |
assignedTo | Especifica uma lista de autores a quem o comentário é atribuído. |
complete | Especifica a percentagem de conclusão do comentário. |
title | Especifica o título de um comentário. |
Elemento Filho | Definição |
---|---|
pc:sldMkLst | Especifica um nome de conteúdo que identifica o diapositivo ao qual o comentário está ancorado. |
ac:deMkLst | Especifica um moniker de conteúdo que identifica o elemento de desenho ao qual o comentário está ancorado. |
ac:txMkLst | Especifica um nome de conteúdo que identifica o intervalo de carateres de texto ao qual o comentário está ancorado. |
unknownAnchor | Especifica uma âncora desconhecida à qual o comentário está ancorado. |
pos | Especifica a posição do comentário, relativamente ao canto superior esquerdo do primeiro objeto ao qual o comentário está ancorado. |
replyLst | Especifica a lista de respostas ao comentário. |
txBody | Especifica o texto de um comentário ou uma resposta de comentário. |
extLst | Especifica uma lista de extensões para um comentário ou uma resposta de comentário. |
O seguinte exemplo de esquema XML define os membros do elemento para além dos cm
atributos obrigatórios e opcionais.
<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>
Como funciona o código de exemplo
O código de exemplo abre o documento de apresentação na instrução using. Em seguida, instancia o CommentAuthorsPart e verifica se existe uma parte dos autores de comentários. Se não existir, adiciona um.
// create missing PowerPointAuthorsPart if it is null
if (presentationDocument.PresentationPart.authorsPart is null)
{
presentationDocument.PresentationPart.AddNewPart<PowerPointAuthorsPart>();
}
O código determina se existe uma parte do autor do PowerPoint existente na parte da apresentação; caso contrário, adiciona um e, em seguida, verifica se existe uma lista de autores e adiciona uma se estiver em falta. Verifica também se o autor que é transmitido está na lista de autores existentes; Se for o caso, atribui o ID de autor existente. Caso contrário, adiciona um novo autor à lista de autores e atribui um ID de autor e os valores dos parâmetros.
// Add missing AuthorList if it is null
if (presentationDocument.PresentationPart.authorsPart!.AuthorList is null)
{
presentationDocument.PresentationPart.authorsPart.AuthorList = new AuthorList();
}
// Get the author or create a new one
Author? author = presentationDocument.PresentationPart.authorsPart.AuthorList
.ChildElements.OfType<Author>().Where(a => a.Name?.Value == name).FirstOrDefault();
if (author is null)
{
string authorId = string.Concat("{", Guid.NewGuid(), "}");
string userId = string.Concat(name.Split(" ").FirstOrDefault() ?? "user", "@example.com::", Guid.NewGuid());
author = new Author() { Id = authorId, Name = name, Initials = initials, UserId = userId, ProviderId = string.Empty };
presentationDocument.PresentationPart.authorsPart.AuthorList.AppendChild(author);
}
Em seguida, o código determina se existe um ID de diapositivo e devolve se um não existe
// Get the Id of the slide to add the comment to
SlideId? slideId = presentationDocument.PresentationPart.Presentation.SlideIdList?.Elements<SlideId>()?.FirstOrDefault();
// If slideId is null, there are no slides, so return
if (slideId is null) return;
No segmento abaixo, o código obtém o ID da relação. Se existir, é utilizado para localizar a parte do diapositivo, caso contrário, é efetuada a enumeração do primeiro diapositivo nas partes do diapositivo. Em seguida, verifica se existe uma parte de comentários do PowerPoint para o diapositivo e se não adiciona uma.
// Get the relationship id of the slide if it exists
string? relId = slideId.RelationshipId;
// Use the relId to get the slide if it exists, otherwise take the first slide in the sequence
SlidePart slidePart = relId is not null ? (SlidePart)presentationPart.GetPartById(relId)
: presentationDocument.PresentationPart.SlideParts.First();
// If the slide part has comments parts take the first PowerPointCommentsPart
// otherwise add a new one
PowerPointCommentPart powerPointCommentPart = slidePart.commentParts.FirstOrDefault() ?? slidePart.AddNewPart<PowerPointCommentPart>();
Abaixo do código, cria um novo comentário moderno e, em seguida, adiciona uma lista de comentários à parte de comentário do PowerPoint se não existir um e adiciona o comentário a essa lista de comentários.
// Create the comment using the new modern comment class DocumentFormat.OpenXml.Office2021.PowerPoint.Comment.Comment
DocumentFormat.OpenXml.Office2021.PowerPoint.Comment.Comment comment = new DocumentFormat.OpenXml.Office2021.PowerPoint.Comment.Comment(
new SlideMonikerList(
new DocumentMoniker(),
new SlideMoniker()
{
CId = cid,
SldId = slideId.Id,
}),
new TextBodyType(
new BodyProperties(),
new ListStyle(),
new Paragraph(new Run(new DocumentFormat.OpenXml.Drawing.Text(text)))))
{
Id = string.Concat("{", Guid.NewGuid(), "}"),
AuthorId = author.Id,
Created = DateTime.Now,
};
// If the comment list does not exist, add one.
powerPointCommentPart.CommentList ??= new DocumentFormat.OpenXml.Office2021.PowerPoint.Comment.CommentList();
// Add the comment to the comment list
powerPointCommentPart.CommentList.AppendChild(comment);
Com os comentários modernos, o diapositivo tem de ter a extensão e a lista de extensões corretas. O código seguinte determina se o diapositivo já tem um SlideExtensionList e SlideExtension e adiciona-os ao diapositivo se não estiverem presentes.
// Get the presentation extension list if it exists
SlideExtensionList? presentationExtensionList = slidePart.Slide.ChildElements.OfType<SlideExtensionList>().FirstOrDefault();
// Create a boolean that determines if this is the slide's first comment
bool isFirstComment = false;
// If the presentation extension list is null, add one and set this as the first comment for the slide
if (presentationExtensionList is null)
{
isFirstComment = true;
slidePart.Slide.AppendChild(new SlideExtensionList());
presentationExtensionList = slidePart.Slide.ChildElements.OfType<SlideExtensionList>().First();
}
// Get the slide extension if it exists
SlideExtension? presentationExtension = presentationExtensionList.ChildElements.OfType<SlideExtension>().FirstOrDefault();
// If the slide extension is null, add it and set this as a new comment
if (presentationExtension is null)
{
isFirstComment = true;
presentationExtensionList.AddChild(new SlideExtension() { Uri = "{6950BFC3-D8DA-4A85-94F7-54DA5524770B}" });
presentationExtension = presentationExtensionList.ChildElements.OfType<SlideExtension>().First();
}
// If this is the first comment for the slide add the comment relationship
if (isFirstComment)
{
presentationExtension.AddChild(new CommentRelationship()
{ Id = slidePart.GetIdOfPart(powerPointCommentPart) });
}
Código de exemplo
Segue-se o exemplo de código completo que mostra como adicionar um novo comentário com um autor novo ou existente a um diapositivo com ou sem comentários existentes.
Observação
Para obter o nome exato do autor e as iniciais, abra o ficheiro de apresentação, clique no item de menu Ficheiro e, em seguida, clique em Opções. A janela PowerPointOptions é aberta e o conteúdo do separador Geral é apresentado. O nome e as iniciais do autor têm de corresponder ao Nome de utilizador e às Iniciais neste separador.
static void AddCommentToPresentation(string file, string initials, string name, string text)
{
using (PresentationDocument presentationDocument = PresentationDocument.Open(file, true))
{
PresentationPart presentationPart = presentationDocument?.PresentationPart ?? throw new MissingFieldException("PresentationPart");
// create missing PowerPointAuthorsPart if it is null
if (presentationDocument.PresentationPart.authorsPart is null)
{
presentationDocument.PresentationPart.AddNewPart<PowerPointAuthorsPart>();
}
// Add missing AuthorList if it is null
if (presentationDocument.PresentationPart.authorsPart!.AuthorList is null)
{
presentationDocument.PresentationPart.authorsPart.AuthorList = new AuthorList();
}
// Get the author or create a new one
Author? author = presentationDocument.PresentationPart.authorsPart.AuthorList
.ChildElements.OfType<Author>().Where(a => a.Name?.Value == name).FirstOrDefault();
if (author is null)
{
string authorId = string.Concat("{", Guid.NewGuid(), "}");
string userId = string.Concat(name.Split(" ").FirstOrDefault() ?? "user", "@example.com::", Guid.NewGuid());
author = new Author() { Id = authorId, Name = name, Initials = initials, UserId = userId, ProviderId = string.Empty };
presentationDocument.PresentationPart.authorsPart.AuthorList.AppendChild(author);
}
// Get the Id of the slide to add the comment to
SlideId? slideId = presentationDocument.PresentationPart.Presentation.SlideIdList?.Elements<SlideId>()?.FirstOrDefault();
// If slideId is null, there are no slides, so return
if (slideId is null) return;
Random ran = new Random();
UInt32Value cid = Convert.ToUInt32(ran.Next(100000000, 999999999));
// Get the relationship id of the slide if it exists
string? relId = slideId.RelationshipId;
// Use the relId to get the slide if it exists, otherwise take the first slide in the sequence
SlidePart slidePart = relId is not null ? (SlidePart)presentationPart.GetPartById(relId)
: presentationDocument.PresentationPart.SlideParts.First();
// If the slide part has comments parts take the first PowerPointCommentsPart
// otherwise add a new one
PowerPointCommentPart powerPointCommentPart = slidePart.commentParts.FirstOrDefault() ?? slidePart.AddNewPart<PowerPointCommentPart>();
// Create the comment using the new modern comment class DocumentFormat.OpenXml.Office2021.PowerPoint.Comment.Comment
DocumentFormat.OpenXml.Office2021.PowerPoint.Comment.Comment comment = new DocumentFormat.OpenXml.Office2021.PowerPoint.Comment.Comment(
new SlideMonikerList(
new DocumentMoniker(),
new SlideMoniker()
{
CId = cid,
SldId = slideId.Id,
}),
new TextBodyType(
new BodyProperties(),
new ListStyle(),
new Paragraph(new Run(new DocumentFormat.OpenXml.Drawing.Text(text)))))
{
Id = string.Concat("{", Guid.NewGuid(), "}"),
AuthorId = author.Id,
Created = DateTime.Now,
};
// If the comment list does not exist, add one.
powerPointCommentPart.CommentList ??= new DocumentFormat.OpenXml.Office2021.PowerPoint.Comment.CommentList();
// Add the comment to the comment list
powerPointCommentPart.CommentList.AppendChild(comment);
// Get the presentation extension list if it exists
SlideExtensionList? presentationExtensionList = slidePart.Slide.ChildElements.OfType<SlideExtensionList>().FirstOrDefault();
// Create a boolean that determines if this is the slide's first comment
bool isFirstComment = false;
// If the presentation extension list is null, add one and set this as the first comment for the slide
if (presentationExtensionList is null)
{
isFirstComment = true;
slidePart.Slide.AppendChild(new SlideExtensionList());
presentationExtensionList = slidePart.Slide.ChildElements.OfType<SlideExtensionList>().First();
}
// Get the slide extension if it exists
SlideExtension? presentationExtension = presentationExtensionList.ChildElements.OfType<SlideExtension>().FirstOrDefault();
// If the slide extension is null, add it and set this as a new comment
if (presentationExtension is null)
{
isFirstComment = true;
presentationExtensionList.AddChild(new SlideExtension() { Uri = "{6950BFC3-D8DA-4A85-94F7-54DA5524770B}" });
presentationExtension = presentationExtensionList.ChildElements.OfType<SlideExtension>().First();
}
// If this is the first comment for the slide add the comment relationship
if (isFirstComment)
{
presentationExtension.AddChild(new CommentRelationship()
{ Id = slidePart.GetIdOfPart(powerPointCommentPart) });
}
}
}