Estilo o formato de fuentes en XLSX

Figura
Figura. Estilo de fuente en WXTABLE

El estilo XLSX se almacena en un único archivo xl/styles.xml conteniendo el estilo de todo el libro. El estilo puede ser principalmente de fuentes y color del texto, fondos, bordes y alineaciones. En terminología XLSX al estilo se le denomina formato. Para ser coherentes con CSS, preferimos aplicar el témino estilo, reservando formato para el formateo de celdas, como los formatos de número o fecha, algo que veremos en un tema posterior.

Hacemos un primer ejemplo que se observa en la Figura para empezar con el estilo de fuentes. Puede ver los datos en formato WXTABLE en el enlace font-style.txt y traducido a XLSX en font-style.xlsx.

Como WXTABLE es una tabla HTML, el CSS particular de las celdas se incluye en el elemento <td>. La celda del ejemplo tiene este estilo CSS:

font-family: Georgia, serif;
font-weight: bold;
font-style: italic;
text-decoration-line: underline line-through;
text-decoration-style: solid;
color: rgb(0, 0, 255);
width: 200px;
text-align: right;
vertical-align: bottom;
font-size: 14pt;
Figura
Figura. Estilo de fuente en GoogleSheet

Una vez exportado a XLSX el resultado en GoogleSheet se muestra en la Figura.

El ancho de la celda (width) así como el ancho y alto de columnas y filas se explicará en un tema siguiente, puesto que XLSX no lo considera dentro de este archivo xl/styles.xml. En cuanto a los rellenos de celda, que con CSS se consigue con la propiedad padding, no son directamente configurables en XLSX. En ese tema siguiente donde se explica el ancho de columnas se hablará sobre los rellenos.

El archivo xl/styles.xml contiene el elemento <styleSheet> que es donde se alberga todo el estilo del libro. El ejemplo anterior tiene este XML que analizaremos en este tema:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
    <numFmts count="0"></numFmts>
    <fonts count="2">
        <font>
            <name val="Arial"/>
            <sz val="12"/>
            <color rgb="FF000000"/>
        </font>
        <font>
            <name val="Georgia"/>
            <family val="1"/>
            <b/>
            <i/>
            <u/>
            <strike/>
            <color rgb="FF0000FF"/>
            <sz val="14.00"/>
        </font>
    </fonts>
    <fills count="2">
        <fill>
            <patternFill patternType="none"/>
        </fill>
        <fill>
            <patternFill patternType="gray125"/>
        </fill>
    </fills>
    <borders count="1">
        <border>
            <left/>
            <right/>
            <top/>
            <bottom/>
            <diagonal/>
        </border>
    </borders>
    <cellStyleXfs count="1">
        <xf borderId="0" fillId="0" fontId="0" numFmtId="0" 
        applyAlignment="1" applyFont="1" applyFill="1" 
        applyFormat="0"/>
    </cellStyleXfs>
    <cellXfs count="2">
        <xf borderId="0" fillId="0" fontId="0" numFmtId="0" 
        xfId="0" applyAlignment="1" applyBorder="0" applyFont="1" 
        applyFill="1" applyFormat="0">
            <alignment horizontal="left" vertical="center"
            shrinkToFit="0" wrapText="1"/>
        </xf>
        <xf borderId="0" fillId="0" fontId="1" numFmtId="0" 
        xfId="0" applyAlignment="1" applyBorder="0" applyFont="1"
         applyFill="1" applyFormat="0">
            <alignment horizontal="right" vertical="bottom" 
            shrinkToFit="0" wrapText="1"/>
        </xf>
    </cellXfs>
    <cellStyles count="1">
        <cellStyle xfId="0" name="Normal" builtinId="0"/>
    </cellStyles>
    <dxfs count="0"></dxfs>
</styleSheet>

En primer lugar diremos que hay algunos elementos que siempre hay que incluir aunque no se estén usando. Para este primer ejemplo sólo se utiliza el segundo <font> y el segundo <xf>. El resto siempre deben estar aunque la hoja esté completamente vacía.

El primer y último elemento <numFmts> y <dxfs> contiene los verdaderos formatos, algo que veremos en un tema posterior. Aunque no se especifiquen siempre hay que ponerlos

En <fonts> se declaran las fuentes en cada elemento <font>. Se indexan desde cero y el primer elemento <font> (con índice cero) siempre debe estar aunque la hoja esté vacía. En ese elemento se declara la fuente y tamaño predeterminado. En WXTABLE es "Arial" 12pt que trasladamos a los elementos <name> y <sz>. Esto es primordial para el cálculo del ancho de columnas como veremos en un tema posterior. El color predeterminado es negro FF000000. Vease que está en un formato ARGB, donde las dos primeras "FF" se refiere a la transparencia (u opacidad, en este caso totalmente opaco), pero parece que esta característica no se soporta en GoogleSheet o Excel web.

El segundo elemento <font> es el del ejemplo. Hemos declarado fuente "Georgia" en el elemento name. La familia de fuentes es "1" declarada en el elemento <family>. No es obligatorio incluirlo pero ayuda a la aplicación que lea el XLSX a buscar una fuente alternativa en caso de que no disponga la declarada. El valor "1" se corresponde con una lista que se declara en OOX, 18.18.94 ST_FontFamily (Font Family), página 2525, como se expone en la primera columna de la siguiente tabla:

XSXCSS
0 Not applicable
1 Romanserif
2 Swisssans-serif
3 Modernmonospace
4 Scriptcursive
5 Decorative

Le hago corresponder las familias que conocemos en CSS, a excepción de la decorativa, en cuyo caso no se incluirá el elemento. El tamaño de la fuente es 14pt. El color es FF0000FF azul. Los elementos vacíos <b>, <i>, <u> y <strike> declaran los estilos negrita, itálica, subrayado y tachado.

Ese elemento <font> es el segundo dentro de <fonts>. Como se indexan desde cero entonces le corresponde el índice 1. El estilo de las celdas se declara dentro de los elementos <cellXfs>. También se indexan desde cero. El primer <xf>, que siempre debe existir aunque el libro esté vacío, declara el estilo para las celdas donde no se indique estilo. El segundo <xf> declara el estilo de la celda de ejemplo.

La documentación de OOX en su apartado 18.8.10 cellXfs (Cell Formats), página 1773, dice que el elemento <cellXfs> contiene el registro de estilos del libro. En el apartado 18.8.45 xf (Format), página 1810, se comenta el elemento <xf>. Sus propiedades borderId, fillId, fontId y numFmtId son índices de estilos o formatos dentro de los elementos de esta hoja. Para la celda de ejemplo tenemos fontId="1" que conjuntamente con applyFont="1" hará que se aplique el estilo de fuente de nuestro ejemplo. Observe que la alineación se incluye en el elemento <alignment>.

La propiedad xfId="0" es un índice de un elemento <xf> dentro de <cellStylesXfs>. En el apartado 18.8.9 cellStyleXfs (Formatting Records) de OOX, página 1772, dice que este elemento contiene el estilo con nombre para todas las celdas del libro. Los estilos con nombre se definen dentro de <cellStyles> y es explicado en el apartado 18.8.7 cellStyle (Cell Style), página 1770. Si se ha utilizado una hoja de cálculo verá que se definen estilos con nombre como "Normal", "Encabezado 1", "Títulos", etc. El estilo "Normal" siempre habrá que incluirlo pues se aplicará por defecto. Observe que el elemento <cellStyle> tiene también un xfId="0" que apunta al único elemento de <cellStyleXfs>.

Cuando una aplicación que lea XLSX aplique estilo a una celda, primero leerá el elemento <xf> en <cellXfs>. Observará su xfId para buscarlo en <cellStyles> y en <cellStyleXfs>, aplicando primero ese estilo. Luego aplicará el estilo particular que aparezca en el elemento de <cellXfs> sobrescribiendo el anterior.

WXTABLE no usa estilos con nombre, por lo que <cellStyles> y <cellStyleXfs> siempre será tal como aparece en el código anterior.

Estilo de fondos en XLSX

Figura
Figura. Color de fondo en WXTABLE

En WXTABLE se aplica el estilo de fondo en la propiedad CSS background. Podemos encontrar background-image para insertar una imagen en el fondo, algo que veremos en un tema posterior. Ahora veremos background-color para aplicar color de fondo. En la Figura vemos que se aplica color anaranjado a tres celdas. En la primera con el color rgb(255, 165, 0). La segunda con transparencia 50% usando rgba(255, 165, 0, 0.5). La tercera mediante una clave de color orange.

Puede comprobar el estilo CSS de las celdad en formato WXTABLE con el enlace background.txt y su traducción a XLSX en background.xlsx. Las celdas tienen este estilo:

"styles": {
    "1,0:3,0": "height:64px;",
    "0,1": "width:150px;",
    "1,1": "background-color:rgb(255, 165, 0);text-align:center;width:150px;",
    "1,2:3,2": "width:96px;",
    "2,1": "background-color:rgba(255, 165, 0, 0.5);text-align:center;width:150px;",
    "3,1": "background-color:orange;background-clip:content-box;padding:0.5em;text-align:center;width:150px;"
}

Observe que la tercera celda tiene padding:0.5em y background-clip:content-box, configurando un relleno blanco y que el color de fondo se limite al área de contenido.

Figura
Figura. Color de fondo en EXCEL

El resultado en EXCEL se muestra en la Figura. Las tres celdas tienen el mismo color final. El relleno y recorte del fondo de CSS no parece que se soporten en XLSX. Al igual que el color con transparencia background-color:rgba(255, 165, 0, 0.5).

El estilo de fondos en el archivo xl/styles.xml se encuentra en el elemento <fills>. Cada uno de sus elementos <fill> define un fondo. Se indexan desde cero y siempre deberá tener al menos los dos primeros elementos con índices 0 y 1. El segundo es requerido por EXCEL, sin que sepa cuál es el motivo. Sólo que si no lo ponemos no podrá abrir el archivo.

Así que el estilo de fondos para el ejemplo comienza en el tercer <fill> con índice 2, como se observa en el código donde hemos obviado contenido que no viene al caso con puntos suspensivos (puede verlo en el apartado anterior):

...
<fills count="4">
    <fill>
        <patternFill patternType="none"/>
    </fill>
    <fill>
        <patternFill patternType="gray125"/>
    </fill>
    <fill>
        <patternFill patternType="solid">
            <fgColor rgb="FFFFA500"/>
            <bgColor indexed="64"/>
        </patternFill>
    </fill>
    <fill>
        <patternFill patternType="solid">
            <fgColor rgb="80FFA500"/>
            <bgColor indexed="64"/>
        </patternFill>
    </fill>
</fills>
...
<cellXfs count="3">
    <xf ...><alignment .../></xf>
    <xf fillId="2" applyFill="1" ...><alignment .../></xf>
    <xf fillId="3" applyFill="1" ...><alignment .../></xf>
</cellXfs>
...

El fondo se incluye en elementos <patternFill patternType="solid">. XLSX permite más cosas en los patrones de fondos, pero esto no es compatible con el CSS que uso en WXTABLE. Así que sólo lo usaremos para aplicar colores de fondo sólidos. En el archivo xl/worksheets/sheet1.xml encontramos el XML con los datos de las tres celdas:

...
<sheetData>
    <row r="1" hidden="0" customHeight="1" ht="48">
        <c r="A1" t="s" s="1">
            <v>0</v>
        </c>
        <c r="B1"/>
    </row>
    <row r="2" hidden="0" customHeight="1" ht="48">
        <c r="A2" t="s" s="2">
            <v>1</v>
        </c>
    </row>
    <row r="3" hidden="0" customHeight="1" ht="57">
        <c r="A3" t="s" s="1">
            <v>2</v>
        </c>
    </row>
</sheetData>
...

Observe que la primera y última celda indexan el estilo s="1" pues tienen como color <fgColor rgb="FFFFA500"> que se corresponde con rgb(255, 165, 0) y la clave de color CSS orange. Mientras que la segunda tiene s="2" que se corresponde con el color con transparencia <fgColor rgb="80FFA500">, aunque GoogleSheet y EXCEL no aplican transparencias. Observe que el hexadecimal 80 es el número decimal 128 y por tanto 128/256 = 0.5, un 50% de transparencia, o mejor dicho, opacidad.

XLSX diferencia entre fgColor y bgColor. El segundo se usa para el caso de patrones, pero como no los usamos le ponemos el color indexado 64, <bgColor indexed="64">, que se corresponde con el color de fondo del sistema (System Foreground). Puede ver esto en OOX, apartado 18.8.27 indexedColors (Color Indexes), página 1782, donde se relacionan los colores indexados, que por otro lado prefiero no usar para el resto de colores, pues parece que se incluye por razones de compatibilidad con versiones anteriores de XLSX.

Figura
Figura. Configurar fondo en WXTABLE

Al estructurarse WXTABLE en HTML y CSS, podemos aplicar estilo adicional para el fondo. En la Figura se observa que hemos aplicado a la tercera celda el recorte del fondo a la caja de contenido (content-box), produciendo el estilo CSS background-clip:content-box.

Otros estilos adicionales como este que se pueden aplicar en CSS son el inicio del fondo background-origin, su posición background-position, la repetición background-repeat y el tamaño background-size. Algunos de estos sólo tiene sentido cuando se usa con una imagen de fondo, tal como veremos en un tema posterior.

En cualquier caso nada de eso es traducible a XLSX. Al menos con lo que hasta ahora he aprendido.

Bordes de celdas en XLSX

Figura
Figura. Bordes en WXTABLE

Los bordes en CSS son díficiles de manejar. El problema es que hay varias formas de declarar bordes en un elemento. Podemos usar border-[side] donde [side] puede ser top, right, bottom o left. En este caso el valor es una cadena "[widthValue] [styleValue] [colorValue]", pudiendo contener uno o más de esos valores separados por uno o más espacios, en cualquier orden. En [widthValue] pondríamos el ancho, algo como 4px. En [styleValue] pondríamos el estilo del borde, un valor entre none, solid, dashed, dotted, double. En [colorValue] pondríamos un valor de color. Como los tres valores tienen una forma de declaración diferente, el navegador siempre podrá diferenciar entre los valores, por eso pueden haber menos de tres y en cualquier orden. Aunque se debe especificar el estilo como mínimo, aplicándose para el resto el estilo heredado.

La otra forma de declarar bordes es con border-[part] donde [part] puede ser width, style o color. Los valores en este caso pueden ser de uno a cuatro valores de ancho, estilo o color respectivamente. Si se especifica 1 valor se aplica a los cuatro lados. Si se usan 2 valores el primero es para los bordes verticales y el segundo para los horizontales. Con 3 valores el primero y tercero son para el borde superior e inferior mientras que el segundo son para derecha e izquierda al mismo tiempo. Con 4 valores se sigue el sentido de las agujas del reloj comenzando por el superior, derecha, inferior e izquierda.

Figura
Figura. Bordes en EXCEL

Esto no deja de plantear un reto importante a la hora de hacer una aplicación para editar bordes en CSS. Y se complica a la hora de traducirlo a XLSX. Intentemos explicarlo con el ejemplo de la Figura, cuya hoja de datos en formato WXTABLE puede ver en el enlace borders.txt y su traducción a XLSX en borders.xlsx. En la Figura puede ver el resultado en EXCEL.

Todos los bordes del ejemplo en WXTABLE y por tanto en CSS tiene un ancho de 4px. Observará que no se traduce el ancho a XLSX de la misma forma. En XLSX se pueden declarar tres anchos: thin, medium y thick. En el módulo exportador tengo en cuenta que si es de 1px se aplicará thin, si es de 2px se aplicará medium y si es mayor o igual que 3px se aplicará thick.

Pero esto sólo se aplica al estilo borde sólido (solid), pues para los otros se ignora el ancho y se aplica el mínimo para representarlo: unos 3px para el borde doble (double) y 1px para el borde rayado (dashed) y borde punteado (dotted). Veamos ahora el contenido en la hoja de estilo xl/styles.xml. El elemento <borders> contiene todos los bordes del libro. En cada elemento <border> se define un borde para los cuatro lados. También se incluye un borde diagonal que, aunque no se declare, es necesario para que funcione el XLSX en EXCEL. Es una definición de borde un poco extraña, cruzando en diagonal la celda. En cualquier caso la ignoro porque WXTABLE no usa ese tipo de borde.

Se indexan desde cero, correspondiendo al índice cero el borde por defecto cuando nos se aplica estilo a una celda, es decir, bordes sin ningún estilo. Lo vemos a continuación:

<borders count="5">
    <border>
        <left/>
        <right/>
        <top/>
        <bottom/>
        <diagonal/>
    </border>
    ...
</borders>

El siguiente borde es el primero del ejemplo, sólido de color rojo de 4px, que en XLSX corresponde al ancho thick:

<borders count="5">
    ...
    <border>
        <left style="thick">
            <color rgb="FFFF0000"/>
        </left>
        <right style="thick">
            <color rgb="FFFF0000"/>
        </right>
        <top style="thick">
            <color rgb="FFFF0000"/>
        </top>
        <bottom style="thick">
            <color rgb="FFFF0000"/>
        </bottom>
        <diagonal/>
    </border>
    ...
</borders>

A continuación viene el segundo del ejemplo, un borde doble en el lado izquierdo. El style es double, aplicando XLSX un ancho de unos 3px siempre. Los otros bordes aparecen con style="" con lo que no serán presentados:

<borders count="5">
    ...
    <border>
        <left style="double">
            <color rgb="FF0000FF"/>
        </left>
        <right style="">
            <color rgb="FF000000"/>
        </right>
        <top style="">
            <color rgb="FF000000"/>
        </top>
        <bottom style="">
            <color rgb="FF000000"/>
        </bottom>
        <diagonal/>
    </border>
    ...
</borders>

La tercera celda del ejemplo tiene un borde punteado en el lado inferior. El style es dotted:

<borders count="5">
    ...
    <border>
        <left style="">
            <color rgb="FF000000"/>
        </left>
        <right style="">
            <color rgb="FF000000"/>
        </right>
        <top style="">
            <color rgb="FF000000"/>
        </top>
        <bottom style="dotted">
            <color rgb="FF008000"/>
        </bottom>
        <diagonal/>
    </border>
    ...
</borders>

Y la última celda tiene los cuatro bordes diferentes:

<borders count="5">
    ...
    <border>
        <left style="double">
            <color rgb="FF0000FF"/>
        </left>
        <right style="dashed">
            <color rgb="FFFF00FF"/>
        </right>
        <top style="thick">
            <color rgb="FFFF0000"/>
        </top>
        <bottom style="dotted">
            <color rgb="FF008000"/>
        </bottom>
        <diagonal/>
    </border>
</borders>

En el elemento <cellXfs> encontramos el segundo <xf> que se corresponde con la primera celda del ejemplo con borde rojo sólido en los cuatro lados, siendo similar para los otros casos. Este es el código donde hemos omitido las partes no relacionadas:

<xf borderId="1" applyBorder="1" ...>
    <alignment .../>
</xf>

Observe que tiene borderId="1" que se corresponde con el segundo elemento <border> dentro de la colección de borders.

Alineaciones en XLSX

Las alineaciones en WXTABLE se basan en CSS y son las horizontales con la propiedad text-align y las verticales con vertical-align. En XLSX es algo parecido. Se declaran en el elemento <alignment> dentro de un <xf applyAlignment="1" ...>:

<alignment 
horizontal="left" 
vertical="center" 
shrinkToFit="0" 
wrapText="1"/>

La alineación horizontal que usamos en CSS es right, center, left y justify. En XLSX también existen estas y son las que se usan. XLSX tiene otras posibilidades que no se utilizan al exportar desde WXTABLE. Ver OOX 18.8.1 alignment (Alignment), página 1762 y 18.18.40 ST_HorizontalAlignment (Horizontal Alignment Type), página 2467.

La alineación vertical que usamos en CSS es top, center, bottom y son las mismas que también se usan en XLSX. Aquí también XLSX tiene más posibilidades que no se utilizan. Ver OOX 18.18.88 ST_VerticalAlignment (Vertical Alignment Types), página 2520.

La propiedad shrinkToFit no se utiliza pues no se soporta en WXTABLE. Se supone que encoge el texto para que quepa en la celda. La otra propiedad wrapText ya la vimos en el tema anterior cuando hablamos de extender o ajustar el texto de las celdas.