axlsx 1.0.15 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.yardopts +1 -0
- data/CHANGELOG.md +58 -2
- data/README.md +425 -207
- data/Rakefile +6 -2
- data/examples/axlsx.xlsx +0 -0
- data/examples/example.csv +1000 -0
- data/examples/example.rb +266 -132
- data/examples/example.xlsx +0 -0
- data/examples/example_streamed.xlsx +0 -0
- data/examples/no-use_autowidth.xlsx +0 -0
- data/examples/real_example.rb +63 -0
- data/examples/sample.png +0 -0
- data/examples/shared_strings_example.xlsx +0 -0
- data/lib/axlsx/content_type/content_type.rb +13 -12
- data/lib/axlsx/content_type/default.rb +10 -6
- data/lib/axlsx/content_type/override.rb +13 -8
- data/lib/axlsx/doc_props/app.rb +38 -41
- data/lib/axlsx/doc_props/core.rb +13 -17
- data/lib/axlsx/drawing/axis.rb +39 -19
- data/lib/axlsx/drawing/bar_3D_chart.rb +34 -32
- data/lib/axlsx/drawing/bar_series.rb +14 -14
- data/lib/axlsx/drawing/cat_axis.rb +16 -14
- data/lib/axlsx/drawing/cat_axis_data.rb +17 -18
- data/lib/axlsx/drawing/chart.rb +40 -41
- data/lib/axlsx/drawing/drawing.rb +27 -12
- data/lib/axlsx/drawing/graphic_frame.rb +22 -21
- data/lib/axlsx/drawing/hlink_click.rb~ +0 -0
- data/lib/axlsx/drawing/hyperlink.rb +96 -0
- data/lib/axlsx/drawing/hyperlink.rb~ +64 -0
- data/lib/axlsx/drawing/line_3D_chart.rb +31 -28
- data/lib/axlsx/drawing/line_series.rb +12 -11
- data/lib/axlsx/drawing/marker.rb +11 -8
- data/lib/axlsx/drawing/named_axis_data.rb +36 -0
- data/lib/axlsx/drawing/one_cell_anchor.rb +18 -16
- data/lib/axlsx/drawing/pic.rb +47 -33
- data/lib/axlsx/drawing/picture_locking.rb +22 -18
- data/lib/axlsx/drawing/pie_3D_chart.rb +11 -8
- data/lib/axlsx/drawing/pie_series.rb +16 -12
- data/lib/axlsx/drawing/scaling.rb +11 -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 +11 -10
- data/lib/axlsx/drawing/series.rb +16 -15
- data/lib/axlsx/drawing/series_title.rb +15 -14
- data/lib/axlsx/drawing/title.rb +27 -26
- data/lib/axlsx/drawing/two_cell_anchor.rb +19 -20
- data/lib/axlsx/drawing/val_axis.rb +9 -7
- data/lib/axlsx/drawing/val_axis_data.rb +18 -17
- data/lib/axlsx/drawing/view_3D.rb +23 -20
- data/lib/axlsx/package.rb +117 -52
- data/lib/axlsx/rels/relationship.rb +27 -8
- data/lib/axlsx/rels/relationships.rb +9 -2
- data/lib/axlsx/stylesheet/border.rb +28 -23
- data/lib/axlsx/stylesheet/border_pr.rb +17 -15
- data/lib/axlsx/stylesheet/cell_alignment.rb +24 -21
- data/lib/axlsx/stylesheet/cell_protection.rb +11 -7
- data/lib/axlsx/stylesheet/cell_style.rb +9 -5
- data/lib/axlsx/stylesheet/color.rb +20 -14
- data/lib/axlsx/stylesheet/fill.rb +8 -5
- data/lib/axlsx/stylesheet/font.rb +22 -14
- data/lib/axlsx/stylesheet/gradient_fill.rb +20 -17
- data/lib/axlsx/stylesheet/gradient_stop.rb +10 -6
- data/lib/axlsx/stylesheet/num_fmt.rb +13 -6
- data/lib/axlsx/stylesheet/pattern_fill.rb +26 -10
- data/lib/axlsx/stylesheet/styles.rb +55 -41
- data/lib/axlsx/stylesheet/table_style.rb +10 -4
- data/lib/axlsx/stylesheet/table_style_element.rb +11 -7
- data/lib/axlsx/stylesheet/table_styles.rb +12 -8
- data/lib/axlsx/stylesheet/xf.rb +30 -25
- data/lib/axlsx/util/cbf.rb +69 -33
- data/lib/axlsx/util/constants.rb +39 -20
- data/lib/axlsx/util/doc/_index.html +84 -0
- data/lib/axlsx/util/doc/class_list.html +47 -0
- data/lib/axlsx/util/doc/css/common.css +1 -0
- data/lib/axlsx/util/doc/css/full_list.css +55 -0
- data/lib/axlsx/util/doc/css/style.css +322 -0
- data/lib/axlsx/util/doc/file_list.html +46 -0
- data/lib/axlsx/util/doc/frames.html +13 -0
- data/lib/axlsx/util/doc/index.html +84 -0
- data/lib/axlsx/util/doc/js/app.js +205 -0
- data/lib/axlsx/util/doc/js/full_list.js +173 -0
- data/lib/axlsx/util/doc/js/jquery.js +16 -0
- data/lib/axlsx/util/doc/method_list.html +46 -0
- data/lib/axlsx/util/doc/top-level-namespace.html +95 -0
- data/lib/axlsx/util/font_tables.rb~ +0 -0
- data/lib/axlsx/util/ms_off_crypto.rb +10 -10
- data/lib/axlsx/util/parser.rb +1 -0
- data/lib/axlsx/util/simple_typed_list.rb +20 -10
- data/lib/axlsx/util/storage.rb +1 -1
- data/lib/axlsx/util/validators.rb +23 -7
- data/lib/axlsx/version.rb +5 -2
- data/lib/axlsx/workbook/shared_strings_table.rb +68 -0
- data/lib/axlsx/workbook/shared_strings_table.rb~ +69 -0
- data/lib/axlsx/workbook/workbook.rb +82 -26
- data/lib/axlsx/workbook/worksheet/cell.rb +166 -130
- data/lib/axlsx/workbook/worksheet/col.rb +114 -0
- data/lib/axlsx/workbook/worksheet/col.rb~ +0 -0
- data/lib/axlsx/workbook/worksheet/date_time_converter.rb +29 -0
- data/lib/axlsx/workbook/worksheet/page_margins.rb +97 -0
- data/lib/axlsx/workbook/worksheet/row.rb +53 -21
- data/lib/axlsx/workbook/worksheet/shared_strings_table.rb~ +0 -0
- 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 +275 -124
- data/lib/axlsx.rb +41 -14
- 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.rb +73 -0
- data/test/content_type/tc_content_type.rb +31 -31
- data/test/content_type/tc_default.rb +9 -22
- data/test/content_type/tc_override.rb +8 -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 +13 -14
- data/test/drawing/tc_drawing.rb +7 -8
- data/test/drawing/tc_graphic_frame.rb +3 -4
- data/test/drawing/tc_hyperlink.rb +69 -0
- 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 +13 -8
- 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/profile.rb +34 -0
- data/test/rels/tc_relationship.rb +10 -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 +10 -6
- 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 +12 -5
- 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 +31 -6
- 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_helper.rb +3 -0
- data/test/tc_package.rb +43 -9
- 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 +38 -0
- data/test/workbook/tc_workbook.rb +32 -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 +80 -13
- 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 +132 -0
- data/test/workbook/worksheet/tc_page_margins.rb +97 -0
- data/test/workbook/worksheet/tc_row.rb +54 -4
- data/test/workbook/worksheet/tc_worksheet.rb +205 -31
- metadata +78 -84
- data/examples/example.rb~ +0 -112
- data/test/drawing/tc_line_series.tc~ +0 -34
- data/test/drawing/tc_picture_locking.rb~ +0 -77
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
#
|
|
1
|
+
# encoding: UTF-8
|
|
2
2
|
module Axlsx
|
|
3
|
-
|
|
4
|
-
# The Worksheet class represents a worksheet in the workbook.
|
|
3
|
+
|
|
4
|
+
# The Worksheet class represents a worksheet in the workbook.
|
|
5
5
|
class Worksheet
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
# The name of the worksheet
|
|
8
8
|
# @return [String]
|
|
9
9
|
attr_reader :name
|
|
@@ -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
|
|
@@ -21,7 +24,7 @@ module Axlsx
|
|
|
21
24
|
|
|
22
25
|
# An array of content based calculated column widths.
|
|
23
26
|
# @note a single auto fit data item is a hash with :longest => [String] and :sz=> [Integer] members.
|
|
24
|
-
# @return [Array] of Hash
|
|
27
|
+
# @return [Array] of Hash
|
|
25
28
|
attr_reader :auto_fit_data
|
|
26
29
|
|
|
27
30
|
# An array of merged cell ranges e.d "A1:B3"
|
|
@@ -34,63 +37,145 @@ module Axlsx
|
|
|
34
37
|
# The first row is considered the header, while subsequent rows are considerd to be data.
|
|
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
|
+
|
|
61
|
+
# Page margins for printing the worksheet.
|
|
62
|
+
# @example
|
|
63
|
+
# wb = Axlsx::Package.new.workbook
|
|
64
|
+
# # using options when creating the worksheet.
|
|
65
|
+
# ws = wb.add_worksheet :page_margins => {:left => 1.9, :header => 0.1}
|
|
66
|
+
#
|
|
67
|
+
# # use the set method of the page_margins object
|
|
68
|
+
# ws.page_margins.set(:bottom => 3, :footer => 0.7)
|
|
69
|
+
#
|
|
70
|
+
# # set page margins in a block
|
|
71
|
+
# ws.page_margins do |margins|
|
|
72
|
+
# margins.right = 6
|
|
73
|
+
# margins.top = 0.2
|
|
74
|
+
# end
|
|
75
|
+
# @see PageMargins#initialize
|
|
76
|
+
# @return [PageMargins]
|
|
77
|
+
def page_margins
|
|
78
|
+
@page_margins ||= PageMargins.new
|
|
79
|
+
yield @page_margins if block_given?
|
|
80
|
+
@page_margins
|
|
81
|
+
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# definition of characters which are less than the maximum width of 0-9 in the default font for use in String#count.
|
|
85
|
+
# This is used for autowidth calculations
|
|
86
|
+
# @return [String]
|
|
87
|
+
def self.thin_chars
|
|
88
|
+
@thin_chars ||= "^.acefijklrstxyzFIJL()-"
|
|
89
|
+
end
|
|
90
|
+
|
|
38
91
|
# Creates a new worksheet.
|
|
39
92
|
# @note the recommended way to manage worksheets is Workbook#add_worksheet
|
|
40
93
|
# @see Workbook#add_worksheet
|
|
41
|
-
# @option options [String] name The name of this
|
|
94
|
+
# @option options [String] name The name of this worksheet.
|
|
95
|
+
# @option options [Hash] page_margins A hash containing page margins for this worksheet. @see PageMargins
|
|
96
|
+
# @option options [Boolean] show_gridlines indicates if gridlines should be shown for this sheet.
|
|
42
97
|
def initialize(wb, options={})
|
|
43
|
-
@drawing = nil
|
|
44
|
-
@rows = SimpleTypedList.new Row
|
|
45
98
|
self.workbook = wb
|
|
46
99
|
@workbook.worksheets << self
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
@magick_draw = Magick::Draw.new
|
|
50
|
-
@cols = SimpleTypedList.new Cell
|
|
100
|
+
|
|
101
|
+
@drawing = @page_margins = @auto_filter = nil
|
|
51
102
|
@merged_cells = []
|
|
103
|
+
@auto_fit_data = []
|
|
104
|
+
|
|
105
|
+
@selected = false
|
|
106
|
+
@show_gridlines = true
|
|
107
|
+
self.name = "Sheet" + (index+1).to_s
|
|
108
|
+
@page_margins = PageMargins.new options[:page_margins] if options[:page_margins]
|
|
109
|
+
|
|
110
|
+
@rows = SimpleTypedList.new Row
|
|
111
|
+
@column_info = SimpleTypedList.new Col
|
|
112
|
+
# @cols = SimpleTypedList.new Cell
|
|
113
|
+
@tables = SimpleTypedList.new Table
|
|
114
|
+
|
|
115
|
+
options.each do |o|
|
|
116
|
+
self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# convinience method to access all cells in this worksheet
|
|
121
|
+
# @return [Array] cells
|
|
122
|
+
def cells
|
|
123
|
+
rows.flatten
|
|
52
124
|
end
|
|
53
125
|
|
|
54
|
-
# Creates merge information for this worksheet.
|
|
126
|
+
# Creates merge information for this worksheet.
|
|
55
127
|
# Cells can be merged by calling the merge_cells method on a worksheet.
|
|
56
|
-
# @example This would merge the three cells C1..E1 #
|
|
128
|
+
# @example This would merge the three cells C1..E1 #
|
|
57
129
|
# worksheet.merge_cells "C1:E1"
|
|
58
130
|
# # you can also provide an array of cells to be merged
|
|
59
131
|
# worksheet.merge_cells worksheet.rows.first.cells[(2..4)]
|
|
60
132
|
# #alternatively you can do it from a single cell
|
|
61
133
|
# worksheet["C1"].merge worksheet["E1"]
|
|
62
|
-
# @param [Array, string]
|
|
134
|
+
# @param [Array, string]
|
|
63
135
|
def merge_cells(cells)
|
|
64
136
|
@merged_cells << if cells.is_a?(String)
|
|
65
137
|
cells
|
|
66
138
|
elsif cells.is_a?(Array)
|
|
67
139
|
cells = cells.sort { |x, y| x.r <=> y.r }
|
|
68
140
|
"#{cells.first.r}:#{cells.last.r}"
|
|
69
|
-
end
|
|
141
|
+
end
|
|
70
142
|
end
|
|
71
143
|
|
|
72
144
|
|
|
73
|
-
#
|
|
74
|
-
#
|
|
75
|
-
# @return [
|
|
76
|
-
def
|
|
77
|
-
|
|
78
|
-
|
|
145
|
+
# The demensions of a worksheet. This is not actually a required element by the spec,
|
|
146
|
+
# but at least a few other document readers expect this for conversion
|
|
147
|
+
# @return [String] the A1:B2 style reference for the first and last row column intersection in the workbook
|
|
148
|
+
def dimension
|
|
149
|
+
dim_start = rows.first.cells.first == nil ? 'A1' : rows.first.cells.first.r
|
|
150
|
+
dim_end = rows.last.cells.last == nil ? 'AA:200' : rows.last.cells.last.r
|
|
151
|
+
"#{dim_start}:#{dim_end}"
|
|
152
|
+
end
|
|
79
153
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
154
|
+
# Indicates if gridlines should be shown in the sheet.
|
|
155
|
+
# This is true by default.
|
|
156
|
+
# @return [Boolean]
|
|
157
|
+
def show_gridlines=(v)
|
|
158
|
+
Axlsx::validate_boolean v
|
|
159
|
+
@show_gridlines = v
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# @see selected
|
|
163
|
+
# @return [Boolean]
|
|
164
|
+
def selected=(v)
|
|
165
|
+
Axlsx::validate_boolean v
|
|
166
|
+
@selected = v
|
|
92
167
|
end
|
|
93
168
|
|
|
169
|
+
|
|
170
|
+
# Indicates if the worksheet should print in a single page.
|
|
171
|
+
# This is true by default.
|
|
172
|
+
# @return [Boolean]
|
|
173
|
+
def fit_to_page=(v)
|
|
174
|
+
Axlsx::validate_boolean v
|
|
175
|
+
@fit_to_page = v
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
|
|
94
179
|
# returns the column and row index for a named based cell
|
|
95
180
|
# @param [String] name The cell or cell range to return. "A1" will return the first cell of the first row.
|
|
96
181
|
# @return [Cell]
|
|
@@ -101,18 +186,26 @@ module Axlsx
|
|
|
101
186
|
end
|
|
102
187
|
|
|
103
188
|
# The name of the worksheet
|
|
189
|
+
# The name of a worksheet must be unique in the workbook, and must not exceed 31 characters
|
|
104
190
|
# @param [String] v
|
|
105
|
-
def name=(v)
|
|
191
|
+
def name=(v)
|
|
106
192
|
DataTypeValidator.validate "Worksheet.name", String, v
|
|
193
|
+
raise ArgumentError, (ERR_SHEET_NAME_TOO_LONG % v) if v.size > 31
|
|
107
194
|
sheet_names = @workbook.worksheets.map { |s| s.name }
|
|
108
|
-
raise ArgumentError, (ERR_DUPLICATE_SHEET_NAME % v) if sheet_names.include?(v)
|
|
109
|
-
@name=v
|
|
195
|
+
raise ArgumentError, (ERR_DUPLICATE_SHEET_NAME % v) if sheet_names.include?(v)
|
|
196
|
+
@name=v
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# The absolute auto filter range
|
|
200
|
+
# @see auto_filter
|
|
201
|
+
def abs_auto_filter
|
|
202
|
+
Axlsx.cell_range(@auto_filter.split(':').collect { |name| name_to_cell(name)}) if @auto_filter
|
|
110
203
|
end
|
|
111
204
|
|
|
112
205
|
# The auto filter range for the worksheet
|
|
113
206
|
# @param [String] v
|
|
114
207
|
# @see auto_filter
|
|
115
|
-
def auto_filter=(v)
|
|
208
|
+
def auto_filter=(v)
|
|
116
209
|
DataTypeValidator.validate "Worksheet.auto_filter", String, v
|
|
117
210
|
@auto_filter = v
|
|
118
211
|
end
|
|
@@ -149,18 +242,55 @@ module Axlsx
|
|
|
149
242
|
@drawing || @drawing = Axlsx::Drawing.new(self)
|
|
150
243
|
end
|
|
151
244
|
|
|
152
|
-
# Adds a row to the worksheet and updates auto fit data
|
|
245
|
+
# Adds a row to the worksheet and updates auto fit data.
|
|
246
|
+
# @example - put a vanilla row in your spreadsheet
|
|
247
|
+
# ws.add_row [1, 'fish on my pl', '8']
|
|
248
|
+
#
|
|
249
|
+
# @example - specify a fixed width for a column in your spreadsheet
|
|
250
|
+
# # The first column will ignore the content of this cell when calculating column autowidth.
|
|
251
|
+
# # The second column will include this text in calculating the columns autowidth
|
|
252
|
+
# # The third cell will set a fixed with of 80 for the column.
|
|
253
|
+
# # If you need to un-fix a column width, use :auto. That will recalculate the column width based on all content in the column
|
|
254
|
+
#
|
|
255
|
+
# ws.add_row ['I wish', 'for a fish', 'on my fish wish dish'], :widths=>[:ignore, :auto, 80]
|
|
256
|
+
#
|
|
257
|
+
# @example - specify a fixed height for a row
|
|
258
|
+
# ws.add_row ['I wish', 'for a fish', 'on my fish wish dish'], :height => 40
|
|
259
|
+
#
|
|
260
|
+
# @example - create and use a style for all cells in the row
|
|
261
|
+
# blue = ws.styles.add_style :color => "#00FF00"
|
|
262
|
+
# ws.add_row [1, 2, 3], :style=>blue
|
|
263
|
+
#
|
|
264
|
+
# @example - only style some cells
|
|
265
|
+
# blue = ws.styles.add_style :color => "#00FF00"
|
|
266
|
+
# red = ws.styles.add_style :color => "#FF0000"
|
|
267
|
+
# big = ws.styles.add_style :sz => 40
|
|
268
|
+
# ws.add_row ["red fish", "blue fish", "one fish", "two fish"], :style=>[red, blue, nil, big] # the last nil is optional
|
|
269
|
+
#
|
|
270
|
+
#
|
|
271
|
+
# @example - force the second cell to be a float value
|
|
272
|
+
# ws.add_row [3, 4, 5], :types => [nil, :float]
|
|
273
|
+
#
|
|
274
|
+
# @example - use << alias
|
|
275
|
+
# ws << [3, 4, 5], :types => [nil, :float]
|
|
276
|
+
#
|
|
277
|
+
# @see Worksheet#column_widths
|
|
153
278
|
# @return [Row]
|
|
154
279
|
# @option options [Array] values
|
|
155
|
-
# @option options [Array, Symbol] types
|
|
156
|
-
# @option options [Array, Integer] style
|
|
280
|
+
# @option options [Array, Symbol] types
|
|
281
|
+
# @option options [Array, Integer] style
|
|
282
|
+
# @option options [Array] widths each member of the widths array will affect how auto_fit behavies.
|
|
283
|
+
# @option options [Float] height the row's height (in points)
|
|
157
284
|
def add_row(values=[], options={})
|
|
158
285
|
Row.new(self, values, options)
|
|
159
|
-
|
|
286
|
+
update_column_info @rows.last.cells, options.delete(:widths) ||[], options.delete(:style) || []
|
|
287
|
+
# update_auto_fit_data @rows.last.cells, options.delete(:widths) || []
|
|
160
288
|
yield @rows.last if block_given?
|
|
161
289
|
@rows.last
|
|
162
290
|
end
|
|
163
291
|
|
|
292
|
+
alias :<< :add_row
|
|
293
|
+
|
|
164
294
|
# Set the style for cells in a specific row
|
|
165
295
|
# @param [Integer] index or range of indexes in the table
|
|
166
296
|
# @param [Integer] the cellXfs index
|
|
@@ -193,8 +323,9 @@ module Axlsx
|
|
|
193
323
|
# @see README.md for an example
|
|
194
324
|
def col_style(index, style, options={})
|
|
195
325
|
offset = options.delete(:row_offset) || 0
|
|
196
|
-
@rows[(offset..-1)].each do |r|
|
|
326
|
+
@rows[(offset..-1)].each do |r|
|
|
197
327
|
cells = r.cells[index]
|
|
328
|
+
next unless cells
|
|
198
329
|
if cells.is_a?(Array)
|
|
199
330
|
cells.each { |c| c.style = style }
|
|
200
331
|
else
|
|
@@ -202,15 +333,31 @@ module Axlsx
|
|
|
202
333
|
end
|
|
203
334
|
end
|
|
204
335
|
end
|
|
205
|
-
|
|
206
|
-
#
|
|
336
|
+
|
|
337
|
+
# This is a helper method that Lets you specify a fixed width for multiple columns in a worksheet in one go.
|
|
338
|
+
# Axlsx is sparse, so if you have not set data for a column, you cannot set the width.
|
|
339
|
+
# Setting a fixed column width to nil will revert the behaviour back to calculating the width for you.
|
|
340
|
+
# @example This would set the first and third column widhts but leave the second column in autofit state.
|
|
341
|
+
# ws.column_widths 7.2, nil, 3
|
|
342
|
+
# @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
|
|
343
|
+
# @param [Integer|Float|Fixnum|nil] values
|
|
344
|
+
def column_widths(*args)
|
|
345
|
+
args.each_with_index do |value, index|
|
|
346
|
+
next if value == nil
|
|
347
|
+
Axlsx::validate_unsigned_numeric(value) unless value == nil
|
|
348
|
+
@column_info[index] ||= Col.new index+1, index+1
|
|
349
|
+
@column_info[index].width = value
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# Adds a chart to this worksheets drawing. This is the recommended way to create charts for your worksheet. This method wraps the complexity of dealing with ooxml drawing, anchors, markers graphic frames chart objects and all the other dirty details.
|
|
207
354
|
# @param [Class] chart_type
|
|
208
355
|
# @option options [Array] start_at
|
|
209
356
|
# @option options [Array] end_at
|
|
210
357
|
# @option options [Cell, String] title
|
|
211
358
|
# @option options [Boolean] show_legend
|
|
212
|
-
# @option options [Integer] style
|
|
213
|
-
# @note each chart type also specifies additional options
|
|
359
|
+
# @option options [Integer] style
|
|
360
|
+
# @note each chart type also specifies additional options
|
|
214
361
|
# @see Chart
|
|
215
362
|
# @see Pie3DChart
|
|
216
363
|
# @see Bar3DChart
|
|
@@ -222,6 +369,14 @@ module Axlsx
|
|
|
222
369
|
chart
|
|
223
370
|
end
|
|
224
371
|
|
|
372
|
+
# needs documentation
|
|
373
|
+
def add_table(ref, options={})
|
|
374
|
+
table = Table.new(ref, self, options)
|
|
375
|
+
@tables << table
|
|
376
|
+
yield table if block_given?
|
|
377
|
+
table
|
|
378
|
+
end
|
|
379
|
+
|
|
225
380
|
# Adds a media item to the worksheets drawing
|
|
226
381
|
# @param [Class] media_type
|
|
227
382
|
# @option options [] unknown
|
|
@@ -231,105 +386,101 @@ module Axlsx
|
|
|
231
386
|
image
|
|
232
387
|
end
|
|
233
388
|
|
|
234
|
-
# Serializes the
|
|
389
|
+
# Serializes the object
|
|
390
|
+
# @param [String] str
|
|
235
391
|
# @return [String]
|
|
236
|
-
def
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
end
|
|
248
|
-
xml.sheetData {
|
|
249
|
-
@rows.each do |row|
|
|
250
|
-
row.to_xml(xml)
|
|
251
|
-
end
|
|
252
|
-
}
|
|
253
|
-
xml.autoFilter :ref=>@auto_filter if @auto_filter
|
|
254
|
-
xml.mergeCells(:count=>@merged_cells.size) { @merged_cells.each { | mc | xml.mergeCell(:ref=>mc) } } unless @merged_cells.empty?
|
|
255
|
-
xml.drawing :"r:id"=>"rId1" if @drawing
|
|
256
|
-
}
|
|
392
|
+
def to_xml_string
|
|
393
|
+
str = '<?xml version="1.0" encoding="UTF-8"?>'
|
|
394
|
+
str.concat "<worksheet xmlns=\"%s\" xmlns:r=\"%s\">" % [XML_NS, XML_NS_R]
|
|
395
|
+
str.concat "<sheetPr><pageSetUpPr fitToPage=\"%s\"></pageSetUpPr></sheetPr>" % fit_to_page if fit_to_page
|
|
396
|
+
str.concat "<dimension ref=\"%s\"></dimension>" % dimension unless rows.size == 0
|
|
397
|
+
str.concat "<sheetViews><sheetView tabSelected='%s' workbookViewId='0' showGridLines='%s'><selection activeCell=\"A1\" sqref=\"A1\"/></sheetView></sheetViews>" % [@selected, show_gridlines]
|
|
398
|
+
|
|
399
|
+
if @column_info.size > 0
|
|
400
|
+
str << "<cols>"
|
|
401
|
+
@column_info.each { |col| col.to_xml_string(str) }
|
|
402
|
+
str.concat '</cols>'
|
|
257
403
|
end
|
|
258
|
-
|
|
404
|
+
str.concat '<sheetData>'
|
|
405
|
+
@rows.each_with_index { |row, index| row.to_xml_string(index, str) }
|
|
406
|
+
str.concat '</sheetData>'
|
|
407
|
+
str.concat "<autoFilter ref='%s'></autoFilter>" % @auto_filter if @auto_filter
|
|
408
|
+
str.concat "<mergeCells count='%s'>%s</mergeCells>" % [@merged_cells.size, @merged_cells.reduce('') { |memo, obj| memo += "<mergeCell ref='%s'></mergeCell>" % obj } ] unless @merged_cells.empty?
|
|
409
|
+
page_margins.to_xml_string(str) if @page_margins
|
|
410
|
+
str.concat "<drawing r:id='rId1'></drawing>" if @drawing
|
|
411
|
+
unless @tables.empty?
|
|
412
|
+
str.concat "<tableParts count='%s'>%s</tableParts>" % [@tables.size, @tables.reduce('') { |memo, obj| memo += "<tablePart r:id='%s'/>" % obj.rId }]
|
|
413
|
+
end
|
|
414
|
+
str + '</worksheet>'
|
|
259
415
|
end
|
|
260
416
|
|
|
261
417
|
# The worksheet relationships. This is managed automatically by the worksheet
|
|
262
418
|
# @return [Relationships]
|
|
263
419
|
def relationships
|
|
264
420
|
r = Relationships.new
|
|
421
|
+
@tables.each do |table|
|
|
422
|
+
r << Relationship.new(TABLE_R, "../#{table.pn}")
|
|
423
|
+
end
|
|
265
424
|
r << Relationship.new(DRAWING_R, "../#{@drawing.pn}") if @drawing
|
|
266
425
|
r
|
|
267
426
|
end
|
|
268
427
|
|
|
269
|
-
|
|
428
|
+
# Returns the cell or cells defined using excel style A1:B3 references.
|
|
429
|
+
# @param [String|Integer] cell_def the string defining the cell or range of cells, or the rownumber
|
|
430
|
+
# @return [Cell, Array]
|
|
431
|
+
def [] (cell_def)
|
|
432
|
+
return rows[cell_def] if cell_def.is_a?(Integer)
|
|
433
|
+
|
|
434
|
+
parts = cell_def.split(':')
|
|
435
|
+
first = name_to_cell parts[0]
|
|
436
|
+
if parts.size == 1
|
|
437
|
+
first
|
|
438
|
+
else
|
|
439
|
+
cells = []
|
|
440
|
+
last = name_to_cell(parts[1])
|
|
441
|
+
rows[(first.row.index..last.row.index)].each do |r|
|
|
442
|
+
r.cells[(first.index..last.index)].each do |c|
|
|
443
|
+
cells << c
|
|
444
|
+
end
|
|
445
|
+
end
|
|
446
|
+
cells
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
private
|
|
270
451
|
|
|
271
452
|
# assigns the owner workbook for this worksheet
|
|
272
453
|
def workbook=(v) DataTypeValidator.validate "Worksheet.workbook", Workbook, v; @workbook = v; end
|
|
273
454
|
|
|
274
|
-
|
|
275
|
-
#
|
|
276
|
-
|
|
277
|
-
# any formatting that will be applied to the data, as well as the actual rendering size when the length and size is equal
|
|
278
|
-
# for two cells.
|
|
279
|
-
# @return [Array] of Cell objects
|
|
280
|
-
# @param [Array] cells an array of cells
|
|
281
|
-
def update_auto_fit_data(cells)
|
|
282
|
-
# TODO delay this until rendering. too much work when we dont know what they are going to do to the sheet.
|
|
455
|
+
|
|
456
|
+
# TODO this needs cleanup!
|
|
457
|
+
def update_column_info(cells, widths=[], style=[])
|
|
283
458
|
styles = self.workbook.styles
|
|
284
459
|
cellXfs, fonts = styles.cellXfs, styles.fonts
|
|
285
460
|
sz = 11
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
if
|
|
295
|
-
|
|
296
|
-
|
|
461
|
+
|
|
462
|
+
cells.each_with_index do |cell, index|
|
|
463
|
+
@column_info[index] ||= Col.new index+1, index+1
|
|
464
|
+
col = @column_info[index]
|
|
465
|
+
width = widths[index]
|
|
466
|
+
col.width = width if [Integer, Float, Fixnum].include?(width.class)
|
|
467
|
+
c_style = style[index] if [Integer, Fixnum].include?(style[index].class)
|
|
468
|
+
next if width == :ignore || (cell.value.is_a?(String) && cell.value.start_with?('=') || cell.value == nil)
|
|
469
|
+
if self.workbook.use_autowidth
|
|
470
|
+
cell_xf = cellXfs[(c_style || 0)]
|
|
471
|
+
font = fonts[(cell_xf.fontId || 0)]
|
|
472
|
+
sz = cell.sz || font.sz || sz
|
|
473
|
+
col.width = [(col.width || 0), calculate_width(cell.value.to_s, sz)].max
|
|
297
474
|
end
|
|
298
|
-
@auto_fit_data[index] = col
|
|
299
|
-
end
|
|
300
|
-
cells
|
|
301
|
-
end
|
|
302
|
-
|
|
303
|
-
# Determines the proper width for a column based on content.
|
|
304
|
-
# @note
|
|
305
|
-
# width = Truncate([!{Number of Characters} * !{Maximum Digit Width} + !{5 pixel padding}]/!{Maximum Digit Width}*256)/256
|
|
306
|
-
# @return [Float]
|
|
307
|
-
# @param [Hash] A hash of auto_fit_data
|
|
308
|
-
def auto_width(col)
|
|
309
|
-
mdw_count, font_scale, mdw = 0, col[:sz]/11.0, 6.0
|
|
310
|
-
mdw_count = col[:longest].scan(/./mu).reduce(0) do | count, char |
|
|
311
|
-
count +=1 if @magick_draw.get_type_metrics(char).max_advance >= mdw
|
|
312
|
-
count
|
|
313
475
|
end
|
|
314
|
-
((mdw_count * mdw + 5) / mdw * 256) / 256.0 * font_scale
|
|
315
476
|
end
|
|
316
477
|
|
|
317
|
-
# Something to look into:
|
|
318
|
-
# width calculation actually needs to be done agains the formatted value for items that apply a
|
|
319
|
-
# format
|
|
320
|
-
# def excel_format(cell)
|
|
321
|
-
# # The most common case.
|
|
322
|
-
# return time.value.to_s if cell.style == 0
|
|
323
|
-
#
|
|
324
|
-
# # The second most common case
|
|
325
|
-
# num_fmt = workbook.styles.cellXfs[items.style].numFmtId
|
|
326
|
-
# return value.to_s if num_fmt == 0
|
|
327
|
-
#
|
|
328
|
-
# format_code = workbook.styles.numFmts[num_fmt]
|
|
329
|
-
# # need to find some exceptionally fast way of parsing value according to
|
|
330
|
-
# # an excel format_code
|
|
331
|
-
# item.value.to_s
|
|
332
|
-
# end
|
|
333
478
|
|
|
479
|
+
# If you *REALLY* want to know what this does, check the spec for how column width is specified.
|
|
480
|
+
def calculate_width(text, sz)
|
|
481
|
+
mdw = 1.78 #magic numbers ftw! This is the widest width of 0..9 in the "standard" font (arial@10px)
|
|
482
|
+
font_scale = sz/10.0
|
|
483
|
+
((text.count(Worksheet.thin_chars) * mdw + 5) / mdw * 256) / 256.0 * font_scale
|
|
484
|
+
end
|
|
334
485
|
end
|
|
335
486
|
end
|
data/lib/axlsx.rb
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
Encoding::default_external = 'UTF-8' unless RUBY_VERSION < '1.9'
|
|
3
|
-
|
|
1
|
+
# encoding: UTF-8
|
|
4
2
|
require 'axlsx/version.rb'
|
|
5
3
|
|
|
6
4
|
require 'axlsx/util/simple_typed_list.rb'
|
|
7
5
|
require 'axlsx/util/constants.rb'
|
|
8
6
|
require 'axlsx/util/validators.rb'
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
#
|
|
7
|
+
require 'axlsx/util/storage.rb'
|
|
8
|
+
|
|
9
|
+
#not even close to being ready but it does not break anything so it stays for now.
|
|
10
|
+
# needs a full re-write to use agile-encryption properly
|
|
11
|
+
require 'axlsx/util/cbf.rb'
|
|
12
|
+
require 'axlsx/util/ms_off_crypto.rb'
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
# to be included with parsable intitites.
|
|
@@ -24,24 +25,29 @@ require 'axlsx/rels/relationships.rb'
|
|
|
24
25
|
require 'axlsx/drawing/drawing.rb'
|
|
25
26
|
require 'axlsx/workbook/workbook.rb'
|
|
26
27
|
require 'axlsx/package.rb'
|
|
27
|
-
|
|
28
28
|
#required gems
|
|
29
29
|
require 'nokogiri'
|
|
30
|
-
require 'active_support/core_ext/object/instance_variables'
|
|
31
|
-
require 'active_support/inflector'
|
|
32
|
-
require 'RMagick'
|
|
33
30
|
require 'zip/zip'
|
|
34
31
|
|
|
35
32
|
#core dependencies
|
|
36
33
|
require 'bigdecimal'
|
|
37
34
|
require 'time'
|
|
38
35
|
|
|
36
|
+
#if object does not have this already, I am borrowing it from active_support.
|
|
37
|
+
# I am a very big fan of activesupports instance_values method, but do not want to require nor include the entire
|
|
38
|
+
# library just for this one method.
|
|
39
|
+
if !Object.respond_to?(:instance_values)
|
|
40
|
+
Object.send :public # patch for 1.8.7 as it uses private scope
|
|
41
|
+
Object.send :define_method, :instance_values do
|
|
42
|
+
Hash[instance_variables.map { |name| [name.to_s[1..-1], instance_variable_get(name)] }]
|
|
43
|
+
end
|
|
44
|
+
end
|
|
39
45
|
|
|
40
46
|
# xlsx generation with charts, images, automated column width, customizable styles and full schema validation. Axlsx excels at helping you generate beautiful Office Open XML Spreadsheet documents without having to understand the entire ECMA specification. Check out the README for some examples of how easy it is. Best of all, you can validate your xlsx file before serialization so you know for sure that anything generated is going to load on your client's machine.
|
|
41
47
|
module Axlsx
|
|
42
48
|
# determines the cell range for the items provided
|
|
43
49
|
def self.cell_range(items)
|
|
44
|
-
return "" unless items.first.is_a? Cell
|
|
50
|
+
return "" unless items.first.is_a? Cell
|
|
45
51
|
ref = "'#{items.first.row.worksheet.name}'!" +
|
|
46
52
|
"#{items.first.r_abs}"
|
|
47
53
|
ref += ":#{items.last.r_abs}" if items.size > 1
|
|
@@ -50,11 +56,32 @@ module Axlsx
|
|
|
50
56
|
|
|
51
57
|
def self.name_to_indices(name)
|
|
52
58
|
raise ArgumentError, 'invalid cell name' unless name.size > 1
|
|
53
|
-
v = name[/[A-Z]+/].reverse.chars.reduce({:base=>1, :i=>0}) do |
|
|
54
|
-
|
|
59
|
+
v = name[/[A-Z]+/].reverse.chars.reduce({:base=>1, :i=>0}) do |val, c|
|
|
60
|
+
val[:i] += ((c.bytes.first - 64) * val[:base]); val[:base] *= 26; val
|
|
55
61
|
end
|
|
56
62
|
|
|
57
|
-
[v[:i]-1, ((name[/[1-9]
|
|
63
|
+
[v[:i]-1, ((name[/[1-9][0-9]*/]).to_i)-1]
|
|
58
64
|
|
|
59
65
|
end
|
|
66
|
+
|
|
67
|
+
# converts the column index into alphabetical values.
|
|
68
|
+
# @note This follows the standard spreadsheet convention of naming columns A to Z, followed by AA to AZ etc.
|
|
69
|
+
# @return [String]
|
|
70
|
+
def self.col_ref(index)
|
|
71
|
+
chars = []
|
|
72
|
+
while index >= 26 do
|
|
73
|
+
chars << ((index % 26) + 65).chr
|
|
74
|
+
index = index / 26 - 1
|
|
75
|
+
end
|
|
76
|
+
chars << (index + 65).chr
|
|
77
|
+
chars.reverse.join
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @return [String] The alpha(column)numeric(row) reference for this sell.
|
|
81
|
+
# @example Relative Cell Reference
|
|
82
|
+
# ws.rows.first.cells.first.r #=> "A1"
|
|
83
|
+
def self.cell_r(c_index, r_index)
|
|
84
|
+
Axlsx::col_ref(c_index).to_s << (r_index+1).to_s
|
|
85
|
+
end
|
|
86
|
+
|
|
60
87
|
end
|
data/lib/schema/dc.xsd
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
Created 2003-04-02
|
|
14
14
|
|
|
15
|
-
Created by
|
|
15
|
+
Created by
|
|
16
16
|
|
|
17
17
|
Tim Cole (t-cole3@uiuc.edu)
|
|
18
18
|
Tom Habing (thabing@uiuc.edu)
|
|
@@ -23,14 +23,14 @@
|
|
|
23
23
|
This schema declares XML elements for the 15 DC elements from the
|
|
24
24
|
http://purl.org/dc/elements/1.1/ namespace.
|
|
25
25
|
|
|
26
|
-
It defines a complexType SimpleLiteral which permits mixed content
|
|
26
|
+
It defines a complexType SimpleLiteral which permits mixed content
|
|
27
27
|
and makes the xml:lang attribute available. It disallows child elements by
|
|
28
28
|
use of minOcccurs/maxOccurs.
|
|
29
29
|
|
|
30
30
|
However, this complexType does permit the derivation of other complexTypes
|
|
31
31
|
which would permit child elements.
|
|
32
32
|
|
|
33
|
-
All elements are declared as substitutable for the abstract element any,
|
|
33
|
+
All elements are declared as substitutable for the abstract element any,
|
|
34
34
|
which means that the default type for all elements is dc:SimpleLiteral.
|
|
35
35
|
|
|
36
36
|
</xs:documentation>
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
It permits text content only with optional
|
|
50
50
|
xml:lang attribute.
|
|
51
51
|
Text is allowed because mixed="true", but sub-elements
|
|
52
|
-
are disallowed because minOccurs="0" and maxOccurs="0"
|
|
52
|
+
are disallowed because minOccurs="0" and maxOccurs="0"
|
|
53
53
|
are on the xs:any tag.
|
|
54
54
|
|
|
55
55
|
This complexType allows for restriction or extension permitting
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
<xs:annotation>
|
|
90
90
|
<xs:documentation xml:lang="en">
|
|
91
91
|
This group is included as a convenience for schema authors
|
|
92
|
-
who need to refer to all the elements in the
|
|
92
|
+
who need to refer to all the elements in the
|
|
93
93
|
http://purl.org/dc/elements/1.1/ namespace.
|
|
94
94
|
</xs:documentation>
|
|
95
95
|
</xs:annotation>
|