mirror of
https://github.com/jmcnamara/libxlsxwriter.git
synced 2026-05-15 14:15:54 -06:00
1088 lines
26 KiB
C
1088 lines
26 KiB
C
/*****************************************************************************
|
|
* styles - A library for creating Excel XLSX styles files.
|
|
*
|
|
* Used in conjunction with the libxlsxwriter library.
|
|
*
|
|
* Copyright 2014, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
|
|
*
|
|
*/
|
|
|
|
#include "xlsxwriter/xmlwriter.h"
|
|
#include "xlsxwriter/styles.h"
|
|
#include "xlsxwriter/utility.h"
|
|
|
|
/*
|
|
* Forward declarations.
|
|
*/
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Private functions.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/*
|
|
* Create a new styles object.
|
|
*/
|
|
lxw_styles *
|
|
_new_styles()
|
|
{
|
|
lxw_styles *styles = calloc(1, sizeof(lxw_styles));
|
|
GOTO_LABEL_ON_MEM_ERROR(styles, mem_error);
|
|
|
|
styles->xf_formats = calloc(1, sizeof(struct lxw_formats));
|
|
GOTO_LABEL_ON_MEM_ERROR(styles->xf_formats, mem_error);
|
|
|
|
STAILQ_INIT(styles->xf_formats);
|
|
|
|
return styles;
|
|
|
|
mem_error:
|
|
_free_styles(styles);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Free a styles object.
|
|
*/
|
|
void
|
|
_free_styles(lxw_styles *styles)
|
|
{
|
|
lxw_format *format;
|
|
|
|
if (!styles)
|
|
return;
|
|
|
|
/* Free the formats in the styles. */
|
|
while (!STAILQ_EMPTY(styles->xf_formats)) {
|
|
format = STAILQ_FIRST(styles->xf_formats);
|
|
STAILQ_REMOVE_HEAD(styles->xf_formats, list_pointers);
|
|
free(format);
|
|
}
|
|
|
|
free(styles->xf_formats);
|
|
free(styles);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* XML functions.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/*
|
|
* Write the XML declaration.
|
|
*/
|
|
STATIC void
|
|
_styles_xml_declaration(lxw_styles *self)
|
|
{
|
|
_xml_declaration(self->file);
|
|
}
|
|
|
|
/*
|
|
* Write the <styleSheet> element.
|
|
*/
|
|
STATIC void
|
|
_write_style_sheet(lxw_styles *self)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_STR("xmlns",
|
|
"http://schemas.openxmlformats.org/spreadsheetml/2006/main");
|
|
|
|
_xml_start_tag(self->file, "styleSheet", &attributes);
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <numFmt> element.
|
|
*/
|
|
STATIC void
|
|
_write_num_fmt(lxw_styles *self, uint8_t num_fmt_id, char *format_code)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_INT("numFmtId", num_fmt_id);
|
|
_PUSH_ATTRIBUTES_STR("formatCode", format_code);
|
|
|
|
_xml_empty_tag(self->file, "numFmt", &attributes);
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <numFmts> element.
|
|
*/
|
|
STATIC void
|
|
_write_num_fmts(lxw_styles *self)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
lxw_format *format;
|
|
|
|
if (!self->num_format_count)
|
|
return;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_INT("count", self->num_format_count);
|
|
|
|
_xml_start_tag(self->file, "numFmts", &attributes);
|
|
|
|
/* Write the numFmts elements. */
|
|
STAILQ_FOREACH(format, self->xf_formats, list_pointers) {
|
|
|
|
/* Ignore built-in number formats, i.e., < 164. */
|
|
if (format->num_format_index < 164)
|
|
continue;
|
|
|
|
_write_num_fmt(self, format->num_format_index, format->num_format);
|
|
}
|
|
|
|
_xml_end_tag(self->file, "numFmts");
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <sz> element.
|
|
*/
|
|
STATIC void
|
|
_write_font_size(lxw_styles *self, uint16_t font_size)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_INT("val", font_size);
|
|
|
|
_xml_empty_tag(self->file, "sz", &attributes);
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <color> element for themes.
|
|
*/
|
|
STATIC void
|
|
_write_font_color_theme(lxw_styles *self, uint8_t theme)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_INT("theme", theme);
|
|
|
|
_xml_empty_tag(self->file, "color", &attributes);
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <color> element for RGB colours.
|
|
*/
|
|
STATIC void
|
|
_write_font_color_rgb(lxw_styles *self, int32_t rgb)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
char rgb_str[ATTR_32];
|
|
|
|
__builtin_snprintf(rgb_str, ATTR_32, "FF%06X", rgb & LXW_COLOR_MASK);
|
|
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_STR("rgb", rgb_str);
|
|
|
|
_xml_empty_tag(self->file, "color", &attributes);
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <name> element.
|
|
*/
|
|
STATIC void
|
|
_write_font_name(lxw_styles *self, const char *font_name)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
|
|
if (strlen(font_name))
|
|
_PUSH_ATTRIBUTES_STR("val", font_name);
|
|
else
|
|
_PUSH_ATTRIBUTES_STR("val", LXW_DEFAULT_FONT_NAME);
|
|
|
|
_xml_empty_tag(self->file, "name", &attributes);
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <family> element.
|
|
*/
|
|
STATIC void
|
|
_write_font_family(lxw_styles *self, uint8_t font_family)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_INT("val", font_family);
|
|
|
|
_xml_empty_tag(self->file, "family", &attributes);
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <scheme> element.
|
|
*/
|
|
STATIC void
|
|
_write_font_scheme(lxw_styles *self, const char *font_scheme)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
|
|
if (strlen(font_scheme))
|
|
_PUSH_ATTRIBUTES_STR("val", font_scheme);
|
|
else
|
|
_PUSH_ATTRIBUTES_STR("val", "minor");
|
|
|
|
_xml_empty_tag(self->file, "scheme", &attributes);
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the underline font element.
|
|
*/
|
|
STATIC void
|
|
_write_font_underline(lxw_styles *self, uint8_t underline)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
|
|
/* Handle the underline variants. */
|
|
if (underline == LXW_UNDERLINE_DOUBLE)
|
|
_PUSH_ATTRIBUTES_STR("val", "double");
|
|
else if (underline == LXW_UNDERLINE_SINGLE_ACCOUNTING)
|
|
_PUSH_ATTRIBUTES_STR("val", "singleAccounting");
|
|
else if (underline == LXW_UNDERLINE_DOUBLE_ACCOUNTING)
|
|
_PUSH_ATTRIBUTES_STR("val", "doubleAccounting");
|
|
/* Default to single underline. */
|
|
|
|
_xml_empty_tag(self->file, "u", &attributes);
|
|
|
|
_FREE_ATTRIBUTES();
|
|
|
|
}
|
|
|
|
/*
|
|
* Write the <vertAlign> font sub-element.
|
|
*/
|
|
STATIC void
|
|
_write_vert_align(lxw_styles *self, const char *align)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_STR("val", align);
|
|
|
|
_xml_empty_tag(self->file, "vertAlign", &attributes);
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <font> element.
|
|
*/
|
|
STATIC void
|
|
_write_font(lxw_styles *self, lxw_format *format)
|
|
{
|
|
_xml_start_tag(self->file, "font", NULL);
|
|
|
|
if (format->bold)
|
|
_xml_empty_tag(self->file, "b", NULL);
|
|
|
|
if (format->italic)
|
|
_xml_empty_tag(self->file, "i", NULL);
|
|
|
|
if (format->font_strikeout)
|
|
_xml_empty_tag(self->file, "strike", NULL);
|
|
|
|
if (format->font_outline)
|
|
_xml_empty_tag(self->file, "outline", NULL);
|
|
|
|
if (format->font_shadow)
|
|
_xml_empty_tag(self->file, "shadow", NULL);
|
|
|
|
if (format->underline)
|
|
_write_font_underline(self, format->underline);
|
|
|
|
if (format->font_script == LXW_FONT_SUPERSCRIPT)
|
|
_write_vert_align(self, "superscript");
|
|
|
|
if (format->font_script == LXW_FONT_SUBSCRIPT)
|
|
_write_vert_align(self, "subscript");
|
|
|
|
if (format->font_size)
|
|
_write_font_size(self, format->font_size);
|
|
|
|
if (format->theme)
|
|
_write_font_color_theme(self, format->theme);
|
|
else if (format->font_color != LXW_COLOR_UNSET)
|
|
_write_font_color_rgb(self, format->font_color);
|
|
else
|
|
_write_font_color_theme(self, LXW_DEFAULT_FONT_THEME);
|
|
|
|
_write_font_name(self, format->font_name);
|
|
_write_font_family(self, format->font_family);
|
|
|
|
/* Only write the scheme element for the default font type if it
|
|
* is a hyperlink. */
|
|
if ((!strlen(format->font_name)
|
|
|| strcmp(LXW_DEFAULT_FONT_NAME, format->font_name) == 0)
|
|
&& !format->hyperlink) {
|
|
_write_font_scheme(self, format->font_scheme);
|
|
}
|
|
|
|
_xml_end_tag(self->file, "font");
|
|
}
|
|
|
|
/*
|
|
* Write the <fonts> element.
|
|
*/
|
|
STATIC void
|
|
_write_fonts(lxw_styles *self)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
lxw_format *format;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_INT("count", self->font_count);
|
|
|
|
_xml_start_tag(self->file, "fonts", &attributes);
|
|
|
|
STAILQ_FOREACH(format, self->xf_formats, list_pointers) {
|
|
if (format->has_font)
|
|
_write_font(self, format);
|
|
}
|
|
|
|
_xml_end_tag(self->file, "fonts");
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the default <fill> element.
|
|
*/
|
|
STATIC void
|
|
_write_default_fill(lxw_styles *self, const char *pattern)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_STR("patternType", pattern);
|
|
|
|
_xml_start_tag(self->file, "fill", NULL);
|
|
_xml_empty_tag(self->file, "patternFill", &attributes);
|
|
_xml_end_tag(self->file, "fill");
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <fgColor> element.
|
|
*/
|
|
STATIC void
|
|
_write_fg_color(lxw_styles *self, lxw_color_t color)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
char rgb_str[ATTR_32];
|
|
|
|
_INIT_ATTRIBUTES();
|
|
|
|
__builtin_snprintf(rgb_str, ATTR_32, "FF%06X", color & LXW_COLOR_MASK);
|
|
_PUSH_ATTRIBUTES_STR("rgb", rgb_str);
|
|
|
|
_xml_empty_tag(self->file, "fgColor", &attributes);
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <bgColor> element.
|
|
*/
|
|
STATIC void
|
|
_write_bg_color(lxw_styles *self, lxw_color_t color)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
char rgb_str[ATTR_32];
|
|
|
|
_INIT_ATTRIBUTES();
|
|
|
|
if (color == LXW_COLOR_UNSET) {
|
|
_PUSH_ATTRIBUTES_STR("indexed", "64");
|
|
}
|
|
else {
|
|
__builtin_snprintf(rgb_str, ATTR_32, "FF%06X",
|
|
color & LXW_COLOR_MASK);
|
|
_PUSH_ATTRIBUTES_STR("rgb", rgb_str);
|
|
}
|
|
|
|
_xml_empty_tag(self->file, "bgColor", &attributes);
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <fill> element.
|
|
*/
|
|
STATIC void
|
|
_write_fill(lxw_styles *self, lxw_format *format)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
|
|
uint8_t pattern = format->pattern;
|
|
lxw_color_t bg_color = format->bg_color;
|
|
lxw_color_t fg_color = format->fg_color;
|
|
|
|
char *patterns[] = {
|
|
"none",
|
|
"solid",
|
|
"mediumGray",
|
|
"darkGray",
|
|
"lightGray",
|
|
"darkHorizontal",
|
|
"darkVertical",
|
|
"darkDown",
|
|
"darkUp",
|
|
"darkGrid",
|
|
"darkTrellis",
|
|
"lightHorizontal",
|
|
"lightVertical",
|
|
"lightDown",
|
|
"lightUp",
|
|
"lightGrid",
|
|
"lightTrellis",
|
|
"gray125",
|
|
"gray0625",
|
|
};
|
|
|
|
_INIT_ATTRIBUTES();
|
|
|
|
_xml_start_tag(self->file, "fill", NULL);
|
|
|
|
if (pattern)
|
|
_PUSH_ATTRIBUTES_STR("patternType", patterns[pattern]);
|
|
|
|
_xml_start_tag(self->file, "patternFill", &attributes);
|
|
|
|
if (fg_color != LXW_COLOR_UNSET)
|
|
_write_fg_color(self, fg_color);
|
|
|
|
_write_bg_color(self, bg_color);
|
|
|
|
_xml_end_tag(self->file, "patternFill");
|
|
_xml_end_tag(self->file, "fill");
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <fills> element.
|
|
*/
|
|
STATIC void
|
|
_write_fills(lxw_styles *self)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
lxw_format *format;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_INT("count", self->fill_count);
|
|
|
|
_xml_start_tag(self->file, "fills", &attributes);
|
|
|
|
/* Write the default fills. */
|
|
_write_default_fill(self, "none");
|
|
_write_default_fill(self, "gray125");
|
|
|
|
STAILQ_FOREACH(format, self->xf_formats, list_pointers) {
|
|
if (format->has_fill)
|
|
_write_fill(self, format);
|
|
}
|
|
|
|
_xml_end_tag(self->file, "fills");
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the border <color> element.
|
|
*/
|
|
STATIC void
|
|
_write_border_color(lxw_styles *self, lxw_color_t color)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
char rgb_str[ATTR_32];
|
|
|
|
_INIT_ATTRIBUTES();
|
|
|
|
if (color) {
|
|
__builtin_snprintf(rgb_str, ATTR_32, "FF%06X",
|
|
color & LXW_COLOR_MASK);
|
|
_PUSH_ATTRIBUTES_STR("rgb", rgb_str);
|
|
}
|
|
else {
|
|
_PUSH_ATTRIBUTES_STR("auto", "1");
|
|
}
|
|
|
|
_xml_empty_tag(self->file, "color", &attributes);
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <border> sub elements such as <right>, <top>, etc.
|
|
*/
|
|
STATIC void
|
|
_write_sub_border(lxw_styles *self, const char *type, uint8_t style,
|
|
lxw_color_t color)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
|
|
char *border_styles[] = {
|
|
"none",
|
|
"thin",
|
|
"medium",
|
|
"dashed",
|
|
"dotted",
|
|
"thick",
|
|
"double",
|
|
"hair",
|
|
"mediumDashed",
|
|
"dashDot",
|
|
"mediumDashDot",
|
|
"dashDotDot",
|
|
"mediumDashDotDot",
|
|
"slantDashDot",
|
|
};
|
|
|
|
if (!style) {
|
|
_xml_empty_tag(self->file, type, NULL);
|
|
return;
|
|
}
|
|
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_STR("style", border_styles[style]);
|
|
|
|
_xml_start_tag(self->file, type, &attributes);
|
|
|
|
_write_border_color(self, color);
|
|
|
|
_xml_end_tag(self->file, type);
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <border> element.
|
|
*/
|
|
STATIC void
|
|
_write_border(lxw_styles *self, lxw_format *format)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
|
|
/* Add attributes for diagonal borders. */
|
|
if (format->diag_type == LXW_DIAGONAL_BORDER_UP) {
|
|
_PUSH_ATTRIBUTES_STR("diagonalUp", "1");
|
|
}
|
|
else if (format->diag_type == LXW_DIAGONAL_BORDER_DOWN) {
|
|
_PUSH_ATTRIBUTES_STR("diagonalDown", "1");
|
|
}
|
|
else if (format->diag_type == LXW_DIAGONAL_BORDER_UP_DOWN) {
|
|
_PUSH_ATTRIBUTES_STR("diagonalUp", "1");
|
|
_PUSH_ATTRIBUTES_STR("diagonalDown", "1");
|
|
}
|
|
|
|
/* Ensure that a default diag border is set if the diag type is set. */
|
|
if (format->diag_type && !format->diag_border) {
|
|
format->diag_border = 1;
|
|
}
|
|
|
|
/* Write the start border tag. */
|
|
_xml_start_tag(self->file, "border", &attributes);
|
|
|
|
/* Write the <border> sub elements. */
|
|
_write_sub_border(self, "left", format->left, format->left_color);
|
|
_write_sub_border(self, "right", format->right, format->right_color);
|
|
_write_sub_border(self, "top", format->top, format->top_color);
|
|
_write_sub_border(self, "bottom", format->bottom, format->bottom_color);
|
|
_write_sub_border(self,
|
|
"diagonal", format->diag_border, format->diag_color);
|
|
|
|
_xml_end_tag(self->file, "border");
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <borders> element.
|
|
*/
|
|
STATIC void
|
|
_write_borders(lxw_styles *self)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
lxw_format *format;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_INT("count", self->border_count);
|
|
|
|
_xml_start_tag(self->file, "borders", &attributes);
|
|
|
|
STAILQ_FOREACH(format, self->xf_formats, list_pointers) {
|
|
if (format->has_border)
|
|
_write_border(self, format);
|
|
}
|
|
|
|
_xml_end_tag(self->file, "borders");
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <xf> element for styles.
|
|
*/
|
|
STATIC void
|
|
_write_style_xf(lxw_styles *self)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_STR("numFmtId", "0");
|
|
_PUSH_ATTRIBUTES_STR("fontId", "0");
|
|
_PUSH_ATTRIBUTES_STR("fillId", "0");
|
|
_PUSH_ATTRIBUTES_STR("borderId", "0");
|
|
|
|
_xml_empty_tag(self->file, "xf", &attributes);
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <cellStyleXfs> element.
|
|
*/
|
|
STATIC void
|
|
_write_cell_style_xfs(lxw_styles *self)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_STR("count", "1");
|
|
|
|
_xml_start_tag(self->file, "cellStyleXfs", &attributes);
|
|
_write_style_xf(self);
|
|
_xml_end_tag(self->file, "cellStyleXfs");
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Check if a format struct has alignment properties set and the
|
|
* "applyAlignment" attribute should be set.
|
|
*/
|
|
STATIC uint8_t
|
|
_apply_alignment(lxw_format *format)
|
|
{
|
|
return format->text_h_align != LXW_ALIGN_NONE
|
|
|| format->text_v_align != LXW_ALIGN_NONE
|
|
|| format->indent != 0
|
|
|| format->rotation != 0
|
|
|| format->text_wrap != 0
|
|
|| format->shrink != 0 || format->reading_order != 0;
|
|
}
|
|
|
|
/*
|
|
* Check if a format struct has alignment properties set apart from the
|
|
* LXW_ALIGN_VERTICAL_BOTTOM which Excel treats as a default.
|
|
*/
|
|
STATIC uint8_t
|
|
_has_alignment(lxw_format *format)
|
|
{
|
|
return format->text_h_align != LXW_ALIGN_NONE
|
|
|| !(format->text_v_align == LXW_ALIGN_NONE ||
|
|
format->text_v_align == LXW_ALIGN_VERTICAL_BOTTOM)
|
|
|| format->indent != 0
|
|
|| format->rotation != 0
|
|
|| format->text_wrap != 0
|
|
|| format->shrink != 0 || format->reading_order != 0;
|
|
}
|
|
|
|
/*
|
|
* Write the <alignment> element.
|
|
*/
|
|
STATIC void
|
|
_write_alignment(lxw_styles *self, lxw_format *format)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
int16_t rotation = format->rotation;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
|
|
/* Indent is only allowed for horizontal left, right and distributed. */
|
|
/* If it is defined for any other alignment or no alignment has been */
|
|
/* set then default to left alignment. */
|
|
if (format->indent
|
|
&& format->text_h_align != LXW_ALIGN_LEFT
|
|
&& format->text_h_align != LXW_ALIGN_RIGHT
|
|
&& format->text_h_align != LXW_ALIGN_DISTRIBUTED) {
|
|
format->text_h_align = LXW_ALIGN_LEFT;
|
|
}
|
|
|
|
/* Check for properties that are mutually exclusive. */
|
|
if (format->text_wrap)
|
|
format->shrink = 0;
|
|
|
|
if (format->text_h_align == LXW_ALIGN_FILL)
|
|
format->shrink = 0;
|
|
|
|
if (format->text_h_align == LXW_ALIGN_JUSTIFY)
|
|
format->shrink = 0;
|
|
|
|
if (format->text_h_align == LXW_ALIGN_DISTRIBUTED)
|
|
format->shrink = 0;
|
|
|
|
if (format->text_h_align != LXW_ALIGN_DISTRIBUTED)
|
|
format->just_distrib = 0;
|
|
|
|
if (format->indent)
|
|
format->just_distrib = 0;
|
|
|
|
if (format->text_h_align == LXW_ALIGN_LEFT)
|
|
_PUSH_ATTRIBUTES_STR("horizontal", "left");
|
|
|
|
if (format->text_h_align == LXW_ALIGN_CENTER)
|
|
_PUSH_ATTRIBUTES_STR("horizontal", "center");
|
|
|
|
if (format->text_h_align == LXW_ALIGN_RIGHT)
|
|
_PUSH_ATTRIBUTES_STR("horizontal", "right");
|
|
|
|
if (format->text_h_align == LXW_ALIGN_FILL)
|
|
_PUSH_ATTRIBUTES_STR("horizontal", "fill");
|
|
|
|
if (format->text_h_align == LXW_ALIGN_JUSTIFY)
|
|
_PUSH_ATTRIBUTES_STR("horizontal", "justify");
|
|
|
|
if (format->text_h_align == LXW_ALIGN_CENTER_ACROSS)
|
|
_PUSH_ATTRIBUTES_STR("horizontal", "centerContinuous");
|
|
|
|
if (format->text_h_align == LXW_ALIGN_DISTRIBUTED)
|
|
_PUSH_ATTRIBUTES_STR("horizontal", "distributed");
|
|
|
|
if (format->just_distrib)
|
|
_PUSH_ATTRIBUTES_STR("justifyLastLine", "1");
|
|
|
|
if (format->text_v_align == LXW_ALIGN_VERTICAL_TOP)
|
|
_PUSH_ATTRIBUTES_STR("vertical", "top");
|
|
|
|
if (format->text_v_align == LXW_ALIGN_VERTICAL_CENTER)
|
|
_PUSH_ATTRIBUTES_STR("vertical", "center");
|
|
|
|
if (format->text_v_align == LXW_ALIGN_VERTICAL_JUSTIFY)
|
|
_PUSH_ATTRIBUTES_STR("vertical", "justify");
|
|
|
|
if (format->text_v_align == LXW_ALIGN_VERTICAL_DISTRIBUTED)
|
|
_PUSH_ATTRIBUTES_STR("vertical", "distributed");
|
|
|
|
if (format->indent)
|
|
_PUSH_ATTRIBUTES_INT("indent", format->indent);
|
|
|
|
/* Map rotation to Excel values. */
|
|
if (rotation) {
|
|
if (rotation == 270)
|
|
rotation = 255;
|
|
else if (rotation < 0)
|
|
rotation = -rotation + 90;
|
|
|
|
_PUSH_ATTRIBUTES_INT("textRotation", rotation);
|
|
}
|
|
|
|
if (format->text_wrap)
|
|
_PUSH_ATTRIBUTES_STR("wrapText", "1");
|
|
|
|
if (format->shrink)
|
|
_PUSH_ATTRIBUTES_STR("shrinkToFit", "1");
|
|
|
|
if (format->reading_order == 1)
|
|
_PUSH_ATTRIBUTES_STR("readingOrder", "1");
|
|
|
|
if (format->reading_order == 2)
|
|
_PUSH_ATTRIBUTES_STR("readingOrder", "2");
|
|
|
|
if (!STAILQ_EMPTY(&attributes))
|
|
_xml_empty_tag(self->file, "alignment", &attributes);
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <protection> element.
|
|
*/
|
|
STATIC void
|
|
_write_protection(lxw_styles *self, lxw_format *format)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
|
|
if (!format->locked)
|
|
_PUSH_ATTRIBUTES_STR("locked", "0");
|
|
|
|
if (format->hidden)
|
|
_PUSH_ATTRIBUTES_STR("hidden", "1");
|
|
|
|
_xml_empty_tag(self->file, "protection", &attributes);
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <xf> element.
|
|
*/
|
|
STATIC void
|
|
_write_xf(lxw_styles *self, lxw_format *format)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
uint8_t has_protection = (!format->locked) | format->hidden;
|
|
uint8_t has_alignment = _has_alignment(format);
|
|
uint8_t apply_alignment = _apply_alignment(format);
|
|
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_INT("numFmtId", format->num_format_index);
|
|
_PUSH_ATTRIBUTES_INT("fontId", format->font_index);
|
|
_PUSH_ATTRIBUTES_INT("fillId", format->fill_index);
|
|
_PUSH_ATTRIBUTES_INT("borderId", format->border_index);
|
|
_PUSH_ATTRIBUTES_STR("xfId", "0");
|
|
|
|
if (format->num_format_index > 0)
|
|
_PUSH_ATTRIBUTES_STR("applyNumberFormat", "1");
|
|
|
|
/* Add applyFont attribute if XF format uses a font element. */
|
|
if (format->font_index > 0)
|
|
_PUSH_ATTRIBUTES_STR("applyFont", "1");
|
|
|
|
/* Add applyFill attribute if XF format uses a fill element. */
|
|
if (format->fill_index > 0)
|
|
_PUSH_ATTRIBUTES_STR("applyFill", "1");
|
|
|
|
/* Add applyBorder attribute if XF format uses a border element. */
|
|
if (format->border_index > 0)
|
|
_PUSH_ATTRIBUTES_STR("applyBorder", "1");
|
|
|
|
/* We can also have applyAlignment without a sub-element. */
|
|
if (apply_alignment)
|
|
_PUSH_ATTRIBUTES_STR("applyAlignment", "1");
|
|
|
|
if (has_protection)
|
|
_PUSH_ATTRIBUTES_STR("applyProtection", "1");
|
|
|
|
/* Write XF with sub-elements if required. */
|
|
if (has_alignment || has_protection) {
|
|
_xml_start_tag(self->file, "xf", &attributes);
|
|
|
|
if (has_alignment)
|
|
_write_alignment(self, format);
|
|
|
|
if (has_protection)
|
|
_write_protection(self, format);
|
|
|
|
_xml_end_tag(self->file, "xf");
|
|
}
|
|
else {
|
|
_xml_empty_tag(self->file, "xf", &attributes);
|
|
}
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <cellXfs> element.
|
|
*/
|
|
STATIC void
|
|
_write_cell_xfs(lxw_styles *self)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
lxw_format *format;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_INT("count", self->xf_count);
|
|
|
|
_xml_start_tag(self->file, "cellXfs", &attributes);
|
|
|
|
STAILQ_FOREACH(format, self->xf_formats, list_pointers) {
|
|
_write_xf(self, format);
|
|
}
|
|
|
|
_xml_end_tag(self->file, "cellXfs");
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <cellStyle> element.
|
|
*/
|
|
STATIC void
|
|
_write_cell_style(lxw_styles *self)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_STR("name", "Normal");
|
|
_PUSH_ATTRIBUTES_STR("xfId", "0");
|
|
_PUSH_ATTRIBUTES_STR("builtinId", "0");
|
|
|
|
_xml_empty_tag(self->file, "cellStyle", &attributes);
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <cellStyles> element.
|
|
*/
|
|
STATIC void
|
|
_write_cell_styles(lxw_styles *self)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_STR("count", "1");
|
|
|
|
_xml_start_tag(self->file, "cellStyles", &attributes);
|
|
_write_cell_style(self);
|
|
_xml_end_tag(self->file, "cellStyles");
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <dxfs> element.
|
|
*/
|
|
STATIC void
|
|
_write_dxfs(lxw_styles *self)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_STR("count", "0");
|
|
|
|
_xml_empty_tag(self->file, "dxfs", &attributes);
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*
|
|
* Write the <tableStyles> element.
|
|
*/
|
|
STATIC void
|
|
_write_table_styles(lxw_styles *self)
|
|
{
|
|
struct xml_attribute_list attributes;
|
|
struct xml_attribute *attribute;
|
|
|
|
_INIT_ATTRIBUTES();
|
|
_PUSH_ATTRIBUTES_STR("count", "0");
|
|
_PUSH_ATTRIBUTES_STR("defaultTableStyle", "TableStyleMedium9");
|
|
_PUSH_ATTRIBUTES_STR("defaultPivotStyle", "PivotStyleLight16");
|
|
|
|
_xml_empty_tag(self->file, "tableStyles", &attributes);
|
|
|
|
_FREE_ATTRIBUTES();
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* XML file assembly functions.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/*
|
|
* Assemble and write the XML file.
|
|
*/
|
|
void
|
|
_styles_assemble_xml_file(lxw_styles *self)
|
|
{
|
|
/* Write the XML declaration. */
|
|
_styles_xml_declaration(self);
|
|
|
|
/* Add the style sheet. */
|
|
_write_style_sheet(self);
|
|
|
|
/* Write the number formats. */
|
|
_write_num_fmts(self);
|
|
|
|
/* Write the fonts. */
|
|
_write_fonts(self);
|
|
|
|
/* Write the fills. */
|
|
_write_fills(self);
|
|
|
|
/* Write the borders element. */
|
|
_write_borders(self);
|
|
|
|
/* Write the cellStyleXfs element. */
|
|
_write_cell_style_xfs(self);
|
|
|
|
/* Write the cellXfs element. */
|
|
_write_cell_xfs(self);
|
|
|
|
/* Write the cellStyles element. */
|
|
_write_cell_styles(self);
|
|
|
|
/* Write the dxfs element. */
|
|
_write_dxfs(self);
|
|
|
|
/* Write the tableStyles element. */
|
|
_write_table_styles(self);
|
|
|
|
/* Write the colors element. */
|
|
/* _write_colors(self); */
|
|
|
|
/* Close the style sheet tag. */
|
|
_xml_end_tag(self->file, "styleSheet");
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Public functions.
|
|
*
|
|
****************************************************************************/
|