write_xlsx 0.64.1 → 0.65.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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/README.rdoc +10 -1
  3. data/examples/conditional_format.rb +251 -18
  4. data/examples/demo.rb +2 -3
  5. data/examples/macros.rb +42 -0
  6. data/examples/outline_collapsed.rb +160 -0
  7. data/examples/republic.png +0 -0
  8. data/examples/shape3.rb +2 -2
  9. data/examples/shape4.rb +5 -5
  10. data/examples/shape5.rb +6 -6
  11. data/examples/shape6.rb +6 -6
  12. data/examples/shape7.rb +11 -11
  13. data/examples/shape8.rb +10 -10
  14. data/examples/shape_all.rb +0 -0
  15. data/examples/vbaProject.bin +0 -0
  16. data/lib/write_xlsx/chart.rb +656 -56
  17. data/lib/write_xlsx/chartsheet.rb +26 -2
  18. data/lib/write_xlsx/format.rb +50 -27
  19. data/lib/write_xlsx/formats.rb +32 -0
  20. data/lib/write_xlsx/package/packager.rb +45 -238
  21. data/lib/write_xlsx/package/table.rb +9 -18
  22. data/lib/write_xlsx/package/xml_writer_simple.rb +26 -9
  23. data/lib/write_xlsx/sheets.rb +223 -0
  24. data/lib/write_xlsx/sparkline.rb +140 -4
  25. data/lib/write_xlsx/version.rb +1 -1
  26. data/lib/write_xlsx/workbook.rb +34 -121
  27. data/lib/write_xlsx/worksheet/data_validation.rb +291 -0
  28. data/lib/write_xlsx/worksheet/hyperlink.rb +111 -0
  29. data/lib/write_xlsx/worksheet/page_setup.rb +170 -0
  30. data/lib/write_xlsx/worksheet.rb +1112 -1334
  31. data/test/helper.rb +1 -1
  32. data/test/package/styles/test_styles_01.rb +1 -10
  33. data/test/package/styles/test_styles_02.rb +1 -10
  34. data/test/package/styles/test_styles_03.rb +1 -10
  35. data/test/package/styles/test_styles_04.rb +1 -10
  36. data/test/package/styles/test_styles_05.rb +1 -10
  37. data/test/package/styles/test_styles_06.rb +1 -10
  38. data/test/package/styles/test_styles_07.rb +1 -10
  39. data/test/package/styles/test_styles_08.rb +1 -10
  40. data/test/package/styles/test_styles_09.rb +1 -10
  41. data/test/perl_output/conditional_format.xlsx +0 -0
  42. data/test/perl_output/outline_collapsed.xlsx +0 -0
  43. data/test/perl_output/protection.xlsx +0 -0
  44. data/test/regression/test_chart_gap01.rb +47 -0
  45. data/test/regression/test_chart_gap02.rb +47 -0
  46. data/test/regression/test_chart_gap03.rb +47 -0
  47. data/test/regression/test_format05.rb +26 -0
  48. data/test/regression/test_rich_string12.rb +32 -0
  49. data/test/regression/xlsx_files/chart_gap01.xlsx +0 -0
  50. data/test/regression/xlsx_files/chart_gap02.xlsx +0 -0
  51. data/test/regression/xlsx_files/chart_gap03.xlsx +0 -0
  52. data/test/regression/xlsx_files/format05.xlsx +0 -0
  53. data/test/regression/xlsx_files/rich_string12.xlsx +0 -0
  54. data/test/test_example_match.rb +253 -20
  55. data/test/worksheet/test_set_column.rb +25 -0
  56. data/test/worksheet/test_worksheet_03.rb +1 -1
  57. data/test/worksheet/test_worksheet_04.rb +1 -1
  58. data/test/worksheet/test_write_array_formula_01.rb +7 -0
  59. data/test/worksheet/test_write_col_breaks.rb +2 -2
  60. data/test/worksheet/test_write_col_info.rb +8 -8
  61. data/test/worksheet/test_write_conditional_formatting.rb +4 -4
  62. data/test/worksheet/test_write_formula_does_not_change_formula_string.rb +18 -0
  63. data/test/worksheet/test_write_header_footer.rb +8 -3
  64. data/test/worksheet/test_write_hyperlink.rb +10 -5
  65. data/test/worksheet/test_write_merge_cells.rb +6 -6
  66. data/test/worksheet/test_write_page_set_up_pr.rb +1 -1
  67. data/test/worksheet/test_write_page_setup.rb +1 -1
  68. data/test/worksheet/test_write_row_breaks.rb +2 -2
  69. data/test/worksheet/test_write_row_element.rb +1 -1
  70. data/test/worksheet/test_write_sheet_pr.rb +2 -2
  71. data/test/worksheet/test_write_sheet_view.rb +0 -9
  72. data/test/worksheet/test_write_url.rb +19 -0
  73. data/test/worksheet/test_write_worksheet_attributes.rb +21 -0
  74. metadata +38 -5
  75. data/lib/write_xlsx/worksheet/print_style.rb +0 -51
  76. data/test/worksheet/test_write_worksheet.rb +0 -19
@@ -12,9 +12,7 @@ module Writexlsx
12
12
  end
13
13
 
14
14
  def set_xml_writer(filename = nil)
15
- fh = File.open(filename, "wb")
16
-
17
- @io = fh
15
+ @filename = filename
18
16
  end
19
17
 
20
18
  def xml_decl(encoding = 'UTF-8', standalone = true)
@@ -28,14 +26,27 @@ module Writexlsx
28
26
  end_tag(tag)
29
27
  end
30
28
 
29
+ def tag_elements_str(tag, attributes = [])
30
+ str = ''
31
+ str << start_tag_str(tag, attributes)
32
+ str << yield
33
+ str << end_tag_str(tag)
34
+ end
35
+
31
36
  def start_tag(tag, attr = [])
32
- str = "<#{tag}#{key_vals(attr)}>"
33
- io_write(str)
37
+ io_write(start_tag_str(tag, attr))
38
+ end
39
+
40
+ def start_tag_str(tag, attr = [])
41
+ "<#{tag}#{key_vals(attr)}>"
34
42
  end
35
43
 
36
44
  def end_tag(tag)
37
- str = "</#{tag}>"
38
- io_write(str)
45
+ io_write(end_tag_str(tag))
46
+ end
47
+
48
+ def end_tag_str(tag)
49
+ "</#{tag}>"
39
50
  end
40
51
 
41
52
  def empty_tag(tag, attr = [])
@@ -44,8 +55,11 @@ module Writexlsx
44
55
  end
45
56
 
46
57
  def empty_tag_encoded(tag, attr = [])
47
- str = "<#{tag}#{key_vals(attr)}/>"
48
- io_write(str)
58
+ io_write(empty_tag_encoded_str(tag, attr))
59
+ end
60
+
61
+ def empty_tag_encoded_str(tag, attr = [])
62
+ "<#{tag}#{key_vals(attr)}/>"
49
63
  end
50
64
 
51
65
  def data_element(tag, data, attr = [])
@@ -75,6 +89,9 @@ module Writexlsx
75
89
  end
76
90
 
77
91
  def close
92
+ if @filename
93
+ File.open(@filename, "wb") { |f| f << string }
94
+ end
78
95
  @io.close
79
96
  end
80
97
 
@@ -0,0 +1,223 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'delegate'
3
+ require 'write_xlsx/package/xml_writer_simple'
4
+
5
+ module Writexlsx
6
+ class Sheets < DelegateClass(Array)
7
+ include Writexlsx::Utility
8
+
9
+ BASE_NAME = { :sheet => 'Sheet', :chart => 'Chart'} # :nodoc:
10
+
11
+ def initialize
12
+ super([])
13
+ end
14
+
15
+ def chartsheet_count
16
+ chartsheets.count
17
+ end
18
+
19
+ def sheetname_count
20
+ self.count - chartname_count
21
+ end
22
+
23
+ def chartname_count
24
+ chartsheet_count
25
+ end
26
+
27
+ def make_and_check_sheet_chart_name(type, name)
28
+ count = sheet_chart_count(type)
29
+ name = "#{BASE_NAME[type]}#{count+1}" unless ptrue?(name)
30
+
31
+ check_valid_sheetname(name)
32
+ name
33
+ end
34
+
35
+ def write_sheets(writer)
36
+ writer.tag_elements('sheets') do
37
+ id_num = 1
38
+ self.each do |sheet|
39
+ write_sheet(writer, sheet, id_num)
40
+ id_num += 1
41
+ end
42
+ end
43
+ end
44
+
45
+ def write_worksheet_files(package_dir)
46
+ dir = "#{package_dir}/xl/worksheets"
47
+ worksheets.each_with_index do |sheet, index|
48
+ write_sheet_files(dir, sheet, index)
49
+ end
50
+ end
51
+
52
+ def write_chartsheet_files(package_dir)
53
+ dir = "#{package_dir}/xl/chartsheets"
54
+ chartsheets.each_with_index do |sheet, index|
55
+ write_sheet_files(dir, sheet, index)
56
+ end
57
+ end
58
+
59
+ def write_vml_files(package_dir)
60
+ dir = "#{package_dir}/xl/drawings"
61
+ self.select { |sheet| sheet.has_vml? }.
62
+ each_with_index do |sheet, index|
63
+ FileUtils.mkdir_p(dir)
64
+
65
+ vml = Package::Vml.new
66
+ vml.set_xml_writer("#{dir}/vmlDrawing#{index+1}.vml")
67
+ vml.assemble_xml_file(sheet)
68
+ end
69
+ end
70
+
71
+ def write_comment_files(package_dir)
72
+ self.select { |sheet| sheet.has_comments? }.
73
+ each_with_index do |sheet, index|
74
+ FileUtils.mkdir_p("#{package_dir}/xl/drawings")
75
+ sheet.comments.set_xml_writer("#{package_dir}/xl/comments#{index+1}.xml")
76
+ sheet.comments.assemble_xml_file
77
+ end
78
+ end
79
+
80
+ def write_table_files(package_dir)
81
+ unless tables.empty?
82
+ dir = "#{package_dir}/xl/tables"
83
+ FileUtils.mkdir_p(dir)
84
+ tables.each_with_index do |table, index|
85
+ table.set_xml_writer("#{dir}/table#{index+1}.xml")
86
+ table.assemble_xml_file
87
+ end
88
+ end
89
+ end
90
+
91
+ def write_chartsheet_rels_files(package_dir)
92
+ write_sheet_rels_files_base(chartsheets, "#{package_dir}/xl/chartsheets/_rels",
93
+ 'sheet')
94
+ end
95
+
96
+ def write_drawing_rels_files(package_dir)
97
+ # write_rels_files_base(
98
+ # self.reject { |sheet| sheet.drawing_links[0].empty? },
99
+ # "#{package_dir}/xl/drawings/_rels",
100
+
101
+ # )
102
+ dir = "#{package_dir}/xl/drawings/_rels"
103
+ self.reject { |sheet| sheet.drawing_links[0].empty? }.
104
+ each_with_index do |sheet, index|
105
+
106
+ FileUtils.mkdir_p(dir)
107
+
108
+ rels = Package::Relationships.new
109
+
110
+ sheet.drawing_links.each do |drawing_datas|
111
+ drawing_datas.each do |drawing_data|
112
+ rels.add_document_relationship(*drawing_data)
113
+ end
114
+ end
115
+
116
+ # Create the .rels file such as /xl/drawings/_rels/sheet1.xml.rels.
117
+ rels.set_xml_writer("#{dir}/drawing#{index+1}.xml.rels")
118
+ rels.assemble_xml_file
119
+ end
120
+ end
121
+
122
+ def write_worksheet_rels_files(package_dir)
123
+ write_sheet_rels_files_base(worksheets, "#{package_dir}/xl/worksheets/_rels",
124
+ 'sheet')
125
+ end
126
+
127
+ def write_sheet_rels_files_base(sheets, dir, body)
128
+ sheets.each_with_index do |sheet, index|
129
+
130
+ next if sheet.external_links.empty?
131
+
132
+ FileUtils.mkdir_p(dir)
133
+
134
+ rels = Package::Relationships.new
135
+
136
+ sheet.external_links.each do |link_datas|
137
+ link_datas.each do |link_data|
138
+ rels.add_worksheet_relationship(*link_data)
139
+ end
140
+ end
141
+
142
+ # Create the .rels file such as /xl/worksheets/_rels/sheet1.xml.rels.
143
+ rels.set_xml_writer("#{dir}/#{body}#{index+1}.xml.rels")
144
+ rels.assemble_xml_file
145
+ end
146
+ end
147
+
148
+ def tables
149
+ self.inject([]) { |tables, sheet| tables + sheet.tables }.flatten
150
+ end
151
+
152
+ def tables_count
153
+ tables.count
154
+ end
155
+
156
+ def index_by_name(sheetname)
157
+ name = sheetname.sub(/^'/,'').sub(/'$/,'')
158
+ self.collect { |sheet| sheet.name }.index(name)
159
+ end
160
+
161
+ private
162
+
163
+ def worksheets
164
+ self.reject { |worksheet| worksheet.is_chartsheet? }
165
+ end
166
+
167
+ def chartsheets
168
+ self.select { |worksheet| worksheet.is_chartsheet? }
169
+ end
170
+
171
+ def sheet_chart_count(type)
172
+ case type
173
+ when :sheet
174
+ sheetname_count
175
+ when :chart
176
+ chartname_count
177
+ end
178
+ end
179
+
180
+ def check_valid_sheetname(name)
181
+ # Check that sheet name is <= 31. Excel limit.
182
+ raise "Sheetname #{name} must be <= #{SHEETNAME_MAX} chars" if name.length > SHEETNAME_MAX
183
+
184
+ # Check that sheetname doesn't contain any invalid characters
185
+ invalid_char = /[\[\]:*?\/\\]/
186
+ if name =~ invalid_char
187
+ raise 'Invalid character []:*?/\\ in worksheet name: ' + name
188
+ end
189
+
190
+ # Check that the worksheet name doesn't already exist since this is a fatal
191
+ # error in Excel 97. The check must also exclude case insensitive matches.
192
+ unless is_sheetname_uniq?(name)
193
+ raise "Worksheet name '#{name}', with case ignored, is already used."
194
+ end
195
+ end
196
+
197
+ def is_sheetname_uniq?(name)
198
+ self.each do |worksheet|
199
+ return false if name.downcase == worksheet.name.downcase
200
+ end
201
+ true
202
+ end
203
+
204
+ def write_sheet_files(dir, sheet, index)
205
+ FileUtils.mkdir_p(dir)
206
+ sheet.set_xml_writer("#{dir}/sheet#{index+1}.xml")
207
+ sheet.assemble_xml_file
208
+ end
209
+
210
+ def write_sheet(writer, sheet, sheet_id) #:nodoc:
211
+ attributes = [
212
+ 'name', sheet.name,
213
+ 'sheetId', sheet_id
214
+ ]
215
+
216
+ if sheet.hidden?
217
+ attributes << 'state' << 'hidden'
218
+ end
219
+ attributes << 'r:id' << "rId#{sheet_id}"
220
+ writer.empty_tag_encoded('sheet', attributes)
221
+ end
222
+ end
223
+ end
@@ -14,10 +14,6 @@ module Writexlsx
14
14
  class Sparkline
15
15
  include Writexlsx::Utility
16
16
 
17
- attr_accessor :locations, :ranges
18
- attr_accessor :date_axis, :series_color, :negative_color, :markers_color
19
- attr_accessor :first_color, :last_color, :high_color, :low_color
20
-
21
17
  def initialize(ws, param, sheetname)
22
18
  @color = {}
23
19
 
@@ -141,8 +137,148 @@ module Writexlsx
141
137
  a
142
138
  end
143
139
 
140
+ #
141
+ # Write the <x14:sparklineGroup> element.
142
+ #
143
+ # Example for order.
144
+ #
145
+ # <x14:sparklineGroup
146
+ # manualMax="0"
147
+ # manualMin="0"
148
+ # lineWeight="2.25"
149
+ # type="column"
150
+ # dateAxis="1"
151
+ # displayEmptyCellsAs="span"
152
+ # markers="1"
153
+ # high="1"
154
+ # low="1"
155
+ # first="1"
156
+ # last="1"
157
+ # negative="1"
158
+ # displayXAxis="1"
159
+ # displayHidden="1"
160
+ # minAxisType="custom"
161
+ # maxAxisType="custom"
162
+ # rightToLeft="1">
163
+ #
164
+ def write_sparkline_group(writer)
165
+ @writer = writer
166
+
167
+ @writer.tag_elements('x14:sparklineGroup', group_attributes) do
168
+ write
169
+ end
170
+ end
171
+
144
172
  private
145
173
 
174
+ def write
175
+ write_color_series
176
+ write_color_negative
177
+ write_color_axis
178
+ write_color_markers
179
+ write_color_first
180
+ write_color_last
181
+ write_color_high
182
+ write_color_low
183
+ write_xmf_date_axis if @date_axis
184
+ write_sparklines
185
+ end
186
+
187
+ #
188
+ # Write the <x14:colorSeries> element.
189
+ #
190
+ def write_color_series
191
+ write_spark_color('x14:colorSeries', @series_color)
192
+ end
193
+
194
+ #
195
+ # Write the <x14:colorNegative> element.
196
+ #
197
+ def write_color_negative
198
+ write_spark_color('x14:colorNegative', @negative_color)
199
+ end
200
+
201
+ #
202
+ # Write the <x14:colorAxis> element.
203
+ #
204
+ def write_color_axis # :nodoc:
205
+ write_spark_color('x14:colorAxis', { :_rgb => 'FF000000'} )
206
+ end
207
+
208
+ #
209
+ # Write the <x14:colorMarkers> element.
210
+ #
211
+ def write_color_markers # :nodoc:
212
+ write_spark_color('x14:colorMarkers', @markers_color)
213
+ end
214
+
215
+ #
216
+ # Write the <x14:colorFirst> element.
217
+ #
218
+ def write_color_first # :nodoc:
219
+ write_spark_color('x14:colorFirst', @first_color)
220
+ end
221
+
222
+ #
223
+ # Write the <x14:colorLast> element.
224
+ #
225
+ def write_color_last # :nodoc:
226
+ write_spark_color('x14:colorLast', @last_color)
227
+ end
228
+
229
+ #
230
+ # Write the <x14:colorHigh> element.
231
+ #
232
+ def write_color_high # :nodoc:
233
+ write_spark_color('x14:colorHigh', @high_color)
234
+ end
235
+
236
+ #
237
+ # Write the <x14:colorLow> element.
238
+ #
239
+ def write_color_low # :nodoc:
240
+ write_spark_color('x14:colorLow', @low_color)
241
+ end
242
+
243
+ #
244
+ # Write the <xm:f> element.
245
+ #
246
+ def write_xmf_date_axis
247
+ @writer.data_element('xm:f', @date_axis)
248
+ end
249
+
250
+ #
251
+ # Write the <x14:sparklines> element and <x14:sparkline> subelements.
252
+ #
253
+ def write_sparklines # :nodoc:
254
+ # Write the sparkline elements.
255
+ @writer.tag_elements('x14:sparklines') do
256
+
257
+ (0 .. count-1).each do |i|
258
+ range = @ranges[i]
259
+ location = @locations[i]
260
+
261
+ @writer.tag_elements('x14:sparkline') do
262
+ @writer.data_element('xm:f', range)
263
+ @writer.data_element('xm:sqref', location)
264
+ end
265
+ end
266
+ end
267
+ end
268
+
269
+ #
270
+ # Helper function for the sparkline color functions below.
271
+ #
272
+ def write_spark_color(element, color) # :nodoc:
273
+ attr = []
274
+
275
+ attr << 'rgb' << color[:_rgb] if color[:_rgb]
276
+ attr << 'theme' << color[:_theme] if color[:_theme]
277
+ attr << 'tint' << color[:_tint] if color[:_tint]
278
+
279
+ @writer.empty_tag(element, attr)
280
+ end
281
+
146
282
  def set_spark_color(user_color, palette_color)
147
283
  return unless palette_color
148
284
 
@@ -1,5 +1,5 @@
1
1
  require 'write_xlsx/workbook'
2
2
 
3
3
  class WriteXLSX < Writexlsx::Workbook
4
- VERSION = "0.64.1"
4
+ VERSION = "0.65.0"
5
5
  end