write_xlsx 0.99.0 → 1.07.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 (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: