caxlsx 3.0.0 → 3.1.0
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/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
|
|