write_xlsx 0.58.0 → 0.59.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.
- data/README.rdoc +7 -1
- data/bin/extract_vba.rb +29 -0
- data/examples/add_vba_project.rb +36 -0
- data/examples/vbaProject.bin +0 -0
- data/lib/write_xlsx/chart.rb +22 -37
- data/lib/write_xlsx/package/conditional_format.rb +593 -0
- data/lib/write_xlsx/package/content_types.rb +17 -0
- data/lib/write_xlsx/package/packager.rb +26 -6
- data/lib/write_xlsx/package/relationships.rb +11 -1
- data/lib/write_xlsx/package/table.rb +284 -62
- data/lib/write_xlsx/utility.rb +179 -0
- data/lib/write_xlsx/version.rb +1 -1
- data/lib/write_xlsx/workbook.rb +14 -1
- data/lib/write_xlsx/worksheet.rb +667 -875
- data/test/package/table/test_table01.rb +1 -2
- data/test/package/table/test_table02.rb +1 -2
- data/test/package/table/test_table03.rb +1 -2
- data/test/package/table/test_table04.rb +1 -2
- data/test/package/table/test_table05.rb +1 -2
- data/test/package/table/test_table06.rb +1 -2
- data/test/package/table/test_table07.rb +1 -2
- data/test/package/table/test_table08.rb +1 -2
- data/test/package/table/test_table09.rb +1 -2
- data/test/package/table/test_table10.rb +1 -2
- data/test/package/table/test_table11.rb +1 -2
- data/test/package/table/test_table12.rb +1 -2
- data/test/package/table/test_write_auto_filter.rb +10 -3
- data/test/package/table/test_write_table_column.rb +9 -2
- data/test/package/table/test_write_table_style_info.rb +12 -11
- data/test/package/table/test_write_xml_declaration.rb +6 -1
- data/test/perl_output/add_vba_project.xlsm +0 -0
- data/test/regression/test_macro01.rb +29 -0
- data/test/regression/xlsx_files/macro01.xlsm +0 -0
- data/test/regression/xlsx_files/vbaProject01.bin +0 -0
- data/test/test_example_match.rb +22 -0
- data/test/vbaProject.bin +0 -0
- metadata +18 -3
@@ -0,0 +1,593 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module Writexlsx
|
4
|
+
module Package
|
5
|
+
class ConditionalFormat
|
6
|
+
include Writexlsx::Utility
|
7
|
+
|
8
|
+
def self.factory(worksheet, *args)
|
9
|
+
range, param =
|
10
|
+
Package::ConditionalFormat.new(worksheet, nil, nil).
|
11
|
+
range_param_for_conditional_formatting(*args)
|
12
|
+
|
13
|
+
case param[:type]
|
14
|
+
when 'cellIs'
|
15
|
+
CellIsFormat.new(worksheet, range, param)
|
16
|
+
when 'aboveAverage'
|
17
|
+
AboveAverageFormat.new(worksheet, range, param)
|
18
|
+
when 'top10'
|
19
|
+
Top10Format.new(worksheet, range, param)
|
20
|
+
when 'containsText', 'notContainsText', 'beginsWith', 'endsWith'
|
21
|
+
TextOrWithFormat.new(worksheet, range, param)
|
22
|
+
when 'timePeriod'
|
23
|
+
TimePeriodFormat.new(worksheet, range, param)
|
24
|
+
when 'containsBlanks', 'notContainsBlanks', 'containsErrors', 'notContainsErrors'
|
25
|
+
BlanksOrErrorsFormat.new(worksheet, range, param)
|
26
|
+
when 'colorScale'
|
27
|
+
ColorScaleFormat.new(worksheet, range, param)
|
28
|
+
when 'dataBar'
|
29
|
+
DataBarFormat.new(worksheet, range, param)
|
30
|
+
when 'expression'
|
31
|
+
ExpressionFormat.new(worksheet, range, param)
|
32
|
+
else # when 'duplicateValues', 'uniqueValues'
|
33
|
+
ConditionalFormat.new(worksheet, range, param)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_reader :range
|
38
|
+
|
39
|
+
def initialize(worksheet, range, param)
|
40
|
+
@worksheet, @range, @param = worksheet, range, param
|
41
|
+
@writer = @worksheet.writer
|
42
|
+
end
|
43
|
+
|
44
|
+
def write_cf_rule
|
45
|
+
@writer.empty_tag('cfRule', attributes)
|
46
|
+
end
|
47
|
+
|
48
|
+
def write_cf_rule_formula_tag(tag = formula)
|
49
|
+
@writer.tag_elements('cfRule', attributes) do
|
50
|
+
write_formula_tag(tag)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def write_formula_tag(data) #:nodoc:
|
55
|
+
data = data.sub(/^=/, '') if data.respond_to?(:sub)
|
56
|
+
@writer.data_element('formula', data)
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# Write the <cfvo> element.
|
61
|
+
#
|
62
|
+
def write_cfvo(type, val)
|
63
|
+
@writer.empty_tag('cfvo', ['type', type, 'val', val])
|
64
|
+
end
|
65
|
+
|
66
|
+
def attributes
|
67
|
+
attr = ['type' , type]
|
68
|
+
attr << 'dxfId' << format if format
|
69
|
+
attr << 'priority' << priority
|
70
|
+
attr
|
71
|
+
end
|
72
|
+
|
73
|
+
def type
|
74
|
+
@param[:type]
|
75
|
+
end
|
76
|
+
|
77
|
+
def format
|
78
|
+
@param[:format]
|
79
|
+
end
|
80
|
+
|
81
|
+
def priority
|
82
|
+
@param[:priority]
|
83
|
+
end
|
84
|
+
|
85
|
+
def criteria
|
86
|
+
@param[:criteria]
|
87
|
+
end
|
88
|
+
|
89
|
+
def maximum
|
90
|
+
@param[:maximum]
|
91
|
+
end
|
92
|
+
|
93
|
+
def minimum
|
94
|
+
@param[:minimum]
|
95
|
+
end
|
96
|
+
|
97
|
+
def value
|
98
|
+
@param[:value]
|
99
|
+
end
|
100
|
+
|
101
|
+
def direction
|
102
|
+
@param[:direction]
|
103
|
+
end
|
104
|
+
|
105
|
+
def formula
|
106
|
+
@param[:formula]
|
107
|
+
end
|
108
|
+
|
109
|
+
def min_type
|
110
|
+
@param[:min_type]
|
111
|
+
end
|
112
|
+
def min_value
|
113
|
+
@param[:min_value]
|
114
|
+
end
|
115
|
+
|
116
|
+
def min_color
|
117
|
+
@param[:min_color]
|
118
|
+
end
|
119
|
+
|
120
|
+
def mid_type
|
121
|
+
@param[:mid_type]
|
122
|
+
end
|
123
|
+
|
124
|
+
def mid_value
|
125
|
+
@param[:mid_value]
|
126
|
+
end
|
127
|
+
|
128
|
+
def mid_color
|
129
|
+
@param[:mid_color]
|
130
|
+
end
|
131
|
+
|
132
|
+
def max_type
|
133
|
+
@param[:max_type]
|
134
|
+
end
|
135
|
+
|
136
|
+
def max_value
|
137
|
+
@param[:max_value]
|
138
|
+
end
|
139
|
+
|
140
|
+
def max_color
|
141
|
+
@param[:max_color]
|
142
|
+
end
|
143
|
+
|
144
|
+
def bar_color
|
145
|
+
@param[:bar_color]
|
146
|
+
end
|
147
|
+
|
148
|
+
def range_param_for_conditional_formatting(*args) # :nodoc:
|
149
|
+
range_start_cell_for_conditional_formatting(*args)
|
150
|
+
param_for_conditional_formatting(*args)
|
151
|
+
|
152
|
+
handling_of_text_criteria if @param[:type] == 'text'
|
153
|
+
handling_of_time_period_criteria if @param[:type] == 'timePeriod'
|
154
|
+
handling_of_blanks_error_types
|
155
|
+
|
156
|
+
[@range, @param]
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def handling_of_text_criteria
|
162
|
+
case @param[:criteria]
|
163
|
+
when 'containsText'
|
164
|
+
@param[:type] = 'containsText';
|
165
|
+
@param[:formula] =
|
166
|
+
%Q!NOT(ISERROR(SEARCH("#{@param[:value]}",#{@start_cell})))!
|
167
|
+
when 'notContains'
|
168
|
+
@param[:type] = 'notContainsText';
|
169
|
+
@param[:formula] =
|
170
|
+
%Q!ISERROR(SEARCH("#{@param[:value]}",#{@start_cell}))!
|
171
|
+
when 'beginsWith'
|
172
|
+
@param[:type] = 'beginsWith'
|
173
|
+
@param[:formula] =
|
174
|
+
%Q!LEFT(#{@start_cell},#{@param[:value].size})="#{@param[:value]}"!
|
175
|
+
when 'endsWith'
|
176
|
+
@param[:type] = 'endsWith'
|
177
|
+
@param[:formula] =
|
178
|
+
%Q!RIGHT(#{@start_cell},#{@param[:value].size})="#{@param[:value]}"!
|
179
|
+
else
|
180
|
+
raise "Invalid text criteria '#{@param[:criteria]} in conditional_formatting()"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def handling_of_time_period_criteria
|
185
|
+
case @param[:criteria]
|
186
|
+
when 'yesterday'
|
187
|
+
@param[:formula] = "FLOOR(#{@start_cell},1)=TODAY()-1"
|
188
|
+
when 'today'
|
189
|
+
@param[:formula] = "FLOOR(#{@start_cell},1)=TODAY()"
|
190
|
+
when 'tomorrow'
|
191
|
+
@param[:formula] = "FLOOR(#{@start_cell},1)=TODAY()+1"
|
192
|
+
when 'last7Days'
|
193
|
+
@param[:formula] =
|
194
|
+
"AND(TODAY()-FLOOR(#{@start_cell},1)<=6,FLOOR(#{@start_cell},1)<=TODAY())"
|
195
|
+
when 'lastWeek'
|
196
|
+
@param[:formula] =
|
197
|
+
"AND(TODAY()-ROUNDDOWN(#{@start_cell},0)>=(WEEKDAY(TODAY())),TODAY()-ROUNDDOWN(#{@start_cell},0)<(WEEKDAY(TODAY())+7))"
|
198
|
+
when 'thisWeek'
|
199
|
+
@param[:formula] =
|
200
|
+
"AND(TODAY()-ROUNDDOWN(#{@start_cell},0)<=WEEKDAY(TODAY())-1,ROUNDDOWN(#{@start_cell},0)-TODAY()<=7-WEEKDAY(TODAY()))"
|
201
|
+
when 'nextWeek'
|
202
|
+
@param[:formula] =
|
203
|
+
"AND(ROUNDDOWN(#{@start_cell},0)-TODAY()>(7-WEEKDAY(TODAY())),ROUNDDOWN(#{@start_cell},0)-TODAY()<(15-WEEKDAY(TODAY())))"
|
204
|
+
when 'lastMonth'
|
205
|
+
@param[:formula] =
|
206
|
+
"AND(MONTH(#{@start_cell})=MONTH(TODAY())-1,OR(YEAR(#{@start_cell})=YEAR(TODAY()),AND(MONTH(#{@start_cell})=1,YEAR(A1)=YEAR(TODAY())-1)))"
|
207
|
+
when 'thisMonth'
|
208
|
+
@param[:formula] =
|
209
|
+
"AND(MONTH(#{@start_cell})=MONTH(TODAY()),YEAR(#{@start_cell})=YEAR(TODAY()))"
|
210
|
+
when 'nextMonth'
|
211
|
+
@param[:formula] =
|
212
|
+
"AND(MONTH(#{@start_cell})=MONTH(TODAY())+1,OR(YEAR(#{@start_cell})=YEAR(TODAY()),AND(MONTH(#{@start_cell})=12,YEAR(#{@start_cell})=YEAR(TODAY())+1)))"
|
213
|
+
else
|
214
|
+
raise "Invalid time_period criteria '#{@param[:criteria]}' in conditional_formatting()"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def handling_of_blanks_error_types
|
219
|
+
# Special handling of blanks/error types.
|
220
|
+
case @param[:type]
|
221
|
+
when 'containsBlanks'
|
222
|
+
@param[:formula] = "LEN(TRIM(#{@start_cell}))=0"
|
223
|
+
when 'notContainsBlanks'
|
224
|
+
@param[:formula] = "LEN(TRIM(#{@start_cell}))>0"
|
225
|
+
when 'containsErrors'
|
226
|
+
@param[:formula] = "ISERROR(#{@start_cell})"
|
227
|
+
when 'notContainsErrors'
|
228
|
+
@param[:formula] = "NOT(ISERROR(#{@start_cell}))"
|
229
|
+
when '2_color_scale'
|
230
|
+
@param[:type] = 'colorScale'
|
231
|
+
|
232
|
+
# Color scales don't use any additional formatting.
|
233
|
+
@param[:format] = nil
|
234
|
+
|
235
|
+
# Turn off 3 color parameters.
|
236
|
+
@param[:mid_type] = nil
|
237
|
+
@param[:mid_color] = nil
|
238
|
+
|
239
|
+
@param[:min_type] ||= 'min'
|
240
|
+
@param[:max_type] ||= 'max'
|
241
|
+
@param[:min_value] ||= 0
|
242
|
+
@param[:max_value] ||= 0
|
243
|
+
@param[:min_color] ||= '#FF7128'
|
244
|
+
@param[:max_color] ||= '#FFEF9C'
|
245
|
+
|
246
|
+
@param[:max_color] = get_palette_color( @param[:max_color] )
|
247
|
+
@param[:min_color] = get_palette_color( @param[:min_color] )
|
248
|
+
when '3_color_scale'
|
249
|
+
@param[:type] = 'colorScale'
|
250
|
+
|
251
|
+
# Color scales don't use any additional formatting.
|
252
|
+
@param[:format] = nil
|
253
|
+
|
254
|
+
@param[:min_type] ||= 'min'
|
255
|
+
@param[:mid_type] ||= 'percentile'
|
256
|
+
@param[:max_type] ||= 'max'
|
257
|
+
@param[:min_value] ||= 0
|
258
|
+
@param[:mid_value] ||= 50
|
259
|
+
@param[:max_value] ||= 0
|
260
|
+
@param[:min_color] ||= '#F8696B'
|
261
|
+
@param[:mid_color] ||= '#FFEB84'
|
262
|
+
@param[:max_color] ||= '#63BE7B'
|
263
|
+
|
264
|
+
@param[:max_color] = get_palette_color(@param[:max_color])
|
265
|
+
@param[:mid_color] = get_palette_color(@param[:mid_color])
|
266
|
+
@param[:min_color] = get_palette_color(@param[:min_color])
|
267
|
+
when 'dataBar'
|
268
|
+
# Color scales don't use any additional formatting.
|
269
|
+
@param[:format] = nil
|
270
|
+
|
271
|
+
@param[:min_type] ||= 'min'
|
272
|
+
@param[:max_type] ||= 'max'
|
273
|
+
@param[:min_value] ||= 0
|
274
|
+
@param[:max_value] ||= 0
|
275
|
+
@param[:bar_color] ||= '#638EC6'
|
276
|
+
|
277
|
+
@param[:bar_color] = get_palette_color(@param[:bar_color])
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
def get_palette_color(index)
|
282
|
+
@worksheet.get_palette_color(index)
|
283
|
+
end
|
284
|
+
|
285
|
+
def range_start_cell_for_conditional_formatting(*args) # :nodoc:
|
286
|
+
row1, row2, col1, col2, user_range, param =
|
287
|
+
row_col_param_for_conditional_formatting(*args)
|
288
|
+
# If the first and last cell are the same write a single cell.
|
289
|
+
if row1 == row2 && col1 == col2
|
290
|
+
range = xl_rowcol_to_cell(row1, col1)
|
291
|
+
@start_cell = range
|
292
|
+
else
|
293
|
+
range = xl_range(row1, row2, col1, col2)
|
294
|
+
@start_cell = xl_rowcol_to_cell(row1, col1)
|
295
|
+
end
|
296
|
+
|
297
|
+
# Override with user defined multiple range if provided.
|
298
|
+
range = user_range if user_range
|
299
|
+
|
300
|
+
@range = range
|
301
|
+
end
|
302
|
+
|
303
|
+
def row_col_param_for_conditional_formatting(*args)
|
304
|
+
# Check for a cell reference in A1 notation and substitute row and column
|
305
|
+
if args[0] =~ /^\D/
|
306
|
+
# Check for a user defined multiple range like B3:K6,B8:K11.
|
307
|
+
user_range = args[0].gsub(/\s*,\s*/, ' ').gsub(/\$/, '') if args[0] =~ /,/
|
308
|
+
end
|
309
|
+
|
310
|
+
row1, col1, row2, col2, param = row_col_notation(args)
|
311
|
+
if row2.respond_to?(:keys)
|
312
|
+
param = row2
|
313
|
+
row2, col2 = row1, col1
|
314
|
+
end
|
315
|
+
raise WriteXLSXInsufficientArgumentError if [row1, col1, row2, col2, param].include?(nil)
|
316
|
+
|
317
|
+
# Check that row and col are valid without storing the values.
|
318
|
+
check_dimensions(row1, col1)
|
319
|
+
check_dimensions(row2, col2)
|
320
|
+
|
321
|
+
# Swap last row/col for first row/col as necessary
|
322
|
+
row1, row2 = row2, row1 if row1 > row2
|
323
|
+
col1, col2 = col2, col1 if col1 > col2
|
324
|
+
|
325
|
+
[row1, row2, col1, col2, user_range, param]
|
326
|
+
end
|
327
|
+
|
328
|
+
def param_for_conditional_formatting(*args) # :nodoc:
|
329
|
+
dummy, dummy, dummy, dummy, dummy, @param =
|
330
|
+
row_col_param_for_conditional_formatting(*args)
|
331
|
+
check_conditional_formatting_parameters(@param)
|
332
|
+
|
333
|
+
@param[:format] = @param[:format].get_dxf_index if @param[:format]
|
334
|
+
@param[:priority] = @worksheet.dxf_priority
|
335
|
+
@worksheet.dxf_priority += 1
|
336
|
+
end
|
337
|
+
|
338
|
+
def check_conditional_formatting_parameters(param) # :nodoc:
|
339
|
+
# Check for valid input parameters.
|
340
|
+
unless (param.keys.uniq - valid_parameter_for_conditional_formatting).empty? &&
|
341
|
+
param.has_key?(:type) &&
|
342
|
+
valid_type_for_conditional_formatting.has_key?(param[:type].downcase)
|
343
|
+
raise WriteXLSXOptionParameterError, "Invalid type : #{param[:type]}"
|
344
|
+
end
|
345
|
+
|
346
|
+
param[:direction] = 'bottom' if param[:type] == 'bottom'
|
347
|
+
param[:type] = valid_type_for_conditional_formatting[param[:type].downcase]
|
348
|
+
|
349
|
+
# Check for valid criteria types.
|
350
|
+
if param.has_key?(:criteria) && valid_criteria_type_for_conditional_formatting.has_key?(param[:criteria].downcase)
|
351
|
+
param[:criteria] = valid_criteria_type_for_conditional_formatting[param[:criteria].downcase]
|
352
|
+
end
|
353
|
+
|
354
|
+
# Convert date/times value if required.
|
355
|
+
if %w[date time cellIs].include?(param[:type])
|
356
|
+
param[:type] = 'cellIs'
|
357
|
+
|
358
|
+
param[:value] = convert_date_time_if_required(param[:value])
|
359
|
+
param[:minimum] = convert_date_time_if_required(param[:minimum])
|
360
|
+
param[:maximum] = convert_date_time_if_required(param[:maximum])
|
361
|
+
end
|
362
|
+
|
363
|
+
# 'Between' and 'Not between' criteria require 2 values.
|
364
|
+
if param[:criteria] == 'between' || param[:criteria] == 'notBetween'
|
365
|
+
unless param.has_key?(:minimum) || param.has_key?(:maximum)
|
366
|
+
raise WriteXLSXOptionParameterError, "Invalid criteria : #{param[:criteria]}"
|
367
|
+
end
|
368
|
+
else
|
369
|
+
param[:minimum] = nil
|
370
|
+
param[:maximum] = nil
|
371
|
+
end
|
372
|
+
|
373
|
+
# Convert date/times value if required.
|
374
|
+
if param[:type] == 'date' || param[:type] == 'time'
|
375
|
+
unless convert_date_time_value(param, :value) || convert_date_time_value(param, :maximum)
|
376
|
+
raise WriteXLSXOptionParameterError
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
def convert_date_time_if_required(val)
|
382
|
+
if val =~ /T/
|
383
|
+
date_time = convert_date_time(val)
|
384
|
+
raise "Invalid date/time value '#{val}' in conditional_formatting()" unless date_time
|
385
|
+
date_time
|
386
|
+
else
|
387
|
+
val
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
# List of valid input parameters for conditional_formatting.
|
392
|
+
def valid_parameter_for_conditional_formatting
|
393
|
+
[
|
394
|
+
:type,
|
395
|
+
:format,
|
396
|
+
:criteria,
|
397
|
+
:value,
|
398
|
+
:minimum,
|
399
|
+
:maximum,
|
400
|
+
:min_type,
|
401
|
+
:mid_type,
|
402
|
+
:max_type,
|
403
|
+
:min_value,
|
404
|
+
:mid_value,
|
405
|
+
:max_value,
|
406
|
+
:min_color,
|
407
|
+
:mid_color,
|
408
|
+
:max_color,
|
409
|
+
:bar_color
|
410
|
+
]
|
411
|
+
end
|
412
|
+
|
413
|
+
# List of valid validation types for conditional_formatting.
|
414
|
+
def valid_type_for_conditional_formatting
|
415
|
+
{
|
416
|
+
'cell' => 'cellIs',
|
417
|
+
'date' => 'date',
|
418
|
+
'time' => 'time',
|
419
|
+
'average' => 'aboveAverage',
|
420
|
+
'duplicate' => 'duplicateValues',
|
421
|
+
'unique' => 'uniqueValues',
|
422
|
+
'top' => 'top10',
|
423
|
+
'bottom' => 'top10',
|
424
|
+
'text' => 'text',
|
425
|
+
'time_period' => 'timePeriod',
|
426
|
+
'blanks' => 'containsBlanks',
|
427
|
+
'no_blanks' => 'notContainsBlanks',
|
428
|
+
'errors' => 'containsErrors',
|
429
|
+
'no_errors' => 'notContainsErrors',
|
430
|
+
'2_color_scale' => '2_color_scale',
|
431
|
+
'3_color_scale' => '3_color_scale',
|
432
|
+
'data_bar' => 'dataBar',
|
433
|
+
'formula' => 'expression'
|
434
|
+
}
|
435
|
+
end
|
436
|
+
|
437
|
+
# List of valid criteria types for conditional_formatting.
|
438
|
+
def valid_criteria_type_for_conditional_formatting
|
439
|
+
{
|
440
|
+
'between' => 'between',
|
441
|
+
'not between' => 'notBetween',
|
442
|
+
'equal to' => 'equal',
|
443
|
+
'=' => 'equal',
|
444
|
+
'==' => 'equal',
|
445
|
+
'not equal to' => 'notEqual',
|
446
|
+
'!=' => 'notEqual',
|
447
|
+
'<>' => 'notEqual',
|
448
|
+
'greater than' => 'greaterThan',
|
449
|
+
'>' => 'greaterThan',
|
450
|
+
'less than' => 'lessThan',
|
451
|
+
'<' => 'lessThan',
|
452
|
+
'greater than or equal to' => 'greaterThanOrEqual',
|
453
|
+
'>=' => 'greaterThanOrEqual',
|
454
|
+
'less than or equal to' => 'lessThanOrEqual',
|
455
|
+
'<=' => 'lessThanOrEqual',
|
456
|
+
'containing' => 'containsText',
|
457
|
+
'not containing' => 'notContains',
|
458
|
+
'begins with' => 'beginsWith',
|
459
|
+
'ends with' => 'endsWith',
|
460
|
+
'yesterday' => 'yesterday',
|
461
|
+
'today' => 'today',
|
462
|
+
'last 7 days' => 'last7Days',
|
463
|
+
'last week' => 'lastWeek',
|
464
|
+
'this week' => 'thisWeek',
|
465
|
+
'next week' => 'nextWeek',
|
466
|
+
'last month' => 'lastMonth',
|
467
|
+
'this month' => 'thisMonth',
|
468
|
+
'next month' => 'nextMonth'
|
469
|
+
}
|
470
|
+
end
|
471
|
+
|
472
|
+
def date_1904?
|
473
|
+
@worksheet.date_1904?
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
class CellIsFormat < ConditionalFormat
|
478
|
+
def attributes
|
479
|
+
super << 'operator' << criteria
|
480
|
+
end
|
481
|
+
|
482
|
+
def write_cf_rule
|
483
|
+
if minimum && maximum
|
484
|
+
@writer.tag_elements('cfRule', attributes) do
|
485
|
+
write_formula_tag(minimum)
|
486
|
+
write_formula_tag(maximum)
|
487
|
+
end
|
488
|
+
else
|
489
|
+
write_cf_rule_formula_tag(value)
|
490
|
+
end
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
class AboveAverageFormat < ConditionalFormat
|
495
|
+
def attributes
|
496
|
+
attr = super
|
497
|
+
attr << 'aboveAverage' << 0 if criteria =~ /below/
|
498
|
+
attr << 'equalAverage' << 1 if criteria =~ /equal/
|
499
|
+
if criteria =~ /([123]) std dev/
|
500
|
+
attr << 'stdDev' << $~[1]
|
501
|
+
end
|
502
|
+
attr
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
class Top10Format < ConditionalFormat
|
507
|
+
def attributes
|
508
|
+
attr = super
|
509
|
+
attr << 'percent' << 1 if criteria == '%'
|
510
|
+
attr << 'bottom' << 1 if direction
|
511
|
+
attr << 'rank' << (value || 10)
|
512
|
+
attr
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
class TextOrWithFormat < ConditionalFormat
|
517
|
+
def attributes
|
518
|
+
attr = super
|
519
|
+
attr << 'operator' << criteria
|
520
|
+
attr << 'text' << value
|
521
|
+
attr
|
522
|
+
end
|
523
|
+
|
524
|
+
def write_cf_rule
|
525
|
+
write_cf_rule_formula_tag
|
526
|
+
end
|
527
|
+
end
|
528
|
+
|
529
|
+
class TimePeriodFormat < ConditionalFormat
|
530
|
+
def attributes
|
531
|
+
super << 'timePeriod' << criteria
|
532
|
+
end
|
533
|
+
|
534
|
+
def write_cf_rule
|
535
|
+
write_cf_rule_formula_tag
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
class BlanksOrErrorsFormat < ConditionalFormat
|
540
|
+
def write_cf_rule
|
541
|
+
write_cf_rule_formula_tag
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
545
|
+
class ColorScaleFormat < ConditionalFormat
|
546
|
+
def write_cf_rule
|
547
|
+
@writer.tag_elements('cfRule', attributes) do
|
548
|
+
write_color_scale
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
#
|
553
|
+
# Write the <colorScale> element.
|
554
|
+
#
|
555
|
+
def write_color_scale
|
556
|
+
@writer.tag_elements('colorScale') do
|
557
|
+
write_cfvo(min_type, min_value)
|
558
|
+
write_cfvo(mid_type, mid_value) if mid_type
|
559
|
+
write_cfvo(max_type, max_value)
|
560
|
+
write_color(@writer, 'rgb', min_color)
|
561
|
+
write_color(@writer, 'rgb', mid_color) if mid_color
|
562
|
+
write_color(@writer, 'rgb', max_color)
|
563
|
+
end
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
567
|
+
class DataBarFormat < ConditionalFormat
|
568
|
+
def write_cf_rule
|
569
|
+
@writer.tag_elements('cfRule', attributes) do
|
570
|
+
write_data_bar
|
571
|
+
end
|
572
|
+
end
|
573
|
+
|
574
|
+
#
|
575
|
+
# Write the <dataBar> element.
|
576
|
+
#
|
577
|
+
def write_data_bar
|
578
|
+
@writer.tag_elements('dataBar') do
|
579
|
+
write_cfvo(min_type, min_value)
|
580
|
+
write_cfvo(max_type, max_value)
|
581
|
+
|
582
|
+
write_color(@writer, 'rgb', bar_color)
|
583
|
+
end
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
587
|
+
class ExpressionFormat < ConditionalFormat
|
588
|
+
def write_cf_rule
|
589
|
+
write_cf_rule_formula_tag(criteria)
|
590
|
+
end
|
591
|
+
end
|
592
|
+
end
|
593
|
+
end
|
@@ -137,12 +137,29 @@ module Writexlsx
|
|
137
137
|
)
|
138
138
|
end
|
139
139
|
|
140
|
+
#
|
141
|
+
# Add a vbaProject to the ContentTypes defaults.
|
142
|
+
#
|
143
|
+
def add_vba_project
|
144
|
+
change_the_workbook_xml_content_type_from_xlsx_to_xlsm
|
145
|
+
add_default('bin', 'application/vnd.ms-office.vbaProject')
|
146
|
+
end
|
147
|
+
|
140
148
|
private
|
141
149
|
|
142
150
|
def write_xml_declaration
|
143
151
|
@writer.xml_decl
|
144
152
|
end
|
145
153
|
|
154
|
+
def change_the_workbook_xml_content_type_from_xlsx_to_xlsm
|
155
|
+
@overrides.collect! do |arr|
|
156
|
+
if arr[0] == '/xl/workbook.xml'
|
157
|
+
arr[1] = 'application/vnd.ms-excel.sheet.macroEnabled.main+xml'
|
158
|
+
end
|
159
|
+
arr
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
146
163
|
#
|
147
164
|
# Write out all of the <Default> types.
|
148
165
|
#
|
@@ -78,6 +78,7 @@ module Writexlsx
|
|
78
78
|
write_chartsheet_rels_files
|
79
79
|
write_drawing_rels_files
|
80
80
|
add_image_files
|
81
|
+
add_vba_project
|
81
82
|
end
|
82
83
|
|
83
84
|
private
|
@@ -281,6 +282,9 @@ module Writexlsx
|
|
281
282
|
# Add the sharedString rel if there is string data in the workbook.
|
282
283
|
content.add_shared_strings unless @workbook.shared_strings_empty?
|
283
284
|
|
285
|
+
# Add vbaProject if present.
|
286
|
+
content.add_vba_project if @workbook.vba_project
|
287
|
+
|
284
288
|
content.set_xml_writer("#{@package_dir}/[Content_Types].xml")
|
285
289
|
content.assemble_xml_file
|
286
290
|
end
|
@@ -344,11 +348,8 @@ module Writexlsx
|
|
344
348
|
|
345
349
|
FileUtils.mkdir_p("#{dir}/xl/tables")
|
346
350
|
|
347
|
-
table_props.each do |
|
348
|
-
table = Package::Table.new
|
349
|
-
|
351
|
+
table_props.each do |table|
|
350
352
|
table.set_xml_writer("#{dir}/xl/tables/table#{index}.xml")
|
351
|
-
table.set_properties(table_prop)
|
352
353
|
table.assemble_xml_file
|
353
354
|
index += 1
|
354
355
|
@table_count += 1
|
@@ -366,7 +367,7 @@ module Writexlsx
|
|
366
367
|
|
367
368
|
rels.add_document_relationship('/officeDocument', 'xl/workbook.xml')
|
368
369
|
rels.add_package_relationship('/metadata/core-properties',
|
369
|
-
'docProps/core')
|
370
|
+
'docProps/core.xml')
|
370
371
|
rels.add_document_relationship('/extended-properties', 'docProps/app.xml')
|
371
372
|
rels.set_xml_writer("#{@package_dir}/_rels/.rels" )
|
372
373
|
rels.assemble_xml_file
|
@@ -398,6 +399,12 @@ module Writexlsx
|
|
398
399
|
|
399
400
|
# Add the sharedString rel if there is string data in the workbook.
|
400
401
|
rels.add_document_relationship('/sharedStrings', 'sharedStrings.xml') unless @workbook.shared_strings_empty?
|
402
|
+
|
403
|
+
# Add vbaProject if present.
|
404
|
+
if @workbook.vba_project
|
405
|
+
rels.add_ms_package_relationship('/vbaProject', 'vbaProject.bin')
|
406
|
+
end
|
407
|
+
|
401
408
|
rels.set_xml_writer("#{@package_dir}/xl/_rels/workbook.xml.rels")
|
402
409
|
rels.assemble_xml_file
|
403
410
|
end
|
@@ -508,7 +515,7 @@ module Writexlsx
|
|
508
515
|
|
509
516
|
|
510
517
|
#
|
511
|
-
# Write the
|
518
|
+
# Write the /xl/media/image?.xml files.
|
512
519
|
#
|
513
520
|
def add_image_files
|
514
521
|
return if @workbook.images.empty?
|
@@ -525,6 +532,19 @@ module Writexlsx
|
|
525
532
|
index += 1
|
526
533
|
end
|
527
534
|
end
|
535
|
+
|
536
|
+
#
|
537
|
+
# Write the vbaProject.bin file.
|
538
|
+
#
|
539
|
+
def add_vba_project
|
540
|
+
dir = @package_dir
|
541
|
+
vba_project = @workbook.vba_project
|
542
|
+
|
543
|
+
return unless vba_project
|
544
|
+
|
545
|
+
FileUtils.mkdir_p("#{dir}/xl")
|
546
|
+
FileUtils.copy(vba_project, "#{dir}/xl/vbaProject.bin")
|
547
|
+
end
|
528
548
|
end
|
529
549
|
end
|
530
550
|
end
|