Share via


Aplicar un tema a una presentación

En este tema se muestra cómo usar las clases del SDK de Open XML para Office para aplicar el tema 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 con acceso de solo lectura, especifique el valor false para este parámetro. Para abrir un documento con acceso de lectura y escritura, especifique el valor true para este parámetro. En la siguiente instrucción using, se abren dos archivos de presentación: la presentación de destino, a la que se va a aplicar un tema; y una presentación de origen, a la que ya se ha aplicado el tema. El archivo de presentación de origen se abre con acceso de solo lectura y el archivo de presentación de destino se abre con acceso de lectura y escritura. En este código, el parámetro themePresentation es una cadena que representa la ruta de acceso del documento de presentación de origen y el parámetro presentationFile es una cadena que representa la ruta de acceso del documento de presentación de destino.

using (PresentationDocument themeDocument = PresentationDocument.Open(themePresentation, false))
using (PresentationDocument presentationDocument = PresentationDocument.Open(presentationFile, true))
{

La instrucción using proporciona una alternativa recomendada a la típica secuencia .Open, .Save, .Close. Asegura que se llamará automáticamente al método Dispose (método interno usado por Open XML SDK 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 themeDocument y presentationDocument.


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

La siguiente información de la especificación ISO/IEC 29500 puede ser útil al trabajar con este elemento.

Este elemento define el tipo complejo de nivel raíz asociado a una hoja de estilos compartida (o tema). Este elemento contiene todas las distintas opciones de formato disponibles para un documento a través de un tema y define la apariencia general del documento cuando se usan objetos con tema en el documento. [Ejemplo: considere la siguiente imagen como un ejemplo de diferentes temas en uso aplicados a una presentación. En este ejemplo, puede ver cómo un tema afecta a la fuente, los colores, los fondos, los rellenos y los efectos de los distintos objetos de una presentación. Fin del ejemplo]

Ejemplo de tema

En este ejemplo, se observa cómo puede afectar un tema a la fuente, los colores, los fondos, los rellenos y los efectos de distintos objetos de una presentación. Fin del ejemplo]

© ISO/IEC29500: 2008.

En la siguiente tabla se enumeran los posibles tipos secundarios de la clase Theme.

Elemento de PresentationML Open XML SDK (clase) Descripción
custClrLst CustomColorList Lista de colores personalizados
extLst ExtensionList Lista de extensiones
extraClrSchemeLst ExtraColorSchemeList Lista de combinaciones de colores adicionales
objectDefaults ObjectDefaults Valores predeterminados de objeto
themeElements ThemeElements Elementos theme

El siguiente fragmento de esquema XML define las cuatro partes del elemento theme. El elemento themeElements es la parte que contiene el formato principal definido en el tema. Las demás partes proporcionan invalidaciones, valores predeterminados y adiciones a la información de themeElements. El tipo complejo que define un tema, CT_OfficeStyleSheet, se define de la siguiente manera:

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

Este tipo complejo también incluye CT_OfficeArtExtensionList, que se usa para la extensibilidad futura de este tipo complejo.


Funcionamiento del código de ejemplo

El código de ejemplo consta de dos sobrecargas de los métodos ApplyThemeToPresentation y GetSlideLayoutType. El segmento de código siguiente muestra el primer método sobrecargado, en el que los dos archivos de presentación, themePresentation y presentationFile, se abren y se pasan al segundo método sobrecargado como parámetros.

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

En el segundo método sobrecargado, el código comienza comprobando si alguno de los archivos de presentación está vacío, en cuyo caso, genera una excepción. A continuación, declara un objeto PresentationPart y lo establece igual que la parte de presentación del objeto PresentationDocument de destino que se pasó para obtener la parte de presentación del documento de presentación. Después, obtiene las partes de patrón de diapositivas de las partes de presentación de los dos objetos que se pasaron y obtiene el identificador de relación de la parte de patrón de diapositivas de la presentación de destino.

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

A continuación, el código quita la parte de tema existente y la parte de patrón de diapositivas de la presentación de destino. Al volver a usar el identificador de relación anterior, agrega la nueva parte de patrón de diapositivas de la presentación de origen a la presentación de destino. También agrega la parte de tema a la presentación de destino.

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

El código recorre en iteración todos los elementos de diseño de diapositivas de la parte maestra de diapositivas y los agrega a la lista de nuevos diseños de diapositivas. Especifica el tipo de diseño predeterminado. En este ejemplo, el código del tipo de diseño predeterminado es "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";

El código procesa una iteración en todas las partes de diapositiva de la presentación de destino y quita la relación de diseño de diapositivas de todas las diapositivas. Usa el método GetSlideLayoutType para encontrar el tipo de diseño de la parte de diseño de diapositiva. Para cualquier diapositiva con una parte de diseño de diapositiva existente, agrega una nueva parte de diseño de diapositiva del mismo tipo que tenía anteriormente. Para cualquier diapositiva sin una parte de diseño de diapositiva existente, agrega una nueva parte de diseño de diapositiva del tipo predeterminado.

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

Para obtener el tipo de diseño de la diapositiva, el código usa el método GetSlideLayoutType que toma la parte de diseño de diapositivas como un parámetro y devuelve al segundo método ApplyThemeToPresentation sobrecargado una cadena que representa el nombre del tipo de diseño de la diapositiva

// Get the slide layout type.
static string? GetSlideLayoutType(SlideLayoutPart slideLayoutPart)
{
    CommonSlideData? slideData = slideLayoutPart.SlideLayout?.CommonSlideData;

    return slideData?.Name;
}

Código de ejemplo

A continuación se proporciona el código de ejemplo completo para copiar un tema de una presentación a otra. Para usar el programa debe crear dos presentaciones: una presentación de origen con el tema que desea copiar, como por ejemplo, Myppt9-theme.pptx; y una presentación de destino, como por ejemplo, Myppt9.pptx. Puede usar la siguiente llamada en su programa para realizar la copia.

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

Después de realizar la llamada, puede inspeccionar el archivo Myppt2.pptx; verá el mismo tema del archivo 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;
}

Consulte también