write_xlsx 0.0.4 → 0.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (688) hide show
  1. data/Gemfile +4 -2
  2. data/README.rdoc +14 -12
  3. data/Rakefile +8 -8
  4. data/VERSION +1 -1
  5. data/examples/chart_secondary_axis.rb +55 -0
  6. data/examples/date_time.rb +84 -0
  7. data/examples/panes.rb +108 -0
  8. data/examples/regions.rb +49 -0
  9. data/examples/shape1.rb +36 -0
  10. data/examples/shape2.rb +53 -0
  11. data/examples/shape3.rb +32 -0
  12. data/examples/shape4.rb +55 -0
  13. data/examples/shape5.rb +48 -0
  14. data/examples/shape6.rb +48 -0
  15. data/examples/shape7.rb +74 -0
  16. data/examples/shape8.rb +73 -0
  17. data/examples/shape_all.rb +241 -0
  18. data/examples/stats.rb +69 -0
  19. data/examples/stats_ext.rb +77 -0
  20. data/examples/stocks.rb +73 -0
  21. data/examples/tables.rb +360 -0
  22. data/lib/write_xlsx.rb +25 -1
  23. data/lib/write_xlsx/chart.rb +656 -260
  24. data/lib/write_xlsx/chart/area.rb +41 -7
  25. data/lib/write_xlsx/chart/bar.rb +16 -17
  26. data/lib/write_xlsx/chart/column.rb +5 -15
  27. data/lib/write_xlsx/chart/line.rb +15 -6
  28. data/lib/write_xlsx/chart/pie.rb +7 -21
  29. data/lib/write_xlsx/chart/scatter.rb +90 -20
  30. data/lib/write_xlsx/chart/stock.rb +24 -43
  31. data/lib/write_xlsx/chartsheet.rb +29 -11
  32. data/lib/write_xlsx/drawing.rb +354 -35
  33. data/lib/write_xlsx/format.rb +72 -21
  34. data/lib/write_xlsx/package/app.rb +2 -5
  35. data/lib/write_xlsx/package/comments.rb +5 -1
  36. data/lib/write_xlsx/package/content_types.rb +10 -0
  37. data/lib/write_xlsx/package/packager.rb +41 -7
  38. data/lib/write_xlsx/package/relationships.rb +1 -1
  39. data/lib/write_xlsx/package/shared_strings.rb +36 -8
  40. data/lib/write_xlsx/package/styles.rb +12 -9
  41. data/lib/write_xlsx/package/table.rb +194 -0
  42. data/lib/write_xlsx/package/vml.rb +26 -27
  43. data/lib/write_xlsx/package/xml_writer_simple.rb +25 -4
  44. data/lib/write_xlsx/shape.rb +172 -0
  45. data/lib/write_xlsx/utility.rb +20 -0
  46. data/lib/write_xlsx/workbook.rb +499 -141
  47. data/lib/write_xlsx/worksheet.rb +1162 -153
  48. data/test/chart/test_add_series.rb +43 -4
  49. data/test/chart/test_write_d_lbls.rb +274 -0
  50. data/test/chart/test_write_major_gridlines.rb +1 -1
  51. data/test/chartsheet/test_chartsheet01.rb +31 -0
  52. data/test/drawing/test_drawing_shape_01.rb +68 -0
  53. data/test/drawing/test_drawing_shape_02.rb +73 -0
  54. data/test/drawing/test_drawing_shape_03.rb +38 -0
  55. data/test/drawing/test_drawing_shape_04.rb +121 -0
  56. data/test/drawing/test_drawing_shape_05.rb +45 -0
  57. data/test/drawing/test_drawing_shape_06.rb +42 -0
  58. data/test/drawing/test_drawing_shape_07.rb +47 -0
  59. data/test/drawing/test_write_a_graphic_frame_locks.rb +18 -0
  60. data/test/drawing/test_write_c_chart.rb +18 -0
  61. data/test/drawing/test_write_c_nv_graphic_frame_pr.rb +28 -0
  62. data/test/drawing/test_write_c_nv_pr.rb +18 -0
  63. data/test/drawing/test_write_col.rb +18 -0
  64. data/test/drawing/test_write_col_off.rb +18 -0
  65. data/test/drawing/test_write_ext.rb +18 -0
  66. data/test/drawing/test_write_pos.rb +18 -0
  67. data/test/drawing/test_write_row.rb +18 -0
  68. data/test/drawing/test_write_row_off.rb +18 -0
  69. data/test/drawing/test_write_xfrm_extension.rb +18 -0
  70. data/test/drawing/test_write_xfrm_offset.rb +18 -0
  71. data/test/helper.rb +93 -50
  72. data/test/package/shared_strings/test_write_si.rb +10 -1
  73. data/test/package/table/test_table01.rb +42 -0
  74. data/test/package/table/test_table02.rb +44 -0
  75. data/test/package/table/test_table03.rb +48 -0
  76. data/test/package/table/test_table04.rb +46 -0
  77. data/test/package/table/test_table05.rb +46 -0
  78. data/test/package/table/test_table06.rb +52 -0
  79. data/test/package/table/test_table07.rb +47 -0
  80. data/test/package/table/test_table08.rb +53 -0
  81. data/test/package/table/test_table09.rb +65 -0
  82. data/test/package/table/test_table10.rb +45 -0
  83. data/test/package/table/test_table11.rb +60 -0
  84. data/test/package/table/test_table12.rb +60 -0
  85. data/test/package/table/test_write_auto_filter.rb +16 -0
  86. data/test/package/table/test_write_table_column.rb +15 -0
  87. data/test/package/table/test_write_table_style_info.rb +25 -0
  88. data/test/package/table/test_write_xml_declaration.rb +15 -0
  89. data/test/perl_output/a_simple.xlsx +0 -0
  90. data/test/perl_output/array_formula.xlsx +0 -0
  91. data/test/perl_output/chart_secondary_axis.xlsx +0 -0
  92. data/test/perl_output/data_validate.xlsx +0 -0
  93. data/test/perl_output/date_time.xlsx +0 -0
  94. data/test/perl_output/demo.xlsx +0 -0
  95. data/test/perl_output/formats.xlsx +0 -0
  96. data/test/perl_output/headers.xlsx +0 -0
  97. data/test/perl_output/outline_collapsed.xlsx +0 -0
  98. data/test/perl_output/panes.xlsx +0 -0
  99. data/test/perl_output/regions.xlsx +0 -0
  100. data/test/perl_output/shape1.xlsx +0 -0
  101. data/test/perl_output/shape2.xlsx +0 -0
  102. data/test/perl_output/shape3.xlsx +0 -0
  103. data/test/perl_output/shape4.xlsx +0 -0
  104. data/test/perl_output/shape5.xlsx +0 -0
  105. data/test/perl_output/shape6.xlsx +0 -0
  106. data/test/perl_output/shape7.xlsx +0 -0
  107. data/test/perl_output/shape8.xlsx +0 -0
  108. data/test/perl_output/shape_all.xlsx +0 -0
  109. data/test/perl_output/stats.xlsx +0 -0
  110. data/test/perl_output/stats_ext.xlsx +0 -0
  111. data/test/perl_output/stocks.xlsx +0 -0
  112. data/test/perl_output/tables.xlsx +0 -0
  113. data/test/regression/images/blue.jpg +0 -0
  114. data/test/regression/images/blue.png +0 -0
  115. data/test/regression/images/grey.jpg +0 -0
  116. data/test/regression/images/grey.png +0 -0
  117. data/test/regression/images/red.bmp +0 -0
  118. data/test/regression/images/red.jpg +0 -0
  119. data/test/regression/images/red.png +0 -0
  120. data/test/regression/images/yellow.jpg +0 -0
  121. data/test/regression/images/yellow.png +0 -0
  122. data/test/regression/test_array_formula01.rb +31 -0
  123. data/test/regression/test_array_formula02.rb +32 -0
  124. data/test/regression/test_autofilter00.rb +84 -0
  125. data/test/regression/test_autofilter01.rb +86 -0
  126. data/test/regression/test_autofilter02.rb +104 -0
  127. data/test/regression/test_autofilter03.rb +104 -0
  128. data/test/regression/test_autofilter04.rb +107 -0
  129. data/test/regression/test_autofilter05.rb +108 -0
  130. data/test/regression/test_autofilter06.rb +108 -0
  131. data/test/regression/test_autofilter07.rb +107 -0
  132. data/test/regression/test_chart_area01.rb +45 -0
  133. data/test/regression/test_chart_area02.rb +45 -0
  134. data/test/regression/test_chart_area03.rb +45 -0
  135. data/test/regression/test_chart_axis01.rb +46 -0
  136. data/test/regression/test_chart_axis02.rb +46 -0
  137. data/test/regression/test_chart_axis03.rb +68 -0
  138. data/test/regression/test_chart_axis04.rb +53 -0
  139. data/test/regression/test_chart_axis05.rb +48 -0
  140. data/test/regression/test_chart_axis06.rb +50 -0
  141. data/test/regression/test_chart_axis07.rb +54 -0
  142. data/test/regression/test_chart_axis08.rb +53 -0
  143. data/test/regression/test_chart_axis09.rb +47 -0
  144. data/test/regression/test_chart_axis10.rb +53 -0
  145. data/test/regression/test_chart_axis11.rb +47 -0
  146. data/test/regression/test_chart_axis12.rb +47 -0
  147. data/test/regression/test_chart_axis13.rb +53 -0
  148. data/test/regression/test_chart_axis14.rb +67 -0
  149. data/test/regression/test_chart_axis15.rb +48 -0
  150. data/test/regression/test_chart_axis16.rb +70 -0
  151. data/test/regression/test_chart_axis17.rb +46 -0
  152. data/test/regression/test_chart_axis18.rb +44 -0
  153. data/test/regression/test_chart_axis19.rb +46 -0
  154. data/test/regression/test_chart_axis20.rb +46 -0
  155. data/test/regression/test_chart_axis21.rb +50 -0
  156. data/test/regression/test_chart_bar01.rb +44 -0
  157. data/test/regression/test_chart_bar02.rb +55 -0
  158. data/test/regression/test_chart_bar03.rb +67 -0
  159. data/test/regression/test_chart_bar04.rb +70 -0
  160. data/test/regression/test_chart_bar05.rb +43 -0
  161. data/test/regression/test_chart_bar06.rb +50 -0
  162. data/test/regression/test_chart_bar07.rb +54 -0
  163. data/test/regression/test_chart_bar08.rb +44 -0
  164. data/test/regression/test_chart_bar09.rb +43 -0
  165. data/test/regression/test_chart_bar10.rb +47 -0
  166. data/test/regression/test_chart_bar11.rb +71 -0
  167. data/test/regression/test_chart_bar12.rb +43 -0
  168. data/test/regression/test_chart_bar13.rb +53 -0
  169. data/test/regression/test_chart_bar14.rb +63 -0
  170. data/test/regression/test_chart_bar15.rb +53 -0
  171. data/test/regression/test_chart_bar16.rb +51 -0
  172. data/test/regression/test_chart_bar17.rb +47 -0
  173. data/test/regression/test_chart_bar18.rb +58 -0
  174. data/test/regression/test_chart_bar19.rb +50 -0
  175. data/test/regression/test_chart_bar20.rb +47 -0
  176. data/test/regression/test_chart_bar21.rb +53 -0
  177. data/test/regression/test_chart_bar22.rb +68 -0
  178. data/test/regression/test_chart_bar23.rb +62 -0
  179. data/test/regression/test_chart_bar24.rb +46 -0
  180. data/test/regression/test_chart_blank01.rb +41 -0
  181. data/test/regression/test_chart_blank02.rb +41 -0
  182. data/test/regression/test_chart_blank03.rb +41 -0
  183. data/test/regression/test_chart_blank04.rb +41 -0
  184. data/test/regression/test_chart_blank05.rb +46 -0
  185. data/test/regression/test_chart_blank06.rb +41 -0
  186. data/test/regression/test_chart_column01.rb +43 -0
  187. data/test/regression/test_chart_column02.rb +45 -0
  188. data/test/regression/test_chart_column03.rb +45 -0
  189. data/test/regression/test_chart_column04.rb +45 -0
  190. data/test/regression/test_chart_column05.rb +39 -0
  191. data/test/regression/test_chart_column06.rb +46 -0
  192. data/test/regression/test_chart_crossing01.rb +50 -0
  193. data/test/regression/test_chart_crossing02.rb +48 -0
  194. data/test/regression/test_chart_crossing03.rb +53 -0
  195. data/test/regression/test_chart_crossing04.rb +53 -0
  196. data/test/regression/test_chart_format01.rb +44 -0
  197. data/test/regression/test_chart_format02.rb +45 -0
  198. data/test/regression/test_chart_format03.rb +46 -0
  199. data/test/regression/test_chart_format04.rb +45 -0
  200. data/test/regression/test_chart_format05.rb +45 -0
  201. data/test/regression/test_chart_format06.rb +52 -0
  202. data/test/regression/test_chart_format07.rb +57 -0
  203. data/test/regression/test_chart_format08.rb +52 -0
  204. data/test/regression/test_chart_format09.rb +56 -0
  205. data/test/regression/test_chart_format10.rb +59 -0
  206. data/test/regression/test_chart_format11.rb +63 -0
  207. data/test/regression/test_chart_format12.rb +60 -0
  208. data/test/regression/test_chart_format13.rb +52 -0
  209. data/test/regression/test_chart_format14.rb +56 -0
  210. data/test/regression/test_chart_format15.rb +54 -0
  211. data/test/regression/test_chart_format16.rb +57 -0
  212. data/test/regression/test_chart_format17.rb +40 -0
  213. data/test/regression/test_chart_format18.rb +44 -0
  214. data/test/regression/test_chart_line01.rb +39 -0
  215. data/test/regression/test_chart_line02.rb +45 -0
  216. data/test/regression/test_chart_name01.rb +46 -0
  217. data/test/regression/test_chart_name02.rb +58 -0
  218. data/test/regression/test_chart_name03.rb +58 -0
  219. data/test/regression/test_chart_pie01.rb +36 -0
  220. data/test/regression/test_chart_scatter01.rb +45 -0
  221. data/test/regression/test_chart_scatter02.rb +49 -0
  222. data/test/regression/test_chart_scatter03.rb +49 -0
  223. data/test/regression/test_chart_scatter04.rb +49 -0
  224. data/test/regression/test_chart_scatter05.rb +49 -0
  225. data/test/regression/test_chart_scatter06.rb +51 -0
  226. data/test/regression/test_chart_scatter07.rb +60 -0
  227. data/test/regression/test_chart_sparse01.rb +54 -0
  228. data/test/regression/test_chart_stock01.rb +72 -0
  229. data/test/regression/test_chart_stock02.rb +73 -0
  230. data/test/regression/test_chart_str01.rb +48 -0
  231. data/test/regression/test_chart_str02.rb +52 -0
  232. data/test/regression/test_chartsheet01.rb +46 -0
  233. data/test/regression/test_chartsheet02.rb +50 -0
  234. data/test/regression/test_chartsheet03.rb +48 -0
  235. data/test/regression/test_chartsheet04.rb +48 -0
  236. data/test/regression/test_chartsheet05.rb +48 -0
  237. data/test/regression/test_chartsheet06.rb +48 -0
  238. data/test/regression/test_chartsheet07.rb +57 -0
  239. data/test/regression/test_chartsheet08.rb +61 -0
  240. data/test/regression/test_chartsheet09.rb +52 -0
  241. data/test/regression/test_comment01.rb +27 -0
  242. data/test/regression/test_comment02.rb +28 -0
  243. data/test/regression/test_comment03.rb +32 -0
  244. data/test/regression/test_comment04.rb +37 -0
  245. data/test/regression/test_comment06.rb +34 -0
  246. data/test/regression/test_comment07.rb +36 -0
  247. data/test/regression/test_comment08.rb +36 -0
  248. data/test/regression/test_comment09.rb +32 -0
  249. data/test/regression/test_comment10.rb +31 -0
  250. data/test/regression/test_cond_format01.rb +46 -0
  251. data/test/regression/test_cond_format02.rb +41 -0
  252. data/test/regression/test_cond_format03.rb +52 -0
  253. data/test/regression/test_cond_format04.rb +51 -0
  254. data/test/regression/test_cond_format05.rb +41 -0
  255. data/test/regression/test_cond_format06.rb +45 -0
  256. data/test/regression/test_cond_format07.rb +62 -0
  257. data/test/regression/test_cond_format08.rb +46 -0
  258. data/test/regression/test_cond_format10.rb +43 -0
  259. data/test/regression/test_cond_format11.rb +47 -0
  260. data/test/regression/test_cond_format12.rb +47 -0
  261. data/test/regression/test_custom_colors01.rb +33 -0
  262. data/test/regression/test_date_1904_01.rb +36 -0
  263. data/test/regression/test_date_1904_02.rb +39 -0
  264. data/test/regression/test_defined_name01.rb +47 -0
  265. data/test/regression/test_escapes01.rb +37 -0
  266. data/test/regression/test_escapes02.rb +33 -0
  267. data/test/regression/test_escapes03.rb +34 -0
  268. data/test/regression/test_fit_to_pages01.rb +37 -0
  269. data/test/regression/test_fit_to_pages02.rb +37 -0
  270. data/test/regression/test_fit_to_pages03.rb +37 -0
  271. data/test/regression/test_fit_to_pages04.rb +37 -0
  272. data/test/regression/test_fit_to_pages05.rb +37 -0
  273. data/test/regression/test_format01.rb +35 -0
  274. data/test/regression/test_format02.rb +47 -0
  275. data/test/regression/test_format03.rb +31 -0
  276. data/test/regression/test_format04.rb +41 -0
  277. data/test/regression/test_gridlines01.rb +36 -0
  278. data/test/regression/test_hyperlink01.rb +23 -0
  279. data/test/regression/test_hyperlink02.rb +27 -0
  280. data/test/regression/test_hyperlink03.rb +32 -0
  281. data/test/regression/test_hyperlink04.rb +31 -0
  282. data/test/regression/test_hyperlink05.rb +26 -0
  283. data/test/regression/test_hyperlink06.rb +25 -0
  284. data/test/regression/test_hyperlink07.rb +24 -0
  285. data/test/regression/test_hyperlink08.rb +24 -0
  286. data/test/regression/test_hyperlink09.rb +25 -0
  287. data/test/regression/test_hyperlink10.rb +24 -0
  288. data/test/regression/test_hyperlink11.rb +24 -0
  289. data/test/regression/test_hyperlink12.rb +25 -0
  290. data/test/regression/test_hyperlink13.rb +24 -0
  291. data/test/regression/test_hyperlink14.rb +24 -0
  292. data/test/regression/test_hyperlink15.rb +26 -0
  293. data/test/regression/test_hyperlink16.rb +26 -0
  294. data/test/regression/test_hyperlink17.rb +27 -0
  295. data/test/regression/test_hyperlink18.rb +27 -0
  296. data/test/regression/test_image01.rb +23 -0
  297. data/test/regression/test_image02.rb +23 -0
  298. data/test/regression/test_image03.rb +23 -0
  299. data/test/regression/test_image04.rb +23 -0
  300. data/test/regression/test_image05.rb +26 -0
  301. data/test/regression/test_image06.rb +36 -0
  302. data/test/regression/test_image07.rb +25 -0
  303. data/test/regression/test_outline01.rb +86 -0
  304. data/test/regression/test_outline02.rb +89 -0
  305. data/test/regression/test_outline03.rb +59 -0
  306. data/test/regression/test_outline04.rb +55 -0
  307. data/test/regression/test_outline05.rb +90 -0
  308. data/test/regression/test_outline06.rb +89 -0
  309. data/test/regression/test_page_breaks01.rb +36 -0
  310. data/test/regression/test_page_breaks02.rb +36 -0
  311. data/test/regression/test_page_breaks03.rb +36 -0
  312. data/test/regression/test_page_breaks04.rb +36 -0
  313. data/test/regression/test_page_breaks05.rb +36 -0
  314. data/test/regression/test_page_breaks06.rb +37 -0
  315. data/test/regression/test_page_view01.rb +36 -0
  316. data/test/regression/test_panes01.rb +66 -0
  317. data/test/regression/test_print_across01.rb +37 -0
  318. data/test/regression/test_print_area01.rb +36 -0
  319. data/test/regression/test_print_area02.rb +36 -0
  320. data/test/regression/test_print_area03.rb +36 -0
  321. data/test/regression/test_print_area04.rb +36 -0
  322. data/test/regression/test_print_area05.rb +36 -0
  323. data/test/regression/test_print_area06.rb +36 -0
  324. data/test/regression/test_print_area07.rb +37 -0
  325. data/test/regression/test_print_options01.rb +36 -0
  326. data/test/regression/test_print_options02.rb +36 -0
  327. data/test/regression/test_print_options03.rb +36 -0
  328. data/test/regression/test_print_options04.rb +36 -0
  329. data/test/regression/test_print_options05.rb +39 -0
  330. data/test/regression/test_print_options06.rb +37 -0
  331. data/test/regression/test_print_scale01.rb +37 -0
  332. data/test/regression/test_print_scale02.rb +37 -0
  333. data/test/regression/test_properties01.rb +40 -0
  334. data/test/regression/test_repeat01.rb +36 -0
  335. data/test/regression/test_repeat02.rb +36 -0
  336. data/test/regression/test_repeat03.rb +37 -0
  337. data/test/regression/test_repeat04.rb +36 -0
  338. data/test/regression/test_repeat05.rb +43 -0
  339. data/test/regression/test_rich_string01.rb +28 -0
  340. data/test/regression/test_rich_string02.rb +28 -0
  341. data/test/regression/test_rich_string03.rb +28 -0
  342. data/test/regression/test_rich_string04.rb +28 -0
  343. data/test/regression/test_rich_string05.rb +30 -0
  344. data/test/regression/test_rich_string06.rb +27 -0
  345. data/test/regression/test_rich_string07.rb +33 -0
  346. data/test/regression/test_rich_string08.rb +29 -0
  347. data/test/regression/test_rich_string09.rb +31 -0
  348. data/test/regression/test_rich_string10.rb +30 -0
  349. data/test/regression/test_rich_string11.rb +29 -0
  350. data/test/regression/test_row_col_format01.rb +24 -0
  351. data/test/regression/test_row_col_format02.rb +25 -0
  352. data/test/regression/test_row_col_format03.rb +24 -0
  353. data/test/regression/test_row_col_format04.rb +25 -0
  354. data/test/regression/test_row_col_format05.rb +26 -0
  355. data/test/regression/test_row_col_format06.rb +29 -0
  356. data/test/regression/test_row_col_format07.rb +24 -0
  357. data/test/regression/test_row_col_format08.rb +34 -0
  358. data/test/regression/test_row_col_format09.rb +33 -0
  359. data/test/regression/test_row_col_format10.rb +24 -0
  360. data/test/regression/test_row_col_format11.rb +23 -0
  361. data/test/regression/test_row_col_format12.rb +23 -0
  362. data/test/regression/test_row_col_format13.rb +28 -0
  363. data/test/regression/test_row_col_format14.rb +28 -0
  364. data/test/regression/test_shape_connect01.rb +46 -0
  365. data/test/regression/test_shape_connect02.rb +42 -0
  366. data/test/regression/test_shape_connect03.rb +78 -0
  367. data/test/regression/test_shape_connect04.rb +75 -0
  368. data/test/regression/test_shape_scale01.rb +47 -0
  369. data/test/regression/test_shape_stencil01.rb +44 -0
  370. data/test/regression/test_shared_strings01.rb +34 -0
  371. data/test/regression/test_shared_strings02.rb +47 -0
  372. data/test/regression/test_simple01.rb +24 -0
  373. data/test/regression/test_simple02.rb +32 -0
  374. data/test/regression/test_tab_color01.rb +29 -0
  375. data/test/regression/test_table01.rb +27 -0
  376. data/test/regression/test_table02.rb +37 -0
  377. data/test/regression/test_table03.rb +30 -0
  378. data/test/regression/test_table04.rb +34 -0
  379. data/test/regression/test_table05.rb +38 -0
  380. data/test/regression/test_table06.rb +41 -0
  381. data/test/regression/test_table07.rb +29 -0
  382. data/test/regression/test_table08.rb +48 -0
  383. data/test/regression/test_table09.rb +65 -0
  384. data/test/regression/test_table10.rb +68 -0
  385. data/test/regression/test_table11.rb +37 -0
  386. data/test/regression/test_table12.rb +36 -0
  387. data/test/regression/test_table13.rb +51 -0
  388. data/test/regression/test_table14.rb +51 -0
  389. data/test/regression/xlsx_files/array_formula01.xlsx +0 -0
  390. data/test/regression/xlsx_files/array_formula02.xlsx +0 -0
  391. data/test/regression/xlsx_files/autofilter00.xlsx +0 -0
  392. data/test/regression/xlsx_files/autofilter01.xlsx +0 -0
  393. data/test/regression/xlsx_files/autofilter02.xlsx +0 -0
  394. data/test/regression/xlsx_files/autofilter03.xlsx +0 -0
  395. data/test/regression/xlsx_files/autofilter04.xlsx +0 -0
  396. data/test/regression/xlsx_files/autofilter05.xlsx +0 -0
  397. data/test/regression/xlsx_files/autofilter06.xlsx +0 -0
  398. data/test/regression/xlsx_files/autofilter07.xlsx +0 -0
  399. data/test/regression/xlsx_files/chart_area01.xlsx +0 -0
  400. data/test/regression/xlsx_files/chart_area02.xlsx +0 -0
  401. data/test/regression/xlsx_files/chart_area03.xlsx +0 -0
  402. data/test/regression/xlsx_files/chart_area04.xlsx +0 -0
  403. data/test/regression/xlsx_files/chart_axis01.xlsx +0 -0
  404. data/test/regression/xlsx_files/chart_axis02.xlsx +0 -0
  405. data/test/regression/xlsx_files/chart_axis03.xlsx +0 -0
  406. data/test/regression/xlsx_files/chart_axis04.xlsx +0 -0
  407. data/test/regression/xlsx_files/chart_axis05.xlsx +0 -0
  408. data/test/regression/xlsx_files/chart_axis06.xlsx +0 -0
  409. data/test/regression/xlsx_files/chart_axis07.xlsx +0 -0
  410. data/test/regression/xlsx_files/chart_axis08.xlsx +0 -0
  411. data/test/regression/xlsx_files/chart_axis09.xlsx +0 -0
  412. data/test/regression/xlsx_files/chart_axis10.xlsx +0 -0
  413. data/test/regression/xlsx_files/chart_axis11.xlsx +0 -0
  414. data/test/regression/xlsx_files/chart_axis12.xlsx +0 -0
  415. data/test/regression/xlsx_files/chart_axis13.xlsx +0 -0
  416. data/test/regression/xlsx_files/chart_axis14.xlsx +0 -0
  417. data/test/regression/xlsx_files/chart_axis15.xlsx +0 -0
  418. data/test/regression/xlsx_files/chart_axis16.xlsx +0 -0
  419. data/test/regression/xlsx_files/chart_axis17.xlsx +0 -0
  420. data/test/regression/xlsx_files/chart_axis18.xlsx +0 -0
  421. data/test/regression/xlsx_files/chart_axis19.xlsx +0 -0
  422. data/test/regression/xlsx_files/chart_axis20.xlsx +0 -0
  423. data/test/regression/xlsx_files/chart_axis21.xlsx +0 -0
  424. data/test/regression/xlsx_files/chart_bar01.xlsx +0 -0
  425. data/test/regression/xlsx_files/chart_bar02.xlsx +0 -0
  426. data/test/regression/xlsx_files/chart_bar03.xlsx +0 -0
  427. data/test/regression/xlsx_files/chart_bar04.xlsx +0 -0
  428. data/test/regression/xlsx_files/chart_bar05.xlsx +0 -0
  429. data/test/regression/xlsx_files/chart_bar06.xlsx +0 -0
  430. data/test/regression/xlsx_files/chart_bar07.xlsx +0 -0
  431. data/test/regression/xlsx_files/chart_bar08.xlsx +0 -0
  432. data/test/regression/xlsx_files/chart_bar09.xlsx +0 -0
  433. data/test/regression/xlsx_files/chart_bar10.xlsx +0 -0
  434. data/test/regression/xlsx_files/chart_bar11.xlsx +0 -0
  435. data/test/regression/xlsx_files/chart_bar12.xlsx +0 -0
  436. data/test/regression/xlsx_files/chart_bar13.xlsx +0 -0
  437. data/test/regression/xlsx_files/chart_bar14.xlsx +0 -0
  438. data/test/regression/xlsx_files/chart_bar15.xlsx +0 -0
  439. data/test/regression/xlsx_files/chart_bar16.xlsx +0 -0
  440. data/test/regression/xlsx_files/chart_bar17.xlsx +0 -0
  441. data/test/regression/xlsx_files/chart_bar18.xlsx +0 -0
  442. data/test/regression/xlsx_files/chart_bar19.xlsx +0 -0
  443. data/test/regression/xlsx_files/chart_bar20.xlsx +0 -0
  444. data/test/regression/xlsx_files/chart_bar21.xlsx +0 -0
  445. data/test/regression/xlsx_files/chart_bar22.xlsx +0 -0
  446. data/test/regression/xlsx_files/chart_bar23.xlsx +0 -0
  447. data/test/regression/xlsx_files/chart_bar24.xlsx +0 -0
  448. data/test/regression/xlsx_files/chart_blank01.xlsx +0 -0
  449. data/test/regression/xlsx_files/chart_blank02.xlsx +0 -0
  450. data/test/regression/xlsx_files/chart_blank03.xlsx +0 -0
  451. data/test/regression/xlsx_files/chart_blank04.xlsx +0 -0
  452. data/test/regression/xlsx_files/chart_blank05.xlsx +0 -0
  453. data/test/regression/xlsx_files/chart_blank06.xlsx +0 -0
  454. data/test/regression/xlsx_files/chart_column01.xlsx +0 -0
  455. data/test/regression/xlsx_files/chart_column02.xlsx +0 -0
  456. data/test/regression/xlsx_files/chart_column03.xlsx +0 -0
  457. data/test/regression/xlsx_files/chart_column04.xlsx +0 -0
  458. data/test/regression/xlsx_files/chart_column05.xlsx +0 -0
  459. data/test/regression/xlsx_files/chart_column06.xlsx +0 -0
  460. data/test/regression/xlsx_files/chart_crossing01.xlsx +0 -0
  461. data/test/regression/xlsx_files/chart_crossing02.xlsx +0 -0
  462. data/test/regression/xlsx_files/chart_crossing03.xlsx +0 -0
  463. data/test/regression/xlsx_files/chart_crossing04.xlsx +0 -0
  464. data/test/regression/xlsx_files/chart_format01.xlsx +0 -0
  465. data/test/regression/xlsx_files/chart_format02.xlsx +0 -0
  466. data/test/regression/xlsx_files/chart_format03.xlsx +0 -0
  467. data/test/regression/xlsx_files/chart_format04.xlsx +0 -0
  468. data/test/regression/xlsx_files/chart_format05.xlsx +0 -0
  469. data/test/regression/xlsx_files/chart_format06.xlsx +0 -0
  470. data/test/regression/xlsx_files/chart_format07.xlsx +0 -0
  471. data/test/regression/xlsx_files/chart_format08.xlsx +0 -0
  472. data/test/regression/xlsx_files/chart_format09.xlsx +0 -0
  473. data/test/regression/xlsx_files/chart_format10.xlsx +0 -0
  474. data/test/regression/xlsx_files/chart_format11.xlsx +0 -0
  475. data/test/regression/xlsx_files/chart_format12.xlsx +0 -0
  476. data/test/regression/xlsx_files/chart_format13.xlsx +0 -0
  477. data/test/regression/xlsx_files/chart_format14.xlsx +0 -0
  478. data/test/regression/xlsx_files/chart_format15.xlsx +0 -0
  479. data/test/regression/xlsx_files/chart_format16.xlsx +0 -0
  480. data/test/regression/xlsx_files/chart_format17.xlsx +0 -0
  481. data/test/regression/xlsx_files/chart_format18.xlsx +0 -0
  482. data/test/regression/xlsx_files/chart_line01.xlsx +0 -0
  483. data/test/regression/xlsx_files/chart_line02.xlsx +0 -0
  484. data/test/regression/xlsx_files/chart_name01.xlsx +0 -0
  485. data/test/regression/xlsx_files/chart_name02.xlsx +0 -0
  486. data/test/regression/xlsx_files/chart_name03.xlsx +0 -0
  487. data/test/regression/xlsx_files/chart_pie01.xlsx +0 -0
  488. data/test/regression/xlsx_files/chart_scatter01.xlsx +0 -0
  489. data/test/regression/xlsx_files/chart_scatter02.xlsx +0 -0
  490. data/test/regression/xlsx_files/chart_scatter03.xlsx +0 -0
  491. data/test/regression/xlsx_files/chart_scatter04.xlsx +0 -0
  492. data/test/regression/xlsx_files/chart_scatter05.xlsx +0 -0
  493. data/test/regression/xlsx_files/chart_scatter06.xlsx +0 -0
  494. data/test/regression/xlsx_files/chart_scatter07.xlsx +0 -0
  495. data/test/regression/xlsx_files/chart_sparse01.xlsx +0 -0
  496. data/test/regression/xlsx_files/chart_stock01.xlsx +0 -0
  497. data/test/regression/xlsx_files/chart_stock02.xlsx +0 -0
  498. data/test/regression/xlsx_files/chart_str01.xlsx +0 -0
  499. data/test/regression/xlsx_files/chart_str02.xlsx +0 -0
  500. data/test/regression/xlsx_files/chartsheet01.xlsx +0 -0
  501. data/test/regression/xlsx_files/chartsheet02.xlsx +0 -0
  502. data/test/regression/xlsx_files/chartsheet03.xlsx +0 -0
  503. data/test/regression/xlsx_files/chartsheet04.xlsx +0 -0
  504. data/test/regression/xlsx_files/chartsheet05.xlsx +0 -0
  505. data/test/regression/xlsx_files/chartsheet06.xlsx +0 -0
  506. data/test/regression/xlsx_files/chartsheet07.xlsx +0 -0
  507. data/test/regression/xlsx_files/chartsheet08.xlsx +0 -0
  508. data/test/regression/xlsx_files/chartsheet09.xlsx +0 -0
  509. data/test/regression/xlsx_files/comment01.xlsx +0 -0
  510. data/test/regression/xlsx_files/comment02.xlsx +0 -0
  511. data/test/regression/xlsx_files/comment03.xlsx +0 -0
  512. data/test/regression/xlsx_files/comment04.xlsx +0 -0
  513. data/test/regression/xlsx_files/comment05.xlsx +0 -0
  514. data/test/regression/xlsx_files/comment06.xlsx +0 -0
  515. data/test/regression/xlsx_files/comment07.xlsx +0 -0
  516. data/test/regression/xlsx_files/comment08.xlsx +0 -0
  517. data/test/regression/xlsx_files/comment09.xlsx +0 -0
  518. data/test/regression/xlsx_files/comment10.xlsx +0 -0
  519. data/test/regression/xlsx_files/cond_format01.xlsx +0 -0
  520. data/test/regression/xlsx_files/cond_format02.xlsx +0 -0
  521. data/test/regression/xlsx_files/cond_format03.xlsx +0 -0
  522. data/test/regression/xlsx_files/cond_format04.xlsx +0 -0
  523. data/test/regression/xlsx_files/cond_format05.xlsx +0 -0
  524. data/test/regression/xlsx_files/cond_format06.xlsx +0 -0
  525. data/test/regression/xlsx_files/cond_format07.xlsx +0 -0
  526. data/test/regression/xlsx_files/cond_format08.xlsx +0 -0
  527. data/test/regression/xlsx_files/cond_format10.xlsx +0 -0
  528. data/test/regression/xlsx_files/cond_format11.xlsx +0 -0
  529. data/test/regression/xlsx_files/cond_format12.xlsx +0 -0
  530. data/test/regression/xlsx_files/custom_colors01.xlsx +0 -0
  531. data/test/regression/xlsx_files/date_1904_01.xlsx +0 -0
  532. data/test/regression/xlsx_files/date_1904_02.xlsx +0 -0
  533. data/test/regression/xlsx_files/defined_name01.xlsx +0 -0
  534. data/test/regression/xlsx_files/escapes01.xlsx +0 -0
  535. data/test/regression/xlsx_files/escapes02.xlsx +0 -0
  536. data/test/regression/xlsx_files/escapes03.xlsx +0 -0
  537. data/test/regression/xlsx_files/filehandle01.xlsx +0 -0
  538. data/test/regression/xlsx_files/fit_to_pages01.xlsx +0 -0
  539. data/test/regression/xlsx_files/fit_to_pages02.xlsx +0 -0
  540. data/test/regression/xlsx_files/fit_to_pages03.xlsx +0 -0
  541. data/test/regression/xlsx_files/fit_to_pages04.xlsx +0 -0
  542. data/test/regression/xlsx_files/fit_to_pages05.xlsx +0 -0
  543. data/test/regression/xlsx_files/format01.xlsx +0 -0
  544. data/test/regression/xlsx_files/format02.xlsx +0 -0
  545. data/test/regression/xlsx_files/format03.xlsx +0 -0
  546. data/test/regression/xlsx_files/format04.xlsx +0 -0
  547. data/test/regression/xlsx_files/gridlines01.xlsx +0 -0
  548. data/test/regression/xlsx_files/hyperlink01.xlsx +0 -0
  549. data/test/regression/xlsx_files/hyperlink02.xlsx +0 -0
  550. data/test/regression/xlsx_files/hyperlink03.xlsx +0 -0
  551. data/test/regression/xlsx_files/hyperlink04.xlsx +0 -0
  552. data/test/regression/xlsx_files/hyperlink05.xlsx +0 -0
  553. data/test/regression/xlsx_files/hyperlink06.xlsx +0 -0
  554. data/test/regression/xlsx_files/hyperlink07.xlsx +0 -0
  555. data/test/regression/xlsx_files/hyperlink08.xlsx +0 -0
  556. data/test/regression/xlsx_files/hyperlink09.xlsx +0 -0
  557. data/test/regression/xlsx_files/hyperlink10.xlsx +0 -0
  558. data/test/regression/xlsx_files/hyperlink11.xlsx +0 -0
  559. data/test/regression/xlsx_files/hyperlink12.xlsx +0 -0
  560. data/test/regression/xlsx_files/hyperlink13.xlsx +0 -0
  561. data/test/regression/xlsx_files/hyperlink14.xlsx +0 -0
  562. data/test/regression/xlsx_files/hyperlink15.xlsx +0 -0
  563. data/test/regression/xlsx_files/hyperlink16.xlsx +0 -0
  564. data/test/regression/xlsx_files/hyperlink17.xlsx +0 -0
  565. data/test/regression/xlsx_files/hyperlink18.xlsx +0 -0
  566. data/test/regression/xlsx_files/image01.xlsx +0 -0
  567. data/test/regression/xlsx_files/image02.xlsx +0 -0
  568. data/test/regression/xlsx_files/image03.xlsx +0 -0
  569. data/test/regression/xlsx_files/image04.xlsx +0 -0
  570. data/test/regression/xlsx_files/image05.xlsx +0 -0
  571. data/test/regression/xlsx_files/image06.xlsx +0 -0
  572. data/test/regression/xlsx_files/image07.xlsx +0 -0
  573. data/test/regression/xlsx_files/outline01.xlsx +0 -0
  574. data/test/regression/xlsx_files/outline02.xlsx +0 -0
  575. data/test/regression/xlsx_files/outline03.xlsx +0 -0
  576. data/test/regression/xlsx_files/outline04.xlsx +0 -0
  577. data/test/regression/xlsx_files/outline05.xlsx +0 -0
  578. data/test/regression/xlsx_files/outline06.xlsx +0 -0
  579. data/test/regression/xlsx_files/page_breaks01.xlsx +0 -0
  580. data/test/regression/xlsx_files/page_breaks02.xlsx +0 -0
  581. data/test/regression/xlsx_files/page_breaks03.xlsx +0 -0
  582. data/test/regression/xlsx_files/page_breaks04.xlsx +0 -0
  583. data/test/regression/xlsx_files/page_breaks05.xlsx +0 -0
  584. data/test/regression/xlsx_files/page_breaks06.xlsx +0 -0
  585. data/test/regression/xlsx_files/page_view01.xlsx +0 -0
  586. data/test/regression/xlsx_files/panes01.xlsx +0 -0
  587. data/test/regression/xlsx_files/print_across01.xlsx +0 -0
  588. data/test/regression/xlsx_files/print_area01.xlsx +0 -0
  589. data/test/regression/xlsx_files/print_area02.xlsx +0 -0
  590. data/test/regression/xlsx_files/print_area03.xlsx +0 -0
  591. data/test/regression/xlsx_files/print_area04.xlsx +0 -0
  592. data/test/regression/xlsx_files/print_area05.xlsx +0 -0
  593. data/test/regression/xlsx_files/print_area06.xlsx +0 -0
  594. data/test/regression/xlsx_files/print_area07.xlsx +0 -0
  595. data/test/regression/xlsx_files/print_options01.xlsx +0 -0
  596. data/test/regression/xlsx_files/print_options02.xlsx +0 -0
  597. data/test/regression/xlsx_files/print_options03.xlsx +0 -0
  598. data/test/regression/xlsx_files/print_options04.xlsx +0 -0
  599. data/test/regression/xlsx_files/print_options05.xlsx +0 -0
  600. data/test/regression/xlsx_files/print_options06.xlsx +0 -0
  601. data/test/regression/xlsx_files/print_scale01.xlsx +0 -0
  602. data/test/regression/xlsx_files/print_scale02.xlsx +0 -0
  603. data/test/regression/xlsx_files/properties01.xlsx +0 -0
  604. data/test/regression/xlsx_files/repeat01.xlsx +0 -0
  605. data/test/regression/xlsx_files/repeat02.xlsx +0 -0
  606. data/test/regression/xlsx_files/repeat03.xlsx +0 -0
  607. data/test/regression/xlsx_files/repeat04.xlsx +0 -0
  608. data/test/regression/xlsx_files/repeat05.xlsx +0 -0
  609. data/test/regression/xlsx_files/rich_string01.xlsx +0 -0
  610. data/test/regression/xlsx_files/rich_string02.xlsx +0 -0
  611. data/test/regression/xlsx_files/rich_string03.xlsx +0 -0
  612. data/test/regression/xlsx_files/rich_string04.xlsx +0 -0
  613. data/test/regression/xlsx_files/rich_string05.xlsx +0 -0
  614. data/test/regression/xlsx_files/rich_string06.xlsx +0 -0
  615. data/test/regression/xlsx_files/rich_string07.xlsx +0 -0
  616. data/test/regression/xlsx_files/rich_string08.xlsx +0 -0
  617. data/test/regression/xlsx_files/rich_string09.xlsx +0 -0
  618. data/test/regression/xlsx_files/rich_string10.xlsx +0 -0
  619. data/test/regression/xlsx_files/rich_string11.xlsx +0 -0
  620. data/test/regression/xlsx_files/row_col_format01.xlsx +0 -0
  621. data/test/regression/xlsx_files/row_col_format02.xlsx +0 -0
  622. data/test/regression/xlsx_files/row_col_format03.xlsx +0 -0
  623. data/test/regression/xlsx_files/row_col_format04.xlsx +0 -0
  624. data/test/regression/xlsx_files/row_col_format05.xlsx +0 -0
  625. data/test/regression/xlsx_files/row_col_format06.xlsx +0 -0
  626. data/test/regression/xlsx_files/row_col_format07.xlsx +0 -0
  627. data/test/regression/xlsx_files/row_col_format08.xlsx +0 -0
  628. data/test/regression/xlsx_files/row_col_format09.xlsx +0 -0
  629. data/test/regression/xlsx_files/row_col_format10.xlsx +0 -0
  630. data/test/regression/xlsx_files/row_col_format11.xlsx +0 -0
  631. data/test/regression/xlsx_files/row_col_format12.xlsx +0 -0
  632. data/test/regression/xlsx_files/row_col_format13.xlsx +0 -0
  633. data/test/regression/xlsx_files/row_col_format14.xlsx +0 -0
  634. data/test/regression/xlsx_files/shape_connect01.xlsx +0 -0
  635. data/test/regression/xlsx_files/shape_connect02.xlsx +0 -0
  636. data/test/regression/xlsx_files/shape_connect03.xlsx +0 -0
  637. data/test/regression/xlsx_files/shape_connect04.xlsx +0 -0
  638. data/test/regression/xlsx_files/shape_scale01.xlsx +0 -0
  639. data/test/regression/xlsx_files/shape_stencil01.xlsx +0 -0
  640. data/test/regression/xlsx_files/shared_strings01.xlsx +0 -0
  641. data/test/regression/xlsx_files/shared_strings02.xlsx +0 -0
  642. data/test/regression/xlsx_files/simple01.xlsx +0 -0
  643. data/test/regression/xlsx_files/simple02.xlsx +0 -0
  644. data/test/regression/xlsx_files/tab_color01.xlsx +0 -0
  645. data/test/regression/xlsx_files/table01.xlsx +0 -0
  646. data/test/regression/xlsx_files/table02.xlsx +0 -0
  647. data/test/regression/xlsx_files/table03.xlsx +0 -0
  648. data/test/regression/xlsx_files/table04.xlsx +0 -0
  649. data/test/regression/xlsx_files/table05.xlsx +0 -0
  650. data/test/regression/xlsx_files/table06.xlsx +0 -0
  651. data/test/regression/xlsx_files/table07.xlsx +0 -0
  652. data/test/regression/xlsx_files/table08.xlsx +0 -0
  653. data/test/regression/xlsx_files/table09.xlsx +0 -0
  654. data/test/regression/xlsx_files/table10.xlsx +0 -0
  655. data/test/regression/xlsx_files/table11.xlsx +0 -0
  656. data/test/regression/xlsx_files/table12.xlsx +0 -0
  657. data/test/regression/xlsx_files/table13.xlsx +0 -0
  658. data/test/regression/xlsx_files/table14.xlsx +0 -0
  659. data/test/test_example_match.rb +2386 -889
  660. data/test/workbook/test_write_defined_names.rb +1 -1
  661. data/test/worksheet/test_cond_format_01.rb +82 -0
  662. data/test/worksheet/test_cond_format_02.rb +87 -0
  663. data/test/worksheet/test_cond_format_03.rb +97 -0
  664. data/test/worksheet/test_cond_format_04.rb +85 -0
  665. data/test/worksheet/test_cond_format_05.rb +96 -0
  666. data/test/worksheet/test_cond_format_06.rb +106 -0
  667. data/test/worksheet/test_cond_format_07.rb +116 -0
  668. data/test/worksheet/test_cond_format_08.rb +115 -0
  669. data/test/worksheet/test_cond_format_09.rb +108 -0
  670. data/test/worksheet/test_cond_format_10.rb +83 -0
  671. data/test/worksheet/test_cond_format_11.rb +85 -0
  672. data/test/worksheet/test_cond_format_12.rb +133 -0
  673. data/test/worksheet/test_cond_format_13.rb +135 -0
  674. data/test/worksheet/test_cond_format_14.rb +132 -0
  675. data/test/worksheet/test_cond_format_15.rb +93 -0
  676. data/test/worksheet/test_cond_format_16.rb +138 -0
  677. data/test/worksheet/test_cond_format_17.rb +141 -0
  678. data/test/worksheet/test_cond_format_18.rb +135 -0
  679. data/test/worksheet/test_cond_format_19.rb +139 -0
  680. data/test/worksheet/test_write_array_formula_01.rb +3 -4
  681. data/test/worksheet/test_write_data_validation_01.rb +1 -1
  682. data/test/worksheet/test_write_data_validation_02.rb +2 -2
  683. data/test/worksheet/test_write_methods.rb +6 -1
  684. data/test/worksheet/test_write_page_setup.rb +1 -1
  685. data/test/worksheet/test_write_sheet_view.rb +1 -1
  686. data/write_xlsx.gemspec +644 -9
  687. metadata +649 -22
  688. data/test/chart/test_write_chart_space.rb +0 -15
@@ -32,9 +32,11 @@ module Writexlsx
32
32
  # set_comments_author
33
33
  # insert_image
34
34
  # insert_chart
35
+ # insert_shape
35
36
  # data_validation
36
- # conditional_format
37
- # get_name
37
+ # conditional_formatting
38
+ # add_table
39
+ # name
38
40
  # activate
39
41
  # select
40
42
  # hide
@@ -62,8 +64,8 @@ module Writexlsx
62
64
  # Row-column notation and A1 notation.
63
65
  #
64
66
  # Row-column notation uses a zero based index for both row and column
65
- # while A1 notation uses the standard Excel alphanumeric sequence of column letter
66
- # and 1-based row. For example:
67
+ # while A1 notation uses the standard Excel alphanumeric sequence of column
68
+ # letter and 1-based row. For example:
67
69
  #
68
70
  # (0, 0) # The top left cell in row-column notation.
69
71
  # ('A1') # The top left cell in A1 notation.
@@ -71,7 +73,8 @@ module Writexlsx
71
73
  # (1999, 29) # Row-column notation.
72
74
  # ('AD2000') # The same cell in A1 notation.
73
75
  #
74
- # Row-column notation is useful if you are referring to cells programmatically:
76
+ # Row-column notation is useful if you are referring to cells
77
+ # programmatically:
75
78
  #
76
79
  # (0..9).each do |i|
77
80
  # worksheet.write(i, 0, 'Hello') # Cells A1 to A10
@@ -99,6 +102,44 @@ module Writexlsx
99
102
  # following sections are given in terms of row-column notation. In all cases
100
103
  # it is also possible to use A1 notation.
101
104
  #
105
+ # == PAGE SET-UP METHODS
106
+ #
107
+ # Page set-up methods affect the way that a worksheet looks
108
+ # when it is printed. They control features such as page headers and footers
109
+ # and margins. These methods are really just standard worksheet methods.
110
+ # They are documented here in a separate section for the sake of clarity.
111
+ #
112
+ # The following methods are available for page set-up:
113
+ #
114
+ # set_landscape()
115
+ # set_portrait()
116
+ # set_page_view()
117
+ # set_paper()
118
+ # center_horizontally()
119
+ # center_vertically()
120
+ # set_margins()
121
+ # set_header()
122
+ # set_footer()
123
+ # repeat_rows()
124
+ # repeat_columns()
125
+ # hide_gridlines()
126
+ # print_row_col_headers()
127
+ # print_area()
128
+ # print_across()
129
+ # fit_to_pages()
130
+ # set_start_page()
131
+ # set_print_scale()
132
+ # set_h_pagebreaks()
133
+ # set_v_pagebreaks()
134
+ # A common requirement when working with WriteXLSX is to apply the same
135
+ # page set-up features to all of the worksheets in a workbook. To do this
136
+ # you can use the sheets() method of the workbook class to access the array
137
+ # of worksheets in a workbook:
138
+ #
139
+ # workbook.sheets.each do |worksheet|
140
+ # worksheet.set_landscape
141
+ # end
142
+ #
102
143
  class Worksheet
103
144
  include Writexlsx::Utility
104
145
 
@@ -177,7 +218,11 @@ def data
177
218
  end
178
219
 
179
220
  def write_cell
180
- @worksheet.writer.tag_elements('c', cell_attributes) do
221
+ attributes = cell_attributes
222
+ if @result && !(@result.to_s =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/)
223
+ attributes << 't' << 'str'
224
+ end
225
+ @worksheet.writer.tag_elements('c', attributes) do
181
226
  @worksheet.write_cell_formula(token)
182
227
  @worksheet.write_cell_value(result || 0)
183
228
  end
@@ -222,11 +267,11 @@ def write_cell
222
267
 
223
268
  if link_type == 1
224
269
  # External link with rel file relationship.
225
- @worksheet.hlink_count += 1
270
+ @worksheet.rel_count += 1
226
271
  @worksheet.hlink_refs <<
227
272
  [
228
273
  link_type, row, col,
229
- @worksheet.hlink_count, @str, @tip
274
+ @worksheet.rel_count, @str, @tip
230
275
  ]
231
276
 
232
277
  @worksheet.external_hyper_links << [ '/hyperlink', @url, 'External' ]
@@ -259,7 +304,7 @@ class PrintStyle # :nodoc:
259
304
  attr_accessor :hbreaks, :vbreaks, :scale # :nodoc:
260
305
  attr_accessor :fit_page, :fit_width, :fit_height, :page_setup_changed # :nodoc:
261
306
  attr_accessor :across # :nodoc:
262
- attr_writer :orientation
307
+ attr_accessor :orientation # :nodoc:
263
308
 
264
309
  def initialize # :nodoc:
265
310
  @margin_left = 0.7
@@ -299,13 +344,15 @@ def orientation?
299
344
  end
300
345
 
301
346
  attr_reader :index # :nodoc:
302
- attr_reader :charts, :images, :drawing # :nodoc:
347
+ attr_reader :charts, :images, :tables, :shapes, :drawing # :nodoc:
303
348
  attr_reader :external_hyper_links, :external_drawing_links # :nodoc:
349
+ attr_reader :external_vml_links, :external_table_links # :nodoc:
304
350
  attr_reader :external_comment_links, :drawing_links # :nodoc:
305
351
  attr_reader :vml_data_id # :nodoc:
306
352
  attr_reader :autofilter_area # :nodoc:
307
353
  attr_reader :writer, :set_rows, :col_formats # :nodoc:
308
- attr_accessor :vml_shape_id, :hlink_count, :hlink_refs # :nodoc:
354
+ attr_accessor :vml_shape_id, :rel_count, :hlink_refs # :nodoc:
355
+ attr_reader :comments_author # :nodoc:
309
356
 
310
357
  def initialize(workbook, index, name) #:nodoc:
311
358
  @writer = Package::XMLWriterSimple.new
@@ -315,7 +362,6 @@ def initialize(workbook, index, name) #:nodoc:
315
362
  @name = name
316
363
  @colinfo = []
317
364
  @cell_data_table = {}
318
- @filter_on = false
319
365
 
320
366
  @print_style = PrintStyle.new
321
367
 
@@ -348,14 +394,21 @@ def initialize(workbook, index, name) #:nodoc:
348
394
  @row_sizes = {}
349
395
  @col_formats = {}
350
396
 
397
+ @last_shape_id = 1
398
+ @rel_count = 0
351
399
  @hlink_count = 0
352
400
  @hlink_refs = []
353
401
  @external_hyper_links = []
354
402
  @external_drawing_links = []
355
403
  @external_comment_links = []
404
+ @external_vml_links = []
405
+ @external_table_links = []
356
406
  @drawing_links = []
357
407
  @charts = []
358
408
  @images = []
409
+ @tables = []
410
+ @shapes = []
411
+ @shape_hash = {}
359
412
 
360
413
  @zoom = 100
361
414
  @outline_row_level = 0
@@ -398,6 +451,7 @@ def assemble_xml_file #:nodoc:
398
451
  write_col_breaks
399
452
  write_drawings
400
453
  write_legacy_drawing
454
+ write_table_parts
401
455
  # write_ext_lst
402
456
  @writer.end_tag('worksheet')
403
457
  @writer.crlf
@@ -578,8 +632,16 @@ def set_first_sheet
578
632
  # worksheet.protect('drowssap', { :insert_rows => true } )
579
633
  #
580
634
  def protect(password = nil, options = {})
581
- # Default values for objects that can be protected.
582
- defaults = {
635
+ check_parameter(options, protect_default_settings.keys, 'protect')
636
+ @protect = protect_default_settings.merge(options)
637
+
638
+ # Set the password after the user defined values.
639
+ @protect[:password] =
640
+ sprintf("%X", encode_password(password)) if password && password != ''
641
+ end
642
+
643
+ def protect_default_settings # :nodoc:
644
+ {
583
645
  :sheet => true,
584
646
  :content => false,
585
647
  :objects => false,
@@ -596,24 +658,10 @@ def protect(password = nil, options = {})
596
658
  :sort => false,
597
659
  :autofilter => false,
598
660
  :pivot_tables => false,
599
- :select_unlocked_cells => true,
661
+ :select_unlocked_cells => true
600
662
  }
601
-
602
- # Overwrite the defaults with user specified values.
603
- options.each do |k, v|
604
- if defaults.has_key?(k)
605
- defaults[k] = options[k]
606
- else
607
- raise "Unknown protection object: #{k}\n"
608
- end
609
- end
610
-
611
- # Set the password after the user defined values.
612
- defaults[:password] =
613
- sprintf("%X", encode_password(password)) if password && password != ''
614
-
615
- @protect = defaults
616
663
  end
664
+ private :protect_default_settings
617
665
 
618
666
  #
619
667
  # :call-seq:
@@ -712,7 +760,7 @@ def set_column(*args)
712
760
  return unless firstcol && lastcol && !data.empty?
713
761
 
714
762
  # Assume second column is the same as first if 0. Avoids KB918419 bug.
715
- lastcol = firstcol if lastcol == 0
763
+ lastcol = firstcol unless ptrue?(lastcol)
716
764
 
717
765
  # Ensure 2nd col is larger than first. Also for KB918419 bug.
718
766
  firstcol, lastcol = lastcol, firstcol if firstcol > lastcol
@@ -724,8 +772,8 @@ def set_column(*args)
724
772
  # the column dimensions in certain cases.
725
773
  ignore_row = 1
726
774
  ignore_col = 1
727
- ignore_col = 0 if format.respond_to?(:xf_index) # Column has a format.
728
- ignore_col = 0 if width && hidden && hidden != 0 # Column has a width but is hidden
775
+ ignore_col = 0 if format.respond_to?(:xf_index) # Column has a format.
776
+ ignore_col = 0 if width && ptrue?(hidden) # Column has a width but is hidden
729
777
 
730
778
  check_dimensions_and_update_max_min_values(0, firstcol, ignore_row, ignore_col)
731
779
  check_dimensions_and_update_max_min_values(0, lastcol, ignore_row, ignore_col)
@@ -746,7 +794,7 @@ def set_column(*args)
746
794
  # Store the col sizes for use when calculating image vertices taking
747
795
  # hidden columns into account. Also store the column formats.
748
796
  width ||= 0 # Ensure width isn't nil.
749
- width = 0 if hidden && hidden != 0 # Set width to zero if col is hidden
797
+ width = 0 if ptrue?(hidden) # Set width to zero if col is hidden
750
798
 
751
799
  (firstcol .. lastcol).each do |col|
752
800
  @col_sizes[col] = width
@@ -1012,13 +1060,18 @@ def set_tab_color(color)
1012
1060
  # If you do not specify a paper type the worksheet will print using
1013
1061
  # the printer's default paper.
1014
1062
  #
1015
- def set_paper(paper_size)
1063
+ def paper=(paper_size)
1016
1064
  if paper_size
1017
1065
  @paper_size = paper_size
1018
1066
  @print_style.page_setup_changed = true
1019
1067
  end
1020
1068
  end
1021
1069
 
1070
+ def set_paper(paper_size)
1071
+ put_deprecate_message("#{self}.set_paper")
1072
+ self::paper = paper_size
1073
+ end
1074
+
1022
1075
  #
1023
1076
  # Set the page header caption and optional margin.
1024
1077
  #
@@ -1419,14 +1472,14 @@ def print_repeat_rows # :nodoc:
1419
1472
  #
1420
1473
  def repeat_columns(*args)
1421
1474
  if args[0] =~ /^\D/
1422
- dummy, first_col, dummy, last_col = substitute_cellref(args)
1475
+ dummy, first_col, dummy, last_col = substitute_cellref(*args)
1423
1476
  else
1424
1477
  first_col, last_col = args
1425
1478
  end
1426
1479
  last_col ||= first_col
1427
1480
 
1428
1481
  area = "#{xl_col_to_name(first_col, 1)}:#{xl_col_to_name(last_col, 1)}"
1429
- @print_style.repeat_rows = "#{quote_sheetname(@name)}!#{area}"
1482
+ @print_style.repeat_cols = "#{quote_sheetname(@name)}!#{area}"
1430
1483
  end
1431
1484
 
1432
1485
  def print_repeat_cols # :nodoc:
@@ -1457,7 +1510,6 @@ def print_area(*args)
1457
1510
 
1458
1511
  # Build up the print area range "=Sheet2!R1C1:R2C1"
1459
1512
  @print_area = convert_name_area(row1, col1, row2, col2)
1460
- @print_area.dup
1461
1513
  end
1462
1514
 
1463
1515
  #
@@ -2235,6 +2287,8 @@ def write_rich_string(*args)
2235
2287
  writer = Package::XMLWriterSimple.new
2236
2288
 
2237
2289
  fragments, length = rich_strings_fragments(rich_strings)
2290
+ # can't allow 2 formats in a row
2291
+ return -4 unless fragments
2238
2292
 
2239
2293
  # If the first token is a string start the <r> element.
2240
2294
  writer.start_tag('r') if !fragments[0].respond_to?(:xf_index)
@@ -2421,6 +2475,14 @@ def write_array_formula(*args)
2421
2475
  formula.sub!(/^=/, '')
2422
2476
 
2423
2477
  store_data_to_table(FormulaArrayCellData.new(self, row1, col1, formula, xf, range, value))
2478
+
2479
+ # Pad out the rest of the area with formatted zeroes.
2480
+ (row1..row2).each do |row|
2481
+ (col1..col2).each do |col|
2482
+ next if row == row1 && col == col1
2483
+ write_number(row, col, 0, xf)
2484
+ end
2485
+ end
2424
2486
  end
2425
2487
 
2426
2488
  # The outline_settings() method is used to control the appearance of
@@ -2539,7 +2601,7 @@ def store_formula(string)
2539
2601
  def write_url(*args)
2540
2602
  # Check for a cell reference in A1 notation and substitute row and column
2541
2603
  row, col, url, xf, str, tip = row_col_notation(args)
2542
- xf, str = str, xf if str.respond_to?(:xf_index)
2604
+ xf, str = str, xf if str.respond_to?(:xf_index) || !xf.respond_to?(:xf_index)
2543
2605
  raise WriteXLSXInsufficientArgumentError if [row, col, url].include?(nil)
2544
2606
 
2545
2607
  link_type = 1
@@ -2555,7 +2617,7 @@ def write_url(*args)
2555
2617
  end
2556
2618
 
2557
2619
  # The displayed string defaults to the url string.
2558
- str ||= url
2620
+ str ||= url.dup
2559
2621
 
2560
2622
  # For external links change the directory separator from Unix to Dos.
2561
2623
  if link_type == 3
@@ -2576,23 +2638,43 @@ def write_url(*args)
2576
2638
  # External links to URLs and to other Excel workbooks have slightly
2577
2639
  # different characteristics that we have to account for.
2578
2640
  if link_type == 1
2641
+ # Substiture white space in url.
2642
+ url = url.sub(/[\s\x00]/, '%20')
2643
+
2579
2644
  # Ordinary URL style external links don't have a "location" string.
2580
2645
  str = nil
2581
2646
  elsif link_type == 3
2582
2647
  # External Workbook links need to be modified into the right format.
2583
2648
  # The URL will look something like 'c:\temp\file.xlsx#Sheet!A1'.
2584
2649
  # We need the part to the left of the # as the URL and the part to
2585
- # the right as the "location" string (if it exists)
2650
+ # the right as the "location" string (if it exists).
2586
2651
  url, str = url.split(/#/)
2587
2652
 
2588
2653
  # Add the file:/// URI to the url if non-local.
2589
- # url = "file:///#{url}" if url =~ m{[\\/]} && url !~ m{^\.\.}
2654
+ if url =~ %r![:]! || # Windows style "C:/" link.
2655
+ url =~ %r!^\\\\! # Network share.
2656
+ url = "file:///#{url}"
2657
+ end
2590
2658
 
2659
+ # Convert a ./dir/file.xlsx link to dir/file.xlsx.
2660
+ url = url.sub(%r!^.\\!, '')
2591
2661
 
2592
2662
  # Treat as a default external link now that the data has been modified.
2593
2663
  link_type = 1
2594
2664
  end
2595
2665
 
2666
+ # Excel limits escaped URL to 255 characters.
2667
+ if url.bytesize > 255
2668
+ raise "URL '#{url}' > 255 characters, it exceeds Excel's limit for URLS."
2669
+ end
2670
+
2671
+ # Check the limit of URLS per worksheet.
2672
+ @hlink_count += 1
2673
+
2674
+ if @hlink_count > 65_530
2675
+ raise "URL '#{url}' added but number of URLS is over Excel's limit of 65,530 URLS per worksheet."
2676
+ end
2677
+
2596
2678
  store_data_to_table(HyperlinkCellData.new(self, row, col, index, xf, link_type, url, str, tip))
2597
2679
  end
2598
2680
 
@@ -2654,7 +2736,7 @@ def write_date_time(*args)
2654
2736
  store_data_to_table(NumberCellData.new(self, row, col, date_time, xf))
2655
2737
  else
2656
2738
  # If the date isn't valid then write it as a string.
2657
- write_string(args) unless date_time
2739
+ write_string(*args)
2658
2740
  end
2659
2741
  end
2660
2742
 
@@ -2706,12 +2788,23 @@ def insert_chart(*args)
2706
2788
  scale_x ||= 1
2707
2789
  scale_y ||= 1
2708
2790
 
2709
- raise "Not a Chart object in insert_chart()" unless chart.is_a?(Chart)
2710
- raise "Not a embedded style Chart object in insert_chart()" if chart.embedded == 0
2791
+ raise "Not a Chart object in insert_chart()" unless chart.is_a?(Chart) || chart.is_a?(Chartsheet)
2792
+ raise "Not a embedded style Chart object in insert_chart()" if chart.respond_to?(:embedded) && chart.embedded == 0
2711
2793
 
2712
2794
  @charts << [row, col, chart, x_offset, y_offset, scale_x, scale_y]
2713
2795
  end
2714
2796
 
2797
+ #
2798
+ # Sort the worksheet charts into the order that they were created in rather
2799
+ # than the insertion order. This is ensure that the chart and drawing objects
2800
+ # written in the same order. The chart id is used to sort back into creation
2801
+ # order.
2802
+ #
2803
+ def sort_charts
2804
+ return if @charts.size < 2
2805
+ @charts = @charts.sort {|a, b| a[2].id <=> b[2].id}
2806
+ end
2807
+
2715
2808
  #
2716
2809
  # :call-seq:
2717
2810
  # insert_image(row, column, filename [ , x, y, scale_x, scale_y ] )
@@ -2760,7 +2853,7 @@ def insert_chart(*args)
2760
2853
  def insert_image(*args)
2761
2854
  # Check for a cell reference in A1 notation and substitute row and column.
2762
2855
  row, col, image, x_offset, y_offset, scale_x, scale_y = row_col_notation(args)
2763
- raise WriteXLSXInsufficientArgumentError if [row, col, chart].include?(nil)
2856
+ raise WriteXLSXInsufficientArgumentError if [row, col, image].include?(nil)
2764
2857
 
2765
2858
  x_offset ||= 0
2766
2859
  y_offset ||= 0
@@ -2934,7 +3027,13 @@ def convert_date_time(date_time_string) #:nodoc:
2934
3027
  # Adjust for Excel erroneously treating 1900 as a leap year.
2935
3028
  days += 1 if !date_1904? and days > 59
2936
3029
 
2937
- days + seconds
3030
+ date_time = sprintf("%0.10f", days + seconds)
3031
+ date_time = date_time.sub(/\.?0+$/, '') if date_time =~ /\./
3032
+ if date_time =~ /\./
3033
+ date_time.to_f
3034
+ else
3035
+ date_time.to_i
3036
+ end
2938
3037
  end
2939
3038
 
2940
3039
  #
@@ -3009,9 +3108,12 @@ def set_row(*args)
3009
3108
 
3010
3109
  return if row.nil?
3011
3110
 
3111
+ # Use min col in check_dimensions. Default to 0 if undefined.
3112
+ min_col = @dim_colmin || 0
3113
+
3012
3114
  # Check that row and col are valid and store max and min values.
3013
- check_dimensions(row, 0)
3014
- store_row_col_max_min_values(row, 0)
3115
+ check_dimensions(row, min_col)
3116
+ store_row_col_max_min_values(row, min_col)
3015
3117
 
3016
3118
  # If the height is 0 the row is hidden and the height is the default.
3017
3119
  if height == 0
@@ -3153,6 +3255,10 @@ def merge_range_type(type, *args)
3153
3255
  #
3154
3256
  def conditional_formatting(*args)
3155
3257
  # Check for a cell reference in A1 notation and substitute row and column
3258
+ if args[0] =~ /^\D/
3259
+ # Check for a user defined multiple range like B3:K6,B8:K11.
3260
+ user_range = args[0].gsub(/\s*,\s*/, ' ').gsub(/\$/, '') if args[0] =~ /,/
3261
+ end
3156
3262
  row1, col1, row2, col2, param = row_col_notation(args)
3157
3263
  if row2.respond_to?(:keys)
3158
3264
  param = row2
@@ -3165,10 +3271,6 @@ def conditional_formatting(*args)
3165
3271
  check_dimensions(row2, col2)
3166
3272
  check_conditional_formatting_parameters(param)
3167
3273
 
3168
- param[:format] = param[:format].get_dxf_index if param[:format]
3169
- param[:priority] = @dxf_priority
3170
- @dxf_priority += 1
3171
-
3172
3274
  # Swap last row/col for first row/col as necessary
3173
3275
  row1, row2 = row2, row1 if row1 > row2
3174
3276
  col1, col2 = col2, col1 if col1 > col2
@@ -3176,8 +3278,133 @@ def conditional_formatting(*args)
3176
3278
  # If the first and last cell are the same write a single cell.
3177
3279
  if row1 == row2 && col1 == col2
3178
3280
  range = xl_rowcol_to_cell(row1, col1)
3281
+ start_cell = range
3179
3282
  else
3180
3283
  range = xl_range(row1, row2, col1, col2)
3284
+ start_cell = xl_rowcol_to_cell(row1, col1)
3285
+ end
3286
+
3287
+ # Override with user defined multiple range if provided.
3288
+ range = user_range if user_range
3289
+
3290
+ param[:format] = param[:format].get_dxf_index if param[:format]
3291
+ param[:priority] = @dxf_priority
3292
+ @dxf_priority += 1
3293
+
3294
+ # Special handling of text criteria.
3295
+ if param[:type] == 'text'
3296
+ case param[:criteria]
3297
+ when 'containsText'
3298
+ param[:type] = 'containsText';
3299
+ param[:formula] = %Q!NOT(ISERROR(SEARCH("#{param[:value]}",#{start_cell})))!
3300
+ when 'notContains'
3301
+ param[:type] = 'notContainsText';
3302
+ param[:formula] = %Q!ISERROR(SEARCH("#{param[:value]}",#{start_cell}))!
3303
+ when 'beginsWith'
3304
+ param[:type] = 'beginsWith'
3305
+ param[:formula] = %Q!LEFT(#{start_cell},1)="#{param[:value]}"!
3306
+ when 'endsWith'
3307
+ param[:type] = 'endsWith'
3308
+ param[:formula] = %Q!RIGHT(#{start_cell},1)="#{param[:value]}"!
3309
+ else
3310
+ raise "Invalid text criteria '#{param[:criteria]} in conditional_formatting()"
3311
+ end
3312
+ end
3313
+
3314
+ # Special handling of time time_period criteria.
3315
+ if param[:type] == 'timePeriod'
3316
+ case param[:criteria]
3317
+ when 'yesterday'
3318
+ param[:formula] = "FLOOR(#{start_cell},1)=TODAY()-1"
3319
+ when 'today'
3320
+ param[:formula] = "FLOOR(#{start_cell},1)=TODAY()"
3321
+ when 'tomorrow'
3322
+ param[:formula] = "FLOOR(#{start_cell},1)=TODAY()+1"
3323
+ when 'last7Days'
3324
+ param[:formula] =
3325
+ "AND(TODAY()-FLOOR(#{start_cell},1)<=6,FLOOR(#{start_cell},1)<=TODAY())"
3326
+ when 'lastWeek'
3327
+ param[:formula] =
3328
+ "AND(TODAY()-ROUNDDOWN(#{start_cell},0)>=(WEEKDAY(TODAY())),TODAY()-ROUNDDOWN(#{start_cell},0)<(WEEKDAY(TODAY())+7))"
3329
+ when 'thisWeek'
3330
+ param[:formula] =
3331
+ "AND(TODAY()-ROUNDDOWN(#{start_cell},0)<=WEEKDAY(TODAY())-1,ROUNDDOWN(#{start_cell},0)-TODAY()<=7-WEEKDAY(TODAY()))"
3332
+ when 'nextWeek'
3333
+ param[:formula] =
3334
+ "AND(ROUNDDOWN(#{start_cell},0)-TODAY()>(7-WEEKDAY(TODAY())),ROUNDDOWN(#{start_cell},0)-TODAY()<(15-WEEKDAY(TODAY())))"
3335
+ when 'lastMonth'
3336
+ param[:formula] =
3337
+ "AND(MONTH(#{start_cell})=MONTH(TODAY())-1,OR(YEAR(#{start_cell})=YEAR(TODAY()),AND(MONTH(#{start_cell})=1,YEAR(A1)=YEAR(TODAY())-1)))"
3338
+ when 'thisMonth'
3339
+ param[:formula] =
3340
+ "AND(MONTH(#{start_cell})=MONTH(TODAY()),YEAR(#{start_cell})=YEAR(TODAY()))"
3341
+ when 'nextMonth'
3342
+ param[:formula] =
3343
+ "AND(MONTH(#{start_cell})=MONTH(TODAY())+1,OR(YEAR(#{start_cell})=YEAR(TODAY()),AND(MONTH(#{start_cell})=12,YEAR(#{start_cell})=YEAR(TODAY())+1)))"
3344
+ else
3345
+ raise "Invalid time_period criteria '#{param[:criteria]}' in conditional_formatting()"
3346
+ end
3347
+ end
3348
+
3349
+ # Special handling of blanks/error types.
3350
+ case param[:type]
3351
+ when 'containsBlanks'
3352
+ param[:formula] = "LEN(TRIM(#{start_cell}))=0"
3353
+ when 'notContainsBlanks'
3354
+ param[:formula] = "LEN(TRIM(#{start_cell}))>0"
3355
+ when 'containsErrors'
3356
+ param[:formula] = "ISERROR(#{start_cell})"
3357
+ when 'notContainsErrors'
3358
+ param[:formula] = "NOT(ISERROR(#{start_cell}))"
3359
+ when '2_color_scale'
3360
+ param[:type] = 'colorScale'
3361
+
3362
+ # Color scales don't use any additional formatting.
3363
+ param[:format] = nil
3364
+
3365
+ # Turn off 3 color parameters.
3366
+ param[:mid_type] = nil
3367
+ param[:mid_color] = nil
3368
+
3369
+ param[:min_type] ||= 'min'
3370
+ param[:max_type] ||= 'max'
3371
+ param[:min_value] ||= 0
3372
+ param[:max_value] ||= 0
3373
+ param[:min_color] ||= '#FF7128'
3374
+ param[:max_color] ||= '#FFEF9C'
3375
+
3376
+ param[:max_color] = get_palette_color( param[:max_color] )
3377
+ param[:min_color] = get_palette_color( param[:min_color] )
3378
+ when '3_color_scale'
3379
+ param[:type] = 'colorScale'
3380
+
3381
+ # Color scales don't use any additional formatting.
3382
+ param[:format] = nil
3383
+
3384
+ param[:min_type] ||= 'min'
3385
+ param[:mid_type] ||= 'percentile'
3386
+ param[:max_type] ||= 'max'
3387
+ param[:min_value] ||= 0
3388
+ param[:mid_value] ||= 50
3389
+ param[:max_value] ||= 0
3390
+ param[:min_color] ||= '#F8696B'
3391
+ param[:mid_color] ||= '#FFEB84'
3392
+ param[:max_color] ||= '#63BE7B'
3393
+
3394
+ param[:max_color] = get_palette_color(param[:max_color])
3395
+ param[:mid_color] = get_palette_color(param[:mid_color])
3396
+ param[:min_color] = get_palette_color(param[:min_color])
3397
+ when 'dataBar'
3398
+ # Color scales don't use any additional formatting.
3399
+ param[:format] = nil
3400
+
3401
+ param[:min_type] ||= 'min'
3402
+ param[:max_type] ||= 'max'
3403
+ param[:min_value] ||= 0
3404
+ param[:max_value] ||= 0
3405
+ param[:bar_color] ||= '#638EC6'
3406
+
3407
+ param[:bar_color] = get_palette_color(param[:bar_color])
3181
3408
  end
3182
3409
 
3183
3410
  # Store the validation information until we close the worksheet.
@@ -3185,6 +3412,228 @@ def conditional_formatting(*args)
3185
3412
  @cond_formats[range] << param
3186
3413
  end
3187
3414
 
3415
+ #
3416
+ # Add an Excel table to a worksheet.
3417
+ #
3418
+ # The add_table() method is used to group a range of cells into
3419
+ # an Excel Table.
3420
+ #
3421
+ # worksheet.add_table('B3:F7', { ... } )
3422
+ #
3423
+ # This method contains a lot of parameters and is described
3424
+ # in detail in a separate section "TABLES IN EXCEL".
3425
+ #
3426
+ # See also the tables.rb program in the examples directory of the distro
3427
+ #
3428
+ def add_table(*args)
3429
+ col_formats = []
3430
+ =begin
3431
+ # We would need to order the write statements very carefully within this
3432
+ # function to support optimisation mode. Disable add_table() when it is
3433
+ # on for now.
3434
+ if @optimization
3435
+ carp "add_table() isn't supported when set_optimization() is on"
3436
+ return -1
3437
+ end
3438
+ =end
3439
+ # Check for a cell reference in A1 notation and substitute row and column
3440
+ row1, col1, row2, col2, param = row_col_notation(args)
3441
+
3442
+ # Check for a valid number of args.
3443
+ raise "Not enough parameters to add_table()" if [row1, col1, row2, col2].include?(nil)
3444
+
3445
+ # Check that row and col are valid without storing the values.
3446
+ check_dimensions_and_update_max_min_values(row1, col1, 1, 1)
3447
+ check_dimensions_and_update_max_min_values(row2, col2, 1, 1)
3448
+
3449
+ # The final hashref contains the validation parameters.
3450
+ param ||= {}
3451
+
3452
+ check_parameter(param, valid_table_parameter, 'add_table')
3453
+
3454
+ # Table count is a member of Workbook, global to all Worksheet.
3455
+ @workbook.table_count += 1
3456
+ table = {}
3457
+ table[:_columns] = []
3458
+ table[:id] = @workbook.table_count
3459
+
3460
+ # Turn on Excel's defaults.
3461
+ param[:banded_rows] ||= 1
3462
+ param[:header_row] ||= 1
3463
+ param[:autofilter] ||= 1
3464
+
3465
+ # Set the table options.
3466
+ table[:_show_first_col] = ptrue?(param[:first_column]) ? 1 : 0
3467
+ table[:_show_last_col] = ptrue?(param[:last_column]) ? 1 : 0
3468
+ table[:_show_row_stripes] = ptrue?(param[:banded_rows]) ? 1 : 0
3469
+ table[:_show_col_stripes] = ptrue?(param[:banded_columns]) ? 1 : 0
3470
+ table[:_header_row_count] = ptrue?(param[:header_row]) ? 1 : 0
3471
+ table[:_totals_row_shown] = ptrue?(param[:total_row]) ? 1 : 0
3472
+
3473
+ # Set the table name.
3474
+ if param[:name]
3475
+ table[:_name] = param[:name]
3476
+ else
3477
+ # Set a default name.
3478
+ table[:_name] = "Table#{table[:id]}"
3479
+ end
3480
+
3481
+ # Set the table style.
3482
+ if param[:style]
3483
+ table[:_style] = param[:style]
3484
+ # Remove whitespace from style name.
3485
+ table[:_style].gsub!(/\s/, '')
3486
+ else
3487
+ table[:_style] = "TableStyleMedium9"
3488
+ end
3489
+
3490
+ # Swap last row/col for first row/col as necessary.
3491
+ row1, row2 = row2, row1 if row1 > row2
3492
+ col1, col2 = col2, col1 if col1 > col2
3493
+
3494
+ # Set the data range rows (without the header and footer).
3495
+ first_data_row = row1
3496
+ last_data_row = row2
3497
+ first_data_row += 1 if param[:header_row] != 0
3498
+ last_data_row -= 1 if param[:total_row]
3499
+
3500
+ # Set the table and autofilter ranges.
3501
+ table[:_range] = xl_range(row1, row2, col1, col2)
3502
+ table[:_a_range] = xl_range(row1, last_data_row, col1, col2)
3503
+
3504
+ # If the header row if off the default is to turn autofilter off.
3505
+ param[:autofilter] = 0 if param[:header_row] == 0
3506
+
3507
+ # Set the autofilter range.
3508
+ if param[:autofilter] && param[:autofilter] != 0
3509
+ table[:_autofilter] = table[:_a_range]
3510
+ end
3511
+
3512
+ # Add the table columns.
3513
+ col_id = 1
3514
+ (col1..col2).each do |col_num|
3515
+ # Set up the default column data.
3516
+ col_data = {
3517
+ :_id => col_id,
3518
+ :_name => "Column#{col_id}",
3519
+ :_total_string => '',
3520
+ :_total_function => '',
3521
+ :_formula => '',
3522
+ :_format => nil
3523
+ }
3524
+
3525
+ # Overwrite the defaults with any use defined values.
3526
+ if param[:columns]
3527
+ # Check if there are user defined values for this column.
3528
+ if user_data = param[:columns][col_id - 1]
3529
+ # Map user defined values to internal values.
3530
+ if user_data[:header] && !user_data[:header].empty?
3531
+ col_data[:_name] = user_data[:header]
3532
+ end
3533
+ # Handle the column formula.
3534
+ if user_data[:formula]
3535
+ formula = user_data[:formula]
3536
+ # Remove the leading = from formula.
3537
+ formula.sub!(/^=/, '')
3538
+ # Covert Excel 2010 "@" ref to 2007 "#This Row".
3539
+ formula.gsub!(/@/,'[#This Row],')
3540
+
3541
+ col_data[:_formula] = formula
3542
+
3543
+ (first_data_row..last_data_row).each do |row|
3544
+ write_formula(row, col_num, formula, user_data[:format])
3545
+ end
3546
+ end
3547
+
3548
+ # Handle the function for the total row.
3549
+ if user_data[:total_function]
3550
+ function = user_data[:total_function]
3551
+
3552
+ # Massage the function name.
3553
+ function = function.downcase
3554
+ function.gsub!(/_/, '')
3555
+ function.gsub!(/\s/,'')
3556
+
3557
+ function = 'countNums' if function == 'countnums'
3558
+ function = 'stdDev' if function == 'stddev'
3559
+
3560
+ col_data[:_total_function] = function
3561
+
3562
+ formula = table_function_to_formula(function, col_data[:_name])
3563
+ write_formula(row2, col_num, formula, user_data[:format])
3564
+ elsif user_data[:total_string]
3565
+ # Total label only (not a function).
3566
+ total_string = user_data[:total_string]
3567
+ col_data[:_total_string] = total_string
3568
+
3569
+ write_string(row2, col_num, total_string, user_data[:format])
3570
+ end
3571
+
3572
+ # Get the dxf format index.
3573
+ if user_data[:format]
3574
+ col_data[:_format] = user_data[:format].get_dxf_index
3575
+ end
3576
+
3577
+ # Store the column format for writing the cell data.
3578
+ # It doesn't matter if it is undefined.
3579
+ col_formats[col_id - 1] = user_data[:format]
3580
+ end
3581
+ end
3582
+
3583
+ # Store the column data.
3584
+ table[:_columns] << col_data
3585
+
3586
+ # Write the column headers to the worksheet.
3587
+ if param[:header_row] != 0
3588
+ write_string(row1, col_num, col_data[:_name])
3589
+ end
3590
+
3591
+ col_id += 1
3592
+ end # Table columns.
3593
+
3594
+ # Write the cell data if supplied.
3595
+ if data = param[:data]
3596
+
3597
+ i = 0 # For indexing the row data.
3598
+ (first_data_row..last_data_row).each do |row|
3599
+ next unless data[i]
3600
+
3601
+ j = 0 # For indexing the col data.
3602
+ (col1..col2).each do |col|
3603
+ token = data[i][j]
3604
+ write(row, col, token, col_formats[j]) if token
3605
+ j += 1
3606
+ end
3607
+ i += 1
3608
+ end
3609
+ end
3610
+
3611
+ # Store the table data.
3612
+ @tables << table
3613
+
3614
+ # Store the link used for the rels file.
3615
+ @external_table_links << ['/table', "../tables/table#{table[:id]}.xml"]
3616
+
3617
+ return table
3618
+ end
3619
+
3620
+ # List of valid input parameters.
3621
+ def valid_table_parameter
3622
+ [
3623
+ :autofilter,
3624
+ :banded_columns,
3625
+ :banded_rows,
3626
+ :columns,
3627
+ :data,
3628
+ :first_column,
3629
+ :header_row,
3630
+ :last_column,
3631
+ :name,
3632
+ :style,
3633
+ :total_row
3634
+ ]
3635
+ end
3636
+
3188
3637
  #
3189
3638
  # :call-seq:
3190
3639
  # data_validation(cell_or_cell_range, options)
@@ -3661,14 +4110,14 @@ def data_validation(*args)
3661
4110
  # If you don't supply an argument or use nil the default option
3662
4111
  # is true, i.e. only the printed gridlines are hidden.
3663
4112
  #
3664
- def hide_gridlines(option = true)
3665
- if option == true
3666
- @print_gridlines = false
3667
- @screen_gridlines = true
3668
- elsif !option
4113
+ def hide_gridlines(option = 1)
4114
+ if option == 0 || !option
3669
4115
  @print_gridlines = true # 1 = display, 0 = hide
3670
4116
  @screen_gridlines = true
3671
4117
  @print_options_changed = true
4118
+ elsif option == 1
4119
+ @print_gridlines = false
4120
+ @screen_gridlines = true
3672
4121
  else
3673
4122
  @print_gridlines = false
3674
4123
  @screen_gridlines = false
@@ -3971,7 +4420,10 @@ def filter_column_list(col, *tokens)
3971
4420
  # per worksheet in line with an Excel internal limitation.
3972
4421
  #
3973
4422
  def set_h_pagebreaks(*args)
3974
- @print_style.hbreaks += args
4423
+ breaks = args.collect do |brk|
4424
+ brk.respond_to?(:to_a) ? brk.to_a : brk
4425
+ end.flatten
4426
+ @print_style.hbreaks += breaks
3975
4427
  end
3976
4428
 
3977
4429
  #
@@ -4069,9 +4521,13 @@ def set_vml_data_id(vml_data_id) # :nodoc:
4069
4521
  count
4070
4522
  end
4071
4523
 
4524
+ def set_external_vml_links(comment_id) # :nodoc:
4525
+ @external_vml_links <<
4526
+ ['/vmlDrawing', "../drawings/vmlDrawing#{comment_id}.vml"]
4527
+ end
4528
+
4072
4529
  def set_external_comment_links(comment_id) # :nodoc:
4073
4530
  @external_comment_links <<
4074
- ['/vmlDrawing', "../drawings/vmlDrawing#{comment_id}.vml"] <<
4075
4531
  ['/comments', "../comments#{comment_id}.xml"]
4076
4532
  end
4077
4533
 
@@ -4090,17 +4546,20 @@ def prepare_chart(index, chart_id, drawing_id) # :nodoc:
4090
4546
 
4091
4547
  dimensions = position_object_emus(col, row, x_offset, y_offset, width, height)
4092
4548
 
4549
+ # Set the chart name for the embedded object if it has been specified.
4550
+ name = chart.name
4551
+
4093
4552
  # Create a Drawing object to use with worksheet unless one already exists.
4094
4553
  if !drawing?
4095
4554
  drawing = Drawing.new
4096
- drawing.add_drawing_object(drawing_type, dimensions)
4555
+ drawing.add_drawing_object(drawing_type, dimensions, 0, 0, name)
4097
4556
  drawing.embedded = 1
4098
4557
 
4099
4558
  @drawing = drawing
4100
4559
 
4101
4560
  @external_drawing_links << ['/drawing', "../drawings/drawing#{drawing_id}.xml" ]
4102
4561
  else
4103
- @drawing.add_drawing_object(drawing_type, dimensions)
4562
+ @drawing.add_drawing_object(drawing_type, dimensions, 0, 0, name)
4104
4563
  end
4105
4564
  @drawing_links << ['/chart', "../charts/chart#{chart_id}.xml"]
4106
4565
  end
@@ -4224,7 +4683,7 @@ def position_object_pixels(col_start, row_start, x1, y1, width, height, is_drawi
4224
4683
 
4225
4684
  # The following is only required for positioning drawing/chart objects
4226
4685
  # and not comments. It is probably the result of a bug.
4227
- if is_drawing
4686
+ if ptrue?(is_drawing)
4228
4687
  col_end -= 1 if width == 0
4229
4688
  row_end -= 1 if height == 0
4230
4689
  end
@@ -4279,12 +4738,30 @@ def write_cell_array_formula(formula, range) #:nodoc:
4279
4738
 
4280
4739
  private
4281
4740
 
4282
- def check_for_valid_input_params(param)
4283
- param.each_key do |param_key|
4284
- unless valid_validation_parameter.include?(param_key)
4285
- raise WriteXLSXOptionParameterError, "Unknown parameter '#{param_key}' in data_validation()"
4286
- end
4741
+ #
4742
+ # Convert a table total function to a worksheet formula.
4743
+ #
4744
+ def table_function_to_formula(function, col_name)
4745
+ subtotals = {
4746
+ :average => 101,
4747
+ :countNums => 102,
4748
+ :count => 103,
4749
+ :max => 104,
4750
+ :min => 105,
4751
+ :stdDev => 107,
4752
+ :sum => 109,
4753
+ :var => 110
4754
+ }
4755
+
4756
+ unless func_num = subtotals[function.to_sym]
4757
+ raise "Unsupported function '#{function}' in add_table()"
4287
4758
  end
4759
+ "SUBTOTAL(#{func_num},[#{col_name}])"
4760
+ end
4761
+
4762
+ def check_for_valid_input_params(param)
4763
+ check_parameter(param, valid_validation_parameter, 'data_validation')
4764
+
4288
4765
  unless param.has_key?(:validate)
4289
4766
  raise WriteXLSXOptionParameterError, "Parameter :validate is required in data_validation()"
4290
4767
  end
@@ -4394,7 +4871,8 @@ def rich_strings_fragments(rich_strings) # :nodoc:
4394
4871
  fragments = []
4395
4872
  rich_strings.each do |token|
4396
4873
  if token.respond_to?(:xf_index)
4397
- raise AugumentError, "Can't allow 2 formats in a row" if last == 'format' && pos > 0
4874
+ # Can't allow 2 formats in a row
4875
+ return nil if last == 'format' && pos > 0
4398
4876
 
4399
4877
  # Token is a format object. Add it to the fragment list.
4400
4878
  fragments << token
@@ -4418,25 +4896,34 @@ def rich_strings_fragments(rich_strings) # :nodoc:
4418
4896
  end
4419
4897
 
4420
4898
  def check_conditional_formatting_parameters(param) # :nodoc:
4421
- # List of valid validation types.
4422
- valid_type = { 'cell' => 'cellIs' }
4423
-
4424
4899
  # Check for valid input parameters.
4425
- unless (param.keys.uniq - [:type, :format, :criteria, :value, :minimum, :maximum]).empty? ||
4426
- param.has_key?(:type) ||
4427
- param.has_key?(:criteria) ||
4428
- valid_type.has_key?(param[:type].downcase) ||
4429
- valid_criteria_type.has_key?(param[:criteria].downcase)
4430
- raise WriteXLSXOptionParameterError
4900
+ unless (param.keys.uniq - valid_parameter_for_conditional_formatting).empty? &&
4901
+ param.has_key?(:type) &&
4902
+ valid_type_for_conditional_formatting.has_key?(param[:type].downcase)
4903
+ raise WriteXLSXOptionParameterError, "Invalid type : #{param[:type]}"
4431
4904
  end
4432
4905
 
4433
- param[:type] = valid_type[param[:type].downcase]
4434
- param[:criteria] = valid_criteria_type[param[:criteria].downcase]
4906
+ param[:direction] = 'bottom' if param[:type] == 'bottom'
4907
+ param[:type] = valid_type_for_conditional_formatting[param[:type].downcase]
4908
+
4909
+ # Check for valid criteria types.
4910
+ if param.has_key?(:criteria) && valid_criteria_type_for_conditional_formatting.has_key?(param[:criteria].downcase)
4911
+ param[:criteria] = valid_criteria_type_for_conditional_formatting[param[:criteria].downcase]
4912
+ end
4913
+
4914
+ # Convert date/times value if required.
4915
+ if %w[date time cellIs].include?(param[:type])
4916
+ param[:type] = 'cellIs'
4917
+
4918
+ param[:value] = convert_date_time_if_required(param[:value])
4919
+ param[:minimum] = convert_date_time_if_required(param[:minimum])
4920
+ param[:maximum] = convert_date_time_if_required(param[:maximum])
4921
+ end
4435
4922
 
4436
4923
  # 'Between' and 'Not between' criteria require 2 values.
4437
4924
  if param[:criteria] == 'between' || param[:criteria] == 'notBetween'
4438
4925
  unless param.has_key?(:minimum) || param.has_key?(:maximum)
4439
- raise WriteXLSXOptionParameterError
4926
+ raise WriteXLSXOptionParameterError, "Invalid criteria : #{param[:criteria]}"
4440
4927
  end
4441
4928
  else
4442
4929
  param[:minimum] = nil
@@ -4451,6 +4938,96 @@ def check_conditional_formatting_parameters(param) # :nodoc:
4451
4938
  end
4452
4939
  end
4453
4940
 
4941
+ def convert_date_time_if_required(val)
4942
+ if val =~ /T/
4943
+ date_time = convert_date_time(val)
4944
+ raise "Invalid date/time value '#{val}' in conditional_formatting()" unless date_time
4945
+ date_time
4946
+ else
4947
+ val
4948
+ end
4949
+ end
4950
+
4951
+ # List of valid input parameters for conditional_formatting.
4952
+ def valid_parameter_for_conditional_formatting
4953
+ [
4954
+ :type,
4955
+ :format,
4956
+ :criteria,
4957
+ :value,
4958
+ :minimum,
4959
+ :maximum,
4960
+ :min_type,
4961
+ :mid_type,
4962
+ :max_type,
4963
+ :min_value,
4964
+ :mid_value,
4965
+ :max_value,
4966
+ :min_color,
4967
+ :mid_color,
4968
+ :max_color,
4969
+ :bar_color
4970
+ ]
4971
+ end
4972
+
4973
+ # List of valid validation types for conditional_formatting.
4974
+ def valid_type_for_conditional_formatting
4975
+ {
4976
+ 'cell' => 'cellIs',
4977
+ 'date' => 'date',
4978
+ 'time' => 'time',
4979
+ 'average' => 'aboveAverage',
4980
+ 'duplicate' => 'duplicateValues',
4981
+ 'unique' => 'uniqueValues',
4982
+ 'top' => 'top10',
4983
+ 'bottom' => 'top10',
4984
+ 'text' => 'text',
4985
+ 'time_period' => 'timePeriod',
4986
+ 'blanks' => 'containsBlanks',
4987
+ 'no_blanks' => 'notContainsBlanks',
4988
+ 'errors' => 'containsErrors',
4989
+ 'no_errors' => 'notContainsErrors',
4990
+ '2_color_scale' => '2_color_scale',
4991
+ '3_color_scale' => '3_color_scale',
4992
+ 'data_bar' => 'dataBar',
4993
+ 'formula' => 'expression'
4994
+ }
4995
+ end
4996
+
4997
+ # List of valid criteria types for conditional_formatting.
4998
+ def valid_criteria_type_for_conditional_formatting
4999
+ {
5000
+ 'between' => 'between',
5001
+ 'not between' => 'notBetween',
5002
+ 'equal to' => 'equal',
5003
+ '=' => 'equal',
5004
+ '==' => 'equal',
5005
+ 'not equal to' => 'notEqual',
5006
+ '!=' => 'notEqual',
5007
+ '<>' => 'notEqual',
5008
+ 'greater than' => 'greaterThan',
5009
+ '>' => 'greaterThan',
5010
+ 'less than' => 'lessThan',
5011
+ '<' => 'lessThan',
5012
+ 'greater than or equal to' => 'greaterThanOrEqual',
5013
+ '>=' => 'greaterThanOrEqual',
5014
+ 'less than or equal to' => 'lessThanOrEqual',
5015
+ '<=' => 'lessThanOrEqual',
5016
+ 'containing' => 'containsText',
5017
+ 'not containing' => 'notContains',
5018
+ 'begins with' => 'beginsWith',
5019
+ 'ends with' => 'endsWith',
5020
+ 'yesterday' => 'yesterday',
5021
+ 'today' => 'today',
5022
+ 'last 7 days' => 'last7Days',
5023
+ 'last week' => 'lastWeek',
5024
+ 'this week' => 'thisWeek',
5025
+ 'next week' => 'nextWeek',
5026
+ 'last month' => 'lastMonth',
5027
+ 'this month' => 'thisMonth',
5028
+ 'next month' => 'nextMonth'
5029
+ }
5030
+ end
4454
5031
  # Pad out the rest of the area with formatted blank cells.
4455
5032
  def write_formatted_blank_to_area(row_first, row_last, col_first, col_last, format)
4456
5033
  (row_first .. row_last).each do |row|
@@ -4625,6 +5202,10 @@ def parse_filter_tokens(expression, tokens) #:nodoc:
4625
5202
  # based on the default or user defined values in the Workbook palette.
4626
5203
  #
4627
5204
  def get_palette_color(index) #:nodoc:
5205
+ if index =~ /^#([0-9A-F]{6})$/i
5206
+ return "FF#{$~[1]}"
5207
+ end
5208
+
4628
5209
  # Adjust the colour index.
4629
5210
  index -= 8
4630
5211
 
@@ -4697,6 +5278,44 @@ def position_object_emus(col_start, row_start, x1, y1, width, height) #:nodoc:
4697
5278
  [col_start, row_start, x1, y1, col_end, row_end, x2, y2, x_abs, y_abs]
4698
5279
  end
4699
5280
 
5281
+ #
5282
+ # Calculate the vertices that define the position of a shape object within
5283
+ # the worksheet in EMUs. Save the vertices with the object.
5284
+ #
5285
+ # The vertices are expressed as English Metric Units (EMUs). There are 12,700
5286
+ # EMUs per point. Therefore, 12,700 * 3 /4 = 9,525 EMUs per pixel.
5287
+ #
5288
+ def position_shape_emus(shape)
5289
+ col_start, row_start, x1, y1, col_end, row_end, x2, y2, x_abs, y_abs =
5290
+ position_object_pixels(
5291
+ shape[:column_start],
5292
+ shape[:row_start],
5293
+ shape[:x_offset],
5294
+ shape[:y_offset],
5295
+ shape[:width] * shape[:scale_x],
5296
+ shape[:height] * shape[:scale_y],
5297
+ shape[:drawing]
5298
+ )
5299
+
5300
+ # Now that x2/y2 have been calculated with a potentially negative
5301
+ # width/height we use the absolute value and convert to EMUs.
5302
+ shape[:width_emu] = (shape[:width] * 9_525).abs.to_i
5303
+ shape[:height_emu] = (shape[:height] * 9_525).abs.to_i
5304
+
5305
+ shape[:column_start] = col_start.to_i
5306
+ shape[:row_start] = row_start.to_i
5307
+ shape[:column_end] = col_end.to_i
5308
+ shape[:row_end] = row_end.to_i
5309
+
5310
+ # Convert the pixel values to EMUs. See above.
5311
+ shape[:x1] = (x1 * 9_525).to_i
5312
+ shape[:y1] = (y1 * 9_525).to_i
5313
+ shape[:x2] = (x2 * 9_525).to_i
5314
+ shape[:y2] = (y2 * 9_525).to_i
5315
+ shape[:x_abs] = (x_abs * 9_525).to_i
5316
+ shape[:y_abs] = (y_abs * 9_525).to_i
5317
+ end
5318
+
4700
5319
  #
4701
5320
  # Convert the width of a cell from user's units to pixels. Excel rounds the
4702
5321
  # column width to the nearest pixel. If the width hasn't been set by the user
@@ -4760,8 +5379,8 @@ def prepare_image(index, image_id, drawing_id, width, height, name, image_type)
4760
5379
  dimensions = position_object_emus(col, row, x_offset, y_offset, width, height)
4761
5380
 
4762
5381
  # Convert from pixels to emus.
4763
- width = int(0.5 + (width * 9_525))
4764
- height = int(0.5 + (height * 9_525))
5382
+ width = (0.5 + (width * 9_525)).to_i
5383
+ height = (0.5 + (height * 9_525)).to_i
4765
5384
 
4766
5385
  # Create a Drawing object to use with worksheet unless one already exists.
4767
5386
  if !drawing?
@@ -4779,6 +5398,234 @@ def prepare_image(index, image_id, drawing_id, width, height, name, image_type)
4779
5398
 
4780
5399
  @drawing_links << ['/image', "../media/image#{image_id}.#{image_type}"]
4781
5400
  end
5401
+ public :prepare_image
5402
+
5403
+ #
5404
+ # Insert a shape into the worksheet.
5405
+ #
5406
+ # This method can be used to insert a Shape object into a worksheet.
5407
+ # The Shape must be created by the add_shape() Workbook method.
5408
+ #
5409
+ # shape = workbook.add_shape(:name => 'My Shape', :type => 'plus')
5410
+ #
5411
+ # # Configure the shape.
5412
+ # shape.set_text('foo')
5413
+ # ...
5414
+ #
5415
+ # # Insert the shape into the a worksheet.
5416
+ # worksheet.insert_shape('E2', shape)
5417
+ #
5418
+ # See add_shape() for details on how to create the Shape object
5419
+ # and Excel::Writer::XLSX::Shape for details on how to configure it.
5420
+ #
5421
+ # The x, y, scale_x and scale_y parameters are optional.
5422
+ #
5423
+ # The parameters x and y can be used to specify an offset
5424
+ # from the top left hand corner of the cell specified by row and col.
5425
+ # The offset values are in pixels.
5426
+ #
5427
+ # worksheet1.insert_shape('E2', chart, 3, 3)
5428
+ #
5429
+ # The parameters scale_x and scale_y can be used to scale the
5430
+ # inserted shape horizontally and vertically:
5431
+ #
5432
+ # # Scale the width by 120% and the height by 150%
5433
+ # worksheet.insert_shape('E2', shape, 0, 0, 1.2, 1.5)
5434
+ # See also the shape*.pl programs in the examples directory of the distro.
5435
+ #
5436
+ def insert_shape(*args)
5437
+ # Check for a cell reference in A1 notation and substitute row and column.
5438
+ row_start, column_start, shape, x_offset, y_offset, scale_x, scale_y =
5439
+ row_col_notation(args)
5440
+ if [row_start, column_start, shape].include?(nil)
5441
+ raise "Insufficient arguments in insert_shape()"
5442
+ end
5443
+
5444
+ # Set the shape properties
5445
+ shape[:row_start] = row_start
5446
+ shape[:column_start] = column_start
5447
+ shape[:x_offset] = x_offset || 0
5448
+ shape[:y_offset] = y_offset || 0
5449
+
5450
+ # Override shape scale if supplied as an argument. Otherwise, use the
5451
+ # existing shape scale factors.
5452
+ shape[:scale_x] = scale_x if scale_x
5453
+ shape[:scale_y] = scale_y if scale_y
5454
+
5455
+ # Assign a shape ID.
5456
+ while true
5457
+ id = shape[:id] || 0
5458
+ used = @shape_hash[id]
5459
+
5460
+ # Test if shape ID is already used. Otherwise assign a new one.
5461
+ if !used && id != 0
5462
+ break
5463
+ else
5464
+ @last_shape_id += 1
5465
+ shape[:id] = @last_shape_id
5466
+ end
5467
+ end
5468
+
5469
+ shape[:element] = @shapes.size
5470
+
5471
+ # Allow lookup of entry into shape array by shape ID.
5472
+ @shape_hash[shape[:id]] = shape[:element]
5473
+
5474
+ # Create link to Worksheet color palette.
5475
+ shape[:palette] = @workbook.palette
5476
+
5477
+ if ptrue?(shape[:stencil])
5478
+ # Insert a copy of the shape, not a reference so that the shape is
5479
+ # used as a stencil. Previously stamped copies don't get modified
5480
+ # if the stencil is modified.
5481
+ insert = shape.dup
5482
+
5483
+ # For connectors change x/y coords based on location of connected shapes.
5484
+ auto_locate_connectors(insert)
5485
+
5486
+ @shapes << insert
5487
+ insert
5488
+ else
5489
+ # For connectors change x/y coords based on location of connected shapes.
5490
+ auto_locate_connectors(shape)
5491
+
5492
+ # Insert a link to the shape on the list of shapes. Connection to
5493
+ # the parent shape is maintained.
5494
+ @shapes << shape
5495
+ return shape
5496
+ end
5497
+ end
5498
+ public :insert_shape
5499
+
5500
+ #
5501
+ # Set up drawing shapes
5502
+ #
5503
+ def prepare_shape(index, drawing_id)
5504
+ shape = @shapes[index]
5505
+ drawing_type = 3
5506
+
5507
+ # Create a Drawing object to use with worksheet unless one already exists.
5508
+ unless drawing?
5509
+ @drawing = Drawing.new
5510
+ @drawing.embedded = 1
5511
+ @external_drawing_links << ['/drawing', "../drawings/drawing#{drawing_id}.xml"]
5512
+ end
5513
+
5514
+ # Validate the he shape against various rules.
5515
+ validate_shape(shape, index)
5516
+ position_shape_emus(shape)
5517
+
5518
+ dimensions = [
5519
+ shape[:column_start], shape[:row_start],
5520
+ shape[:x1], shape[:y1],
5521
+ shape[:column_end], shape[:row_end],
5522
+ shape[:x2], shape[:y2],
5523
+ shape[:x_abs], shape[:y_abs],
5524
+ shape[:width_emu], shape[:height_emu]
5525
+ ]
5526
+
5527
+ drawing.add_drawing_object(drawing_type, dimensions, shape[:name], shape)
5528
+ end
5529
+ public :prepare_shape
5530
+
5531
+ #
5532
+ # Re-size connector shapes if they are connected to other shapes.
5533
+ #
5534
+ def auto_locate_connectors(shape)
5535
+ # Valid connector shapes.
5536
+ connector_shapes = {
5537
+ :straightConnector => 1,
5538
+ :Connector => 1,
5539
+ :bentConnector => 1,
5540
+ :curvedConnector => 1,
5541
+ :line => 1
5542
+ }
5543
+
5544
+ shape_base = shape[:type].chop.to_sym # Remove the number of segments from end of type.
5545
+ shape[:connect] = connector_shapes[shape_base] ? 1 : 0
5546
+ return if shape[:connect] == 0
5547
+
5548
+ # Both ends have to be connected to size it.
5549
+ return if shape[:start] == 0 && shape[:end] == 0
5550
+
5551
+ # Both ends need to provide info about where to connect.
5552
+ return if shape[:start_side] == 0 && shape[:end_side] == 0
5553
+
5554
+ sid = shape[:start]
5555
+ eid = shape[:end]
5556
+
5557
+ slink_id = @shape_hash[sid] || 0
5558
+ sls = @shapes.fetch(slink_id, Hash.new(0))
5559
+ elink_id = @shape_hash[eid] || 0
5560
+ els = @shapes.fetch(elink_id, Hash.new(0))
5561
+
5562
+ # Assume shape connections are to the middle of an object, and
5563
+ # not a corner (for now).
5564
+ connect_type = shape[:start_side] + shape[:end_side]
5565
+ smidx = sls[:x_offset] + sls[:width] / 2
5566
+ emidx = els[:x_offset] + els[:width] / 2
5567
+ smidy = sls[:y_offset] + sls[:height] / 2
5568
+ emidy = els[:y_offset] + els[:height] / 2
5569
+ netx = (smidx - emidx).abs
5570
+ nety = (smidy - emidy).abs
5571
+
5572
+ if connect_type == 'bt'
5573
+ sy = sls[:y_offset] + sls[:height]
5574
+ ey = els[:y_offset]
5575
+
5576
+ shape[:width] = (emidx - smidx).to_i.abs
5577
+ shape[:x_offset] = [smidx, emidx].min.to_i
5578
+ shape[:height] =
5579
+ (els[:y_offset] - (sls[:y_offset] + sls[:height])).to_i.abs
5580
+ shape[:y_offset] =
5581
+ [sls[:y_offset] + sls[:height], els[:y_offset]].min.to_i
5582
+ shape[:flip_h] = smidx < emidx ? 1 : 0
5583
+ shape[:rotation] = 90
5584
+
5585
+ if sy > ey
5586
+ shape[:flip_v] = 1
5587
+
5588
+ # Create 3 adjustments for an end shape vertically above a
5589
+ # start shape. Adjustments count from the upper left object.
5590
+ if shape[:adjustments].empty?
5591
+ shape[:adjustments] = [-10, 50, 110]
5592
+ end
5593
+ shape[:type] = 'bentConnector5'
5594
+ end
5595
+ elsif connect_type == 'rl'
5596
+ shape[:width] =
5597
+ (els[:x_offset] - (sls[:x_offset] + sls[:width])).to_i.abs
5598
+ shape[:height] = (emidy - smidy).to_i.abs
5599
+ shape[:x_offset] =
5600
+ [sls[:x_offset] + sls[:width], els[:x_offset]].min
5601
+ shape[:y_offset] = [smidy, emidy].min
5602
+
5603
+ shape[:flip_h] = 1 if smidx < emidx && smidy > emidy
5604
+ shape[:flip_h] = 1 if smidx > emidx && smidy < emidy
5605
+
5606
+ if smidx > emidx
5607
+ # Create 3 adjustments for an end shape to the left of a
5608
+ # start shape.
5609
+ if shape[:adjustments].empty?
5610
+ shape[:adjustments] = [-10, 50, 110]
5611
+ end
5612
+ shape[:type] = 'bentConnector5'
5613
+ end
5614
+ end
5615
+ end
5616
+
5617
+ #
5618
+ # Check shape attributes to ensure they are valid.
5619
+ #
5620
+ def validate_shape(shape, index)
5621
+ unless %w[l ctr r just].include?(shape[:align])
5622
+ raise "Shape #{index} (#{shape[:type]}) alignment (#{shape[:align]}) not in ['l', 'ctr', 'r', 'just']\n"
5623
+ end
5624
+
5625
+ unless %w[t ctr b].include?(shape[:valign])
5626
+ raise "Shape #{index} (#{shape[:type]}) vertical alignment (#{shape[:valign]}) not in ['t', 'ctr', 'v']\n"
5627
+ end
5628
+ end
4782
5629
 
4783
5630
  #
4784
5631
  # Based on the algorithm provided by Daniel Rentz of OpenOffice.
@@ -4819,13 +5666,14 @@ def write_worksheet #:nodoc:
4819
5666
  # Write the <sheetPr> element for Sheet level properties.
4820
5667
  #
4821
5668
  def write_sheet_pr #:nodoc:
4822
- return if !fit_page? && !filter_on? && !tab_color?
5669
+ return if !fit_page? && !filter_on? && !tab_color? && !outline_changed?
4823
5670
  attributes = []
4824
5671
  (attributes << 'filterMode' << 1) if filter_on?
4825
5672
 
4826
- if fit_page? || tab_color?
5673
+ if fit_page? || tab_color? || outline_changed?
4827
5674
  @writer.tag_elements('sheetPr', attributes) do
4828
5675
  write_tab_color
5676
+ write_outline_pr
4829
5677
  write_page_set_up_pr
4830
5678
  end
4831
5679
  else
@@ -4896,6 +5744,9 @@ def write_sheet_view #:nodoc:
4896
5744
  # Show that the sheet tab is selected.
4897
5745
  attributes << 'tabSelected' << 1 if @selected
4898
5746
 
5747
+ # Turn outlines off. Also required in the outlinePr element.
5748
+ attributes << "showOutlineSymbols" << 0 if @outline_on
5749
+
4899
5750
  # Set the page view/layout mode if required.
4900
5751
  # TODO. Add pageBreakPreview mode when requested.
4901
5752
  (attributes << 'view' << 'pageLayout') if page_view?
@@ -4988,6 +5839,7 @@ def write_col_info(*args) #:nodoc:
4988
5839
  padding = 5.0
4989
5840
  if width && width > 0
4990
5841
  width = ((width * max_digit_width + padding) / max_digit_width * 256).to_i/256.0
5842
+ width = width.to_i if width.to_s =~ /\.0+$/
4991
5843
  end
4992
5844
  attributes = [
4993
5845
  'min', min + 1,
@@ -5039,6 +5891,8 @@ def write_rows #:nodoc:
5039
5891
 
5040
5892
  write_cell_column_dimension(row_num)
5041
5893
  @writer.end_tag('row')
5894
+ elsif @comments[row_num]
5895
+ write_empty_row(row_num, span, *(@set_rows[row_num]))
5042
5896
  else
5043
5897
  # Row attributes only.
5044
5898
  write_empty_row(row_num, nil, *(@set_rows[row_num]))
@@ -5107,12 +5961,12 @@ def write_row_element(r, spans = nil, height = 15, format = nil, hidden = false,
5107
5961
  (attributes << 's' << xf_index) if xf_index != 0
5108
5962
  (attributes << 'customFormat' << 1 ) if format
5109
5963
  (attributes << 'ht' << height) if height != 15
5110
- (attributes << 'hidden' << 1 ) if !!hidden && hidden != 0
5964
+ (attributes << 'hidden' << 1 ) if ptrue?(hidden)
5111
5965
  (attributes << 'customHeight' << 1 ) if height != 15
5112
- (attributes << 'outlineLevel' << level) if !!level && level != 0
5113
- (attributes << 'collapsed' << 1 ) if !!collapsed && collapsed != 0
5966
+ (attributes << 'outlineLevel' << level) if ptrue?(level)
5967
+ (attributes << 'collapsed' << 1 ) if ptrue?(collapsed)
5114
5968
 
5115
- if empty_row && empty_row != 0
5969
+ if ptrue?(empty_row)
5116
5970
  @writer.empty_tag('row', attributes)
5117
5971
  else
5118
5972
  @writer.start_tag('row', attributes)
@@ -5373,16 +6227,20 @@ def write_mx_plv #:nodoc:
5373
6227
  # Write the <mergeCells> element.
5374
6228
  #
5375
6229
  def write_merge_cells #:nodoc:
5376
- return if @merge.empty?
5377
-
5378
- attributes = ['count', @merge.size]
5379
-
5380
- @writer.tag_elements('mergeCells', attributes) do
5381
- # Write the mergeCell element.
6230
+ write_some_elements('mergeCells', @merge) do
5382
6231
  @merge.each { |merged_range| write_merge_cell(merged_range) }
5383
6232
  end
5384
6233
  end
5385
6234
 
6235
+ def write_some_elements(tag, container)
6236
+ return if container.empty?
6237
+
6238
+ attributes = ['count', container.size]
6239
+
6240
+ @writer.tag_elements(tag, attributes) do
6241
+ yield
6242
+ end
6243
+ end
5386
6244
 
5387
6245
  #
5388
6246
  # Write the <mergeCell> element.
@@ -5531,7 +6389,8 @@ def write_autofilters #:nodoc:
5531
6389
  tokens = @filter_cols[col]
5532
6390
  type = @filter_type[col]
5533
6391
 
5534
- write_filter_column(col, type, *tokens)
6392
+ # Filters are relative to first column in the autofilter.
6393
+ write_filter_column(col - col1, type, *tokens)
5535
6394
  end
5536
6395
  end
5537
6396
 
@@ -5689,6 +6548,22 @@ def write_tab_color #:nodoc:
5689
6548
  @writer.empty_tag('tabColor', attributes)
5690
6549
  end
5691
6550
 
6551
+ #
6552
+ # Write the <outlinePr> element.
6553
+ #
6554
+ def write_outline_pr
6555
+ attributes = []
6556
+
6557
+ return unless outline_changed?
6558
+
6559
+ attributes << "applyStyles" << 1 if @outline_style != 0
6560
+ attributes << "summaryBelow" << 0 if @outline_below == 0
6561
+ attributes << "summaryRight" << 0 if @outline_right == 0
6562
+ attributes << "showOutlineSymbols" << 0 if @outline_on == 0
6563
+
6564
+ @writer.empty_tag('outlinePr', attributes)
6565
+ end
6566
+
5692
6567
  #
5693
6568
  # Write the <sheetProtection> element.
5694
6569
  #
@@ -5696,27 +6571,27 @@ def write_sheet_protection #:nodoc:
5696
6571
  return unless protect?
5697
6572
 
5698
6573
  attributes = []
5699
- attributes << "password" << @protect[:password] if @protect[:password]
5700
- attributes << "sheet" << 1 if @protect[:sheet]
5701
- attributes << "content" << 1 if @protect[:content]
5702
- attributes << "objects" << 1 if !@protect[:objects]
5703
- attributes << "scenarios" << 1 if !@protect[:scenarios]
5704
- attributes << "formatCells" << 0 if @protect[:format_cells]
5705
- attributes << "formatColumns" << 0 if @protect[:format_columns]
5706
- attributes << "formatRows" << 0 if @protect[:format_rows]
5707
- attributes << "insertColumns" << 0 if @protect[:insert_columns]
5708
- attributes << "insertRows" << 0 if @protect[:insert_rows]
5709
- attributes << "insertHyperlinks" << 0 if @protect[:insert_hyperlinks]
5710
- attributes << "deleteColumns" << 0 if @protect[:delete_columns]
5711
- attributes << "deleteRows" << 0 if @protect[:delete_rows]
5712
-
5713
- attributes << "selectLockedCells" << 1 if !@protect[:select_locked_cells]
5714
-
5715
- attributes << "sort" << 0 if @protect[:sort]
5716
- attributes << "autoFilter" << 0 if @protect[:autofilter]
5717
- attributes << "pivotTables" << 0 if @protect[:pivot_tables]
5718
-
5719
- attributes << "selectUnlockedCells" << 1 if !@protect[:select_unlocked_cells]
6574
+ attributes << "password" << @protect[:password] if ptrue?(@protect[:password])
6575
+ attributes << "sheet" << 1 if ptrue?(@protect[:sheet])
6576
+ attributes << "content" << 1 if ptrue?(@protect[:content])
6577
+ attributes << "objects" << 1 unless ptrue?(@protect[:objects])
6578
+ attributes << "scenarios" << 1 unless ptrue?(@protect[:scenarios])
6579
+ attributes << "formatCells" << 0 if ptrue?(@protect[:format_cells])
6580
+ attributes << "formatColumns" << 0 if ptrue?(@protect[:format_columns])
6581
+ attributes << "formatRows" << 0 if ptrue?(@protect[:format_rows])
6582
+ attributes << "insertColumns" << 0 if ptrue?(@protect[:insert_columns])
6583
+ attributes << "insertRows" << 0 if ptrue?(@protect[:insert_rows])
6584
+ attributes << "insertHyperlinks" << 0 if ptrue?(@protect[:insert_hyperlinks])
6585
+ attributes << "deleteColumns" << 0 if ptrue?(@protect[:delete_columns])
6586
+ attributes << "deleteRows" << 0 if ptrue?(@protect[:delete_rows])
6587
+
6588
+ attributes << "selectLockedCells" << 1 unless ptrue?(@protect[:select_locked_cells])
6589
+
6590
+ attributes << "sort" << 0 if ptrue?(@protect[:sort])
6591
+ attributes << "autoFilter" << 0 if ptrue?(@protect[:autofilter])
6592
+ attributes << "pivotTables" << 0 if ptrue?(@protect[:pivot_tables])
6593
+
6594
+ attributes << "selectUnlockedCells" << 1 unless ptrue?(@protect[:select_unlocked_cells])
5720
6595
 
5721
6596
  @writer.empty_tag('sheetProtection', attributes)
5722
6597
  end
@@ -5725,7 +6600,9 @@ def write_sheet_protection #:nodoc:
5725
6600
  # Write the <drawing> elements.
5726
6601
  #
5727
6602
  def write_drawings #:nodoc:
5728
- write_drawing(@hlink_count + 1) if drawing?
6603
+ return unless drawing?
6604
+ @rel_count += 1
6605
+ write_drawing(@rel_count)
5729
6606
  end
5730
6607
 
5731
6608
  #
@@ -5746,8 +6623,8 @@ def write_legacy_drawing #:nodoc:
5746
6623
  return unless has_comments?
5747
6624
 
5748
6625
  # Increment the relationship id for any drawings or comments.
5749
- id = @hlink_count + 1
5750
- id += 1 if @drawing
6626
+ @rel_count += 1
6627
+ id = @rel_count
5751
6628
 
5752
6629
  attributes = ['r:id', "rId#{id}"]
5753
6630
 
@@ -5775,9 +6652,9 @@ def write_font(writer, format) #:nodoc:
5775
6652
 
5776
6653
  theme = format.theme
5777
6654
  color = format.color
5778
- if !theme.nil? && theme != 0
6655
+ if ptrue?(theme)
5779
6656
  write_color(writer, 'theme', theme)
5780
- elsif !color.nil? && color != 0
6657
+ elsif ptrue?(color)
5781
6658
  color = get_palette_color(color)
5782
6659
  write_color(writer, 'rgb', color)
5783
6660
  else
@@ -5820,14 +6697,40 @@ def write_color(writer, name, value) #:nodoc:
5820
6697
  end
5821
6698
 
5822
6699
  #
5823
- # Write the <dataValidations> element.
6700
+ # Write the <tableParts> element.
5824
6701
  #
5825
- def write_data_validations #:nodoc:
5826
- return if @validations.empty?
6702
+ def write_table_parts
6703
+ # Return if worksheet doesn't contain any tables.
6704
+ return if @tables.empty?
6705
+
6706
+ attributes = ['count', @tables.size]
6707
+
6708
+ @writer.tag_elements('tableParts', attributes) do
6709
+
6710
+ @tables.each do |table|
6711
+ # Write the tablePart element.
6712
+ @rel_count += 1
6713
+ write_table_part(@rel_count)
6714
+ end
6715
+ end
6716
+ end
6717
+
6718
+ #
6719
+ # Write the <tablePart> element.
6720
+ #
6721
+ def write_table_part(id)
6722
+ r_id = "rId#{id}"
5827
6723
 
5828
- attributes = ['count', @validations.size]
6724
+ attributes = ['r:id', r_id]
5829
6725
 
5830
- @writer.tag_elements('dataValidations', attributes) do
6726
+ @writer.empty_tag('tablePart', attributes)
6727
+ end
6728
+
6729
+ #
6730
+ # Write the <dataValidations> element.
6731
+ #
6732
+ def write_data_validations #:nodoc:
6733
+ write_some_elements('dataValidations', @validations) do
5831
6734
  @validations.each { |validation| write_data_validation(validation) }
5832
6735
  end
5833
6736
  end
@@ -5912,9 +6815,48 @@ def write_formula_2(formula) #:nodoc:
5912
6815
  # in Perl module : _write_formula()
5913
6816
  #
5914
6817
  def write_formula_tag(data) #:nodoc:
6818
+ data = data.sub(/^=/, '') if data.respond_to?(:sub)
5915
6819
  @writer.data_element('formula', data)
5916
6820
  end
5917
6821
 
6822
+ #
6823
+ # Write the <colorScale> element.
6824
+ #
6825
+ def write_color_scale(param)
6826
+ @writer.tag_elements('colorScale') do
6827
+ write_cfvo(param[:min_type], param[:min_value])
6828
+ write_cfvo(param[:mid_type], param[:mid_value]) if param[:mid_type]
6829
+ write_cfvo(param[:max_type], param[:max_value])
6830
+ write_color(@writer, 'rgb', param[:min_color])
6831
+ write_color(@writer, 'rgb', param[:mid_color]) if param[:mid_color]
6832
+ write_color(@writer, 'rgb', param[:max_color])
6833
+ end
6834
+ end
6835
+
6836
+ #
6837
+ # Write the <dataBar> element.
6838
+ #
6839
+ def write_data_bar(param)
6840
+ @writer.tag_elements('dataBar') do
6841
+ write_cfvo(param[:min_type], param[:min_value])
6842
+ write_cfvo(param[:max_type], param[:max_value])
6843
+
6844
+ write_color(@writer, 'rgb', param[:bar_color])
6845
+ end
6846
+ end
6847
+
6848
+ #
6849
+ # Write the <cfvo> element.
6850
+ #
6851
+ def write_cfvo(type, val)
6852
+ attributes = [
6853
+ 'type', type,
6854
+ 'val', val
6855
+ ]
6856
+
6857
+ @writer.empty_tag('cfvo', attributes)
6858
+ end
6859
+
5918
6860
  #
5919
6861
  # Write the Worksheet conditional formats.
5920
6862
  #
@@ -5928,6 +6870,23 @@ def write_conditional_formats #:nodoc:
5928
6870
  #
5929
6871
  # Write the <conditionalFormatting> element.
5930
6872
  #
6873
+ # The conditional_formatting() method is used to add formatting
6874
+ # to a cell or range of cells based on user defined criteria.
6875
+ #
6876
+ # worksheet.conditional_formatting('A1:J10',
6877
+ # {
6878
+ # :type => 'cell',
6879
+ # :criteria => '>=',
6880
+ # :value => 50,
6881
+ # :format => format1
6882
+ # }
6883
+ # )
6884
+ # This method contains a lot of parameters and is described
6885
+ # in detail in a separate section "CONDITIONAL FORMATTING IN EXCEL".
6886
+ #
6887
+ # See also the conditional_format.rb program in the examples directory
6888
+ # of the distro
6889
+ #
5931
6890
  def write_conditional_formatting(range, params) #:nodoc:
5932
6891
  attributes = ['sqref', range]
5933
6892
 
@@ -5946,10 +6905,11 @@ def write_cf_rule(param) #:nodoc:
5946
6905
  attributes << 'dxfId' << param[:format]
5947
6906
  end
5948
6907
  attributes << 'priority' << param[:priority]
5949
- attributes << 'operator' << param[:criteria]
5950
6908
 
5951
- @writer.tag_elements('cfRule', attributes) do
5952
- if param[:type] == 'cellIs'
6909
+ case param[:type]
6910
+ when 'cellIs'
6911
+ attributes << 'operator' << param[:criteria]
6912
+ @writer.tag_elements('cfRule', attributes) do
5953
6913
  if param[:minimum] && param[:maximum]
5954
6914
  write_formula_tag(param[:minimum])
5955
6915
  write_formula_tag(param[:maximum])
@@ -5957,6 +6917,48 @@ def write_cf_rule(param) #:nodoc:
5957
6917
  write_formula_tag(param[:value])
5958
6918
  end
5959
6919
  end
6920
+ when 'aboveAverage'
6921
+ attributes << 'aboveAverage' << 0 if param[:criteria] =~ /below/
6922
+ attributes << 'equalAverage' << 1 if param[:criteria] =~ /equal/
6923
+ if param[:criteria] =~ /([123]) std dev/
6924
+ attributes << 'stdDev' << $~[1]
6925
+ end
6926
+ @writer.empty_tag('cfRule', attributes)
6927
+ when 'top10'
6928
+ attributes << 'percent' << 1 if param[:criteria] == '%'
6929
+ attributes << 'bottom' << 1 if param[:direction]
6930
+ rank = param[:value] || 10
6931
+ attributes << 'rank' << rank
6932
+ @writer.empty_tag('cfRule', attributes)
6933
+ when 'duplicateValues', 'uniqueValues'
6934
+ @writer.empty_tag('cfRule', attributes)
6935
+ when 'containsText', 'notContainsText', 'beginsWith', 'endsWith'
6936
+ attributes << 'operator' << param[:criteria]
6937
+ attributes << 'text' << param[:value]
6938
+ @writer.tag_elements('cfRule', attributes) do
6939
+ write_formula_tag(param[:formula])
6940
+ end
6941
+ when 'timePeriod'
6942
+ attributes << 'timePeriod' << param[:criteria]
6943
+ @writer.tag_elements('cfRule', attributes) do
6944
+ write_formula_tag(param[:formula])
6945
+ end
6946
+ when 'containsBlanks', 'notContainsBlanks', 'containsErrors', 'notContainsErrors'
6947
+ @writer.tag_elements('cfRule', attributes) do
6948
+ write_formula_tag(param[:formula])
6949
+ end
6950
+ when 'colorScale'
6951
+ @writer.tag_elements('cfRule', attributes) do
6952
+ write_color_scale(param)
6953
+ end
6954
+ when 'dataBar'
6955
+ @writer.tag_elements('cfRule', attributes) do
6956
+ write_data_bar(param)
6957
+ end
6958
+ when 'expression'
6959
+ @writer.tag_elements('cfRule', attributes) do
6960
+ write_formula_tag(param[:criteria])
6961
+ end
5960
6962
  end
5961
6963
  end
5962
6964
 
@@ -5981,7 +6983,7 @@ def row_col_notation(args) # :nodoc:
5981
6983
 
5982
6984
  #
5983
6985
  # Check that row and col are valid and store max and min values for use in
5984
- # DIMENSIONS record. See, store_dimensions().
6986
+ # other methods/elements.
5985
6987
  #
5986
6988
  # The ignore_row/ignore_col flags is used to indicate that we wish to
5987
6989
  # perform the dimension check without storing the value.
@@ -6029,19 +7031,15 @@ def calculate_spans #:nodoc:
6029
7031
  span_min = nil
6030
7032
  span_max = 0
6031
7033
  spans = []
7034
+
6032
7035
  (@dim_rowmin .. @dim_rowmax).each do |row_num|
6033
7036
  if @cell_data_table[row_num]
6034
- (@dim_colmin .. @dim_colmax).each do |col_num|
6035
- if @cell_data_table[row_num][col_num]
6036
- if !span_min
6037
- span_min = col_num
6038
- span_max = col_num
6039
- else
6040
- span_min = col_num if col_num < span_min
6041
- span_max = col_num if col_num > span_max
6042
- end
6043
- end
6044
- end
7037
+ span_min, span_max = calc_spans(@cell_data_table, row_num, span_min, span_max)
7038
+ end
7039
+
7040
+ # Calculate spans for comments.
7041
+ if @comments[row_num]
7042
+ span_min, span_max = calc_spans(@comments, row_num, span_min, span_max)
6045
7043
  end
6046
7044
 
6047
7045
  if ((row_num + 1) % 16 == 0) || (row_num == @dim_rowmax)
@@ -6058,6 +7056,21 @@ def calculate_spans #:nodoc:
6058
7056
  @row_spans = spans
6059
7057
  end
6060
7058
 
7059
+ def calc_spans(data, row_num, span_min, span_max)
7060
+ (@dim_colmin .. @dim_colmax).each do |col_num|
7061
+ if data[row_num][col_num]
7062
+ if !span_min
7063
+ span_min = col_num
7064
+ span_max = col_num
7065
+ else
7066
+ span_min = col_num if col_num < span_min
7067
+ span_max = col_num if col_num > span_max
7068
+ end
7069
+ end
7070
+ end
7071
+ [span_min, span_max]
7072
+ end
7073
+
6061
7074
  def xf(format) #:nodoc:
6062
7075
  if format.kind_of?(Format)
6063
7076
  format.xf_index
@@ -6131,23 +7144,19 @@ def fit_page? #:nodoc:
6131
7144
  end
6132
7145
 
6133
7146
  def filter_on? #:nodoc:
6134
- if @filter_on
6135
- @filter_on != 0
6136
- else
6137
- false
6138
- end
7147
+ ptrue?(@filter_on)
6139
7148
  end
6140
7149
 
6141
7150
  def tab_color? #:nodoc:
6142
- if @tab_color
6143
- @tab_color != 0
6144
- else
6145
- false
6146
- end
7151
+ ptrue?(@tab_color)
7152
+ end
7153
+
7154
+ def outline_changed?
7155
+ ptrue?(@outline_changed)
6147
7156
  end
6148
7157
 
6149
7158
  def zoom_scale_normal? #:nodoc:
6150
- !!@zoom_scale_normal
7159
+ ptrue?(@zoom_scale_normal)
6151
7160
  end
6152
7161
 
6153
7162
  def page_view? #:nodoc: