Support newer Excel hyperlink length of 2079.

This commit is contained in:
John McNamara 2019-12-15 11:48:58 +00:00
parent dcecc632d5
commit 373ff53382
13 changed files with 107 additions and 11 deletions

View File

@ -133,6 +133,9 @@ typedef enum lxw_error {
/** Worksheet row or column index out of range. */
LXW_ERROR_WORKSHEET_INDEX_OUT_OF_RANGE,
/** Maximum hyperlink length (2079) exceeded. */
LXW_ERROR_WORKSHEET_MAX_URL_LENGTH_EXCEEDED,
/** Maximum number of worksheet URLs (65530) exceeded. */
LXW_ERROR_WORKSHEET_MAX_NUMBER_URLS_EXCEEDED,

View File

@ -282,6 +282,7 @@ typedef struct lxw_workbook {
uint16_t border_count;
uint16_t fill_count;
uint8_t optimize;
uint16_t max_url_length;
uint8_t has_png;
uint8_t has_jpeg;

View File

@ -867,6 +867,7 @@ typedef struct lxw_worksheet {
struct lxw_autofilter autofilter;
uint16_t merged_range_count;
uint16_t max_url_length;
lxw_row_t *hbreaks;
lxw_col_t *vbreaks;
@ -903,6 +904,7 @@ typedef struct lxw_worksheet_init_data {
char *quoted_name;
char *tmpdir;
lxw_format *default_url_format;
uint16_t max_url_length;
} lxw_worksheet_init_data;
@ -1321,10 +1323,14 @@ lxw_error worksheet_write_datetime(lxw_worksheet *worksheet,
* **Note:**
*
* libxlsxwriter will escape the following characters in URLs as required
* by Excel: `\s " < > \ [ ] ^ { }` unless the URL already contains `%%xx`
* style escapes. In which case it is assumed that the URL was escaped
* correctly by the user and will by passed directly to Excel.
* by Excel: `\s " < > \ [ ] ^ { }`. Existing URL `%%xx` style escapes in
* the string are ignored to allow for user-escaped strings.
*
* **Note:**
*
* The maximum allowable URL length in recent versions of Excel is 2079
* characters. In older versions of Excel (and libxlsxwriter <= 0.8.8) the
* limit was 255 characters.
*/
lxw_error worksheet_write_url(lxw_worksheet *worksheet,
lxw_row_t row,

View File

@ -24,7 +24,7 @@
#include <stdint.h>
#include "utility.h"
#define LXW_MAX_ATTRIBUTE_LENGTH 256
#define LXW_MAX_ATTRIBUTE_LENGTH 2080 /* Max URL length. */
#define LXW_ATTR_32 32
#define LXW_ATTRIBUTE_COPY(dst, src) \

View File

@ -40,6 +40,7 @@ char *error_strings[LXW_MAX_ERRNO + 1] = {
"String exceeds Excel's limit of 32,767 characters.",
"Error finding internal string index.",
"Worksheet row or column index out of range.",
"Maximum hyperlink length (2079) exceeded.",
"Maximum number of worksheet URLs (65530) exceeded.",
"Couldn't read image dimensions or DPI.",
"Unknown error number."

View File

@ -1495,6 +1495,8 @@ workbook_new_opt(const char *filename, lxw_workbook_options *options)
workbook->options.use_zip64 = options->use_zip64;
}
workbook->max_url_length = 2079;
return workbook;
mem_error:
@ -1513,7 +1515,7 @@ workbook_add_worksheet(lxw_workbook *self, const char *sheetname)
lxw_worksheet *worksheet = NULL;
lxw_worksheet_name *worksheet_name = NULL;
lxw_error error;
lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
char *new_name = NULL;
if (sheetname) {
@ -1553,6 +1555,7 @@ workbook_add_worksheet(lxw_workbook *self, const char *sheetname)
init_data.first_sheet = &self->first_sheet;
init_data.tmpdir = self->options.tmpdir;
init_data.default_url_format = self->default_url_format;
init_data.max_url_length = self->max_url_length;
/* Create a new worksheet object. */
worksheet = lxw_worksheet_new(&init_data);
@ -1596,7 +1599,7 @@ workbook_add_chartsheet(lxw_workbook *self, const char *sheetname)
lxw_chartsheet *chartsheet = NULL;
lxw_chartsheet_name *chartsheet_name = NULL;
lxw_error error;
lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
char *new_name = NULL;
if (sheetname) {

View File

@ -194,6 +194,7 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data)
worksheet->outline_below = LXW_TRUE;
worksheet->outline_right = LXW_FALSE;
worksheet->tab_color = LXW_COLOR_UNSET;
worksheet->max_url_length = 2079;
if (init_data) {
worksheet->name = init_data->name;
@ -206,6 +207,7 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data)
worksheet->active_sheet = init_data->active_sheet;
worksheet->first_sheet = init_data->first_sheet;
worksheet->default_url_format = init_data->default_url_format;
worksheet->max_url_length = init_data->max_url_length;
}
return worksheet;
@ -2114,6 +2116,14 @@ lxw_worksheet_prepare_image(lxw_worksheet *self,
GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error);
}
/* Check if URL exceeds Excel's length limit. */
if (lxw_utf8_strlen(url) > self->max_url_length) {
LXW_WARN_FORMAT2("worksheet_insert_image()/_opt(): URL exceeds "
"Excel's allowable length of %d characters: %s",
self->max_url_length, url);
goto mem_error;
}
STAILQ_INSERT_TAIL(self->drawing_links, relationship, list_pointers);
drawing_object->url_rel_index = _get_drawing_rel_index(self, NULL);
@ -4260,22 +4270,28 @@ worksheet_write_url_opt(lxw_worksheet *self,
char *found_string;
char *tmp_string = NULL;
lxw_format *format = NULL;
lxw_error err;
size_t string_size;
size_t i;
lxw_error err = LXW_ERROR_MEMORY_MALLOC_FAILED;
enum cell_types link_type = HYPERLINK_URL;
if (!url || !*url)
return LXW_ERROR_NULL_PARAMETER_IGNORED;
/* Check the Excel limit of URLS per worksheet. */
if (self->hlink_count > LXW_MAX_NUMBER_URLS)
if (self->hlink_count > LXW_MAX_NUMBER_URLS) {
LXW_WARN("worksheet_write_url()/_opt(): URL ignored since it exceeds "
"the maximum number of allowed worksheet URLs (65530).");
return LXW_ERROR_WORKSHEET_MAX_NUMBER_URLS_EXCEEDED;
}
err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
if (err)
return err;
/* Reset default error condition. */
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
/* Set the URI scheme from internal links. */
found_string = strstr(url, "internal:");
if (found_string)
@ -4387,9 +4403,14 @@ worksheet_write_url_opt(lxw_worksheet *self,
}
/* Excel limits escaped URL to 255 characters. */
if (lxw_utf8_strlen(url_copy) > 255)
/* Check if URL exceeds Excel's length limit. */
if (lxw_utf8_strlen(url_copy) > self->max_url_length) {
LXW_WARN_FORMAT2("worksheet_write_url()/_opt(): URL exceeds "
"Excel's allowable length of %d characters: %s",
self->max_url_length, url_copy);
err = LXW_ERROR_WORKSHEET_MAX_URL_LENGTH_EXCEEDED;
goto mem_error;
}
/* Use the default URL format if none is specified. */
if (!user_format)
@ -4401,6 +4422,9 @@ worksheet_write_url_opt(lxw_worksheet *self,
if (err)
goto mem_error;
/* Reset default error condition. */
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
link = _new_hyperlink_cell(row_num, col_num, link_type, url_copy,
url_string, tooltip_copy);
GOTO_LABEL_ON_MEM_ERROR(link, mem_error);
@ -4417,7 +4441,7 @@ mem_error:
free(url_external);
free(url_string);
free(tooltip_copy);
return LXW_ERROR_MEMORY_MALLOC_FAILED;
return err;
}
/*

View File

@ -12,6 +12,10 @@
int main() {
lxw_workbook *workbook = workbook_new("test_hyperlink18.xlsx");
/* Set shorter length for testing. */
workbook->max_url_length = 255;
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
workbook_unset_default_url_format(workbook);

View File

@ -0,0 +1,28 @@
/*****************************************************************************
* Test cases for libxlsxwriter.
*
* Test to compare output against Excel files.
*
* Copyright 2014-2019, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("test_hyperlink46.xlsx");
/* Set shorter length for testing. */
workbook->max_url_length = 255;
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
worksheet_write_string(worksheet, CELL("A1"), "Foo" , NULL);
worksheet_write_string(worksheet, CELL("A3"), "Bar" , NULL);
/* This link is too long and should be ignored, with a warning. */
worksheet_write_url(worksheet, CELL("A2"), "http://foo.com/this_is_a_long_hyperlink_that_exceeds_a_limit_of_255_characters_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" , NULL);
return workbook_close(workbook);
}

View File

@ -0,0 +1,20 @@
/*****************************************************************************
* Test cases for libxlsxwriter.
*
* Test to compare output against Excel files.
*
* Copyright 2014-2019, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("test_hyperlink47.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
worksheet_write_url(worksheet, CELL("A1"), "http://foo.com/this_is_a_long_hyperlink_that_exceeds_a_limit_of_255_characters_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" , NULL);
return workbook_close(workbook);
}

View File

@ -168,3 +168,9 @@ class TestCompareXLSXFiles(base_test_class.XLSXBaseTest):
def test_hyperlink45(self):
self.run_exe_test('test_hyperlink45')
def test_hyperlink46(self):
self.run_exe_test('test_hyperlink46')
def test_hyperlink47(self):
self.run_exe_test('test_hyperlink47')

Binary file not shown.

Binary file not shown.