rubyXL 1.2.10 → 2.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.
Files changed (101) hide show
  1. data/Gemfile +14 -10
  2. data/Gemfile.lock +80 -21
  3. data/LICENSE.txt +1 -1
  4. data/README.rdoc +88 -82
  5. data/Rakefile +7 -2
  6. data/VERSION +1 -1
  7. data/lib/rubyXL.rb +13 -7
  8. data/lib/rubyXL/cell.rb +108 -268
  9. data/lib/rubyXL/generic_storage.rb +40 -0
  10. data/lib/rubyXL/objects/border.rb +66 -0
  11. data/lib/rubyXL/objects/calculation_chain.rb +28 -0
  12. data/lib/rubyXL/objects/cell_style.rb +75 -0
  13. data/lib/rubyXL/objects/color.rb +25 -0
  14. data/lib/rubyXL/objects/column_range.rb +74 -0
  15. data/lib/rubyXL/objects/container_nodes.rb +122 -0
  16. data/lib/rubyXL/objects/data_validation.rb +43 -0
  17. data/lib/rubyXL/objects/document_properties.rb +76 -0
  18. data/lib/rubyXL/objects/extensions.rb +36 -0
  19. data/lib/rubyXL/objects/fill.rb +57 -0
  20. data/lib/rubyXL/objects/font.rb +111 -0
  21. data/lib/rubyXL/objects/formula.rb +24 -0
  22. data/lib/rubyXL/objects/ooxml_object.rb +295 -0
  23. data/lib/rubyXL/objects/reference.rb +110 -0
  24. data/lib/rubyXL/objects/relationships.rb +59 -0
  25. data/lib/rubyXL/objects/shared_strings.rb +57 -0
  26. data/lib/rubyXL/objects/sheet_data.rb +149 -0
  27. data/lib/rubyXL/objects/sheet_view.rb +71 -0
  28. data/lib/rubyXL/objects/stylesheet.rb +200 -0
  29. data/lib/rubyXL/objects/text.rb +87 -0
  30. data/lib/rubyXL/objects/theme.rb +64 -0
  31. data/lib/rubyXL/objects/workbook.rb +233 -0
  32. data/lib/rubyXL/objects/worksheet.rb +485 -0
  33. data/lib/rubyXL/parser.rb +78 -442
  34. data/lib/rubyXL/workbook.rb +216 -385
  35. data/lib/rubyXL/worksheet.rb +509 -1062
  36. data/lib/rubyXL/writer/content_types_writer.rb +104 -68
  37. data/lib/rubyXL/writer/core_writer.rb +26 -43
  38. data/lib/rubyXL/writer/generic_writer.rb +43 -0
  39. data/lib/rubyXL/writer/root_rels_writer.rb +11 -19
  40. data/lib/rubyXL/writer/styles_writer.rb +6 -398
  41. data/lib/rubyXL/writer/theme_writer.rb +321 -327
  42. data/lib/rubyXL/writer/workbook_writer.rb +63 -67
  43. data/lib/rubyXL/writer/worksheet_writer.rb +29 -218
  44. data/rdoc/created.rid +39 -0
  45. data/rdoc/fonts.css +167 -0
  46. data/rdoc/fonts/Lato-Light.ttf +0 -0
  47. data/rdoc/fonts/Lato-LightItalic.ttf +0 -0
  48. data/rdoc/fonts/Lato-Regular.ttf +0 -0
  49. data/rdoc/fonts/Lato-RegularItalic.ttf +0 -0
  50. data/rdoc/fonts/SourceCodePro-Bold.ttf +0 -0
  51. data/rdoc/fonts/SourceCodePro-Regular.ttf +0 -0
  52. data/rdoc/images/add.png +0 -0
  53. data/rdoc/images/arrow_up.png +0 -0
  54. data/rdoc/images/brick.png +0 -0
  55. data/rdoc/images/brick_link.png +0 -0
  56. data/rdoc/images/bug.png +0 -0
  57. data/rdoc/images/bullet_black.png +0 -0
  58. data/rdoc/images/bullet_toggle_minus.png +0 -0
  59. data/rdoc/images/bullet_toggle_plus.png +0 -0
  60. data/rdoc/images/date.png +0 -0
  61. data/rdoc/images/delete.png +0 -0
  62. data/rdoc/images/find.png +0 -0
  63. data/rdoc/images/loadingAnimation.gif +0 -0
  64. data/rdoc/images/macFFBgHack.png +0 -0
  65. data/rdoc/images/package.png +0 -0
  66. data/rdoc/images/page_green.png +0 -0
  67. data/rdoc/images/page_white_text.png +0 -0
  68. data/rdoc/images/page_white_width.png +0 -0
  69. data/rdoc/images/plugin.png +0 -0
  70. data/rdoc/images/ruby.png +0 -0
  71. data/rdoc/images/tag_blue.png +0 -0
  72. data/rdoc/images/tag_green.png +0 -0
  73. data/rdoc/images/transparent.png +0 -0
  74. data/rdoc/images/wrench.png +0 -0
  75. data/rdoc/images/wrench_orange.png +0 -0
  76. data/rdoc/images/zoom.png +0 -0
  77. data/rdoc/js/darkfish.js +140 -0
  78. data/rdoc/js/jquery.js +18 -0
  79. data/rdoc/js/navigation.js +142 -0
  80. data/rdoc/js/search.js +109 -0
  81. data/rdoc/js/search_index.js +1 -0
  82. data/rdoc/js/searcher.js +228 -0
  83. data/rdoc/rdoc.css +580 -0
  84. data/rubyXL.gemspec +90 -34
  85. data/spec/lib/cell_spec.rb +29 -59
  86. data/spec/lib/parser_spec.rb +35 -19
  87. data/spec/lib/reference_spec.rb +29 -0
  88. data/spec/lib/stylesheet_spec.rb +29 -0
  89. data/spec/lib/workbook_spec.rb +22 -17
  90. data/spec/lib/worksheet_spec.rb +47 -202
  91. metadata +185 -148
  92. data/lib/.DS_Store +0 -0
  93. data/lib/rubyXL/Hash.rb +0 -60
  94. data/lib/rubyXL/color.rb +0 -14
  95. data/lib/rubyXL/private_class.rb +0 -265
  96. data/lib/rubyXL/writer/app_writer.rb +0 -62
  97. data/lib/rubyXL/writer/calc_chain_writer.rb +0 -33
  98. data/lib/rubyXL/writer/shared_strings_writer.rb +0 -30
  99. data/lib/rubyXL/writer/workbook_rels_writer.rb +0 -59
  100. data/lib/rubyXL/zip.rb +0 -20
  101. data/spec/lib/hash_spec.rb +0 -28
data/lib/rubyXL/parser.rb CHANGED
@@ -1,467 +1,103 @@
1
1
  require 'rubygems'
2
2
  require 'nokogiri'
3
- require 'zip/zip' #rubyzip
4
- require File.expand_path(File.join(File.dirname(__FILE__),'Hash'))
3
+ require 'tmpdir'
4
+ require 'rubyXL/generic_storage'
5
5
 
6
6
  module RubyXL
7
7
 
8
8
  class Parser
9
- attr_reader :data_only, :num_sheets
10
- @@parsed_column_hash ={}
11
- # converts cell string (such as "AA1") to matrix indices
12
- def Parser.convert_to_index(cell_string)
13
- index = [-1,-1]
14
- if(cell_string =~ /^([A-Z]+)(\d+)$/)
15
-
16
- one = $1
17
- row = $2.to_i - 1 #-1 for 0 indexing
18
- col = 0
19
- i = 0
20
- if @@parsed_column_hash[one].nil?
21
- two = one.reverse #because of 26^i calculation
22
- two.each_byte do |c|
23
- int_val = c - 64 #converts A to 1
24
- col += int_val * 26**(i)
25
- i=i+1
26
- end
27
- @@parsed_column_hash[one] = col
28
- else
29
- col = @@parsed_column_hash[one]
30
- end
31
- col -= 1 #zer0 index
32
- index[0] = row
33
- index[1] = col
34
- end
35
- return index
9
+ def self.parse(file_path, opts = {})
10
+ self.new(opts).parse(file_path)
36
11
  end
37
12
 
38
-
39
- # data_only allows only the sheet data to be parsed, so as to speed up parsing
13
+ # +:data_only+ allows only the sheet data to be parsed, so as to speed up parsing
40
14
  # However, using this option will result in date-formatted cells being interpreted as numbers
41
- def Parser.parse(file_path, opts = {})
42
-
43
- # options handling
44
- @data_only = opts.is_a?(TrueClass)||!!opts[:data_only]
45
- skip_filename_check = !!opts[:skip_filename_check]
46
-
47
- files = Parser.decompress(file_path, skip_filename_check)
48
- wb = Parser.fill_workbook(file_path, files)
49
-
50
- if(files['sharedString'] != nil)
51
- wb.num_strings = Integer(files['sharedString'].css('sst').attribute('count').value())
52
- wb.size = Integer(files['sharedString'].css('sst').attribute('uniqueCount').value())
53
-
54
- files['sharedString'].css('si').each do |node|
55
- unless node.css('r').empty?
56
- text = node.css('r t').children.to_a.join
57
- node.children.remove
58
- node << "<t xml:space=\"preserve\">#{text}</t>"
59
- end
60
- end
61
-
62
- string_nodes = files['sharedString'].css('si t')
63
- wb.shared_strings = {}
64
- string_nodes.each_with_index do |node,i|
65
- string = node.children.to_s
66
- wb.shared_strings[i] = string
67
- wb.shared_strings[string] = i
68
- end
69
- end
70
- #styles are needed for formatting reasons as that is how dates are determined
71
- styles = files['styles'].css('cellXfs xf')
72
- style_hash = Hash.xml_node_to_hash(files['styles'].root)
73
- fill_styles(wb,style_hash)
74
-
75
- #will be nil if these files do not exist
76
- wb.external_links = files['externalLinks']
77
- wb.drawings = files['drawings']
78
- wb.printer_settings = files['printerSettings']
79
- wb.worksheet_rels = files['worksheetRels']
80
- wb.macros = files['vbaProject']
81
-
82
- #for each worksheet:
83
- #1. find the dimensions of the data matrix
84
- #2. Fill in the matrix with data from worksheet/shared_string files
85
- #3. Apply styles
86
- wb.worksheets.each_index do |i|
87
- Parser.fill_worksheet(wb,i,files,wb.shared_strings)
88
- end
89
-
90
- return wb
15
+ def initialize(opts = {})
16
+ @data_only = opts.is_a?(TrueClass) || opts[:data_only]
17
+ @skip_filename_check = opts[:skip_filename_check]
91
18
  end
92
19
 
93
- private
94
-
95
- #fills hashes for various styles
96
- def Parser.fill_styles(wb,style_hash)
97
- ###NUM FORMATS###
98
- if style_hash[:numFmts].nil?
99
- style_hash[:numFmts] = {:attributes => {:count => 0}, :numFmt => []}
100
- elsif style_hash[:numFmts][:attributes][:count]==1
101
- style_hash[:numFmts][:numFmt] = [style_hash[:numFmts][:numFmt]]
102
- end
103
- wb.num_fmts = style_hash[:numFmts]
104
-
105
- ###FONTS###
106
- wb.fonts = {}
107
- if style_hash[:fonts][:attributes][:count]==1
108
- style_hash[:fonts][:font] = [style_hash[:fonts][:font]]
109
- end
110
-
111
- style_hash[:fonts][:font].each_with_index do |f,i|
112
- wb.fonts[i.to_s] = {:font=>f,:count=>0}
113
- end
114
-
115
- ###FILLS###
116
- wb.fills = {}
117
- if style_hash[:fills][:attributes][:count]==1
118
- style_hash[:fills][:fill] = [style_hash[:fills][:fill]]
119
- end
120
-
121
- style_hash[:fills][:fill].each_with_index do |f,i|
122
- wb.fills[i.to_s] = {:fill=>f,:count=>0}
123
- end
124
-
125
- ###BORDERS###
126
- wb.borders = {}
127
- if style_hash[:borders][:attributes][:count] == 1
128
- style_hash[:borders][:border] = [style_hash[:borders][:border]]
129
- end
130
-
131
- style_hash[:borders][:border].each_with_index do |b,i|
132
- wb.borders[i.to_s] = {:border=>b, :count=>0}
133
- end
134
-
135
- wb.cell_style_xfs = style_hash[:cellStyleXfs]
136
- wb.cell_xfs = style_hash[:cellXfs]
137
- wb.cell_styles = style_hash[:cellStyles]
138
-
139
- wb.colors = style_hash[:colors]
140
-
141
- #fills out count information for each font, fill, and border
142
- if wb.cell_xfs[:xf].is_a?(::Hash)
143
- wb.cell_xfs[:xf] = [wb.cell_xfs[:xf]]
144
- end
145
- wb.cell_xfs[:xf].each do |style|
146
- id = style[:attributes][:fontId].to_s
147
- unless id.nil?
148
- wb.fonts[id][:count] += 1
149
- end
150
-
151
- id = style[:attributes][:fillId].to_s
152
- unless id.nil?
153
- wb.fills[id][:count] += 1
154
- end
155
-
156
- id = style[:attributes][:borderId].to_s
157
- unless id.nil?
158
- wb.borders[id][:count] += 1
159
- end
160
- end
161
- end
162
-
163
- # i is the sheet number
164
- # files is the hash which includes information for each worksheet
165
- # shared_strings has group of indexed strings which the cells reference
166
- def Parser.fill_worksheet(wb,i,files,shared_strings)
167
- wb.worksheets[i] = Parser.create_matrix(wb, i, files)
168
- j = i+1
169
-
170
- namespaces = files[j].root.namespaces()
171
- unless @data_only
172
- sheet_views_node= files[j].xpath('/xmlns:worksheet/xmlns:sheetViews[xmlns:sheetView]',namespaces).first
173
- wb.worksheets[i].sheet_view = Hash.xml_node_to_hash(sheet_views_node)[:sheetView]
174
-
175
- ##col styles##
176
- cols_node_set = files[j].xpath('/xmlns:worksheet/xmlns:cols',namespaces)
177
- unless cols_node_set.empty?
178
- wb.worksheets[i].cols= Hash.xml_node_to_hash(cols_node_set.first)[:col]
179
- end
180
- ##end col styles##
181
-
182
- ##merge_cells##
183
- merge_cells_node = files[j].xpath('/xmlns:worksheet/xmlns:mergeCells[xmlns:mergeCell]',namespaces)
184
- unless merge_cells_node.empty?
185
- wb.worksheets[i].merged_cells = Hash.xml_node_to_hash(merge_cells_node.first)[:mergeCell]
186
- end
187
- ##end merge_cells##
188
-
189
- ##sheet_view pane##
190
- pane_data = wb.worksheets[i].sheet_view[:pane]
191
- wb.worksheets[i].pane = pane_data
192
- ##end sheet_view pane##
193
-
194
- ##data_validation##
195
- data_validations_node = files[j].xpath('/xmlns:worksheet/xmlns:dataValidations[xmlns:dataValidation]',namespaces)
196
- unless data_validations_node.empty?
197
- wb.worksheets[i].validations = Hash.xml_node_to_hash(data_validations_node.first)[:dataValidation]
198
- else
199
- wb.worksheets[i].validations=nil
200
- end
201
- ##end data_validation##
202
-
203
- #extLst
204
- ext_list_node=files[j].xpath('/xmlns:worksheet/xmlns:extLst',namespaces)
205
- unless ext_list_node.empty?
206
- wb.worksheets[i].extLst = Hash.xml_node_to_hash(ext_list_node.first)
207
- else
208
- wb.worksheets[i].extLst=nil
209
- end
210
- #extLst
211
-
212
- ##legacy drawing##
213
- legacy_drawing_node = files[j].xpath('/xmlns:worksheet/xmlns:legacyDrawing',namespaces)
214
- unless legacy_drawing_node.empty?
215
- wb.worksheets[i].legacy_drawing = Hash.xml_node_to_hash(legacy_drawing_node.first)
216
- else
217
- wb.worksheets[i].legacy_drawing = nil
218
- end
219
- ##end legacy drawing
220
- end
221
-
222
-
223
- row_data = files[j].xpath('/xmlns:worksheet/xmlns:sheetData/xmlns:row[xmlns:c[xmlns:v]]',namespaces)
224
- row_data.each do |row|
225
- unless @data_only
226
- ##row styles##
227
- row_style = '0'
228
- row_attributes = row.attributes
229
- unless row_attributes['s'].nil?
230
- row_style = row_attributes['s'].value
231
- end
232
-
233
- wb.worksheets[i].row_styles[row_attributes['r'].content] = { :style => row_style }
234
-
235
- if !row_attributes['ht'].nil? && (!row_attributes['ht'].content.nil? || row_attributes['ht'].content.strip != "" )
236
- wb.worksheets[i].change_row_height(Integer(row_attributes['r'].content)-1,
237
- Float(row_attributes['ht'].content))
238
- end
239
- ##end row styles##
240
- end
241
-
242
- unless @data_only
243
- c_row = row.search('./xmlns:c')
244
- else
245
- c_row = row.search('./xmlns:c[xmlns:v[text()]]')
246
- end
247
- c_row.each do |value|
248
- #attributes is from the excel cell(c) and is basically location information and style and type
249
- value_attributes= value.attributes
250
- # r attribute contains the location like A1
251
- cell_index = Parser.convert_to_index(value_attributes['r'].content)
252
- style_index = 0
253
-
254
- # t is optional and contains the type of the cell
255
- data_type = value_attributes['t'].content if value_attributes['t']
256
- element_hash ={}
257
- value.children.each do |node|
258
- element_hash["#{node.name()}_element"]=node
259
- end
260
- # v is the value element that is part of the cell
261
- if element_hash["v_element"]
262
- v_element_content = element_hash["v_element"].content
263
- else
264
- v_element_content=""
265
- end
266
- if v_element_content =="" #no data
267
- cell_data = nil
268
- elsif data_type == 's' #shared string
269
- str_index = Integer(v_element_content)
270
- cell_data = shared_strings[str_index].to_s
271
- elsif data_type=='str' #raw string
272
- cell_data = v_element_content
273
- elsif data_type=='e' #error
274
- cell_data = v_element_content
275
- else# (value.css('v').to_s != "") && (value.css('v').children.to_s != "") #is number
276
- data_type = ''
277
- if(v_element_content =~ /\./ or v_element_content =~ /\d+e\-?\d+/i) #is float
278
- cell_data = Float(v_element_content)
279
- else
280
- cell_data = Integer(v_element_content)
281
- end
282
- end
283
- # f is the formula element
284
- cell_formula = nil
285
- fmla_css = element_hash["f_element"]
286
- if fmla_css && fmla_css.content
287
- fmla_css_content= fmla_css.content
288
- if(fmla_css_content != "")
289
- cell_formula = fmla_css_content
290
- cell_formula_attr = {}
291
- fmla_css_attributes = fmla_css.attributes
292
- cell_formula_attr['t'] = fmla_css_attributes['t'].content if fmla_css_attributes['t']
293
- cell_formula_attr['ref'] = fmla_css_attributes['ref'].content if fmla_css_attributes['ref']
294
- cell_formula_attr['si'] = fmla_css_attributes['si'].content if fmla_css_attributes['si']
295
- end
296
- end
297
-
298
- style_index = value['s'].to_i #nil goes to 0 (default)
299
-
300
- wb.worksheets[i].sheet_data[cell_index[0]][cell_index[1]] =
301
- Cell.new(wb.worksheets[i],cell_index[0],cell_index[1],cell_data,cell_formula,
302
- data_type,style_index,cell_formula_attr)
303
- cell = wb.worksheets[i].sheet_data[cell_index[0]][cell_index[1]]
304
- end
305
- end
20
+ def data_only
21
+ @data_only = true
22
+ self
306
23
  end
307
24
 
308
- def Parser.decompress(file_path, skip_filename_check = false)
309
- #ensures it is an xlsx/xlsm file
310
- if(file_path =~ /(.+)\.xls(x|m)/)
311
- dir_path = $1.to_s
312
- else
313
- if skip_filename_check
314
- dir_path = file_path
315
- else
316
- raise 'Not .xlsx or .xlsm excel file'
317
- end
318
- end
25
+ def parse(xl_file_path, opts = {})
26
+ raise 'Not .xlsx or .xlsm excel file' unless @skip_filename_check ||
27
+ %w{.xlsx .xlsm}.include?(File.extname(xl_file_path))
319
28
 
320
- dir_path = File.join(File.dirname(dir_path), make_safe_name(Time.now.to_s))
321
- #copies excel file to zip file in same directory
322
- zip_path = dir_path + '.zip'
29
+ dir_path = File.join(File.dirname(xl_file_path), Dir::Tmpname.make_tmpname(['rubyXL', '.tmp'], nil))
323
30
 
324
- FileUtils.cp(file_path,zip_path)
31
+ ::Zip::File.open(xl_file_path) { |zip_file|
32
+ zip_file.each { |f|
33
+ fpath = File.join(dir_path, f.name)
34
+ FileUtils.mkdir_p(File.dirname(fpath))
35
+ zip_file.extract(f, fpath) unless File.exist?(fpath)
36
+ }
37
+ }
325
38
 
326
- MyZip.new.unzip(zip_path,dir_path)
327
- File.delete(zip_path)
39
+ workbook_file = Nokogiri::XML.parse(File.open(File.join(dir_path, 'xl', 'workbook.xml'), 'r'))
328
40
 
329
- files = Hash.new
41
+ wb = RubyXL::Workbook.parse(workbook_file)
42
+ wb.filepath = xl_file_path
330
43
 
331
- files['app'] = Nokogiri::XML.parse(File.open(File.join(dir_path,'docProps','app.xml'),'r'))
332
- files['core'] = Nokogiri::XML.parse(File.open(File.join(dir_path,'docProps','core.xml'),'r'))
333
-
334
- files['workbook'] = Nokogiri::XML.parse(File.open(File.join(dir_path,'xl','workbook.xml'),'r'))
335
-
336
- if(File.exist?(File.join(dir_path,'xl','sharedStrings.xml')))
337
- files['sharedString'] = Nokogiri::XML.parse(File.open(File.join(dir_path,'xl','sharedStrings.xml'),'r'))
338
- end
44
+ wb.relationship_container = RubyXL::WorkbookRelationships.parse_file(dir_path)
339
45
 
340
46
  unless @data_only
341
- #preserves external links
342
- if File.directory?(File.join(dir_path,'xl','externalLinks'))
343
- files['externalLinks'] = {}
344
- ext_links_path = File.join(dir_path,'xl','externalLinks')
345
- FileUtils.mkdir_p(ext_links_path)
346
- files['externalLinks']['rels'] = []
347
- dir = Dir.new(ext_links_path).entries.reject {|f| [".", "..", ".DS_Store", "_rels"].include? f}
348
-
349
- dir.each_with_index do |link,i|
350
- files['externalLinks'][i+1] = File.read(File.join(ext_links_path,link))
351
- end
352
-
353
- if File.directory?(File.join(ext_links_path,'_rels'))
354
- dir = Dir.new(File.join(ext_links_path,'_rels')).entries.reject{|f| [".","..",".DS_Store"].include? f}
355
- dir.each_with_index do |rel,i|
356
- files['externalLinks']['rels'][i+1] = File.read(File.join(ext_links_path,'_rels',rel))
357
- end
358
- end
359
- end
360
-
361
- if File.directory?(File.join(dir_path,'xl','drawings'))
362
- files['drawings'] = {}
363
- drawings_path = File.join(dir_path,'xl','drawings','_rels')
364
- FileUtils.mkdir_p(drawings_path)
365
- dir = Dir.new(drawings_path).entries.reject {|f| [".", "..", ".DS_Store"].include? f}
366
- dir.each_with_index do |draw,i|
367
- files['drawings'][i+1] = File.read(File.join(drawings_path,draw))
368
- end
369
- end
370
-
371
- if File.directory?(File.join(dir_path,'xl','printerSettings'))
372
- files['printerSettings'] = {}
373
- printer_path = File.join(dir_path,'xl','printerSettings')
374
- FileUtils.mkdir_p(printer_path)
375
- dir = Dir.new(printer_path).entries.reject {|f| [".","..",".DS_Store"].include? f}
376
-
377
- dir.each_with_index do |print, i|
378
- files['printerSettings'][i+1] = File.open(File.join(printer_path,print), 'rb').read
379
- end
380
- end
381
-
382
- if File.directory?(File.join(dir_path,"xl",'worksheets','_rels'))
383
- files['worksheetRels'] = {}
384
- worksheet_rels_path = File.join(dir_path,'xl','worksheets','_rels')
385
- FileUtils.mkdir_p(worksheet_rels_path)
386
- dir = Dir.new(worksheet_rels_path).entries.reject {|f| [".","..",".DS_Store"].include? f}
387
- dir.each_with_index do |rel, i|
388
- files['worksheetRels'][i+1] = File.read(File.join(worksheet_rels_path,rel))
389
- end
390
- end
391
-
392
- if File.exist?(File.join(dir_path,'xl','vbaProject.bin'))
393
- files['vbaProject'] = File.open(File.join(dir_path,"xl","vbaProject.bin"),'rb').read
394
- end
395
- end
396
- files['styles'] = Nokogiri::XML.parse(File.open(File.join(dir_path,'xl','styles.xml'),'r'))
397
- @num_sheets = files['workbook'].css('sheets').children.size
398
- @num_sheets = Integer(@num_sheets)
47
+ wb.media = RubyXL::GenericStorage.new(File.join('xl', 'media')).binary.load_dir(dir_path)
48
+ wb.external_links = RubyXL::GenericStorage.new(File.join('xl', 'externalLinks')).load_dir(dir_path)
49
+ wb.external_links_rels = RubyXL::GenericStorage.new(File.join('xl', 'externalLinks', '_rels')).load_dir(dir_path)
50
+ wb.drawings = RubyXL::GenericStorage.new(File.join('xl', 'drawings')).load_dir(dir_path)
51
+ wb.drawings_rels = RubyXL::GenericStorage.new(File.join('xl', 'drawings', '_rels')).load_dir(dir_path)
52
+ wb.charts = RubyXL::GenericStorage.new(File.join('xl', 'charts')).load_dir(dir_path)
53
+ wb.chart_rels = RubyXL::GenericStorage.new(File.join('xl', 'charts', '_rels')).load_dir(dir_path)
54
+ wb.printer_settings = RubyXL::GenericStorage.new(File.join('xl', 'printerSettings')).binary.load_dir(dir_path)
55
+ wb.worksheet_rels = RubyXL::GenericStorage.new(File.join('xl', 'worksheets', '_rels')).load_dir(dir_path)
56
+ wb.macros = RubyXL::GenericStorage.new('xl').binary.load_file(dir_path, 'vbaProject.bin')
57
+ wb.theme = RubyXL::GenericStorage.new(File.join('xl', 'theme')).load_file(dir_path, 'theme1.xml')
58
+
59
+ core_file = Nokogiri::XML.parse(File.open(File.join(dir_path, 'docProps', 'core.xml'), 'r'))
60
+ wb.creator = core_file.css('dc|creator').children.to_s
61
+ wb.modifier = core_file.css('cp|last_modified_by').children.to_s
62
+ wb.created_at = core_file.css('dcterms|created').children.to_s
63
+ wb.modified_at = core_file.css('dcterms|modified').children.to_s
64
+
65
+ wb.document_properties = RubyXL::DocumentProperties.parse_file(dir_path)
66
+ wb.calculation_chain = RubyXL::CalculationChain.parse_file(dir_path)
67
+ end
68
+
69
+ wb.shared_strings_container = RubyXL::SharedStringsTable.parse_file(dir_path)
70
+ wb.stylesheet = RubyXL::Stylesheet.parse_file(dir_path)
399
71
 
400
- #adds all worksheet xml files to files hash
401
- i=1
402
- 1.upto(@num_sheets) do
403
- filename = 'sheet'+i.to_s
404
- files[i] = Nokogiri::XML.parse(File.open(File.join(dir_path,'xl','worksheets',filename+'.xml'),'r'))
405
- i=i+1
406
- end
407
-
408
- FileUtils.rm_rf(dir_path)
409
-
410
- return files
411
- end
412
-
413
- def Parser.fill_workbook(file_path, files)
414
- wb = Workbook.new([nil],file_path)
415
-
416
- unless @data_only
417
- wb.creator = files['core'].css('dc|creator').children.to_s
418
- wb.modifier = files['core'].css('cp|last_modified_by').children.to_s
419
- wb.created_at = files['core'].css('dcterms|created').children.to_s
420
- wb.modified_at = files['core'].css('dcterms|modified').children.to_s
421
-
422
- wb.company = files['app'].css('Company').children.to_s
423
- wb.application = files['app'].css('Application').children.to_s
424
- wb.appversion = files['app'].css('AppVersion').children.to_s
425
- end
426
-
427
- wb.shared_strings_XML = files['sharedString'].to_s
428
- wb.defined_names = files['workbook'].css('definedNames').to_s
429
- wb.date1904 = files['workbook'].css('workbookPr').attribute('date1904').to_s == '1'
430
-
431
- wb.worksheets = Array.new(@num_sheets) #array of Worksheet objs
432
- wb
433
- end
434
-
435
- #sheet_names, dimensions
436
- def Parser.create_matrix(wb,i, files)
437
- sheet_names = files['app'].css('TitlesOfParts vt|vector vt|lpstr').children
438
- sheet = Worksheet.new(wb,sheet_names[i].to_s,[])
439
-
440
- dimensions = files[i+1].css('dimension').attribute('ref').to_s
441
- if(dimensions =~ /^([A-Z]+\d+:)?([A-Z]+\d+)$/)
442
- index = convert_to_index($2)
443
-
444
- rows = index[0]+1
445
- cols = index[1]+1
446
-
447
- #creates matrix filled with nils
448
- rows.times {sheet.sheet_data << Array.new(cols)}
449
- else
450
- raise 'invalid file'
451
- end
452
- sheet
453
- end
454
-
455
- def Parser.safe_filename(name, allow_mb_chars=false)
456
- # "\w" represents [0-9A-Za-z_] plus any multi-byte char
457
- regexp = allow_mb_chars ? /[^\w]/ : /[^0-9a-zA-Z\_]/
458
- name.gsub(regexp, "_")
459
- end
72
+ #fills out count information for each font, fill, and border
73
+ wb.cell_xfs.each { |style|
74
+ id = style.font_id
75
+ wb.fonts[id].count += 1 #unless id.nil?
76
+
77
+ id = style.fill_id
78
+ wb.fills[id].count += 1 #unless id.nil?
79
+
80
+ id = style.border_id
81
+ wb.borders[id].count += 1 #unless id.nil?
82
+ }
83
+
84
+ wb.worksheet_container.sheets.each_with_index { |sheet, i|
85
+ sheet_file_path = wb.relationship_container.find_by_rid(sheet.r_id).target
86
+ worksheet = RubyXL::Worksheet.parse(File.open(File.join(dir_path, 'xl', sheet_file_path)))
87
+ worksheet.sheet_data.rows.each { |r|
88
+ next if r.nil?
89
+ r.worksheet = worksheet
90
+ r.cells.each { |c| c.worksheet = worksheet unless c.nil? }
91
+ }
92
+ worksheet.workbook = wb
93
+ worksheet.sheet_name = sheet.name
94
+ worksheet.sheet_id = sheet.sheet_id
95
+ wb.worksheets[i] = worksheet
96
+ }
97
+
98
+ FileUtils.remove_entry_secure(dir_path)
460
99
 
461
- # Turns the passed in string into something safe for a filename
462
- def Parser.make_safe_name(name, allow_mb_chars=false)
463
- ext = safe_filename(File.extname(name), allow_mb_chars).gsub(/^_/, '.')
464
- "#{safe_filename(name.gsub(ext, ""), allow_mb_chars)}#{ext}".gsub(/\(/, '_').gsub(/\)/, '_').gsub(/__+/, '_').gsub(/^_/, '').gsub(/_$/, '')
100
+ return wb
465
101
  end
466
102
 
467
103
  end