diff --git a/Changes.txt b/Changes.txt index 1befe633..ea17ba55 100644 --- a/Changes.txt +++ b/Changes.txt @@ -2,11 +2,17 @@ @page changes Changes +## 0.1.5 May 3 2015 + +- Added `worksheet_write_url()` function to write urls/hyperlinks to + worksheets. See also @ref hyperlinks.c. + + ## 0.1.4 March 18 2015 - Added `worksheet_autofilter()` function to add autofilters to worksheets. See also @ref autofilter.c. - + ## 0.1.3 March 15 2015 diff --git a/Readme.md b/Readme.md index 87fc10c5..2f58691a 100644 --- a/Readme.md +++ b/Readme.md @@ -9,7 +9,7 @@ A C library for creating Excel XLSX files. ## The libxlsxwriter library -Libxlsxwriter is a C library that can be used to write text, numbers and formulas to multiple worksheets in an Excel 2007+ XLSX file. +Libxlsxwriter is a C library that can be used to write text, numbers, formulas and hyperlinks to multiple worksheets in an Excel 2007+ XLSX file. It supports features such as: diff --git a/dev/release/fix_example_docs.pl b/dev/release/fix_example_docs.pl index e8d2d5f7..9adb4e8c 100644 --- a/dev/release/fix_example_docs.pl +++ b/dev/release/fix_example_docs.pl @@ -22,6 +22,7 @@ [ 'dates_and_times01.c', 'Writing dates and times with numbers' ], [ 'dates_and_times02.c', 'Writing dates and times with datetime' ], [ 'dates_and_times03.c', 'Dates and times with different formats' ], + [ 'hyperlinks.c', 'A example of writing urls/hyperlinks' ], [ 'array_formula.c', 'A example of using array formulas' ], [ 'utf8.c', 'A example of some UTF-8 text' ], [ 'constant_memory.c', 'Write a large file with constant memory usage' ], diff --git a/docs/images/hyperlinks.png b/docs/images/hyperlinks.png new file mode 100644 index 00000000..54230ed4 Binary files /dev/null and b/docs/images/hyperlinks.png differ diff --git a/docs/images/hyperlinks_short.png b/docs/images/hyperlinks_short.png new file mode 100644 index 00000000..cb7cd3b3 Binary files /dev/null and b/docs/images/hyperlinks_short.png differ diff --git a/docs/images/hyperlinks_short2.png b/docs/images/hyperlinks_short2.png new file mode 100644 index 00000000..63ad3a85 Binary files /dev/null and b/docs/images/hyperlinks_short2.png differ diff --git a/docs/src/examples.dox b/docs/src/examples.dox index f81ba397..de7105e0 100644 --- a/docs/src/examples.dox +++ b/docs/src/examples.dox @@ -83,10 +83,17 @@ Next example: @ref dates_and_times03.c @example dates_and_times03.c Example of writing dates and times in Excel using different date formats. -Next example: @ref array_formula.c +Next example: @ref hyperlinks.c @image html date_example03.png +@example hyperlinks.c +Example of writing urls/hyperlinks to a worksheet. + +Next example: @ref array_formula.c +@image html hyperlinks.png + + @example array_formula.c Example of writing array formulas to a worksheet. diff --git a/docs/src/introduction.dox b/docs/src/introduction.dox index a08889fc..f3ca777c 100644 --- a/docs/src/introduction.dox +++ b/docs/src/introduction.dox @@ -5,7 +5,7 @@ libxlsxwriter is a C library for writing files in the Excel 2007+ XLSX file format. -It can be used to write text, numbers, and formulas to multiple +It can be used to write text, numbers, formulas and hyperlinks to multiple worksheets and it supports features such as formatting. The main advantages of using `libxlsxwriter` are: diff --git a/docs/src/mainpage.dox b/docs/src/mainpage.dox index aa6bea3e..9da8d2cc 100644 --- a/docs/src/mainpage.dox +++ b/docs/src/mainpage.dox @@ -9,8 +9,8 @@ @section mainpage_intro libxlsxwriter -Libxlsxwriter is a C library that can be used to write text, numbers and -formulas to multiple worksheets in an Excel 2007+ XLSX file. It supports +Libxlsxwriter is a C library that can be used to write text, numbers, formulas +and hyperlinks to multiple worksheets in an Excel 2007+ XLSX file. It supports features such as: - 100% compatible Excel XLSX files diff --git a/examples/hyperlinks.c b/examples/hyperlinks.c new file mode 100644 index 00000000..f4a321b6 --- /dev/null +++ b/examples/hyperlinks.c @@ -0,0 +1,55 @@ +/* + * Example of writing urls/hyperlinks with the libxlsxwriter library. + * + * Copyright 2014-2015, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + /* Create a new workbook. */ + lxw_workbook *workbook = new_workbook("hyperlinks.xlsx"); + + /* Add a worksheet. */ + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + /* Add some cell formats for the hyperlinks. */ + lxw_format *url_format = workbook_add_format(workbook); + lxw_format *red_format = workbook_add_format(workbook); + + /* Create the standard url link format. */ + format_set_underline (url_format, LXW_UNDERLINE_SINGLE); + format_set_font_color(url_format, LXW_COLOR_BLUE); + + /* Create another sample format. */ + format_set_underline (red_format, LXW_UNDERLINE_SINGLE); + format_set_font_color(red_format, LXW_COLOR_RED); + + /* Widen the first column to make the text clearer. */ + worksheet_set_column(worksheet, 0, 0, 30, NULL, NULL); + + /* Write a hyperlink. */ + worksheet_write_url(worksheet, 0, 0, "http://libxlsxwriter.github.io", url_format); + + /* Write a hyperlink but overwrite the displayed string. */ + worksheet_write_url (worksheet, 2, 0, "http://libxlsxwriter.github.io", url_format); + worksheet_write_string(worksheet, 2, 0, "Read the documentation.", url_format); + + /* Write a hyperlink with a different format. */ + worksheet_write_url(worksheet, 4, 0, "http://libxlsxwriter.github.io", red_format); + + /* Write a mail hyperlink. */ + worksheet_write_url (worksheet, 6, 0, "mailto:jmcnamara@cpan.org", url_format); + + /* Write a mail hyperlink and overwrite the displayed string. */ + worksheet_write_url (worksheet, 8, 0, "mailto:jmcnamara@cpan.org", url_format); + worksheet_write_string(worksheet, 8, 0, "Drop me a line.", url_format); + + + /* Close the workbook, save the file and free any memory. */ + workbook_close(workbook); + + return 0; +} diff --git a/include/xlsxwriter/worksheet.h b/include/xlsxwriter/worksheet.h index 98ea4460..ae9b8eaa 100644 --- a/include/xlsxwriter/worksheet.h +++ b/include/xlsxwriter/worksheet.h @@ -247,6 +247,7 @@ typedef struct lxw_worksheet { uint16_t fit_height; uint16_t fit_width; uint16_t horizontal_dpi; + uint16_t hlink_count; uint16_t page_start; uint16_t print_scale; uint16_t rel_count; @@ -589,10 +590,139 @@ int8_t worksheet_write_url_opt(lxw_worksheet *worksheet, lxw_col_t col_num, const char *url, lxw_format *format, const char *string, const char *tooltip); - +/** + * + * @param worksheet Pointer to the lxw_worksheet instance to be updated. + * @param row The zero indexed row number. + * @param col The zero indexed column number. + * @param url The url to write to the cell. + * @param format A pointer to a Format instance or NULL. + * + * @return A #lxw_write_error code. + * + * + * The `%worksheet_write_url()` function is used to write a URL/hyperlink to a + * worksheet cell specified by `row` and `column`. + * + * @code + * worksheet_write_url(worksheet, 0, 0, "http://libxlsxwriter.github.io", url_format); + * @endcode + * + * @image html hyperlinks_short.png + * + * The `format` parameter is used to apply formatting to the cell. This + * parameter can be `NULL` to indicate no formatting or it can be a @ref + * format.h "Format" object. The typical worksheet format for a hyperlink is a + * blue underline: + * + * @code + * lxw_format *url_format = workbook_add_format(workbook); + * + * format_set_underline (url_format, LXW_UNDERLINE_SINGLE); + * format_set_font_color(url_format, LXW_COLOR_BLUE); + * + * @endcode + * + * The usual web style URI's are supported: `%http://`, `%https://`, `%ftp://` + * and `mailto:` : + * + * @code + * worksheet_write_url(worksheet, 0, 0, "ftp://www.python.org/", url_format); + * worksheet_write_url(worksheet, 1, 0, "http://www.python.org/", url_format); + * worksheet_write_url(worksheet, 2, 0, "https://www.python.org/", url_format); + * worksheet_write_url(worksheet, 3, 0, "mailto:jmcnamaracpan.org", url_format); + * + * @endcode + * + * An Excel hyperlink is comprised of two elements: the displayed string and + * the non-displayed link. By default the displayed string is the same as the + * link. However, it is possible to overwrite it with any other + * `libxlsxwriter` type using the appropriate `worksheet_write_*()` + * function. The most common case is to overwrite the displayed link text with + * another string: + * + * @code + * // Write a hyperlink but overwrite the displayed string. + * worksheet_write_url (worksheet, 2, 0, "http://libxlsxwriter.github.io", url_format); + * worksheet_write_string(worksheet, 2, 0, "Read the documentation.", url_format); + * + * @endcode + * + * @image html hyperlinks_short2.png + * + * Two local URIs are supported: `internal:` and `external:`. These are used + * for hyperlinks to internal worksheet references or external workbook and + * worksheet references: + * + * @code + * worksheet_write_url(worksheet, 0, 0, "internal:Sheet2!A1", url_format); + * worksheet_write_url(worksheet, 1, 0, "internal:Sheet2!B2", url_format); + * worksheet_write_url(worksheet, 2, 0, "internal:Sheet2!A1:B2", url_format); + * worksheet_write_url(worksheet, 3, 0, "internal:'Sales Data'!A1", url_format); + * worksheet_write_url(worksheet, 4, 0, "external:c:\\temp\\foo.xlsx", url_format); + * worksheet_write_url(worksheet, 5, 0, "external:c:\\foo.xlsx#Sheet2!A1", url_format); + * worksheet_write_url(worksheet, 6, 0, "external:..\\foo.xlsx", url_format); + * worksheet_write_url(worksheet, 7, 0, "external:..\\foo.xlsx#Sheet2!A1", url_format); + * worksheet_write_url(worksheet, 8, 0, "external:\\\\NET\\share\\foo.xlsx", url_format); + * + * @endcode + * + * Worksheet references are typically of the form `Sheet1!A1`. You can also + * link to a worksheet range using the standard Excel notation: + * `Sheet1!A1:B2`. + * + * In external links the workbook and worksheet name must be separated by the + * `#` character: + * + * @code + * worksheet_write_url(worksheet, 0, 0, "external:c:\\foo.xlsx#Sheet2!A1", url_format); + * @endcode + * + * You can also link to a named range in the target worksheet: For example say + * you have a named range called `my_name` in the workbook `c:\temp\foo.xlsx` + * you could link to it as follows: + * + * @code + * worksheet_write_url(worksheet, 0, 0, "external:c:\\temp\\foo.xlsx#my_name", url_format); + * + * @endcode + * + * Excel requires that worksheet names containing spaces or non alphanumeric + * characters are single quoted as follows: + * + * @code + * worksheet_write_url(worksheet, 0, 0, "internal:'Sales Data'!A1", url_format); + * @endcode + * + * Links to network files are also supported. Network files normally begin + * with two back slashes as follows `\\NETWORK\etc`. In order to represent + * this in a C string literal the backslashes should be escaped: + * @code + * worksheet_write_url(worksheet, 0, 0, "external:\\\\NET\\share\\foo.xlsx", url_format); + * @endcode + * + * + * Alternatively, you can use Windows style forward slashes. These are + * translated internally to backslashes: + * + * @code + * worksheet_write_url(worksheet, 0, 0, "external:c:/temp/foo.xlsx", url_format); + * worksheet_write_url(worksheet, 1, 0, "external://NET/share/foo.xlsx", url_format); + * + * @endcode + * + * + * **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. + * + */ int8_t worksheet_write_url(lxw_worksheet *worksheet, - lxw_row_t row_num, - lxw_col_t col_num, const char *url, + lxw_row_t row, + lxw_col_t col, const char *url, lxw_format *format); /** diff --git a/src/worksheet.c b/src/worksheet.c index 1d1b7191..3a4e54de 100644 --- a/src/worksheet.c +++ b/src/worksheet.c @@ -2163,9 +2163,12 @@ worksheet_write_url_opt(lxw_worksheet *self, int8_t err; size_t string_size; size_t i; - enum cell_types link_type = HYPERLINK_URL; + /* Check the Excel limit of URLS per worksheet. */ + if (self->hlink_count > 65530) + return -5; + if (!url || !strlen(url)) return -4; @@ -2266,6 +2269,7 @@ worksheet_write_url_opt(lxw_worksheet *self, GOTO_LABEL_ON_MEM_ERROR(url_copy, mem_error); free(url_external); + url_external = NULL; } } @@ -2321,10 +2325,15 @@ worksheet_write_url_opt(lxw_worksheet *self, GOTO_LABEL_ON_MEM_ERROR(url_copy, mem_error); free(url_external); + url_external = NULL; } } + /* Excel limits escaped URL to 255 characters. */ + if (strlen(url_copy) > 255) + goto mem_error; + err = worksheet_write_string(self, row_num, col_num, string_copy, format); if (err) goto mem_error; @@ -2336,6 +2345,7 @@ worksheet_write_url_opt(lxw_worksheet *self, _insert_hyperlink(self, row_num, col_num, link); free(string_copy); + self->hlink_count++; return 0; mem_error: diff --git a/test/functional/src/test_hyperlink18.c b/test/functional/src/test_hyperlink18.c index c4d5190b..b5352af5 100644 --- a/test/functional/src/test_hyperlink18.c +++ b/test/functional/src/test_hyperlink18.c @@ -16,5 +16,10 @@ int main() { worksheet_write_url(worksheet, CELL("A1"), "http://google.com/00000000001111111111222222222233333333334444444444555555555566666666666777777777778888888888999999999990000000000111111111122222222223333333333444444444455555555556666666666677777777777888888888899999999999000000000011111111112222222222x", NULL); + /* This longer url should be ignored. */ + worksheet_write_url(worksheet, CELL("A1"), "http://google.com/00000000001111111111222222222233333333334444444444555555555566666666666777777777778888888888999999999990000000000111111111122222222223333333333444444444455555555556666666666677777777777888888888899999999999000000000011111111112222222222xy", NULL); + + + return workbook_close(workbook); }