write_xlsx 1.13.0 → 1.15.1

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.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +25 -4
  3. data/Changes +23 -0
  4. data/lib/write_xlsx/chart/area.rb +2 -2
  5. data/lib/write_xlsx/chart/axis.rb +55 -32
  6. data/lib/write_xlsx/chart/axis_writer.rb +528 -0
  7. data/lib/write_xlsx/chart/bar.rb +2 -2
  8. data/lib/write_xlsx/chart/caption.rb +16 -9
  9. data/lib/write_xlsx/chart/chart_area.rb +121 -0
  10. data/lib/write_xlsx/chart/column.rb +2 -2
  11. data/lib/write_xlsx/chart/d_pt_point_writer.rb +14 -0
  12. data/lib/write_xlsx/chart/doughnut.rb +0 -3
  13. data/lib/write_xlsx/chart/formatting_writer.rb +652 -0
  14. data/lib/write_xlsx/chart/initialization.rb +100 -0
  15. data/lib/write_xlsx/chart/line.rb +4 -3
  16. data/lib/write_xlsx/chart/pie.rb +6 -2
  17. data/lib/write_xlsx/chart/radar.rb +2 -2
  18. data/lib/write_xlsx/chart/scatter.rb +4 -3
  19. data/lib/write_xlsx/chart/series.rb +35 -15
  20. data/lib/write_xlsx/chart/series_data.rb +132 -0
  21. data/lib/write_xlsx/chart/series_writer.rb +318 -0
  22. data/lib/write_xlsx/chart/settings.rb +226 -0
  23. data/lib/write_xlsx/chart/stock.rb +2 -2
  24. data/lib/write_xlsx/chart/table.rb +50 -0
  25. data/lib/write_xlsx/chart/xml_writer.rb +305 -0
  26. data/lib/write_xlsx/chart.rb +286 -2477
  27. data/lib/write_xlsx/chartsheet.rb +31 -82
  28. data/lib/write_xlsx/constants.rb +11 -0
  29. data/lib/write_xlsx/drawing.rb +5 -3
  30. data/lib/write_xlsx/format/alignment_state.rb +39 -0
  31. data/lib/write_xlsx/format/alignment_style.rb +92 -0
  32. data/lib/write_xlsx/format/border_state.rb +47 -0
  33. data/lib/write_xlsx/format/border_style.rb +116 -0
  34. data/lib/write_xlsx/format/fill_state.rb +26 -0
  35. data/lib/write_xlsx/format/fill_style.rb +52 -0
  36. data/lib/write_xlsx/format/font_state.rb +74 -0
  37. data/lib/write_xlsx/format/font_style.rb +172 -0
  38. data/lib/write_xlsx/format/format_state.rb +65 -0
  39. data/lib/write_xlsx/format/number_format_state.rb +20 -0
  40. data/lib/write_xlsx/format/number_format_style.rb +28 -0
  41. data/lib/write_xlsx/format/protection_state.rb +20 -0
  42. data/lib/write_xlsx/format/protection_style.rb +28 -0
  43. data/lib/write_xlsx/format.rb +1093 -426
  44. data/lib/write_xlsx/formats.rb +0 -2
  45. data/lib/write_xlsx/image_property.rb +4 -1
  46. data/lib/write_xlsx/inserted_chart.rb +1 -1
  47. data/lib/write_xlsx/object_positioning.rb +15 -1
  48. data/lib/write_xlsx/package/app.rb +2 -2
  49. data/lib/write_xlsx/package/button.rb +6 -2
  50. data/lib/write_xlsx/package/comments.rb +11 -3
  51. data/lib/write_xlsx/package/conditional_format.rb +7 -3
  52. data/lib/write_xlsx/package/content_types.rb +2 -2
  53. data/lib/write_xlsx/package/core.rb +2 -2
  54. data/lib/write_xlsx/package/custom.rb +3 -2
  55. data/lib/write_xlsx/package/metadata.rb +2 -2
  56. data/lib/write_xlsx/package/packager.rb +0 -3
  57. data/lib/write_xlsx/package/relationships.rb +2 -2
  58. data/lib/write_xlsx/package/rich_value.rb +4 -2
  59. data/lib/write_xlsx/package/rich_value_rel.rb +2 -2
  60. data/lib/write_xlsx/package/rich_value_structure.rb +2 -2
  61. data/lib/write_xlsx/package/rich_value_types.rb +3 -3
  62. data/lib/write_xlsx/package/shared_strings.rb +2 -2
  63. data/lib/write_xlsx/package/styles.rb +13 -9
  64. data/lib/write_xlsx/package/table.rb +8 -2
  65. data/lib/write_xlsx/package/theme.rb +0 -3
  66. data/lib/write_xlsx/package/vml.rb +2 -2
  67. data/lib/write_xlsx/page_setup.rb +3 -1
  68. data/lib/write_xlsx/shape.rb +97 -100
  69. data/lib/write_xlsx/sheets.rb +6 -1
  70. data/lib/write_xlsx/sparkline.rb +2 -2
  71. data/lib/write_xlsx/utility/cell_reference.rb +124 -0
  72. data/lib/write_xlsx/utility/chart_formatting.rb +262 -0
  73. data/lib/write_xlsx/utility/common.rb +44 -0
  74. data/lib/write_xlsx/utility/date_time.rb +113 -0
  75. data/lib/write_xlsx/utility/dimensions.rb +40 -0
  76. data/lib/write_xlsx/utility/drawing.rb +136 -0
  77. data/lib/write_xlsx/utility/rich_text.rb +184 -0
  78. data/lib/write_xlsx/utility/sheetname_quoting.rb +73 -0
  79. data/lib/write_xlsx/utility/string_width.rb +45 -0
  80. data/lib/write_xlsx/utility/url.rb +27 -0
  81. data/lib/write_xlsx/utility/xml_primitives.rb +32 -0
  82. data/lib/write_xlsx/version.rb +1 -1
  83. data/lib/write_xlsx/workbook/chart_data.rb +188 -0
  84. data/lib/write_xlsx/workbook/format_preparation.rb +199 -0
  85. data/lib/write_xlsx/workbook/initialization.rb +223 -0
  86. data/lib/write_xlsx/workbook/package_preparation.rb +231 -0
  87. data/lib/write_xlsx/workbook/workbook_writer.rb +164 -0
  88. data/lib/write_xlsx/workbook.rb +143 -981
  89. data/lib/write_xlsx/worksheet/autofilter.rb +3 -1
  90. data/lib/write_xlsx/worksheet/cell_data.rb +5 -1
  91. data/lib/write_xlsx/worksheet/columns.rb +8 -3
  92. data/lib/write_xlsx/worksheet/data_validation.rb +9 -1
  93. data/lib/write_xlsx/worksheet/data_writing.rb +42 -15
  94. data/lib/write_xlsx/worksheet/drawing_preparation.rb +1 -1
  95. data/lib/write_xlsx/worksheet/formatting.rb +3 -1
  96. data/lib/write_xlsx/worksheet/hyperlink.rb +9 -1
  97. data/lib/write_xlsx/worksheet/row_col_sizing.rb +3 -1
  98. data/lib/write_xlsx/worksheet/xml_writer.rb +9 -4
  99. data/lib/write_xlsx/worksheet.rb +19 -2
  100. metadata +42 -3
  101. data/lib/write_xlsx/utility.rb +0 -1034
@@ -0,0 +1,188 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ module Writexlsx
5
+ class Workbook
6
+ module ChartData
7
+ private
8
+
9
+ #
10
+ # Convert a range formula such as Sheet1!$B$1:$B$5 into a sheet name and cell
11
+ # range such as ( 'Sheet1', 0, 1, 4, 1 ).
12
+ #
13
+ def get_chart_range(range) # :nodoc:
14
+ # Split the range formula into sheetname and cells at the last '!'.
15
+ pos = range.rindex('!')
16
+ return nil unless pos
17
+
18
+ if pos > 0
19
+ sheetname = range[0, pos]
20
+ cells = range[(pos + 1)..-1]
21
+ end
22
+
23
+ # Split the cell range into 2 cells or else use single cell for both.
24
+ if cells =~ /:/
25
+ cell_1, cell_2 = cells.split(":")
26
+ else
27
+ cell_1 = cells
28
+ cell_2 = cells
29
+ end
30
+
31
+ # Remove leading/trailing apostrophes and convert escaped quotes to single.
32
+ sheetname.sub!(/^'/, '')
33
+ sheetname.sub!(/'$/, '')
34
+ sheetname.gsub!("''", "'")
35
+
36
+ row_start, col_start = xl_cell_to_rowcol(cell_1)
37
+ row_end, col_end = xl_cell_to_rowcol(cell_2)
38
+
39
+ # Check that we have a 1D range only.
40
+ return nil if row_start != row_end && col_start != col_end
41
+
42
+ [sheetname, row_start, col_start, row_end, col_end]
43
+ end
44
+
45
+ #
46
+ # Add "cached" data to charts to provide the numCache and strCache data for
47
+ # series and title/axis ranges.
48
+ #
49
+ def add_chart_data # :nodoc:
50
+ worksheets = {}
51
+ seen_ranges = {}
52
+
53
+ # Map worksheet names to worksheet objects.
54
+ @worksheets.each { |worksheet| worksheets[worksheet.name] = worksheet }
55
+
56
+ # Build an array of the worksheet charts including any combined charts.
57
+ @charts.collect { |chart| [chart, chart.combined] }.flatten.compact
58
+ .each do |chart|
59
+ chart.formula_ids.each do |range, id|
60
+ # Skip if the series has user defined data.
61
+ if chart.formula_data[id]
62
+ seen_ranges[range] = chart.formula_data[id] unless seen_ranges[range]
63
+ next
64
+ # Check to see if the data is already cached locally.
65
+ elsif seen_ranges[range]
66
+ chart.formula_data[id] = seen_ranges[range]
67
+ next
68
+ end
69
+
70
+ # Convert the range formula to a sheet name and cell range.
71
+ sheetname, *cells = get_chart_range(range)
72
+
73
+ # Skip if we couldn't parse the formula.
74
+ next unless sheetname
75
+
76
+ # Handle non-contiguous ranges: (Sheet1!$A$1:$A$2,Sheet1!$A$4:$A$5).
77
+ # We don't try to parse the ranges. We just return an empty list.
78
+ if sheetname =~ /^\([^,]+,/
79
+ chart.formula_data[id] = []
80
+ seen_ranges[range] = []
81
+ next
82
+ end
83
+
84
+ # Raise if the name is unknown since it indicates a user error in
85
+ # a chart series formula.
86
+ raise "Unknown worksheet reference '#{sheetname}' in range '#{range}' passed to add_series()\n" unless worksheets[sheetname]
87
+
88
+ # Add the data to the chart.
89
+ # And store range data locally to avoid lookup if seen agein.
90
+ chart.formula_data[id] =
91
+ seen_ranges[range] = chart_data(worksheets[sheetname], cells)
92
+ end
93
+ end
94
+ end
95
+
96
+ def chart_data(worksheet, cells)
97
+ # Get the data from the worksheet table.
98
+ data = worksheet.get_range_data(*cells)
99
+
100
+ # Convert shared string indexes to strings.
101
+ data.collect do |token|
102
+ if token.is_a?(Hash)
103
+ string = @shared_strings.string(token[:sst_id])
104
+
105
+ # Ignore rich strings for now. Deparse later if necessary.
106
+ if string =~ /^<r>/ && string =~ %r{</r>$}
107
+ ''
108
+ else
109
+ string
110
+ end
111
+ else
112
+ token
113
+ end
114
+ end
115
+ end
116
+
117
+ #
118
+ # Sort internal and user defined names in the same order as used by Excel.
119
+ # This may not be strictly necessary but unsorted elements caused a lot of
120
+ # issues in the the Spreadsheet::WriteExcel binary version. Also makes
121
+ # comparison testing easier.
122
+ #
123
+ def sort_defined_names(names) # :nodoc:
124
+ names.sort do |a, b|
125
+ name_a = normalise_defined_name(a[0])
126
+ name_b = normalise_defined_name(b[0])
127
+ sheet_a = normalise_sheet_name(a[2])
128
+ sheet_b = normalise_sheet_name(b[2])
129
+ # Primary sort based on the defined name.
130
+ if name_a > name_b
131
+ 1
132
+ elsif name_a < name_b
133
+ -1
134
+ elsif sheet_a >= sheet_b # name_a == name_b
135
+ # Secondary sort based on the sheet name.
136
+ 1
137
+ else
138
+ -1
139
+ end
140
+ end
141
+ end
142
+
143
+ # Used in the above sort routine to normalise the defined names. Removes any
144
+ # leading '_xmln.' from internal names and lowercases the strings.
145
+ def normalise_defined_name(name) # :nodoc:
146
+ name.sub(/^_xlnm./, '').downcase
147
+ end
148
+
149
+ # Used in the above sort routine to normalise the worksheet names for the
150
+ # secondary sort. Removes leading quote and lowercases the strings.
151
+ def normalise_sheet_name(name) # :nodoc:
152
+ name.sub(/^'/, '').downcase
153
+ end
154
+
155
+ #
156
+ # Extract the named ranges from the sorted list of defined names. These are
157
+ # used in the App.xml file.
158
+ #
159
+ def extract_named_ranges(defined_names) # :nodoc:
160
+ named_ranges = []
161
+
162
+ defined_names.each do |defined_name|
163
+ name, index, range = defined_name
164
+
165
+ # Skip autoFilter ranges.
166
+ next if name == '_xlnm._FilterDatabase'
167
+
168
+ # We are only interested in defined names with ranges.
169
+ next unless range =~ /^([^!]+)!/
170
+
171
+ sheet_name = ::Regexp.last_match(1)
172
+
173
+ # Match Print_Area and Print_Titles xlnm types.
174
+ if name =~ /^_xlnm\.(.*)$/
175
+ xlnm_type = ::Regexp.last_match(1)
176
+ name = "#{sheet_name}!#{xlnm_type}"
177
+ elsif index != -1
178
+ name = "#{sheet_name}!#{name}"
179
+ end
180
+
181
+ named_ranges << name
182
+ end
183
+
184
+ named_ranges
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,199 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ module Writexlsx
5
+ class Workbook
6
+ module FormatPreparation
7
+ private
8
+
9
+ #
10
+ # Prepare all of the format properties prior to passing them to Styles.rb.
11
+ #
12
+ def prepare_format_properties # :nodoc:
13
+ # Separate format objects into XF and DXF formats.
14
+ prepare_formats
15
+
16
+ # Set the font index for the format objects.
17
+ prepare_fonts
18
+
19
+ # Set the number format index for the format objects.
20
+ prepare_num_formats
21
+
22
+ # Set the border index for the format objects.
23
+ prepare_borders
24
+
25
+ # Set the fill index for the format objects.
26
+ prepare_fills
27
+ end
28
+
29
+ #
30
+ # Iterate through the XF Format objects and separate them into XF and DXF
31
+ # formats.
32
+ #
33
+ def prepare_formats # :nodoc:
34
+ @formats.formats.each do |format|
35
+ xf_index = format.xf_index
36
+ dxf_index = format.dxf_index
37
+
38
+ @xf_formats[xf_index] = format if xf_index
39
+ @dxf_formats[dxf_index] = format if dxf_index
40
+ end
41
+ end
42
+
43
+ #
44
+ # Iterate through the XF Format objects and give them an index to non-default
45
+ # font elements.
46
+ #
47
+ def prepare_fonts # :nodoc:
48
+ fonts = {}
49
+
50
+ @xf_formats.each { |format| format.set_font_info(fonts) }
51
+
52
+ @font_count = fonts.size
53
+
54
+ # For the DXF formats we only need to check if the properties have changed.
55
+ @dxf_formats.each do |format|
56
+ # The only font properties that can change for a DXF format are: color,
57
+ # bold, italic, underline and strikethrough.
58
+ format.has_dxf_font(true) if format.color? || format.bold? || format.italic? || format.underline? || format.strikeout?
59
+ end
60
+ end
61
+
62
+ #
63
+ # Iterate through the XF Format objects and give them an index to non-default
64
+ # number format elements.
65
+ #
66
+ # User defined records start from index 0xA4.
67
+ #
68
+ def prepare_num_formats # :nodoc:
69
+ num_formats = []
70
+ unique_num_formats = {}
71
+ index = 164
72
+
73
+ (@xf_formats + @dxf_formats).each do |format|
74
+ num_format = format.num_format
75
+
76
+ # Check if num_format is an index to a built-in number format.
77
+ # Also check for a string of zeros, which is a valid number format
78
+ # string but would evaluate to zero.
79
+ #
80
+ if num_format.to_s =~ /^\d+$/ && num_format.to_s !~ /^0+\d/
81
+ # Number format '0' is indexed as 1 in Excel.
82
+ num_format = 1 if num_format == 0
83
+ # Index to a built-in number format.
84
+ format.num_format_index = num_format
85
+ next
86
+ elsif num_format.to_s == 'General'
87
+ # The 'General' format has an number format index of 0.
88
+ format.num_format_index = 0
89
+ next
90
+ end
91
+
92
+ if unique_num_formats[num_format]
93
+ # Number format has already been used.
94
+ format.num_format_index = unique_num_formats[num_format]
95
+ else
96
+ # Add a new number format.
97
+ unique_num_formats[num_format] = index
98
+ format.num_format_index = index
99
+ index += 1
100
+
101
+ # Only store/increase number format count for XF formats
102
+ # (not for DXF formats).
103
+ num_formats << num_format if ptrue?(format.xf_index)
104
+ end
105
+ end
106
+
107
+ @num_formats = num_formats
108
+ end
109
+
110
+ #
111
+ # Iterate through the XF Format objects and give them an index to non-default
112
+ # border elements.
113
+ #
114
+ def prepare_borders # :nodoc:
115
+ borders = {}
116
+
117
+ @xf_formats.each { |format| format.set_border_info(borders) }
118
+
119
+ @border_count = borders.size
120
+
121
+ # For the DXF formats we only need to check if the properties have changed.
122
+ @dxf_formats.each do |format|
123
+ key = format.get_border_key
124
+ format.has_dxf_border(true) if key =~ /[^0:]/
125
+ end
126
+ end
127
+
128
+ #
129
+ # Iterate through the XF Format objects and give them an index to non-default
130
+ # fill elements.
131
+ #
132
+ # The user defined fill properties start from 2 since there are 2 default
133
+ # fills: patternType="none" and patternType="gray125".
134
+ #
135
+ def prepare_fills # :nodoc:
136
+ fills = {}
137
+ index = 2 # Start from 2. See above.
138
+
139
+ # Add the default fills.
140
+ fills['0:0:0'] = 0
141
+ fills['17:0:0'] = 1
142
+
143
+ # Store the DXF colors separately since them may be reversed below.
144
+ @dxf_formats.each do |format|
145
+ next unless format.fill_style.pattern != 0 || format.fill_style.bg_color != 0 || format.fill_style.fg_color != 0
146
+
147
+ format.has_dxf_fill(true)
148
+ format.dxf_bg_color = format.fill_style.bg_color
149
+ format.dxf_fg_color = format.fill_style.fg_color
150
+ end
151
+
152
+ @xf_formats.each do |format|
153
+ # The following logical statements jointly take care of special cases
154
+ # in relation to cell colours and patterns:
155
+ # 1. For a solid fill (_pattern == 1) Excel reverses the role of
156
+ # foreground and background colours, and
157
+ # 2. If the user specifies a foreground or background colour without
158
+ # a pattern they probably wanted a solid fill, so we fill in the
159
+ # defaults.
160
+ #
161
+ if format.fill_style.pattern == 1 && ne_0?(format.fill_style.bg_color) && ne_0?(format.fill_style.fg_color)
162
+ format.fill_style.fg_color, format.fill_style.bg_color = format.fill_style.bg_color, format.fill_style.fg_color
163
+ elsif format.fill_style.pattern <= 1 && ne_0?(format.fill_style.bg_color) && eq_0?(format.fill_style.fg_color)
164
+ format.fill_style.fg_color = format.fill_style.bg_color
165
+ format.fill_style.bg_color = 0
166
+ format.fill_style.pattern = 1
167
+ elsif format.fill_style.pattern <= 1 && eq_0?(format.fill_style.bg_color) && ne_0?(format.fill_style.fg_color)
168
+ format.fill_style.bg_color = 0
169
+ format.fill_style.pattern = 1
170
+ end
171
+
172
+ key = format.get_fill_key
173
+
174
+ if fills[key]
175
+ # Fill has already been used.
176
+ format.fill_index = fills[key]
177
+ format.has_fill(false)
178
+ else
179
+ # This is a new fill.
180
+ fills[key] = index
181
+ format.fill_index = index
182
+ format.has_fill(true)
183
+ index += 1
184
+ end
185
+ end
186
+
187
+ @fill_count = index
188
+ end
189
+
190
+ def eq_0?(val)
191
+ !ptrue?(val)
192
+ end
193
+
194
+ def ne_0?(val)
195
+ !eq_0?(val)
196
+ end
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,223 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ module Writexlsx
5
+ class Workbook
6
+ #
7
+ # Workbook initialization helpers extracted from Workbook to keep the main
8
+ # class focused on public API and orchestration.
9
+ #
10
+ module Initialization
11
+ private
12
+
13
+ def setup_core_state(file, options, default_formats)
14
+ @options = options.dup # for test
15
+ @default_formats = default_formats.dup # for test
16
+ @writer = Package::XMLWriterSimple.new
17
+
18
+ @file = file
19
+ @tempdir = options[:tempdir] ||
20
+ File.join(
21
+ Dir.tmpdir,
22
+ Digest::MD5.hexdigest("#{Time.now.to_f}-#{Process.pid}")
23
+ )
24
+
25
+ @date_1904 = options[:date_1904] || false
26
+ @activesheet = 0
27
+ @firstsheet = 0
28
+ @selected = 0
29
+ @fileclosed = false
30
+ @optimization = options[:optimization] || 0
31
+ @excel2003_style = options[:excel2003_style] || false
32
+ @read_only = 0
33
+
34
+ @strings_to_urls =
35
+ options[:strings_to_urls].nil? || options[:strings_to_urls] ? true : false
36
+
37
+ @max_url_length = if options[:max_url_length]
38
+ [options[:max_url_length].to_i, 255].max
39
+ else
40
+ MAX_URL_LENGTH
41
+ end
42
+ end
43
+
44
+ def setup_workbook_state(_options)
45
+ @worksheets = Sheets.new
46
+ @charts = []
47
+ @drawings = []
48
+ @defined_names = []
49
+ @named_ranges = []
50
+ @custom_colors = []
51
+ @doc_properties = {}
52
+ @custom_properties = []
53
+ @image_types = {}
54
+ @images = []
55
+ @has_comments = false
56
+ @has_metadata = false
57
+ @has_embedded_images = false
58
+ @has_embedded_descriptions = false
59
+
60
+ @x_window = 240
61
+ @y_window = 15
62
+ @window_width = 16_095
63
+ @window_height = 9_660
64
+ @tab_ratio = 600
65
+ end
66
+
67
+ def setup_format_state(_default_formats)
68
+ @formats = Formats.new
69
+ @xf_formats = []
70
+ @dxf_formats = []
71
+ @num_formats = []
72
+ end
73
+
74
+ def setup_shared_strings
75
+ @shared_strings = Package::SharedStrings.new
76
+ end
77
+
78
+ def setup_embedded_assets
79
+ @embedded_image_indexes = {}
80
+ @embedded_images = []
81
+ end
82
+
83
+ def setup_calculation_state
84
+ @calc_id = 124519
85
+ @calc_mode = 'auto'
86
+ @calc_on_load = true
87
+ end
88
+
89
+ def setup_default_formats
90
+ if @excel2003_style
91
+ add_format(
92
+ @default_formats.merge(
93
+ xf_index: 0,
94
+ font_family: 0,
95
+ font: 'Arial',
96
+ size: 10,
97
+ theme: -1
98
+ )
99
+ )
100
+ else
101
+ add_format(@default_formats.merge(xf_index: 0))
102
+ end
103
+
104
+ # Add a default URL format.
105
+ @default_url_format = add_format(hyperlink: 1)
106
+ end
107
+
108
+ #
109
+ # Workbook の生成時のオプションハッシュを解析する
110
+ #
111
+ def process_workbook_options(*params)
112
+ case params.size
113
+ when 0
114
+ [{}, {}]
115
+ when 1 # one hash
116
+ options_keys = %i[tempdir date_1904 optimization excel2003_style strings_to_urls max_url_length]
117
+
118
+ hash = params.first
119
+ options = hash.select { |k, _v| options_keys.include?(k) }
120
+
121
+ default_format_properties =
122
+ hash[:default_format_properties] ||
123
+ hash.reject { |k, _v| options_keys.include?(k) }
124
+
125
+ [options, default_format_properties.dup]
126
+ when 2 # array which includes options and default_format_properties
127
+ options, default_format_properties = params
128
+ default_format_properties ||= {}
129
+
130
+ [options.dup, default_format_properties.dup]
131
+ end
132
+ end
133
+
134
+ def filename
135
+ setup_filename unless @filename
136
+ @filename
137
+ end
138
+
139
+ def fileobj
140
+ setup_filename unless @fileobj
141
+ @fileobj
142
+ end
143
+
144
+ def setup_filename # :nodoc:
145
+ if @file.respond_to?(:to_str) && @file != ''
146
+ @filename = @file
147
+ @fileobj = nil
148
+ elsif @file.respond_to?(:write)
149
+ @filename = File.join(tempdir, Digest::MD5.hexdigest(Time.now.to_s) + '.xlsx.tmp')
150
+ @fileobj = @file
151
+ else
152
+ raise "'#{@file}' must be valid filename String of IO object."
153
+ end
154
+ end
155
+
156
+ attr_reader :tempdir
157
+
158
+ #
159
+ # Sets the colour palette to the Excel defaults.
160
+ #
161
+ def set_color_palette # :nodoc:
162
+ @palette = [
163
+ [0x00, 0x00, 0x00, 0x00], # 8
164
+ [0xff, 0xff, 0xff, 0x00], # 9
165
+ [0xff, 0x00, 0x00, 0x00], # 10
166
+ [0x00, 0xff, 0x00, 0x00], # 11
167
+ [0x00, 0x00, 0xff, 0x00], # 12
168
+ [0xff, 0xff, 0x00, 0x00], # 13
169
+ [0xff, 0x00, 0xff, 0x00], # 14
170
+ [0x00, 0xff, 0xff, 0x00], # 15
171
+ [0x80, 0x00, 0x00, 0x00], # 16
172
+ [0x00, 0x80, 0x00, 0x00], # 17
173
+ [0x00, 0x00, 0x80, 0x00], # 18
174
+ [0x80, 0x80, 0x00, 0x00], # 19
175
+ [0x80, 0x00, 0x80, 0x00], # 20
176
+ [0x00, 0x80, 0x80, 0x00], # 21
177
+ [0xc0, 0xc0, 0xc0, 0x00], # 22
178
+ [0x80, 0x80, 0x80, 0x00], # 23
179
+ [0x99, 0x99, 0xff, 0x00], # 24
180
+ [0x99, 0x33, 0x66, 0x00], # 25
181
+ [0xff, 0xff, 0xcc, 0x00], # 26
182
+ [0xcc, 0xff, 0xff, 0x00], # 27
183
+ [0x66, 0x00, 0x66, 0x00], # 28
184
+ [0xff, 0x80, 0x80, 0x00], # 29
185
+ [0x00, 0x66, 0xcc, 0x00], # 30
186
+ [0xcc, 0xcc, 0xff, 0x00], # 31
187
+ [0x00, 0x00, 0x80, 0x00], # 32
188
+ [0xff, 0x00, 0xff, 0x00], # 33
189
+ [0xff, 0xff, 0x00, 0x00], # 34
190
+ [0x00, 0xff, 0xff, 0x00], # 35
191
+ [0x80, 0x00, 0x80, 0x00], # 36
192
+ [0x80, 0x00, 0x00, 0x00], # 37
193
+ [0x00, 0x80, 0x80, 0x00], # 38
194
+ [0x00, 0x00, 0xff, 0x00], # 39
195
+ [0x00, 0xcc, 0xff, 0x00], # 40
196
+ [0xcc, 0xff, 0xff, 0x00], # 41
197
+ [0xcc, 0xff, 0xcc, 0x00], # 42
198
+ [0xff, 0xff, 0x99, 0x00], # 43
199
+ [0x99, 0xcc, 0xff, 0x00], # 44
200
+ [0xff, 0x99, 0xcc, 0x00], # 45
201
+ [0xcc, 0x99, 0xff, 0x00], # 46
202
+ [0xff, 0xcc, 0x99, 0x00], # 47
203
+ [0x33, 0x66, 0xff, 0x00], # 48
204
+ [0x33, 0xcc, 0xcc, 0x00], # 49
205
+ [0x99, 0xcc, 0x00, 0x00], # 50
206
+ [0xff, 0xcc, 0x00, 0x00], # 51
207
+ [0xff, 0x99, 0x00, 0x00], # 52
208
+ [0xff, 0x66, 0x00, 0x00], # 53
209
+ [0x66, 0x66, 0x99, 0x00], # 54
210
+ [0x96, 0x96, 0x96, 0x00], # 55
211
+ [0x00, 0x33, 0x66, 0x00], # 56
212
+ [0x33, 0x99, 0x66, 0x00], # 57
213
+ [0x00, 0x33, 0x00, 0x00], # 58
214
+ [0x33, 0x33, 0x00, 0x00], # 59
215
+ [0x99, 0x33, 0x00, 0x00], # 60
216
+ [0x99, 0x33, 0x66, 0x00], # 61
217
+ [0x33, 0x33, 0x99, 0x00], # 62
218
+ [0x33, 0x33, 0x33, 0x00] # 63
219
+ ]
220
+ end
221
+ end
222
+ end
223
+ end