rubyXL-git-ref-6002046 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +20 -0
  3. data/Gemfile.lock +63 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.rdoc +197 -0
  6. data/Rakefile +58 -0
  7. data/VERSION +1 -0
  8. data/lib/rubyXL.rb +21 -0
  9. data/lib/rubyXL/cell.rb +325 -0
  10. data/lib/rubyXL/generic_storage.rb +40 -0
  11. data/lib/rubyXL/objects/border.rb +53 -0
  12. data/lib/rubyXL/objects/cell_style.rb +73 -0
  13. data/lib/rubyXL/objects/color.rb +23 -0
  14. data/lib/rubyXL/objects/column_range.rb +88 -0
  15. data/lib/rubyXL/objects/data_validation.rb +31 -0
  16. data/lib/rubyXL/objects/defined_name.rb +27 -0
  17. data/lib/rubyXL/objects/fill.rb +42 -0
  18. data/lib/rubyXL/objects/font.rb +109 -0
  19. data/lib/rubyXL/objects/formula.rb +8 -0
  20. data/lib/rubyXL/objects/ooxml_object.rb +177 -0
  21. data/lib/rubyXL/objects/reference.rb +98 -0
  22. data/lib/rubyXL/objects/sheet_view.rb +62 -0
  23. data/lib/rubyXL/objects/worksheet.rb +11 -0
  24. data/lib/rubyXL/parser.rb +307 -0
  25. data/lib/rubyXL/private_class.rb +95 -0
  26. data/lib/rubyXL/shared_strings.rb +35 -0
  27. data/lib/rubyXL/workbook.rb +342 -0
  28. data/lib/rubyXL/worksheet.rb +1118 -0
  29. data/lib/rubyXL/writer/app_writer.rb +51 -0
  30. data/lib/rubyXL/writer/calc_chain_writer.rb +18 -0
  31. data/lib/rubyXL/writer/content_types_writer.rb +113 -0
  32. data/lib/rubyXL/writer/core_writer.rb +34 -0
  33. data/lib/rubyXL/writer/generic_writer.rb +33 -0
  34. data/lib/rubyXL/writer/root_rels_writer.rb +17 -0
  35. data/lib/rubyXL/writer/shared_strings_writer.rb +21 -0
  36. data/lib/rubyXL/writer/styles_writer.rb +64 -0
  37. data/lib/rubyXL/writer/theme_writer.rb +337 -0
  38. data/lib/rubyXL/writer/workbook_rels_writer.rb +43 -0
  39. data/lib/rubyXL/writer/workbook_writer.rb +73 -0
  40. data/lib/rubyXL/writer/worksheet_writer.rb +164 -0
  41. data/lib/rubyXL/zip.rb +20 -0
  42. data/rdoc/created.rid +36 -0
  43. data/rdoc/fonts.css +167 -0
  44. data/rdoc/fonts/Lato-Light.ttf +0 -0
  45. data/rdoc/fonts/Lato-LightItalic.ttf +0 -0
  46. data/rdoc/fonts/Lato-Regular.ttf +0 -0
  47. data/rdoc/fonts/Lato-RegularItalic.ttf +0 -0
  48. data/rdoc/fonts/SourceCodePro-Bold.ttf +0 -0
  49. data/rdoc/fonts/SourceCodePro-Regular.ttf +0 -0
  50. data/rdoc/images/add.png +0 -0
  51. data/rdoc/images/arrow_up.png +0 -0
  52. data/rdoc/images/brick.png +0 -0
  53. data/rdoc/images/brick_link.png +0 -0
  54. data/rdoc/images/bug.png +0 -0
  55. data/rdoc/images/bullet_black.png +0 -0
  56. data/rdoc/images/bullet_toggle_minus.png +0 -0
  57. data/rdoc/images/bullet_toggle_plus.png +0 -0
  58. data/rdoc/images/date.png +0 -0
  59. data/rdoc/images/delete.png +0 -0
  60. data/rdoc/images/find.png +0 -0
  61. data/rdoc/images/loadingAnimation.gif +0 -0
  62. data/rdoc/images/macFFBgHack.png +0 -0
  63. data/rdoc/images/package.png +0 -0
  64. data/rdoc/images/page_green.png +0 -0
  65. data/rdoc/images/page_white_text.png +0 -0
  66. data/rdoc/images/page_white_width.png +0 -0
  67. data/rdoc/images/plugin.png +0 -0
  68. data/rdoc/images/ruby.png +0 -0
  69. data/rdoc/images/tag_blue.png +0 -0
  70. data/rdoc/images/tag_green.png +0 -0
  71. data/rdoc/images/transparent.png +0 -0
  72. data/rdoc/images/wrench.png +0 -0
  73. data/rdoc/images/wrench_orange.png +0 -0
  74. data/rdoc/images/zoom.png +0 -0
  75. data/rdoc/js/darkfish.js +140 -0
  76. data/rdoc/js/jquery.js +18 -0
  77. data/rdoc/js/navigation.js +142 -0
  78. data/rdoc/js/search.js +109 -0
  79. data/rdoc/js/search_index.js +1 -0
  80. data/rdoc/js/searcher.js +228 -0
  81. data/rdoc/rdoc.css +580 -0
  82. data/rubyXL-git-ref-6002046.gemspec +143 -0
  83. data/spec/lib/cell_spec.rb +407 -0
  84. data/spec/lib/color_spec.rb +14 -0
  85. data/spec/lib/parser_spec.rb +80 -0
  86. data/spec/lib/workbook_spec.rb +73 -0
  87. data/spec/lib/worksheet_spec.rb +1789 -0
  88. metadata +231 -0
@@ -0,0 +1,95 @@
1
+ module RubyXL
2
+ class PrivateClass
3
+ private
4
+
5
+ #validate and modify methods
6
+ def validate_horizontal_alignment(alignment)
7
+ if alignment.to_s == '' || alignment == 'center' || alignment == 'distributed' || alignment == 'justify' || alignment == 'left' || alignment == 'right'
8
+ return true
9
+ end
10
+ raise 'Only center, distributed, justify, left, and right are valid horizontal alignments'
11
+ end
12
+
13
+ def validate_vertical_alignment(alignment)
14
+ if alignment.to_s == '' || alignment == 'center' || alignment == 'distributed' || alignment == 'justify' || alignment == 'top' || alignment == 'bottom'
15
+ return true
16
+ end
17
+ raise 'Only center, distributed, justify, top, and bottom are valid vertical alignments'
18
+ end
19
+
20
+ def validate_text_wrap(wrap)
21
+ raise 'Only true or false are valid wraps' unless wrap.is_a?(FalseClass) || wrap.is_a?(TrueClass)
22
+ end
23
+
24
+ def validate_border(weight)
25
+ if weight.to_s == '' || weight == 'thin' || weight == 'thick' || weight == 'hairline' || weight == 'medium'
26
+ return true
27
+ end
28
+ raise 'Border weights must only be "hairline", "thin", "medium", or "thick"'
29
+ end
30
+
31
+ def validate_nonnegative(row_or_col)
32
+ if row_or_col < 0
33
+ raise 'Row and Column arguments must be nonnegative'
34
+ end
35
+ end
36
+
37
+ # This method checks to see if there is an equivalent xf that exists
38
+ def find_xf(workbook, xf)
39
+ workbook.cell_xfs.each_with_index { |xfs, index| return index if xfs == xf }
40
+ return nil
41
+ end
42
+
43
+ # Determines if xf exists
44
+ # If yes, return id of existing xf
45
+ # If no, appends xf to xf array
46
+ def modify_xf(workbook, xf)
47
+ existing_xf_id = find_xf(workbook, xf)
48
+ if !existing_xf_id.nil?
49
+ xf_id = existing_xf_id
50
+ else
51
+ xf.apply_font = true
52
+ xf_id = workbook.cell_xfs.size - 1
53
+ end
54
+ return xf_id
55
+ end
56
+
57
+ #modifies fill array (copies, appends, adds color and solid attribute)
58
+ #then styles array (copies, appends)
59
+ def modify_fill(workbook, style_index, rgb)
60
+ xf = workbook.cell_xfs[style_index]
61
+ new_fill = RubyXL::Fill.new(:pattern_fill =>
62
+ RubyXL::PatternFill.new(:pattern_type => 'solid', :fg_color => RubyXL::Color.new(:rgb => rgb)))
63
+ new_xf = workbook.register_new_fill(new_fill, xf)
64
+ workbook.register_new_xf(new_xf, style_index)
65
+ end
66
+
67
+ #is_horizontal is true when doing horizontal alignment,
68
+ #false when doing vertical alignment
69
+ def modify_alignment(workbook, style_index, is_horizontal, alignment)
70
+ old_xf = workbook.cell_xfs[style_index]
71
+
72
+ xf = old_xf.dup
73
+ xf.alignment ||= RubyXL::Alignment.new
74
+
75
+ if is_horizontal then xf.alignment.horizontal = alignment
76
+ else xf.alignment.vertical = alignment
77
+ end
78
+ xf.apply_alignment = true
79
+
80
+ workbook.register_new_xf(xf, style_index)
81
+ end
82
+
83
+ def modify_text_wrap(workbook, style_index, wrapText = false)
84
+ old_xf = workbook.cell_xfs[style_index]
85
+
86
+ xf = old_xf.dup
87
+ xf.alignment ||= RubyXL::Alignment.new
88
+ xf.alignment.wrap_text = wrapText
89
+ xf.apply_alignment = true
90
+
91
+ workbook.register_new_xf(xf, style_index)
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,35 @@
1
+ module RubyXL
2
+ class SharedStrings
3
+
4
+ attr_accessor :count_attr, :unique_count_attr
5
+
6
+ def initialize
7
+ # So far, going by the structure that the original creator had in mind. However,
8
+ # since the actual implementation is now extracted into a separate class,
9
+ # we will be able to transparrently change it later if needs be.
10
+ @content_by_index = []
11
+ @index_by_content = {}
12
+ @unique_count_attr = @count_attr = nil # TODO
13
+ end
14
+
15
+ def empty?
16
+ @content_by_index.empty?
17
+ end
18
+
19
+ def add(str, index)
20
+ @content_by_index[index] = str
21
+ @index_by_content[str] = index
22
+ end
23
+
24
+ def get_index(str, add_if_missing = false)
25
+ index = @index_by_content[str]
26
+ index = add(str) if index.nil? && add_if_missing
27
+ index
28
+ end
29
+
30
+ def[](index)
31
+ @content_by_index[index]
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,342 @@
1
+ require 'rubyXL/writer/generic_writer'
2
+ require 'rubyXL/writer/content_types_writer'
3
+ require 'rubyXL/writer/root_rels_writer'
4
+ require 'rubyXL/writer/app_writer'
5
+ require 'rubyXL/writer/core_writer'
6
+ require 'rubyXL/writer/theme_writer'
7
+ require 'rubyXL/writer/workbook_rels_writer'
8
+ require 'rubyXL/writer/workbook_writer'
9
+ require 'rubyXL/writer/styles_writer'
10
+ require 'rubyXL/writer/shared_strings_writer'
11
+ require 'rubyXL/writer/worksheet_writer'
12
+ require 'rubyXL/zip'
13
+ require 'rubyXL/shared_strings'
14
+ require 'date'
15
+
16
+ module RubyXL
17
+ class Workbook
18
+ include Enumerable
19
+ attr_accessor :worksheets, :filepath, :creator, :modifier, :created_at,
20
+ :modified_at, :company, :application, :appversion, :num_fmts, :fonts, :fills,
21
+ :borders, :cell_xfs, :cell_style_xfs, :cell_styles, :calc_chain, :theme,
22
+ :date1904, :media, :external_links, :external_links_rels, :style_corrector,
23
+ :drawings, :drawings_rels, :charts, :chart_rels,
24
+ :worksheet_rels, :printer_settings, :macros, :colors, :shared_strings_XML, :defined_names, :column_lookup_hash
25
+
26
+ attr_reader :shared_strings
27
+
28
+ APPLICATION = 'Microsoft Macintosh Excel'
29
+ APPVERSION = '12.0000'
30
+
31
+ def initialize(worksheets=[], filepath=nil, creator=nil, modifier=nil, created_at=nil,
32
+ company='', application=APPLICATION,
33
+ appversion=APPVERSION, date1904=0)
34
+
35
+ # Order of sheets in the +worksheets+ array corresponds to the order of pages in Excel UI.
36
+ # SheetId's, rId's, etc. are completely unrelated to ordering.
37
+ @worksheets = worksheets || []
38
+
39
+ @worksheets << Worksheet.new(self) if @worksheets.empty?
40
+
41
+ @filepath = filepath
42
+ @creator = creator
43
+ @modifier = modifier
44
+ @company = company
45
+ @application = application
46
+ @appversion = appversion
47
+ @num_fmts = []
48
+ @num_fmts_by_id = nil
49
+ @fonts = []
50
+ @fills = nil
51
+ @borders = []
52
+ @cell_xfs = []
53
+ @cell_style_xfs = []
54
+ @cell_styles = []
55
+ @shared_strings = RubyXL::SharedStrings.new
56
+ @calc_chain = nil #unnecessary?
57
+ @date1904 = date1904 > 0
58
+ @media = RubyXL::GenericStorage.new(File.join('xl', 'media')).binary
59
+ @external_links = RubyXL::GenericStorage.new(File.join('xl', 'externalLinks'))
60
+ @external_links_rels= RubyXL::GenericStorage.new(File.join('xl', 'externalLinks', '_rels'))
61
+ @style_corrector = nil
62
+ @drawings = RubyXL::GenericStorage.new(File.join('xl', 'drawings'))
63
+ @drawings_rels = RubyXL::GenericStorage.new(File.join('xl', 'drawings', '_rels'))
64
+ @charts = RubyXL::GenericStorage.new(File.join('xl', 'charts'))
65
+ @chart_rels = RubyXL::GenericStorage.new(File.join('xl', 'charts', '_rels'))
66
+ @worksheet_rels = RubyXL::GenericStorage.new(File.join('xl', 'worksheets', '_rels'))
67
+ @theme = RubyXL::GenericStorage.new(File.join('xl', 'theme'))
68
+ @printer_settings = RubyXL::GenericStorage.new(File.join('xl', 'printerSettings')).binary
69
+ @macros = RubyXL::GenericStorage.new('xl').binary
70
+ @colors = {}
71
+ @shared_strings_XML = nil
72
+ @defined_names = []
73
+ @column_lookup_hash = {}
74
+
75
+ begin
76
+ @created_at = DateTime.parse(created_at).strftime('%Y-%m-%dT%TZ')
77
+ rescue
78
+ t = Time.now
79
+ @created_at = t.strftime('%Y-%m-%dT%TZ')
80
+ end
81
+ @modified_at = @created_at
82
+
83
+ fill_styles()
84
+ fill_shared_strings()
85
+ end
86
+
87
+ # Finds worksheet by its name or numerical index
88
+ def [](ind)
89
+ case ind
90
+ when Integer then worksheets[ind]
91
+ when String then worksheets.find { |ws| ws.sheet_name == ind }
92
+ end
93
+ end
94
+
95
+ # Create new simple worksheet and add it to the workbook worksheets
96
+ #
97
+ # @param [String] The name for the new worksheet
98
+ def add_worksheet(name = nil)
99
+ new_worksheet = Worksheet.new(self, name)
100
+ worksheets << new_worksheet
101
+ new_worksheet
102
+ end
103
+
104
+ def each
105
+ worksheets.each{|i| yield i}
106
+ end
107
+
108
+ def num_fmts_by_id
109
+ return @num_fmts_by_id unless @num_fmts_by_id.nil?
110
+
111
+ @num_fmts_by_id = {}
112
+
113
+ num_fmts.each { |fmt| @num_fmts_by_id[fmt.num_fmt_id] = fmt }
114
+
115
+ @num_fmts_by_id
116
+ end
117
+
118
+ #filepath of xlsx file (including file itself)
119
+ def write(filepath = @filepath)
120
+ validate_before_write
121
+
122
+ extension = File.extname(filepath)
123
+ unless %w{.xlsx .xlsm}.include?(extension)
124
+ raise "Only xlsx and xlsm files are supported. Unsupported extension: #{extension}"
125
+ end
126
+
127
+ dirpath = File.dirname(filepath)
128
+ temppath = File.join(dirpath, Dir::Tmpname.make_tmpname([ File.basename(filepath), '.tmp' ], nil))
129
+ FileUtils.mkdir_p(temppath)
130
+ zippath = File.join(temppath, 'file.zip')
131
+
132
+ Zip::File.open(zippath, Zip::File::CREATE) { |zipfile|
133
+ [ Writer::ContentTypesWriter, Writer::RootRelsWriter, Writer::AppWriter, Writer::CoreWriter,
134
+ Writer::ThemeWriter, Writer::WorkbookRelsWriter, Writer::WorkbookWriter, Writer::StylesWriter
135
+ ].each { |writer_class| writer_class.new(self).add_to_zip(zipfile) }
136
+
137
+ Writer::SharedStringsWriter.new(self).add_to_zip(zipfile) unless @shared_strings.empty?
138
+
139
+ [ @media, @external_links, @external_links_rels,
140
+ @drawings, @drawings_rels, @charts, @chart_rels,
141
+ @printer_settings, @worksheet_rels, @macros ].each { |s| s.add_to_zip(zipfile) }
142
+
143
+ @worksheets.each_index { |i| Writer::WorksheetWriter.new(self, i).add_to_zip(zipfile) }
144
+ }
145
+
146
+ FileUtils.mv(zippath, filepath)
147
+ FileUtils.rm_rf(temppath) if File.exist?(filepath)
148
+
149
+ return filepath
150
+ end
151
+
152
+ def base_date
153
+ if @date1904 then
154
+ Date.new(1904, 1, 1)
155
+ else
156
+ # Subtracting one day to accomodate for erroneous 1900 leap year compatibility only for 1900 based dates
157
+ Date.new(1899, 12, 31) - 1
158
+ end
159
+ end
160
+ private :base_date
161
+
162
+ def date_to_num(date)
163
+ date && (date.ajd - base_date().ajd).to_i
164
+ end
165
+
166
+ def num_to_date(num)
167
+ num && (base_date + num)
168
+ end
169
+
170
+ def date_num_fmt?(num_fmt)
171
+ @num_fmt_date_hash ||= {}
172
+ if @num_fmt_date_hash[num_fmt].nil?
173
+ @num_fmt_date_hash[num_fmt] = is_date_format?(num_fmt)
174
+ end
175
+ return @num_fmt_date_hash[num_fmt]
176
+ end
177
+
178
+ def is_date_format?(num_fmt)
179
+ num_fmt = num_fmt.downcase
180
+ skip_chars = ['$', '-', '+', '/', '(', ')', ':', ' ']
181
+ num_chars = ['0', '#', '?']
182
+ non_date_formats = ['0.00e+00', '##0.0e+0', 'general', '@']
183
+ date_chars = ['y','m','d','h','s']
184
+
185
+ state = 0
186
+ s = ''
187
+
188
+ num_fmt.split(//).each do |c|
189
+ case state
190
+ when 0 then
191
+ if c == '"'
192
+ state = 1
193
+ elsif ['\\', '_', '*'].include?(c)
194
+ state = 2
195
+ elsif skip_chars.include?(c)
196
+ next
197
+ else
198
+ s << c
199
+ end
200
+ when 1 then
201
+ state = 0 if c == '"'
202
+ when 2 then
203
+ state = 0
204
+ end
205
+ end
206
+
207
+ s.gsub!(/\[[^\]]*\]/, '')
208
+
209
+ return false if non_date_formats.include?(s)
210
+
211
+ separator = ';'
212
+ got_sep = 0
213
+ date_count = 0
214
+ num_count = 0
215
+
216
+ s.split(//).each do |c|
217
+ if date_chars.include?(c)
218
+ date_count += 1
219
+ elsif num_chars.include?(c)
220
+ num_count += 1
221
+ elsif c == separator
222
+ got_sep = 1
223
+ end
224
+ end
225
+
226
+ if date_count > 0 && num_count == 0
227
+ return true
228
+ elsif num_count > 0 && date_count == 0
229
+ return false
230
+ elsif date_count
231
+ # ambiguous result
232
+ elsif got_sep == 0
233
+ # constant result
234
+ end
235
+
236
+ return date_count > num_count
237
+ end
238
+
239
+ def get_fill_color(xf)
240
+ fill = @fills[xf.fill_id]
241
+ pattern = fill && fill.pattern_fill
242
+ color = pattern && pattern.fg_color
243
+ color && color.rgb || 'ffffff'
244
+ end
245
+
246
+ def register_new_fill(new_fill, old_xf)
247
+ new_xf = old_xf.dup
248
+
249
+ unless fills[old_xf.fill_id].count == 1 && old_xf.fill_id > 2 # If the old fill is not used anymore, just replace it
250
+ new_xf.fill_id = fills.find_index { |x| x == new_fill } # Use existing fill, if it exists
251
+ new_xf.fill_id ||= fills.size # If this fill has never existed before, add it to collection.
252
+ end
253
+
254
+ fills[old_xf.fill_id].count -= 1
255
+ new_fill.count += 1
256
+ fills[new_xf.fill_id] = new_fill
257
+
258
+ new_xf.apply_fill = true
259
+ new_xf
260
+ end
261
+
262
+ def register_new_font(new_font, old_xf)
263
+ new_xf = old_xf.dup
264
+
265
+ unless fonts[old_xf.font_id].count == 1 && old_xf.font_id > 1 # If the old font is not used anymore, just replace it
266
+ new_xf.font_id = fonts.find_index { |x| x == new_font } # Use existing font, if it exists
267
+ new_xf.font_id ||= fonts.size # If this font has never existed before, add it to collection.
268
+ end
269
+
270
+ fonts[old_xf.font_id].count -= 1
271
+ new_font.count += 1
272
+ fonts[new_xf.font_id] = new_font
273
+
274
+ new_xf.apply_font = true
275
+ new_xf
276
+ end
277
+
278
+ def register_new_border(new_border, old_xf)
279
+ new_xf = old_xf.dup
280
+
281
+ unless borders[old_xf.border_id].count == 1 && old_xf.border_id > 0 # If the old border not used anymore, just replace it
282
+ new_xf.border_id = borders.find_index { |x| x == new_border } # Use existing border, if it exists
283
+ new_xf.border_id ||= borders.size # If this border has never existed before, add it to collection.
284
+ end
285
+
286
+ borders[old_xf.border_id].count -= 1
287
+ new_border.count += 1
288
+ borders[new_xf.border_id] = new_border
289
+
290
+ new_xf.apply_border = true
291
+ new_xf
292
+ end
293
+
294
+ def register_new_xf(new_xf, old_style_index)
295
+ new_xf_id = cell_xfs.find_index { |xf| xf == new_xf } # Use existing XF, if it exists
296
+ new_xf_id ||= cell_xfs.size # If this XF has never existed before, add it to collection.
297
+
298
+ cell_xfs[old_style_index].count -= 1
299
+ new_xf.count += 1
300
+ cell_xfs[new_xf_id] = new_xf
301
+
302
+ new_xf_id
303
+ end
304
+
305
+ private
306
+
307
+ # Do not change. Excel requires that some of these styles be default,
308
+ # and will simply assume that the 0 and 1 indexed fonts are the default values.
309
+ def fill_styles()
310
+
311
+ @fonts = [ RubyXL::Font.new(:name => RubyXL::StringValue.new(:val => 'Verdana'), :sz => RubyXL::FloatValue.new(:val => 10) ),
312
+ RubyXL::Font.new(:name => RubyXL::StringValue.new(:val => 'Verdana'), :sz => RubyXL::FloatValue.new(:val => 8) ) ]
313
+
314
+ @fills = [ RubyXL::Fill.new(:pattern_fill => RubyXL::PatternFill.new(:pattern_type => 'none')),
315
+ RubyXL::Fill.new(:pattern_fill => RubyXL::PatternFill.new(:pattern_type => 'gray125')) ]
316
+
317
+ @borders = [ RubyXL::Border.new ]
318
+
319
+ @cell_style_xfs = [ RubyXL::XF.new(:num_fmt_id => 0, :font_id => 0, :fill_id => 0, :border_id => 0) ]
320
+ @cell_xfs = [ RubyXL::XF.new(:num_fmt_id => 0, :font_id => 0, :fill_id => 0, :border_id => 0, :xfId => 0) ]
321
+ @cell_styles = [ RubyXL::CellStyle.new({ :builtin_id => 0, :name => 'Normal', :xf_id => 0 }) ]
322
+ end
323
+
324
+
325
+ #fills shared strings hash, contains each unique string
326
+ def fill_shared_strings()
327
+ @worksheets.compact.each { |sheet|
328
+ sheet.sheet_data.each { |row|
329
+ row.each { |cell|
330
+ if cell && cell.value && cell.datatype == RubyXL::Cell::SHARED_STRING then
331
+ get_index(cell.value.to_s, :add_if_missing)
332
+ end
333
+ }
334
+ }
335
+ }
336
+ end
337
+
338
+ def validate_before_write
339
+ ## TODO CHECK IF STYLE IS OK if not raise
340
+ end
341
+ end
342
+ end