write_xlsx 0.64.1 → 0.65.0

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