write_xlsx 1.11.1 → 1.11.2
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/Changes +9 -0
- data/examples/shape_all.rb +1 -1
- data/lib/write_xlsx/chart/pie.rb +14 -2
- data/lib/write_xlsx/chart/series.rb +48 -0
- data/lib/write_xlsx/chart.rb +65 -12
- data/lib/write_xlsx/chartsheet.rb +10 -1
- data/lib/write_xlsx/col_name.rb +7 -3
- data/lib/write_xlsx/format.rb +1 -1
- data/lib/write_xlsx/package/app.rb +9 -5
- data/lib/write_xlsx/package/conditional_format.rb +2 -2
- data/lib/write_xlsx/package/packager.rb +2 -3
- data/lib/write_xlsx/package/table.rb +74 -20
- data/lib/write_xlsx/package/xml_writer_simple.rb +32 -44
- data/lib/write_xlsx/sheets.rb +6 -2
- data/lib/write_xlsx/sparkline.rb +2 -2
- data/lib/write_xlsx/utility.rb +11 -9
- data/lib/write_xlsx/version.rb +1 -1
- data/lib/write_xlsx/workbook.rb +2 -2
- data/lib/write_xlsx/worksheet/cell_data.rb +16 -16
- data/lib/write_xlsx/worksheet/hyperlink.rb +2 -2
- data/lib/write_xlsx/worksheet.rb +87 -35
- data/write_xlsx.gemspec +2 -0
- metadata +31 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9dae4b31e210af6cea2f7df87d92e29d48e8b36d7408cfc0c542b4aa0fe4ea22
|
4
|
+
data.tar.gz: 3c1be9afcd157d1c7a4aabf6f0688b049a80699133e6a3d31d17874bce453b5a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7975e43767efe81a4d32819a62ce805d88c51c879e7456f9ea653be43521501bf79612d61ea95ab065efcf5bf7c43fa7f4c2e657a0ab09e211310200237faceb
|
7
|
+
data.tar.gz: 03f711b7f01ba88e9e84676ce442737bf8c7cd14db67cd04bdd50aa30eddaad850133d476e11b7a0c0ed25f485740810d6724e0449f7343cc10345bc67ba9c5c
|
data/Changes
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
Change history of write_xlsx rubygem.
|
2
2
|
|
3
|
+
2023-12-26 v1.11.2
|
4
|
+
Fix issue where header images in chartsheets weren't displayed.
|
5
|
+
Add support for custom table total functions.
|
6
|
+
Add chart option to display N/A as empty cells.
|
7
|
+
Add support for leader lines to all chart types.
|
8
|
+
Fix issue where column formulas were overwritten by table data
|
9
|
+
add Worksheet#very_hidden method.
|
10
|
+
Add add trendline equation formatting for chart.
|
11
|
+
|
3
12
|
2023-08-09 v1.11.1
|
4
13
|
Fixed issue #110. Redefining costant Struct::ColInfo
|
5
14
|
|
data/examples/shape_all.rb
CHANGED
@@ -218,7 +218,7 @@ shapes_list.each_line do |line|
|
|
218
218
|
line = line.chomp
|
219
219
|
next unless line =~ /^\w/ # Skip blank lines and comments.
|
220
220
|
|
221
|
-
sheet, name = line.split(
|
221
|
+
sheet, name = line.split("\t")
|
222
222
|
if last_sheet != sheet
|
223
223
|
worksheet = workbook.add_worksheet(sheet)
|
224
224
|
row = 2
|
data/lib/write_xlsx/chart/pie.rb
CHANGED
@@ -123,8 +123,7 @@ module Writexlsx
|
|
123
123
|
# Write the <c:legend> element.
|
124
124
|
#
|
125
125
|
def write_legend
|
126
|
-
|
127
|
-
allowed = %w[right left top bottom]
|
126
|
+
allowed = %w[right left top bottom]
|
128
127
|
delete_series = @legend.delete_series || []
|
129
128
|
|
130
129
|
if @legend.position =~ /^overlay_/
|
@@ -207,6 +206,19 @@ module Writexlsx
|
|
207
206
|
def write_first_slice_ang
|
208
207
|
@writer.empty_tag('c:firstSliceAng', [['val', @rotation]])
|
209
208
|
end
|
209
|
+
|
210
|
+
#
|
211
|
+
# Write the <c:showLeaderLines> element. This is for Pie/Doughnut charts.
|
212
|
+
# Other chart types only supported leader lines after Excel 2015 via an
|
213
|
+
# extension element.
|
214
|
+
#
|
215
|
+
def write_show_leader_lines
|
216
|
+
val = 1
|
217
|
+
|
218
|
+
attributes = [['val', val]]
|
219
|
+
|
220
|
+
@writer.empty_tag('c:showLeaderLines', attributes)
|
221
|
+
end
|
210
222
|
end
|
211
223
|
end
|
212
224
|
end
|
@@ -59,10 +59,13 @@ module Writexlsx
|
|
59
59
|
class Trendline < Chartline
|
60
60
|
attr_reader :name, :forward, :backward, :order, :period
|
61
61
|
attr_reader :intercept, :display_equation, :display_r_squared
|
62
|
+
attr_reader :label
|
62
63
|
|
63
64
|
def initialize(params)
|
64
65
|
super(params)
|
65
66
|
|
67
|
+
@label = trendline_label_properties(params[:label])
|
68
|
+
|
66
69
|
@name = params[:name]
|
67
70
|
@forward = params[:forward]
|
68
71
|
@backward = params[:backward]
|
@@ -76,6 +79,51 @@ module Writexlsx
|
|
76
79
|
|
77
80
|
private
|
78
81
|
|
82
|
+
#
|
83
|
+
# Convert user defined trendline label properties to the structure required
|
84
|
+
# internally.
|
85
|
+
#
|
86
|
+
def trendline_label_properties(_label)
|
87
|
+
return unless _label || _label.is_a?(Hash)
|
88
|
+
|
89
|
+
# Copy the user supplied properties.
|
90
|
+
label = {}
|
91
|
+
|
92
|
+
# Set the font properties for the label.
|
93
|
+
label[:font] = convert_font_args(_label[:font]) if ptrue?(_label[:font])
|
94
|
+
|
95
|
+
# Set the line properties for the label.
|
96
|
+
line = line_properties(_label[:line])
|
97
|
+
|
98
|
+
# Allow 'border' as a synonym for 'line'.
|
99
|
+
line = line_properties(_label[:border]) if ptrue?(_label[:border])
|
100
|
+
|
101
|
+
# Set the fill properties for the label.
|
102
|
+
fill = fill_properties(_label[:fill])
|
103
|
+
|
104
|
+
# Set the pattern properties for the label.
|
105
|
+
pattern = pattern_properties(_label[:pattern])
|
106
|
+
|
107
|
+
# Set the gradient fill properties for the label.
|
108
|
+
gradient = gradient_properties(_label[:gradient])
|
109
|
+
|
110
|
+
# Pattern fill overrides solid fill.
|
111
|
+
fill = nil if ptrue?(pattern)
|
112
|
+
|
113
|
+
# Gradient fill overrides solid and pattern fills.
|
114
|
+
if ptrue?(gradient)
|
115
|
+
pattern = nil
|
116
|
+
fill = nil
|
117
|
+
end
|
118
|
+
|
119
|
+
label[:line] = line
|
120
|
+
label[:fill] = fill
|
121
|
+
label[:pattern] = pattern
|
122
|
+
label[:gradient] = gradient
|
123
|
+
|
124
|
+
label
|
125
|
+
end
|
126
|
+
|
79
127
|
def types
|
80
128
|
{
|
81
129
|
exponential: 'exp',
|
data/lib/write_xlsx/chart.rb
CHANGED
@@ -568,6 +568,13 @@ module Writexlsx
|
|
568
568
|
@is_secondary
|
569
569
|
end
|
570
570
|
|
571
|
+
#
|
572
|
+
# Set the option for displaying #N/A as an empty cell in a chart.
|
573
|
+
#
|
574
|
+
def show_na_as_empty_cell
|
575
|
+
@show_na_as_empty = true
|
576
|
+
end
|
577
|
+
|
571
578
|
private
|
572
579
|
|
573
580
|
def axis_setup
|
@@ -598,6 +605,7 @@ module Writexlsx
|
|
598
605
|
@cross_between = 'between'
|
599
606
|
@date_category = false
|
600
607
|
@show_blanks = 'gap'
|
608
|
+
@show_na_as_empty = false
|
601
609
|
@show_hidden_data = false
|
602
610
|
@show_crosses = true
|
603
611
|
end
|
@@ -782,6 +790,9 @@ module Writexlsx
|
|
782
790
|
|
783
791
|
# Write the c:dispBlanksAs element.
|
784
792
|
write_disp_blanks_as
|
793
|
+
|
794
|
+
# Write the c:extLst element.
|
795
|
+
write_ext_lst_display_na if @show_na_as_empty
|
785
796
|
end
|
786
797
|
end
|
787
798
|
|
@@ -950,12 +961,13 @@ module Writexlsx
|
|
950
961
|
write_val(series)
|
951
962
|
# Write the c:smooth element.
|
952
963
|
write_c_smooth(series.smooth) if ptrue?(@smooth_allowed)
|
953
|
-
|
964
|
+
# Write the c:extLst element.
|
965
|
+
write_ext_lst_inverted_fill(series.inverted_color) if series.inverted_color
|
954
966
|
end
|
955
967
|
@series_index += 1
|
956
968
|
end
|
957
969
|
|
958
|
-
def
|
970
|
+
def write_ext_lst_inverted_fill(color)
|
959
971
|
uri = '{6F2FDCE9-48DA-4B69-8628-5D25D57E5C99}'
|
960
972
|
xmlns_c_14 =
|
961
973
|
'http://schemas.microsoft.com/office/drawing/2007/8/2/chart'
|
@@ -980,6 +992,31 @@ module Writexlsx
|
|
980
992
|
end
|
981
993
|
end
|
982
994
|
|
995
|
+
#
|
996
|
+
# Write the <c:extLst> element for the display N/A as empty cell option.
|
997
|
+
#
|
998
|
+
def write_ext_lst_display_na
|
999
|
+
uri = '{56B9EC1D-385E-4148-901F-78D8002777C0}'
|
1000
|
+
xmlns_c_16 = 'http://schemas.microsoft.com/office/drawing/2017/03/chart'
|
1001
|
+
|
1002
|
+
attributes1 = [
|
1003
|
+
['uri', uri],
|
1004
|
+
['xmlns:c16r3', xmlns_c_16]
|
1005
|
+
]
|
1006
|
+
|
1007
|
+
attributes2 = [
|
1008
|
+
['val', 1]
|
1009
|
+
]
|
1010
|
+
|
1011
|
+
@writer.tag_elements('c:extLst') do
|
1012
|
+
@writer.tag_elements('c:ext', attributes1) do
|
1013
|
+
@writer.tag_elements('c16r3:dataDisplayOptions16') do
|
1014
|
+
@writer.empty_tag('c16r3:dispNaAsBlank', attributes2)
|
1015
|
+
end
|
1016
|
+
end
|
1017
|
+
end
|
1018
|
+
end
|
1019
|
+
|
983
1020
|
def write_ser_base(series)
|
984
1021
|
# Write the c:idx element.
|
985
1022
|
write_idx(@series_index)
|
@@ -2122,7 +2159,7 @@ module Writexlsx
|
|
2122
2159
|
# Write the c:dispEq element.
|
2123
2160
|
write_disp_eq
|
2124
2161
|
# Write the c:trendlineLbl element.
|
2125
|
-
write_trendline_lbl
|
2162
|
+
write_trendline_lbl(trendline)
|
2126
2163
|
end
|
2127
2164
|
end
|
2128
2165
|
end
|
@@ -2199,12 +2236,18 @@ module Writexlsx
|
|
2199
2236
|
#
|
2200
2237
|
# Write the <c:trendlineLbl> element.
|
2201
2238
|
#
|
2202
|
-
def write_trendline_lbl
|
2239
|
+
def write_trendline_lbl(trendline)
|
2203
2240
|
@writer.tag_elements('c:trendlineLbl') do
|
2204
2241
|
# Write the c:layout element.
|
2205
2242
|
write_layout
|
2206
2243
|
# Write the c:numFmt element.
|
2207
2244
|
write_trendline_num_fmt
|
2245
|
+
# Write the c:spPr element for the label formatting.
|
2246
|
+
write_sp_pr(trendline.label)
|
2247
|
+
# Write the data label font elements.
|
2248
|
+
if trendline.label && ptrue?(trendline.label[:font])
|
2249
|
+
write_axis_font(trendline.label[:font])
|
2250
|
+
end
|
2208
2251
|
end
|
2209
2252
|
end
|
2210
2253
|
|
@@ -2544,11 +2587,25 @@ module Writexlsx
|
|
2544
2587
|
@writer.data_element('c:separator', data)
|
2545
2588
|
end
|
2546
2589
|
|
2547
|
-
#
|
2548
|
-
#
|
2549
|
-
#
|
2590
|
+
# Write the <c:showLeaderLines> element. This is different for Pie/Doughnut
|
2591
|
+
# charts. Other chart types only supported leader lines after Excel 2015 via
|
2592
|
+
# an extension element.
|
2550
2593
|
def write_show_leader_lines
|
2551
|
-
|
2594
|
+
uri = '{CE6537A1-D6FC-4f65-9D91-7224C49458BB}'
|
2595
|
+
xmlns_c_15 = 'http://schemas.microsoft.com/office/drawing/2012/chart'
|
2596
|
+
|
2597
|
+
attributes1 = [
|
2598
|
+
['uri', uri],
|
2599
|
+
['xmlns:c15', xmlns_c_15]
|
2600
|
+
]
|
2601
|
+
|
2602
|
+
attributes2 = [['val', 1]]
|
2603
|
+
|
2604
|
+
@writer.tag_elements('c:extLst') do
|
2605
|
+
@writer.tag_elements('c:ext', attributes1) do
|
2606
|
+
@writer.empty_tag('c15:showLeaderLines', attributes2)
|
2607
|
+
end
|
2608
|
+
end
|
2552
2609
|
end
|
2553
2610
|
|
2554
2611
|
#
|
@@ -2862,8 +2919,6 @@ module Writexlsx
|
|
2862
2919
|
# Write the <a:fillToRect> element.
|
2863
2920
|
#
|
2864
2921
|
def write_a_fill_to_rect(type)
|
2865
|
-
attributes = []
|
2866
|
-
|
2867
2922
|
attributes = if type == 'shape'
|
2868
2923
|
[
|
2869
2924
|
['l', 50000],
|
@@ -2885,8 +2940,6 @@ module Writexlsx
|
|
2885
2940
|
# Write the <a:tileRect> element.
|
2886
2941
|
#
|
2887
2942
|
def write_a_tile_rect(type)
|
2888
|
-
attributes = []
|
2889
|
-
|
2890
2943
|
attributes = if type == 'shape'
|
2891
2944
|
[]
|
2892
2945
|
else
|
@@ -54,6 +54,8 @@ module Writexlsx
|
|
54
54
|
write_header_footer
|
55
55
|
# Write the drawing element.
|
56
56
|
write_drawings
|
57
|
+
# Write the legaacyDrawingHF element.
|
58
|
+
write_legacy_drawing_hf
|
57
59
|
# Close the worksheet tag.
|
58
60
|
end
|
59
61
|
end
|
@@ -145,6 +147,10 @@ module Writexlsx
|
|
145
147
|
@chart.show_blanks_as(*args)
|
146
148
|
end
|
147
149
|
|
150
|
+
def show_na_as_empty_cell
|
151
|
+
@chart.show_na_as_empty_cell(*args)
|
152
|
+
end
|
153
|
+
|
148
154
|
def show_hidden_data(*args)
|
149
155
|
@chart.show_hidden_data(*args)
|
150
156
|
end
|
@@ -185,7 +191,10 @@ module Writexlsx
|
|
185
191
|
end
|
186
192
|
|
187
193
|
def external_links
|
188
|
-
[
|
194
|
+
[
|
195
|
+
@external_drawing_links,
|
196
|
+
@external_vml_links
|
197
|
+
]
|
189
198
|
end
|
190
199
|
|
191
200
|
private
|
data/lib/write_xlsx/col_name.rb
CHANGED
@@ -7,12 +7,16 @@ class ColName
|
|
7
7
|
include Singleton
|
8
8
|
|
9
9
|
def initialize
|
10
|
-
@col_str_table =
|
10
|
+
@col_str_table = []
|
11
|
+
@row_str_table = []
|
11
12
|
end
|
12
13
|
|
13
14
|
def col_str(col)
|
14
|
-
@col_str_table[col]
|
15
|
-
|
15
|
+
@col_str_table[col] ||= col_str_build(col)
|
16
|
+
end
|
17
|
+
|
18
|
+
def row_str(row)
|
19
|
+
@row_str_table[row] ||= row.to_s
|
16
20
|
end
|
17
21
|
|
18
22
|
private
|
data/lib/write_xlsx/format.rb
CHANGED
@@ -324,7 +324,7 @@ module Writexlsx
|
|
324
324
|
return colors[color_code.downcase.to_sym] if colors[color_code.downcase.to_sym]
|
325
325
|
|
326
326
|
# or the default color if string is unrecognised,
|
327
|
-
|
327
|
+
0x00 if color_code =~ /\D/
|
328
328
|
else
|
329
329
|
# or an index < 8 mapped into the correct range,
|
330
330
|
return color_code + 8 if color_code < 8
|
@@ -46,7 +46,9 @@ module Writexlsx
|
|
46
46
|
add_heading_pair(
|
47
47
|
[
|
48
48
|
'Worksheets',
|
49
|
-
@workbook.worksheets.reject
|
49
|
+
@workbook.worksheets.reject do |s|
|
50
|
+
s.is_chartsheet? || s.very_hidden?
|
51
|
+
end.count
|
50
52
|
]
|
51
53
|
)
|
52
54
|
end
|
@@ -57,14 +59,16 @@ module Writexlsx
|
|
57
59
|
|
58
60
|
def add_worksheet_part_names
|
59
61
|
@workbook.worksheets
|
60
|
-
|
61
|
-
|
62
|
+
.reject { |sheet| sheet.is_chartsheet? || sheet.very_hidden? }
|
63
|
+
.each do |sheet|
|
64
|
+
add_part_name(sheet.name)
|
65
|
+
end
|
62
66
|
end
|
63
67
|
|
64
68
|
def add_chartsheet_part_names
|
65
69
|
@workbook.worksheets
|
66
|
-
|
67
|
-
|
70
|
+
.select { |sheet| sheet.is_chartsheet? }
|
71
|
+
.each { |sheet| add_part_name(sheet.name) }
|
68
72
|
end
|
69
73
|
|
70
74
|
def add_part_name(part_name)
|
@@ -411,7 +411,7 @@ module Writexlsx
|
|
411
411
|
# Check for a cell reference in A1 notation and substitute row and column
|
412
412
|
user_range = if args[0].to_s =~ (/^\D/) && (args[0] =~ /,/)
|
413
413
|
# Check for a user defined multiple range like B3:K6,B8:K11.
|
414
|
-
args[0].sub(/^=/, '').gsub(/\s*,\s*/, ' ').gsub(
|
414
|
+
args[0].sub(/^=/, '').gsub(/\s*,\s*/, ' ').gsub("$", '')
|
415
415
|
end
|
416
416
|
|
417
417
|
if (row_col_array = row_col_notation(args.first))
|
@@ -769,7 +769,7 @@ module Writexlsx
|
|
769
769
|
attr = super
|
770
770
|
attr << ['percent', 1] if criteria == '%'
|
771
771
|
attr << ['bottom', 1] if direction
|
772
|
-
attr << ['rank',
|
772
|
+
attr << ['rank', value || 10]
|
773
773
|
attr
|
774
774
|
end
|
775
775
|
end
|
@@ -142,13 +142,12 @@ module Writexlsx
|
|
142
142
|
def write_app_file
|
143
143
|
app = Package::App.new(@workbook)
|
144
144
|
|
145
|
+
# Add the Worksheet parts.
|
146
|
+
app.add_worksheet_part_names
|
145
147
|
# Add the Worksheet heading pairs.
|
146
148
|
app.add_worksheet_heading_pairs
|
147
149
|
# Add the Chartsheet heading pairs.
|
148
150
|
app.add_chartsheet_heading_pairs
|
149
|
-
|
150
|
-
# Add the Worksheet parts.
|
151
|
-
app.add_worksheet_part_names
|
152
151
|
# Add the Chartsheet parts.
|
153
152
|
app.add_chartsheet_part_names
|
154
153
|
# Add the Named Range heading pairs.
|
@@ -12,13 +12,14 @@ module Writexlsx
|
|
12
12
|
class ColumnData
|
13
13
|
attr_reader :id
|
14
14
|
attr_accessor :name, :format, :formula, :name_format
|
15
|
-
attr_accessor :total_string, :total_function
|
15
|
+
attr_accessor :total_string, :total_function, :custom_total
|
16
16
|
|
17
17
|
def initialize(id, param = {})
|
18
18
|
@id = id
|
19
19
|
@name = "Column#{id}"
|
20
20
|
@total_string = ''
|
21
21
|
@total_function = ''
|
22
|
+
@custom_total = ''
|
22
23
|
@formula = ''
|
23
24
|
@format = nil
|
24
25
|
@name_format = nil
|
@@ -51,6 +52,7 @@ module Writexlsx
|
|
51
52
|
|
52
53
|
add_the_table_columns
|
53
54
|
write_the_cell_data_if_supplied
|
55
|
+
write_any_columns_formulas_after_the_user_supplied_table_data
|
54
56
|
store_filter_cell_positions
|
55
57
|
end
|
56
58
|
|
@@ -78,7 +80,7 @@ module Writexlsx
|
|
78
80
|
# Set up the default column data.
|
79
81
|
col_data = Package::Table::ColumnData.new(col_id + 1, @param[:columns])
|
80
82
|
|
81
|
-
|
83
|
+
overwrite_the_defaults_with_any_use_defined_values(col_id, col_data, col_num)
|
82
84
|
|
83
85
|
# Store the column data.
|
84
86
|
@columns << col_data
|
@@ -89,7 +91,7 @@ module Writexlsx
|
|
89
91
|
end # Table columns.
|
90
92
|
end
|
91
93
|
|
92
|
-
def
|
94
|
+
def overwrite_the_defaults_with_any_use_defined_values(col_id, col_data, col_num)
|
93
95
|
# Check if there are user defined values for this column.
|
94
96
|
if @param[:columns] && (user_data = @param[:columns][col_id])
|
95
97
|
# Map user defined values to internal values.
|
@@ -156,6 +158,23 @@ module Writexlsx
|
|
156
158
|
end
|
157
159
|
end
|
158
160
|
|
161
|
+
def write_any_columns_formulas_after_the_user_supplied_table_data
|
162
|
+
col_id = 0
|
163
|
+
|
164
|
+
(@col1..@col2).each do |col|
|
165
|
+
column_data = @columns[col_id]
|
166
|
+
if ptrue?(column_data) && ptrue?(column_data.formula)
|
167
|
+
formula_format = @col_formats[col_id]
|
168
|
+
formula = column_data.formula
|
169
|
+
|
170
|
+
(@first_data_row..@last_data_row).each do |row|
|
171
|
+
@worksheet.write_formula(row, col, formula, formula_format)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
col_id += 1
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
159
178
|
def store_filter_cell_positions
|
160
179
|
if ptrue?(@param[:autofilter])
|
161
180
|
(@col1..@col2).each do |col|
|
@@ -234,25 +253,45 @@ module Writexlsx
|
|
234
253
|
]
|
235
254
|
end
|
236
255
|
|
237
|
-
def handle_the_column_formula(col_data,
|
256
|
+
def handle_the_column_formula(col_data, _col_num, formula, _format)
|
238
257
|
return unless formula
|
239
258
|
|
240
|
-
|
259
|
+
formula = formula.sub(/^=/, '').gsub(/@/, '[#This Row],')
|
241
260
|
|
242
|
-
|
243
|
-
|
244
|
-
|
261
|
+
# Escape any future functions.
|
262
|
+
col_data.formula = @worksheet.prepare_formula(formula, 1)
|
263
|
+
|
264
|
+
# (@first_data_row..@last_data_row).each do |row|
|
265
|
+
# @worksheet.write_formula(row, col_num, col_data.formula, format)
|
266
|
+
# end
|
245
267
|
end
|
246
268
|
|
247
269
|
def handle_the_function_for_the_table_row(row2, col_data, col_num, user_data)
|
248
|
-
|
270
|
+
formula = ''
|
271
|
+
function = user_data[:total_function]
|
272
|
+
function = 'countNums' if function == 'count_nums'
|
273
|
+
function = 'stdDev' if function == 'std_dev'
|
249
274
|
|
250
|
-
|
251
|
-
|
275
|
+
subtotals = {
|
276
|
+
average: 101,
|
277
|
+
countNums: 102,
|
278
|
+
count: 103,
|
279
|
+
max: 104,
|
280
|
+
min: 105,
|
281
|
+
stdDev: 106,
|
282
|
+
sum: 109,
|
283
|
+
var: 110
|
284
|
+
}
|
252
285
|
|
253
|
-
|
286
|
+
if subtotals[function.to_sym]
|
287
|
+
formula = table_function_to_formula(function, col_data.name)
|
288
|
+
else
|
289
|
+
formula = @worksheet.prepare_formula(function, 1)
|
290
|
+
col_data.custom_total = formula
|
291
|
+
function = 'custom'
|
292
|
+
end
|
254
293
|
|
255
|
-
|
294
|
+
col_data.total_function = function
|
256
295
|
@worksheet.write_formula(row2, col_num, formula, user_data[:format], user_data[:total_value])
|
257
296
|
end
|
258
297
|
|
@@ -260,10 +299,10 @@ module Writexlsx
|
|
260
299
|
# Convert a table total function to a worksheet formula.
|
261
300
|
#
|
262
301
|
def table_function_to_formula(function, col_name)
|
263
|
-
col_name = col_name.gsub(
|
264
|
-
.gsub(
|
265
|
-
.gsub(
|
266
|
-
.gsub(
|
302
|
+
col_name = col_name.gsub("'", "''")
|
303
|
+
.gsub("#", "'#")
|
304
|
+
.gsub("[", "'[")
|
305
|
+
.gsub("]", "']")
|
267
306
|
|
268
307
|
subtotals = {
|
269
308
|
average: 101,
|
@@ -395,10 +434,16 @@ module Writexlsx
|
|
395
434
|
|
396
435
|
attributes << [:dataDxfId, col_data.format] if col_data.format
|
397
436
|
|
398
|
-
if ptrue?(col_data.formula)
|
437
|
+
if ptrue?(col_data.formula) || ptrue?(col_data.custom_total)
|
399
438
|
@writer.tag_elements('tableColumn', attributes) do
|
400
|
-
|
401
|
-
|
439
|
+
if ptrue?(col_data.formula)
|
440
|
+
# Write the calculatedColumnFormula element.
|
441
|
+
write_calculated_column_formula(col_data.formula)
|
442
|
+
end
|
443
|
+
if ptrue?(col_data.custom_total)
|
444
|
+
# Write the totalsRowFormula element.
|
445
|
+
write_totals_row_formula(col_data.custom_total)
|
446
|
+
end
|
402
447
|
end
|
403
448
|
else
|
404
449
|
@writer.empty_tag('tableColumn', attributes)
|
@@ -425,6 +470,15 @@ module Writexlsx
|
|
425
470
|
def write_calculated_column_formula(formula)
|
426
471
|
@writer.data_element('calculatedColumnFormula', formula)
|
427
472
|
end
|
473
|
+
|
474
|
+
#
|
475
|
+
# _write_totals_row_formula()
|
476
|
+
#
|
477
|
+
# Write the <totalsRowFormula> element.
|
478
|
+
#
|
479
|
+
def write_totals_row_formula(formula)
|
480
|
+
@writer.data_element('totalsRowFormula', formula)
|
481
|
+
end
|
428
482
|
end
|
429
483
|
end
|
430
484
|
end
|
@@ -29,33 +29,28 @@ module Writexlsx
|
|
29
29
|
io_write(str)
|
30
30
|
end
|
31
31
|
|
32
|
-
def tag_elements(tag, attributes =
|
32
|
+
def tag_elements(tag, attributes = nil)
|
33
33
|
start_tag(tag, attributes)
|
34
34
|
yield
|
35
35
|
end_tag(tag)
|
36
36
|
end
|
37
37
|
|
38
|
-
def tag_elements_str(tag, attributes =
|
38
|
+
def tag_elements_str(tag, attributes = nil)
|
39
39
|
start_tag_str(tag, attributes) +
|
40
40
|
yield +
|
41
41
|
end_tag_str(tag)
|
42
42
|
end
|
43
43
|
|
44
|
-
def start_tag(tag, attr =
|
44
|
+
def start_tag(tag, attr = nil)
|
45
45
|
io_write(start_tag_str(tag, attr))
|
46
46
|
end
|
47
47
|
|
48
|
-
def start_tag_str(tag, attr =
|
49
|
-
if attr.empty?
|
50
|
-
|
51
|
-
unless result
|
52
|
-
result = "<#{tag}>"
|
53
|
-
@tag_start_cache[tag] = result
|
54
|
-
end
|
48
|
+
def start_tag_str(tag, attr = nil)
|
49
|
+
if attr.nil? || attr.empty?
|
50
|
+
@tag_start_cache[tag] ||= "<#{tag}>"
|
55
51
|
else
|
56
|
-
|
52
|
+
"<#{tag}#{key_vals(attr)}>"
|
57
53
|
end
|
58
|
-
result
|
59
54
|
end
|
60
55
|
|
61
56
|
def end_tag(tag)
|
@@ -63,28 +58,15 @@ module Writexlsx
|
|
63
58
|
end
|
64
59
|
|
65
60
|
def end_tag_str(tag)
|
66
|
-
|
67
|
-
unless result
|
68
|
-
result = "</#{tag}>"
|
69
|
-
@tag_end_cache[tag] = result
|
70
|
-
end
|
71
|
-
result
|
61
|
+
@tag_end_cache[tag] ||= "</#{tag}>"
|
72
62
|
end
|
73
63
|
|
74
|
-
def empty_tag(tag, attr =
|
64
|
+
def empty_tag(tag, attr = nil)
|
75
65
|
str = "<#{tag}#{key_vals(attr)}/>"
|
76
66
|
io_write(str)
|
77
67
|
end
|
78
68
|
|
79
|
-
def
|
80
|
-
io_write(empty_tag_encoded_str(tag, attr))
|
81
|
-
end
|
82
|
-
|
83
|
-
def empty_tag_encoded_str(tag, attr = [])
|
84
|
-
"<#{tag}#{key_vals(attr)}/>"
|
85
|
-
end
|
86
|
-
|
87
|
-
def data_element(tag, data, attr = [])
|
69
|
+
def data_element(tag, data, attr = nil)
|
88
70
|
tag_elements(tag, attr) { io_write(escape_data(data)) }
|
89
71
|
end
|
90
72
|
|
@@ -126,31 +108,37 @@ module Writexlsx
|
|
126
108
|
|
127
109
|
private
|
128
110
|
|
129
|
-
def key_val(key, val)
|
130
|
-
%( #{key}="#{val}")
|
131
|
-
end
|
132
|
-
|
133
111
|
def key_vals(attribute)
|
134
|
-
attribute
|
135
|
-
|
112
|
+
if attribute
|
113
|
+
result = "".dup
|
114
|
+
attribute.each do |attr|
|
115
|
+
# Generate and concat %( #{key}="#{val}") values for attribute pair
|
116
|
+
result << " "
|
117
|
+
result << attr.first.to_s
|
118
|
+
result << '="'
|
119
|
+
result << escape_attributes(attr.last).to_s
|
120
|
+
result << '"'
|
121
|
+
end
|
122
|
+
result
|
123
|
+
end
|
136
124
|
end
|
137
125
|
|
138
126
|
def escape_attributes(str = '')
|
139
|
-
return str unless str.
|
127
|
+
return str unless str.respond_to?(:match) && str =~ /["&<>\n]/
|
140
128
|
|
141
129
|
str
|
142
|
-
.gsub(
|
143
|
-
.gsub(
|
144
|
-
.gsub(
|
145
|
-
.gsub(
|
146
|
-
.gsub(
|
130
|
+
.gsub("&", "&")
|
131
|
+
.gsub('"', """)
|
132
|
+
.gsub("<", "<")
|
133
|
+
.gsub(">", ">")
|
134
|
+
.gsub("\n", "
")
|
147
135
|
end
|
148
136
|
|
149
137
|
def escape_data(str = '')
|
150
|
-
if str.
|
151
|
-
str.gsub(
|
152
|
-
.gsub(
|
153
|
-
.gsub(
|
138
|
+
if str.respond_to?(:match) && str =~ /[&<>]/
|
139
|
+
str.gsub("&", '&')
|
140
|
+
.gsub("<", '<')
|
141
|
+
.gsub(">", '>')
|
154
142
|
else
|
155
143
|
str
|
156
144
|
end
|
data/lib/write_xlsx/sheets.rb
CHANGED
@@ -252,9 +252,13 @@ module Writexlsx
|
|
252
252
|
['sheetId', sheet_id]
|
253
253
|
]
|
254
254
|
|
255
|
-
|
255
|
+
if sheet.hidden?
|
256
|
+
attributes << %w[state hidden]
|
257
|
+
elsif sheet.very_hidden?
|
258
|
+
attributes << %w[state veryHidden]
|
259
|
+
end
|
256
260
|
attributes << r_id_attributes(sheet_id)
|
257
|
-
writer.
|
261
|
+
writer.empty_tag('sheet', attributes)
|
258
262
|
end
|
259
263
|
end
|
260
264
|
end
|
data/lib/write_xlsx/sparkline.rb
CHANGED
@@ -43,13 +43,13 @@ module Writexlsx
|
|
43
43
|
# Cleanup the input ranges.
|
44
44
|
@ranges.collect! do |range|
|
45
45
|
# Remove the absolute reference $ symbols.
|
46
|
-
range = range.gsub(
|
46
|
+
range = range.gsub("$", '')
|
47
47
|
# Convert a simple range into a full Sheet1!A1:D1 range.
|
48
48
|
range = "#{sheetname}!#{range}" unless range =~ /!/
|
49
49
|
range
|
50
50
|
end
|
51
51
|
# Cleanup the input locations.
|
52
|
-
@locations.collect! { |location| location.gsub(
|
52
|
+
@locations.collect! { |location| location.gsub("$", '') }
|
53
53
|
|
54
54
|
# Map options.
|
55
55
|
@high = param[:high_point]
|
data/lib/write_xlsx/utility.rb
CHANGED
@@ -31,10 +31,12 @@ module Writexlsx
|
|
31
31
|
#
|
32
32
|
# xl_rowcol_to_cell($row, col, row_absolute, col_absolute)
|
33
33
|
#
|
34
|
-
def xl_rowcol_to_cell(
|
35
|
-
|
34
|
+
def xl_rowcol_to_cell(row_or_name, col, row_absolute = false, col_absolute = false)
|
35
|
+
if row_or_name.is_a?(Integer)
|
36
|
+
row_or_name += 1 # Change from 0-indexed to 1 indexed.
|
37
|
+
end
|
36
38
|
col_str = xl_col_to_name(col, col_absolute)
|
37
|
-
"#{col_str}#{absolute_char(row_absolute)}#{
|
39
|
+
"#{col_str}#{absolute_char(row_absolute)}#{row_or_name}"
|
38
40
|
end
|
39
41
|
|
40
42
|
#
|
@@ -53,7 +55,7 @@ module Writexlsx
|
|
53
55
|
|
54
56
|
# Convert base26 column string to number
|
55
57
|
# All your Base are belong to us.
|
56
|
-
chars = col.split(
|
58
|
+
chars = col.split("")
|
57
59
|
expn = 0
|
58
60
|
col = 0
|
59
61
|
|
@@ -112,7 +114,7 @@ module Writexlsx
|
|
112
114
|
#
|
113
115
|
def xl_string_pixel_width(string)
|
114
116
|
length = 0
|
115
|
-
string.to_s.split(
|
117
|
+
string.to_s.split("").each { |char| length += CHAR_WIDTHS[char] || 8 }
|
116
118
|
|
117
119
|
length
|
118
120
|
end
|
@@ -128,7 +130,7 @@ module Writexlsx
|
|
128
130
|
name = sheetname.dup
|
129
131
|
if name =~ /\W/ && !(name =~ /^'/)
|
130
132
|
# Double quote and single quoted strings.
|
131
|
-
name = name.gsub(
|
133
|
+
name = name.gsub("'", "''")
|
132
134
|
name = "'#{name}'"
|
133
135
|
end
|
134
136
|
name
|
@@ -159,7 +161,7 @@ module Writexlsx
|
|
159
161
|
seconds = 0 # Time expressed as fraction of 24h hours in seconds
|
160
162
|
|
161
163
|
# Split into date and time.
|
162
|
-
date, time = date_time.split(
|
164
|
+
date, time = date_time.split("T")
|
163
165
|
|
164
166
|
# We allow the time portion of the input DateTime to be optional.
|
165
167
|
if time
|
@@ -207,7 +209,7 @@ module Writexlsx
|
|
207
209
|
# becomes 100 for 4 and 100 year leapdays and 400 for 400 year leapdays.
|
208
210
|
#
|
209
211
|
epoch = date_1904? ? 1904 : 1900
|
210
|
-
offset = date_1904? ?
|
212
|
+
offset = date_1904? ? 4 : 0
|
211
213
|
norm = 300
|
212
214
|
range = year - epoch
|
213
215
|
|
@@ -248,7 +250,7 @@ module Writexlsx
|
|
248
250
|
def escape_url(url)
|
249
251
|
unless url =~ /%[0-9a-fA-F]{2}/
|
250
252
|
# Escape the URL escape symbol.
|
251
|
-
url = url.gsub(
|
253
|
+
url = url.gsub("%", "%25")
|
252
254
|
|
253
255
|
# Escape whitespae in URL.
|
254
256
|
url = url.gsub(/[\s\x00]/, '%20')
|
data/lib/write_xlsx/version.rb
CHANGED
data/lib/write_xlsx/workbook.rb
CHANGED
@@ -715,7 +715,7 @@ module Writexlsx
|
|
715
715
|
|
716
716
|
# Split the cell range into 2 cells or else use single cell for both.
|
717
717
|
if cells =~ /:/
|
718
|
-
cell_1, cell_2 = cells.split(
|
718
|
+
cell_1, cell_2 = cells.split(":")
|
719
719
|
else
|
720
720
|
cell_1 = cells
|
721
721
|
cell_2 = cells
|
@@ -724,7 +724,7 @@ module Writexlsx
|
|
724
724
|
# Remove leading/trailing apostrophes and convert escaped quotes to single.
|
725
725
|
sheetname.sub!(/^'/, '')
|
726
726
|
sheetname.sub!(/'$/, '')
|
727
|
-
sheetname.gsub!(
|
727
|
+
sheetname.gsub!("''", "'")
|
728
728
|
|
729
729
|
row_start, col_start = xl_cell_to_rowcol(cell_1)
|
730
730
|
row_end, col_end = xl_cell_to_rowcol(cell_2)
|
@@ -12,10 +12,10 @@ module Writexlsx
|
|
12
12
|
# attributes for the <cell> element. This is the innermost loop so efficiency is
|
13
13
|
# important where possible.
|
14
14
|
#
|
15
|
-
def cell_attributes(worksheet, row, col) # :nodoc:
|
15
|
+
def cell_attributes(worksheet, row, row_name, col) # :nodoc:
|
16
16
|
xf_index = xf ? xf.get_xf_index : 0
|
17
17
|
attributes = [
|
18
|
-
['r', xl_rowcol_to_cell(
|
18
|
+
['r', xl_rowcol_to_cell(row_name, col)]
|
19
19
|
]
|
20
20
|
|
21
21
|
# Add the cell format index.
|
@@ -48,8 +48,8 @@ module Writexlsx
|
|
48
48
|
@token
|
49
49
|
end
|
50
50
|
|
51
|
-
def write_cell(worksheet, row, col)
|
52
|
-
worksheet.writer.tag_elements('c', cell_attributes(worksheet, row, col)) do
|
51
|
+
def write_cell(worksheet, row, row_name, col)
|
52
|
+
worksheet.writer.tag_elements('c', cell_attributes(worksheet, row, row_name, col)) do
|
53
53
|
worksheet.write_cell_value(token)
|
54
54
|
end
|
55
55
|
end
|
@@ -69,8 +69,8 @@ module Writexlsx
|
|
69
69
|
end
|
70
70
|
|
71
71
|
TYPE_STR_ATTRS = %w[t s].freeze
|
72
|
-
def write_cell(worksheet, row, col)
|
73
|
-
attributes = cell_attributes(worksheet, row, col)
|
72
|
+
def write_cell(worksheet, row, row_name, col)
|
73
|
+
attributes = cell_attributes(worksheet, row, row_name, col)
|
74
74
|
attributes << TYPE_STR_ATTRS
|
75
75
|
worksheet.writer.tag_elements('c', attributes) do
|
76
76
|
worksheet.write_cell_value(token)
|
@@ -101,11 +101,11 @@ module Writexlsx
|
|
101
101
|
@result || 0
|
102
102
|
end
|
103
103
|
|
104
|
-
def write_cell(worksheet, row, col)
|
104
|
+
def write_cell(worksheet, row, row_name, col)
|
105
105
|
truefalse = { 'TRUE' => 1, 'FALSE' => 0 }
|
106
106
|
error_code = ['#DIV/0!', '#N/A', '#NAME?', '#NULL!', '#NUM!', '#REF!', '#VALUE!']
|
107
107
|
|
108
|
-
attributes = cell_attributes(worksheet, row, col)
|
108
|
+
attributes = cell_attributes(worksheet, row, row_name, col)
|
109
109
|
if @result && !(@result.to_s =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/)
|
110
110
|
if truefalse[@result]
|
111
111
|
attributes << %w[t b]
|
@@ -137,8 +137,8 @@ module Writexlsx
|
|
137
137
|
@result || 0
|
138
138
|
end
|
139
139
|
|
140
|
-
def write_cell(worksheet, row, col)
|
141
|
-
worksheet.writer.tag_elements('c', cell_attributes(worksheet, row, col)) do
|
140
|
+
def write_cell(worksheet, row, row_name, col)
|
141
|
+
worksheet.writer.tag_elements('c', cell_attributes(worksheet, row, row_name, col)) do
|
142
142
|
worksheet.write_cell_array_formula(token, range)
|
143
143
|
worksheet.write_cell_value(result)
|
144
144
|
end
|
@@ -159,9 +159,9 @@ module Writexlsx
|
|
159
159
|
@result || 0
|
160
160
|
end
|
161
161
|
|
162
|
-
def write_cell(worksheet, row, col)
|
162
|
+
def write_cell(worksheet, row, row_name, col)
|
163
163
|
# Add metadata linkage for dynamic array formulas.
|
164
|
-
attributes = cell_attributes(worksheet, row, col)
|
164
|
+
attributes = cell_attributes(worksheet, row, row_name, col)
|
165
165
|
attributes << %w[cm 1]
|
166
166
|
|
167
167
|
worksheet.writer.tag_elements('c', attributes) do
|
@@ -183,8 +183,8 @@ module Writexlsx
|
|
183
183
|
@token
|
184
184
|
end
|
185
185
|
|
186
|
-
def write_cell(worksheet, row, col)
|
187
|
-
attributes = cell_attributes(worksheet, row, col)
|
186
|
+
def write_cell(worksheet, row, row_name, col)
|
187
|
+
attributes = cell_attributes(worksheet, row, row_name, col)
|
188
188
|
|
189
189
|
attributes << %w[t b]
|
190
190
|
worksheet.writer.tag_elements('c', attributes) do
|
@@ -202,8 +202,8 @@ module Writexlsx
|
|
202
202
|
''
|
203
203
|
end
|
204
204
|
|
205
|
-
def write_cell(worksheet, row, col)
|
206
|
-
worksheet.writer.empty_tag('c', cell_attributes(worksheet, row, col))
|
205
|
+
def write_cell(worksheet, row, row_name, col)
|
206
|
+
worksheet.writer.empty_tag('c', cell_attributes(worksheet, row, row_name, col))
|
207
207
|
end
|
208
208
|
end
|
209
209
|
end
|
@@ -28,7 +28,7 @@ module Writexlsx
|
|
28
28
|
normalized_str = str.sub(/^mailto:/, '')
|
29
29
|
|
30
30
|
# Split url into the link and optional anchor/location.
|
31
|
-
url, @url_str = url.split(
|
31
|
+
url, @url_str = url.split("#", 2)
|
32
32
|
|
33
33
|
# Escape URL unless it looks already escaped.
|
34
34
|
url = escape_url(url)
|
@@ -106,7 +106,7 @@ module Writexlsx
|
|
106
106
|
str = str.sub(/^mailto:/, '')
|
107
107
|
|
108
108
|
# Split url into the link and optional anchor/location.
|
109
|
-
url, url_str = url.split(
|
109
|
+
url, url_str = url.split("#", 2)
|
110
110
|
|
111
111
|
# Escape URL unless it looks already escaped.
|
112
112
|
url = escape_url(url)
|
data/lib/write_xlsx/worksheet.rb
CHANGED
@@ -228,15 +228,26 @@ module Writexlsx
|
|
228
228
|
#
|
229
229
|
# Hide this worksheet.
|
230
230
|
#
|
231
|
-
def hide
|
232
|
-
@hidden =
|
231
|
+
def hide(hidden = :hidden)
|
232
|
+
@hidden = hidden
|
233
233
|
@selected = false
|
234
234
|
@workbook.activesheet = 0 if @workbook.activesheet == @index
|
235
235
|
@workbook.firstsheet = 0 if @workbook.firstsheet == @index
|
236
236
|
end
|
237
237
|
|
238
|
+
#
|
239
|
+
# Hide this worksheet. This can only be unhidden from VBA.
|
240
|
+
#
|
241
|
+
def very_hidden
|
242
|
+
hide(:very_hidden)
|
243
|
+
end
|
244
|
+
|
238
245
|
def hidden? # :nodoc:
|
239
|
-
@hidden
|
246
|
+
@hidden == :hidden
|
247
|
+
end
|
248
|
+
|
249
|
+
def very_hidden? # :nodoc:
|
250
|
+
@hidden == :very_hidden
|
240
251
|
end
|
241
252
|
|
242
253
|
#
|
@@ -271,7 +282,7 @@ module Writexlsx
|
|
271
282
|
if range.nil?
|
272
283
|
raise "The range must be defined in unprotect_range())\n"
|
273
284
|
else
|
274
|
-
range = range.gsub(
|
285
|
+
range = range.gsub("$", "")
|
275
286
|
range = range.sub(/^=/, "")
|
276
287
|
@num_protected_ranges += 1
|
277
288
|
end
|
@@ -612,8 +623,17 @@ module Writexlsx
|
|
612
623
|
#
|
613
624
|
# This method is used to display the worksheet in "Page View/Layout" mode.
|
614
625
|
#
|
615
|
-
def set_page_view(flag =
|
616
|
-
@page_view =
|
626
|
+
def set_page_view(flag = 1)
|
627
|
+
@page_view = flag
|
628
|
+
end
|
629
|
+
|
630
|
+
#
|
631
|
+
# set_pagebreak_view
|
632
|
+
#
|
633
|
+
# Set the page view mode.
|
634
|
+
#
|
635
|
+
def set_pagebreak_view
|
636
|
+
@page_view = 2
|
617
637
|
end
|
618
638
|
|
619
639
|
#
|
@@ -648,7 +668,7 @@ module Writexlsx
|
|
648
668
|
raise 'Header string must be less than 255 characters' if string.length > 255
|
649
669
|
|
650
670
|
# Replace the Excel placeholder &[Picture] with the internal &G.
|
651
|
-
@page_setup.header = string.gsub(
|
671
|
+
@page_setup.header = string.gsub("&[Picture]", '&G')
|
652
672
|
|
653
673
|
@page_setup.header_footer_aligns = options[:align_with_margins] if options[:align_with_margins]
|
654
674
|
|
@@ -664,7 +684,7 @@ module Writexlsx
|
|
664
684
|
end
|
665
685
|
|
666
686
|
# placeholeder /&G/ の数
|
667
|
-
placeholder_count = @page_setup.header.scan(
|
687
|
+
placeholder_count = @page_setup.header.scan("&G").count
|
668
688
|
|
669
689
|
image_count = @header_images.count
|
670
690
|
|
@@ -685,7 +705,7 @@ module Writexlsx
|
|
685
705
|
@page_setup.footer = string.dup
|
686
706
|
|
687
707
|
# Replace the Excel placeholder &[Picture] with the internal &G.
|
688
|
-
@page_setup.footer = string.gsub(
|
708
|
+
@page_setup.footer = string.gsub("&[Picture]", '&G')
|
689
709
|
|
690
710
|
@page_setup.header_footer_aligns = options[:align_with_margins] if options[:align_with_margins]
|
691
711
|
|
@@ -701,7 +721,7 @@ module Writexlsx
|
|
701
721
|
end
|
702
722
|
|
703
723
|
# placeholeder /&G/ の数
|
704
|
-
placeholder_count = @page_setup.footer.scan(
|
724
|
+
placeholder_count = @page_setup.footer.scan("&G").count
|
705
725
|
|
706
726
|
image_count = @footer_images.count
|
707
727
|
|
@@ -1320,7 +1340,7 @@ module Writexlsx
|
|
1320
1340
|
# Utility method to strip equal sign and array braces from a formula
|
1321
1341
|
# and also expand out future and dynamic array formulas.
|
1322
1342
|
#
|
1323
|
-
def prepare_formula(given_formula)
|
1343
|
+
def prepare_formula(given_formula, expand_future_functions = nil)
|
1324
1344
|
# Ignore empty/null formulas.
|
1325
1345
|
return given_formula unless ptrue?(given_formula)
|
1326
1346
|
|
@@ -1331,26 +1351,46 @@ module Writexlsx
|
|
1331
1351
|
return formula if formula =~ /_xlfn\./
|
1332
1352
|
|
1333
1353
|
# Expand dynamic array formulas.
|
1334
|
-
formula = expand_formula(formula, '
|
1354
|
+
formula = expand_formula(formula, 'ANCHORARRAY\(')
|
1355
|
+
formula = expand_formula(formula, 'BYCOL\(')
|
1356
|
+
formula = expand_formula(formula, 'BYROW\(')
|
1357
|
+
formula = expand_formula(formula, 'CHOOSECOLS\(')
|
1358
|
+
formula = expand_formula(formula, 'CHOOSEROWS\(')
|
1359
|
+
formula = expand_formula(formula, 'DROP\(')
|
1360
|
+
formula = expand_formula(formula, 'EXPAND\(')
|
1361
|
+
formula = expand_formula(formula, 'FILTER\(', '._xlws')
|
1362
|
+
formula = expand_formula(formula, 'HSTACK\(')
|
1335
1363
|
formula = expand_formula(formula, 'LAMBDA\(')
|
1364
|
+
formula = expand_formula(formula, 'MAKEARRAY\(')
|
1365
|
+
formula = expand_formula(formula, 'MAP\(')
|
1366
|
+
formula = expand_formula(formula, 'RANDARRAY\(')
|
1367
|
+
formula = expand_formula(formula, 'REDUCE\(')
|
1368
|
+
formula = expand_formula(formula, 'SCAN\(')
|
1369
|
+
formula = expand_formula(formula, 'SEQUENCE\(')
|
1336
1370
|
formula = expand_formula(formula, 'SINGLE\(')
|
1371
|
+
formula = expand_formula(formula, 'SORT\(', '._xlws')
|
1337
1372
|
formula = expand_formula(formula, 'SORTBY\(')
|
1373
|
+
formula = expand_formula(formula, 'SWITCH\(')
|
1374
|
+
formula = expand_formula(formula, 'TAKE\(')
|
1375
|
+
formula = expand_formula(formula, 'TEXTSPLIT\(')
|
1376
|
+
formula = expand_formula(formula, 'TOCOL\(')
|
1377
|
+
formula = expand_formula(formula, 'TOROW\(')
|
1338
1378
|
formula = expand_formula(formula, 'UNIQUE\(')
|
1339
|
-
formula = expand_formula(formula, '
|
1379
|
+
formula = expand_formula(formula, 'VSTACK\(')
|
1380
|
+
formula = expand_formula(formula, 'WRAPCOLS\(')
|
1381
|
+
formula = expand_formula(formula, 'WRAPROWS\(')
|
1340
1382
|
formula = expand_formula(formula, 'XLOOKUP\(')
|
1341
|
-
formula = expand_formula(formula, 'SEQUENCE\(')
|
1342
|
-
formula = expand_formula(formula, 'RANDARRAY\(')
|
1343
|
-
formula = expand_formula(formula, 'SORT\(', '._xlws')
|
1344
|
-
formula = expand_formula(formula, 'ANCHORARRAY\(')
|
1345
|
-
formula = expand_formula(formula, 'FILTER\(', '._xlws')
|
1346
1383
|
|
1347
|
-
|
1384
|
+
if !@use_future_functions && !ptrue?(expand_future_functions)
|
1385
|
+
return formula
|
1386
|
+
end
|
1348
1387
|
|
1349
1388
|
# Future functions.
|
1350
1389
|
formula = expand_formula(formula, 'ACOTH\(')
|
1351
1390
|
formula = expand_formula(formula, 'ACOT\(')
|
1352
1391
|
formula = expand_formula(formula, 'AGGREGATE\(')
|
1353
1392
|
formula = expand_formula(formula, 'ARABIC\(')
|
1393
|
+
formula = expand_formula(formula, 'ARRAYTOTEXT\(')
|
1354
1394
|
formula = expand_formula(formula, 'BASE\(')
|
1355
1395
|
formula = expand_formula(formula, 'BETA.DIST\(')
|
1356
1396
|
formula = expand_formula(formula, 'BETA.INV\(')
|
@@ -1415,7 +1455,9 @@ module Writexlsx
|
|
1415
1455
|
formula = expand_formula(formula, 'IMSINH\(')
|
1416
1456
|
formula = expand_formula(formula, 'IMTAN\(')
|
1417
1457
|
formula = expand_formula(formula, 'ISFORMULA\(')
|
1458
|
+
formula = expand_formula(formula, 'ISOMITTED\(')
|
1418
1459
|
formula = expand_formula(formula, 'ISOWEEKNUM\(')
|
1460
|
+
formula = expand_formula(formula, 'LET\(')
|
1419
1461
|
formula = expand_formula(formula, 'LOGNORM.DIST\(')
|
1420
1462
|
formula = expand_formula(formula, 'LOGNORM.INV\(')
|
1421
1463
|
formula = expand_formula(formula, 'MAXIFS\(')
|
@@ -1450,24 +1492,26 @@ module Writexlsx
|
|
1450
1492
|
formula = expand_formula(formula, 'SKEW.P\(')
|
1451
1493
|
formula = expand_formula(formula, 'STDEV.P\(')
|
1452
1494
|
formula = expand_formula(formula, 'STDEV.S\(')
|
1453
|
-
formula = expand_formula(formula, 'SWITCH\(')
|
1454
1495
|
formula = expand_formula(formula, 'T.DIST.2T\(')
|
1455
1496
|
formula = expand_formula(formula, 'T.DIST.RT\(')
|
1456
1497
|
formula = expand_formula(formula, 'T.DIST\(')
|
1457
1498
|
formula = expand_formula(formula, 'T.INV.2T\(')
|
1458
1499
|
formula = expand_formula(formula, 'T.INV\(')
|
1459
1500
|
formula = expand_formula(formula, 'T.TEST\(')
|
1501
|
+
formula = expand_formula(formula, 'TEXTAFTER\(')
|
1502
|
+
formula = expand_formula(formula, 'TEXTBEFORE\(')
|
1460
1503
|
formula = expand_formula(formula, 'TEXTJOIN\(')
|
1461
1504
|
formula = expand_formula(formula, 'UNICHAR\(')
|
1462
1505
|
formula = expand_formula(formula, 'UNICODE\(')
|
1506
|
+
formula = expand_formula(formula, 'VALUETOTEXT\(')
|
1463
1507
|
formula = expand_formula(formula, 'VAR.P\(')
|
1464
1508
|
formula = expand_formula(formula, 'VAR.S\(')
|
1465
1509
|
formula = expand_formula(formula, 'WEBSERVICE\(')
|
1466
1510
|
formula = expand_formula(formula, 'WEIBULL.DIST\(')
|
1511
|
+
formula = expand_formula(formula, 'XMATCH\(')
|
1467
1512
|
formula = expand_formula(formula, 'XOR\(')
|
1468
1513
|
expand_formula(formula, 'Z.TEST\(')
|
1469
1514
|
end
|
1470
|
-
private :prepare_formula
|
1471
1515
|
|
1472
1516
|
#
|
1473
1517
|
# :call-seq:
|
@@ -1551,7 +1595,7 @@ module Writexlsx
|
|
1551
1595
|
end
|
1552
1596
|
|
1553
1597
|
# Modify the formula string, as needed.
|
1554
|
-
formula = prepare_formula(formula)
|
1598
|
+
formula = prepare_formula(formula, 1)
|
1555
1599
|
|
1556
1600
|
store_data_to_table(
|
1557
1601
|
if type == 'a'
|
@@ -2926,7 +2970,7 @@ module Writexlsx
|
|
2926
2970
|
tokens.map! do |token|
|
2927
2971
|
token.sub!(/^"/, '')
|
2928
2972
|
token.sub!(/"$/, '')
|
2929
|
-
token.gsub!(
|
2973
|
+
token.gsub!('""', '"')
|
2930
2974
|
|
2931
2975
|
# if token is number, convert to numeric.
|
2932
2976
|
if token =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/
|
@@ -3208,13 +3252,13 @@ module Writexlsx
|
|
3208
3252
|
target = escape_url(url.sub(/^external:/, ''))
|
3209
3253
|
|
3210
3254
|
# Additional escape not required in worksheet hyperlinks
|
3211
|
-
target = target.gsub(
|
3255
|
+
target = target.gsub("#", '%23')
|
3212
3256
|
|
3213
3257
|
# Prefix absolute paths (not relative) with file:///
|
3214
3258
|
target = if target =~ /^\w:/ || target =~ /^\\\\/
|
3215
3259
|
"file:///#{target}"
|
3216
3260
|
else
|
3217
|
-
target.gsub(
|
3261
|
+
target.gsub("\\", '/')
|
3218
3262
|
end
|
3219
3263
|
end
|
3220
3264
|
|
@@ -3437,7 +3481,7 @@ EOS
|
|
3437
3481
|
def encode_password(password) # :nodoc:
|
3438
3482
|
hash = 0
|
3439
3483
|
|
3440
|
-
password.reverse.split(
|
3484
|
+
password.reverse.split("").each do |char|
|
3441
3485
|
hash = ((hash >> 14) & 0x01) | ((hash << 1) & 0x7fff)
|
3442
3486
|
hash ^= char.ord
|
3443
3487
|
end
|
@@ -3559,16 +3603,27 @@ EOS
|
|
3559
3603
|
attributes << ["showOutlineSymbols", 0] if @outline_on
|
3560
3604
|
|
3561
3605
|
# Set the page view/layout mode if required.
|
3562
|
-
|
3563
|
-
|
3606
|
+
case @page_view
|
3607
|
+
when 1
|
3608
|
+
attributes << %w[view pageLayout]
|
3609
|
+
when 2
|
3610
|
+
attributes << %w[view pageBreakPreview]
|
3611
|
+
end
|
3564
3612
|
|
3565
3613
|
# Set the first visible cell.
|
3566
3614
|
attributes << ['topLeftCell', @top_left_cell] if ptrue?(@top_left_cell)
|
3567
3615
|
|
3568
3616
|
# Set the zoom level.
|
3569
3617
|
if @zoom != 100
|
3570
|
-
attributes << ['zoomScale', @zoom]
|
3571
|
-
|
3618
|
+
attributes << ['zoomScale', @zoom]
|
3619
|
+
|
3620
|
+
if @page_view == 1
|
3621
|
+
attributes << ['zoomScalePageLayoutView', @zoom]
|
3622
|
+
elsif @page_view == 2
|
3623
|
+
attributes << ['zoomScaleSheetLayoutView', @zoom]
|
3624
|
+
elsif ptrue?(@zoom_scale_normal)
|
3625
|
+
attributes << ['zoomScaleNormal', @zoom]
|
3626
|
+
end
|
3572
3627
|
end
|
3573
3628
|
|
3574
3629
|
attributes << ['workbookViewId', 0]
|
@@ -3754,9 +3809,10 @@ EOS
|
|
3754
3809
|
|
3755
3810
|
def write_cell_column_dimension(row_num) # :nodoc:
|
3756
3811
|
row = @cell_data_table[row_num]
|
3812
|
+
row_name = (row_num + 1).to_s
|
3757
3813
|
(@dim_colmin..@dim_colmax).each do |col_num|
|
3758
3814
|
if (cell = row[col_num])
|
3759
|
-
cell.write_cell(self, row_num, col_num)
|
3815
|
+
cell.write_cell(self, row_num, row_name, col_num)
|
3760
3816
|
end
|
3761
3817
|
end
|
3762
3818
|
end
|
@@ -4755,10 +4811,6 @@ EOS
|
|
4755
4811
|
ptrue?(@zoom_scale_normal)
|
4756
4812
|
end
|
4757
4813
|
|
4758
|
-
def page_view? # :nodoc:
|
4759
|
-
!!@page_view
|
4760
|
-
end
|
4761
|
-
|
4762
4814
|
def right_to_left? # :nodoc:
|
4763
4815
|
!!@right_to_left
|
4764
4816
|
end
|
data/write_xlsx.gemspec
CHANGED
@@ -20,9 +20,11 @@ Gem::Specification.new do |gem|
|
|
20
20
|
end
|
21
21
|
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
22
22
|
gem.require_paths = ['lib']
|
23
|
+
gem.add_runtime_dependency 'nkf'
|
23
24
|
gem.add_runtime_dependency 'rubyzip', '>= 1.0.0'
|
24
25
|
gem.add_development_dependency 'byebug'
|
25
26
|
gem.add_development_dependency 'minitest'
|
27
|
+
gem.add_development_dependency 'mutex_m'
|
26
28
|
gem.add_development_dependency 'rake'
|
27
29
|
gem.add_development_dependency 'rubocop'
|
28
30
|
gem.add_development_dependency 'rubocop-minitest'
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: write_xlsx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.11.
|
4
|
+
version: 1.11.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hideo NAKAMURA
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-12-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nkf
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: rubyzip
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,6 +66,20 @@ dependencies:
|
|
52
66
|
- - ">="
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mutex_m
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
84
|
name: rake
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -276,7 +304,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
276
304
|
- !ruby/object:Gem::Version
|
277
305
|
version: '0'
|
278
306
|
requirements: []
|
279
|
-
rubygems_version: 3.
|
307
|
+
rubygems_version: 3.5.3
|
280
308
|
signing_key:
|
281
309
|
specification_version: 4
|
282
310
|
summary: write_xlsx is a gem to create a new file in the Excel 2007+ XLSX format.
|