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
data/lib/write_xlsx/workbook.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
require 'write_xlsx/package/xml_writer_simple'
|
3
3
|
require 'write_xlsx/package/packager'
|
4
|
+
require 'write_xlsx/sheets'
|
4
5
|
require 'write_xlsx/worksheet'
|
5
6
|
require 'write_xlsx/chartsheet'
|
7
|
+
require 'write_xlsx/formats'
|
6
8
|
require 'write_xlsx/format'
|
7
9
|
require 'write_xlsx/shape'
|
8
10
|
require 'write_xlsx/utility'
|
@@ -14,6 +16,8 @@ require 'digest/md5'
|
|
14
16
|
|
15
17
|
module Writexlsx
|
16
18
|
|
19
|
+
OFFICE_URL = 'http://schemas.microsoft.com/office/' # :nodoc:
|
20
|
+
|
17
21
|
# The WriteXLSX provides an object oriented interface to a new Excel workbook.
|
18
22
|
# The following methods are available through a new workbook.
|
19
23
|
#
|
@@ -34,12 +38,9 @@ module Writexlsx
|
|
34
38
|
|
35
39
|
include Writexlsx::Utility
|
36
40
|
|
37
|
-
BASE_NAME = { :sheet => 'Sheet', :chart => 'Chart'} # :nodoc:
|
38
|
-
|
39
41
|
attr_writer :firstsheet # :nodoc:
|
40
42
|
attr_reader :palette # :nodoc:
|
41
|
-
attr_reader :
|
42
|
-
attr_reader :worksheets, :sheetnames, :charts, :drawings # :nodoc:
|
43
|
+
attr_reader :worksheets, :charts, :drawings # :nodoc:
|
43
44
|
attr_reader :num_comment_files, :num_vml_files, :named_ranges # :nodoc:
|
44
45
|
attr_reader :doc_properties # :nodoc:
|
45
46
|
attr_reader :image_types, :images # :nodoc:
|
@@ -97,19 +98,12 @@ module Writexlsx
|
|
97
98
|
@firstsheet = 0
|
98
99
|
@selected = 0
|
99
100
|
@fileclosed = false
|
100
|
-
@
|
101
|
-
@chart_name = 'Chart'
|
102
|
-
@sheetname_count = 0
|
103
|
-
@chartname_count = 0
|
104
|
-
@worksheets = []
|
101
|
+
@worksheets = Sheets.new
|
105
102
|
@charts = []
|
106
103
|
@drawings = []
|
107
|
-
@
|
108
|
-
@formats = []
|
104
|
+
@formats = Formats.new
|
109
105
|
@xf_formats = []
|
110
|
-
@xf_format_indices = {}
|
111
106
|
@dxf_formats = []
|
112
|
-
@dxf_format_indices = {}
|
113
107
|
@font_count = 0
|
114
108
|
@num_format_count = 0
|
115
109
|
@defined_names = []
|
@@ -265,7 +259,7 @@ module Writexlsx
|
|
265
259
|
write_book_views
|
266
260
|
|
267
261
|
# Write the worksheet names and ids.
|
268
|
-
write_sheets
|
262
|
+
@worksheets.write_sheets(@writer)
|
269
263
|
|
270
264
|
# Write the workbook defined names.
|
271
265
|
write_defined_names
|
@@ -305,7 +299,6 @@ module Writexlsx
|
|
305
299
|
name = check_sheetname(name)
|
306
300
|
worksheet = Worksheet.new(self, @worksheets.size, name)
|
307
301
|
@worksheets << worksheet
|
308
|
-
@sheetnames << name
|
309
302
|
worksheet
|
310
303
|
end
|
311
304
|
|
@@ -403,7 +396,6 @@ module Writexlsx
|
|
403
396
|
chartsheet = Chartsheet.new(self, @worksheets.size, sheetname)
|
404
397
|
chartsheet.chart = chart
|
405
398
|
@worksheets << chartsheet
|
406
|
-
@sheetnames << sheetname
|
407
399
|
end
|
408
400
|
@charts << chart
|
409
401
|
ptrue?(embedded) ? chart : chartsheet
|
@@ -422,15 +414,9 @@ module Writexlsx
|
|
422
414
|
# Format properties and how to set them.
|
423
415
|
#
|
424
416
|
def add_format(properties = {})
|
425
|
-
|
426
|
-
@xf_format_indices,
|
427
|
-
@dxf_format_indices,
|
428
|
-
properties
|
429
|
-
]
|
417
|
+
format = Format.new(@formats, properties)
|
430
418
|
|
431
|
-
format
|
432
|
-
|
433
|
-
@formats.push(format) # Store format reference
|
419
|
+
@formats.formats.push(format) # Store format reference
|
434
420
|
|
435
421
|
format
|
436
422
|
end
|
@@ -716,7 +702,7 @@ module Writexlsx
|
|
716
702
|
if name =~ /^(.*)!(.*)$/
|
717
703
|
sheetname = $1
|
718
704
|
name = $2
|
719
|
-
sheet_index =
|
705
|
+
sheet_index = @worksheets.index_by_name(sheetname)
|
720
706
|
else
|
721
707
|
sheet_index = -1 # Use -1 to indicate global names.
|
722
708
|
end
|
@@ -946,12 +932,21 @@ module Writexlsx
|
|
946
932
|
@shared_strings.empty?
|
947
933
|
end
|
948
934
|
|
949
|
-
def
|
950
|
-
@
|
935
|
+
def chartsheet_count
|
936
|
+
@worksheets.chartsheet_count
|
951
937
|
end
|
952
938
|
|
953
|
-
def
|
954
|
-
|
939
|
+
def style_properties
|
940
|
+
[
|
941
|
+
@xf_formats,
|
942
|
+
@palette,
|
943
|
+
@font_count,
|
944
|
+
@num_format_count,
|
945
|
+
@border_count,
|
946
|
+
@fill_count,
|
947
|
+
@custom_colors,
|
948
|
+
@dxf_formats
|
949
|
+
]
|
955
950
|
end
|
956
951
|
|
957
952
|
private
|
@@ -1037,52 +1032,11 @@ module Writexlsx
|
|
1037
1032
|
# invalid characters and if the name is unique in the workbook.
|
1038
1033
|
#
|
1039
1034
|
def check_sheetname(name) #:nodoc:
|
1040
|
-
make_and_check_sheet_chart_name(:sheet, name)
|
1035
|
+
@worksheets.make_and_check_sheet_chart_name(:sheet, name)
|
1041
1036
|
end
|
1042
1037
|
|
1043
1038
|
def check_chart_sheetname(name)
|
1044
|
-
make_and_check_sheet_chart_name(:chart, name)
|
1045
|
-
end
|
1046
|
-
|
1047
|
-
def make_and_check_sheet_chart_name(type, name)
|
1048
|
-
count = sheet_chart_count_increment(type)
|
1049
|
-
name = "#{BASE_NAME[type]}#{count}" unless ptrue?(name)
|
1050
|
-
|
1051
|
-
check_valid_sheetname(name)
|
1052
|
-
name
|
1053
|
-
end
|
1054
|
-
|
1055
|
-
def sheet_chart_count_increment(type)
|
1056
|
-
case type
|
1057
|
-
when :sheet
|
1058
|
-
@sheetname_count += 1
|
1059
|
-
when :chart
|
1060
|
-
@chartname_count += 1
|
1061
|
-
end
|
1062
|
-
end
|
1063
|
-
|
1064
|
-
def check_valid_sheetname(name)
|
1065
|
-
# Check that sheet name is <= 31. Excel limit.
|
1066
|
-
raise "Sheetname #{name} must be <= #{SHEETNAME_MAX} chars" if name.length > SHEETNAME_MAX
|
1067
|
-
|
1068
|
-
# Check that sheetname doesn't contain any invalid characters
|
1069
|
-
invalid_char = /[\[\]:*?\/\\]/
|
1070
|
-
if name =~ invalid_char
|
1071
|
-
raise 'Invalid character []:*?/\\ in worksheet name: ' + name
|
1072
|
-
end
|
1073
|
-
|
1074
|
-
# Check that the worksheet name doesn't already exist since this is a fatal
|
1075
|
-
# error in Excel 97. The check must also exclude case insensitive matches.
|
1076
|
-
unless is_sheetname_uniq?(name)
|
1077
|
-
raise "Worksheet name '#{name}', with case ignored, is already used."
|
1078
|
-
end
|
1079
|
-
end
|
1080
|
-
|
1081
|
-
def is_sheetname_uniq?(name)
|
1082
|
-
@worksheets.each do |worksheet|
|
1083
|
-
return false if name.downcase == worksheet.name.downcase
|
1084
|
-
end
|
1085
|
-
true
|
1039
|
+
@worksheets.make_and_check_sheet_chart_name(:chart, name)
|
1086
1040
|
end
|
1087
1041
|
|
1088
1042
|
#
|
@@ -1184,29 +1138,6 @@ module Writexlsx
|
|
1184
1138
|
@writer.empty_tag('workbookView', attributes)
|
1185
1139
|
end
|
1186
1140
|
|
1187
|
-
def write_sheets #:nodoc:
|
1188
|
-
@writer.tag_elements('sheets') do
|
1189
|
-
id_num = 1
|
1190
|
-
@worksheets.each do |sheet|
|
1191
|
-
write_sheet(sheet.name, id_num, sheet.hidden?)
|
1192
|
-
id_num += 1
|
1193
|
-
end
|
1194
|
-
end
|
1195
|
-
end
|
1196
|
-
|
1197
|
-
def write_sheet(name, sheet_id, hidden = false) #:nodoc:
|
1198
|
-
attributes = [
|
1199
|
-
'name', name,
|
1200
|
-
'sheetId', sheet_id
|
1201
|
-
]
|
1202
|
-
|
1203
|
-
if hidden
|
1204
|
-
attributes << 'state' << 'hidden'
|
1205
|
-
end
|
1206
|
-
attributes << 'r:id' << "rId#{sheet_id}"
|
1207
|
-
@writer.empty_tag_encoded('sheet', attributes)
|
1208
|
-
end
|
1209
|
-
|
1210
1141
|
def write_calc_pr #:nodoc:
|
1211
1142
|
attributes = ['calcId', 124519]
|
1212
1143
|
@writer.empty_tag('calcPr', attributes)
|
@@ -1219,9 +1150,10 @@ module Writexlsx
|
|
1219
1150
|
|
1220
1151
|
def write_ext #:nodoc:
|
1221
1152
|
tag = 'ext'
|
1153
|
+
uri = "#{OFFICE_URL}mac/excel/2008/main"
|
1222
1154
|
attributes = [
|
1223
|
-
'xmlns:mx',
|
1224
|
-
'uri',
|
1155
|
+
'xmlns:mx', uri,
|
1156
|
+
'uri', uri
|
1225
1157
|
]
|
1226
1158
|
@writer.tag_elements(tag, attributes) { write_mx_arch_id }
|
1227
1159
|
end
|
@@ -1289,8 +1221,7 @@ module Writexlsx
|
|
1289
1221
|
add_chart_data
|
1290
1222
|
|
1291
1223
|
# Package the workbook.
|
1292
|
-
packager = Package::Packager.new
|
1293
|
-
packager.add_workbook(self)
|
1224
|
+
packager = Package::Packager.new(self)
|
1294
1225
|
packager.set_package_dir(@tempdir)
|
1295
1226
|
packager.create_package
|
1296
1227
|
|
@@ -1328,7 +1259,7 @@ module Writexlsx
|
|
1328
1259
|
# formats.
|
1329
1260
|
#
|
1330
1261
|
def prepare_formats #:nodoc:
|
1331
|
-
@formats.each do |format|
|
1262
|
+
@formats.formats.each do |format|
|
1332
1263
|
xf_index = format.xf_index
|
1333
1264
|
dxf_index = format.dxf_index
|
1334
1265
|
|
@@ -1562,8 +1493,7 @@ module Writexlsx
|
|
1562
1493
|
# Add a font format for cell comments.
|
1563
1494
|
if comment_files > 0
|
1564
1495
|
format = Format.new(
|
1565
|
-
@
|
1566
|
-
@dxf_format_indices,
|
1496
|
+
@formats,
|
1567
1497
|
:font => 'Tahoma',
|
1568
1498
|
:size => 8,
|
1569
1499
|
:color_indexed => 81,
|
@@ -1572,7 +1502,7 @@ module Writexlsx
|
|
1572
1502
|
|
1573
1503
|
format.get_xf_index
|
1574
1504
|
|
1575
|
-
@formats << format
|
1505
|
+
@formats.formats << format
|
1576
1506
|
end
|
1577
1507
|
end
|
1578
1508
|
|
@@ -1728,7 +1658,7 @@ module Writexlsx
|
|
1728
1658
|
|
1729
1659
|
drawing_id += 1
|
1730
1660
|
|
1731
|
-
|
1661
|
+
sheet.charts.each_with_index do |chart, index|
|
1732
1662
|
chart_ref_id += 1
|
1733
1663
|
sheet.prepare_chart(index, chart_ref_id, drawing_id)
|
1734
1664
|
end
|
@@ -1758,23 +1688,6 @@ module Writexlsx
|
|
1758
1688
|
@drawing_count = drawing_id
|
1759
1689
|
end
|
1760
1690
|
|
1761
|
-
#
|
1762
|
-
# Convert a sheet name to its index. Return undef otherwise.
|
1763
|
-
#
|
1764
|
-
def get_sheet_index(sheetname) #:nodoc:
|
1765
|
-
sheet_count = @sheetnames.size
|
1766
|
-
sheet_index = nil
|
1767
|
-
|
1768
|
-
sheetname.sub!(/^'/, '')
|
1769
|
-
sheetname.sub!(/'$/, '')
|
1770
|
-
|
1771
|
-
( 0 .. sheet_count - 1 ).each do |i|
|
1772
|
-
sheet_index = i if sheetname == @sheetnames[i]
|
1773
|
-
end
|
1774
|
-
|
1775
|
-
sheet_index
|
1776
|
-
end
|
1777
|
-
|
1778
1691
|
#
|
1779
1692
|
# Extract information from the image file such as dimension, type, filename,
|
1780
1693
|
# and extension. Also keep track of previously seen images to optimise out
|
@@ -0,0 +1,291 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module Writexlsx
|
4
|
+
class Worksheet
|
5
|
+
class DataValidation # :nodoc:
|
6
|
+
include Writexlsx::Utility
|
7
|
+
|
8
|
+
attr_reader :value, :source, :minimum, :maximum, :validate, :criteria
|
9
|
+
attr_reader :error_type, :cells, :other_cells
|
10
|
+
attr_reader :ignore_blank, :dropdown, :show_input, :show_error
|
11
|
+
attr_reader :error_title, :error_message, :input_title, :input_message
|
12
|
+
|
13
|
+
def initialize(*args)
|
14
|
+
# Check for a cell reference in A1 notation and substitute row and column.
|
15
|
+
row1, col1, row2, col2, options = row_col_notation(args)
|
16
|
+
if row2.respond_to?(:keys)
|
17
|
+
options_to_instance_variable(row2.dup)
|
18
|
+
row2, col2 = row1, col1
|
19
|
+
elsif options.respond_to?(:keys)
|
20
|
+
options_to_instance_variable(options.dup)
|
21
|
+
else
|
22
|
+
raise WriteXLSXInsufficientArgumentError
|
23
|
+
end
|
24
|
+
raise WriteXLSXInsufficientArgumentError if [row1, col1, row2, col2].include?(nil)
|
25
|
+
check_for_valid_input_params
|
26
|
+
|
27
|
+
check_dimensions(row1, col1)
|
28
|
+
check_dimensions(row2, col2)
|
29
|
+
@cells = [[row1, col1, row2, col2]]
|
30
|
+
|
31
|
+
@value = @source if @source
|
32
|
+
@value = @minimum if @minimum
|
33
|
+
|
34
|
+
@validate = valid_validation_type[@validate.downcase]
|
35
|
+
if @validate == 'none'
|
36
|
+
@validate_none = true
|
37
|
+
return
|
38
|
+
end
|
39
|
+
if ['list', 'custom'].include?(@validate)
|
40
|
+
@criteria = 'between'
|
41
|
+
@maximum = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
check_criteria_required
|
45
|
+
check_valid_citeria_types
|
46
|
+
@criteria = valid_criteria_type[@criteria.downcase]
|
47
|
+
|
48
|
+
check_maximum_value_when_criteria_is_between_or_notbetween
|
49
|
+
@error_type = has_key?(:error_type) ? error_type_hash[@error_type.downcase] : 0
|
50
|
+
|
51
|
+
convert_date_time_value_if_required
|
52
|
+
set_some_defaults
|
53
|
+
|
54
|
+
# A (for now) undocumented parameter to pass additional cell ranges.
|
55
|
+
@other_cells.each { |cells| @cells << cells } if has_key?(:other_cells)
|
56
|
+
end
|
57
|
+
|
58
|
+
def options_to_instance_variable(params)
|
59
|
+
params.each do |k, v|
|
60
|
+
instance_variable_set("@#{k}", v)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def keys
|
65
|
+
self.instance_variables.collect { |v| v.to_s.sub(/@/, '').to_sym }
|
66
|
+
end
|
67
|
+
|
68
|
+
def validate_none?
|
69
|
+
@validate_none
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# Write the <dataValidation> element.
|
74
|
+
#
|
75
|
+
def write_data_validation(writer) #:nodoc:
|
76
|
+
@writer = writer
|
77
|
+
@writer.tag_elements('dataValidation', attributes) do
|
78
|
+
# Write the formula1 element.
|
79
|
+
write_formula_1(@value)
|
80
|
+
# Write the formula2 element.
|
81
|
+
write_formula_2(@maximum) if @maximum
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
#
|
88
|
+
# Write the <formula1> element.
|
89
|
+
#
|
90
|
+
def write_formula_1(formula) #:nodoc:
|
91
|
+
# Convert a list array ref into a comma separated string.
|
92
|
+
formula = %!"#{formula.join(',')}"! if formula.kind_of?(Array)
|
93
|
+
|
94
|
+
formula = formula.sub(/^=/, '') if formula.respond_to?(:sub)
|
95
|
+
|
96
|
+
@writer.data_element('formula1', formula)
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Write the <formula2> element.
|
101
|
+
#
|
102
|
+
def write_formula_2(formula) #:nodoc:
|
103
|
+
formula = formula.sub(/^=/, '') if formula.respond_to?(:sub)
|
104
|
+
|
105
|
+
@writer.data_element('formula2', formula)
|
106
|
+
end
|
107
|
+
|
108
|
+
def attributes
|
109
|
+
sqref = ''
|
110
|
+
attributes = []
|
111
|
+
|
112
|
+
# Set the cell range(s) for the data validation.
|
113
|
+
@cells.each do |cells|
|
114
|
+
# Add a space between multiple cell ranges.
|
115
|
+
sqref += ' ' if sqref != ''
|
116
|
+
|
117
|
+
row_first, col_first, row_last, col_last = cells
|
118
|
+
|
119
|
+
# Swap last row/col for first row/col as necessary
|
120
|
+
row_first, row_last = row_last, row_first if row_first > row_last
|
121
|
+
col_first, col_last = col_last, col_first if col_first > col_last
|
122
|
+
|
123
|
+
# If the first and last cell are the same write a single cell.
|
124
|
+
if row_first == row_last && col_first == col_last
|
125
|
+
sqref += xl_rowcol_to_cell(row_first, col_first)
|
126
|
+
else
|
127
|
+
sqref += xl_range(row_first, row_last, col_first, col_last)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
attributes << 'type' << @validate
|
132
|
+
attributes << 'operator' << @criteria if @criteria != 'between'
|
133
|
+
|
134
|
+
if @error_type
|
135
|
+
attributes << 'errorStyle' << 'warning' if @error_type == 1
|
136
|
+
attributes << 'errorStyle' << 'information' if @error_type == 2
|
137
|
+
end
|
138
|
+
attributes << 'allowBlank' << 1 if @ignore_blank != 0
|
139
|
+
attributes << 'showDropDown' << 1 if @dropdown == 0
|
140
|
+
attributes << 'showInputMessage' << 1 if @show_input != 0
|
141
|
+
attributes << 'showErrorMessage' << 1 if @show_error != 0
|
142
|
+
|
143
|
+
attributes << 'errorTitle' << @error_title if @error_title
|
144
|
+
attributes << 'error' << @error_message if @error_message
|
145
|
+
attributes << 'promptTitle' << @input_title if @input_title
|
146
|
+
attributes << 'prompt' << @input_message if @input_message
|
147
|
+
attributes << 'sqref' << sqref
|
148
|
+
end
|
149
|
+
|
150
|
+
def has_key?(key)
|
151
|
+
keys.index(key)
|
152
|
+
end
|
153
|
+
|
154
|
+
def set_some_defaults
|
155
|
+
@ignore_blank ||= 1
|
156
|
+
@dropdown ||= 1
|
157
|
+
@show_input ||= 1
|
158
|
+
@show_error ||= 1
|
159
|
+
end
|
160
|
+
|
161
|
+
def check_for_valid_input_params
|
162
|
+
check_parameter(self, valid_validation_parameter, 'data_validation')
|
163
|
+
|
164
|
+
unless has_key?(:validate)
|
165
|
+
raise WriteXLSXOptionParameterError, "Parameter :validate is required in data_validation()"
|
166
|
+
end
|
167
|
+
unless valid_validation_type.has_key?(@validate.downcase)
|
168
|
+
raise WriteXLSXOptionParameterError,
|
169
|
+
"Unknown validation type '#{@validate}' for parameter :validate in data_validation()"
|
170
|
+
end
|
171
|
+
if @error_type && !error_type_hash.has_key?(@error_type.downcase)
|
172
|
+
raise WriteXLSXOptionParameterError,
|
173
|
+
"Unknown criteria type '#param[:error_type}' for parameter :error_type in data_validation()"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def check_criteria_required
|
178
|
+
unless has_key?(:criteria)
|
179
|
+
raise WriteXLSXOptionParameterError, "Parameter :criteria is required in data_validation()"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def check_maximum_value_when_criteria_is_between_or_notbetween
|
184
|
+
if @criteria == 'between' || @criteria == 'notBetween'
|
185
|
+
unless has_key?(:maximum)
|
186
|
+
raise WriteXLSXOptionParameterError,
|
187
|
+
"Parameter :maximum is required in data_validation() when using :between or :not between criteria"
|
188
|
+
end
|
189
|
+
else
|
190
|
+
@maximum = nil
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def check_valid_citeria_types
|
195
|
+
unless valid_criteria_type.has_key?(@criteria.downcase)
|
196
|
+
raise WriteXLSXOptionParameterError,
|
197
|
+
"Unknown criteria type '#{@criteria}' for parameter :criteria in data_validation()"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def convert_date_time_value_if_required
|
202
|
+
@date_1904 = date_1904?
|
203
|
+
if @validate == 'date' || @validate == 'time'
|
204
|
+
unless convert_date_time_value(:value) && convert_date_time_value(:maximum)
|
205
|
+
raise WriteXLSXOptionParameterError, "Invalid date/time value."
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def error_type_hash
|
211
|
+
{'stop' => 0, 'warning' => 1, 'information' => 2}
|
212
|
+
end
|
213
|
+
|
214
|
+
def valid_validation_type # :nodoc:
|
215
|
+
{
|
216
|
+
'any' => 'none',
|
217
|
+
'any value' => 'none',
|
218
|
+
'whole number' => 'whole',
|
219
|
+
'whole' => 'whole',
|
220
|
+
'integer' => 'whole',
|
221
|
+
'decimal' => 'decimal',
|
222
|
+
'list' => 'list',
|
223
|
+
'date' => 'date',
|
224
|
+
'time' => 'time',
|
225
|
+
'text length' => 'textLength',
|
226
|
+
'length' => 'textLength',
|
227
|
+
'custom' => 'custom'
|
228
|
+
}
|
229
|
+
end
|
230
|
+
|
231
|
+
# List of valid input parameters.
|
232
|
+
def valid_validation_parameter
|
233
|
+
[
|
234
|
+
:validate,
|
235
|
+
:criteria,
|
236
|
+
:value,
|
237
|
+
:source,
|
238
|
+
:minimum,
|
239
|
+
:maximum,
|
240
|
+
:ignore_blank,
|
241
|
+
:dropdown,
|
242
|
+
:show_input,
|
243
|
+
:input_title,
|
244
|
+
:input_message,
|
245
|
+
:show_error,
|
246
|
+
:error_title,
|
247
|
+
:error_message,
|
248
|
+
:error_type,
|
249
|
+
:other_cells
|
250
|
+
]
|
251
|
+
end
|
252
|
+
|
253
|
+
# List of valid criteria types.
|
254
|
+
def valid_criteria_type # :nodoc:
|
255
|
+
{
|
256
|
+
'between' => 'between',
|
257
|
+
'not between' => 'notBetween',
|
258
|
+
'equal to' => 'equal',
|
259
|
+
'=' => 'equal',
|
260
|
+
'==' => 'equal',
|
261
|
+
'not equal to' => 'notEqual',
|
262
|
+
'!=' => 'notEqual',
|
263
|
+
'<>' => 'notEqual',
|
264
|
+
'greater than' => 'greaterThan',
|
265
|
+
'>' => 'greaterThan',
|
266
|
+
'less than' => 'lessThan',
|
267
|
+
'<' => 'lessThan',
|
268
|
+
'greater than or equal to' => 'greaterThanOrEqual',
|
269
|
+
'>=' => 'greaterThanOrEqual',
|
270
|
+
'less than or equal to' => 'lessThanOrEqual',
|
271
|
+
'<=' => 'lessThanOrEqual'
|
272
|
+
}
|
273
|
+
end
|
274
|
+
|
275
|
+
def convert_date_time_value(key) # :nodoc:
|
276
|
+
value = instance_variable_get("@#{key}")
|
277
|
+
if value && value =~ /T/
|
278
|
+
date_time = convert_date_time(value)
|
279
|
+
instance_variable_set("@#{key}", date_time) if date_time
|
280
|
+
date_time
|
281
|
+
else
|
282
|
+
true
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def date_1904?
|
287
|
+
@date_1904
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module Writexlsx
|
4
|
+
class Worksheet
|
5
|
+
class Hyperlink # :nodoc:
|
6
|
+
include Writexlsx::Utility
|
7
|
+
|
8
|
+
attr_reader :url, :link_type, :str, :url_str
|
9
|
+
attr_accessor :tip, :display
|
10
|
+
|
11
|
+
def initialize(url, str = nil)
|
12
|
+
link_type = 1
|
13
|
+
|
14
|
+
# Remove the URI scheme from internal links.
|
15
|
+
if url =~ /^internal:/
|
16
|
+
url = url.sub(/^internal:/, '')
|
17
|
+
link_type = 2
|
18
|
+
# Remove the URI scheme from external links.
|
19
|
+
elsif url =~ /^external:/
|
20
|
+
url = url.sub(/^external:/, '')
|
21
|
+
link_type = 3
|
22
|
+
end
|
23
|
+
|
24
|
+
# The displayed string defaults to the url string.
|
25
|
+
str ||= url.dup
|
26
|
+
|
27
|
+
# For external links change the directory separator from Unix to Dos.
|
28
|
+
if link_type == 3
|
29
|
+
url = url.gsub(%r|/|, '\\')
|
30
|
+
str.gsub!(%r|/|, '\\')
|
31
|
+
end
|
32
|
+
|
33
|
+
# Strip the mailto header.
|
34
|
+
str.sub!(/^mailto:/, '')
|
35
|
+
|
36
|
+
# Copy string for use in hyperlink elements.
|
37
|
+
url_str = str.dup
|
38
|
+
|
39
|
+
# External links to URLs and to other Excel workbooks have slightly
|
40
|
+
# different characteristics that we have to account for.
|
41
|
+
if link_type == 1
|
42
|
+
# Escape URL unless it looks already escaped.
|
43
|
+
unless url =~ /%[0-9a-fA-F]{2}/
|
44
|
+
# Escape the URL escape symbol.
|
45
|
+
url = url.gsub(/%/, "%25")
|
46
|
+
|
47
|
+
# Escape whitespae in URL.
|
48
|
+
url = url.gsub(/[\s\x00]/, '%20')
|
49
|
+
|
50
|
+
# Escape other special characters in URL.
|
51
|
+
re = /(["<>\[\]`^{}])/
|
52
|
+
while re =~ url
|
53
|
+
match = $~[1]
|
54
|
+
url = url.sub(re, sprintf("%%%x", match.ord))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Ordinary URL style external links don't have a "location" string.
|
59
|
+
url_str = nil
|
60
|
+
elsif link_type == 3
|
61
|
+
# External Workbook links need to be modified into the right format.
|
62
|
+
# The URL will look something like 'c:\temp\file.xlsx#Sheet!A1'.
|
63
|
+
# We need the part to the left of the # as the URL and the part to
|
64
|
+
# the right as the "location" string (if it exists).
|
65
|
+
url, url_str = url.split(/#/)
|
66
|
+
|
67
|
+
# Add the file:/// URI to the url if non-local.
|
68
|
+
if url =~ %r![:]! || # Windows style "C:/" link.
|
69
|
+
url =~ %r!^\\\\! # Network share.
|
70
|
+
url = "file:///#{url}"
|
71
|
+
end
|
72
|
+
|
73
|
+
# Convert a ./dir/file.xlsx link to dir/file.xlsx.
|
74
|
+
url = url.sub(%r!^.\\!, '')
|
75
|
+
|
76
|
+
# Treat as a default external link now that the data has been modified.
|
77
|
+
link_type = 1
|
78
|
+
end
|
79
|
+
|
80
|
+
# Excel limits escaped URL to 255 characters.
|
81
|
+
if url.bytesize > 255
|
82
|
+
raise "URL '#{url}' > 255 characters, it exceeds Excel's limit for URLS."
|
83
|
+
end
|
84
|
+
@url = url
|
85
|
+
@link_type = link_type
|
86
|
+
@str = str
|
87
|
+
@url_str = url_str
|
88
|
+
end
|
89
|
+
|
90
|
+
def write_external_attributes(row, col, id)
|
91
|
+
ref = xl_rowcol_to_cell(row, col)
|
92
|
+
|
93
|
+
attributes = ['ref', ref, 'r:id', "rId#{id}"]
|
94
|
+
|
95
|
+
attributes << 'location' << url_str if url_str
|
96
|
+
attributes << 'display' << display if display
|
97
|
+
attributes << 'tooltip' << tip if tip
|
98
|
+
attributes
|
99
|
+
end
|
100
|
+
|
101
|
+
def write_internal_attributes(row, col)
|
102
|
+
ref = xl_rowcol_to_cell(row, col)
|
103
|
+
|
104
|
+
attributes = ['ref', ref, 'location', url]
|
105
|
+
|
106
|
+
attributes << 'tooltip' << tip if tip
|
107
|
+
attributes << 'display' << str
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|