diff --git a/Changes.txt b/Changes.txt index 834d859e..fa88e586 100644 --- a/Changes.txt +++ b/Changes.txt @@ -1,22 +1,26 @@ /** @page changes Changes +## 0.3.1 January 9 2016 + +- Improved performance 20-30% for large data files. + ## 0.3.0 January 4 2016 - Renamed `worksheet_set_row()` function to `worksheet_set_row_opt()` for consistency with current and future APIs. The `worksheet_set_row()` function is now used without the options parameter. - + Note: This is a backward incompatible change. - - + + - Renamed `worksheet_set_column()` function to `worksheet_set_column_opt()` for consistency with current and future APIs. The `worksheet_set_column()` function is now used without the options parameter. - + Note: This is a backward incompatible change. - + ## 0.2.9 January 3 2016 diff --git a/include/xlsxwriter.h b/include/xlsxwriter.h index e99718f6..88166579 100644 --- a/include/xlsxwriter.h +++ b/include/xlsxwriter.h @@ -18,6 +18,6 @@ #include "xlsxwriter/format.h" #include "xlsxwriter/utility.h" -#define LXW_VERSION "0.3.0" +#define LXW_VERSION "0.3.1" #endif /* __LXW_XLSXWRITER_H__ */ diff --git a/include/xlsxwriter/xmlwriter.h b/include/xlsxwriter/xmlwriter.h index 9947564f..449b0848 100644 --- a/include/xlsxwriter/xmlwriter.h +++ b/include/xlsxwriter/xmlwriter.h @@ -167,6 +167,8 @@ void lxw_xml_data_element(FILE * xmlfile, char *lxw_escape_control_characters(const char *string); +char *lxw_escape_data(const char *data); + /* *INDENT-OFF* */ #ifdef __cplusplus } diff --git a/libxlsxwriter.podspec b/libxlsxwriter.podspec index 914ebf1d..9f08e6f0 100644 --- a/libxlsxwriter.podspec +++ b/libxlsxwriter.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "libxlsxwriter" - s.version = "0.3.0" + s.version = "0.3.1" s.summary = "Libxlsxwriter: A C library for creating Excel XLSX files." s.ios.deployment_target = "6.0" s.osx.deployment_target = "10.8" diff --git a/src/core.c b/src/core.c index 55dc6ebf..f599d041 100644 --- a/src/core.c +++ b/src/core.c @@ -92,12 +92,12 @@ _write_cp_core_properties(lxw_core *self) LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("xmlns:cp", - "http://schemas.openxmlformats.org/package/2006/metadata/core-properties"); + "http://schemas.openxmlformats.org/package/2006/metadata/core-properties"); LXW_PUSH_ATTRIBUTES_STR("xmlns:dc", "http://purl.org/dc/elements/1.1/"); LXW_PUSH_ATTRIBUTES_STR("xmlns:dcterms", "http://purl.org/dc/terms/"); LXW_PUSH_ATTRIBUTES_STR("xmlns:dcmitype", "http://purl.org/dc/dcmitype/"); LXW_PUSH_ATTRIBUTES_STR("xmlns:xsi", - "http://www.w3.org/2001/XMLSchema-instance"); + "http://www.w3.org/2001/XMLSchema-instance"); lxw_xml_start_tag(self->file, "cp:coreProperties", &attributes); diff --git a/src/styles.c b/src/styles.c index 4b1022c8..fa3d46b6 100644 --- a/src/styles.c +++ b/src/styles.c @@ -89,7 +89,7 @@ _write_style_sheet(lxw_styles *self) struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("xmlns", - "http://schemas.openxmlformats.org/spreadsheetml/2006/main"); + "http://schemas.openxmlformats.org/spreadsheetml/2006/main"); lxw_xml_start_tag(self->file, "styleSheet", &attributes); diff --git a/src/worksheet.c b/src/worksheet.c index 94136b39..909bcb7a 100644 --- a/src/worksheet.c +++ b/src/worksheet.c @@ -2173,30 +2173,77 @@ _get_image_properties(lxw_image_options *image_options) ****************************************************************************/ /* - * Write out a number worksheet cell. + * Write out a number worksheet cell. Doesn't use the xml functions as an + * optimization in the inner cell writing loop. */ STATIC void -_write_number_cell(lxw_worksheet *self, lxw_cell *cell) +_write_number_cell(lxw_worksheet *self, char *range, + int32_t style_index, lxw_cell *cell) { - char data[ATTR_32]; - - lxw_snprintf(data, ATTR_32, "%.16g", cell->u.number); - - lxw_xml_data_element(self->file, "v", data, NULL); - + if (style_index) + fprintf(self->file, + "%.16g", + range, style_index, cell->u.number); + else + fprintf(self->file, + "%.16g", range, cell->u.number); } /* - * Write out a string worksheet cell. + * Write out a string worksheet cell. Doesn't use the xml functions as an + * optimization in the inner cell writing loop. */ STATIC void -_write_string_cell(lxw_worksheet *self, lxw_cell *cell) +_write_string_cell(lxw_worksheet *self, char *range, + int32_t style_index, lxw_cell *cell) { - char data[ATTR_32]; + if (style_index) + fprintf(self->file, + "%d", + range, style_index, cell->u.string_id); + else + fprintf(self->file, + "%d", + range, cell->u.string_id); +} - lxw_snprintf(data, ATTR_32, "%d", cell->u.string_id); +/* + * Write out an inline string. Doesn't use the xml functions as an + * optimization in the inner cell writing loop. + */ +STATIC void +_write_inline_string_cell(lxw_worksheet *self, char *range, + int32_t style_index, lxw_cell *cell) +{ + char *string = lxw_escape_data(cell->u.string); - lxw_xml_data_element(self->file, "v", data, NULL); + /* Add attribute to preserve leading or trailing whitespace. */ + if (isspace((unsigned char) string[0]) + || isspace((unsigned char) string[strlen(string) - 1])) { + + if (style_index) + fprintf(self->file, + "" + "%s", + range, style_index, string); + else + fprintf(self->file, + "" + "%s", + range, string); + } + else { + if (style_index) + fprintf(self->file, + "" + "%s", range, style_index, string); + else + fprintf(self->file, + "" + "%s", range, string); + } + + free(string); } /* @@ -2235,30 +2282,6 @@ _write_array_formula_num_cell(lxw_worksheet *self, lxw_cell *cell) LXW_FREE_ATTRIBUTES(); } -/* - * Write out an inline string. - */ -STATIC void -_write_inline_string_cell(lxw_worksheet *self, lxw_cell *cell) -{ - struct xml_attribute_list attributes; - struct xml_attribute *attribute; - char *string = cell->u.string; - - LXW_INIT_ATTRIBUTES(); - - /* Add attribute to preserve leading or trailing whitespace. */ - if (isspace((unsigned char) string[0]) - || isspace((unsigned char) string[strlen(string) - 1])) - LXW_PUSH_ATTRIBUTES_STR("xml:space", "preserve"); - - lxw_xml_start_tag(self->file, "is", NULL); - lxw_xml_data_element(self->file, "t", string, &attributes); - lxw_xml_end_tag(self->file, "is"); - - LXW_FREE_ATTRIBUTES(); -} - /* * Calculate the "spans" attribute of the tag. This is an XLSX * optimization and isn't strictly required. However, it makes comparing @@ -2308,44 +2331,44 @@ _write_cell(lxw_worksheet *self, lxw_cell *cell, lxw_format *row_format) char range[MAX_CELL_NAME_LENGTH] = { 0 }; lxw_row_t row_num = cell->row_num; lxw_col_t col_num = cell->col_num; - int32_t index = 0; + int32_t style_index = 0; lxw_rowcol_to_cell(range, row_num, col_num); + if (cell->format) { + style_index = lxw_format_get_xf_index(cell->format); + } + else if (row_format) { + style_index = lxw_format_get_xf_index(row_format); + } + else if (col_num < self->col_formats_max && self->col_formats[col_num]) { + style_index = lxw_format_get_xf_index(self->col_formats[col_num]); + } + + /* Unrolled optimization for most commonly written cell types. */ + if (cell->type == NUMBER_CELL) { + _write_number_cell(self, range, style_index, cell); + return; + } + + if (cell->type == STRING_CELL) { + _write_string_cell(self, range, style_index, cell); + return; + } + + if (cell->type == INLINE_STRING_CELL) { + _write_inline_string_cell(self, range, style_index, cell); + return; + } + + /* For other cell types use the general functions. */ LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("r", range); - if (cell->format) { - index = lxw_format_get_xf_index(cell->format); - } - else if (row_format) { - index = lxw_format_get_xf_index(row_format); - } - else if (col_num < self->col_formats_max && self->col_formats[col_num]) { - index = lxw_format_get_xf_index(self->col_formats[col_num]); - } + if (style_index) + LXW_PUSH_ATTRIBUTES_INT("s", style_index); - if (index) - LXW_PUSH_ATTRIBUTES_INT("s", index); - - if (cell->type == NUMBER_CELL) { - lxw_xml_start_tag(self->file, "c", &attributes); - _write_number_cell(self, cell); - lxw_xml_end_tag(self->file, "c"); - } - else if (cell->type == STRING_CELL) { - LXW_PUSH_ATTRIBUTES_STR("t", "s"); - lxw_xml_start_tag(self->file, "c", &attributes); - _write_string_cell(self, cell); - lxw_xml_end_tag(self->file, "c"); - } - else if (cell->type == INLINE_STRING_CELL) { - LXW_PUSH_ATTRIBUTES_STR("t", "inlineStr"); - lxw_xml_start_tag(self->file, "c", &attributes); - _write_inline_string_cell(self, cell); - lxw_xml_end_tag(self->file, "c"); - } - else if (cell->type == FORMULA_CELL) { + if (cell->type == FORMULA_CELL) { lxw_xml_start_tag(self->file, "c", &attributes); _write_formula_num_cell(self, cell); lxw_xml_end_tag(self->file, "c"); diff --git a/src/xmlwriter.c b/src/xmlwriter.c index 70cdafbc..e244fe0c 100644 --- a/src/xmlwriter.c +++ b/src/xmlwriter.c @@ -18,7 +18,7 @@ /* Forward declarations. */ char *_escape_attributes(struct xml_attribute *attribute); -char *_escape_data(const char *data); +char *lxw_escape_data(const char *data); void _fprint_escaped_attributes(FILE * xmlfile, struct xml_attribute_list *attributes); @@ -180,7 +180,7 @@ _escape_attributes(struct xml_attribute *attribute) * in that double quotes are not escaped by Excel. */ char * -_escape_data(const char *data) +lxw_escape_data(const char *data) { size_t encoded_len = (strlen(data) * 5 + 1); @@ -305,7 +305,7 @@ _fprint_escaped_data(FILE * xmlfile, const char *data) fprintf(xmlfile, "%s", data); } else { - char *encoded = _escape_data(data); + char *encoded = lxw_escape_data(data); if (encoded) { fprintf(xmlfile, "%s", encoded); free(encoded);