diff --git a/.travis.yml b/.travis.yml index 16244a3..68d228b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,4 +41,4 @@ script: - ./travis/run-test.sh - make clean && phpize --clean - phpize && ./configure --enable-reader && make clean && make - - if [ -n "$USE_VALGRIND" ]; then REPORT_EXIT_STATUS=1 php -n run-tests.php -m -n -d extension_dir=./modules/ -d extension=xlswriter.so -P --show-diff --set-timeout 120; fi + - if [ -n "$USE_VALGRIND" ]; then REPORT_EXIT_STATUS=1 php -n run-tests.php -m -n -d extension_dir=./modules/ -d extension=xlswriter.so -P --show-all --set-timeout 120; fi diff --git a/config.m4 b/config.m4 index 8532ee5..0cc9d89 100644 --- a/config.m4 +++ b/config.m4 @@ -13,15 +13,16 @@ PHP_ARG_ENABLE(reader, enable xlsx reader support, if test "$PHP_XLSWRITER" != "no"; then xls_writer_sources=" xlswriter.c \ - kernel/exception.c \ - kernel/resource.c \ + kernel/chart.c \ kernel/common.c \ kernel/excel.c \ - kernel/write.c \ + kernel/exception.c \ kernel/format.c \ - kernel/chart.c \ kernel/help.c \ + kernel/resource.c \ + kernel/rich_string.c \ kernel/validation.c \ + kernel/write.c \ " xls_read_sources=" diff --git a/config.w32 b/config.w32 index 1a2466e..9afe8f6 100644 --- a/config.w32 +++ b/config.w32 @@ -18,17 +18,18 @@ if (PHP_XLSWRITER != "no") { AC_DEFINE("HAVE_LXW_VERSION", 1, "lxw_version available in 0.7.7"); ADD_SOURCES(configure_module_dirname + "\\kernel", "\ - exception.c \ - resource.c \ + chart.c \ common.c \ + csv.c \ excel.c \ - write.c \ + exception.c \ format.c \ - chart.c \ - read.c \ - csv.c \ help.c \ + read.c \ + resource.c \ + rich_string.c \ validation.c \ + write.c \ ", "xlswriter"); ADD_SOURCES(configure_module_dirname + "\\library\\libxlsxwriter\\third_party\\minizip", "\ diff --git a/include/rich_string.h b/include/rich_string.h new file mode 100644 index 0000000..5653c58 --- /dev/null +++ b/include/rich_string.h @@ -0,0 +1,20 @@ +/* + +----------------------------------------------------------------------+ + | XlsWriter Extension | + +----------------------------------------------------------------------+ + | Copyright (c) 2017-2018 The Viest | + +----------------------------------------------------------------------+ + | http://www.viest.me | + +----------------------------------------------------------------------+ + | Author: viest | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_EXT_XLS_EXPORT_RICH_STRING_H +#define PHP_EXT_XLS_EXPORT_RICH_STRING_H + +extern zend_class_entry *vtiful_rich_string_ce; + +VTIFUL_STARTUP_FUNCTION(rich_string); + +#endif diff --git a/include/xlswriter.h b/include/xlswriter.h index 8655674..3ca25c6 100644 --- a/include/xlswriter.h +++ b/include/xlswriter.h @@ -36,6 +36,7 @@ #include "exception.h" #include "format.h" #include "chart.h" +#include "rich_string.h" #include "help.h" #ifdef ENABLE_READER @@ -95,6 +96,10 @@ typedef struct { lxw_chart_series *series; } xls_resource_chart_t; +typedef struct { + lxw_rich_string_tuple *tuple; +} xls_resource_rich_string_t; + typedef struct _vtiful_xls_object { xls_resource_read_t read_ptr; xls_resource_write_t write_ptr; @@ -118,16 +123,22 @@ typedef struct _vtiful_validation_object { zend_object zo; } validation_object; +typedef struct _vtiful_rich_string_object { + xls_resource_rich_string_t ptr; + zend_object zo; +} rich_string_object; + #define REGISTER_CLASS_CONST_LONG(class_name, const_name, value) \ zend_declare_class_constant_long(class_name, const_name, sizeof(const_name)-1, (zend_long)value); #define REGISTER_CLASS_PROPERTY_NULL(class_name, property_name, acc) \ zend_declare_property_null(class_name, ZEND_STRL(property_name), acc); -#define Z_XLS_P(zv) php_vtiful_xls_fetch_object(Z_OBJ_P(zv)); -#define Z_CHART_P(zv) php_vtiful_chart_fetch_object(Z_OBJ_P(zv)); -#define Z_FORMAT_P(zv) php_vtiful_format_fetch_object(Z_OBJ_P(zv)); -#define Z_VALIDATION_P(zv) php_vtiful_validation_fetch_object(Z_OBJ_P(zv)); +#define Z_XLS_P(zv) php_vtiful_xls_fetch_object(Z_OBJ_P(zv)); +#define Z_CHART_P(zv) php_vtiful_chart_fetch_object(Z_OBJ_P(zv)); +#define Z_FORMAT_P(zv) php_vtiful_format_fetch_object(Z_OBJ_P(zv)); +#define Z_VALIDATION_P(zv) php_vtiful_validation_fetch_object(Z_OBJ_P(zv)); +#define Z_RICH_STR_P(zv) php_vtiful_rich_string_fetch_object(Z_OBJ_P(zv)); #define WORKBOOK_NOT_INITIALIZED(xls_object_t) \ do { \ @@ -254,6 +265,14 @@ static inline validation_object *php_vtiful_validation_fetch_object(zend_object return (validation_object *)((char *)(obj) - XtOffsetOf(validation_object, zo)); } +static inline rich_string_object *php_vtiful_rich_string_fetch_object(zend_object *obj) { + if (obj == NULL) { + return NULL; + } + + return (rich_string_object *)((char *)(obj) - XtOffsetOf(validation_object, zo)); +} + static inline void php_vtiful_close_resource(zend_object *obj) { if (obj == NULL) { return; @@ -287,10 +306,11 @@ static inline void php_vtiful_close_resource(zend_object *obj) { intern->read_ptr.data_type_default = READ_TYPE_EMPTY; } -lxw_format * zval_get_format(zval *handle); -lxw_data_validation * zval_get_validation(zval *resource); -xls_resource_write_t * zval_get_resource(zval *handle); -xls_resource_chart_t * zval_get_chart(zval *resource); +lxw_format * zval_get_format(zval *handle); +lxw_data_validation * zval_get_validation(zval *resource); +lxw_rich_string_tuple * zval_get_rich_string(zval *resource); +xls_resource_write_t * zval_get_resource(zval *handle); +xls_resource_chart_t * zval_get_chart(zval *resource); STATIC lxw_error _store_defined_name(lxw_workbook *self, const char *name, const char *app_name, const char *formula, int16_t index, uint8_t hidden); @@ -327,6 +347,7 @@ void worksheet_set_rows(lxw_row_t start, lxw_row_t end, double height, xls_resou void image_writer(zval *value, zend_long row, zend_long columns, double width, double height, xls_resource_write_t *res); void formula_writer(zend_string *value, zend_long row, zend_long columns, xls_resource_write_t *res, lxw_format *format); void type_writer(zval *value, zend_long row, zend_long columns, xls_resource_write_t *res, zend_string *format, lxw_format *format_handle); +void rich_string_writer(zend_long row, zend_long columns, xls_resource_write_t *res, zval *rich_strings, lxw_format *format); void datetime_writer(lxw_datetime *datetime, zend_long row, zend_long columns, zend_string *format, xls_resource_write_t *res, lxw_format *format_handle); void url_writer(zend_long row, zend_long columns, xls_resource_write_t *res, zend_string *url, zend_string *text, zend_string *tool_tip, lxw_format *format); diff --git a/kernel/excel.c b/kernel/excel.c index 190ba64..44621dc 100644 --- a/kernel/excel.c +++ b/kernel/excel.c @@ -117,6 +117,13 @@ ZEND_BEGIN_ARG_INFO_EX(xls_insert_text_arginfo, 0, 0, 3) ZEND_ARG_INFO(0, format_handle) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(xls_insert_rtext_arginfo, 0, 0, 3) + ZEND_ARG_INFO(0, row) + ZEND_ARG_INFO(0, column) + ZEND_ARG_INFO(0, rich_strings) + ZEND_ARG_INFO(0, format_handle) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(xls_insert_date_arginfo, 0, 0, 3) ZEND_ARG_INFO(0, row) ZEND_ARG_INFO(0, column) @@ -700,6 +707,37 @@ PHP_METHOD(vtiful_xls, insertText) } /* }}} */ +/** {{{ \Vtiful\Kernel\Excel::insertRichText(int $row, int $column, array $richString[, resource $formatHandle]) + */ +PHP_METHOD(vtiful_xls, insertRichText) +{ + zend_long row = 0, column = 0; + zval *rich_strings = NULL, *format_handle = NULL; + + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_LONG(row) + Z_PARAM_LONG(column) + Z_PARAM_ARRAY(rich_strings) + Z_PARAM_OPTIONAL + Z_PARAM_RESOURCE_OR_NULL(format_handle) + ZEND_PARSE_PARAMETERS_END(); + + ZVAL_COPY(return_value, getThis()); + + xls_object *obj = Z_XLS_P(getThis()); + + WORKBOOK_NOT_INITIALIZED(obj); + + SHEET_LINE_SET(obj, row); + + if (format_handle != NULL) { + rich_string_writer(row, column, &obj->write_ptr, rich_strings, zval_get_format(format_handle)); + } else { + rich_string_writer(row, column, &obj->write_ptr, rich_strings, obj->format_ptr.format); + } +} +/* }}} */ + /** {{{ \Vtiful\Kernel\Excel::insertDate(int $row, int $column, int $timestamp[, string $format, resource $formatHandle]) */ PHP_METHOD(vtiful_xls, insertDate) @@ -1632,6 +1670,7 @@ zend_function_entry xls_methods[] = { PHP_ME(vtiful_xls, getHandle, xls_get_handle_arginfo, ZEND_ACC_PUBLIC) PHP_ME(vtiful_xls, autoFilter, xls_auto_filter_arginfo, ZEND_ACC_PUBLIC) PHP_ME(vtiful_xls, insertText, xls_insert_text_arginfo, ZEND_ACC_PUBLIC) + PHP_ME(vtiful_xls, insertRichText, xls_insert_rtext_arginfo, ZEND_ACC_PUBLIC) PHP_ME(vtiful_xls, insertDate, xls_insert_date_arginfo, ZEND_ACC_PUBLIC) PHP_ME(vtiful_xls, insertChart, xls_insert_chart_arginfo, ZEND_ACC_PUBLIC) PHP_ME(vtiful_xls, insertUrl, xls_insert_url_arginfo, ZEND_ACC_PUBLIC) diff --git a/kernel/format.c b/kernel/format.c index 15a1e53..bf9fc30 100644 --- a/kernel/format.c +++ b/kernel/format.c @@ -386,7 +386,6 @@ PHP_METHOD(vtiful_format, toResource) } /* }}} */ - /** {{{ format_methods */ zend_function_entry format_methods[] = { diff --git a/kernel/resource.c b/kernel/resource.c index c419035..ad03692 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -59,6 +59,19 @@ xls_resource_chart_t *zval_get_chart(zval *resource) } /* }}} */ +/* {{{ */ +lxw_rich_string_tuple *zval_get_rich_string(zval *resource) +{ + lxw_rich_string_tuple *res; + + if((res = (lxw_rich_string_tuple *)zend_fetch_resource(Z_RES_P(resource), VTIFUL_RESOURCE_NAME, le_xls_writer)) == NULL) { + zend_throw_exception(vtiful_exception_ce, "rich string resources resolution fail", 210); + } + + return res; +} +/* }}} */ + /* {{{ */ lxw_data_validation *zval_get_validation(zval *resource) { diff --git a/kernel/rich_string.c b/kernel/rich_string.c new file mode 100644 index 0000000..130b0bf --- /dev/null +++ b/kernel/rich_string.c @@ -0,0 +1,125 @@ +/* + +----------------------------------------------------------------------+ + | XlsWriter Extension | + +----------------------------------------------------------------------+ + | Copyright (c) 2017-2018 The Viest | + +----------------------------------------------------------------------+ + | http://www.viest.me | + +----------------------------------------------------------------------+ + | Author: viest | + +----------------------------------------------------------------------+ +*/ + +#include "xlswriter.h" + +zend_class_entry *vtiful_rich_string_ce; + +/* {{{ rich_string_objects_new + */ +static zend_object_handlers rich_string_handlers; + +static zend_always_inline void *vtiful_rich_string_object_alloc(size_t obj_size, zend_class_entry *ce) { + void *obj = emalloc(obj_size); + memset(obj, 0, obj_size); + return obj; +} + +PHP_VTIFUL_API zend_object *rich_string_objects_new(zend_class_entry *ce) +{ + rich_string_object *rich_string = vtiful_rich_string_object_alloc(sizeof(rich_string_object), ce); + + zend_object_std_init(&rich_string->zo, ce); + object_properties_init(&rich_string->zo, ce); + + rich_string->ptr.tuple = NULL; + rich_string->zo.handlers = &rich_string_handlers; + + return &rich_string->zo; +} +/* }}} */ + +/* {{{ rich_string_objects_free + */ +static void rich_string_objects_free(zend_object *object) +{ + rich_string_object *intern = php_vtiful_rich_string_fetch_object(object); + + if (intern->ptr.tuple != NULL) { + efree(intern->ptr.tuple); + intern->ptr.tuple = NULL; + } + + zend_object_std_dtor(&intern->zo); +} +/* }}} */ + +/* {{{ ARG_INFO + */ +ZEND_BEGIN_ARG_INFO_EX(rich_string_construct_arginfo, 0, 0, 1) + ZEND_ARG_INFO(0, text) + ZEND_ARG_INFO(0, format_handle) +ZEND_END_ARG_INFO() +/* }}} */ + +/** {{{ \Vtiful\Kernel\RichString::__construct(string $text, resource $format) + */ +PHP_METHOD(vtiful_rich_string, __construct) +{ + zend_string *text = NULL; + zval *format_handle = NULL; + rich_string_object *obj = NULL; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(text) + Z_PARAM_OPTIONAL + Z_PARAM_RESOURCE_OR_NULL(format_handle) + ZEND_PARSE_PARAMETERS_END(); + + ZVAL_COPY(return_value, getThis()); + + obj = Z_RICH_STR_P(getThis()); + + if (obj->ptr.tuple != NULL) { + return; + } + + lxw_rich_string_tuple *instance = (lxw_rich_string_tuple *)ecalloc(1, sizeof(lxw_rich_string_tuple)); + + zend_string *zstr = zend_string_copy(text); + + if (format_handle == NULL) { + instance->format = NULL; + instance->string = ZSTR_VAL(zstr); + } else { + instance->format = zval_get_format(format_handle); + instance->string = ZSTR_VAL(zstr); + } + + obj->ptr.tuple = instance; +} +/* }}} */ + +/** {{{ rich_string_methods +*/ +zend_function_entry rich_string_methods[] = { + PHP_ME(vtiful_rich_string, __construct, rich_string_construct_arginfo, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +/* }}} */ + +/** {{{ VTIFUL_STARTUP_FUNCTION +*/ +VTIFUL_STARTUP_FUNCTION(rich_string) { + zend_class_entry ce; + + INIT_NS_CLASS_ENTRY(ce, "Vtiful\\Kernel", "RichString", rich_string_methods); + ce.create_object = rich_string_objects_new; + vtiful_rich_string_ce = zend_register_internal_class(&ce); + + memcpy(&rich_string_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + rich_string_handlers.offset = XtOffsetOf(rich_string_object, zo); + rich_string_handlers.free_obj = rich_string_objects_free; + + return SUCCESS; +} +/* }}} */ \ No newline at end of file diff --git a/kernel/write.c b/kernel/write.c index 67b6e49..3228352 100644 --- a/kernel/write.c +++ b/kernel/write.c @@ -90,6 +90,49 @@ void type_writer(zval *value, zend_long row, zend_long columns, xls_resource_wri } } +/* + * Write the rich string to the file + */ +void rich_string_writer(zend_long row, zend_long columns, xls_resource_write_t *res, zval *rich_strings, lxw_format *format) +{ + int index = 0, resource_count = 0; + zval *zv_rich_string = NULL; + + lxw_col_t lxw_col = (lxw_col_t)columns; + lxw_row_t lxw_row = (lxw_row_t)row; + + if (Z_TYPE_P(rich_strings) != IS_ARRAY) { + return; + } + + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(rich_strings), zv_rich_string) + if (Z_TYPE_P(zv_rich_string) != IS_OBJECT) { + continue; + } + + if (!instanceof_function(Z_OBJCE_P(zv_rich_string), vtiful_rich_string_ce)) { + zend_throw_exception(vtiful_exception_ce, "The parameter must be an instance of Vtiful\\Kernel\\RichString.", 500); + return; + } + + resource_count++; + ZEND_HASH_FOREACH_END(); + + lxw_rich_string_tuple **rich_string_list = (lxw_rich_string_tuple **)ecalloc(resource_count + 1,sizeof(lxw_rich_string_tuple *)); + + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(rich_strings), zv_rich_string) + rich_string_object *obj = Z_RICH_STR_P(zv_rich_string); + rich_string_list[index] = obj->ptr.tuple; + index++; + ZEND_HASH_FOREACH_END(); + + rich_string_list[index] = NULL; + + WORKSHEET_WRITER_EXCEPTION(worksheet_write_rich_string(res->worksheet, lxw_row, lxw_col, rich_string_list, format)); + + efree(rich_string_list); +} + void format_copy(lxw_format *new_format, lxw_format *other_format) { new_format->bold = other_format->bold; diff --git a/tests/rich_string.phpt b/tests/rich_string.phpt new file mode 100644 index 0000000..2472a1b --- /dev/null +++ b/tests/rich_string.phpt @@ -0,0 +1,56 @@ +--TEST-- +Check for vtiful presence +--SKIPIF-- + +--FILE-- + './tests']; +$fileObject = new \Vtiful\Kernel\Excel($config); + +$fileObject = $fileObject->fileName("rich_string.xlsx") + ->header(['rich string']); + +$fileHandle = $fileObject->getHandle(); + +$format1 = new \Vtiful\Kernel\Format($fileHandle); +$colorRed = $format1->fontColor(\Vtiful\Kernel\Format::COLOR_GREEN)->toResource(); + +$format2 = new \Vtiful\Kernel\Format($fileHandle); +$colorOrange = $format2->fontColor(\Vtiful\Kernel\Format::COLOR_ORANGE)->toResource(); + +$richStringOne = new \Vtiful\Kernel\RichString('red ', $colorRed); +$richStringTwo = new \Vtiful\Kernel\RichString('orange', $colorOrange); + +$fileObject->insertRichText(1, 0, [ + $richStringOne, + $richStringTwo +]); + +$filePath = $fileObject->output(); + +$data = $fileObject->openFile('rich_string.xlsx') + ->openSheet() + ->getSheetData(); + +var_dump($filePath); +var_dump($data); +?> +--CLEAN-- + +--EXPECT-- +string(24) "./tests/rich_string.xlsx" +array(2) { + [0]=> + array(1) { + [0]=> + string(11) "rich string" + } + [1]=> + array(1) { + [0]=> + string(10) "red orange" + } +} + diff --git a/xlswriter.c b/xlswriter.c index b0aeb50..4ae129a 100644 --- a/xlswriter.c +++ b/xlswriter.c @@ -56,6 +56,7 @@ PHP_MINIT_FUNCTION(xlswriter) VTIFUL_STARTUP_MODULE(format); VTIFUL_STARTUP_MODULE(chart); VTIFUL_STARTUP_MODULE(validation); + VTIFUL_STARTUP_MODULE(rich_string); le_xls_writer = zend_register_list_destructors_ex(_php_vtiful_xls_close, NULL, VTIFUL_RESOURCE_NAME, module_number);