write_xlsx 0.97.0 → 1.04.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 (313) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Changes +78 -0
  4. data/LICENSE.txt +1 -1
  5. data/README.md +2 -2
  6. data/examples/a_simple.rb +1 -1
  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 +90 -12
  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/data_validate.rb +1 -1
  28. data/examples/date_time.rb +1 -1
  29. data/examples/demo.rb +4 -1
  30. data/examples/formats.rb +1 -1
  31. data/examples/headers.rb +1 -1
  32. data/examples/hide_row_col.rb +1 -1
  33. data/examples/hide_sheet.rb +1 -1
  34. data/examples/hyperlink1.rb +1 -1
  35. data/examples/indent.rb +1 -1
  36. data/examples/macros.rb +1 -1
  37. data/examples/merge1.rb +1 -1
  38. data/examples/merge2.rb +1 -1
  39. data/examples/merge3.rb +1 -1
  40. data/examples/merge4.rb +1 -1
  41. data/examples/merge5.rb +1 -1
  42. data/examples/merge6.rb +1 -1
  43. data/examples/outline.rb +1 -1
  44. data/examples/outline_collapsed.rb +1 -1
  45. data/examples/panes.rb +1 -1
  46. data/examples/properties.rb +1 -1
  47. data/examples/regions.rb +1 -1
  48. data/examples/rich_strings.rb +1 -1
  49. data/examples/right_to_left.rb +1 -1
  50. data/examples/shape1.rb +1 -1
  51. data/examples/shape2.rb +1 -1
  52. data/examples/shape3.rb +1 -1
  53. data/examples/shape4.rb +1 -1
  54. data/examples/shape5.rb +1 -1
  55. data/examples/shape6.rb +1 -1
  56. data/examples/shape7.rb +1 -1
  57. data/examples/shape8.rb +1 -1
  58. data/examples/shape_all.rb +1 -1
  59. data/examples/sparklines1.rb +1 -1
  60. data/examples/sparklines2.rb +1 -1
  61. data/examples/stats.rb +1 -1
  62. data/examples/stats_ext.rb +1 -1
  63. data/examples/stocks.rb +1 -1
  64. data/examples/tab_colors.rb +1 -1
  65. data/examples/tables.rb +78 -43
  66. data/lib/write_xlsx/chart.rb +43 -35
  67. data/lib/write_xlsx/chart/area.rb +1 -1
  68. data/lib/write_xlsx/chart/axis.rb +2 -2
  69. data/lib/write_xlsx/chart/bar.rb +1 -1
  70. data/lib/write_xlsx/chart/column.rb +1 -1
  71. data/lib/write_xlsx/chart/doughnut.rb +1 -1
  72. data/lib/write_xlsx/chart/legend.rb +14 -0
  73. data/lib/write_xlsx/chart/line.rb +16 -2
  74. data/lib/write_xlsx/chart/pie.rb +30 -15
  75. data/lib/write_xlsx/chart/radar.rb +1 -1
  76. data/lib/write_xlsx/chart/scatter.rb +1 -1
  77. data/lib/write_xlsx/chart/stock.rb +1 -1
  78. data/lib/write_xlsx/chartsheet.rb +35 -7
  79. data/lib/write_xlsx/drawing.rb +86 -30
  80. data/lib/write_xlsx/format.rb +9 -9
  81. data/lib/write_xlsx/package/comments.rb +61 -58
  82. data/lib/write_xlsx/package/conditional_format.rb +9 -1
  83. data/lib/write_xlsx/package/relationships.rb +4 -4
  84. data/lib/write_xlsx/package/styles.rb +26 -8
  85. data/lib/write_xlsx/package/table.rb +13 -7
  86. data/lib/write_xlsx/package/vml.rb +20 -19
  87. data/lib/write_xlsx/shape.rb +4 -3
  88. data/lib/write_xlsx/sheets.rb +18 -16
  89. data/lib/write_xlsx/sparkline.rb +1 -1
  90. data/lib/write_xlsx/utility.rb +96 -7
  91. data/lib/write_xlsx/version.rb +1 -1
  92. data/lib/write_xlsx/workbook.rb +99 -49
  93. data/lib/write_xlsx/worksheet.rb +225 -145
  94. data/lib/write_xlsx/worksheet/data_validation.rb +10 -14
  95. data/lib/write_xlsx/worksheet/hyperlink.rb +16 -37
  96. data/test/chart/test_write_legend_pos.rb +9 -1
  97. data/test/chartsheet/test_write_sheet_protection.rb +91 -0
  98. data/test/drawing/test_drawing_chart_01.rb +6 -2
  99. data/test/drawing/test_drawing_image_01.rb +12 -3
  100. data/test/drawing/test_drawing_shape_01.rb +8 -5
  101. data/test/drawing/test_drawing_shape_02.rb +12 -5
  102. data/test/drawing/test_drawing_shape_03.rb +8 -5
  103. data/test/drawing/test_drawing_shape_04.rb +8 -24
  104. data/test/drawing/test_drawing_shape_05.rb +8 -5
  105. data/test/drawing/test_drawing_shape_06.rb +11 -6
  106. data/test/drawing/test_drawing_shape_07.rb +11 -6
  107. data/test/drawing/test_write_a_graphic_frame_locks.rb +1 -1
  108. data/test/drawing/test_write_c_chart.rb +1 -1
  109. data/test/drawing/test_write_c_nv_graphic_frame_pr.rb +1 -1
  110. data/test/drawing/test_write_c_nv_pr.rb +1 -1
  111. data/test/drawing/test_write_col.rb +1 -1
  112. data/test/drawing/test_write_col_off.rb +1 -1
  113. data/test/drawing/test_write_ext.rb +1 -1
  114. data/test/drawing/test_write_pos.rb +1 -1
  115. data/test/drawing/test_write_row.rb +1 -1
  116. data/test/drawing/test_write_row_off.rb +1 -1
  117. data/test/drawing/test_write_xfrm_extension.rb +1 -1
  118. data/test/drawing/test_write_xfrm_offset.rb +1 -1
  119. data/test/package/comments/test_comments_01.rb +54 -0
  120. data/test/package/comments/test_comments_02.rb +54 -0
  121. data/test/perl_output/chart_gauge.xlsx +0 -0
  122. data/test/perl_output/chart_line.xlsx +0 -0
  123. data/test/perl_output/comments2.xlsx +0 -0
  124. data/test/perl_output/formats.xlsx +0 -0
  125. data/test/perl_output/tables.xlsx +0 -0
  126. data/test/regression/images/happy.jpg +0 -0
  127. data/test/regression/images/red2.png +0 -0
  128. data/test/regression/test_array_formula03.rb +36 -0
  129. data/test/regression/test_autofilter08.rb +110 -0
  130. data/test/regression/test_autofilter09.rb +110 -0
  131. data/test/regression/test_autofilter10.rb +110 -0
  132. data/test/regression/test_chart_axis26.rb +10 -8
  133. data/test/regression/test_chart_axis27.rb +1 -1
  134. data/test/regression/test_chart_axis28.rb +1 -1
  135. data/test/regression/test_chart_axis29.rb +1 -1
  136. data/test/regression/test_chart_axis33.rb +1 -1
  137. data/test/regression/test_chart_axis42.rb +44 -0
  138. data/test/regression/test_chart_axis43.rb +44 -0
  139. data/test/regression/test_chart_axis44.rb +54 -0
  140. data/test/regression/test_chart_axis45.rb +54 -0
  141. data/test/regression/test_chart_axis46.rb +54 -0
  142. data/test/regression/test_chart_combined10.rb +43 -0
  143. data/test/regression/test_chart_combined11.rb +63 -0
  144. data/test/regression/test_chart_data_labels25.rb +1 -1
  145. data/test/regression/test_chart_doughnut07.rb +37 -0
  146. data/test/regression/test_chart_font09.rb +1 -1
  147. data/test/regression/test_chart_legend03.rb +41 -0
  148. data/test/regression/test_chart_legend04.rb +41 -0
  149. data/test/regression/test_chart_legend05.rb +41 -0
  150. data/test/regression/test_chart_legend06.rb +41 -0
  151. data/test/regression/test_chart_legend07.rb +38 -0
  152. data/test/regression/test_chart_line05.rb +43 -0
  153. data/test/regression/test_chart_line06.rb +43 -0
  154. data/test/regression/test_chart_size03.rb +4 -1
  155. data/test/regression/test_comment13.rb +36 -0
  156. data/test/regression/test_comment14.rb +29 -0
  157. data/test/regression/test_comment15.rb +28 -0
  158. data/test/regression/test_comment16.rb +34 -0
  159. data/test/regression/test_cond_format19.rb +64 -0
  160. data/test/regression/test_cond_format20.rb +43 -0
  161. data/test/regression/test_format15.rb +26 -0
  162. data/test/regression/test_header_image15.rb +36 -0
  163. data/test/regression/test_header_image16.rb +42 -0
  164. data/test/regression/test_header_image17.rb +46 -0
  165. data/test/regression/test_header_image18.rb +48 -0
  166. data/test/regression/test_header_image19.rb +36 -0
  167. data/test/regression/test_hyperlink32.rb +27 -0
  168. data/test/regression/test_hyperlink33.rb +28 -0
  169. data/test/regression/test_hyperlink34.rb +33 -0
  170. data/test/regression/test_hyperlink35.rb +39 -0
  171. data/test/regression/test_hyperlink36.rb +34 -0
  172. data/test/regression/test_hyperlink37.rb +33 -0
  173. data/test/regression/test_hyperlink38.rb +27 -0
  174. data/test/regression/test_hyperlink39.rb +27 -0
  175. data/test/regression/test_hyperlink40.rb +27 -0
  176. data/test/regression/test_hyperlink41.rb +27 -0
  177. data/test/regression/test_hyperlink42.rb +27 -0
  178. data/test/regression/test_hyperlink43.rb +27 -0
  179. data/test/regression/test_hyperlink44.rb +27 -0
  180. data/test/regression/test_hyperlink45.rb +27 -0
  181. data/test/regression/test_hyperlink47.rb +27 -0
  182. data/test/regression/test_hyperlink48.rb +31 -0
  183. data/test/regression/test_hyperlink49.rb +29 -0
  184. data/test/regression/test_image06.rb +5 -5
  185. data/test/regression/test_image08.rb +5 -4
  186. data/test/regression/test_image15.rb +4 -2
  187. data/test/regression/test_image28.rb +1 -1
  188. data/test/regression/test_image36.rb +26 -0
  189. data/test/regression/test_image44.rb +28 -0
  190. data/test/regression/test_image45.rb +29 -0
  191. data/test/regression/test_image46.rb +29 -0
  192. data/test/regression/test_image47.rb +28 -0
  193. data/test/regression/test_image48.rb +32 -0
  194. data/test/regression/test_image49.rb +38 -0
  195. data/test/regression/test_image50.rb +24 -0
  196. data/test/regression/test_image51.rb +30 -0
  197. data/test/regression/test_object_position01.rb +26 -0
  198. data/test/regression/test_object_position02.rb +26 -0
  199. data/test/regression/test_object_position03.rb +26 -0
  200. data/test/regression/test_object_position04.rb +44 -0
  201. data/test/regression/test_object_position06.rb +28 -0
  202. data/test/regression/test_object_position07.rb +28 -0
  203. data/test/regression/test_object_position08.rb +47 -0
  204. data/test/regression/test_object_position09.rb +50 -0
  205. data/test/regression/test_object_position10.rb +28 -0
  206. data/test/regression/test_object_position12.rb +25 -0
  207. data/test/regression/test_object_position13.rb +25 -0
  208. data/test/regression/test_object_position14.rb +25 -0
  209. data/test/regression/test_object_position15.rb +29 -0
  210. data/test/regression/test_object_position16.rb +29 -0
  211. data/test/regression/test_object_position17.rb +29 -0
  212. data/test/regression/test_object_position18.rb +29 -0
  213. data/test/regression/test_object_position19.rb +29 -0
  214. data/test/regression/test_object_position20.rb +29 -0
  215. data/test/regression/test_shape_connect01.rb +4 -2
  216. data/test/regression/test_table23.rb +56 -0
  217. data/test/regression/test_table24.rb +27 -0
  218. data/test/regression/test_table25.rb +27 -0
  219. data/test/regression/xlsx_files/array_formula03.xlsx +0 -0
  220. data/test/regression/xlsx_files/autofilter08.xlsx +0 -0
  221. data/test/regression/xlsx_files/autofilter09.xlsx +0 -0
  222. data/test/regression/xlsx_files/autofilter10.xlsx +0 -0
  223. data/test/regression/xlsx_files/chart_axis26.xlsx +0 -0
  224. data/test/regression/xlsx_files/chart_axis27.xlsx +0 -0
  225. data/test/regression/xlsx_files/chart_axis28.xlsx +0 -0
  226. data/test/regression/xlsx_files/chart_axis29.xlsx +0 -0
  227. data/test/regression/xlsx_files/chart_axis33.xlsx +0 -0
  228. data/test/regression/xlsx_files/chart_axis42.xlsx +0 -0
  229. data/test/regression/xlsx_files/chart_axis43.xlsx +0 -0
  230. data/test/regression/xlsx_files/chart_axis44.xlsx +0 -0
  231. data/test/regression/xlsx_files/chart_axis45.xlsx +0 -0
  232. data/test/regression/xlsx_files/chart_axis46.xlsx +0 -0
  233. data/test/regression/xlsx_files/chart_combined10.xlsx +0 -0
  234. data/test/regression/xlsx_files/chart_combined11.xlsx +0 -0
  235. data/test/regression/xlsx_files/chart_data_labels25.xlsx +0 -0
  236. data/test/regression/xlsx_files/chart_doughnut07.xlsx +0 -0
  237. data/test/regression/xlsx_files/chart_font09.xlsx +0 -0
  238. data/test/regression/xlsx_files/chart_legend03.xlsx +0 -0
  239. data/test/regression/xlsx_files/chart_legend04.xlsx +0 -0
  240. data/test/regression/xlsx_files/chart_legend05.xlsx +0 -0
  241. data/test/regression/xlsx_files/chart_legend06.xlsx +0 -0
  242. data/test/regression/xlsx_files/chart_legend07.xlsx +0 -0
  243. data/test/regression/xlsx_files/chart_line05.xlsx +0 -0
  244. data/test/regression/xlsx_files/chart_line06.xlsx +0 -0
  245. data/test/regression/xlsx_files/comment13.xlsx +0 -0
  246. data/test/regression/xlsx_files/comment14.xlsx +0 -0
  247. data/test/regression/xlsx_files/comment15.xlsx +0 -0
  248. data/test/regression/xlsx_files/comment16.xlsx +0 -0
  249. data/test/regression/xlsx_files/cond_format19.xlsx +0 -0
  250. data/test/regression/xlsx_files/cond_format20.xlsx +0 -0
  251. data/test/regression/xlsx_files/format15.xlsx +0 -0
  252. data/test/regression/xlsx_files/header_image15.xlsx +0 -0
  253. data/test/regression/xlsx_files/header_image16.xlsx +0 -0
  254. data/test/regression/xlsx_files/header_image17.xlsx +0 -0
  255. data/test/regression/xlsx_files/header_image18.xlsx +0 -0
  256. data/test/regression/xlsx_files/header_image19.xlsx +0 -0
  257. data/test/regression/xlsx_files/hyperlink32.xlsx +0 -0
  258. data/test/regression/xlsx_files/hyperlink33.xlsx +0 -0
  259. data/test/regression/xlsx_files/hyperlink34.xlsx +0 -0
  260. data/test/regression/xlsx_files/hyperlink35.xlsx +0 -0
  261. data/test/regression/xlsx_files/hyperlink36.xlsx +0 -0
  262. data/test/regression/xlsx_files/hyperlink37.xlsx +0 -0
  263. data/test/regression/xlsx_files/hyperlink38.xlsx +0 -0
  264. data/test/regression/xlsx_files/hyperlink39.xlsx +0 -0
  265. data/test/regression/xlsx_files/hyperlink40.xlsx +0 -0
  266. data/test/regression/xlsx_files/hyperlink41.xlsx +0 -0
  267. data/test/regression/xlsx_files/hyperlink42.xlsx +0 -0
  268. data/test/regression/xlsx_files/hyperlink43.xlsx +0 -0
  269. data/test/regression/xlsx_files/hyperlink44.xlsx +0 -0
  270. data/test/regression/xlsx_files/hyperlink45.xlsx +0 -0
  271. data/test/regression/xlsx_files/hyperlink46.xlsx +0 -0
  272. data/test/regression/xlsx_files/hyperlink47.xlsx +0 -0
  273. data/test/regression/xlsx_files/hyperlink48.xlsx +0 -0
  274. data/test/regression/xlsx_files/hyperlink49.xlsx +0 -0
  275. data/test/regression/xlsx_files/image06.xlsx +0 -0
  276. data/test/regression/xlsx_files/image36.xlsx +0 -0
  277. data/test/regression/xlsx_files/image44.xlsx +0 -0
  278. data/test/regression/xlsx_files/image45.xlsx +0 -0
  279. data/test/regression/xlsx_files/image46.xlsx +0 -0
  280. data/test/regression/xlsx_files/image47.xlsx +0 -0
  281. data/test/regression/xlsx_files/image48.xlsx +0 -0
  282. data/test/regression/xlsx_files/image49.xlsx +0 -0
  283. data/test/regression/xlsx_files/image50.xlsx +0 -0
  284. data/test/regression/xlsx_files/image51.xlsx +0 -0
  285. data/test/regression/xlsx_files/object_position01.xlsx +0 -0
  286. data/test/regression/xlsx_files/object_position02.xlsx +0 -0
  287. data/test/regression/xlsx_files/object_position03.xlsx +0 -0
  288. data/test/regression/xlsx_files/object_position04.xlsx +0 -0
  289. data/test/regression/xlsx_files/object_position06.xlsx +0 -0
  290. data/test/regression/xlsx_files/object_position07.xlsx +0 -0
  291. data/test/regression/xlsx_files/object_position08.xlsx +0 -0
  292. data/test/regression/xlsx_files/object_position09.xlsx +0 -0
  293. data/test/regression/xlsx_files/object_position10.xlsx +0 -0
  294. data/test/regression/xlsx_files/object_position12.xlsx +0 -0
  295. data/test/regression/xlsx_files/object_position13.xlsx +0 -0
  296. data/test/regression/xlsx_files/object_position14.xlsx +0 -0
  297. data/test/regression/xlsx_files/object_position15.xlsx +0 -0
  298. data/test/regression/xlsx_files/object_position16.xlsx +0 -0
  299. data/test/regression/xlsx_files/object_position17.xlsx +0 -0
  300. data/test/regression/xlsx_files/object_position18.xlsx +0 -0
  301. data/test/regression/xlsx_files/object_position19.xlsx +0 -0
  302. data/test/regression/xlsx_files/object_position20.xlsx +0 -0
  303. data/test/regression/xlsx_files/table23.xlsx +0 -0
  304. data/test/regression/xlsx_files/table24.xlsx +0 -0
  305. data/test/regression/xlsx_files/table25.xlsx +0 -0
  306. data/test/test_example_match.rb +955 -780
  307. data/test/workbook/test_check_sheetname.rb +51 -0
  308. data/test/workbook/test_write_workbook_view.rb +36 -0
  309. data/test/worksheet/test_write_data_validation_02.rb +17 -0
  310. data/test/worksheet/test_write_sheet_view.rb +19 -1
  311. data/write_xlsx.gemspec +1 -0
  312. metadata +349 -7
  313. data/test/package/comments/test_write_text_t.rb +0 -44
@@ -8,14 +8,14 @@ module Writexlsx
8
8
  # Used in conjunction with WriteXLSX.
9
9
  #
10
10
  # Copyright 2000-2012, John McNamara, jmcnamara@cpan.org
11
- # Converted to ruby by Hideo NAKAMURA, cxn03651@msj.biglobe.ne.jp
11
+ # Converted to ruby by Hideo NAKAMURA, nakamura.hideo@gmail.com
12
12
  #
13
13
  class Shape
14
14
  include Writexlsx::Utility
15
15
 
16
16
  attr_reader :edit_as, :type, :drawing
17
17
  attr_reader :tx_box, :fill, :line, :format
18
- attr_reader :align, :valign
18
+ attr_reader :align, :valign, :anchor
19
19
  attr_accessor :name, :connect, :type, :id, :start, :end, :rotation
20
20
  attr_accessor :flip_h, :flip_v, :adjustments, :palette, :text, :stencil
21
21
  attr_accessor :row_start, :row_end, :column_start, :column_end
@@ -183,11 +183,12 @@ def calc_position_emus(worksheet)
183
183
  @y_abs = (y_abslt * 9_525).to_i
184
184
  end
185
185
 
186
- def set_position(row_start, column_start, x_offset, y_offset, x_scale, y_scale)
186
+ def set_position(row_start, column_start, x_offset, y_offset, x_scale, y_scale, anchor)
187
187
  @row_start = row_start
188
188
  @column_start = column_start
189
189
  @x_offset = x_offset || 0
190
190
  @y_offset = y_offset || 0
191
+ @anchor = anchor || 1
191
192
 
192
193
  # Override shape scale if supplied as an argument. Otherwise, use the
193
194
  # existing shape scale factors.
@@ -67,18 +67,18 @@ def write_vml_files(package_dir)
67
67
  vml = Package::Vml.new
68
68
  vml.set_xml_writer("#{dir}/vmlDrawing#{index}.vml")
69
69
  vml.assemble_xml_file(
70
- sheet.vml_data_id, sheet.vml_shape_id,
71
- sheet.sorted_comments, sheet.buttons_data
72
- )
70
+ sheet.vml_data_id, sheet.vml_shape_id,
71
+ sheet.sorted_comments, sheet.buttons_data
72
+ )
73
73
  index += 1
74
74
  end
75
75
  if sheet.has_header_vml?
76
76
  vml = Package::Vml.new
77
77
  vml.set_xml_writer("#{dir}/vmlDrawing#{index}.vml")
78
78
  vml.assemble_xml_file(
79
- sheet.vml_header_id, sheet.vml_header_id * 1024,
80
- [], [], sheet.header_images_data
81
- )
79
+ sheet.vml_header_id, sheet.vml_header_id * 1024,
80
+ [], [], sheet.header_images_data
81
+ )
82
82
  write_vml_drawing_rels_files(package_dir, sheet, index)
83
83
  index += 1
84
84
  end
@@ -106,16 +106,12 @@ def write_table_files(package_dir)
106
106
  end
107
107
 
108
108
  def write_chartsheet_rels_files(package_dir)
109
- write_sheet_rels_files_base(chartsheets, "#{package_dir}/xl/chartsheets/_rels",
110
- 'sheet')
109
+ write_sheet_rels_files_base(
110
+ chartsheets, "#{package_dir}/xl/chartsheets/_rels", 'sheet'
111
+ )
111
112
  end
112
113
 
113
114
  def write_drawing_rels_files(package_dir)
114
- # write_rels_files_base(
115
- # self.reject { |sheet| sheet.drawing_links[0].empty? },
116
- # "#{package_dir}/xl/drawings/_rels",
117
-
118
- # )
119
115
  dir = "#{package_dir}/xl/drawings/_rels"
120
116
 
121
117
  index = 0
@@ -159,8 +155,9 @@ def write_vml_drawing_rels_files(package_dir, worksheet, index)
159
155
  end
160
156
 
161
157
  def write_worksheet_rels_files(package_dir)
162
- write_sheet_rels_files_base(worksheets, "#{package_dir}/xl/worksheets/_rels",
163
- 'sheet')
158
+ write_sheet_rels_files_base(
159
+ worksheets, "#{package_dir}/xl/worksheets/_rels", 'sheet'
160
+ )
164
161
  end
165
162
 
166
163
  def write_sheet_rels_files_base(sheets, dir, body)
@@ -230,10 +227,15 @@ def check_valid_sheetname(name)
230
227
  raise 'Invalid character []:*?/\\ in worksheet name: ' + name
231
228
  end
232
229
 
230
+ # Check that sheetname doesn't start or end with an apostrophe.
231
+ if name =~ /^'/ || name =~ /'$/
232
+ raise "Worksheet name #{name} cannot start or end with an "
233
+ end
234
+
233
235
  # Check that the worksheet name doesn't already exist since this is a fatal
234
236
  # error in Excel 97. The check must also exclude case insensitive matches.
235
237
  unless is_sheetname_uniq?(name)
236
- raise "Worksheet name '#{name}', with case ignored, is already used."
238
+ raise "apostropheWorksheet name '#{name}', with case ignored, is already used."
237
239
  end
238
240
  end
239
241
 
@@ -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]/
@@ -218,6 +218,25 @@ def convert_date_time(date_time_string) #:nodoc:
218
218
  end
219
219
  end
220
220
 
221
+ def escape_url(url)
222
+ unless url =~ /%[0-9a-fA-F]{2}/
223
+ # Escape the URL escape symbol.
224
+ url = url.gsub(/%/, "%25")
225
+
226
+ # Escape whitespae in URL.
227
+ url = url.gsub(/[\s\x00]/, '%20')
228
+
229
+ # Escape other special characters in URL.
230
+ re = /(["<>\[\]`^{}])/
231
+ while re =~ url
232
+ match = $~[1]
233
+ url = url.sub(re, sprintf("%%%x", match.ord))
234
+ end
235
+ end
236
+
237
+ url
238
+ end
239
+
221
240
  def absolute_char(absolute)
222
241
  absolute ? '$' : ''
223
242
  end
@@ -361,6 +380,64 @@ def float_to_str(float)
361
380
  end
362
381
  end
363
382
 
383
+ #
384
+ # Convert user defined legend properties to the structure required internally.
385
+ #
386
+ def legend_properties(params)
387
+ legend = Writexlsx::Chart::Legend.new
388
+
389
+ legend.position = params[:position] || 'right'
390
+ legend.delete_series = params[:delete_series]
391
+ legend.font = convert_font_args(params[:font])
392
+
393
+ # Set the legend layout.
394
+ legend.layout = layout_properties(params[:layout])
395
+
396
+ # Turn off the legend.
397
+ if params[:none]
398
+ legend.position = 'none'
399
+ end
400
+
401
+ # Set the line properties for the legend.
402
+ line = line_properties(params[:line])
403
+
404
+ # Allow 'border' as a synonym for 'line'.
405
+ if params[:border]
406
+ line = line_properties(params[:border])
407
+ end
408
+
409
+ # Set the fill properties for the legend.
410
+ fill = fill_properties(params[:fill])
411
+
412
+ # Set the pattern properties for the legend.
413
+ pattern = pattern_properties(params[:pattern])
414
+
415
+ # Set the gradient fill properties for the legend.
416
+ gradient = gradient_properties(params[:gradient])
417
+
418
+ # Pattern fill overrides solid fill.
419
+ if pattern
420
+ fill = nil
421
+ end
422
+
423
+ # Gradient fill overrides solid and pattern fills.
424
+ if gradient
425
+ pattern = nil
426
+ fill = nil
427
+ end
428
+
429
+ # Set the legend layout.
430
+ layout = layout_properties(params[:layout])
431
+
432
+ legend.line = line
433
+ legend.fill = fill
434
+ legend.pattern = pattern
435
+ legend.gradient = gradient
436
+ legend.layout = layout
437
+
438
+ @legend = legend
439
+ end
440
+
364
441
  #
365
442
  # Convert user defined layout properties to the format required internally.
366
443
  #
@@ -727,14 +804,14 @@ def params_to_font(params)
727
804
  #
728
805
  # Write the <c:txPr> element.
729
806
  #
730
- def write_tx_pr(horiz, font) # :nodoc:
807
+ def write_tx_pr(is_y_axis, font) # :nodoc:
731
808
  rotation = nil
732
809
  if font && font[:_rotation]
733
810
  rotation = font[:_rotation]
734
811
  end
735
812
  @writer.tag_elements('c:txPr') do
736
813
  # Write the a:bodyPr element.
737
- write_a_body_pr(rotation, horiz)
814
+ write_a_body_pr(rotation, is_y_axis)
738
815
  # Write the a:lstStyle element.
739
816
  write_a_lst_style
740
817
  # Write the a:p element.
@@ -745,11 +822,23 @@ def write_tx_pr(horiz, font) # :nodoc:
745
822
  #
746
823
  # Write the <a:bodyPr> element.
747
824
  #
748
- def write_a_body_pr(rot, horiz = nil) # :nodoc:
749
- rot = -5400000 if !rot && ptrue?(horiz)
825
+ def write_a_body_pr(rot, is_y_axis = nil) # :nodoc:
826
+ rot = -5400000 if !rot && ptrue?(is_y_axis)
750
827
  attributes = []
751
- attributes << ['rot', rot] if rot
752
- attributes << ['vert', 'horz'] if ptrue?(horiz)
828
+ if rot
829
+ if rot == 16_200_000
830
+ # 270 deg/stacked angle.
831
+ attributes << ['rot', 0]
832
+ attributes << ['vert', 'wordArtVert']
833
+ elsif rot == 16_260_000
834
+ # 271 deg/stacked angle.
835
+ attributes << ['rot', 0]
836
+ attributes << ['vert', 'eaVert']
837
+ else
838
+ attributes << ['rot', rot]
839
+ attributes << ['vert', 'horz']
840
+ end
841
+ end
753
842
 
754
843
  @writer.empty_tag('a:bodyPr', attributes)
755
844
  end
@@ -1 +1 @@
1
- WriteXLSX_VERSION = "0.97.0"
1
+ WriteXLSX_VERSION = "1.04.0"
@@ -49,6 +49,7 @@ class Workbook
49
49
  attr_reader :shared_strings # :nodoc:
50
50
  attr_reader :vba_project # :nodoc:
51
51
  attr_reader :excel2003_style # :nodoc:
52
+ attr_reader :max_url_length # :nodoc:
52
53
  attr_reader :strings_to_urls # :nodoc:
53
54
  attr_reader :default_url_format # :nodoc:
54
55
 
@@ -123,13 +124,20 @@ def initialize(file, *option_params)
123
124
  @y_window = 15
124
125
  @window_width = 16095
125
126
  @window_height = 9660
126
- @tab_ratio = 500
127
+ @tab_ratio = 600
127
128
  @excel2003_style = options[:excel2003_style] || false
128
129
  @table_count = 0
129
130
  @image_types = {}
130
131
  @images = []
131
132
  @strings_to_urls = (options[:strings_to_urls].nil? || options[:strings_to_urls]) ? true : false
132
133
 
134
+ @max_url_length = 2079
135
+ @has_comments = false
136
+ if options[:max_url_length]
137
+ @max_url_length = options[:max_url_length]
138
+
139
+ @max_url_length = 2079 if @max_url_length < 250
140
+ end
133
141
  # Structures for the shared strings data.
134
142
  @shared_strings = Package::SharedStrings.new
135
143
 
@@ -784,6 +792,19 @@ def set_size(width = nil, height = nil)
784
792
  end
785
793
  end
786
794
 
795
+ #
796
+ # Set the ratio of space for worksheet tabs.
797
+ #
798
+ def set_tab_ratio(tab_ratio = nil)
799
+ return if !tab_ratio
800
+
801
+ if tab_ratio < 0 || tab_ratio > 100
802
+ raise "Tab ratio outside range: 0 <= zoom <= 100"
803
+ else
804
+ @tab_ratio = (tab_ratio * 10).to_i
805
+ end
806
+ end
807
+
787
808
  #
788
809
  # The set_properties method can be used to set the document properties
789
810
  # of the Excel file created by WriteXLSX. These properties are visible
@@ -1095,7 +1116,8 @@ def style_properties
1095
1116
  @border_count,
1096
1117
  @fill_count,
1097
1118
  @custom_colors,
1098
- @dxf_formats
1119
+ @dxf_formats,
1120
+ @has_comments
1099
1121
  ]
1100
1122
  end
1101
1123
 
@@ -1308,7 +1330,7 @@ def write_workbook_view #:nodoc:
1308
1330
  ['windowWidth', @window_width],
1309
1331
  ['windowHeight', @window_height]
1310
1332
  ]
1311
- if @tab_ratio != 500
1333
+ if @tab_ratio != 600
1312
1334
  attributes << ['tabRatio', @tab_ratio]
1313
1335
  end
1314
1336
  if @firstsheet > 0
@@ -1512,9 +1534,17 @@ def prepare_num_formats #:nodoc:
1512
1534
  # string but would evaluate to zero.
1513
1535
  #
1514
1536
  if num_format.to_s =~ /^\d+$/ && num_format.to_s !~ /^0+\d/
1537
+ # Number format '0' is indexed as 1 in Excel.
1538
+ if num_format == 0
1539
+ num_format = 1
1540
+ end
1515
1541
  # Index to a built-in number format.
1516
1542
  format.num_format_index = num_format
1517
1543
  next
1544
+ elsif num_format.to_s == 'General'
1545
+ # The 'General' format has an number format index of 0.
1546
+ format.num_format_index = 0
1547
+ next
1518
1548
  end
1519
1549
 
1520
1550
  if num_formats[num_format]
@@ -1683,11 +1713,14 @@ def prepare_vml_objects #:nodoc:
1683
1713
  if sheet.has_comments?
1684
1714
  comment_files += 1
1685
1715
  comment_id += 1
1716
+ @has_comments = true
1686
1717
  end
1687
1718
  vml_drawing_id += 1
1688
1719
 
1689
- sheet.prepare_vml_objects(vml_data_id, vml_shape_id,
1690
- vml_drawing_id, comment_id)
1720
+ sheet.prepare_vml_objects(
1721
+ vml_data_id, vml_shape_id,
1722
+ vml_drawing_id, comment_id
1723
+ )
1691
1724
 
1692
1725
  # Each VML file should start with a shape id incremented by 1024.
1693
1726
  vml_data_id += 1 * ( 1 + sheet.num_comments_block )
@@ -1710,8 +1743,6 @@ def prepare_vml_objects #:nodoc:
1710
1743
  end
1711
1744
  end
1712
1745
 
1713
- add_font_format_for_cell_comments if num_comment_files > 0
1714
-
1715
1746
  # Set the workbook vba_codename if one of the sheets has a button and
1716
1747
  # the workbook has a vbaProject binary.
1717
1748
  if has_button && @vba_project && !@vba_codename
@@ -1731,19 +1762,6 @@ def prepare_tables
1731
1762
  end
1732
1763
  end
1733
1764
 
1734
- def add_font_format_for_cell_comments
1735
- format = Format.new(
1736
- @formats,
1737
- :font => 'Tahoma',
1738
- :size => 8,
1739
- :color_indexed => 81,
1740
- :font_only => 1
1741
- )
1742
-
1743
- format.get_xf_index
1744
- @formats.formats << format
1745
- end
1746
-
1747
1765
  #
1748
1766
  # Add "cached" data to charts to provide the numCache and strCache data for
1749
1767
  # series and title/axis ranges.
@@ -1894,16 +1912,19 @@ def extract_named_ranges(defined_names) #:nodoc:
1894
1912
  # Iterate through the worksheets and set up any chart or image drawings.
1895
1913
  #
1896
1914
  def prepare_drawings #:nodoc:
1897
- chart_ref_id = 0
1898
- image_ref_id = 0
1899
- drawing_id = 0
1915
+ chart_ref_id = 0
1916
+ image_ref_id = 0
1917
+ drawing_id = 0
1918
+ ref_id = 0
1919
+ image_ids = {}
1920
+ header_image_ids = {}
1900
1921
  @worksheets.each do |sheet|
1901
1922
  chart_count = sheet.charts.size
1902
1923
  image_count = sheet.images.size
1903
1924
  shape_count = sheet.shapes.size
1904
1925
  header_image_count = sheet.header_images.size
1905
1926
  footer_image_count = sheet.footer_images.size
1906
- has_drawing = false
1927
+ has_drawings = false
1907
1928
 
1908
1929
  # Check that some image or drawing needs to be processed.
1909
1930
  next if chart_count + image_count + shape_count + header_image_count + footer_image_count == 0
@@ -1911,7 +1932,24 @@ def prepare_drawings #:nodoc:
1911
1932
  # Don't increase the drawing_id header/footer images.
1912
1933
  if chart_count + image_count + shape_count > 0
1913
1934
  drawing_id += 1
1914
- has_drawing = true
1935
+ has_drawings = true
1936
+ end
1937
+
1938
+ # Prepare the worksheet images.
1939
+ sheet.images.each_with_index do |image, index|
1940
+ filename = image[2]
1941
+ type, width, height, name, x_dpi, y_dpi, md5 = get_image_properties(image[2])
1942
+ if image_ids[md5]
1943
+ ref_id = image_ids[md5]
1944
+ else
1945
+ image_ref_id += 1
1946
+ image_ids[md5] = ref_id = image_ref_id
1947
+ @images << [filename, type]
1948
+ end
1949
+ sheet.prepare_image(
1950
+ index, ref_id, drawing_id, width, height,
1951
+ name, type, x_dpi, y_dpi, md5
1952
+ )
1915
1953
  end
1916
1954
 
1917
1955
  # Prepare the worksheet charts.
@@ -1920,13 +1958,6 @@ def prepare_drawings #:nodoc:
1920
1958
  sheet.prepare_chart(index, chart_ref_id, drawing_id)
1921
1959
  end
1922
1960
 
1923
- # Prepare the worksheet images.
1924
- sheet.images.each_with_index do |image, index|
1925
- type, width, height, name, x_dpi, y_dpi = get_image_properties(image[2])
1926
- image_ref_id += 1
1927
- sheet.prepare_image(index, image_ref_id, drawing_id, width, height, name, type, x_dpi, y_dpi)
1928
- end
1929
-
1930
1961
  # Prepare the worksheet shapes.
1931
1962
  sheet.shapes.each_with_index do |shape, index|
1932
1963
  sheet.prepare_shape(index, drawing_id)
@@ -1937,13 +1968,21 @@ def prepare_drawings #:nodoc:
1937
1968
  filename = sheet.header_images[index][0]
1938
1969
  position = sheet.header_images[index][1]
1939
1970
 
1940
- type, width, height, name, x_dpi, y_dpi =
1971
+ type, width, height, name, x_dpi, y_dpi, md5 =
1941
1972
  get_image_properties(filename)
1942
1973
 
1943
- image_ref_id += 1
1974
+ if header_image_ids[md5]
1975
+ ref_id = header_image_ids[md5]
1976
+ else
1977
+ image_ref_id += 1
1978
+ header_image_ids[md5] = ref_id = image_ref_id
1979
+ @images << [filename, type]
1980
+ end
1944
1981
 
1945
- sheet.prepare_header_image(image_ref_id, width, height,
1946
- name, type, position, x_dpi, y_dpi)
1982
+ sheet.prepare_header_image(
1983
+ ref_id, width, height, name, type,
1984
+ position, x_dpi, y_dpi, md5
1985
+ )
1947
1986
  end
1948
1987
 
1949
1988
  # Prepare the footer images.
@@ -1951,18 +1990,26 @@ def prepare_drawings #:nodoc:
1951
1990
  filename = sheet.footer_images[index][0]
1952
1991
  position = sheet.footer_images[index][1]
1953
1992
 
1954
- type, width, height, name, x_dpi, y_dpi =
1993
+ type, width, height, name, x_dpi, y_dpi, md5 =
1955
1994
  get_image_properties(filename)
1956
1995
 
1957
- image_ref_id += 1
1996
+ if header_image_ids[md5]
1997
+ ref_id = header_image_ids[md5]
1998
+ else
1999
+ image_ref_id += 1
2000
+ header_image_ids[md5] = ref_id = image_ref_id
2001
+ @images << [filename, type]
2002
+ end
1958
2003
 
1959
- sheet.prepare_header_image(image_ref_id, width, height,
1960
- name, type, position, x_dpi, y_dpi)
2004
+ sheet.prepare_header_image(
2005
+ ref_id, width, height, name, type,
2006
+ position, x_dpi, y_dpi, md5
2007
+ )
1961
2008
  end
1962
2009
 
1963
- if has_drawing
1964
- drawing = sheet.drawing
1965
- @drawings << drawing
2010
+ if has_drawings
2011
+ drawings = sheet.drawings
2012
+ @drawings << drawings
1966
2013
  end
1967
2014
  end
1968
2015
 
@@ -1986,6 +2033,7 @@ def get_image_properties(filename)
1986
2033
 
1987
2034
  # Open the image file and import the data.
1988
2035
  data = File.binread(filename)
2036
+ md5 = Digest::MD5.hexdigest(data)
1989
2037
  if data.unpack('x A3')[0] == 'PNG'
1990
2038
  # Test for PNGs.
1991
2039
  type, width, height, x_dpi, y_dpi = process_png(data)
@@ -2003,13 +2051,11 @@ def get_image_properties(filename)
2003
2051
  raise "Unsupported image format for file: #{filename}\n"
2004
2052
  end
2005
2053
 
2006
- @images << [filename, type]
2007
-
2008
2054
  # Set a default dpi for images with 0 dpi.
2009
2055
  x_dpi = 96 if x_dpi == 0
2010
2056
  y_dpi = 96 if y_dpi == 0
2011
2057
 
2012
- [type, width, height, File.basename(filename), x_dpi, y_dpi]
2058
+ [type, width, height, File.basename(filename), x_dpi, y_dpi, md5]
2013
2059
  end
2014
2060
 
2015
2061
  #
@@ -2064,16 +2110,20 @@ def process_jpg(data, filename)
2064
2110
  offset = 2
2065
2111
  data_length = data.bytesize
2066
2112
 
2067
- # Search through the image data to read the height and width in the
2068
- # 0xFFC0/C2 element. Also read the DPI in the 0xFFE0 element.
2113
+ # Search through the image data to read the JPEG markers.
2069
2114
  while offset < data_length
2070
2115
  marker = data[offset+0, 2].unpack("n")[0]
2071
2116
  length = data[offset+2, 2].unpack("n")[0]
2072
2117
 
2073
- if marker == 0xFFC0 || marker == 0xFFC2
2118
+ # Read the height and width in the 0xFFCn elements
2119
+ # (Except C4, C8 and CC which aren't SOF markers).
2120
+ if (marker & 0xFFF0) == 0xFFC0 &&
2121
+ marker != 0xFFC4 && marker != 0xFFCC
2074
2122
  height = data[offset+5, 2].unpack("n")[0]
2075
2123
  width = data[offset+7, 2].unpack("n")[0]
2076
2124
  end
2125
+
2126
+ # Read the DPI in the 0xFFE0 element.
2077
2127
  if marker == 0xFFE0
2078
2128
  units = data[offset + 11, 1].unpack("C")[0]
2079
2129
  x_density = data[offset + 12, 2].unpack("n")[0]