spreadsheet-excel 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/CHANGES +124 -0
  2. data/CVS/Entries +10 -0
  3. data/CVS/Repository +1 -0
  4. data/CVS/Root +1 -0
  5. data/README +69 -0
  6. data/doc/CVS/Entries +3 -0
  7. data/doc/CVS/Repository +1 -0
  8. data/doc/CVS/Root +1 -0
  9. data/doc/format.txt +330 -0
  10. data/doc/spreadsheet.txt +178 -0
  11. data/examples/CVS/Entries +3 -0
  12. data/examples/CVS/Repository +1 -0
  13. data/examples/CVS/Root +1 -0
  14. data/examples/example_basic.rb +27 -0
  15. data/examples/example_format.rb +69 -0
  16. data/lib/CVS/Entries +1 -0
  17. data/lib/CVS/Repository +1 -0
  18. data/lib/CVS/Root +1 -0
  19. data/lib/spreadsheet/CVS/Entries +7 -0
  20. data/lib/spreadsheet/CVS/Repository +1 -0
  21. data/lib/spreadsheet/CVS/Root +1 -0
  22. data/lib/spreadsheet/biffwriter.rb +46 -0
  23. data/lib/spreadsheet/excel.rb +13 -0
  24. data/lib/spreadsheet/format.rb +359 -0
  25. data/lib/spreadsheet/olewriter.rb +178 -0
  26. data/lib/spreadsheet/workbook.rb +279 -0
  27. data/lib/spreadsheet/worksheet.rb +449 -0
  28. data/test/CVS/Entries +9 -0
  29. data/test/CVS/Repository +1 -0
  30. data/test/CVS/Root +1 -0
  31. data/test/perl_output/CVS/Entries +10 -0
  32. data/test/perl_output/CVS/Repository +1 -0
  33. data/test/perl_output/CVS/Root +1 -0
  34. data/test/perl_output/README +31 -0
  35. data/test/perl_output/f_font_biff +0 -0
  36. data/test/perl_output/f_font_key +1 -0
  37. data/test/perl_output/f_xf_biff +0 -0
  38. data/test/perl_output/ole_write_header +0 -0
  39. data/test/perl_output/ws_colinfo +1 -0
  40. data/test/perl_output/ws_store_dimensions +1 -0
  41. data/test/perl_output/ws_store_selection +1 -0
  42. data/test/perl_output/ws_store_window2 +1 -0
  43. data/test/tc_all.rb +6 -0
  44. data/test/tc_biff.rb +62 -0
  45. data/test/tc_excel.rb +26 -0
  46. data/test/tc_format.rb +133 -0
  47. data/test/tc_ole.rb +125 -0
  48. data/test/tc_workbook.rb +71 -0
  49. data/test/tc_worksheet.rb +142 -0
  50. data/test/ts_all.rb +9 -0
  51. metadata +103 -0
@@ -0,0 +1,178 @@
1
+ # olewriter.rb
2
+ #
3
+ # This class should never be instantiated directly. The entire class, and all
4
+ # its methods should be considered private.
5
+
6
+ class MaxSizeError < StandardError; end
7
+
8
+ class OLEWriter < IO
9
+
10
+ # Not meant for public consumption
11
+ MaxSize = 7087104
12
+ BlockSize = 4096
13
+ BlockDiv = 512
14
+ ListBlocks = 127
15
+
16
+ attr_reader :biff_size, :book_size, :big_blocks, :list_blocks
17
+ attr_reader :root_start, :size_allowed
18
+
19
+ # Accept an IO object, a fileno, or a String
20
+ def initialize(arg, &block)
21
+ if arg.kind_of?(String)
22
+ super(File.open(arg, "w+").fileno, "w+", &block)
23
+ elsif arg.respond_to?(:fileno)
24
+ super(arg.fileno, "w+", &block)
25
+ else
26
+ super(arg, "w+", &block)
27
+ end
28
+ binmode
29
+
30
+ @biff_only = false
31
+ @size_allowed = true
32
+ @biff_size = 0
33
+ @book_size = 0
34
+ @big_blocks = 0
35
+ @list_blocks = 0
36
+ @root_start = 0
37
+ @block_count = 4
38
+ end
39
+
40
+ # Set the size of the data to be written to the OLE stream
41
+ #
42
+ # @big_blocks = (109 depot block x (128 -1 marker word)
43
+ # - (1 x end words)) = 13842
44
+ #
45
+ # MaxSize = @big_blocks * 512 bytes = 7087104
46
+ def set_size(size = BlockSize)
47
+ raise MaxSizeError if size > MaxSize
48
+
49
+ @biff_size = size
50
+
51
+ if biff_size > BlockSize
52
+ @book_size = size
53
+ else
54
+ @book_size = BlockSize
55
+ end
56
+
57
+ @size_allowed = true
58
+ end
59
+
60
+ # Calculate various sizes needed for the OLE stream
61
+ def calculate_sizes
62
+ @big_blocks = (@book_size.to_f/BlockDiv.to_f).ceil
63
+ @list_blocks = (@big_blocks / ListBlocks) + 1
64
+ @root_start = @big_blocks
65
+ end
66
+
67
+ # Write root entry, big block list and close the filehandle.
68
+ def close
69
+ if @size_allowed == true
70
+ write_padding
71
+ write_property_storage
72
+ write_big_block_depot
73
+ end
74
+ super
75
+ end
76
+
77
+ # Write the OLE header block
78
+ def write_header
79
+ return if @biff_only == true
80
+ calculate_sizes
81
+ root_start = @root_start
82
+
83
+ write([0xD0CF11E0, 0xA1B11AE1].pack("NN"))
84
+ write([0x00, 0x00, 0x00, 0x00].pack("VVVV"))
85
+ write([0x3E, 0x03, -2, 0x09].pack("vvvv"))
86
+ write([0x06, 0x00, 0x00].pack("VVV"))
87
+ write([@list_blocks, root_start].pack("VV"))
88
+ write([0x00, 0x1000,-2].pack("VVV"))
89
+ write([0x00, -2 ,0x00].pack("VVV"))
90
+
91
+ unused = [-1].pack("V")
92
+
93
+ 1.upto(@list_blocks){
94
+ root_start += 1
95
+ write([root_start].pack("V"))
96
+ }
97
+
98
+ @list_blocks.upto(108){
99
+ write(unused)
100
+ }
101
+ end
102
+
103
+ # Write a big block depot
104
+ def write_big_block_depot
105
+ total_blocks = @list_blocks * 128
106
+ used_blocks = @big_blocks + @list_blocks + 2
107
+
108
+ marker = [-3].pack("V")
109
+ eoc = [-2].pack("V")
110
+ unused = [-1].pack("V")
111
+
112
+ num_blocks = @big_blocks - 1
113
+
114
+ 1.upto(num_blocks){|n|
115
+ write([n].pack("V"))
116
+ }
117
+
118
+ write eoc
119
+ write eoc
120
+
121
+ 1.upto(@list_blocks){ write(marker) }
122
+
123
+ used_blocks.upto(total_blocks){ write(unused) }
124
+
125
+ end
126
+
127
+ # Write property storage
128
+ def write_property_storage
129
+ write_pps('Root Entry', 0x05, 1, -2, 0x00)
130
+ write_pps('Book', 0x02, -1, 0x00, @book_size)
131
+ write_pps("", 0x00, -1, 0x00, 0x0000)
132
+ write_pps("", 0x00, -1, 0x00, 0x0000)
133
+ end
134
+
135
+ # Write property sheet in property storage
136
+ def write_pps(name, type, dir, start, size)
137
+ length = 0
138
+ ord_name = []
139
+ unless name.empty?
140
+ name += "\0"
141
+ ord_name = name.unpack("c*")
142
+ length = name.length * 2
143
+ end
144
+
145
+ zero = [0].pack("C")
146
+ unknown = [0].pack("V")
147
+
148
+ write(ord_name.pack("v*"))
149
+
150
+ for n in 1..64-length
151
+ write(zero)
152
+ end
153
+
154
+ write([length,type,-1,-1,dir].pack("vvVVV"))
155
+
156
+ for n in 1..5
157
+ write(unknown)
158
+ end
159
+
160
+ for n in 1..4
161
+ write([0].pack("V"))
162
+ end
163
+
164
+ write([start,size].pack("VV"))
165
+ write(unknown)
166
+ end
167
+
168
+ # Pad the end of the file
169
+ def write_padding
170
+ min_size = 512
171
+ min_size = BlockSize if @biff_size < BlockSize
172
+
173
+ if @biff_size % min_size != 0
174
+ padding = min_size - (@biff_size % min_size)
175
+ write("\0" * padding)
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,279 @@
1
+ class Workbook < BIFFWriter
2
+ BOF = 11
3
+ EOF = 4
4
+ SheetName = "Sheet"
5
+
6
+ attr_accessor :date_system
7
+ attr_reader :formats, :xf_index, :worksheets
8
+
9
+ def initialize(file)
10
+ super
11
+
12
+ @file = file
13
+ @format = Format.new
14
+
15
+ @active_sheet = 0
16
+ @first_sheet = 0
17
+ @biffsize = 0
18
+ @date_system = 1900
19
+ @xf_index = 16
20
+
21
+ @worksheets = []
22
+ @formats = []
23
+
24
+ @url_format = add_format(:color=>"blue", :underline=>1)
25
+ end
26
+
27
+ def close
28
+ store_workbook
29
+ end
30
+
31
+ def add_format(*args)
32
+ if args[0].kind_of?(Hash)
33
+ f = Format.new(args[0], @xf_index)
34
+ elsif args[0].nil?
35
+ f = Format.new
36
+ else
37
+ raise TypeError unless args[0].kind_of?(Format)
38
+ f = args[0]
39
+ f.xf_index = @xf_index
40
+ end
41
+ @xf_index += 1
42
+ @formats.push(f)
43
+ return f
44
+ end
45
+
46
+ def add_worksheet(name=nil)
47
+ index = @worksheets.length
48
+
49
+ if name.nil?
50
+ name = SheetName + (index + 1).to_s
51
+ end
52
+
53
+ args = [name,index, @active_sheet, @first_sheet, @url_format]
54
+ ws = Worksheet.new(*args)
55
+ @worksheets[index] = ws
56
+ return ws
57
+ end
58
+
59
+ def calc_sheet_offsets
60
+ offset = @datasize
61
+ @worksheets.each{ |sheet|
62
+ offset += BOF + sheet.name.length
63
+ }
64
+
65
+ offset += EOF
66
+
67
+ @worksheets.each{ |sheet|
68
+ sheet.offset = offset
69
+ offset += sheet.datasize
70
+ }
71
+
72
+ @biffsize = offset
73
+ end
74
+
75
+ def store_workbook
76
+ @worksheets.each{ |sheet|
77
+ sheet.close
78
+ }
79
+
80
+ store_bof(0x0005)
81
+ store_window1
82
+ store_date_system
83
+ store_all_fonts
84
+ store_all_num_formats
85
+ store_all_xfs
86
+ store_all_styles
87
+ calc_sheet_offsets
88
+
89
+ @worksheets.each{ |sheet|
90
+ store_boundsheet(sheet.name, sheet.offset)
91
+ }
92
+
93
+ store_eof
94
+ store_ole_file
95
+ end
96
+
97
+ def store_ole_file
98
+ OLEWriter.open(@file){ |ole|
99
+ ole.set_size(@biffsize)
100
+ ole.write_header
101
+ ole.print(@data)
102
+ @worksheets.each{ |sheet|
103
+ ole.print(sheet.data)
104
+ }
105
+ ole.close # Added because of bug introduced in Ruby 1.8.3
106
+ }
107
+ end
108
+
109
+ def store_window1
110
+ record = 0x003D
111
+ length = 0x0012
112
+
113
+ xWn = 0x0000
114
+ yWn = 0x0000
115
+ dxWn = 0x25BC
116
+ dyWn = 0x1572
117
+
118
+ grbit = 0x0038
119
+ ctabsel = 0x0001
120
+ wTabRatio = 0x0258
121
+
122
+ itabFirst = @first_sheet
123
+ itabCur = @active_sheet
124
+
125
+ header = [record,length].pack("vv")
126
+ fields = [xWn,yWn,dxWn,dyWn,grbit,itabCur,itabFirst,ctabsel,wTabRatio]
127
+ data = fields.pack("vvvvvvvvv")
128
+
129
+ append(header,data)
130
+ end
131
+
132
+ def store_all_fonts
133
+ font = @format.font_biff
134
+ for n in 1..5
135
+ append(font)
136
+ end
137
+
138
+ fonts = Hash.new(0)
139
+ index = 6
140
+ key = @format.font_key
141
+ fonts[key] = 0
142
+
143
+ @formats.each{ |format|
144
+ key = format.font_key
145
+ if fonts.has_key?(key)
146
+ format.font_index = fonts[key]
147
+ else
148
+ fonts[key] = index
149
+ format.font_index = index
150
+ index += 1
151
+ append(format.font_biff)
152
+ end
153
+ }
154
+ end
155
+
156
+ def store_xf(style)
157
+ name = 0x00E0
158
+ length = 0x0010
159
+
160
+ ifnt = 0x0000
161
+ ifmt = 0x0000
162
+ align = 0x0020
163
+ icv = 0x20C0
164
+ fill = 0x0000
165
+ brd_line = 0x0000
166
+ brd_color = 0x0000
167
+
168
+ header = [name, length].pack("vv")
169
+ fields = [ifnt,ifmt,style,align,icv,fill,brd_line,brd_color]
170
+ data = fields.pack("vvvvvvvv")
171
+
172
+ append(header, data);
173
+ end
174
+
175
+ def store_all_num_formats
176
+ index = 164
177
+
178
+ num_formats_hash = {}
179
+ num_formats_array = []
180
+
181
+ @formats.each{ |format|
182
+ num_format = format.num_format
183
+ next if num_format.kind_of?(Numeric)
184
+ if num_formats_hash.has_key?(num_format)
185
+ format.num_format = num_formats_hash[num_format]
186
+ else
187
+ num_formats_hash[num_format] = index
188
+ format.num_format = index
189
+ num_formats_array.push(num_format)
190
+ index += 1
191
+ end
192
+ }
193
+
194
+ index = 164
195
+ num_formats_array.each{ |num_format|
196
+ store_num_format(num_format,index)
197
+ index += 1
198
+ }
199
+ end
200
+
201
+ def store_all_xfs
202
+ xf = @format.xf_biff(0xFFF5)
203
+ for n in 1..15
204
+ append(xf)
205
+ end
206
+
207
+ xf = @format.xf_biff(0x0001)
208
+ append(xf)
209
+
210
+ @formats.each{ |format|
211
+ xf = format.xf_biff(0x0001)
212
+ append(xf)
213
+ }
214
+ end
215
+
216
+ def store_style
217
+ record = 0x0293
218
+ length = 0x0004
219
+
220
+ ixfe = 0x8000
221
+ builtin = 0x00
222
+ iLevel = 0xff
223
+
224
+ header = [record, length].pack("vv")
225
+ data = [ixfe, builtin, iLevel].pack("vCC")
226
+
227
+ append(header, data)
228
+ end
229
+
230
+ alias store_all_styles store_style
231
+
232
+ def store_boundsheet(sheet_name, offset)
233
+ name = 0x0085
234
+ length = 0x07 + sheet_name.length
235
+
236
+ grbit = 0x0000
237
+ cch = sheet_name.length
238
+
239
+ header = [name, length].pack("vv")
240
+ data = [offset, grbit, cch].pack("VvC")
241
+
242
+ append(header, data, sheet_name)
243
+ end
244
+
245
+ def store_num_format(format, ifmt)
246
+ record = 0x041E
247
+ cch = format.length
248
+ length = 0x03 + cch
249
+
250
+ header = [record, length].pack("vv")
251
+ data = [ifmt, cch].pack("vC")
252
+
253
+ append(header, data, format)
254
+ end
255
+
256
+ def store_date_system
257
+ record = 0x0022
258
+ length = 0x0002
259
+
260
+ f1904 = 0
261
+ f1904 = 1 if @date_system == 1904
262
+
263
+ header = [record, length].pack("vv")
264
+ data = [f1904].pack("v")
265
+
266
+ append(header, data)
267
+ end
268
+ end
269
+
270
+ =begin
271
+ = Notes on the difference between Workbook.pm and workbook.rb
272
+ ---deprecated methods
273
+ I generally elminated any deprecated methods. That means no 'write'
274
+ methods.
275
+ ---date_system
276
+ This is the 1904 attribute. However, since a number can't be a method,
277
+ this doesn't work very well for attribute_accessor. Besides, date_system
278
+ is more descriptive.
279
+ =end