write_xlsx 1.02.0 → 1.08.1

Sign up to get free protection for your applications and to get access to all the features.
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'