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.
- checksums.yaml +4 -4
- data/README.rdoc +10 -1
- data/examples/conditional_format.rb +251 -18
- data/examples/demo.rb +2 -3
- data/examples/macros.rb +42 -0
- data/examples/outline_collapsed.rb +160 -0
- data/examples/republic.png +0 -0
- data/examples/shape3.rb +2 -2
- data/examples/shape4.rb +5 -5
- data/examples/shape5.rb +6 -6
- data/examples/shape6.rb +6 -6
- data/examples/shape7.rb +11 -11
- data/examples/shape8.rb +10 -10
- data/examples/shape_all.rb +0 -0
- data/examples/vbaProject.bin +0 -0
- data/lib/write_xlsx/chart.rb +656 -56
- data/lib/write_xlsx/chartsheet.rb +26 -2
- data/lib/write_xlsx/format.rb +50 -27
- data/lib/write_xlsx/formats.rb +32 -0
- data/lib/write_xlsx/package/packager.rb +45 -238
- data/lib/write_xlsx/package/table.rb +9 -18
- data/lib/write_xlsx/package/xml_writer_simple.rb +26 -9
- data/lib/write_xlsx/sheets.rb +223 -0
- data/lib/write_xlsx/sparkline.rb +140 -4
- data/lib/write_xlsx/version.rb +1 -1
- data/lib/write_xlsx/workbook.rb +34 -121
- data/lib/write_xlsx/worksheet/data_validation.rb +291 -0
- data/lib/write_xlsx/worksheet/hyperlink.rb +111 -0
- data/lib/write_xlsx/worksheet/page_setup.rb +170 -0
- data/lib/write_xlsx/worksheet.rb +1112 -1334
- data/test/helper.rb +1 -1
- data/test/package/styles/test_styles_01.rb +1 -10
- data/test/package/styles/test_styles_02.rb +1 -10
- data/test/package/styles/test_styles_03.rb +1 -10
- data/test/package/styles/test_styles_04.rb +1 -10
- data/test/package/styles/test_styles_05.rb +1 -10
- data/test/package/styles/test_styles_06.rb +1 -10
- data/test/package/styles/test_styles_07.rb +1 -10
- data/test/package/styles/test_styles_08.rb +1 -10
- data/test/package/styles/test_styles_09.rb +1 -10
- data/test/perl_output/conditional_format.xlsx +0 -0
- data/test/perl_output/outline_collapsed.xlsx +0 -0
- data/test/perl_output/protection.xlsx +0 -0
- data/test/regression/test_chart_gap01.rb +47 -0
- data/test/regression/test_chart_gap02.rb +47 -0
- data/test/regression/test_chart_gap03.rb +47 -0
- data/test/regression/test_format05.rb +26 -0
- data/test/regression/test_rich_string12.rb +32 -0
- data/test/regression/xlsx_files/chart_gap01.xlsx +0 -0
- data/test/regression/xlsx_files/chart_gap02.xlsx +0 -0
- data/test/regression/xlsx_files/chart_gap03.xlsx +0 -0
- data/test/regression/xlsx_files/format05.xlsx +0 -0
- data/test/regression/xlsx_files/rich_string12.xlsx +0 -0
- data/test/test_example_match.rb +253 -20
- data/test/worksheet/test_set_column.rb +25 -0
- data/test/worksheet/test_worksheet_03.rb +1 -1
- data/test/worksheet/test_worksheet_04.rb +1 -1
- data/test/worksheet/test_write_array_formula_01.rb +7 -0
- data/test/worksheet/test_write_col_breaks.rb +2 -2
- data/test/worksheet/test_write_col_info.rb +8 -8
- data/test/worksheet/test_write_conditional_formatting.rb +4 -4
- data/test/worksheet/test_write_formula_does_not_change_formula_string.rb +18 -0
- data/test/worksheet/test_write_header_footer.rb +8 -3
- data/test/worksheet/test_write_hyperlink.rb +10 -5
- data/test/worksheet/test_write_merge_cells.rb +6 -6
- data/test/worksheet/test_write_page_set_up_pr.rb +1 -1
- data/test/worksheet/test_write_page_setup.rb +1 -1
- data/test/worksheet/test_write_row_breaks.rb +2 -2
- data/test/worksheet/test_write_row_element.rb +1 -1
- data/test/worksheet/test_write_sheet_pr.rb +2 -2
- data/test/worksheet/test_write_sheet_view.rb +0 -9
- data/test/worksheet/test_write_url.rb +19 -0
- data/test/worksheet/test_write_worksheet_attributes.rb +21 -0
- metadata +38 -5
- data/lib/write_xlsx/worksheet/print_style.rb +0 -51
- 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
|
-
|
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
|
-
|
33
|
-
|
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
|
-
|
38
|
-
|
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
|
-
|
48
|
-
|
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
|
data/lib/write_xlsx/sparkline.rb
CHANGED
@@ -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
|
|
data/lib/write_xlsx/version.rb
CHANGED