From 9e645b2a5c7b403c4b45dbb299abe5acaaf62aa4 Mon Sep 17 00:00:00 2001 From: John McNamara Date: Sat, 31 Dec 2016 02:35:38 +0000 Subject: [PATCH] Initial working chart line formatting. --- .indent.pro | 1 + include/xlsxwriter/chart.h | 13 ++ src/chart.c | 113 ++++++++++++++---- test/functional/src/test_chart_format01.c | 48 ++++++++ test/functional/src/test_chart_format02.c | 54 +++++++++ test/functional/test_chart_format.py | 20 ++++ .../functional/xlsx_files/chart_format01.xlsx | Bin 0 -> 9301 bytes .../functional/xlsx_files/chart_format02.xlsx | Bin 0 -> 9337 bytes 8 files changed, 223 insertions(+), 26 deletions(-) create mode 100644 test/functional/src/test_chart_format01.c create mode 100644 test/functional/src/test_chart_format02.c create mode 100644 test/functional/test_chart_format.py create mode 100644 test/functional/xlsx_files/chart_format01.xlsx create mode 100644 test/functional/xlsx_files/chart_format02.xlsx diff --git a/.indent.pro b/.indent.pro index fd6e35f4..45396d03 100644 --- a/.indent.pro +++ b/.indent.pro @@ -53,6 +53,7 @@ -T lxw_chart_font -T lxw_chart_legend -T lxw_chart_legend_position +-T lxw_chart_line -T lxw_chart_series -T lxw_chart_title -T lxw_chart_type diff --git a/include/xlsxwriter/chart.h b/include/xlsxwriter/chart.h index 3d343595..1e3637ad 100644 --- a/include/xlsxwriter/chart.h +++ b/include/xlsxwriter/chart.h @@ -225,6 +225,16 @@ typedef struct lxw_series_data_point { } lxw_series_data_point; +typedef struct lxw_chart_line { + + lxw_color_t color; + uint8_t none; + uint8_t dash_type; + uint8_t has_color; + float width; + +} lxw_chart_line; + typedef struct lxw_chart_fill { lxw_color_t color; @@ -305,6 +315,7 @@ typedef struct lxw_chart_series { lxw_series_range *categories; lxw_series_range *values; lxw_chart_title title; + lxw_chart_line *line; STAILQ_ENTRY (lxw_chart_series) list_pointers; @@ -605,6 +616,8 @@ void chart_series_set_name_range(lxw_chart_series *series, const char *sheetname, lxw_row_t row, lxw_col_t col); +void chart_series_set_line(lxw_chart_series *series, lxw_chart_line *line); + /** * @brief Set the name caption of the an axis. * diff --git a/src/chart.c b/src/chart.c index e177f072..89c21adc 100644 --- a/src/chart.c +++ b/src/chart.c @@ -56,6 +56,7 @@ _chart_series_free(lxw_chart_series *series) return; free(series->title.name); + free(series->line); _chart_free_range(series->categories); _chart_free_range(series->values); @@ -242,6 +243,28 @@ _chart_convert_font_args(lxw_chart_font *user_font) return font; } +/* + * Create a copy of a user supplied line. + */ +STATIC lxw_chart_line * +_chart_convert_line_args(lxw_chart_line *user_line) +{ + lxw_chart_line *line; + + if (!user_line) + return NULL; + + line = calloc(1, sizeof(struct lxw_chart_line)); + RETURN_ON_MEM_ERROR(line, NULL); + + memcpy(line, user_line, sizeof(lxw_chart_line)); + + if (line->color) + line->has_color = LXW_TRUE; + + return line; +} + /* * Add unique ids for primary or secondary axes. */ @@ -519,15 +542,14 @@ _chart_write_a_srgb_clr(lxw_chart *self, lxw_color_t color, * Write the element. */ STATIC void -_chart_write_a_solid_fill(lxw_chart *self, lxw_chart_fill *fill) +_chart_write_a_solid_fill(lxw_chart *self, lxw_color_t color, + uint8_t transparency) { lxw_xml_start_tag(self->file, "a:solidFill", NULL); - if (fill->has_color) { - /* Write the a:srgbClr element. */ - _chart_write_a_srgb_clr(self, fill->color, fill->transparency); - } + /* Write the a:srgbClr element. */ + _chart_write_a_srgb_clr(self, color, transparency); lxw_xml_end_tag(self->file, "a:solidFill"); } @@ -600,11 +622,7 @@ _chart_write_a_def_rpr(lxw_chart *self, lxw_chart_font *font) lxw_xml_start_tag(self->file, "a:defRPr", &attributes); if (has_color) { - lxw_chart_fill fill = { 0, 0, 0 }; - - fill.color = font->color; - fill.has_color = LXW_TRUE; - _chart_write_a_solid_fill(self, &fill); + _chart_write_a_solid_fill(self, font->color, LXW_FALSE); } if (has_latin) { @@ -676,11 +694,7 @@ _chart_write_a_r_pr(lxw_chart *self, lxw_chart_font *font) lxw_xml_start_tag(self->file, "a:rPr", &attributes); if (has_color) { - lxw_chart_fill fill = { 0, 0, 0 }; - - fill.color = font->color; - fill.has_color = LXW_TRUE; - _chart_write_a_solid_fill(self, &fill); + _chart_write_a_solid_fill(self, font->color, LXW_FALSE); } if (has_latin) { @@ -1330,19 +1344,35 @@ _chart_write_a_no_fill(lxw_chart *self) * Write the element. */ STATIC void -_chart_write_a_ln(lxw_chart *self) +_chart_write_a_ln(lxw_chart *self, lxw_chart_line *line) { struct xml_attribute_list attributes; struct xml_attribute *attribute; - char w[] = "28575"; + float width_flt = line->width; + uint32_t width_int; LXW_INIT_ATTRIBUTES(); - LXW_PUSH_ATTRIBUTES_STR("w", w); + + /* Round width to nearest 0.25, like Excel. */ + width_flt = (float) (uint32_t) ((line->width + 0.125) * 4.0) / 4.0; + + /* Convert to internal units. */ + width_int = (uint32_t) (0.5 + (12700.0 * width_flt)); + + if (width_int) + LXW_PUSH_ATTRIBUTES_INT("w", width_int); lxw_xml_start_tag(self->file, "a:ln", &attributes); - /* Write the a:noFill element. */ - _chart_write_a_no_fill(self); + /* Write the line fill. */ + if (line->none) { + /* Write the a:noFill element. */ + _chart_write_a_no_fill(self); + } + else if (line->has_color) { + /* Write the a:solidFill element. */ + _chart_write_a_solid_fill(self, line->color, LXW_FALSE); + } lxw_xml_end_tag(self->file, "a:ln"); @@ -1353,12 +1383,16 @@ _chart_write_a_ln(lxw_chart *self) * Write the element. */ STATIC void -_chart_write_sp_pr(lxw_chart *self) +_chart_write_sp_pr(lxw_chart *self, lxw_chart_series *series) { + + if (!series->line) + return; + lxw_xml_start_tag(self->file, "c:spPr", NULL); /* Write the a:ln element. */ - _chart_write_a_ln(self); + _chart_write_a_ln(self, series->line); lxw_xml_end_tag(self->file, "c:spPr"); } @@ -1627,6 +1661,9 @@ _chart_write_ser(lxw_chart *self, lxw_chart_series *series) /* Write the series name. */ _chart_write_series_name(self, series); + /* Write the c:spPr element. */ + _chart_write_sp_pr(self, series); + /* Write the c:marker element. */ _chart_write_marker(self); @@ -1656,10 +1693,11 @@ _chart_write_xval_ser(lxw_chart *self, lxw_chart_series *series) /* Write the c:order element. */ _chart_write_order(self, index); - if (self->type == LXW_CHART_SCATTER) { - /* Write the c:spPr element. */ - _chart_write_sp_pr(self); - } + /* Write the series name. */ + _chart_write_series_name(self, series); + + /* Write the c:spPr element. */ + _chart_write_sp_pr(self, series); if (self->type == LXW_CHART_SCATTER_STRAIGHT || self->type == LXW_CHART_SCATTER_SMOOTH) { @@ -2501,6 +2539,17 @@ _chart_write_scatter_chart(lxw_chart *self) _chart_write_scatter_style(self); STAILQ_FOREACH(series, self->series_list, list_pointers) { + + /* 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, 0x0, LXW_FALSE, 2.25 }; + series->line = _chart_convert_line_args(&line); + } + } + /* Write the c:ser element. */ _chart_write_xval_ser(self, series); } @@ -3043,6 +3092,18 @@ chart_series_set_values(lxw_chart_series *series, const char *sheetname, first_row, first_col, last_row, last_col); } +/* + * Set a line type for a series. + */ +void +chart_series_set_line(lxw_chart_series *series, lxw_chart_line *line) +{ + if (!line) + return; + + series->line = _chart_convert_line_args(line); +} + /* * Set an axis caption. */ diff --git a/test/functional/src/test_chart_format01.c b/test/functional/src/test_chart_format01.c new file mode 100644 index 00000000..35bb6272 --- /dev/null +++ b/test/functional/src/test_chart_format01.c @@ -0,0 +1,48 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = new_workbook("test_chart_format01.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_LINE); + + /* For testing, copy the randomly generated axis ids in the target file. */ + chart->axis_id_1 = 46335872; + chart->axis_id_2 = 46365696; + + uint8_t data[5][3] = { + {1, 2, 3}, + {2, 4, 6}, + {3, 6, 9}, + {4, 8, 12}, + {5, 10, 15} + }; + + int row, col; + for (row = 0; row < 5; row++) + for (col = 0; col < 3; col++) + worksheet_write_number(worksheet, row, col, data[row][col], NULL); + + chart_add_series(chart, + "=Sheet1!$A$1:$A$5", + "=Sheet1!$B$1:$B$5" + ); + + chart_add_series(chart, + "=Sheet1!$A$1:$A$5", + "=Sheet1!$C$1:$C$5" + ); + + worksheet_insert_chart(worksheet, CELL("E9"), chart); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_chart_format02.c b/test/functional/src/test_chart_format02.c new file mode 100644 index 00000000..eb671ee4 --- /dev/null +++ b/test/functional/src/test_chart_format02.c @@ -0,0 +1,54 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = new_workbook("test_chart_format02.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_LINE); + + /* For testing, copy the randomly generated axis ids in the target file. */ + chart->axis_id_1 = 46335872; + chart->axis_id_2 = 46365696; + + uint8_t data[5][3] = { + {1, 2, 3}, + {2, 4, 6}, + {3, 6, 9}, + {4, 8, 12}, + {5, 10, 15} + }; + + int row, col; + for (row = 0; row < 5; row++) + for (col = 0; col < 3; col++) + worksheet_write_number(worksheet, row, col, data[row][col], NULL); + + lxw_chart_series *series1 = chart_add_series(chart, + "=Sheet1!$A$1:$A$5", + "=Sheet1!$B$1:$B$5" + ); + + chart_add_series(chart, + "=Sheet1!$A$1:$A$5", + "=Sheet1!$C$1:$C$5" + ); + + /* 'line': { 'color': "red" }, */ + lxw_chart_line line = {.color = LXW_COLOR_RED}; + + chart_series_set_line(series1, &line); + + + worksheet_insert_chart(worksheet, CELL("E9"), chart); + + return workbook_close(workbook); +} diff --git a/test/functional/test_chart_format.py b/test/functional/test_chart_format.py new file mode 100644 index 00000000..fe4632b7 --- /dev/null +++ b/test/functional/test_chart_format.py @@ -0,0 +1,20 @@ +############################################################################### +# +# Tests for libxlsxwriter. +# +# Copyright 2014-2017, John McNamara, jmcnamara@cpan.org +# + +import base_test_class + +class TestCompareXLSXFiles(base_test_class.XLSXBaseTest): + """ + Test file created with libxlsxwriter against a file created by Excel. + + """ + + def test_chart_format01(self): + self.run_exe_test('test_chart_format01') + + def test_chart_format02(self): + self.run_exe_test('test_chart_format02') diff --git a/test/functional/xlsx_files/chart_format01.xlsx b/test/functional/xlsx_files/chart_format01.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..53d3c110db7be7f544f2bd7e1b5a272b40dffb2e GIT binary patch literal 9301 zcmeHN^;?wdx}Kp!LZn2x8w3QTq`OPHQ)1{4=`N+aJEXfE8b(^WBn9b4sWZCP-mb-R zpFiN7{rxcCeAheAea-tm_tT%E3@jWr00Dpm0077UDB{FNwm<;j8V&$>1Xu!Ui-2sM zOl_U?Ro(4O9d()AY^+Ih=76+W02t`+|9kzPJAwDA5cw`v%vOpcjOYqldkB#1Q*AJe z#8&njpDM0MwZ8h+{hfnZZW}7hNY(sAZuMGr-kO@^kLQsC^_Ysh+ST??jUH=k$JLj{ zcrq=Ym?s!wt0S*5#_a<7%_go|297@i*-Pp*s(HlkI-d^`5cme37;MXI2vd;jc7=J6 zO&h^q+jG2L9iK(w+^p&`m>8gWhFR-pO(uMeiFS0@wh}#>0w?mJa?X; zlUu!Db}xMr^Mia;yS8tIHx{LL_GyLQ)2E04z}+1zK=E(JSEt5GaRz0SEY!A80mDFj z2UBZD7UmzHq&b8CHM;*Z#ugDX0yVM+y$6!5BI{RQZ6%Si^IK#kTgX(2KTz(I)J5h} z3SOKC!1_rW&ySsd8F`_-w7+SoN0RHNyuqU!K)g66nu3L_381#Hx#U5ET=R;58hi?P_pMWzdJ8~i~X#EMLO zli*iKh#rU~0^j+*RX1?Rf!tU&J&h9OBLp)Z@j_fSM9ff&N9K!CgdtM?I45Zn4+pNu zjbSsW=X4ZK@9Mej$z?)EB|o3=Er&S@?(Fz7XId0rwhi;G76z=ynOquzdoWb>p=$dl z*CT`H>^?(Rn4nxI03ZR~tXck`yDP}S$_NCq`VngWq(0ma>O+Il|L^O4G~^Z3GmBjW z-ufqb^1Gnq$Mw&~3D@`B0Lb2vbSDIu#Y}nYulOwrN^;I3l_M-VQrft_Sj&>fZn}^c ziQdTS*VMl#pFC9aw9;Ey2vhpSmEXmWYOQMmF+eg3?eHheVpF3u^ z3ERYB`Gznu4`2I1FR`pls+zPml`L5QF z@qWh-bG6$uNvO`Tq5QyrDjv#@zcM2t`e&FDyO6y0f98=A92250M^7f`vjq9x5NmT+Z+y)T(h-7wf)nMi`A@NuE4UAhRTb#M+Ra%G>$Es!_1?&HsQA!%A7$14{*O%EIA zX3lvlgNE1Hc^hn)B0hC#@zpqZPM;JP=c2aV%Jd*hi&J^!JSXI+-Ft&*&r|sWw(G5w zZt#r`3LWoW z97{#LlWG*;+u%6K9$Fh zgL;t^-q^vVBAB}2@Sb>h(6sDNg-op?J+nBoK%Q4i++mc({Fo%z>o}qM?ExZ-_~?G= zBod-yC%Ww;LXbpn1>4f_r%9uyk46mooBCN^ej^b}ge;fp=|{UbZj~OZg2G1|q!%bG z1~`yvsT8V{7Q2KFspu#&Tv+;5b&sjcgQ}&yreR*!f7bkZzAZt%Vn4Tx9E>3Kiq?9f zGJCyNg=8gdLJ31xn2N7FKL(P4W&`B9*Ke6U7g=%gLgQcqv~Unoe{%g*EyMPK002T+`ZI z7EaPY74}a*i!OS)+zMTxf~u9`o=X34xPMjbPlp@uZV0856*J(#?*Q}McKkJFbU|Cl zXz3?JjqyE`iW0sy=nBKH&Hr#Bjuxh-PL6-Zo1YIqS^b|juP1s)e+Eo2x$?Kcsy-?N zftdaLKHHcl-k254y(m;#pgaDY9;A#*+W#1YIBnU;(Km?;N8^MmaUj_x`{3$zQ_(Po z(V)TthObxw_ta=1wu^Mtwxw`_Kolf#st;Kup-Kffgu|=6lQUCvIpFg>oK9X{gl`s0 zls$O&I&*K{h1KbBlQUZ81!G*~W&aQxma&ACf7}64N3il{f7VxNPqp{I53$TJ z0D#JqAGO?17wTkTYGcas^YiDHcQqA2^PJcn7&ESL+?-J*Yp2T>j!BzViK6ujOeG#Z znKbkC7y>_u>fuT_JR5dtn0GovFgY5cdUGdTs_A-!fXzmrkT^P*ed8s~A1uo~&lCs( zyva@J#G?%f;%nvzM}9Y1q7i=R$^0EKDj0+l8J$97;^!H%%KTQ`&u9n$!qQ2q5^2{V z7;lX_g_l#EzhVVPJw$z7k>P#!^_$A+*Ke_F!6R=;Zk?{T#*Vs3n3&90nBV%Zb|x-+ zoqw4o$$gRGb#i=!x8bJQefPbKrsMkB{W&I^1=(0QOXl_Y?m&2jz|BodAJw6auY}lT zl(=Y)_Bs;LA(9h&kUbR*g?&3t7p96iJi4z7@^%Ia(dV+~W~Pnk_K~hH1&?T9^)eS- zO^5=F3M`PnJTY7cTyUEcF-xP_iJZ`DxN(R3J8T-McM}=B(dcanH}0!z(}YoDN0sh7 zUyR~5+Gi(ZAdE_)&HHj07lpOAuk>}s`Q3Lv83Cm&Vlg6F89BB*3YsX~a2HEBujIt- z2jwo-HLiHh+zb=1g9)1@C6PW#2asTGCLQr)&=JN*6Rw8fpm{wit(5Ht>s*TID9|4| z3F1q%J&mrm9C| zVF_ogz5z{JMN@h=gBDzBcL(ll@i#vb@YT>?a$A~QbakHg*xA`H(Gjq!)5X>(YHN0{ z^sL&B$&Hsg4q)R{bQHIho}iJLVeIw9|FrWF7`+}`KpD_i+sNA}Xi$TITrQA9PZ+@M zOWd+)N$x8vg@{F^(>Xi*AaQ)kZEGT)|1sxjGI$C$bds24e#MzTjR?(w%?yJ4@f*8w zZ8>sU{OtGmING8co{z7zir5`XACJivJ2r06`(q@k`GXfyMgvK=bUzHrapzb~*>Qp+ zBI*|R5Yn1@+*ZhSDMk3Gi{I5QK?F)3)0kEnF~gF&NWaM&aWB*;`%soY&<<><;V$kk zZ}-#PY~wmo#s#CYY`5}9zhz{mrU@TYnzwp3ML>LPKYmVa_dc+X7FZ#(cxj0HVKJ&A z0PRX8cGl!uXKI=3Q!eCJb}EK@?Rk|dh*)Hk=n2_qXzcMpfbeHD40l?vA73k>%t(5p z(M@rY;@>`_`|!5hYQRMJjhd5-q3HZ@dqIBO2=+sNyEg&i*3{^%jrzg#TkHi`wluP0 zqbzpu1!Xn`3#)0=x<@dO&%4}aQXvx#r79c{6I3quG>E|x%Brtze)W@$;fgS+Z>Y-o z_`P@RYNuW>)9^dSdq5If2Y7KF{drdlK zxL^cgel1AJGyU*czBltK!)%mxR8IcsavDL*K^rIH?e=gVH9PIbl;Omv$@z#Fl(g!kMOXqP_b0Ck!qMEoO(a?l=BL@&gTtU+Eoc0dR8W#zIchxg21h0(x1l>w_jA= zDXp+y*$ab;M7#iXIN$~Zxd)vHIp1S!_@bDjT@Y z{D#&{vzphQ^2T3=k|DzbJ>eWKuMGR9T&(kp!8J50*g@2@LcQF*Zzk!z+*Wh1K5r2< zu4X=^l4Av%Q~73}5hn5ZtTzx2jhA)&HehG@cbDfvgS81X0Y&>GWH~yyTbnxm$U5^> zz^@iqG28HtF@!I~TTsz#s?tRa$)e%}>qkqi2lT)eCV5BI`_twoyi1EJ=xrJb3hbZ^ zv-Q=ZU7<8jD6k;*;> zRs(If8c1D|GcC%wn!#lR4zBug+`pJ)TF|JF(^Ay8w?D7umx7`$MwGL%udnY}Py%Bu zW6jaLpY#%i-+M<8n-6x)2~QDL>EiqW<~wMMj1!o%>W1GD#Fm z^#@JRlF5x#o#s`CX-`wzqGao96KidKfV3HZNn9*}xG$CRk$d$sGxqWGdILTo0eRuG|5(-?T{1;S!1 z%JQLj=Ek1lVXfj#1^iabGWnK8&^J;>xX%YjiibMtx8Ip=UkQvnx-5w?1&%A@62LX| z`)ID`8&dG4eP#<+*Lm=`zblx|>Uplh6qFwZVk513#=*Yu;fnVrZ~!kPWRdvFnW|-N0|=p3xoZf5s3+S zpU4iv2#i(W?P;37zVK*(aU|bIJo6l7d|iLIp}^q1_xr6bISJ$A`~1@NwGTI6nAnU= zzryBih*u~%O;+{c2Jp!F$OD{G0gC``TNpw_pBF34h_V*0$|-KPcK9>GJVb~r(JtGP zE!PI)1kZ~lt@s?ov^qj#!)hm}1;w-$$|>{=hb97dggQn{3Gb-Z3}%Z@J0f1@_NJ)g zd`o1@;C`$ipwSztZ%ef%9W9}i`QnvN%lBz9~T=ky{ybe zc+s-Bv@I1}t6Ov7aZ}@E(OtLnZqc;WB{Cmj8Pyo;Eqby)ZTcGjdIiZ`W&Z|~esHCS zVEb+P10l1x2lZv8o#IYy&FBIq*@0{MWeGgKsX79}VR*dHXZYM(lS6F?k*z!`ESSay zlk6L#4oBy^3~QWZFwZw-O1*s1^=#ECzU1`pdiuRF?c`xl3rAM||988vx=%B7v)^vOK+rTaJ6 z&8;6jkbhQp@GIHQjZl9%0JX9|3--ShIKN7ph}aQ@JXUO<1L~_69Bl{Ko59U+tZcno z;O@qjCEi0HaSJJJ#G3=pT6D%iL(tyzxyM0W#nGhpd=!NRMVzcfBML1rxm38HKHK|l z;kGreTZ0*Oi53~z6H%mMoW$I|;xxP%$SCB`fm~L$N8T%|CjuFgb|$sYS8d+FEo?R_ znD~I|T4XcBgEXuO3Cvr<+`h9fs>Xi4P;^}fZ`md)(#5n1dNuYqr8n?HT{dY>w(OQO z{+?@GepUE2V;0w4MCeBF@~EI~_6O7~6WZOZPmszZVkK5W)8*l%=1$^gbXn^ak)Fyo zL?I>hw8=S(c902;(hLJ(YcBy>r5??O2E7?cze5I{|`YgxtE`tGPYc%cVn>#Um8r#_|A;JYTF-G7VkD98crM zDz)_7q){Qw(z%KEy(7SxhhUV7X4SzA>KX6AO@1$_9135i;)C!}=Z#C`iQj7*qhJAF z461ESXiw}Rv}bApGFEf|*+JbY$ieiF(cFJqrBFKo1jOjebg^RhEW>w*40xoPm5GSV z`wkL!`x=NHF;Ti|S4l}@?Vaz*-Mv?*zD)8MzFBl+8Bj;mMASF1&R2UYiJc3;p3YHj zA#97=IV6CedP=2HhO@qfgj*H{36J24vw~F+=Q7ZN=RBk|r2{$Cx|V0-Yqui~Sg#We zhHKkUI^J5kG#Xy}?*Uj=-9o9jsgsNwVS|Q5MJbdIk!RneG$SIt%|<{T)RZOn^KM0t zsP*PN9sEeg;A(v>Z@lna`%oxt0a077x85d~bKCgx8m_~JIP*%aOWL_%)hA5WZV-*N zQ0^$}g1)Op*tIo9v*E~fNuAX8UGA6G{pX&$Km_tbp)Jg-yjE)0iwy#t@T4eKd0_{Y zUOKso?aAY|Cs(B%sGQjA+}HX>1Tm&kMk#q5)aw};J*mLxt{m3)}{2)Pfr97h7GnD|TmKy4bDn(5! z(J9IE(_EW8`{wk5M+2FcXQ6S;{4VWVWVzwlSP(Hf6eP|mWZfSXjmxasw)@oZ18|8`N|#Jj6GidUF`F@EfV|1UKr%Si1|RWclE6 zO#p>ZQFgl&D9Fe`=i;=Zz_}KdMBqRn)VHo#W`Nf!M8CL$H5Gj5=sF*wGWXXM3V0&Zc1K4@ZfYrCsF0#>e9q7%W9lK4^ z4S9al&w3=!-Qo*sb{yssNCv(B(iW=Aw$k0HeL56~_eT244TLnj*2-L>YsL1d)wjkA z`ad#E&iw}po~;1Rn!bPxHEvH%yUrZyXun19zL<_{y*9o1_hc0a!vxJbfB#~`zqIwQ z`)^)zP?Y&Qz~6_n{}TLh|IthT%c%Cg;NK@*e-*TZHs}8H-0MEh{fWpgq)_O)8~5fT z_l56w_J0ZABL5WrZJ+->%Kc{YFO)IVzoYzz*7AM8`wiM(fWr^|y{G@JU3(w!e$Vq4 z;74dZ2|c9y9nkv#_bbp}0N&6N1`6=29=$JmzufsHDu?@1^dH5~KM?MvbH5NO9^XHX zU&-Bll)tA5zc2uRbZ95|A4$V~@xOcezluL5{)_mZeqT`r9?Ie$)jAr$0%{1;q(7ei E2UOl0`Tzg` literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/chart_format02.xlsx b/test/functional/xlsx_files/chart_format02.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..6431ea3d3f4cbcd3062d0f670f5c296c819da448 GIT binary patch literal 9337 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 x3BND^fHp+8_#ZjLP4T~b`oD?`lK(~gPrt7SLP4