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.
Files changed (48) hide show
  1. data/Gemfile +16 -0
  2. data/Gemfile.lock +34 -0
  3. data/LICENSE.txt +20 -0
  4. data/README +0 -0
  5. data/README.rdoc +19 -0
  6. data/Rakefile +53 -0
  7. data/VERSION +1 -0
  8. data/lib/.DS_Store +0 -0
  9. data/lib/Hash.rb +60 -0
  10. data/lib/cell.rb +360 -0
  11. data/lib/color.rb +14 -0
  12. data/lib/parser.rb +413 -0
  13. data/lib/private_class.rb +182 -0
  14. data/lib/rubyXL.rb +9 -0
  15. data/lib/test.html +1 -0
  16. data/lib/tests/test.rb +110 -0
  17. data/lib/tests/test10.rb +16 -0
  18. data/lib/tests/test2.rb +118 -0
  19. data/lib/tests/test3.rb +76 -0
  20. data/lib/tests/test4.rb +92 -0
  21. data/lib/tests/test5.rb +90 -0
  22. data/lib/tests/test6.rb +50 -0
  23. data/lib/tests/test7.rb +48 -0
  24. data/lib/tests/test8.rb +12 -0
  25. data/lib/tests/test9.rb +60 -0
  26. data/lib/workbook.rb +336 -0
  27. data/lib/worksheet.rb +1245 -0
  28. data/lib/writer/app_writer.rb +62 -0
  29. data/lib/writer/calc_chain_writer.rb +33 -0
  30. data/lib/writer/content_types_writer.rb +77 -0
  31. data/lib/writer/core_writer.rb +51 -0
  32. data/lib/writer/root_rels_writer.rb +25 -0
  33. data/lib/writer/shared_strings_writer.rb +44 -0
  34. data/lib/writer/styles_writer.rb +376 -0
  35. data/lib/writer/theme_writer.rb +346 -0
  36. data/lib/writer/workbook_rels_writer.rb +59 -0
  37. data/lib/writer/workbook_writer.rb +77 -0
  38. data/lib/writer/worksheet_writer.rb +208 -0
  39. data/lib/zip.rb +20 -0
  40. data/pkg/rubyXL-1.0.4.gem +0 -0
  41. data/rubyXL.gemspec +106 -0
  42. data/spec/lib/cell_spec.rb +359 -0
  43. data/spec/lib/color_spec.rb +14 -0
  44. data/spec/lib/hash_spec.rb +28 -0
  45. data/spec/lib/parser_spec.rb +49 -0
  46. data/spec/lib/workbook_spec.rb +51 -0
  47. data/spec/lib/worksheet_spec.rb +1650 -0
  48. metadata +222 -0
@@ -0,0 +1,50 @@
1
+ require '../rubyXL'
2
+ require 'rubygems'
3
+ # require File.expand_path(File.join(File.dirname(__FILE__),'workbook'))
4
+ # require File.expand_path(File.join(File.dirname(__FILE__),'parser'))
5
+ # require File.expand_path(File.join(File.dirname(__FILE__),'color'))
6
+ # require File.expand_path(File.join(File.dirname(__FILE__),'cell'))
7
+
8
+ module RubyXL
9
+ wb = Workbook.new([],nil)
10
+ wb.worksheets = [Worksheet.new(wb,'Sheet1')]
11
+ cell = wb.worksheets[0].add_cell(0,0,'1.00.0')
12
+ # cell.change_font_italics(false)
13
+ # p cell.is_italicized(wb)
14
+ # cell.change_font_bold(false)
15
+ # p cell.is_bolded(wb)
16
+ # cell.change_font_underline(false)
17
+ # p cell.is_underlined(wb)
18
+ # p cell.font_name(wb)
19
+ # p cell.font_size(wb)
20
+ # p cell.font_color(wb)
21
+ # p cell.fill_color(wb)
22
+
23
+ wb.worksheets[0].add_cell(2,5,'$1,000.00')
24
+ wb.worksheets[0].add_cell(3,3,'6/14/11')
25
+ wb.worksheets[0].add_cell(4,0,1)
26
+ wb.worksheets[0].add_cell(4,1,2)
27
+ wb.worksheets[0].add_cell(4,2,3)
28
+ wb.worksheets[0].add_cell(4,3,4)
29
+ wb.worksheets[0].add_cell(4,4,0,'AVERAGE(A5:D5)')
30
+
31
+ cell.change_font_color('ff0000')
32
+
33
+ wb.write('/Users/vbhagwat/Desktop/test2/Output/nums.xlsx')
34
+
35
+
36
+ wb2 = Parser.parse('/Users/vbhagwat/Desktop/5-1_5-20.xlsx')
37
+ # wb2.worksheets[0].merge_cells(0,1,0,2)
38
+ # wb2.worksheets[0].merge_cells(0,0,0,1)
39
+ #.change_font_size(30)
40
+ wb2.write('/Users/vbhagwat/Desktop/test2/Output/nums2.xlsx')
41
+
42
+ wb3 = Parser.parse('/Users/vbhagwat/Documents/excelTestFiles/paneWorkbook2/paneWorkbook2.xlsx')
43
+
44
+
45
+
46
+ # p Color.find(8)
47
+ # c = Color::ColorProperties
48
+ # p c.has_value?({:hex=>'#000000', :name=>"black"})
49
+ # p Color.find('black')
50
+ end
@@ -0,0 +1,48 @@
1
+ require '../rubyXL'
2
+ require 'rubygems'
3
+ require 'rubyXL'
4
+ # require File.expand_path(File.join(File.dirname(__FILE__),'workbook'))
5
+
6
+ module RubyXL
7
+ wb = Workbook.new([],nil)
8
+ wb.worksheets = [Worksheet.new(wb,'Sheet1')]
9
+
10
+ cell = wb.worksheets[0].add_cell(0,0,'A1')
11
+ cell2 = wb.worksheets[0].add_cell(0,1,'B1')
12
+ cell3 = wb.worksheets[0].add_cell(0,5,'F1')
13
+
14
+ wb.worksheets[0].sheet_data[0].each do |c|
15
+ unless c.nil?
16
+ c.change_font_bold(true)
17
+ c.change_font_underline(true)
18
+ end
19
+ end
20
+
21
+ cell.change_horizontal_alignment('center')
22
+ wb.worksheets[0].change_row_horizontal_alignment(0,'justify')
23
+ wb.worksheets[0].change_row_vertical_alignment(0,'center')
24
+
25
+ wb.worksheets[0].change_row_fill(0,'FF0000')
26
+ cell2.change_fill('0000FF')
27
+
28
+ cell4 = wb.worksheets[0].add_cell(0,3,'D1')
29
+ puts '
30
+
31
+
32
+
33
+ '
34
+ p wb.fills
35
+ puts '
36
+
37
+
38
+
39
+
40
+ '
41
+ cell4.change_fill('A43502')
42
+
43
+ cell5 = wb.worksheets[0].add_cell(1,0,'A2')
44
+ wb.worksheets[0].change_row_fill(1,'00FF00')
45
+ cell5.change_fill('FF0000')
46
+
47
+ wb.write('/Users/vbhagwat/Desktop/test2/Output/test7.xlsx')
48
+ end
@@ -0,0 +1,12 @@
1
+ require '../rubyXL'
2
+ require 'rubygems'
3
+ # require File.expand_path(File.join(File.dirname(__FILE__),'workbook'))
4
+ # require File.expand_path(File.join(File.dirname(__FILE__),'parser'))
5
+
6
+ module RubyXL
7
+
8
+ wb = Parser.parse('/Users/vbhagwat/Desktop/macros.xlsm', false)
9
+ wb.write('/Users/vbhagwat/Desktop/test2/Output/macros.xlsm')
10
+ puts "completed writing /Users/vbhagwat/Desktop/test2/Output/macros.xlsm"
11
+
12
+ end
@@ -0,0 +1,60 @@
1
+ require 'rubygems'
2
+ require 'nokogiri'
3
+ require File.expand_path(File.join(File.dirname(__FILE__),'Hash'))
4
+
5
+ module RubyXL
6
+ testWb = Nokogiri::XML.parse(File.read('/Users/vbhagwat/Desktop/testWorkbook.xml'))
7
+ testStyles = Nokogiri::XML.parse(File.read('/Users/vbhagwat/Desktop/testStyles.xml'))
8
+
9
+ puts "testWb.css('workbook definedNames')[0]"
10
+ p testWb.css('workbook definedNames')[0]
11
+ puts '/css'
12
+
13
+
14
+
15
+
16
+ Hash.xml_node_to_hash(test_wb.css('workbook definedNames')[0])
17
+
18
+ puts '
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+ '
39
+ h= Hash.xml_node_to_hash(test_styles.css('styleSheet cellXfs')[0])
40
+
41
+ puts '
42
+
43
+
44
+
45
+
46
+
47
+ '
48
+ p h
49
+ #
50
+ # :cellXfs=>{
51
+ # :xf=>[{
52
+ # :attributes=>{
53
+ # :xfId=>0, :fontId=>0, :numFmtId=>0, :borderId=>0, :fillId=>0}},
54
+ # {:attributes=>{
55
+ # :applyFill=>0, :applyFont=>0, :applyAlignment=>1, :xfId=>0, :fontId=>0,
56
+ # :numFmtId=>0, :applyNumberFormat=>0, :borderId=>0, :applyBorder=>0, :fillId=>0},
57
+ # :alignment=>{:attributes=>{:horizontal=>"center"}}}],
58
+ #
59
+ # :attributes=>{:count=>2}}
60
+ end
@@ -0,0 +1,336 @@
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 'zip'
12
+
13
+ module RubyXL
14
+ class Workbook
15
+ attr_accessor :worksheets, :filepath, :creator, :modifier, :created_at,
16
+ :modified_at, :company, :application, :appversion, :num_fmts, :fonts, :fills,
17
+ :borders, :cell_xfs, :cell_style_xfs, :cell_styles, :shared_strings, :calc_chain,
18
+ :num_strings, :size, :date1904, :external_links, :style_corrector, :drawings,
19
+ :worksheet_rels, :printer_settings, :macros, :colors, :shared_strings_XML
20
+
21
+ APPLICATION = 'Microsoft Macintosh Excel'
22
+ APPVERSION = '12.0000'
23
+ SHEET_NAME = 'Sheet1'
24
+ def initialize(worksheets=[], filepath=nil, creator=nil, modifier=nil, created_at=nil,
25
+ company='', application=APPLICATION,
26
+ appversion=APPVERSION, date_1904=0)
27
+ if worksheets.nil? || worksheets.empty?
28
+ @worksheets = [Worksheet.new(self,SHEET_NAME)]
29
+ else
30
+ @worksheets = worksheets
31
+ end
32
+ @filepath = filepath
33
+ @creator = creator
34
+ @modifier = modifier
35
+ @company = company
36
+ @application = application
37
+ @appversion = appversion
38
+ @num_fmts = nil
39
+ @fonts = nil
40
+ @fills = nil
41
+ @borders = nil
42
+ @cell_xfs = nil
43
+ @cell_style_xfs = nil
44
+ @cell_styles = nil
45
+ @shared_strings = nil
46
+ @calc_chain = nil #unnecessary?
47
+ @num_strings = 0 #num strings total
48
+ @size = 0 #num strings in shared_strings array
49
+ @date_1904 = date_1904
50
+ @external_links = nil
51
+ @style_corrector = nil
52
+ @drawings = nil
53
+ @worksheet_rels = nil
54
+ @printer_settings = nil
55
+ @macros = nil
56
+ @colors = nil
57
+ @shared_strings_XML = nil
58
+
59
+ begin
60
+ @created_at = DateTime.parse(created_at).strftime('%Y-%m-%dT%TZ')
61
+ rescue
62
+ t = Time.now
63
+ @created_at = t.strftime('%Y-%m-%dT%TZ')
64
+ end
65
+ @modified_at = @created_at
66
+
67
+ fill_styles()
68
+ fill_shared_strings()
69
+ end
70
+
71
+ # allows easier access to worksheets
72
+ def [](worksheet)
73
+ return worksheets[worksheet]
74
+ end
75
+
76
+ #filepath of xlsx file (including file itself)
77
+ def write(filepath=@filepath)
78
+ validate_before_write
79
+ dirpath = ''
80
+ extension = 'xls'
81
+ if(filepath =~ /((.|\s)*)\.xls(x|m)$/)
82
+ dirpath = $1.to_s()
83
+ extension += $3.to_s
84
+ end
85
+ filename = ''
86
+ if(filepath =~ /\/((.|\s)*)\/((.|\s)*)\.xls(x|m)$/)
87
+ filename = $3.to_s()
88
+ end
89
+
90
+ #creates zip file, writes each type of file to zip folder
91
+ #zips package and renames it to xlsx.
92
+ zippath = File.join(dirpath, filename + '.zip')
93
+ File.unlink(zippath) if File.exists?(zippath)
94
+ FileUtils.mkdir_p(File.join(dirpath,zippath))
95
+ Zip::ZipFile.open(zippath, Zip::ZipFile::CREATE) do |zipfile|
96
+ writer = Writer::ContentTypesWriter.new(dirpath,self)
97
+ zipfile.get_output_stream('[Content_Types].xml') {|f| f.puts(writer.write())}
98
+
99
+ writer = Writer::RootRelsWriter.new(dirpath,self)
100
+ zipfile.get_output_stream(File.join('_rels','.rels')) {|f| f.puts(writer.write())}
101
+
102
+ writer = Writer::AppWriter.new(dirpath,self)
103
+ zipfile.get_output_stream(File.join('docProps','app.xml')) {|f| f.puts(writer.write())}
104
+
105
+ writer = Writer::CoreWriter.new(dirpath,self)
106
+ zipfile.get_output_stream(File.join('docProps','core.xml')) {|f| f.puts(writer.write())}
107
+
108
+ writer = Writer::ThemeWriter.new(dirpath,self)
109
+ zipfile.get_output_stream(File.join('xl','theme','theme1.xml')) {|f| f.puts(writer.write())}
110
+
111
+ writer = Writer::WorkbookRelsWriter.new(dirpath,self)
112
+ zipfile.get_output_stream(File.join('xl','_rels','workbook.xml.rels')) {|f| f.puts(writer.write())}
113
+
114
+ writer = Writer::WorkbookWriter.new(dirpath,self)
115
+ zipfile.get_output_stream(File.join('xl','workbook.xml')) {|f| f.puts(writer.write())}
116
+
117
+ writer = Writer::StylesWriter.new(dirpath,self)
118
+ zipfile.get_output_stream(File.join('xl','styles.xml')) {|f| f.puts(writer.write())}
119
+
120
+ unless @shared_strings.nil?
121
+ writer = Writer::SharedStringsWriter.new(dirpath,self)
122
+ zipfile.get_output_stream(File.join('xl','sharedStrings.xml')) {|f| f.puts(writer.write())}
123
+ end
124
+
125
+ #preserves external links (exactly, no modification allowed)
126
+ unless @external_links.nil?
127
+ #-1 because of rels
128
+ 1.upto(@external_links.size-1) do |i|
129
+ zipfile.get_output_stream(
130
+ File.join('xl','externalLinks',"externalLink#{i}.xml")) {|f|
131
+ f.puts(@external_links[i])
132
+ }
133
+ end
134
+ @external_links['rels'].each_index do |i|
135
+ unless @external_links['rels'][i].nil?
136
+ zipfile.get_output_stream(
137
+ File.join('xl','externalLinks','_rels',"externalLink#{i}.xml.rels")) {|f|
138
+ f.puts(@external_links['rels'][i])
139
+ }
140
+ end
141
+ end
142
+ end
143
+
144
+ #preserves drawings (exactly, no modification allowed)
145
+ unless @drawings.nil?
146
+ 1.upto(@drawings.size) do |i|
147
+ zipfile.get_output_stream(
148
+ File.join('xl','drawings',"vmlDrawing#{i}.vml")) {|f|
149
+ f.puts(@drawings[i])
150
+ }
151
+ end
152
+ end
153
+
154
+ unless @printer_settings.nil?
155
+ 1.upto(@printer_settings.size) do |i|
156
+ zipfile.get_output_stream(
157
+ File.join('xl','printerSettings',"printerSettings#{i}.bin")) {|f|
158
+ f.puts(@printer_settings[i])
159
+ }
160
+ end
161
+ end
162
+
163
+ unless @worksheet_rels.nil?
164
+ 1.upto(@worksheet_rels.size) do |i|
165
+ zipfile.get_output_stream(
166
+ File.join('xl','worksheets','_rels',"sheet#{i}.xml.rels")) {|f|
167
+ f.puts(@worksheet_rels[i])
168
+ }
169
+ end
170
+ end
171
+
172
+ unless @macros.nil?
173
+ zipfile.get_output_stream(File.join('xl','vbaProject.bin')) {|f| f.puts(@macros)}
174
+ end
175
+
176
+ @worksheets.each_with_index do |sheet,i|
177
+ writer = Writer::WorksheetWriter.new(dirpath,self,i)
178
+ zipfile.get_output_stream(File.join('xl','worksheets',"sheet#{i+1}.xml")) {|f| f.puts(writer.write())}
179
+ end
180
+ end
181
+
182
+ FileUtils.cp(zippath,File.join(dirpath,filename+".#{extension}"))
183
+ FileUtils.cp(File.join(dirpath,filename+".#{extension}"),filepath)
184
+ if File.exist?(filepath)
185
+ FileUtils.rm_rf(dirpath)
186
+ end
187
+ end
188
+
189
+ #gets style object from style array given index
190
+ def get_style(style_index)
191
+ if !@cell_xfs[:xf].is_a?Array
192
+ @cell_xfs[:xf] = [@cell_xfs[:xf]]
193
+ end
194
+
195
+ xf_obj = @cell_xfs[:xf]
196
+ if xf_obj.is_a?Array
197
+ xf_obj = xf_obj[Integer(style_index)]
198
+ end
199
+ xf_obj
200
+ end
201
+
202
+ #gets attributes of above style object
203
+ #necessary because can take the form of hash or array,
204
+ #based on odd behavior of Nokogiri
205
+ def get_style_attributes(xf_obj)
206
+ if xf_obj.is_a?Array
207
+ xf = xf_obj[1]
208
+ else
209
+ xf = xf_obj[:attributes]
210
+ end
211
+ end
212
+
213
+ def get_fill_color(xf_attributes)
214
+ 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?
215
+ 'ffffff' #white
216
+ else
217
+ @fills[xf_attributes[:fillId]][:fill][:patternFill][:fgColor][:attributes][:rgb]
218
+ end
219
+ end
220
+
221
+
222
+ private
223
+
224
+ # Do not change. Excel requires that some of these styles be default,
225
+ # and will simply assume that the 0 and 1 indexed fonts are the default values.
226
+ def fill_styles()
227
+ @fonts = {
228
+ '0' => {
229
+ :font => {
230
+ :sz => { :attributes => { :val => 10 } },
231
+ :name => { :attributes => { :val => "Verdana" } }
232
+ },
233
+ :count=>1
234
+ },
235
+ '1' => {
236
+ :font => {
237
+ :sz => { :attributes => { :val => 8 } },
238
+ :name => { :attributes => { :val => "Verdana" } }
239
+ },
240
+ :count=>0
241
+ }
242
+ }
243
+
244
+ @fills = {
245
+ '0' => {
246
+ :fill => {
247
+ :patternFill => { :attributes => { :patternType => "none" } }
248
+ },
249
+ :count=>1} ,
250
+ '1' => {
251
+ :fill => {
252
+ :patternFill => { :attributes => { :patternType => "gray125" } }
253
+ },
254
+ :count=>0
255
+ }
256
+ }
257
+
258
+ @borders = {
259
+ '0' => {
260
+ :border => {
261
+ :left => { },
262
+ :right => { },
263
+ :top => { },
264
+ :bottom => { },
265
+ :diagonal => { }
266
+ },
267
+ :count => 1 #count = how many styles reference it
268
+ }
269
+ }
270
+
271
+ @cell_style_xfs = {
272
+ :attributes => {
273
+ :count => 1
274
+ },
275
+ :xf => {
276
+ :attributes => { :numFmtId => 0, :fontId => 0, :fillId => 0, :borderId => 0 }
277
+ }
278
+ }
279
+ @cell_xfs = {
280
+ :attributes => {
281
+ :count => 1
282
+ },
283
+ :xf => {
284
+ :attributes => { :numFmtId => 0, :fontId => 0, :fillId => 0, :borderId => 0, :xfId => 0 }
285
+ }
286
+ }
287
+ @cell_styles = {
288
+ :cellStyle => {
289
+ :attributes => { :builtinId=>0, :name=>"Normal", :xfId=>0 }
290
+ },
291
+ :attributes => { :count => 1 }
292
+ }
293
+ end
294
+
295
+
296
+ #fills shared strings hash, contains each unique string
297
+ def fill_shared_strings()
298
+ if @shared_strings.nil?
299
+ string_hash = {}
300
+ string_index = 0
301
+ @num_strings = 0
302
+ #fill hash for shared strings
303
+ @worksheets.each do |sheet|
304
+ unless sheet.nil?
305
+ sheet.sheet_data.each do |row|
306
+ row.each do |cell|
307
+ unless cell.nil? || cell.value.nil?
308
+ #if string not already seen, add it to hash
309
+ if cell.datatype == 's'
310
+ if string_hash[cell.value.to_s].nil?
311
+ string_hash[string_index]=cell.value.to_s
312
+ string_hash[cell.value.to_s]=string_index
313
+ string_index += 1
314
+ end
315
+ @num_strings += 1
316
+ end
317
+ end
318
+ end
319
+ end
320
+ end
321
+ end
322
+
323
+ if string_hash[0].nil?
324
+ @shared_strings = nil
325
+ else
326
+ @shared_strings = string_hash
327
+ @size = string_index
328
+ end
329
+ end
330
+ end
331
+
332
+ def validate_before_write
333
+ ## TODO CHECK IF STYLE IS OK if not raise
334
+ end
335
+ end
336
+ end