Compartir a través de


Agregar un comentario a una diapositiva en una presentación

En este tema se muestra cómo usar las clases del SDK de Open XML para Office para agregar un comentario a la primera diapositiva de una presentación mediante programación.

Nota:

Este ejemplo es para comentarios modernos de PowerPoint. En el caso de los comentarios clásicos, vea el ejemplo archivado en GitHub.

Estructura básica de un documento de presentación

La estructura básica de un PresentationML documento consta de varias partes, entre las que se encuentra la parte principal que contiene la definición de presentación. El siguiente texto de la especificación ISO/IEC 29500 presenta la forma general de un PresentationML paquete.

La parte principal de un PresentationML paquete comienza con un elemento raíz de presentación. Dicho elemento contiene una presentación que, a su vez, hace referencia a una lista de diapositivas, a otra de patrones de diapositivas, a otra de patrones de notas y a otra de patrones de documentos. La lista de diapositivas hace referencia a todas las diapositivas de la presentación, la de patrones de diapositivas a todos los patrones de diapositivas que se han usado en la presentación, el patrón de notas contiene información acerca del formato de las páginas de notas y el patrón de documentos describe la apariencia de los documentos.

Un documento es un conjunto impreso de diapositivas que se pueden proporcionar a un público.

Al igual que el texto y los gráficos, cada diapositiva puede incluir comentarios y notas, tener un diseño y formar parte de una o varias presentaciones personalizadas. Un comentario es una anotación dirigida a la persona que se encarga del mantenimiento de las diapositivas de la presentación. Una nota es un aviso o texto dirigido al moderador o al público.

Otras características que un PresentationML documento puede incluir son las siguientes: animación, audio, vídeo y transiciones entre diapositivas .

Un PresentationML documento no se almacena como un cuerpo grande en una sola parte. En su lugar, los elementos que implementan ciertas agrupaciones de funcionalidades se almacenan en partes independientes. Por ejemplo, todos los autores de un documento se almacenan en una parte de autores mientras que cada diapositiva tiene su propia parte.

ISO/IEC 29500: 2016

El siguiente ejemplo de código XML representa una presentación que contiene dos diapositivas denotadas por los identificadores 267 y 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>

Con el SDK de Open XML, puede crear contenido y estructura de documentos mediante clases fuertemente tipadas que corresponden a elementos PresentationML. Puede encontrar estas clases en el espacio de nombres. En la tabla siguiente se enumeran los nombres de clase de las clases correspondientes a los sldelementos , sldLayout, sldMastery notesMaster .

Elemento de PresentationML Open XML SDK (clase) Descripción
<sld/> Slide Diapositiva de presentación. Es el elemento raíz de SlidePart.
<sldLayout/> SlideLayout Diseño de la diapositiva. Es el elemento raíz de SlideLayoutPart.
<sldMaster/> SlideMaster Patrón de diapositivas. Es el elemento raíz de SlideMasterPart.
<notesMaster/> NotesMaster Patrón de notas (o handoutMaster). Es el elemento raíz de NotesMasterPart.

Estructura del elemento de comentario moderno

El siguiente elemento XML especifica un único comentario. Contiene el texto del comentario (t) y los atributos que hacen referencia a su autor (authorId), la fecha y hora de creación (created) y el identificador de comentario (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>

En las tablas siguientes se enumeran las definiciones de los posibles elementos secundarios y atributos del cm elemento (comentario). Para obtener la definición completa, consulte MS-PPTX 2.16.3.3 CT_Comment

Atributo Definición
id Especifica el identificador de un comentario o una respuesta de comentario.
authorId Especifica el identificador de autor de un comentario o una respuesta de comentario.
status Especifica el estado de un comentario o una respuesta de comentario.
creado Especifica la fecha y hora en que se crea la respuesta de comentario o comentario.
startDate Especifica la fecha de inicio del comentario.
dueDate Especifica la fecha de vencimiento del comentario.
assignedTo Especifica una lista de autores a los que se asigna el comentario.
complete Especifica el porcentaje de finalización del comentario.
title Especifica el título de un comentario.
Elemento secundario Definición
pc:sldMkLst Especifica un moniker de contenido que identifica la diapositiva a la que está delimitado el comentario.
ac:deMkLst Especifica un moniker de contenido que identifica el elemento de dibujo al que está delimitado el comentario.
ac:txMkLst Especifica un moniker de contenido que identifica el intervalo de caracteres de texto al que está delimitado el comentario.
unknownAnchor Especifica un delimitador desconocido al que está delimitado el comentario.
Pos Especifica la posición del comentario, en relación con la esquina superior izquierda del primer objeto al que está delimitado el comentario.
replyLst Especifica la lista de respuestas al comentario.
txBody Especifica el texto de un comentario o una respuesta de comentario.
extLst Especifica una lista de extensiones para un comentario o una respuesta de comentario.

En el siguiente ejemplo de esquema XML se definen los miembros del cm elemento además de los atributos obligatorios y opcionales.

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

Funcionamiento del código de ejemplo

El código de ejemplo abre el documento de presentación en la instrucción using. Posteriormente, crea una instancia de CommentAuthorsPart y comprueba si existe una parte de autores de comentarios. Si no es así, agrega uno.

// create missing PowerPointAuthorsPart if it is null
if (presentationDocument.PresentationPart.authorsPart is null)
{
    presentationDocument.PresentationPart.AddNewPart<PowerPointAuthorsPart>();
}

El código determina si hay una parte de autor de PowerPoint existente en la parte de presentación; Si no es así, agrega uno y, a continuación, comprueba si hay una lista de autores y agrega uno si falta. También comprueba que el autor que se pasa está en la lista de autores existentes; Si es así, asigna el identificador de autor existente. Si no es así, agrega un nuevo autor a la lista de autores y asigna un identificador de autor y los valores de parámetro.

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

A continuación, el código determina si hay un identificador de diapositiva y devuelve si no existe uno.

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

En el segmento siguiente, el código obtiene el identificador de relación. Si existe, se usa para buscar la parte de diapositiva; de lo contrario, se toma la primera diapositiva de las partes enumerables de las diapositivas. A continuación, comprueba que hay una parte de comentarios de PowerPoint para la diapositiva y, si no, agrega una.

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

Debajo del código se crea un nuevo comentario moderno y, a continuación, se agrega una lista de comentarios a la parte de comentario de PowerPoint si no existe y se agrega el comentario a esa lista de comentarios.

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

Con los comentarios modernos, la diapositiva debe tener la lista de extensiones y la extensión correctas. El código siguiente determina si la diapositiva ya tiene slideExtensionList y SlideExtension y los agrega a la diapositiva si no están 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 ejemplo

A continuación se muestra el ejemplo de código completo que muestra cómo agregar un nuevo comentario con un autor nuevo o existente a una diapositiva con o sin comentarios existentes.

Nota:

[!NOTA] Para obtener el nombre y las iniciales exactas del autor, abra el archivo de presentación, haga clic en el elemento de menú Archivo y, a continuación, haga clic en Opciones. Se abre la ventana PowerPointOptions y se muestra el contenido de la pestaña General . El nombre y las iniciales del autor deben coincidir con el Nombre de usuario y las Iniciales de esta ficha.

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

Recursos adicionales