write_xlsx 0.99.0 → 1.07.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (314) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +0 -1
  4. data/Changes +70 -0
  5. data/README.md +1 -1
  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_labels.rb +320 -0
  15. data/examples/chart_data_table.rb +9 -3
  16. data/examples/chart_data_tools.rb +25 -7
  17. data/examples/chart_doughnut.rb +17 -5
  18. data/examples/chart_gauge.rb +73 -0
  19. data/examples/chart_line.rb +90 -12
  20. data/examples/chart_pareto.rb +1 -1
  21. data/examples/chart_pie.rb +9 -3
  22. data/examples/chart_radar.rb +13 -4
  23. data/examples/chart_scatter.rb +5 -2
  24. data/examples/chart_secondary_axis.rb +5 -2
  25. data/examples/chart_stock.rb +1 -1
  26. data/examples/chart_styles.rb +1 -1
  27. data/examples/colors.rb +1 -1
  28. data/examples/data_validate.rb +1 -1
  29. data/examples/date_time.rb +1 -1
  30. data/examples/demo.rb +4 -1
  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 +1 -1
  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 +78 -43
  67. data/lib/write_xlsx/chart.rb +163 -34
  68. data/lib/write_xlsx/chart/area.rb +1 -1
  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/line.rb +16 -2
  73. data/lib/write_xlsx/chart/pie.rb +21 -8
  74. data/lib/write_xlsx/chart/radar.rb +1 -1
  75. data/lib/write_xlsx/chart/scatter.rb +1 -1
  76. data/lib/write_xlsx/chart/series.rb +100 -0
  77. data/lib/write_xlsx/chart/stock.rb +1 -1
  78. data/lib/write_xlsx/chartsheet.rb +5 -5
  79. data/lib/write_xlsx/drawing.rb +86 -30
  80. data/lib/write_xlsx/format.rb +5 -5
  81. data/lib/write_xlsx/package/comments.rb +11 -11
  82. data/lib/write_xlsx/package/relationships.rb +4 -4
  83. data/lib/write_xlsx/package/styles.rb +26 -8
  84. data/lib/write_xlsx/package/table.rb +8 -7
  85. data/lib/write_xlsx/package/vml.rb +20 -19
  86. data/lib/write_xlsx/shape.rb +4 -3
  87. data/lib/write_xlsx/sheets.rb +18 -16
  88. data/lib/write_xlsx/sparkline.rb +1 -1
  89. data/lib/write_xlsx/utility.rb +40 -7
  90. data/lib/write_xlsx/version.rb +1 -1
  91. data/lib/write_xlsx/workbook.rb +69 -44
  92. data/lib/write_xlsx/worksheet.rb +206 -138
  93. data/lib/write_xlsx/worksheet/hyperlink.rb +16 -37
  94. data/test/drawing/test_drawing_chart_01.rb +6 -2
  95. data/test/drawing/test_drawing_image_01.rb +12 -3
  96. data/test/drawing/test_drawing_shape_01.rb +8 -5
  97. data/test/drawing/test_drawing_shape_02.rb +12 -5
  98. data/test/drawing/test_drawing_shape_03.rb +8 -5
  99. data/test/drawing/test_drawing_shape_04.rb +8 -24
  100. data/test/drawing/test_drawing_shape_05.rb +8 -5
  101. data/test/drawing/test_drawing_shape_06.rb +11 -6
  102. data/test/drawing/test_drawing_shape_07.rb +11 -6
  103. data/test/drawing/test_write_a_graphic_frame_locks.rb +1 -1
  104. data/test/drawing/test_write_c_chart.rb +1 -1
  105. data/test/drawing/test_write_c_nv_graphic_frame_pr.rb +1 -1
  106. data/test/drawing/test_write_c_nv_pr.rb +1 -1
  107. data/test/drawing/test_write_col.rb +1 -1
  108. data/test/drawing/test_write_col_off.rb +1 -1
  109. data/test/drawing/test_write_ext.rb +1 -1
  110. data/test/drawing/test_write_pos.rb +1 -1
  111. data/test/drawing/test_write_row.rb +1 -1
  112. data/test/drawing/test_write_row_off.rb +1 -1
  113. data/test/drawing/test_write_xfrm_extension.rb +1 -1
  114. data/test/drawing/test_write_xfrm_offset.rb +1 -1
  115. data/test/perl_output/chart_data_labels.xlsx +0 -0
  116. data/test/perl_output/chart_gauge.xlsx +0 -0
  117. data/test/perl_output/chart_line.xlsx +0 -0
  118. data/test/perl_output/comments2.xlsx +0 -0
  119. data/test/perl_output/tables.xlsx +0 -0
  120. data/test/regression/images/red2.png +0 -0
  121. data/test/regression/test_array_formula04.rb +31 -0
  122. data/test/regression/test_chart_axis26.rb +10 -8
  123. data/test/regression/test_chart_axis27.rb +1 -1
  124. data/test/regression/test_chart_axis28.rb +1 -1
  125. data/test/regression/test_chart_axis29.rb +1 -1
  126. data/test/regression/test_chart_axis33.rb +1 -1
  127. data/test/regression/test_chart_axis44.rb +54 -0
  128. data/test/regression/test_chart_axis45.rb +54 -0
  129. data/test/regression/test_chart_axis46.rb +54 -0
  130. data/test/regression/test_chart_combined10.rb +43 -0
  131. data/test/regression/test_chart_combined11.rb +63 -0
  132. data/test/regression/test_chart_data_labels25.rb +1 -1
  133. data/test/regression/test_chart_data_labels26.rb +44 -0
  134. data/test/regression/test_chart_data_labels27.rb +44 -0
  135. data/test/regression/test_chart_data_labels28.rb +52 -0
  136. data/test/regression/test_chart_data_labels29.rb +43 -0
  137. data/test/regression/test_chart_data_labels30.rb +46 -0
  138. data/test/regression/test_chart_data_labels31.rb +49 -0
  139. data/test/regression/test_chart_data_labels32.rb +54 -0
  140. data/test/regression/test_chart_data_labels33.rb +52 -0
  141. data/test/regression/test_chart_data_labels34.rb +54 -0
  142. data/test/regression/test_chart_data_labels35.rb +46 -0
  143. data/test/regression/test_chart_data_labels36.rb +54 -0
  144. data/test/regression/test_chart_data_labels37.rb +51 -0
  145. data/test/regression/test_chart_data_labels38.rb +54 -0
  146. data/test/regression/test_chart_data_labels39.rb +53 -0
  147. data/test/regression/test_chart_data_labels40.rb +53 -0
  148. data/test/regression/test_chart_data_labels41.rb +54 -0
  149. data/test/regression/test_chart_data_labels42.rb +58 -0
  150. data/test/regression/test_chart_data_labels43.rb +58 -0
  151. data/test/regression/test_chart_data_labels44.rb +56 -0
  152. data/test/regression/test_chart_data_labels45.rb +57 -0
  153. data/test/regression/test_chart_data_labels46.rb +61 -0
  154. data/test/regression/test_chart_data_labels47.rb +61 -0
  155. data/test/regression/test_chart_doughnut07.rb +37 -0
  156. data/test/regression/test_chart_font09.rb +1 -1
  157. data/test/regression/test_chart_line05.rb +43 -0
  158. data/test/regression/test_chart_line06.rb +43 -0
  159. data/test/regression/test_chart_size03.rb +4 -1
  160. data/test/regression/test_comment14.rb +29 -0
  161. data/test/regression/test_comment15.rb +28 -0
  162. data/test/regression/test_comment16.rb +34 -0
  163. data/test/regression/test_header_image15.rb +36 -0
  164. data/test/regression/test_header_image16.rb +42 -0
  165. data/test/regression/test_header_image17.rb +46 -0
  166. data/test/regression/test_header_image18.rb +48 -0
  167. data/test/regression/test_header_image19.rb +36 -0
  168. data/test/regression/test_hyperlink32.rb +27 -0
  169. data/test/regression/test_hyperlink33.rb +28 -0
  170. data/test/regression/test_hyperlink34.rb +33 -0
  171. data/test/regression/test_hyperlink35.rb +39 -0
  172. data/test/regression/test_hyperlink36.rb +34 -0
  173. data/test/regression/test_hyperlink37.rb +33 -0
  174. data/test/regression/test_hyperlink38.rb +27 -0
  175. data/test/regression/test_hyperlink39.rb +27 -0
  176. data/test/regression/test_hyperlink40.rb +27 -0
  177. data/test/regression/test_hyperlink41.rb +27 -0
  178. data/test/regression/test_hyperlink42.rb +27 -0
  179. data/test/regression/test_hyperlink43.rb +27 -0
  180. data/test/regression/test_hyperlink44.rb +27 -0
  181. data/test/regression/test_hyperlink45.rb +27 -0
  182. data/test/regression/test_hyperlink47.rb +27 -0
  183. data/test/regression/test_hyperlink48.rb +31 -0
  184. data/test/regression/test_hyperlink49.rb +29 -0
  185. data/test/regression/test_image06.rb +5 -5
  186. data/test/regression/test_image08.rb +5 -4
  187. data/test/regression/test_image15.rb +4 -2
  188. data/test/regression/test_image28.rb +1 -1
  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_table24.rb +27 -0
  217. data/test/regression/test_table25.rb +27 -0
  218. data/test/regression/xlsx_files/array_formula04.xlsx +0 -0
  219. data/test/regression/xlsx_files/chart_axis26.xlsx +0 -0
  220. data/test/regression/xlsx_files/chart_axis27.xlsx +0 -0
  221. data/test/regression/xlsx_files/chart_axis28.xlsx +0 -0
  222. data/test/regression/xlsx_files/chart_axis29.xlsx +0 -0
  223. data/test/regression/xlsx_files/chart_axis33.xlsx +0 -0
  224. data/test/regression/xlsx_files/chart_axis44.xlsx +0 -0
  225. data/test/regression/xlsx_files/chart_axis45.xlsx +0 -0
  226. data/test/regression/xlsx_files/chart_axis46.xlsx +0 -0
  227. data/test/regression/xlsx_files/chart_combined10.xlsx +0 -0
  228. data/test/regression/xlsx_files/chart_combined11.xlsx +0 -0
  229. data/test/regression/xlsx_files/chart_data_labels25.xlsx +0 -0
  230. data/test/regression/xlsx_files/chart_data_labels26.xlsx +0 -0
  231. data/test/regression/xlsx_files/chart_data_labels27.xlsx +0 -0
  232. data/test/regression/xlsx_files/chart_data_labels28.xlsx +0 -0
  233. data/test/regression/xlsx_files/chart_data_labels29.xlsx +0 -0
  234. data/test/regression/xlsx_files/chart_data_labels30.xlsx +0 -0
  235. data/test/regression/xlsx_files/chart_data_labels31.xlsx +0 -0
  236. data/test/regression/xlsx_files/chart_data_labels32.xlsx +0 -0
  237. data/test/regression/xlsx_files/chart_data_labels33.xlsx +0 -0
  238. data/test/regression/xlsx_files/chart_data_labels34.xlsx +0 -0
  239. data/test/regression/xlsx_files/chart_data_labels35.xlsx +0 -0
  240. data/test/regression/xlsx_files/chart_data_labels36.xlsx +0 -0
  241. data/test/regression/xlsx_files/chart_data_labels37.xlsx +0 -0
  242. data/test/regression/xlsx_files/chart_data_labels38.xlsx +0 -0
  243. data/test/regression/xlsx_files/chart_data_labels39.xlsx +0 -0
  244. data/test/regression/xlsx_files/chart_data_labels40.xlsx +0 -0
  245. data/test/regression/xlsx_files/chart_data_labels41.xlsx +0 -0
  246. data/test/regression/xlsx_files/chart_data_labels42.xlsx +0 -0
  247. data/test/regression/xlsx_files/chart_data_labels43.xlsx +0 -0
  248. data/test/regression/xlsx_files/chart_data_labels44.xlsx +0 -0
  249. data/test/regression/xlsx_files/chart_data_labels45.xlsx +0 -0
  250. data/test/regression/xlsx_files/chart_data_labels46.xlsx +0 -0
  251. data/test/regression/xlsx_files/chart_data_labels47.xlsx +0 -0
  252. data/test/regression/xlsx_files/chart_doughnut07.xlsx +0 -0
  253. data/test/regression/xlsx_files/chart_font09.xlsx +0 -0
  254. data/test/regression/xlsx_files/chart_line05.xlsx +0 -0
  255. data/test/regression/xlsx_files/chart_line06.xlsx +0 -0
  256. data/test/regression/xlsx_files/comment14.xlsx +0 -0
  257. data/test/regression/xlsx_files/comment15.xlsx +0 -0
  258. data/test/regression/xlsx_files/comment16.xlsx +0 -0
  259. data/test/regression/xlsx_files/header_image15.xlsx +0 -0
  260. data/test/regression/xlsx_files/header_image16.xlsx +0 -0
  261. data/test/regression/xlsx_files/header_image17.xlsx +0 -0
  262. data/test/regression/xlsx_files/header_image18.xlsx +0 -0
  263. data/test/regression/xlsx_files/header_image19.xlsx +0 -0
  264. data/test/regression/xlsx_files/hyperlink32.xlsx +0 -0
  265. data/test/regression/xlsx_files/hyperlink33.xlsx +0 -0
  266. data/test/regression/xlsx_files/hyperlink34.xlsx +0 -0
  267. data/test/regression/xlsx_files/hyperlink35.xlsx +0 -0
  268. data/test/regression/xlsx_files/hyperlink36.xlsx +0 -0
  269. data/test/regression/xlsx_files/hyperlink37.xlsx +0 -0
  270. data/test/regression/xlsx_files/hyperlink38.xlsx +0 -0
  271. data/test/regression/xlsx_files/hyperlink39.xlsx +0 -0
  272. data/test/regression/xlsx_files/hyperlink40.xlsx +0 -0
  273. data/test/regression/xlsx_files/hyperlink41.xlsx +0 -0
  274. data/test/regression/xlsx_files/hyperlink42.xlsx +0 -0
  275. data/test/regression/xlsx_files/hyperlink43.xlsx +0 -0
  276. data/test/regression/xlsx_files/hyperlink44.xlsx +0 -0
  277. data/test/regression/xlsx_files/hyperlink45.xlsx +0 -0
  278. data/test/regression/xlsx_files/hyperlink46.xlsx +0 -0
  279. data/test/regression/xlsx_files/hyperlink47.xlsx +0 -0
  280. data/test/regression/xlsx_files/hyperlink48.xlsx +0 -0
  281. data/test/regression/xlsx_files/hyperlink49.xlsx +0 -0
  282. data/test/regression/xlsx_files/image06.xlsx +0 -0
  283. data/test/regression/xlsx_files/image44.xlsx +0 -0
  284. data/test/regression/xlsx_files/image45.xlsx +0 -0
  285. data/test/regression/xlsx_files/image46.xlsx +0 -0
  286. data/test/regression/xlsx_files/image47.xlsx +0 -0
  287. data/test/regression/xlsx_files/image48.xlsx +0 -0
  288. data/test/regression/xlsx_files/image49.xlsx +0 -0
  289. data/test/regression/xlsx_files/image50.xlsx +0 -0
  290. data/test/regression/xlsx_files/image51.xlsx +0 -0
  291. data/test/regression/xlsx_files/object_position01.xlsx +0 -0
  292. data/test/regression/xlsx_files/object_position02.xlsx +0 -0
  293. data/test/regression/xlsx_files/object_position03.xlsx +0 -0
  294. data/test/regression/xlsx_files/object_position04.xlsx +0 -0
  295. data/test/regression/xlsx_files/object_position06.xlsx +0 -0
  296. data/test/regression/xlsx_files/object_position07.xlsx +0 -0
  297. data/test/regression/xlsx_files/object_position08.xlsx +0 -0
  298. data/test/regression/xlsx_files/object_position09.xlsx +0 -0
  299. data/test/regression/xlsx_files/object_position10.xlsx +0 -0
  300. data/test/regression/xlsx_files/object_position12.xlsx +0 -0
  301. data/test/regression/xlsx_files/object_position13.xlsx +0 -0
  302. data/test/regression/xlsx_files/object_position14.xlsx +0 -0
  303. data/test/regression/xlsx_files/object_position15.xlsx +0 -0
  304. data/test/regression/xlsx_files/object_position16.xlsx +0 -0
  305. data/test/regression/xlsx_files/object_position17.xlsx +0 -0
  306. data/test/regression/xlsx_files/object_position18.xlsx +0 -0
  307. data/test/regression/xlsx_files/object_position19.xlsx +0 -0
  308. data/test/regression/xlsx_files/object_position20.xlsx +0 -0
  309. data/test/regression/xlsx_files/table24.xlsx +0 -0
  310. data/test/regression/xlsx_files/table25.xlsx +0 -0
  311. data/test/test_example_match.rb +1268 -780
  312. data/test/workbook/test_check_sheetname.rb +51 -0
  313. data/write_xlsx.gemspec +1 -0
  314. metadata +367 -5
@@ -1 +1 @@
1
- WriteXLSX_VERSION = "0.99.0"
1
+ WriteXLSX_VERSION = "1.07.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
 
@@ -130,6 +131,13 @@ def initialize(file, *option_params)
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
 
@@ -1108,7 +1116,8 @@ def style_properties
1108
1116
  @border_count,
1109
1117
  @fill_count,
1110
1118
  @custom_colors,
1111
- @dxf_formats
1119
+ @dxf_formats,
1120
+ @has_comments
1112
1121
  ]
1113
1122
  end
1114
1123
 
@@ -1704,11 +1713,14 @@ def prepare_vml_objects #:nodoc:
1704
1713
  if sheet.has_comments?
1705
1714
  comment_files += 1
1706
1715
  comment_id += 1
1716
+ @has_comments = true
1707
1717
  end
1708
1718
  vml_drawing_id += 1
1709
1719
 
1710
- sheet.prepare_vml_objects(vml_data_id, vml_shape_id,
1711
- vml_drawing_id, comment_id)
1720
+ sheet.prepare_vml_objects(
1721
+ vml_data_id, vml_shape_id,
1722
+ vml_drawing_id, comment_id
1723
+ )
1712
1724
 
1713
1725
  # Each VML file should start with a shape id incremented by 1024.
1714
1726
  vml_data_id += 1 * ( 1 + sheet.num_comments_block )
@@ -1731,8 +1743,6 @@ def prepare_vml_objects #:nodoc:
1731
1743
  end
1732
1744
  end
1733
1745
 
1734
- add_font_format_for_cell_comments if num_comment_files > 0
1735
-
1736
1746
  # Set the workbook vba_codename if one of the sheets has a button and
1737
1747
  # the workbook has a vbaProject binary.
1738
1748
  if has_button && @vba_project && !@vba_codename
@@ -1752,19 +1762,6 @@ def prepare_tables
1752
1762
  end
1753
1763
  end
1754
1764
 
1755
- def add_font_format_for_cell_comments
1756
- format = Format.new(
1757
- @formats,
1758
- :font => 'Tahoma',
1759
- :size => 8,
1760
- :color_indexed => 81,
1761
- :font_only => 1
1762
- )
1763
-
1764
- format.get_xf_index
1765
- @formats.formats << format
1766
- end
1767
-
1768
1765
  #
1769
1766
  # Add "cached" data to charts to provide the numCache and strCache data for
1770
1767
  # series and title/axis ranges.
@@ -1915,16 +1912,19 @@ def extract_named_ranges(defined_names) #:nodoc:
1915
1912
  # Iterate through the worksheets and set up any chart or image drawings.
1916
1913
  #
1917
1914
  def prepare_drawings #:nodoc:
1918
- chart_ref_id = 0
1919
- image_ref_id = 0
1920
- 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 = {}
1921
1921
  @worksheets.each do |sheet|
1922
1922
  chart_count = sheet.charts.size
1923
1923
  image_count = sheet.images.size
1924
1924
  shape_count = sheet.shapes.size
1925
1925
  header_image_count = sheet.header_images.size
1926
1926
  footer_image_count = sheet.footer_images.size
1927
- has_drawing = false
1927
+ has_drawings = false
1928
1928
 
1929
1929
  # Check that some image or drawing needs to be processed.
1930
1930
  next if chart_count + image_count + shape_count + header_image_count + footer_image_count == 0
@@ -1932,7 +1932,24 @@ def prepare_drawings #:nodoc:
1932
1932
  # Don't increase the drawing_id header/footer images.
1933
1933
  if chart_count + image_count + shape_count > 0
1934
1934
  drawing_id += 1
1935
- 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
+ )
1936
1953
  end
1937
1954
 
1938
1955
  # Prepare the worksheet charts.
@@ -1941,13 +1958,6 @@ def prepare_drawings #:nodoc:
1941
1958
  sheet.prepare_chart(index, chart_ref_id, drawing_id)
1942
1959
  end
1943
1960
 
1944
- # Prepare the worksheet images.
1945
- sheet.images.each_with_index do |image, index|
1946
- type, width, height, name, x_dpi, y_dpi = get_image_properties(image[2])
1947
- image_ref_id += 1
1948
- sheet.prepare_image(index, image_ref_id, drawing_id, width, height, name, type, x_dpi, y_dpi)
1949
- end
1950
-
1951
1961
  # Prepare the worksheet shapes.
1952
1962
  sheet.shapes.each_with_index do |shape, index|
1953
1963
  sheet.prepare_shape(index, drawing_id)
@@ -1958,13 +1968,21 @@ def prepare_drawings #:nodoc:
1958
1968
  filename = sheet.header_images[index][0]
1959
1969
  position = sheet.header_images[index][1]
1960
1970
 
1961
- type, width, height, name, x_dpi, y_dpi =
1971
+ type, width, height, name, x_dpi, y_dpi, md5 =
1962
1972
  get_image_properties(filename)
1963
1973
 
1964
- 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
1965
1981
 
1966
- sheet.prepare_header_image(image_ref_id, width, height,
1967
- 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
+ )
1968
1986
  end
1969
1987
 
1970
1988
  # Prepare the footer images.
@@ -1972,18 +1990,26 @@ def prepare_drawings #:nodoc:
1972
1990
  filename = sheet.footer_images[index][0]
1973
1991
  position = sheet.footer_images[index][1]
1974
1992
 
1975
- type, width, height, name, x_dpi, y_dpi =
1993
+ type, width, height, name, x_dpi, y_dpi, md5 =
1976
1994
  get_image_properties(filename)
1977
1995
 
1978
- 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
1979
2003
 
1980
- sheet.prepare_header_image(image_ref_id, width, height,
1981
- 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
+ )
1982
2008
  end
1983
2009
 
1984
- if has_drawing
1985
- drawing = sheet.drawing
1986
- @drawings << drawing
2010
+ if has_drawings
2011
+ drawings = sheet.drawings
2012
+ @drawings << drawings
1987
2013
  end
1988
2014
  end
1989
2015
 
@@ -2007,6 +2033,7 @@ def get_image_properties(filename)
2007
2033
 
2008
2034
  # Open the image file and import the data.
2009
2035
  data = File.binread(filename)
2036
+ md5 = Digest::MD5.hexdigest(data)
2010
2037
  if data.unpack('x A3')[0] == 'PNG'
2011
2038
  # Test for PNGs.
2012
2039
  type, width, height, x_dpi, y_dpi = process_png(data)
@@ -2024,13 +2051,11 @@ def get_image_properties(filename)
2024
2051
  raise "Unsupported image format for file: #{filename}\n"
2025
2052
  end
2026
2053
 
2027
- @images << [filename, type]
2028
-
2029
2054
  # Set a default dpi for images with 0 dpi.
2030
2055
  x_dpi = 96 if x_dpi == 0
2031
2056
  y_dpi = 96 if y_dpi == 0
2032
2057
 
2033
- [type, width, height, File.basename(filename), x_dpi, y_dpi]
2058
+ [type, width, height, File.basename(filename), x_dpi, y_dpi, md5]
2034
2059
  end
2035
2060
 
2036
2061
  #
@@ -285,7 +285,7 @@ class Worksheet
285
285
  PADDING = 5 # :nodoc:
286
286
 
287
287
  attr_reader :index # :nodoc:
288
- attr_reader :charts, :images, :tables, :shapes, :drawing # :nodoc:
288
+ attr_reader :charts, :images, :tables, :shapes, :drawings # :nodoc:
289
289
  attr_reader :header_images, :footer_images # :nodoc:
290
290
  attr_reader :vml_drawing_links # :nodoc:
291
291
  attr_reader :vml_data_id # :nodoc:
@@ -309,6 +309,7 @@ def initialize(workbook, index, name) #:nodoc:
309
309
  @excel_version = 2007
310
310
  @palette = workbook.palette
311
311
  @default_url_format = workbook.default_url_format
312
+ @max_url_length = workbook.max_url_length
312
313
 
313
314
  @page_setup = PageSetup.new
314
315
 
@@ -356,6 +357,10 @@ def initialize(workbook, index, name) #:nodoc:
356
357
  @sparklines = []
357
358
  @shapes = []
358
359
  @shape_hash = {}
360
+ @drawing_rels = {}
361
+ @drawing_rels_id = 0
362
+ @vml_drawing_rels = {}
363
+ @vml_drawing_rels_id = 0
359
364
  @header_images = []
360
365
  @footer_images = []
361
366
 
@@ -365,6 +370,7 @@ def initialize(workbook, index, name) #:nodoc:
365
370
  @original_row_height = 15
366
371
  @default_row_height = 15
367
372
  @default_row_pixels = 20
373
+ @default_col_width = 8.43
368
374
  @default_col_pixels = 64
369
375
  @default_row_rezoed = 0
370
376
 
@@ -768,10 +774,10 @@ def set_column(*args)
768
774
 
769
775
  # Store the col sizes for use when calculating image vertices taking
770
776
  # hidden columns into account. Also store the column formats.
771
- width = 0 if ptrue?(hidden) # Set width to zero if hidden
777
+ width = @default_col_width unless width
772
778
 
773
779
  (firstcol .. lastcol).each do |col|
774
- @col_sizes[col] = width
780
+ @col_sizes[col] = [width, hidden]
775
781
  @col_formats[col] = format if format
776
782
  end
777
783
  end
@@ -2169,7 +2175,7 @@ def write_comment(*args)
2169
2175
  @has_vml = true
2170
2176
 
2171
2177
  # Process the properties of the cell comment.
2172
- @comments.add(Package::Comment.new(@workbook, self, row, col, string, options))
2178
+ @comments.add(@workbook, self, row, col, string, options)
2173
2179
  end
2174
2180
 
2175
2181
  #
@@ -2508,7 +2514,9 @@ def write_array_formula(*args)
2508
2514
  col1, col2 = col2, col1 if col1 > col2
2509
2515
 
2510
2516
  # Check that row and col are valid and store max and min values
2517
+ check_dimensions(row1, col1)
2511
2518
  check_dimensions(row2, col2)
2519
+ store_row_col_max_min_values(row1, col1)
2512
2520
  store_row_col_max_min_values(row2, col2)
2513
2521
 
2514
2522
  # Define array range
@@ -2781,7 +2789,7 @@ def write_url(*args)
2781
2789
  store_hyperlink(row, col, hyperlink)
2782
2790
 
2783
2791
  if hyperlinks_count > 65_530
2784
- raise "URL '#{url}' added but number of URLS is over Excel's limit of 65,530 URLS per worksheet."
2792
+ raise "URL '#{url}' added but URL exceeds Excel's limit of 65,530 URLs per worksheet."
2785
2793
  end
2786
2794
 
2787
2795
  # Add the default URL format.
@@ -2990,13 +2998,25 @@ def write_date_time(*args)
2990
2998
  #
2991
2999
  def insert_chart(*args)
2992
3000
  # Check for a cell reference in A1 notation and substitute row and column.
2993
- row, col, chart, x_offset, y_offset, x_scale, y_scale = row_col_notation(args)
3001
+ row, col, chart, *options = row_col_notation(args)
2994
3002
  raise WriteXLSXInsufficientArgumentError if [row, col, chart].include?(nil)
2995
3003
 
3004
+ if options.first.class == Hash
3005
+ params = options.first
3006
+ x_offset = params[:x_offset]
3007
+ y_offset = params[:y_offset]
3008
+ x_scale = params[:x_scale]
3009
+ y_scale = params[:y_scale]
3010
+ anchor = params[:object_position]
3011
+
3012
+ else
3013
+ x_offset, y_offset, x_scale, y_scale, anchor = options
3014
+ end
2996
3015
  x_offset ||= 0
2997
3016
  y_offset ||= 0
2998
3017
  x_scale ||= 1
2999
3018
  y_scale ||= 1
3019
+ anchor ||= 1
3000
3020
 
3001
3021
  raise "Not a Chart object in insert_chart()" unless chart.is_a?(Chart) || chart.is_a?(Chartsheet)
3002
3022
  raise "Not a embedded style Chart object in insert_chart()" if chart.respond_to?(:embedded) && chart.embedded == 0
@@ -3014,61 +3034,41 @@ def insert_chart(*args)
3014
3034
  x_offset = chart.x_offset if ptrue?(chart.x_offset)
3015
3035
  y_offset = chart.y_offset if ptrue?(chart.y_offset)
3016
3036
 
3017
- @charts << [row, col, chart, x_offset, y_offset, x_scale, y_scale]
3037
+ @charts << [row, col, chart, x_offset, y_offset, x_scale, y_scale, anchor]
3018
3038
  end
3019
3039
 
3020
3040
  #
3021
3041
  # :call-seq:
3022
- # insert_image(row, column, filename, x=0, y=0, x_scale=1, y_scale=1)
3023
- #
3024
- # Partially supported. Currently only works for 96 dpi images.
3025
- #
3026
- # This method can be used to insert a image into a worksheet. The image
3027
- # can be in PNG, JPEG or BMP format. The x, y, x_scale and y_scale
3028
- # parameters are optional.
3029
- #
3030
- # worksheet1.insert_image('A1', 'ruby.bmp')
3031
- # worksheet2.insert_image('A1', '../images/ruby.bmp')
3032
- # worksheet3.insert_image('A1', '.c:\images\ruby.bmp')
3033
- #
3034
- # The parameters +x+ and +y+ can be used to specify an offset from the top
3035
- # left hand corner of the cell specified by +row+ and +column+. The offset
3036
- # values are in pixels.
3037
- #
3038
- # worksheet1.insert_image('A1', 'ruby.bmp', 32, 10)
3039
- #
3040
- # The offsets can be greater than the width or height of the underlying
3041
- # cell. This can be occasionally useful if you wish to align two or more
3042
- # images relative to the same cell.
3043
- #
3044
- # The parameters +x_scale+ and +y_scale+ can be used to scale the inserted
3045
- # image horizontally and vertically:
3046
- #
3047
- # # Scale the inserted image: width x 2.0, height x 0.8
3048
- # worksheet.insert_image('A1', 'perl.bmp', 0, 0, 2, 0.8)
3049
- #
3050
- # Note: you must call set_row() or set_column() before insert_image()
3051
- # if you wish to change the default dimensions of any of the rows or
3052
- # columns that the image occupies. The height of a row can also change
3053
- # if you use a font that is larger than the default. This in turn will
3054
- # affect the scaling of your image. To avoid this you should explicitly
3055
- # set the height of the row using set_row() if it contains a font size
3056
- # that will change the row height.
3057
- #
3058
- # BMP images must be 24 bit, true colour, bitmaps. In general it is
3059
- # best to avoid BMP images since they aren't compressed.
3042
+ # insert_image(row, column, filename, options)
3060
3043
  #
3061
3044
  def insert_image(*args)
3062
3045
  # Check for a cell reference in A1 notation and substitute row and column.
3063
- row, col, image, x_offset, y_offset, x_scale, y_scale = row_col_notation(args)
3046
+ row, col, image, *options = row_col_notation(args)
3064
3047
  raise WriteXLSXInsufficientArgumentError if [row, col, image].include?(nil)
3065
3048
 
3049
+ if options.first.class == Hash
3050
+ # Newer hash bashed options
3051
+ params = options.first
3052
+ x_offset = params[:x_offset]
3053
+ y_offset = params[:y_offset]
3054
+ x_scale = params[:x_scale]
3055
+ y_scale = params[:y_scale]
3056
+ anchor = params[:object_position]
3057
+ url = params[:url]
3058
+ tip = params[:tip]
3059
+ else
3060
+ x_offset, y_offset, x_scale, y_scale, anchor = options
3061
+ end
3066
3062
  x_offset ||= 0
3067
3063
  y_offset ||= 0
3068
3064
  x_scale ||= 1
3069
3065
  y_scale ||= 1
3066
+ anchor ||= 2
3070
3067
 
3071
- @images << [row, col, image, x_offset, y_offset, x_scale, y_scale]
3068
+ @images << [
3069
+ row, col, image, x_offset, y_offset,
3070
+ x_scale, y_scale, url, tip, anchor
3071
+ ]
3072
3072
  end
3073
3073
 
3074
3074
  #
@@ -3306,10 +3306,8 @@ def set_row(*args)
3306
3306
  # Store the row change to allow optimisations.
3307
3307
  @row_size_changed = true
3308
3308
 
3309
- height = 0 if ptrue?(hidden)
3310
-
3311
3309
  # Store the row sizes for use when calculating image vertices.
3312
- @row_sizes[row] = height
3310
+ @row_sizes[row] = [height, hidden]
3313
3311
  end
3314
3312
 
3315
3313
  #
@@ -3367,9 +3365,11 @@ def merge_range(*args)
3367
3365
  row_first, row_last = row_last, row_first if row_first > row_last
3368
3366
  col_first, col_last = col_last, col_first if col_first > col_last
3369
3367
 
3370
- # Check that column number is valid and store the max value
3371
- check_dimensions(row_last, col_last)
3372
- store_row_col_max_min_values(row_last, col_last)
3368
+ # Check that the data range is valid and store the max and min values.
3369
+ check_dimensions(row_first, col_first)
3370
+ check_dimensions(row_last, col_last)
3371
+ store_row_col_max_min_values(row_first, col_first)
3372
+ store_row_col_max_min_values(row_last, col_last)
3373
3373
 
3374
3374
  # Store the merge range.
3375
3375
  @merge << [row_first, col_first, row_last, col_last]
@@ -3432,9 +3432,11 @@ def merge_range_type(type, *args)
3432
3432
  row_first, row_last = row_last, row_first if row_first > row_last
3433
3433
  col_first, col_last = col_last, col_first if col_first > col_last
3434
3434
 
3435
- # Check that column number is valid and store the max value
3436
- check_dimensions(row_last, col_last)
3437
- store_row_col_max_min_values(row_last, col_last)
3435
+ # Check that the data range is valid and store the max and min values.
3436
+ check_dimensions(row_first, col_first)
3437
+ check_dimensions(row_last, col_last)
3438
+ store_row_col_max_min_values(row_first, col_first)
3439
+ store_row_col_max_min_values(row_last, col_last)
3438
3440
 
3439
3441
  # Store the merge range.
3440
3442
  @merge << [row_first, col_first, row_last, col_last]
@@ -5724,7 +5726,7 @@ def set_external_comment_links(comment_id) # :nodoc:
5724
5726
  def prepare_chart(index, chart_id, drawing_id) # :nodoc:
5725
5727
  drawing_type = 1
5726
5728
 
5727
- row, col, chart, x_offset, y_offset, x_scale, y_scale = @charts[index]
5729
+ row, col, chart, x_offset, y_offset, x_scale, y_scale, anchor = @charts[index]
5728
5730
  chart.id = chart_id - 1
5729
5731
  x_scale ||= 0
5730
5732
  y_scale ||= 0
@@ -5736,22 +5738,21 @@ def prepare_chart(index, chart_id, drawing_id) # :nodoc:
5736
5738
  width = (0.5 + (width * x_scale)).to_i
5737
5739
  height = (0.5 + (height * y_scale)).to_i
5738
5740
 
5739
- dimensions = position_object_emus(col, row, x_offset, y_offset, width, height)
5741
+ dimensions = position_object_emus(col, row, x_offset, y_offset, width, height, anchor)
5740
5742
 
5741
5743
  # Set the chart name for the embedded object if it has been specified.
5742
5744
  name = chart.name
5743
5745
 
5744
5746
  # Create a Drawing object to use with worksheet unless one already exists.
5745
- if !drawing?
5746
- drawing = Drawing.new
5747
- drawing.add_drawing_object(drawing_type, dimensions, 0, 0, name)
5748
- drawing.embedded = 1
5749
-
5750
- @drawing = drawing
5747
+ drawing = Drawing.new(drawing_type, dimensions, 0, 0, name, nil, anchor, drawing_rel_index, 0, nil)
5748
+ if !drawings?
5749
+ @drawings = Drawings.new
5750
+ @drawings.add_drawing_object(drawing)
5751
+ @drawings.embedded = 1
5751
5752
 
5752
5753
  @external_drawing_links << ['/drawing', "../drawings/drawing#{drawing_id}.xml" ]
5753
5754
  else
5754
- @drawing.add_drawing_object(drawing_type, dimensions, 0, 0, name)
5755
+ @drawings.add_drawing_object(drawing)
5755
5756
  end
5756
5757
  @drawing_links << ['/chart', "../charts/chart#{chart_id}.xml"]
5757
5758
  end
@@ -5822,6 +5823,10 @@ def get_range_data(row_start, col_start, row_end, col_end) # :nodoc:
5822
5823
  # The values of col_start and row_start are passed in from the calling
5823
5824
  # function. The values of col_end and row_end are calculated by subtracting
5824
5825
  # the width and height of the object from the width and height of the
5826
+ # The anchor/object position defines how images are scaled for hidden rows and
5827
+ # columns. For option 1 "Move and size with cells" the size of the hidden
5828
+ # row/column is subtracted from the image.
5829
+ #
5825
5830
  # underlying cells.
5826
5831
  #
5827
5832
  # col_start # Col containing upper left corner of object.
@@ -5834,7 +5839,7 @@ def get_range_data(row_start, col_start, row_end, col_end) # :nodoc:
5834
5839
  # y2 # Distance to bottom of object.
5835
5840
  # width # Width of object frame.
5836
5841
  # height # Height of object frame.
5837
- def position_object_pixels(col_start, row_start, x1, y1, width, height) #:nodoc:
5842
+ def position_object_pixels(col_start, row_start, x1, y1, width, height, anchor = nil) #:nodoc:
5838
5843
  # Adjust start column for negative offsets.
5839
5844
  while x1 < 0 && col_start > 0
5840
5845
  x1 += size_col(col_start - 1)
@@ -5853,7 +5858,7 @@ def position_object_pixels(col_start, row_start, x1, y1, width, height) #:nodoc:
5853
5858
 
5854
5859
  # Calculate the absolute x offset of the top-left vertex.
5855
5860
  if @col_size_changed
5856
- x_abs = (0 .. col_start-1).inject(0) {|sum, col| sum += size_col(col)}
5861
+ x_abs = (0 .. col_start-1).inject(0) {|sum, col| sum += size_col(col, anchor)}
5857
5862
  else
5858
5863
  # Optimisation for when the column widths haven't changed.
5859
5864
  x_abs = @default_col_pixels * col_start
@@ -5863,7 +5868,7 @@ def position_object_pixels(col_start, row_start, x1, y1, width, height) #:nodoc:
5863
5868
  # Calculate the absolute y offset of the top-left vertex.
5864
5869
  # Store the column change to allow optimisations.
5865
5870
  if @row_size_changed
5866
- y_abs = (0 .. row_start-1).inject(0) {|sum, row| sum += size_row(row)}
5871
+ y_abs = (0 .. row_start-1).inject(0) {|sum, row| sum += size_row(row, anchor)}
5867
5872
  else
5868
5873
  # Optimisation for when the row heights haven't changed.
5869
5874
  y_abs = @default_row_pixels * row_start
@@ -5871,23 +5876,36 @@ def position_object_pixels(col_start, row_start, x1, y1, width, height) #:nodoc:
5871
5876
  y_abs += y1
5872
5877
 
5873
5878
  # Adjust start column for offsets that are greater than the col width.
5874
- x1, col_start = adjust_column_offset(x1, col_start)
5879
+ while x1 >= size_col(col_start, anchor)
5880
+ x1 -= size_col(col_start)
5881
+ col_start += 1
5882
+ end
5875
5883
 
5876
5884
  # Adjust start row for offsets that are greater than the row height.
5877
- y1, row_start = adjust_row_offset(y1, row_start)
5885
+ while y1 >= size_row(row_start, anchor)
5886
+ y1 -= size_row(row_start)
5887
+ row_start += 1
5888
+ end
5878
5889
 
5879
5890
  # Initialise end cell to the same as the start cell.
5880
5891
  col_end = col_start
5881
5892
  row_end = row_start
5882
5893
 
5883
- width += x1
5884
- height += y1
5894
+ # Only offset the image in the cell if the row/col isn't hidden.
5895
+ width += x1 if size_col(col_start, anchor) > 0
5896
+ height += y1 if size_row(row_start, anchor) > 0
5885
5897
 
5886
5898
  # Subtract the underlying cell widths to find the end cell of the object.
5887
- width, col_end = adjust_column_offset(width, col_end)
5899
+ while width >= size_col(col_end, anchor)
5900
+ width -= size_col(col_end, anchor)
5901
+ col_end += 1
5902
+ end
5888
5903
 
5889
5904
  # Subtract the underlying cell heights to find the end cell of the object.
5890
- height, row_end = adjust_row_offset(height, row_end)
5905
+ while height >= size_row(row_end, anchor)
5906
+ height -= size_row(row_end, anchor)
5907
+ row_end += 1
5908
+ end
5891
5909
 
5892
5910
  # The end vertices are whatever is left from the width and height.
5893
5911
  x2 = width
@@ -6072,6 +6090,33 @@ def write_sparkline_groups
6072
6090
 
6073
6091
  private
6074
6092
 
6093
+ #
6094
+ # Get the index used to address a drawing rel link.
6095
+ #
6096
+ def drawing_rel_index(target = nil)
6097
+ if !target
6098
+ # Undefined values for drawings like charts will always be unique.
6099
+ @drawing_rels_id += 1
6100
+ elsif ptrue?(@drawing_rels[target])
6101
+ @drawing_rels[target]
6102
+ else
6103
+ @drawing_rels_id += 1
6104
+ @drawing_rels[target] = @drawing_rels_id
6105
+ end
6106
+ end
6107
+
6108
+ #
6109
+ # Get the index used to address a vml_drawing rel link.
6110
+ #
6111
+ def get_vml_drawing_rel_index(target)
6112
+ if @vml_drawing_rels[target]
6113
+ @vml_drawing_rels[target]
6114
+ else
6115
+ @vml_drawing_rels_id += 1
6116
+ @vml_drawing_rels[target] = @vml_drawing_rels_id
6117
+ end
6118
+ end
6119
+
6075
6120
  def hyperlinks_count
6076
6121
  @hyperlinks.keys.inject(0) { |s, n| s += @hyperlinks[n].keys.size }
6077
6122
  end
@@ -6347,32 +6392,16 @@ def sort_pagebreaks(*args) #:nodoc:
6347
6392
  end
6348
6393
  end
6349
6394
 
6350
- def adjust_column_offset(x, column)
6351
- while x >= size_col(column)
6352
- x -= size_col(column)
6353
- column += 1
6354
- end
6355
- [x, column]
6356
- end
6357
-
6358
- def adjust_row_offset(y, row)
6359
- while y >= size_row(row)
6360
- y -= size_row(row)
6361
- row += 1
6362
- end
6363
- [y, row]
6364
- end
6365
-
6366
6395
  #
6367
6396
  # Calculate the vertices that define the position of a graphical object within
6368
6397
  # the worksheet in EMUs.
6369
6398
  #
6370
6399
  # The vertices are expressed as English Metric Units (EMUs). There are 12,700
6371
- # EMUs per point. Therefore, 12,700 * 3 /4 = 9,525 EMUs per pixel.
6400
+ # EMUs per point. Therefore, 12,700 * 3 /4 = 9,525 EMUs per el.
6372
6401
  #
6373
- def position_object_emus(col_start, row_start, x1, y1, width, height, x_dpi = 96, y_dpi = 96) #:nodoc:
6402
+ def position_object_emus(col_start, row_start, x1, y1, width, height, anchor = nil) #:nodoc:
6374
6403
  col_start, row_start, x1, y1, col_end, row_end, x2, y2, x_abs, y_abs =
6375
- position_object_pixels(col_start, row_start, x1, y1, width, height)
6404
+ position_object_pixels(col_start, row_start, x1, y1, width, height, anchor)
6376
6405
 
6377
6406
  # Convert the pixel values to EMUs. See above.
6378
6407
  x1 = (0.5 + 9_525 * x1).to_i
@@ -6388,15 +6417,16 @@ def position_object_emus(col_start, row_start, x1, y1, width, height, x_dpi = 96
6388
6417
  #
6389
6418
  # Convert the width of a cell from user's units to pixels. Excel rounds the
6390
6419
  # column width to the nearest pixel. If the width hasn't been set by the user
6391
- # we use the default value. If the column is hidden it has a value of zero.
6420
+ # we use the default value. A hidden column is treated as having a width of
6421
+ # zero unless it has the special "object_position" of 4 (size with cells).
6392
6422
  #
6393
- def size_col(col) #:nodoc:
6423
+ def size_col(col, anchor = 0) #:nodoc:
6394
6424
  # Look up the cell value to see if it has been changed.
6395
6425
  if @col_sizes[col]
6396
- width = @col_sizes[col]
6426
+ width, hidden = @col_sizes[col]
6397
6427
 
6398
6428
  # Convert to pixels.
6399
- if width == 0
6429
+ if hidden == 1 && anchor != 4
6400
6430
  pixels = 0
6401
6431
  elsif width < 1
6402
6432
  pixels = (width * (MAX_DIGIT_WIDTH + PADDING) + 0.5).to_i
@@ -6411,15 +6441,16 @@ def size_col(col) #:nodoc:
6411
6441
 
6412
6442
  #
6413
6443
  # Convert the height of a cell from user's units to pixels. If the height
6414
- # hasn't been set by the user we use the default value. If the row is hidden
6415
- # it has a value of zero.
6444
+ # hasn't been set by the user we use the default value. A hidden row is
6445
+ # treated as having a height of zero unless it has the special
6446
+ # "object_position" of 4 (size with cells).
6416
6447
  #
6417
- def size_row(row) #:nodoc:
6448
+ def size_row(row, anchor = 0) #:nodoc:
6418
6449
  # Look up the cell value to see if it has been changed
6419
6450
  if @row_sizes[row]
6420
- height = @row_sizes[row]
6451
+ height, hidden = @row_sizes[row]
6421
6452
 
6422
- if height == 0
6453
+ if hidden == 1 && anchor != 4
6423
6454
  pixels = 0
6424
6455
  else
6425
6456
  pixels = (4 / 3.0 * height).to_i
@@ -6433,13 +6464,13 @@ def size_row(row) #:nodoc:
6433
6464
  #
6434
6465
  # Set up image/drawings.
6435
6466
  #
6436
- def prepare_image(index, image_id, drawing_id, width, height, name, image_type, x_dpi = 96, y_dpi = 96) #:nodoc:
6467
+ def prepare_image(index, image_id, drawing_id, width, height, name, image_type, x_dpi = 96, y_dpi = 96, md5 = nil) #:nodoc:
6437
6468
  x_dpi ||= 96
6438
6469
  y_dpi ||= 96
6439
6470
  drawing_type = 2
6440
- drawing
6441
6471
 
6442
- row, col, image, x_offset, y_offset, x_scale, y_scale = @images[index]
6472
+ row, col, image, x_offset, y_offset,
6473
+ x_scale, y_scale, url, tip, anchor = @images[index]
6443
6474
 
6444
6475
  width *= x_scale
6445
6476
  height *= y_scale
@@ -6447,36 +6478,72 @@ def prepare_image(index, image_id, drawing_id, width, height, name, image_type,
6447
6478
  width *= 96.0 / x_dpi
6448
6479
  height *= 96.0 / y_dpi
6449
6480
 
6450
- dimensions = position_object_emus(col, row, x_offset, y_offset, width, height)
6481
+ dimensions = position_object_emus(col, row, x_offset, y_offset, width, height, anchor)
6451
6482
 
6452
6483
  # Convert from pixels to emus.
6453
6484
  width = (0.5 + (width * 9_525)).to_i
6454
6485
  height = (0.5 + (height * 9_525)).to_i
6455
6486
 
6456
6487
  # Create a Drawing object to use with worksheet unless one already exists.
6457
- if !drawing?
6458
- drawing = Drawing.new
6459
- drawing.embedded = 1
6488
+ drawing = Drawing.new(drawing_type, dimensions, width, height, name, nil, anchor, 0, 0, tip)
6489
+ if !drawings?
6490
+ drawings = Drawings.new
6491
+ drawings.embedded = 1
6460
6492
 
6461
- @drawing = drawing
6493
+ @drawings = drawings
6462
6494
 
6463
6495
  @external_drawing_links << ['/drawing', "../drawings/drawing#{drawing_id}.xml"]
6464
6496
  else
6465
- drawing = @drawing
6497
+ drawings = @drawings
6498
+ end
6499
+ drawings.add_drawing_object(drawing)
6500
+
6501
+ if url
6502
+ rel_type = '/hyperlink'
6503
+ target_mode = 'External'
6504
+ if url =~ %r!^[fh]tt?ps?://! || url =~ /^mailto:/
6505
+ target = escape_url(url)
6506
+ end
6507
+ if url =~ /^external:/
6508
+ target = escape_url(url.sub(/^external:/, 'file:///'))
6509
+ # Additional escape not required in worksheet hyperlinks
6510
+ target = target.gsub(/#/, '%23')
6511
+ end
6512
+ if url =~ /^internal:/
6513
+ target = url.sub(/^internal:/, '#')
6514
+ target_mode = nil
6515
+ end
6516
+
6517
+ if target.length > 255
6518
+ raise <<"EOS"
6519
+ Ignoring URL #{target} where link or anchor > 255 characters since it exceeds Excel's limit for URLS. See LIMITATIONS section of the WriteXLSX documentation.
6520
+ EOS
6521
+ end
6522
+
6523
+ if target && !@drawing_rels[url]
6524
+ @drawing_links << [rel_type, target, target_mode]
6525
+ end
6526
+ drawing.url_rel_index = drawing_rel_index(url)
6466
6527
  end
6467
- drawing.add_drawing_object(drawing_type, dimensions, width, height, name)
6468
6528
 
6469
- @drawing_links << ['/image', "../media/image#{image_id}.#{image_type}"]
6529
+ if !@drawing_rels[md5]
6530
+ @drawing_links << ['/image', "../media/image#{image_id}.#{image_type}"]
6531
+ end
6532
+ drawing.rel_index = drawing_rel_index(md5)
6470
6533
  end
6471
6534
  public :prepare_image
6472
6535
 
6473
- def prepare_header_image(image_id, width, height, name, image_type, position, x_dpi, y_dpi)
6536
+ def prepare_header_image(image_id, width, height, name, image_type, position, x_dpi, y_dpi, md5)
6474
6537
  # Strip the extension from the filename.
6475
6538
  body = name.dup
6476
6539
  body[/\.[^\.]+$/, 0] = ''
6477
6540
 
6478
- @header_images_array << [width, height, body, position, x_dpi, y_dpi]
6479
- @vml_drawing_links << ['/image', "../media/image#{image_id}.#{image_type}" ]
6541
+ if !@vml_drawing_rels[md5]
6542
+ @vml_drawing_links << ['/image', "../media/image#{image_id}.#{image_type}" ]
6543
+ end
6544
+
6545
+ ref_id = get_vml_drawing_rel_index(md5)
6546
+ @header_images_array << [width, height, body, position, x_dpi, y_dpi, ref_id]
6480
6547
  end
6481
6548
  public :prepare_header_image
6482
6549
 
@@ -6519,16 +6586,16 @@ def prepare_header_image(image_id, width, height, name, image_type, position, x_
6519
6586
  #
6520
6587
  def insert_shape(*args)
6521
6588
  # Check for a cell reference in A1 notation and substitute row and column.
6522
- row_start, column_start, shape, x_offset, y_offset, x_scale, y_scale =
6589
+ row_start, column_start, shape, x_offset, y_offset, x_scale, y_scale, anchor =
6523
6590
  row_col_notation(args)
6524
6591
  if [row_start, column_start, shape].include?(nil)
6525
6592
  raise "Insufficient arguments in insert_shape()"
6526
6593
  end
6527
6594
 
6528
6595
  shape.set_position(
6529
- row_start, column_start, x_offset, y_offset,
6530
- x_scale, y_scale
6531
- )
6596
+ row_start, column_start, x_offset, y_offset,
6597
+ x_scale, y_scale, anchor
6598
+ )
6532
6599
  # Assign a shape ID.
6533
6600
  while true
6534
6601
  id = shape.id || 0
@@ -6572,9 +6639,9 @@ def prepare_shape(index, drawing_id)
6572
6639
  shape = @shapes[index]
6573
6640
 
6574
6641
  # Create a Drawing object to use with worksheet unless one already exists.
6575
- unless drawing?
6576
- @drawing = Drawing.new
6577
- @drawing.embedded = 1
6642
+ unless drawings?
6643
+ @drawings = Drawings.new
6644
+ @drawings.embedded = 1
6578
6645
  @external_drawing_links << ['/drawing', "../drawings/drawing#{drawing_id}.xml"]
6579
6646
  @has_shapes = true
6580
6647
  end
@@ -6584,13 +6651,14 @@ def prepare_shape(index, drawing_id)
6584
6651
  shape.calc_position_emus(self)
6585
6652
 
6586
6653
  drawing_type = 3
6587
- drawing.add_drawing_object(drawing_type, shape.dimensions, shape.name, shape)
6654
+ drawing = Drawing.new(drawing_type, shape.dimensions, shape.width_emu, shape.height_emu, shape.name, shape, shape.anchor, drawing_rel_index, 0, nil)
6655
+ drawings.add_drawing_object(drawing)
6588
6656
  end
6589
6657
  public :prepare_shape
6590
6658
 
6591
6659
  #
6592
6660
  # This method handles the parameters passed to insert_button as well as
6593
- # calculating the comment object position and vertices.
6661
+ # calculating the button object position and vertices.
6594
6662
  #
6595
6663
  def button_params(row, col, params)
6596
6664
  button = Writexlsx::Package::Button.new
@@ -6619,7 +6687,7 @@ def button_params(row, col, params)
6619
6687
  params[:x_offset] = 0 if !params[:x_offset]
6620
6688
  params[:y_offset] = 0 if !params[:y_offset]
6621
6689
 
6622
- # Scale the size of the comment box if required.
6690
+ # Scale the size of the button box if required.
6623
6691
  if params[:x_scale]
6624
6692
  params[:width] = params[:width] * params[:x_scale]
6625
6693
  end
@@ -6634,15 +6702,15 @@ def button_params(row, col, params)
6634
6702
  params[:start_row] = row
6635
6703
  params[:start_col] = col
6636
6704
 
6637
- # Calculate the positions of comment object.
6705
+ # Calculate the positions of button object.
6638
6706
  vertices = position_object_pixels(
6639
- params[:start_col],
6640
- params[:start_row],
6641
- params[:x_offset],
6642
- params[:y_offset],
6643
- params[:width],
6644
- params[:height]
6645
- )
6707
+ params[:start_col],
6708
+ params[:start_row],
6709
+ params[:x_offset],
6710
+ params[:y_offset],
6711
+ params[:width],
6712
+ params[:height]
6713
+ )
6646
6714
 
6647
6715
  # Add the width and height for VML.
6648
6716
  vertices << [params[:width], params[:height]]
@@ -6882,7 +6950,7 @@ def col_info_attributes(args)
6882
6950
  custom_width = false if width.nil? && hidden == 0
6883
6951
  custom_width = false if width == 8.43
6884
6952
 
6885
- width = hidden == 0 ? 8.43 : 0 unless width
6953
+ width = hidden == 0 ? @default_col_width : 0 unless width
6886
6954
 
6887
6955
  # Convert column width from user units to character width.
6888
6956
  if width && width < 1
@@ -7492,7 +7560,7 @@ def write_sheet_protection #:nodoc:
7492
7560
  # Write the <drawing> elements.
7493
7561
  #
7494
7562
  def write_drawings #:nodoc:
7495
- increment_rel_id_and_write_r_id('drawing') if drawing?
7563
+ increment_rel_id_and_write_r_id('drawing') if drawings?
7496
7564
  end
7497
7565
 
7498
7566
  #
@@ -7970,8 +8038,8 @@ def autofilter_ref? #:nodoc:
7970
8038
  !!@autofilter_ref
7971
8039
  end
7972
8040
 
7973
- def drawing? #:nodoc:
7974
- !!@drawing
8041
+ def drawings? #:nodoc:
8042
+ !!@drawings
7975
8043
  end
7976
8044
 
7977
8045
  def remove_white_space(margin) #:nodoc: