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 el SDK de Open XML, la PresentationDocument clase representa un paquete de documento 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 Open método que usa una ruta de acceso de archivo y a 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))
{
Con v3.0.0+ el Close() método se ha quitado en favor de confiar en la instrucción using.
Esto garantiza que se llama automáticamente al Dispose() método cuando se alcanza la llave de cierre. El bloque que sigue a la using
instrucción establece un ámbito para el objeto que se crea o se denomina en la using
instrucción , en este caso themeDocument
y presentationDocument
.
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 sld
elementos , 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]
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/IEC 29500: 2016
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;
}