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.
- 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