workbook 0.5 → 0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -3
  3. data/lib/workbook/book.rb +13 -12
  4. data/lib/workbook/column.rb +13 -6
  5. data/lib/workbook/format.rb +16 -1
  6. data/lib/workbook/modules/cell.rb +3 -2
  7. data/lib/workbook/modules/diff_sort.rb +2 -2
  8. data/lib/workbook/modules/type_parser.rb +3 -2
  9. data/lib/workbook/readers/xls_reader.rb +4 -4
  10. data/lib/workbook/readers/xls_shared.rb +39 -0
  11. data/lib/workbook/readers/xlsx_reader.rb +159 -21
  12. data/lib/workbook/row.rb +2 -2
  13. data/lib/workbook/sheet.rb +9 -2
  14. data/lib/workbook/table.rb +20 -8
  15. data/lib/workbook/template.rb +5 -1
  16. data/lib/workbook/types/date.rb +1 -1
  17. data/lib/workbook/version.rb +1 -1
  18. data/lib/workbook/writers/xlsx_writer.rb +7 -7
  19. data/test/artifacts/txt_in_xls.xls +0 -0
  20. data/test/test_book.rb +7 -6
  21. data/test/test_format.rb +10 -0
  22. data/test/test_functional.rb +3 -3
  23. data/test/test_modules_cell.rb +19 -11
  24. data/test/test_modules_table_diff_sort.rb +0 -2
  25. data/test/test_modules_type_parser.rb +7 -5
  26. data/test/test_readers_csv_reader.rb +4 -4
  27. data/test/test_readers_ods_reader.rb +9 -9
  28. data/test/test_readers_txt_reader.rb +5 -5
  29. data/test/test_readers_xls_reader.rb +11 -15
  30. data/test/test_readers_xls_shared.rb +10 -0
  31. data/test/test_readers_xlsx_reader.rb +12 -9
  32. data/test/test_row.rb +6 -6
  33. data/test/test_table.rb +13 -11
  34. data/test/test_template.rb +10 -1
  35. data/test/test_types_date.rb +3 -0
  36. data/test/test_writers_html_writer.rb +1 -1
  37. data/test/test_writers_xls_writer.rb +9 -10
  38. data/test/test_writers_xlsx_writer.rb +2 -2
  39. data/workbook.gemspec +0 -1
  40. metadata +4 -18
  41. data/rbeautify.rb +0 -234
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bc0e6be07c59fd70553ab1c6e7be1b47259d44fc
4
- data.tar.gz: 2c38be86478a4e7af84cdcd2b44b9df3353d6fa1
3
+ metadata.gz: ca12f4d088bcc71159ed61caae720bdc19457bc0
4
+ data.tar.gz: 95109a2df0f6c2c6759486fe94d3c61d28e4cf7d
5
5
  SHA512:
6
- metadata.gz: 122ccd6febc910869615018a44e2e34323c5d870e417013b0a33777005c78e94afc0b174bea6c88b1ab9a5d53dc2e01ac6edf9927f4d2f2da673577bbc949a20
7
- data.tar.gz: 58ab99448e24d2207c5412c67370dc10232d55a5a97eaf78f090c150db5d09eb39297d2c80e4daf95fad33412e54f2f1fe8b02f6e5a5a994164b5be749e8aa5f
6
+ metadata.gz: e7b4eef2a1fcb1d4600379b17c7a08584387ca188c8a13bb2bffc4fadbf0315585d8ed7e2d057b2e664970bbdd5e3ac38dff8d032f77b924dca9f0d1d2f1911a
7
+ data.tar.gz: 89b85aeca814d7fe4f3dc5b6fcab22452d8a496dd45d125f9ad2fb6582a3bfc88144573db7bd5964a68a1201792cd37094cae90f5c1c0d099e1cf208e1d11d1f
data/README.md CHANGED
@@ -59,7 +59,7 @@ Alternatively (more spreadsheet like) you can read cells like this (writing to b
59
59
  If you want to use an existing file as a template (which you can create in Excel to create nice looking templates),
60
60
  simply clone the row, and add it back:
61
61
 
62
- b = Workbook::Book.open("template.xls")
62
+ b = Workbook::Book.import("template.xls")
63
63
  table = b.sheet.table
64
64
  template_row = table[1] # can be any, but I typically have a well
65
65
  # formatted header row + an example template
@@ -78,7 +78,7 @@ simply clone the row, and add it back:
78
78
  Another typical use case is exporting a list of ActiveRecord-objects to xls (it is assumed that the headers of the excel-table correspond
79
79
  (like "Total order price" and `total_order_price` match) to the headers of the database-table ):
80
80
 
81
- b = Workbook::Book.open("template.xls")
81
+ b = Workbook::Book.import("template.xls")
82
82
  table = b.sheet.table
83
83
  template_row = table[1] # see above
84
84
  Order.where("created_at > ?", Time.now - 1.week).each do |order|
@@ -152,6 +152,5 @@ Workbook uses the following gems:
152
152
  * [ruby-ole](http://code.google.com/p/ruby-ole/) Used in the Spreadsheet Gem (Copyright © 2007-2010 Charles Lowe; MIT)
153
153
  * [FasterCSV](http://fastercsv.rubyforge.org/) Used for reading CSV (comma separated text) and TXT (tab separated text) files (Copyright © James Edward Gray II; GPL2 & Ruby License)
154
154
  * [rchardet](http://rubyforge.org/projects/rchardet) Used for detecting encoding in CSV and TXT importers (Copyright © JMHodges; LGPL)
155
- * [roo](https://github.com/roo-rb/roo) Temporarily used for reading the newer .xlsx files (without formatting) (Copyright © 2008-2014 Thomas Preymesser, Ben Woosley, MIT License)
156
155
  * [axslx](https://github.com/randym/axlsx) Used for writing the newer .xlsx files (with formatting) (Copyright © 2011, 2012 Randy Morgan, MIT License)
157
156
  * [Nokogiri](http://nokogiri.org/) Used for reading ODS documents (Copyright © 2008 - 2012 Aaron Patterson, Mike Dalessio, Charles Nutter, Sergio Arbeo, Patrick Mahoney, Yoko Harada; MIT Licensed)
data/lib/workbook/book.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
+ require 'open-uri'
2
3
  require 'workbook/writers/xls_writer'
3
4
  require 'workbook/writers/xlsx_writer'
4
5
  require 'workbook/writers/html_writer'
@@ -47,15 +48,15 @@ module Workbook
47
48
 
48
49
  # @param [Workbook::Sheet, Array] sheet create a new workbook based on an existing sheet, or initialize a sheet based on the array
49
50
  # @return [Workbook::Book]
50
- def initialize sheet=Workbook::Sheet.new([], self, options={})
51
+ def initialize sheet=nil
51
52
  if sheet.is_a? Workbook::Sheet
52
53
  self.push sheet
53
- else
54
- self.push Workbook::Sheet.new(sheet, self, options)
54
+ elsif sheet
55
+ self.push Workbook::Sheet.new(sheet, self, {})
55
56
  end
56
57
  end
57
58
 
58
- # @return [Workbook::Format] returns the template describing how the document should be/is formatted
59
+ # @return [Workbook::Template] returns the template describing how the document should be/is formatted
59
60
  def template
60
61
  @template ||= Workbook::Template.new
61
62
  end
@@ -70,7 +71,7 @@ module Workbook
70
71
  #
71
72
  # @return [String] the title of the workbook
72
73
  def title
73
- @title ? @title : "untitled document"
74
+ (defined?(@title) and !@title.nil?) ? @title : "untitled document"
74
75
  end
75
76
 
76
77
  def title= t
@@ -115,7 +116,7 @@ module Workbook
115
116
  # @param [String] filename a string with a reference to the file to be opened
116
117
  # @param [String] extension an optional string enforcing a certain parser (based on the file extension, e.g. 'txt', 'csv' or 'xls')
117
118
  # @return [Workbook::Book] A new instance, based on the filename
118
- def open filename, extension=nil, options={}
119
+ def import filename, extension=nil, options={}
119
120
  extension = file_extension(filename) unless extension
120
121
  if ['txt','csv','xml'].include?(extension)
121
122
  open_text filename, extension, options
@@ -131,7 +132,7 @@ module Workbook
131
132
  # @return [Workbook::Book] A new instance, based on the filename
132
133
  def open_binary filename, extension=nil, options={}
133
134
  extension = file_extension(filename) unless extension
134
- f = File.open(filename,'rb')
135
+ f = open(filename)
135
136
  send("load_#{extension}".to_sym, f, options)
136
137
  end
137
138
 
@@ -141,9 +142,7 @@ module Workbook
141
142
  # @param [String] extension an optional string enforcing a certain parser (based on the file extension, e.g. 'txt', 'csv' or 'xls')
142
143
  def open_text filename, extension=nil, options={}
143
144
  extension = file_extension(filename) unless extension
144
- f = File.open(filename,'r')
145
- t = f.read
146
- t = text_to_utf8(t)
145
+ t = text_to_utf8(open(filename).read)
147
146
  send("load_#{extension}".to_sym, t, options)
148
147
  end
149
148
 
@@ -183,7 +182,9 @@ module Workbook
183
182
  #
184
183
  # @return [String] The file extension
185
184
  def file_extension(filename)
186
- File.extname(filename).gsub('.','').downcase if filename
185
+ ext = File.extname(filename).gsub('.','').downcase if filename
186
+ # for remote files which has asset id after extension
187
+ ext.split('?')[0]
187
188
  end
188
189
 
189
190
  # Load the CSV data contained in the given StringIO or String object
@@ -215,7 +216,7 @@ module Workbook
215
216
  # @return [Workbook::Book] A new instance, based on the filename
216
217
  def open filename, extension=nil
217
218
  wb = self.new
218
- wb.open filename, extension
219
+ wb.import filename, extension
219
220
  return wb
220
221
  end
221
222
 
@@ -3,7 +3,7 @@ module Workbook
3
3
 
4
4
  # Column helps us to store general properties of a column, and lets us easily perform operations on values within a column
5
5
  class Column
6
- attr_accessor :limit, :table #character limit
6
+ attr_accessor :limit, :width #character limit
7
7
 
8
8
  def initialize(table=nil, options={})
9
9
  self.table = table
@@ -12,12 +12,12 @@ module Workbook
12
12
 
13
13
  # Returns column type, either :primary_key, :string, :text, :integer, :float, :decimal, :datetime, :date, :binary, :boolean
14
14
  def column_type
15
- return @column_type if @column_type
15
+ return @column_type if defined?(@column_type)
16
16
  ind = self.index
17
17
  table[1..500].each do |row|
18
18
  if row[ind] and row[ind].cell_type
19
19
  cel_column_type = row[ind].cell_type
20
- if cel_column_type == @column_type or @column_type.nil?
20
+ if !defined?(@column_type) or @column_type.nil? or cel_column_type == @column_type
21
21
  @column_type = cel_column_type
22
22
  else
23
23
  @column_type = :string
@@ -33,9 +33,16 @@ module Workbook
33
33
  table.columns.index self
34
34
  end
35
35
 
36
- def table= t
37
- raise(ArgumentError, "value should be nil or Workbook::Table") unless [NilClass,Workbook::Table].include? t.class
38
- @table = t
36
+ # Set the table this column belongs to
37
+ # @param [Workbook::Table] table this column belongs to
38
+ def table= table
39
+ raise(ArgumentError, "value should be nil or Workbook::Table") unless [NilClass,Workbook::Table].include? table.class
40
+ @table = table
41
+ end
42
+
43
+ # @return [Workbook::Table]
44
+ def table
45
+ @table
39
46
  end
40
47
 
41
48
  def column_type= column_type
@@ -24,7 +24,11 @@ module Workbook
24
24
  # @param [Workbook::Format, Hash] options (e.g. :background, :color, :background_color, :font_weight (integer or css-type labels)
25
25
  # @return [String] the name of the format, default: nil
26
26
  def initialize options={}, name=nil
27
- options.each {|k,v| self[k]=v}
27
+ if options.is_a? String
28
+ name = options
29
+ else
30
+ options.each {|k,v| self[k]=v}
31
+ end
28
32
  self.name = name
29
33
  end
30
34
 
@@ -89,5 +93,16 @@ module Workbook
89
93
  formats.each{|a| ff.merge!(a) }
90
94
  return ff
91
95
  end
96
+
97
+ # Formatting is sometimes the only way to detect the cells' type.
98
+ def derived_type
99
+ if self[:numberformat]
100
+ if self[:numberformat].to_s.match("h")
101
+ :time
102
+ elsif self[:numberformat].to_s.match("y")
103
+ :date
104
+ end
105
+ end
106
+ end
92
107
  end
93
108
  end
@@ -1,6 +1,7 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  require 'workbook/modules/type_parser'
3
3
  require 'workbook/nil_value'
4
+ require 'date'
4
5
 
5
6
  module Workbook
6
7
  module Modules
@@ -63,7 +64,7 @@ module Workbook
63
64
  #
64
65
  # @return [Symbol] the type of cell, compatible with Workbook::Column'types
65
66
  def cell_type
66
- tp = case value.class.to_s
67
+ case value.class.to_s
67
68
  when "String" then :string
68
69
  when "FalseClass" then :boolean
69
70
  when "TrueClass" then :boolean
@@ -118,7 +119,7 @@ module Workbook
118
119
  # @return [Workbook::Format] the current format
119
120
  def format
120
121
  # return @workbook_format if @workbook_format
121
- if row and template and row.header? and !@workbook_format
122
+ if row and template and row.header? and !defined?(@workbook_format)
122
123
  @workbook_format = template.create_or_find_format_by(:header)
123
124
  else
124
125
  @workbook_format ||= Workbook::Format.new
@@ -105,7 +105,7 @@ module Workbook
105
105
  #
106
106
  # @return [Workbook::Table] the empty table, linked to a book
107
107
  def diff_template
108
- return @diff_template if @diff_template
108
+ return @diff_template if defined?(@diff_template)
109
109
  diffbook = Workbook::Book.new_diff_template
110
110
  difftable = diffbook.sheet.table
111
111
  @diff_template ||= difftable
@@ -175,7 +175,7 @@ module Workbook
175
175
 
176
176
  # returns a placeholder row, for internal use only
177
177
  def placeholder_row
178
- if @placeholder_row != nil
178
+ if defined?(@placeholder_row) and !@placeholder_row.nil?
179
179
  return @placeholder_row
180
180
  else
181
181
  @placeholder_row = Workbook::Row.new [nil]
@@ -13,7 +13,7 @@ module Workbook
13
13
  # Return the different active string parsers
14
14
  # @return [Array<Symbol>] A list of parsers
15
15
  def string_parsers
16
- @string_parsers ||= [:string_cleaner,:string_nil_converter,:string_integer_converter,:string_boolean_converter]
16
+ @string_parsers ||= [:string_cleaner,:string_integer_converter,:string_boolean_converter]
17
17
  end
18
18
 
19
19
  # Set the list of string parsers
@@ -32,8 +32,9 @@ module Workbook
32
32
  # Returns the parsed value (retrieved by calling #value)
33
33
  # @return [Object] The parsed object, ideally a date or integer when found to be a such...
34
34
  def parse options={}
35
- options = {:detect_date=>false}.merge(options)
35
+ options = {:detect_date=>false, :convert_empty_to_nil=>true}.merge(options)
36
36
  string_parsers.push :string_optimistic_date_converter if options[:detect_date]
37
+ string_parsers.push :string_nil_converter if options[:convert_empty_to_nil]
37
38
  v = value
38
39
  string_parsers_as_procs.each do |p|
39
40
  if v.is_a? String
@@ -2,7 +2,6 @@
2
2
  require 'spreadsheet'
3
3
  require 'workbook/readers/xls_shared'
4
4
 
5
-
6
5
  module Workbook
7
6
  module Readers
8
7
  module XlsReader
@@ -16,9 +15,10 @@ module Workbook
16
15
  rescue Ole::Storage::FormatError => e
17
16
  begin
18
17
  # Assuming it is a tab separated txt inside .xls
19
- open(file_obj.path, 'txt')
20
- rescue
21
- raise e
18
+ import(file_obj.path, 'txt')
19
+ rescue Exception => ef
20
+
21
+ raise ef
22
22
  end
23
23
  end
24
24
 
@@ -1,4 +1,7 @@
1
1
  # -*- encoding : utf-8 -*-
2
+
3
+ require 'date'
4
+
2
5
  module Workbook
3
6
  module Readers
4
7
  module XlsShared
@@ -8,6 +11,7 @@ module Workbook
8
11
  # @param [String, nil] ms_nr_format (nil returns nil)
9
12
  # @return [String, nil]
10
13
  def ms_formatting_to_strftime ms_nr_format
14
+ ms_nr_format = num_fmt_id_to_ms_formatting(ms_nr_format) if ms_nr_format.is_a? Integer
11
15
  if ms_nr_format
12
16
  ms_nr_format = ms_nr_format.to_s.downcase
13
17
  return nil if ms_nr_format == 'general' or ms_nr_format == ""
@@ -34,6 +38,33 @@ module Workbook
34
38
  end
35
39
  end
36
40
 
41
+ # Convert numFmtId to msmarkup
42
+ # @param [String, Integer] num_fmt_id numFmtId
43
+ # @return [String] number format (excel markup)
44
+ def num_fmt_id_to_ms_formatting num_fmt_id
45
+ # from: https://stackoverflow.com/questions/4730152/what-indicates-an-office-open-xml-cell-contains-a-date-time-value
46
+ {'0'=>nil, '1'=>'0', '2'=>'0.00', '3'=>'#,##0', '4'=>'#,##0.00',
47
+ '9'=>'0%', '10'=>'0.00%', '11'=>'0.00E+00', '12'=>'# ?/?',
48
+ '13'=>'# ??/??', '14'=>'mm-dd-yy', '15'=>'d-mmm-yy', '16'=>'d-mmm',
49
+ '17'=>'mmm-yy', '18'=>'h:mm AM/PM', '19'=>'h:mm:ss AM/PM',
50
+ '20'=>'h:mm', '21'=>'h:mm:ss', '22'=>'m/d/yy h:mm',
51
+ '37'=>'#,##0 ;(#,##0)', '38'=>'#,##0 ;[Red](#,##0)',
52
+ '39'=>'#,##0.00;(#,##0.00)', '40'=>'#,##0.00;[Red](#,##0.00)',
53
+ '44'=>'_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)',
54
+ '45'=>'mm:ss', '46'=>'[h]:mm:ss', '47'=>'mmss.0', '48'=>'##0.0E+0',
55
+ '49'=>'@', '27'=>'[$-404]e/m/d', '30'=>'m/d/yy', '36'=>'[$-404]e/m/d',
56
+ '50'=>'[$-404]e/m/d', '57'=>'[$-404]e/m/d', '59'=>'t0', '60'=>'t0.00',
57
+ '61'=>'t#,##0', '62'=>'t#,##0.00', '67'=>'t0%', '68'=>'t0.00%',
58
+ '69'=>'t# ?/?', '70' => 't# ??/??'}[num_fmt_id.to_s]
59
+ end
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
37
68
  # Attempt to convert html-hex color value to xls color number
38
69
  #
39
70
  # @param [String] hex color
@@ -54,6 +85,14 @@ module Workbook
54
85
  return numberformat.gsub('%Y','yyyy').gsub('%A','dddd').gsub('%B','mmmm').gsub('%a','ddd').gsub('%b','mmm').gsub('%y','yy').gsub('%d','dd').gsub('%m','mm').gsub('%y','y').gsub('%y','%%y').gsub('%e','d')
55
86
  end
56
87
 
88
+ def xls_number_to_time number, base_date = DateTime.new(1899,12,30)
89
+ base_date + number.to_f
90
+ end
91
+
92
+ def xls_number_to_date number, base_date = Date.new(1899,12,30)
93
+ base_date + number.to_i
94
+ end
95
+
57
96
  XLS_COLORS = {:xls_color_1=>'#000000',
58
97
  :xls_color_2=>'#FFFFFF',
59
98
  :xls_color_3=>'#FF0000',
@@ -1,39 +1,177 @@
1
1
  # -*- encoding : utf-8 -*-
2
- require 'roo'
3
2
  require 'workbook/readers/xls_shared'
4
3
 
5
-
6
4
  module Workbook
7
5
  module Readers
8
6
  module XlsxReader
9
7
  include Workbook::Readers::XlsShared
10
8
 
11
- # Load method for .xlsm files, an office open file format, hence compatible with .xlsx (it emphasizes that it contains macros)
12
- #
13
- # @param [String, File] file_obj a string with a reference to the file to be written to
14
9
  def load_xlsm file_obj, options={}
15
10
  self.load_xlsx file_obj, options
16
11
  end
17
12
  def load_xlsx file_obj, options={}
18
13
  file_obj = file_obj.path if file_obj.is_a? File
19
- # file_obj = file_obj.match(/^\/(.*)/) ? file_obj : "./#{file_obj}"
20
- # p "opening #{file_obj}"
21
- sp = Roo::Excelx.new(file_obj)
22
- template.add_raw sp, raw_object_class: Roo::Spreadsheet
23
- parse_xlsx sp
24
- end
25
-
26
- def parse_xlsx xlsx_spreadsheet=template.raws[Roo::Spreadsheet], options={}
27
- options = {:additional_type_parsing=>false}.merge options
28
- sheet_index = 0
29
- xlsx_spreadsheet.each_with_pagename do |sheet_name, sheet|
30
- s = create_or_open_sheet_at(sheet_index)
31
- sheet.each_with_index do |row, rowi|
32
- s.table << row
14
+ sheets = {}
15
+ shared_string_file = ""
16
+ styles = ""
17
+ workbook = ""
18
+ workbook_rels = ""
19
+ Zip::File.open(file_obj) do |zipfile|
20
+ zipfile.entries.each do |file|
21
+ if file.name.match(/^xl\/worksheets\/(.*)\.xml$/)
22
+ sheets[file.name.sub(/^xl\//,'')] = zipfile.read(file.name)
23
+ elsif file.name.match(/xl\/sharedStrings.xml$/)
24
+ shared_string_file = zipfile.read(file.name)
25
+ elsif file.name.match(/xl\/workbook.xml$/)
26
+ workbook = zipfile.read(file.name)
27
+ elsif file.name.match(/xl\/_rels\/workbook.xml.rels$/)
28
+ workbook_rels = zipfile.read(file.name)
29
+ elsif file.name.match(/xl\/styles.xml$/)
30
+ styles = zipfile.read(file.name)
31
+ end
32
+ # content = zipfile.read(file.name) if file.name == "content.xml"
33
+ end
34
+ end
35
+
36
+ parse_xlsx_styles(styles)
37
+
38
+
39
+ relation_file = {}
40
+ Nokogiri::XML(workbook_rels).css("Relationships Relationship").each do |relship|
41
+ relation_file[relship.attr("Id")]=relship.attr("Target")
42
+ end
43
+
44
+ @shared_strings = parse_shared_string_file(shared_string_file)
45
+
46
+ Nokogiri::XML(workbook).css("sheets sheet").each do |sheet|
47
+ name = sheet.attr("name")
48
+ filename = relation_file[sheet.attr("r:id")]
49
+ state = sheet.attr("state")
50
+ if state != "hidden"
51
+ sheet = sheets[filename]
52
+ self.push parse_xlsx_sheet(sheet)
53
+ self.last.name = name
54
+ end
55
+ end
56
+
57
+ @shared_strings = nil
58
+ self.each do |sheet|
59
+ sheet.each do |table|
60
+ table.trim!
33
61
  end
34
- sheet_index += 1
35
62
  end
36
63
  end
64
+
65
+ def parse_xlsx_styles(styles)
66
+ styles = Nokogiri::XML(styles)
67
+
68
+ fonts = parse_xlsx_fonts styles
69
+ backgrounds = extract_xlsx_backgrounds styles
70
+ customNumberFormats = extract_xlsx_number_formats styles
71
+
72
+
73
+ styles.css("cellXfs xf").each do |cellformat|
74
+ hash = {}
75
+ # <xf numFmtId="0" fontId="1" fillId="2" borderId="0" xfId="0" applyFont="1" applyFill="1" applyAlignment="1">
76
+ background_hash = backgrounds[cellformat.attr("applyFill").to_i]
77
+ hash.merge!(background_hash) if background_hash
78
+
79
+ font_hash = fonts[cellformat.attr("applyFill").to_i]
80
+ hash.merge!(font_hash) if font_hash
81
+
82
+ id = (cellformat.attr("numFmtId")).to_i
83
+ if id >= 164
84
+ hash[:numberformat] = customNumberFormats[id]
85
+ else
86
+ hash[:numberformat] = ms_formatting_to_strftime(id)
87
+ end
88
+ self.template.add_format Workbook::Format.new(hash)
89
+ end
90
+ end
91
+
92
+ # Extracts fonts descriptors from styles.xml
93
+ def parse_xlsx_fonts styles
94
+ styles.css("fonts font").collect do |font|
95
+ hash = {}
96
+ hash[:font_family] = font.css("name").first.attr("val") if font.css("name")
97
+ hash[:font_size] = font.css("sz").first.attr("val").to_i if font.css("name")
98
+ hash
99
+ end
100
+ end
101
+
102
+ # Extracts number formats from styles.xml
103
+ def extract_xlsx_number_formats styles
104
+ hash = {}
105
+ styles.css("numFmts numFmt").each do |fmt|
106
+ format_id = fmt.attr("numFmtId").to_i
107
+ parsed_format_string = ms_formatting_to_strftime(fmt.attr("formatCode"))
108
+ hash[format_id] = parsed_format_string
109
+ end
110
+ hash
111
+ end
112
+
113
+ def extract_xlsx_backgrounds styles
114
+ styles.css("fills fill").collect do |fill|
115
+ hash = {}
116
+ patternFill = fill.css("patternFill").first
117
+ # TODO: convert to html-hex
118
+ hash[:background] = patternFill.attr("patternType") if patternFill
119
+ hash
120
+ end
121
+ end
122
+
123
+ def parse_shared_string_file file
124
+ Nokogiri::XML(file).css("sst si t").collect{|a| a.text}
125
+ end
126
+ def parse_xlsx_sheet sheet_xml
127
+ sheet = Workbook::Sheet.new
128
+ table = sheet.table
129
+
130
+ noko_xml = Nokogiri::XML(sheet_xml)
131
+
132
+ rows = noko_xml.css('sheetData row').collect{|row| parse_xlsx_row(row)}
133
+ rows.each do |row|
134
+ table << row
135
+ end
136
+
137
+ columns = noko_xml.css('cols col').collect{|col| parse_xlsx_column(col) }
138
+ table.columns = columns
139
+
140
+ sheet
141
+ end
142
+
143
+ def parse_xlsx_column column
144
+ col = Workbook::Column.new()
145
+ col.width = column.attr("width").to_f
146
+ col
147
+ end
148
+ def parse_xlsx_row row
149
+ cells = row.css('c').collect{|a| parse_xlsx_cell(a)}
150
+ row = Workbook::Row.new(cells)
151
+ end
152
+ def parse_xlsx_cell cell
153
+ # style_id = cell.attr('s')
154
+ # p cell
155
+ type = cell.attr('t')
156
+ formatIndex = cell.attr('s').to_i
157
+ value = cell.text
158
+ fmt = template.formats[formatIndex]
159
+ # puts type
160
+ if type == "n" or type == nil
161
+ if fmt.derived_type == :date
162
+ value = xls_number_to_date(value)
163
+ elsif fmt.derived_type == :time
164
+ value = xls_number_to_time(value)
165
+ elsif type == "n"
166
+ value = value.match(/\./) ? value.to_f : value.to_i
167
+ end
168
+ elsif type == "s"
169
+ value = @shared_strings[value.to_i]
170
+ end
171
+ Workbook::Cell.new(value, format: fmt)
172
+ end
173
+ def parse_xlsx
174
+ end
37
175
  end
38
176
  end
39
- end
177
+ end