spreadsheet-excel 0.3.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 (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