write_xlsx 0.89.0 → 1.01.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 (340) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Changes +98 -0
  4. data/LICENSE.txt +1 -1
  5. data/README.md +2 -2
  6. data/examples/a_simple.rb +2 -7
  7. data/examples/add_vba_project.rb +1 -1
  8. data/examples/array_formula.rb +1 -1
  9. data/examples/chart_area.rb +5 -2
  10. data/examples/chart_bar.rb +5 -2
  11. data/examples/chart_clustered.rb +1 -1
  12. data/examples/chart_column.rb +5 -2
  13. data/examples/chart_combined.rb +1 -1
  14. data/examples/chart_data_table.rb +9 -3
  15. data/examples/chart_data_tools.rb +25 -7
  16. data/examples/chart_doughnut.rb +17 -5
  17. data/examples/chart_gauge.rb +73 -0
  18. data/examples/chart_line.rb +5 -2
  19. data/examples/chart_pareto.rb +1 -1
  20. data/examples/chart_pie.rb +9 -3
  21. data/examples/chart_radar.rb +13 -4
  22. data/examples/chart_scatter.rb +5 -2
  23. data/examples/chart_secondary_axis.rb +5 -2
  24. data/examples/chart_stock.rb +1 -1
  25. data/examples/chart_styles.rb +1 -1
  26. data/examples/colors.rb +1 -1
  27. data/examples/conditional_format.rb +73 -46
  28. data/examples/data_validate.rb +1 -1
  29. data/examples/date_time.rb +1 -1
  30. data/examples/demo.rb +5 -8
  31. data/examples/formats.rb +1 -1
  32. data/examples/headers.rb +1 -1
  33. data/examples/hide_row_col.rb +1 -1
  34. data/examples/hide_sheet.rb +1 -1
  35. data/examples/hyperlink1.rb +5 -12
  36. data/examples/indent.rb +1 -1
  37. data/examples/macros.rb +1 -1
  38. data/examples/merge1.rb +1 -1
  39. data/examples/merge2.rb +1 -1
  40. data/examples/merge3.rb +1 -1
  41. data/examples/merge4.rb +1 -1
  42. data/examples/merge5.rb +1 -1
  43. data/examples/merge6.rb +1 -1
  44. data/examples/outline.rb +1 -1
  45. data/examples/outline_collapsed.rb +1 -1
  46. data/examples/panes.rb +1 -1
  47. data/examples/properties.rb +1 -1
  48. data/examples/regions.rb +1 -1
  49. data/examples/rich_strings.rb +1 -1
  50. data/examples/right_to_left.rb +1 -1
  51. data/examples/shape1.rb +1 -1
  52. data/examples/shape2.rb +1 -1
  53. data/examples/shape3.rb +1 -1
  54. data/examples/shape4.rb +1 -1
  55. data/examples/shape5.rb +1 -1
  56. data/examples/shape6.rb +1 -1
  57. data/examples/shape7.rb +1 -1
  58. data/examples/shape8.rb +1 -1
  59. data/examples/shape_all.rb +1 -1
  60. data/examples/sparklines1.rb +1 -1
  61. data/examples/sparklines2.rb +1 -1
  62. data/examples/stats.rb +1 -1
  63. data/examples/stats_ext.rb +1 -1
  64. data/examples/stocks.rb +1 -1
  65. data/examples/tab_colors.rb +1 -1
  66. data/examples/tables.rb +1 -1
  67. data/lib/write_xlsx/chart.rb +124 -240
  68. data/lib/write_xlsx/chart/area.rb +1 -1
  69. data/lib/write_xlsx/chart/axis.rb +4 -4
  70. data/lib/write_xlsx/chart/bar.rb +1 -1
  71. data/lib/write_xlsx/chart/caption.rb +3 -1
  72. data/lib/write_xlsx/chart/column.rb +1 -1
  73. data/lib/write_xlsx/chart/doughnut.rb +1 -1
  74. data/lib/write_xlsx/chart/legend.rb +14 -0
  75. data/lib/write_xlsx/chart/line.rb +1 -1
  76. data/lib/write_xlsx/chart/pie.rb +32 -15
  77. data/lib/write_xlsx/chart/radar.rb +1 -1
  78. data/lib/write_xlsx/chart/scatter.rb +1 -1
  79. data/lib/write_xlsx/chart/series.rb +11 -7
  80. data/lib/write_xlsx/chart/stock.rb +1 -1
  81. data/lib/write_xlsx/chartsheet.rb +35 -7
  82. data/lib/write_xlsx/drawing.rb +28 -8
  83. data/lib/write_xlsx/format.rb +19 -15
  84. data/lib/write_xlsx/package/comments.rb +57 -54
  85. data/lib/write_xlsx/package/conditional_format.rb +360 -39
  86. data/lib/write_xlsx/package/content_types.rb +10 -0
  87. data/lib/write_xlsx/package/core.rb +8 -6
  88. data/lib/write_xlsx/package/custom.rb +125 -0
  89. data/lib/write_xlsx/package/packager.rb +26 -0
  90. data/lib/write_xlsx/package/styles.rb +53 -21
  91. data/lib/write_xlsx/package/table.rb +16 -4
  92. data/lib/write_xlsx/shape.rb +4 -3
  93. data/lib/write_xlsx/sheets.rb +11 -1
  94. data/lib/write_xlsx/sparkline.rb +1 -1
  95. data/lib/write_xlsx/utility.rb +305 -35
  96. data/lib/write_xlsx/version.rb +1 -1
  97. data/lib/write_xlsx/workbook.rb +132 -12
  98. data/lib/write_xlsx/worksheet.rb +397 -163
  99. data/lib/write_xlsx/worksheet/data_validation.rb +10 -14
  100. data/lib/write_xlsx/worksheet/hyperlink.rb +4 -13
  101. data/test/chart/test_write_legend_pos.rb +9 -1
  102. data/test/chartsheet/test_write_sheet_protection.rb +91 -0
  103. data/test/drawing/test_drawing_chart_01.rb +6 -2
  104. data/test/drawing/test_drawing_image_01.rb +12 -3
  105. data/test/drawing/test_drawing_shape_01.rb +8 -5
  106. data/test/drawing/test_drawing_shape_02.rb +12 -5
  107. data/test/drawing/test_drawing_shape_03.rb +8 -5
  108. data/test/drawing/test_drawing_shape_04.rb +8 -24
  109. data/test/drawing/test_drawing_shape_05.rb +8 -5
  110. data/test/drawing/test_drawing_shape_06.rb +11 -6
  111. data/test/drawing/test_drawing_shape_07.rb +11 -6
  112. data/test/drawing/test_write_a_graphic_frame_locks.rb +1 -1
  113. data/test/drawing/test_write_c_chart.rb +1 -1
  114. data/test/drawing/test_write_c_nv_graphic_frame_pr.rb +1 -1
  115. data/test/drawing/test_write_c_nv_pr.rb +1 -1
  116. data/test/drawing/test_write_col.rb +1 -1
  117. data/test/drawing/test_write_col_off.rb +1 -1
  118. data/test/drawing/test_write_ext.rb +1 -1
  119. data/test/drawing/test_write_pos.rb +1 -1
  120. data/test/drawing/test_write_row.rb +1 -1
  121. data/test/drawing/test_write_row_off.rb +1 -1
  122. data/test/drawing/test_write_xfrm_extension.rb +1 -1
  123. data/test/drawing/test_write_xfrm_offset.rb +1 -1
  124. data/test/helper.rb +6 -1
  125. data/test/package/comments/test_comments_01.rb +54 -0
  126. data/test/package/comments/test_comments_02.rb +54 -0
  127. data/test/perl_output/chart_gauge.xlsx +0 -0
  128. data/test/perl_output/formats.xlsx +0 -0
  129. data/test/regression/_test_hyperlink31.rb +26 -0
  130. data/test/regression/images/happy.jpg +0 -0
  131. data/test/regression/images/zero_dpi.jpg +0 -0
  132. data/test/regression/test_array_formula03.rb +36 -0
  133. data/test/regression/test_autofilter08.rb +110 -0
  134. data/test/regression/test_autofilter09.rb +110 -0
  135. data/test/regression/test_autofilter10.rb +110 -0
  136. data/test/regression/test_chart_axis26.rb +10 -8
  137. data/test/regression/test_chart_axis27.rb +1 -1
  138. data/test/regression/test_chart_axis28.rb +1 -1
  139. data/test/regression/test_chart_axis29.rb +1 -1
  140. data/test/regression/test_chart_axis33.rb +1 -1
  141. data/test/regression/test_chart_axis42.rb +44 -0
  142. data/test/regression/test_chart_axis43.rb +44 -0
  143. data/test/regression/test_chart_axis44.rb +54 -0
  144. data/test/regression/test_chart_axis45.rb +54 -0
  145. data/test/regression/test_chart_axis46.rb +54 -0
  146. data/test/regression/test_chart_bar08.rb +3 -0
  147. data/test/regression/test_chart_bar11.rb +3 -0
  148. data/test/regression/test_chart_bar14.rb +3 -0
  149. data/test/regression/test_chart_chartarea05.rb +16 -17
  150. data/test/regression/test_chart_chartarea06.rb +49 -0
  151. data/test/regression/test_chart_combined10.rb +43 -0
  152. data/test/regression/test_chart_combined11.rb +63 -0
  153. data/test/regression/test_chart_data_labels25.rb +61 -0
  154. data/test/regression/test_chart_doughnut07.rb +37 -0
  155. data/test/regression/test_chart_font09.rb +1 -1
  156. data/test/regression/test_chart_format26.rb +48 -0
  157. data/test/regression/test_chart_format27.rb +58 -0
  158. data/test/regression/test_chart_format28.rb +52 -0
  159. data/test/regression/test_chart_format29.rb +59 -0
  160. data/test/regression/test_chart_format30.rb +53 -0
  161. data/test/regression/test_chart_format31.rb +60 -0
  162. data/test/regression/test_chart_legend03.rb +41 -0
  163. data/test/regression/test_chart_legend04.rb +41 -0
  164. data/test/regression/test_chart_legend05.rb +41 -0
  165. data/test/regression/test_chart_legend06.rb +41 -0
  166. data/test/regression/test_chart_legend07.rb +38 -0
  167. data/test/regression/test_chart_size03.rb +4 -1
  168. data/test/regression/test_chart_table03.rb +56 -0
  169. data/test/regression/test_comment13.rb +36 -0
  170. data/test/regression/test_comment14.rb +29 -0
  171. data/test/regression/test_cond_format14.rb +42 -0
  172. data/test/regression/test_cond_format15.rb +53 -0
  173. data/test/regression/test_cond_format16.rb +53 -0
  174. data/test/regression/test_cond_format17.rb +37 -0
  175. data/test/regression/test_cond_format18.rb +136 -0
  176. data/test/regression/test_cond_format19.rb +64 -0
  177. data/test/regression/test_cond_format20.rb +43 -0
  178. data/test/regression/test_date_1904_01.rb +1 -1
  179. data/test/regression/test_escapes04.rb +3 -0
  180. data/test/regression/test_escapes05.rb +3 -0
  181. data/test/regression/test_escapes07.rb +3 -0
  182. data/test/regression/test_escapes08.rb +3 -0
  183. data/test/regression/test_format15.rb +26 -0
  184. data/test/regression/test_hyperlink01.rb +3 -0
  185. data/test/regression/test_hyperlink02.rb +3 -0
  186. data/test/regression/test_hyperlink03.rb +4 -0
  187. data/test/regression/test_hyperlink04.rb +3 -0
  188. data/test/regression/test_hyperlink05.rb +3 -0
  189. data/test/regression/test_hyperlink06.rb +3 -0
  190. data/test/regression/test_hyperlink07.rb +3 -0
  191. data/test/regression/test_hyperlink08.rb +3 -0
  192. data/test/regression/test_hyperlink09.rb +3 -0
  193. data/test/regression/test_hyperlink10.rb +3 -0
  194. data/test/regression/test_hyperlink11.rb +3 -0
  195. data/test/regression/test_hyperlink12.rb +3 -0
  196. data/test/regression/test_hyperlink13.rb +3 -0
  197. data/test/regression/test_hyperlink14.rb +3 -0
  198. data/test/regression/test_hyperlink15.rb +3 -0
  199. data/test/regression/test_hyperlink16.rb +3 -0
  200. data/test/regression/test_hyperlink17.rb +3 -0
  201. data/test/regression/test_hyperlink18.rb +3 -0
  202. data/test/regression/test_hyperlink20.rb +3 -0
  203. data/test/regression/test_hyperlink21.rb +3 -0
  204. data/test/regression/test_hyperlink22.rb +3 -0
  205. data/test/regression/test_hyperlink23.rb +3 -0
  206. data/test/regression/test_hyperlink24.rb +3 -0
  207. data/test/regression/test_hyperlink25.rb +3 -0
  208. data/test/regression/test_hyperlink26.rb +3 -0
  209. data/test/regression/test_hyperlink27.rb +27 -0
  210. data/test/regression/test_hyperlink28.rb +50 -0
  211. data/test/regression/test_hyperlink29.rb +27 -0
  212. data/test/regression/test_hyperlink30.rb +36 -0
  213. data/test/regression/test_image08.rb +5 -4
  214. data/test/regression/test_image15.rb +4 -2
  215. data/test/regression/test_image28.rb +1 -1
  216. data/test/regression/test_image35.rb +26 -0
  217. data/test/regression/test_image36.rb +26 -0
  218. data/test/regression/test_image44.rb +28 -0
  219. data/test/regression/test_image45.rb +28 -0
  220. data/test/regression/test_image46.rb +29 -0
  221. data/test/regression/test_image47.rb +28 -0
  222. data/test/regression/test_object_position01.rb +26 -0
  223. data/test/regression/test_object_position02.rb +26 -0
  224. data/test/regression/test_object_position03.rb +26 -0
  225. data/test/regression/test_object_position04.rb +44 -0
  226. data/test/regression/test_object_position06.rb +28 -0
  227. data/test/regression/test_object_position07.rb +28 -0
  228. data/test/regression/test_object_position08.rb +47 -0
  229. data/test/regression/test_object_position09.rb +50 -0
  230. data/test/regression/test_object_position10.rb +28 -0
  231. data/test/regression/test_properties01.rb +1 -4
  232. data/test/regression/test_properties02.rb +1 -4
  233. data/test/regression/test_properties03.rb +26 -0
  234. data/test/regression/test_properties04.rb +61 -0
  235. data/test/regression/test_properties05.rb +30 -0
  236. data/test/regression/test_shape_connect01.rb +4 -2
  237. data/test/regression/test_table03.rb +3 -0
  238. data/test/regression/test_table04.rb +3 -0
  239. data/test/regression/test_table05.rb +3 -0
  240. data/test/regression/test_table06.rb +3 -0
  241. data/test/regression/test_table20.rb +34 -0
  242. data/test/regression/test_table21.rb +36 -0
  243. data/test/regression/test_table22.rb +32 -0
  244. data/test/regression/test_table23.rb +56 -0
  245. data/test/regression/test_utf8_11.rb +23 -0
  246. data/test/regression/xlsx_files/array_formula03.xlsx +0 -0
  247. data/test/regression/xlsx_files/autofilter08.xlsx +0 -0
  248. data/test/regression/xlsx_files/autofilter09.xlsx +0 -0
  249. data/test/regression/xlsx_files/autofilter10.xlsx +0 -0
  250. data/test/regression/xlsx_files/chart_axis26.xlsx +0 -0
  251. data/test/regression/xlsx_files/chart_axis27.xlsx +0 -0
  252. data/test/regression/xlsx_files/chart_axis28.xlsx +0 -0
  253. data/test/regression/xlsx_files/chart_axis29.xlsx +0 -0
  254. data/test/regression/xlsx_files/chart_axis33.xlsx +0 -0
  255. data/test/regression/xlsx_files/chart_axis42.xlsx +0 -0
  256. data/test/regression/xlsx_files/chart_axis43.xlsx +0 -0
  257. data/test/regression/xlsx_files/chart_axis44.xlsx +0 -0
  258. data/test/regression/xlsx_files/chart_axis45.xlsx +0 -0
  259. data/test/regression/xlsx_files/chart_axis46.xlsx +0 -0
  260. data/test/regression/xlsx_files/chart_chartarea05.xlsx +0 -0
  261. data/test/regression/xlsx_files/chart_chartarea06.xlsx +0 -0
  262. data/test/regression/xlsx_files/chart_combined10.xlsx +0 -0
  263. data/test/regression/xlsx_files/chart_combined11.xlsx +0 -0
  264. data/test/regression/xlsx_files/chart_data_labels25.xlsx +0 -0
  265. data/test/regression/xlsx_files/chart_doughnut07.xlsx +0 -0
  266. data/test/regression/xlsx_files/chart_font09.xlsx +0 -0
  267. data/test/regression/xlsx_files/chart_format26.xlsx +0 -0
  268. data/test/regression/xlsx_files/chart_format27.xlsx +0 -0
  269. data/test/regression/xlsx_files/chart_format28.xlsx +0 -0
  270. data/test/regression/xlsx_files/chart_format29.xlsx +0 -0
  271. data/test/regression/xlsx_files/chart_format30.xlsx +0 -0
  272. data/test/regression/xlsx_files/chart_format31.xlsx +0 -0
  273. data/test/regression/xlsx_files/chart_legend03.xlsx +0 -0
  274. data/test/regression/xlsx_files/chart_legend04.xlsx +0 -0
  275. data/test/regression/xlsx_files/chart_legend05.xlsx +0 -0
  276. data/test/regression/xlsx_files/chart_legend06.xlsx +0 -0
  277. data/test/regression/xlsx_files/chart_legend07.xlsx +0 -0
  278. data/test/regression/xlsx_files/chart_table03.xlsx +0 -0
  279. data/test/regression/xlsx_files/comment13.xlsx +0 -0
  280. data/test/regression/xlsx_files/comment14.xlsx +0 -0
  281. data/test/regression/xlsx_files/cond_format14.xlsx +0 -0
  282. data/test/regression/xlsx_files/cond_format15.xlsx +0 -0
  283. data/test/regression/xlsx_files/cond_format16.xlsx +0 -0
  284. data/test/regression/xlsx_files/cond_format17.xlsx +0 -0
  285. data/test/regression/xlsx_files/cond_format18.xlsx +0 -0
  286. data/test/regression/xlsx_files/cond_format19.xlsx +0 -0
  287. data/test/regression/xlsx_files/cond_format20.xlsx +0 -0
  288. data/test/regression/xlsx_files/date_1904_01.xlsx +0 -0
  289. data/test/regression/xlsx_files/format15.xlsx +0 -0
  290. data/test/regression/xlsx_files/hyperlink27.xlsx +0 -0
  291. data/test/regression/xlsx_files/hyperlink28.xlsx +0 -0
  292. data/test/regression/xlsx_files/hyperlink29.xlsx +0 -0
  293. data/test/regression/xlsx_files/hyperlink30.xlsx +0 -0
  294. data/test/regression/xlsx_files/hyperlink31.xlsx +0 -0
  295. data/test/regression/xlsx_files/image35.xlsx +0 -0
  296. data/test/regression/xlsx_files/image36.xlsx +0 -0
  297. data/test/regression/xlsx_files/image44.xlsx +0 -0
  298. data/test/regression/xlsx_files/image45.xlsx +0 -0
  299. data/test/regression/xlsx_files/image46.xlsx +0 -0
  300. data/test/regression/xlsx_files/image47.xlsx +0 -0
  301. data/test/regression/xlsx_files/object_position01.xlsx +0 -0
  302. data/test/regression/xlsx_files/object_position02.xlsx +0 -0
  303. data/test/regression/xlsx_files/object_position03.xlsx +0 -0
  304. data/test/regression/xlsx_files/object_position04.xlsx +0 -0
  305. data/test/regression/xlsx_files/object_position06.xlsx +0 -0
  306. data/test/regression/xlsx_files/object_position07.xlsx +0 -0
  307. data/test/regression/xlsx_files/object_position08.xlsx +0 -0
  308. data/test/regression/xlsx_files/object_position09.xlsx +0 -0
  309. data/test/regression/xlsx_files/object_position10.xlsx +0 -0
  310. data/test/regression/xlsx_files/properties03.xlsx +0 -0
  311. data/test/regression/xlsx_files/properties04.xlsx +0 -0
  312. data/test/regression/xlsx_files/properties05.xlsx +0 -0
  313. data/test/regression/xlsx_files/table21.xlsx +0 -0
  314. data/test/regression/xlsx_files/table22.xlsx +0 -0
  315. data/test/regression/xlsx_files/table23.xlsx +0 -0
  316. data/test/regression/xlsx_files/utf8_11.xlsx +0 -0
  317. data/test/test_example_match.rb +836 -771
  318. data/test/workbook/test_check_sheetname.rb +61 -0
  319. data/test/workbook/test_worksheet_by_name.rb +35 -0
  320. data/test/workbook/test_write_workbook_view.rb +117 -0
  321. data/test/worksheet/test_cond_format_22.rb +266 -0
  322. data/test/worksheet/test_cond_format_23.rb +242 -0
  323. data/test/worksheet/test_cond_format_24.rb +303 -0
  324. data/test/worksheet/test_data_bar_01.rb +53 -0
  325. data/test/worksheet/test_data_bar_02.rb +79 -0
  326. data/test/worksheet/test_data_bar_03.rb +147 -0
  327. data/test/worksheet/test_data_bar_04.rb +145 -0
  328. data/test/worksheet/test_data_bar_05.rb +147 -0
  329. data/test/worksheet/test_data_bar_06.rb +145 -0
  330. data/test/worksheet/test_data_bar_07.rb +146 -0
  331. data/test/worksheet/test_data_bar_08.rb +54 -0
  332. data/test/worksheet/test_data_bar_09.rb +80 -0
  333. data/test/worksheet/test_data_bar_10.rb +165 -0
  334. data/test/worksheet/test_data_bar_11.rb +167 -0
  335. data/test/worksheet/test_data_bar_12.rb +104 -0
  336. data/test/worksheet/test_write_data_validation_02.rb +44 -0
  337. data/test/worksheet/test_write_hyperlink.rb +0 -7
  338. data/test/worksheet/test_write_sheet_view.rb +19 -1
  339. metadata +308 -5
  340. data/test/package/comments/test_write_text_t.rb +0 -44
@@ -230,10 +230,20 @@ def check_valid_sheetname(name)
230
230
  raise 'Invalid character []:*?/\\ in worksheet name: ' + name
231
231
  end
232
232
 
233
+ # Check that sheetname doesn't start or end with an apostrophe.
234
+ if name =~ /^'/ || name =~ /'$/
235
+ raise "Worksheet name #{name} cannot start or end with an "
236
+ end
237
+
238
+ # Check that sheetname isn't a reserved word.
239
+ if name =~ /history/i
240
+ raise "Worksheet name cannot be Excel reserved word 'History'"
241
+ end
242
+
233
243
  # Check that the worksheet name doesn't already exist since this is a fatal
234
244
  # error in Excel 97. The check must also exclude case insensitive matches.
235
245
  unless is_sheetname_uniq?(name)
236
- raise "Worksheet name '#{name}', with case ignored, is already used."
246
+ raise "apostropheWorksheet name '#{name}', with case ignored, is already used."
237
247
  end
238
248
  end
239
249
 
@@ -9,7 +9,7 @@ module Writexlsx
9
9
  # Used in conjunction with WriteXLSX.
10
10
  #
11
11
  # Copyright 2000-2012, John McNamara, jmcnamara@cpan.org
12
- # Converted to ruby by Hideo NAKAMURA, cxn03651@msj.biglobe.ne.jp
12
+ # Converted to ruby by Hideo NAKAMURA, nakamura.hideo@gmail.com
13
13
  #
14
14
  class Sparkline
15
15
  include Writexlsx::Utility
@@ -119,7 +119,7 @@ def check_dimensions(row, col)
119
119
  # nil if the date is invalid.
120
120
  #
121
121
  def convert_date_time(date_time_string) #:nodoc:
122
- date_time = date_time_string.sub(/^\s+/, '').sub(/\s+$/, '').sub(/Z$/, '')
122
+ date_time = date_time_string.to_s.sub(/^\s+/, '').sub(/\s+$/, '').sub(/Z$/, '')
123
123
 
124
124
  # Check for invalid date char.
125
125
  return nil if date_time =~ /[^0-9T:\-\.Z]/
@@ -320,7 +320,7 @@ def check_parameter(params, valid_keys, method)
320
320
  invalids = params.keys - valid_keys
321
321
  unless invalids.empty?
322
322
  raise WriteXLSXOptionParameterError,
323
- "Unknown parameter '#{invalids.join(', ')}' in #{method}."
323
+ "Unknown parameter '#{invalids.join(', ')}' in #{method}."
324
324
  end
325
325
  true
326
326
  end
@@ -361,6 +361,64 @@ def float_to_str(float)
361
361
  end
362
362
  end
363
363
 
364
+ #
365
+ # Convert user defined legend properties to the structure required internally.
366
+ #
367
+ def legend_properties(params)
368
+ legend = Writexlsx::Chart::Legend.new
369
+
370
+ legend.position = params[:position] || 'right'
371
+ legend.delete_series = params[:delete_series]
372
+ legend.font = convert_font_args(params[:font])
373
+
374
+ # Set the legend layout.
375
+ legend.layout = layout_properties(params[:layout])
376
+
377
+ # Turn off the legend.
378
+ if params[:none]
379
+ legend.position = 'none'
380
+ end
381
+
382
+ # Set the line properties for the legend.
383
+ line = line_properties(params[:line])
384
+
385
+ # Allow 'border' as a synonym for 'line'.
386
+ if params[:border]
387
+ line = line_properties(params[:border])
388
+ end
389
+
390
+ # Set the fill properties for the legend.
391
+ fill = fill_properties(params[:fill])
392
+
393
+ # Set the pattern properties for the legend.
394
+ pattern = pattern_properties(params[:pattern])
395
+
396
+ # Set the gradient fill properties for the legend.
397
+ gradient = gradient_properties(params[:gradient])
398
+
399
+ # Pattern fill overrides solid fill.
400
+ if pattern
401
+ fill = nil
402
+ end
403
+
404
+ # Gradient fill overrides solid and pattern fills.
405
+ if gradient
406
+ pattern = nil
407
+ fill = nil
408
+ end
409
+
410
+ # Set the legend layout.
411
+ layout = layout_properties(params[:layout])
412
+
413
+ legend.line = line
414
+ legend.fill = fill
415
+ legend.pattern = pattern
416
+ legend.gradient = gradient
417
+ legend.layout = layout
418
+
419
+ @legend = legend
420
+ end
421
+
364
422
  #
365
423
  # Convert user defined layout properties to the format required internally.
366
424
  #
@@ -372,7 +430,7 @@ def layout_properties(args, is_text = false)
372
430
  # Check for valid properties.
373
431
  args.keys.each do |key|
374
432
  unless properties.include?(key.to_sym)
375
- raise "Property '#{key}' not allowed in layout options\n"
433
+ raise "Property '#{key}' not allowed in layout options\n"
376
434
  end
377
435
  end
378
436
 
@@ -405,9 +463,9 @@ def pixels_to_points(vertices)
405
463
 
406
464
  def v_shape_attributes_base(id, z_index)
407
465
  [
408
- ['id', "_x0000_s#{id}"],
409
- ['type', type],
410
- ['style', (v_shape_style_base(z_index, vertices) + style_addition).join]
466
+ ['id', "_x0000_s#{id}"],
467
+ ['type', type],
468
+ ['style', (v_shape_style_base(z_index, vertices) + style_addition).join]
411
469
  ]
412
470
  end
413
471
 
@@ -425,17 +483,17 @@ def v_shape_style_base(z_index, vertices)
425
483
 
426
484
  def shape_style_base(left_str, top_str, width_str, height_str, z_index_str)
427
485
  [
428
- 'position:absolute;',
429
- 'margin-left:',
430
- left_str, 'pt;',
431
- 'margin-top:',
432
- top_str, 'pt;',
433
- 'width:',
434
- width_str, 'pt;',
435
- 'height:',
436
- height_str, 'pt;',
437
- 'z-index:',
438
- z_index_str, ';'
486
+ 'position:absolute;',
487
+ 'margin-left:',
488
+ left_str, 'pt;',
489
+ 'margin-top:',
490
+ top_str, 'pt;',
491
+ 'width:',
492
+ width_str, 'pt;',
493
+ 'height:',
494
+ height_str, 'pt;',
495
+ 'z-index:',
496
+ z_index_str, ';'
439
497
  ]
440
498
  end
441
499
 
@@ -500,10 +558,10 @@ def write_font(font)
500
558
  color = '#000000'
501
559
 
502
560
  attributes = [
503
- ['face', face],
504
- ['size', size],
505
- ['color', color]
506
- ]
561
+ ['face', face],
562
+ ['size', size],
563
+ ['color', color]
564
+ ]
507
565
  @writer.data_element('font', caption, attributes)
508
566
  end
509
567
 
@@ -612,7 +670,7 @@ def pattern_properties(args) # :nodoc:
612
670
  'small_check' => 'smCheck',
613
671
  'large_check' => 'lgCheck',
614
672
  'outlined_diamond' => 'openDmnd',
615
- 'solid_diamond' => 'solidDmnd'
673
+ 'solid_diamond' => 'solidDmnd'
616
674
  }
617
675
 
618
676
  # Check for valid types.
@@ -627,7 +685,7 @@ def pattern_properties(args) # :nodoc:
627
685
 
628
686
  pattern
629
687
  end
630
-
688
+
631
689
  def line_fill_properties(params)
632
690
  return { :_defined => 0 } unless params
633
691
  ret = params.dup
@@ -690,22 +748,234 @@ def process_workbook_options(*params)
690
748
  [options.dup, default_format_properties.dup]
691
749
  end
692
750
  end
751
+
752
+ #
753
+ # Convert user defined font values into private hash values.
754
+ #
755
+ def convert_font_args(params)
756
+ return unless params
757
+ font = params_to_font(params)
758
+
759
+ # Convert font size units.
760
+ font[:_size] *= 100 if font[:_size] && font[:_size] != 0
761
+
762
+ # Convert rotation into 60,000ths of a degree.
763
+ if ptrue?(font[:_rotation])
764
+ font[:_rotation] = 60_000 * font[:_rotation].to_i
765
+ end
766
+
767
+ font
768
+ end
769
+
770
+ def params_to_font(params)
771
+ {
772
+ :_name => params[:name],
773
+ :_color => params[:color],
774
+ :_size => params[:size],
775
+ :_bold => params[:bold],
776
+ :_italic => params[:italic],
777
+ :_underline => params[:underline],
778
+ :_pitch_family => params[:pitch_family],
779
+ :_charset => params[:charset],
780
+ :_baseline => params[:baseline] || 0,
781
+ :_rotation => params[:rotation]
782
+ }
783
+ end
784
+
785
+ #
786
+ # Write the <c:txPr> element.
787
+ #
788
+ def write_tx_pr(is_y_axis, font) # :nodoc:
789
+ rotation = nil
790
+ if font && font[:_rotation]
791
+ rotation = font[:_rotation]
792
+ end
793
+ @writer.tag_elements('c:txPr') do
794
+ # Write the a:bodyPr element.
795
+ write_a_body_pr(rotation, is_y_axis)
796
+ # Write the a:lstStyle element.
797
+ write_a_lst_style
798
+ # Write the a:p element.
799
+ write_a_p_formula(font)
800
+ end
801
+ end
802
+
803
+ #
804
+ # Write the <a:bodyPr> element.
805
+ #
806
+ def write_a_body_pr(rot, is_y_axis = nil) # :nodoc:
807
+ rot = -5400000 if !rot && ptrue?(is_y_axis)
808
+ attributes = []
809
+ if rot
810
+ if rot == 16_200_000
811
+ # 270 deg/stacked angle.
812
+ attributes << ['rot', 0]
813
+ attributes << ['vert', 'wordArtVert']
814
+ elsif rot == 16_260_000
815
+ # 271 deg/stacked angle.
816
+ attributes << ['rot', 0]
817
+ attributes << ['vert', 'eaVert']
818
+ else
819
+ attributes << ['rot', rot]
820
+ attributes << ['vert', 'horz']
821
+ end
822
+ end
823
+
824
+ @writer.empty_tag('a:bodyPr', attributes)
825
+ end
826
+
827
+ #
828
+ # Write the <a:lstStyle> element.
829
+ #
830
+ def write_a_lst_style # :nodoc:
831
+ @writer.empty_tag('a:lstStyle')
832
+ end
833
+
834
+ #
835
+ # Write the <a:p> element for formula titles.
836
+ #
837
+ def write_a_p_formula(font = nil) # :nodoc:
838
+ @writer.tag_elements('a:p') do
839
+ # Write the a:pPr element.
840
+ write_a_p_pr_formula(font)
841
+ # Write the a:endParaRPr element.
842
+ write_a_end_para_rpr
843
+ end
844
+ end
845
+
846
+ #
847
+ # Write the <a:pPr> element for formula titles.
848
+ #
849
+ def write_a_p_pr_formula(font) # :nodoc:
850
+ @writer.tag_elements('a:pPr') { write_a_def_rpr(font) }
851
+ end
852
+
853
+ #
854
+ # Write the <a:defRPr> element.
855
+ #
856
+ def write_a_def_rpr(font = nil) # :nodoc:
857
+ write_def_rpr_r_pr_common(
858
+ font,
859
+ get_font_style_attributes(font),
860
+ 'a:defRPr'
861
+ )
862
+ end
863
+
864
+ def write_def_rpr_r_pr_common(font, style_attributes, tag) # :nodoc:
865
+ latin_attributes = get_font_latin_attributes(font)
866
+ has_color = ptrue?(font) && ptrue?(font[:_color])
867
+
868
+ if !latin_attributes.empty? || has_color
869
+ @writer.tag_elements(tag, style_attributes) do
870
+ if has_color
871
+ write_a_solid_fill(:color => font[:_color])
872
+ end
873
+ if !latin_attributes.empty?
874
+ write_a_latin(latin_attributes)
875
+ end
876
+ end
877
+ else
878
+ @writer.empty_tag(tag, style_attributes)
879
+ end
880
+ end
881
+
882
+ #
883
+ # Get the font latin attributes from a font hash.
884
+ #
885
+ def get_font_latin_attributes(font)
886
+ return [] unless font
887
+
888
+ attributes = []
889
+ attributes << ['typeface', font[:_name]] if ptrue?(font[:_name])
890
+ attributes << ['pitchFamily', font[:_pitch_family]] if font[:_pitch_family]
891
+ attributes << ['charset', font[:_charset]] if font[:_charset]
892
+
893
+ attributes
894
+ end
895
+
896
+ #
897
+ # Write the <a:solidFill> element.
898
+ #
899
+ def write_a_solid_fill(fill) # :nodoc:
900
+ @writer.tag_elements('a:solidFill') do
901
+ if fill[:color]
902
+ # Write the a:srgbClr element.
903
+ write_a_srgb_clr(color(fill[:color]), fill[:transparency])
904
+ end
905
+ end
906
+ end
907
+
908
+ #
909
+ # Write the <a:srgbClr> element.
910
+ #
911
+ def write_a_srgb_clr(color, transparency = nil) # :nodoc:
912
+ tag = 'a:srgbClr'
913
+ attributes = [ ['val', color] ]
914
+
915
+ if ptrue?(transparency)
916
+ @writer.tag_elements(tag, attributes) do
917
+ write_a_alpha(transparency)
918
+ end
919
+ else
920
+ @writer.empty_tag(tag, attributes)
921
+ end
922
+ end
923
+
924
+ #
925
+ # Convert the user specified colour index or string to a rgb colour.
926
+ #
927
+ def color(color_code) # :nodoc:
928
+ if color_code and color_code =~ /^#[0-9a-fA-F]{6}$/
929
+ # Convert a HTML style #RRGGBB color.
930
+ color_code.sub(/^#/, '').upcase
931
+ else
932
+ index = Format.color(color_code)
933
+ raise "Unknown color '#{color_code}' used in chart formatting." unless index
934
+ palette_color(index)
935
+ end
936
+ end
937
+
938
+ #
939
+ # Get the font style attributes from a font hash.
940
+ #
941
+ def get_font_style_attributes(font)
942
+ return [] unless font
943
+
944
+ attributes = []
945
+ attributes << ['sz', font[:_size]] if ptrue?(font[:_size])
946
+ attributes << ['b', font[:_bold]] if font[:_bold]
947
+ attributes << ['i', font[:_italic]] if font[:_italic]
948
+ attributes << ['u', 'sng'] if font[:_underline]
949
+
950
+ # Turn off baseline when testing fonts that don't have it.
951
+ if font[:_baseline] != -1
952
+ attributes << ['baseline', font[:_baseline]]
953
+ end
954
+ attributes
955
+ end
956
+
957
+ #
958
+ # Write the <a:endParaRPr> element.
959
+ #
960
+ def write_a_end_para_rpr # :nodoc:
961
+ @writer.empty_tag('a:endParaRPr', [ ['lang', 'en-US'] ])
962
+ end
693
963
  end
694
964
 
695
965
  module WriteDPtPoint
696
- #
697
- # Write an individual <c:dPt> element. Override the parent method to add
698
- # markers.
699
- #
700
- def write_d_pt_point(index, point)
701
- @writer.tag_elements('c:dPt') do
702
- # Write the c:idx element.
703
- write_idx(index)
704
- @writer.tag_elements('c:marker') do
705
- # Write the c:spPr element.
706
- write_sp_pr(point)
707
- end
966
+ #
967
+ # Write an individual <c:dPt> element. Override the parent method to add
968
+ # markers.
969
+ #
970
+ def write_d_pt_point(index, point)
971
+ @writer.tag_elements('c:dPt') do
972
+ # Write the c:idx element.
973
+ write_idx(index)
974
+ @writer.tag_elements('c:marker') do
975
+ # Write the c:spPr element.
976
+ write_sp_pr(point)
708
977
  end
709
978
  end
979
+ end
710
980
  end
711
981
  end
@@ -1 +1 @@
1
- WriteXLSX_VERSION = "0.89.0"
1
+ WriteXLSX_VERSION = "1.01.0"
@@ -44,11 +44,14 @@ class Workbook
44
44
  attr_reader :worksheets, :charts, :drawings # :nodoc:
45
45
  attr_reader :named_ranges # :nodoc:
46
46
  attr_reader :doc_properties # :nodoc:
47
+ attr_reader :custom_properties # :nodoc:
47
48
  attr_reader :image_types, :images # :nodoc:
48
49
  attr_reader :shared_strings # :nodoc:
49
50
  attr_reader :vba_project # :nodoc:
50
51
  attr_reader :excel2003_style # :nodoc:
51
52
  attr_reader :strings_to_urls # :nodoc:
53
+ attr_reader :default_url_format # :nodoc:
54
+
52
55
  #
53
56
  # A new Excel workbook is created using the +new+ constructor
54
57
  # which accepts either a filename or an IO object as a parameter.
@@ -114,13 +117,13 @@ def initialize(file, *option_params)
114
117
  @named_ranges = []
115
118
  @custom_colors = []
116
119
  @doc_properties = {}
117
- @local_time = Time.now
120
+ @custom_properties = []
118
121
  @optimization = options[:optimization] || 0
119
122
  @x_window = 240
120
123
  @y_window = 15
121
124
  @window_width = 16095
122
125
  @window_height = 9660
123
- @tab_ratio = 500
126
+ @tab_ratio = 600
124
127
  @excel2003_style = options[:excel2003_style] || false
125
128
  @table_count = 0
126
129
  @image_types = {}
@@ -141,6 +144,10 @@ def initialize(file, *option_params)
141
144
  else
142
145
  add_format(default_formats.merge(:xf_index => 0))
143
146
  end
147
+
148
+ # Add a default URL format.
149
+ @default_url_format = add_format(:hyperlink => 1)
150
+
144
151
  set_color_palette
145
152
  end
146
153
 
@@ -203,6 +210,14 @@ def sheets(*args)
203
210
  end
204
211
  end
205
212
 
213
+ #
214
+ # Return a worksheet object in the workbook using the sheetname.
215
+ #
216
+ def worksheet_by_name(sheetname = nil)
217
+ sheets.select { |s| s.name == sheetname }.first
218
+ end
219
+ alias get_worksheet_by_name worksheet_by_name
220
+
206
221
  #
207
222
  # Set the date system: false = 1900 (the default), true = 1904
208
223
  #
@@ -750,6 +765,38 @@ def define_name(name, formula)
750
765
  @defined_names.push([ name, sheet_index, formula.sub(/^=/, '') ])
751
766
  end
752
767
 
768
+ #
769
+ # Set the workbook size.
770
+ #
771
+ def set_size(width = nil, height = nil)
772
+ if ptrue?(width)
773
+ # Convert to twips at 96 dpi.
774
+ @window_width = width.to_i * 1440 / 96
775
+ else
776
+ @window_width = 16095
777
+ end
778
+
779
+ if ptrue?(height)
780
+ # Convert to twips at 96 dpi.
781
+ @window_height = height.to_i * 1440 / 96
782
+ else
783
+ @window_height = 9660
784
+ end
785
+ end
786
+
787
+ #
788
+ # Set the ratio of space for worksheet tabs.
789
+ #
790
+ def set_tab_ratio(tab_ratio = nil)
791
+ return if !tab_ratio
792
+
793
+ if tab_ratio < 0 || tab_ratio > 100
794
+ raise "Tab ratio outside range: 0 <= zoom <= 100"
795
+ else
796
+ @tab_ratio = (tab_ratio * 10).to_i
797
+ end
798
+ end
799
+
753
800
  #
754
801
  # The set_properties method can be used to set the document properties
755
802
  # of the Excel file created by WriteXLSX. These properties are visible
@@ -806,11 +853,58 @@ def set_properties(params)
806
853
  end
807
854
 
808
855
  # Set the creation time unless specified by the user.
809
- params[:created] = @local_time unless params.has_key?(:created)
856
+ params[:created] = @createtime unless params.has_key?(:created)
810
857
 
811
858
  @doc_properties = params.dup
812
859
  end
813
860
 
861
+ #
862
+ # Set a user defined custom document property.
863
+ #
864
+ def set_custom_property(name, value, type = nil)
865
+ # Valid types.
866
+ valid_type = {
867
+ 'text' => 1,
868
+ 'date' => 1,
869
+ 'number' => 1,
870
+ 'number_int' => 1,
871
+ 'bool' => 1,
872
+ }
873
+
874
+ if !name || (type != 'bool' && !value)
875
+ raise "The name and value parameters must be defined in set_custom_property()"
876
+ end
877
+
878
+ # Determine the type for strings and numbers if it hasn't been specified.
879
+ if !ptrue?(type)
880
+ if value =~ /^\d+$/
881
+ type = 'number_int'
882
+ elsif value =~
883
+ /^([+-]?)(?=[0-9]|\.[0-9])[0-9]*(\.[0-9]*)?([Ee]([+-]?[0-9]+))?$/
884
+ type = 'number'
885
+ else
886
+ type = 'text'
887
+ end
888
+ end
889
+
890
+ # Check for valid validation types.
891
+ if !valid_type[type]
892
+ raise "Unknown custom type '$type' in set_custom_property()"
893
+ end
894
+
895
+ # Check for strings longer than Excel's limit of 255 chars.
896
+ if type == 'text' && value.length > 255
897
+ raise "Length of text custom value '$value' exceeds Excel's limit of 255 in set_custom_property()"
898
+ end
899
+
900
+ if type == 'bool'
901
+ value = value ? 1 : 0
902
+ end
903
+
904
+ @custom_properties << [name, value, type]
905
+ end
906
+
907
+
814
908
  #
815
909
  # The add_vba_project method can be used to add macros or functions to an
816
910
  # WriteXLSX file using a binary VBA project file that has been extracted
@@ -869,6 +963,16 @@ def set_calc_mode(mode, calc_id = nil)
869
963
  @calc_id = calc_id if calc_id
870
964
  end
871
965
 
966
+ #
967
+ # Get the default url format used when a user defined format isn't specified
968
+ # with write_url(). The format is the hyperlink style defined by Excel for the
969
+ # default theme.
970
+ #
971
+ def default_url_format
972
+ @default_url_format
973
+ end
974
+ alias get_default_url_format default_url_format
975
+
872
976
  #
873
977
  # Change the RGB components of the elements in the colour palette.
874
978
  #
@@ -1217,7 +1321,7 @@ def write_workbook_view #:nodoc:
1217
1321
  ['windowWidth', @window_width],
1218
1322
  ['windowHeight', @window_height]
1219
1323
  ]
1220
- if @tab_ratio != 500
1324
+ if @tab_ratio != 600
1221
1325
  attributes << ['tabRatio', @tab_ratio]
1222
1326
  end
1223
1327
  if @firstsheet > 0
@@ -1421,9 +1525,17 @@ def prepare_num_formats #:nodoc:
1421
1525
  # string but would evaluate to zero.
1422
1526
  #
1423
1527
  if num_format.to_s =~ /^\d+$/ && num_format.to_s !~ /^0+\d/
1528
+ # Number format '0' is indexed as 1 in Excel.
1529
+ if num_format == 0
1530
+ num_format = 1
1531
+ end
1424
1532
  # Index to a built-in number format.
1425
1533
  format.num_format_index = num_format
1426
1534
  next
1535
+ elsif num_format.to_s == 'General'
1536
+ # The 'General' format has an number format index of 0.
1537
+ format.num_format_index = 0
1538
+ next
1427
1539
  end
1428
1540
 
1429
1541
  if num_formats[num_format]
@@ -1812,7 +1924,7 @@ def prepare_drawings #:nodoc:
1812
1924
  shape_count = sheet.shapes.size
1813
1925
  header_image_count = sheet.header_images.size
1814
1926
  footer_image_count = sheet.footer_images.size
1815
- has_drawing = false
1927
+ has_drawings = false
1816
1928
 
1817
1929
  # Check that some image or drawing needs to be processed.
1818
1930
  next if chart_count + image_count + shape_count + header_image_count + footer_image_count == 0
@@ -1820,7 +1932,7 @@ def prepare_drawings #:nodoc:
1820
1932
  # Don't increase the drawing_id header/footer images.
1821
1933
  if chart_count + image_count + shape_count > 0
1822
1934
  drawing_id += 1
1823
- has_drawing = true
1935
+ has_drawings = true
1824
1936
  end
1825
1937
 
1826
1938
  # Prepare the worksheet charts.
@@ -1869,9 +1981,9 @@ def prepare_drawings #:nodoc:
1869
1981
  name, type, position, x_dpi, y_dpi)
1870
1982
  end
1871
1983
 
1872
- if has_drawing
1873
- drawing = sheet.drawing
1874
- @drawings << drawing
1984
+ if has_drawings
1985
+ drawings = sheet.drawings
1986
+ @drawings << drawings
1875
1987
  end
1876
1988
  end
1877
1989
 
@@ -1914,6 +2026,10 @@ def get_image_properties(filename)
1914
2026
 
1915
2027
  @images << [filename, type]
1916
2028
 
2029
+ # Set a default dpi for images with 0 dpi.
2030
+ x_dpi = 96 if x_dpi == 0
2031
+ y_dpi = 96 if y_dpi == 0
2032
+
1917
2033
  [type, width, height, File.basename(filename), x_dpi, y_dpi]
1918
2034
  end
1919
2035
 
@@ -1969,16 +2085,20 @@ def process_jpg(data, filename)
1969
2085
  offset = 2
1970
2086
  data_length = data.bytesize
1971
2087
 
1972
- # Search through the image data to read the height and width in the
1973
- # 0xFFC0/C2 element. Also read the DPI in the 0xFFE0 element.
2088
+ # Search through the image data to read the JPEG markers.
1974
2089
  while offset < data_length
1975
2090
  marker = data[offset+0, 2].unpack("n")[0]
1976
2091
  length = data[offset+2, 2].unpack("n")[0]
1977
2092
 
1978
- if marker == 0xFFC0 || marker == 0xFFC2
2093
+ # Read the height and width in the 0xFFCn elements
2094
+ # (Except C4, C8 and CC which aren't SOF markers).
2095
+ if (marker & 0xFFF0) == 0xFFC0 &&
2096
+ marker != 0xFFC4 && marker != 0xFFCC
1979
2097
  height = data[offset+5, 2].unpack("n")[0]
1980
2098
  width = data[offset+7, 2].unpack("n")[0]
1981
2099
  end
2100
+
2101
+ # Read the DPI in the 0xFFE0 element.
1982
2102
  if marker == 0xFFE0
1983
2103
  units = data[offset + 11, 1].unpack("C")[0]
1984
2104
  x_density = data[offset + 12, 2].unpack("n")[0]