From 0b171db26e10222b744fdd750bb61af1e305c827 Mon Sep 17 00:00:00 2001 From: Jeroen Ooms Date: Fri, 9 Feb 2024 15:29:44 +0100 Subject: [PATCH] Import libxlsxwriter From https://github.com/jmcnamara/libxlsxwriter/commit/b0c76b33 --- DESCRIPTION | 2 +- NEWS | 3 + src/Makevars | 5 +- src/include/xlsxwriter.h | 7 +- src/include/xlsxwriter/app.h | 2 +- src/include/xlsxwriter/chart.h | 31 +- src/include/xlsxwriter/chartsheet.h | 10 +- src/include/xlsxwriter/comment.h | 2 +- src/include/xlsxwriter/common.h | 131 +- src/include/xlsxwriter/content_types.h | 5 +- src/include/xlsxwriter/core.h | 2 +- src/include/xlsxwriter/custom.h | 2 +- src/include/xlsxwriter/drawing.h | 9 +- src/include/xlsxwriter/format.h | 24 +- src/include/xlsxwriter/hash_table.h | 2 +- src/include/xlsxwriter/metadata.h | 49 + src/include/xlsxwriter/packager.h | 41 +- src/include/xlsxwriter/relationships.h | 2 +- src/include/xlsxwriter/shared_strings.h | 2 +- src/include/xlsxwriter/styles.h | 4 +- src/include/xlsxwriter/table.h | 51 + src/include/xlsxwriter/theme.h | 2 +- src/include/xlsxwriter/third_party/ioapi.h | 54 +- src/include/xlsxwriter/third_party/md5.h | 22 +- src/include/xlsxwriter/third_party/zip.h | 312 +-- src/include/xlsxwriter/utility.h | 39 +- src/include/xlsxwriter/vml.h | 2 +- src/include/xlsxwriter/workbook.h | 82 +- src/include/xlsxwriter/worksheet.h | 1070 ++++++++- src/include/xlsxwriter/xmlwriter.h | 16 +- src/libxlsxwriter/app.c | 2 +- src/libxlsxwriter/chart.c | 72 +- src/libxlsxwriter/chartsheet.c | 11 +- src/libxlsxwriter/comment.c | 2 +- src/libxlsxwriter/content_types.c | 22 +- src/libxlsxwriter/core.c | 2 +- src/libxlsxwriter/custom.c | 2 +- src/libxlsxwriter/drawing.c | 50 +- src/libxlsxwriter/format.c | 13 +- src/libxlsxwriter/hash_table.c | 2 +- src/libxlsxwriter/metadata.c | 283 +++ src/libxlsxwriter/packager.c | 568 ++++- src/libxlsxwriter/relationships.c | 2 +- src/libxlsxwriter/shared_strings.c | 2 +- src/libxlsxwriter/styles.c | 23 +- src/libxlsxwriter/table.c | 304 +++ src/libxlsxwriter/theme.c | 2 +- src/libxlsxwriter/utility.c | 113 +- src/libxlsxwriter/vml.c | 144 +- src/libxlsxwriter/workbook.c | 258 ++- src/libxlsxwriter/worksheet.c | 2303 +++++++++++++++++--- src/libxlsxwriter/xmlwriter.c | 43 +- src/md5/md5.c | 24 +- src/md5/md5.h | 22 +- src/minizip/ioapi.c | 86 +- src/minizip/ioapi.h | 54 +- src/minizip/zip.c | 393 ++-- src/minizip/zip.h | 312 +-- 58 files changed, 5662 insertions(+), 1437 deletions(-) create mode 100644 src/include/xlsxwriter/metadata.h create mode 100644 src/include/xlsxwriter/table.h create mode 100644 src/libxlsxwriter/metadata.c create mode 100644 src/libxlsxwriter/table.c diff --git a/DESCRIPTION b/DESCRIPTION index a3287a1..171ea0e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: writexl Type: Package Title: Export Data Frames to Excel 'xlsx' Format -Version: 1.4.2 +Version: 1.5.0 Authors@R: c( person("Jeroen", "Ooms", ,"jeroen@berkeley.edu", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-4035-0289")), diff --git a/NEWS b/NEWS index 569c09f..a9f5039 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,6 @@ +1.5.0 + - Update libxlsxwriter from b0c76b33 + 1.4.2 - Bugfix for NA timestamps diff --git a/src/Makevars b/src/Makevars index 242d264..79b3aca 100644 --- a/src/Makevars +++ b/src/Makevars @@ -5,8 +5,9 @@ LIBXLSXWRITER = \ libxlsxwriter/core.o libxlsxwriter/relationships.o libxlsxwriter/worksheet.o \ libxlsxwriter/custom.o libxlsxwriter/shared_strings.o libxlsxwriter/xmlwriter.o \ libxlsxwriter/drawing.o libxlsxwriter/styles.o tmpfileplus/tmpfileplus.o \ - libxlsxwriter/chartsheet.o minizip/ioapi.o minizip/zip.o \ - libxlsxwriter/comment.o libxlsxwriter/vml.o md5/md5.o + libxlsxwriter/chartsheet.o minizip/ioapi.o minizip/zip.o libxlsxwriter/metadata.o \ + libxlsxwriter/comment.o libxlsxwriter/vml.o md5/md5.o libxlsxwriter/table.o \ + STATICLIB=libxlsxwriter/libstatxlsxwriter.a diff --git a/src/include/xlsxwriter.h b/src/include/xlsxwriter.h index 1a59460..702b08e 100644 --- a/src/include/xlsxwriter.h +++ b/src/include/xlsxwriter.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. */ /** @@ -18,7 +18,8 @@ #include "xlsxwriter/format.h" #include "xlsxwriter/utility.h" -#define LXW_VERSION "1.0.3" -#define LXW_VERSION_ID 103 +#define LXW_VERSION "1.1.5" +#define LXW_VERSION_ID 115 +#define LXW_SOVERSION "5" #endif /* __LXW_XLSXWRITER_H__ */ diff --git a/src/include/xlsxwriter/app.h b/src/include/xlsxwriter/app.h index a6a7e37..34f2e3b 100644 --- a/src/include/xlsxwriter/app.h +++ b/src/include/xlsxwriter/app.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * app - A libxlsxwriter library for creating Excel XLSX app files. * diff --git a/src/include/xlsxwriter/chart.h b/src/include/xlsxwriter/chart.h index d30194d..3a90056 100644 --- a/src/include/xlsxwriter/chart.h +++ b/src/include/xlsxwriter/chart.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * chart - A libxlsxwriter library for creating Excel XLSX chart files. * @@ -699,7 +699,7 @@ typedef struct lxw_chart_pattern { typedef struct lxw_chart_font { /** The chart font name, such as "Arial" or "Calibri". */ - char *name; + const char *name; /** The chart font size. The default is 11. */ double size; @@ -713,9 +713,12 @@ typedef struct lxw_chart_font { /** The chart font underline property. Set to 0 or 1. */ uint8_t underline; - /** The chart font rotation property. Range: -90 to 90, and 270-271. - * The angle 270 gives a stacked (top to bottom) alignment. - * The angle 271 gives a stacked alignment for East Asian fonts. + /** The chart font rotation property. Range: -90 to 90, and 270, 271 and 360: + * + * - The angles -90 to 90 are the normal range shown in the Excel user interface. + * - The angle 270 gives a stacked (top to bottom) alignment. + * - The angle 271 gives a stacked alignment for East Asian fonts. + * - The angle 360 gives an explicit angle of 0 to override the y axis default. * */ int32_t rotation; @@ -797,7 +800,7 @@ typedef struct lxw_chart_data_label { /** The string or formula value for the data label. See * @ref chart_custom_labels. */ - char *value; + const char *value; /** Option to hide/delete the data label from the chart series. * See @ref chart_custom_labels. */ @@ -3232,7 +3235,7 @@ void chart_title_set_name_range(lxw_chart *chart, const char *sheetname, * chart title: * * @code - * lxw_chart_font font = {.bold = LXW_TRUE, .color = LXW_COLOR_BLUE}; + * lxw_chart_font font = {.color = LXW_COLOR_BLUE}; * * chart_title_set_name(chart, "Year End Results"); * chart_title_set_name_font(chart, &font); @@ -3240,6 +3243,20 @@ void chart_title_set_name_range(lxw_chart *chart, const char *sheetname, * * @image html chart_title_set_name_font.png * + * In Excel a chart title font is bold by default (as shown in the image + * above). To turn off bold in the font you cannot use #LXW_FALSE (0) since + * that is indistinguishable from an uninitialized value. Instead you should + * use #LXW_EXPLICIT_FALSE: + * + * @code + * lxw_chart_font font = {.bold = LXW_EXPLICIT_FALSE, .color = LXW_COLOR_BLUE}; + * + * chart_title_set_name(chart, "Year End Results"); + * chart_title_set_name_font(chart, &font); + * @endcode + * + * @image html chart_title_set_name_font2.png + * * For more information see @ref chart_fonts. */ void chart_title_set_name_font(lxw_chart *chart, lxw_chart_font *font); diff --git a/src/include/xlsxwriter/chartsheet.h b/src/include/xlsxwriter/chartsheet.h index c1baccd..5406249 100644 --- a/src/include/xlsxwriter/chartsheet.h +++ b/src/include/xlsxwriter/chartsheet.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * chartsheet - A libxlsxwriter library for creating Excel XLSX chartsheet files. * @@ -80,10 +80,10 @@ typedef struct lxw_chartsheet { struct lxw_protection_obj protection; uint8_t is_protected; - char *name; - char *quoted_name; - char *tmpdir; - uint32_t index; + const char *name; + const char *quoted_name; + const char *tmpdir; + uint16_t index; uint8_t active; uint8_t selected; uint8_t hidden; diff --git a/src/include/xlsxwriter/comment.h b/src/include/xlsxwriter/comment.h index 9a8a59c..e13f353 100644 --- a/src/include/xlsxwriter/comment.h +++ b/src/include/xlsxwriter/comment.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * comment - A libxlsxwriter library for creating Excel XLSX comment files. * diff --git a/src/include/xlsxwriter/common.h b/src/include/xlsxwriter/common.h index e8e3989..12679b4 100644 --- a/src/include/xlsxwriter/common.h +++ b/src/include/xlsxwriter/common.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. */ /** @@ -9,7 +9,7 @@ * * @brief Common functions and defines for the libxlsxwriter library. * - * + * * */ #ifndef __LXW_COMMON_H__ @@ -50,7 +50,10 @@ enum lxw_boolean { /** False value. */ LXW_FALSE, /** True value. */ - LXW_TRUE + LXW_TRUE, + /** False value. Used to turn off a property that is default on, in order + * to distinguish it from an uninitialized value. */ + LXW_EXPLICIT_FALSE }; /** @@ -203,7 +206,7 @@ enum lxw_custom_property_types { #define LXW_DATETIME_LENGTH sizeof("2016-12-12T23:00:00Z") /* GUID string length. */ -#define LXW_GUID_LENGTH sizeof("{12345678-1234-1234-1234-1234567890AB}") +#define LXW_GUID_LENGTH sizeof("{12345678-1234-1234-1234-1234567890AB}\0") #define LXW_EPOCH_1900 0 #define LXW_EPOCH_1904 1 @@ -223,13 +226,14 @@ enum lxw_custom_property_types { #define LXW_SCHEMA_DOCUMENT LXW_SCHEMA_ROOT "/officeDocument/2006/relationships" #define LXW_SCHEMA_CONTENT LXW_SCHEMA_ROOT "/package/2006/content-types" +/* Use REprintf() for error handling when compiled as an R library. */ #ifdef USE_R_LANG #include - #define LXW_PRINTF REprintf - #define LXW_STDERR +#define LXW_PRINTF REprintf +#define LXW_STDERR #else - #define LXW_PRINTF fprintf - #define LXW_STDERR stderr, +#define LXW_PRINTF fprintf +#define LXW_STDERR stderr, #endif #define LXW_ERROR(message) \ @@ -239,32 +243,42 @@ enum lxw_custom_property_types { LXW_ERROR("Memory allocation failed.") #define GOTO_LABEL_ON_MEM_ERROR(pointer, label) \ - if (!pointer) { \ - LXW_MEM_ERROR(); \ - goto label; \ - } + do { \ + if (!pointer) { \ + LXW_MEM_ERROR(); \ + goto label; \ + } \ + } while (0) #define RETURN_ON_MEM_ERROR(pointer, error) \ - if (!pointer) { \ - LXW_MEM_ERROR(); \ - return error; \ - } + do { \ + if (!pointer) { \ + LXW_MEM_ERROR(); \ + return error; \ + } \ + } while (0) #define RETURN_VOID_ON_MEM_ERROR(pointer) \ - if (!pointer) { \ - LXW_MEM_ERROR(); \ - return; \ - } + do { \ + if (!pointer) { \ + LXW_MEM_ERROR(); \ + return; \ + } \ + } while (0) #define RETURN_ON_ERROR(error) \ - if (error) \ - return error; + do { \ + if (error) \ + return error; \ + } while (0) #define RETURN_AND_ZIPCLOSE_ON_ERROR(error) \ - if (error) { \ - zipClose(self->zipfile, NULL); \ - return error; \ -} + do { \ + if (error) { \ + zipClose(self->zipfile, NULL); \ + return error; \ + } \ + } while (0) #define LXW_WARN(message) \ LXW_PRINTF(LXW_STDERR "[WARNING]: " message "\n") @@ -279,52 +293,67 @@ enum lxw_custom_property_types { #define LXW_WARN_FORMAT2(message, var1, var2) \ LXW_PRINTF(LXW_STDERR "[WARNING]: " message "\n", var1, var2) +#define LXW_WARN_FORMAT3(message, var1, var2, var3) \ + LXW_PRINTF(LXW_STDERR "[WARNING]: " message "\n", var1, var2, var3) + /* Chart axis type checks. */ #define LXW_WARN_CAT_AXIS_ONLY(function) \ - if (!axis->is_category) { \ - LXW_PRINTF(LXW_STDERR "[WARNING]: " \ - function "() is only valid for category axes\n"); \ - return; \ - } + do { \ + if (!axis->is_category) { \ + LXW_PRINTF(LXW_STDERR "[WARNING]: " \ + function "() is only valid for category axes\n"); \ + return; \ + } \ + } while (0) #define LXW_WARN_VALUE_AXIS_ONLY(function) \ - if (!axis->is_value) { \ - LXW_PRINTF(LXW_STDERR "[WARNING]: " \ + do { \ + if (!axis->is_value) { \ + LXW_PRINTF(LXW_STDERR "[WARNING]: " \ function "() is only valid for value axes\n"); \ - return; \ - } + return; \ + } \ + } while (0) #define LXW_WARN_DATE_AXIS_ONLY(function) \ - if (!axis->is_date) { \ - LXW_PRINTF(LXW_STDERR "[WARNING]: " \ - function "() is only valid for date axes\n"); \ - return; \ - } + do { \ + if (!axis->is_date) { \ + LXW_PRINTF(LXW_STDERR "[WARNING]: " \ + function "() is only valid for date axes\n"); \ + return; \ + } \ + } while (0) #define LXW_WARN_CAT_AND_DATE_AXIS_ONLY(function) \ - if (!axis->is_category && !axis->is_date) { \ - LXW_PRINTF(LXW_STDERR "[WARNING]: " \ + do { \ + if (!axis->is_category && !axis->is_date) { \ + LXW_PRINTF(LXW_STDERR "[WARNING]: " \ function "() is only valid for category and date axes\n"); \ - return; \ - } + return; \ + } \ + } while (0) #define LXW_WARN_VALUE_AND_DATE_AXIS_ONLY(function) \ - if (!axis->is_value && !axis->is_date) { \ - LXW_PRINTF(LXW_STDERR "[WARNING]: " \ + do { \ + if (!axis->is_value && !axis->is_date) { \ + LXW_PRINTF(LXW_STDERR "[WARNING]: " \ function "() is only valid for value and date axes\n"); \ - return; \ - } + return; \ + } \ + } while (0) #ifndef LXW_BIG_ENDIAN +#define LXW_UINT16_HOST(n) (n) +#define LXW_UINT32_HOST(n) (n) +#define LXW_UINT16_NETWORK(n) ((((n) & 0x00FF) << 8) | (((n) & 0xFF00) >> 8)) #define LXW_UINT32_NETWORK(n) ((((n) & 0xFF) << 24) | \ (((n) & 0xFF00) << 8) | \ (((n) & 0xFF0000) >> 8) | \ (((n) & 0xFF000000) >> 24)) -#define LXW_UINT16_NETWORK(n) ((((n) & 0x00FF) << 8) | (((n) & 0xFF00) >> 8)) -#define LXW_UINT32_HOST(n) (n) #else -#define LXW_UINT32_NETWORK(n) (n) #define LXW_UINT16_NETWORK(n) (n) +#define LXW_UINT32_NETWORK(n) (n) +#define LXW_UINT16_HOST(n) ((((n) & 0x00FF) << 8) | (((n) & 0xFF00) >> 8)) #define LXW_UINT32_HOST(n) ((((n) & 0xFF) << 24) | \ (((n) & 0xFF00) << 8) | \ (((n) & 0xFF0000) >> 8) | \ diff --git a/src/include/xlsxwriter/content_types.h b/src/include/xlsxwriter/content_types.h index 03649e3..1cb811b 100644 --- a/src/include/xlsxwriter/content_types.h +++ b/src/include/xlsxwriter/content_types.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * content_types - A libxlsxwriter library for creating Excel XLSX * content_types files. @@ -53,6 +53,8 @@ void lxw_ct_add_chart_name(lxw_content_types *content_types, const char *name); void lxw_ct_add_drawing_name(lxw_content_types *content_types, const char *name); +void lxw_ct_add_table_name(lxw_content_types *content_types, + const char *name); void lxw_ct_add_comment_name(lxw_content_types *content_types, const char *name); void lxw_ct_add_vml_name(lxw_content_types *content_types); @@ -60,6 +62,7 @@ void lxw_ct_add_vml_name(lxw_content_types *content_types); void lxw_ct_add_shared_strings(lxw_content_types *content_types); void lxw_ct_add_calc_chain(lxw_content_types *content_types); void lxw_ct_add_custom_properties(lxw_content_types *content_types); +void lxw_ct_add_metadata(lxw_content_types *content_types); /* Declarations required for unit testing. */ #ifdef TESTING diff --git a/src/include/xlsxwriter/core.h b/src/include/xlsxwriter/core.h index 998bc37..40479ba 100644 --- a/src/include/xlsxwriter/core.h +++ b/src/include/xlsxwriter/core.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * core - A libxlsxwriter library for creating Excel XLSX core files. * diff --git a/src/include/xlsxwriter/custom.h b/src/include/xlsxwriter/custom.h index 9f2e15f..3231257 100644 --- a/src/include/xlsxwriter/custom.h +++ b/src/include/xlsxwriter/custom.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * custom - A libxlsxwriter library for creating Excel custom property files. * diff --git a/src/include/xlsxwriter/drawing.h b/src/include/xlsxwriter/drawing.h index f455758..1025a04 100644 --- a/src/include/xlsxwriter/drawing.h +++ b/src/include/xlsxwriter/drawing.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * drawing - A libxlsxwriter library for creating Excel XLSX drawing files. * @@ -27,7 +27,8 @@ enum image_types { LXW_IMAGE_UNKNOWN = 0, LXW_IMAGE_PNG, LXW_IMAGE_JPEG, - LXW_IMAGE_BMP + LXW_IMAGE_BMP, + LXW_IMAGE_GIF }; /* Coordinates used in a drawing object. */ @@ -44,8 +45,8 @@ typedef struct lxw_drawing_object { uint8_t anchor; struct lxw_drawing_coords from; struct lxw_drawing_coords to; - uint32_t col_absolute; - uint32_t row_absolute; + uint64_t col_absolute; + uint64_t row_absolute; uint32_t width; uint32_t height; uint8_t shape; diff --git a/src/include/xlsxwriter/format.h b/src/include/xlsxwriter/format.h index d1915e5..9ff8ee1 100644 --- a/src/include/xlsxwriter/format.h +++ b/src/include/xlsxwriter/format.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. */ /** @@ -435,6 +435,8 @@ typedef struct lxw_format { uint8_t color_indexed; uint8_t font_only; + uint8_t quote_prefix; + STAILQ_ENTRY (lxw_format) list_pointers; } lxw_format; @@ -1278,6 +1280,26 @@ void format_set_diag_border(lxw_format *format, uint8_t style); */ void format_set_diag_color(lxw_format *format, lxw_color_t color); +/** + * @brief Turn on quote prefix for the format. + * + * @param format Pointer to a Format instance. + * + * Set the quote prefix property of a format to ensure a string is treated + * as a string after editing. This is the same as prefixing the string with + * a single quote in Excel. You don't need to add the quote to the + * string but you do need to add the format. + * + * @code + * format = workbook_add_format(workbook); + * format_set_quote_prefix(format); + * + * worksheet_write_string(worksheet, 0, 0, "=Foo", format); + * @endcode + * + */ +void format_set_quote_prefix(lxw_format *format); + void format_set_font_outline(lxw_format *format); void format_set_font_shadow(lxw_format *format); void format_set_font_family(lxw_format *format, uint8_t value); diff --git a/src/include/xlsxwriter/hash_table.h b/src/include/xlsxwriter/hash_table.h index 4bafeaa..d334d14 100644 --- a/src/include/xlsxwriter/hash_table.h +++ b/src/include/xlsxwriter/hash_table.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * hash_table - Hash table functions for libxlsxwriter. * diff --git a/src/include/xlsxwriter/metadata.h b/src/include/xlsxwriter/metadata.h new file mode 100644 index 0000000..86cb5cd --- /dev/null +++ b/src/include/xlsxwriter/metadata.h @@ -0,0 +1,49 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + * metadata - A libxlsxwriter library for creating Excel XLSX metadata files. + * + */ +#ifndef __LXW_METADATA_H__ +#define __LXW_METADATA_H__ + +#include + +#include "common.h" + +/* + * Struct to represent a metadata object. + */ +typedef struct lxw_metadata { + + FILE *file; + +} lxw_metadata; + + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +lxw_metadata *lxw_metadata_new(void); +void lxw_metadata_free(lxw_metadata *metadata); +void lxw_metadata_assemble_xml_file(lxw_metadata *self); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +STATIC void _metadata_xml_declaration(lxw_metadata *self); + +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_METADATA_H__ */ diff --git a/src/include/xlsxwriter/packager.h b/src/include/xlsxwriter/packager.h index 92b5d06..e5ba793 100644 --- a/src/include/xlsxwriter/packager.h +++ b/src/include/xlsxwriter/packager.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * packager - A libxlsxwriter library for creating Excel XLSX packager files. * @@ -12,6 +12,9 @@ #include #ifdef USE_SYSTEM_MINIZIP +#ifdef __GNUC__ +#pragma GCC system_header +#endif #include "minizip/zip.h" #else #include "third_party/zip.h" @@ -24,6 +27,7 @@ #include "app.h" #include "core.h" #include "custom.h" +#include "table.h" #include "theme.h" #include "styles.h" #include "format.h" @@ -31,22 +35,25 @@ #include "relationships.h" #include "vml.h" #include "comment.h" +#include "metadata.h" #define LXW_ZIP_BUFFER_SIZE (16384) /* If zip returns a ZIP_XXX error then errno is set and we can trap that in * workbook.c. Otherwise return a default libxlsxwriter error. */ -#define RETURN_ON_ZIP_ERROR(err, default_err) \ - if (err == ZIP_ERRNO) \ - return LXW_ERROR_ZIP_FILE_OPERATION; \ - else if (err == ZIP_PARAMERROR) \ - return LXW_ERROR_ZIP_PARAMETER_ERROR; \ - else if (err == ZIP_BADZIPFILE) \ - return LXW_ERROR_ZIP_BAD_ZIP_FILE; \ - else if (err == ZIP_INTERNALERROR) \ - return LXW_ERROR_ZIP_INTERNAL_ERROR; \ - else \ - return default_err; +#define RETURN_ON_ZIP_ERROR(err, default_err) \ + do { \ + if (err == ZIP_ERRNO) \ + return LXW_ERROR_ZIP_FILE_OPERATION; \ + else if (err == ZIP_PARAMERROR) \ + return LXW_ERROR_ZIP_PARAMETER_ERROR; \ + else if (err == ZIP_BADZIPFILE) \ + return LXW_ERROR_ZIP_BAD_ZIP_FILE; \ + else if (err == ZIP_INTERNALERROR) \ + return LXW_ERROR_ZIP_INTERNAL_ERROR; \ + else \ + return default_err; \ + } while (0) /* * Struct to represent a packager. @@ -57,11 +64,13 @@ typedef struct lxw_packager { lxw_workbook *workbook; size_t buffer_size; + size_t output_buffer_size; zipFile zipfile; zip_fileinfo zipfile_info; - char *filename; - char *buffer; - char *tmpdir; + const char *filename; + const char *buffer; + char *output_buffer; + const char *tmpdir; uint8_t use_zip64; } lxw_packager; @@ -73,7 +82,7 @@ extern "C" { #endif /* *INDENT-ON* */ -lxw_packager *lxw_packager_new(const char *filename, char *tmpdir, +lxw_packager *lxw_packager_new(const char *filename, const char *tmpdir, uint8_t use_zip64); void lxw_packager_free(lxw_packager *packager); lxw_error lxw_create_package(lxw_packager *self); diff --git a/src/include/xlsxwriter/relationships.h b/src/include/xlsxwriter/relationships.h index 6f08efc..d72ad0a 100644 --- a/src/include/xlsxwriter/relationships.h +++ b/src/include/xlsxwriter/relationships.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * relationships - A libxlsxwriter library for creating Excel XLSX * relationships files. diff --git a/src/include/xlsxwriter/shared_strings.h b/src/include/xlsxwriter/shared_strings.h index 4719e65..98a2710 100644 --- a/src/include/xlsxwriter/shared_strings.h +++ b/src/include/xlsxwriter/shared_strings.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * shared_strings - A libxlsxwriter library for creating Excel XLSX * sst files. diff --git a/src/include/xlsxwriter/styles.h b/src/include/xlsxwriter/styles.h index f86e239..780fdb6 100644 --- a/src/include/xlsxwriter/styles.h +++ b/src/include/xlsxwriter/styles.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * styles - A libxlsxwriter library for creating Excel XLSX styles files. * @@ -44,7 +44,7 @@ extern "C" { lxw_styles *lxw_styles_new(void); void lxw_styles_free(lxw_styles *styles); void lxw_styles_assemble_xml_file(lxw_styles *self); -void lxw_styles_write_string_fragment(lxw_styles *self, char *string); +void lxw_styles_write_string_fragment(lxw_styles *self, const char *string); void lxw_styles_write_rich_font(lxw_styles *styles, lxw_format *format); /* Declarations required for unit testing. */ diff --git a/src/include/xlsxwriter/table.h b/src/include/xlsxwriter/table.h new file mode 100644 index 0000000..452d83c --- /dev/null +++ b/src/include/xlsxwriter/table.h @@ -0,0 +1,51 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + * table - A libxlsxwriter library for creating Excel XLSX table files. + * + */ +#ifndef __LXW_TABLE_H__ +#define __LXW_TABLE_H__ + +#include + +#include "common.h" + +/* + * Struct to represent a table object. + */ +typedef struct lxw_table { + + FILE *file; + + struct lxw_table_obj *table_obj; + +} lxw_table; + + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +lxw_table *lxw_table_new(void); +void lxw_table_free(lxw_table *table); +void lxw_table_assemble_xml_file(lxw_table *self); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +STATIC void _table_xml_declaration(lxw_table *self); + +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_TABLE_H__ */ diff --git a/src/include/xlsxwriter/theme.h b/src/include/xlsxwriter/theme.h index d86cb7e..4a2a2e6 100644 --- a/src/include/xlsxwriter/theme.h +++ b/src/include/xlsxwriter/theme.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * theme - A libxlsxwriter library for creating Excel XLSX theme files. * diff --git a/src/include/xlsxwriter/third_party/ioapi.h b/src/include/xlsxwriter/third_party/ioapi.h index 623c9bb..c79aa1e 100644 --- a/src/include/xlsxwriter/third_party/ioapi.h +++ b/src/include/xlsxwriter/third_party/ioapi.h @@ -56,7 +56,7 @@ #define ftello64 ftell #define fseeko64 fseek #else -#if defined(__FreeBSD__) || defined(__OpenBSD__) +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) || defined(MINIZIP_FOPEN_NO_64) #define fopen64 fopen #define ftello64 ftello #define fseeko64 fseeko @@ -88,7 +88,7 @@ #include "mz64conf.h" #endif -/* a type choosen by DEFINE */ +/* a type chosen by DEFINE */ #ifdef HAVE_64BIT_INT_CUSTOM typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; #else @@ -97,8 +97,7 @@ typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; typedef uint64_t ZPOS64_T; #else -/* Maximum unsigned 32-bit value used as placeholder for zip64 */ -#define MAXU32 0xffffffff + #if defined(_MSC_VER) || defined(__BORLANDC__) typedef unsigned __int64 ZPOS64_T; @@ -108,7 +107,10 @@ typedef unsigned long long int ZPOS64_T; #endif #endif - +/* Maximum unsigned 32-bit value used as placeholder for zip64 */ +#ifndef MAXU32 +#define MAXU32 (0xffffffff) +#endif #ifdef __cplusplus extern "C" { @@ -137,22 +139,18 @@ extern "C" { -/* Workaround for modified zconf.h on Gentoo system. */ -#if defined(_Z_OF) -#define OF _Z_OF -#endif -typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); -typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); -typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); -typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); -typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); +typedef voidpf (ZCALLBACK *open_file_func) (voidpf opaque, const char* filename, int mode); +typedef uLong (ZCALLBACK *read_file_func) (voidpf opaque, voidpf stream, void* buf, uLong size); +typedef uLong (ZCALLBACK *write_file_func) (voidpf opaque, voidpf stream, const void* buf, uLong size); +typedef int (ZCALLBACK *close_file_func) (voidpf opaque, voidpf stream); +typedef int (ZCALLBACK *testerror_file_func) (voidpf opaque, voidpf stream); -typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); -typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); +typedef long (ZCALLBACK *tell_file_func) (voidpf opaque, voidpf stream); +typedef long (ZCALLBACK *seek_file_func) (voidpf opaque, voidpf stream, uLong offset, int origin); -/* here is the "old" 32 bits structure structure */ +/* here is the "old" 32 bits structure */ typedef struct zlib_filefunc_def_s { open_file_func zopen_file; @@ -165,9 +163,9 @@ typedef struct zlib_filefunc_def_s voidpf opaque; } zlib_filefunc_def; -typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream)); -typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); -typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode)); +typedef ZPOS64_T (ZCALLBACK *tell64_file_func) (voidpf opaque, voidpf stream); +typedef long (ZCALLBACK *seek64_file_func) (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin); +typedef voidpf (ZCALLBACK *open64_file_func) (voidpf opaque, const void* filename, int mode); typedef struct zlib_filefunc64_def_s { @@ -181,8 +179,8 @@ typedef struct zlib_filefunc64_def_s voidpf opaque; } zlib_filefunc64_def; -void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def)); -void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); +void fill_fopen64_filefunc(zlib_filefunc64_def* pzlib_filefunc_def); +void fill_fopen_filefunc(zlib_filefunc_def* pzlib_filefunc_def); /* now internal definition, only for zip.c and unzip.h */ typedef struct zlib_filefunc64_32_def_s @@ -196,16 +194,16 @@ typedef struct zlib_filefunc64_32_def_s #define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) #define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) -/* #define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) */ -/* #define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) */ +//#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) +//#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) #define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) #define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) -voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)); -long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)); -ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)); +voidpf call_zopen64(const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode); +long call_zseek64(const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin); +ZPOS64_T call_ztell64(const zlib_filefunc64_32_def* pfilefunc,voidpf filestream); -void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); #define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) #define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) diff --git a/src/include/xlsxwriter/third_party/md5.h b/src/include/xlsxwriter/third_party/md5.h index 8d0844f..2da44bf 100644 --- a/src/include/xlsxwriter/third_party/md5.h +++ b/src/include/xlsxwriter/third_party/md5.h @@ -23,21 +23,23 @@ * See md5.c for more information. */ -#ifndef __LXW_MD5_H__ -#define __LXW_MD5_H__ +#ifdef HAVE_OPENSSL +#include +#elif !defined(_MD5_H) +#define _MD5_H /* Any 32-bit or wider unsigned integer data type will do */ -typedef unsigned int uint32_t; +typedef unsigned int MD5_u32plus; typedef struct { - uint32_t lo, hi; - uint32_t a, b, c, d; + MD5_u32plus lo, hi; + MD5_u32plus a, b, c, d; unsigned char buffer[64]; - uint32_t block[16]; -} lxw_md5_ctx; + MD5_u32plus block[16]; +} MD5_CTX; -extern void lxw_md5_init(lxw_md5_ctx *ctx); -extern void lxw_md5_update(lxw_md5_ctx *ctx, const void *data, unsigned long size); -extern void lxw_md5_final(unsigned char *result, lxw_md5_ctx *ctx); +extern void MD5_Init(MD5_CTX *ctx); +extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); +extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); #endif diff --git a/src/include/xlsxwriter/third_party/zip.h b/src/include/xlsxwriter/third_party/zip.h index c177744..efe6314 100644 --- a/src/include/xlsxwriter/third_party/zip.h +++ b/src/include/xlsxwriter/third_party/zip.h @@ -49,7 +49,7 @@ extern "C" { #endif -/* #define HAVE_BZIP2 */ +//#define HAVE_BZIP2 #ifndef _ZLIB_H #include "zlib.h" @@ -101,12 +101,12 @@ typedef voidp zipFile; /* tm_zip contain date/time info */ typedef struct tm_zip_s { - uInt tm_sec; /* seconds after the minute - [0,59] */ - uInt tm_min; /* minutes after the hour - [0,59] */ - uInt tm_hour; /* hours since midnight - [0,23] */ - uInt tm_mday; /* day of the month - [1,31] */ - uInt tm_mon; /* months since January - [0,11] */ - uInt tm_year; /* years - [1980..2044] */ + int tm_sec; /* seconds after the minute - [0,59] */ + int tm_min; /* minutes after the hour - [0,59] */ + int tm_hour; /* hours since midnight - [0,23] */ + int tm_mday; /* day of the month - [1,31] */ + int tm_mon; /* months since January - [0,11] */ + int tm_year; /* years - [1980..2044] */ } tm_zip; typedef struct @@ -126,8 +126,8 @@ typedef const char* zipcharpc; #define APPEND_STATUS_CREATEAFTER (1) #define APPEND_STATUS_ADDINZIP (2) -extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); -extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); +extern zipFile ZEXPORT zipOpen(const char *pathname, int append); +extern zipFile ZEXPORT zipOpen64(const void *pathname, int append); /* Create a zipfile. pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on @@ -144,50 +144,55 @@ extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); /* Note : there is no delete function into a zipfile. If you want delete file into a zipfile, you must open a zipfile, and create another - Of couse, you can use RAW reading and writing to copy the file you did not want delte + Of course, you can use RAW reading and writing to copy the file you did not want delete */ -extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, - int append, - zipcharpc* globalcomment, - zlib_filefunc_def* pzlib_filefunc_def)); +extern zipFile ZEXPORT zipOpen2(const char *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc_def* pzlib_filefunc_def); -extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname, +extern zipFile ZEXPORT zipOpen2_64(const void *pathname, int append, zipcharpc* globalcomment, - zlib_filefunc64_def* pzlib_filefunc_def)); - -extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level)); - -extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int zip64)); + zlib_filefunc64_def* pzlib_filefunc_def); + +extern zipFile ZEXPORT zipOpen3(const void *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc64_32_def* pzlib_filefunc64_32_def); + +extern int ZEXPORT zipOpenNewFileInZip(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level); + +extern int ZEXPORT zipOpenNewFileInZip64(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int zip64); /* Open a file in the ZIP for writing. filename : the filename in zip (if NULL, '-' without quote will be used *zipfi contain supplemental information if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local - contains the extrafield data the the local header + contains the extrafield data for the local header if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global - contains the extrafield data the the local header + contains the extrafield data for the global header if comment != NULL, comment contain the comment string method contain the compression method (0 for store, Z_DEFLATED for deflate) level contain the level of compression (can be Z_DEFAULT_COMPRESSION) @@ -197,70 +202,69 @@ extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, */ -extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int raw)); - - -extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int raw, - int zip64)); +extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw); + + +extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int zip64); /* Same than zipOpenNewFileInZip, except if raw=1, we write raw file */ -extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int raw, - int windowBits, - int memLevel, - int strategy, - const char* password, - uLong crcForCrypting)); - -extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int raw, - int windowBits, - int memLevel, - int strategy, - const char* password, - uLong crcForCrypting, - int zip64 - )); +extern int ZEXPORT zipOpenNewFileInZip3(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting); + +extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + int zip64); /* Same than zipOpenNewFileInZip2, except @@ -269,47 +273,45 @@ extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, crcForCrypting : crc of file to compress (needed for crypting) */ -extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int raw, - int windowBits, - int memLevel, - int strategy, - const char* password, - uLong crcForCrypting, - uLong versionMadeBy, - uLong flagBase - )); - - -extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int raw, - int windowBits, - int memLevel, - int strategy, - const char* password, - uLong crcForCrypting, - uLong versionMadeBy, - uLong flagBase, - int zip64 - )); +extern int ZEXPORT zipOpenNewFileInZip4(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase); + + +extern int ZEXPORT zipOpenNewFileInZip4_64(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase, + int zip64); /* Same than zipOpenNewFileInZip4, except versionMadeBy : value for Version made by field @@ -317,25 +319,25 @@ extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, */ -extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, - const void* buf, - unsigned len)); +extern int ZEXPORT zipWriteInFileInZip(zipFile file, + const void* buf, + unsigned len); /* Write data in the zipfile */ -extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); +extern int ZEXPORT zipCloseFileInZip(zipFile file); /* Close the current file in the zipfile */ -extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, - uLong uncompressed_size, - uLong crc32)); +extern int ZEXPORT zipCloseFileInZipRaw(zipFile file, + uLong uncompressed_size, + uLong crc32); -extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, - ZPOS64_T uncompressed_size, - uLong crc32)); +extern int ZEXPORT zipCloseFileInZipRaw64(zipFile file, + ZPOS64_T uncompressed_size, + uLong crc32); /* Close the current file in the zipfile, for file opened with @@ -343,14 +345,14 @@ extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, uncompressed_size and crc32 are value for the uncompressed size */ -extern int ZEXPORT zipClose OF((zipFile file, - const char* global_comment)); +extern int ZEXPORT zipClose(zipFile file, + const char* global_comment); /* Close the zipfile */ -extern int ZEXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader)); +extern int ZEXPORT zipRemoveExtraInfoBlock(char* pData, int* dataLen, short sHeader); /* zipRemoveExtraInfoBlock - Added by Mathias Svensson diff --git a/src/include/xlsxwriter/utility.h b/src/include/xlsxwriter/utility.h index e61813e..3c770f5 100644 --- a/src/include/xlsxwriter/utility.h +++ b/src/include/xlsxwriter/utility.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. */ /** @@ -9,7 +9,7 @@ * * @brief Utility functions for libxlsxwriter. * - * + * * */ @@ -183,7 +183,7 @@ uint16_t lxw_name_to_col_2(const char *col_str); * @return A double representing an Excel datetime. * * The `%lxw_datetime_to_excel_datetime()` function converts a datetime in - * #lxw_datetime to and Excel datetime number: + * #lxw_datetime to an Excel datetime number: * * @code * lxw_datetime datetime = {2013, 2, 28, 12, 0, 0.0}; @@ -198,6 +198,26 @@ double lxw_datetime_to_excel_datetime(lxw_datetime *datetime); double lxw_datetime_to_excel_date_epoch(lxw_datetime *datetime, uint8_t date_1904); +/** + * @brief Converts a unix datetime to an Excel datetime number. + * + * @param unixtime Unix time (seconds since 1970-01-01) + * + * @return A double representing an Excel datetime. + * + * The `%lxw_unixtime_to_excel_date()` function converts a unix datetime to + * an Excel datetime number: + * + * @code + * double excel_datetime = lxw_unixtime_to_excel_date(946684800); + * @endcode + * + * See @ref working_with_dates for more details. + */ +double lxw_unixtime_to_excel_date(int64_t unixtime); + +double lxw_unixtime_to_excel_date_epoch(int64_t unixtime, uint8_t date_1904); + char *lxw_strdup(const char *str); char *lxw_strdup_formula(const char *formula); @@ -212,16 +232,19 @@ void lxw_str_tolower(char *str); #define lxw_strcasecmp strcasecmp #endif -FILE *lxw_tmpfile(char *tmpdir); +FILE *lxw_tmpfile(const char *tmpdir); +FILE *lxw_get_filehandle(char **buf, size_t *size, const char *tmpdir); FILE *lxw_fopen(const char *filename, const char *mode); -/* Use a user defined function to format doubles in sprintf or else a simple - * macro (the default). */ -#ifdef USE_DOUBLE_FUNCTION +/* Use the third party dtoa function to avoid locale issues with sprintf + * double formatting. Otherwise we use a simple macro that falls back to the + * default c-lib sprintf. + */ +#ifdef USE_DTOA_LIBRARY int lxw_sprintf_dbl(char *data, double number); #else #define lxw_sprintf_dbl(data, number) \ - lxw_snprintf(data, LXW_ATTR_32, "%.16g", number) + lxw_snprintf(data, LXW_ATTR_32, "%.16G", number) #endif uint16_t lxw_hash_password(const char *password); diff --git a/src/include/xlsxwriter/vml.h b/src/include/xlsxwriter/vml.h index 8331452..b5a5866 100644 --- a/src/include/xlsxwriter/vml.h +++ b/src/include/xlsxwriter/vml.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * vml - A libxlsxwriter library for creating Excel XLSX vml files. * diff --git a/src/include/xlsxwriter/workbook.h b/src/include/xlsxwriter/workbook.h index 1d64e6c..8988461 100644 --- a/src/include/xlsxwriter/workbook.h +++ b/src/include/xlsxwriter/workbook.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. */ /** @@ -182,34 +182,34 @@ typedef struct lxw_defined_name { */ typedef struct lxw_doc_properties { /** The title of the Excel Document. */ - char *title; + const char *title; /** The subject of the Excel Document. */ - char *subject; + const char *subject; /** The author of the Excel Document. */ - char *author; + const char *author; /** The manager field of the Excel Document. */ - char *manager; + const char *manager; /** The company field of the Excel Document. */ - char *company; + const char *company; /** The category of the Excel Document. */ - char *category; + const char *category; /** The keywords of the Excel Document. */ - char *keywords; + const char *keywords; /** The comment field of the Excel Document. */ - char *comments; + const char *comments; /** The status of the Excel Document. */ - char *status; + const char *status; /** The hyperlink base URL of the Excel Document. */ - char *hyperlink_base; + const char *hyperlink_base; /** The file creation date/time shown in Excel. This defaults to the * current time and date if set to 0. If you wish to create files that are @@ -243,6 +243,13 @@ typedef struct lxw_doc_properties { * for more information. This option is off by default. * * [zip64_wiki]: https://en.wikipedia.org/wiki/Zip_(file_format)#ZIP64 + + * - `output_buffer`: Output to a buffer instead of a file. The buffer must be + * freed manually by calling free(). This option can only be used if filename + * is NULL. + * + * - `output_buffer_size`: Used with output_buffer to get the size of the + * created buffer. This option can only be used if filename is NULL. * * @note In `constant_memory` mode each row of in-memory data is written to * disk and then freed when a new row is started via one of the @@ -263,11 +270,16 @@ typedef struct lxw_workbook_options { uint8_t constant_memory; /** Directory to use for the temporary files created by libxlsxwriter. */ - char *tmpdir; + const char *tmpdir; /** Allow ZIP64 extensions when creating the xlsx file zip container. */ uint8_t use_zip64; + /** Output buffer to use instead of writing to a file */ + const char **output_buffer; + + /** Used with output_buffer to get the size of the created buffer */ + size_t *output_buffer_size; } lxw_workbook_options; /** @@ -287,6 +299,7 @@ typedef struct lxw_workbook { struct lxw_chartsheet_names *chartsheet_names; struct lxw_image_md5s *image_md5s; struct lxw_image_md5s *header_image_md5s; + struct lxw_image_md5s *background_md5s; struct lxw_charts *charts; struct lxw_charts *ordered_charts; struct lxw_formats *formats; @@ -319,13 +332,16 @@ typedef struct lxw_workbook { uint8_t has_png; uint8_t has_jpeg; uint8_t has_bmp; + uint8_t has_gif; uint8_t has_vml; uint8_t has_comments; + uint8_t has_metadata; lxw_hash_table *used_xf_formats; lxw_hash_table *used_dxf_formats; char *vba_project; + char *vba_project_signature; char *vba_codename; lxw_format *default_url_format; @@ -373,7 +389,9 @@ lxw_workbook *workbook_new(const char *filename); * @code * lxw_workbook_options options = {.constant_memory = LXW_TRUE, * .tmpdir = "C:\\Temp", - * .use_zip64 = LXW_FALSE}; + * .use_zip64 = LXW_FALSE, + * .output_buffer = NULL, + * .output_buffer_size = NULL}; * * lxw_workbook *workbook = workbook_new_opt("filename.xlsx", &options); * @endcode @@ -397,6 +415,13 @@ lxw_workbook *workbook_new(const char *filename); * * [zip64_wiki]: https://en.wikipedia.org/wiki/Zip_(file_format)#ZIP64 * + * - `output_buffer`: Output to a memory buffer instead of a file. The buffer + * must be freed manually by calling `free()`. This option can only be used if + * filename is NULL. + * + * - `output_buffer_size`: Used with output_buffer to get the size of the + * created buffer. This option can only be used if filename is `NULL`. + * * @note In `constant_memory` mode each row of in-memory data is written to * disk and then freed when a new row is started via one of the * `worksheet_write_*()` functions. Therefore, once this option is active data @@ -942,7 +967,7 @@ lxw_error workbook_validate_sheet_name(lxw_workbook *workbook, * workbook_add_vba_project(workbook, "vbaProject.bin"); * @endcode * - * Only one `vbaProject.bin file` can be added per workbook. The name doesn't + * Only one `vbaProject.bin` file can be added per workbook. The name doesn't * have to be `vbaProject.bin`. Any suitable path/name for an existing VBA bin * file will do. * @@ -961,6 +986,35 @@ lxw_error workbook_validate_sheet_name(lxw_workbook *workbook, lxw_error workbook_add_vba_project(lxw_workbook *workbook, const char *filename); +/** + * @brief Add a vbaProject binary and a vbaProjectSignature binary to the Excel + * workbook. + * + * @param workbook Pointer to a lxw_workbook instance. + * @param vba_project The path/filename of the vbaProject.bin file. + * @param signature The path/filename of the vbaProjectSignature.bin file. + * + * The `%workbook_add_signed_vba_project()` function can be used to add digitally + * signed macros or functions to a workbook. The function adds a binary VBA project + * file and a binary VBA project signature file that have been extracted from an + * existing Excel xlsm file with digitally signed macros: + * + * @code + * workbook_add_signed_vba_project(workbook, "vbaProject.bin", "vbaProjectSignature.bin"); + * @endcode + * + * Only one `vbaProject.bin` file can be added per workbook. The name doesn't + * have to be `vbaProject.bin`. Any suitable path/name for an existing VBA bin + * file will do. The same applies for `vbaProjectSignature.bin`. + * + * See also @ref working_with_macros + * + * @return A #lxw_error. + */ +lxw_error workbook_add_signed_vba_project(lxw_workbook *workbook, + const char *vba_project, + const char *signature); + /** * @brief Set the VBA name for the workbook. * diff --git a/src/include/xlsxwriter/worksheet.h b/src/include/xlsxwriter/worksheet.h index 7669040..caf3f81 100644 --- a/src/include/xlsxwriter/worksheet.h +++ b/src/include/xlsxwriter/worksheet.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. */ /** @@ -504,11 +504,11 @@ enum lxw_conditional_icon_types { /** Icon style: 3 symbols with tick mark, exclamation and cross. */ LXW_CONDITIONAL_ICONS_3_SYMBOLS_UNCIRCLED, - /** Icon style: 3 colored arrows showing up, diagonal up, diagonal down + /** Icon style: 4 colored arrows showing up, diagonal up, diagonal down * and down. */ LXW_CONDITIONAL_ICONS_4_ARROWS_COLORED, - /** Icon style: 3 gray arrows showing up, diagonal up, diagonal down and + /** Icon style: 4 gray arrows showing up, diagonal up, diagonal down and * down. */ LXW_CONDITIONAL_ICONS_4_ARROWS_GRAY, @@ -536,6 +536,121 @@ enum lxw_conditional_icon_types { LXW_CONDITIONAL_ICONS_5_QUARTERS }; +/** @brief The type of table style. + * + * The type of table style (Light, Medium or Dark). + */ +enum lxw_table_style_type { + + LXW_TABLE_STYLE_TYPE_DEFAULT, + + /** Light table style. */ + LXW_TABLE_STYLE_TYPE_LIGHT, + + /** Light table style. */ + LXW_TABLE_STYLE_TYPE_MEDIUM, + + /** Light table style. */ + LXW_TABLE_STYLE_TYPE_DARK +}; + +/** + * @brief Standard Excel functions for totals in tables. + * + * Definitions for the standard Excel functions that are available via the + * dropdown in the total row of an Excel table. + * + */ +enum lxw_table_total_functions { + + LXW_TABLE_FUNCTION_NONE = 0, + + /** Use the average function as the table total. */ + LXW_TABLE_FUNCTION_AVERAGE = 101, + + /** Use the count numbers function as the table total. */ + LXW_TABLE_FUNCTION_COUNT_NUMS = 102, + + /** Use the count function as the table total. */ + LXW_TABLE_FUNCTION_COUNT = 103, + + /** Use the max function as the table total. */ + LXW_TABLE_FUNCTION_MAX = 104, + + /** Use the min function as the table total. */ + LXW_TABLE_FUNCTION_MIN = 105, + + /** Use the standard deviation function as the table total. */ + LXW_TABLE_FUNCTION_STD_DEV = 107, + + /** Use the sum function as the table total. */ + LXW_TABLE_FUNCTION_SUM = 109, + + /** Use the var function as the table total. */ + LXW_TABLE_FUNCTION_VAR = 110 +}; + +/** @brief The criteria used in autofilter rules. + * + * Criteria used to define an autofilter rule condition. + */ +enum lxw_filter_criteria { + LXW_FILTER_CRITERIA_NONE, + + /** Filter cells equal to a value. */ + LXW_FILTER_CRITERIA_EQUAL_TO, + + /** Filter cells not equal to a value. */ + LXW_FILTER_CRITERIA_NOT_EQUAL_TO, + + /** Filter cells greater than a value. */ + LXW_FILTER_CRITERIA_GREATER_THAN, + + /** Filter cells less than a value. */ + LXW_FILTER_CRITERIA_LESS_THAN, + + /** Filter cells greater than or equal to a value. */ + LXW_FILTER_CRITERIA_GREATER_THAN_OR_EQUAL_TO, + + /** Filter cells less than or equal to a value. */ + LXW_FILTER_CRITERIA_LESS_THAN_OR_EQUAL_TO, + + /** Filter cells that are blank. */ + LXW_FILTER_CRITERIA_BLANKS, + + /** Filter cells that are not blank. */ + LXW_FILTER_CRITERIA_NON_BLANKS +}; + +/** + * @brief And/or operator when using 2 filter rules. + * + * And/or operator conditions when using 2 filter rules with + * worksheet_filter_column2(). In general LXW_FILTER_OR is used with + * LXW_FILTER_CRITERIA_EQUAL_TO and LXW_FILTER_AND is used with the other + * filter criteria. + */ +enum lxw_filter_operator { + /** Logical "and" of 2 filter rules. */ + LXW_FILTER_AND, + + /** Logical "or" of 2 filter rules. */ + LXW_FILTER_OR +}; + +/* Internal filter types. */ +enum lxw_filter_type { + LXW_FILTER_TYPE_NONE, + + LXW_FILTER_TYPE_SINGLE, + + LXW_FILTER_TYPE_AND, + + LXW_FILTER_TYPE_OR, + + LXW_FILTER_TYPE_STRING_LIST +}; + /** Options to control the positioning of worksheet objects such as images * or charts. See @ref working_with_object_positioning. */ enum lxw_object_position { @@ -543,10 +658,10 @@ enum lxw_object_position { /** Default positioning for the object. */ LXW_OBJECT_POSITION_DEFAULT, - /** Move and size with the worksheet object with the cells. */ + /** Move and size the worksheet object with the cells. */ LXW_OBJECT_MOVE_AND_SIZE, - /** Move but don't size with the worksheet object with the cells. */ + /** Move but don't size the worksheet object with the cells. */ LXW_OBJECT_MOVE_DONT_SIZE, /** Don't move or size the worksheet object with the cells. */ @@ -602,6 +717,7 @@ enum cell_types { INLINE_RICH_STRING_CELL, FORMULA_CELL, ARRAY_FORMULA_CELL, + DYNAMIC_ARRAY_FORMULA_CELL, BLANK_CELL, BOOLEAN_CELL, COMMENT, @@ -703,6 +819,7 @@ STAILQ_HEAD(lxw_cond_format_list, lxw_cond_format_obj); STAILQ_HEAD(lxw_image_props, lxw_object_properties); STAILQ_HEAD(lxw_chart_props, lxw_object_properties); STAILQ_HEAD(lxw_comment_objs, lxw_vml_obj); +STAILQ_HEAD(lxw_table_objs, lxw_table_obj); /** * @brief Options for rows and columns. @@ -771,6 +888,7 @@ typedef struct lxw_print_area { typedef struct lxw_autofilter { uint8_t in_use; + uint8_t has_rules; lxw_row_t first_row; lxw_row_t last_row; lxw_col_t first_col; @@ -861,7 +979,7 @@ typedef struct lxw_data_validation { * is applied using a cell reference. It is valid for any of the * `_FORMULA` validation types. */ - char *value_formula; + const char *value_formula; /** * This parameter is used to set a list of strings for a drop down list. @@ -880,7 +998,7 @@ typedef struct lxw_data_validation { * Note, the string list is restricted by Excel to 255 characters, * including comma separators. */ - char **value_list; + const char **value_list; /** * This parameter is used to set the limiting value to which the date or @@ -898,7 +1016,7 @@ typedef struct lxw_data_validation { * This parameter is the same as `value_formula` but for the minimum value * when a `BETWEEN` criteria is used. */ - char *minimum_formula; + const char *minimum_formula; /** * This parameter is the same as `value_datetime` but for the minimum value @@ -916,7 +1034,7 @@ typedef struct lxw_data_validation { * This parameter is the same as `value_formula` but for the maximum value * when a `BETWEEN` criteria is used. */ - char *maximum_formula; + const char *maximum_formula; /** * This parameter is the same as `value_datetime` but for the maximum value @@ -932,7 +1050,7 @@ typedef struct lxw_data_validation { * * The maximum title length is 32 characters. */ - char *input_title; + const char *input_title; /** * The input_message parameter is used to set the input message that is @@ -941,7 +1059,7 @@ typedef struct lxw_data_validation { * The message can be split over several lines using newlines. The maximum * message length is 255 characters. */ - char *input_message; + const char *input_message; /** * The error_title parameter is used to set the title of the error message @@ -949,7 +1067,7 @@ typedef struct lxw_data_validation { * default error title is 'Microsoft Excel'. The maximum title length is * 32 characters. */ - char *error_title; + const char *error_title; /** * The error_message parameter is used to set the error message that is @@ -960,7 +1078,7 @@ typedef struct lxw_data_validation { * The message can be split over several lines using newlines. The maximum * message length is 255 characters. */ - char *error_message; + const char *error_message; } lxw_data_validation; @@ -1024,7 +1142,7 @@ typedef struct lxw_conditional_format { * value_string exists in the struct then the number value is * ignored. Note, if the condition refers to a text string then it must * be double quoted like this `"foo"`. */ - char *value_string; + const char *value_string; /** The format field is used to specify the #lxw_format format that will * be applied to the cell when the conditional formatting criterion is @@ -1045,7 +1163,7 @@ typedef struct lxw_conditional_format { /** The minimum string value used for Cell, Color Scale and Data Bar conditional * formats. Usually used to set ranges like `=A1`. */ - char *min_value_string; + const char *min_value_string; /** The rule used for the minimum condition in Color Scale and Data Bar * conditional formats. The rule types are defined in @@ -1062,7 +1180,7 @@ typedef struct lxw_conditional_format { /** The middle string value used for Color Scale and Data Bar conditional * formats. Usually used to set ranges like `=A1`. */ - char *mid_value_string; + const char *mid_value_string; /** The rule used for the middle condition in Color Scale and Data Bar * conditional formats. The rule types are defined in @@ -1080,7 +1198,7 @@ typedef struct lxw_conditional_format { /** The maximum string value used for Cell, Color Scale and Data Bar conditional * formats. Usually used to set ranges like `=A1`. */ - char *max_value_string; + const char *max_value_string; /** The rule used for the maximum condition in Color Scale and Data Bar * conditional formats. The rule types are defined in @@ -1186,7 +1304,7 @@ typedef struct lxw_conditional_format { * conditional format and any others separated by spaces. For example * `"A1 C1:C5 E2 G1:G100"`. */ - char *multi_range; + const char *multi_range; /** The stop_if_true parameter can be used to set the "stop if true" * feature of a conditional formatting rule when more than one rule is @@ -1261,6 +1379,319 @@ typedef struct lxw_cond_format_hash_element { RB_ENTRY (lxw_cond_format_hash_element) tree_pointers; } lxw_cond_format_hash_element; +/** + * @brief Table columns options. + * + * Structure to set the options of a table column added with + * worksheet_add_table(). See @ref ww_tables_columns. + */ +typedef struct lxw_table_column { + + /** Set the header name/caption for the column. If NULL the header defaults + * to Column 1, Column 2, etc. */ + const char *header; + + /** Set the formula for the column. */ + const char *formula; + + /** Set the string description for the column total. */ + const char *total_string; + + /** Set the function for the column total. */ + uint8_t total_function; + + /** Set the format for the column header. */ + lxw_format *header_format; + + /** Set the format for the data rows in the column. */ + lxw_format *format; + + /** Set the formula value for the column total (not generally required). */ + double total_value; + +} lxw_table_column; + +/** + * @brief Worksheet table options. + * + * Options used to define worksheet tables. See @ref working_with_tables for + * more information. + * + */ +typedef struct lxw_table_options { + + /** + * The `name` parameter is used to set the name of the table. This + * parameter is optional and by default tables are named `Table1`, + * `Table2`, etc. in the worksheet order that they are added. + * + * @code + * lxw_table_options options = {.name = "Sales"}; + * + * worksheet_add_table(worksheet, RANGE("B3:G8"), &options); + * @endcode + * + * If you override the table name you must ensure that it doesn't clash + * with an existing table name and that it follows Excel's requirements + * for table names, see the Microsoft Office documentation on + * [Naming an Excel Table] + * (https://support.microsoft.com/en-us/office/rename-an-excel-table-fbf49a4f-82a3-43eb-8ba2-44d21233b114). + */ + const char *name; + + /** + * The `no_header_row` parameter can be used to turn off the header row in + * the table. It is on by default: + * + * @code + * lxw_table_options options = {.no_header_row = LXW_TRUE}; + * + * worksheet_add_table(worksheet, RANGE("B4:F7"), &options); + * @endcode + * + * @image html tables4.png + * + * Without this option the header row will contain default captions such + * as `Column 1`, ``Column 2``, etc. These captions can be overridden + * using the `columns` parameter shown below. + * + */ + uint8_t no_header_row; + + /** + * The `no_autofilter` parameter can be used to turn off the autofilter in + * the header row. It is on by default: + * + * @code + * lxw_table_options options = {.no_autofilter = LXW_TRUE}; + * + * worksheet_add_table(worksheet, RANGE("B3:F7"), &options); + * @endcode + * + * @image html tables3.png + * + * The autofilter is only shown if the `no_header_row` parameter is off + * (the default). Filter conditions within the table are not supported. + * + */ + uint8_t no_autofilter; + + /** + * The `no_banded_rows` parameter can be used to turn off the rows of alternating + * color in the table. It is on by default: + * + * @code + * lxw_table_options options = {.no_banded_rows = LXW_TRUE}; + * + * worksheet_add_table(worksheet, RANGE("B3:F7"), &options); + * @endcode + * + * @image html tables6.png + * + */ + uint8_t no_banded_rows; + + /** + * The `banded_columns` parameter can be used to used to create columns of + * alternating color in the table. It is off by default: + * + * @code + * lxw_table_options options = {.banded_columns = LXW_TRUE}; + * + * worksheet_add_table(worksheet, RANGE("B3:F7"), &options); + * @endcode + * + * The banded columns formatting is shown in the image in the previous + * section above. + */ + uint8_t banded_columns; + + /** + * The `first_column` parameter can be used to highlight the first column + * of the table. The type of highlighting will depend on the `style_type` + * of the table. It may be bold text or a different color. It is off by + * default: + * + * @code + * lxw_table_options options = {.first_column = LXW_TRUE, .last_column = LXW_TRUE}; + * + * worksheet_add_table(worksheet, RANGE("B3:F7"), &options); + * @endcode + * + * @image html tables5.png + */ + uint8_t first_column; + + /** + * The `last_column` parameter can be used to highlight the last column of + * the table. The type of highlighting will depend on the `style` of the + * table. It may be bold text or a different color. It is off by default: + * + * @code + * lxw_table_options options = {.first_column = LXW_TRUE, .last_column = LXW_TRUE}; + * + * worksheet_add_table(worksheet, RANGE("B3:F7"), &options); + * @endcode + * + * The `last_column` formatting is shown in the image in the previous + * section above. + */ + uint8_t last_column; + + /** + * The `style_type` parameter can be used to set the style of the table, + * in conjunction with the `style_type_number` parameter: + * + * @code + * lxw_table_options options = { + * .style_type = LXW_TABLE_STYLE_TYPE_LIGHT, + * .style_type_number = 11, + * }; + * + * worksheet_add_table(worksheet, RANGE("B3:G8"), &options); + * @endcode + * + * + * @image html tables11.png + * + * There are three types of table style in Excel: Light, Medium and Dark + * which are represented using the #lxw_table_style_type enum values: + * + * - #LXW_TABLE_STYLE_TYPE_LIGHT + * + * - #LXW_TABLE_STYLE_TYPE_MEDIUM + * + * - #LXW_TABLE_STYLE_TYPE_DARK + * + * Within those ranges there are between 11 and 28 other style types which + * can be set with `style_type_number` (depending on the style type). + * Check Excel to find the style that you want. The dialog with the + * options laid out in numeric order are shown below: + * + * @image html tables14.png + * + * The default table style in Excel is 'Table Style Medium 9' (highlighted + * with a green border in the image above), which is set by default in + * libxlsxwriter as: + * + * @code + * lxw_table_options options = { + * .style_type = LXW_TABLE_STYLE_TYPE_MEDIUM, + * .style_type_number = 9, + * }; + * @endcode + * + * You can also turn the table style off by setting it to Light 0: + * + * @code + * lxw_table_options options = { + * .style_type = LXW_TABLE_STYLE_TYPE_LIGHT, + * .style_type_number = 0, + * }; + * @endcode + * + * @image html tables13.png + * + */ + uint8_t style_type; + + /** + * The `style_type_number` parameter is used with `style_type` to set the + * style of a worksheet table. */ + uint8_t style_type_number; + + /** + * The `total_row` parameter can be used to turn on the total row in the + * last row of a table. It is distinguished from the other rows by a + * different formatting and also with dropdown `SUBTOTAL` functions: + * + * @code + * lxw_table_options options = {.total_row = LXW_TRUE}; + * + * worksheet_add_table(worksheet, RANGE("B3:G8"), &options); + * @endcode + * + * @image html tables9.png + * + * The default total row doesn't have any captions or functions. These + * must by specified via the `columns` parameter below. + */ + uint8_t total_row; + + /** + * The `columns` parameter can be used to set properties for columns + * within the table. See @ref ww_tables_columns for a detailed + * explanation. + */ + lxw_table_column **columns; + +} lxw_table_options; + +typedef struct lxw_table_obj { + char *name; + char *total_string; + lxw_table_column **columns; + uint8_t banded_columns; + uint8_t first_column; + uint8_t last_column; + uint8_t no_autofilter; + uint8_t no_banded_rows; + uint8_t no_header_row; + uint8_t style_type; + uint8_t style_type_number; + uint8_t total_row; + + lxw_row_t first_row; + lxw_col_t first_col; + lxw_row_t last_row; + lxw_col_t last_col; + lxw_col_t num_cols; + uint32_t id; + + char sqref[LXW_MAX_ATTRIBUTE_LENGTH]; + char filter_sqref[LXW_MAX_ATTRIBUTE_LENGTH]; + STAILQ_ENTRY (lxw_table_obj) list_pointers; + +} lxw_table_obj; + +/** + * @brief Options for autofilter rules. + * + * Options to define an autofilter rule. + * + */ +typedef struct lxw_filter_rule { + + /** The #lxw_filter_criteria to define the rule. */ + uint8_t criteria; + + /** String value to which the criteria applies. */ + const char *value_string; + + /** Numeric value to which the criteria applies (if value_string isn't used). */ + double value; + +} lxw_filter_rule; + +typedef struct lxw_filter_rule_obj { + + uint8_t type; + uint8_t is_custom; + uint8_t has_blanks; + lxw_col_t col_num; + + uint8_t criteria1; + uint8_t criteria2; + double value1; + double value2; + char *value1_string; + char *value2_string; + + uint16_t num_list_filters; + char **list; + +} lxw_filter_rule_obj; + /** * @brief Options for inserted images. * @@ -1289,7 +1720,7 @@ typedef struct lxw_image_options { * used to provide a text description of the image to help * accessibility. Defaults to the image filename as in Excel. Set to "" * to ignore the description field. */ - char *description; + const char *description; /** Optional parameter to help accessibility. It is used to mark the image * as decorative, and thus uninformative, for automated screen @@ -1299,10 +1730,10 @@ typedef struct lxw_image_options { /** Add an optional hyperlink to the image. Follows the same URL rules * and types as `worksheet_write_url()`. */ - char *url; + const char *url; /** Add an optional mouseover tip for a hyperlink to the image. */ - char *tip; + const char *tip; } lxw_image_options; @@ -1330,6 +1761,18 @@ typedef struct lxw_chart_options { * See @ref working_with_object_positioning.*/ uint8_t object_position; + /** Optional description or "Alt text" for the chart. This field can be + * used to provide a text description of the chart to help + * accessibility. Defaults to the image filename as in Excel. Set to NULL + * to ignore the description field. */ + const char *description; + + /** Optional parameter to help accessibility. It is used to mark the chart + * as decorative, and thus uninformative, for automated screen + * readers. As in Excel, if this parameter is in use the `description` + * field isn't written. */ + uint8_t decorative; + } lxw_chart_options; /* Internal struct to represent lxw_image_options and lxw_chart_options @@ -1350,7 +1793,7 @@ typedef struct lxw_object_properties { FILE *stream; uint8_t image_type; uint8_t is_image_buffer; - unsigned char *image_buffer; + char *image_buffer; size_t image_buffer_size; double width; double height; @@ -1359,6 +1802,7 @@ typedef struct lxw_object_properties { double y_dpi; lxw_chart *chart; uint8_t is_duplicate; + uint8_t is_background; char *md5; char *image_position; uint8_t decorative; @@ -1388,7 +1832,7 @@ typedef struct lxw_comment_options { * worksheet. The default author for all cell comments in a worksheet can * be set using the `worksheet_set_comments_author()` function. Set to * NULL if not required. See also @ref ww_comments_author. */ - char *author; + const char *author; /** This option is used to set the width of the cell comment box * explicitly in pixels. The default width is 128 pixels. See also @ref @@ -1415,7 +1859,7 @@ typedef struct lxw_comment_options { /** This option is used to set the font for the comment. The default font * is 'Tahoma'. See also @ref ww_comments_font_name. */ - char *font_name; + const char *font_name; /** This option is used to set the font size for the comment. The default * is 8. See also @ref ww_comments_font_size. */ @@ -1447,6 +1891,50 @@ typedef struct lxw_comment_options { } lxw_comment_options; +/** + * @brief Options for inserted buttons. + * + * Options for modifying buttons inserted via `worksheet_insert_button()`. + * + */ +typedef struct lxw_button_options { + + /** Sets the caption on the button. The default is "Button n" where n is + * the current number of buttons in the worksheet, including this + * button. */ + const char *caption; + + /** Name of the macro to run when the button is pressed. The macro must be + * included with workbook_add_vba_project(). */ + const char *macro; + + /** Optional description or "Alt text" for the button. This field can be + * used to provide a text description of the button to help + * accessibility. Set to NULL to ignore the description field. */ + const char *description; + + /** This option is used to set the width of the cell button box + * explicitly in pixels. The default width is 64 pixels. */ + uint16_t width; + + /** This option is used to set the height of the cell button box + * explicitly in pixels. The default height is 20 pixels. */ + uint16_t height; + + /** X scale of the button as a decimal. */ + double x_scale; + + /** Y scale of the button as a decimal. */ + double y_scale; + + /** Offset from the left of the cell in pixels. */ + int32_t x_offset; + + /** Offset from the top of the cell in pixels. */ + int32_t y_offset; + +} lxw_button_options; + /* Internal structure for VML object options. */ typedef struct lxw_vml_obj { @@ -1456,8 +1944,8 @@ typedef struct lxw_vml_obj { lxw_col_t start_col; int32_t x_offset; int32_t y_offset; - uint32_t col_absolute; - uint32_t row_absolute; + uint64_t col_absolute; + uint64_t row_absolute; uint32_t width; uint32_t height; double x_dpi; @@ -1475,6 +1963,7 @@ typedef struct lxw_vml_obj { char *text; char *image_position; char *name; + char *macro; STAILQ_ENTRY (lxw_vml_obj) list_pointers; } lxw_vml_obj; @@ -1494,17 +1983,17 @@ typedef struct lxw_header_footer_options { /** The left header image filename, with path if required. This should * have a corresponding `&G/&[Picture]` placeholder in the `&L` section of * the header/footer string. See `worksheet_set_header_opt()`. */ - char *image_left; + const char *image_left; /** The center header image filename, with path if required. This should * have a corresponding `&G/&[Picture]` placeholder in the `&C` section of * the header/footer string. See `worksheet_set_header_opt()`. */ - char *image_center; + const char *image_center; /** The right header image filename, with path if required. This should * have a corresponding `&G/&[Picture]` placeholder in the `&R` section of * the header/footer string. See `worksheet_set_header_opt()`. */ - char *image_right; + const char *image_right; } lxw_header_footer_options; @@ -1605,7 +2094,7 @@ typedef struct lxw_rich_string_tuple { lxw_format *format; /** The string fragment. */ - char *string; + const char *string; } lxw_rich_string_tuple; /** @@ -1619,6 +2108,8 @@ typedef struct lxw_worksheet { FILE *file; FILE *optimize_tmpfile; + char *optimize_buffer; + size_t optimize_buffer_size; struct lxw_table_rows *table; struct lxw_table_rows *hyperlinks; struct lxw_table_rows *comments; @@ -1633,6 +2124,9 @@ typedef struct lxw_worksheet { struct lxw_vml_drawing_rel_ids *vml_drawing_rel_ids; struct lxw_comment_objs *comment_objs; struct lxw_comment_objs *header_image_objs; + struct lxw_comment_objs *button_objs; + struct lxw_table_objs *table_objs; + uint16_t table_count; lxw_row_t dim_rowmin; lxw_row_t dim_rowmax; @@ -1640,11 +2134,11 @@ typedef struct lxw_worksheet { lxw_col_t dim_colmax; lxw_sst *sst; - char *name; - char *quoted_name; - char *tmpdir; + const char *name; + const char *quoted_name; + const char *tmpdir; - uint32_t index; + uint16_t index; uint8_t active; uint8_t selected; uint8_t hidden; @@ -1696,8 +2190,11 @@ typedef struct lxw_worksheet { uint8_t show_zeros; uint8_t vcenter; uint8_t zoom_scale_normal; + uint8_t black_white; uint8_t num_validations; + uint8_t has_dynamic_arrays; char *vba_codename; + uint16_t num_buttons; lxw_color_t tab_color; @@ -1739,8 +2236,10 @@ typedef struct lxw_worksheet { struct lxw_rel_tuples *external_drawing_links; struct lxw_rel_tuples *drawing_links; struct lxw_rel_tuples *vml_drawing_links; + struct lxw_rel_tuples *external_table_links; struct lxw_panes panes; + char top_left_cell[LXW_MAX_CELL_NAME_LENGTH]; struct lxw_protection_obj protection; @@ -1750,9 +2249,12 @@ typedef struct lxw_worksheet { uint8_t has_vml; uint8_t has_comments; uint8_t has_header_vml; + uint8_t has_background_image; + uint8_t has_buttons; lxw_rel_tuple *external_vml_comment_link; lxw_rel_tuple *external_comment_link; lxw_rel_tuple *external_vml_header_link; + lxw_rel_tuple *external_background_link; char *comment_author; char *vml_data_id_str; char *vml_header_id_str; @@ -1782,6 +2284,10 @@ typedef struct lxw_worksheet { lxw_object_properties *footer_left_object_props; lxw_object_properties *footer_center_object_props; lxw_object_properties *footer_right_object_props; + lxw_object_properties *background_image; + + lxw_filter_rule_obj **filter_rules; + lxw_col_t num_filter_rules; STAILQ_ENTRY (lxw_worksheet) list_pointers; @@ -1791,15 +2297,15 @@ typedef struct lxw_worksheet { * Worksheet initialization data. */ typedef struct lxw_worksheet_init_data { - uint32_t index; + uint16_t index; uint8_t hidden; uint8_t optimize; uint16_t *active_sheet; uint16_t *first_sheet; lxw_sst *sst; - char *name; - char *quoted_name; - char *tmpdir; + const char *name; + const char *quoted_name; + const char *tmpdir; lxw_format *default_url_format; uint16_t max_url_length; @@ -1834,7 +2340,7 @@ typedef struct lxw_cell { union { double number; int32_t string_id; - char *string; + const char *string; } u; double formula_result; @@ -2014,16 +2520,16 @@ lxw_error worksheet_write_formula(lxw_worksheet *worksheet, * @brief Write an array formula to a worksheet cell. * * @param worksheet Pointer to a lxw_worksheet instance to be updated. - * @param first_row The first row of the range. (All zero indexed.) - * @param first_col The first column of the range. - * @param last_row The last row of the range. - * @param last_col The last col of the range. - * @param formula Array formula to write to cell. - * @param format A pointer to a Format instance or NULL. + * @param first_row The first row of the range. (All zero indexed.) + * @param first_col The first column of the range. + * @param last_row The last row of the range. + * @param last_col The last col of the range. + * @param formula Array formula to write to cell. + * @param format A pointer to a Format instance or NULL. * * @return A #lxw_error code. * - * The `%worksheet_write_array_formula()` function writes an array formula to + * The `%worksheet_write_array_formula()` function writes an array formula to * a cell range. In Excel an array formula is a formula that performs a * calculation on a set of values. * @@ -2060,6 +2566,94 @@ lxw_error worksheet_write_array_formula(lxw_worksheet *worksheet, const char *formula, lxw_format *format); +/** + * @brief Write an Excel 365 dynamic array formula to a worksheet range. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param first_row The first row of the range. (All zero indexed.) + * @param first_col The first column of the range. + * @param last_row The last row of the range. + * @param last_col The last col of the range. + * @param formula Dynamic Array formula to write to cell. + * @param format A pointer to a Format instance or NULL. + * + * @return A #lxw_error code. + * + * + * The `%worksheet_write_dynamic_array_formula()` function writes an Excel 365 + * dynamic array formula to a cell range. Some examples of functions that + * return dynamic arrays are: + * + * - `FILTER` + * - `RANDARRAY` + * - `SEQUENCE` + * - `SORTBY` + * - `SORT` + * - `UNIQUE` + * - `XLOOKUP` + * - `XMATCH` + * + * Dynamic array formulas and their usage in libxlsxwriter is explained in + * detail @ref ww_formulas_dynamic_arrays. The following is a example usage: + * + * @code + * worksheet_write_dynamic_array_formula(worksheet, 1, 5, 1, 5, + * "=_xlfn._xlws.FILTER(A1:D17,C1:C17=K2)", + * NULL); + * @endcode + * + * This formula gives the results shown in the image below. + * + * @image html dynamic_arrays02.png + * + * The need for the `_xlfn._xlws.` prefix in the formula is explained in @ref + * ww_formulas_future. + */ +lxw_error worksheet_write_dynamic_array_formula(lxw_worksheet *worksheet, + lxw_row_t first_row, + lxw_col_t first_col, + lxw_row_t last_row, + lxw_col_t last_col, + const char *formula, + lxw_format *format); + +/** + * @brief Write an Excel 365 dynamic array formula to a worksheet cell. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param row The zero indexed row number. + * @param col The zero indexed column number. + * @param formula Formula string to write to cell. + * @param format A pointer to a Format instance or NULL. + * + * @return A #lxw_error code. + * + * The `%worksheet_write_dynamic_formula()` function is similar to the + * `worksheet_write_dynamic_array_formula()` function, shown above, except + * that it writes a dynamic array formula to a single cell, rather than a + * range. This is a syntactic shortcut since the array range isn't generally + * known for a dynamic range and specifying the initial cell is sufficient for + * Excel, as shown in the example below: + * + * @code + * worksheet_write_dynamic_formula(worksheet, 7, 1, + * "=_xlfn._xlws.SORT(_xlfn.UNIQUE(B2:B17))", + * NULL); + * @endcode + * + * This formula gives the following result: + * + * @image html dynamic_arrays01.png + * + * The need for the `_xlfn.` and `_xlfn._xlws.` prefixes in the formula is + * explained in @ref ww_formulas_future. + */ +lxw_error worksheet_write_dynamic_formula(lxw_worksheet *worksheet, + lxw_row_t row, + lxw_col_t col, + const char *formula, + lxw_format *format); + lxw_error worksheet_write_array_formula_num(lxw_worksheet *worksheet, lxw_row_t first_row, lxw_col_t first_col, @@ -2069,6 +2663,22 @@ lxw_error worksheet_write_array_formula_num(lxw_worksheet *worksheet, lxw_format *format, double result); +lxw_error worksheet_write_dynamic_array_formula_num(lxw_worksheet *worksheet, + lxw_row_t first_row, + lxw_col_t first_col, + lxw_row_t last_row, + lxw_col_t last_col, + const char *formula, + lxw_format *format, + double result); + +lxw_error worksheet_write_dynamic_formula_num(lxw_worksheet *worksheet, + lxw_row_t row, + lxw_col_t col, + const char *formula, + lxw_format *format, + double result); + /** * @brief Write a date or time to a worksheet cell. * @@ -2080,7 +2690,7 @@ lxw_error worksheet_write_array_formula_num(lxw_worksheet *worksheet, * * @return A #lxw_error code. * - * The `worksheet_write_datetime()` function can be used to write a date or + * The `%worksheet_write_datetime()` function can be used to write a date or * time to the cell specified by `row` and `column`: * * @dontinclude dates_and_times02.c @@ -2101,6 +2711,49 @@ lxw_error worksheet_write_datetime(lxw_worksheet *worksheet, lxw_col_t col, lxw_datetime *datetime, lxw_format *format); +/** + * @brief Write a Unix datetime to a worksheet cell. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param row The zero indexed row number. + * @param col The zero indexed column number. + * @param unixtime The Unix datetime to write to the cell. + * @param format A pointer to a Format instance or NULL. + * + * @return A #lxw_error code. + * + * The `%worksheet_write_unixtime()` function can be used to write dates and + * times in Unix date format to the cell specified by `row` and + * `column`. [Unix Time](https://en.wikipedia.org/wiki/Unix_time) which is a + * common integer time format. It is defined as the number of seconds since + * the Unix epoch (1970-01-01 00:00 UTC). Negative values can also be used for + * dates prior to 1970: + * + * @dontinclude dates_and_times03.c + * @skip 1970 + * @until 2208988800 + * + * The `format` parameter should be used to apply formatting to the cell using + * a @ref format.h "Format" object as shown above. Without a date format the + * datetime will appear as a number only. + * + * The output from this code sample is: + * + * @image html date_example03.png + * + * Unixtime is generally represented with a 32 bit `time_t` type which has a + * range of approximately 1900-12-14 to 2038-01-19. To access the full Excel + * date range of 1900-01-01 to 9999-12-31 this function uses a 64 bit + * parameter. + * + * See @ref working_with_dates for more information about handling dates and + * times in libxlsxwriter. + */ +lxw_error worksheet_write_unixtime(lxw_worksheet *worksheet, + lxw_row_t row, + lxw_col_t col, int64_t unixtime, + lxw_format *format); + /** * * @param worksheet Pointer to a lxw_worksheet instance to be updated. @@ -2954,7 +3607,7 @@ lxw_error worksheet_set_column_pixels_opt(lxw_worksheet *worksheet, * @return A #lxw_error code. * * This function can be used to insert a image into a worksheet. The image can - * be in PNG, JPEG or BMP format: + * be in PNG, JPEG, GIF or BMP format: * * @code * worksheet_insert_image(worksheet, 2, 1, "logo.png"); @@ -3129,6 +3782,70 @@ lxw_error worksheet_insert_image_buffer_opt(lxw_worksheet *worksheet, size_t image_size, lxw_image_options *options); +/** + * @brief Set the background image for a worksheet. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param filename The image filename, with path if required. + * + * @return A #lxw_error code. + * + * The `%worksheet_set_background()` function can be used to set the + * background image for a worksheet: + * + * @code + * worksheet_set_background(worksheet, "logo.png"); + * @endcode + * + * @image html background.png + * + * The ``set_background()`` method supports all the image formats supported by + * `worksheet_insert_image()`. + * + * Some people use this method to add a watermark background to their + * document. However, Microsoft recommends using a header image [to set a + * watermark][watermark]. The choice of method depends on whether you want the + * watermark to be visible in normal viewing mode or just when the file is + * printed. In libxlsxwriter you can get the header watermark effect using + * `worksheet_set_header()`: + * + * @code + * lxw_header_footer_options header_options = {.image_center = "watermark.png"}; + * worksheet_set_header_opt(worksheet, "&C&G", &header_options); + * @endcode + * + * [watermark]:https://support.microsoft.com/en-us/office/add-a-watermark-in-excel-a372182a-d733-484e-825c-18ddf3edf009 + * + */ +lxw_error worksheet_set_background(lxw_worksheet *worksheet, + const char *filename); + +/** + * @brief Set the background image for a worksheet, from a buffer. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param image_buffer Pointer to an array of bytes that holds the image data. + * @param image_size The size of the array of bytes. + * + * @return A #lxw_error code. + * + * This function can be used to insert a background image into a worksheet + * from a memory buffer: + * + * @code + * worksheet_set_background_buffer(worksheet, image_buffer, image_size); + * @endcode + * + * The buffer should be a pointer to an array of unsigned char data with a + * specified size. + * + * See `worksheet_set_background()` for more details. + * + */ +lxw_error worksheet_set_background_buffer(lxw_worksheet *worksheet, + const unsigned char *image_buffer, + size_t image_size); + /** * @brief Insert a chart object into a worksheet. * @@ -3282,7 +3999,7 @@ lxw_error worksheet_merge_range(lxw_worksheet *worksheet, lxw_row_t first_row, * range of worksheet data. This allows users to filter the data based on * simple criteria so that some data is shown and some is hidden. * - * @image html autofilter.png + * @image html autofilter3.png * * To add an autofilter to a worksheet: * @@ -3293,13 +4010,147 @@ lxw_error worksheet_merge_range(lxw_worksheet *worksheet, lxw_row_t first_row, * worksheet_autofilter(worksheet, RANGE("A1:D51")); * @endcode * - * Note: it isn't currently possible to apply filter conditions to the - * autofilter. + * In order to apply a filter condition it is necessary to add filter rules to + * the columns using either the `%worksheet_filter_column()`, + * `%worksheet_filter_column2()` or `%worksheet_filter_list()` functions: + * + * - `worksheet_filter_column()`: filter on a single criterion such as "Column == + * East". More complex conditions such as "<=" or ">=" can also be use. + * + * - `worksheet_filter_column2()`: filter on two criteria such as "Column == East + * or Column == West". Complex conditions can also be used. + * + * - `worksheet_filter_list()`: filter on a list of values such as "Column in (East, West, + * North)". + * + * These functions are explained below. It isn't sufficient to just specify + * the filter condition. You must also hide any rows that don't match the + * filter condition. See @ref ww_autofilters_data for more details. + * */ lxw_error worksheet_autofilter(lxw_worksheet *worksheet, lxw_row_t first_row, lxw_col_t first_col, lxw_row_t last_row, lxw_col_t last_col); +/** + * @brief Write a filter rule to an autofilter column. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param col The column in the autofilter that the rule applies to. + * @param rule The lxw_filter_rule autofilter rule. + * + * @return A #lxw_error code. + * + * The `worksheet_filter_column` function can be used to filter columns in a + * autofilter range based on single rule conditions: + * + * @code + * lxw_filter_rule filter_rule = {.criteria = LXW_FILTER_CRITERIA_EQUAL_TO, + * .value_string = "East"}; + * + * worksheet_filter_column(worksheet, 0, &filter_rule); + *@endcode + * + * @image html autofilter4.png + * + * The rules and criteria are explained in more detail in @ref + * ww_autofilters_criteria in @ref working_with_autofilters. + * + * The `col` parameter is a zero indexed column number and must refer to a + * column in an existing autofilter created with `worksheet_autofilter()`. + * + * It isn't sufficient to just specify the filter condition. You must also + * hide any rows that don't match the filter condition. See @ref + * ww_autofilters_data for more details. + */ +lxw_error worksheet_filter_column(lxw_worksheet *worksheet, lxw_col_t col, + lxw_filter_rule *rule); + +/** + * @brief Write two filter rules to an autofilter column. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param col The column in the autofilter that the rules applies to. + * @param rule1 First lxw_filter_rule autofilter rule. + * @param rule2 Second lxw_filter_rule autofilter rule. + * @param and_or A #lxw_filter_operator and/or operator. + * + * @return A #lxw_error code. + * + * The `worksheet_filter_column2` function can be used to filter columns in a autofilter + * range based on two rule conditions: + * + * @code + * lxw_filter_rule filter_rule1 = {.criteria = LXW_FILTER_CRITERIA_EQUAL_TO, + * .value_string = "East"}; + * + * lxw_filter_rule filter_rule2 = {.criteria = LXW_FILTER_CRITERIA_EQUAL_TO, + * .value_string = "South"}; + * + * worksheet_filter_column2(worksheet, 0, &filter_rule1, &filter_rule2, LXW_FILTER_OR); + * @endcode + * + * @image html autofilter5.png + * + * The rules and criteria are explained in more detail in @ref + * ww_autofilters_criteria in @ref working_with_autofilters. + * + * The `col` parameter is a zero indexed column number and must refer to a + * column in an existing autofilter created with `worksheet_autofilter()`. + * + * The `and_or` parameter is either "and (LXW_FILTER_AND)" or "or (LXW_FILTER_OR)". + * + * It isn't sufficient to just specify the filter condition. You must also + * hide any rows that don't match the filter condition. See @ref + * ww_autofilters_data for more details. + */ +lxw_error worksheet_filter_column2(lxw_worksheet *worksheet, lxw_col_t col, + lxw_filter_rule *rule1, + lxw_filter_rule *rule2, uint8_t and_or); +/** + * @brief Write multiple string filters to an autofilter column. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param col The column in the autofilter that the rules applies to. + * @param list A NULL terminated array of strings to filter on. + * + * @return A #lxw_error code. + * + * The `worksheet_filter_column_list()` function can be used specify multiple + * string matching criteria. This is a newer type of filter introduced in + * Excel 2007. Prior to that it was only possible to have either 1 or 2 filter + * conditions, such as the ones used by `worksheet_filter_column()` and + * `worksheet_filter_column2()`. + * + * As an example, consider a column that contains data for the months of the + * year. The `%worksheet_filter_list()` function can be used to filter out + * data rows for different months: + * + * @code + * char* list[] = {"March", "April", "May", NULL}; + * + * worksheet_filter_list(worksheet, 0, list); + * @endcode + * + * @image html autofilter2.png + * + * + * Note, the array must be NULL terminated to indicate the end of the array of + * strings. To filter blanks as part of the list use `Blanks` as a list item: + * + * @code + * char* list[] = {"March", "April", "May", "Blanks", NULL}; + * + * worksheet_filter_list(worksheet, 0, list); + * @endcode + * + * It isn't sufficient to just specify the filter condition. You must also + * hide any rows that don't match the filter condition. See @ref + * ww_autofilters_data for more details. + */ +lxw_error worksheet_filter_list(lxw_worksheet *worksheet, lxw_col_t col, + const char **list); + /** * @brief Add a data validation to a cell. * @@ -3459,6 +4310,70 @@ lxw_error worksheet_conditional_format_range(lxw_worksheet *worksheet, lxw_col_t last_col, lxw_conditional_format *conditional_format); +/** + * @brief Insert a button object into a worksheet. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param row The zero indexed row number. + * @param col The zero indexed column number. + * @param options A #lxw_button_options object to set the button properties. + * + * @return A #lxw_error code. + * + * The `%worksheet_insert_button()` function can be used to insert an Excel + * form button into a worksheet. This function is generally only useful when + * used in conjunction with the `workbook_add_vba_project()` function to tie + * the button to a macro from an embedded VBA project: + * + * @code + * lxw_button_options options = {.caption = "Press Me", + * .macro = "say_hello"}; + * + * worksheet_insert_button(worksheet, 2, 1, &options); + * @endcode + * + * @image html macros.png + * + * The button properties are set using the lxw_button_options struct. + * + * See also @ref working_with_macros + */ +lxw_error worksheet_insert_button(lxw_worksheet *worksheet, lxw_row_t row, + lxw_col_t col, lxw_button_options *options); + +/** + * @brief Add an Excel table to a worksheet. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param first_row The first row of the range. (All zero indexed.) + * @param first_col The first column of the range. + * @param last_row The last row of the range. + * @param last_col The last col of the range. + * @param options A #lxw_table_options struct to define the table options. + * + * @return A #lxw_error code. + * + * The `%worksheet_add_table()` function is used to add a table to a + * worksheet. Tables in Excel are a way of grouping a range of cells into a + * single entity that has common formatting or that can be referenced from + * formulas. Tables can have column headers, autofilters, total rows, column + * formulas and default formatting. + * + * @code + * worksheet_add_table(worksheet, 2, 1, 6, 5, NULL); + * @endcode + * + * Output: + * + * @image html tables1.png + * + * See @ref working_with_tables for more detailed usage information and also + * @ref tables.c. + * + */ +lxw_error worksheet_add_table(lxw_worksheet *worksheet, lxw_row_t first_row, + lxw_col_t first_col, lxw_row_t last_row, + lxw_col_t last_col, lxw_table_options *options); /** * @brief Make a worksheet the active, i.e., visible worksheet. @@ -3667,6 +4582,27 @@ void worksheet_set_selection(lxw_worksheet *worksheet, lxw_row_t first_row, lxw_col_t first_col, lxw_row_t last_row, lxw_col_t last_col); +/** + * @brief Set the first visible cell at the top left of a worksheet. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param row The cell row (zero indexed). + * @param col The cell column (zero indexed). + * + * The `%worksheet_set_top_left_cell()` function can be used to set the + * top leftmost visible cell in the worksheet: + * + * @code + * worksheet_set_top_left_cell(worksheet, 31, 26); + * worksheet_set_top_left_cell(worksheet, CELL("AA32")); // Same as above. + * @endcode + * + * @image html top_left_cell.png + * + */ +void worksheet_set_top_left_cell(lxw_worksheet *worksheet, lxw_row_t row, + lxw_col_t col); + /** * @brief Set the page orientation as landscape. * @@ -4397,13 +5333,14 @@ void worksheet_fit_to_pages(lxw_worksheet *worksheet, uint16_t width, uint16_t height); /** - * @brief Set the start page number when printing. + * @brief Set the start/first page number when printing. * * @param worksheet Pointer to a lxw_worksheet instance to be updated. - * @param start_page Starting page number. + * @param start_page Page number of the starting page when printing. * - * The `%worksheet_set_start_page()` function is used to set the number of - * the starting page when the worksheet is printed out: + * The `%worksheet_set_start_page()` function is used to set the number number + * of the first page when the worksheet is printed out. It is the same as the + * "First Page Number" option in Excel: * * @code * // Start print from page 2. @@ -4438,6 +5375,18 @@ void worksheet_set_start_page(lxw_worksheet *worksheet, uint16_t start_page); */ void worksheet_set_print_scale(lxw_worksheet *worksheet, uint16_t scale); +/** + * @brief Set the worksheet to print in black and white + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * + * Set the option to print the worksheet in black and white: + * @code + * worksheet_print_black_and_white(worksheet); + * @endcode + */ +void worksheet_print_black_and_white(lxw_worksheet *worksheet); + /** * @brief Display the worksheet cells from right to left for some versions of * Excel. @@ -4826,6 +5775,10 @@ void lxw_worksheet_prepare_header_image(lxw_worksheet *worksheet, uint32_t image_ref_id, lxw_object_properties *object_props); +void lxw_worksheet_prepare_background(lxw_worksheet *worksheet, + uint32_t image_ref_id, + lxw_object_properties *object_props); + void lxw_worksheet_prepare_chart(lxw_worksheet *worksheet, uint32_t chart_ref_id, uint32_t drawing_id, lxw_object_properties *object_props, @@ -4837,10 +5790,13 @@ uint32_t lxw_worksheet_prepare_vml_objects(lxw_worksheet *worksheet, uint32_t vml_drawing_id, uint32_t comment_id); -void lxw_worksheet_prepare_header_vml_objects(lxw_worksheet *self, +void lxw_worksheet_prepare_header_vml_objects(lxw_worksheet *worksheet, uint32_t vml_header_id, uint32_t vml_drawing_id); +void lxw_worksheet_prepare_tables(lxw_worksheet *worksheet, + uint32_t table_id); + lxw_row *lxw_worksheet_find_row(lxw_worksheet *worksheet, lxw_row_t row_num); lxw_cell *lxw_worksheet_find_cell_in_row(lxw_row *row, lxw_col_t col_num); /* @@ -4890,6 +5846,8 @@ STATIC void _worksheet_write_data_validations(lxw_worksheet *self); STATIC double _pixels_to_height(double pixels); STATIC double _pixels_to_width(double pixels); + +STATIC void _worksheet_write_auto_filter(lxw_worksheet *worksheet); #endif /* TESTING */ /* *INDENT-OFF* */ diff --git a/src/include/xlsxwriter/xmlwriter.h b/src/include/xlsxwriter/xmlwriter.h index 243e604..3994095 100644 --- a/src/include/xlsxwriter/xmlwriter.h +++ b/src/include/xlsxwriter/xmlwriter.h @@ -1,7 +1,7 @@ /* * libxlsxwriter * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * xmlwriter - A libxlsxwriter library for creating Excel XLSX * XML files. @@ -55,7 +55,7 @@ STAILQ_HEAD(xml_attribute_list, xml_attribute); /* Create a new attribute struct to add to a xml_attribute_list. */ struct xml_attribute *lxw_new_attribute_str(const char *key, const char *value); -struct xml_attribute *lxw_new_attribute_int(const char *key, uint32_t value); +struct xml_attribute *lxw_new_attribute_int(const char *key, uint64_t value); struct xml_attribute *lxw_new_attribute_dbl(const char *key, double value); /* Macro to initialize the xml_attribute_list pointers. */ @@ -85,11 +85,13 @@ struct xml_attribute *lxw_new_attribute_dbl(const char *key, double value); /* Macro to free xml_attribute_list and attribute. */ #define LXW_FREE_ATTRIBUTES() \ - while (!STAILQ_EMPTY(&attributes)) { \ - attribute = STAILQ_FIRST(&attributes); \ - STAILQ_REMOVE_HEAD(&attributes, list_entries); \ - free(attribute); \ - } + do { \ + while (!STAILQ_EMPTY(&attributes)) { \ + attribute = STAILQ_FIRST(&attributes); \ + STAILQ_REMOVE_HEAD(&attributes, list_entries); \ + free(attribute); \ + } \ + } while (0) /** * Create the XML declaration in an XML file. diff --git a/src/libxlsxwriter/app.c b/src/libxlsxwriter/app.c index 0e80985..f43b5c9 100644 --- a/src/libxlsxwriter/app.c +++ b/src/libxlsxwriter/app.c @@ -3,7 +3,7 @@ * * Used in conjunction with the libxlsxwriter library. * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ diff --git a/src/libxlsxwriter/chart.c b/src/libxlsxwriter/chart.c index 7731b7f..a199dae 100644 --- a/src/libxlsxwriter/chart.c +++ b/src/libxlsxwriter/chart.c @@ -3,7 +3,7 @@ * * Used in conjunction with the libxlsxwriter library. * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ @@ -78,7 +78,7 @@ _chart_free_font(lxw_chart_font *font) if (!font) return; - free(font->name); + free((void *) font->name); free(font); } @@ -466,7 +466,7 @@ _chart_set_default_marker_type(lxw_chart *self, uint8_t type) /* * Set an axis number format. */ -void +STATIC void _chart_axis_set_default_num_format(lxw_chart_axis *axis, char *num_format) { if (!num_format) @@ -1142,6 +1142,11 @@ _chart_write_a_body_pr(lxw_chart *self, int32_t rotation, LXW_PUSH_ATTRIBUTES_STR("rot", "0"); LXW_PUSH_ATTRIBUTES_STR("vert", "eaVert"); } + else if (rotation == 21600000) { + /* 360 deg = 0 for y axis. */ + LXW_PUSH_ATTRIBUTES_STR("rot", "0"); + LXW_PUSH_ATTRIBUTES_STR("vert", "horz"); + } else { LXW_PUSH_ATTRIBUTES_INT("rot", rotation); LXW_PUSH_ATTRIBUTES_STR("vert", "horz"); @@ -1673,28 +1678,33 @@ _chart_write_a_ln(lxw_chart *self, lxw_chart_line *line) /* Convert to internal units. */ width_int = (uint32_t) (0.5 + (12700.0 * width_flt)); - if (width_int) + if (line->width > 0.0) LXW_PUSH_ATTRIBUTES_INT("w", width_int); - lxw_xml_start_tag(self->file, "a:ln", &attributes); + if (line->none || line->color || line->dash_type) { + lxw_xml_start_tag(self->file, "a:ln", &attributes); - /* Write the line fill. */ - if (line->none) { - /* Write the a:noFill element. */ - _chart_write_a_no_fill(self); - } - else if (line->color) { - /* Write the a:solidFill element. */ - _chart_write_a_solid_fill(self, line->color, line->transparency); - } + /* Write the line fill. */ + if (line->none) { + /* Write the a:noFill element. */ + _chart_write_a_no_fill(self); + } + else if (line->color) { + /* Write the a:solidFill element. */ + _chart_write_a_solid_fill(self, line->color, line->transparency); + } - /* Write the line/dash type. */ - if (line->dash_type) { - /* Write the a:prstDash element. */ - _chart_write_a_prst_dash(self, line->dash_type); - } + /* Write the line/dash type. */ + if (line->dash_type) { + /* Write the a:prstDash element. */ + _chart_write_a_prst_dash(self, line->dash_type); + } - lxw_xml_end_tag(self->file, "a:ln"); + lxw_xml_end_tag(self->file, "a:ln"); + } + else { + lxw_xml_empty_tag(self->file, "a:ln", &attributes); + } LXW_FREE_ATTRIBUTES(); } @@ -4659,17 +4669,15 @@ _chart_write_scatter_chart(lxw_chart *self) /* Add default scatter chart formatting to the series data unless * it has already been specified by the user.*/ - if (self->type == LXW_CHART_SCATTER) { - if (!series->line) { - lxw_chart_line line = { - 0x000000, - LXW_TRUE, - 2.25, - LXW_CHART_LINE_DASH_SOLID, - 0 - }; - series->line = _chart_convert_line_args(&line); - } + if (self->type == LXW_CHART_SCATTER && !series->line) { + lxw_chart_line line = { + 0x000000, + LXW_TRUE, + 2.25, + LXW_CHART_LINE_DASH_SOLID, + 0 + }; + series->line = _chart_convert_line_args(&line); } /* Write the c:ser element. */ @@ -5588,7 +5596,7 @@ chart_series_set_labels_custom(lxw_chart_series *series, for (i = 0; i < data_label_count; i++) { lxw_chart_data_label *user_label = data_labels[i]; lxw_chart_custom_label *data_label = &series->data_labels[i]; - char *src_value = user_label->value; + const char *src_value = user_label->value; data_label->hide = user_label->hide; data_label->font = _chart_convert_font_args(user_label->font); diff --git a/src/libxlsxwriter/chartsheet.c b/src/libxlsxwriter/chartsheet.c index 1b23638..b6c6725 100644 --- a/src/libxlsxwriter/chartsheet.c +++ b/src/libxlsxwriter/chartsheet.c @@ -3,7 +3,7 @@ * * Used in conjunction with the libxlsxwriter library. * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ @@ -66,8 +66,8 @@ lxw_chartsheet_free(lxw_chartsheet *chartsheet) return; lxw_worksheet_free(chartsheet->worksheet); - free(chartsheet->name); - free(chartsheet->quoted_name); + free((void *) chartsheet->name); + free((void *) chartsheet->quoted_name); free(chartsheet); } @@ -271,14 +271,13 @@ chartsheet_set_chart_opt(lxw_chartsheet *self, object_props->y_scale = user_options->y_scale; } - /* TODO. Read defaults from chart. */ object_props->width = 480; object_props->height = 288; - if (!object_props->x_scale) + if (object_props->x_scale == 0.0) object_props->x_scale = 1; - if (!object_props->y_scale) + if (object_props->y_scale == 0.0) object_props->y_scale = 1; /* Store chart references so they can be ordered in the workbook. */ diff --git a/src/libxlsxwriter/comment.c b/src/libxlsxwriter/comment.c index d3ac553..da45688 100644 --- a/src/libxlsxwriter/comment.c +++ b/src/libxlsxwriter/comment.c @@ -3,7 +3,7 @@ * * Used in conjunction with the libxlsxwriter library. * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ diff --git a/src/libxlsxwriter/content_types.c b/src/libxlsxwriter/content_types.c index 967f285..75c0b8c 100644 --- a/src/libxlsxwriter/content_types.c +++ b/src/libxlsxwriter/content_types.c @@ -3,7 +3,7 @@ * * Used in conjunction with the libxlsxwriter library. * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ @@ -322,6 +322,16 @@ lxw_ct_add_drawing_name(lxw_content_types *self, const char *name) lxw_ct_add_override(self, name, LXW_APP_DOCUMENT "drawing+xml"); } +/* + * Add the name of a table to the ContentTypes overrides. + */ +void +lxw_ct_add_table_name(lxw_content_types *self, const char *name) +{ + lxw_ct_add_override(self, name, + LXW_APP_DOCUMENT "spreadsheetml.table+xml"); +} + /* * Add the name of a VML drawing to the ContentTypes overrides. */ @@ -370,3 +380,13 @@ lxw_ct_add_custom_properties(lxw_content_types *self) lxw_ct_add_override(self, "/docProps/custom.xml", LXW_APP_DOCUMENT "custom-properties+xml"); } + +/* + * Add the metadata file to the ContentTypes overrides. + */ +void +lxw_ct_add_metadata(lxw_content_types *self) +{ + lxw_ct_add_override(self, "/xl/metadata.xml", + LXW_APP_DOCUMENT "spreadsheetml.sheetMetadata+xml"); +} diff --git a/src/libxlsxwriter/core.c b/src/libxlsxwriter/core.c index 7a1e2c6..da8e3c3 100644 --- a/src/libxlsxwriter/core.c +++ b/src/libxlsxwriter/core.c @@ -3,7 +3,7 @@ * * Used in conjunction with the libxlsxwriter library. * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ diff --git a/src/libxlsxwriter/custom.c b/src/libxlsxwriter/custom.c index da153be..135ab8c 100644 --- a/src/libxlsxwriter/custom.c +++ b/src/libxlsxwriter/custom.c @@ -3,7 +3,7 @@ * * Used in conjunction with the libxlsxwriter library. * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ diff --git a/src/libxlsxwriter/drawing.c b/src/libxlsxwriter/drawing.c index 4460bce..1903f9e 100644 --- a/src/libxlsxwriter/drawing.c +++ b/src/libxlsxwriter/drawing.c @@ -3,7 +3,7 @@ * * Used in conjunction with the libxlsxwriter library. * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ @@ -168,15 +168,13 @@ _drawing_write_row_off(lxw_drawing *self, char *data) } /* - * Write the element. + * Write the main part of the and elements. */ STATIC void -_drawing_write_from(lxw_drawing *self, lxw_drawing_coords *coords) +_drawing_write_coords(lxw_drawing *self, lxw_drawing_coords *coords) { char data[LXW_UINT32_T_LENGTH]; - lxw_xml_start_tag(self->file, "xdr:from", NULL); - lxw_snprintf(data, LXW_UINT32_T_LENGTH, "%u", coords->col); _drawing_write_col(self, data); @@ -190,6 +188,17 @@ _drawing_write_from(lxw_drawing *self, lxw_drawing_coords *coords) lxw_snprintf(data, LXW_UINT32_T_LENGTH, "%u", (uint32_t) coords->row_offset); _drawing_write_row_off(self, data); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_from(lxw_drawing *self, lxw_drawing_coords *coords) +{ + lxw_xml_start_tag(self->file, "xdr:from", NULL); + + _drawing_write_coords(self, coords); lxw_xml_end_tag(self->file, "xdr:from"); } @@ -200,23 +209,9 @@ _drawing_write_from(lxw_drawing *self, lxw_drawing_coords *coords) STATIC void _drawing_write_to(lxw_drawing *self, lxw_drawing_coords *coords) { - char data[LXW_UINT32_T_LENGTH]; - lxw_xml_start_tag(self->file, "xdr:to", NULL); - lxw_snprintf(data, LXW_UINT32_T_LENGTH, "%u", coords->col); - _drawing_write_col(self, data); - - lxw_snprintf(data, LXW_UINT32_T_LENGTH, "%u", - (uint32_t) coords->col_offset); - _drawing_write_col_off(self, data); - - lxw_snprintf(data, LXW_UINT32_T_LENGTH, "%u", coords->row); - _drawing_write_row(self, data); - - lxw_snprintf(data, LXW_UINT32_T_LENGTH, "%u", - (uint32_t) coords->row_offset); - _drawing_write_row_off(self, data); + _drawing_write_coords(self, coords); lxw_xml_end_tag(self->file, "xdr:to"); } @@ -653,12 +648,13 @@ _drawing_write_c_nv_graphic_frame_pr(lxw_drawing *self) * Write the element. */ STATIC void -_drawing_write_nv_graphic_frame_pr(lxw_drawing *self, uint32_t index) +_drawing_write_nv_graphic_frame_pr(lxw_drawing *self, uint32_t index, + lxw_drawing_object *drawing_object) { lxw_xml_start_tag(self->file, "xdr:nvGraphicFramePr", NULL); /* Write the xdr:cNvPr element. */ - _drawing_write_c_nv_pr(self, "Chart", index, NULL); + _drawing_write_c_nv_pr(self, "Chart", index, drawing_object); /* Write the xdr:cNvGraphicFramePr element. */ _drawing_write_c_nv_graphic_frame_pr(self); @@ -786,7 +782,8 @@ _drawing_write_a_graphic(lxw_drawing *self, uint32_t index) */ STATIC void _drawing_write_graphic_frame(lxw_drawing *self, uint32_t index, - uint32_t rel_index) + uint32_t rel_index, + lxw_drawing_object *drawing_object) { struct xml_attribute_list attributes; struct xml_attribute *attribute; @@ -797,7 +794,7 @@ _drawing_write_graphic_frame(lxw_drawing *self, uint32_t index, lxw_xml_start_tag(self->file, "xdr:graphicFrame", &attributes); /* Write the xdr:nvGraphicFramePr element. */ - _drawing_write_nv_graphic_frame_pr(self, index); + _drawing_write_nv_graphic_frame_pr(self, index, drawing_object); /* Write the xdr:xfrm element. */ _drawing_write_xfrm(self); @@ -834,7 +831,8 @@ _drawing_write_two_cell_anchor(lxw_drawing *self, uint32_t index, if (drawing_object->type == LXW_DRAWING_CHART) { /* Write the xdr:graphicFrame element for charts. */ - _drawing_write_graphic_frame(self, index, drawing_object->rel_index); + _drawing_write_graphic_frame(self, index, drawing_object->rel_index, + drawing_object); } else if (drawing_object->type == LXW_DRAWING_IMAGE) { /* Write the xdr:pic element. */ @@ -913,7 +911,7 @@ _drawing_write_absolute_anchor(lxw_drawing *self, uint32_t frame_index) _drawing_write_ext(self, 6162675, 6124575); } - _drawing_write_graphic_frame(self, frame_index, frame_index); + _drawing_write_graphic_frame(self, frame_index, frame_index, NULL); /* Write the xdr:clientData element. */ _drawing_write_client_data(self); diff --git a/src/libxlsxwriter/format.c b/src/libxlsxwriter/format.c index c0ed96b..384e821 100644 --- a/src/libxlsxwriter/format.c +++ b/src/libxlsxwriter/format.c @@ -3,7 +3,7 @@ * * Used in conjunction with the libxlsxwriter library. * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ @@ -98,6 +98,8 @@ lxw_format_new(void) format->color_indexed = LXW_FALSE; format->font_only = LXW_FALSE; + format->quote_prefix = LXW_FALSE; + return format; mem_error: @@ -799,3 +801,12 @@ format_set_hyperlink(lxw_format *self) self->underline = LXW_UNDERLINE_SINGLE; self->theme = 10; } + +/* + * Set the quote_prefix property. + */ +void +format_set_quote_prefix(lxw_format *self) +{ + self->quote_prefix = LXW_TRUE; +} diff --git a/src/libxlsxwriter/hash_table.c b/src/libxlsxwriter/hash_table.c index a0a20c2..98efc7a 100644 --- a/src/libxlsxwriter/hash_table.c +++ b/src/libxlsxwriter/hash_table.c @@ -3,7 +3,7 @@ * * Used in conjunction with the libxlsxwriter library. * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ diff --git a/src/libxlsxwriter/metadata.c b/src/libxlsxwriter/metadata.c new file mode 100644 index 0000000..6ce07fa --- /dev/null +++ b/src/libxlsxwriter/metadata.c @@ -0,0 +1,283 @@ +/***************************************************************************** + * metadata - A library for creating Excel XLSX metadata files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/metadata.h" +#include "xlsxwriter/utility.h" + +/* + * Forward declarations. + */ + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ + +/* + * Create a new metadata object. + */ +lxw_metadata * +lxw_metadata_new(void) +{ + lxw_metadata *metadata = calloc(1, sizeof(lxw_metadata)); + GOTO_LABEL_ON_MEM_ERROR(metadata, mem_error); + + return metadata; + +mem_error: + lxw_metadata_free(metadata); + return NULL; +} + +/* + * Free a metadata object. + */ +void +lxw_metadata_free(lxw_metadata *metadata) +{ + if (!metadata) + return; + + free(metadata); +} + +/***************************************************************************** + * + * XML functions. + * + ****************************************************************************/ + +/* + * Write the XML declaration. + */ +STATIC void +_metadata_xml_declaration(lxw_metadata *self) +{ + lxw_xml_declaration(self->file); +} + +/* + * Write the element. + */ +STATIC void +_metadata_write_metadata(lxw_metadata *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char xmlns[] = "http://schemas.openxmlformats.org/" + "spreadsheetml/2006/main"; + char xmlns_xda[] = "http://schemas.microsoft.com/office/" + "spreadsheetml/2017/dynamicarray"; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns); + LXW_PUSH_ATTRIBUTES_STR("xmlns:xda", xmlns_xda); + + lxw_xml_start_tag(self->file, "metadata", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_metadata_write_metadata_type(lxw_metadata *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("name", "XLDAPR"); + LXW_PUSH_ATTRIBUTES_INT("minSupportedVersion", 120000); + LXW_PUSH_ATTRIBUTES_INT("copy", 1); + LXW_PUSH_ATTRIBUTES_INT("pasteAll", 1); + LXW_PUSH_ATTRIBUTES_INT("pasteValues", 1); + LXW_PUSH_ATTRIBUTES_INT("merge", 1); + LXW_PUSH_ATTRIBUTES_INT("splitFirst", 1); + LXW_PUSH_ATTRIBUTES_INT("rowColShift", 1); + LXW_PUSH_ATTRIBUTES_INT("clearFormats", 1); + LXW_PUSH_ATTRIBUTES_INT("clearComments", 1); + LXW_PUSH_ATTRIBUTES_INT("assign", 1); + LXW_PUSH_ATTRIBUTES_INT("coerce", 1); + LXW_PUSH_ATTRIBUTES_INT("cellMeta", 1); + + lxw_xml_empty_tag(self->file, "metadataType", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_metadata_write_metadata_types(lxw_metadata *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("count", 1); + + lxw_xml_start_tag(self->file, "metadataTypes", &attributes); + + /* Write the metadataType element. */ + _metadata_write_metadata_type(self); + + lxw_xml_end_tag(self->file, "metadataTypes"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_metadata_write_xda_dynamic_array_properties(lxw_metadata *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("fDynamic", "1"); + LXW_PUSH_ATTRIBUTES_STR("fCollapsed", "0"); + + lxw_xml_empty_tag(self->file, "xda:dynamicArrayProperties", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_metadata_write_ext(lxw_metadata *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("uri", "{bdbb8cdc-fa1e-496e-a857-3c3f30c029c3}"); + + lxw_xml_start_tag(self->file, "ext", &attributes); + + /* Write the xda:dynamicArrayProperties element. */ + _metadata_write_xda_dynamic_array_properties(self); + + lxw_xml_end_tag(self->file, "ext"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_metadata_write_future_metadata(lxw_metadata *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("name", "XLDAPR"); + LXW_PUSH_ATTRIBUTES_INT("count", 1); + + lxw_xml_start_tag(self->file, "futureMetadata", &attributes); + + lxw_xml_start_tag(self->file, "bk", NULL); + + lxw_xml_start_tag(self->file, "extLst", NULL); + + /* Write the ext element. */ + _metadata_write_ext(self); + + lxw_xml_end_tag(self->file, "extLst"); + + lxw_xml_end_tag(self->file, "bk"); + + lxw_xml_end_tag(self->file, "futureMetadata"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_metadata_write_rc(lxw_metadata *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("t", "1"); + LXW_PUSH_ATTRIBUTES_STR("v", "0"); + + lxw_xml_empty_tag(self->file, "rc", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_metadata_write_cell_metadata(lxw_metadata *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("count", "1"); + + lxw_xml_start_tag(self->file, "cellMetadata", &attributes); + + lxw_xml_start_tag(self->file, "bk", NULL); + + /* Write the rc element. */ + _metadata_write_rc(self); + + lxw_xml_end_tag(self->file, "bk"); + + lxw_xml_end_tag(self->file, "cellMetadata"); + + LXW_FREE_ATTRIBUTES(); +} + +/***************************************************************************** + * + * XML file assembly functions. + * + ****************************************************************************/ + +/* + * Assemble and write the XML file. + */ +void +lxw_metadata_assemble_xml_file(lxw_metadata *self) +{ + /* Write the XML declaration. */ + _metadata_xml_declaration(self); + + /* Write the metadata element. */ + _metadata_write_metadata(self); + + /* Write the metadataTypes element. */ + _metadata_write_metadata_types(self); + + /* Write the futureMetadata element. */ + _metadata_write_future_metadata(self); + + /* Write the cellMetadata element. */ + _metadata_write_cell_metadata(self); + + lxw_xml_end_tag(self->file, "metadata"); +} diff --git a/src/libxlsxwriter/packager.c b/src/libxlsxwriter/packager.c index 740e558..d5d2a1b 100644 --- a/src/libxlsxwriter/packager.c +++ b/src/libxlsxwriter/packager.c @@ -1,9 +1,45 @@ /***************************************************************************** - * packager - A library for creating Excel XLSX packager files. + * packager - A library for assembling xml files into an Excel XLSX file. * - * Used in conjunction with the libxlsxwriter library. + * A class for writing the Excel XLSX Packager file. * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * This module is used in conjunction with libxlsxwriter to create an + * Excel XLSX container file. + * + * From Wikipedia: The Open Packaging Conventions (OPC) is a + * container-file technology initially created by Microsoft to store + * a combination of XML and non-XML files that together form a single + * entity such as an Open XML Paper Specification (OpenXPS) + * document. http://en.wikipedia.org/wiki/Open_Packaging_Conventions. + * + * At its simplest an Excel XLSX file contains the following elements:: + * + * ____ [Content_Types].xml + * | + * |____ docProps + * | |____ app.xml + * | |____ core.xml + * | + * |____ xl + * | |____ workbook.xml + * | |____ worksheets + * | | |____ sheet1.xml + * | | + * | |____ styles.xml + * | | + * | |____ theme + * | | |____ theme1.xml + * | | + * | |_____rels + * | |____ workbook.xml.rels + * | + * |_____rels + * |____ .rels + * + * The Packager class coordinates the classes that represent the + * elements of the package and writes them into the XLSX file. + * + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ @@ -16,9 +52,13 @@ STATIC lxw_error _add_file_to_zip(lxw_packager *self, FILE * file, const char *filename); -STATIC lxw_error _add_buffer_to_zip(lxw_packager *self, unsigned char *buffer, +STATIC lxw_error _add_buffer_to_zip(lxw_packager *self, const char *buffer, size_t buffer_size, const char *filename); +STATIC lxw_error _add_to_zip(lxw_packager *self, FILE * file, + char **buffer, size_t *buffer_size, + const char *filename); + STATIC lxw_error _write_vml_drawing_rels_file(lxw_packager *self, lxw_worksheet *worksheet, uint32_t index); @@ -41,7 +81,7 @@ STATIC lxw_error _write_vml_drawing_rels_file(lxw_packager *self, #ifdef _WIN32 /* Silence Windows warning with duplicate symbol for SLIST_ENTRY in local - * queue.h and widows.h. */ + * queue.h and windows.h. */ #undef SLIST_ENTRY #include @@ -77,21 +117,75 @@ _open_zipfile_win32(const char *filename) #endif +STATIC voidpf ZCALLBACK +_fopen_memstream(voidpf opaque, const char *filename, int mode) +{ + lxw_packager *packager = (lxw_packager *) opaque; + (void) filename; + (void) mode; + return lxw_get_filehandle(&packager->output_buffer, + &packager->output_buffer_size, + packager->tmpdir); +} + +STATIC int ZCALLBACK +_fclose_memstream(voidpf opaque, voidpf stream) +{ + lxw_packager *packager = (lxw_packager *) opaque; + FILE *file = (FILE *) stream; + long size; + + /* Ensure memstream buffer is updated */ + if (fflush(file)) + goto mem_error; + + /* If the memstream is backed by a temporary file, no buffer is created, + so create it manually. */ + if (!packager->output_buffer) { + if (fseek(file, 0L, SEEK_END)) + goto mem_error; + + size = ftell(file); + if (size == -1) + goto mem_error; + + packager->output_buffer = malloc(size); + GOTO_LABEL_ON_MEM_ERROR(packager->output_buffer, mem_error); + + rewind(file); + if (fread((void *) packager->output_buffer, size, 1, file) < 1) + goto mem_error; + + packager->output_buffer_size = size; + } + + return fclose(file); + +mem_error: + fclose(file); + return EOF; +} + /* * Create a new packager object. */ lxw_packager * -lxw_packager_new(const char *filename, char *tmpdir, uint8_t use_zip64) +lxw_packager_new(const char *filename, const char *tmpdir, uint8_t use_zip64) { + zlib_filefunc_def filefunc; lxw_packager *packager = calloc(1, sizeof(lxw_packager)); GOTO_LABEL_ON_MEM_ERROR(packager, mem_error); packager->buffer = calloc(1, LXW_ZIP_BUFFER_SIZE); GOTO_LABEL_ON_MEM_ERROR(packager->buffer, mem_error); - packager->filename = lxw_strdup(filename); + packager->filename = NULL; packager->tmpdir = tmpdir; - GOTO_LABEL_ON_MEM_ERROR(packager->filename, mem_error); + + if (filename) { + packager->filename = lxw_strdup(filename); + GOTO_LABEL_ON_MEM_ERROR(packager->filename, mem_error); + } packager->buffer_size = LXW_ZIP_BUFFER_SIZE; packager->use_zip64 = use_zip64; @@ -107,12 +201,24 @@ lxw_packager_new(const char *filename, char *tmpdir, uint8_t use_zip64) packager->zipfile_info.internal_fa = 0; packager->zipfile_info.external_fa = 0; + packager->output_buffer = NULL; + packager->output_buffer_size = 0; + /* Create a zip container for the xlsx file. */ + if (packager->filename) { #ifdef _WIN32 - packager->zipfile = _open_zipfile_win32(packager->filename); + packager->zipfile = _open_zipfile_win32(packager->filename); #else - packager->zipfile = zipOpen(packager->filename, 0); + packager->zipfile = zipOpen(packager->filename, 0); #endif + } + else { + fill_fopen_filefunc(&filefunc); + filefunc.opaque = packager; + filefunc.zopen_file = _fopen_memstream; + filefunc.zclose_file = _fclose_memstream; + packager->zipfile = zipOpen2(packager->filename, 0, NULL, &filefunc); + } if (packager->zipfile == NULL) goto mem_error; @@ -133,8 +239,8 @@ lxw_packager_free(lxw_packager *packager) if (!packager) return; - free(packager->buffer); - free(packager->filename); + free((void *) packager->buffer); + free((void *) packager->filename); free(packager); } @@ -152,14 +258,18 @@ _write_workbook_file(lxw_packager *self) lxw_workbook *workbook = self->workbook; lxw_error err; - workbook->file = lxw_tmpfile(self->tmpdir); + char *buffer = NULL; + size_t buffer_size = 0; + workbook->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!workbook->file) return LXW_ERROR_CREATING_TMPFILE; lxw_workbook_assemble_xml_file(workbook); - err = _add_file_to_zip(self, workbook->file, "xl/workbook.xml"); + err = _add_to_zip(self, workbook->file, &buffer, &buffer_size, + "xl/workbook.xml"); fclose(workbook->file); + free(buffer); RETURN_ON_ERROR(err); return LXW_NO_ERROR; @@ -175,6 +285,8 @@ _write_worksheet_files(lxw_packager *self) lxw_sheet *sheet; lxw_worksheet *worksheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; + char *buffer = NULL; + size_t buffer_size = 0; uint32_t index = 1; lxw_error err; @@ -190,14 +302,17 @@ _write_worksheet_files(lxw_packager *self) if (worksheet->optimize_row) lxw_worksheet_write_single_row(worksheet); - worksheet->file = lxw_tmpfile(self->tmpdir); + worksheet->file = lxw_get_filehandle(&buffer, &buffer_size, + self->tmpdir); if (!worksheet->file) return LXW_ERROR_CREATING_TMPFILE; lxw_worksheet_assemble_xml_file(worksheet); - err = _add_file_to_zip(self, worksheet->file, sheetname); + err = _add_to_zip(self, worksheet->file, &buffer, &buffer_size, + sheetname); fclose(worksheet->file); + free(buffer); RETURN_ON_ERROR(err); } @@ -214,6 +329,8 @@ _write_chartsheet_files(lxw_packager *self) lxw_sheet *sheet; lxw_chartsheet *chartsheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; + char *buffer = NULL; + size_t buffer_size = 0; uint32_t index = 1; lxw_error err; @@ -226,14 +343,17 @@ _write_chartsheet_files(lxw_packager *self) lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, "xl/chartsheets/sheet%d.xml", index++); - chartsheet->file = lxw_tmpfile(self->tmpdir); + chartsheet->file = lxw_get_filehandle(&buffer, &buffer_size, + self->tmpdir); if (!chartsheet->file) return LXW_ERROR_CREATING_TMPFILE; lxw_chartsheet_assemble_xml_file(chartsheet); - err = _add_file_to_zip(self, chartsheet->file, sheetname); + err = _add_to_zip(self, chartsheet->file, &buffer, &buffer_size, + sheetname); fclose(chartsheet->file); + free(buffer); RETURN_ON_ERROR(err); } @@ -330,6 +450,35 @@ _add_vba_project(lxw_packager *self) return LXW_NO_ERROR; } +/* + * Write the xl/vbaProjectSignature.bin file. + */ +STATIC lxw_error +_add_vba_project_signature(lxw_packager *self) +{ + lxw_workbook *workbook = self->workbook; + lxw_error err; + FILE *image_stream; + + if (!workbook->vba_project_signature) + return LXW_NO_ERROR; + + /* Check that the image file exists and can be opened. */ + image_stream = lxw_fopen(workbook->vba_project_signature, "rb"); + if (!image_stream) { + LXW_WARN_FORMAT1("Error adding vbaProjectSignature.bin to xlsx file: " + "file doesn't exist or can't be opened: %s.", + workbook->vba_project_signature); + return LXW_ERROR_CREATING_TMPFILE; + } + + err = _add_file_to_zip(self, image_stream, "xl/vbaProjectSignature.bin"); + fclose(image_stream); + RETURN_ON_ERROR(err); + + return LXW_NO_ERROR; +} + /* * Write the chart files. */ @@ -339,6 +488,8 @@ _write_chart_files(lxw_packager *self) lxw_workbook *workbook = self->workbook; lxw_chart *chart; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; + char *buffer = NULL; + size_t buffer_size = 0; uint32_t index = 1; lxw_error err; @@ -347,14 +498,16 @@ _write_chart_files(lxw_packager *self) lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, "xl/charts/chart%d.xml", index++); - chart->file = lxw_tmpfile(self->tmpdir); + chart->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!chart->file) return LXW_ERROR_CREATING_TMPFILE; lxw_chart_assemble_xml_file(chart); - err = _add_file_to_zip(self, chart->file, sheetname); + err = _add_to_zip(self, chart->file, &buffer, &buffer_size, + sheetname); fclose(chart->file); + free(buffer); RETURN_ON_ERROR(err); } @@ -389,6 +542,8 @@ _write_drawing_files(lxw_packager *self) lxw_worksheet *worksheet; lxw_drawing *drawing; char filename[LXW_FILENAME_LENGTH] = { 0 }; + char *buffer = NULL; + size_t buffer_size = 0; uint32_t index = 1; lxw_error err; @@ -404,14 +559,17 @@ _write_drawing_files(lxw_packager *self) lxw_snprintf(filename, LXW_FILENAME_LENGTH, "xl/drawings/drawing%d.xml", index++); - drawing->file = lxw_tmpfile(self->tmpdir); + drawing->file = lxw_get_filehandle(&buffer, &buffer_size, + self->tmpdir); if (!drawing->file) return LXW_ERROR_CREATING_TMPFILE; lxw_drawing_assemble_xml_file(drawing); - err = _add_file_to_zip(self, drawing->file, filename); + err = _add_to_zip(self, drawing->file, &buffer, &buffer_size, + filename); fclose(drawing->file); + free(buffer); RETURN_ON_ERROR(err); } } @@ -446,6 +604,90 @@ _get_drawing_count(lxw_packager *self) return drawing_count; } +/* + * Write the worksheet table files. + */ +STATIC lxw_error +_write_table_files(lxw_packager *self) +{ + lxw_workbook *workbook = self->workbook; + lxw_sheet *sheet; + lxw_worksheet *worksheet; + lxw_table *table; + lxw_table_obj *table_obj; + lxw_error err; + + char filename[LXW_FILENAME_LENGTH] = { 0 }; + char *buffer = NULL; + size_t buffer_size = 0; + uint32_t index = 1; + + STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { + if (sheet->is_chartsheet) + continue; + else + worksheet = sheet->u.worksheet; + + if (STAILQ_EMPTY(worksheet->table_objs)) + continue; + + STAILQ_FOREACH(table_obj, worksheet->table_objs, list_pointers) { + + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "xl/tables/table%d.xml", index++); + + table = lxw_table_new(); + if (!table) { + err = LXW_ERROR_MEMORY_MALLOC_FAILED; + RETURN_ON_ERROR(err); + } + + table->file = lxw_get_filehandle(&buffer, &buffer_size, + self->tmpdir); + if (!table->file) { + lxw_table_free(table); + return LXW_ERROR_CREATING_TMPFILE; + } + + table->table_obj = table_obj; + + lxw_table_assemble_xml_file(table); + + err = _add_to_zip(self, table->file, &buffer, &buffer_size, + filename); + fclose(table->file); + free(buffer); + lxw_table_free(table); + RETURN_ON_ERROR(err); + } + } + + return LXW_NO_ERROR; +} + +/* + * Count the table files. + */ +uint32_t +_get_table_count(lxw_packager *self) +{ + lxw_workbook *workbook = self->workbook; + lxw_sheet *sheet; + lxw_worksheet *worksheet; + uint32_t table_count = 0; + + STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { + if (sheet->is_chartsheet) + worksheet = sheet->u.chartsheet->worksheet; + else + worksheet = sheet->u.worksheet; + + table_count += worksheet->table_count; + } + + return table_count; +} + /* * Write the comment/header VML files. */ @@ -457,6 +699,8 @@ _write_vml_files(lxw_packager *self) lxw_worksheet *worksheet; lxw_vml *vml; char filename[LXW_FILENAME_LENGTH] = { 0 }; + char *buffer = NULL; + size_t buffer_size = 0; uint32_t index = 1; lxw_error err; @@ -478,13 +722,15 @@ _write_vml_files(lxw_packager *self) lxw_snprintf(filename, LXW_FILENAME_LENGTH, "xl/drawings/vmlDrawing%d.vml", index++); - vml->file = lxw_tmpfile(self->tmpdir); + vml->file = lxw_get_filehandle(&buffer, &buffer_size, + self->tmpdir); if (!vml->file) { lxw_vml_free(vml); return LXW_ERROR_CREATING_TMPFILE; } vml->comment_objs = worksheet->comment_objs; + vml->button_objs = worksheet->button_objs; vml->vml_shape_id = worksheet->vml_shape_id; vml->comment_display_default = worksheet->comment_display_default; @@ -493,15 +739,18 @@ _write_vml_files(lxw_packager *self) } else { fclose(vml->file); + free(buffer); lxw_vml_free(vml); return LXW_ERROR_MEMORY_MALLOC_FAILED; } lxw_vml_assemble_xml_file(vml); - err = _add_file_to_zip(self, vml->file, filename); + err = _add_to_zip(self, vml->file, &buffer, &buffer_size, + filename); fclose(vml->file); + free(buffer); lxw_vml_free(vml); RETURN_ON_ERROR(err); @@ -519,7 +768,8 @@ _write_vml_files(lxw_packager *self) lxw_snprintf(filename, LXW_FILENAME_LENGTH, "xl/drawings/vmlDrawing%d.vml", index++); - vml->file = lxw_tmpfile(self->tmpdir); + vml->file = lxw_get_filehandle(&buffer, &buffer_size, + self->tmpdir); if (!vml->file) { lxw_vml_free(vml); return LXW_ERROR_CREATING_TMPFILE; @@ -533,15 +783,18 @@ _write_vml_files(lxw_packager *self) } else { fclose(vml->file); + free(buffer); lxw_vml_free(vml); return LXW_ERROR_MEMORY_MALLOC_FAILED; } lxw_vml_assemble_xml_file(vml); - err = _add_file_to_zip(self, vml->file, filename); + err = _add_to_zip(self, vml->file, &buffer, &buffer_size, + filename); fclose(vml->file); + free(buffer); lxw_vml_free(vml); RETURN_ON_ERROR(err); @@ -562,6 +815,8 @@ _write_comment_files(lxw_packager *self) lxw_worksheet *worksheet; lxw_comment *comment; char filename[LXW_FILENAME_LENGTH] = { 0 }; + char *buffer = NULL; + size_t buffer_size = 0; uint32_t index = 1; lxw_error err; @@ -581,7 +836,8 @@ _write_comment_files(lxw_packager *self) lxw_snprintf(filename, LXW_FILENAME_LENGTH, "xl/comments%d.xml", index++); - comment->file = lxw_tmpfile(self->tmpdir); + comment->file = lxw_get_filehandle(&buffer, &buffer_size, + self->tmpdir); if (!comment->file) { lxw_comment_free(comment); return LXW_ERROR_CREATING_TMPFILE; @@ -592,9 +848,11 @@ _write_comment_files(lxw_packager *self) lxw_comment_assemble_xml_file(comment); - err = _add_file_to_zip(self, comment->file, filename); + err = _add_to_zip(self, comment->file, &buffer, &buffer_size, + filename); fclose(comment->file); + free(buffer); lxw_comment_free(comment); RETURN_ON_ERROR(err); @@ -610,20 +868,24 @@ STATIC lxw_error _write_shared_strings_file(lxw_packager *self) { lxw_sst *sst = self->workbook->sst; + char *buffer = NULL; + size_t buffer_size = 0; lxw_error err; /* Skip the sharedStrings file if there are no shared strings. */ if (!sst->string_count) return LXW_NO_ERROR; - sst->file = lxw_tmpfile(self->tmpdir); + sst->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!sst->file) return LXW_ERROR_CREATING_TMPFILE; lxw_sst_assemble_xml_file(sst); - err = _add_file_to_zip(self, sst->file, "xl/sharedStrings.xml"); + err = _add_to_zip(self, sst->file, &buffer, &buffer_size, + "xl/sharedStrings.xml"); fclose(sst->file); + free(buffer); RETURN_ON_ERROR(err); return LXW_NO_ERROR; @@ -641,6 +903,8 @@ _write_app_file(lxw_packager *self) lxw_chartsheet *chartsheet; lxw_defined_name *defined_name; lxw_app *app; + char *buffer = NULL; + size_t buffer_size = 0; uint32_t named_range_count = 0; char *autofilter; char *has_range; @@ -653,7 +917,7 @@ _write_app_file(lxw_packager *self) goto mem_error; } - app->file = lxw_tmpfile(self->tmpdir); + app->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!app->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; @@ -711,9 +975,11 @@ _write_app_file(lxw_packager *self) lxw_app_assemble_xml_file(app); - err = _add_file_to_zip(self, app->file, "docProps/app.xml"); + err = _add_to_zip(self, app->file, &buffer, &buffer_size, + "docProps/app.xml"); fclose(app->file); + free(buffer); mem_error: lxw_app_free(app); @@ -729,13 +995,15 @@ _write_core_file(lxw_packager *self) { lxw_error err = LXW_NO_ERROR; lxw_core *core = lxw_core_new(); + char *buffer = NULL; + size_t buffer_size = 0; if (!core) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; } - core->file = lxw_tmpfile(self->tmpdir); + core->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!core->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; @@ -745,9 +1013,11 @@ _write_core_file(lxw_packager *self) lxw_core_assemble_xml_file(core); - err = _add_file_to_zip(self, core->file, "docProps/core.xml"); + err = _add_to_zip(self, core->file, &buffer, &buffer_size, + "docProps/core.xml"); fclose(core->file); + free(buffer); mem_error: lxw_core_free(core); @@ -755,6 +1025,47 @@ _write_core_file(lxw_packager *self) return err; } +/* + * Write the metadata.xml file. + */ +STATIC lxw_error +_write_metadata_file(lxw_packager *self) +{ + lxw_error err = LXW_NO_ERROR; + lxw_metadata *metadata; + char *buffer = NULL; + size_t buffer_size = 0; + + if (!self->workbook->has_metadata) + return LXW_NO_ERROR; + + metadata = lxw_metadata_new(); + + if (!metadata) { + err = LXW_ERROR_MEMORY_MALLOC_FAILED; + goto mem_error; + } + + metadata->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); + if (!metadata->file) { + err = LXW_ERROR_CREATING_TMPFILE; + goto mem_error; + } + + lxw_metadata_assemble_xml_file(metadata); + + err = _add_to_zip(self, metadata->file, &buffer, &buffer_size, + "xl/metadata.xml"); + + fclose(metadata->file); + free(buffer); + +mem_error: + lxw_metadata_free(metadata); + + return err; +} + /* * Write the custom.xml file. */ @@ -762,6 +1073,8 @@ STATIC lxw_error _write_custom_file(lxw_packager *self) { lxw_custom *custom; + char *buffer = NULL; + size_t buffer_size = 0; lxw_error err = LXW_NO_ERROR; if (STAILQ_EMPTY(self->workbook->custom_properties)) @@ -773,7 +1086,7 @@ _write_custom_file(lxw_packager *self) goto mem_error; } - custom->file = lxw_tmpfile(self->tmpdir); + custom->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!custom->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; @@ -783,9 +1096,11 @@ _write_custom_file(lxw_packager *self) lxw_custom_assemble_xml_file(custom); - err = _add_file_to_zip(self, custom->file, "docProps/custom.xml"); + err = _add_to_zip(self, custom->file, &buffer, &buffer_size, + "docProps/custom.xml"); fclose(custom->file); + free(buffer); mem_error: lxw_custom_free(custom); @@ -800,13 +1115,15 @@ _write_theme_file(lxw_packager *self) { lxw_error err = LXW_NO_ERROR; lxw_theme *theme = lxw_theme_new(); + char *buffer = NULL; + size_t buffer_size = 0; if (!theme) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; } - theme->file = lxw_tmpfile(self->tmpdir); + theme->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!theme->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; @@ -814,9 +1131,11 @@ _write_theme_file(lxw_packager *self) lxw_theme_assemble_xml_file(theme); - err = _add_file_to_zip(self, theme->file, "xl/theme/theme1.xml"); + err = _add_to_zip(self, theme->file, &buffer, &buffer_size, + "xl/theme/theme1.xml"); fclose(theme->file); + free(buffer); mem_error: lxw_theme_free(theme); @@ -831,6 +1150,8 @@ STATIC lxw_error _write_styles_file(lxw_packager *self) { lxw_styles *styles = lxw_styles_new(); + char *buffer = NULL; + size_t buffer_size = 0; lxw_hash_element *hash_element; lxw_error err = LXW_NO_ERROR; @@ -877,7 +1198,7 @@ _write_styles_file(lxw_packager *self) styles->dxf_count = self->workbook->used_dxf_formats->unique_count; styles->has_comments = self->workbook->has_comments; - styles->file = lxw_tmpfile(self->tmpdir); + styles->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!styles->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; @@ -885,9 +1206,11 @@ _write_styles_file(lxw_packager *self) lxw_styles_assemble_xml_file(styles); - err = _add_file_to_zip(self, styles->file, "xl/styles.xml"); + err = _add_to_zip(self, styles->file, &buffer, &buffer_size, + "xl/styles.xml"); fclose(styles->file); + free(buffer); mem_error: lxw_styles_free(styles); @@ -902,6 +1225,8 @@ STATIC lxw_error _write_content_types_file(lxw_packager *self) { lxw_content_types *content_types = lxw_content_types_new(); + char *buffer = NULL; + size_t buffer_size = 0; lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; char filename[LXW_MAX_ATTRIBUTE_LENGTH] = { 0 }; @@ -910,6 +1235,7 @@ _write_content_types_file(lxw_packager *self) uint32_t chartsheet_index = 1; uint32_t drawing_count = _get_drawing_count(self); uint32_t chart_count = _get_chart_count(self); + uint32_t table_count = _get_table_count(self); lxw_error err = LXW_NO_ERROR; if (!content_types) { @@ -917,7 +1243,8 @@ _write_content_types_file(lxw_packager *self) goto mem_error; } - content_types->file = lxw_tmpfile(self->tmpdir); + content_types->file = lxw_get_filehandle(&buffer, &buffer_size, + self->tmpdir); if (!content_types->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; @@ -932,6 +1259,9 @@ _write_content_types_file(lxw_packager *self) if (workbook->has_bmp) lxw_ct_add_default(content_types, "bmp", "image/bmp"); + if (workbook->has_gif) + lxw_ct_add_default(content_types, "gif", "image/gif"); + if (workbook->vba_project) lxw_ct_add_default(content_types, "bin", "application/vnd.ms-office.vbaProject"); @@ -943,6 +1273,10 @@ _write_content_types_file(lxw_packager *self) lxw_ct_add_override(content_types, "/xl/workbook.xml", LXW_APP_DOCUMENT "spreadsheetml.sheet.main+xml"); + if (workbook->vba_project_signature) + lxw_ct_add_override(content_types, "/xl/vbaProjectSignature.bin", + "application/vnd.ms-office.vbaProjectSignature"); + STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) { lxw_snprintf(filename, LXW_FILENAME_LENGTH, @@ -968,6 +1302,12 @@ _write_content_types_file(lxw_packager *self) lxw_ct_add_drawing_name(content_types, filename); } + for (index = 1; index <= table_count; index++) { + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "/xl/tables/table%d.xml", index); + lxw_ct_add_table_name(content_types, filename); + } + if (workbook->has_vml) lxw_ct_add_vml_name(content_types); @@ -983,11 +1323,16 @@ _write_content_types_file(lxw_packager *self) if (!STAILQ_EMPTY(self->workbook->custom_properties)) lxw_ct_add_custom_properties(content_types); + if (workbook->has_metadata) + lxw_ct_add_metadata(content_types); + lxw_content_types_assemble_xml_file(content_types); - err = _add_file_to_zip(self, content_types->file, "[Content_Types].xml"); + err = _add_to_zip(self, content_types->file, &buffer, &buffer_size, + "[Content_Types].xml"); fclose(content_types->file); + free(buffer); mem_error: lxw_content_types_free(content_types); @@ -1002,6 +1347,8 @@ STATIC lxw_error _write_workbook_rels_file(lxw_packager *self) { lxw_relationships *rels = lxw_relationships_new(); + char *buffer = NULL; + size_t buffer_size = 0; lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; @@ -1014,7 +1361,7 @@ _write_workbook_rels_file(lxw_packager *self) goto mem_error; } - rels->file = lxw_tmpfile(self->tmpdir); + rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!rels->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; @@ -1046,11 +1393,16 @@ _write_workbook_rels_file(lxw_packager *self) lxw_add_ms_package_relationship(rels, "/vbaProject", "vbaProject.bin"); + if (workbook->has_metadata) + lxw_add_document_relationship(rels, "/sheetMetadata", "metadata.xml"); + lxw_relationships_assemble_xml_file(rels); - err = _add_file_to_zip(self, rels->file, "xl/_rels/workbook.xml.rels"); + err = _add_to_zip(self, rels->file, &buffer, &buffer_size, + "xl/_rels/workbook.xml.rels"); fclose(rels->file); + free(buffer); mem_error: lxw_free_relationships(rels); @@ -1066,6 +1418,8 @@ STATIC lxw_error _write_worksheet_rels_file(lxw_packager *self) { lxw_relationships *rels; + char *buffer = NULL; + size_t buffer_size = 0; lxw_rel_tuple *rel; lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; @@ -1084,14 +1438,16 @@ _write_worksheet_rels_file(lxw_packager *self) if (STAILQ_EMPTY(worksheet->external_hyperlinks) && STAILQ_EMPTY(worksheet->external_drawing_links) && + STAILQ_EMPTY(worksheet->external_table_links) && !worksheet->external_vml_header_link && !worksheet->external_vml_comment_link && + !worksheet->external_background_link && !worksheet->external_comment_link) continue; rels = lxw_relationships_new(); - rels->file = lxw_tmpfile(self->tmpdir); + rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!rels->file) { lxw_free_relationships(rels); return LXW_ERROR_CREATING_TMPFILE; @@ -1117,6 +1473,16 @@ _write_worksheet_rels_file(lxw_packager *self) lxw_add_worksheet_relationship(rels, rel->type, rel->target, rel->target_mode); + rel = worksheet->external_background_link; + if (rel) + lxw_add_worksheet_relationship(rels, rel->type, rel->target, + rel->target_mode); + + STAILQ_FOREACH(rel, worksheet->external_table_links, list_pointers) { + lxw_add_worksheet_relationship(rels, rel->type, rel->target, + rel->target_mode); + } + rel = worksheet->external_comment_link; if (rel) lxw_add_worksheet_relationship(rels, rel->type, rel->target, @@ -1127,9 +1493,10 @@ _write_worksheet_rels_file(lxw_packager *self) lxw_relationships_assemble_xml_file(rels); - err = _add_file_to_zip(self, rels->file, sheetname); + err = _add_to_zip(self, rels->file, &buffer, &buffer_size, sheetname); fclose(rels->file); + free(buffer); lxw_free_relationships(rels); RETURN_ON_ERROR(err); @@ -1146,6 +1513,8 @@ STATIC lxw_error _write_chartsheet_rels_file(lxw_packager *self) { lxw_relationships *rels; + char *buffer = NULL; + size_t buffer_size = 0; lxw_rel_tuple *rel; lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; @@ -1162,13 +1531,12 @@ _write_chartsheet_rels_file(lxw_packager *self) index++; - /* TODO. This should never be empty. Put check higher up. */ if (STAILQ_EMPTY(worksheet->external_drawing_links)) continue; rels = lxw_relationships_new(); - rels->file = lxw_tmpfile(self->tmpdir); + rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!rels->file) { lxw_free_relationships(rels); return LXW_ERROR_CREATING_TMPFILE; @@ -1189,9 +1557,10 @@ _write_chartsheet_rels_file(lxw_packager *self) lxw_relationships_assemble_xml_file(rels); - err = _add_file_to_zip(self, rels->file, sheetname); + err = _add_to_zip(self, rels->file, &buffer, &buffer_size, sheetname); fclose(rels->file); + free(buffer); lxw_free_relationships(rels); RETURN_ON_ERROR(err); @@ -1208,6 +1577,8 @@ STATIC lxw_error _write_drawing_rels_file(lxw_packager *self) { lxw_relationships *rels; + char *buffer = NULL; + size_t buffer_size = 0; lxw_rel_tuple *rel; lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; @@ -1227,7 +1598,7 @@ _write_drawing_rels_file(lxw_packager *self) rels = lxw_relationships_new(); - rels->file = lxw_tmpfile(self->tmpdir); + rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!rels->file) { lxw_free_relationships(rels); return LXW_ERROR_CREATING_TMPFILE; @@ -1244,9 +1615,10 @@ _write_drawing_rels_file(lxw_packager *self) lxw_relationships_assemble_xml_file(rels); - err = _add_file_to_zip(self, rels->file, sheetname); + err = _add_to_zip(self, rels->file, &buffer, &buffer_size, sheetname); fclose(rels->file); + free(buffer); lxw_free_relationships(rels); RETURN_ON_ERROR(err); @@ -1264,13 +1636,15 @@ _write_vml_drawing_rels_file(lxw_packager *self, lxw_worksheet *worksheet, uint32_t index) { lxw_relationships *rels; + char *buffer = NULL; + size_t buffer_size = 0; lxw_rel_tuple *rel; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; lxw_error err = LXW_NO_ERROR; rels = lxw_relationships_new(); - rels->file = lxw_tmpfile(self->tmpdir); + rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!rels->file) { lxw_free_relationships(rels); return LXW_ERROR_CREATING_TMPFILE; @@ -1287,9 +1661,54 @@ _write_vml_drawing_rels_file(lxw_packager *self, lxw_worksheet *worksheet, lxw_relationships_assemble_xml_file(rels); - err = _add_file_to_zip(self, rels->file, sheetname); + err = _add_to_zip(self, rels->file, &buffer, &buffer_size, sheetname); + + fclose(rels->file); + free(buffer); + lxw_free_relationships(rels); + + return err; +} + +/* + * Write the vbaProject .rels xml file. + */ +STATIC lxw_error +_write_vba_project_rels_file(lxw_packager *self) +{ + lxw_relationships *rels; + lxw_workbook *workbook = self->workbook; + lxw_error err = LXW_NO_ERROR; + char *buffer = NULL; + size_t buffer_size = 0; + + if (!workbook->vba_project_signature) + return LXW_NO_ERROR; + + rels = lxw_relationships_new(); + if (!rels) { + err = LXW_ERROR_MEMORY_MALLOC_FAILED; + goto mem_error; + } + + rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); + if (!rels->file) { + err = LXW_ERROR_CREATING_TMPFILE; + goto mem_error; + } + + lxw_add_ms_package_relationship(rels, "/vbaProjectSignature", + "vbaProjectSignature.bin"); + + lxw_relationships_assemble_xml_file(rels); + + err = _add_to_zip(self, rels->file, &buffer, &buffer_size, + "xl/_rels/vbaProject.bin.rels"); fclose(rels->file); + free(buffer); + +mem_error: lxw_free_relationships(rels); return err; @@ -1302,6 +1721,8 @@ STATIC lxw_error _write_root_rels_file(lxw_packager *self) { lxw_relationships *rels = lxw_relationships_new(); + char *buffer = NULL; + size_t buffer_size = 0; lxw_error err = LXW_NO_ERROR; if (!rels) { @@ -1309,7 +1730,7 @@ _write_root_rels_file(lxw_packager *self) goto mem_error; } - rels->file = lxw_tmpfile(self->tmpdir); + rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!rels->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; @@ -1331,9 +1752,10 @@ _write_root_rels_file(lxw_packager *self) lxw_relationships_assemble_xml_file(rels); - err = _add_file_to_zip(self, rels->file, "_rels/.rels"); + err = _add_to_zip(self, rels->file, &buffer, &buffer_size, "_rels/.rels"); fclose(rels->file); + free(buffer); mem_error: lxw_free_relationships(rels); @@ -1370,12 +1792,12 @@ _add_file_to_zip(lxw_packager *self, FILE * file, const char *filename) fflush(file); rewind(file); - size_read = fread(self->buffer, 1, self->buffer_size, file); + size_read = fread((void *) self->buffer, 1, self->buffer_size, file); while (size_read) { if (size_read < self->buffer_size) { - if (feof(file) == 0) { + if (ferror(file)) { LXW_ERROR("Error reading member file data"); RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD); } @@ -1389,7 +1811,8 @@ _add_file_to_zip(lxw_packager *self, FILE * file, const char *filename) RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD); } - size_read = fread(self->buffer, 1, self->buffer_size, file); + size_read = + fread((void *) (void *) self->buffer, 1, self->buffer_size, file); } error = zipCloseFileInZip(self->zipfile); @@ -1402,8 +1825,8 @@ _add_file_to_zip(lxw_packager *self, FILE * file, const char *filename) } STATIC lxw_error -_add_buffer_to_zip(lxw_packager *self, unsigned char *buffer, - size_t buffer_size, const char *filename) +_add_buffer_to_zip(lxw_packager *self, const char *buffer, size_t buffer_size, + const char *filename) { int16_t error = ZIP_OK; @@ -1438,8 +1861,19 @@ _add_buffer_to_zip(lxw_packager *self, unsigned char *buffer, return LXW_NO_ERROR; } +STATIC lxw_error +_add_to_zip(lxw_packager *self, FILE * file, char **buffer, + size_t *buffer_size, const char *filename) +{ + /* Flush to ensure buffer is updated when using a memory-backed file. */ + fflush(file); + return *buffer ? + _add_buffer_to_zip(self, *buffer, *buffer_size, filename) : + _add_file_to_zip(self, file, filename); +} + /* - * Write the xml files that make up the XLXS OPC package. + * Write the xml files that make up the XLSX OPC package. */ lxw_error lxw_create_package(lxw_packager *self) @@ -1477,6 +1911,9 @@ lxw_create_package(lxw_packager *self) error = _write_comment_files(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); + error = _write_table_files(self); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); + error = _write_shared_strings_file(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); @@ -1504,9 +1941,18 @@ lxw_create_package(lxw_packager *self) error = _add_vba_project(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); + error = _add_vba_project_signature(self); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); + + error = _write_vba_project_rels_file(self); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); + error = _write_core_file(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); + error = _write_metadata_file(self); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); + error = _write_app_file(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); diff --git a/src/libxlsxwriter/relationships.c b/src/libxlsxwriter/relationships.c index 452439b..0761e98 100644 --- a/src/libxlsxwriter/relationships.c +++ b/src/libxlsxwriter/relationships.c @@ -3,7 +3,7 @@ * * Used in conjunction with the libxlsxwriter library. * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ diff --git a/src/libxlsxwriter/shared_strings.c b/src/libxlsxwriter/shared_strings.c index 7a06b42..c4506ea 100644 --- a/src/libxlsxwriter/shared_strings.c +++ b/src/libxlsxwriter/shared_strings.c @@ -3,7 +3,7 @@ * * Used in conjunction with the libxlsxwriter library. * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ diff --git a/src/libxlsxwriter/styles.c b/src/libxlsxwriter/styles.c index 72b8e0e..5aadfbd 100644 --- a/src/libxlsxwriter/styles.c +++ b/src/libxlsxwriter/styles.c @@ -3,7 +3,7 @@ * * Used in conjunction with the libxlsxwriter library. * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ @@ -85,7 +85,7 @@ lxw_styles_free(lxw_styles *styles) * Write the element for rich strings. */ void -lxw_styles_write_string_fragment(lxw_styles *self, char *string) +lxw_styles_write_string_fragment(lxw_styles *self, const char *string) { struct xml_attribute_list attributes; struct xml_attribute *attribute; @@ -1045,13 +1045,16 @@ _write_alignment(lxw_styles *self, lxw_format *format) LXW_INIT_ATTRIBUTES(); - /* Indent is only allowed for horizontal left, right and distributed. */ + /* Indent is only allowed for some alignment properties. */ /* 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_DISTRIBUTED + && format->text_v_align != LXW_ALIGN_VERTICAL_TOP + && format->text_v_align != LXW_ALIGN_VERTICAL_BOTTOM + && format->text_v_align != LXW_ALIGN_VERTICAL_DISTRIBUTED) { format->text_h_align = LXW_ALIGN_LEFT; } @@ -1110,9 +1113,6 @@ _write_alignment(lxw_styles *self, lxw_format *format) if (format->text_v_align == LXW_ALIGN_VERTICAL_DISTRIBUTED) LXW_PUSH_ATTRIBUTES_STR("vertical", "distributed"); - if (format->indent) - LXW_PUSH_ATTRIBUTES_INT("indent", format->indent); - /* Map rotation to Excel values. */ if (rotation) { if (rotation == 270) @@ -1123,6 +1123,9 @@ _write_alignment(lxw_styles *self, lxw_format *format) LXW_PUSH_ATTRIBUTES_INT("textRotation", rotation); } + if (format->indent) + LXW_PUSH_ATTRIBUTES_INT("indent", format->indent); + if (format->text_wrap) LXW_PUSH_ATTRIBUTES_STR("wrapText", "1"); @@ -1182,6 +1185,9 @@ _write_xf(lxw_styles *self, lxw_format *format) LXW_PUSH_ATTRIBUTES_INT("borderId", format->border_index); LXW_PUSH_ATTRIBUTES_INT("xfId", format->xf_id); + if (format->quote_prefix) + LXW_PUSH_ATTRIBUTES_STR("quotePrefix", "1"); + if (format->num_format_index > 0) LXW_PUSH_ATTRIBUTES_STR("applyNumberFormat", "1"); @@ -1416,9 +1422,6 @@ lxw_styles_assemble_xml_file(lxw_styles *self) /* Write the tableStyles element. */ _write_table_styles(self); - /* Write the colors element. */ - /* _write_colors(self); */ - /* Close the style sheet tag. */ lxw_xml_end_tag(self->file, "styleSheet"); } diff --git a/src/libxlsxwriter/table.c b/src/libxlsxwriter/table.c new file mode 100644 index 0000000..1fa114e --- /dev/null +++ b/src/libxlsxwriter/table.c @@ -0,0 +1,304 @@ +/***************************************************************************** + * table - A library for creating Excel XLSX table files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/worksheet.h" +#include "xlsxwriter/table.h" +#include "xlsxwriter/utility.h" + +/* + * Forward declarations. + */ + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ + +/* + * Create a new table object. + */ +lxw_table * +lxw_table_new(void) +{ + lxw_table *table = calloc(1, sizeof(lxw_table)); + GOTO_LABEL_ON_MEM_ERROR(table, mem_error); + + return table; + +mem_error: + lxw_table_free(table); + return NULL; +} + +/* + * Free a table object. + */ +void +lxw_table_free(lxw_table *table) +{ + if (!table) + return; + + free(table); +} + +/***************************************************************************** + * + * XML functions. + * + ****************************************************************************/ + +/* + * Write the XML declaration. + */ +STATIC void +_table_xml_declaration(lxw_table *self) +{ + lxw_xml_declaration(self->file); +} + +/* + * Write the element. + */ +STATIC void +_table_write_table(lxw_table *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char xmlns[] = + "http://schemas.openxmlformats.org/spreadsheetml/2006/main"; + lxw_table_obj *table_obj = self->table_obj; + + LXW_INIT_ATTRIBUTES(); + + LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns); + LXW_PUSH_ATTRIBUTES_INT("id", table_obj->id); + + if (table_obj->name) + LXW_PUSH_ATTRIBUTES_STR("name", table_obj->name); + else + LXW_PUSH_ATTRIBUTES_STR("name", "Table1"); + + if (table_obj->name) + LXW_PUSH_ATTRIBUTES_STR("displayName", table_obj->name); + else + LXW_PUSH_ATTRIBUTES_STR("displayName", "Table1"); + + LXW_PUSH_ATTRIBUTES_STR("ref", table_obj->sqref); + + if (table_obj->no_header_row) + LXW_PUSH_ATTRIBUTES_STR("headerRowCount", "0"); + + if (table_obj->total_row) + LXW_PUSH_ATTRIBUTES_STR("totalsRowCount", "1"); + else + LXW_PUSH_ATTRIBUTES_STR("totalsRowShown", "0"); + + lxw_xml_start_tag(self->file, "table", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_table_write_auto_filter(lxw_table *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (self->table_obj->no_autofilter) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("ref", self->table_obj->filter_sqref); + + lxw_xml_empty_tag(self->file, "autoFilter", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_table_write_table_column(lxw_table *self, uint16_t id, + lxw_table_column *column) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + int32_t dfx_id; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("id", id); + + LXW_PUSH_ATTRIBUTES_STR("name", column->header); + + if (column->total_string) { + LXW_PUSH_ATTRIBUTES_STR("totalsRowLabel", column->total_string); + } + else if (column->total_function) { + if (column->total_function == LXW_TABLE_FUNCTION_AVERAGE) + LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "average"); + if (column->total_function == LXW_TABLE_FUNCTION_COUNT_NUMS) + LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "countNums"); + if (column->total_function == LXW_TABLE_FUNCTION_COUNT) + LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "count"); + if (column->total_function == LXW_TABLE_FUNCTION_MAX) + LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "max"); + if (column->total_function == LXW_TABLE_FUNCTION_MIN) + LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "min"); + if (column->total_function == LXW_TABLE_FUNCTION_STD_DEV) + LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "stdDev"); + if (column->total_function == LXW_TABLE_FUNCTION_SUM) + LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "sum"); + if (column->total_function == LXW_TABLE_FUNCTION_VAR) + LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "var"); + } + + if (column->format) { + dfx_id = lxw_format_get_dxf_index(column->format); + LXW_PUSH_ATTRIBUTES_INT("dataDxfId", dfx_id); + } + + if (column->formula) { + lxw_xml_start_tag(self->file, "tableColumn", &attributes); + lxw_xml_data_element(self->file, "calculatedColumnFormula", + column->formula, NULL); + lxw_xml_end_tag(self->file, "tableColumn"); + } + else { + lxw_xml_empty_tag(self->file, "tableColumn", &attributes); + } + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_table_write_table_columns(lxw_table *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + uint16_t i; + uint16_t num_cols = self->table_obj->num_cols; + lxw_table_column **columns = self->table_obj->columns; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("count", num_cols); + + lxw_xml_start_tag(self->file, "tableColumns", &attributes); + + for (i = 0; i < num_cols; i++) + _table_write_table_column(self, i + 1, columns[i]); + + lxw_xml_end_tag(self->file, "tableColumns"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_table_write_table_style_info(lxw_table *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char name[LXW_ATTR_32]; + lxw_table_obj *table_obj = self->table_obj; + + LXW_INIT_ATTRIBUTES(); + + if (table_obj->style_type == LXW_TABLE_STYLE_TYPE_LIGHT) { + if (table_obj->style_type_number != 0) { + lxw_snprintf(name, LXW_ATTR_32, "TableStyleLight%d", + table_obj->style_type_number); + LXW_PUSH_ATTRIBUTES_STR("name", name); + } + } + else if (table_obj->style_type == LXW_TABLE_STYLE_TYPE_MEDIUM) { + lxw_snprintf(name, LXW_ATTR_32, "TableStyleMedium%d", + table_obj->style_type_number); + LXW_PUSH_ATTRIBUTES_STR("name", name); + } + else if (table_obj->style_type == LXW_TABLE_STYLE_TYPE_DARK) { + lxw_snprintf(name, LXW_ATTR_32, "TableStyleDark%d", + table_obj->style_type_number); + LXW_PUSH_ATTRIBUTES_STR("name", name); + } + else { + LXW_PUSH_ATTRIBUTES_STR("name", "TableStyleMedium9"); + } + + if (table_obj->first_column) + LXW_PUSH_ATTRIBUTES_STR("showFirstColumn", "1"); + else + LXW_PUSH_ATTRIBUTES_STR("showFirstColumn", "0"); + + if (table_obj->last_column) + LXW_PUSH_ATTRIBUTES_STR("showLastColumn", "1"); + else + LXW_PUSH_ATTRIBUTES_STR("showLastColumn", "0"); + + if (table_obj->no_banded_rows) + LXW_PUSH_ATTRIBUTES_STR("showRowStripes", "0"); + else + LXW_PUSH_ATTRIBUTES_STR("showRowStripes", "1"); + + if (table_obj->banded_columns) + LXW_PUSH_ATTRIBUTES_STR("showColumnStripes", "1"); + else + LXW_PUSH_ATTRIBUTES_STR("showColumnStripes", "0"); + + lxw_xml_empty_tag(self->file, "tableStyleInfo", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/***************************************************************************** + * + * XML file assembly functions. + * + ****************************************************************************/ + +/* + * Assemble and write the XML file. + */ +void +lxw_table_assemble_xml_file(lxw_table *self) +{ + /* Write the XML declaration. */ + _table_xml_declaration(self); + + /* Write the table element. */ + _table_write_table(self); + + /* Write the autoFilter element. */ + _table_write_auto_filter(self); + + /* Write the tableColumns element. */ + _table_write_table_columns(self); + + /* Write the tableStyleInfo element. */ + _table_write_table_style_info(self); + + lxw_xml_end_tag(self->file, "table"); +} + +/***************************************************************************** + * + * Public functions. + * + ****************************************************************************/ diff --git a/src/libxlsxwriter/theme.c b/src/libxlsxwriter/theme.c index 8c85ea2..a246e61 100644 --- a/src/libxlsxwriter/theme.c +++ b/src/libxlsxwriter/theme.c @@ -3,7 +3,7 @@ * * Used in conjunction with the libxlsxwriter library. * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ diff --git a/src/libxlsxwriter/utility.c b/src/libxlsxwriter/utility.c index 6f73c89..a16f2aa 100644 --- a/src/libxlsxwriter/utility.c +++ b/src/libxlsxwriter/utility.c @@ -3,10 +3,14 @@ * * Used in conjunction with the libxlsxwriter library. * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ +#ifdef USE_FMEMOPEN +#define _POSIX_C_SOURCE 200809L +#endif + #include #include #include @@ -16,6 +20,10 @@ #include "xlsxwriter/common.h" #include "xlsxwriter/third_party/tmpfileplus.h" +#ifdef USE_DTOA_LIBRARY +#include "xlsxwriter/third_party/emyg_dtoa.h" +#endif + char *error_strings[LXW_MAX_ERRNO + 1] = { "No error.", "Memory error, failed to malloc() required memory.", @@ -395,7 +403,7 @@ lxw_datetime_to_excel_date_epoch(lxw_datetime *datetime, uint8_t date_1904) /* Add days for all previous years. */ days += range * 365; /* Add 4 year leapdays. */ - days += (range) / 4; + days += range / 4; /* Remove 100 year leapdays. */ days -= (range + offset) / 100; /* Add 400 year leapdays. */ @@ -419,6 +427,34 @@ lxw_datetime_to_excel_datetime(lxw_datetime *datetime) return lxw_datetime_to_excel_date_epoch(datetime, LXW_FALSE); } +/* + * Convert a unix datetime (1970/01/01 epoch) to an Excel serial date, with a + * 1900 epoch. + */ +double +lxw_unixtime_to_excel_date(int64_t unixtime) +{ + return lxw_unixtime_to_excel_date_epoch(unixtime, LXW_FALSE); +} + +/* + * Convert a unix datetime (1970/01/01 epoch) to an Excel serial date, with a + * 1900 or 1904 epoch. + */ +double +lxw_unixtime_to_excel_date_epoch(int64_t unixtime, uint8_t date_1904) +{ + double excel_datetime = 0.0; + double epoch = date_1904 ? 24107.0 : 25568.0; + + excel_datetime = epoch + (unixtime / (24 * 60 * 60.0)); + + if (!date_1904 && excel_datetime >= 60.0) + excel_datetime = excel_datetime + 1.0; + + return excel_datetime; +} + /* Simple strdup() implementation since it isn't ANSI C. */ char * lxw_strdup(const char *str) @@ -536,7 +572,7 @@ lxw_quote_sheetname(const char *str) * version if required for safety or portability. */ FILE * -lxw_tmpfile(char *tmpdir) +lxw_tmpfile(const char *tmpdir) { #ifndef USE_STANDARD_TMPFILE return tmpfileplus(tmpdir, NULL, NULL, 0); @@ -546,28 +582,34 @@ lxw_tmpfile(char *tmpdir) #endif } +/** + * Return a memory-backed file if supported, otherwise a temporary one + */ +FILE * +lxw_get_filehandle(char **buf, size_t *size, const char *tmpdir) +{ + static size_t s; + if (!size) + size = &s; + *buf = NULL; + *size = 0; +#ifdef USE_FMEMOPEN + (void) tmpdir; + return open_memstream(buf, size); +#else + return lxw_tmpfile(tmpdir); +#endif +} + /* - * Sample function to handle sprintf of doubles for locale portable code. This - * is usually handled by a lxw_sprintf_dbl() macro but it can be replaced with - * a function of the same name. - * - * The code below is a simplified example that changes numbers like 123,45 to - * 123.45. End-users can replace this with something more rigorous if - * required. + * Use third party function to handle sprintf of doubles for locale portable + * code. */ -#ifdef USE_DOUBLE_FUNCTION +#ifdef USE_DTOA_LIBRARY int lxw_sprintf_dbl(char *data, double number) { - char *tmp; - - lxw_snprintf(data, LXW_ATTR_32, "%.16g", number); - - /* Replace comma with decimal point. */ - tmp = strchr(data, ','); - if (tmp) - *tmp = '.'; - + emyg_dtoa(number, data); return 0; } #endif @@ -591,32 +633,27 @@ lxw_version_id(void) } /* - * Hash a worksheet password. Based on the algorithm provided by Daniel Rentz - * of OpenOffice. + * Hash a worksheet password. Based on the algorithm in ECMA-376-4:2016, + * Office Open XML File Formats - Transitional Migration Features, + * Additional attributes for workbookProtection element (Part 1, §18.2.29). */ uint16_t lxw_hash_password(const char *password) { - size_t count; - size_t i; - uint16_t hash = 0x0000; - - count = strlen(password); - - for (i = 0; i < (uint8_t) count; i++) { - uint32_t low_15; - uint32_t high_15; - uint32_t letter = password[i] << (i + 1); + uint16_t byte_count = (uint16_t) strlen(password); + uint16_t hash = 0; + const char *p = &password[byte_count]; - low_15 = letter & 0x7fff; - high_15 = letter & (0x7fff << 15); - high_15 = high_15 >> 15; - letter = low_15 | high_15; + if (!byte_count) + return hash; - hash ^= letter; + while (p-- != password) { + hash = ((hash >> 14) & 0x01) | ((hash << 1) & 0x7fff); + hash ^= *p & 0xFF; } - hash ^= count; + hash = ((hash >> 14) & 0x01) | ((hash << 1) & 0x7fff); + hash ^= byte_count; hash ^= 0xCE4B; return hash; diff --git a/src/libxlsxwriter/vml.c b/src/libxlsxwriter/vml.c index 8ab696c..b73b78b 100644 --- a/src/libxlsxwriter/vml.c +++ b/src/libxlsxwriter/vml.c @@ -3,7 +3,7 @@ * * Used in conjunction with the libxlsxwriter library. * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ @@ -127,10 +127,9 @@ _vml_write_text_valign(lxw_vml *self) * Write the element. */ STATIC void -_vml_write_fmla_macro(lxw_vml *self) +_vml_write_fmla_macro(lxw_vml *self, lxw_vml_obj *vml_obj) { - lxw_xml_data_element(self->file, "x:FmlaMacro", "[0]!Button1_Click", - NULL); + lxw_xml_data_element(self->file, "x:FmlaMacro", vml_obj->macro, NULL); } /* @@ -182,11 +181,11 @@ _vml_write_rotation_lock(lxw_vml *self) * Write the element. */ STATIC void -_vml_write_column(lxw_vml *self, lxw_vml_obj *comment_obj) +_vml_write_column(lxw_vml *self, lxw_vml_obj *vml_obj) { char data[LXW_ATTR_32]; - lxw_snprintf(data, LXW_ATTR_32, "%d", comment_obj->col); + lxw_snprintf(data, LXW_ATTR_32, "%d", vml_obj->col); lxw_xml_data_element(self->file, "x:Column", data, NULL); } @@ -195,11 +194,11 @@ _vml_write_column(lxw_vml *self, lxw_vml_obj *comment_obj) * Write the element. */ STATIC void -_vml_write_row(lxw_vml *self, lxw_vml_obj *comment_obj) +_vml_write_row(lxw_vml *self, lxw_vml_obj *vml_obj) { char data[LXW_ATTR_32]; - lxw_snprintf(data, LXW_ATTR_32, "%d", comment_obj->row); + lxw_snprintf(data, LXW_ATTR_32, "%d", vml_obj->row); lxw_xml_data_element(self->file, "x:Row", data, NULL); } @@ -217,20 +216,20 @@ _vml_write_auto_fill(lxw_vml *self) * Write the element. */ STATIC void -_vml_write_anchor(lxw_vml *self, lxw_vml_obj *comment_obj) +_vml_write_anchor(lxw_vml *self, lxw_vml_obj *vml_obj) { char anchor_data[LXW_MAX_ATTRIBUTE_LENGTH]; lxw_snprintf(anchor_data, LXW_MAX_ATTRIBUTE_LENGTH, "%d, %d, %d, %d, %d, %d, %d, %d", - comment_obj->from.col, - (uint32_t) comment_obj->from.col_offset, - comment_obj->from.row, - (uint32_t) comment_obj->from.row_offset, - comment_obj->to.col, - (uint32_t) comment_obj->to.col_offset, - comment_obj->to.row, (uint32_t) comment_obj->to.row_offset); + vml_obj->from.col, + (uint32_t) vml_obj->from.col_offset, + vml_obj->from.row, + (uint32_t) vml_obj->from.row_offset, + vml_obj->to.col, + (uint32_t) vml_obj->to.col_offset, + vml_obj->to.row, (uint32_t) vml_obj->to.row_offset); lxw_xml_data_element(self->file, "x:Anchor", anchor_data, NULL); } @@ -311,7 +310,7 @@ _vml_write_shapetype_lock(lxw_vml *self) * Write the element. */ STATIC void -_vml_write_font(lxw_vml *self) +_vml_write_font(lxw_vml *self, lxw_vml_obj *vml_obj) { struct xml_attribute_list attributes; struct xml_attribute *attribute; @@ -321,7 +320,7 @@ _vml_write_font(lxw_vml *self) LXW_PUSH_ATTRIBUTES_STR("size", "220"); LXW_PUSH_ATTRIBUTES_STR("color", "#000000"); - lxw_xml_data_element(self->file, "font", "Button 1", &attributes); + lxw_xml_data_element(self->file, "font", vml_obj->name, &attributes); LXW_FREE_ATTRIBUTES(); } @@ -471,7 +470,7 @@ _vml_write_image_shapetype(lxw_vml *self) * Write the element. */ STATIC void -_vml_write_button_client_data(lxw_vml *self) +_vml_write_button_client_data(lxw_vml *self, lxw_vml_obj *vml_obj) { struct xml_attribute_list attributes; struct xml_attribute *attribute; @@ -482,7 +481,7 @@ _vml_write_button_client_data(lxw_vml *self) lxw_xml_start_tag(self->file, "x:ClientData", &attributes); /* Write the element. */ - _vml_write_anchor(self, NULL); + _vml_write_anchor(self, vml_obj); /* Write the x:PrintObject element. */ _vml_write_print_object(self); @@ -491,7 +490,7 @@ _vml_write_button_client_data(lxw_vml *self) _vml_write_auto_fill(self); /* Write the x:FmlaMacro element. */ - _vml_write_fmla_macro(self); + _vml_write_fmla_macro(self, vml_obj); /* Write the x:TextHAlign element. */ _vml_write_text_halign(self); @@ -508,7 +507,7 @@ _vml_write_button_client_data(lxw_vml *self) * Write the
element. */ STATIC void -_vml_write_button_div(lxw_vml *self) +_vml_write_button_div(lxw_vml *self, lxw_vml_obj *vml_obj) { struct xml_attribute_list attributes; struct xml_attribute *attribute; @@ -519,7 +518,7 @@ _vml_write_button_div(lxw_vml *self) lxw_xml_start_tag(self->file, "div", &attributes); /* Write the font element. */ - _vml_write_font(self); + _vml_write_font(self, vml_obj); lxw_xml_end_tag(self->file, "div"); @@ -530,7 +529,7 @@ _vml_write_button_div(lxw_vml *self) * Write the element. */ STATIC void -_vml_write_button_textbox(lxw_vml *self) +_vml_write_button_textbox(lxw_vml *self, lxw_vml_obj *vml_obj) { struct xml_attribute_list attributes; struct xml_attribute *attribute; @@ -542,7 +541,7 @@ _vml_write_button_textbox(lxw_vml *self) lxw_xml_start_tag(self->file, "v:textbox", &attributes); /* Write the div element. */ - _vml_write_button_div(self); + _vml_write_button_div(self, vml_obj); lxw_xml_end_tag(self->file, "v:textbox"); @@ -592,22 +591,49 @@ _vml_write_button_path(lxw_vml *self) * Write the element for buttons. */ STATIC void -_vml_write_button_shape(lxw_vml *self) +_vml_write_button_shape(lxw_vml *self, uint32_t vml_shape_id, + uint32_t z_index, lxw_vml_obj *vml_obj) { struct xml_attribute_list attributes; struct xml_attribute *attribute; - char id[] = "_x0000_s1025"; char type[] = "#_x0000_t201"; - char style[] = "position:absolute;margin-left:96pt;margin-top:15pt;" - "width:48pt;height:15pt;z-index:1;mso-wrap-style:tight"; char o_button[] = "t"; char fillcolor[] = "buttonFace [67]"; char strokecolor[] = "windowText [64]"; char o_insetmode[] = "auto"; + char id[LXW_ATTR_32]; + char margin_left[LXW_ATTR_32]; + char margin_top[LXW_ATTR_32]; + char width[LXW_ATTR_32]; + char height[LXW_ATTR_32]; + char style[LXW_MAX_ATTRIBUTE_LENGTH]; + + lxw_sprintf_dbl(margin_left, vml_obj->col_absolute * 0.75); + lxw_sprintf_dbl(margin_top, vml_obj->row_absolute * 0.75); + lxw_sprintf_dbl(width, vml_obj->width * 0.75); + lxw_sprintf_dbl(height, vml_obj->height * 0.75); + + lxw_snprintf(id, LXW_ATTR_32, "_x0000_s%d", vml_shape_id); + + lxw_snprintf(style, + LXW_MAX_ATTRIBUTE_LENGTH, + "position:absolute;" + "margin-left:%spt;" + "margin-top:%spt;" + "width:%spt;" + "height:%spt;" + "z-index:%d;" + "mso-wrap-style:tight", + margin_left, margin_top, width, height, z_index); + LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("id", id); LXW_PUSH_ATTRIBUTES_STR("type", type); + + if (vml_obj->text) + LXW_PUSH_ATTRIBUTES_STR("alt", vml_obj->text); + LXW_PUSH_ATTRIBUTES_STR("style", style); LXW_PUSH_ATTRIBUTES_STR("o:button", o_button); LXW_PUSH_ATTRIBUTES_STR("fillcolor", fillcolor); @@ -623,10 +649,10 @@ _vml_write_button_shape(lxw_vml *self) _vml_write_rotation_lock(self); /* Write the v:textbox element. */ - _vml_write_button_textbox(self); + _vml_write_button_textbox(self, vml_obj); /* Write the x:ClientData element. */ - _vml_write_button_client_data(self); + _vml_write_button_client_data(self, vml_obj); lxw_xml_end_tag(self->file, "v:shape"); @@ -672,7 +698,7 @@ _vml_write_button_shapetype(lxw_vml *self) * Write the element. */ STATIC void -_vml_write_comment_client_data(lxw_vml *self, lxw_vml_obj *comment_obj) +_vml_write_comment_client_data(lxw_vml *self, lxw_vml_obj *vml_obj) { struct xml_attribute_list attributes; struct xml_attribute *attribute; @@ -689,19 +715,19 @@ _vml_write_comment_client_data(lxw_vml *self, lxw_vml_obj *comment_obj) _vml_write_size_with_cells(self); /* Write the element. */ - _vml_write_anchor(self, comment_obj); + _vml_write_anchor(self, vml_obj); /* Write the element. */ _vml_write_auto_fill(self); /* Write the element. */ - _vml_write_row(self, comment_obj); + _vml_write_row(self, vml_obj); /* Write the element. */ - _vml_write_column(self, comment_obj); + _vml_write_column(self, vml_obj); /* Write the x:Visible element. */ - if (comment_obj->visible == LXW_COMMENT_DISPLAY_VISIBLE) + if (vml_obj->visible == LXW_COMMENT_DISPLAY_VISIBLE) _vml_write_visible(self); lxw_xml_end_tag(self->file, "x:ClientData"); @@ -793,7 +819,7 @@ _vml_write_comment_path(lxw_vml *self, uint8_t has_gradient, char *type) */ STATIC void _vml_write_comment_shape(lxw_vml *self, uint32_t vml_shape_id, - uint32_t z_index, lxw_vml_obj *comment_obj) + uint32_t z_index, lxw_vml_obj *vml_obj) { struct xml_attribute_list attributes; struct xml_attribute *attribute; @@ -808,24 +834,24 @@ _vml_write_comment_shape(lxw_vml *self, uint32_t vml_shape_id, char type[] = "#_x0000_t202"; char o_insetmode[] = "auto"; - lxw_sprintf_dbl(margin_left, comment_obj->col_absolute * 0.75); - lxw_sprintf_dbl(margin_top, comment_obj->row_absolute * 0.75); - lxw_sprintf_dbl(width, comment_obj->width * 0.75); - lxw_sprintf_dbl(height, comment_obj->height * 0.75); + lxw_sprintf_dbl(margin_left, vml_obj->col_absolute * 0.75); + lxw_sprintf_dbl(margin_top, vml_obj->row_absolute * 0.75); + lxw_sprintf_dbl(width, vml_obj->width * 0.75); + lxw_sprintf_dbl(height, vml_obj->height * 0.75); lxw_snprintf(id, LXW_ATTR_32, "_x0000_s%d", vml_shape_id); - if (comment_obj->visible == LXW_COMMENT_DISPLAY_DEFAULT) - comment_obj->visible = self->comment_display_default; + if (vml_obj->visible == LXW_COMMENT_DISPLAY_DEFAULT) + vml_obj->visible = self->comment_display_default; - if (comment_obj->visible == LXW_COMMENT_DISPLAY_VISIBLE) + if (vml_obj->visible == LXW_COMMENT_DISPLAY_VISIBLE) lxw_snprintf(visible, LXW_ATTR_32, "visible"); else lxw_snprintf(visible, LXW_ATTR_32, "hidden"); - if (comment_obj->color) + if (vml_obj->color) lxw_snprintf(fillcolor, LXW_ATTR_32, "#%06x", - comment_obj->color & LXW_COLOR_MASK); + vml_obj->color & LXW_COLOR_MASK); else lxw_snprintf(fillcolor, LXW_ATTR_32, "#%06x", 0xffffe1); @@ -862,7 +888,7 @@ _vml_write_comment_shape(lxw_vml *self, uint32_t vml_shape_id, _vml_write_comment_textbox(self); /* Write the x:ClientData element. */ - _vml_write_comment_client_data(self, comment_obj); + _vml_write_comment_client_data(self, vml_obj); lxw_xml_end_tag(self->file, "v:shape"); @@ -981,33 +1007,37 @@ lxw_vml_assemble_xml_file(lxw_vml *self) /* Write the o:shapelayout element. */ _vml_write_shapelayout(self); - if (self->comment_objs) { + if (self->button_objs && !STAILQ_EMPTY(self->button_objs)) { /* Write the element. */ - _vml_write_comment_shapetype(self); + _vml_write_button_shapetype(self); - STAILQ_FOREACH(comment_obj, self->comment_objs, list_pointers) { + STAILQ_FOREACH(button_obj, self->button_objs, list_pointers) { self->vml_shape_id++; /* Write the element. */ - _vml_write_comment_shape(self, self->vml_shape_id, z_index, - comment_obj); + _vml_write_button_shape(self, self->vml_shape_id, z_index, + button_obj); z_index++; } } - if (self->button_objs) { + if (self->comment_objs && !STAILQ_EMPTY(self->comment_objs)) { /* Write the element. */ - _vml_write_button_shapetype(self); + _vml_write_comment_shapetype(self); + + STAILQ_FOREACH(comment_obj, self->comment_objs, list_pointers) { + self->vml_shape_id++; - STAILQ_FOREACH(button_obj, self->button_objs, list_pointers) { /* Write the element. */ - _vml_write_button_shape(self); + _vml_write_comment_shape(self, self->vml_shape_id, z_index, + comment_obj); + z_index++; } } - if (self->image_objs) { + if (self->image_objs && !STAILQ_EMPTY(self->image_objs)) { /* Write the element. */ _vml_write_image_shapetype(self); diff --git a/src/libxlsxwriter/workbook.c b/src/libxlsxwriter/workbook.c index 858e9a2..ddc7f70 100644 --- a/src/libxlsxwriter/workbook.c +++ b/src/libxlsxwriter/workbook.c @@ -3,7 +3,7 @@ * * Used in conjunction with the libxlsxwriter library. * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ @@ -66,16 +66,16 @@ STATIC void _free_doc_properties(lxw_doc_properties *properties) { if (properties) { - free(properties->title); - free(properties->subject); - free(properties->author); - free(properties->manager); - free(properties->company); - free(properties->category); - free(properties->keywords); - free(properties->comments); - free(properties->status); - free(properties->hyperlink_base); + free((void *) properties->title); + free((void *) properties->subject); + free((void *) properties->author); + free((void *) properties->manager); + free((void *) properties->company); + free((void *) properties->category); + free((void *) properties->keywords); + free((void *) properties->comments); + free((void *) properties->status); + free((void *) properties->hyperlink_base); } free(properties); @@ -244,12 +244,27 @@ lxw_workbook_free(lxw_workbook *workbook) free(workbook->header_image_md5s); } + if (workbook->background_md5s) { + for (image_md5 = RB_MIN(lxw_image_md5s, workbook->background_md5s); + image_md5; image_md5 = next_image_md5) { + + next_image_md5 = + RB_NEXT(lxw_image_md5s, workbook->image_md5, image_md5); + RB_REMOVE(lxw_image_md5s, workbook->background_md5s, image_md5); + free(image_md5->md5); + free(image_md5); + } + + free(workbook->background_md5s); + } + lxw_hash_free(workbook->used_xf_formats); lxw_hash_free(workbook->used_dxf_formats); lxw_sst_free(workbook->sst); - free(workbook->options.tmpdir); + free((void *) workbook->options.tmpdir); free(workbook->ordered_charts); free(workbook->vba_project); + free(workbook->vba_project_signature); free(workbook->vba_codename); free(workbook); } @@ -470,7 +485,6 @@ _prepare_fills(lxw_workbook *self) if (format->pattern <= LXW_PATTERN_SOLID && format->bg_color == LXW_COLOR_UNSET && format->fg_color != LXW_COLOR_UNSET) { - format->bg_color = LXW_COLOR_UNSET; format->pattern = LXW_PATTERN_SOLID; } @@ -910,7 +924,7 @@ _populate_range_dimensions(lxw_workbook *self, lxw_series_range *range) /* Create a copy of the formula to modify and parse into parts. */ lxw_snprintf(formula, LXW_MAX_FORMULA_RANGE_LENGTH, "%s", range->formula); - /* Check for valid formula. TODO. This needs stronger validation. */ + /* Check for valid formula. Note, This needs stronger validation. */ tmp_str = strchr(formula, '!'); if (tmp_str == NULL) { @@ -1001,6 +1015,25 @@ _add_chart_cache_data(lxw_workbook *self) } } +/* + * Store the image types used in the workbook to update the content types. + */ +STATIC void +_store_image_type(lxw_workbook *self, uint8_t image_type) +{ + if (image_type == LXW_IMAGE_PNG) + self->has_png = LXW_TRUE; + + if (image_type == LXW_IMAGE_JPEG) + self->has_jpeg = LXW_TRUE; + + if (image_type == LXW_IMAGE_BMP) + self->has_bmp = LXW_TRUE; + + if (image_type == LXW_IMAGE_GIF) + self->has_gif = LXW_TRUE; +} + /* * Iterate through the worksheets and set up any chart or image drawings. */ @@ -1032,23 +1065,58 @@ _prepare_drawings(lxw_workbook *self) if (STAILQ_EMPTY(worksheet->image_props) && STAILQ_EMPTY(worksheet->chart_data) - && !worksheet->has_header_vml) { + && !worksheet->has_header_vml && !worksheet->has_background_image) { continue; } drawing_id++; + /* Prepare background images. */ + if (worksheet->has_background_image) { + + object_props = worksheet->background_image; + + _store_image_type(self, object_props->image_type); + + /* Check for duplicate images and only store the first instance. */ + if (object_props->md5) { + tmp_image_md5.md5 = object_props->md5; + found_duplicate_image = RB_FIND(lxw_image_md5s, + self->background_md5s, + &tmp_image_md5); + } + + if (found_duplicate_image) { + ref_id = found_duplicate_image->id; + object_props->is_duplicate = LXW_TRUE; + } + else { + image_ref_id++; + ref_id = image_ref_id; + +#ifndef USE_NO_MD5 + new_image_md5 = calloc(1, sizeof(lxw_image_md5)); +#endif + if (new_image_md5 && object_props->md5) { + new_image_md5->id = ref_id; + new_image_md5->md5 = lxw_strdup(object_props->md5); + + RB_INSERT(lxw_image_md5s, self->background_md5s, + new_image_md5); + } + } + + lxw_worksheet_prepare_background(worksheet, ref_id, object_props); + } + /* Prepare worksheet images. */ STAILQ_FOREACH(object_props, worksheet->image_props, list_pointers) { - if (object_props->image_type == LXW_IMAGE_PNG) - self->has_png = LXW_TRUE; - - if (object_props->image_type == LXW_IMAGE_JPEG) - self->has_jpeg = LXW_TRUE; + /* Ignore background image added above. */ + if (object_props->is_background) + continue; - if (object_props->image_type == LXW_IMAGE_BMP) - self->has_bmp = LXW_TRUE; + _store_image_type(self, object_props->image_type); /* Check for duplicate images and only store the first instance. */ if (object_props->md5) { @@ -1099,14 +1167,7 @@ _prepare_drawings(lxw_workbook *self) if (!object_props) continue; - if (object_props->image_type == LXW_IMAGE_PNG) - self->has_png = LXW_TRUE; - - if (object_props->image_type == LXW_IMAGE_JPEG) - self->has_jpeg = LXW_TRUE; - - if (object_props->image_type == LXW_IMAGE_BMP) - self->has_bmp = LXW_TRUE; + _store_image_type(self, object_props->image_type); /* Check for duplicate images and only store the first instance. */ if (object_props->md5) { @@ -1148,7 +1209,6 @@ _prepare_drawings(lxw_workbook *self) /* * Iterate through the worksheets and set up the VML objects. */ - STATIC void _prepare_vml(lxw_workbook *self) { @@ -1349,6 +1409,34 @@ _prepare_defined_names(lxw_workbook *self) } } +/* + * Iterate through the worksheets and set up the table objects. + */ +STATIC void +_prepare_tables(lxw_workbook *self) +{ + lxw_worksheet *worksheet; + lxw_sheet *sheet; + uint32_t table_id = 0; + uint32_t table_count = 0; + + STAILQ_FOREACH(sheet, self->sheets, list_pointers) { + if (sheet->is_chartsheet) + continue; + else + worksheet = sheet->u.worksheet; + + table_count = worksheet->table_count; + + if (table_count == 0) + continue; + + lxw_worksheet_prepare_tables(worksheet, table_id + 1); + + table_id += table_count; + } +} + /***************************************************************************** * * XML functions. @@ -1724,11 +1812,16 @@ workbook_new_opt(const char *filename, lxw_workbook_options *options) GOTO_LABEL_ON_MEM_ERROR(workbook->image_md5s, mem_error); RB_INIT(workbook->image_md5s); - /* Add the image MD5 tree. */ + /* Add the header image MD5 tree. */ workbook->header_image_md5s = calloc(1, sizeof(struct lxw_image_md5s)); GOTO_LABEL_ON_MEM_ERROR(workbook->header_image_md5s, mem_error); RB_INIT(workbook->header_image_md5s); + /* Add the background image MD5 tree. */ + workbook->background_md5s = calloc(1, sizeof(struct lxw_image_md5s)); + GOTO_LABEL_ON_MEM_ERROR(workbook->background_md5s, mem_error); + RB_INIT(workbook->background_md5s); + /* Add the charts list. */ workbook->charts = calloc(1, sizeof(struct lxw_charts)); GOTO_LABEL_ON_MEM_ERROR(workbook->charts, mem_error); @@ -1788,6 +1881,8 @@ workbook_new_opt(const char *filename, lxw_workbook_options *options) workbook->options.constant_memory = options->constant_memory; workbook->options.tmpdir = lxw_strdup(options->tmpdir); workbook->options.use_zip64 = options->use_zip64; + workbook->options.output_buffer = options->output_buffer; + workbook->options.output_buffer_size = options->output_buffer_size; } workbook->max_url_length = 2079; @@ -1816,7 +1911,7 @@ workbook_add_worksheet(lxw_workbook *self, const char *sheetname) if (sheetname) { /* Use the user supplied name. */ init_data.name = lxw_strdup(sheetname); - init_data.quoted_name = lxw_quote_sheetname((char *) sheetname); + init_data.quoted_name = lxw_quote_sheetname(sheetname); } else { /* Use the default SheetN name. */ @@ -1877,8 +1972,8 @@ workbook_add_worksheet(lxw_workbook *self, const char *sheetname) return worksheet; mem_error: - free(init_data.name); - free(init_data.quoted_name); + free((void *) init_data.name); + free((void *) init_data.quoted_name); free(worksheet_name); free(worksheet); return NULL; @@ -1900,7 +1995,7 @@ workbook_add_chartsheet(lxw_workbook *self, const char *sheetname) if (sheetname) { /* Use the user supplied name. */ init_data.name = lxw_strdup(sheetname); - init_data.quoted_name = lxw_quote_sheetname((char *) sheetname); + init_data.quoted_name = lxw_quote_sheetname(sheetname); } else { /* Use the default SheetN name. */ @@ -1961,8 +2056,8 @@ workbook_add_chartsheet(lxw_workbook *self, const char *sheetname) return chartsheet; mem_error: - free(init_data.name); - free(init_data.quoted_name); + free((void *) init_data.name); + free((void *) init_data.quoted_name); free(chartsheet_name); free(chartsheet); return NULL; @@ -2025,12 +2120,12 @@ workbook_close(lxw_workbook *self) sheet = STAILQ_FIRST(self->sheets); if (!sheet->is_chartsheet) { worksheet = sheet->u.worksheet; - worksheet->selected = 1; + worksheet->selected = LXW_TRUE; worksheet->hidden = 0; } } - /* Set the active sheet. */ + /* Set the active sheet and check if a metadata file is needed. */ STAILQ_FOREACH(sheet, self->sheets, list_pointers) { if (sheet->is_chartsheet) continue; @@ -2038,7 +2133,10 @@ workbook_close(lxw_workbook *self) worksheet = sheet->u.worksheet; if (worksheet->index == self->active_sheet) - worksheet->active = 1; + worksheet->active = LXW_TRUE; + + if (worksheet->has_dynamic_arrays) + self->has_metadata = LXW_TRUE; } /* Set workbook and worksheet VBA codenames if a macro has been added. */ @@ -2073,6 +2171,9 @@ workbook_close(lxw_workbook *self) /* Add cached data to charts. */ _add_chart_cache_data(self); + /* Set the table ids for the worksheet tables. */ + _prepare_tables(self); + /* Create a packager object to assemble sub-elements into a zip file. */ packager = lxw_packager_new(self->filename, self->options.tmpdir, @@ -2081,8 +2182,8 @@ workbook_close(lxw_workbook *self) /* If the packager fails it is generally due to a zip permission error. */ if (packager == NULL) { LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): " - "Error creating '%s'. " - "System error = %s\n", self->filename, strerror(errno)); + "Error creating '%s'. " + "System error = %s\n", self->filename, strerror(errno)); error = LXW_ERROR_CREATING_XLSX_FILE; goto mem_error; @@ -2094,51 +2195,57 @@ workbook_close(lxw_workbook *self) /* Assemble all the sub-files in the xlsx package. */ error = lxw_create_package(packager); + if (!self->filename) { + *self->options.output_buffer = packager->output_buffer; + *self->options.output_buffer_size = packager->output_buffer_size; + } + /* Error and non-error conditions fall through to the cleanup code. */ if (error == LXW_ERROR_CREATING_TMPFILE) { LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): " - "Error creating tmpfile(s) to assemble '%s'. " - "System error = %s\n", self->filename, strerror(errno)); + "Error creating tmpfile(s) to assemble '%s'. " + "System error = %s\n", self->filename, strerror(errno)); } /* If LXW_ERROR_ZIP_FILE_OPERATION then errno is set by zip. */ if (error == LXW_ERROR_ZIP_FILE_OPERATION) { LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): " - "Zip ZIP_ERRNO error while creating xlsx file '%s'. " - "System error = %s\n", self->filename, strerror(errno)); + "Zip ZIP_ERRNO error while creating xlsx file '%s'. " + "System error = %s\n", self->filename, strerror(errno)); } /* If LXW_ERROR_ZIP_PARAMETER_ERROR then errno is set by zip. */ if (error == LXW_ERROR_ZIP_PARAMETER_ERROR) { LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): " - "Zip ZIP_PARAMERROR error while creating xlsx file '%s'. " - "System error = %s\n", self->filename, strerror(errno)); + "Zip ZIP_PARAMERROR error while creating xlsx file '%s'. " + "System error = %s\n", self->filename, strerror(errno)); } /* If LXW_ERROR_ZIP_BAD_ZIP_FILE then errno is set by zip. */ if (error == LXW_ERROR_ZIP_BAD_ZIP_FILE) { LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): " - "Zip ZIP_BADZIPFILE error while creating xlsx file '%s'. " - "This may require the use_zip64 option for large files. " - "System error = %s\n", self->filename, strerror(errno)); + "Zip ZIP_BADZIPFILE error while creating xlsx file '%s'. " + "This may require the use_zip64 option for large files. " + "System error = %s\n", self->filename, strerror(errno)); } /* If LXW_ERROR_ZIP_INTERNAL_ERROR then errno is set by zip. */ if (error == LXW_ERROR_ZIP_INTERNAL_ERROR) { LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): " - "Zip ZIP_INTERNALERROR error while creating xlsx file '%s'. " - "System error = %s\n", self->filename, strerror(errno)); + "Zip ZIP_INTERNALERROR error while creating xlsx file '%s'. " + "System error = %s\n", self->filename, strerror(errno)); } /* The next 2 error conditions don't set errno. */ if (error == LXW_ERROR_ZIP_FILE_ADD) { LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): " - "Zip error adding file to xlsx file '%s'.\n", self->filename); + "Zip error adding file to xlsx file '%s'.\n", + self->filename); } if (error == LXW_ERROR_ZIP_CLOSE) { LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): " - "Zip error closing xlsx file '%s'.\n", self->filename); + "Zip error closing xlsx file '%s'.\n", self->filename); } mem_error: @@ -2532,7 +2639,7 @@ workbook_add_vba_project(lxw_workbook *self, const char *filename) if (!filename) { LXW_WARN("workbook_add_vba_project(): " - "filename must be specified."); + "project filename must be specified."); return LXW_ERROR_NULL_PARAMETER_IGNORED; } @@ -2540,7 +2647,7 @@ workbook_add_vba_project(lxw_workbook *self, const char *filename) filehandle = lxw_fopen(filename, "rb"); if (!filehandle) { LXW_WARN_FORMAT1("workbook_add_vba_project(): " - "file doesn't exist or can't be opened: %s.", + "project file doesn't exist or can't be opened: %s.", filename); return LXW_ERROR_PARAMETER_VALIDATION; } @@ -2551,6 +2658,41 @@ workbook_add_vba_project(lxw_workbook *self, const char *filename) return LXW_NO_ERROR; } +/* + * Add a vbaProject binary and a vbaProjectSignature binary to the Excel workbook. + */ +lxw_error +workbook_add_signed_vba_project(lxw_workbook *self, + const char *vba_project, + const char *signature) +{ + FILE *filehandle; + + lxw_error error = workbook_add_vba_project(self, vba_project); + if (error != LXW_NO_ERROR) + return error; + + if (!signature) { + LXW_WARN("workbook_add_signed_vba_project(): " + "signature filename must be specified."); + return LXW_ERROR_NULL_PARAMETER_IGNORED; + } + + /* Check that the vbaProjectSignature file exists and can be opened. */ + filehandle = lxw_fopen(signature, "rb"); + if (!filehandle) { + LXW_WARN_FORMAT1("workbook_add_signed_vba_project(): " + "signature file doesn't exist or can't be opened: %s.", + signature); + return LXW_ERROR_PARAMETER_VALIDATION; + } + fclose(filehandle); + + self->vba_project_signature = lxw_strdup(signature); + + return LXW_NO_ERROR; +} + /* * Set the VBA name for the workbook. */ diff --git a/src/libxlsxwriter/worksheet.c b/src/libxlsxwriter/worksheet.c index cc1a6e9..a38cd79 100644 --- a/src/libxlsxwriter/worksheet.c +++ b/src/libxlsxwriter/worksheet.c @@ -3,7 +3,7 @@ * * Used in conjunction with the libxlsxwriter library. * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ @@ -15,14 +15,21 @@ #include "xlsxwriter/worksheet.h" #include "xlsxwriter/format.h" #include "xlsxwriter/utility.h" + +#ifdef USE_OPENSSL_MD5 +#include +#else +#ifndef USE_NO_MD5 #include "xlsxwriter/third_party/md5.h" +#endif +#endif #define LXW_STR_MAX 32767 #define LXW_BUFFER_SIZE 4096 #define LXW_PRINT_ACROSS 1 #define LXW_VALIDATION_MAX_TITLE_LENGTH 32 #define LXW_VALIDATION_MAX_STRING_LENGTH 255 - +#define LXW_THIS_ROW "[#This Row]," /* * Forward declarations. */ @@ -146,6 +153,10 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data) GOTO_LABEL_ON_MEM_ERROR(worksheet->header_image_objs, mem_error); STAILQ_INIT(worksheet->header_image_objs); + worksheet->button_objs = calloc(1, sizeof(struct lxw_comment_objs)); + GOTO_LABEL_ON_MEM_ERROR(worksheet->button_objs, mem_error); + STAILQ_INIT(worksheet->button_objs); + worksheet->selections = calloc(1, sizeof(struct lxw_selections)); GOTO_LABEL_ON_MEM_ERROR(worksheet->selections, mem_error); STAILQ_INIT(worksheet->selections); @@ -155,6 +166,10 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data) GOTO_LABEL_ON_MEM_ERROR(worksheet->data_validations, mem_error); STAILQ_INIT(worksheet->data_validations); + worksheet->table_objs = calloc(1, sizeof(struct lxw_table_objs)); + GOTO_LABEL_ON_MEM_ERROR(worksheet->table_objs, mem_error); + STAILQ_INIT(worksheet->table_objs); + worksheet->external_hyperlinks = calloc(1, sizeof(struct lxw_rel_tuples)); GOTO_LABEL_ON_MEM_ERROR(worksheet->external_hyperlinks, mem_error); STAILQ_INIT(worksheet->external_hyperlinks); @@ -172,10 +187,19 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data) GOTO_LABEL_ON_MEM_ERROR(worksheet->vml_drawing_links, mem_error); STAILQ_INIT(worksheet->vml_drawing_links); + worksheet->external_table_links = + calloc(1, sizeof(struct lxw_rel_tuples)); + GOTO_LABEL_ON_MEM_ERROR(worksheet->external_table_links, mem_error); + STAILQ_INIT(worksheet->external_table_links); + if (init_data && init_data->optimize) { FILE *tmpfile; - tmpfile = lxw_tmpfile(init_data->tmpdir); + worksheet->optimize_buffer = NULL; + worksheet->optimize_buffer_size = 0; + tmpfile = lxw_get_filehandle(&worksheet->optimize_buffer, + &worksheet->optimize_buffer_size, + init_data->tmpdir); if (!tmpfile) { LXW_ERROR("Error creating tmpfile() for worksheet in " "'constant_memory' mode."); @@ -193,7 +217,7 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data) RB_INIT(worksheet->drawing_rel_ids); worksheet->vml_drawing_rel_ids = - calloc(1, sizeof(struct lxw_drawing_rel_ids)); + calloc(1, sizeof(struct lxw_vml_drawing_rel_ids)); GOTO_LABEL_ON_MEM_ERROR(worksheet->vml_drawing_rel_ids, mem_error); RB_INIT(worksheet->vml_drawing_rel_ids); @@ -287,10 +311,52 @@ _free_vml_object(lxw_vml_obj *vml_obj) free(vml_obj->text); free(vml_obj->image_position); free(vml_obj->name); + free(vml_obj->macro); free(vml_obj); } +/* + * Free autofilter rule object. + */ +STATIC void +_free_filter_rule(lxw_filter_rule_obj *rule_obj) +{ + uint16_t i; + + if (!rule_obj) + return; + + free(rule_obj->value1_string); + free(rule_obj->value2_string); + + if (rule_obj->list) { + for (i = 0; i < rule_obj->num_list_filters; i++) + free(rule_obj->list[i]); + + free(rule_obj->list); + } + + free(rule_obj); +} + +/* + * Free autofilter rules. + */ +STATIC void +_free_filter_rules(lxw_worksheet *worksheet) +{ + uint16_t i; + + if (!worksheet->filter_rules) + return; + + for (i = 0; i < worksheet->num_filter_rules; i++) + _free_filter_rule(worksheet->filter_rules[i]); + + free(worksheet->filter_rules); +} + /* * Free a worksheet cell. */ @@ -303,7 +369,7 @@ _free_cell(lxw_cell *cell) if (cell->type != NUMBER_CELL && cell->type != STRING_CELL && cell->type != BLANK_CELL && cell->type != BOOLEAN_CELL) { - free(cell->u.string); + free((void *) cell->u.string); } free(cell->user_data1); @@ -354,6 +420,7 @@ _free_object_properties(lxw_object_properties *object_property) free(object_property->md5); free(object_property->image_position); free(object_property); + object_property = NULL; } /* @@ -410,6 +477,43 @@ _free_relationship(lxw_rel_tuple *relationship) free(relationship); } +/* + * Free a worksheet table column object. + */ +STATIC void +_free_worksheet_table_column(lxw_table_column *column) +{ + if (!column) + return; + + free((void *) column->header); + free((void *) column->formula); + free((void *) column->total_string); + + free(column); +} + +/* + * Free a worksheet table object. + */ +STATIC void +_free_worksheet_table(lxw_table_obj *table) +{ + uint16_t i; + + if (!table) + return; + + for (i = 0; i < table->num_cols; i++) + _free_worksheet_table_column(table->columns[i]); + + free(table->name); + free(table->total_string); + free(table->columns); + + free(table); +} + /* * Free a worksheet object. */ @@ -421,11 +525,12 @@ lxw_worksheet_free(lxw_worksheet *worksheet) lxw_col_t col; lxw_merged_range *merged_range; lxw_object_properties *object_props; - lxw_vml_obj *header_image_vml; + lxw_vml_obj *vml_obj; lxw_selection *selection; lxw_data_val_obj *data_validation; lxw_rel_tuple *relationship; lxw_cond_format_obj *cond_format; + lxw_table_obj *table_obj; struct lxw_drawing_rel_id *drawing_rel_id; struct lxw_drawing_rel_id *next_drawing_rel_id; struct lxw_cond_format_hash_element *cond_format_elem; @@ -516,14 +621,24 @@ lxw_worksheet_free(lxw_worksheet *worksheet) if (worksheet->header_image_objs) { while (!STAILQ_EMPTY(worksheet->header_image_objs)) { - header_image_vml = STAILQ_FIRST(worksheet->header_image_objs); + vml_obj = STAILQ_FIRST(worksheet->header_image_objs); STAILQ_REMOVE_HEAD(worksheet->header_image_objs, list_pointers); - _free_vml_object(header_image_vml); + _free_vml_object(vml_obj); } free(worksheet->header_image_objs); } + if (worksheet->button_objs) { + while (!STAILQ_EMPTY(worksheet->button_objs)) { + vml_obj = STAILQ_FIRST(worksheet->button_objs); + STAILQ_REMOVE_HEAD(worksheet->button_objs, list_pointers); + _free_vml_object(vml_obj); + } + + free(worksheet->button_objs); + } + if (worksheet->selections) { while (!STAILQ_EMPTY(worksheet->selections)) { selection = STAILQ_FIRST(worksheet->selections); @@ -534,6 +649,16 @@ lxw_worksheet_free(lxw_worksheet *worksheet) free(worksheet->selections); } + if (worksheet->table_objs) { + while (!STAILQ_EMPTY(worksheet->table_objs)) { + table_obj = STAILQ_FIRST(worksheet->table_objs); + STAILQ_REMOVE_HEAD(worksheet->table_objs, list_pointers); + _free_worksheet_table(table_obj); + } + + free(worksheet->table_objs); + } + if (worksheet->data_validations) { while (!STAILQ_EMPTY(worksheet->data_validations)) { data_validation = STAILQ_FIRST(worksheet->data_validations); @@ -572,6 +697,13 @@ lxw_worksheet_free(lxw_worksheet *worksheet) } free(worksheet->vml_drawing_links); + while (!STAILQ_EMPTY(worksheet->external_table_links)) { + relationship = STAILQ_FIRST(worksheet->external_table_links); + STAILQ_REMOVE_HEAD(worksheet->external_table_links, list_pointers); + _free_relationship(relationship); + } + free(worksheet->external_table_links); + if (worksheet->drawing_rel_ids) { for (drawing_rel_id = RB_MIN(lxw_drawing_rel_ids, worksheet->drawing_rel_ids); @@ -634,6 +766,9 @@ lxw_worksheet_free(lxw_worksheet *worksheet) _free_relationship(worksheet->external_vml_comment_link); _free_relationship(worksheet->external_comment_link); _free_relationship(worksheet->external_vml_header_link); + _free_relationship(worksheet->external_background_link); + + _free_filter_rules(worksheet); if (worksheet->array) { for (col = 0; col < LXW_COL_MAX; col++) { @@ -650,8 +785,8 @@ lxw_worksheet_free(lxw_worksheet *worksheet) free(worksheet->hbreaks); free(worksheet->vbreaks); - free(worksheet->name); - free(worksheet->quoted_name); + free((void *) worksheet->name); + free((void *) worksheet->quoted_name); free(worksheet->vba_codename); free(worksheet->vml_data_id_str); free(worksheet->vml_header_id_str); @@ -761,7 +896,7 @@ _new_inline_string_cell(lxw_row_t row_num, */ STATIC lxw_cell * _new_inline_rich_string_cell(lxw_row_t row_num, - lxw_col_t col_num, char *string, + lxw_col_t col_num, const char *string, lxw_format *format) { lxw_cell *cell = calloc(1, sizeof(lxw_cell)); @@ -800,18 +935,22 @@ _new_formula_cell(lxw_row_t row_num, */ STATIC lxw_cell * _new_array_formula_cell(lxw_row_t row_num, lxw_col_t col_num, char *formula, - char *range, lxw_format *format) + char *range, lxw_format *format, uint8_t is_dynamic) { lxw_cell *cell = calloc(1, sizeof(lxw_cell)); RETURN_ON_MEM_ERROR(cell, cell); cell->row_num = row_num; cell->col_num = col_num; - cell->type = ARRAY_FORMULA_CELL; cell->format = format; cell->u.string = formula; cell->user_data1 = range; + if (is_dynamic) + cell->type = DYNAMIC_ARRAY_FORMULA_CELL; + else + cell->type = ARRAY_FORMULA_CELL; + return cell; } @@ -1288,12 +1427,12 @@ _find_vml_drawing_rel_index(lxw_worksheet *self, char *target) * handles forward and back slashes. It doesn't copy exactly the return * format of basename(). */ -char * +const char * lxw_basename(const char *path) { - char *forward_slash; - char *back_slash; + const char *forward_slash; + const char *back_slash; if (!path) return NULL; @@ -1302,7 +1441,7 @@ lxw_basename(const char *path) back_slash = strrchr(path, '\\'); if (!forward_slash && !back_slash) - return (char *) path; + return path; if (forward_slash > back_slash) return forward_slash + 1; @@ -1313,7 +1452,7 @@ lxw_basename(const char *path) /* Function to count the total concatenated length of the strings in a * validation list array, including commas. */ size_t -_validation_list_length(char **list) +_validation_list_length(const char **list) { uint8_t i = 0; size_t length = 0; @@ -1321,7 +1460,7 @@ _validation_list_length(char **list) if (!list || !list[0]) return 0; - while (list[i] && length <= LXW_VALIDATION_MAX_STRING_LENGTH) { + while (list[i] && length < LXW_VALIDATION_MAX_STRING_LENGTH) { /* Include commas in the length. */ length += 1 + lxw_utf8_strlen(list[i]); i++; @@ -1336,14 +1475,14 @@ _validation_list_length(char **list) /* Function to convert an array of strings into a CSV string for data * validation lists. */ char * -_validation_list_to_csv(char **list) +_validation_list_to_csv(const char **list) { uint8_t i = 0; char *str; /* Create a buffer for the concatenated, and quoted, string. */ - /* Add +3 for quotes and EOL. */ - str = calloc(1, LXW_VALIDATION_MAX_STRING_LENGTH + 3); + /* Allow for 4 byte UTF-8 chars and add 3 bytes for quotes and EOL. */ + str = calloc(1, LXW_VALIDATION_MAX_STRING_LENGTH * 4 + 3); if (!str) return NULL; @@ -1391,6 +1530,434 @@ _pixels_to_height(double pixels) return pixels * 0.75; } +/* Check and set if an autofilter is a standard or custom filter. */ +void +_set_custom_filter(lxw_filter_rule_obj *rule_obj) +{ + rule_obj->is_custom = LXW_TRUE; + + if (rule_obj->criteria1 == LXW_FILTER_CRITERIA_EQUAL_TO) + rule_obj->is_custom = LXW_FALSE; + + if (rule_obj->criteria1 == LXW_FILTER_CRITERIA_BLANKS) + rule_obj->is_custom = LXW_FALSE; + + if (rule_obj->criteria2 != LXW_FILTER_CRITERIA_NONE) { + if (rule_obj->criteria1 == LXW_FILTER_CRITERIA_EQUAL_TO) + rule_obj->is_custom = LXW_FALSE; + + if (rule_obj->criteria1 == LXW_FILTER_CRITERIA_BLANKS) + rule_obj->is_custom = LXW_FALSE; + + if (rule_obj->type == LXW_FILTER_TYPE_AND) + rule_obj->is_custom = LXW_TRUE; + } + + if (rule_obj->value1_string && strpbrk(rule_obj->value1_string, "*?")) + rule_obj->is_custom = LXW_TRUE; + + if (rule_obj->value2_string && strpbrk(rule_obj->value2_string, "*?")) + rule_obj->is_custom = LXW_TRUE; +} + +/* Check and copy user input for table styles in worksheet_add_table(). */ +void +_check_and_copy_table_style(lxw_table_obj *table_obj, + lxw_table_options *user_options) +{ + if (!user_options) + return; + + /* Set the defaults. */ + table_obj->style_type = LXW_TABLE_STYLE_TYPE_MEDIUM; + table_obj->style_type_number = 9; + + if (user_options->style_type > LXW_TABLE_STYLE_TYPE_DARK) { + LXW_WARN_FORMAT1 + ("worksheet_add_table(): invalid style_type = %d. " + "Using default TableStyleMedium9", user_options->style_type); + + table_obj->style_type = LXW_TABLE_STYLE_TYPE_MEDIUM; + table_obj->style_type_number = 9; + } + else { + table_obj->style_type = user_options->style_type; + } + + /* Each type (light, medium and dark) has a different number of styles. */ + if (user_options->style_type == LXW_TABLE_STYLE_TYPE_LIGHT) { + if (user_options->style_type_number > 21) { + LXW_WARN_FORMAT1("worksheet_add_table(): " + "invalid style_type_number = %d for style type " + "LXW_TABLE_STYLE_TYPE_LIGHT. " + "Using default TableStyleMedium9", + user_options->style_type); + + table_obj->style_type = LXW_TABLE_STYLE_TYPE_MEDIUM; + table_obj->style_type_number = 9; + } + else { + table_obj->style_type_number = user_options->style_type_number; + } + } + + if (user_options->style_type == LXW_TABLE_STYLE_TYPE_MEDIUM) { + if (user_options->style_type_number < 1 + || user_options->style_type_number > 28) { + LXW_WARN_FORMAT1("worksheet_add_table(): " + "invalid style_type_number = %d for style type " + "LXW_TABLE_STYLE_TYPE_MEDIUM. " + "Using default TableStyleMedium9", + user_options->style_type_number); + + table_obj->style_type = LXW_TABLE_STYLE_TYPE_MEDIUM; + table_obj->style_type_number = 9; + } + else { + table_obj->style_type_number = user_options->style_type_number; + } + } + + if (user_options->style_type == LXW_TABLE_STYLE_TYPE_DARK) { + if (user_options->style_type_number < 1 + || user_options->style_type_number > 11) { + LXW_WARN_FORMAT1("worksheet_add_table(): " + "invalid style_type_number = %d for style type " + "LXW_TABLE_STYLE_TYPE_DARK. " + "Using default TableStyleMedium9", + user_options->style_type_number); + + table_obj->style_type = LXW_TABLE_STYLE_TYPE_MEDIUM; + table_obj->style_type_number = 9; + } + else { + table_obj->style_type_number = user_options->style_type_number; + } + } +} + +/* Set the defaults for table columns in worksheet_add_table(). */ +lxw_error +_set_default_table_columns(lxw_table_obj *table_obj) +{ + + char col_name[LXW_ATTR_32]; + char *header; + uint16_t i; + lxw_table_column *column; + uint16_t num_cols = table_obj->num_cols; + lxw_table_column **columns = table_obj->columns; + + for (i = 0; i < num_cols; i++) { + lxw_snprintf(col_name, LXW_ATTR_32, "Column%d", i + 1); + + column = calloc(num_cols, sizeof(lxw_table_column)); + RETURN_ON_MEM_ERROR(column, LXW_ERROR_MEMORY_MALLOC_FAILED); + + header = lxw_strdup(col_name); + if (!header) { + free(column); + RETURN_ON_MEM_ERROR(header, LXW_ERROR_MEMORY_MALLOC_FAILED); + } + columns[i] = column; + columns[i]->header = header; + } + + return LXW_NO_ERROR; +} + +/* Convert Excel 2010 style "@" structural references to the Excel 2007 style + * "[#This Row]" in table formulas. This is the format that Excel uses to + * store the references. */ +char * +_expand_table_formula(const char *formula) +{ + char *expanded; + const char *ptr; + size_t i; + size_t ref_count = 0; + size_t expanded_len; + + ptr = formula; + + while (*ptr++) { + if (*ptr == '@') + ref_count++; + } + + if (ref_count == 0) { + /* String doesn't need to be expanded. Just copy it. */ + expanded = lxw_strdup_formula(formula); + } + else { + /* Convert "@" in the formula string to "[#This Row],". */ + expanded_len = strlen(formula) + (sizeof(LXW_THIS_ROW) * ref_count); + expanded = calloc(1, expanded_len); + + if (!expanded) + return NULL; + + i = 0; + ptr = formula; + /* Ignore the = in the formula. */ + if (*ptr == '=') + ptr++; + + /* Do the "@" expansion. */ + while (*ptr) { + if (*ptr == '@') { + strcat(&expanded[i], LXW_THIS_ROW); + i += sizeof(LXW_THIS_ROW) - 1; + } + else { + expanded[i] = *ptr; + i++; + } + + ptr++; + } + } + + return expanded; +} + +/* Set user values for table columns in worksheet_add_table(). */ +lxw_error +_set_custom_table_columns(lxw_table_obj *table_obj, + lxw_table_options *user_options) +{ + char *str; + uint16_t i; + lxw_table_column *table_column; + lxw_table_column *user_column; + uint16_t num_cols = table_obj->num_cols; + lxw_table_column **user_columns = user_options->columns; + + for (i = 0; i < num_cols; i++) { + + user_column = user_columns[i]; + table_column = table_obj->columns[i]; + + /* NULL indicates end of user input array. */ + if (user_column == NULL) + return LXW_NO_ERROR; + + if (user_column->header) { + if (lxw_utf8_strlen(user_column->header) > 255) { + LXW_WARN_FORMAT("worksheet_add_table(): column parameter " + "'header' exceeds Excel length limit of 255."); + return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; + } + + str = lxw_strdup(user_column->header); + RETURN_ON_MEM_ERROR(str, LXW_ERROR_MEMORY_MALLOC_FAILED); + + /* Free the default column header. */ + free((void *) table_column->header); + table_column->header = str; + } + + if (user_column->total_string) { + str = lxw_strdup(user_column->total_string); + RETURN_ON_MEM_ERROR(str, LXW_ERROR_MEMORY_MALLOC_FAILED); + + table_column->total_string = str; + } + + if (user_column->formula) { + str = _expand_table_formula(user_column->formula); + RETURN_ON_MEM_ERROR(str, LXW_ERROR_MEMORY_MALLOC_FAILED); + + table_column->formula = str; + } + + table_column->format = user_column->format; + table_column->total_value = user_column->total_value; + table_column->header_format = user_column->header_format; + table_column->total_function = user_column->total_function; + } + + return LXW_NO_ERROR; +} + +/* Write a worksheet table column formula like SUBTOTAL(109,[Column1]). */ +void +_write_column_function(lxw_worksheet *self, lxw_row_t row, lxw_col_t col, + lxw_table_column *column) +{ + size_t offset; + char formula[LXW_MAX_ATTRIBUTE_LENGTH]; + lxw_format *format = column->format; + uint8_t total_function = column->total_function; + double value = column->total_value; + const char *header = column->header; + + /* Write the subtotal formula number. */ + lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, "SUBTOTAL(%d,[", + total_function); + + /* Copy the header string but escape any special characters. Note, this is + * guaranteed to fit in the 2k buffer since the header is max 255 + * characters, checked in _set_custom_table_columns(). */ + offset = strlen(formula); + while (*header) { + switch (*header) { + case '\'': + case '#': + case '[': + case ']': + formula[offset++] = '\''; + formula[offset] = *header; + break; + default: + formula[offset] = *header; + break; + } + offset++; + header++; + } + + /* Write the end of the string. */ + memcpy(&formula[offset], "])\0", sizeof("])\0")); + + worksheet_write_formula_num(self, row, col, formula, format, value); +} + +/* Write a worksheet table column formula. */ +void +_write_column_formula(lxw_worksheet *self, lxw_row_t first_row, + lxw_row_t last_row, lxw_col_t col, + lxw_table_column *column) +{ + lxw_row_t row; + const char *formula = column->formula; + lxw_format *format = column->format; + + for (row = first_row; row <= last_row; row++) + worksheet_write_formula(self, row, col, formula, format); +} + +/* Set the defaults for table columns in worksheet_add_table(). */ +void +_write_table_column_data(lxw_worksheet *self, lxw_table_obj *table_obj) +{ + uint16_t i; + lxw_table_column *column; + lxw_table_column **columns = table_obj->columns; + + lxw_col_t col; + lxw_row_t first_row = table_obj->first_row; + lxw_col_t first_col = table_obj->first_col; + lxw_row_t last_row = table_obj->last_row; + lxw_row_t first_data_row = first_row; + lxw_row_t last_data_row = last_row; + + if (!table_obj->no_header_row) + first_data_row++; + + if (table_obj->total_row) + last_data_row--; + + for (i = 0; i < table_obj->num_cols; i++) { + col = first_col + i; + column = columns[i]; + + if (table_obj->no_header_row == LXW_FALSE) + worksheet_write_string(self, first_row, col, column->header, + column->header_format); + + if (column->total_string) + worksheet_write_string(self, last_row, col, column->total_string, + NULL); + + if (column->total_function) + _write_column_function(self, last_row, col, column); + + if (column->formula) + _write_column_formula(self, first_data_row, last_data_row, col, + column); + } +} + +/* + * Check that there are sufficient data rows in a worksheet table. + */ +lxw_error +_check_table_rows(lxw_row_t first_row, lxw_row_t last_row, + lxw_table_options *user_options) +{ + lxw_row_t num_non_header_rows = last_row - first_row; + + if (user_options && user_options->no_header_row == LXW_TRUE) + num_non_header_rows++; + + if (num_non_header_rows == 0) { + LXW_WARN_FORMAT("worksheet_add_table(): " + "table must have at least 1 non-header row."); + return LXW_ERROR_PARAMETER_VALIDATION; + } + + return LXW_NO_ERROR; +} + +/* + * Check that the the table name is valid. + */ +lxw_error +_check_table_name(lxw_table_options *user_options) +{ + const char *name; + char *ptr; + char first[2] = { 0, 0 }; + + if (!user_options) + return LXW_NO_ERROR; + + if (!user_options->name) + return LXW_NO_ERROR; + + name = user_options->name; + + /* Check table name length. */ + if (lxw_utf8_strlen(name) > 255) { + LXW_WARN_FORMAT("worksheet_add_table(): " + "Table name exceeds Excel's limit of 255."); + return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; + } + + /* Check some short invalid names. */ + if (strlen(name) == 1 + && (name[0] == 'C' || name[0] == 'c' || name[0] == 'R' + || name[0] == 'r')) { + LXW_WARN_FORMAT1("worksheet_add_table(): " + "invalid table name \"%s\".", name); + return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; + } + + /* Check for invalid characters in Table name, while trying to allow + * for utf8 strings. */ + ptr = strpbrk(name, " !\"#$%&'()*+,-/:;<=>?@[\\]^`{|}~"); + if (ptr) { + LXW_WARN_FORMAT2("worksheet_add_table(): " + "invalid character '%c' in table name \"%s\".", + *ptr, name); + return LXW_ERROR_PARAMETER_VALIDATION; + } + + /* Check for invalid initial character in Table name, while trying to allow + * for utf8 strings. */ + first[0] = name[0]; + ptr = strpbrk(first, " !\"#$%&'()*+,-./0123456789:;<=>?@[\\]^`{|}~"); + if (ptr) { + LXW_WARN_FORMAT2("worksheet_add_table(): " + "invalid first character '%c' in table name \"%s\".", + *ptr, name); + return LXW_ERROR_PARAMETER_VALIDATION; + } + + return LXW_NO_ERROR; +} + /***************************************************************************** * * XML functions. @@ -1834,36 +2401,35 @@ _worksheet_write_sheet_view(lxw_worksheet *self) LXW_PUSH_ATTRIBUTES_STR("showGridLines", "0"); /* Hide zeroes in cells. */ - if (!self->show_zeros) { + if (!self->show_zeros) LXW_PUSH_ATTRIBUTES_STR("showZeros", "0"); - } /* Display worksheet right to left for Hebrew, Arabic and others. */ - if (self->right_to_left) { + if (self->right_to_left) LXW_PUSH_ATTRIBUTES_STR("rightToLeft", "1"); - } /* Show that the sheet tab is selected. */ if (self->selected) LXW_PUSH_ATTRIBUTES_STR("tabSelected", "1"); /* Turn outlines off. Also required in the outlinePr element. */ - if (!self->outline_on) { + if (!self->outline_on) LXW_PUSH_ATTRIBUTES_STR("showOutlineSymbols", "0"); - } /* Set the page view/layout mode if required. */ if (self->page_view) LXW_PUSH_ATTRIBUTES_STR("view", "pageLayout"); + /* Set the top left cell if required. */ + if (self->top_left_cell[0]) + LXW_PUSH_ATTRIBUTES_STR("topLeftCell", self->top_left_cell); + /* Set the zoom level. */ - if (self->zoom != 100) { - if (!self->page_view) { - LXW_PUSH_ATTRIBUTES_INT("zoomScale", self->zoom); + if (self->zoom != 100 && !self->page_view) { + LXW_PUSH_ATTRIBUTES_INT("zoomScale", self->zoom); - if (self->zoom_scale_normal) - LXW_PUSH_ATTRIBUTES_INT("zoomScaleNormal", self->zoom); - } + if (self->zoom_scale_normal) + LXW_PUSH_ATTRIBUTES_INT("zoomScaleNormal", self->zoom); } LXW_PUSH_ATTRIBUTES_STR("workbookViewId", "0"); @@ -1962,18 +2528,27 @@ _worksheet_write_optimized_sheet_data(lxw_worksheet *self) lxw_xml_start_tag(self->file, "sheetData", NULL); - /* Flush and rewind the temp file. */ + /* Flush the temp file. */ fflush(self->optimize_tmpfile); - rewind(self->optimize_tmpfile); - while (read_size) { - read_size = - fread(buffer, 1, LXW_BUFFER_SIZE, self->optimize_tmpfile); + if (self->optimize_buffer) { /* Ignore return value. There is no easy way to raise error. */ - (void) fwrite(buffer, 1, read_size, self->file); + (void) fwrite(self->optimize_buffer, self->optimize_buffer_size, + 1, self->file); + } + else { + /* Rewind the temp file. */ + rewind(self->optimize_tmpfile); + while (read_size) { + read_size = + fread(buffer, 1, LXW_BUFFER_SIZE, self->optimize_tmpfile); + /* Ignore return value. There is no easy way to raise error. */ + (void) fwrite(buffer, 1, read_size, self->file); + } } fclose(self->optimize_tmpfile); + free(self->optimize_buffer); lxw_xml_end_tag(self->file, "sheetData"); } @@ -2064,6 +2639,9 @@ _worksheet_write_page_setup(lxw_worksheet *self) else LXW_PUSH_ATTRIBUTES_STR("orientation", "landscape"); + if (self->black_white) + LXW_PUSH_ATTRIBUTES_STR("blackAndWhite", "1"); + /* Set start page active flag. */ if (self->page_start) LXW_PUSH_ATTRIBUTES_INT("useFirstPageNumber", 1); @@ -2415,7 +2993,6 @@ _worksheet_position_object_pixels(lxw_worksheet *self, drawing_object->to.row_offset = y2; drawing_object->col_absolute = x_abs; drawing_object->row_absolute = y_abs; - } /* @@ -2562,35 +3139,131 @@ _get_comment_params(lxw_vml_obj *comment, lxw_comment_options *options) } /* - * Calculate the comment object position and vertices. + * This function handles the additional optional parameters to + * worksheet_insert_button() as well as calculating the button object + * position and vertices. */ -void -_worksheet_position_vml_object(lxw_worksheet *self, lxw_vml_obj *comment) +lxw_error +_get_button_params(lxw_vml_obj *button, uint16_t button_number, + lxw_button_options *options) +{ + + int32_t x_offset = 0; + int32_t y_offset = 0; + uint32_t height = LXW_DEF_ROW_HEIGHT_PIXELS; + uint32_t width = LXW_DEF_COL_WIDTH_PIXELS; + double x_scale = 1.0; + double y_scale = 1.0; + lxw_row_t row = button->row; + lxw_col_t col = button->col; + char buffer[LXW_ATTR_32]; + uint8_t has_caption = LXW_FALSE; + uint8_t has_macro = LXW_FALSE; + size_t len; + + /* Set any user defined options. */ + if (options) { + + if (options->width > 0.0) + width = options->width; + + if (options->height > 0.0) + height = options->height; + + if (options->x_scale > 0.0) + x_scale = options->x_scale; + + if (options->y_scale > 0.0) + y_scale = options->y_scale; + + if (options->x_offset != 0) + x_offset = options->x_offset; + + if (options->y_offset != 0) + y_offset = options->y_offset; + + if (options->caption) { + button->name = lxw_strdup(options->caption); + RETURN_ON_MEM_ERROR(button->name, LXW_ERROR_MEMORY_MALLOC_FAILED); + has_caption = LXW_TRUE; + } + + if (options->macro) { + len = sizeof("[0]!") + strlen(options->macro); + button->macro = calloc(1, len); + RETURN_ON_MEM_ERROR(button->macro, + LXW_ERROR_MEMORY_MALLOC_FAILED); + + if (button->macro) + lxw_snprintf(button->macro, len, "[0]!%s", options->macro); + + has_macro = LXW_TRUE; + } + + if (options->description) { + button->text = lxw_strdup(options->description); + RETURN_ON_MEM_ERROR(button->text, LXW_ERROR_MEMORY_MALLOC_FAILED); + } + } + + if (!has_caption) { + lxw_snprintf(buffer, LXW_ATTR_32, "Button %d", button_number); + button->name = lxw_strdup(buffer); + RETURN_ON_MEM_ERROR(button->name, LXW_ERROR_MEMORY_MALLOC_FAILED); + } + + if (!has_macro) { + lxw_snprintf(buffer, LXW_ATTR_32, "[0]!Button%d_Click", + button_number); + button->macro = lxw_strdup(buffer); + RETURN_ON_MEM_ERROR(button->macro, LXW_ERROR_MEMORY_MALLOC_FAILED); + } + + /* Scale the width/height to the default/user scale and round to the + * nearest pixel. */ + width = (uint32_t) (0.5 + x_scale * width); + height = (uint32_t) (0.5 + y_scale * height); + + button->width = width; + button->height = height; + button->start_col = col; + button->start_row = row; + button->x_offset = x_offset; + button->y_offset = y_offset; + + return LXW_NO_ERROR; +} + +/* + * Calculate the vml_obj object position and vertices. + */ +void +_worksheet_position_vml_object(lxw_worksheet *self, lxw_vml_obj *vml_obj) { lxw_object_properties object_props; lxw_drawing_object drawing_object; - object_props.col = comment->start_col; - object_props.row = comment->start_row; - object_props.x_offset = comment->x_offset; - object_props.y_offset = comment->y_offset; - object_props.width = comment->width; - object_props.height = comment->height; + object_props.col = vml_obj->start_col; + object_props.row = vml_obj->start_row; + object_props.x_offset = vml_obj->x_offset; + object_props.y_offset = vml_obj->y_offset; + object_props.width = vml_obj->width; + object_props.height = vml_obj->height; drawing_object.anchor = LXW_OBJECT_DONT_MOVE_DONT_SIZE; _worksheet_position_object_pixels(self, &object_props, &drawing_object); - comment->from.col = drawing_object.from.col; - comment->from.row = drawing_object.from.row; - comment->from.col_offset = drawing_object.from.col_offset; - comment->from.row_offset = drawing_object.from.row_offset; - comment->to.col = drawing_object.to.col; - comment->to.row = drawing_object.to.row; - comment->to.col_offset = drawing_object.to.col_offset; - comment->to.row_offset = drawing_object.to.row_offset; - comment->col_absolute = drawing_object.col_absolute; - comment->row_absolute = drawing_object.row_absolute; + vml_obj->from.col = drawing_object.from.col; + vml_obj->from.row = drawing_object.from.row; + vml_obj->from.col_offset = drawing_object.from.col_offset; + vml_obj->from.row_offset = drawing_object.from.row_offset; + vml_obj->to.col = drawing_object.to.col; + vml_obj->to.row = drawing_object.to.row; + vml_obj->to.col_offset = drawing_object.to.col_offset; + vml_obj->to.row_offset = drawing_object.to.row_offset; + vml_obj->col_absolute = drawing_object.col_absolute; + vml_obj->row_absolute = drawing_object.row_absolute; } /* @@ -2822,8 +3495,8 @@ lxw_worksheet_prepare_header_image(lxw_worksheet *self, header_image_vml = calloc(1, sizeof(lxw_vml_obj)); GOTO_LABEL_ON_MEM_ERROR(header_image_vml, mem_error); - header_image_vml->width = object_props->width; - header_image_vml->height = object_props->height; + header_image_vml->width = (uint32_t) object_props->width; + header_image_vml->height = (uint32_t) object_props->height; header_image_vml->x_dpi = object_props->x_dpi; header_image_vml->y_dpi = object_props->y_dpi; header_image_vml->rel_index = 1; @@ -2854,6 +3527,44 @@ lxw_worksheet_prepare_header_image(lxw_worksheet *self, } } +/* + * Set up background image. + */ +void +lxw_worksheet_prepare_background(lxw_worksheet *self, + uint32_t image_ref_id, + lxw_object_properties *object_props) +{ + lxw_rel_tuple *relationship = NULL; + char filename[LXW_FILENAME_LENGTH]; + + STAILQ_INSERT_TAIL(self->image_props, object_props, list_pointers); + + relationship = calloc(1, sizeof(lxw_rel_tuple)); + RETURN_VOID_ON_MEM_ERROR(relationship); + + relationship->type = lxw_strdup("/image"); + GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); + + lxw_snprintf(filename, 32, "../media/image%d.%s", image_ref_id, + object_props->extension); + + relationship->target = lxw_strdup(filename); + GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); + + self->external_background_link = relationship; + + return; + +mem_error: + if (relationship) { + free(relationship->type); + free(relationship->target); + free(relationship->target_mode); + free(relationship); + } +} + /* * Set up chart/drawings. */ @@ -2906,10 +3617,11 @@ lxw_worksheet_prepare_chart(lxw_worksheet *self, drawing_object->anchor = object_props->object_position; drawing_object->type = LXW_DRAWING_CHART; - drawing_object->description = lxw_strdup("TODO_DESC"); + drawing_object->description = lxw_strdup(object_props->description); drawing_object->tip = NULL; drawing_object->rel_index = _get_drawing_rel_index(self, NULL); drawing_object->url_rel_index = 0; + drawing_object->decorative = object_props->decorative; /* Scale to user scale. */ width = object_props->width * object_props->x_scale; @@ -3116,6 +3828,56 @@ lxw_worksheet_prepare_header_vml_objects(lxw_worksheet *self, return; } +/* + * Set up external linkage for VML header/footer images. + */ +void +lxw_worksheet_prepare_tables(lxw_worksheet *self, uint32_t table_id) +{ + lxw_table_obj *table_obj; + lxw_rel_tuple *relationship; + char name[LXW_ATTR_32]; + char filename[LXW_FILENAME_LENGTH]; + + STAILQ_FOREACH(table_obj, self->table_objs, list_pointers) { + + relationship = calloc(1, sizeof(lxw_rel_tuple)); + GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); + + relationship->type = lxw_strdup("/table"); + GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); + + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "../tables/table%d.xml", table_id); + + relationship->target = lxw_strdup(filename); + GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); + + STAILQ_INSERT_TAIL(self->external_table_links, relationship, + list_pointers); + + if (!table_obj->name) { + lxw_snprintf(name, LXW_ATTR_32, "Table%d", table_id); + table_obj->name = lxw_strdup(name); + GOTO_LABEL_ON_MEM_ERROR(table_obj->name, mem_error); + } + table_obj->id = table_id; + table_id++; + } + + return; + +mem_error: + if (relationship) { + free(relationship->type); + free(relationship->target); + free(relationship->target_mode); + free(relationship); + } + + return; +} + /* * Extract width and height information from a PNG file. */ @@ -3325,7 +4087,7 @@ _process_jpeg(lxw_object_properties *image_props) if (!feof(stream)) { fseek_err = fseek(stream, offset, SEEK_CUR); if (fseek_err) - goto file_error; + break; } } @@ -3399,6 +4161,55 @@ _process_bmp(lxw_object_properties *image_props) return LXW_ERROR_IMAGE_DIMENSIONS; } +/* + * Extract width and height information from a GIF file. + */ +STATIC lxw_error +_process_gif(lxw_object_properties *image_props) +{ + uint16_t width = 0; + uint16_t height = 0; + double x_dpi = 96; + double y_dpi = 96; + int fseek_err; + + FILE *stream = image_props->stream; + + /* Skip another 2 bytes to the start of the GIF height/width. */ + fseek_err = fseek(stream, 2, SEEK_CUR); + if (fseek_err) + goto file_error; + + if (fread(&width, sizeof(width), 1, stream) < 1) + width = 0; + + if (fread(&height, sizeof(height), 1, stream) < 1) + height = 0; + + /* Ensure that we read some valid data from the file. */ + if (width == 0) + goto file_error; + + height = LXW_UINT16_HOST(height); + width = LXW_UINT16_HOST(width); + + /* Set the image metadata. */ + image_props->image_type = LXW_IMAGE_GIF; + image_props->width = width; + image_props->height = height; + image_props->x_dpi = x_dpi; + image_props->y_dpi = y_dpi; + image_props->extension = lxw_strdup("gif"); + + return LXW_NO_ERROR; + +file_error: + LXW_WARN_FORMAT1("worksheet image insertion: " + "no size data found in: %s.", image_props->filename); + + return LXW_ERROR_IMAGE_DIMENSIONS; +} + /* * Extract information from the image file such as dimension, type, filename, * and extension. @@ -3409,7 +4220,7 @@ _get_image_properties(lxw_object_properties *image_props) unsigned char signature[4]; #ifndef USE_NO_MD5 uint8_t i; - lxw_md5_ctx md5_context; + MD5_CTX md5_context; size_t size_read; char buffer[LXW_IMAGE_BUFFER_SIZE]; unsigned char md5_checksum[LXW_MD5_SIZE]; @@ -3435,6 +4246,10 @@ _get_image_properties(lxw_object_properties *image_props) if (_process_bmp(image_props) != LXW_NO_ERROR) return LXW_ERROR_IMAGE_DIMENSIONS; } + else if (memcmp(signature, "GIF8", 4) == 0) { + if (_process_gif(image_props) != LXW_NO_ERROR) + return LXW_ERROR_IMAGE_DIMENSIONS; + } else { LXW_WARN_FORMAT1("worksheet image insertion: " "unsupported image format for: %s.", @@ -3447,16 +4262,16 @@ _get_image_properties(lxw_object_properties *image_props) * images to reduce the xlsx file size.*/ rewind(image_props->stream); - lxw_md5_init(&md5_context); + MD5_Init(&md5_context); size_read = fread(buffer, 1, LXW_IMAGE_BUFFER_SIZE, image_props->stream); while (size_read) { - lxw_md5_update(&md5_context, buffer, size_read); + MD5_Update(&md5_context, buffer, (unsigned long) size_read); size_read = fread(buffer, 1, LXW_IMAGE_BUFFER_SIZE, image_props->stream); } - lxw_md5_final(md5_checksum, &md5_context); + MD5_Final(md5_checksum, &md5_context); /* Create a 32 char hex string buffer for the MD5 checksum. */ image_props->md5 = calloc(1, LXW_MD5_SIZE * 2 + 1); @@ -3542,7 +4357,7 @@ STATIC void _write_number_cell(lxw_worksheet *self, char *range, int32_t style_index, lxw_cell *cell) { -#ifdef USE_DOUBLE_FUNCTION +#ifdef USE_DTOA_LIBRARY char data[LXW_ATTR_32]; lxw_sprintf_dbl(data, cell->u.number); @@ -3556,11 +4371,11 @@ _write_number_cell(lxw_worksheet *self, char *range, #else if (style_index) fprintf(self->file, - "%.16g", + "%.16G", range, style_index, cell->u.number); else fprintf(self->file, - "%.16g", range, cell->u.number); + "%.16G", range, cell->u.number); #endif } @@ -3631,31 +4446,16 @@ STATIC void _write_inline_rich_string_cell(lxw_worksheet *self, char *range, int32_t style_index, lxw_cell *cell) { - char *string = cell->u.string; - - /* Add attribute to preserve leading or trailing whitespace. */ - if (isspace((unsigned char) string[0]) - || isspace((unsigned char) string[strlen(string) - 1])) { + const char *string = cell->u.string; - 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); - } + if (style_index) + fprintf(self->file, + "" + "%s", range, style_index, string); + else + fprintf(self->file, + "" + "%s", range, string); } /* @@ -3711,10 +4511,10 @@ _write_boolean_cell(lxw_worksheet *self, lxw_cell *cell) { char data[LXW_ATTR_32]; - if (cell->u.number) - data[0] = '1'; - else + if (cell->u.number == 0.0) data[0] = '0'; + else + data[0] = '1'; data[1] = '\0'; @@ -3845,6 +4645,12 @@ _write_cell(lxw_worksheet *self, lxw_cell *cell, lxw_format *row_format) _write_array_formula_num_cell(self, cell); lxw_xml_end_tag(self->file, "c"); } + else if (cell->type == DYNAMIC_ARRAY_FORMULA_CELL) { + LXW_PUSH_ATTRIBUTES_STR("cm", "1"); + lxw_xml_start_tag(self->file, "c", &attributes); + _write_array_formula_num_cell(self, cell); + lxw_xml_end_tag(self->file, "c"); + } LXW_FREE_ATTRIBUTES(); } @@ -3937,11 +4743,11 @@ lxw_worksheet_write_single_row(lxw_worksheet *self) /* Process a header/footer image and store it in the correct slot. */ lxw_error -_worksheet_set_header_footer_image(lxw_worksheet *self, char *filename, +_worksheet_set_header_footer_image(lxw_worksheet *self, const char *filename, uint8_t image_position) { FILE *image_stream; - char *description; + const char *description; lxw_object_properties *object_props; char *image_strings[] = { "LH", "CH", "RH", "LF", "CF", "RF" }; @@ -3993,8 +4799,6 @@ _worksheet_set_header_footer_image(lxw_worksheet *self, char *filename, fclose(image_stream); return LXW_ERROR_IMAGE_DIMENSIONS; } - - return LXW_NO_ERROR; } /* @@ -4370,103 +5174,307 @@ _worksheet_write_col_breaks(lxw_worksheet *self) } /* - * Write the element. + * Write the element. */ STATIC void -_worksheet_write_auto_filter(lxw_worksheet *self) +_worksheet_write_filter(lxw_worksheet *self, const char *str, double num, + uint8_t criteria) { struct xml_attribute_list attributes; struct xml_attribute *attribute; - char range[LXW_MAX_CELL_RANGE_LENGTH]; - if (!self->autofilter.in_use) + if (criteria == LXW_FILTER_CRITERIA_BLANKS) return; - lxw_rowcol_to_range(range, - self->autofilter.first_row, - self->autofilter.first_col, - self->autofilter.last_row, self->autofilter.last_col); - LXW_INIT_ATTRIBUTES(); - LXW_PUSH_ATTRIBUTES_STR("ref", range); - lxw_xml_empty_tag(self->file, "autoFilter", &attributes); + if (str) + LXW_PUSH_ATTRIBUTES_STR("val", str); + else + LXW_PUSH_ATTRIBUTES_DBL("val", num); + + lxw_xml_empty_tag(self->file, "filter", &attributes); LXW_FREE_ATTRIBUTES(); } /* - * Write the element for external links. + * Write the element as simple equality. */ STATIC void -_worksheet_write_hyperlink_external(lxw_worksheet *self, lxw_row_t row_num, - lxw_col_t col_num, const char *location, - const char *tooltip, uint16_t id) +_worksheet_write_filter_standard(lxw_worksheet *self, + lxw_filter_rule_obj *filter) { struct xml_attribute_list attributes; struct xml_attribute *attribute; - char ref[LXW_MAX_CELL_NAME_LENGTH]; - char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; - lxw_rowcol_to_cell(ref, row_num, col_num); + LXW_INIT_ATTRIBUTES(); - lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", id); + if (filter->has_blanks) { + LXW_PUSH_ATTRIBUTES_STR("blank", "1"); + } - LXW_INIT_ATTRIBUTES(); - LXW_PUSH_ATTRIBUTES_STR("ref", ref); - LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); + if (filter->type == LXW_FILTER_TYPE_SINGLE && filter->has_blanks) { + lxw_xml_empty_tag(self->file, "filters", &attributes); - if (location) - LXW_PUSH_ATTRIBUTES_STR("location", location); + } + else { + lxw_xml_start_tag(self->file, "filters", &attributes); - if (tooltip) - LXW_PUSH_ATTRIBUTES_STR("tooltip", tooltip); + /* Write the filter element. */ + if (filter->type == LXW_FILTER_TYPE_SINGLE) { + _worksheet_write_filter(self, filter->value1_string, + filter->value1, filter->criteria1); + } + else if (filter->type == LXW_FILTER_TYPE_AND + || filter->type == LXW_FILTER_TYPE_OR) { + _worksheet_write_filter(self, filter->value1_string, + filter->value1, filter->criteria1); + _worksheet_write_filter(self, filter->value2_string, + filter->value2, filter->criteria2); + } - lxw_xml_empty_tag(self->file, "hyperlink", &attributes); + lxw_xml_end_tag(self->file, "filters"); + } LXW_FREE_ATTRIBUTES(); } /* - * Write the element for internal links. + * Write the element. */ STATIC void -_worksheet_write_hyperlink_internal(lxw_worksheet *self, lxw_row_t row_num, - lxw_col_t col_num, const char *location, - const char *display, const char *tooltip) +_worksheet_write_custom_filter(lxw_worksheet *self, const char *str, + double num, uint8_t criteria) { struct xml_attribute_list attributes; struct xml_attribute *attribute; - char ref[LXW_MAX_CELL_NAME_LENGTH]; - - lxw_rowcol_to_cell(ref, row_num, col_num); LXW_INIT_ATTRIBUTES(); - LXW_PUSH_ATTRIBUTES_STR("ref", ref); - - if (location) - LXW_PUSH_ATTRIBUTES_STR("location", location); - if (tooltip) - LXW_PUSH_ATTRIBUTES_STR("tooltip", tooltip); - - if (display) - LXW_PUSH_ATTRIBUTES_STR("display", display); + if (criteria == LXW_FILTER_CRITERIA_NOT_EQUAL_TO) + LXW_PUSH_ATTRIBUTES_STR("operator", "notEqual"); + if (criteria == LXW_FILTER_CRITERIA_GREATER_THAN) + LXW_PUSH_ATTRIBUTES_STR("operator", "greaterThan"); + else if (criteria == LXW_FILTER_CRITERIA_GREATER_THAN_OR_EQUAL_TO) + LXW_PUSH_ATTRIBUTES_STR("operator", "greaterThanOrEqual"); + else if (criteria == LXW_FILTER_CRITERIA_LESS_THAN) + LXW_PUSH_ATTRIBUTES_STR("operator", "lessThan"); + else if (criteria == LXW_FILTER_CRITERIA_LESS_THAN_OR_EQUAL_TO) + LXW_PUSH_ATTRIBUTES_STR("operator", "lessThanOrEqual"); + + if (str) + LXW_PUSH_ATTRIBUTES_STR("val", str); + else + LXW_PUSH_ATTRIBUTES_DBL("val", num); - lxw_xml_empty_tag(self->file, "hyperlink", &attributes); + lxw_xml_empty_tag(self->file, "customFilter", &attributes); LXW_FREE_ATTRIBUTES(); } /* - * Process any stored hyperlinks in row/col order and write the - * element. The attributes are different for internal and external links. + * Write the element as a list. */ STATIC void -_worksheet_write_hyperlinks(lxw_worksheet *self) +_worksheet_write_filter_list(lxw_worksheet *self, lxw_filter_rule_obj *filter) { - - lxw_row *row; + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + uint16_t i; + + LXW_INIT_ATTRIBUTES(); + + if (filter->has_blanks) { + LXW_PUSH_ATTRIBUTES_STR("blank", "1"); + } + + lxw_xml_start_tag(self->file, "filters", &attributes); + + for (i = 0; i < filter->num_list_filters; i++) { + _worksheet_write_filter(self, filter->list[i], 0, 0); + } + + lxw_xml_end_tag(self->file, "filters"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_filter_custom(lxw_worksheet *self, + lxw_filter_rule_obj *filter) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (filter->type == LXW_FILTER_TYPE_AND) + LXW_PUSH_ATTRIBUTES_STR("and", "1"); + + lxw_xml_start_tag(self->file, "customFilters", &attributes); + + /* Write the filter element. */ + if (filter->type == LXW_FILTER_TYPE_SINGLE) { + _worksheet_write_custom_filter(self, filter->value1_string, + filter->value1, filter->criteria1); + } + else if (filter->type == LXW_FILTER_TYPE_AND + || filter->type == LXW_FILTER_TYPE_OR) { + _worksheet_write_custom_filter(self, filter->value1_string, + filter->value1, filter->criteria1); + _worksheet_write_custom_filter(self, filter->value2_string, + filter->value2, filter->criteria2); + } + + lxw_xml_end_tag(self->file, "customFilters"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_filter_column(lxw_worksheet *self, + lxw_filter_rule_obj *filter) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (!filter) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("colId", filter->col_num); + + lxw_xml_start_tag(self->file, "filterColumn", &attributes); + + if (filter->list) + _worksheet_write_filter_list(self, filter); + else if (filter->is_custom) + _worksheet_write_filter_custom(self, filter); + else + _worksheet_write_filter_standard(self, filter); + + lxw_xml_end_tag(self->file, "filterColumn"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_auto_filter(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char range[LXW_MAX_CELL_RANGE_LENGTH]; + uint16_t i; + + if (!self->autofilter.in_use) + return; + + lxw_rowcol_to_range(range, + self->autofilter.first_row, + self->autofilter.first_col, + self->autofilter.last_row, self->autofilter.last_col); + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("ref", range); + + if (self->autofilter.has_rules) { + lxw_xml_start_tag(self->file, "autoFilter", &attributes); + + for (i = 0; i < self->num_filter_rules; i++) + _worksheet_write_filter_column(self, self->filter_rules[i]); + + lxw_xml_end_tag(self->file, "autoFilter"); + + } + else { + lxw_xml_empty_tag(self->file, "autoFilter", &attributes); + } + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element for external links. + */ +STATIC void +_worksheet_write_hyperlink_external(lxw_worksheet *self, lxw_row_t row_num, + lxw_col_t col_num, const char *location, + const char *tooltip, uint16_t id) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char ref[LXW_MAX_CELL_NAME_LENGTH]; + char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; + + lxw_rowcol_to_cell(ref, row_num, col_num); + + lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", id); + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("ref", ref); + LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); + + if (location) + LXW_PUSH_ATTRIBUTES_STR("location", location); + + if (tooltip) + LXW_PUSH_ATTRIBUTES_STR("tooltip", tooltip); + + lxw_xml_empty_tag(self->file, "hyperlink", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element for internal links. + */ +STATIC void +_worksheet_write_hyperlink_internal(lxw_worksheet *self, lxw_row_t row_num, + lxw_col_t col_num, const char *location, + const char *display, const char *tooltip) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char ref[LXW_MAX_CELL_NAME_LENGTH]; + + lxw_rowcol_to_cell(ref, row_num, col_num); + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("ref", ref); + + if (location) + LXW_PUSH_ATTRIBUTES_STR("location", location); + + if (tooltip) + LXW_PUSH_ATTRIBUTES_STR("tooltip", tooltip); + + if (display) + LXW_PUSH_ATTRIBUTES_STR("display", display); + + lxw_xml_empty_tag(self->file, "hyperlink", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Process any stored hyperlinks in row/col order and write the + * element. The attributes are different for internal and external links. + */ +STATIC void +_worksheet_write_hyperlinks(lxw_worksheet *self) +{ + + lxw_row *row; lxw_cell *link; lxw_rel_tuple *relationship; @@ -4657,6 +5665,30 @@ _worksheet_write_legacy_drawing_hf(lxw_worksheet *self) } +/* + * Write the element. + */ +STATIC void +_worksheet_write_picture(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; + + if (!self->has_background_image) + return; + else + self->rel_count++; + + lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", self->rel_count); + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); + + lxw_xml_empty_tag(self->file, "picture", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + /* * Write the element. */ @@ -5349,7 +6381,7 @@ _worksheet_write_cf_rule_top(lxw_worksheet *self, LXW_PUSH_ATTRIBUTES_INT("bottom", 1); /* Rank must be an int in the range 1-1000 . */ - if (cond_format->min_value < 1.0 || cond_format->min_value > 1.0) + if (cond_format->min_value < 1.0 || cond_format->min_value > 1000.0) LXW_PUSH_ATTRIBUTES_DBL("rank", 10); else LXW_PUSH_ATTRIBUTES_DBL("rank", (uint16_t) cond_format->min_value); @@ -6519,6 +7551,56 @@ _worksheet_write_ignored_errors(lxw_worksheet *self) lxw_xml_end_tag(self->file, "ignoredErrors"); } +/* + * Write the element. + */ +STATIC void +_worksheet_write_table_part(lxw_worksheet *self, uint16_t id) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; + + lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", id); + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); + + lxw_xml_empty_tag(self->file, "tablePart", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_table_parts(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + lxw_table_obj *table_obj; + + if (!self->table_count) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("count", self->table_count); + + lxw_xml_start_tag(self->file, "tableParts", &attributes); + + STAILQ_FOREACH(table_obj, self->table_objs, list_pointers) { + self->rel_count++; + + /* Write the tablePart element. */ + _worksheet_write_table_part(self, self->rel_count); + } + + lxw_xml_end_tag(self->file, "tableParts"); + + LXW_FREE_ATTRIBUTES(); +} + /* * External functions to call intern XML methods shared with chartsheet. */ @@ -6646,6 +7728,12 @@ lxw_worksheet_assemble_xml_file(lxw_worksheet *self) /* Write the legacyDrawingHF element. */ _worksheet_write_legacy_drawing_hf(self); + /* Write the picture element. */ + _worksheet_write_picture(self); + + /* Write the tableParts element. */ + _worksheet_write_table_parts(self); + /* Write the extLst element. */ _worksheet_write_ext_list(self); @@ -6823,16 +7911,16 @@ worksheet_write_formula(lxw_worksheet *self, } /* - * Write a formula with a numerical result to a cell in Excel. + * Internal shared function for various array formula functions. */ lxw_error -worksheet_write_array_formula_num(lxw_worksheet *self, - lxw_row_t first_row, - lxw_col_t first_col, - lxw_row_t last_row, - lxw_col_t last_col, - const char *formula, - lxw_format *format, double result) +_store_array_formula(lxw_worksheet *self, + lxw_row_t first_row, + lxw_col_t first_col, + lxw_row_t last_row, + lxw_col_t last_col, + const char *formula, lxw_format *format, double result, + uint8_t is_dynamic) { lxw_cell *cell; lxw_row_t tmp_row; @@ -6870,7 +7958,7 @@ worksheet_write_array_formula_num(lxw_worksheet *self, RETURN_ON_MEM_ERROR(range, LXW_ERROR_MEMORY_MALLOC_FAILED); if (first_row == last_row && first_col == last_col) - lxw_rowcol_to_cell(range, first_row, last_col); + lxw_rowcol_to_cell(range, first_row, first_col); else lxw_rowcol_to_range(range, first_row, first_col, last_row, last_col); @@ -6881,7 +7969,7 @@ worksheet_write_array_formula_num(lxw_worksheet *self, else formula_copy = lxw_strdup(formula + 1); else - formula_copy = lxw_strdup(formula); + formula_copy = lxw_strdup_formula(formula); /* Strip trailing "}" from formula. */ if (formula_copy[strlen(formula_copy) - 1] == '}') @@ -6889,12 +7977,15 @@ worksheet_write_array_formula_num(lxw_worksheet *self, /* Create a new array formula cell object. */ cell = _new_array_formula_cell(first_row, first_col, - formula_copy, range, format); + formula_copy, range, format, is_dynamic); cell->formula_result = result; _insert_cell(self, first_row, first_col, cell); + if (is_dynamic) + self->has_dynamic_arrays = LXW_TRUE; + /* Pad out the rest of the area with formatted zeroes. */ if (!self->optimize) { for (tmp_row = first_row; tmp_row <= last_row; tmp_row++) { @@ -6911,7 +8002,24 @@ worksheet_write_array_formula_num(lxw_worksheet *self, } /* - * Write an array formula with a default result to a cell in Excel . + * Write an array formula with a numerical result to a cell in Excel. + */ +lxw_error +worksheet_write_array_formula_num(lxw_worksheet *self, + lxw_row_t first_row, + lxw_col_t first_col, + lxw_row_t last_row, + lxw_col_t last_col, + const char *formula, + lxw_format *format, double result) +{ + return _store_array_formula(self, first_row, first_col, + last_row, last_col, formula, format, result, + LXW_FALSE); +} + +/* + * Write an array formula with a default result to a cell in Excel. */ lxw_error worksheet_write_array_formula(lxw_worksheet *self, @@ -6921,29 +8029,89 @@ worksheet_write_array_formula(lxw_worksheet *self, lxw_col_t last_col, const char *formula, lxw_format *format) { - return worksheet_write_array_formula_num(self, first_row, first_col, - last_row, last_col, formula, - format, 0); + return _store_array_formula(self, first_row, first_col, + last_row, last_col, formula, format, 0, + LXW_FALSE); } /* - * Write a blank cell with a format to a cell in Excel. + * Write a single cell dynamic array formula with a default result to a cell. */ lxw_error -worksheet_write_blank(lxw_worksheet *self, - lxw_row_t row_num, lxw_col_t col_num, - lxw_format *format) +worksheet_write_dynamic_formula(lxw_worksheet *self, + lxw_row_t row, + lxw_col_t col, + const char *formula, lxw_format *format) { - lxw_cell *cell; - lxw_error err; - - /* Blank cells without formatting are ignored by Excel. */ - if (!format) - return LXW_NO_ERROR; + return _store_array_formula(self, row, col, row, col, formula, format, 0, + LXW_TRUE); +} - err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); - if (err) - return err; +/* + * Write a single cell dynamic array formula with a numerical result to a cell. + */ +lxw_error +worksheet_write_dynamic_formula_num(lxw_worksheet *self, + lxw_row_t row, + lxw_col_t col, + const char *formula, + lxw_format *format, double result) +{ + return _store_array_formula(self, row, col, row, col, formula, format, + result, LXW_TRUE); +} + +/* + * Write a dynamic array formula with a numerical result to a cell in Excel. + */ +lxw_error +worksheet_write_dynamic_array_formula_num(lxw_worksheet *self, + lxw_row_t first_row, + lxw_col_t first_col, + lxw_row_t last_row, + lxw_col_t last_col, + const char *formula, + lxw_format *format, double result) +{ + return _store_array_formula(self, first_row, first_col, + last_row, last_col, formula, format, result, + LXW_TRUE); +} + +/* + * Write a dynamic array formula with a default result to a cell in Excel. + */ +lxw_error +worksheet_write_dynamic_array_formula(lxw_worksheet *self, + lxw_row_t first_row, + lxw_col_t first_col, + lxw_row_t last_row, + lxw_col_t last_col, + const char *formula, lxw_format *format) +{ + return _store_array_formula(self, first_row, first_col, + last_row, last_col, formula, format, 0, + LXW_TRUE); +} + +/* + * Write a blank cell with a format to a cell in Excel. + */ +lxw_error +worksheet_write_blank(lxw_worksheet *self, + lxw_row_t row_num, lxw_col_t col_num, + lxw_format *format) +{ + lxw_cell *cell; + lxw_error err; + + /* Blank cells without formatting are ignored by Excel. */ + if (!format) + return LXW_NO_ERROR; + + err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); + if (err) + return err; cell = _new_blank_cell(row_num, col_num, format); @@ -7000,6 +8168,32 @@ worksheet_write_datetime(lxw_worksheet *self, return LXW_NO_ERROR; } +/* + * Write a date and or time to a cell in Excel. + */ +lxw_error +worksheet_write_unixtime(lxw_worksheet *self, + lxw_row_t row_num, + lxw_col_t col_num, + int64_t unixtime, lxw_format *format) +{ + lxw_cell *cell; + double excel_date; + lxw_error err; + + err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); + if (err) + return err; + + excel_date = lxw_unixtime_to_excel_date_epoch(unixtime, LXW_EPOCH_1900); + + cell = _new_number_cell(row_num, col_num, excel_date, format); + + _insert_cell(self, row_num, col_num, cell); + + return LXW_NO_ERROR; +} + /* * Write a hyperlink/url to an Excel file. */ @@ -7228,7 +8422,7 @@ worksheet_write_rich_string(lxw_worksheet *self, uint8_t i; long file_size; char *rich_string = NULL; - char *string_copy = NULL; + const char *string_copy = NULL; lxw_styles *styles = NULL; lxw_format *default_format = NULL; lxw_rich_string_tuple *rich_string_tuple = NULL; @@ -7257,7 +8451,7 @@ worksheet_write_rich_string(lxw_worksheet *self, return err; /* Create a tmp file for the styles object. */ - tmpfile = lxw_tmpfile(self->tmpdir); + tmpfile = lxw_get_filehandle(&rich_string, NULL, self->tmpdir); if (!tmpfile) return LXW_ERROR_CREATING_TMPFILE; @@ -7293,34 +8487,37 @@ worksheet_write_rich_string(lxw_worksheet *self, lxw_styles_free(styles); lxw_format_free(default_format); - /* Flush the file and read the size to calculate the required memory. */ + /* Flush the file. */ fflush(tmpfile); - file_size = ftell(tmpfile); - - /* Allocate a buffer for the rich string xml data. */ - rich_string = calloc(file_size + 1, 1); - GOTO_LABEL_ON_MEM_ERROR(rich_string, mem_error); - /* Rewind the file and read the data into the memory buffer. */ - rewind(tmpfile); - if (fread(rich_string, file_size, 1, tmpfile) < 1) { - fclose(tmpfile); - free(rich_string); - return LXW_ERROR_READING_TMPFILE; + if (!rich_string) { + /* Read the size to calculate the required memory. */ + file_size = ftell(tmpfile); + /* Allocate a buffer for the rich string xml data. */ + rich_string = calloc(file_size + 1, 1); + GOTO_LABEL_ON_MEM_ERROR(rich_string, mem_error); + + /* Rewind the file and read the data into the memory buffer. */ + rewind(tmpfile); + if (fread((void *) rich_string, file_size, 1, tmpfile) < 1) { + fclose(tmpfile); + free((void *) rich_string); + return LXW_ERROR_READING_TMPFILE; + } } /* Close the temp file. */ fclose(tmpfile); if (lxw_utf8_strlen(rich_string) > LXW_STR_MAX) { - free(rich_string); + free((void *) rich_string); return LXW_ERROR_MAX_STRING_LENGTH_EXCEEDED; } if (!self->optimize) { /* Get the SST element and string id. */ sst_element = lxw_get_sst_index(self->sst, rich_string, LXW_TRUE); - free(rich_string); + free((void *) rich_string); if (!sst_element) return LXW_ERROR_SHARED_STRING_INDEX_NOT_FOUND; @@ -7333,7 +8530,7 @@ worksheet_write_rich_string(lxw_worksheet *self, /* Look for and escape control chars in the string. */ if (lxw_has_control_characters(rich_string)) { string_copy = lxw_escape_control_characters(rich_string); - free(rich_string); + free((void *) rich_string); } else { string_copy = rich_string; @@ -7469,7 +8666,7 @@ worksheet_set_column_opt(lxw_worksheet *self, /* Resize the col_options array if required. */ if (firstcol >= self->col_options_max) { - lxw_col_t col; + lxw_col_t col_tmp; lxw_col_t old_size = self->col_options_max; lxw_col_t new_size = _next_power_of_two(firstcol + 1); lxw_col_options **new_ptr = realloc(self->col_options, @@ -7477,8 +8674,8 @@ worksheet_set_column_opt(lxw_worksheet *self, sizeof(lxw_col_options *)); if (new_ptr) { - for (col = old_size; col < new_size; col++) - new_ptr[col] = NULL; + for (col_tmp = old_size; col_tmp < new_size; col_tmp++) + new_ptr[col_tmp] = NULL; self->col_options = new_ptr; self->col_options_max = new_size; @@ -7763,10 +8960,8 @@ worksheet_autofilter(lxw_worksheet *self, lxw_row_t first_row, lxw_row_t tmp_row; lxw_col_t tmp_col; lxw_error err; - - /* Excel doesn't allow a single cell to be merged */ - if (first_row == last_row && first_col == last_col) - return LXW_ERROR_PARAMETER_VALIDATION; + lxw_filter_rule_obj **filter_rules; + lxw_col_t num_filter_rules; /* Swap last row/col with first row/col as necessary */ if (first_row > last_row) { @@ -7785,13 +8980,392 @@ worksheet_autofilter(lxw_worksheet *self, lxw_row_t first_row, if (err) return err; + /* Create a array to hold filter rules. */ + self->autofilter.in_use = LXW_FALSE; + self->autofilter.has_rules = LXW_FALSE; + _free_filter_rules(self); + num_filter_rules = last_col - first_col + 1; + filter_rules = calloc(num_filter_rules, sizeof(lxw_filter_rule_obj *)); + RETURN_ON_MEM_ERROR(filter_rules, LXW_ERROR_MEMORY_MALLOC_FAILED); + self->autofilter.in_use = LXW_TRUE; self->autofilter.first_row = first_row; self->autofilter.first_col = first_col; self->autofilter.last_row = last_row; self->autofilter.last_col = last_col; + self->filter_rules = filter_rules; + self->num_filter_rules = num_filter_rules; + + return LXW_NO_ERROR; +} + +/* + * Set a autofilter rule for a filter column. + */ +lxw_error +worksheet_filter_column(lxw_worksheet *self, lxw_col_t col, + lxw_filter_rule *rule) +{ + lxw_filter_rule_obj *rule_obj; + uint16_t rule_index; + + if (!rule) { + LXW_WARN("worksheet_filter_column(): rule parameter cannot be NULL"); + return LXW_ERROR_PARAMETER_VALIDATION; + } + + if (self->autofilter.in_use == LXW_FALSE) { + LXW_WARN("worksheet_filter_column(): " + "Worksheet autofilter range hasn't been defined. " + "Use worksheet_autofilter() first."); + return LXW_ERROR_PARAMETER_VALIDATION; + } + + if (col < self->autofilter.first_col || col > self->autofilter.last_col) { + LXW_WARN_FORMAT3("worksheet_filter_column(): " + "Column '%d' is outside autofilter range " + "'%d <= col <= %d'.", col, + self->autofilter.first_col, + self->autofilter.last_col); + return LXW_ERROR_PARAMETER_VALIDATION; + } + + /* Free any previous rule in the column slot. */ + rule_index = col - self->autofilter.first_col; + _free_filter_rule(self->filter_rules[rule_index]); + + /* Create a new rule and copy user input. */ + rule_obj = calloc(1, sizeof(lxw_filter_rule_obj)); + RETURN_ON_MEM_ERROR(rule_obj, LXW_ERROR_MEMORY_MALLOC_FAILED); + + rule_obj->col_num = rule_index; + rule_obj->type = LXW_FILTER_TYPE_SINGLE; + rule_obj->criteria1 = rule->criteria; + rule_obj->value1 = rule->value; + + if (rule_obj->criteria1 != LXW_FILTER_CRITERIA_NON_BLANKS) { + rule_obj->value1_string = lxw_strdup(rule->value_string); + } + else { + rule_obj->criteria1 = LXW_FILTER_CRITERIA_NOT_EQUAL_TO; + rule_obj->value1_string = lxw_strdup(" "); + } + + if (rule_obj->criteria1 == LXW_FILTER_CRITERIA_BLANKS) + rule_obj->has_blanks = LXW_TRUE; + + _set_custom_filter(rule_obj); + + self->filter_rules[rule_index] = rule_obj; + self->filter_on = LXW_TRUE; + self->autofilter.has_rules = LXW_TRUE; + + return LXW_NO_ERROR; +} + +/* + * Set two autofilter rules for a filter column. + */ +lxw_error +worksheet_filter_column2(lxw_worksheet *self, lxw_col_t col, + lxw_filter_rule *rule1, lxw_filter_rule *rule2, + uint8_t and_or) +{ + lxw_filter_rule_obj *rule_obj; + uint16_t rule_index; + + if (!rule1 || !rule2) { + LXW_WARN("worksheet_filter_column2(): rule parameter cannot be NULL"); + return LXW_ERROR_PARAMETER_VALIDATION; + } + + if (self->autofilter.in_use == LXW_FALSE) { + LXW_WARN("worksheet_filter_column2(): " + "Worksheet autofilter range hasn't been defined. " + "Use worksheet_autofilter() first."); + return LXW_ERROR_PARAMETER_VALIDATION; + } + + if (col < self->autofilter.first_col || col > self->autofilter.last_col) { + LXW_WARN_FORMAT3("worksheet_filter_column2(): " + "Column '%d' is outside autofilter range " + "'%d <= col <= %d'.", col, + self->autofilter.first_col, + self->autofilter.last_col); + return LXW_ERROR_PARAMETER_VALIDATION; + } + + /* Free any previous rule in the column slot. */ + rule_index = col - self->autofilter.first_col; + _free_filter_rule(self->filter_rules[rule_index]); + + /* Create a new rule and copy user input. */ + rule_obj = calloc(1, sizeof(lxw_filter_rule_obj)); + RETURN_ON_MEM_ERROR(rule_obj, LXW_ERROR_MEMORY_MALLOC_FAILED); + + if (and_or == LXW_FILTER_AND) + rule_obj->type = LXW_FILTER_TYPE_AND; + else + rule_obj->type = LXW_FILTER_TYPE_OR; + + rule_obj->col_num = rule_index; + + rule_obj->criteria1 = rule1->criteria; + rule_obj->value1 = rule1->value; + + rule_obj->criteria2 = rule2->criteria; + rule_obj->value2 = rule2->value; + + if (rule_obj->criteria1 != LXW_FILTER_CRITERIA_NON_BLANKS) { + rule_obj->value1_string = lxw_strdup(rule1->value_string); + } + else { + rule_obj->criteria1 = LXW_FILTER_CRITERIA_NOT_EQUAL_TO; + rule_obj->value1_string = lxw_strdup(" "); + } + + if (rule_obj->criteria2 != LXW_FILTER_CRITERIA_NON_BLANKS) { + rule_obj->value2_string = lxw_strdup(rule2->value_string); + } + else { + rule_obj->criteria2 = LXW_FILTER_CRITERIA_NOT_EQUAL_TO; + rule_obj->value2_string = lxw_strdup(" "); + } + + if (rule_obj->criteria1 == LXW_FILTER_CRITERIA_BLANKS) + rule_obj->has_blanks = LXW_TRUE; + + if (rule_obj->criteria2 == LXW_FILTER_CRITERIA_BLANKS) + rule_obj->has_blanks = LXW_TRUE; + + _set_custom_filter(rule_obj); + + self->filter_rules[rule_index] = rule_obj; + self->filter_on = LXW_TRUE; + self->autofilter.has_rules = LXW_TRUE; + + return LXW_NO_ERROR; +} + +/* + * Set two autofilter rules for a filter column. + */ +lxw_error +worksheet_filter_list(lxw_worksheet *self, lxw_col_t col, const char **list) +{ + lxw_filter_rule_obj *rule_obj; + uint16_t rule_index; + uint8_t has_blanks = LXW_FALSE; + uint16_t num_filters = 0; + uint16_t input_list_index; + uint16_t rule_obj_list_index; + const char *str; + char **tmp_list; + + if (!list) { + LXW_WARN("worksheet_filter_list(): list parameter cannot be NULL"); + return LXW_ERROR_PARAMETER_VALIDATION; + } + + if (self->autofilter.in_use == LXW_FALSE) { + LXW_WARN("worksheet_filter_list(): " + "Worksheet autofilter range hasn't been defined. " + "Use worksheet_autofilter() first."); + return LXW_ERROR_PARAMETER_VALIDATION; + } + + if (col < self->autofilter.first_col || col > self->autofilter.last_col) { + LXW_WARN_FORMAT3("worksheet_filter_list(): " + "Column '%d' is outside autofilter range " + "'%d <= col <= %d'.", col, + self->autofilter.first_col, + self->autofilter.last_col); + return LXW_ERROR_PARAMETER_VALIDATION; + } + + /* Count the number of non "Blanks" strings in the input list. */ + input_list_index = 0; + while ((str = list[input_list_index]) != NULL) { + if (strncmp(str, "Blanks", 6) == 0) + has_blanks = LXW_TRUE; + else + num_filters++; + + input_list_index++; + } + + /* There should be at least one filter string. */ + if (num_filters == 0) { + LXW_WARN("worksheet_filter_list(): " + "list must have at least 1 non-blanks item."); + return LXW_ERROR_PARAMETER_VALIDATION; + } + + /* Free any previous rule in the column slot. */ + rule_index = col - self->autofilter.first_col; + _free_filter_rule(self->filter_rules[rule_index]); + + /* Create a new rule and copy user input. */ + rule_obj = calloc(1, sizeof(lxw_filter_rule_obj)); + RETURN_ON_MEM_ERROR(rule_obj, LXW_ERROR_MEMORY_MALLOC_FAILED); + + tmp_list = calloc(num_filters + 1, sizeof(char *)); + GOTO_LABEL_ON_MEM_ERROR(tmp_list, mem_error); + + /* Copy input list (without any "Blanks" command) to an internal list. */ + input_list_index = 0; + rule_obj_list_index = 0; + while ((str = list[input_list_index]) != NULL) { + if (strncmp(str, "Blanks", 6) != 0) { + tmp_list[rule_obj_list_index] = lxw_strdup(str); + rule_obj_list_index++; + } + + input_list_index++; + } + + rule_obj->list = tmp_list; + rule_obj->num_list_filters = num_filters; + rule_obj->is_custom = LXW_FALSE; + rule_obj->col_num = rule_index; + rule_obj->type = LXW_FILTER_TYPE_STRING_LIST; + rule_obj->has_blanks = has_blanks; + + self->filter_rules[rule_index] = rule_obj; + self->filter_on = LXW_TRUE; + self->autofilter.has_rules = LXW_TRUE; + return LXW_NO_ERROR; + +mem_error: + free(rule_obj); + return LXW_ERROR_MEMORY_MALLOC_FAILED; + +} + +/* + * Add an Excel table to the worksheet. + */ +lxw_error +worksheet_add_table(lxw_worksheet *self, lxw_row_t first_row, + lxw_col_t first_col, lxw_row_t last_row, + lxw_col_t last_col, lxw_table_options *user_options) +{ + lxw_row_t tmp_row; + lxw_col_t tmp_col; + lxw_col_t num_cols; + lxw_error err; + lxw_table_obj *table_obj; + lxw_table_column **columns; + + if (self->optimize) { + LXW_WARN_FORMAT("worksheet_add_table(): " + "worksheet tables aren't supported in " + "'constant_memory' mode"); + return LXW_ERROR_FEATURE_NOT_SUPPORTED; + } + + /* Swap last row/col with first row/col as necessary */ + if (first_row > last_row) { + tmp_row = last_row; + last_row = first_row; + first_row = tmp_row; + } + if (first_col > last_col) { + tmp_col = last_col; + last_col = first_col; + first_col = tmp_col; + } + + /* Check that column number is valid and store the max value */ + err = _check_dimensions(self, last_row, last_col, LXW_TRUE, LXW_TRUE); + if (err) + return err; + + num_cols = last_col - first_col + 1; + + /* Check that there are sufficient data rows. */ + err = _check_table_rows(first_row, last_row, user_options); + if (err) + return err; + + /* Check that the the table name is valid. */ + err = _check_table_name(user_options); + if (err) + return err; + + /* Create a table object to copy from the user options. */ + table_obj = calloc(1, sizeof(lxw_table_obj)); + RETURN_ON_MEM_ERROR(table_obj, LXW_ERROR_MEMORY_MALLOC_FAILED); + + columns = calloc(num_cols, sizeof(lxw_table_column *)); + GOTO_LABEL_ON_MEM_ERROR(columns, error); + + table_obj->columns = columns; + table_obj->num_cols = num_cols; + table_obj->first_row = first_row; + table_obj->first_col = first_col; + table_obj->last_row = last_row; + table_obj->last_col = last_col; + + err = _set_default_table_columns(table_obj); + if (err) + goto error; + + /* Create the table range. */ + lxw_rowcol_to_range(table_obj->sqref, + first_row, first_col, last_row, last_col); + lxw_rowcol_to_range(table_obj->filter_sqref, + first_row, first_col, last_row, last_col); + + /* Validate and copy user options to an internal object. */ + if (user_options) { + + _check_and_copy_table_style(table_obj, user_options); + + table_obj->total_row = user_options->total_row; + table_obj->last_column = user_options->last_column; + table_obj->first_column = user_options->first_column; + table_obj->no_autofilter = user_options->no_autofilter; + table_obj->no_header_row = user_options->no_header_row; + table_obj->no_banded_rows = user_options->no_banded_rows; + table_obj->banded_columns = user_options->banded_columns; + + if (user_options->no_header_row) + table_obj->no_autofilter = LXW_TRUE; + + if (user_options->columns) { + err = _set_custom_table_columns(table_obj, user_options); + if (err) + goto error; + } + + if (user_options->total_row) { + lxw_rowcol_to_range(table_obj->filter_sqref, + first_row, first_col, last_row - 1, last_col); + } + + if (user_options->name) { + table_obj->name = lxw_strdup(user_options->name); + if (!table_obj->name) { + err = LXW_ERROR_MEMORY_MALLOC_FAILED; + goto error; + } + } + } + + _write_table_column_data(self, table_obj); + + STAILQ_INSERT_TAIL(self->table_objs, table_obj, list_pointers); + self->table_count++; + + return LXW_NO_ERROR; + +error: + _free_worksheet_table(table_obj); + return err; + } /* @@ -7911,6 +9485,18 @@ worksheet_set_selection(lxw_worksheet *self, STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); } +/* + * Set the first visible cell at the top left of the worksheet. + */ +void +worksheet_set_top_left_cell(lxw_worksheet *self, lxw_row_t row, lxw_col_t col) +{ + if (row == 0 && col == 0) + return; + + lxw_rowcol_to_cell(self->top_left_cell, row, col); +} + /* * Set panes and mark them as frozen. With extra options. */ @@ -8048,25 +9634,30 @@ worksheet_set_header_opt(lxw_worksheet *self, const char *string, lxw_header_footer_options *options) { lxw_error err; + char *tmp_header; char *found_string; char *offset_string; uint8_t placeholder_count = 0; uint8_t image_count = 0; - if (!string) + if (!string) { + LXW_WARN_FORMAT("worksheet_set_header_opt/footer_opt(): " + "header/footer string cannot be NULL."); return LXW_ERROR_NULL_PARAMETER_IGNORED; + } - if (lxw_utf8_strlen(string) > LXW_HEADER_FOOTER_MAX) + if (lxw_utf8_strlen(string) > LXW_HEADER_FOOTER_MAX) { + LXW_WARN_FORMAT("worksheet_set_header_opt/footer_opt(): " + "header/footer string exceeds Excel's limit of " + "255 characters."); return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; + } - /* Clear existing header. */ - free(self->header); - - self->header = lxw_strdup(string); - RETURN_ON_MEM_ERROR(self->header, LXW_ERROR_MEMORY_MALLOC_FAILED); + tmp_header = lxw_strdup(string); + RETURN_ON_MEM_ERROR(tmp_header, LXW_ERROR_MEMORY_MALLOC_FAILED); /* Replace &[Picture] with &G which is used internally by Excel. */ - while ((found_string = strstr(self->header, "&[Picture]"))) { + while ((found_string = strstr(tmp_header, "&[Picture]"))) { found_string++; *found_string = 'G'; @@ -8078,7 +9669,7 @@ worksheet_set_header_opt(lxw_worksheet *self, const char *string, } /* Count &G placeholders and ensure there are sufficient images. */ - found_string = self->header; + found_string = tmp_header; while (*found_string) { if (*found_string == '&' && *(found_string + 1) == 'G') placeholder_count++; @@ -8091,12 +9682,14 @@ worksheet_set_header_opt(lxw_worksheet *self, const char *string, "string \"%s\" does not match the number of supplied " "images.", string); - /* Reset the header string. */ - free(self->header); - + free(tmp_header); return LXW_ERROR_PARAMETER_VALIDATION; } + /* Free any previous header string so we can overwrite it. */ + free(self->header); + self->header = NULL; + if (options) { /* Ensure there are enough images to match the placeholders. There is * a potential bug where there are sufficient images but in the wrong @@ -8114,9 +9707,7 @@ worksheet_set_header_opt(lxw_worksheet *self, const char *string, "string \"%s\" does not match the number of supplied " "images.", string); - /* Reset the header string. */ - free(self->header); - + free(tmp_header); return LXW_ERROR_PARAMETER_VALIDATION; } @@ -8132,7 +9723,7 @@ worksheet_set_header_opt(lxw_worksheet *self, const char *string, options->image_left, HEADER_LEFT); if (err) { - free(self->header); + free(tmp_header); return err; } @@ -8140,7 +9731,7 @@ worksheet_set_header_opt(lxw_worksheet *self, const char *string, options->image_center, HEADER_CENTER); if (err) { - free(self->header); + free(tmp_header); return err; } @@ -8148,12 +9739,13 @@ worksheet_set_header_opt(lxw_worksheet *self, const char *string, options->image_right, HEADER_RIGHT); if (err) { - free(self->header); + free(tmp_header); return err; } } - self->header_footer_changed = 1; + self->header = tmp_header; + self->header_footer_changed = LXW_TRUE; return LXW_NO_ERROR; } @@ -8166,25 +9758,30 @@ worksheet_set_footer_opt(lxw_worksheet *self, const char *string, lxw_header_footer_options *options) { lxw_error err; + char *tmp_footer; char *found_string; char *offset_string; uint8_t placeholder_count = 0; uint8_t image_count = 0; - if (!string) + if (!string) { + LXW_WARN_FORMAT("worksheet_set_header_opt/footer_opt(): " + "header/footer string cannot be NULL."); return LXW_ERROR_NULL_PARAMETER_IGNORED; + } - if (lxw_utf8_strlen(string) > LXW_HEADER_FOOTER_MAX) + if (lxw_utf8_strlen(string) > LXW_HEADER_FOOTER_MAX) { + LXW_WARN_FORMAT("worksheet_set_header_opt/footer_opt(): " + "header/footer string exceeds Excel's limit of " + "255 characters."); return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; + } - /* Clear existing header. */ - free(self->footer); - - self->footer = lxw_strdup(string); - RETURN_ON_MEM_ERROR(self->footer, LXW_ERROR_MEMORY_MALLOC_FAILED); + tmp_footer = lxw_strdup(string); + RETURN_ON_MEM_ERROR(tmp_footer, LXW_ERROR_MEMORY_MALLOC_FAILED); /* Replace &[Picture] with &G which is used internally by Excel. */ - while ((found_string = strstr(self->footer, "&[Picture]"))) { + while ((found_string = strstr(tmp_footer, "&[Picture]"))) { found_string++; *found_string = 'G'; @@ -8196,7 +9793,7 @@ worksheet_set_footer_opt(lxw_worksheet *self, const char *string, } /* Count &G placeholders and ensure there are sufficient images. */ - found_string = self->footer; + found_string = tmp_footer; while (*found_string) { if (*found_string == '&' && *(found_string + 1) == 'G') placeholder_count++; @@ -8209,12 +9806,14 @@ worksheet_set_footer_opt(lxw_worksheet *self, const char *string, "string \"%s\" does not match the number of supplied " "images.", string); - /* Reset the footer string. */ - free(self->footer); - + free(tmp_footer); return LXW_ERROR_PARAMETER_VALIDATION; } + /* Free any previous footer string so we can overwrite it. */ + free(self->footer); + self->footer = NULL; + if (options) { /* Ensure there are enough images to match the placeholders. There is * a potential bug where there are sufficient images but in the wrong @@ -8232,9 +9831,7 @@ worksheet_set_footer_opt(lxw_worksheet *self, const char *string, "string \"%s\" does not match the number of supplied " "images.", string); - /* Reset the header string. */ - free(self->footer); - + free(tmp_footer); return LXW_ERROR_PARAMETER_VALIDATION; } @@ -8250,7 +9847,7 @@ worksheet_set_footer_opt(lxw_worksheet *self, const char *string, options->image_left, FOOTER_LEFT); if (err) { - free(self->footer); + free(tmp_footer); return err; } @@ -8258,7 +9855,7 @@ worksheet_set_footer_opt(lxw_worksheet *self, const char *string, options->image_center, FOOTER_CENTER); if (err) { - free(self->footer); + free(tmp_footer); return err; } @@ -8266,12 +9863,13 @@ worksheet_set_footer_opt(lxw_worksheet *self, const char *string, options->image_right, FOOTER_RIGHT); if (err) { - free(self->footer); + free(tmp_footer); return err; } } - self->header_footer_changed = 1; + self->footer = tmp_footer; + self->header_footer_changed = LXW_TRUE; return LXW_NO_ERROR; } @@ -8480,6 +10078,16 @@ worksheet_set_print_scale(lxw_worksheet *self, uint16_t scale) self->page_setup_changed = LXW_TRUE; } +/* + * Set the print in black and white option. + */ +void +worksheet_print_black_and_white(lxw_worksheet *self) +{ + self->black_white = LXW_TRUE; + self->page_setup_changed = LXW_TRUE; +} + /* * Store the horizontal page breaks on a worksheet. */ @@ -8655,7 +10263,7 @@ worksheet_set_default_row(lxw_worksheet *self, double height, } /* - * Insert an image into the worksheet. + * Insert an image with options into the worksheet. */ lxw_error worksheet_insert_image_opt(lxw_worksheet *self, @@ -8664,7 +10272,7 @@ worksheet_insert_image_opt(lxw_worksheet *self, lxw_image_options *user_options) { FILE *image_stream; - char *description; + const char *description; lxw_object_properties *object_props; if (!filename) { @@ -8719,10 +10327,10 @@ worksheet_insert_image_opt(lxw_worksheet *self, object_props->row = row_num; object_props->col = col_num; - if (!object_props->x_scale) + if (object_props->x_scale == 0.0) object_props->x_scale = 1; - if (!object_props->y_scale) + if (object_props->y_scale == 0.0) object_props->y_scale = 1; if (_get_image_properties(object_props) == LXW_NO_ERROR) { @@ -8748,6 +10356,9 @@ worksheet_insert_image(lxw_worksheet *self, return worksheet_insert_image_opt(self, row_num, col_num, filename, NULL); } +/* + * Insert an image buffer, with options, into the worksheet. + */ lxw_error worksheet_insert_image_buffer_opt(lxw_worksheet *self, lxw_row_t row_num, @@ -8768,10 +10379,12 @@ worksheet_insert_image_buffer_opt(lxw_worksheet *self, /* Write the image buffer to a file (preferably in memory) so we can read * the dimensions like an ordinary file. */ #ifdef USE_FMEMOPEN - image_stream = fmemopen(NULL, image_size, "w+b"); + image_stream = fmemopen((void *) image_buffer, image_size, "rb"); + + if (!image_stream) + return LXW_ERROR_CREATING_TMPFILE; #else image_stream = lxw_tmpfile(self->tmpdir); -#endif if (!image_stream) return LXW_ERROR_CREATING_TMPFILE; @@ -8782,6 +10395,7 @@ worksheet_insert_image_buffer_opt(lxw_worksheet *self, } rewind(image_stream); +#endif /* Create a new object to hold the image properties. */ object_props = calloc(1, sizeof(lxw_object_properties)); @@ -8821,10 +10435,10 @@ worksheet_insert_image_buffer_opt(lxw_worksheet *self, object_props->row = row_num; object_props->col = col_num; - if (!object_props->x_scale) + if (object_props->x_scale == 0.0) object_props->x_scale = 1; - if (!object_props->y_scale) + if (object_props->y_scale == 0.0) object_props->y_scale = 1; if (_get_image_properties(object_props) == LXW_NO_ERROR) { @@ -8839,6 +10453,9 @@ worksheet_insert_image_buffer_opt(lxw_worksheet *self, } } +/* + * Insert an image buffer into the worksheet. + */ lxw_error worksheet_insert_image_buffer(lxw_worksheet *self, lxw_row_t row_num, @@ -8850,6 +10467,132 @@ worksheet_insert_image_buffer(lxw_worksheet *self, image_buffer, image_size, NULL); } +/* + * Set an image as a worksheet background. + */ +lxw_error +worksheet_set_background(lxw_worksheet *self, const char *filename) +{ + FILE *image_stream; + lxw_object_properties *object_props; + + if (!filename) { + LXW_WARN("worksheet_set_background(): " + "filename must be specified."); + return LXW_ERROR_NULL_PARAMETER_IGNORED; + } + + /* Check that the image file exists and can be opened. */ + image_stream = lxw_fopen(filename, "rb"); + if (!image_stream) { + LXW_WARN_FORMAT1("worksheet_set_background(): " + "file doesn't exist or can't be opened: %s.", + filename); + return LXW_ERROR_PARAMETER_VALIDATION; + } + + /* Create a new object to hold the image properties. */ + object_props = calloc(1, sizeof(lxw_object_properties)); + if (!object_props) { + fclose(image_stream); + return LXW_ERROR_MEMORY_MALLOC_FAILED; + } + + /* Copy other options or set defaults. */ + object_props->filename = lxw_strdup(filename); + object_props->stream = image_stream; + object_props->is_background = LXW_TRUE; + + if (_get_image_properties(object_props) == LXW_NO_ERROR) { + _free_object_properties(self->background_image); + self->background_image = object_props; + self->has_background_image = LXW_TRUE; + fclose(image_stream); + return LXW_NO_ERROR; + } + else { + _free_object_properties(object_props); + fclose(image_stream); + return LXW_ERROR_IMAGE_DIMENSIONS; + } +} + +/* + * Set an image buffer as a worksheet background. + */ +lxw_error +worksheet_set_background_buffer(lxw_worksheet *self, + const unsigned char *image_buffer, + size_t image_size) +{ + FILE *image_stream; + lxw_object_properties *object_props; + + if (!image_size) { + LXW_WARN("worksheet_set_background(): " "size must be non-zero."); + return LXW_ERROR_NULL_PARAMETER_IGNORED; + } + + /* Write the image buffer to a file (preferably in memory) so we can read + * the dimensions like an ordinary file. */ +#ifdef USE_FMEMOPEN + image_stream = fmemopen((void *) image_buffer, image_size, "rb"); + + if (!image_stream) + return LXW_ERROR_CREATING_TMPFILE; +#else + image_stream = lxw_tmpfile(self->tmpdir); + + if (!image_stream) + return LXW_ERROR_CREATING_TMPFILE; + + if (fwrite(image_buffer, 1, image_size, image_stream) != image_size) { + fclose(image_stream); + return LXW_ERROR_CREATING_TMPFILE; + } + + rewind(image_stream); +#endif + + /* Create a new object to hold the image properties. */ + object_props = calloc(1, sizeof(lxw_object_properties)); + if (!object_props) { + fclose(image_stream); + return LXW_ERROR_MEMORY_MALLOC_FAILED; + } + + /* Store the image data in the properties structure. */ + object_props->image_buffer = calloc(1, image_size); + if (!object_props->image_buffer) { + _free_object_properties(object_props); + fclose(image_stream); + return LXW_ERROR_MEMORY_MALLOC_FAILED; + } + else { + memcpy(object_props->image_buffer, image_buffer, image_size); + object_props->image_buffer_size = image_size; + object_props->is_image_buffer = LXW_TRUE; + } + + /* Copy other options or set defaults. */ + object_props->filename = lxw_strdup("image_buffer"); + object_props->stream = image_stream; + object_props->is_background = LXW_TRUE; + + if (_get_image_properties(object_props) == LXW_NO_ERROR) { + _free_object_properties(self->background_image); + self->background_image = object_props; + self->has_background_image = LXW_TRUE; + fclose(image_stream); + return LXW_NO_ERROR; + } + else { + _free_object_properties(object_props); + fclose(image_stream); + return LXW_ERROR_IMAGE_DIMENSIONS; + } +} + /* * Insert an chart into the worksheet. */ @@ -8902,6 +10645,8 @@ worksheet_insert_chart_opt(lxw_worksheet *self, object_props->x_scale = user_options->x_scale; object_props->y_scale = user_options->y_scale; object_props->object_position = user_options->object_position; + object_props->description = lxw_strdup(user_options->description); + object_props->decorative = user_options->decorative; } /* Copy other options or set defaults. */ @@ -8911,10 +10656,10 @@ worksheet_insert_chart_opt(lxw_worksheet *self, object_props->width = 480; object_props->height = 288; - if (!object_props->x_scale) + if (object_props->x_scale == 0.0) object_props->x_scale = 1; - if (!object_props->y_scale) + if (object_props->y_scale == 0.0) object_props->y_scale = 1; /* Store chart references so they can be ordered in the workbook. */ @@ -9109,7 +10854,7 @@ worksheet_data_validation_range(lxw_worksheet *self, lxw_row_t first_row, /* Create the data validation range. */ if (first_row == last_row && first_col == last_col) - lxw_rowcol_to_cell(copy->sqref, first_row, last_col); + lxw_rowcol_to_cell(copy->sqref, first_row, first_col); else lxw_rowcol_to_range(copy->sqref, first_row, first_col, last_row, last_col); @@ -9172,11 +10917,6 @@ worksheet_data_validation_range(lxw_worksheet *self, lxw_row_t first_row, GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error); } - if (validation->validate == LXW_VALIDATION_TYPE_LIST_FORMULA) { - copy->value_formula = lxw_strdup_formula(validation->value_formula); - GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error); - } - if (validation->validate == LXW_VALIDATION_TYPE_DATE || validation->validate == LXW_VALIDATION_TYPE_TIME) { if (is_between) { @@ -9289,13 +11029,13 @@ worksheet_conditional_format_range(lxw_worksheet *self, lxw_row_t first_row, /* Create the data validation range. */ if (first_row == last_row && first_col == last_col) - lxw_rowcol_to_cell(cond_format->sqref, first_row, last_col); + lxw_rowcol_to_cell(cond_format->sqref, first_row, first_col); else lxw_rowcol_to_range(cond_format->sqref, first_row, first_col, last_row, last_col); /* Store the first cell string for text and date rules. */ - lxw_rowcol_to_cell(cond_format->first_cell, first_row, last_col); + lxw_rowcol_to_cell(cond_format->first_cell, first_row, first_col); /* Overwrite the sqref range with a user supplied set of ranges. */ if (user_options->multi_range) { @@ -9417,6 +11157,49 @@ worksheet_conditional_format_cell(lxw_worksheet *self, row, col, options); } +/* + * Insert a button object into the worksheet. + */ +lxw_error +worksheet_insert_button(lxw_worksheet *self, lxw_row_t row_num, + lxw_col_t col_num, lxw_button_options *options) +{ + lxw_error err; + lxw_vml_obj *button; + + err = _check_dimensions(self, row_num, col_num, LXW_TRUE, LXW_TRUE); + if (err) + return err; + + button = calloc(1, sizeof(lxw_vml_obj)); + GOTO_LABEL_ON_MEM_ERROR(button, mem_error); + + button->row = row_num; + button->col = col_num; + + /* Set user and default parameters for the button. */ + err = _get_button_params(button, 1 + self->num_buttons, options); + if (err) + goto mem_error; + + /* Calculate the worksheet position of the button. */ + _worksheet_position_vml_object(self, button); + + self->has_vml = LXW_TRUE; + self->has_buttons = LXW_TRUE; + self->num_buttons++; + + STAILQ_INSERT_TAIL(self->button_objs, button, list_pointers); + + return LXW_NO_ERROR; + +mem_error: + if (button) + _free_vml_object(button); + + return LXW_ERROR_MEMORY_MALLOC_FAILED; +} + /* * Set the VBA name for the worksheet. */ diff --git a/src/libxlsxwriter/xmlwriter.c b/src/libxlsxwriter/xmlwriter.c index 12bb8c0..59d49e0 100644 --- a/src/libxlsxwriter/xmlwriter.c +++ b/src/libxlsxwriter/xmlwriter.c @@ -3,7 +3,7 @@ * * Used in conjunction with the libxlsxwriter library. * - * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ @@ -17,6 +17,7 @@ #define LXW_LT "<" #define LXW_GT ">" #define LXW_QUOT """ +#define LXW_NL " " /* Defines. */ #define LXW_MAX_ENCODED_ATTRIBUTE_LENGTH (LXW_MAX_ATTRIBUTE_LENGTH*6) @@ -178,6 +179,10 @@ _escape_attributes(struct xml_attribute *attribute) memcpy(p_encoded, LXW_QUOT, sizeof(LXW_QUOT) - 1); p_encoded += sizeof(LXW_QUOT) - 1; break; + case '\n': + memcpy(p_encoded, LXW_NL, sizeof(LXW_NL) - 1); + p_encoded += sizeof(LXW_NL) - 1; + break; default: *p_encoded = *p_attr; p_encoded++; @@ -316,20 +321,20 @@ lxw_escape_url_characters(const char *string, uint8_t escape_hash) while (*string) { switch (*string) { - case (' '): - case ('"'): - case ('<'): - case ('>'): - case ('['): - case (']'): - case ('`'): - case ('^'): - case ('{'): - case ('}'): + case ' ': + case '"': + case '<': + case '>': + case '[': + case ']': + case '`': + case '^': + case '{': + case '}': lxw_snprintf(p_encoded, escape_len + 1, "%%%2x", *string); p_encoded += escape_len; break; - case ('#'): + case '#': /* This is only escaped for "external:" style links. */ if (escape_hash) { lxw_snprintf(p_encoded, escape_len + 1, "%%%2x", *string); @@ -340,7 +345,7 @@ lxw_escape_url_characters(const char *string, uint8_t escape_hash) p_encoded++; } break; - case ('%'): + case '%': /* Only escape % if it isn't already an escape. */ if (!isxdigit(*(string + 1)) || !isxdigit(*(string + 2))) { lxw_snprintf(p_encoded, escape_len + 1, "%%%2x", *string); @@ -373,7 +378,7 @@ _fprint_escaped_attributes(FILE * xmlfile, STAILQ_FOREACH(attribute, attributes, list_entries) { fprintf(xmlfile, " %s=", attribute->key); - if (!strpbrk(attribute->value, "&<>\"")) { + if (!strpbrk(attribute->value, "&<>\"\n")) { fprintf(xmlfile, "\"%s\"", attribute->value); } else { @@ -420,12 +425,18 @@ lxw_new_attribute_str(const char *key, const char *value) /* Create a new integer XML attribute. */ struct xml_attribute * -lxw_new_attribute_int(const char *key, uint32_t value) +lxw_new_attribute_int(const char *key, uint64_t value) { struct xml_attribute *attribute = malloc(sizeof(struct xml_attribute)); LXW_ATTRIBUTE_COPY(attribute->key, key); - lxw_snprintf(attribute->value, LXW_MAX_ATTRIBUTE_LENGTH, "%d", value); + +#if defined(_MSC_VER) + lxw_snprintf(attribute->value, LXW_MAX_ATTRIBUTE_LENGTH, "%lld", value); +#else + lxw_snprintf(attribute->value, LXW_MAX_ATTRIBUTE_LENGTH, "%ld", + (long) value); +#endif return attribute; } diff --git a/src/md5/md5.c b/src/md5/md5.c index 73f1914..b235e17 100644 --- a/src/md5/md5.c +++ b/src/md5/md5.c @@ -79,16 +79,16 @@ */ #if defined(__i386__) || defined(__x86_64__) || defined(__vax__) #define SET(n) \ - (*(uint32_t *)&ptr[(n) * 4]) + (*(MD5_u32plus *)&ptr[(n) * 4]) #define GET(n) \ SET(n) #else #define SET(n) \ (ctx->block[(n)] = \ - (uint32_t)ptr[(n) * 4] | \ - ((uint32_t)ptr[(n) * 4 + 1] << 8) | \ - ((uint32_t)ptr[(n) * 4 + 2] << 16) | \ - ((uint32_t)ptr[(n) * 4 + 3] << 24)) + (MD5_u32plus)ptr[(n) * 4] | \ + ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ + ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ + ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) #define GET(n) \ (ctx->block[(n)]) #endif @@ -97,11 +97,11 @@ * This processes one or more 64-byte data blocks, but does NOT update the bit * counters. There are no alignment requirements. */ -static const void *body(lxw_md5_ctx *ctx, const void *data, unsigned long size) +static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) { const unsigned char *ptr; - uint32_t a, b, c, d; - uint32_t saved_a, saved_b, saved_c, saved_d; + MD5_u32plus a, b, c, d; + MD5_u32plus saved_a, saved_b, saved_c, saved_d; ptr = (const unsigned char *)data; @@ -204,7 +204,7 @@ static const void *body(lxw_md5_ctx *ctx, const void *data, unsigned long size) return ptr; } -void lxw_md5_init(lxw_md5_ctx *ctx) +void MD5_Init(MD5_CTX *ctx) { ctx->a = 0x67452301; ctx->b = 0xefcdab89; @@ -215,9 +215,9 @@ void lxw_md5_init(lxw_md5_ctx *ctx) ctx->hi = 0; } -void lxw_md5_update(lxw_md5_ctx *ctx, const void *data, unsigned long size) +void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) { - uint32_t saved_lo; + MD5_u32plus saved_lo; unsigned long used, available; saved_lo = ctx->lo; @@ -255,7 +255,7 @@ void lxw_md5_update(lxw_md5_ctx *ctx, const void *data, unsigned long size) (dst)[2] = (unsigned char)((src) >> 16); \ (dst)[3] = (unsigned char)((src) >> 24); -void lxw_md5_final(unsigned char *result, lxw_md5_ctx *ctx) +void MD5_Final(unsigned char *result, MD5_CTX *ctx) { unsigned long used, available; diff --git a/src/md5/md5.h b/src/md5/md5.h index 8d0844f..2da44bf 100644 --- a/src/md5/md5.h +++ b/src/md5/md5.h @@ -23,21 +23,23 @@ * See md5.c for more information. */ -#ifndef __LXW_MD5_H__ -#define __LXW_MD5_H__ +#ifdef HAVE_OPENSSL +#include +#elif !defined(_MD5_H) +#define _MD5_H /* Any 32-bit or wider unsigned integer data type will do */ -typedef unsigned int uint32_t; +typedef unsigned int MD5_u32plus; typedef struct { - uint32_t lo, hi; - uint32_t a, b, c, d; + MD5_u32plus lo, hi; + MD5_u32plus a, b, c, d; unsigned char buffer[64]; - uint32_t block[16]; -} lxw_md5_ctx; + MD5_u32plus block[16]; +} MD5_CTX; -extern void lxw_md5_init(lxw_md5_ctx *ctx); -extern void lxw_md5_update(lxw_md5_ctx *ctx, const void *data, unsigned long size); -extern void lxw_md5_final(unsigned char *result, lxw_md5_ctx *ctx); +extern void MD5_Init(MD5_CTX *ctx); +extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); +extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); #endif diff --git a/src/minizip/ioapi.c b/src/minizip/ioapi.c index 07c4866..782d324 100644 --- a/src/minizip/ioapi.c +++ b/src/minizip/ioapi.c @@ -14,7 +14,7 @@ #define _CRT_SECURE_NO_WARNINGS #endif -#if defined(__APPLE__) || defined(IOAPI_NO_64) +#if defined(__APPLE__) || defined(IOAPI_NO_64) || defined(__HAIKU__) || defined(MINIZIP_FOPEN_NO_64) // In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions #define FOPEN_FUNC(filename, mode) fopen(filename, mode) #define FTELLO_FUNC(stream) ftello(stream) @@ -28,8 +28,7 @@ #include "ioapi.h" -voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode) -{ +voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc, const void*filename, int mode) { if (pfilefunc->zfile_func64.zopen64_file != NULL) return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,filename,mode); else @@ -38,8 +37,7 @@ voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename } } -long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin) -{ +long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin) { if (pfilefunc->zfile_func64.zseek64_file != NULL) return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin); else @@ -52,13 +50,12 @@ long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZP } } -ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream) -{ +ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc, voidpf filestream) { if (pfilefunc->zfile_func64.zseek64_file != NULL) return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream); else { - uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream); + uLong tell_uLong = (uLong)(*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream); if ((tell_uLong) == MAXU32) return (ZPOS64_T)-1; else @@ -66,11 +63,9 @@ ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream } } -void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32) -{ +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32, const zlib_filefunc_def* p_filefunc32) { p_filefunc64_32->zfile_func64.zopen64_file = NULL; p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file; - p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file; p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file; p_filefunc64_32->zfile_func64.ztell64_file = NULL; @@ -84,19 +79,10 @@ void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filef -static voidpf ZCALLBACK fopen_file_func OF((voidpf opaque, const char* filename, int mode)); -static uLong ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); -static uLong ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size)); -static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream)); -static long ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); -static int ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream)); -static int ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream)); - -static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode) -{ +static voidpf ZCALLBACK fopen_file_func(voidpf opaque, const char* filename, int mode) { FILE* file = NULL; const char* mode_fopen = NULL; - (void) opaque; + (void)opaque; if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) mode_fopen = "rb"; else @@ -111,11 +97,10 @@ static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, in return file; } -static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, int mode) -{ +static voidpf ZCALLBACK fopen64_file_func(voidpf opaque, const void* filename, int mode) { FILE* file = NULL; const char* mode_fopen = NULL; - (void) opaque; + (void)opaque; if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) mode_fopen = "rb"; else @@ -131,44 +116,39 @@ static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, } -static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size) -{ +static uLong ZCALLBACK fread_file_func(voidpf opaque, voidpf stream, void* buf, uLong size) { uLong ret; - (void) opaque; + (void)opaque; ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); return ret; } -static uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size) -{ +static uLong ZCALLBACK fwrite_file_func(voidpf opaque, voidpf stream, const void* buf, uLong size) { uLong ret; - (void) opaque; + (void)opaque; ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); return ret; } -static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream) -{ +static long ZCALLBACK ftell_file_func(voidpf opaque, voidpf stream) { long ret; - (void) opaque; + (void)opaque; ret = ftell((FILE *)stream); return ret; } -static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream) -{ +static ZPOS64_T ZCALLBACK ftell64_file_func(voidpf opaque, voidpf stream) { ZPOS64_T ret; - (void) opaque; - ret = FTELLO_FUNC((FILE *)stream); + (void)opaque; + ret = (ZPOS64_T)FTELLO_FUNC((FILE *)stream); return ret; } -static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offset, int origin) -{ +static long ZCALLBACK fseek_file_func(voidpf opaque, voidpf stream, uLong offset, int origin) { int fseek_origin=0; long ret; - (void) opaque; + (void)opaque; switch (origin) { case ZLIB_FILEFUNC_SEEK_CUR : @@ -183,16 +163,15 @@ static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offs default: return -1; } ret = 0; - if (fseek((FILE *)stream, offset, fseek_origin) != 0) + if (fseek((FILE *)stream, (long)offset, fseek_origin) != 0) ret = -1; return ret; } -static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) -{ +static long ZCALLBACK fseek64_file_func(voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) { int fseek_origin=0; long ret; - (void) opaque; + (void)opaque; switch (origin) { case ZLIB_FILEFUNC_SEEK_CUR : @@ -208,30 +187,28 @@ static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T } ret = 0; - if(FSEEKO_FUNC((FILE *)stream, offset, fseek_origin) != 0) + if(FSEEKO_FUNC((FILE *)stream, (z_off64_t)offset, fseek_origin) != 0) ret = -1; return ret; } -static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream) -{ +static int ZCALLBACK fclose_file_func(voidpf opaque, voidpf stream) { int ret; - (void) opaque; + (void)opaque; ret = fclose((FILE *)stream); return ret; } -static int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream) -{ +static int ZCALLBACK ferror_file_func(voidpf opaque, voidpf stream) { int ret; - (void) opaque; + (void)opaque; ret = ferror((FILE *)stream); return ret; } -void fill_fopen_filefunc (zlib_filefunc_def* pzlib_filefunc_def) { +void fill_fopen_filefunc(zlib_filefunc_def* pzlib_filefunc_def) { pzlib_filefunc_def->zopen_file = fopen_file_func; pzlib_filefunc_def->zread_file = fread_file_func; pzlib_filefunc_def->zwrite_file = fwrite_file_func; @@ -242,8 +219,7 @@ void fill_fopen_filefunc (zlib_filefunc_def* pzlib_filefunc_def) { pzlib_filefunc_def->opaque = NULL; } -void fill_fopen64_filefunc (zlib_filefunc64_def* pzlib_filefunc_def) -{ +void fill_fopen64_filefunc(zlib_filefunc64_def* pzlib_filefunc_def) { pzlib_filefunc_def->zopen64_file = fopen64_file_func; pzlib_filefunc_def->zread_file = fread_file_func; pzlib_filefunc_def->zwrite_file = fwrite_file_func; diff --git a/src/minizip/ioapi.h b/src/minizip/ioapi.h index 623c9bb..c79aa1e 100644 --- a/src/minizip/ioapi.h +++ b/src/minizip/ioapi.h @@ -56,7 +56,7 @@ #define ftello64 ftell #define fseeko64 fseek #else -#if defined(__FreeBSD__) || defined(__OpenBSD__) +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) || defined(MINIZIP_FOPEN_NO_64) #define fopen64 fopen #define ftello64 ftello #define fseeko64 fseeko @@ -88,7 +88,7 @@ #include "mz64conf.h" #endif -/* a type choosen by DEFINE */ +/* a type chosen by DEFINE */ #ifdef HAVE_64BIT_INT_CUSTOM typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; #else @@ -97,8 +97,7 @@ typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; typedef uint64_t ZPOS64_T; #else -/* Maximum unsigned 32-bit value used as placeholder for zip64 */ -#define MAXU32 0xffffffff + #if defined(_MSC_VER) || defined(__BORLANDC__) typedef unsigned __int64 ZPOS64_T; @@ -108,7 +107,10 @@ typedef unsigned long long int ZPOS64_T; #endif #endif - +/* Maximum unsigned 32-bit value used as placeholder for zip64 */ +#ifndef MAXU32 +#define MAXU32 (0xffffffff) +#endif #ifdef __cplusplus extern "C" { @@ -137,22 +139,18 @@ extern "C" { -/* Workaround for modified zconf.h on Gentoo system. */ -#if defined(_Z_OF) -#define OF _Z_OF -#endif -typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); -typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); -typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); -typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); -typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); +typedef voidpf (ZCALLBACK *open_file_func) (voidpf opaque, const char* filename, int mode); +typedef uLong (ZCALLBACK *read_file_func) (voidpf opaque, voidpf stream, void* buf, uLong size); +typedef uLong (ZCALLBACK *write_file_func) (voidpf opaque, voidpf stream, const void* buf, uLong size); +typedef int (ZCALLBACK *close_file_func) (voidpf opaque, voidpf stream); +typedef int (ZCALLBACK *testerror_file_func) (voidpf opaque, voidpf stream); -typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); -typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); +typedef long (ZCALLBACK *tell_file_func) (voidpf opaque, voidpf stream); +typedef long (ZCALLBACK *seek_file_func) (voidpf opaque, voidpf stream, uLong offset, int origin); -/* here is the "old" 32 bits structure structure */ +/* here is the "old" 32 bits structure */ typedef struct zlib_filefunc_def_s { open_file_func zopen_file; @@ -165,9 +163,9 @@ typedef struct zlib_filefunc_def_s voidpf opaque; } zlib_filefunc_def; -typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream)); -typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); -typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode)); +typedef ZPOS64_T (ZCALLBACK *tell64_file_func) (voidpf opaque, voidpf stream); +typedef long (ZCALLBACK *seek64_file_func) (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin); +typedef voidpf (ZCALLBACK *open64_file_func) (voidpf opaque, const void* filename, int mode); typedef struct zlib_filefunc64_def_s { @@ -181,8 +179,8 @@ typedef struct zlib_filefunc64_def_s voidpf opaque; } zlib_filefunc64_def; -void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def)); -void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); +void fill_fopen64_filefunc(zlib_filefunc64_def* pzlib_filefunc_def); +void fill_fopen_filefunc(zlib_filefunc_def* pzlib_filefunc_def); /* now internal definition, only for zip.c and unzip.h */ typedef struct zlib_filefunc64_32_def_s @@ -196,16 +194,16 @@ typedef struct zlib_filefunc64_32_def_s #define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) #define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) -/* #define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) */ -/* #define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) */ +//#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) +//#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) #define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) #define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) -voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)); -long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)); -ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)); +voidpf call_zopen64(const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode); +long call_zseek64(const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin); +ZPOS64_T call_ztell64(const zlib_filefunc64_32_def* pfilefunc,voidpf filestream); -void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); #define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) #define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) diff --git a/src/minizip/zip.c b/src/minizip/zip.c index 555bd98..0720586 100644 --- a/src/minizip/zip.c +++ b/src/minizip/zip.c @@ -14,8 +14,8 @@ Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file archives Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of some functions. Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data - It is used when recreting zip archive with RAW when deleting items from a zip. - ZIP64 data is automaticly added to items that needs it, and existing ZIP64 data need to be removed. + It is used when recreating zip archive with RAW when deleting items from a zip. + ZIP64 data is automatically added to items that needs it, and existing ZIP64 data need to be removed. Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib is required) Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer @@ -25,14 +25,13 @@ #include #include #include +#include #include #include "zlib.h" #include "zip.h" #ifdef STDC # include -# include -# include #endif #ifdef NO_ERRNO_H extern int errno; @@ -47,7 +46,7 @@ /* compile with -Dlocal if your debugger can't find static symbols */ #ifndef VERSIONMADEBY -# define VERSIONMADEBY (0x0) /* platform depedent */ +# define VERSIONMADEBY (0x0) /* platform dependent */ #endif #ifndef Z_BUFSIZE @@ -61,9 +60,6 @@ #ifndef ALLOC # define ALLOC(size) (malloc(size)) #endif -#ifndef TRYFREE -# define TRYFREE(p) {if (p) free(p);} -#endif /* #define SIZECENTRALDIRITEM (0x2e) @@ -116,7 +112,7 @@ typedef struct linkedlist_datablock_internal_s struct linkedlist_datablock_internal_s* next_datablock; uLong avail_in_this_block; uLong filled_in_this_block; - uLong unused; /* for future use and alignement */ + uLong unused; /* for future use and alignment */ unsigned char data[SIZEDATA_INDATABLOCK]; } linkedlist_datablock_internal; @@ -138,40 +134,40 @@ typedef struct uInt pos_in_buffered_data; /* last written byte in buffered_data */ ZPOS64_T pos_local_header; /* offset of the local header of the file - currenty writing */ + currently writing */ char* central_header; /* central header data for the current file */ uLong size_centralExtra; uLong size_centralheader; /* size of the central header for cur file */ uLong size_centralExtraFree; /* Extra bytes allocated to the centralheader but that are not used */ uLong flag; /* flag of the file currently writing */ - int method; /* compression method of file currenty wr.*/ + int method; /* compression method of file currently wr.*/ int raw; /* 1 for directly writing raw data */ Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ uLong dosDate; uLong crc32; int encrypt; - int zip64; /* Add ZIP64 extened information in the extra field */ + int zip64; /* Add ZIP64 extended information in the extra field */ ZPOS64_T pos_zip64extrainfo; ZPOS64_T totalCompressedData; ZPOS64_T totalUncompressedData; #ifndef NOCRYPT unsigned long keys[3]; /* keys defining the pseudo-random sequence */ const z_crc_t* pcrc_32_tab; - int crypt_header_size; + unsigned crypt_header_size; #endif } curfile64_info; typedef struct { zlib_filefunc64_32_def z_filefunc; - voidpf filestream; /* io structore of the zipfile */ + voidpf filestream; /* io structure of the zipfile */ linkedlist_data central_dir;/* datablock with central dir in construction*/ int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ - curfile64_info ci; /* info on the file curretly writing */ + curfile64_info ci; /* info on the file currently writing */ ZPOS64_T begin_pos; /* position of the beginning of the zipfile */ - ZPOS64_T add_position_when_writting_offset; + ZPOS64_T add_position_when_writing_offset; ZPOS64_T number_entry; #ifndef NO_ADDFILEINEXISTINGZIP @@ -186,8 +182,7 @@ typedef struct #include "crypt.h" #endif -local linkedlist_datablock_internal* allocate_new_datablock(void) -{ +local linkedlist_datablock_internal* allocate_new_datablock(void) { linkedlist_datablock_internal* ldi; ldi = (linkedlist_datablock_internal*) ALLOC(sizeof(linkedlist_datablock_internal)); @@ -200,30 +195,26 @@ local linkedlist_datablock_internal* allocate_new_datablock(void) return ldi; } -local void free_datablock(linkedlist_datablock_internal* ldi) -{ +local void free_datablock(linkedlist_datablock_internal* ldi) { while (ldi!=NULL) { linkedlist_datablock_internal* ldinext = ldi->next_datablock; - TRYFREE(ldi); + free(ldi); ldi = ldinext; } } -local void init_linkedlist(linkedlist_data* ll) -{ +local void init_linkedlist(linkedlist_data* ll) { ll->first_block = ll->last_block = NULL; } -local void free_linkedlist(linkedlist_data* ll) -{ +local void free_linkedlist(linkedlist_data* ll) { free_datablock(ll->first_block); ll->first_block = ll->last_block = NULL; } -local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) -{ +local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) { linkedlist_datablock_internal* ldi; const unsigned char* from_copy; @@ -238,7 +229,7 @@ local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) } ldi = ll->last_block; - from_copy = (unsigned char*)buf; + from_copy = (const unsigned char*)buf; while (len>0) { @@ -283,9 +274,7 @@ local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) nbByte == 1, 2 ,4 or 8 (byte, short or long, ZPOS64_T) */ -local int zip64local_putValue OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte)); -local int zip64local_putValue (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte) -{ +local int zip64local_putValue(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte) { unsigned char buf[8]; int n; for (n = 0; n < nbByte; n++) @@ -301,15 +290,13 @@ local int zip64local_putValue (const zlib_filefunc64_32_def* pzlib_filefunc_def, } } - if (ZWRITE64(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte) + if (ZWRITE64(*pzlib_filefunc_def,filestream,buf,(uLong)nbByte)!=(uLong)nbByte) return ZIP_ERRNO; else return ZIP_OK; } -local void zip64local_putValue_inmemory OF((void* dest, ZPOS64_T x, int nbByte)); -local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte) -{ +local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte) { unsigned char* buf=(unsigned char*)dest; int n; for (n = 0; n < nbByte; n++) { @@ -329,25 +316,21 @@ local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte) /****************************************************************************/ -local uLong zip64local_TmzDateToDosDate(const tm_zip* ptm) -{ +local uLong zip64local_TmzDateToDosDate(const tm_zip* ptm) { uLong year = (uLong)ptm->tm_year; if (year>=1980) year-=1980; else if (year>=80) year-=80; return - (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | - ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); + (uLong) (((uLong)(ptm->tm_mday) + (32 * (uLong)(ptm->tm_mon+1)) + (512 * year)) << 16) | + (((uLong)ptm->tm_sec/2) + (32 * (uLong)ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); } /****************************************************************************/ -local int zip64local_getByte OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi)); - -local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def,voidpf filestream,int* pi) -{ +local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int* pi) { unsigned char c; int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); if (err==1) @@ -368,10 +351,7 @@ local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def,vo /* =========================================================================== Reads a long in LSB order from the given gz_stream. Sets */ -local int zip64local_getShort OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); - -local int zip64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) -{ +local int zip64local_getShort(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) { uLong x ; int i = 0; int err; @@ -390,10 +370,7 @@ local int zip64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, return err; } -local int zip64local_getLong OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); - -local int zip64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) -{ +local int zip64local_getLong(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) { uLong x ; int i = 0; int err; @@ -420,11 +397,8 @@ local int zip64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, return err; } -local int zip64local_getLong64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX)); - -local int zip64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) -{ +local int zip64local_getLong64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) { ZPOS64_T x; int i = 0; int err; @@ -475,10 +449,7 @@ local int zip64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def Locate the Central directory of a zipfile (at the end, just before the global comment) */ -local ZPOS64_T zip64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); - -local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) -{ +local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) { unsigned char* buf; ZPOS64_T uSizeFile; ZPOS64_T uBackRead; @@ -519,19 +490,17 @@ local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_f break; for (i=(int)uReadSize-3; (i--)>0;) - { if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) { - uPosFound = uReadPos+i; + uPosFound = uReadPos+(unsigned)i; break; } - } - if (uPosFound!=0) - break; + if (uPosFound!=0) + break; } - TRYFREE(buf); + free(buf); return uPosFound; } @@ -539,10 +508,7 @@ local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_f Locate the End of Zip64 Central directory locator and from there find the CD of a zipfile (at the end, just before the global comment) */ -local ZPOS64_T zip64local_SearchCentralDir64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); - -local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) -{ +local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) { unsigned char* buf; ZPOS64_T uSizeFile; ZPOS64_T uBackRead; @@ -588,7 +554,7 @@ local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib // Signature "0x07064b50" Zip64 end of central directory locater if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) { - uPosFound = uReadPos+i; + uPosFound = uReadPos+(unsigned)i; break; } } @@ -597,7 +563,7 @@ local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib break; } - TRYFREE(buf); + free(buf); if (uPosFound == 0) return 0; @@ -609,7 +575,7 @@ local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) return 0; - /* number of the disk with the start of the zip64 end of central directory */ + /* number of the disk with the start of the zip64 end of central directory */ if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) return 0; if (uL != 0) @@ -639,8 +605,7 @@ local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib return relativeOffset; } -int LoadCentralDirectoryRecord(zip64_internal* pziinit) -{ +local int LoadCentralDirectoryRecord(zip64_internal* pziinit) { int err=ZIP_OK; ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ @@ -649,10 +614,10 @@ int LoadCentralDirectoryRecord(zip64_internal* pziinit) ZPOS64_T central_pos; uLong uL; - uLong number_disk; /* number of the current dist, used for - spaning ZIP, unsupported, always 0*/ - uLong number_disk_with_CD; /* number the the disk with central dir, used - for spaning ZIP, unsupported, always 0*/ + uLong number_disk; /* number of the current disk, used for + spanning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number of the disk with central dir, used + for spanning ZIP, unsupported, always 0*/ ZPOS64_T number_entry; ZPOS64_T number_entry_CD; /* total number of entries in the central dir @@ -809,7 +774,7 @@ int LoadCentralDirectoryRecord(zip64_internal* pziinit) } byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir); - pziinit->add_position_when_writting_offset = byte_before_the_zipfile; + pziinit->add_position_when_writing_offset = byte_before_the_zipfile; { ZPOS64_T size_central_dir_to_read = size_central_dir; @@ -832,7 +797,7 @@ int LoadCentralDirectoryRecord(zip64_internal* pziinit) size_central_dir_to_read-=read_this; } - TRYFREE(buf_read); + free(buf_read); } pziinit->begin_pos = byte_before_the_zipfile; pziinit->number_entry = number_entry_CD; @@ -848,8 +813,7 @@ int LoadCentralDirectoryRecord(zip64_internal* pziinit) /************************************************************/ -extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def) -{ +extern zipFile ZEXPORT zipOpen3(const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def) { zip64_internal ziinit; zip64_internal* zi; int err=ZIP_OK; @@ -877,7 +841,7 @@ extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* gl ziinit.in_opened_file_inzip = 0; ziinit.ci.stream_initialised = 0; ziinit.number_entry = 0; - ziinit.add_position_when_writting_offset = 0; + ziinit.add_position_when_writing_offset = 0; init_linkedlist(&(ziinit.central_dir)); @@ -907,9 +871,9 @@ extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* gl if (err != ZIP_OK) { # ifndef NO_ADDFILEINEXISTINGZIP - TRYFREE(ziinit.globalcomment); + free(ziinit.globalcomment); # endif /* !NO_ADDFILEINEXISTINGZIP*/ - TRYFREE(zi); + free(zi); return NULL; } else @@ -919,8 +883,7 @@ extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* gl } } -extern zipFile ZEXPORT zipOpen2 (const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc32_def) -{ +extern zipFile ZEXPORT zipOpen2(const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc32_def) { if (pzlib_filefunc32_def != NULL) { zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; @@ -931,8 +894,7 @@ extern zipFile ZEXPORT zipOpen2 (const char *pathname, int append, zipcharpc* gl return zipOpen3(pathname, append, globalcomment, NULL); } -extern zipFile ZEXPORT zipOpen2_64 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def) -{ +extern zipFile ZEXPORT zipOpen2_64(const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def) { if (pzlib_filefunc_def != NULL) { zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; @@ -947,18 +909,15 @@ extern zipFile ZEXPORT zipOpen2_64 (const void *pathname, int append, zipcharpc* -extern zipFile ZEXPORT zipOpen (const char* pathname, int append) -{ +extern zipFile ZEXPORT zipOpen(const char* pathname, int append) { return zipOpen3((const void*)pathname,append,NULL,NULL); } -extern zipFile ZEXPORT zipOpen64 (const void* pathname, int append) -{ +extern zipFile ZEXPORT zipOpen64(const void* pathname, int append) { return zipOpen3(pathname,append,NULL,NULL); } -int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local) -{ +local int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local) { /* write the local header */ int err; uInt size_filename = (uInt)strlen(filename); @@ -1036,8 +995,8 @@ int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_ex // Remember position of Zip64 extended info for the local file header. (needed when we update size after done with file) zi->ci.pos_zip64extrainfo = ZTELL64(zi->z_filefunc,zi->filestream); - err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)HeaderID,2); - err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)DataSize,2); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)HeaderID,2); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)DataSize,2); err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)UncompressedSize,8); err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)CompressedSize,8); @@ -1054,14 +1013,13 @@ int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_ex It is not done here because then we need to realloc a new buffer since parameters are 'const' and I want to minimize unnecessary allocations. */ -extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, - const void* extrafield_local, uInt size_extrafield_local, - const void* extrafield_global, uInt size_extrafield_global, - const char* comment, int method, int level, int raw, - int windowBits,int memLevel, int strategy, - const char* password, uLong crcForCrypting, - uLong versionMadeBy, uLong flagBase, int zip64) -{ +extern int ZEXPORT zipOpenNewFileInZip4_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase, int zip64) { zip64_internal* zi; uInt size_filename; uInt size_comment; @@ -1069,7 +1027,7 @@ extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, int err = ZIP_OK; # ifdef NOCRYPT - (void)(crcForCrypting); + (void) (crcForCrypting); if (password != NULL) return ZIP_PARAMERROR; # endif @@ -1085,6 +1043,17 @@ extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, return ZIP_PARAMERROR; #endif + // The filename and comment length must fit in 16 bits. + if ((filename!=NULL) && (strlen(filename)>0xffff)) + return ZIP_PARAMERROR; + if ((comment!=NULL) && (strlen(comment)>0xffff)) + return ZIP_PARAMERROR; + // The extra field length must fit in 16 bits. If the member also requires + // a Zip64 extra block, that will also need to fit within that 16-bit + // length, but that will be checked for later. + if ((size_extrafield_local>0xffff) || (size_extrafield_global>0xffff)) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; if (zi->in_opened_file_inzip == 1) @@ -1166,7 +1135,7 @@ extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, if(zi->ci.pos_local_header >= 0xffffffff) zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)0xffffffff,4); else - zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writting_offset,4); + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writing_offset,4); for (i=0;ici.central_header+SIZECENTRALHEADER+i) = *(filename+i); @@ -1264,35 +1233,33 @@ extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, return err; } -extern int ZEXPORT zipOpenNewFileInZip4 (zipFile file, const char* filename, const zip_fileinfo* zipfi, - const void* extrafield_local, uInt size_extrafield_local, - const void* extrafield_global, uInt size_extrafield_global, - const char* comment, int method, int level, int raw, - int windowBits,int memLevel, int strategy, - const char* password, uLong crcForCrypting, - uLong versionMadeBy, uLong flagBase) -{ - return zipOpenNewFileInZip4_64 (file, filename, zipfi, - extrafield_local, size_extrafield_local, - extrafield_global, size_extrafield_global, - comment, method, level, raw, - windowBits, memLevel, strategy, - password, crcForCrypting, versionMadeBy, flagBase, 0); +extern int ZEXPORT zipOpenNewFileInZip4(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase) { + return zipOpenNewFileInZip4_64(file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, versionMadeBy, flagBase, 0); } -extern int ZEXPORT zipOpenNewFileInZip3 (zipFile file, const char* filename, const zip_fileinfo* zipfi, - const void* extrafield_local, uInt size_extrafield_local, - const void* extrafield_global, uInt size_extrafield_global, - const char* comment, int method, int level, int raw, - int windowBits,int memLevel, int strategy, - const char* password, uLong crcForCrypting) -{ - return zipOpenNewFileInZip4_64 (file, filename, zipfi, - extrafield_local, size_extrafield_local, - extrafield_global, size_extrafield_global, - comment, method, level, raw, - windowBits, memLevel, strategy, - password, crcForCrypting, VERSIONMADEBY, 0, 0); +extern int ZEXPORT zipOpenNewFileInZip3(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting) { + return zipOpenNewFileInZip4_64(file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, 0); } extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, @@ -1300,70 +1267,64 @@ extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, c const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits,int memLevel, int strategy, - const char* password, uLong crcForCrypting, int zip64) -{ - return zipOpenNewFileInZip4_64 (file, filename, zipfi, - extrafield_local, size_extrafield_local, - extrafield_global, size_extrafield_global, - comment, method, level, raw, - windowBits, memLevel, strategy, - password, crcForCrypting, VERSIONMADEBY, 0, zip64); + const char* password, uLong crcForCrypting, int zip64) { + return zipOpenNewFileInZip4_64(file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, zip64); } extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, - const char* comment, int method, int level, int raw) -{ - return zipOpenNewFileInZip4_64 (file, filename, zipfi, - extrafield_local, size_extrafield_local, - extrafield_global, size_extrafield_global, - comment, method, level, raw, - -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, - NULL, 0, VERSIONMADEBY, 0, 0); + const char* comment, int method, int level, int raw) { + return zipOpenNewFileInZip4_64(file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); } extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, - const void* extrafield_local, uInt size_extrafield_local, - const void* extrafield_global, uInt size_extrafield_global, - const char* comment, int method, int level, int raw, int zip64) -{ - return zipOpenNewFileInZip4_64 (file, filename, zipfi, - extrafield_local, size_extrafield_local, - extrafield_global, size_extrafield_global, - comment, method, level, raw, - -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, - NULL, 0, VERSIONMADEBY, 0, zip64); + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, int zip64) { + return zipOpenNewFileInZip4_64(file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); } -extern int ZEXPORT zipOpenNewFileInZip64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, - const void* extrafield_local, uInt size_extrafield_local, - const void*extrafield_global, uInt size_extrafield_global, - const char* comment, int method, int level, int zip64) -{ - return zipOpenNewFileInZip4_64 (file, filename, zipfi, - extrafield_local, size_extrafield_local, - extrafield_global, size_extrafield_global, - comment, method, level, 0, - -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, - NULL, 0, VERSIONMADEBY, 0, zip64); +extern int ZEXPORT zipOpenNewFileInZip64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int zip64) { + return zipOpenNewFileInZip4_64(file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); } -extern int ZEXPORT zipOpenNewFileInZip (zipFile file, const char* filename, const zip_fileinfo* zipfi, - const void* extrafield_local, uInt size_extrafield_local, - const void*extrafield_global, uInt size_extrafield_global, - const char* comment, int method, int level) -{ - return zipOpenNewFileInZip4_64 (file, filename, zipfi, - extrafield_local, size_extrafield_local, - extrafield_global, size_extrafield_global, - comment, method, level, 0, - -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, - NULL, 0, VERSIONMADEBY, 0, 0); +extern int ZEXPORT zipOpenNewFileInZip(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level) { + return zipOpenNewFileInZip4_64(file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); } -local int zip64FlushWriteBuffer(zip64_internal* zi) -{ +local int zip64FlushWriteBuffer(zip64_internal* zi) { int err=ZIP_OK; if (zi->ci.encrypt != 0) @@ -1401,8 +1362,7 @@ local int zip64FlushWriteBuffer(zip64_internal* zi) return err; } -extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned int len) -{ +extern int ZEXPORT zipWriteInFileInZip(zipFile file, const void* buf, unsigned int len) { zip64_internal* zi; int err=ZIP_OK; @@ -1452,7 +1412,7 @@ extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned in else #endif { - zi->ci.stream.next_in = (Bytef*)buf; + zi->ci.stream.next_in = (Bytef*)(uintptr_t)buf; zi->ci.stream.avail_in = len; while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) @@ -1473,11 +1433,6 @@ extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned in { uLong uTotalOutBefore = zi->ci.stream.total_out; err=deflate(&zi->ci.stream, Z_NO_FLUSH); - if(uTotalOutBefore > zi->ci.stream.total_out) - { - int bBreak = 0; - bBreak++; - } zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; } @@ -1508,17 +1463,15 @@ extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned in return err; } -extern int ZEXPORT zipCloseFileInZipRaw (zipFile file, uLong uncompressed_size, uLong crc32) -{ +extern int ZEXPORT zipCloseFileInZipRaw(zipFile file, uLong uncompressed_size, uLong crc32) { return zipCloseFileInZipRaw64 (file, uncompressed_size, crc32); } -extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_size, uLong crc32) -{ +extern int ZEXPORT zipCloseFileInZipRaw64(zipFile file, ZPOS64_T uncompressed_size, uLong crc32) { zip64_internal* zi; ZPOS64_T compressed_size; uLong invalidValue = 0xffffffff; - short datasize = 0; + unsigned datasize = 0; int err=ZIP_OK; if (file == NULL) @@ -1655,7 +1608,7 @@ extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_s if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree) { - // we can not write more data to the buffer that we have room for. + // we cannot write more data to the buffer that we have room for. return ZIP_BADZIPFILE; } @@ -1749,15 +1702,13 @@ extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_s return err; } -extern int ZEXPORT zipCloseFileInZip (zipFile file) -{ +extern int ZEXPORT zipCloseFileInZip(zipFile file) { return zipCloseFileInZipRaw (file,0,0); } -int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip) -{ +local int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip) { int err = ZIP_OK; - ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writting_offset; + ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writing_offset; err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDLOCHEADERMAGIC,4); @@ -1776,8 +1727,7 @@ int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eo return err; } -int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) -{ +local int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) { int err = ZIP_OK; uLong Zip64DataSize = 44; @@ -1810,13 +1760,13 @@ int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centra if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ { - ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writing_offset; err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (ZPOS64_T)pos,8); } return err; } -int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) -{ + +local int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) { int err = ZIP_OK; /*signature*/ @@ -1851,20 +1801,19 @@ int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ { - ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writing_offset; if(pos >= 0xffffffff) { err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)0xffffffff,4); } else - err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4); + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writing_offset),4); } return err; } -int Write_GlobalComment(zip64_internal* zi, const char* global_comment) -{ +local int Write_GlobalComment(zip64_internal* zi, const char* global_comment) { int err = ZIP_OK; uInt size_global_comment = 0; @@ -1881,8 +1830,7 @@ int Write_GlobalComment(zip64_internal* zi, const char* global_comment) return err; } -extern int ZEXPORT zipClose (zipFile file, const char* global_comment) -{ +extern int ZEXPORT zipClose(zipFile file, const char* global_comment) { zip64_internal* zi; int err = 0; uLong size_centraldir = 0; @@ -1923,8 +1871,8 @@ extern int ZEXPORT zipClose (zipFile file, const char* global_comment) } free_linkedlist(&(zi->central_dir)); - pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; - if(pos >= 0xffffffff || zi->number_entry > 0xFFFF) + pos = centraldir_pos_inzip - zi->add_position_when_writing_offset; + if(pos >= 0xffffffff || zi->number_entry >= 0xFFFF) { ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc,zi->filestream); Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); @@ -1943,15 +1891,14 @@ extern int ZEXPORT zipClose (zipFile file, const char* global_comment) err = ZIP_ERRNO; #ifndef NO_ADDFILEINEXISTINGZIP - TRYFREE(zi->globalcomment); + free(zi->globalcomment); #endif - TRYFREE(zi); + free(zi); return err; } -extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHeader) -{ +extern int ZEXPORT zipRemoveExtraInfoBlock(char* pData, int* dataLen, short sHeader) { char* p = pData; int size = 0; char* pNewHeader; @@ -1961,10 +1908,10 @@ extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHe int retVal = ZIP_OK; - if(pData == NULL || *dataLen < 4) + if(pData == NULL || dataLen == NULL || *dataLen < 4) return ZIP_PARAMERROR; - pNewHeader = (char*)ALLOC(*dataLen); + pNewHeader = (char*)ALLOC((unsigned)*dataLen); pTmp = pNewHeader; while(p < (pData + *dataLen)) @@ -2003,7 +1950,7 @@ extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHe else retVal = ZIP_ERRNO; - TRYFREE(pNewHeader); + free(pNewHeader); return retVal; } diff --git a/src/minizip/zip.h b/src/minizip/zip.h index c177744..efe6314 100644 --- a/src/minizip/zip.h +++ b/src/minizip/zip.h @@ -49,7 +49,7 @@ extern "C" { #endif -/* #define HAVE_BZIP2 */ +//#define HAVE_BZIP2 #ifndef _ZLIB_H #include "zlib.h" @@ -101,12 +101,12 @@ typedef voidp zipFile; /* tm_zip contain date/time info */ typedef struct tm_zip_s { - uInt tm_sec; /* seconds after the minute - [0,59] */ - uInt tm_min; /* minutes after the hour - [0,59] */ - uInt tm_hour; /* hours since midnight - [0,23] */ - uInt tm_mday; /* day of the month - [1,31] */ - uInt tm_mon; /* months since January - [0,11] */ - uInt tm_year; /* years - [1980..2044] */ + int tm_sec; /* seconds after the minute - [0,59] */ + int tm_min; /* minutes after the hour - [0,59] */ + int tm_hour; /* hours since midnight - [0,23] */ + int tm_mday; /* day of the month - [1,31] */ + int tm_mon; /* months since January - [0,11] */ + int tm_year; /* years - [1980..2044] */ } tm_zip; typedef struct @@ -126,8 +126,8 @@ typedef const char* zipcharpc; #define APPEND_STATUS_CREATEAFTER (1) #define APPEND_STATUS_ADDINZIP (2) -extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); -extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); +extern zipFile ZEXPORT zipOpen(const char *pathname, int append); +extern zipFile ZEXPORT zipOpen64(const void *pathname, int append); /* Create a zipfile. pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on @@ -144,50 +144,55 @@ extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); /* Note : there is no delete function into a zipfile. If you want delete file into a zipfile, you must open a zipfile, and create another - Of couse, you can use RAW reading and writing to copy the file you did not want delte + Of course, you can use RAW reading and writing to copy the file you did not want delete */ -extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, - int append, - zipcharpc* globalcomment, - zlib_filefunc_def* pzlib_filefunc_def)); +extern zipFile ZEXPORT zipOpen2(const char *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc_def* pzlib_filefunc_def); -extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname, +extern zipFile ZEXPORT zipOpen2_64(const void *pathname, int append, zipcharpc* globalcomment, - zlib_filefunc64_def* pzlib_filefunc_def)); - -extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level)); - -extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int zip64)); + zlib_filefunc64_def* pzlib_filefunc_def); + +extern zipFile ZEXPORT zipOpen3(const void *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc64_32_def* pzlib_filefunc64_32_def); + +extern int ZEXPORT zipOpenNewFileInZip(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level); + +extern int ZEXPORT zipOpenNewFileInZip64(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int zip64); /* Open a file in the ZIP for writing. filename : the filename in zip (if NULL, '-' without quote will be used *zipfi contain supplemental information if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local - contains the extrafield data the the local header + contains the extrafield data for the local header if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global - contains the extrafield data the the local header + contains the extrafield data for the global header if comment != NULL, comment contain the comment string method contain the compression method (0 for store, Z_DEFLATED for deflate) level contain the level of compression (can be Z_DEFAULT_COMPRESSION) @@ -197,70 +202,69 @@ extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, */ -extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int raw)); - - -extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int raw, - int zip64)); +extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw); + + +extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int zip64); /* Same than zipOpenNewFileInZip, except if raw=1, we write raw file */ -extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int raw, - int windowBits, - int memLevel, - int strategy, - const char* password, - uLong crcForCrypting)); - -extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int raw, - int windowBits, - int memLevel, - int strategy, - const char* password, - uLong crcForCrypting, - int zip64 - )); +extern int ZEXPORT zipOpenNewFileInZip3(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting); + +extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + int zip64); /* Same than zipOpenNewFileInZip2, except @@ -269,47 +273,45 @@ extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, crcForCrypting : crc of file to compress (needed for crypting) */ -extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int raw, - int windowBits, - int memLevel, - int strategy, - const char* password, - uLong crcForCrypting, - uLong versionMadeBy, - uLong flagBase - )); - - -extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int raw, - int windowBits, - int memLevel, - int strategy, - const char* password, - uLong crcForCrypting, - uLong versionMadeBy, - uLong flagBase, - int zip64 - )); +extern int ZEXPORT zipOpenNewFileInZip4(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase); + + +extern int ZEXPORT zipOpenNewFileInZip4_64(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase, + int zip64); /* Same than zipOpenNewFileInZip4, except versionMadeBy : value for Version made by field @@ -317,25 +319,25 @@ extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, */ -extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, - const void* buf, - unsigned len)); +extern int ZEXPORT zipWriteInFileInZip(zipFile file, + const void* buf, + unsigned len); /* Write data in the zipfile */ -extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); +extern int ZEXPORT zipCloseFileInZip(zipFile file); /* Close the current file in the zipfile */ -extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, - uLong uncompressed_size, - uLong crc32)); +extern int ZEXPORT zipCloseFileInZipRaw(zipFile file, + uLong uncompressed_size, + uLong crc32); -extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, - ZPOS64_T uncompressed_size, - uLong crc32)); +extern int ZEXPORT zipCloseFileInZipRaw64(zipFile file, + ZPOS64_T uncompressed_size, + uLong crc32); /* Close the current file in the zipfile, for file opened with @@ -343,14 +345,14 @@ extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, uncompressed_size and crc32 are value for the uncompressed size */ -extern int ZEXPORT zipClose OF((zipFile file, - const char* global_comment)); +extern int ZEXPORT zipClose(zipFile file, + const char* global_comment); /* Close the zipfile */ -extern int ZEXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader)); +extern int ZEXPORT zipRemoveExtraInfoBlock(char* pData, int* dataLen, short sHeader); /* zipRemoveExtraInfoBlock - Added by Mathias Svensson