mirror of
https://github.com/jmcnamara/libxlsxwriter
synced 2025-03-28 21:13:14 +00:00
Support newer Excel hyperlink length of 2079.
This commit is contained in:
parent
dcecc632d5
commit
373ff53382
@ -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,
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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) \
|
||||
|
@ -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."
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
|
28
test/functional/src/test_hyperlink46.c
Normal file
28
test/functional/src/test_hyperlink46.c
Normal 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);
|
||||
}
|
20
test/functional/src/test_hyperlink47.c
Normal file
20
test/functional/src/test_hyperlink47.c
Normal 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);
|
||||
}
|
@ -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')
|
||||
|
BIN
test/functional/xlsx_files/hyperlink46.xlsx
Normal file
BIN
test/functional/xlsx_files/hyperlink46.xlsx
Normal file
Binary file not shown.
BIN
test/functional/xlsx_files/hyperlink47.xlsx
Normal file
BIN
test/functional/xlsx_files/hyperlink47.xlsx
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user