axlsx 2.0.1 → 3.0.0.pre
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 +5 -0
- data/README.md +23 -23
- data/Rakefile +9 -11
- data/examples/auto_filter.rb +10 -1
- data/examples/conditional_formatting/example_conditional_formatting.rb +18 -3
- data/examples/example.rb +102 -4
- data/examples/merge_cells.rb +17 -0
- data/examples/no_grid_with_borders.rb +18 -0
- data/examples/pivot_test.rb +63 -0
- data/examples/split.rb +16 -0
- data/lib/axlsx/content_type/abstract_content_type.rb +1 -1
- data/lib/axlsx/content_type/content_type.rb +1 -1
- data/lib/axlsx/doc_props/app.rb +1 -1
- data/lib/axlsx/doc_props/core.rb +5 -5
- data/lib/axlsx/drawing/area_chart.rb +99 -0
- data/lib/axlsx/drawing/area_series.rb +110 -0
- data/lib/axlsx/drawing/axes.rb +1 -1
- data/lib/axlsx/drawing/axis.rb +12 -9
- data/lib/axlsx/drawing/bar_3D_chart.rb +13 -13
- data/lib/axlsx/drawing/bar_chart.rb +143 -0
- data/lib/axlsx/drawing/bar_series.rb +9 -9
- data/lib/axlsx/drawing/bubble_chart.rb +59 -0
- data/lib/axlsx/drawing/bubble_series.rb +63 -0
- data/lib/axlsx/drawing/cat_axis.rb +5 -5
- data/lib/axlsx/drawing/chart.rb +52 -8
- data/lib/axlsx/drawing/d_lbls.rb +3 -3
- data/lib/axlsx/drawing/drawing.rb +6 -1
- data/lib/axlsx/drawing/graphic_frame.rb +3 -3
- data/lib/axlsx/drawing/hyperlink.rb +1 -3
- data/lib/axlsx/drawing/line_3D_chart.rb +2 -2
- data/lib/axlsx/drawing/line_chart.rb +10 -10
- data/lib/axlsx/drawing/line_series.rb +32 -3
- data/lib/axlsx/drawing/marker.rb +1 -1
- data/lib/axlsx/drawing/num_data.rb +4 -4
- data/lib/axlsx/drawing/num_data_source.rb +6 -6
- data/lib/axlsx/drawing/num_val.rb +3 -1
- data/lib/axlsx/drawing/one_cell_anchor.rb +3 -2
- data/lib/axlsx/drawing/pic.rb +25 -19
- data/lib/axlsx/drawing/picture_locking.rb +1 -3
- data/lib/axlsx/drawing/pie_3D_chart.rb +5 -6
- data/lib/axlsx/drawing/pie_series.rb +6 -6
- data/lib/axlsx/drawing/scaling.rb +6 -6
- data/lib/axlsx/drawing/scatter_chart.rb +10 -10
- data/lib/axlsx/drawing/scatter_series.rb +40 -7
- data/lib/axlsx/drawing/ser_axis.rb +2 -2
- data/lib/axlsx/drawing/series.rb +3 -3
- data/lib/axlsx/drawing/series_title.rb +2 -2
- data/lib/axlsx/drawing/str_data.rb +3 -3
- data/lib/axlsx/drawing/str_val.rb +3 -1
- data/lib/axlsx/drawing/title.rb +22 -4
- data/lib/axlsx/drawing/two_cell_anchor.rb +6 -1
- data/lib/axlsx/drawing/val_axis.rb +1 -1
- data/lib/axlsx/drawing/view_3D.rb +2 -2
- data/lib/axlsx/drawing/vml_drawing.rb +1 -1
- data/lib/axlsx/package.rb +34 -32
- data/lib/axlsx/rels/relationship.rb +1 -1
- data/lib/axlsx/rels/relationships.rb +7 -4
- data/lib/axlsx/stylesheet/border_pr.rb +2 -2
- data/lib/axlsx/stylesheet/cell_alignment.rb +1 -3
- data/lib/axlsx/stylesheet/cell_protection.rb +1 -3
- data/lib/axlsx/stylesheet/cell_style.rb +1 -3
- data/lib/axlsx/stylesheet/color.rb +1 -3
- data/lib/axlsx/stylesheet/font.rb +1 -1
- data/lib/axlsx/stylesheet/gradient_stop.rb +1 -1
- data/lib/axlsx/stylesheet/num_fmt.rb +10 -3
- data/lib/axlsx/stylesheet/pattern_fill.rb +1 -1
- data/lib/axlsx/stylesheet/styles.rb +7 -7
- data/lib/axlsx/stylesheet/table_style_element.rb +1 -3
- data/lib/axlsx/util/accessors.rb +6 -6
- data/lib/axlsx/util/constants.rb +107 -99
- data/lib/axlsx/util/mime_type_utils.rb +11 -0
- data/lib/axlsx/util/options_parser.rb +2 -1
- data/lib/axlsx/util/parser.rb +4 -4
- data/lib/axlsx/util/serialized_attributes.rb +16 -6
- data/lib/axlsx/util/simple_typed_list.rb +28 -52
- data/lib/axlsx/util/storage.rb +4 -4
- data/lib/axlsx/util/validators.rb +29 -17
- data/lib/axlsx/version.rb +1 -1
- data/lib/axlsx/workbook/defined_name.rb +11 -12
- data/lib/axlsx/workbook/defined_names.rb +2 -2
- data/lib/axlsx/workbook/shared_strings_table.rb +5 -5
- data/lib/axlsx/workbook/workbook.rb +36 -11
- data/lib/axlsx/workbook/workbook_view.rb +80 -0
- data/lib/axlsx/workbook/workbook_views.rb +22 -0
- data/lib/axlsx/workbook/worksheet/auto_filter/auto_filter.rb +2 -2
- data/lib/axlsx/workbook/worksheet/auto_filter/filters.rb +1 -3
- data/lib/axlsx/workbook/worksheet/break.rb +1 -3
- data/lib/axlsx/workbook/worksheet/cell.rb +136 -74
- data/lib/axlsx/workbook/worksheet/cell_serializer.rb +63 -43
- data/lib/axlsx/workbook/worksheet/cfvo.rb +1 -3
- data/lib/axlsx/workbook/worksheet/cfvos.rb +4 -1
- data/lib/axlsx/workbook/worksheet/col.rb +7 -10
- data/lib/axlsx/workbook/worksheet/col_breaks.rb +2 -2
- data/lib/axlsx/workbook/worksheet/cols.rb +5 -2
- data/lib/axlsx/workbook/worksheet/comment.rb +5 -6
- data/lib/axlsx/workbook/worksheet/comments.rb +9 -12
- data/lib/axlsx/workbook/worksheet/conditional_formatting.rb +1 -1
- data/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb +1 -1
- data/lib/axlsx/workbook/worksheet/data_bar.rb +4 -6
- data/lib/axlsx/workbook/worksheet/data_validation.rb +6 -4
- data/lib/axlsx/workbook/worksheet/dimension.rb +2 -2
- data/lib/axlsx/workbook/worksheet/header_footer.rb +6 -8
- data/lib/axlsx/workbook/worksheet/icon_set.rb +3 -5
- data/lib/axlsx/workbook/worksheet/merged_cells.rb +4 -2
- data/lib/axlsx/workbook/worksheet/outline_pr.rb +33 -0
- data/lib/axlsx/workbook/worksheet/page_margins.rb +1 -3
- data/lib/axlsx/workbook/worksheet/page_set_up_pr.rb +1 -1
- data/lib/axlsx/workbook/worksheet/page_setup.rb +21 -23
- data/lib/axlsx/workbook/worksheet/pane.rb +1 -3
- data/lib/axlsx/workbook/worksheet/pivot_table.rb +44 -28
- data/lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb +4 -4
- data/lib/axlsx/workbook/worksheet/print_options.rb +1 -3
- data/lib/axlsx/workbook/worksheet/protected_range.rb +1 -3
- data/lib/axlsx/workbook/worksheet/protected_ranges.rb +5 -2
- data/lib/axlsx/workbook/worksheet/rich_text.rb +55 -0
- data/lib/axlsx/workbook/worksheet/rich_text_run.rb +250 -0
- data/lib/axlsx/workbook/worksheet/row.rb +40 -51
- data/lib/axlsx/workbook/worksheet/row_breaks.rb +2 -2
- data/lib/axlsx/workbook/worksheet/selection.rb +1 -3
- data/lib/axlsx/workbook/worksheet/sheet_data.rb +3 -1
- data/lib/axlsx/workbook/worksheet/sheet_pr.rb +21 -3
- data/lib/axlsx/workbook/worksheet/sheet_protection.rb +1 -3
- data/lib/axlsx/workbook/worksheet/table.rb +6 -6
- data/lib/axlsx/workbook/worksheet/table_style_info.rb +1 -3
- data/lib/axlsx/workbook/worksheet/tables.rb +4 -1
- data/lib/axlsx/workbook/worksheet/worksheet.rb +64 -78
- data/lib/axlsx/workbook/worksheet/worksheet_drawing.rb +10 -10
- data/lib/axlsx/workbook/worksheet/worksheet_hyperlinks.rb +3 -3
- data/lib/axlsx.rb +34 -15
- data/test/drawing/tc_area_chart.rb +39 -0
- data/test/drawing/tc_area_series.rb +71 -0
- data/test/drawing/tc_axis.rb +27 -0
- data/test/drawing/tc_bar_chart.rb +71 -0
- data/test/drawing/tc_bubble_chart.rb +44 -0
- data/test/drawing/tc_bubble_series.rb +21 -0
- data/test/drawing/tc_chart.rb +23 -10
- data/test/drawing/tc_data_source.rb +6 -0
- data/test/drawing/tc_drawing.rb +2 -2
- data/test/drawing/tc_line_chart.rb +5 -5
- data/test/drawing/tc_line_series.rb +47 -6
- data/test/drawing/tc_pic.rb +11 -15
- data/test/drawing/tc_scatter_series.rb +36 -1
- data/test/drawing/tc_str_val.rb +9 -0
- data/test/drawing/tc_title.rb +5 -0
- data/test/stylesheet/tc_styles.rb +2 -2
- data/test/tc_axlsx.rb +31 -0
- data/test/tc_helper.rb +2 -0
- data/test/tc_package.rb +19 -1
- data/test/util/tc_mime_type_utils.rb +13 -0
- data/test/util/tc_simple_typed_list.rb +2 -3
- data/test/util/tc_validators.rb +34 -10
- data/test/workbook/tc_defined_name.rb +12 -4
- data/test/workbook/tc_shared_strings_table.rb +16 -1
- data/test/workbook/tc_workbook.rb +38 -3
- data/test/workbook/tc_workbook_view.rb +50 -0
- data/test/workbook/worksheet/auto_filter/tc_filters.rb +1 -1
- data/test/workbook/worksheet/tc_break.rb +1 -1
- data/test/workbook/worksheet/tc_cell.rb +76 -8
- data/test/workbook/worksheet/tc_col.rb +2 -2
- data/test/workbook/worksheet/tc_conditional_formatting.rb +2 -2
- data/test/workbook/worksheet/tc_data_bar.rb +1 -1
- data/test/workbook/worksheet/tc_data_validation.rb +11 -11
- data/test/workbook/worksheet/tc_header_footer.rb +2 -2
- data/test/workbook/worksheet/tc_icon_set.rb +1 -1
- data/test/workbook/worksheet/tc_outline_pr.rb +19 -0
- data/test/workbook/worksheet/tc_page_setup.rb +3 -3
- data/test/workbook/worksheet/tc_pivot_table.rb +21 -6
- data/test/workbook/worksheet/tc_print_options.rb +1 -1
- data/test/workbook/worksheet/tc_rich_text.rb +44 -0
- data/test/workbook/worksheet/tc_rich_text_run.rb +172 -0
- data/test/workbook/worksheet/tc_row.rb +7 -2
- data/test/workbook/worksheet/tc_sheet_calc_pr.rb +1 -1
- data/test/workbook/worksheet/tc_sheet_format_pr.rb +4 -4
- data/test/workbook/worksheet/tc_sheet_pr.rb +26 -4
- data/test/workbook/worksheet/tc_sheet_protection.rb +5 -5
- data/test/workbook/worksheet/tc_sheet_view.rb +4 -4
- data/test/workbook/worksheet/tc_table.rb +2 -3
- data/test/workbook/worksheet/tc_worksheet.rb +99 -45
- metadata +142 -64
@@ -4,14 +4,10 @@ module Axlsx
|
|
4
4
|
# The Worksheet class represents a worksheet in the workbook.
|
5
5
|
class Worksheet
|
6
6
|
include Axlsx::OptionsParser
|
7
|
-
|
7
|
+
include Axlsx::SerializedAttributes
|
8
8
|
# definition of characters which are less than the maximum width of 0-9 in the default font for use in String#count.
|
9
9
|
# This is used for autowidth calculations
|
10
|
-
|
11
|
-
def self.thin_chars
|
12
|
-
# removed 'e' and 'y' from this list - as a GUESS
|
13
|
-
@thin_chars ||= "^.acfijklrstxzFIJL()-"
|
14
|
-
end
|
10
|
+
THIN_CHARS = '^.acfijklrstxzFIJL()-'.freeze
|
15
11
|
|
16
12
|
# Creates a new worksheet.
|
17
13
|
# @note the recommended way to manage worksheets is Workbook#add_worksheet
|
@@ -24,12 +20,15 @@ module Axlsx
|
|
24
20
|
def initialize(wb, options={})
|
25
21
|
self.workbook = wb
|
26
22
|
@sheet_protection = nil
|
27
|
-
|
28
23
|
initialize_page_options(options)
|
29
24
|
parse_options options
|
30
25
|
@workbook.worksheets << self
|
26
|
+
@sheet_id = index + 1
|
27
|
+
yield self if block_given?
|
31
28
|
end
|
32
29
|
|
30
|
+
serializable_attributes :sheet_id, :state
|
31
|
+
|
33
32
|
# Initalizes page margin, setup and print options
|
34
33
|
# @param [Hash] options Options passed in from the initializer
|
35
34
|
def initialize_page_options(options)
|
@@ -44,7 +43,23 @@ module Axlsx
|
|
44
43
|
# The name of the worksheet
|
45
44
|
# @return [String]
|
46
45
|
def name
|
47
|
-
@name ||=
|
46
|
+
@name ||= "Sheet" + (index+1).to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
# Specifies the visible state of this sheet. Allowed states are
|
50
|
+
# :visible, :hidden or :very_hidden. The default value is :visible.
|
51
|
+
#
|
52
|
+
# Worksheets in the :hidden state can be shown using the sheet formatting properties in excel.
|
53
|
+
# :very_hidden sheets should be inaccessible to end users.
|
54
|
+
# @param [Symbol] sheet_state The visible state for this sheet.
|
55
|
+
def state=(sheet_state)
|
56
|
+
RestrictionValidator.validate :worksheet_state, [:visible, :hidden, :very_hidden], sheet_state
|
57
|
+
@state = sheet_state
|
58
|
+
end
|
59
|
+
|
60
|
+
# The visibility of this sheet
|
61
|
+
def state
|
62
|
+
@state ||= :visible
|
48
63
|
end
|
49
64
|
|
50
65
|
# The sheet calculation properties
|
@@ -76,7 +91,7 @@ module Axlsx
|
|
76
91
|
# @see [SheetFormatPr]
|
77
92
|
def sheet_format_pr
|
78
93
|
@sheet_format_pr ||= SheetFormatPr.new
|
79
|
-
|
94
|
+
yield @sheet_format_pr if block_given?
|
80
95
|
@sheet_format_pr
|
81
96
|
end
|
82
97
|
|
@@ -129,7 +144,7 @@ module Axlsx
|
|
129
144
|
# @return [SimpleTypedList]
|
130
145
|
# @see Worksheet#add_row
|
131
146
|
def rows
|
132
|
-
@rows ||=
|
147
|
+
@rows ||= SimpleTypedList.new Row
|
133
148
|
end
|
134
149
|
|
135
150
|
# returns the sheet data as columns
|
@@ -137,12 +152,12 @@ module Axlsx
|
|
137
152
|
# cell at a specific index. The block will be called with the row and column
|
138
153
|
# index in the missing cell was found.
|
139
154
|
# @example
|
140
|
-
# cols { |row_index, column_index|
|
155
|
+
# cols { |row_index, column_index| puts "warn - row #{row_index} does not have a cell at #{column_index}" }
|
141
156
|
def cols(&block)
|
142
157
|
@rows.transpose(&block)
|
143
158
|
end
|
144
159
|
|
145
|
-
#
|
160
|
+
# A range that excel will apply an auto-filter to "A1:B3"
|
146
161
|
# This will turn filtering on for the cells in the range.
|
147
162
|
# The first row is considered the header, while subsequent rows are considered to be data.
|
148
163
|
# @return String
|
@@ -249,7 +264,7 @@ module Axlsx
|
|
249
264
|
@header_footer
|
250
265
|
end
|
251
266
|
|
252
|
-
#
|
267
|
+
# convenience method to access all cells in this worksheet
|
253
268
|
# @return [Array] cells
|
254
269
|
def cells
|
255
270
|
rows.flatten
|
@@ -290,50 +305,6 @@ module Axlsx
|
|
290
305
|
@sheet_pr ||= SheetPr.new self
|
291
306
|
end
|
292
307
|
|
293
|
-
# Indicates if gridlines should be shown in the sheet.
|
294
|
-
# This is true by default.
|
295
|
-
# @return [Boolean]
|
296
|
-
# @deprecated Use SheetView#show_grid_lines= instead.
|
297
|
-
def show_gridlines=(v)
|
298
|
-
warn('axlsx::DEPRECIATED: Worksheet#show_gridlines= has been depreciated. This value can be set over SheetView#show_grid_lines=.')
|
299
|
-
Axlsx::validate_boolean v
|
300
|
-
sheet_view.show_grid_lines = v
|
301
|
-
end
|
302
|
-
|
303
|
-
# @see selected
|
304
|
-
# @return [Boolean]
|
305
|
-
# @deprecated Use SheetView#tab_selected= instead.
|
306
|
-
def selected=(v)
|
307
|
-
warn('axlsx::DEPRECIATED: Worksheet#selected= has been depreciated. This value can be set over SheetView#tab_selected=.')
|
308
|
-
Axlsx::validate_boolean v
|
309
|
-
sheet_view.tab_selected = v
|
310
|
-
end
|
311
|
-
|
312
|
-
# Indicates if the worksheet should show gridlines or not
|
313
|
-
# @return Boolean
|
314
|
-
# @deprecated Use SheetView#show_grid_lines instead.
|
315
|
-
def show_gridlines
|
316
|
-
warn('axlsx::DEPRECIATED: Worksheet#show_gridlines has been depreciated. This value can get over SheetView#show_grid_lines.')
|
317
|
-
sheet_view.show_grid_lines
|
318
|
-
end
|
319
|
-
|
320
|
-
# Indicates if the worksheet is selected in the workbook
|
321
|
-
# It is possible to have more than one worksheet selected, however it might cause issues
|
322
|
-
# in some older versions of excel when using copy and paste.
|
323
|
-
# @return Boolean
|
324
|
-
# @deprecated Use SheetView#tab_selected instead.
|
325
|
-
def selected
|
326
|
-
warn('axlsx::DEPRECIATED: Worksheet#selected has been depreciated. This value can get over SheetView#tab_selected.')
|
327
|
-
sheet_view.tab_selected
|
328
|
-
end
|
329
|
-
|
330
|
-
# (see #fit_to_page)
|
331
|
-
# @return [Boolean]
|
332
|
-
def fit_to_page=(v)
|
333
|
-
warn('axlsx::DEPRECIATED: Worksheet#fit_to_page has been depreciated. This value will automatically be set for you when you use PageSetup#fit_to.')
|
334
|
-
fit_to_page?
|
335
|
-
end
|
336
|
-
|
337
308
|
# The name of the worksheet
|
338
309
|
# The name of a worksheet must be unique in the workbook, and must not exceed 31 characters
|
339
310
|
# @param [String] name
|
@@ -346,7 +317,7 @@ module Axlsx
|
|
346
317
|
# @param [String] v
|
347
318
|
# @see auto_filter
|
348
319
|
def auto_filter=(v)
|
349
|
-
DataTypeValidator.validate
|
320
|
+
DataTypeValidator.validate :worksheet_auto_filter, String, v
|
350
321
|
auto_filter.range = v
|
351
322
|
end
|
352
323
|
|
@@ -427,10 +398,10 @@ module Axlsx
|
|
427
398
|
# @option options [Array] widths each member of the widths array will affect how auto_fit behavies.
|
428
399
|
# @option options [Float] height the row's height (in points)
|
429
400
|
def add_row(values=[], options={})
|
430
|
-
Row.new(self, values, options)
|
431
|
-
update_column_info
|
432
|
-
yield
|
433
|
-
|
401
|
+
row = Row.new(self, values, options)
|
402
|
+
update_column_info row, options.delete(:widths)
|
403
|
+
yield row if block_given?
|
404
|
+
row
|
434
405
|
end
|
435
406
|
|
436
407
|
alias :<< :add_row
|
@@ -522,7 +493,7 @@ module Axlsx
|
|
522
493
|
# @example
|
523
494
|
# ws.add_page_break("A4")
|
524
495
|
def add_page_break(cell)
|
525
|
-
DataTypeValidator.validate
|
496
|
+
DataTypeValidator.validate :worksheet_page_break, [String, Cell], cell
|
526
497
|
column_index, row_index = if cell.is_a?(String)
|
527
498
|
Axlsx.name_to_indices(cell)
|
528
499
|
else
|
@@ -535,12 +506,12 @@ module Axlsx
|
|
535
506
|
end
|
536
507
|
|
537
508
|
# This is a helper method that Lets you specify a fixed width for multiple columns in a worksheet in one go.
|
538
|
-
#
|
509
|
+
# Note that you must call column_widths AFTER adding data, otherwise the width will not be set successfully.
|
539
510
|
# Setting a fixed column width to nil will revert the behaviour back to calculating the width for you on the next call to add_row.
|
540
511
|
# @example This would set the first and third column widhts but leave the second column in autofit state.
|
541
512
|
# ws.column_widths 7.2, nil, 3
|
542
513
|
# @note For updating only a single column it is probably easier to just set the width of the ws.column_info[col_index].width directly
|
543
|
-
# @param [Integer|Float|
|
514
|
+
# @param [Integer|Float|nil] widths
|
544
515
|
def column_widths(*widths)
|
545
516
|
widths.each_with_index do |value, index|
|
546
517
|
next if value == nil
|
@@ -559,7 +530,7 @@ module Axlsx
|
|
559
530
|
# @see README.md for an example
|
560
531
|
def col_style(index, style, options={})
|
561
532
|
offset = options.delete(:row_offset) || 0
|
562
|
-
cells = @rows[(offset..-1)].map { |row| row
|
533
|
+
cells = @rows[(offset..-1)].map { |row| row[index] }.flatten.compact
|
563
534
|
cells.each { |cell| cell.style = style }
|
564
535
|
end
|
565
536
|
|
@@ -577,18 +548,27 @@ module Axlsx
|
|
577
548
|
cells.each { |cell| cell.style = style }
|
578
549
|
end
|
579
550
|
|
551
|
+
# Returns a sheet node serialization for this sheet in the workbook.
|
552
|
+
def to_sheet_node_xml_string(str='')
|
553
|
+
add_autofilter_defined_name_to_workbook
|
554
|
+
str << '<sheet '
|
555
|
+
serialized_attributes str
|
556
|
+
str << ('name="' << name << '" ')
|
557
|
+
str << ('r:id="' << rId << '"></sheet>')
|
558
|
+
end
|
559
|
+
|
580
560
|
# Serializes the worksheet object to an xml string
|
581
561
|
# This intentionally does not use nokogiri for performance reasons
|
582
562
|
# @return [String]
|
583
|
-
def to_xml_string
|
563
|
+
def to_xml_string str=''
|
564
|
+
add_autofilter_defined_name_to_workbook
|
584
565
|
auto_filter.apply if auto_filter.range
|
585
|
-
str
|
566
|
+
str << '<?xml version="1.0" encoding="UTF-8"?>'
|
586
567
|
str << worksheet_node
|
587
568
|
serializable_parts.each do |item|
|
588
569
|
item.to_xml_string(str) if item
|
589
570
|
end
|
590
571
|
str << '</worksheet>'
|
591
|
-
Axlsx::sanitize(str)
|
592
572
|
end
|
593
573
|
|
594
574
|
# The worksheet relationships. This is managed automatically by the worksheet
|
@@ -606,7 +586,7 @@ module Axlsx
|
|
606
586
|
# Returns the cell or cells defined using excel style A1:B3 references.
|
607
587
|
# @param [String|Integer] cell_def the string defining the cell or range of cells, or the rownumber
|
608
588
|
# @return [Cell, Array]
|
609
|
-
def []
|
589
|
+
def [](cell_def)
|
610
590
|
return rows[cell_def] if cell_def.is_a?(Integer)
|
611
591
|
parts = cell_def.split(':').map{ |part| name_to_cell part }
|
612
592
|
if parts.size == 1
|
@@ -622,7 +602,7 @@ module Axlsx
|
|
622
602
|
def name_to_cell(name)
|
623
603
|
col_index, row_index = *Axlsx::name_to_indices(name)
|
624
604
|
r = rows[row_index]
|
625
|
-
r
|
605
|
+
r[col_index] if r
|
626
606
|
end
|
627
607
|
|
628
608
|
# shortcut method to access styles direclty from the worksheet
|
@@ -675,7 +655,7 @@ module Axlsx
|
|
675
655
|
end
|
676
656
|
|
677
657
|
def validate_sheet_name(name)
|
678
|
-
DataTypeValidator.validate
|
658
|
+
DataTypeValidator.validate :worksheet_name, String, name
|
679
659
|
raise ArgumentError, (ERR_SHEET_NAME_TOO_LONG % name) if name.size > 31
|
680
660
|
raise ArgumentError, (ERR_SHEET_NAME_CHARACTER_FORBIDDEN % name) if '[]*/\?:'.chars.any? { |char| name.include? char }
|
681
661
|
name = Axlsx::coder.encode(name)
|
@@ -695,8 +675,8 @@ module Axlsx
|
|
695
675
|
def range(*cell_def)
|
696
676
|
first, last = cell_def
|
697
677
|
cells = []
|
698
|
-
rows[(first.row.
|
699
|
-
r
|
678
|
+
rows[(first.row.row_index..last.row.row_index)].each do |r|
|
679
|
+
r[(first.index..last.index)].each do |c|
|
700
680
|
cells << c
|
701
681
|
end
|
702
682
|
end
|
@@ -734,7 +714,7 @@ module Axlsx
|
|
734
714
|
# Helper method for parsingout the root node for worksheet
|
735
715
|
# @return [String]
|
736
716
|
def worksheet_node
|
737
|
-
|
717
|
+
"<worksheet xmlns=\"#{XML_NS}\" xmlns:r=\"#{XML_NS_R}\" xml:space=\"#{xml_space}\">"
|
738
718
|
end
|
739
719
|
|
740
720
|
def sheet_data
|
@@ -753,11 +733,12 @@ module Axlsx
|
|
753
733
|
|
754
734
|
def workbook=(v) DataTypeValidator.validate "Worksheet.workbook", Workbook, v; @workbook = v; end
|
755
735
|
|
756
|
-
def update_column_info(cells, widths=
|
736
|
+
def update_column_info(cells, widths=nil)
|
757
737
|
cells.each_with_index do |cell, index|
|
738
|
+
width = widths ? widths[index] : nil
|
758
739
|
col = find_or_create_column_info(index)
|
759
|
-
next if
|
760
|
-
col.update_width(cell,
|
740
|
+
next if width == :ignore
|
741
|
+
col.update_width(cell, width, workbook.use_autowidth)
|
761
742
|
end
|
762
743
|
end
|
763
744
|
|
@@ -765,5 +746,10 @@ module Axlsx
|
|
765
746
|
column_info[index] ||= Col.new(index + 1, index + 1)
|
766
747
|
end
|
767
748
|
|
749
|
+
def add_autofilter_defined_name_to_workbook
|
750
|
+
return if !auto_filter.range
|
751
|
+
workbook.add_defined_name auto_filter.defined_name, name: '_xlnm._FilterDatabase', local_sheet_id: index, hidden: 1
|
752
|
+
end
|
753
|
+
|
768
754
|
end
|
769
755
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Axlsx
|
2
|
-
|
2
|
+
|
3
3
|
# This is a utility class for serialing the drawing node in a
|
4
4
|
# worksheet. Drawing objects have their own serialization that exports
|
5
5
|
# a drawing document. This is only for the single node in the
|
@@ -17,7 +17,7 @@ module Axlsx
|
|
17
17
|
attr_reader :worksheet
|
18
18
|
|
19
19
|
attr_reader :drawing
|
20
|
-
|
20
|
+
|
21
21
|
# adds a chart to the drawing object
|
22
22
|
# @param [Class] chart_type The type of chart to add
|
23
23
|
# @param [Hash] options Options to pass on to the drawing and chart
|
@@ -26,17 +26,17 @@ module Axlsx
|
|
26
26
|
@drawing ||= Drawing.new worksheet
|
27
27
|
drawing.add_chart(chart_type, options)
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
# adds an image to the drawing object
|
31
|
-
# @param [Hash] options Options to pass on to the drawing and image
|
31
|
+
# @param [Hash] options Options to pass on to the drawing and image
|
32
32
|
# @see Worksheet#add_image
|
33
33
|
def add_image(options)
|
34
|
-
@drawing ||= Drawing.new
|
34
|
+
@drawing ||= Drawing.new(worksheet)
|
35
35
|
drawing.add_image(options)
|
36
|
-
end
|
37
|
-
|
36
|
+
end
|
37
|
+
|
38
38
|
# helper method to tell us if the drawing has something in it or not
|
39
|
-
# @return [Boolean]
|
39
|
+
# @return [Boolean]
|
40
40
|
def has_drawing?
|
41
41
|
@drawing.is_a? Drawing
|
42
42
|
end
|
@@ -45,13 +45,13 @@ module Axlsx
|
|
45
45
|
# @return [Relationship]
|
46
46
|
def relationship
|
47
47
|
return unless has_drawing?
|
48
|
-
Relationship.new(self, DRAWING_R, "../#{drawing.pn}")
|
48
|
+
Relationship.new(self, DRAWING_R, "../#{drawing.pn}")
|
49
49
|
end
|
50
50
|
|
51
51
|
# Serialize the drawing for the worksheet
|
52
52
|
# @param [String] str
|
53
53
|
def to_xml_string(str = '')
|
54
|
-
return unless has_drawing?
|
54
|
+
return unless has_drawing?
|
55
55
|
str << "<drawing r:id='#{relationship.Id}'/>"
|
56
56
|
end
|
57
57
|
end
|
@@ -15,8 +15,8 @@ module Axlsx
|
|
15
15
|
# @see WorksheetHyperlink#initialize
|
16
16
|
# @return [WorksheetHyperlink]
|
17
17
|
def add(options)
|
18
|
-
|
19
|
-
|
18
|
+
self << WorksheetHyperlink.new(@worksheet, options)
|
19
|
+
last
|
20
20
|
end
|
21
21
|
|
22
22
|
# The relationships required by this collection's hyperlinks
|
@@ -31,7 +31,7 @@ module Axlsx
|
|
31
31
|
def to_xml_string(str='')
|
32
32
|
return if empty?
|
33
33
|
str << '<hyperlinks>'
|
34
|
-
|
34
|
+
each { |hyperlink| hyperlink.to_xml_string(str) }
|
35
35
|
str << '</hyperlinks>'
|
36
36
|
end
|
37
37
|
end
|
data/lib/axlsx.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
require 'htmlentities'
|
3
3
|
require 'axlsx/version.rb'
|
4
|
+
require 'mimemagic'
|
4
5
|
|
5
6
|
require 'axlsx/util/simple_typed_list.rb'
|
6
7
|
require 'axlsx/util/constants.rb'
|
@@ -10,6 +11,7 @@ require 'axlsx/util/serialized_attributes'
|
|
10
11
|
require 'axlsx/util/options_parser'
|
11
12
|
# to be included with parsable intitites.
|
12
13
|
#require 'axlsx/util/parser.rb'
|
14
|
+
require 'axlsx/util/mime_type_utils'
|
13
15
|
|
14
16
|
require 'axlsx/stylesheet/styles.rb'
|
15
17
|
|
@@ -53,7 +55,7 @@ module Axlsx
|
|
53
55
|
cells = sort_cells(cells)
|
54
56
|
reference = "#{cells.first.reference(absolute)}:#{cells.last.reference(absolute)}"
|
55
57
|
if absolute
|
56
|
-
escaped_name = cells.first.row.worksheet.name.gsub
|
58
|
+
escaped_name = cells.first.row.worksheet.name.gsub ''', "''"
|
57
59
|
"'#{escaped_name}'!#{reference}"
|
58
60
|
else
|
59
61
|
reference
|
@@ -65,7 +67,7 @@ module Axlsx
|
|
65
67
|
# @param [Array] cells
|
66
68
|
# @return [Array]
|
67
69
|
def self.sort_cells(cells)
|
68
|
-
cells.sort { |x, y| [x.index, x.row.
|
70
|
+
cells.sort { |x, y| [x.index, x.row.row_index] <=> [y.index, y.row.row_index] }
|
69
71
|
end
|
70
72
|
|
71
73
|
#global reference html entity encoding
|
@@ -88,20 +90,21 @@ module Axlsx
|
|
88
90
|
# @note This follows the standard spreadsheet convention of naming columns A to Z, followed by AA to AZ etc.
|
89
91
|
# @return [String]
|
90
92
|
def self.col_ref(index)
|
91
|
-
chars =
|
93
|
+
chars = ''
|
92
94
|
while index >= 26 do
|
93
|
-
|
94
|
-
|
95
|
+
index, char = index.divmod(26)
|
96
|
+
chars.prepend((char + 65).chr)
|
97
|
+
index -= 1
|
95
98
|
end
|
96
|
-
chars
|
97
|
-
chars
|
99
|
+
chars.prepend((index + 65).chr)
|
100
|
+
chars
|
98
101
|
end
|
99
102
|
|
100
103
|
# @return [String] The alpha(column)numeric(row) reference for this sell.
|
101
104
|
# @example Relative Cell Reference
|
102
105
|
# ws.rows.first.cells.first.r #=> "A1"
|
103
106
|
def self.cell_r(c_index, r_index)
|
104
|
-
|
107
|
+
col_ref(c_index) << (r_index+1).to_s
|
105
108
|
end
|
106
109
|
|
107
110
|
# Creates an array of individual cell references based on an excel reference range.
|
@@ -113,7 +116,7 @@ module Axlsx
|
|
113
116
|
end_col, end_row = name_to_indices($2)
|
114
117
|
(start_row..end_row).to_a.map do |row_num|
|
115
118
|
(start_col..end_col).to_a.map do |col_num|
|
116
|
-
|
119
|
+
cell_r(col_num, row_num)
|
117
120
|
end
|
118
121
|
end
|
119
122
|
end
|
@@ -127,14 +130,30 @@ module Axlsx
|
|
127
130
|
s.gsub(/_(.)/){ $1.upcase }
|
128
131
|
end
|
129
132
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
133
|
+
# returns the provided string with all invalid control charaters
|
134
|
+
# removed.
|
135
|
+
# @param [String] str The string to process
|
136
|
+
# @return [String]
|
137
|
+
def self.sanitize(str)
|
138
|
+
if str.frozen?
|
139
|
+
str.delete(CONTROL_CHARS)
|
140
|
+
else
|
141
|
+
str.delete!(CONTROL_CHARS)
|
142
|
+
str
|
136
143
|
end
|
144
|
+
end
|
137
145
|
|
146
|
+
# If value is boolean return 1 or 0
|
147
|
+
# else return the value
|
148
|
+
# @param [Object] value The value to process
|
149
|
+
# @return [Object]
|
150
|
+
def self.booleanize(value)
|
151
|
+
if value == true || value == false
|
152
|
+
value ? 1 : 0
|
153
|
+
else
|
154
|
+
value
|
155
|
+
end
|
156
|
+
end
|
138
157
|
|
139
158
|
# Instructs the serializer to not try to escape cell value input.
|
140
159
|
# This will give you a huge speed bonus, but if you content has <, > or other xml character data
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'tc_helper.rb'
|
2
|
+
|
3
|
+
class TestAreaChart < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@p = Axlsx::Package.new
|
7
|
+
ws = @p.workbook.add_worksheet
|
8
|
+
@row = ws.add_row ["one", 1, Time.now]
|
9
|
+
@chart = ws.add_chart Axlsx::AreaChart, :title => "fishery"
|
10
|
+
end
|
11
|
+
|
12
|
+
def teardown
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_initialization
|
16
|
+
assert_equal(@chart.grouping, :standard, "grouping defualt incorrect")
|
17
|
+
assert_equal(@chart.series_type, Axlsx::AreaSeries, "series type incorrect")
|
18
|
+
assert(@chart.cat_axis.is_a?(Axlsx::CatAxis), "category axis not created")
|
19
|
+
assert(@chart.val_axis.is_a?(Axlsx::ValAxis), "value access not created")
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_grouping
|
23
|
+
assert_raise(ArgumentError, "require valid grouping") { @chart.grouping = :inverted }
|
24
|
+
assert_nothing_raised("allow valid grouping") { @chart.grouping = :stacked }
|
25
|
+
assert(@chart.grouping == :stacked)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_to_xml
|
29
|
+
schema = Nokogiri::XML::Schema(File.open(Axlsx::DRAWING_XSD))
|
30
|
+
doc = Nokogiri::XML(@chart.to_xml_string)
|
31
|
+
errors = []
|
32
|
+
schema.validate(doc).each do |error|
|
33
|
+
errors.push error
|
34
|
+
puts error.message
|
35
|
+
end
|
36
|
+
assert(errors.empty?, "error free validation")
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'tc_helper.rb'
|
2
|
+
|
3
|
+
class TestAreaSeries < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
p = Axlsx::Package.new
|
7
|
+
@ws = p.workbook.add_worksheet :name=>"hmmm"
|
8
|
+
chart = @ws.add_chart Axlsx::AreaChart, :title => "fishery"
|
9
|
+
@series = chart.add_series(
|
10
|
+
:data => [0,1,2],
|
11
|
+
:labels => ["zero", "one", "two"],
|
12
|
+
:title => "bob",
|
13
|
+
:color => "#FF0000",
|
14
|
+
:show_marker => true,
|
15
|
+
:smooth => true
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_initialize
|
20
|
+
assert_equal(@series.title.text, "bob", "series title has been applied")
|
21
|
+
assert_equal(@series.labels.class, Axlsx::AxDataSource)
|
22
|
+
assert_equal(@series.data.class, Axlsx::NumDataSource)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_show_marker
|
26
|
+
assert_equal(true, @series.show_marker)
|
27
|
+
@series.show_marker = false
|
28
|
+
assert_equal(false, @series.show_marker)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_smooth
|
32
|
+
assert_equal(true, @series.smooth)
|
33
|
+
@series.smooth = false
|
34
|
+
assert_equal(false, @series.smooth)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_marker_symbol
|
38
|
+
assert_equal(:default, @series.marker_symbol)
|
39
|
+
@series.marker_symbol = :circle
|
40
|
+
assert_equal(:circle, @series.marker_symbol)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_to_xml_string
|
44
|
+
doc = Nokogiri::XML(wrap_with_namespaces(@series))
|
45
|
+
assert(doc.xpath("//srgbClr[@val='#{@series.color}']"))
|
46
|
+
assert_equal(xpath_with_namespaces(doc, "//c:marker").size, 0)
|
47
|
+
assert(doc.xpath("//smooth"))
|
48
|
+
|
49
|
+
@series.marker_symbol = :diamond
|
50
|
+
doc = Nokogiri::XML(wrap_with_namespaces(@series))
|
51
|
+
assert_equal(xpath_with_namespaces(doc, "//c:marker/c:symbol[@val='diamond']").size, 1)
|
52
|
+
|
53
|
+
@series.show_marker = false
|
54
|
+
doc = Nokogiri::XML(wrap_with_namespaces(@series))
|
55
|
+
assert_equal(xpath_with_namespaces(doc, "//c:marker/c:symbol[@val='none']").size, 1)
|
56
|
+
end
|
57
|
+
|
58
|
+
def wrap_with_namespaces(series)
|
59
|
+
'<c:chartSpace xmlns:c="' <<
|
60
|
+
Axlsx::XML_NS_C <<
|
61
|
+
'" xmlns:a="' <<
|
62
|
+
Axlsx::XML_NS_A <<
|
63
|
+
'">' <<
|
64
|
+
series.to_xml_string <<
|
65
|
+
'</c:chartSpace>'
|
66
|
+
end
|
67
|
+
|
68
|
+
def xpath_with_namespaces(doc, xpath)
|
69
|
+
doc.xpath(xpath, "a" => Axlsx::XML_NS_A, "c" => Axlsx::XML_NS_C)
|
70
|
+
end
|
71
|
+
end
|
data/test/drawing/tc_axis.rb
CHANGED
@@ -61,6 +61,33 @@ class TestAxis < Test::Unit::TestCase
|
|
61
61
|
assert_nothing_raised("accepts valid format code") { @axis.format_code = "00.##" }
|
62
62
|
end
|
63
63
|
|
64
|
+
def create_chart_with_formatting(format_string=nil)
|
65
|
+
p = Axlsx::Package.new
|
66
|
+
p.workbook.add_worksheet(:name => "Formatting Test") do |sheet|
|
67
|
+
sheet.add_row(['test', 20])
|
68
|
+
sheet.add_chart(Axlsx::Bar3DChart, :start_at => [0,5], :end_at => [10, 20], :title => "Test Formatting") do |chart|
|
69
|
+
chart.add_series :data => sheet["B1:B1"], :labels => sheet["A1:A1"]
|
70
|
+
chart.val_axis.format_code = format_string if format_string
|
71
|
+
doc = Nokogiri::XML(chart.to_xml_string)
|
72
|
+
yield doc
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_format_code_resets_source_linked
|
78
|
+
create_chart_with_formatting("#,##0.00") do |doc|
|
79
|
+
assert_equal(doc.xpath("//c:valAx/c:numFmt[@formatCode='#,##0.00']").size, 1)
|
80
|
+
assert_equal(doc.xpath("//c:valAx/c:numFmt[@sourceLinked='0']").size, 1)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_no_format_code_keeps_source_linked
|
85
|
+
create_chart_with_formatting do |doc|
|
86
|
+
assert_equal(doc.xpath("//c:valAx/c:numFmt[@formatCode='General']").size, 1)
|
87
|
+
assert_equal(doc.xpath("//c:valAx/c:numFmt[@sourceLinked='1']").size, 1)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
64
91
|
def test_crosses
|
65
92
|
assert_raise(ArgumentError, "requires valid crosses") { @axis.crosses = 1 }
|
66
93
|
assert_nothing_raised("accepts valid crosses") { @axis.crosses = :min }
|