rubyXL 1.0.4
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.
- data/Gemfile +16 -0
- data/Gemfile.lock +34 -0
- data/LICENSE.txt +20 -0
- data/README +0 -0
- data/README.rdoc +19 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/lib/.DS_Store +0 -0
- data/lib/Hash.rb +60 -0
- data/lib/cell.rb +360 -0
- data/lib/color.rb +14 -0
- data/lib/parser.rb +413 -0
- data/lib/private_class.rb +182 -0
- data/lib/rubyXL.rb +9 -0
- data/lib/test.html +1 -0
- data/lib/tests/test.rb +110 -0
- data/lib/tests/test10.rb +16 -0
- data/lib/tests/test2.rb +118 -0
- data/lib/tests/test3.rb +76 -0
- data/lib/tests/test4.rb +92 -0
- data/lib/tests/test5.rb +90 -0
- data/lib/tests/test6.rb +50 -0
- data/lib/tests/test7.rb +48 -0
- data/lib/tests/test8.rb +12 -0
- data/lib/tests/test9.rb +60 -0
- data/lib/workbook.rb +336 -0
- data/lib/worksheet.rb +1245 -0
- data/lib/writer/app_writer.rb +62 -0
- data/lib/writer/calc_chain_writer.rb +33 -0
- data/lib/writer/content_types_writer.rb +77 -0
- data/lib/writer/core_writer.rb +51 -0
- data/lib/writer/root_rels_writer.rb +25 -0
- data/lib/writer/shared_strings_writer.rb +44 -0
- data/lib/writer/styles_writer.rb +376 -0
- data/lib/writer/theme_writer.rb +346 -0
- data/lib/writer/workbook_rels_writer.rb +59 -0
- data/lib/writer/workbook_writer.rb +77 -0
- data/lib/writer/worksheet_writer.rb +208 -0
- data/lib/zip.rb +20 -0
- data/pkg/rubyXL-1.0.4.gem +0 -0
- data/rubyXL.gemspec +106 -0
- data/spec/lib/cell_spec.rb +359 -0
- data/spec/lib/color_spec.rb +14 -0
- data/spec/lib/hash_spec.rb +28 -0
- data/spec/lib/parser_spec.rb +49 -0
- data/spec/lib/workbook_spec.rb +51 -0
- data/spec/lib/worksheet_spec.rb +1650 -0
- metadata +222 -0
data/lib/color.rb
ADDED
data/lib/parser.rb
ADDED
@@ -0,0 +1,413 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'zip/zip' #rubyzip
|
4
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'html_generator'))
|
5
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'Hash'))
|
6
|
+
|
7
|
+
module RubyXL
|
8
|
+
|
9
|
+
class Parser
|
10
|
+
attr_reader :data_only, :num_sheets
|
11
|
+
|
12
|
+
# converts cell string (such as "AA1") to matrix indices
|
13
|
+
def Parser.convert_to_index(cell_string)
|
14
|
+
index = Array.new(2)
|
15
|
+
index[0]=-1
|
16
|
+
index[1]=-1
|
17
|
+
if(cell_string =~ /^([A-Z]+)(\d+)$/)
|
18
|
+
one = $1.to_s
|
19
|
+
row = Integer($2) - 1 #-1 for 0 indexing
|
20
|
+
col = 0
|
21
|
+
i = 0
|
22
|
+
one = one.reverse #because of 26^i calculation
|
23
|
+
one.each_byte do |c|
|
24
|
+
int_val = c - 64 #converts A to 1
|
25
|
+
col += int_val * 26**(i)
|
26
|
+
i=i+1
|
27
|
+
end
|
28
|
+
col -= 1 #zer0 index
|
29
|
+
index[0] = row
|
30
|
+
index[1] = col
|
31
|
+
end
|
32
|
+
return index
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
# data_only allows only the sheet data to be parsed, so as to speed up parsing
|
37
|
+
def Parser.parse(file_path, data_only=false)
|
38
|
+
@data_only = data_only
|
39
|
+
files = Parser.decompress(file_path)
|
40
|
+
wb = Parser.fill_workbook(file_path, files)
|
41
|
+
|
42
|
+
if(files['sharedString'] != nil)
|
43
|
+
wb.num_strings = Integer(files['sharedString'].css('sst').attribute('count').value())
|
44
|
+
wb.size = Integer(files['sharedString'].css('sst').attribute('uniqueCount').value())
|
45
|
+
|
46
|
+
shared_strings = files['sharedString'].css('si t').children
|
47
|
+
wb.shared_strings = {}
|
48
|
+
shared_strings.each_with_index do |string,i|
|
49
|
+
wb.shared_strings[i] = string.to_s
|
50
|
+
wb.shared_strings[string.to_s] = i
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
unless @data_only
|
55
|
+
styles = files['styles'].css('cellXfs xf')
|
56
|
+
style_hash = Hash.xml_node_to_hash(files['styles'].root)
|
57
|
+
fill_styles(wb,style_hash)
|
58
|
+
|
59
|
+
#will be nil if these files do not exist
|
60
|
+
wb.external_links = files['externalLinks']
|
61
|
+
wb.drawings = files['drawings']
|
62
|
+
wb.printer_settings = files['printerSettings']
|
63
|
+
wb.worksheet_rels = files['worksheetRels']
|
64
|
+
wb.macros = files['vbaProject']
|
65
|
+
end
|
66
|
+
|
67
|
+
#for each worksheet:
|
68
|
+
#1. find the dimensions of the data matrix
|
69
|
+
#2. Fill in the matrix with data from worksheet/shared_string files
|
70
|
+
#3. Apply styles
|
71
|
+
wb.worksheets.each_index do |i|
|
72
|
+
Parser.fill_worksheet(wb,i,files,shared_strings)
|
73
|
+
end
|
74
|
+
|
75
|
+
return wb
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
#fills hashes for various styles
|
81
|
+
def Parser.fill_styles(wb,style_hash)
|
82
|
+
wb.num_fmts = style_hash[:numFmts]
|
83
|
+
|
84
|
+
###FONTS###
|
85
|
+
wb.fonts = {}
|
86
|
+
if style_hash[:fonts][:attributes][:count]==1
|
87
|
+
style_hash[:fonts][:font] = [style_hash[:fonts][:font]]
|
88
|
+
end
|
89
|
+
|
90
|
+
style_hash[:fonts][:font].each_with_index do |f,i|
|
91
|
+
wb.fonts[i.to_s] = {:font=>f,:count=>0}
|
92
|
+
end
|
93
|
+
|
94
|
+
###FILLS###
|
95
|
+
wb.fills = {}
|
96
|
+
if style_hash[:fills][:attributes][:count]==1
|
97
|
+
style_hash[:fills][:fill] = [style_hash[:fills][:fill]]
|
98
|
+
end
|
99
|
+
|
100
|
+
style_hash[:fills][:fill].each_with_index do |f,i|
|
101
|
+
wb.fills[i.to_s] = {:fill=>f,:count=>0}
|
102
|
+
end
|
103
|
+
|
104
|
+
###BORDERS###
|
105
|
+
wb.borders = {}
|
106
|
+
if style_hash[:borders][:attributes][:count] == 1
|
107
|
+
style_hash[:borders][:border] = [style_hash[:borders][:border]]
|
108
|
+
end
|
109
|
+
|
110
|
+
style_hash[:borders][:border].each_with_index do |b,i|
|
111
|
+
wb.borders[i.to_s] = {:border=>b, :count=>0}
|
112
|
+
end
|
113
|
+
|
114
|
+
wb.cell_style_xfs = style_hash[:cellStyleXfs]
|
115
|
+
wb.cell_xfs = style_hash[:cellXfs]
|
116
|
+
wb.cell_styles = style_hash[:cellStyles]
|
117
|
+
|
118
|
+
wb.colors = style_hash[:colors]
|
119
|
+
|
120
|
+
#fills out count information for each font, fill, and border
|
121
|
+
if wb.cell_xfs[:xf].is_a?(::Hash)
|
122
|
+
wb.cell_xfs[:xf] = [wb.cell_xfs[:xf]]
|
123
|
+
end
|
124
|
+
wb.cell_xfs[:xf].each do |style|
|
125
|
+
id = style[:attributes][:fontId].to_s
|
126
|
+
unless id.nil?
|
127
|
+
wb.fonts[id][:count] += 1
|
128
|
+
end
|
129
|
+
|
130
|
+
id = style[:attributes][:fillId].to_s
|
131
|
+
unless id.nil?
|
132
|
+
wb.fills[id][:count] += 1
|
133
|
+
end
|
134
|
+
|
135
|
+
id = style[:attributes][:borderId].to_s
|
136
|
+
unless id.nil?
|
137
|
+
wb.borders[id][:count] += 1
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# i is the sheet number
|
143
|
+
# files is the hash which includes information for each worksheet
|
144
|
+
# shared_strings has group of indexed strings which the cells reference
|
145
|
+
def Parser.fill_worksheet(wb,i,files,shared_strings)
|
146
|
+
wb.worksheets[i] = Parser.create_matrix(wb, i, files)
|
147
|
+
j = i+1
|
148
|
+
|
149
|
+
unless @data_only
|
150
|
+
hash = Hash.xml_node_to_hash(files[j].root)
|
151
|
+
|
152
|
+
wb.worksheets[i].sheet_view = hash[:sheetViews][:sheetView]
|
153
|
+
|
154
|
+
##col styles##
|
155
|
+
col_data = hash[:cols]
|
156
|
+
unless col_data.nil?
|
157
|
+
wb.worksheets[i].cols=col_data[:col]
|
158
|
+
end
|
159
|
+
##end col styles##
|
160
|
+
|
161
|
+
##merge_cells##
|
162
|
+
merge_data = hash[:mergeCells]
|
163
|
+
unless merge_data.nil?
|
164
|
+
wb.worksheets[i].merged_cells = merge_data[:mergeCell]
|
165
|
+
end
|
166
|
+
##end merge_cells##
|
167
|
+
|
168
|
+
##sheet_view pane##
|
169
|
+
pane_data = hash[:sheetViews][:sheetView][:pane]
|
170
|
+
wb.worksheets[i].pane = pane_data
|
171
|
+
##end sheet_view pane##
|
172
|
+
|
173
|
+
##data_validation##
|
174
|
+
data_validation = hash[:dataValidations]
|
175
|
+
unless data_validation.nil?
|
176
|
+
data_validation = data_validation[:dataValidation]
|
177
|
+
end
|
178
|
+
wb.worksheets[i].validations = data_validation
|
179
|
+
##end data_validation##
|
180
|
+
|
181
|
+
#extLst
|
182
|
+
wb.worksheets[i].extLst = hash[:extLst]
|
183
|
+
#extLst
|
184
|
+
|
185
|
+
##legacy drawing##
|
186
|
+
drawing = hash[:legacyDrawing]
|
187
|
+
wb.worksheets[i].legacy_drawing = drawing
|
188
|
+
##end legacy drawing
|
189
|
+
end
|
190
|
+
|
191
|
+
row_data = files[j].css('sheetData row')
|
192
|
+
|
193
|
+
if(row_data.to_s != "")
|
194
|
+
row_data.each do |row|
|
195
|
+
|
196
|
+
unless @data_only
|
197
|
+
##row styles##
|
198
|
+
unless row.css('c').nil?
|
199
|
+
row_style = '0'
|
200
|
+
unless row.attribute('s').nil?
|
201
|
+
row_style = row.attribute('s').value.to_s
|
202
|
+
end
|
203
|
+
|
204
|
+
wb.worksheets[i].row_styles[row.attribute('r').to_s] = { :style => row_style.to_s }
|
205
|
+
|
206
|
+
unless row.attribute('ht').to_s == ""
|
207
|
+
wb.worksheets[i].change_row_height(Integer(row.attribute('r').to_s)-1,
|
208
|
+
Float(row.attribute('ht').to_s))
|
209
|
+
end
|
210
|
+
end
|
211
|
+
##end row styles##
|
212
|
+
end
|
213
|
+
|
214
|
+
row = row.css('c')
|
215
|
+
row.each do |value|
|
216
|
+
cell_index = Parser.convert_to_index(value.attribute('r').to_s)
|
217
|
+
style_index = nil
|
218
|
+
|
219
|
+
data_type = value.attribute('t').to_s
|
220
|
+
|
221
|
+
if data_type == 's' #shared string
|
222
|
+
str_index = Integer(value.css('v').children.to_s)
|
223
|
+
cell_data = shared_strings[str_index].to_s
|
224
|
+
elsif data_type=='str' #raw string
|
225
|
+
cell_data = value.css('v').children.to_s
|
226
|
+
elsif data_type=='e' #error
|
227
|
+
cell_data = value.css('v').children.to_s
|
228
|
+
elsif value.css('v').to_s != "" #is number
|
229
|
+
data_type = ''
|
230
|
+
if(value.css('v').children.to_s =~ /\./) #is float
|
231
|
+
cell_data = Float(value.css('v').children.to_s)
|
232
|
+
else
|
233
|
+
cell_data = Integer(value.css('v').children.to_s)
|
234
|
+
end
|
235
|
+
else #no data
|
236
|
+
cell_data = nil
|
237
|
+
end
|
238
|
+
cell_formula = nil
|
239
|
+
if(value.css('f').to_s != "")
|
240
|
+
cell_formula = value.css('f').children.to_s
|
241
|
+
end
|
242
|
+
|
243
|
+
unless @data_only
|
244
|
+
style_index = Integer(value['s']) #nil goes to 0 (default)
|
245
|
+
else
|
246
|
+
style_index = 0
|
247
|
+
end
|
248
|
+
|
249
|
+
wb.worksheets[i].sheet_data[cell_index[0]][cell_index[1]] =
|
250
|
+
Cell.new(wb.worksheets[i],cell_index[0],cell_index[1],cell_data,cell_formula,
|
251
|
+
data_type,style_index)
|
252
|
+
cell = wb.worksheets[i].sheet_data[cell_index[0]][cell_index[1]]
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def Parser.decompress(file_path)
|
259
|
+
#ensures it is an xlsx/xlsm file
|
260
|
+
if(file_path =~ /(.+)\.xls(x|m)/)
|
261
|
+
dir_path = $1.to_s
|
262
|
+
else
|
263
|
+
raise 'Not .xlsx or .xlsm excel file'
|
264
|
+
end
|
265
|
+
|
266
|
+
dir_path = File.join(File.dirname(dir_path), make_safe_name(Time.now.to_s))
|
267
|
+
#copies excel file to zip file in same directory
|
268
|
+
zip_path = dir_path + '.zip'
|
269
|
+
|
270
|
+
FileUtils.cp(file_path,zip_path)
|
271
|
+
|
272
|
+
MyZip.new.unzip(zip_path,dir_path)
|
273
|
+
File.delete(zip_path)
|
274
|
+
|
275
|
+
files = Hash.new
|
276
|
+
|
277
|
+
files['app'] = Nokogiri::XML.parse(File.read(File.join(dir_path,'docProps','app.xml')))
|
278
|
+
files['core'] = Nokogiri::XML.parse(File.read(File.join(dir_path,'docProps','core.xml')))
|
279
|
+
|
280
|
+
files['workbook'] = Nokogiri::XML.parse(File.read(File.join(dir_path,'xl','workbook.xml')))
|
281
|
+
|
282
|
+
if(File.exist?(File.join(dir_path,'xl','sharedStrings.xml')))
|
283
|
+
files['sharedString'] = Nokogiri::XML.parse(File.read(File.join(dir_path,'xl','sharedStrings.xml')))
|
284
|
+
end
|
285
|
+
|
286
|
+
unless @data_only
|
287
|
+
#preserves external links
|
288
|
+
if File.directory?(File.join(dir_path,'xl','externalLinks'))
|
289
|
+
files['externalLinks'] = {}
|
290
|
+
ext_links_path = File.join(dir_path,'xl','externalLinks')
|
291
|
+
files['externalLinks']['rels'] = []
|
292
|
+
dir = Dir.new(ext_links_path).entries.reject {|f| [".", "..", ".DS_Store", "_rels"].include? f}
|
293
|
+
|
294
|
+
dir.each_with_index do |link,i|
|
295
|
+
files['externalLinks'][i+1] = File.read(File.join(ext_links_path,link))
|
296
|
+
end
|
297
|
+
|
298
|
+
if File.directory?(File.join(ext_links_path,'_rels'))
|
299
|
+
dir = Dir.new(File.join(ext_links_path,'_rels')).entries.reject{|f| [".","..",".DS_Store"].include? f}
|
300
|
+
dir.each_with_index do |rel,i|
|
301
|
+
files['externalLinks']['rels'][i+1] = File.read(File.join(ext_links_path,'_rels',rel))
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
if File.directory?(File.join(dir_path,'xl','drawings'))
|
307
|
+
files['drawings'] = {}
|
308
|
+
drawings_path = File.join(dir_path,'xl','drawings')
|
309
|
+
|
310
|
+
dir = Dir.new(drawings_path).entries.reject {|f| [".", "..", ".DS_Store"].include? f}
|
311
|
+
dir.each_with_index do |draw,i|
|
312
|
+
files['drawings'][i+1] = File.read(File.join(drawings_path,draw))
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
if File.directory?(File.join(dir_path,'xl','printerSettings'))
|
317
|
+
files['printerSettings'] = {}
|
318
|
+
printer_path = File.join(dir_path,'xl','printerSettings')
|
319
|
+
|
320
|
+
dir = Dir.new(printer_path).entries.reject {|f| [".","..",".DS_Store"].include? f}
|
321
|
+
|
322
|
+
dir.each_with_index do |print, i|
|
323
|
+
files['printerSettings'][i+1] = File.open(File.join(printer_path,print), 'rb').read
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
if File.directory?(File.join(dir_path,"xl",'worksheets','_rels'))
|
328
|
+
files['worksheetRels'] = {}
|
329
|
+
worksheet_rels_path = File.join(dir_path,'xl','worksheets','_rels')
|
330
|
+
|
331
|
+
dir = Dir.new(worksheet_rels_path).entries.reject {|f| [".","..",".DS_Store"].include? f}
|
332
|
+
dir.each_with_index do |rel, i|
|
333
|
+
files['worksheetRels'][i+1] = File.read(File.join(worksheet_rels_path,rel))
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
if File.exist?(File.join(dir_path,'xl','vbaProject.bin'))
|
338
|
+
files['vbaProject'] = File.open(File.join(dir_path,"xl","vbaProject.bin"),'rb').read
|
339
|
+
end
|
340
|
+
|
341
|
+
files['styles'] = Nokogiri::XML.parse(File.read(File.join(dir_path,'xl','styles.xml')))
|
342
|
+
end
|
343
|
+
|
344
|
+
@num_sheets = files['workbook'].css('sheets').children.size
|
345
|
+
@num_sheets = Integer(@num_sheets)
|
346
|
+
|
347
|
+
#adds all worksheet xml files to files hash
|
348
|
+
i=1
|
349
|
+
1.upto(@num_sheets) do
|
350
|
+
filename = 'sheet'+i.to_s
|
351
|
+
files[i] = Nokogiri::XML.parse(File.read(File.join(dir_path,'xl','worksheets',filename+'.xml')))
|
352
|
+
i=i+1
|
353
|
+
end
|
354
|
+
|
355
|
+
FileUtils.rm_rf(dir_path)
|
356
|
+
|
357
|
+
return files
|
358
|
+
end
|
359
|
+
|
360
|
+
def Parser.fill_workbook(file_path, files)
|
361
|
+
wb = Workbook.new([nil],file_path)
|
362
|
+
|
363
|
+
unless @data_only
|
364
|
+
wb.creator = files['core'].css('dc|creator').children.to_s
|
365
|
+
wb.modifier = files['core'].css('cp|last_modified_by').children.to_s
|
366
|
+
wb.created_at = files['core'].css('dcterms|created').children.to_s
|
367
|
+
wb.modified_at = files['core'].css('dcterms|modified').children.to_s
|
368
|
+
|
369
|
+
wb.company = files['app'].css('Company').children.to_s
|
370
|
+
wb.application = files['app'].css('Application').children.to_s
|
371
|
+
wb.appversion = files['app'].css('AppVersion').children.to_s
|
372
|
+
end
|
373
|
+
|
374
|
+
wb.shared_strings_XML = files['sharedString']
|
375
|
+
|
376
|
+
wb.worksheets = Array.new(@num_sheets) #array of Worksheet objs
|
377
|
+
wb
|
378
|
+
end
|
379
|
+
|
380
|
+
#sheet_names, dimensions
|
381
|
+
def Parser.create_matrix(wb,i, files)
|
382
|
+
sheet_names = files['app'].css('TitlesOfParts vt|vector vt|lpstr').children
|
383
|
+
sheet = Worksheet.new(wb,sheet_names[i].to_s,[])
|
384
|
+
|
385
|
+
dimensions = files[i+1].css('dimension').attribute('ref').to_s
|
386
|
+
if(dimensions =~ /^([A-Z]+\d+:)?([A-Z]+\d+)$/)
|
387
|
+
index = convert_to_index($2)
|
388
|
+
|
389
|
+
rows = index[0]+1
|
390
|
+
cols = index[1]+1
|
391
|
+
|
392
|
+
#creates matrix filled with nils
|
393
|
+
rows.times {sheet.sheet_data << Array.new(cols)}
|
394
|
+
else
|
395
|
+
raise 'invalid file'
|
396
|
+
end
|
397
|
+
sheet
|
398
|
+
end
|
399
|
+
|
400
|
+
def Parser.safe_filename(name, allow_mb_chars=false)
|
401
|
+
# "\w" represents [0-9A-Za-z_] plus any multi-byte char
|
402
|
+
regexp = allow_mb_chars ? /[^\w]/ : /[^0-9a-zA-Z\_]/
|
403
|
+
name.gsub(regexp, "_")
|
404
|
+
end
|
405
|
+
|
406
|
+
# Turns the passed in string into something safe for a filename
|
407
|
+
def Parser.make_safe_name(name, allow_mb_chars=false)
|
408
|
+
ext = safe_filename(File.extname(name), allow_mb_chars).gsub(/^_/, '.')
|
409
|
+
"#{safe_filename(name.gsub(ext, ""), allow_mb_chars)}#{ext}".gsub(/\(/, '_').gsub(/\)/, '_').gsub(/__+/, '_').gsub(/^_/, '').gsub(/_$/, '')
|
410
|
+
end
|
411
|
+
|
412
|
+
end
|
413
|
+
end
|
@@ -0,0 +1,182 @@
|
|
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
|
+
#modifies font array (copies,appends) then styles array (copies,appends)
|
34
|
+
#does not actually modify font object,
|
35
|
+
#allows method caller to do that (cell or worksheet)
|
36
|
+
def modify_font(workbook, style_index)
|
37
|
+
# xf_obj = workbook.get_style(style_index)
|
38
|
+
xf = workbook.get_style_attributes(workbook.get_style(style_index))
|
39
|
+
|
40
|
+
#modify fonts array
|
41
|
+
font_id = xf[:fontId]
|
42
|
+
font = workbook.fonts[font_id.to_s][:font]
|
43
|
+
|
44
|
+
#else, just change the attribute itself, done in calling method.
|
45
|
+
if workbook.fonts[font_id.to_s][:count] > 1 || font_id == 0
|
46
|
+
old_size = workbook.fonts.size.to_s
|
47
|
+
workbook.fonts[old_size] = {}
|
48
|
+
workbook.fonts[old_size][:font] = deep_copy(font)
|
49
|
+
workbook.fonts[old_size][:count] = 1
|
50
|
+
workbook.fonts[font_id.to_s][:count] -= 1
|
51
|
+
|
52
|
+
#modify styles array
|
53
|
+
font_id = old_size
|
54
|
+
|
55
|
+
if workbook.cell_xfs[:xf].is_a?Array
|
56
|
+
workbook.cell_xfs[:xf] << deep_copy({:attributes=>xf})
|
57
|
+
else
|
58
|
+
workbook.cell_xfs[:xf] = [workbook.cell_xfs[:xf], deep_copy({:attributes=>xf})]
|
59
|
+
end
|
60
|
+
|
61
|
+
xf = workbook.get_style_attributes(workbook.cell_xfs[:xf].last)
|
62
|
+
xf[:fontId] = font_id
|
63
|
+
xf[:applyFont] = '1'
|
64
|
+
workbook.cell_xfs[:attributes][:count] += 1
|
65
|
+
return workbook.cell_xfs[:xf].size-1 #returns new style_index
|
66
|
+
else
|
67
|
+
return style_index
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
#modifies fill array (copies, appends, adds color and solid attribute)
|
72
|
+
#then styles array (copies, appends)
|
73
|
+
def modify_fill(workbook, style_index, rgb)
|
74
|
+
xf_obj = workbook.get_style(style_index)
|
75
|
+
xf = workbook.get_style_attributes(xf_obj)
|
76
|
+
#modify fill array
|
77
|
+
fill_id = xf[:fillId]
|
78
|
+
|
79
|
+
fill = workbook.fills[fill_id.to_s][:fill]
|
80
|
+
if workbook.fills[fill_id.to_s][:count] > 1 || fill_id == 0 || fill_id == 1
|
81
|
+
old_size = workbook.fills.size.to_s
|
82
|
+
workbook.fills[old_size] = {}
|
83
|
+
workbook.fills[old_size][:fill] = deep_copy(fill)
|
84
|
+
workbook.fills[old_size][:count] = 1
|
85
|
+
workbook.fills[fill_id.to_s][:count] -= 1
|
86
|
+
|
87
|
+
change_wb_fill(workbook, old_size,rgb)
|
88
|
+
|
89
|
+
#modify styles array
|
90
|
+
fill_id = old_size
|
91
|
+
if workbook.cell_xfs[:xf].is_a?Array
|
92
|
+
workbook.cell_xfs[:xf] << deep_copy({:attributes=>xf})
|
93
|
+
else
|
94
|
+
workbook.cell_xfs[:xf] = [workbook.cell_xfs[:xf], deep_copy({:attributes=>xf})]
|
95
|
+
end
|
96
|
+
xf = workbook.get_style_attributes(workbook.cell_xfs[:xf].last)
|
97
|
+
xf[:fillId] = fill_id
|
98
|
+
xf[:applyFill] = '1'
|
99
|
+
workbook.cell_xfs[:attributes][:count] += 1
|
100
|
+
return workbook.cell_xfs[:xf].size-1
|
101
|
+
else
|
102
|
+
change_wb_fill(workbook, fill_id.to_s,rgb)
|
103
|
+
return style_index
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def modify_border(workbook, style_index)
|
108
|
+
xf_obj = workbook.get_style(style_index)
|
109
|
+
xf = workbook.get_style_attributes(xf_obj)
|
110
|
+
|
111
|
+
border_id = Integer(xf[:borderId])
|
112
|
+
border = workbook.borders[border_id.to_s][:border]
|
113
|
+
if workbook.borders[border_id.to_s][:count] > 1 || border_id == 0 || border_id == 1
|
114
|
+
old_size = workbook.borders.size.to_s
|
115
|
+
workbook.borders[old_size] = {}
|
116
|
+
workbook.borders[old_size][:border] = deep_copy(border)
|
117
|
+
workbook.borders[old_size][:count] = 1
|
118
|
+
|
119
|
+
border_id = old_size
|
120
|
+
|
121
|
+
if workbook.cell_xfs[:xf].is_a?Array
|
122
|
+
workbook.cell_xfs[:xf] << deep_copy(xf_obj)
|
123
|
+
else
|
124
|
+
workbook.cell_xfs[:xf] = [workbook.cell_xfs[:xf], deep_copy(xf_obj)]
|
125
|
+
end
|
126
|
+
|
127
|
+
xf = workbook.get_style_attributes(workbook.cell_xfs[:xf].last)
|
128
|
+
xf[:borderId] = border_id
|
129
|
+
xf[:applyBorder] = '1'
|
130
|
+
workbook.cell_xfs[:attributes][:count] += 1
|
131
|
+
return workbook.cell_xfs[:xf].size-1
|
132
|
+
else
|
133
|
+
return style_index
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
#is_horizontal is true when doing horizontal alignment,
|
138
|
+
#false when doing vertical alignment
|
139
|
+
def modify_alignment(workbook, style_index, is_horizontal, alignment)
|
140
|
+
old_xf_obj = workbook.get_style(style_index)
|
141
|
+
|
142
|
+
xf_obj = deep_copy(old_xf_obj)
|
143
|
+
|
144
|
+
if xf_obj[:alignment].nil? || xf_obj[:alignment][:attributes].nil?
|
145
|
+
xf_obj[:alignment] = {:attributes=>{:horizontal=>nil, :vertical=>nil}}
|
146
|
+
end
|
147
|
+
|
148
|
+
if is_horizontal
|
149
|
+
xf_obj[:alignment][:attributes][:horizontal] = alignment.to_s
|
150
|
+
else
|
151
|
+
xf_obj[:alignment][:attributes][:vertical] = alignment.to_s
|
152
|
+
end
|
153
|
+
|
154
|
+
if workbook.cell_xfs[:xf].is_a?Array
|
155
|
+
workbook.cell_xfs[:xf] << deep_copy(xf_obj)
|
156
|
+
else
|
157
|
+
workbook.cell_xfs[:xf] = [workbook.cell_xfs[:xf], deep_copy(xf_obj)]
|
158
|
+
end
|
159
|
+
|
160
|
+
xf = workbook.get_style_attributes(workbook.cell_xfs[:xf].last)
|
161
|
+
xf[:applyAlignment] = '1'
|
162
|
+
workbook.cell_xfs[:attributes][:count] += 1
|
163
|
+
workbook.cell_xfs[:xf].size-1
|
164
|
+
end
|
165
|
+
|
166
|
+
#returns non-shallow copy of hash
|
167
|
+
def deep_copy(hash)
|
168
|
+
Marshal.load(Marshal.dump(hash))
|
169
|
+
end
|
170
|
+
|
171
|
+
def change_wb_fill(workbook, fill_index, rgb)
|
172
|
+
if workbook.fills[fill_index][:fill][:patternFill][:fgColor].nil?
|
173
|
+
workbook.fills[fill_index][:fill][:patternFill][:fgColor] = {:attributes => {:rgb => ''}}
|
174
|
+
end
|
175
|
+
workbook.fills[fill_index][:fill][:patternFill][:fgColor][:attributes][:rgb] = rgb
|
176
|
+
|
177
|
+
#previously none, doesn't show fill
|
178
|
+
workbook.fills[fill_index][:fill][:patternFill][:attributes][:patternType] = 'solid'
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
end
|