Aplicar un estilo a un párrafo de un documento de procesamiento de texto
En este tema se muestra cómo usar las clases del SDK de Open XML para Office para aplicar mediante programación un estilo a un párrafo dentro de un documento de procesamiento de texto. Contiene un método ApplyStyleToParagraph de ejemplo para ilustrar esta tarea, además de varios métodos de ejemplo adicionales para comprobar si existe un estilo, agregar un nuevo estilo y agregar la parte styles.
ApplyStyleToParagraph (método)
El método de ejemplo ApplyStyleToParagraph se puede usar para aplicar un estilo a un párrafo. Primero debe obtener una referencia al documento así como una referencia al párrafo al que desee asignar un estilo. El método acepta cuatro parámetros que indican: la referencia al documento de procesamiento de texto abierto, el styleid del estilo que desea aplicar, el nombre del estilo que desea aplicar y la referencia al párrafo al que desea aplicar el estilo.
public static void ApplyStyleToParagraph(WordprocessingDocument doc, string styleid, string stylename, Paragraph p)
En las siguientes secciones de este tema se explica la implementación de este método y el código de apoyo, así como el modo de llamarlo. Encontrará el listado completo de códigos en la sección Código de ejemplo al final de este tema.
Obtención de un objeto WordprocessingDocument
La sección Código de ejemplo también muestra el código que se debe configurar para llamar al método de ejemplo. Para usar el método para aplicar un estilo a un párrafo de un documento, primero necesita una referencia para abrir el documento. En Open XML SDK, la clase WordprocessingDocument representa un paquete de documentos de Word. Para abrir y trabajar con un documento de Word, cree una instancia de la clase WordprocessingDocument desde el documento. Después de crear la instancia, úsela para obtener acceso al elemento de documento principal que contiene el texto del documento. El contenido de la parte del documento principal se representa en el paquete como XML mediante marcado WordprocessingML.
Para crear la instancia de clase, llame a una de las sobrecargas del método Open(). El siguiente código de ejemplo muestra cómo usar la sobrecarga WordprocessingDocument.Open. El primer parámetro tiene una cadena que representa la ruta de acceso completa al documento para abrir. El segundo parámetro toma un valor de true o false y representa si se debe abrir el archivo para su edición. En este ejemplo el parámetro es true para permitir el acceso de lectura/escritura al archivo.
using (WordprocessingDocument doc =
WordprocessingDocument.Open(fileName, true))
{
// Code removed here.
}
Estructura de un documento WordProcessingML
La estructura de documento básica de un documento WordProcessingML contiene los elementos document y body, seguidos de uno o varios elementos a nivel de bloque, como p, que representa un párrafo. Un párrafo contiene uno o varios elementos r. La r representa a run (segmento), que es una región de texto con un conjunto de propiedades comunes, como el formato. Un segmento contiene uno o varios elementos t. El elemento t contiene un intervalo de texto. En el siguiente ejemplo de código se muestra el marcado WordprocessingML de un documento que contiene el texto "Example text".
<w:document xmlns:w="https://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body>
<w:p>
<w:r>
<w:t>Example text.</w:t>
</w:r>
</w:p>
</w:body>
</w:document>
Con el SDK de Open XML, puede crear contenido y estructura de documentos mediante clases fuertemente tipadas que corresponden a elementos WordprocessingML . Puede encontrar estas clases en el espacio de nombres DocumentFormat.OpenXml.Wordprocessing . La tabla siguiente muestra los nombres de las clases que corresponden a los elementos document, body, p, r y t.
Elemento de WordprocessingML | Open XML SDK (clase) | Descripción |
---|---|---|
documento | Document | El elemento raíz del elemento de documento principal. |
body | Body | El contenedor de las estructuras a nivel de bloque, como párrafos, tablas, anotaciones y otras recogidas en la especificación ISO/IEC 29500. |
p | Paragraph | Un párrafo. |
r | Run | Un segmento. |
t | Text | Un intervalo de texto. |
Para obtener más información sobre la estructura general de los elementos y elementos de un documento WordprocessingML, vea Estructura de un documento WordprocessingML.
Obtener el estilo del párrafo
Tras abrir el archivo, el código de ejemplo recupera una referencia al primer párrafo. Puesto que el cuerpo de un documento típico de procesamiento de texto contiene muchos tipos de elementos, el código filtra los descendientes del cuerpo del documento a los del tipo Paragraph. A continuación, el método ElementAtOrDefault se emplea para recuperar una referencia al párrafo. Puesto que los elementos están indizados para que empiecen de cero, debe pasar un cero para recuperar la referencia al primer párrafo, tal como se muestra en el siguiente ejemplo de código.
// Get the first paragraph.
Paragraph p =
doc.MainDocumentPart.Document.Body.Descendants<Paragraph>()
.ElementAtOrDefault(0);
// Check for a null reference.
if (p == null)
{
// Code removed here…
}
La referencia al párrafo encontrado se almacena en una variable llamada p. Si un párrafo no se encuentra en el índice especificado, el método ElementAtOrDefault devuelve null como valor predeterminado. Esto proporciona una oportunidad para probar el valor null y lanzar un error con un mensaje de error apropiado.
Una vez que tenga las referencias al documento y el párrafo, puede llamar al método de ejemplo ApplyStyleToParagraph para realizar el trabajo restante. Para llamar al método, debe pasar la referencia al documento como primer parámetro, el styleid del estilo que desee aplicar como segundo parámetro, el nombre del estilo como tercer parámetro y la referencia al párrafo al que desee aplicar el estilo como cuarto parámetro.
Agregar el elemento de propiedades de párrafo
El primer paso del método de ejemplo es asegurarse de que el párrafo tenga un elemento propiedades del párrafo. El elemento propiedades del párrafo es un elemento secundario del párrafo e incluye un conjunto de propiedades que le permiten especificar el formato del párrafo.
La siguiente información de la especificación ISO/IEC 29500 presenta el elemento pPr (propiedades del párrafo) que se usa para especificar el formato de un párrafo. Tenga en cuenta que los números de sección precedidos de § son de la especificación ISO.
Dentro del párrafo, todo el formato enriquecido del nivel del párrafo se almacena en el elemento pPr (§17.3.1.25; §17.3.1.26). [Nota: algunos ejemplos de propiedades del párrafo son alineación, borde, invalidación de guiones, sangría, interlineado, sombreado, dirección del texto y control viudo/huérfano.
Entre las propiedades se encuentra el elemento pStyle para especificar el estilo que se debe aplicar al párrafo. Por ejemplo, el siguiente marcado de ejemplo muestra un elemento pStyle que especifica el estilo "OverdueAmount".
<w:p xmlns:w="https://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:pPr>
<w:pStyle w:val="OverdueAmount" />
</w:pPr>
...
</w:p>
En SDK de Open XML, el elemento pPr se representa mediante la clase ParagraphProperties. El código determina si existe el elemento y crea una nueva instancia de la clase ParagraphProperties si no lo hace. El elemento pPr es un elemento secundario del elemento p (paragraph); por lo tanto, el método PrependChild<T>(T) se usa para agregar la instancia, como se muestra en el siguiente ejemplo de código.
// If the paragraph has no ParagraphProperties object, create one.
if (p.Elements<ParagraphProperties>().Count() == 0)
{
p.PrependChild<ParagraphProperties>(new ParagraphProperties());
}
// Get the paragraph properties element of the paragraph.
ParagraphProperties pPr = p.Elements<ParagraphProperties>().First();
Agregar la parte Estilos
Una vez encontrado el párrafo y teniendo el elemento propiedades del párrafo, asegúrese de cumplir con los requisitos previos para aplicar el estilo. Los estilos en WordprocessingML se almacenan en su propia parte única. Aunque generalmente es cierto que la parte así como un conjunto de estilos base se crean automáticamente al crear el documento mediante una aplicación como Microsoft Word, la parte styles no es necesaria para que un documento se considere válido. Si crea el documento mediante programación usando el SDK de Open XML, la parte styles no se crea automáticamente; debe crearla explícitamente. Por lo tanto, el siguiente código comprueba que existe la parte styles y la crea si no existe.
// Get the Styles part for this document.
StyleDefinitionsPart part =
doc.MainDocumentPart.StyleDefinitionsPart;
// If the Styles part does not exist, add it and then add the style.
if (part == null)
{
part = AddStylesPartToPackage(doc);
// Code removed here...
}
El método de ejemplo AddStylesPartToPackage realiza la tarea de agregar la parte styles. Crea una parte del tipo StyleDefinitionsPart y la agrega como elemento secundario a la parte del documento principal. El código se anexa al elemento raíz Styles, que es el elemento principal que contiene todos los estilos. El elemento Styles se representa por la clase Styles en el SDK de Open XML. Finalmente, el código guarda la parte.
// Add a StylesDefinitionsPart to the document. Returns a reference to it.
public static StyleDefinitionsPart AddStylesPartToPackage(WordprocessingDocument doc)
{
StyleDefinitionsPart part;
part = doc.MainDocumentPart.AddNewPart<StyleDefinitionsPart>();
Styles root = new Styles();
root.Save(part);
return part;
}
Comprobar que el estilo existe
La aplicación de un estilo que no existe a un párrafo no tiene ningún efecto; no se genera ninguna excepción y no se produce ningún cambio de formato. El código de ejemplo comprueba que el estilo exista antes de intentar aplicarlo. Los estilos se almacenan en la parte styles, por lo que si la parte styles no existe, el estilo en si no puede existir.
Si la parte styles existe, el código comprueba si hay un estilo que coincida llamando al método de ejemplo IsStyleIdInDocument y pasando el documento y el styleid. Si no se encuentra ninguna coincidencia en styleid, el código intenta buscar el styleid llamando al método de ejemplo GetStyleIdFromStyleName y pasándole el nombre de estilo.
Si el estilo no existe, porque la parte styles no existía o bien porque la parte styles existe pero el estilo no existe, el código llama al método de ejemplo AddNewStyle para agregar el estilo.
// Get the Styles part for this document.
StyleDefinitionsPart part =
doc.MainDocumentPart.StyleDefinitionsPart;
// If the Styles part does not exist, add it and then add the style.
if (part == null)
{
part = AddStylesPartToPackage(doc);
AddNewStyle(part, styleid, stylename);
}
else
{
// If the style is not in the document, add it.
if (IsStyleIdInDocument(doc, styleid) != true)
{
// No match on styleid, so let's try style name.
string styleidFromName = GetStyleIdFromStyleName(doc, stylename);
if (styleidFromName == null)
{
AddNewStyle(part, styleid, stylename);
}
else
styleid = styleidFromName;
}
}
Dentro del método de ejemplo IsStyleInDocument, el trabajo empieza recuperando el elemento Styles mediante la propiedad Styles de StyleDefinitionsPart de la parte del documento principal y, a continuación, determinando si existe algún estilo como elemento secundario de dicho elemento. Todos los elementos de estilo se almacenan como elementos secundarios del elemento styles.
Si los estilos no existen, el código busca una coincidencia en el styleid. El styleid es un atributo del estilo que se usa en muchos sitios del documento para referirse al estilo y puede considerarse como su identificador principal. Generalmente se usa el styleid para identificar un estilo en código. El método FirstOrDefault tiene el valor predeterminado null si no se encuentra ninguna coincidencia, por lo que el código comprueba el valor null para ver si coincide un estilo, tal como se muestra en la cita siguiente.
// Return true if the style id is in the document, false otherwise.
public static bool IsStyleIdInDocument(WordprocessingDocument doc,
string styleid)
{
// Get access to the Styles element for this document.
Styles s = doc.MainDocumentPart.StyleDefinitionsPart.Styles;
// Check that there are styles and how many.
int n = s.Elements<Style>().Count();
if (n==0)
return false;
// Look for a match on styleid.
Style style = s.Elements<Style>()
.Where(st => (st.StyleId == styleid) && (st.Type == StyleValues.Paragraph))
.FirstOrDefault();
if (style == null)
return false;
return true;
}
Cuando no se encuentra el estilo basándose en el styleid, el código intenta encontrar una coincidencia basándose en el nombre del estilo. El método de ejemplo GetStyleIdFromStyleName realiza esta tarea, buscando una coincidencia en un nombre de estilo y devolviendo el styleid para el elemento coincidente si se encuentra, o el valor null si no se encuentra.
Agregar el estilo a la parte styles
El método de ejemplo AddNewStyle toma tres parámetros. El primer parámetro toma una referencia a la parte styles. El segundo parámetro toma el styleid del estilo y el tercer parámetro toma el nombre del estilo. El código AddNewStyle crea la definición del estilo con nombre dentro de la parte especificada.
Para crear el estilo, el código crea una instancia de la clase Style y configura determinadas propiedades, como el Tipo de estilo (párrafo) y elStyleId. Como se ha mencionado anteriormente, el documento usa el styleid para referirse al estilo y se puede considerar como el identificador primario. Generalmente se usa el styleid para identificar un estilo en código. Un estilo también puede tener un nombre de estilo descriptivo e independiente para que aparezca en la interfaz de usuario. A menudo, por lo tanto, el nombre de estilo aparece en mayúsculas o minúsculas, según corresponda, con espacio (por ejemplo, Encabezado 1), mientras que el styleid es más sucinto (por ejemplo, encabezado1) y pensado para uso interno. En el siguiente código de ejemplo, el styleid y el nombre de estilo toman sus valores a partir de los parámetros styleid y stylename.
El siguiente paso es especificar unas cuantas propiedades adicionales, como el estilo en el que se basa el nuevo estilo y el estilo que se debe aplicar automáticamente al siguiente párrafo. El código especifica ambos como estilo "Normal". Tenga en cuenta que el valor que se debe especificar aquí es el styleid del estilo normal. El código anexa estas propiedades como elementos secundarios del elemento de estilo.
Una vez que el código haya terminado de crear la instancia del estilo y configurar las propiedades básicas, trabaje sobre el formato del estilo. El formato del estilo se realiza en los elementos propiedades del párrafo (pPr) y propiedades del segmento (rPr). Para establecer las características de fuente y color para los segmentos de un párrafo, use las propiedades del segmento.
Para crear el elemento rPr con los elementos secundarios adecuados, el código crea una instancia de la clase StyleRunProperties y, a continuación, agrega instancias de las clases de propiedad correspondiente. Para este ejemplo de código, el estilo especifica la fuente Lucida Console, un tamaño de fuente de 12, presentado en negrita y cursiva, con el color de énfasis 2 de la parte del tema de documento. El tamaño se especifica en medios puntos, de modo que un valor 24 es equivalente a 12 puntos.
Una vez completada la definición de estilo, el código anexa el estilo o el elemento styles a la parte styles, como se muestra en el siguiente ejemplo de código.
// Create a new style with the specified styleid and stylename and add it to the specified
// style definitions part.
private static void AddNewStyle(StyleDefinitionsPart styleDefinitionsPart,
string styleid, string stylename)
{
// Get access to the root element of the styles part.
Styles styles = styleDefinitionsPart.Styles;
// Create a new paragraph style and specify some of the properties.
Style style = new Style() { Type = StyleValues.Paragraph,
StyleId = styleid,
CustomStyle = true };
StyleName styleName1 = new StyleName() { Val = stylename };
BasedOn basedOn1 = new BasedOn() { Val = "Normal" };
NextParagraphStyle nextParagraphStyle1 = new NextParagraphStyle() { Val = "Normal" };
style.Append(styleName1);
style.Append(basedOn1);
style.Append(nextParagraphStyle1);
// Create the StyleRunProperties object and specify some of the run properties.
StyleRunProperties styleRunProperties1 = new StyleRunProperties();
Bold bold1 = new Bold();
Color color1 = new Color() { ThemeColor = ThemeColorValues.Accent2 };
RunFonts font1 = new RunFonts() { Ascii = "Lucida Console" };
Italic italic1 = new Italic();
// Specify a 12 point size.
FontSize fontSize1 = new FontSize() { Val = "24" };
styleRunProperties1.Append(bold1);
styleRunProperties1.Append(color1);
styleRunProperties1.Append(font1);
styleRunProperties1.Append(fontSize1);
styleRunProperties1.Append(italic1);
// Add the run properties to the style.
style.Append(styleRunProperties1);
// Add the style to the styles part.
styles.Append(style);
}
Aplicar el estilo al párrafo
Ahora, el código de ejemplo ha localizado el párrafo, ha agregado el elemento propiedades del párrafo si era necesario, ha buscado el elemento styles y lo ha agregado si era necesario, y ha buscado el estilo y lo ha agregado si era necesario. Ahora, establezca el estilo del párrafo. Para ello, el código crea una instancia de la clase ParagraphStyleId con el styleid y, a continuación, coloca una referencia a esa instancia en la propiedad ParagraphStyleId del objeto propiedades del párrafo. De este modo se crea y se asigna el valor apropiado para el elemento pStyle que especifica el estilo que se debe usar para el párrafo.
// Set the style of the paragraph.
pPr.ParagraphStyleId = new ParagraphStyleId(){Val = styleid};
Código de ejemplo
Puede usar el método de ejemplo ApplyStyleToParagraph para aplicar un estilo con nombre a un párrafo en un documento de procesamiento de texto mediante el SDK de Open XML. El siguiente código muestra cómo abrir y adquirir una referencia a un documento de procesamiento de texto, recuperar una referencia al primer párrafo y, a continuación, llamar al método ApplyStyleToParagraph.
Para llamar al método, pase la referencia al documento como primer parámetro, el styleid del estilo que desee aplicar como segundo parámetro, el nombre del estilo como tercer parámetro y la referencia al párrafo al que desee aplicar el estilo como cuarto parámetro. Por ejemplo, el siguiente código se aplica al estilo "Overdue Amount" para el primer párrafo en el archivo de ejemplo llamado ApplyStyleToParagraph.docx.
string filename = @"C:\Users\Public\Documents\ApplyStyleToParagraph.docx";
using (WordprocessingDocument doc =
WordprocessingDocument.Open(filename, true))
{
// Get the first paragraph.
Paragraph p =
doc.MainDocumentPart.Document.Body.Descendants<Paragraph>()
.ElementAtOrDefault(1);
// Check for a null reference.
if (p == null)
{
throw new ArgumentOutOfRangeException("p",
"Paragraph was not found.");
}
ApplyStyleToParagraph(doc, "OverdueAmount", "Overdue Amount", p);
}
El siguiente es el ejemplo de código completo en C# y Visual Basic.
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System;
using System.Linq;
// Apply a style to a paragraph.
static void ApplyStyleToParagraph(WordprocessingDocument doc, string styleid, string stylename, Paragraph p)
{
if (doc is null)
{
throw new ArgumentNullException(nameof(doc));
}
// If the paragraph has no ParagraphProperties object, create one.
if (p.Elements<ParagraphProperties>().Count() == 0)
{
p.PrependChild<ParagraphProperties>(new ParagraphProperties());
}
// Get the paragraph properties element of the paragraph.
ParagraphProperties pPr = p.Elements<ParagraphProperties>().First();
// Get the Styles part for this document.
StyleDefinitionsPart? part = doc.MainDocumentPart?.StyleDefinitionsPart;
// If the Styles part does not exist, add it and then add the style.
if (part is null)
{
part = AddStylesPartToPackage(doc);
AddNewStyle(part, styleid, stylename);
}
else
{
// If the style is not in the document, add it.
if (IsStyleIdInDocument(doc, styleid) != true)
{
// No match on styleid, so let's try style name.
string? styleidFromName = GetStyleIdFromStyleName(doc, stylename);
if (styleidFromName is null)
{
AddNewStyle(part, styleid, stylename);
}
else
styleid = styleidFromName;
}
}
// Set the style of the paragraph.
pPr.ParagraphStyleId = new ParagraphStyleId() { Val = styleid };
}
// Return true if the style id is in the document, false otherwise.
static bool IsStyleIdInDocument(WordprocessingDocument doc, string styleid)
{
// Get access to the Styles element for this document.
Styles? s = doc.MainDocumentPart?.StyleDefinitionsPart?.Styles;
if (s is null)
{
return false;
}
// Check that there are styles and how many.
int n = s.Elements<Style>().Count();
if (n == 0)
{
return false;
}
// Look for a match on styleid.
Style? style = s.Elements<Style>()
.Where(st => (st.StyleId is not null && st.StyleId == styleid) && (st.Type is not null && st.Type == StyleValues.Paragraph))
.FirstOrDefault();
if (style is null)
{
return false;
}
return true;
}
// Return styleid that matches the styleName, or null when there's no match.
static string? GetStyleIdFromStyleName(WordprocessingDocument doc, string styleName)
{
StyleDefinitionsPart? stylePart = doc.MainDocumentPart?.StyleDefinitionsPart;
string? styleId = stylePart?.Styles?.Descendants<StyleName>()
.Where(s =>
{
OpenXmlElement? p = s.Parent;
EnumValue<StyleValues>? styleValue = p is null ? null : ((Style)p).Type;
return s.Val is not null && s.Val.Value is not null && s.Val.Value.Equals(styleName) &&
(styleValue is not null && styleValue == StyleValues.Paragraph);
})
.Select(n =>
{
OpenXmlElement? p = n.Parent;
return p is null ? null : ((Style)p).StyleId;
}).FirstOrDefault();
return styleId;
}
// Create a new style with the specified styleid and stylename and add it to the specified
// style definitions part.
static void AddNewStyle(StyleDefinitionsPart styleDefinitionsPart, string styleid, string stylename)
{
// Get access to the root element of the styles part.
styleDefinitionsPart.Styles ??= new Styles();
Styles styles = styleDefinitionsPart.Styles;
// Create a new paragraph style and specify some of the properties.
Style style = new Style()
{
Type = StyleValues.Paragraph,
StyleId = styleid,
CustomStyle = true
};
StyleName styleName1 = new StyleName() { Val = stylename };
BasedOn basedOn1 = new BasedOn() { Val = "Normal" };
NextParagraphStyle nextParagraphStyle1 = new NextParagraphStyle() { Val = "Normal" };
style.Append(styleName1);
style.Append(basedOn1);
style.Append(nextParagraphStyle1);
// Create the StyleRunProperties object and specify some of the run properties.
StyleRunProperties styleRunProperties1 = new StyleRunProperties();
Bold bold1 = new Bold();
Color color1 = new Color() { ThemeColor = ThemeColorValues.Accent2 };
RunFonts font1 = new RunFonts() { Ascii = "Lucida Console" };
Italic italic1 = new Italic();
// Specify a 12 point size.
FontSize fontSize1 = new FontSize() { Val = "24" };
styleRunProperties1.Append(bold1);
styleRunProperties1.Append(color1);
styleRunProperties1.Append(font1);
styleRunProperties1.Append(fontSize1);
styleRunProperties1.Append(italic1);
// Add the run properties to the style.
style.Append(styleRunProperties1);
// Add the style to the styles part.
styles.Append(style);
}
// Add a StylesDefinitionsPart to the document. Returns a reference to it.
static StyleDefinitionsPart AddStylesPartToPackage(WordprocessingDocument doc)
{
MainDocumentPart mainDocumentPart = doc.MainDocumentPart ?? doc.AddMainDocumentPart();
StyleDefinitionsPart part = mainDocumentPart.AddNewPart<StyleDefinitionsPart>();
Styles root = new Styles();
root.Save(part);
return part;
}