caxlsx 3.0.0 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +126 -31
- data/README.md +78 -170
- data/examples/{image1.jpeg → assets/image1.jpeg} +0 -0
- data/examples/generate.rb +15 -0
- data/lib/axlsx.rb +2 -3
- data/lib/axlsx/drawing/bar_chart.rb +3 -3
- data/lib/axlsx/drawing/bar_series.rb +3 -5
- data/lib/axlsx/drawing/d_lbls.rb +1 -1
- data/lib/axlsx/drawing/pie_series.rb +1 -1
- data/lib/axlsx/drawing/series_title.rb +3 -1
- data/lib/axlsx/drawing/title.rb +3 -2
- data/lib/axlsx/package.rb +53 -19
- data/lib/axlsx/rels/relationship.rb +26 -25
- data/lib/axlsx/stylesheet/font.rb +10 -2
- data/lib/axlsx/util/constants.rb +2 -1
- data/lib/axlsx/util/mime_type_utils.rb +1 -1
- data/lib/axlsx/util/validators.rb +2 -2
- data/lib/axlsx/util/zip_command.rb +73 -0
- data/lib/axlsx/version.rb +1 -1
- data/lib/axlsx/workbook/workbook.rb +0 -9
- data/lib/axlsx/workbook/worksheet/cell.rb +32 -5
- data/lib/axlsx/workbook/worksheet/col.rb +8 -4
- 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/rich_text_run.rb +1 -1
- data/lib/axlsx/workbook/worksheet/row.rb +6 -3
- data/lib/axlsx/workbook/worksheet/table.rb +1 -1
- data/lib/axlsx/workbook/worksheet/worksheet.rb +17 -4
- data/lib/caxlsx.rb +2 -0
- 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/drawing/tc_series_title.rb +21 -0
- data/test/drawing/tc_title.rb +16 -0
- 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/rels/tc_relationship.rb +8 -0
- data/test/stylesheet/tc_font.rb +14 -2
- data/test/stylesheet/tc_styles.rb +27 -1
- data/test/tc_axlsx.rb +6 -0
- data/test/tc_helper.rb +0 -2
- data/test/tc_package.rb +82 -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 +68 -2
- data/test/workbook/worksheet/tc_col.rb +16 -1
- 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_rich_text_run.rb +3 -2
- data/test/workbook/worksheet/tc_row.rb +38 -0
- data/test/workbook/worksheet/tc_table.rb +10 -0
- data/test/workbook/worksheet/tc_worksheet.rb +24 -15
- metadata +130 -151
- 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 -59
- 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 -885
- 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
- data/lib/axlsx/util/parser.rb +0 -44
@@ -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
@@ -197,15 +197,6 @@ require 'axlsx/workbook/worksheet/selection.rb'
|
|
197
197
|
@worksheets[index] if index
|
198
198
|
end
|
199
199
|
|
200
|
-
# lets come back to this later when we are ready for parsing.
|
201
|
-
#def self.parse entry
|
202
|
-
# io = entry.get_input_stream
|
203
|
-
# w = self.new
|
204
|
-
# w.parser_xml = Nokogiri::XML(io.read)
|
205
|
-
# w.parse_string :date1904, "//xmlns:workbookPr/@date1904"
|
206
|
-
# w
|
207
|
-
#end
|
208
|
-
|
209
200
|
# Creates a new Workbook
|
210
201
|
# The recomended way to work with workbooks is via Package#workbook
|
211
202
|
# @option options [Boolean] date1904. If this is not specified, date1904 is set to false. Office 2011 for Mac defaults to false.
|
@@ -12,7 +12,7 @@ module Axlsx
|
|
12
12
|
|
13
13
|
# @param [Row] row The row this cell belongs to.
|
14
14
|
# @param [Any] value The value associated with this cell.
|
15
|
-
# @option options [Symbol] type The intended data type for this cell. If not specified the data type will be determined internally based on the
|
15
|
+
# @option options [Symbol] type The intended data type for this cell. If not specified the data type will be determined internally based on the value provided.
|
16
16
|
# @option options [Integer] style The index of the cellXfs item to be applied to this cell. If not specified, the default style (0) will be applied.
|
17
17
|
# @option options [String] font_name
|
18
18
|
# @option options [Integer] charset
|
@@ -30,6 +30,10 @@ module Axlsx
|
|
30
30
|
# @option options [String] color an 8 letter rgb specification
|
31
31
|
# @option options [Number] formula_value The value to cache for a formula cell.
|
32
32
|
# @option options [Symbol] scheme must be one of :none, major, :minor
|
33
|
+
# @option options [Boolean] escape_formulas - Whether to treat a value starting with an equal
|
34
|
+
# sign as formula (default) or as simple string.
|
35
|
+
# Allowing user generated data to be interpreted as formulas can be dangerous
|
36
|
+
# (see https://www.owasp.org/index.php/CSV_Injection for details).
|
33
37
|
def initialize(row, value = nil, options = {})
|
34
38
|
@row = row
|
35
39
|
# Do not use instance vars if not needed to use less RAM
|
@@ -38,6 +42,8 @@ module Axlsx
|
|
38
42
|
type = options.delete(:type) || cell_type_from_value(value)
|
39
43
|
self.type = type unless type == :string
|
40
44
|
|
45
|
+
escape_formulas = options[:escape_formulas]
|
46
|
+
self.escape_formulas = escape_formulas unless escape_formulas.nil?
|
41
47
|
|
42
48
|
val = options.delete(:style)
|
43
49
|
self.style = val unless val.nil? || val == 0
|
@@ -102,6 +108,18 @@ module Axlsx
|
|
102
108
|
self.value = @value unless !defined?(@value) || @value.nil?
|
103
109
|
end
|
104
110
|
|
111
|
+
# Whether to treat a value starting with an equal
|
112
|
+
# sign as formula (default) or as simple string.
|
113
|
+
# Allowing user generated data to be interpreted as formulas can be dangerous
|
114
|
+
# (see https://www.owasp.org/index.php/CSV_Injection for details).
|
115
|
+
# @return [Boolean]
|
116
|
+
attr_reader :escape_formulas
|
117
|
+
|
118
|
+
def escape_formulas=(v)
|
119
|
+
Axlsx.validate_boolean(v)
|
120
|
+
@escape_formulas = v
|
121
|
+
end
|
122
|
+
|
105
123
|
# The value of this cell.
|
106
124
|
# @return [String, Integer, Float, Time, Boolean] casted value based on cell's type attribute.
|
107
125
|
attr_reader :value
|
@@ -324,6 +342,8 @@ module Axlsx
|
|
324
342
|
end
|
325
343
|
|
326
344
|
def is_formula?
|
345
|
+
return false if escape_formulas
|
346
|
+
|
327
347
|
type == :string && @value.to_s.start_with?(?=)
|
328
348
|
end
|
329
349
|
|
@@ -353,6 +373,7 @@ module Axlsx
|
|
353
373
|
# @return [Float]
|
354
374
|
def autowidth
|
355
375
|
return if is_formula? || value.nil?
|
376
|
+
|
356
377
|
if contains_rich_text?
|
357
378
|
string_width('', font_size) + value.autowidth
|
358
379
|
elsif styles.cellXfs[style].alignment && styles.cellXfs[style].alignment.wrap_text
|
@@ -389,7 +410,7 @@ module Axlsx
|
|
389
410
|
# - scaling is not linear as font sizes increase
|
390
411
|
def string_width(string, font_size)
|
391
412
|
font_scale = font_size / 10.0
|
392
|
-
(string.to_s.
|
413
|
+
(string.to_s.size + 3) * font_scale
|
393
414
|
end
|
394
415
|
|
395
416
|
# we scale the font size if bold style is applied to either the style font or
|
@@ -430,9 +451,11 @@ module Axlsx
|
|
430
451
|
:time
|
431
452
|
elsif v.is_a?(TrueClass) || v.is_a?(FalseClass)
|
432
453
|
:boolean
|
433
|
-
elsif v.to_s =~ Axlsx::NUMERIC_REGEX
|
454
|
+
elsif v.to_s =~ Axlsx::NUMERIC_REGEX && v.respond_to?(:to_i)
|
434
455
|
:integer
|
435
|
-
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)
|
436
459
|
:float
|
437
460
|
elsif v.to_s =~ Axlsx::ISO_8601_REGEX
|
438
461
|
:iso_8601
|
@@ -452,7 +475,11 @@ module Axlsx
|
|
452
475
|
case type
|
453
476
|
when :date
|
454
477
|
self.style = STYLE_DATE if self.style == 0
|
455
|
-
v
|
478
|
+
if !v.is_a?(Date) && v.respond_to?(:to_date)
|
479
|
+
v.to_date
|
480
|
+
else
|
481
|
+
v
|
482
|
+
end
|
456
483
|
when :time
|
457
484
|
self.style = STYLE_DATE if self.style == 0
|
458
485
|
if !v.is_a?(Time) && v.respond_to?(:to_time)
|
@@ -4,6 +4,10 @@ module Axlsx
|
|
4
4
|
# The Col class defines column attributes for columns in sheets.
|
5
5
|
class Col
|
6
6
|
|
7
|
+
# Maximum column width limit in MS Excel is 255 characters
|
8
|
+
# https://support.microsoft.com/en-us/office/excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3
|
9
|
+
MAX_WIDTH = 255
|
10
|
+
|
7
11
|
include Axlsx::OptionsParser
|
8
12
|
include Axlsx::SerializedAttributes
|
9
13
|
# Create a new Col objects
|
@@ -111,10 +115,10 @@ module Axlsx
|
|
111
115
|
# TODO!!!
|
112
116
|
#Axlsx.validate_unsigned_numeric(v) unless v == nil
|
113
117
|
@custom_width = @best_fit = v != nil
|
114
|
-
@width = v
|
118
|
+
@width = v.nil? ? v : [v, MAX_WIDTH].min
|
115
119
|
end
|
116
120
|
|
117
|
-
# updates the width for this col based on the cells autowidth and
|
121
|
+
# updates the width for this col based on the cells autowidth and
|
118
122
|
# an optionally specified fixed width
|
119
123
|
# @param [Cell] cell The cell to use in updating this col's width
|
120
124
|
# @param [Integer] fixed_width If this is specified the width is set
|
@@ -127,8 +131,8 @@ module Axlsx
|
|
127
131
|
elsif use_autowidth
|
128
132
|
cell_width = cell.autowidth
|
129
133
|
self.width = cell_width unless (width || 0) > (cell_width || 0)
|
130
|
-
end
|
131
|
-
end
|
134
|
+
end
|
135
|
+
end
|
132
136
|
|
133
137
|
# Serialize this columns data to an xml string
|
134
138
|
# @param [String] str
|
@@ -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>'
|
@@ -215,7 +215,7 @@ module Axlsx
|
|
215
215
|
# - scaling is not linear as font sizes increase
|
216
216
|
def string_width(string, font_size)
|
217
217
|
font_scale = font_size / 10.0
|
218
|
-
string.
|
218
|
+
string.size * font_scale
|
219
219
|
end
|
220
220
|
|
221
221
|
# we scale the font size if bold style is applied to either the style font or
|
@@ -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)
|
@@ -147,13 +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 = options.delete(:types), options.delete(:style), options.delete(:formula_values)
|
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
|
156
|
+
options[:escape_formulas] = escape_formulas.is_a?(Array) ? escape_formulas[index] : escape_formulas if escape_formulas
|
154
157
|
options[:formula_value] = formula_values[index] if formula_values.is_a?(Array)
|
155
158
|
|
156
|
-
self[index] = Cell.new(self, value, options)
|
159
|
+
self[index + offset.to_i] = Cell.new(self, value, options)
|
157
160
|
end
|
158
161
|
end
|
159
162
|
end
|
@@ -80,7 +80,7 @@ module Axlsx
|
|
80
80
|
str << ('<autoFilter ref="' << @ref << '"/>')
|
81
81
|
str << ('<tableColumns count="' << header_cells.length.to_s << '">')
|
82
82
|
header_cells.each_with_index do |cell,index|
|
83
|
-
str << ('<tableColumn id ="' << (index+1).to_s << '" name="' << cell.
|
83
|
+
str << ('<tableColumn id ="' << (index+1).to_s << '" name="' << cell.clean_value << '"/>')
|
84
84
|
end
|
85
85
|
str << '</tableColumns>'
|
86
86
|
table_style_info.to_xml_string(str)
|
@@ -5,9 +5,6 @@ module Axlsx
|
|
5
5
|
class Worksheet
|
6
6
|
include Axlsx::OptionsParser
|
7
7
|
include Axlsx::SerializedAttributes
|
8
|
-
# definition of characters which are less than the maximum width of 0-9 in the default font for use in String#count.
|
9
|
-
# This is used for autowidth calculations
|
10
|
-
THIN_CHARS = '^.acfijklrstxzFIJL()-'.freeze
|
11
8
|
|
12
9
|
# Creates a new worksheet.
|
13
10
|
# @note the recommended way to manage worksheets is Workbook#add_worksheet
|
@@ -390,6 +387,15 @@ module Axlsx
|
|
390
387
|
# @example - use << alias
|
391
388
|
# ws << [3, 4, 5], :types => [nil, :float]
|
392
389
|
#
|
390
|
+
# @example - specify whether a row should escape formulas or not
|
391
|
+
# ws.add_row ['=IF(2+2=4,4,5)', 2, 3], :escape_formulas=>true
|
392
|
+
#
|
393
|
+
# @example - specify whether a certain cells in a row should escape formulas or not
|
394
|
+
# ws.add_row ['=IF(2+2=4,4,5)', '=IF(13+13=4,4,5)'], :escape_formulas=>[true, false]
|
395
|
+
#
|
396
|
+
# @example - add a column offset when adding a row (inserts 'n' blank, unstyled columns before data)
|
397
|
+
# ws.add_row ['I wish', 'for a fish', 'on my fish wish dish'], offset: 3
|
398
|
+
#
|
393
399
|
# @see Worksheet#column_widths
|
394
400
|
# @return [Row]
|
395
401
|
# @option options [Array] values
|
@@ -397,6 +403,11 @@ module Axlsx
|
|
397
403
|
# @option options [Array, Integer] style
|
398
404
|
# @option options [Array] widths each member of the widths array will affect how auto_fit behavies.
|
399
405
|
# @option options [Float] height the row's height (in points)
|
406
|
+
# @option options [Integer] offset - add empty columns before values
|
407
|
+
# @option options [Array, Boolean] escape_formulas - Whether to treat a value starting with an equal
|
408
|
+
# sign as formula (default) or as simple string.
|
409
|
+
# Allowing user generated data to be interpreted as formulas can be dangerous
|
410
|
+
# (see https://www.owasp.org/index.php/CSV_Injection for details).
|
400
411
|
def add_row(values=[], options={})
|
401
412
|
row = Row.new(self, values, options)
|
402
413
|
update_column_info row, options.delete(:widths)
|
@@ -656,7 +667,9 @@ module Axlsx
|
|
656
667
|
|
657
668
|
def validate_sheet_name(name)
|
658
669
|
DataTypeValidator.validate :worksheet_name, String, name
|
659
|
-
|
670
|
+
# ignore first character (BOM) after encoding to utf16 because Excel does so, too.
|
671
|
+
character_length = name.encode("utf-16")[1..-1].encode("utf-16").bytesize / 2
|
672
|
+
raise ArgumentError, (ERR_SHEET_NAME_TOO_LONG % name) if character_length > 31
|
660
673
|
raise ArgumentError, (ERR_SHEET_NAME_CHARACTER_FORBIDDEN % name) if '[]*/\?:'.chars.any? { |char| name.include? char }
|
661
674
|
name = Axlsx::coder.encode(name)
|
662
675
|
sheet_names = @workbook.worksheets.reject { |s| s == self }.map { |s| s.name }
|
data/lib/caxlsx.rb
ADDED
data/test/drawing/tc_drawing.rb
CHANGED
@@ -23,7 +23,7 @@ class TestDrawing < Test::Unit::TestCase
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def test_add_image
|
26
|
-
src = File.dirname(__FILE__) + "
|
26
|
+
src = File.dirname(__FILE__) + "/../fixtures/image1.jpeg"
|
27
27
|
image = @ws.add_image(:image_src => src, :start_at=>[0,0], :width=>600, :height=>400)
|
28
28
|
assert(@ws.drawing.anchors.last.is_a?(Axlsx::OneCellAnchor))
|
29
29
|
assert(image.is_a?(Axlsx::Pic))
|
@@ -31,7 +31,7 @@ class TestDrawing < Test::Unit::TestCase
|
|
31
31
|
assert_equal(400, image.height)
|
32
32
|
end
|
33
33
|
def test_add_two_cell_anchor_image
|
34
|
-
src = File.dirname(__FILE__) + "
|
34
|
+
src = File.dirname(__FILE__) + "/../fixtures/image1.jpeg"
|
35
35
|
image = @ws.add_image(:image_src => src, :start_at=>[0,0], :end_at => [15,0])
|
36
36
|
assert(@ws.drawing.anchors.last.is_a?(Axlsx::TwoCellAnchor))
|
37
37
|
assert(image.is_a?(Axlsx::Pic))
|
@@ -5,7 +5,7 @@ class TestHyperlink < Test::Unit::TestCase
|
|
5
5
|
def setup
|
6
6
|
@p = Axlsx::Package.new
|
7
7
|
ws = @p.workbook.add_worksheet
|
8
|
-
@test_img = File.dirname(__FILE__) + "
|
8
|
+
@test_img = File.dirname(__FILE__) + "/../fixtures/image1.jpeg"
|
9
9
|
@image = ws.add_image :image_src => @test_img, :hyperlink => "http://axlsx.blogspot.com"
|
10
10
|
@hyperlink = @image.hyperlink
|
11
11
|
end
|
@@ -5,7 +5,7 @@ class TestOneCellAnchor < Test::Unit::TestCase
|
|
5
5
|
def setup
|
6
6
|
@p = Axlsx::Package.new
|
7
7
|
@ws = @p.workbook.add_worksheet
|
8
|
-
@test_img = File.dirname(__FILE__) + "
|
8
|
+
@test_img = File.dirname(__FILE__) + "/../fixtures/image1.jpeg"
|
9
9
|
@image = @ws.add_image :image_src => @test_img
|
10
10
|
@anchor = @image.anchor
|
11
11
|
end
|
data/test/drawing/tc_pic.rb
CHANGED
@@ -5,10 +5,10 @@ class TestPic < Test::Unit::TestCase
|
|
5
5
|
def setup
|
6
6
|
@p = Axlsx::Package.new
|
7
7
|
ws = @p.workbook.add_worksheet
|
8
|
-
@test_img = @test_img_jpg = File.dirname(__FILE__) + "
|
9
|
-
@test_img_png = File.dirname(__FILE__) + "
|
10
|
-
@test_img_gif = File.dirname(__FILE__) + "
|
11
|
-
@test_img_fake = File.dirname(__FILE__) + "
|
8
|
+
@test_img = @test_img_jpg = File.dirname(__FILE__) + "/../fixtures/image1.jpeg"
|
9
|
+
@test_img_png = File.dirname(__FILE__) + "/../fixtures/image1.png"
|
10
|
+
@test_img_gif = File.dirname(__FILE__) + "/../fixtures/image1.gif"
|
11
|
+
@test_img_fake = File.dirname(__FILE__) + "/../fixtures/image1_fake.jpg"
|
12
12
|
@image = ws.add_image :image_src => @test_img, :hyperlink => 'https://github.com/randym', :tooltip => "What's up doc?", :opacity => 5
|
13
13
|
end
|
14
14
|
|