caxlsx 3.0.2 → 3.1.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.
- 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
|
[](https://travis-ci.com/caxlsx/caxlsx)
|
3
|
+
[](http://badge.fury.io/rb/caxlsx)
|
5
|
+

|
6
|
+

|
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
|