workbook 0.8.1 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +21 -0
- data/.gitignore +4 -1
- data/.ruby-version +1 -1
- data/.travis.yml +4 -4
- data/CHANGELOG.md +8 -0
- data/Gemfile +2 -2
- data/README.md +9 -7
- data/Rakefile +6 -6
- data/json_test.json +1 -0
- data/lib/workbook/book.rb +73 -62
- data/lib/workbook/cell.rb +58 -13
- data/lib/workbook/column.rb +31 -28
- data/lib/workbook/format.rb +23 -24
- data/lib/workbook/generatetypes.rb +4 -4
- data/lib/workbook/modules/cache.rb +6 -7
- data/lib/workbook/modules/cell.rb +77 -100
- data/lib/workbook/modules/diff_sort.rb +92 -83
- data/lib/workbook/modules/raw_objects_storage.rb +6 -8
- data/lib/workbook/modules/type_parser.rb +30 -22
- data/lib/workbook/nil_value.rb +4 -9
- data/lib/workbook/readers/csv_reader.rb +7 -10
- data/lib/workbook/readers/ods_reader.rb +51 -50
- data/lib/workbook/readers/txt_reader.rb +6 -8
- data/lib/workbook/readers/xls_reader.rb +21 -33
- data/lib/workbook/readers/xls_shared.rb +106 -117
- data/lib/workbook/readers/xlsx_reader.rb +45 -46
- data/lib/workbook/row.rb +99 -84
- data/lib/workbook/sheet.rb +47 -38
- data/lib/workbook/table.rb +96 -72
- data/lib/workbook/template.rb +12 -15
- data/lib/workbook/types/false.rb +0 -1
- data/lib/workbook/types/nil.rb +0 -1
- data/lib/workbook/types/nil_class.rb +1 -1
- data/lib/workbook/types/numeric.rb +1 -1
- data/lib/workbook/types/string.rb +1 -1
- data/lib/workbook/types/time.rb +1 -1
- data/lib/workbook/types/true.rb +0 -1
- data/lib/workbook/types/true_class.rb +1 -1
- data/lib/workbook/version.rb +2 -3
- data/lib/workbook/writers/csv_table_writer.rb +10 -13
- data/lib/workbook/writers/html_writer.rb +34 -38
- data/lib/workbook/writers/json_table_writer.rb +8 -11
- data/lib/workbook/writers/xls_writer.rb +30 -36
- data/lib/workbook/writers/xlsx_writer.rb +45 -29
- data/lib/workbook.rb +16 -15
- data/test/artifacts/currency_test.ods +0 -0
- data/test/helper.rb +6 -5
- data/test/test_book.rb +41 -38
- data/test/test_column.rb +26 -24
- data/test/test_format.rb +51 -55
- data/test/test_functional.rb +7 -8
- data/test/test_modules_cache.rb +18 -17
- data/test/test_modules_cell.rb +55 -46
- data/test/test_modules_table_diff_sort.rb +55 -64
- data/test/test_modules_type_parser.rb +61 -31
- data/test/test_readers_csv_reader.rb +48 -42
- data/test/test_readers_ods_reader.rb +36 -31
- data/test/test_readers_txt_reader.rb +21 -23
- data/test/test_readers_xls_reader.rb +20 -23
- data/test/test_readers_xls_shared.rb +2 -3
- data/test/test_readers_xlsx_reader.rb +44 -37
- data/test/test_row.rb +105 -109
- data/test/test_sheet.rb +35 -41
- data/test/test_table.rb +82 -60
- data/test/test_template.rb +16 -15
- data/test/test_types_date.rb +4 -6
- data/test/test_writers_csv_writer.rb +24 -0
- data/test/test_writers_html_writer.rb +42 -41
- data/test/test_writers_json_writer.rb +16 -9
- data/test/test_writers_xls_writer.rb +50 -35
- data/test/test_writers_xlsx_writer.rb +62 -34
- data/workbook.gemspec +25 -27
- metadata +96 -42
@@ -1,34 +1,36 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# -*- encoding : utf-8 -*-
|
4
|
-
# frozen_string_literal: true
|
5
3
|
module Workbook
|
6
4
|
module Readers
|
7
5
|
module OdsReader
|
8
6
|
# reads self with and ods-type content.xml
|
9
7
|
# @param [String,File] file_obj a file or file reference
|
10
8
|
# @return [Workbook::Book] self
|
11
|
-
def load_ods file_obj, options={}
|
9
|
+
def load_ods file_obj, options = {}
|
12
10
|
file_obj = file_obj.path if file_obj.is_a? File
|
13
11
|
content = ""
|
14
12
|
styles = ""
|
13
|
+
|
15
14
|
Zip::File.open(file_obj) do |zipfile|
|
16
15
|
zipfile.entries.each do |file|
|
17
16
|
styles = zipfile.read(file.name) if file.name == "styles.xml"
|
18
17
|
content = zipfile.read(file.name) if file.name == "content.xml"
|
19
18
|
end
|
20
19
|
end
|
20
|
+
|
21
21
|
content = Nokogiri.XML(content)
|
22
22
|
styles = Nokogiri.XML(styles)
|
23
|
+
|
23
24
|
template.add_raw content
|
24
25
|
parse_ods_style styles
|
25
26
|
parse_ods content, options
|
26
|
-
|
27
|
+
|
28
|
+
self
|
27
29
|
end
|
28
30
|
|
29
31
|
def set_format_property format, property, value
|
30
32
|
value.strip!
|
31
|
-
format[property] = value if value
|
33
|
+
format[property] = value if value && (value != "")
|
32
34
|
end
|
33
35
|
|
34
36
|
def parse_ods_style parse_ods_style
|
@@ -37,14 +39,14 @@ module Workbook
|
|
37
39
|
if style_family == "table-cell"
|
38
40
|
format = Workbook::Format.new
|
39
41
|
format.name = style.xpath("@style:name").to_s
|
40
|
-
format.parent =
|
42
|
+
format.parent = template.formats[style.xpath("@style:parent-style-name").to_s]
|
41
43
|
set_format_property format, :border, style.xpath("style:table-cell-properties/@fo:border").to_s
|
42
|
-
set_format_property format, :vertical_align, style.xpath("style:table-cell-properties/@style:vertical-align").to_s.gsub("automatic","auto")
|
43
|
-
set_format_property format, :padding, style.xpath("style:table-cell-properties/@fo:padding").to_s.gsub("automatic","auto")
|
44
|
+
set_format_property format, :vertical_align, style.xpath("style:table-cell-properties/@style:vertical-align").to_s.gsub("automatic", "auto")
|
45
|
+
set_format_property format, :padding, style.xpath("style:table-cell-properties/@fo:padding").to_s.gsub("automatic", "auto")
|
44
46
|
set_format_property format, :font, style.xpath("style:text-properties/style:font-name").to_s + " " + style.xpath("style:text-properties/fo:font-size").to_s + " " + style.xpath("style:text-properties/fo:font-weight").to_s
|
45
47
|
set_format_property format, :color, style.xpath("style:text-properties/@fo:color").to_s
|
46
48
|
set_format_property format, :background_color, style.xpath("style:table-cell-properties/@fo:background-color").to_s
|
47
|
-
|
49
|
+
template.add_format(format)
|
48
50
|
end
|
49
51
|
end
|
50
52
|
end
|
@@ -52,105 +54,104 @@ module Workbook
|
|
52
54
|
# updates self with and ods-type content.xml
|
53
55
|
# @param [Nokogiri::XML::Document] ods_spreadsheet nokogirified content.xml
|
54
56
|
# @return [Workbook::Book] self
|
55
|
-
def parse_ods ods_spreadsheet=template.raws[Nokogiri::XML::Document], options={}
|
56
|
-
require
|
57
|
+
def parse_ods ods_spreadsheet = template.raws[Nokogiri::XML::Document], options = {}
|
58
|
+
require "cgi"
|
57
59
|
|
58
|
-
options = {:additional_type_parsing=>false}.merge options
|
59
|
-
# styles
|
60
|
-
#puts ods_spreadsheet
|
61
60
|
parse_ods_style ods_spreadsheet
|
62
61
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
parse_local_table(workbook_sheet,table,tableindex)
|
62
|
+
ods_spreadsheet.xpath("//office:body/office:spreadsheet").each_with_index do |sheet, sheetindex|
|
63
|
+
workbook_sheet = create_or_open_sheet_at(sheetindex)
|
64
|
+
sheet.xpath("table:table").each_with_index do |table, tableindex|
|
65
|
+
parse_local_table(workbook_sheet, table, tableindex)
|
68
66
|
end
|
69
67
|
end
|
70
|
-
|
68
|
+
self
|
71
69
|
end
|
72
70
|
|
73
|
-
#parse the contents of an entire table by parsing every row in it and adding it to the table
|
74
|
-
def parse_local_table(sheet,table,tableindex)
|
75
|
-
local_table = sheet
|
71
|
+
# parse the contents of an entire table by parsing every row in it and adding it to the table
|
72
|
+
def parse_local_table(sheet, table, tableindex)
|
73
|
+
local_table = sheet[tableindex]
|
76
74
|
local_table.name = table.xpath("@table:name").to_s
|
77
|
-
|
75
|
+
|
78
76
|
table.xpath("table:table-row").each do |row|
|
79
77
|
local_table << parse_local_row(row)
|
80
78
|
end
|
79
|
+
|
81
80
|
local_table.trim!
|
82
81
|
end
|
83
82
|
|
84
|
-
#set column count
|
83
|
+
# set column count
|
85
84
|
def get_column_count(table)
|
86
85
|
first_row = table.xpath("table:table-row").first
|
87
86
|
cells = first_row.xpath("table:table-cell|table:covered-table-cell")
|
88
87
|
column_count = 0
|
89
88
|
cells.each do |cell|
|
90
|
-
if cell.xpath(
|
91
|
-
|
89
|
+
column_count += if cell.xpath("@table:number-columns-spanned").children.size > 0
|
90
|
+
cell.xpath("@table:number-columns-spanned").children[0].inner_text.to_i
|
92
91
|
else
|
93
|
-
|
92
|
+
1
|
94
93
|
end
|
95
94
|
end
|
96
95
|
column_count
|
97
96
|
end
|
98
97
|
|
99
|
-
#parse the contents of an entire row by parsing every cell in it and adding it to the row
|
98
|
+
# parse the contents of an entire row by parsing every cell in it and adding it to the row
|
100
99
|
def parse_local_row(row)
|
101
100
|
cells = row.xpath("table:table-cell|table:covered-table-cell")
|
102
101
|
workbook_row = Workbook::Row.new
|
103
102
|
cells.each do |cell|
|
104
103
|
@cell = cell
|
105
|
-
repeat =
|
106
|
-
workbook_cell = Workbook::Cell.new
|
104
|
+
repeat = cell_repeat
|
105
|
+
workbook_cell = Workbook::Cell.new
|
107
106
|
workbook_cell.value = @cell.nil? ? nil : parse_local_cell(workbook_cell)
|
108
107
|
repeat.times do
|
109
108
|
workbook_row << workbook_cell
|
110
109
|
end
|
111
110
|
end
|
112
|
-
|
111
|
+
workbook_row
|
113
112
|
end
|
114
113
|
|
115
|
-
def
|
116
|
-
pre_set = @cell.xpath(
|
117
|
-
return 1 if
|
118
|
-
return 1 unless
|
114
|
+
def cell_repeat
|
115
|
+
pre_set = @cell.xpath("@table:number-columns-repeated").to_s
|
116
|
+
return 1 if pre_set.nil? || pre_set == "" # if not present, don't repeat.
|
117
|
+
return 1 unless pre_set.to_i.to_s == pre_set.to_s # return 1 if it's not a valid integer
|
119
118
|
return 1 if pre_set.to_i < 1 # return 1, negative repeats make no sense
|
120
|
-
|
119
|
+
pre_set.to_i
|
121
120
|
end
|
122
121
|
|
123
|
-
#parse the contents of a single cell
|
122
|
+
# parse the contents of a single cell
|
124
123
|
def parse_local_cell(workbook_cell)
|
125
124
|
return Workbook::NilValue.new(:covered) if @cell.name == "covered-table-cell"
|
126
|
-
|
127
|
-
valuetype = @cell.xpath(
|
125
|
+
configure_cell_attributes(workbook_cell)
|
126
|
+
valuetype = @cell.xpath("@office:value-type").to_s
|
128
127
|
parse_local_value(valuetype)
|
129
128
|
end
|
130
129
|
|
131
130
|
# Sets cell attributes for rowspan, colspan and format
|
132
|
-
def
|
133
|
-
workbook_cell.format =
|
134
|
-
workbook_cell.colspan= @cell.xpath(
|
135
|
-
workbook_cell.rowspan= @cell.xpath(
|
131
|
+
def configure_cell_attributes(workbook_cell)
|
132
|
+
workbook_cell.format = template.formats[@cell.xpath("@table:style-name").to_s]
|
133
|
+
workbook_cell.colspan = @cell.xpath("@table:number-columns-spanned").to_s
|
134
|
+
workbook_cell.rowspan = @cell.xpath("@table:number-rows-spanned").to_s
|
136
135
|
end
|
137
136
|
|
138
137
|
# Sets value in right context type
|
139
138
|
def parse_local_value(valuetype)
|
140
139
|
value = CGI.unescapeHTML(@cell.xpath("text:p//text()").to_s)
|
141
|
-
value =
|
140
|
+
value = value == "" ? nil : value
|
141
|
+
|
142
142
|
case valuetype
|
143
|
-
when
|
143
|
+
when "integer"
|
144
144
|
value = @cell.xpath("@office:value").to_s.to_i
|
145
|
-
when
|
145
|
+
when "float"
|
146
146
|
value = @cell.xpath("@office:value").to_s.to_f
|
147
|
-
value = value.to_i unless @cell.xpath("@office:value").to_s
|
148
|
-
when
|
147
|
+
value = value.to_i unless /\./.match?(@cell.xpath("@office:value").to_s) # sadly most integers are typed as floats...
|
148
|
+
when "date"
|
149
149
|
value = DateTime.parse(@cell.xpath("@office:date-value").to_s)
|
150
|
+
when "currency"
|
151
|
+
value = @cell.xpath("@office:value").to_s.to_f
|
150
152
|
end
|
151
153
|
value
|
152
154
|
end
|
153
|
-
|
154
155
|
end
|
155
156
|
end
|
156
157
|
end
|
@@ -1,19 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# -*- encoding : utf-8 -*-
|
4
2
|
# frozen_string_literal: true
|
3
|
+
|
5
4
|
module Workbook
|
6
5
|
module Readers
|
7
6
|
module TxtReader
|
8
|
-
def load_txt text, options={}
|
7
|
+
def load_txt text, options = {}
|
9
8
|
csv = text
|
10
|
-
parse_txt
|
9
|
+
parse_txt(csv, options)
|
11
10
|
end
|
12
11
|
|
13
|
-
def parse_txt
|
14
|
-
csv =
|
15
|
-
|
16
|
-
self[0]=Workbook::Sheet.new(csv,self,{:parse_cells_on_batch_creation=>true, :cell_parse_options=>{:detect_date=>true}}) unless sheet.has_contents?
|
12
|
+
def parse_txt(csv_raw, options = {})
|
13
|
+
csv = csv_raw.split("\n").collect { |l| CSV.parse_line(l, col_sep: "\t") }
|
14
|
+
self[0] = Workbook::Sheet.new(csv, self, parse_cells_on_batch_creation: true, cell_parse_options: {detect_date: true}) unless sheet.has_contents?
|
17
15
|
end
|
18
16
|
end
|
19
17
|
end
|
@@ -1,9 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# -*- encoding : utf-8 -*-
|
4
2
|
# frozen_string_literal: true
|
5
|
-
|
6
|
-
require
|
3
|
+
|
4
|
+
require "spreadsheet"
|
5
|
+
require "workbook/readers/xls_shared"
|
7
6
|
|
8
7
|
module Workbook
|
9
8
|
module Readers
|
@@ -11,20 +10,11 @@ module Workbook
|
|
11
10
|
include Workbook::Readers::XlsShared
|
12
11
|
|
13
12
|
def load_xls file_obj, options
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
begin
|
20
|
-
# Assuming it is a tab separated txt inside .xls
|
21
|
-
import(file_obj.path, 'txt')
|
22
|
-
rescue Exception => ef
|
23
|
-
|
24
|
-
raise ef
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
13
|
+
sp = Spreadsheet.open(file_obj, "rb")
|
14
|
+
template.add_raw sp
|
15
|
+
parse_xls sp, options
|
16
|
+
rescue Ole::Storage::FormatError
|
17
|
+
import(file_obj.path, "txt")
|
28
18
|
end
|
29
19
|
|
30
20
|
def parse_xls_cell xls_cell, xls_row, ci
|
@@ -33,9 +23,9 @@ module Workbook
|
|
33
23
|
rv = Workbook::Cell.new xls_cell
|
34
24
|
rv.parse!
|
35
25
|
rescue ArgumentError => e
|
36
|
-
if e.message.match(
|
26
|
+
if e.message.match?("not a Spreadsheet::Formula")
|
37
27
|
v = xls_cell.value
|
38
|
-
if v.
|
28
|
+
if v.instance_of?(Float) && xls_row.format(ci).date?
|
39
29
|
xls_row[ci] = v
|
40
30
|
v = xls_row.datetime(ci)
|
41
31
|
end
|
@@ -43,14 +33,12 @@ module Workbook
|
|
43
33
|
v = "----!"
|
44
34
|
end
|
45
35
|
rv = Workbook::Cell.new v
|
46
|
-
elsif e.message.match(
|
36
|
+
elsif e.message.match?("not a Spreadsheet::Link")
|
47
37
|
rv = Workbook::Cell.new xls_cell.to_s
|
48
|
-
elsif e.message.match(
|
49
|
-
rv = Workbook::Cell.new xls_cell.to_s
|
50
|
-
elsif e.message.match('not a Spreadsheet::Excel::Error')
|
38
|
+
elsif e.message.match?("not a Spreadsheet::Excel::Error")
|
51
39
|
rv = "._."
|
52
40
|
else
|
53
|
-
rv = "._."
|
41
|
+
rv = "._." # raise e (we're going to be silent for now)
|
54
42
|
end
|
55
43
|
end
|
56
44
|
rv
|
@@ -64,8 +52,8 @@ module Workbook
|
|
64
52
|
col_width = col_widths[ci]
|
65
53
|
end
|
66
54
|
|
67
|
-
f = template.create_or_find_format_by "object_id_#{xls_format.object_id}",col_width
|
68
|
-
f[:width]= col_width
|
55
|
+
f = template.create_or_find_format_by "object_id_#{xls_format.object_id}", col_width
|
56
|
+
f[:width] = col_width
|
69
57
|
f[:rotation] = xls_format.rotation if xls_format.rotation
|
70
58
|
f[:background_color] = xls_color_to_html_hex(xls_format.pattern_fg_color)
|
71
59
|
f[:number_format] = ms_formatting_to_strftime(xls_format.number_format)
|
@@ -81,15 +69,14 @@ module Workbook
|
|
81
69
|
def parse_xls_row ri, s, xls_sheet
|
82
70
|
xls_row = xls_sheet.row(ri)
|
83
71
|
r = s.table.create_or_open_row_at(ri)
|
84
|
-
col_widths = xls_sheet.columns.collect{|c| c
|
85
|
-
xls_row.each_with_index do |xls_cell,ci|
|
72
|
+
col_widths = xls_sheet.columns.collect { |c| c&.width }
|
73
|
+
xls_row.each_with_index do |xls_cell, ci|
|
86
74
|
r[ci] = parse_xls_cell xls_cell, xls_row, ci
|
87
75
|
r[ci].format = parse_xls_format xls_row, ci, ri, col_widths
|
88
76
|
end
|
89
77
|
end
|
90
78
|
|
91
|
-
def parse_xls xls_spreadsheet=template.raws[Spreadsheet::Excel::Workbook], options={}
|
92
|
-
options = {:additional_type_parsing=>true}.merge options
|
79
|
+
def parse_xls xls_spreadsheet = template.raws[Spreadsheet::Excel::Workbook], options = {}
|
93
80
|
number_of_worksheets = xls_spreadsheet.worksheets.count
|
94
81
|
number_of_worksheets.times do |si|
|
95
82
|
xls_sheet = xls_spreadsheet.worksheets[si]
|
@@ -103,15 +90,16 @@ module Workbook
|
|
103
90
|
end
|
104
91
|
rescue TypeError
|
105
92
|
puts "WARNING: Failed at worksheet (#{si})... ignored"
|
106
|
-
#ignore SpreadsheetGem can be buggy...
|
93
|
+
# ignore SpreadsheetGem can be buggy...
|
107
94
|
end
|
108
95
|
end
|
109
96
|
end
|
110
97
|
end
|
111
98
|
|
112
99
|
private
|
100
|
+
|
113
101
|
def xls_color_to_html_hex color_sym
|
114
|
-
Workbook::Book::XLS_COLORS[color_sym]
|
102
|
+
Workbook::Book::XLS_COLORS[color_sym] || "#000000"
|
115
103
|
end
|
116
104
|
end
|
117
105
|
end
|
@@ -1,13 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# -*- encoding : utf-8 -*-
|
4
2
|
# frozen_string_literal: true
|
5
|
-
|
3
|
+
|
4
|
+
require "date"
|
6
5
|
|
7
6
|
module Workbook
|
8
7
|
module Readers
|
9
8
|
module XlsShared
|
10
|
-
|
11
9
|
# Converts standard (ruby/C++/unix/...) strftime formatting to MS's formatting
|
12
10
|
#
|
13
11
|
# @param [String, nil] ms_nr_format (nil returns nil)
|
@@ -16,26 +14,26 @@ module Workbook
|
|
16
14
|
ms_nr_format = num_fmt_id_to_ms_formatting(ms_nr_format) if ms_nr_format.is_a? Integer
|
17
15
|
if ms_nr_format
|
18
16
|
ms_nr_format = ms_nr_format.to_s.downcase
|
19
|
-
return nil if ms_nr_format ==
|
17
|
+
return nil if (ms_nr_format == "general") || (ms_nr_format == "")
|
20
18
|
translation_table = {
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
19
|
+
"yyyy" => "%Y",
|
20
|
+
"dddd" => "%A",
|
21
|
+
"mmmm" => "%B",
|
22
|
+
"ddd" => "%a",
|
23
|
+
"mmm" => "%b",
|
24
|
+
"yy" => "%y",
|
25
|
+
"dd" => "%d",
|
26
|
+
"mm" => "%m",
|
27
|
+
"y" => "%y",
|
28
|
+
"%%y" => "%y",
|
29
|
+
"d" => "%e",
|
30
|
+
"%%e" => "%d",
|
31
|
+
"m" => "%m",
|
32
|
+
"%%m" => "%m",
|
33
|
+
";@" => "",
|
34
|
+
"\\" => ""
|
37
35
|
}
|
38
|
-
translation_table.each{|k,v| ms_nr_format.gsub!(k,v) }
|
36
|
+
translation_table.each { |k, v| ms_nr_format.gsub!(k, v) }
|
39
37
|
ms_nr_format
|
40
38
|
end
|
41
39
|
end
|
@@ -45,37 +43,30 @@ module Workbook
|
|
45
43
|
# @return [String] number format (excel markup)
|
46
44
|
def num_fmt_id_to_ms_formatting num_fmt_id
|
47
45
|
# from: https://stackoverflow.com/questions/4730152/what-indicates-an-office-open-xml-cell-contains-a-date-time-value
|
48
|
-
{
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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]
|
61
59
|
end
|
62
60
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
61
|
# Attempt to convert html-hex color value to xls color number
|
71
62
|
#
|
72
63
|
# @param [String] hex color
|
73
64
|
# @return [String] xls color
|
74
65
|
def html_color_to_xls_color hex
|
75
|
-
Workbook::Readers::XlsShared::XLS_COLORS.each do |k,v|
|
76
|
-
return k if (v == hex
|
66
|
+
Workbook::Readers::XlsShared::XLS_COLORS.each do |k, v|
|
67
|
+
return k if (v == hex) || (hex && (hex != "") && (k == hex.to_sym))
|
77
68
|
end
|
78
|
-
|
69
|
+
nil
|
79
70
|
end
|
80
71
|
|
81
72
|
# Converts standard (ruby/C++/unix/...) strftime formatting to MS's formatting
|
@@ -84,86 +75,84 @@ module Workbook
|
|
84
75
|
# @return [String, nil]
|
85
76
|
def strftime_to_ms_format numberformat
|
86
77
|
return nil if numberformat.nil?
|
87
|
-
|
78
|
+
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")
|
88
79
|
end
|
89
80
|
|
90
|
-
def xls_number_to_time number, base_date = DateTime.new(1899,12,30)
|
81
|
+
def xls_number_to_time number, base_date = DateTime.new(1899, 12, 30)
|
91
82
|
base_date + number.to_f
|
92
83
|
end
|
93
84
|
|
94
|
-
def xls_number_to_date number, base_date = Date.new(1899,12,30)
|
85
|
+
def xls_number_to_date number, base_date = Date.new(1899, 12, 30)
|
95
86
|
base_date + number.to_i
|
96
87
|
end
|
97
88
|
|
98
|
-
XLS_COLORS = {:
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
}
|
89
|
+
XLS_COLORS = {xls_color_1: "#000000",
|
90
|
+
xls_color_2: "#FFFFFF",
|
91
|
+
xls_color_3: "#FF0000",
|
92
|
+
xls_color_4: "#00FF00",
|
93
|
+
xls_color_5: "#0000FF",
|
94
|
+
xls_color_6: "#FFFF00",
|
95
|
+
xls_color_7: "#FF00FF",
|
96
|
+
xls_color_8: "#00FFFF",
|
97
|
+
xls_color_9: "#800000",
|
98
|
+
xls_color_10: "#008000",
|
99
|
+
xls_color_11: "#000080",
|
100
|
+
xls_color_12: "#808000",
|
101
|
+
xls_color_13: "#800080",
|
102
|
+
xls_color_14: "#008080",
|
103
|
+
xls_color_15: "#C0C0C0",
|
104
|
+
xls_color_16: "#808080",
|
105
|
+
xls_color_17: "#9999FF",
|
106
|
+
xls_color_18: "#993366",
|
107
|
+
xls_color_19: "#FFFFCC",
|
108
|
+
xls_color_20: "#CCFFFF",
|
109
|
+
xls_color_21: "#660066",
|
110
|
+
xls_color_22: "#FF8080",
|
111
|
+
xls_color_23: "#0066CC",
|
112
|
+
xls_color_24: "#CCCCFF",
|
113
|
+
xls_color_25: "#000080",
|
114
|
+
xls_color_26: "#FF00FF",
|
115
|
+
xls_color_27: "#FFFF00",
|
116
|
+
xls_color_28: "#00FFFF",
|
117
|
+
xls_color_29: "#800080",
|
118
|
+
xls_color_30: "#800000",
|
119
|
+
xls_color_31: "#008080",
|
120
|
+
xls_color_32: "#0000FF",
|
121
|
+
xls_color_33: "#00CCFF",
|
122
|
+
xls_color_34: "#CCFFFF",
|
123
|
+
xls_color_35: "#CCFFCC",
|
124
|
+
xls_color_36: "#FFFF99",
|
125
|
+
xls_color_37: "#99CCFF",
|
126
|
+
xls_color_38: "#FF99CC",
|
127
|
+
xls_color_39: "#CC99FF",
|
128
|
+
xls_color_40: "#FFCC99",
|
129
|
+
xls_color_41: "#3366FF",
|
130
|
+
xls_color_42: "#33CCCC",
|
131
|
+
xls_color_43: "#99CC00",
|
132
|
+
xls_color_44: "#FFCC00",
|
133
|
+
xls_color_45: "#FF9900",
|
134
|
+
xls_color_46: "#FF6600",
|
135
|
+
xls_color_47: "#666699",
|
136
|
+
xls_color_48: "#969696",
|
137
|
+
xls_color_49: "#003366",
|
138
|
+
xls_color_50: "#339966",
|
139
|
+
xls_color_51: "#003300",
|
140
|
+
xls_color_52: "#333300",
|
141
|
+
xls_color_53: "#993300",
|
142
|
+
xls_color_54: "#993366",
|
143
|
+
xls_color_55: "#333399",
|
144
|
+
xls_color_56: "#333333",
|
145
|
+
black: "#000000",
|
146
|
+
white: "#FFFFFF",
|
147
|
+
red: "#FF0000",
|
148
|
+
green: "#00FF00",
|
149
|
+
blue: "#0000FF",
|
150
|
+
yellow: "#FFFF00",
|
151
|
+
magenta: "#FF00FF",
|
152
|
+
cyan: "#00FFFF",
|
153
|
+
border: "#FFFFFF",
|
154
|
+
text: "#000000",
|
155
|
+
lime: "#00f94c"}
|
166
156
|
end
|
167
157
|
end
|
168
158
|
end
|
169
|
-
|