axlsx 1.0.18 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +11 -3
- data/README.md +93 -18
- data/examples/example.csv +1000 -0
- data/examples/example.rb +97 -5
- data/examples/example.xlsx +0 -0
- data/examples/example_streamed.xlsx +0 -0
- data/examples/no-use_autowidth.xlsx +0 -0
- data/examples/shared_strings_example.xlsx +0 -0
- data/lib/axlsx.rb +30 -9
- data/lib/axlsx/content_type/content_type.rb +9 -9
- data/lib/axlsx/content_type/default.rb +9 -6
- data/lib/axlsx/content_type/override.rb +12 -8
- data/lib/axlsx/doc_props/app.rb +37 -40
- data/lib/axlsx/doc_props/core.rb +12 -17
- data/lib/axlsx/drawing/axis.rb +38 -19
- data/lib/axlsx/drawing/bar_3D_chart.rb +33 -32
- data/lib/axlsx/drawing/bar_series.rb +13 -14
- data/lib/axlsx/drawing/cat_axis.rb +15 -14
- data/lib/axlsx/drawing/cat_axis_data.rb +16 -18
- data/lib/axlsx/drawing/chart.rb +37 -38
- data/lib/axlsx/drawing/drawing.rb +15 -12
- data/lib/axlsx/drawing/graphic_frame.rb +21 -21
- data/lib/axlsx/drawing/hyperlink.rb +12 -11
- data/lib/axlsx/drawing/line_3D_chart.rb +30 -28
- data/lib/axlsx/drawing/line_series.rb +11 -11
- data/lib/axlsx/drawing/marker.rb +10 -8
- data/lib/axlsx/drawing/named_axis_data.rb +36 -0
- data/lib/axlsx/drawing/one_cell_anchor.rb +17 -16
- data/lib/axlsx/drawing/pic.rb +24 -37
- data/lib/axlsx/drawing/picture_locking.rb +21 -18
- data/lib/axlsx/drawing/pie_3D_chart.rb +10 -8
- data/lib/axlsx/drawing/pie_series.rb +15 -12
- data/lib/axlsx/drawing/scaling.rb +10 -10
- data/lib/axlsx/drawing/scatter_chart.rb +69 -0
- data/lib/axlsx/drawing/scatter_series.rb +39 -0
- data/lib/axlsx/drawing/ser_axis.rb +10 -10
- data/lib/axlsx/drawing/series.rb +15 -15
- data/lib/axlsx/drawing/series_title.rb +14 -14
- data/lib/axlsx/drawing/title.rb +26 -26
- data/lib/axlsx/drawing/two_cell_anchor.rb +18 -20
- data/lib/axlsx/drawing/val_axis.rb +8 -7
- data/lib/axlsx/drawing/val_axis_data.rb +17 -17
- data/lib/axlsx/drawing/view_3D.rb +22 -20
- data/lib/axlsx/package.rb +32 -15
- data/lib/axlsx/rels/relationship.rb +9 -6
- data/lib/axlsx/rels/relationships.rb +7 -1
- data/lib/axlsx/stylesheet/#num_fmt.rb# +69 -0
- data/lib/axlsx/stylesheet/border.rb +27 -23
- data/lib/axlsx/stylesheet/border_pr.rb +16 -15
- data/lib/axlsx/stylesheet/cell_alignment.rb +23 -21
- data/lib/axlsx/stylesheet/cell_protection.rb +10 -7
- data/lib/axlsx/stylesheet/cell_style.rb +8 -5
- data/lib/axlsx/stylesheet/color.rb +20 -14
- data/lib/axlsx/stylesheet/fill.rb +7 -5
- data/lib/axlsx/stylesheet/font.rb +14 -14
- data/lib/axlsx/stylesheet/gradient_fill.rb +19 -16
- data/lib/axlsx/stylesheet/gradient_stop.rb +9 -5
- data/lib/axlsx/stylesheet/num_fmt.rb +12 -6
- data/lib/axlsx/stylesheet/pattern_fill.rb +25 -10
- data/lib/axlsx/stylesheet/styles.rb +41 -32
- data/lib/axlsx/stylesheet/table_style.rb +9 -4
- data/lib/axlsx/stylesheet/table_style_element.rb +10 -7
- data/lib/axlsx/stylesheet/table_styles.rb +11 -8
- data/lib/axlsx/stylesheet/xf.rb +29 -25
- data/lib/axlsx/util/constants.rb +4 -0
- data/lib/axlsx/util/simple_typed_list.rb +18 -9
- data/lib/axlsx/util/validators.rb +13 -6
- data/lib/axlsx/version.rb +1 -1
- data/lib/axlsx/workbook/shared_strings_table.rb +19 -21
- data/lib/axlsx/workbook/workbook.rb +43 -19
- data/lib/axlsx/workbook/worksheet/cell.rb +93 -91
- data/lib/axlsx/workbook/worksheet/col.rb +114 -0
- data/lib/axlsx/workbook/worksheet/col.rb~ +0 -0
- data/lib/axlsx/workbook/worksheet/page_margins.rb +16 -13
- data/lib/axlsx/workbook/worksheet/row.rb +13 -13
- data/lib/axlsx/workbook/worksheet/table.rb +96 -0
- data/lib/axlsx/workbook/worksheet/table.rb~ +97 -0
- data/lib/axlsx/workbook/worksheet/worksheet.rb +152 -118
- data/lib/schema/dc.xsd +5 -5
- data/lib/schema/dcmitype.xsd +5 -3
- data/lib/schema/dcterms.xsd +15 -15
- data/lib/schema/opc-coreProperties.xsd +6 -2
- data/lib/schema/xml.xsd +7 -8
- data/test/#benchmark.txt# +7 -0
- data/test/#tc_helper.rb# +3 -0
- data/test/benchmark.rb +81 -0
- data/test/benchmark.rb~ +0 -0
- data/test/benchmark.txt +6 -0
- data/test/benchmark.txt~ +6 -0
- data/test/content_type/tc_content_type.rb +30 -32
- data/test/content_type/tc_default.rb +8 -23
- data/test/content_type/tc_override.rb +7 -21
- data/test/doc_props/tc_app.rb +2 -8
- data/test/doc_props/tc_core.rb +6 -7
- data/test/drawing/tc_axis.rb +7 -3
- data/test/drawing/tc_bar_3D_chart.rb +6 -7
- data/test/drawing/tc_bar_series.rb +4 -5
- data/test/drawing/tc_cat_axis.rb +2 -3
- data/test/drawing/tc_cat_axis_data.rb +2 -3
- data/test/drawing/tc_chart.rb +11 -12
- data/test/drawing/tc_drawing.rb +7 -8
- data/test/drawing/tc_graphic_frame.rb +3 -4
- data/test/drawing/tc_hyperlink.rb +2 -3
- data/test/drawing/tc_line_3d_chart.rb +5 -6
- data/test/drawing/tc_line_series.rb +3 -4
- data/test/drawing/tc_marker.rb +3 -4
- data/test/drawing/tc_one_cell_anchor.rb +6 -7
- data/test/drawing/tc_pic.rb +8 -9
- data/test/drawing/tc_picture_locking.rb +2 -3
- data/test/drawing/tc_pie_3D_chart.rb +5 -6
- data/test/drawing/tc_pie_series.rb +4 -5
- data/test/drawing/tc_scaling.rb +3 -4
- data/test/drawing/tc_scatter_chart.rb +43 -0
- data/test/drawing/tc_scatter_series.rb +20 -0
- data/test/drawing/tc_ser_axis.rb +2 -3
- data/test/drawing/tc_series.rb +4 -5
- data/test/drawing/tc_series_title.rb +4 -5
- data/test/drawing/tc_title.rb +4 -5
- data/test/drawing/tc_two_cell_anchor.rb +4 -5
- data/test/drawing/tc_val_axis.rb +2 -3
- data/test/drawing/tc_val_axis_data.rb +2 -3
- data/test/drawing/tc_view_3D.rb +6 -7
- data/test/example.csv +1000 -0
- data/test/example.xlsx +0 -0
- data/test/example_streamed.xlsx +0 -0
- data/test/profile.rb +33 -0
- data/test/rels/tc_relationship.rb +5 -6
- data/test/rels/tc_relationships.rb +4 -5
- data/test/stylesheet/tc_border.rb +3 -4
- data/test/stylesheet/tc_border_pr.rb +3 -4
- data/test/stylesheet/tc_cell_alignment.rb +4 -5
- data/test/stylesheet/tc_cell_protection.rb +2 -3
- data/test/stylesheet/tc_cell_style.rb +2 -3
- data/test/stylesheet/tc_color.rb +2 -3
- data/test/stylesheet/tc_fill.rb +1 -2
- data/test/stylesheet/tc_font.rb +5 -6
- data/test/stylesheet/tc_gradient_fill.rb +1 -2
- data/test/stylesheet/tc_gradient_stop.rb +1 -2
- data/test/stylesheet/tc_num_fmt.rb +1 -2
- data/test/stylesheet/tc_pattern_fill.rb +3 -4
- data/test/stylesheet/tc_styles.rb +15 -9
- data/test/stylesheet/tc_table_style.rb +2 -3
- data/test/stylesheet/tc_table_style_element.rb +2 -3
- data/test/stylesheet/tc_table_styles.rb +3 -4
- data/test/stylesheet/tc_xf.rb +16 -17
- data/test/tc_axlsx.rb +39 -0
- data/test/tc_axlsx.rb~ +0 -0
- data/test/tc_helper.rb +3 -0
- data/test/tc_helper.rb~ +3 -0
- data/test/tc_package.rb +13 -10
- data/test/util/tc_simple_typed_list.rb +8 -9
- data/test/util/tc_validators.rb +7 -8
- data/test/workbook/tc_shared_strings_table.rb +5 -6
- data/test/workbook/tc_workbook.rb +24 -6
- data/test/workbook/worksheet/table/tc_table.rb +71 -0
- data/test/workbook/worksheet/table/tc_table.rb~ +72 -0
- data/test/workbook/worksheet/tc_cell.rb +24 -10
- data/test/workbook/worksheet/tc_col.rb +59 -0
- data/test/workbook/worksheet/tc_col.rb~ +10 -0
- data/test/workbook/worksheet/tc_date_time_converter.rb +1 -2
- data/test/workbook/worksheet/tc_page_margins.rb +6 -9
- data/test/workbook/worksheet/tc_row.rb +26 -12
- data/test/workbook/worksheet/tc_worksheet.rb +134 -68
- metadata +150 -90
- data/test/drawing/tc_hyperlink.rb~ +0 -71
- data/test/workbook/tc_shared_strings_table.rb~ +0 -8
- data/test/workbook/worksheet/tc_date_time_converter.rb~ +0 -69
@@ -0,0 +1,114 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module Axlsx
|
3
|
+
|
4
|
+
# The Col class defines column attributes for columns in sheets.
|
5
|
+
class Col
|
6
|
+
|
7
|
+
# First column affected by this 'column info' record.
|
8
|
+
# @return [Integer]
|
9
|
+
attr_reader :min
|
10
|
+
|
11
|
+
# Last column affected by this 'column info' record.
|
12
|
+
# @return [Integer]
|
13
|
+
attr_reader :max
|
14
|
+
|
15
|
+
# Flag indicating if the specified column(s) is set to 'best fit'. 'Best fit' is set to true under these conditions:
|
16
|
+
# The column width has never been manually set by the user, AND The column width is not the default width
|
17
|
+
# 'Best fit' means that when numbers are typed into a cell contained in a 'best fit' column, the column width should
|
18
|
+
# automatically resize to display the number. [Note: In best fit cases, column width must not be made smaller, only larger. end note]
|
19
|
+
# @return [Boolean]
|
20
|
+
attr_reader :bestFit
|
21
|
+
|
22
|
+
# Flag indicating if the outlining of the affected column(s) is in the collapsed state.
|
23
|
+
# @return [Boolean]
|
24
|
+
attr_reader :collapsed
|
25
|
+
|
26
|
+
# Flag indicating if the affected column(s) are hidden on this worksheet.
|
27
|
+
# @return [Boolean]
|
28
|
+
attr_reader :hidden
|
29
|
+
|
30
|
+
# Outline level of affected column(s). Range is 0 to 7.
|
31
|
+
# @return [Integer]
|
32
|
+
attr_reader :outlineLevel
|
33
|
+
|
34
|
+
# Flag indicating if the phonetic information should be displayed by default for the affected column(s) of the worksheet.
|
35
|
+
# @return [Boolean]
|
36
|
+
attr_reader :phonetic
|
37
|
+
|
38
|
+
# Default style for the affected column(s). Affects cells not yet allocated in the column(s). In other words, this style applies to new columns.
|
39
|
+
# @return [Integer]
|
40
|
+
attr_reader :style
|
41
|
+
|
42
|
+
# The width of the column
|
43
|
+
# @return [Numeric]
|
44
|
+
attr_reader :width
|
45
|
+
|
46
|
+
# @return [Boolean]
|
47
|
+
attr_reader :customWidth
|
48
|
+
|
49
|
+
# @see Col#collapsed
|
50
|
+
def collapsed=(v)
|
51
|
+
Axlsx.validate_boolean(v)
|
52
|
+
@collapsed = v
|
53
|
+
end
|
54
|
+
|
55
|
+
# @see Col#hidden
|
56
|
+
def hidden=(v)
|
57
|
+
Axlsx.validate_boolean(v)
|
58
|
+
@hidden = v
|
59
|
+
end
|
60
|
+
|
61
|
+
# @see Col#outline
|
62
|
+
def outlineLevel=(v)
|
63
|
+
Axlsx.validate_boolean(v)
|
64
|
+
@outlineLevel = v
|
65
|
+
end
|
66
|
+
|
67
|
+
# @see Col#phonetic
|
68
|
+
def phonetic=(v)
|
69
|
+
Axlsx.validate_boolean(v)
|
70
|
+
@phonetic = v
|
71
|
+
end
|
72
|
+
|
73
|
+
# @see Col#style
|
74
|
+
def style=(v)
|
75
|
+
Axlsx.validate_unsigned_int(v)
|
76
|
+
@style = v
|
77
|
+
end
|
78
|
+
|
79
|
+
# @see Col#width
|
80
|
+
def width=(v)
|
81
|
+
Axlsx.validate_unsigned_numeric(v) unless v == nil
|
82
|
+
@customWidth = @bestFit = v != nil
|
83
|
+
@width = v
|
84
|
+
end
|
85
|
+
|
86
|
+
# Create a new Col objects
|
87
|
+
# @param min First column affected by this 'column info' record.
|
88
|
+
# @param max Last column affected by this 'column info' record.
|
89
|
+
# @option options [Boolean] collapsed see Col#collapsed
|
90
|
+
# @option options [Boolean] hidden see Col#hidden
|
91
|
+
# @option options [Boolean] outlineLevel see Col#outlineLevel
|
92
|
+
# @option options [Boolean] phonetic see Col#phonetic
|
93
|
+
# @option options [Integer] style see Col#style
|
94
|
+
# @option options [Numeric] width see Col#width
|
95
|
+
def initialize(min, max, options={})
|
96
|
+
Axlsx.validate_unsigned_int(max)
|
97
|
+
Axlsx.validate_unsigned_int(min)
|
98
|
+
@min = min
|
99
|
+
@max = max
|
100
|
+
options.each do |o|
|
101
|
+
self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Serialize this columns data to an xml string
|
106
|
+
# @param [String] str
|
107
|
+
# @return [String]
|
108
|
+
def to_xml_string(str = '')
|
109
|
+
attrs = self.instance_values.reject{ |key, value| value == nil }
|
110
|
+
str << '<col ' << attrs.map { |key, value| '' << key << '="' << value.to_s << '"' }.join(' ') << '/>'
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
File without changes
|
@@ -12,13 +12,13 @@ module Axlsx
|
|
12
12
|
|
13
13
|
# Default left and right margin (in inches)
|
14
14
|
DEFAULT_LEFT_RIGHT = 0.75
|
15
|
-
|
15
|
+
|
16
16
|
# Default top and bottom margins (in inches)
|
17
17
|
DEFAULT_TOP_BOTTOM = 1.00
|
18
|
-
|
18
|
+
|
19
19
|
# Default header and footer margins (in inches)
|
20
20
|
DEFAULT_HEADER_FOOTER = 0.50
|
21
|
-
|
21
|
+
|
22
22
|
# Left margin (in inches)
|
23
23
|
# @return [Float]
|
24
24
|
attr_reader :left
|
@@ -26,23 +26,23 @@ module Axlsx
|
|
26
26
|
# Right margin (in inches)
|
27
27
|
# @return [Float]
|
28
28
|
attr_reader :right
|
29
|
-
|
29
|
+
|
30
30
|
# Top margin (in inches)
|
31
31
|
# @return [Float]
|
32
32
|
attr_reader :top
|
33
|
-
|
33
|
+
|
34
34
|
# Bottom margin (in inches)
|
35
35
|
# @return [Float]
|
36
36
|
attr_reader :bottom
|
37
|
-
|
37
|
+
|
38
38
|
# Header margin (in inches)
|
39
39
|
# @return [Float]
|
40
40
|
attr_reader :header
|
41
|
-
|
41
|
+
|
42
42
|
# Footer margin (in inches)
|
43
43
|
# @return [Float]
|
44
44
|
attr_reader :footer
|
45
|
-
|
45
|
+
|
46
46
|
# Creates a new PageMargins object
|
47
47
|
# @option options [Numeric] left The left margin in inches
|
48
48
|
# @option options [Numeric] right The right margin in inches
|
@@ -60,7 +60,7 @@ module Axlsx
|
|
60
60
|
self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
|
61
61
|
end
|
62
62
|
end
|
63
|
-
|
63
|
+
|
64
64
|
# Set some or all margins at once.
|
65
65
|
# @param [Hash] margins the margins to set (possible keys are :left, :right, :top, :bottom, :header and :footer).
|
66
66
|
def set(margins)
|
@@ -69,7 +69,7 @@ module Axlsx
|
|
69
69
|
send("#{k}=", v)
|
70
70
|
end
|
71
71
|
end
|
72
|
-
|
72
|
+
|
73
73
|
# @see left
|
74
74
|
def left=(v); Axlsx::validate_unsigned_numeric(v); @left = v end
|
75
75
|
# @see right
|
@@ -84,11 +84,14 @@ module Axlsx
|
|
84
84
|
def footer=(v); Axlsx::validate_unsigned_numeric(v); @footer = v end
|
85
85
|
|
86
86
|
# Serializes the page margins element
|
87
|
+
# @param [String] str
|
88
|
+
# @return [String]
|
87
89
|
# @note For compatibility, this is a noop unless custom margins have been specified.
|
88
|
-
# @param [Nokogiri::XML::Builder] xml The document builder instance this objects xml will be added to.
|
89
90
|
# @see #custom_margins_specified?
|
90
|
-
def
|
91
|
-
|
91
|
+
def to_xml_string(str = '')
|
92
|
+
str << '<pageMargins '
|
93
|
+
str << instance_values.map { |key, value| '' << key << '="' << value.to_s << '"' }.join(' ')
|
94
|
+
str << '/>'
|
92
95
|
end
|
93
96
|
end
|
94
97
|
end
|
@@ -60,19 +60,26 @@ module Axlsx
|
|
60
60
|
end
|
61
61
|
|
62
62
|
# Serializes the row
|
63
|
-
# @param [
|
63
|
+
# @param [Integer] r_index The row index, 0 based.
|
64
|
+
# @param [String] str The string this rows xml will be appended to.
|
64
65
|
# @return [String]
|
65
|
-
def
|
66
|
-
|
67
|
-
|
68
|
-
|
66
|
+
def to_xml_string(r_index, str = '')
|
67
|
+
str << '<row r="' << (r_index + 1 ).to_s << '" '
|
68
|
+
if custom_height?
|
69
|
+
str << 'customHeight="1" ht="' << height.to_s << '">'
|
70
|
+
else
|
71
|
+
str << '>'
|
72
|
+
end
|
73
|
+
@cells.each_with_index { |cell, c_index| cell.to_xml_string(r_index, c_index, str) }
|
74
|
+
str << '</row>'
|
75
|
+
str
|
69
76
|
end
|
70
77
|
|
71
78
|
# Adds a singel sell to the row based on the data provided and updates the worksheet's autofit data.
|
72
79
|
# @return [Cell]
|
73
80
|
def add_cell(value="", options={})
|
74
81
|
c = Cell.new(self, value, options)
|
75
|
-
|
82
|
+
worksheet.send(:update_column_info, self.cells, self.cells.map(&:style))
|
76
83
|
c
|
77
84
|
end
|
78
85
|
|
@@ -106,13 +113,6 @@ module Axlsx
|
|
106
113
|
# assigns the owning worksheet for this row
|
107
114
|
def worksheet=(v) DataTypeValidator.validate "Row.worksheet", Worksheet, v; @worksheet=v; end
|
108
115
|
|
109
|
-
# Tell the worksheet to update autofit data for the columns based on this row's cells.
|
110
|
-
# @return [SimpleTypedList]
|
111
|
-
def update_auto_fit_data
|
112
|
-
worksheet.send(:update_auto_fit_data, self.cells)
|
113
|
-
end
|
114
|
-
|
115
|
-
|
116
116
|
# Converts values, types, and style options into cells and associates them with this row.
|
117
117
|
# A new cell is created for each item in the values array.
|
118
118
|
# If value option is defined and is a symbol it is applied to all the cells created.
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module Axlsx
|
3
|
+
# Table
|
4
|
+
# @note Worksheet#add_table is the recommended way to create tables for your worksheets.
|
5
|
+
# @see README for examples
|
6
|
+
class Table
|
7
|
+
|
8
|
+
|
9
|
+
# The reference to the table data
|
10
|
+
# @return [String]
|
11
|
+
attr_reader :ref
|
12
|
+
|
13
|
+
# The name of the table.
|
14
|
+
# @return [String]
|
15
|
+
attr_reader :name
|
16
|
+
|
17
|
+
# The style for the table.
|
18
|
+
# @return [TableStyle]
|
19
|
+
attr_reader :style
|
20
|
+
|
21
|
+
# Creates a new Table object
|
22
|
+
# @param [String] ref The reference to the table data.
|
23
|
+
# @param [Sheet] ref The sheet containing the table data.
|
24
|
+
# @option options [Cell, String] name
|
25
|
+
# @option options [TableStyle] style
|
26
|
+
def initialize(ref, sheet, options={})
|
27
|
+
@ref = ref
|
28
|
+
@sheet = sheet
|
29
|
+
@style = nil
|
30
|
+
@sheet.workbook.tables << self
|
31
|
+
@name = "Table#{index+1}"
|
32
|
+
options.each do |o|
|
33
|
+
self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
|
34
|
+
end
|
35
|
+
yield self if block_given?
|
36
|
+
end
|
37
|
+
|
38
|
+
# The index of this chart in the workbooks charts collection
|
39
|
+
# @return [Integer]
|
40
|
+
def index
|
41
|
+
@sheet.workbook.tables.index(self)
|
42
|
+
end
|
43
|
+
|
44
|
+
# The part name for this table
|
45
|
+
# @return [String]
|
46
|
+
def pn
|
47
|
+
"#{TABLE_PN % (index+1)}"
|
48
|
+
end
|
49
|
+
|
50
|
+
# The relation reference id for this table
|
51
|
+
# @return [String]
|
52
|
+
def rId
|
53
|
+
"rId#{index+1}"
|
54
|
+
end
|
55
|
+
|
56
|
+
# The name of the Table.
|
57
|
+
# @param [String, Cell] v
|
58
|
+
# @return [Title]
|
59
|
+
def name=(v)
|
60
|
+
DataTypeValidator.validate "#{self.class}.name", [String], v
|
61
|
+
if v.is_a?(String)
|
62
|
+
@name = v
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Serializes the object
|
67
|
+
# @param [String] str
|
68
|
+
# @return [String]
|
69
|
+
def to_xml_string(str = '')
|
70
|
+
str << '<?xml version="1.0" encoding="UTF-8"?>'
|
71
|
+
str << '<table xmlns="' << XML_NS << '" id="' << (index+1).to_s << '" name="' << @name << '" displayName="' << @name.gsub(/\s/,'_') << '" '
|
72
|
+
str << 'ref="' << @ref << '" totalsRowShown="0">'
|
73
|
+
str << '<autoFilter ref="' << @ref << '"/>'
|
74
|
+
str << '<tableColumns count="' << header_cells.length.to_s << '">'
|
75
|
+
header_cells.each_with_index do |cell,index|
|
76
|
+
str << '<tableColumn id ="' << (index+1).to_s << '" name="' << cell.value << '"/>'
|
77
|
+
end
|
78
|
+
str << '</tableColumns>'
|
79
|
+
#TODO implement tableStyleInfo
|
80
|
+
str << '<tableStyleInfo showFirstColumn="0" showLastColumn="0" showRowStripes="1" showColumnStripes="0" name="TableStyleMedium9" />'
|
81
|
+
str << '</table>'
|
82
|
+
end
|
83
|
+
|
84
|
+
# The style for the table.
|
85
|
+
# TODO
|
86
|
+
# def style=(v) DataTypeValidator.validate "Table.style", Integer, v, lambda { |arg| arg >= 1 && arg <= 48 }; @style = v; end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
# get the header cells (hackish)
|
91
|
+
def header_cells
|
92
|
+
header = @ref.gsub(/^(\w+)(\d+)\:(\w+)\d+$/, '\1\2:\3\2')
|
93
|
+
@sheet[header]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module Axlsx
|
3
|
+
# Table
|
4
|
+
# @note Worksheet#add_table is the recommended way to create charts for your worksheets.
|
5
|
+
# @see README for examples
|
6
|
+
class Table
|
7
|
+
|
8
|
+
|
9
|
+
# The reference to the table data
|
10
|
+
# @return [String]
|
11
|
+
attr_reader :ref
|
12
|
+
|
13
|
+
# The name of the table.
|
14
|
+
# @return [String]
|
15
|
+
attr_reader :name
|
16
|
+
|
17
|
+
# The style for the table.
|
18
|
+
# @return [TableStyle]
|
19
|
+
attr_reader :style
|
20
|
+
|
21
|
+
# Creates a new Table object
|
22
|
+
# @param [String] ref The reference to the table data.
|
23
|
+
# @param [Sheet] ref The sheet containing the table data.
|
24
|
+
# @option options [Cell, String] name
|
25
|
+
# @option options [TableStyle] style
|
26
|
+
def initialize(ref, sheet, options={})
|
27
|
+
@ref = ref
|
28
|
+
@sheet = sheet
|
29
|
+
@style = nil
|
30
|
+
@sheet.workbook.tables << self
|
31
|
+
@name = "Table#{index+1}"
|
32
|
+
options.each do |o|
|
33
|
+
self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
|
34
|
+
end
|
35
|
+
yield self if block_given?
|
36
|
+
end
|
37
|
+
|
38
|
+
# The index of this chart in the workbooks charts collection
|
39
|
+
# @return [Integer]
|
40
|
+
def index
|
41
|
+
@sheet.workbook.tables.index(self)
|
42
|
+
end
|
43
|
+
|
44
|
+
# The part name for this table
|
45
|
+
# @return [String]
|
46
|
+
def pn
|
47
|
+
"#{TABLE_PN % (index+1)}"
|
48
|
+
end
|
49
|
+
|
50
|
+
# The relation reference id for this table
|
51
|
+
# @return [String]
|
52
|
+
def rId
|
53
|
+
"rId#{index+1}"
|
54
|
+
end
|
55
|
+
|
56
|
+
# The name of the Table.
|
57
|
+
# @param [String, Cell] v
|
58
|
+
# @return [Title]
|
59
|
+
def name=(v)
|
60
|
+
DataTypeValidator.validate "#{self.class}.name", [String], v
|
61
|
+
if v.is_a?(String)
|
62
|
+
@name = v
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
# The style for the table.
|
68
|
+
# TODO
|
69
|
+
# def style=(v) DataTypeValidator.validate "Chart.style", Integer, v, lambda { |arg| arg >= 1 && arg <= 48 }; @style = v; end
|
70
|
+
|
71
|
+
# Table Serialization
|
72
|
+
# serializes the table
|
73
|
+
def to_xml
|
74
|
+
builder = Nokogiri::XML::Builder.new(:encoding => ENCODING) do |xml|
|
75
|
+
xml.table(:xmlns => XML_NS, :id => index+1, :name => @name, :displayName => @name.gsub(/\s/,'_'), :ref => @ref, :totalsRowShown => 0) {
|
76
|
+
xml.autoFilter :ref=>@ref
|
77
|
+
xml.tableColumns(:count => header_cells.length) {
|
78
|
+
header_cells.each_with_index do |cell,index|
|
79
|
+
xml.tableColumn :id => index+1, :name => cell.value
|
80
|
+
end
|
81
|
+
}
|
82
|
+
xml.tableStyleInfo :showFirstColumn=>"0", :showLastColumn=>"0", :showRowStripes=>"1", :showColumnStripes=>"0", :name=>"TableStyleMedium9"
|
83
|
+
#TODO implement tableStyleInfo
|
84
|
+
}
|
85
|
+
end
|
86
|
+
builder.to_xml(:save_with => 0)
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
# get the header cells (hackish)
|
92
|
+
def header_cells
|
93
|
+
header = @ref.gsub(/^(\w+)(\d+)\:(\w+)\d+$/, '\1\2:\3\2')
|
94
|
+
@sheet[header]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -12,6 +12,9 @@ module Axlsx
|
|
12
12
|
# @return [Workbook]
|
13
13
|
attr_reader :workbook
|
14
14
|
|
15
|
+
# The tables in this worksheet
|
16
|
+
# @return [Array] of Table
|
17
|
+
attr_reader :tables
|
15
18
|
|
16
19
|
# The rows in this worksheet
|
17
20
|
# @note The recommended way to manage rows is Worksheet#add_row
|
@@ -35,6 +38,26 @@ module Axlsx
|
|
35
38
|
# @return Array
|
36
39
|
attr_reader :auto_filter
|
37
40
|
|
41
|
+
# Indicates if the worksheet should show gridlines or not
|
42
|
+
# @return Boolean
|
43
|
+
attr_reader :show_gridlines
|
44
|
+
|
45
|
+
|
46
|
+
# Indicates if the worksheet is selected in the workbook
|
47
|
+
# It is possible to have more than one worksheet selected, however it might cause issues
|
48
|
+
# in some older versions of excel when using copy and paste.
|
49
|
+
# @return Boolean
|
50
|
+
attr_reader :selected
|
51
|
+
|
52
|
+
# Indicates if the worksheet should print in a single page
|
53
|
+
# @return Boolean
|
54
|
+
attr_reader :fit_to_page
|
55
|
+
|
56
|
+
|
57
|
+
# Column info for the sheet
|
58
|
+
# @return [SimpleTypedList]
|
59
|
+
attr_reader :column_info
|
60
|
+
|
38
61
|
# Page margins for printing the worksheet.
|
39
62
|
# @example
|
40
63
|
# wb = Axlsx::Package.new.workbook
|
@@ -55,6 +78,7 @@ module Axlsx
|
|
55
78
|
@page_margins ||= PageMargins.new
|
56
79
|
yield @page_margins if block_given?
|
57
80
|
@page_margins
|
81
|
+
|
58
82
|
end
|
59
83
|
|
60
84
|
# Creates a new worksheet.
|
@@ -62,19 +86,36 @@ module Axlsx
|
|
62
86
|
# @see Workbook#add_worksheet
|
63
87
|
# @option options [String] name The name of this worksheet.
|
64
88
|
# @option options [Hash] page_margins A hash containing page margins for this worksheet. @see PageMargins
|
89
|
+
# @option options [Boolean] show_gridlines indicates if gridlines should be shown for this sheet.
|
65
90
|
def initialize(wb, options={})
|
66
|
-
@drawing = @page_margins = @auto_filter = nil
|
67
|
-
@rows = SimpleTypedList.new Row
|
68
91
|
self.workbook = wb
|
69
92
|
@workbook.worksheets << self
|
70
|
-
@auto_fit_data = []
|
71
|
-
self.name = options[:name] || "Sheet" + (index+1).to_s
|
72
93
|
|
73
|
-
@
|
74
|
-
@cols = SimpleTypedList.new Cell
|
94
|
+
@drawing = @page_margins = @auto_filter = nil
|
75
95
|
@merged_cells = []
|
96
|
+
@auto_fit_data = []
|
76
97
|
|
98
|
+
@selected = false
|
99
|
+
@show_gridlines = true
|
100
|
+
self.name = "Sheet" + (index+1).to_s
|
77
101
|
@page_margins = PageMargins.new options[:page_margins] if options[:page_margins]
|
102
|
+
|
103
|
+
@rows = SimpleTypedList.new Row
|
104
|
+
@column_info = SimpleTypedList.new Col
|
105
|
+
# @cols = SimpleTypedList.new Cell
|
106
|
+
@tables = SimpleTypedList.new Table
|
107
|
+
|
108
|
+
if self.workbook.use_autowidth
|
109
|
+
require 'RMagick' unless defined?(Magick)
|
110
|
+
@magick_draw = Magick::Draw.new
|
111
|
+
else
|
112
|
+
@magick_draw = nil
|
113
|
+
end
|
114
|
+
|
115
|
+
options.each do |o|
|
116
|
+
self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
|
117
|
+
end
|
118
|
+
|
78
119
|
end
|
79
120
|
|
80
121
|
# convinience method to access all cells in this worksheet
|
@@ -109,29 +150,31 @@ module Axlsx
|
|
109
150
|
"#{rows.first.cells.first.r}:#{rows.last.cells.last.r}"
|
110
151
|
end
|
111
152
|
|
153
|
+
# Indicates if gridlines should be shown in the sheet.
|
154
|
+
# This is true by default.
|
155
|
+
# @return [Boolean]
|
156
|
+
def show_gridlines=(v)
|
157
|
+
Axlsx::validate_boolean v
|
158
|
+
@show_gridlines = v
|
159
|
+
end
|
112
160
|
|
113
|
-
#
|
114
|
-
# @
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
first = name_to_cell parts[0]
|
161
|
+
# @see selected
|
162
|
+
# @return [Boolean]
|
163
|
+
def selected=(v)
|
164
|
+
Axlsx::validate_boolean v
|
165
|
+
@selected = v
|
166
|
+
end
|
120
167
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
cells << c
|
129
|
-
end
|
130
|
-
end
|
131
|
-
cells
|
132
|
-
end
|
168
|
+
|
169
|
+
# Indicates if the worksheet should print in a single page.
|
170
|
+
# This is true by default.
|
171
|
+
# @return [Boolean]
|
172
|
+
def fit_to_page=(v)
|
173
|
+
Axlsx::validate_boolean v
|
174
|
+
@fit_to_page = v
|
133
175
|
end
|
134
176
|
|
177
|
+
|
135
178
|
# returns the column and row index for a named based cell
|
136
179
|
# @param [String] name The cell or cell range to return. "A1" will return the first cell of the first row.
|
137
180
|
# @return [Cell]
|
@@ -152,6 +195,12 @@ module Axlsx
|
|
152
195
|
@name=v
|
153
196
|
end
|
154
197
|
|
198
|
+
# The absolute auto filter range
|
199
|
+
# @see auto_filter
|
200
|
+
def abs_auto_filter
|
201
|
+
Axlsx.cell_range(@auto_filter.split(':').collect { |name| name_to_cell(name)}) if @auto_filter
|
202
|
+
end
|
203
|
+
|
155
204
|
# The auto filter range for the worksheet
|
156
205
|
# @param [String] v
|
157
206
|
# @see auto_filter
|
@@ -233,7 +282,8 @@ module Axlsx
|
|
233
282
|
# @option options [Float] height the row's height (in points)
|
234
283
|
def add_row(values=[], options={})
|
235
284
|
Row.new(self, values, options)
|
236
|
-
|
285
|
+
update_column_info @rows.last.cells, options.delete(:widths) ||[], options.delete(:style) || []
|
286
|
+
# update_auto_fit_data @rows.last.cells, options.delete(:widths) || []
|
237
287
|
yield @rows.last if block_given?
|
238
288
|
@rows.last
|
239
289
|
end
|
@@ -292,9 +342,9 @@ module Axlsx
|
|
292
342
|
# @param [Integer|Float|Fixnum|nil] values
|
293
343
|
def column_widths(*args)
|
294
344
|
args.each_with_index do |value, index|
|
295
|
-
raise ArgumentError, "Invalid column specification" unless index < @
|
345
|
+
raise ArgumentError, "Invalid column specification" unless index < @column_info.size
|
296
346
|
Axlsx::validate_unsigned_numeric(value) unless value == nil
|
297
|
-
@
|
347
|
+
@column_info[index].width = value
|
298
348
|
end
|
299
349
|
end
|
300
350
|
|
@@ -317,6 +367,14 @@ module Axlsx
|
|
317
367
|
chart
|
318
368
|
end
|
319
369
|
|
370
|
+
# needs documentation
|
371
|
+
def add_table(ref, options={})
|
372
|
+
table = Table.new(ref, self, options)
|
373
|
+
@tables << table
|
374
|
+
yield table if block_given?
|
375
|
+
table
|
376
|
+
end
|
377
|
+
|
320
378
|
# Adds a media item to the worksheets drawing
|
321
379
|
# @param [Class] media_type
|
322
380
|
# @option options [] unknown
|
@@ -326,125 +384,101 @@ module Axlsx
|
|
326
384
|
image
|
327
385
|
end
|
328
386
|
|
329
|
-
# Serializes the
|
387
|
+
# Serializes the object
|
388
|
+
# @param [String] str
|
330
389
|
# @return [String]
|
331
|
-
def
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
}
|
353
|
-
end
|
354
|
-
xml.sheetData {
|
355
|
-
@rows.each do |row|
|
356
|
-
row.to_xml(xml)
|
357
|
-
end
|
358
|
-
}
|
359
|
-
xml.autoFilter :ref=>@auto_filter if @auto_filter
|
360
|
-
xml.mergeCells(:count=>@merged_cells.size) { @merged_cells.each { | mc | xml.mergeCell(:ref=>mc) } } unless @merged_cells.empty?
|
361
|
-
page_margins.to_xml(xml) if @page_margins
|
362
|
-
xml.drawing :"r:id"=>"rId1" if @drawing
|
363
|
-
}
|
390
|
+
def to_xml_string
|
391
|
+
str = '<?xml version="1.0" encoding="UTF-8"?>'
|
392
|
+
str.concat "<worksheet xmlns=\"%s\" xmlns:r=\"%s\">" % [XML_NS, XML_NS_R]
|
393
|
+
str.concat "<sheetPr><pageSetUpPr fitToPage=\"%s\"></pageSetUpPr></sheetPr>" % fit_to_page if fit_to_page
|
394
|
+
str.concat "<dimension ref=\"%s\"></dimension>" % dimension unless rows.size == 0
|
395
|
+
str.concat "<sheetViews><sheetView tabSelected='%s' workbookViewId='0' showGridLines='%s'><selection activeCell=\"A1\" sqref=\"A1\"/></sheetView></sheetViews>" % [@selected, show_gridlines]
|
396
|
+
|
397
|
+
if @column_info.size > 0
|
398
|
+
str << "<cols>"
|
399
|
+
@column_info.each { |col| col.to_xml_string(str) }
|
400
|
+
str.concat '</cols>'
|
401
|
+
end
|
402
|
+
str.concat '<sheetData>'
|
403
|
+
@rows.each_with_index { |row, index| row.to_xml_string(index, str) }
|
404
|
+
str.concat '</sheetData>'
|
405
|
+
page_margins.to_xml_string(str) if @page_margins
|
406
|
+
str.concat "<autoFilter ref='%s'></autoFilter>" % @auto_filter if @auto_filter
|
407
|
+
str.concat "<mergeCells count='%s'>%s</mergeCells>" % [@merged_cells.size, @merged_cells.reduce('') { |memo, obj| "<mergeCell ref='%s'></mergeCell>" % obj } ] unless @merged_cells.empty?
|
408
|
+
str.concat "<drawing r:id='rId1'></drawing>" if @drawing
|
409
|
+
unless @tables.empty?
|
410
|
+
str.concat "<tableParts count='%s'>%s</tableParts>" % [@tables.size, @tables.reduce('') { |memo, obj| memo += "<tablePart r:id='%s'/>" % obj.rId }]
|
364
411
|
end
|
365
|
-
|
412
|
+
str + '</worksheet>'
|
366
413
|
end
|
367
414
|
|
368
415
|
# The worksheet relationships. This is managed automatically by the worksheet
|
369
416
|
# @return [Relationships]
|
370
417
|
def relationships
|
371
418
|
r = Relationships.new
|
419
|
+
@tables.each do |table|
|
420
|
+
r << Relationship.new(TABLE_R, "../#{table.pn}")
|
421
|
+
end
|
372
422
|
r << Relationship.new(DRAWING_R, "../#{@drawing.pn}") if @drawing
|
373
423
|
r
|
374
424
|
end
|
375
425
|
|
426
|
+
# Returns the cell or cells defined using excel style A1:B3 references.
|
427
|
+
# @param [String|Integer] cell_def the string defining the cell or range of cells, or the rownumber
|
428
|
+
# @return [Cell, Array]
|
429
|
+
|
430
|
+
def [] (cell_def)
|
431
|
+
return rows[cell_def] if cell_def.is_a?(Integer)
|
432
|
+
|
433
|
+
parts = cell_def.split(':')
|
434
|
+
first = name_to_cell parts[0]
|
435
|
+
if parts.size == 1
|
436
|
+
first
|
437
|
+
else
|
438
|
+
cells = []
|
439
|
+
last = name_to_cell(parts[1])
|
440
|
+
rows[(first.row.index..last.row.index)].each do |r|
|
441
|
+
r.cells[(first.index..last.index)].each do |c|
|
442
|
+
cells << c
|
443
|
+
end
|
444
|
+
end
|
445
|
+
cells
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
376
449
|
private
|
377
450
|
|
378
451
|
# assigns the owner workbook for this worksheet
|
379
452
|
def workbook=(v) DataTypeValidator.validate "Worksheet.workbook", Workbook, v; @workbook = v; end
|
380
453
|
|
381
|
-
|
382
|
-
|
383
|
-
# attempt to identify the longest cell in the column. This is not 100% accurate as it needs to take into account
|
384
|
-
# any formatting that will be applied to the data, as well as the actual rendering size when the length and size is equal
|
385
|
-
# for two cells.
|
386
|
-
|
387
|
-
# @return [Array] of Cell objects
|
388
|
-
# @param [Array] cells an array of cells
|
389
|
-
# @param [Array] widths an array of cell widths @see Worksheet#add_row
|
390
|
-
def update_auto_fit_data(cells, widths=[])
|
391
|
-
# TODO delay this until rendering. too much work when we dont know what they are going to do to the sheet.
|
454
|
+
|
455
|
+
def update_column_info(cells, widths=[], style=[])
|
392
456
|
styles = self.workbook.styles
|
393
457
|
cellXfs, fonts = styles.cellXfs, styles.fonts
|
394
458
|
sz = 11
|
395
|
-
cells.each_with_index do |
|
396
|
-
|
459
|
+
cells.each_with_index do |cell, index|
|
460
|
+
@column_info[index] ||= Col.new index+1, index+1
|
461
|
+
col = @column_info[index]
|
397
462
|
width = widths[index]
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
font = fonts[cell_xf.fontId || 0]
|
407
|
-
sz = item.sz || font.sz || fonts[0].sz
|
408
|
-
if (col[:longest].scan(/./mu).size * col[:sz]) < (item.value.to_s.scan(/./mu).size * sz)
|
409
|
-
col[:sz] = sz
|
410
|
-
col[:longest] = item.value.to_s
|
463
|
+
col.width = width if [Integer, Float, Fixnum].include?(width.class)
|
464
|
+
c_style = style[index] if [Integer, Fixnum].include?(style[index].class)
|
465
|
+
next if width == :ignore || col.width || (cell.value.is_a?(String) && cell.value.start_with?('='))
|
466
|
+
if self.workbook.use_autowidth
|
467
|
+
cell_xf = cellXfs[(c_style || 0)]
|
468
|
+
font = fonts[(cell_xf.fontId || 0)]
|
469
|
+
sz = cell.sz || font.sz || sz
|
470
|
+
col.width = [(col.width || 0), calculate_width(cell.value.to_s, sz)].max
|
411
471
|
end
|
412
472
|
end
|
413
|
-
cells
|
414
473
|
end
|
415
474
|
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
# @return [Float]
|
420
|
-
# @param [Hash] A hash of auto_fit_data
|
421
|
-
def auto_width(col)
|
422
|
-
return col[:fixed] unless col[:fixed] == nil
|
423
|
-
|
424
|
-
mdw_count, font_scale, mdw = 0, col[:sz]/11.0, 6.0
|
425
|
-
mdw_count = col[:longest].scan(/./mu).reduce(0) do | count, char |
|
475
|
+
def calculate_width(text, sz)
|
476
|
+
mdw_count, font_scale, mdw = 0, sz/11.0, 6.0
|
477
|
+
mdw_count = text.scan(/./mu).reduce(0) do | count, char |
|
426
478
|
count +=1 if @magick_draw.get_type_metrics(char).max_advance >= mdw
|
427
479
|
count
|
428
480
|
end
|
429
481
|
((mdw_count * mdw + 5) / mdw * 256) / 256.0 * font_scale
|
430
482
|
end
|
431
|
-
|
432
|
-
# Something to look into:
|
433
|
-
# width calculation actually needs to be done agains the formatted value for items that apply a
|
434
|
-
# format
|
435
|
-
# def excel_format(cell)
|
436
|
-
# # The most common case.
|
437
|
-
# return time.value.to_s if cell.style == 0
|
438
|
-
#
|
439
|
-
# # The second most common case
|
440
|
-
# num_fmt = workbook.styles.cellXfs[items.style].numFmtId
|
441
|
-
# return value.to_s if num_fmt == 0
|
442
|
-
#
|
443
|
-
# format_code = workbook.styles.numFmts[num_fmt]
|
444
|
-
# # need to find some exceptionally fast way of parsing value according to
|
445
|
-
# # an excel format_code
|
446
|
-
# item.value.to_s
|
447
|
-
# end
|
448
|
-
|
449
483
|
end
|
450
484
|
end
|