Share via


Mover un párrafo de una presentación a otra

En este tema se muestra cómo usar las clases del SDK de Open XML para Office para mover un párrafo de una presentación a otra mediante programación.

Obtención de un objeto PresentationDocument

En Open XML SDK, la clase PresentationDocument representa un paquete de documentos de presentación. Para trabajar con un documento de presentación, debe crear primero una instancia de la clase PresentationDocument y, a continuación, trabajar con esa instancia. Para crear la instancia de clase a partir del documento, llame al método Open(String, Boolean) que usa una ruta de acceso de archivo y un valor booleano como segundo parámetro para especificar si un documento es editable. Para abrir un documento en modo de lectura y escritura, especifique el valor true para este parámetro, como se muestra en la siguiente instrucción using. En este código, el parámetro file es una cadena que representa la ruta de acceso del archivo a partir del cual desea abrir el documento.

    using (PresentationDocument doc = PresentationDocument.Open(file, true))
    {
        // Insert other code here.
    }

La instrucción using proporciona una alternativa recomendada a la típica secuencia .Open, .Save, .Close. Garantiza que se llamará automáticamente al método Dispose (un método interno que Open XML SDK usa para limpiar recursos) cuando se llegue a la llave de cierre. El bloque que sigue a la instrucción using establece un ámbito para el objeto que se crea o se nombra en la instrucción using, en este caso doc.

Estructura básica de un documento de presentación

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

La parte principal de un paquete PresentationML 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 documento PresentationML puede incluir son las siguientes: animación, audio, vídeo y transiciones entre diapositivas .

Los documentos PresentationML no se almacenan como un gran cuerpo en una sola parte. En su lugar, los elementos que implementan ciertas agrupaciones de funcionalidades se almacenan en partes independientes. Por ejemplo, todos los comentarios de un documento se almacenan en una parte de comentarios mientras que cada diapositiva cuenta con su propia parte.

© ISO/IEC29500: 2008.

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 DocumentFormat.OpenXml.Presentation . En la tabla siguiente se enumeran los nombres de las clases que corresponden a los elementos sld, sldLayout, sldMaster y notesMaster.

Elemento de PresentationML Open XML SDK (clase) Descripción
Sld Diapositiva 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 de ShapeTextBody

El siguiente texto de la especificación ISO/IEC 29500 presenta la estructura de este elemento.

Este elemento especifica la existencia de texto incluido dentro de la forma correspondiente. Todo el texto visible y las propiedades relacionadas con este se incluyen en este elemento. Puede haber varios párrafos y, en su interior, varios segmentos de texto.

© ISO/IEC29500: 2008.

En la siguiente tabla se enumeran los elementos secundarios del cuerpo del texto de la forma y la descripción de cada uno de ellos.

Elemento secundario Descripción
bodyPr Propiedades del cuerpo
lstStyle Estilos de lista de textos
p Párrafos de texto

El siguiente fragmento de esquema XML define el contenido de este elemento:

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

Funcionamiento del código de ejemplo

El código de este tema consta de dos métodos, MoveParagraphToPresentation y GetFirstSlide. El primer método toma dos parámetros de cadena: uno que representa el archivo de origen, que contiene el párrafo que se va a mover, y otro que representa el archivo de destino, al que se mueve el párrafo. El método abre ambos archivos de presentación y, a continuación, llama al método GetFirstSlide para obtener la primera diapositiva de cada archivo. A continuación, obtiene la primera forma TextBody de cada diapositiva y el primer párrafo de la forma de origen. Realiza un clon profundo del párrafo de origen, copiando no solo el propio objeto Paragraph de origen, sino también todo lo contenido en ese objeto, incluido su texto. A continuación, inserta el párrafo clonado en el archivo de destino y quita el párrafo de origen del archivo de origen y lo reemplaza por un párrafo de marcador de posición. Por último, guarda las diapositivas modificadas en ambas presentaciones.

    // Moves a paragraph range in a TextBody shape in the source document
    // to another TextBody shape in the target document.
    public static void MoveParagraphToPresentation(string sourceFile, string targetFile)
    {
        // Open the source file as read/write.
        using (PresentationDocument sourceDoc = PresentationDocument.Open(sourceFile, true))
        {
            // 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<Drawing.Paragraph>(p1);

                // Replace the removed paragraph with a placeholder.
                textBody1.AppendChild<Drawing.Paragraph>(new Drawing.Paragraph());

                // Save the slide in the source file.
                slide1.Slide.Save();

                // Save the slide in the target file.
                slide2.Slide.Save();
            }
        }
    }

El método GetFirstSlide toma el objeto PresentationDocument que se ha pasado, obtiene la parte de presentación y, luego, consigue el identificador de la primera diapositiva en la lista de diapositivas. A continuación, obtiene el identificador de relación de la diapositiva, obtiene la parte de diapositiva del identificador de relación y devuelve la parte de diapositiva al método de llamada.

    // Get the slide part of the first slide in the presentation document.
    public static SlidePart GetFirstSlide(PresentationDocument presentationDocument)
    {
        // Get relationship ID of the first slide
        PresentationPart part = presentationDocument.PresentationPart;
        SlideId slideId = part.Presentation.SlideIdList.GetFirstChild<SlideId>();
        string relId = slideId.RelationshipId;

        // Get the slide part by the relationship ID.
        SlidePart slidePart = (SlidePart)part.GetPartById(relId);

        return slidePart;
    }

Código de ejemplo

Mediante este código de ejemplo, puede mover un párrafo de una presentación a otra. En el programa, puede usar la siguiente llamada al método MoveParagraphToPresentation para mover el primer párrafo del archivo de presentación "Myppt4.pptx" al archivo de presentación "Myppt12.pptx".

    string sourceFile = @"C:\Users\Public\Documents\Myppt4.pptx";
    string targetFile = @"C:\Users\Public\Documents\Myppt12.pptx";
    MoveParagraphToPresentation(sourceFile, targetFile);

Una vez ejecutado el programa, observe el contenido de los archivos de origen y de destino para ver el párrafo que se ha movido.

A continuación se incluye el código de ejemplo completo en C# y Visual Basic.


using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Presentation;
using System;
using System.Linq;
using Drawing = DocumentFormat.OpenXml.Drawing;

MoveParagraphToPresentation(args[0], args[1]);

// 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))
    // 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<Drawing.Paragraph>(p1);

        // Replace the removed paragraph with a placeholder.
        textBody1.AppendChild<Drawing.Paragraph>(new Drawing.Paragraph());

        // Save the slide in the source file.
        slide1.Slide.Save();

        // Save the slide in the target file.
        slide2.Slide.Save();
    }
}

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