write_xlsx 1.02.0 → 1.08.1

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 (189) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -1
  3. data/Changes +72 -0
  4. data/README.md +1 -1
  5. data/examples/chart_data_labels.rb +320 -0
  6. data/examples/chart_line.rb +85 -10
  7. data/examples/tables.rb +77 -42
  8. data/lib/write_xlsx/chart/line.rb +15 -1
  9. data/lib/write_xlsx/chart/series.rb +100 -0
  10. data/lib/write_xlsx/chart.rb +155 -33
  11. data/lib/write_xlsx/drawing.rb +80 -17
  12. data/lib/write_xlsx/format.rb +5 -5
  13. data/lib/write_xlsx/package/app.rb +3 -3
  14. data/lib/write_xlsx/package/comments.rb +4 -4
  15. data/lib/write_xlsx/package/conditional_format.rb +2 -8
  16. data/lib/write_xlsx/package/packager.rb +1 -0
  17. data/lib/write_xlsx/package/relationships.rb +2 -2
  18. data/lib/write_xlsx/package/styles.rb +42 -11
  19. data/lib/write_xlsx/package/table.rb +16 -7
  20. data/lib/write_xlsx/package/vml.rb +20 -19
  21. data/lib/write_xlsx/sheets.rb +12 -20
  22. data/lib/write_xlsx/utility.rb +9 -3
  23. data/lib/write_xlsx/version.rb +1 -1
  24. data/lib/write_xlsx/workbook.rb +76 -35
  25. data/lib/write_xlsx/worksheet/data_validation.rb +1 -6
  26. data/lib/write_xlsx/worksheet.rb +197 -57
  27. data/test/drawing/{test_write_ext.rb → test_write_xdr_ext.rb} +2 -2
  28. data/test/perl_output/chart_data_labels.xlsx +0 -0
  29. data/test/perl_output/chart_line.xlsx +0 -0
  30. data/test/perl_output/comments2.xlsx +0 -0
  31. data/test/perl_output/tables.xlsx +0 -0
  32. data/test/regression/images/red2.png +0 -0
  33. data/test/regression/test_array_formula04.rb +31 -0
  34. data/test/regression/test_chart_crossing01.rb +1 -1
  35. data/test/regression/test_chart_crossing05.rb +46 -0
  36. data/test/regression/test_chart_crossing06.rb +46 -0
  37. data/test/regression/test_chart_data_labels26.rb +44 -0
  38. data/test/regression/test_chart_data_labels27.rb +44 -0
  39. data/test/regression/test_chart_data_labels28.rb +52 -0
  40. data/test/regression/test_chart_data_labels29.rb +43 -0
  41. data/test/regression/test_chart_data_labels30.rb +46 -0
  42. data/test/regression/test_chart_data_labels31.rb +49 -0
  43. data/test/regression/test_chart_data_labels32.rb +54 -0
  44. data/test/regression/test_chart_data_labels33.rb +52 -0
  45. data/test/regression/test_chart_data_labels34.rb +54 -0
  46. data/test/regression/test_chart_data_labels35.rb +46 -0
  47. data/test/regression/test_chart_data_labels36.rb +54 -0
  48. data/test/regression/test_chart_data_labels37.rb +51 -0
  49. data/test/regression/test_chart_data_labels38.rb +54 -0
  50. data/test/regression/test_chart_data_labels39.rb +53 -0
  51. data/test/regression/test_chart_data_labels40.rb +53 -0
  52. data/test/regression/test_chart_data_labels41.rb +54 -0
  53. data/test/regression/test_chart_data_labels42.rb +58 -0
  54. data/test/regression/test_chart_data_labels43.rb +58 -0
  55. data/test/regression/test_chart_data_labels44.rb +56 -0
  56. data/test/regression/test_chart_data_labels45.rb +57 -0
  57. data/test/regression/test_chart_data_labels46.rb +61 -0
  58. data/test/regression/test_chart_data_labels47.rb +61 -0
  59. data/test/regression/test_chart_data_labels48.rb +55 -0
  60. data/test/regression/test_chart_data_labels49.rb +55 -0
  61. data/test/regression/test_chart_data_labels50.rb +57 -0
  62. data/test/regression/test_chart_line05.rb +43 -0
  63. data/test/regression/test_chart_line06.rb +43 -0
  64. data/test/regression/test_comment15.rb +28 -0
  65. data/test/regression/test_comment16.rb +34 -0
  66. data/test/regression/test_format16.rb +24 -0
  67. data/test/regression/test_format17.rb +24 -0
  68. data/test/regression/test_header04.rb +30 -0
  69. data/test/regression/test_header_image15.rb +36 -0
  70. data/test/regression/test_header_image16.rb +42 -0
  71. data/test/regression/test_header_image17.rb +46 -0
  72. data/test/regression/test_header_image18.rb +48 -0
  73. data/test/regression/test_header_image19.rb +36 -0
  74. data/test/regression/test_hyperlink48.rb +31 -0
  75. data/test/regression/test_hyperlink49.rb +29 -0
  76. data/test/regression/test_hyperlink50.rb +27 -0
  77. data/test/regression/test_hyperlink51.rb +27 -0
  78. data/test/regression/test_ignore_error01.rb +23 -0
  79. data/test/regression/test_ignore_error02.rb +24 -0
  80. data/test/regression/test_ignore_error03.rb +26 -0
  81. data/test/regression/test_ignore_error04.rb +26 -0
  82. data/test/regression/test_ignore_error05.rb +32 -0
  83. data/test/regression/test_ignore_error06.rb +32 -0
  84. data/test/regression/test_image45.rb +2 -1
  85. data/test/regression/test_image46.rb +1 -1
  86. data/test/regression/test_image48.rb +32 -0
  87. data/test/regression/test_image49.rb +38 -0
  88. data/test/regression/test_image50.rb +24 -0
  89. data/test/regression/test_image51.rb +30 -0
  90. data/test/regression/test_image52.rb +26 -0
  91. data/test/regression/test_image53.rb +26 -0
  92. data/test/regression/test_image54.rb +26 -0
  93. data/test/regression/test_image55.rb +27 -0
  94. data/test/regression/test_object_position12.rb +25 -0
  95. data/test/regression/test_object_position13.rb +25 -0
  96. data/test/regression/test_object_position14.rb +25 -0
  97. data/test/regression/test_object_position15.rb +29 -0
  98. data/test/regression/test_object_position16.rb +29 -0
  99. data/test/regression/test_object_position17.rb +29 -0
  100. data/test/regression/test_object_position18.rb +29 -0
  101. data/test/regression/test_object_position19.rb +29 -0
  102. data/test/regression/test_object_position20.rb +29 -0
  103. data/test/regression/test_protect04.rb +32 -0
  104. data/test/regression/test_protect05.rb +35 -0
  105. data/test/regression/test_protect06.rb +35 -0
  106. data/test/regression/test_protect07.rb +23 -0
  107. data/test/regression/test_table24.rb +27 -0
  108. data/test/regression/test_table25.rb +27 -0
  109. data/test/regression/test_table26.rb +38 -0
  110. data/test/regression/xlsx_files/array_formula04.xlsx +0 -0
  111. data/test/regression/xlsx_files/chart_crossing05.xlsx +0 -0
  112. data/test/regression/xlsx_files/chart_crossing06.xlsx +0 -0
  113. data/test/regression/xlsx_files/chart_data_labels26.xlsx +0 -0
  114. data/test/regression/xlsx_files/chart_data_labels27.xlsx +0 -0
  115. data/test/regression/xlsx_files/chart_data_labels28.xlsx +0 -0
  116. data/test/regression/xlsx_files/chart_data_labels29.xlsx +0 -0
  117. data/test/regression/xlsx_files/chart_data_labels30.xlsx +0 -0
  118. data/test/regression/xlsx_files/chart_data_labels31.xlsx +0 -0
  119. data/test/regression/xlsx_files/chart_data_labels32.xlsx +0 -0
  120. data/test/regression/xlsx_files/chart_data_labels33.xlsx +0 -0
  121. data/test/regression/xlsx_files/chart_data_labels34.xlsx +0 -0
  122. data/test/regression/xlsx_files/chart_data_labels35.xlsx +0 -0
  123. data/test/regression/xlsx_files/chart_data_labels36.xlsx +0 -0
  124. data/test/regression/xlsx_files/chart_data_labels37.xlsx +0 -0
  125. data/test/regression/xlsx_files/chart_data_labels38.xlsx +0 -0
  126. data/test/regression/xlsx_files/chart_data_labels39.xlsx +0 -0
  127. data/test/regression/xlsx_files/chart_data_labels40.xlsx +0 -0
  128. data/test/regression/xlsx_files/chart_data_labels41.xlsx +0 -0
  129. data/test/regression/xlsx_files/chart_data_labels42.xlsx +0 -0
  130. data/test/regression/xlsx_files/chart_data_labels43.xlsx +0 -0
  131. data/test/regression/xlsx_files/chart_data_labels44.xlsx +0 -0
  132. data/test/regression/xlsx_files/chart_data_labels45.xlsx +0 -0
  133. data/test/regression/xlsx_files/chart_data_labels46.xlsx +0 -0
  134. data/test/regression/xlsx_files/chart_data_labels47.xlsx +0 -0
  135. data/test/regression/xlsx_files/chart_data_labels48.xlsx +0 -0
  136. data/test/regression/xlsx_files/chart_data_labels49.xlsx +0 -0
  137. data/test/regression/xlsx_files/chart_data_labels50.xlsx +0 -0
  138. data/test/regression/xlsx_files/chart_line05.xlsx +0 -0
  139. data/test/regression/xlsx_files/chart_line06.xlsx +0 -0
  140. data/test/regression/xlsx_files/comment15.xlsx +0 -0
  141. data/test/regression/xlsx_files/comment16.xlsx +0 -0
  142. data/test/regression/xlsx_files/format16.xlsx +0 -0
  143. data/test/regression/xlsx_files/format17.xlsx +0 -0
  144. data/test/regression/xlsx_files/header04.xlsx +0 -0
  145. data/test/regression/xlsx_files/header_image15.xlsx +0 -0
  146. data/test/regression/xlsx_files/header_image16.xlsx +0 -0
  147. data/test/regression/xlsx_files/header_image17.xlsx +0 -0
  148. data/test/regression/xlsx_files/header_image18.xlsx +0 -0
  149. data/test/regression/xlsx_files/header_image19.xlsx +0 -0
  150. data/test/regression/xlsx_files/hyperlink46.xlsx +0 -0
  151. data/test/regression/xlsx_files/hyperlink50.xlsx +0 -0
  152. data/test/regression/xlsx_files/hyperlink51.xlsx +0 -0
  153. data/test/regression/xlsx_files/ignore_error01.xlsx +0 -0
  154. data/test/regression/xlsx_files/ignore_error02.xlsx +0 -0
  155. data/test/regression/xlsx_files/ignore_error03.xlsx +0 -0
  156. data/test/regression/xlsx_files/ignore_error04.xlsx +0 -0
  157. data/test/regression/xlsx_files/ignore_error05.xlsx +0 -0
  158. data/test/regression/xlsx_files/ignore_error06.xlsx +0 -0
  159. data/test/regression/xlsx_files/image45.xlsx +0 -0
  160. data/test/regression/xlsx_files/image46.xlsx +0 -0
  161. data/test/regression/xlsx_files/image48.xlsx +0 -0
  162. data/test/regression/xlsx_files/image49.xlsx +0 -0
  163. data/test/regression/xlsx_files/image50.xlsx +0 -0
  164. data/test/regression/xlsx_files/image51.xlsx +0 -0
  165. data/test/regression/xlsx_files/image52.xlsx +0 -0
  166. data/test/regression/xlsx_files/image53.xlsx +0 -0
  167. data/test/regression/xlsx_files/image54.xlsx +0 -0
  168. data/test/regression/xlsx_files/image55.xlsx +0 -0
  169. data/test/regression/xlsx_files/object_position12.xlsx +0 -0
  170. data/test/regression/xlsx_files/object_position13.xlsx +0 -0
  171. data/test/regression/xlsx_files/object_position14.xlsx +0 -0
  172. data/test/regression/xlsx_files/object_position15.xlsx +0 -0
  173. data/test/regression/xlsx_files/object_position16.xlsx +0 -0
  174. data/test/regression/xlsx_files/object_position17.xlsx +0 -0
  175. data/test/regression/xlsx_files/object_position18.xlsx +0 -0
  176. data/test/regression/xlsx_files/object_position19.xlsx +0 -0
  177. data/test/regression/xlsx_files/object_position20.xlsx +0 -0
  178. data/test/regression/xlsx_files/protect04.xlsx +0 -0
  179. data/test/regression/xlsx_files/protect05.xlsx +0 -0
  180. data/test/regression/xlsx_files/protect06.xlsx +0 -0
  181. data/test/regression/xlsx_files/protect07.xlsx +0 -0
  182. data/test/regression/xlsx_files/table24.xlsx +0 -0
  183. data/test/regression/xlsx_files/table25.xlsx +0 -0
  184. data/test/regression/xlsx_files/table26.xlsx +0 -0
  185. data/test/test_example_match.rb +433 -10
  186. data/test/utility/test_range.rb +20 -0
  187. data/test/workbook/test_check_sheetname.rb +0 -10
  188. data/write_xlsx.gemspec +1 -0
  189. metadata +323 -8
@@ -52,6 +52,7 @@ module Writexlsx
52
52
  attr_reader :max_url_length # :nodoc:
53
53
  attr_reader :strings_to_urls # :nodoc:
54
54
  attr_reader :default_url_format # :nodoc:
55
+ attr_reader :read_only # :nodoc:
55
56
 
56
57
  #
57
58
  # A new Excel workbook is created using the +new+ constructor
@@ -132,6 +133,8 @@ module Writexlsx
132
133
  @strings_to_urls = (options[:strings_to_urls].nil? || options[:strings_to_urls]) ? true : false
133
134
 
134
135
  @max_url_length = 2079
136
+ @has_comments = false
137
+ @read_only = 0
135
138
  if options[:max_url_length]
136
139
  @max_url_length = options[:max_url_length]
137
140
 
@@ -291,6 +294,9 @@ module Writexlsx
291
294
  # Write the XLSX file version.
292
295
  write_file_version
293
296
 
297
+ # Write the fileSharing element.
298
+ write_file_sharing
299
+
294
300
  # Write the workbook properties.
295
301
  write_workbook_pr
296
302
 
@@ -953,6 +959,13 @@ module Writexlsx
953
959
  end
954
960
  end
955
961
 
962
+ #
963
+ # Set the Excel "Read-only recommended" save option.
964
+ #
965
+ def read_only_recommended
966
+ @read_only = 2
967
+ end
968
+
956
969
  #
957
970
  # set_calc_mode()
958
971
  #
@@ -1115,7 +1128,8 @@ module Writexlsx
1115
1128
  @border_count,
1116
1129
  @fill_count,
1117
1130
  @custom_colors,
1118
- @dxf_formats
1131
+ @dxf_formats,
1132
+ @has_comments
1119
1133
  ]
1120
1134
  end
1121
1135
 
@@ -1309,6 +1323,17 @@ module Writexlsx
1309
1323
  @writer.empty_tag('fileVersion', attributes)
1310
1324
  end
1311
1325
 
1326
+ #
1327
+ # Write the <fileSharing> element.
1328
+ #
1329
+ def write_file_sharing
1330
+ return if !ptrue?(@read_only)
1331
+
1332
+ attributes = []
1333
+ attributes << ['readOnlyRecommended', 1]
1334
+ @writer.empty_tag('fileSharing', attributes)
1335
+ end
1336
+
1312
1337
  def write_workbook_pr #:nodoc:
1313
1338
  attributes = []
1314
1339
  attributes << ['codeName', @vba_codename] if ptrue?(@vba_codename)
@@ -1711,11 +1736,14 @@ module Writexlsx
1711
1736
  if sheet.has_comments?
1712
1737
  comment_files += 1
1713
1738
  comment_id += 1
1739
+ @has_comments = true
1714
1740
  end
1715
1741
  vml_drawing_id += 1
1716
1742
 
1717
- sheet.prepare_vml_objects(vml_data_id, vml_shape_id,
1718
- vml_drawing_id, comment_id)
1743
+ sheet.prepare_vml_objects(
1744
+ vml_data_id, vml_shape_id,
1745
+ vml_drawing_id, comment_id
1746
+ )
1719
1747
 
1720
1748
  # Each VML file should start with a shape id incremented by 1024.
1721
1749
  vml_data_id += 1 * ( 1 + sheet.num_comments_block )
@@ -1738,8 +1766,6 @@ module Writexlsx
1738
1766
  end
1739
1767
  end
1740
1768
 
1741
- add_font_format_for_cell_comments if num_comment_files > 0
1742
-
1743
1769
  # Set the workbook vba_codename if one of the sheets has a button and
1744
1770
  # the workbook has a vbaProject binary.
1745
1771
  if has_button && @vba_project && !@vba_codename
@@ -1759,19 +1785,6 @@ module Writexlsx
1759
1785
  end
1760
1786
  end
1761
1787
 
1762
- def add_font_format_for_cell_comments
1763
- format = Format.new(
1764
- @formats,
1765
- :font => 'Tahoma',
1766
- :size => 8,
1767
- :color_indexed => 81,
1768
- :font_only => 1
1769
- )
1770
-
1771
- format.get_xf_index
1772
- @formats.formats << format
1773
- end
1774
-
1775
1788
  #
1776
1789
  # Add "cached" data to charts to provide the numCache and strCache data for
1777
1790
  # series and title/axis ranges.
@@ -1922,9 +1935,12 @@ module Writexlsx
1922
1935
  # Iterate through the worksheets and set up any chart or image drawings.
1923
1936
  #
1924
1937
  def prepare_drawings #:nodoc:
1925
- chart_ref_id = 0
1926
- image_ref_id = 0
1927
- drawing_id = 0
1938
+ chart_ref_id = 0
1939
+ image_ref_id = 0
1940
+ drawing_id = 0
1941
+ ref_id = 0
1942
+ image_ids = {}
1943
+ header_image_ids = {}
1928
1944
  @worksheets.each do |sheet|
1929
1945
  chart_count = sheet.charts.size
1930
1946
  image_count = sheet.images.size
@@ -1944,9 +1960,19 @@ module Writexlsx
1944
1960
 
1945
1961
  # Prepare the worksheet images.
1946
1962
  sheet.images.each_with_index do |image, index|
1947
- type, width, height, name, x_dpi, y_dpi = get_image_properties(image[2])
1948
- image_ref_id += 1
1949
- sheet.prepare_image(index, image_ref_id, drawing_id, width, height, name, type, x_dpi, y_dpi)
1963
+ filename = image[2]
1964
+ type, width, height, name, x_dpi, y_dpi, md5 = get_image_properties(image[2])
1965
+ if image_ids[md5]
1966
+ ref_id = image_ids[md5]
1967
+ else
1968
+ image_ref_id += 1
1969
+ image_ids[md5] = ref_id = image_ref_id
1970
+ @images << [filename, type]
1971
+ end
1972
+ sheet.prepare_image(
1973
+ index, ref_id, drawing_id, width, height,
1974
+ name, type, x_dpi, y_dpi, md5
1975
+ )
1950
1976
  end
1951
1977
 
1952
1978
  # Prepare the worksheet charts.
@@ -1965,13 +1991,21 @@ module Writexlsx
1965
1991
  filename = sheet.header_images[index][0]
1966
1992
  position = sheet.header_images[index][1]
1967
1993
 
1968
- type, width, height, name, x_dpi, y_dpi =
1994
+ type, width, height, name, x_dpi, y_dpi, md5 =
1969
1995
  get_image_properties(filename)
1970
1996
 
1971
- image_ref_id += 1
1997
+ if header_image_ids[md5]
1998
+ ref_id = header_image_ids[md5]
1999
+ else
2000
+ image_ref_id += 1
2001
+ header_image_ids[md5] = ref_id = image_ref_id
2002
+ @images << [filename, type]
2003
+ end
1972
2004
 
1973
- sheet.prepare_header_image(image_ref_id, width, height,
1974
- name, type, position, x_dpi, y_dpi)
2005
+ sheet.prepare_header_image(
2006
+ ref_id, width, height, name, type,
2007
+ position, x_dpi, y_dpi, md5
2008
+ )
1975
2009
  end
1976
2010
 
1977
2011
  # Prepare the footer images.
@@ -1979,13 +2013,21 @@ module Writexlsx
1979
2013
  filename = sheet.footer_images[index][0]
1980
2014
  position = sheet.footer_images[index][1]
1981
2015
 
1982
- type, width, height, name, x_dpi, y_dpi =
2016
+ type, width, height, name, x_dpi, y_dpi, md5 =
1983
2017
  get_image_properties(filename)
1984
2018
 
1985
- image_ref_id += 1
2019
+ if header_image_ids[md5]
2020
+ ref_id = header_image_ids[md5]
2021
+ else
2022
+ image_ref_id += 1
2023
+ header_image_ids[md5] = ref_id = image_ref_id
2024
+ @images << [filename, type]
2025
+ end
1986
2026
 
1987
- sheet.prepare_header_image(image_ref_id, width, height,
1988
- name, type, position, x_dpi, y_dpi)
2027
+ sheet.prepare_header_image(
2028
+ ref_id, width, height, name, type,
2029
+ position, x_dpi, y_dpi, md5
2030
+ )
1989
2031
  end
1990
2032
 
1991
2033
  if has_drawings
@@ -2014,6 +2056,7 @@ module Writexlsx
2014
2056
 
2015
2057
  # Open the image file and import the data.
2016
2058
  data = File.binread(filename)
2059
+ md5 = Digest::MD5.hexdigest(data)
2017
2060
  if data.unpack('x A3')[0] == 'PNG'
2018
2061
  # Test for PNGs.
2019
2062
  type, width, height, x_dpi, y_dpi = process_png(data)
@@ -2031,13 +2074,11 @@ module Writexlsx
2031
2074
  raise "Unsupported image format for file: #{filename}\n"
2032
2075
  end
2033
2076
 
2034
- @images << [filename, type]
2035
-
2036
2077
  # Set a default dpi for images with 0 dpi.
2037
2078
  x_dpi = 96 if x_dpi == 0
2038
2079
  y_dpi = 96 if y_dpi == 0
2039
2080
 
2040
- [type, width, height, File.basename(filename), x_dpi, y_dpi]
2081
+ [type, width, height, File.basename(filename), x_dpi, y_dpi, md5]
2041
2082
  end
2042
2083
 
2043
2084
  #
@@ -138,12 +138,7 @@ module Writexlsx
138
138
  row_first, row_last = row_last, row_first if row_first > row_last
139
139
  col_first, col_last = col_last, col_first if col_first > col_last
140
140
 
141
- # If the first and last cell are the same write a single cell.
142
- if row_first == row_last && col_first == col_last
143
- sqref += xl_rowcol_to_cell(row_first, col_first)
144
- else
145
- sqref += xl_range(row_first, row_last, col_first, col_last)
146
- end
141
+ sqref += xl_range(row_first, row_last, col_first, col_last)
147
142
  end
148
143
 
149
144
  if @validate != 'none'