caxlsx 3.0.2 → 3.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +31 -8
- data/README.md +4 -0
- data/examples/{image1.jpeg → assets/image1.jpeg} +0 -0
- data/examples/generate.rb +15 -0
- data/lib/axlsx/drawing/bar_3D_chart.rb +5 -8
- data/lib/axlsx/drawing/bar_chart.rb +13 -18
- data/lib/axlsx/drawing/bar_series.rb +18 -1
- data/lib/axlsx/drawing/pie_series.rb +1 -1
- data/lib/axlsx/package.rb +44 -6
- data/lib/axlsx/util/constants.rb +2 -1
- data/lib/axlsx/util/mime_type_utils.rb +1 -1
- data/lib/axlsx/util/validators.rb +1 -1
- data/lib/axlsx/util/zip_command.rb +73 -0
- data/lib/axlsx/version.rb +1 -1
- data/lib/axlsx/workbook/worksheet/cell.rb +9 -3
- data/lib/axlsx/workbook/worksheet/data_validation.rb +4 -4
- data/lib/axlsx/workbook/worksheet/pivot_table.rb +7 -2
- data/lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb +1 -1
- data/lib/axlsx/workbook/worksheet/row.rb +6 -4
- data/lib/axlsx/workbook/worksheet/table.rb +1 -1
- data/lib/axlsx/workbook/worksheet/worksheet.rb +7 -1
- data/lib/axlsx.rb +7 -5
- data/test/drawing/tc_bar_3D_chart.rb +26 -11
- data/test/drawing/tc_bar_chart.rb +26 -11
- data/test/drawing/tc_bar_series.rb +10 -1
- data/test/drawing/tc_drawing.rb +2 -2
- data/test/drawing/tc_hyperlink.rb +1 -1
- data/test/drawing/tc_one_cell_anchor.rb +1 -1
- data/test/drawing/tc_pic.rb +4 -4
- data/test/drawing/tc_pie_series.rb +2 -1
- data/test/fixtures/image1.gif +0 -0
- data/test/fixtures/image1.jpeg +0 -0
- data/test/fixtures/image1.jpg +0 -0
- data/test/fixtures/image1.png +0 -0
- data/test/fixtures/image1_fake.jpg +0 -0
- data/test/tc_helper.rb +0 -2
- data/test/tc_package.rb +80 -13
- data/test/util/tc_mime_type_utils.rb +1 -1
- data/test/util/tc_validators.rb +1 -1
- data/test/workbook/worksheet/tc_cell.rb +38 -0
- data/test/workbook/worksheet/tc_pivot_table.rb +8 -0
- data/test/workbook/worksheet/tc_pivot_table_cache_definition.rb +8 -0
- data/test/workbook/worksheet/tc_row.rb +21 -0
- data/test/workbook/worksheet/tc_table.rb +10 -0
- data/test/workbook/worksheet/tc_worksheet.rb +16 -18
- metadata +116 -137
- data/examples/2010_comments.rb +0 -17
- data/examples/anchor_swapping.rb +0 -28
- data/examples/auto_filter.rb +0 -25
- data/examples/basic_charts.rb +0 -58
- data/examples/chart_colors.rb +0 -88
- data/examples/colored_links.rb +0 -45
- data/examples/conditional_formatting/example_conditional_formatting.rb +0 -89
- data/examples/conditional_formatting/getting_barred.rb +0 -37
- data/examples/conditional_formatting/hitting_the_high_notes.rb +0 -37
- data/examples/conditional_formatting/scaled_colors.rb +0 -39
- data/examples/conditional_formatting/stop_and_go.rb +0 -37
- data/examples/data_validation.rb +0 -67
- data/examples/example.rb +0 -900
- data/examples/extractive.rb +0 -45
- data/examples/ios_preview.rb +0 -14
- data/examples/merge_cells.rb +0 -17
- data/examples/no_grid_with_borders.rb +0 -18
- data/examples/page_setup.rb +0 -11
- data/examples/pivot_table.rb +0 -39
- data/examples/pivot_test.rb +0 -63
- data/examples/sheet_protection.rb +0 -10
- data/examples/skydrive/real_example.rb +0 -63
- data/examples/split.rb +0 -16
- data/examples/styles.rb +0 -66
- data/examples/underline.rb +0 -13
- data/examples/wrap_text.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ef44b04d373e873848cd74d5bebae4d327cd0d9cba06ee74de6d0aac35b51ab9
|
4
|
+
data.tar.gz: b54abdd82f277253216da4ad9d938c2508c9f1ed54abd080d4437e5523c470e1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bce9d28013a2ec3ffec4be335a2b933ba38fb508dbc4f486a3df135dc81122a236ff9439792ac8bc64c4150e523af261deea02c5a86b9f3838a38b2a26e0d537
|
7
|
+
data.tar.gz: 1bca0d82c582818a822feefbb499caef3b622f8d4203543a1c6d11f57876944df01db0bafb30d6238f9a268d731a0a78de0ed4efa97a205db43465d73dab7d74
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,29 @@
|
|
1
1
|
CHANGELOG
|
2
2
|
---------
|
3
3
|
|
4
|
+
- **September.22.21**: 3.1.1
|
5
|
+
- [PR #107](https://github.com/caxlsx/caxlsx/pull/107) - Add overlap to bar charts
|
6
|
+
- [PR #108](https://github.com/caxlsx/caxlsx/pull/108) - Fix gap depth and gap depth validators for bar charts and 3D bar charts
|
7
|
+
|
8
|
+
- **March.27.21**: 3.1.0
|
9
|
+
- [PR #95](https://github.com/caxlsx/caxlsx/pull/95) - Replace mimemagic with marcel
|
10
|
+
- [PR #87](https://github.com/caxlsx/caxlsx/pull/87) - Implement :offset option for worksheet#add_row
|
11
|
+
- [PR #79](https://github.com/caxlsx/caxlsx/pull/79) - Add support for format in pivot tables
|
12
|
+
- [PR #77](https://github.com/caxlsx/caxlsx/pull/77) - Fix special characters in table header
|
13
|
+
- [PR #57](https://github.com/caxlsx/caxlsx/pull/57) - Deprecate using #serialize with boolean argument: Calls like `Package#serialize("name.xlsx", false)` should be replaced with `Package#serialize("name.xlsx", confirm_valid: false)`.
|
14
|
+
|
15
|
+
- **January.5.21**: 3.0.4
|
16
|
+
- [PR #72](https://github.com/caxlsx/caxlsx/pull/72) - Relax Ruby dependency to allow for Ruby 3. This required Travis to be upgraded from Ubuntu Trusty to Ubuntu Bionic. rbx-3 was dropped.
|
17
|
+
- [PR #71](https://github.com/caxlsx/caxlsx/pull/71) - Adds date type to validator so sheet.add_data_validation works with date type. Addresses [I #26](https://github.com/caxlsx/caxlsx/issues/26) - Date Data Validation not working
|
18
|
+
- [PR #70](https://github.com/caxlsx/caxlsx/pull/70) - Fix worksheet title length enforcement caused by switching from size to bytesize. Addresses [I #67](https://github.com/caxlsx/caxlsx/issues/67) - character length error in worksheet name when using Japanese, which was introduced by addressing [I #588](https://github.com/randym/axlsx/issues/588) in the old Axlsx repo.
|
19
|
+
|
20
|
+
|
21
|
+
- **December.7.20**: 3.0.3
|
22
|
+
- [PR #62](https://github.com/caxlsx/caxlsx/pull/62) - Fix edge cases in format detection for objects whose string representation made them look like numbers but the object didn’t respond to `#to_i` or `#to_f`.
|
23
|
+
- [PR #56](https://github.com/caxlsx/caxlsx/pull/56) - Add `zip_command` option to `#serialize` for faster serialization of large Excel files by using a zip binary
|
24
|
+
- [PR #54](https://github.com/caxlsx/caxlsx/pull/54) - Fix type detection for floats with out-of-rage exponents
|
25
|
+
- [I #67](https://github.com/caxlsx/caxlsx/issues/67) - Fix regression in worksheet name length enforcement: Some unicode characters were counted incorrectly, so that names that previously worked fine now stopped working. (This was introduced in 3.0.2)
|
26
|
+
|
4
27
|
- **July.16.20**: 3.0.2
|
5
28
|
- [I #51](https://github.com/caxlsx/caxlsx/issues/51) - Images do not import on Windows. IO read set explicitly to binary mode.
|
6
29
|
- [PR #53](https://github.com/caxlsx/caxlsx/pull/53) - Limit column width to 255. Maximum column width limit in MS Excel is 255 characters, see https://support.microsoft.com/en-us/office/excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3
|
@@ -133,7 +156,7 @@ CHANGELOG
|
|
133
156
|
- added in interop requirements so that charts are properly exported
|
134
157
|
to PDF from Libra Office
|
135
158
|
- various readability improvements and work standardizing attribute
|
136
|
-
names to snake_case. Aliases are provided for backward compatiblity
|
159
|
+
names to snake_case. Aliases are provided for backward compatiblity
|
137
160
|
|
138
161
|
- **June.11.12**: 1.1.7
|
139
162
|
- fix chart rendering issue when label offset is specified as a
|
@@ -174,23 +197,23 @@ in value caches
|
|
174
197
|
- Added support for specifying the color of data series in charts.
|
175
198
|
- bugfix using add_cell on row mismanaged calls to update_column_info.
|
176
199
|
|
177
|
-
- **
|
200
|
+
- **April.25.12:**: 1.1.3
|
178
201
|
- Primarily because I am stupid.....Updates to readme to properly report version, add in missing docs and restructure example directory.
|
179
202
|
|
180
|
-
- **
|
203
|
+
- **April.25.12:**: 1.1.2
|
181
204
|
- Conditional Formatting completely implemented.
|
182
205
|
- refactoring / documentation for Style#add_style
|
183
206
|
- added in label rotation for chart axis labels
|
184
207
|
- bugfix to properly assign style and type info to cells when only partial information is provided in the types/style option
|
185
208
|
|
186
|
-
- **
|
209
|
+
- **April.18.12**: 1.1.1
|
187
210
|
- bugfix for autowidth calculations across multiple rows
|
188
211
|
- bugfix for dimension calculations with nil cells.
|
189
212
|
- REMOVED RMAGICK dependency WOOT!
|
190
213
|
- Update readme to show screenshot of gem output.
|
191
214
|
- Cleanup benchmark and add benchmark rake task
|
192
215
|
|
193
|
-
- **
|
216
|
+
- **April.3.12**: 1.1.0
|
194
217
|
- bugfix patch name_to_indecies to properly handle extended ranges.
|
195
218
|
- bugfix properly serialize chart title.
|
196
219
|
- lower rake minimum requirement for 1.8.7 apps that don't want to move on to 0.9 NOTE this will be reverted for 2.0.0 with workbook parsing!
|
@@ -205,7 +228,7 @@ in value caches
|
|
205
228
|
- Major (like 7x faster!) performance updates.
|
206
229
|
- Gem now supports for JRuby 1.6.7, as well as experimental support for Rubinius
|
207
230
|
|
208
|
-
- **
|
231
|
+
- **March.5.12**: 1.0.18
|
209
232
|
https://github.com/randym/axlsx/compare/1.0.17...1.0.18
|
210
233
|
- bugfix custom borders are not properly applied when using styles.add_style
|
211
234
|
- interop worksheet names must be 31 characters or less or some versions of office complain about repairs
|
@@ -215,14 +238,14 @@ in value caches
|
|
215
238
|
- added << alias for add_row
|
216
239
|
- removed presetting of date1904 based on authoring platform. Now defaults to use 1900 epoch (date1904 = false)
|
217
240
|
|
218
|
-
- **
|
241
|
+
- **February.14.12**: 1.0.17
|
219
242
|
https://github.com/randym/axlsx/compare/1.0.16...1.0.17
|
220
243
|
- Added in support for serializing to StringIO
|
221
244
|
- Added in support for using shared strings table. This makes most of the features in axlsx interoperable with iWorks Numbers
|
222
245
|
- Added in support for fixed column_widths
|
223
246
|
- Removed unneeded dependencies on active-support and i18n
|
224
247
|
|
225
|
-
- **
|
248
|
+
- **February.2.12**: 1.0.16
|
226
249
|
https://github.com/randym/axlsx/compare/1.0.15...1.0.16
|
227
250
|
- Bug fix for schema file locations when validating in rails
|
228
251
|
- Added hyperlink to images
|
data/README.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Caxlsx (Community Continued Version)
|
2
2
|
[![Build Status](https://travis-ci.com/caxlsx/caxlsx.svg?branch=master)](https://travis-ci.com/caxlsx/caxlsx)
|
3
|
+
[![Gem
|
4
|
+
Version](https://badge.fury.io/rb/caxlsx.svg)](http://badge.fury.io/rb/caxlsx)
|
5
|
+
![Total downloads](http://ruby-gem-downloads-badge.herokuapp.com/caxlsx?type=total)
|
6
|
+
![Downloads for 3.1.1 (latest)](http://ruby-gem-downloads-badge.herokuapp.com/caxlsx/3.1.1?label=downloads%203.1.1)
|
3
7
|
|
4
8
|
## Notice: Community Axlsx Organization
|
5
9
|
|
File without changes
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
files = if !ARGV.empty?
|
4
|
+
ARGV.select { |file| File.exist?(file) }
|
5
|
+
else
|
6
|
+
Dir['*_example.md']
|
7
|
+
end
|
8
|
+
|
9
|
+
files.each do |file|
|
10
|
+
puts "Executing #{file.split('.')[0].tr('_', ' ')}"
|
11
|
+
code = File.read(file).match(/```ruby(?<code>.+)```/m)[:code]
|
12
|
+
unless code.nil?
|
13
|
+
eval(['$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"', code].join("\n"))
|
14
|
+
end
|
15
|
+
end
|
@@ -31,17 +31,17 @@ module Axlsx
|
|
31
31
|
alias :barDir :bar_dir
|
32
32
|
|
33
33
|
# space between bar or column clusters, as a percentage of the bar or column width.
|
34
|
-
# @return [
|
34
|
+
# @return [Integer]
|
35
35
|
attr_reader :gap_depth
|
36
36
|
alias :gapDepth :gap_depth
|
37
37
|
|
38
38
|
# space between bar or column clusters, as a percentage of the bar or column width.
|
39
|
-
# @return [
|
39
|
+
# @return [Integer]
|
40
40
|
def gap_width
|
41
41
|
@gap_width ||= 150
|
42
42
|
end
|
43
43
|
alias :gapWidth :gap_width
|
44
|
-
|
44
|
+
|
45
45
|
#grouping for a column, line, or area chart.
|
46
46
|
# must be one of [:percentStacked, :clustered, :standard, :stacked]
|
47
47
|
# @return [Symbol]
|
@@ -56,9 +56,6 @@ module Axlsx
|
|
56
56
|
@shape ||= :box
|
57
57
|
end
|
58
58
|
|
59
|
-
# validation regex for gap amount percent
|
60
|
-
GAP_AMOUNT_PERCENT = /0*(([0-9])|([1-9][0-9])|([1-4][0-9][0-9])|500)%/
|
61
|
-
|
62
59
|
# Creates a new bar chart object
|
63
60
|
# @param [GraphicFrame] frame The workbook that owns this chart.
|
64
61
|
# @option options [Cell, String] title
|
@@ -102,14 +99,14 @@ module Axlsx
|
|
102
99
|
|
103
100
|
# space between bar or column clusters, as a percentage of the bar or column width.
|
104
101
|
def gap_width=(v)
|
105
|
-
|
102
|
+
RangeValidator.validate "Bar3DChart.gap_width", 0, 500, v
|
106
103
|
@gap_width=(v)
|
107
104
|
end
|
108
105
|
alias :gapWidth= :gap_width=
|
109
106
|
|
110
107
|
# space between bar or column clusters, as a percentage of the bar or column width.
|
111
108
|
def gap_depth=(v)
|
112
|
-
|
109
|
+
RangeValidator.validate "Bar3DChart.gap_depth", 0, 500, v
|
113
110
|
@gap_depth=(v)
|
114
111
|
end
|
115
112
|
alias :gapDepth= :gap_depth=
|
@@ -31,12 +31,7 @@ module Axlsx
|
|
31
31
|
alias :barDir :bar_dir
|
32
32
|
|
33
33
|
# space between bar or column clusters, as a percentage of the bar or column width.
|
34
|
-
# @return [
|
35
|
-
attr_reader :gap_depth
|
36
|
-
alias :gapDepth :gap_depth
|
37
|
-
|
38
|
-
# space between bar or column clusters, as a percentage of the bar or column width.
|
39
|
-
# @return [String]
|
34
|
+
# @return [Integer]
|
40
35
|
def gap_width
|
41
36
|
@gap_width ||= 150
|
42
37
|
end
|
@@ -49,6 +44,12 @@ module Axlsx
|
|
49
44
|
@grouping ||= :clustered
|
50
45
|
end
|
51
46
|
|
47
|
+
# Overlap between series
|
48
|
+
# @return [Integer]
|
49
|
+
def overlap
|
50
|
+
@overlap ||= 0
|
51
|
+
end
|
52
|
+
|
52
53
|
# The shape of the bars or columns
|
53
54
|
# must be one of [:cone, :coneToMax, :box, :cylinder, :pyramid, :pyramidToMax]
|
54
55
|
# @return [Symbol]
|
@@ -56,9 +57,6 @@ module Axlsx
|
|
56
57
|
@shape ||= :box
|
57
58
|
end
|
58
59
|
|
59
|
-
# validation regex for gap amount percent
|
60
|
-
GAP_AMOUNT_PERCENT = /0*(([0-9])|([1-9][0-9])|([1-4][0-9][0-9])|500)%/
|
61
|
-
|
62
60
|
# Creates a new bar chart object
|
63
61
|
# @param [GraphicFrame] frame The workbook that owns this chart.
|
64
62
|
# @option options [Cell, String] title
|
@@ -66,12 +64,11 @@ module Axlsx
|
|
66
64
|
# @option options [Symbol] bar_dir
|
67
65
|
# @option options [Symbol] grouping
|
68
66
|
# @option options [String] gap_width
|
69
|
-
# @option options [String] gap_depth
|
70
67
|
# @option options [Symbol] shape
|
71
68
|
# @see Chart
|
72
69
|
def initialize(frame, options={})
|
73
70
|
@vary_colors = true
|
74
|
-
@gap_width, @
|
71
|
+
@gap_width, @overlap, @shape = nil, nil, nil
|
75
72
|
super(frame, options)
|
76
73
|
@series_type = BarSeries
|
77
74
|
@d_lbls = nil
|
@@ -94,17 +91,15 @@ module Axlsx
|
|
94
91
|
|
95
92
|
# space between bar or column clusters, as a percentage of the bar or column width.
|
96
93
|
def gap_width=(v)
|
97
|
-
|
94
|
+
RangeValidator.validate "BarChart.gap_width", 0, 500, v
|
98
95
|
@gap_width=(v)
|
99
96
|
end
|
100
97
|
alias :gapWidth= :gap_width=
|
101
98
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
@gap_depth=(v)
|
99
|
+
def overlap=(v)
|
100
|
+
RangeValidator.validate "BarChart.overlap", -100, 100, v
|
101
|
+
@overlap=(v)
|
106
102
|
end
|
107
|
-
alias :gapDepth= :gap_depth=
|
108
103
|
|
109
104
|
# The shape of the bars or columns
|
110
105
|
# must be one of [:cone, :coneToMax, :box, :cylinder, :pyramid, :pyramidToMax]
|
@@ -124,8 +119,8 @@ module Axlsx
|
|
124
119
|
str << ('<c:varyColors val="' << vary_colors.to_s << '"/>')
|
125
120
|
@series.each { |ser| ser.to_xml_string(str) }
|
126
121
|
@d_lbls.to_xml_string(str) if @d_lbls
|
122
|
+
str << ('<c:overlap val="' << @overlap.to_s << '"/>') unless @overlap.nil?
|
127
123
|
str << ('<c:gapWidth val="' << @gap_width.to_s << '"/>') unless @gap_width.nil?
|
128
|
-
str << ('<c:gapDepth val="' << @gap_depth.to_s << '"/>') unless @gap_depth.nil?
|
129
124
|
str << ('<c:shape val="' << @shape.to_s << '"/>') unless @shape.nil?
|
130
125
|
axes.to_xml_string(str, :ids => true)
|
131
126
|
str << '</c:barChart>'
|
@@ -22,12 +22,18 @@ module Axlsx
|
|
22
22
|
# An array of rgb colors to apply to your bar chart.
|
23
23
|
attr_reader :colors
|
24
24
|
|
25
|
+
# The fill color for this series.
|
26
|
+
# Red, green, and blue is expressed as sequence of hex digits, RRGGBB.
|
27
|
+
# @return [String]
|
28
|
+
attr_reader :series_color
|
29
|
+
|
25
30
|
# Creates a new series
|
26
31
|
# @option options [Array, SimpleTypedList] data
|
27
32
|
# @option options [Array, SimpleTypedList] labels
|
28
33
|
# @option options [String] title
|
29
34
|
# @option options [String] shape
|
30
35
|
# @option options [String] colors an array of colors to use when rendering each data point
|
36
|
+
# @option options [String] series_color a color to use when rendering series
|
31
37
|
# @param [Chart] chart
|
32
38
|
def initialize(chart, options={})
|
33
39
|
@shape = :box
|
@@ -40,6 +46,10 @@ module Axlsx
|
|
40
46
|
# @see colors
|
41
47
|
def colors=(v) DataTypeValidator.validate "BarSeries.colors", [Array], v; @colors = v end
|
42
48
|
|
49
|
+
def series_color=(v)
|
50
|
+
@series_color = v
|
51
|
+
end
|
52
|
+
|
43
53
|
# @see shape
|
44
54
|
def shape=(v)
|
45
55
|
RestrictionValidator.validate "BarSeries.shape", [:cone, :coneToMax, :box, :cylinder, :pyramid, :pyramidToMax], v
|
@@ -60,9 +70,16 @@ module Axlsx
|
|
60
70
|
str << '</a:solidFill></c:spPr></c:dPt>'
|
61
71
|
end
|
62
72
|
|
73
|
+
if series_color
|
74
|
+
str << '<c:spPr><a:solidFill>'
|
75
|
+
str << ('<a:srgbClr val="' << series_color << '"/>')
|
76
|
+
str << '</a:solidFill>'
|
77
|
+
str << '</c:spPr>'
|
78
|
+
end
|
79
|
+
|
63
80
|
@labels.to_xml_string(str) unless @labels.nil?
|
64
81
|
@data.to_xml_string(str) unless @data.nil?
|
65
|
-
# this is actually only required for shapes other than box
|
82
|
+
# this is actually only required for shapes other than box
|
66
83
|
str << ('<c:shape val="' << shape.to_s << '"></c:shape>')
|
67
84
|
end
|
68
85
|
end
|
@@ -47,7 +47,7 @@ module Axlsx
|
|
47
47
|
# @return [String]
|
48
48
|
def to_xml_string(str = '')
|
49
49
|
super(str) do
|
50
|
-
str << '<c:explosion val="' + @explosion + '"/>' unless @explosion.nil?
|
50
|
+
str << '<c:explosion val="' + @explosion.to_s + '"/>' unless @explosion.nil?
|
51
51
|
colors.each_with_index do |c, index|
|
52
52
|
str << '<c:dPt>'
|
53
53
|
str << ('<c:idx val="' << index.to_s << '"/>')
|
data/lib/axlsx/package.rb
CHANGED
@@ -74,10 +74,14 @@ module Axlsx
|
|
74
74
|
# Serialize your workbook to disk as an xlsx document.
|
75
75
|
#
|
76
76
|
# @param [String] output The name of the file you want to serialize your package to
|
77
|
-
# @param [
|
77
|
+
# @param [Hash] options
|
78
|
+
# @option options [Boolean] :confirm_valid Validate the package prior to serialization.
|
79
|
+
# @option options [String] :zip_command When `nil`, `#serialize` with RubyZip to
|
80
|
+
# zip the XLSX file contents. When a String, the provided zip command (e.g.,
|
81
|
+
# "zip") is used to zip the file contents (may be faster for large files)
|
78
82
|
# @return [Boolean] False if confirm_valid and validation errors exist. True if the package was serialized
|
79
83
|
# @note A tremendous amount of effort has gone into ensuring that you cannot create invalid xlsx documents.
|
80
|
-
# confirm_valid should be used in the rare case that you cannot open the serialized file.
|
84
|
+
# options[:confirm_valid] should be used in the rare case that you cannot open the serialized file.
|
81
85
|
# @see Package#validate
|
82
86
|
# @example
|
83
87
|
# # This is how easy it is to create a valid xlsx file. Of course you might want to add a sheet or two, and maybe some data, styles and charts.
|
@@ -88,13 +92,24 @@ module Axlsx
|
|
88
92
|
# # ......add cool stuff to your workbook......
|
89
93
|
# p.serialize("example.xlsx")
|
90
94
|
#
|
95
|
+
# # Serialize to a file, using a system zip binary
|
96
|
+
# p.serialize("example.xlsx", zip_command: "zip", confirm_valid: false)
|
97
|
+
# p.serialize("example.xlsx", zip_command: "/path/to/zip")
|
98
|
+
# p.serialize("example.xlsx", zip_command: "zip -1")
|
99
|
+
#
|
91
100
|
# # Serialize to a stream
|
92
101
|
# s = p.to_stream()
|
93
102
|
# File.open('example_streamed.xlsx', 'w') { |f| f.write(s.read) }
|
94
|
-
def serialize(output,
|
103
|
+
def serialize(output, options = {}, secondary_options = nil)
|
104
|
+
confirm_valid, zip_command = parse_serialize_options(options, secondary_options)
|
95
105
|
return false unless !confirm_valid || self.validate.empty?
|
106
|
+
zip_provider = if zip_command
|
107
|
+
ZipCommand.new(zip_command)
|
108
|
+
else
|
109
|
+
Zip::OutputStream
|
110
|
+
end
|
96
111
|
Relationship.initialize_ids_cache
|
97
|
-
|
112
|
+
zip_provider.open(output) do |zip|
|
98
113
|
write_parts(zip)
|
99
114
|
end
|
100
115
|
true
|
@@ -153,8 +168,8 @@ module Axlsx
|
|
153
168
|
private
|
154
169
|
|
155
170
|
# Writes the package parts to a zip archive.
|
156
|
-
# @param [Zip::OutputStream] zip
|
157
|
-
# @return [Zip::OutputStream]
|
171
|
+
# @param [Zip::OutputStream, ZipCommand] zip
|
172
|
+
# @return [Zip::OutputStream, ZipCommand]
|
158
173
|
def write_parts(zip)
|
159
174
|
p = parts
|
160
175
|
p.each do |part|
|
@@ -346,5 +361,28 @@ module Axlsx
|
|
346
361
|
rels.lock
|
347
362
|
rels
|
348
363
|
end
|
364
|
+
|
365
|
+
# Parse the arguments of `#serialize`
|
366
|
+
# @return [Boolean, (String or nil)] Returns an array where the first value is
|
367
|
+
# `confirm_valid` and the second is the `zip_command`.
|
368
|
+
# @private
|
369
|
+
def parse_serialize_options(options, secondary_options)
|
370
|
+
if secondary_options
|
371
|
+
warn "[DEPRECATION] Axlsx::Package#serialize with 3 arguments is deprecated. " +
|
372
|
+
"Use keyword args instead e.g., package.serialize(output, confirm_valid: false, zip_command: 'zip')"
|
373
|
+
end
|
374
|
+
if options.is_a?(Hash)
|
375
|
+
options.merge!(secondary_options || {})
|
376
|
+
invalid_keys = options.keys - [:confirm_valid, :zip_command]
|
377
|
+
if invalid_keys.any?
|
378
|
+
raise ArgumentError.new("Invalid keyword arguments: #{invalid_keys}")
|
379
|
+
end
|
380
|
+
[options.fetch(:confirm_valid, false), options.fetch(:zip_command, nil)]
|
381
|
+
else
|
382
|
+
warn "[DEPRECATION] Axlsx::Package#serialize with confirm_valid as a boolean is deprecated. " +
|
383
|
+
"Use keyword args instead e.g., package.serialize(output, confirm_valid: false)"
|
384
|
+
parse_serialize_options((secondary_options || {}).merge(confirm_valid: options), nil)
|
385
|
+
end
|
386
|
+
end
|
349
387
|
end
|
350
388
|
end
|
data/lib/axlsx/util/constants.rb
CHANGED
@@ -393,7 +393,8 @@ module Axlsx
|
|
393
393
|
ISO_8601_REGEX = /\A(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[0-1]|0[1-9]|[1-2][0-9])T(2[0-3]|[0-1][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z|[+-](?:2[0-3]|[0-1][0-9]):[0-5][0-9])?\Z/.freeze
|
394
394
|
|
395
395
|
# FLOAT recognition
|
396
|
-
|
396
|
+
SAFE_FLOAT_REGEX = /\A[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]{1,2})?\Z/.freeze
|
397
|
+
MAYBE_FLOAT_REGEX = /\A[-+]?[0-9]*\.?[0-9]+[eE](?<exp>[-+]?[0-9]{3})\Z/.freeze
|
397
398
|
|
398
399
|
# Numeric recognition
|
399
400
|
NUMERIC_REGEX = /\A[+-]?\d+?\Z/.freeze
|
@@ -269,7 +269,7 @@ module Axlsx
|
|
269
269
|
# valid types must be one of custom, data, decimal, list, none, textLength, time, whole
|
270
270
|
# @param [Any] v The value validated
|
271
271
|
def self.validate_data_validation_type(v)
|
272
|
-
RestrictionValidator.validate :data_validation_type, [:custom, :data, :decimal, :list, :none, :textLength, :time, :whole], v
|
272
|
+
RestrictionValidator.validate :data_validation_type, [:custom, :data, :decimal, :list, :none, :textLength, :date, :time, :whole], v
|
273
273
|
end
|
274
274
|
|
275
275
|
# Requires that the value is a valid sheet view type.
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'open3'
|
3
|
+
require 'shellwords'
|
4
|
+
|
5
|
+
module Axlsx
|
6
|
+
|
7
|
+
# The ZipCommand class supports zipping the Excel file contents using
|
8
|
+
# a binary zip program instead of RubyZip's `Zip::OutputStream`.
|
9
|
+
#
|
10
|
+
# The methods provided here mimic `Zip::OutputStream` so that `ZipCommand` can
|
11
|
+
# be used as a drop-in replacement. Note that method signatures are not
|
12
|
+
# identical to `Zip::OutputStream`, they are only sufficiently close so that
|
13
|
+
# `ZipCommand` and `Zip::OutputStream` can be interchangeably used within
|
14
|
+
# `caxlsx`.
|
15
|
+
class ZipCommand
|
16
|
+
# Raised when the zip command exits with a non-zero status.
|
17
|
+
class ZipError < StandardError; end
|
18
|
+
|
19
|
+
def initialize(zip_command)
|
20
|
+
@current_file = nil
|
21
|
+
@files = []
|
22
|
+
@zip_command = zip_command
|
23
|
+
end
|
24
|
+
|
25
|
+
# Create a temporary directory for writing files to.
|
26
|
+
#
|
27
|
+
# The directory and its contents are removed at the end of the block.
|
28
|
+
def open(output, &block)
|
29
|
+
Dir.mktmpdir do |dir|
|
30
|
+
@dir = dir
|
31
|
+
block.call(self)
|
32
|
+
write_file
|
33
|
+
zip_parts(output)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Closes the current entry and opens a new for writing.
|
38
|
+
def put_next_entry(entry)
|
39
|
+
write_file
|
40
|
+
@current_file = "#{@dir}/#{entry.name}"
|
41
|
+
@files << entry.name
|
42
|
+
FileUtils.mkdir_p(File.dirname(@current_file))
|
43
|
+
end
|
44
|
+
|
45
|
+
# Write to a buffer that will be written to the current entry
|
46
|
+
def write(content)
|
47
|
+
@buffer << content
|
48
|
+
end
|
49
|
+
alias << write
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def write_file
|
54
|
+
if @current_file
|
55
|
+
@buffer.rewind
|
56
|
+
File.open(@current_file, "wb") { |f| f.write @buffer.read }
|
57
|
+
end
|
58
|
+
@current_file = nil
|
59
|
+
@buffer = StringIO.new
|
60
|
+
end
|
61
|
+
|
62
|
+
def zip_parts(output)
|
63
|
+
output = Shellwords.shellescape(File.absolute_path(output))
|
64
|
+
inputs = Shellwords.shelljoin(@files)
|
65
|
+
escaped_dir = Shellwords.shellescape(@dir)
|
66
|
+
command = "cd #{escaped_dir} && #{@zip_command} #{output} #{inputs}"
|
67
|
+
stdout_and_stderr, status = Open3.capture2e(command)
|
68
|
+
if !status.success?
|
69
|
+
raise(ZipError.new(stdout_and_stderr))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/axlsx/version.rb
CHANGED
@@ -451,9 +451,11 @@ module Axlsx
|
|
451
451
|
:time
|
452
452
|
elsif v.is_a?(TrueClass) || v.is_a?(FalseClass)
|
453
453
|
:boolean
|
454
|
-
elsif v.to_s =~ Axlsx::NUMERIC_REGEX
|
454
|
+
elsif v.to_s =~ Axlsx::NUMERIC_REGEX && v.respond_to?(:to_i)
|
455
455
|
:integer
|
456
|
-
elsif v.to_s =~ Axlsx::
|
456
|
+
elsif v.to_s =~ Axlsx::SAFE_FLOAT_REGEX && v.respond_to?(:to_f)
|
457
|
+
:float
|
458
|
+
elsif (matchdata = v.to_s.match(MAYBE_FLOAT_REGEX)) && (Float::MIN_10_EXP..Float::MAX_10_EXP).cover?(matchdata[:exp].to_i) && v.respond_to?(:to_f)
|
457
459
|
:float
|
458
460
|
elsif v.to_s =~ Axlsx::ISO_8601_REGEX
|
459
461
|
:iso_8601
|
@@ -473,7 +475,11 @@ module Axlsx
|
|
473
475
|
case type
|
474
476
|
when :date
|
475
477
|
self.style = STYLE_DATE if self.style == 0
|
476
|
-
v
|
478
|
+
if !v.is_a?(Date) && v.respond_to?(:to_date)
|
479
|
+
v.to_date
|
480
|
+
else
|
481
|
+
v
|
482
|
+
end
|
477
483
|
when :time
|
478
484
|
self.style = STYLE_DATE if self.style == 0
|
479
485
|
if !v.is_a?(Time) && v.respond_to?(:to_time)
|
@@ -171,7 +171,7 @@ module Axlsx
|
|
171
171
|
def formula1=(v); Axlsx::validate_string(v); @formula1 = v end
|
172
172
|
|
173
173
|
# @see formula2
|
174
|
-
def formula2=(v); Axlsx::validate_string(v); @formula2 = v end
|
174
|
+
def formula2=(v); Axlsx::validate_string(v); @formula2 = v end
|
175
175
|
|
176
176
|
# @see allowBlank
|
177
177
|
def allowBlank=(v); Axlsx::validate_boolean(v); @allowBlank = v end
|
@@ -216,8 +216,8 @@ module Axlsx
|
|
216
216
|
valid_attributes = get_valid_attributes
|
217
217
|
|
218
218
|
str << '<dataValidation '
|
219
|
-
str << instance_values.map do |key, value|
|
220
|
-
'' << key << '="' << Axlsx.booleanize(value).to_s << '"' if (valid_attributes.include?(key.to_sym) && !CHILD_ELEMENTS.include?(key.to_sym))
|
219
|
+
str << instance_values.map do |key, value|
|
220
|
+
'' << key << '="' << Axlsx.booleanize(value).to_s << '"' if (valid_attributes.include?(key.to_sym) && !CHILD_ELEMENTS.include?(key.to_sym))
|
221
221
|
end.join(' ')
|
222
222
|
str << '>'
|
223
223
|
str << ('<formula1>' << self.formula1 << '</formula1>') if @formula1 and valid_attributes.include?(:formula1)
|
@@ -229,7 +229,7 @@ module Axlsx
|
|
229
229
|
def get_valid_attributes
|
230
230
|
attributes = [:allowBlank, :error, :errorStyle, :errorTitle, :prompt, :promptTitle, :showErrorMessage, :showInputMessage, :sqref, :type ]
|
231
231
|
|
232
|
-
if [:whole, :decimal, :data, :time, :textLength].include?(@type)
|
232
|
+
if [:whole, :decimal, :data, :time, :date, :textLength].include?(@type)
|
233
233
|
attributes << [:operator, :formula1]
|
234
234
|
attributes << [:formula2] if [:between, :notBetween].include?(@operator)
|
235
235
|
elsif @type == :list
|
@@ -111,8 +111,12 @@ module Axlsx
|
|
111
111
|
if data_field.is_a? String
|
112
112
|
data_field = {:ref => data_field}
|
113
113
|
end
|
114
|
-
data_field.
|
115
|
-
|
114
|
+
data_field.each do |key, value|
|
115
|
+
if key == :num_fmt
|
116
|
+
DataTypeValidator.validate "#{self.class}.data[]", [Integer], value
|
117
|
+
else
|
118
|
+
DataTypeValidator.validate "#{self.class}.data[]", [String], value
|
119
|
+
end
|
116
120
|
end
|
117
121
|
@data << data_field
|
118
122
|
end
|
@@ -212,6 +216,7 @@ module Axlsx
|
|
212
216
|
data.each do |datum_value|
|
213
217
|
# The correct name prefix in ["Sum","Average", etc...]
|
214
218
|
str << "<dataField name='#{(datum_value[:subtotal]||'')} of #{datum_value[:ref]}' fld='#{header_index_of(datum_value[:ref])}' baseField='0' baseItem='0'"
|
219
|
+
str << " numFmtId='#{datum_value[:num_fmt]}'" if datum_value[:num_fmt]
|
215
220
|
str << " subtotal='#{datum_value[:subtotal]}' " if datum_value[:subtotal]
|
216
221
|
str << "/>"
|
217
222
|
end
|
@@ -53,7 +53,7 @@ module Axlsx
|
|
53
53
|
str << '</cacheSource>'
|
54
54
|
str << ( '<cacheFields count="' << pivot_table.header_cells_count.to_s << '">')
|
55
55
|
pivot_table.header_cells.each do |cell|
|
56
|
-
str << ( '<cacheField name="' << cell.
|
56
|
+
str << ( '<cacheField name="' << cell.clean_value << '" numFmtId="0">')
|
57
57
|
str << '<sharedItems count="0">'
|
58
58
|
str << '</sharedItems>'
|
59
59
|
str << '</cacheField>'
|
@@ -25,11 +25,12 @@ module Axlsx
|
|
25
25
|
# @option options [Array, Symbol] types
|
26
26
|
# @option options [Array, Integer] style
|
27
27
|
# @option options [Float] height the row's height (in points)
|
28
|
+
# @option options [Integer] offset - add empty columns before values
|
28
29
|
# @see Row#array_to_cells
|
29
30
|
# @see Cell
|
30
31
|
def initialize(worksheet, values=[], options={})
|
31
32
|
self.worksheet = worksheet
|
32
|
-
super(Cell, nil, values.size)
|
33
|
+
super(Cell, nil, values.size + options[:offset].to_i)
|
33
34
|
self.height = options.delete(:height)
|
34
35
|
worksheet.rows << self
|
35
36
|
array_to_cells(values, options)
|
@@ -56,7 +57,7 @@ module Axlsx
|
|
56
57
|
attr_reader :outline_level
|
57
58
|
alias :outlineLevel :outline_level
|
58
59
|
|
59
|
-
# The style applied
|
60
|
+
# The style applied to the row. This affects the entire row.
|
60
61
|
# @return [Integer]
|
61
62
|
attr_reader :s
|
62
63
|
|
@@ -147,14 +148,15 @@ module Axlsx
|
|
147
148
|
# @option options [Array, Integer] style
|
148
149
|
def array_to_cells(values, options={})
|
149
150
|
DataTypeValidator.validate :array_to_cells, Array, values
|
150
|
-
types, style, formula_values, escape_formulas = options.delete(:types), options.delete(:style), options.delete(:formula_values), options.delete(:escape_formulas)
|
151
|
+
types, style, formula_values, escape_formulas, offset = options.delete(:types), options.delete(:style), options.delete(:formula_values), options.delete(:escape_formulas), options.delete(:offset)
|
152
|
+
offset.to_i.times { |index| self[index] = Cell.new(self) } if offset
|
151
153
|
values.each_with_index do |value, index|
|
152
154
|
options[:style] = style.is_a?(Array) ? style[index] : style if style
|
153
155
|
options[:type] = types.is_a?(Array) ? types[index] : types if types
|
154
156
|
options[:escape_formulas] = escape_formulas.is_a?(Array) ? escape_formulas[index] : escape_formulas if escape_formulas
|
155
157
|
options[:formula_value] = formula_values[index] if formula_values.is_a?(Array)
|
156
158
|
|
157
|
-
self[index] = Cell.new(self, value, options)
|
159
|
+
self[index + offset.to_i] = Cell.new(self, value, options)
|
158
160
|
end
|
159
161
|
end
|
160
162
|
end
|