listen360-rubyXL 1.2.10.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +16 -0
- data/Gemfile.lock +34 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +197 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/lib/.DS_Store +0 -0
- data/lib/rubyXL.rb +10 -0
- data/lib/rubyXL/Hash.rb +60 -0
- data/lib/rubyXL/cell.rb +461 -0
- data/lib/rubyXL/color.rb +14 -0
- data/lib/rubyXL/parser.rb +468 -0
- data/lib/rubyXL/private_class.rb +265 -0
- data/lib/rubyXL/workbook.rb +450 -0
- data/lib/rubyXL/worksheet.rb +1493 -0
- data/lib/rubyXL/writer/app_writer.rb +62 -0
- data/lib/rubyXL/writer/calc_chain_writer.rb +33 -0
- data/lib/rubyXL/writer/content_types_writer.rb +77 -0
- data/lib/rubyXL/writer/core_writer.rb +51 -0
- data/lib/rubyXL/writer/root_rels_writer.rb +25 -0
- data/lib/rubyXL/writer/shared_strings_writer.rb +30 -0
- data/lib/rubyXL/writer/styles_writer.rb +407 -0
- data/lib/rubyXL/writer/theme_writer.rb +343 -0
- data/lib/rubyXL/writer/workbook_rels_writer.rb +59 -0
- data/lib/rubyXL/writer/workbook_writer.rb +77 -0
- data/lib/rubyXL/writer/worksheet_writer.rb +230 -0
- data/lib/rubyXL/zip.rb +20 -0
- data/rubyXL.gemspec +92 -0
- data/spec/lib/cell_spec.rb +385 -0
- data/spec/lib/color_spec.rb +14 -0
- data/spec/lib/hash_spec.rb +28 -0
- data/spec/lib/parser_spec.rb +66 -0
- data/spec/lib/workbook_spec.rb +51 -0
- data/spec/lib/worksheet_spec.rb +1782 -0
- metadata +207 -0
@@ -0,0 +1,265 @@
|
|
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_border(weight)
|
21
|
+
if weight.to_s == '' || weight == 'thin' || weight == 'thick' || weight == 'hairline' || weight == 'medium'
|
22
|
+
return true
|
23
|
+
end
|
24
|
+
raise 'Border weights must only be "hairline", "thin", "medium", or "thick"'
|
25
|
+
end
|
26
|
+
|
27
|
+
def validate_nonnegative(row_or_col)
|
28
|
+
if row_or_col < 0
|
29
|
+
raise 'Row and Column arguments must be nonnegative'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# This method checks to see if there is an equivalent font that exists
|
34
|
+
def find_font(workbook, font)
|
35
|
+
workbook.fonts.each {|font_id, f|
|
36
|
+
if f[:font][:i] == font[:i] &&
|
37
|
+
f[:font][:b] == font[:b] &&
|
38
|
+
f[:font][:u] == font[:u] &&
|
39
|
+
f[:font][:strike] == font[:strike] &&
|
40
|
+
f[:font][:name][:attributes][:val] == font[:name][:attributes][:val] &&
|
41
|
+
f[:font][:sz][:attributes][:val] == font[:sz][:attributes][:val] &&
|
42
|
+
(f[:font][:color] && f[:font][:color][:attributes][:rgb]) == (font[:color] && font[:color][:attributes][:rgb])
|
43
|
+
return font_id
|
44
|
+
end
|
45
|
+
}
|
46
|
+
return nil
|
47
|
+
end
|
48
|
+
|
49
|
+
# Helper method to modify the font color
|
50
|
+
def modify_font_color(font, font_color)
|
51
|
+
if font[:color].nil?
|
52
|
+
font[:color] = {:attributes => {:rgb => ''}}
|
53
|
+
end
|
54
|
+
font[:color][:attributes][:rgb] = font_color.to_s
|
55
|
+
return font
|
56
|
+
end
|
57
|
+
|
58
|
+
# Helper method to modify the font's italics settings
|
59
|
+
def modify_font_italics(font, italicized)
|
60
|
+
if italicized
|
61
|
+
font[:i] = {}
|
62
|
+
else
|
63
|
+
font[:i] = nil
|
64
|
+
end
|
65
|
+
return font
|
66
|
+
end
|
67
|
+
|
68
|
+
# Helper method to modify the font's bold settings
|
69
|
+
def modify_font_bold(font, bolded)
|
70
|
+
if bolded
|
71
|
+
font[:b] = {}
|
72
|
+
else
|
73
|
+
font[:b] = nil
|
74
|
+
end
|
75
|
+
return font
|
76
|
+
end
|
77
|
+
|
78
|
+
# Helper method to modify the font's underline settings
|
79
|
+
def modify_font_underline(font, underlined)
|
80
|
+
if underlined
|
81
|
+
font[:u] = {}
|
82
|
+
else
|
83
|
+
font[:u] = nil
|
84
|
+
end
|
85
|
+
return font
|
86
|
+
end
|
87
|
+
|
88
|
+
# Helper method to modify the font's strikethrough settings
|
89
|
+
def modify_font_strikethrough(font, struckthrough)
|
90
|
+
if struckthrough
|
91
|
+
font[:strike] = {}
|
92
|
+
else
|
93
|
+
font[:strike] = nil
|
94
|
+
end
|
95
|
+
return font
|
96
|
+
end
|
97
|
+
|
98
|
+
# Determines if font exists
|
99
|
+
# If yes, return id of existing font
|
100
|
+
# If no, appends font to font array
|
101
|
+
def modify_font(workbook, font, old_font_id)
|
102
|
+
font_id = old_font_id
|
103
|
+
existing_font_id = find_font(workbook, font)
|
104
|
+
if !existing_font_id.nil?
|
105
|
+
font_id = existing_font_id
|
106
|
+
workbook.fonts[font_id][:count] += 1
|
107
|
+
workbook.fonts[old_font_id][:count] -= 1
|
108
|
+
elsif workbook.fonts[old_font_id.to_s][:count] > 1 || old_font_id == '0'
|
109
|
+
font_id = workbook.fonts.size.to_s
|
110
|
+
workbook.fonts[font_id] = {}
|
111
|
+
workbook.fonts[font_id][:font] = font
|
112
|
+
workbook.fonts[font_id][:count] = 1
|
113
|
+
workbook.fonts[old_font_id][:count] -= 1
|
114
|
+
else
|
115
|
+
workbook.fonts[font_id][:font] = font
|
116
|
+
end
|
117
|
+
return font_id
|
118
|
+
end
|
119
|
+
|
120
|
+
# This method checks to see if there is an equivalent xf that exists
|
121
|
+
def find_xf(workbook, xf)
|
122
|
+
workbook.cell_xfs[:xf].each_with_index {|xfs, index|
|
123
|
+
if xfs[:attributes][:borderId] == xf[:borderId] &&
|
124
|
+
xfs[:attributes][:xfId] == xf[:xfId] &&
|
125
|
+
xfs[:attributes][:fillId] == xf[:fillId] &&
|
126
|
+
xfs[:attributes][:numFmtId] == xf[:numFmtId] &&
|
127
|
+
xfs[:attributes][:fontId] == xf[:fontId]
|
128
|
+
return index
|
129
|
+
end
|
130
|
+
}
|
131
|
+
return nil
|
132
|
+
end
|
133
|
+
|
134
|
+
# Determines if xf exists
|
135
|
+
# If yes, return id of existing xf
|
136
|
+
# If no, appends xf to xf array
|
137
|
+
def modify_xf(workbook, xf)
|
138
|
+
existing_xf_id = find_xf(workbook, xf)
|
139
|
+
if !existing_xf_id.nil?
|
140
|
+
xf_id = existing_xf_id
|
141
|
+
else
|
142
|
+
if workbook.cell_xfs[:xf].is_a?Array
|
143
|
+
workbook.cell_xfs[:xf] << {:attributes=>xf}
|
144
|
+
else
|
145
|
+
workbook.cell_xfs[:xf] = [workbook.cell_xfs[:xf], {:attributes=>xf}]
|
146
|
+
end
|
147
|
+
xf[:applyFont] = '1'
|
148
|
+
workbook.cell_xfs[:attributes][:count] += 1
|
149
|
+
xf_id = workbook.cell_xfs[:xf].size - 1
|
150
|
+
end
|
151
|
+
return xf_id
|
152
|
+
end
|
153
|
+
|
154
|
+
#modifies fill array (copies, appends, adds color and solid attribute)
|
155
|
+
#then styles array (copies, appends)
|
156
|
+
def modify_fill(workbook, style_index, rgb)
|
157
|
+
xf_obj = workbook.get_style(style_index)
|
158
|
+
xf = workbook.get_style_attributes(xf_obj)
|
159
|
+
#modify fill array
|
160
|
+
fill_id = xf[:fillId]
|
161
|
+
|
162
|
+
fill = workbook.fills[fill_id.to_s][:fill]
|
163
|
+
if workbook.fills[fill_id.to_s][:count] > 1 || fill_id == 0 || fill_id == 1
|
164
|
+
old_size = workbook.fills.size.to_s
|
165
|
+
workbook.fills[old_size] = {}
|
166
|
+
workbook.fills[old_size][:fill] = deep_copy(fill)
|
167
|
+
workbook.fills[old_size][:count] = 1
|
168
|
+
workbook.fills[fill_id.to_s][:count] -= 1
|
169
|
+
|
170
|
+
change_wb_fill(workbook, old_size,rgb)
|
171
|
+
|
172
|
+
#modify styles array
|
173
|
+
fill_id = old_size
|
174
|
+
if workbook.cell_xfs[:xf].is_a?Array
|
175
|
+
workbook.cell_xfs[:xf] << deep_copy({:attributes=>xf})
|
176
|
+
else
|
177
|
+
workbook.cell_xfs[:xf] = [workbook.cell_xfs[:xf], deep_copy({:attributes=>xf})]
|
178
|
+
end
|
179
|
+
xf = workbook.get_style_attributes(workbook.cell_xfs[:xf].last)
|
180
|
+
xf[:fillId] = fill_id
|
181
|
+
xf[:applyFill] = '1'
|
182
|
+
workbook.cell_xfs[:attributes][:count] += 1
|
183
|
+
return workbook.cell_xfs[:xf].size-1
|
184
|
+
else
|
185
|
+
change_wb_fill(workbook, fill_id.to_s,rgb)
|
186
|
+
return style_index
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def modify_border(workbook, style_index)
|
191
|
+
xf_obj = workbook.get_style(style_index)
|
192
|
+
xf = workbook.get_style_attributes(xf_obj)
|
193
|
+
|
194
|
+
border_id = Integer(xf[:borderId])
|
195
|
+
border = workbook.borders[border_id.to_s][:border]
|
196
|
+
if workbook.borders[border_id.to_s][:count] > 1 || border_id == 0 || border_id == 1
|
197
|
+
old_size = workbook.borders.size.to_s
|
198
|
+
workbook.borders[old_size] = {}
|
199
|
+
workbook.borders[old_size][:border] = deep_copy(border)
|
200
|
+
workbook.borders[old_size][:count] = 1
|
201
|
+
|
202
|
+
border_id = old_size
|
203
|
+
|
204
|
+
if workbook.cell_xfs[:xf].is_a?Array
|
205
|
+
workbook.cell_xfs[:xf] << deep_copy(xf_obj)
|
206
|
+
else
|
207
|
+
workbook.cell_xfs[:xf] = [workbook.cell_xfs[:xf], deep_copy(xf_obj)]
|
208
|
+
end
|
209
|
+
|
210
|
+
xf = workbook.get_style_attributes(workbook.cell_xfs[:xf].last)
|
211
|
+
xf[:borderId] = border_id
|
212
|
+
xf[:applyBorder] = '1'
|
213
|
+
workbook.cell_xfs[:attributes][:count] += 1
|
214
|
+
return workbook.cell_xfs[:xf].size-1
|
215
|
+
else
|
216
|
+
return style_index
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
#is_horizontal is true when doing horizontal alignment,
|
221
|
+
#false when doing vertical alignment
|
222
|
+
def modify_alignment(workbook, style_index, is_horizontal, alignment)
|
223
|
+
old_xf_obj = workbook.get_style(style_index)
|
224
|
+
|
225
|
+
xf_obj = deep_copy(old_xf_obj)
|
226
|
+
|
227
|
+
if xf_obj[:alignment].nil? || xf_obj[:alignment][:attributes].nil?
|
228
|
+
xf_obj[:alignment] = {:attributes=>{:horizontal=>nil, :vertical=>nil}}
|
229
|
+
end
|
230
|
+
|
231
|
+
if is_horizontal
|
232
|
+
xf_obj[:alignment][:attributes][:horizontal] = alignment.to_s
|
233
|
+
else
|
234
|
+
xf_obj[:alignment][:attributes][:vertical] = alignment.to_s
|
235
|
+
end
|
236
|
+
|
237
|
+
if workbook.cell_xfs[:xf].is_a?Array
|
238
|
+
workbook.cell_xfs[:xf] << deep_copy(xf_obj)
|
239
|
+
else
|
240
|
+
workbook.cell_xfs[:xf] = [workbook.cell_xfs[:xf], deep_copy(xf_obj)]
|
241
|
+
end
|
242
|
+
|
243
|
+
xf = workbook.get_style_attributes(workbook.cell_xfs[:xf].last)
|
244
|
+
xf[:applyAlignment] = '1'
|
245
|
+
workbook.cell_xfs[:attributes][:count] += 1
|
246
|
+
workbook.cell_xfs[:xf].size-1
|
247
|
+
end
|
248
|
+
|
249
|
+
#returns non-shallow copy of hash
|
250
|
+
def deep_copy(hash)
|
251
|
+
Marshal.load(Marshal.dump(hash))
|
252
|
+
end
|
253
|
+
|
254
|
+
def change_wb_fill(workbook, fill_index, rgb)
|
255
|
+
if workbook.fills[fill_index][:fill][:patternFill][:fgColor].nil?
|
256
|
+
workbook.fills[fill_index][:fill][:patternFill][:fgColor] = {:attributes => {:rgb => ''}}
|
257
|
+
end
|
258
|
+
workbook.fills[fill_index][:fill][:patternFill][:fgColor][:attributes][:rgb] = rgb
|
259
|
+
|
260
|
+
#previously none, doesn't show fill
|
261
|
+
workbook.fills[fill_index][:fill][:patternFill][:attributes][:patternType] = 'solid'
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|
265
|
+
end
|
@@ -0,0 +1,450 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'writer','content_types_writer'))
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'writer','root_rels_writer'))
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'writer','app_writer'))
|
4
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'writer','core_writer'))
|
5
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'writer','theme_writer'))
|
6
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'writer','workbook_rels_writer'))
|
7
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'writer','workbook_writer'))
|
8
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'writer','styles_writer'))
|
9
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'writer','shared_strings_writer'))
|
10
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'writer','worksheet_writer'))
|
11
|
+
require 'rubyXL/zip'
|
12
|
+
require 'date'
|
13
|
+
|
14
|
+
module RubyXL
|
15
|
+
class Workbook
|
16
|
+
include Enumerable
|
17
|
+
attr_accessor :worksheets, :filepath, :creator, :modifier, :created_at,
|
18
|
+
:modified_at, :company, :application, :appversion, :num_fmts, :num_fmts_hash, :fonts, :fills,
|
19
|
+
:borders, :cell_xfs, :cell_style_xfs, :cell_styles, :shared_strings, :calc_chain,
|
20
|
+
:num_strings, :size, :date1904, :external_links, :style_corrector, :drawings,
|
21
|
+
:worksheet_rels, :printer_settings, :macros, :colors, :shared_strings_XML, :defined_names, :column_lookup_hash
|
22
|
+
|
23
|
+
|
24
|
+
APPLICATION = 'Microsoft Macintosh Excel'
|
25
|
+
APPVERSION = '12.0000'
|
26
|
+
SHEET_NAME = 'Sheet1'
|
27
|
+
def initialize(worksheets=[], filepath=nil, creator=nil, modifier=nil, created_at=nil,
|
28
|
+
company='', application=APPLICATION,
|
29
|
+
appversion=APPVERSION, date1904=0)
|
30
|
+
if worksheets.nil? || worksheets.empty?
|
31
|
+
@worksheets = [Worksheet.new(self,SHEET_NAME)]
|
32
|
+
else
|
33
|
+
@worksheets = worksheets
|
34
|
+
end
|
35
|
+
@filepath = filepath
|
36
|
+
@creator = creator
|
37
|
+
@modifier = modifier
|
38
|
+
@company = company
|
39
|
+
@application = application
|
40
|
+
@appversion = appversion
|
41
|
+
@num_fmts = nil
|
42
|
+
@num_fmts_hash = nil
|
43
|
+
@fonts = nil
|
44
|
+
@fills = nil
|
45
|
+
@borders = nil
|
46
|
+
@cell_xfs = nil
|
47
|
+
@cell_style_xfs = nil
|
48
|
+
@cell_styles = nil
|
49
|
+
@shared_strings = nil
|
50
|
+
@calc_chain = nil #unnecessary?
|
51
|
+
@num_strings = 0 #num strings total
|
52
|
+
@size = 0 #num strings in shared_strings array
|
53
|
+
@date1904 = date1904 > 0
|
54
|
+
@external_links = nil
|
55
|
+
@style_corrector = nil
|
56
|
+
@drawings = nil
|
57
|
+
@worksheet_rels = nil
|
58
|
+
@printer_settings = nil
|
59
|
+
@macros = nil
|
60
|
+
@colors = nil
|
61
|
+
@shared_strings_XML = nil
|
62
|
+
@defined_names = nil
|
63
|
+
@column_lookup_hash = {}
|
64
|
+
|
65
|
+
begin
|
66
|
+
@created_at = DateTime.parse(created_at).strftime('%Y-%m-%dT%TZ')
|
67
|
+
rescue
|
68
|
+
t = Time.now
|
69
|
+
@created_at = t.strftime('%Y-%m-%dT%TZ')
|
70
|
+
end
|
71
|
+
@modified_at = @created_at
|
72
|
+
|
73
|
+
fill_styles()
|
74
|
+
fill_shared_strings()
|
75
|
+
end
|
76
|
+
|
77
|
+
# allows easier access to worksheets
|
78
|
+
def [](worksheet)
|
79
|
+
return worksheets[worksheet]
|
80
|
+
end
|
81
|
+
|
82
|
+
def each
|
83
|
+
worksheets.each{|i| yield i}
|
84
|
+
end
|
85
|
+
|
86
|
+
def num_fmts_by_id
|
87
|
+
|
88
|
+
return @num_fmts_hash unless @num_fmts_hash.nil?
|
89
|
+
if num_fmts
|
90
|
+
@num_fmts_hash={}
|
91
|
+
num_fmts[:numFmt].each do |num_fmt|
|
92
|
+
@num_fmts_hash[num_fmt[:attributes][:numFmtId]]=num_fmt
|
93
|
+
end
|
94
|
+
@num_fmts_hash
|
95
|
+
else
|
96
|
+
{}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
#filepath of xlsx file (including file itself)
|
101
|
+
def write(filepath=@filepath)
|
102
|
+
validate_before_write
|
103
|
+
if !(filepath =~ /(.+)\.xls(x|m)/)
|
104
|
+
raise "Only xlsx and xlsm files are supported. Unsupported type for file: #{filepath}"
|
105
|
+
end
|
106
|
+
dirpath = ''
|
107
|
+
extension = 'xls'
|
108
|
+
if(filepath =~ /((.|\s)*)\.xls(x|m)$/)
|
109
|
+
dirpath = $1.to_s()
|
110
|
+
extension += $3.to_s
|
111
|
+
end
|
112
|
+
filename = ''
|
113
|
+
if(filepath =~ /\/((.|\s)*)\/((.|\s)*)\.xls(x|m)$/)
|
114
|
+
filename = $3.to_s()
|
115
|
+
end
|
116
|
+
|
117
|
+
#creates zip file, writes each type of file to zip folder
|
118
|
+
#zips package and renames it to xlsx.
|
119
|
+
zippath = File.join(dirpath, filename + '.zip')
|
120
|
+
File.unlink(zippath) if File.exists?(zippath)
|
121
|
+
FileUtils.mkdir_p(File.join(dirpath,zippath))
|
122
|
+
Zip::ZipFile.open(zippath, Zip::ZipFile::CREATE) do |zipfile|
|
123
|
+
writer = Writer::ContentTypesWriter.new(dirpath,self)
|
124
|
+
zipfile.get_output_stream('[Content_Types].xml') {|f| f.puts(writer.write())}
|
125
|
+
|
126
|
+
writer = Writer::RootRelsWriter.new(dirpath,self)
|
127
|
+
zipfile.get_output_stream(File.join('_rels','.rels')) {|f| f.puts(writer.write())}
|
128
|
+
|
129
|
+
writer = Writer::AppWriter.new(dirpath,self)
|
130
|
+
zipfile.get_output_stream(File.join('docProps','app.xml')) {|f| f.puts(writer.write())}
|
131
|
+
|
132
|
+
writer = Writer::CoreWriter.new(dirpath,self)
|
133
|
+
zipfile.get_output_stream(File.join('docProps','core.xml')) {|f| f.puts(writer.write())}
|
134
|
+
|
135
|
+
writer = Writer::ThemeWriter.new(dirpath,self)
|
136
|
+
zipfile.get_output_stream(File.join('xl','theme','theme1.xml')) {|f| f.puts(writer.write())}
|
137
|
+
|
138
|
+
writer = Writer::WorkbookRelsWriter.new(dirpath,self)
|
139
|
+
zipfile.get_output_stream(File.join('xl','_rels','workbook.xml.rels')) {|f| f.puts(writer.write())}
|
140
|
+
|
141
|
+
writer = Writer::WorkbookWriter.new(dirpath,self)
|
142
|
+
zipfile.get_output_stream(File.join('xl','workbook.xml')) {|f| f.puts(writer.write())}
|
143
|
+
|
144
|
+
writer = Writer::StylesWriter.new(dirpath,self)
|
145
|
+
zipfile.get_output_stream(File.join('xl','styles.xml')) {|f| f.puts(writer.write())}
|
146
|
+
|
147
|
+
unless @shared_strings.nil?
|
148
|
+
writer = Writer::SharedStringsWriter.new(dirpath,self)
|
149
|
+
zipfile.get_output_stream(File.join('xl','sharedStrings.xml')) {|f| f.puts(writer.write())}
|
150
|
+
end
|
151
|
+
|
152
|
+
#preserves external links (exactly, no modification allowed)
|
153
|
+
unless @external_links.nil?
|
154
|
+
#-1 because of rels
|
155
|
+
1.upto(@external_links.size-1) do |i|
|
156
|
+
zipfile.get_output_stream(
|
157
|
+
File.join('xl','externalLinks',"externalLink#{i}.xml")) {|f|
|
158
|
+
f.puts(@external_links[i])
|
159
|
+
}
|
160
|
+
end
|
161
|
+
@external_links['rels'].each_index do |i|
|
162
|
+
unless @external_links['rels'][i].nil?
|
163
|
+
zipfile.get_output_stream(
|
164
|
+
File.join('xl','externalLinks','_rels',"externalLink#{i}.xml.rels")) {|f|
|
165
|
+
f.puts(@external_links['rels'][i])
|
166
|
+
}
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
#preserves drawings (exactly, no modification allowed)
|
172
|
+
unless @drawings.nil?
|
173
|
+
1.upto(@drawings.size) do |i|
|
174
|
+
zipfile.get_output_stream(
|
175
|
+
File.join('xl','drawings',"vmlDrawing#{i}.vml")) {|f|
|
176
|
+
f.puts(@drawings[i])
|
177
|
+
}
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
unless @printer_settings.nil?
|
182
|
+
1.upto(@printer_settings.size) do |i|
|
183
|
+
zipfile.get_output_stream(
|
184
|
+
File.join('xl','printerSettings',"printerSettings#{i}.bin")) {|f|
|
185
|
+
f.puts(@printer_settings[i])
|
186
|
+
}
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
unless @worksheet_rels.nil?
|
191
|
+
1.upto(@worksheet_rels.size) do |i|
|
192
|
+
zipfile.get_output_stream(
|
193
|
+
File.join('xl','worksheets','_rels',"sheet#{i}.xml.rels")) {|f|
|
194
|
+
f.puts(@worksheet_rels[i])
|
195
|
+
}
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
unless @macros.nil?
|
200
|
+
zipfile.get_output_stream(File.join('xl','vbaProject.bin')) {|f| f.puts(@macros)}
|
201
|
+
end
|
202
|
+
|
203
|
+
@worksheets.each_with_index do |sheet,i|
|
204
|
+
writer = Writer::WorksheetWriter.new(dirpath,self,i)
|
205
|
+
zipfile.get_output_stream(File.join('xl','worksheets',"sheet#{i+1}.xml")) {|f| f.puts(writer.write())}
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
FileUtils.cp(zippath,File.join(dirpath,filename+".#{extension}"))
|
210
|
+
FileUtils.cp(File.join(dirpath,filename+".#{extension}"),filepath)
|
211
|
+
if File.exist?(filepath)
|
212
|
+
FileUtils.rm_rf(dirpath)
|
213
|
+
end
|
214
|
+
return filepath
|
215
|
+
end
|
216
|
+
|
217
|
+
def date_to_num(date)
|
218
|
+
return nil if date.nil?
|
219
|
+
if @date1904
|
220
|
+
compare_date = DateTime.parse('December 31, 1903')
|
221
|
+
else
|
222
|
+
compare_date = DateTime.parse('December 31, 1899')
|
223
|
+
end
|
224
|
+
# add one day to compare date for erroneous 1900 leap year compatibility
|
225
|
+
date.ajd + 1 - compare_date.ajd
|
226
|
+
end
|
227
|
+
|
228
|
+
def num_to_date(num)
|
229
|
+
return nil if num.nil?
|
230
|
+
if @date1904
|
231
|
+
compare_date = DateTime.parse('December 31, 1903')
|
232
|
+
else
|
233
|
+
compare_date = DateTime.parse('December 31, 1899')
|
234
|
+
end
|
235
|
+
# subtract one day to compare date for erroneous 1900 leap year compatibility
|
236
|
+
compare_date - 1 + num
|
237
|
+
end
|
238
|
+
|
239
|
+
def date_num_fmt?(num_fmt)
|
240
|
+
@num_fmt_date_hash ||= {}
|
241
|
+
if @num_fmt_date_hash[num_fmt].nil?
|
242
|
+
@num_fmt_date_hash[num_fmt] = is_date_format?(num_fmt)
|
243
|
+
end
|
244
|
+
return @num_fmt_date_hash[num_fmt]
|
245
|
+
end
|
246
|
+
|
247
|
+
def is_date_format?(num_fmt)
|
248
|
+
skip_chars = ['$', '-', '+', '/', '(', ')', ':', ' ']
|
249
|
+
num_chars = ['0', '#', '?']
|
250
|
+
non_date_formats = ['0.00E+00', '##0.0E+0', 'General', 'GENERAL', 'general', '@']
|
251
|
+
date_chars = ['y','m','d','h','s']
|
252
|
+
|
253
|
+
state = 0
|
254
|
+
s = ''
|
255
|
+
num_fmt.split(//).each do |c|
|
256
|
+
if state == 0
|
257
|
+
if c == '"'
|
258
|
+
state = 1
|
259
|
+
elsif ['\\', '_', '*'].include?(c)
|
260
|
+
state = 2
|
261
|
+
elsif skip_chars.include?(c)
|
262
|
+
next
|
263
|
+
else
|
264
|
+
s << c
|
265
|
+
end
|
266
|
+
elsif state == 1
|
267
|
+
if c == '"'
|
268
|
+
state = 0
|
269
|
+
end
|
270
|
+
elsif state == 2
|
271
|
+
state = 0
|
272
|
+
end
|
273
|
+
end
|
274
|
+
s.gsub!(/\[[^\]]*\]/, '')
|
275
|
+
if non_date_formats.include?(s)
|
276
|
+
return false
|
277
|
+
end
|
278
|
+
separator = ';'
|
279
|
+
got_sep = 0
|
280
|
+
date_count = 0
|
281
|
+
num_count = 0
|
282
|
+
s.split(//).each do |c|
|
283
|
+
if date_chars.include?(c)
|
284
|
+
date_count += 1
|
285
|
+
elsif num_chars.include?(c)
|
286
|
+
num_count += 1
|
287
|
+
elsif c == separator
|
288
|
+
got_sep = 1
|
289
|
+
end
|
290
|
+
end
|
291
|
+
if date_count > 0 && num_count == 0
|
292
|
+
return true
|
293
|
+
elsif num_count > 0 && date_count == 0
|
294
|
+
return false
|
295
|
+
elsif date_count
|
296
|
+
# ambiguous result
|
297
|
+
elsif got_sep == 0
|
298
|
+
# constant result
|
299
|
+
end
|
300
|
+
return date_count > num_count
|
301
|
+
end
|
302
|
+
|
303
|
+
#gets style object from style array given index
|
304
|
+
def get_style(style_index)
|
305
|
+
if !@cell_xfs[:xf].is_a?Array
|
306
|
+
@cell_xfs[:xf] = [@cell_xfs[:xf]]
|
307
|
+
end
|
308
|
+
|
309
|
+
xf_obj = @cell_xfs[:xf]
|
310
|
+
if xf_obj.is_a?Array
|
311
|
+
xf_obj = xf_obj[Integer(style_index)]
|
312
|
+
end
|
313
|
+
xf_obj
|
314
|
+
end
|
315
|
+
|
316
|
+
#gets attributes of above style object
|
317
|
+
#necessary because can take the form of hash or array,
|
318
|
+
#based on odd behavior of Nokogiri
|
319
|
+
def get_style_attributes(xf_obj)
|
320
|
+
if xf_obj.is_a?Array
|
321
|
+
xf = xf_obj[1]
|
322
|
+
else
|
323
|
+
xf = xf_obj[:attributes]
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
def get_fill_color(xf_attributes)
|
328
|
+
if @fills[xf_attributes[:fillId]].nil? || @fills[xf_attributes[:fillId]][:fill].nil? || @fills[xf_attributes[:fillId]][:fill][:patternFill].nil? || @fills[xf_attributes[:fillId]][:fill][:patternFill][:fgColor].nil?
|
329
|
+
'ffffff' #white
|
330
|
+
else
|
331
|
+
@fills[xf_attributes[:fillId]][:fill][:patternFill][:fgColor][:attributes][:rgb]
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
|
336
|
+
private
|
337
|
+
|
338
|
+
# Do not change. Excel requires that some of these styles be default,
|
339
|
+
# and will simply assume that the 0 and 1 indexed fonts are the default values.
|
340
|
+
def fill_styles()
|
341
|
+
@fonts = {
|
342
|
+
'0' => {
|
343
|
+
:font => {
|
344
|
+
:sz => { :attributes => { :val => 10 } },
|
345
|
+
:name => { :attributes => { :val => "Verdana" } }
|
346
|
+
},
|
347
|
+
:count=>1
|
348
|
+
},
|
349
|
+
'1' => {
|
350
|
+
:font => {
|
351
|
+
:sz => { :attributes => { :val => 8 } },
|
352
|
+
:name => { :attributes => { :val => "Verdana" } }
|
353
|
+
},
|
354
|
+
:count=>0
|
355
|
+
}
|
356
|
+
}
|
357
|
+
|
358
|
+
@fills = {
|
359
|
+
'0' => {
|
360
|
+
:fill => {
|
361
|
+
:patternFill => { :attributes => { :patternType => "none" } }
|
362
|
+
},
|
363
|
+
:count=>1} ,
|
364
|
+
'1' => {
|
365
|
+
:fill => {
|
366
|
+
:patternFill => { :attributes => { :patternType => "gray125" } }
|
367
|
+
},
|
368
|
+
:count=>0
|
369
|
+
}
|
370
|
+
}
|
371
|
+
|
372
|
+
@borders = {
|
373
|
+
'0' => {
|
374
|
+
:border => {
|
375
|
+
:left => { },
|
376
|
+
:right => { },
|
377
|
+
:top => { },
|
378
|
+
:bottom => { },
|
379
|
+
:diagonal => { }
|
380
|
+
},
|
381
|
+
:count => 1 #count = how many styles reference it
|
382
|
+
}
|
383
|
+
}
|
384
|
+
|
385
|
+
@cell_style_xfs = {
|
386
|
+
:attributes => {
|
387
|
+
:count => 1
|
388
|
+
},
|
389
|
+
:xf => {
|
390
|
+
:attributes => { :numFmtId => 0, :fontId => 0, :fillId => 0, :borderId => 0 }
|
391
|
+
}
|
392
|
+
}
|
393
|
+
@cell_xfs = {
|
394
|
+
:attributes => {
|
395
|
+
:count => 1
|
396
|
+
},
|
397
|
+
:xf => {
|
398
|
+
:attributes => { :numFmtId => 0, :fontId => 0, :fillId => 0, :borderId => 0, :xfId => 0 }
|
399
|
+
}
|
400
|
+
}
|
401
|
+
@cell_styles = {
|
402
|
+
:cellStyle => {
|
403
|
+
:attributes => { :builtinId=>0, :name=>"Normal", :xfId=>0 }
|
404
|
+
},
|
405
|
+
:attributes => { :count => 1 }
|
406
|
+
}
|
407
|
+
end
|
408
|
+
|
409
|
+
|
410
|
+
#fills shared strings hash, contains each unique string
|
411
|
+
def fill_shared_strings()
|
412
|
+
if @shared_strings.nil?
|
413
|
+
string_hash = {}
|
414
|
+
string_index = 0
|
415
|
+
@num_strings = 0
|
416
|
+
#fill hash for shared strings
|
417
|
+
@worksheets.each do |sheet|
|
418
|
+
unless sheet.nil?
|
419
|
+
sheet.sheet_data.each do |row|
|
420
|
+
row.each do |cell|
|
421
|
+
unless cell.nil? || cell.value.nil?
|
422
|
+
#if string not already seen, add it to hash
|
423
|
+
if cell.datatype == 's'
|
424
|
+
if string_hash[cell.value.to_s].nil?
|
425
|
+
string_hash[string_index]=cell.value.to_s
|
426
|
+
string_hash[cell.value.to_s]=string_index
|
427
|
+
string_index += 1
|
428
|
+
end
|
429
|
+
@num_strings += 1
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
if string_hash.empty?
|
438
|
+
@shared_strings = nil
|
439
|
+
else
|
440
|
+
@shared_strings = string_hash
|
441
|
+
@size = string_index
|
442
|
+
end
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
def validate_before_write
|
447
|
+
## TODO CHECK IF STYLE IS OK if not raise
|
448
|
+
end
|
449
|
+
end
|
450
|
+
end
|