WriteExcel 0.2.0
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/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +17 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/examples/a_simple.rb +42 -0
- data/examples/autofilters.rb +266 -0
- data/examples/bigfile.rb +30 -0
- data/examples/copyformat.rb +51 -0
- data/examples/data_validate.rb +278 -0
- data/examples/date_time.rb +86 -0
- data/examples/demo.rb +118 -0
- data/examples/diag_border.rb +35 -0
- data/examples/formats.rb +489 -0
- data/examples/header.rb +136 -0
- data/examples/hidden.rb +28 -0
- data/examples/hyperlink.rb +42 -0
- data/examples/images.rb +52 -0
- data/examples/merge1.rb +39 -0
- data/examples/merge2.rb +44 -0
- data/examples/merge3.rb +65 -0
- data/examples/merge4.rb +82 -0
- data/examples/merge5.rb +79 -0
- data/examples/protection.rb +46 -0
- data/examples/regions.rb +52 -0
- data/examples/repeat.rb +42 -0
- data/examples/stats.rb +75 -0
- data/examples/stocks.rb +80 -0
- data/examples/tab_colors.rb +30 -0
- data/lib/WriteExcel.rb +30 -0
- data/lib/WriteExcel/biffwriter.rb +259 -0
- data/lib/WriteExcel/chart.rb +217 -0
- data/lib/WriteExcel/excelformula.y +138 -0
- data/lib/WriteExcel/excelformulaparser.rb +573 -0
- data/lib/WriteExcel/format.rb +1108 -0
- data/lib/WriteExcel/formula.rb +986 -0
- data/lib/WriteExcel/olewriter.rb +322 -0
- data/lib/WriteExcel/properties.rb +250 -0
- data/lib/WriteExcel/storage_lite.rb +590 -0
- data/lib/WriteExcel/workbook.rb +2602 -0
- data/lib/WriteExcel/worksheet.rb +6378 -0
- data/spec/WriteExcel_spec.rb +7 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +9 -0
- data/test/tc_all.rb +31 -0
- data/test/tc_biff.rb +104 -0
- data/test/tc_chart.rb +22 -0
- data/test/tc_example_match.rb +1280 -0
- data/test/tc_format.rb +1264 -0
- data/test/tc_formula.rb +63 -0
- data/test/tc_ole.rb +110 -0
- data/test/tc_storage_lite.rb +102 -0
- data/test/tc_workbook.rb +115 -0
- data/test/tc_worksheet.rb +115 -0
- data/test/test_00_IEEE_double.rb +14 -0
- data/test/test_01_add_worksheet.rb +12 -0
- data/test/test_02_merge_formats.rb +58 -0
- data/test/test_04_dimensions.rb +397 -0
- data/test/test_05_rows.rb +182 -0
- data/test/test_06_extsst.rb +80 -0
- data/test/test_11_date_time.rb +484 -0
- data/test/test_12_date_only.rb +506 -0
- data/test/test_13_date_seconds.rb +486 -0
- data/test/test_21_escher.rb +629 -0
- data/test/test_22_mso_drawing_group.rb +739 -0
- data/test/test_23_note.rb +78 -0
- data/test/test_24_txo.rb +80 -0
- data/test/test_26_autofilter.rb +327 -0
- data/test/test_27_autofilter.rb +144 -0
- data/test/test_28_autofilter.rb +174 -0
- data/test/test_29_process_jpg.rb +131 -0
- data/test/test_30_validation_dval.rb +82 -0
- data/test/test_31_validation_dv_strings.rb +131 -0
- data/test/test_32_validation_dv_formula.rb +211 -0
- data/test/test_40_property_types.rb +191 -0
- data/test/test_41_properties.rb +238 -0
- data/test/test_42_set_properties.rb +430 -0
- data/test/ts_all.rb +34 -0
- metadata +154 -0
data/examples/merge5.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
|
3
|
+
###############################################################################
|
4
|
+
#
|
5
|
+
# Example of how to use the Spreadsheet::WriteExcel merge_cells() workbook
|
6
|
+
# method with complex formatting and rotation.
|
7
|
+
#
|
8
|
+
#
|
9
|
+
# reverse('©'), September 2002, John McNamara, jmcnamara@cpan.org
|
10
|
+
#
|
11
|
+
# original written in Perl by John McNamara
|
12
|
+
# converted to Ruby by Hideo Nakamura, cxn03651@msj.biglobe.ne.jp
|
13
|
+
#
|
14
|
+
|
15
|
+
require 'rubygems'
|
16
|
+
require 'WriteExcel'
|
17
|
+
|
18
|
+
# Create a new workbook and add a worksheet
|
19
|
+
workbook = Spreadsheet::WriteExcel.new('merge5.xls')
|
20
|
+
worksheet = workbook.add_worksheet
|
21
|
+
|
22
|
+
|
23
|
+
# Increase the cell size of the merged cells to highlight the formatting.
|
24
|
+
(3..8).each { |col| worksheet.set_row(col, 36) }
|
25
|
+
[1, 3, 5].each { |n| worksheet.set_column(n, n, 15) }
|
26
|
+
|
27
|
+
|
28
|
+
###############################################################################
|
29
|
+
#
|
30
|
+
# Rotation 1, letters run from top to bottom
|
31
|
+
#
|
32
|
+
format1 = workbook.add_format(
|
33
|
+
:border => 6,
|
34
|
+
:bold => 1,
|
35
|
+
:color => 'red',
|
36
|
+
:valign => 'vcentre',
|
37
|
+
:align => 'centre',
|
38
|
+
:rotation => 270
|
39
|
+
)
|
40
|
+
|
41
|
+
|
42
|
+
worksheet.merge_range('B4:B9', 'Rotation 270', format1)
|
43
|
+
|
44
|
+
|
45
|
+
###############################################################################
|
46
|
+
#
|
47
|
+
# Rotation 2, 90° anticlockwise
|
48
|
+
#
|
49
|
+
format2 = workbook.add_format(
|
50
|
+
:border => 6,
|
51
|
+
:bold => 1,
|
52
|
+
:color => 'red',
|
53
|
+
:valign => 'vcentre',
|
54
|
+
:align => 'centre',
|
55
|
+
:rotation => 90
|
56
|
+
)
|
57
|
+
|
58
|
+
|
59
|
+
worksheet.merge_range('D4:D9', 'Rotation 90°', format2)
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
###############################################################################
|
64
|
+
#
|
65
|
+
# Rotation 3, 90° clockwise
|
66
|
+
#
|
67
|
+
format3 = workbook.add_format(
|
68
|
+
:border => 6,
|
69
|
+
:bold => 1,
|
70
|
+
:color => 'red',
|
71
|
+
:valign => 'vcentre',
|
72
|
+
:align => 'centre',
|
73
|
+
:rotation => -90
|
74
|
+
)
|
75
|
+
|
76
|
+
|
77
|
+
worksheet.merge_range('F4:F9', 'Rotation -90°', format3)
|
78
|
+
|
79
|
+
workbook.close
|
@@ -0,0 +1,46 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
|
3
|
+
########################################################################
|
4
|
+
#
|
5
|
+
# Example of cell locking and formula hiding in an Excel worksheet via
|
6
|
+
# the Spreadsheet::WriteExcel module.
|
7
|
+
#
|
8
|
+
# reverse('©'), August 2001, John McNamara, jmcnamara@cpan.org
|
9
|
+
#
|
10
|
+
# original written in Perl by John McNamara
|
11
|
+
# converted to Ruby by Hideo Nakamura, cxn03651@msj.biglobe.ne.jp
|
12
|
+
#
|
13
|
+
|
14
|
+
require 'rubygems'
|
15
|
+
require 'WriteExcel'
|
16
|
+
|
17
|
+
workbook = Spreadsheet::WriteExcel.new("protection.xls")
|
18
|
+
worksheet = workbook.add_worksheet
|
19
|
+
|
20
|
+
# Create some format objects
|
21
|
+
locked = workbook.add_format(:locked => 1)
|
22
|
+
unlocked = workbook.add_format(:locked => 0)
|
23
|
+
hidden = workbook.add_format(:hidden => 1)
|
24
|
+
|
25
|
+
# Format the columns
|
26
|
+
worksheet.set_column('A:A', 42)
|
27
|
+
worksheet.set_selection('B3:B3')
|
28
|
+
|
29
|
+
# Protect the worksheet
|
30
|
+
worksheet.protect
|
31
|
+
|
32
|
+
# Examples of cell locking and hiding
|
33
|
+
worksheet.write('A1', 'Cell B1 is locked. It cannot be edited.')
|
34
|
+
worksheet.write('B1', '=1+2', locked)
|
35
|
+
|
36
|
+
worksheet.write('A2', 'Cell B2 is unlocked. It can be edited.')
|
37
|
+
worksheet.write('B2', '=1+2', unlocked)
|
38
|
+
|
39
|
+
worksheet.write('A3', "Cell B3 is hidden. The formula isn't visible.")
|
40
|
+
worksheet.write('B3', '=1+2', hidden)
|
41
|
+
|
42
|
+
worksheet.write('A5', 'Use Menu->Tools->Protection->Unprotect Sheet')
|
43
|
+
worksheet.write('A6', 'to remove the worksheet protection. ')
|
44
|
+
|
45
|
+
workbook.close
|
46
|
+
|
data/examples/regions.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
|
3
|
+
###############################################################################
|
4
|
+
#
|
5
|
+
# Example of how to use the WriteExcel module to write a basic multiple
|
6
|
+
# worksheet Excel file.
|
7
|
+
#
|
8
|
+
# reverse('©'), March 2001, John McNamara, jmcnamara@cpan.org
|
9
|
+
#
|
10
|
+
# original written in Perl by John McNamara
|
11
|
+
# converted to Ruby by Hideo Nakamura, cxn03651@msj.biglobe.ne.jp
|
12
|
+
#
|
13
|
+
|
14
|
+
require 'rubygems'
|
15
|
+
require 'WriteExcel'
|
16
|
+
|
17
|
+
workbook = Spreadsheet::WriteExcel.new("regions.xls")
|
18
|
+
|
19
|
+
# Add some worksheets
|
20
|
+
north = workbook.add_worksheet("North")
|
21
|
+
south = workbook.add_worksheet("South")
|
22
|
+
east = workbook.add_worksheet("East")
|
23
|
+
west = workbook.add_worksheet("West")
|
24
|
+
|
25
|
+
# Add a Format
|
26
|
+
format = workbook.add_format()
|
27
|
+
format.set_bold()
|
28
|
+
format.set_color('blue')
|
29
|
+
|
30
|
+
# Add a caption to each worksheet
|
31
|
+
workbook.sheets.each do |worksheet|
|
32
|
+
worksheet.write(0, 0, "Sales", format)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Write some data
|
36
|
+
north.write(0, 1, 200000)
|
37
|
+
south.write(0, 1, 100000)
|
38
|
+
east.write(0, 1, 150000)
|
39
|
+
west.write(0, 1, 100000)
|
40
|
+
|
41
|
+
# Set the active worksheet
|
42
|
+
bp=1
|
43
|
+
south.activate()
|
44
|
+
|
45
|
+
# Set the width of the first column
|
46
|
+
south.set_column(0, 0, 20)
|
47
|
+
|
48
|
+
# Set the active cell
|
49
|
+
south.set_selection(0, 1)
|
50
|
+
|
51
|
+
workbook.close
|
52
|
+
|
data/examples/repeat.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
|
3
|
+
######################################################################
|
4
|
+
#
|
5
|
+
# Example of writing repeated formulas.
|
6
|
+
#
|
7
|
+
# reverse('©'), August 2002, John McNamara, jmcnamara@cpan.org
|
8
|
+
#
|
9
|
+
# original written in Perl by John McNamara
|
10
|
+
# converted to Ruby by Hideo Nakamura, cxn03651@msj.biglobe.ne.jp
|
11
|
+
#
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'WriteExcel'
|
15
|
+
|
16
|
+
workbook = Spreadsheet::WriteExcel.new("repeat.xls")
|
17
|
+
worksheet = workbook.add_worksheet
|
18
|
+
|
19
|
+
limit = 1000
|
20
|
+
|
21
|
+
# Write a column of numbers
|
22
|
+
0.upto(limit) do |row|
|
23
|
+
worksheet.write(row, 0, row)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Store a formula
|
27
|
+
formula = worksheet.store_formula('=A1*5+4')
|
28
|
+
|
29
|
+
# Write a column of formulas based on the stored formula
|
30
|
+
0.upto(limit) do |row|
|
31
|
+
worksheet.repeat_formula(row, 1, formula, nil,
|
32
|
+
/A1/, 'A'+(row+1).to_s)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Direct formula writing. As a speed comparison uncomment the
|
36
|
+
# following and run the program again
|
37
|
+
|
38
|
+
#for row (0..limit) {
|
39
|
+
# worksheet.write_formula(row, 2, '=A'.(row+1).'*5+4')
|
40
|
+
#}
|
41
|
+
|
42
|
+
workbook.close
|
data/examples/stats.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
|
3
|
+
###############################################################################
|
4
|
+
#
|
5
|
+
# This is a simple example of how to use functions with the
|
6
|
+
# Spreadsheet::WriteExcel module.
|
7
|
+
#
|
8
|
+
# reverse('©'), March 2001, John McNamara, jmcnamara@cpan.org
|
9
|
+
#
|
10
|
+
# original written in Perl by John McNamara
|
11
|
+
# converted to Ruby by Hideo Nakamura, cxn03651@msj.biglobe.ne.jp
|
12
|
+
#
|
13
|
+
|
14
|
+
require 'rubygems'
|
15
|
+
require 'WriteExcel'
|
16
|
+
include Spreadsheet
|
17
|
+
xlsfile = 'stats.xls'
|
18
|
+
|
19
|
+
workbook = Spreadsheet::WriteExcel.new(xlsfile)
|
20
|
+
worksheet = workbook.add_worksheet('Test data')
|
21
|
+
|
22
|
+
# Set the column width for columns 1
|
23
|
+
worksheet.set_column(0, 0, 20)
|
24
|
+
|
25
|
+
|
26
|
+
# Create a format for the headings
|
27
|
+
format = workbook.add_format
|
28
|
+
format.set_bold
|
29
|
+
|
30
|
+
|
31
|
+
# Write the sample data
|
32
|
+
worksheet.write(0, 0, 'Sample', format)
|
33
|
+
worksheet.write(0, 1, 1)
|
34
|
+
worksheet.write(0, 2, 2)
|
35
|
+
worksheet.write(0, 3, 3)
|
36
|
+
worksheet.write(0, 4, 4)
|
37
|
+
worksheet.write(0, 5, 5)
|
38
|
+
worksheet.write(0, 6, 6)
|
39
|
+
worksheet.write(0, 7, 7)
|
40
|
+
worksheet.write(0, 8, 8)
|
41
|
+
|
42
|
+
worksheet.write(1, 0, 'Length', format)
|
43
|
+
worksheet.write(1, 1, 25.4)
|
44
|
+
worksheet.write(1, 2, 25.4)
|
45
|
+
worksheet.write(1, 3, 24.8)
|
46
|
+
worksheet.write(1, 4, 25.0)
|
47
|
+
worksheet.write(1, 5, 25.3)
|
48
|
+
worksheet.write(1, 6, 24.9)
|
49
|
+
worksheet.write(1, 7, 25.2)
|
50
|
+
worksheet.write(1, 8, 24.8)
|
51
|
+
|
52
|
+
# Write some statistical functions
|
53
|
+
worksheet.write(4, 0, 'Count', format)
|
54
|
+
worksheet.write(4, 1, '=COUNT(B1:I1)')
|
55
|
+
|
56
|
+
worksheet.write(5, 0, 'Sum', format)
|
57
|
+
worksheet.write(5, 1, '=SUM(B2:I2)')
|
58
|
+
|
59
|
+
worksheet.write(6, 0, 'Average', format)
|
60
|
+
worksheet.write(6, 1, '=AVERAGE(B2:I2)')
|
61
|
+
|
62
|
+
worksheet.write(7, 0, 'Min', format)
|
63
|
+
worksheet.write(7, 1, '=MIN(B2:I2)')
|
64
|
+
|
65
|
+
worksheet.write(8, 0, 'Max', format)
|
66
|
+
worksheet.write(8, 1, '=MAX(B2:I2)')
|
67
|
+
|
68
|
+
worksheet.write(9, 0, 'Standard Deviation', format)
|
69
|
+
worksheet.write(9, 1, '=STDEV(B2:I2)')
|
70
|
+
|
71
|
+
worksheet.write(10, 0, 'Kurtosis', format)
|
72
|
+
worksheet.write(10, 1, '=KURT(B2:I2)')
|
73
|
+
|
74
|
+
workbook.close
|
75
|
+
|
data/examples/stocks.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
|
3
|
+
###############################################################################
|
4
|
+
#
|
5
|
+
# Example of formatting using the Spreadsheet::WriteExcel module
|
6
|
+
#
|
7
|
+
# This example shows how to use a conditional numerical format
|
8
|
+
# with colours to indicate if a share price has gone up or down.
|
9
|
+
#
|
10
|
+
# reverse('©'), March 2001, John McNamara, jmcnamara@cpan.org
|
11
|
+
#
|
12
|
+
# original written in Perl by John McNamara
|
13
|
+
# converted to Ruby by Hideo Nakamura, cxn03651@msj.biglobe.ne.jp
|
14
|
+
#
|
15
|
+
|
16
|
+
require 'rubygems'
|
17
|
+
require 'WriteExcel'
|
18
|
+
|
19
|
+
# Create a new workbook and add a worksheet
|
20
|
+
workbook = Spreadsheet::WriteExcel.new("stocks.xls")
|
21
|
+
worksheet = workbook.add_worksheet
|
22
|
+
|
23
|
+
# Set the column width for columns 1, 2, 3 and 4
|
24
|
+
worksheet.set_column(0, 3, 15)
|
25
|
+
|
26
|
+
|
27
|
+
# Create a format for the column headings
|
28
|
+
header = workbook.add_format
|
29
|
+
header.set_bold
|
30
|
+
header.set_size(12)
|
31
|
+
header.set_color('blue')
|
32
|
+
|
33
|
+
|
34
|
+
# Create a format for the stock price
|
35
|
+
f_price = workbook.add_format
|
36
|
+
f_price.set_align('left')
|
37
|
+
f_price.set_num_format('$0.00')
|
38
|
+
|
39
|
+
|
40
|
+
# Create a format for the stock volume
|
41
|
+
f_volume = workbook.add_format
|
42
|
+
f_volume.set_align('left')
|
43
|
+
f_volume.set_num_format('#,##0')
|
44
|
+
|
45
|
+
|
46
|
+
# Create a format for the price change. This is an example of a conditional
|
47
|
+
# format. The number is formatted as a percentage. If it is positive it is
|
48
|
+
# formatted in green, if it is negative it is formatted in red and if it is
|
49
|
+
# zero it is formatted as the default font colour (in this case black).
|
50
|
+
# Note: the [Green] format produces an unappealing lime green. Try
|
51
|
+
# [Color 10] instead for a dark green.
|
52
|
+
#
|
53
|
+
f_change = workbook.add_format
|
54
|
+
f_change.set_align('left')
|
55
|
+
f_change.set_num_format('[Green]0.0%;[Red]-0.0%;0.0%')
|
56
|
+
|
57
|
+
|
58
|
+
# Write out the data
|
59
|
+
worksheet.write(0, 0, 'Company', header)
|
60
|
+
worksheet.write(0, 1, 'Price', header)
|
61
|
+
worksheet.write(0, 2, 'Volume', header)
|
62
|
+
worksheet.write(0, 3, 'Change', header)
|
63
|
+
|
64
|
+
worksheet.write(1, 0, 'Damage Inc.' )
|
65
|
+
worksheet.write(1, 1, 30.25, f_price) # $30.25
|
66
|
+
worksheet.write(1, 2, 1234567, f_volume) # 1,234,567
|
67
|
+
worksheet.write(1, 3, 0.085, f_change) # 8.5% in green
|
68
|
+
|
69
|
+
worksheet.write(2, 0, 'Dump Corp.' )
|
70
|
+
worksheet.write(2, 1, 1.56, f_price) # $1.56
|
71
|
+
worksheet.write(2, 2, 7564, f_volume) # 7,564
|
72
|
+
worksheet.write(2, 3, -0.015, f_change) # -1.5% in red
|
73
|
+
|
74
|
+
worksheet.write(3, 0, 'Rev Ltd.' )
|
75
|
+
worksheet.write(3, 1, 0.13, f_price) # $0.13
|
76
|
+
worksheet.write(3, 2, 321, f_volume) # 321
|
77
|
+
worksheet.write(3, 3, 0, f_change) # 0 in the font color (black)
|
78
|
+
|
79
|
+
|
80
|
+
workbook.close
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
|
3
|
+
#######################################################################
|
4
|
+
#
|
5
|
+
# Example of how to set Excel worksheet tab colours.
|
6
|
+
#
|
7
|
+
# reverse('©'), May 2006, John McNamara, jmcnamara@cpan.org
|
8
|
+
#
|
9
|
+
# original written in Perl by John McNamara
|
10
|
+
# converted to Ruby by Hideo Nakamura, cxn03651@msj.biglobe.ne.jp
|
11
|
+
#
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'WriteExcel'
|
15
|
+
|
16
|
+
workbook = Spreadsheet::WriteExcel.new('tab_colors.xls')
|
17
|
+
|
18
|
+
worksheet1 = workbook.add_worksheet
|
19
|
+
worksheet2 = workbook.add_worksheet
|
20
|
+
worksheet3 = workbook.add_worksheet
|
21
|
+
worksheet4 = workbook.add_worksheet
|
22
|
+
|
23
|
+
# Worsheet1 will have the default tab colour.
|
24
|
+
worksheet2.set_tab_color('red')
|
25
|
+
worksheet3.set_tab_color('green')
|
26
|
+
worksheet4.set_tab_color(0x35) # Orange
|
27
|
+
|
28
|
+
workbook.close
|
29
|
+
|
30
|
+
workbook.close
|
data/lib/WriteExcel.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require "date"
|
5
|
+
require "digest/md5"
|
6
|
+
require "nkf"
|
7
|
+
require "strscan"
|
8
|
+
require "racc/parser.rb"
|
9
|
+
require 'tempfile'
|
10
|
+
require 'stringio'
|
11
|
+
require "iconv"
|
12
|
+
bdir = "#{File.join(File.dirname(__FILE__), "WriteExcel")}"
|
13
|
+
|
14
|
+
autoload :Workbook, File.join(bdir.to_s, "workbook.rb")
|
15
|
+
autoload :BIFFWriter, File.join(bdir.to_s, "biffwriter.rb")
|
16
|
+
autoload :OLEWriter, File.join(bdir.to_s, "olewriter.rb")
|
17
|
+
autoload :ExcelFormulaParser, File.join(bdir.to_s, "excelformulaparser.rb")
|
18
|
+
autoload :Formula, File.join(bdir.to_s, "formula.rb")
|
19
|
+
autoload :Format , File.join(bdir.to_s, "format.rb")
|
20
|
+
autoload :Worksheet, File.join(bdir.to_s, "worksheet.rb")
|
21
|
+
require File.join(bdir.to_s, "properties.rb")
|
22
|
+
autoload :OLEStorageLite, File.join(bdir.to_s, "storage_lite.rb")
|
23
|
+
|
24
|
+
bdir = nil
|
25
|
+
|
26
|
+
class Spreadsheet
|
27
|
+
class WriteExcel < Workbook
|
28
|
+
VERSION = "0.2.0"
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
#
|
2
|
+
# BIFFwriter - An abstract base class for Excel workbooks and worksheets.
|
3
|
+
#
|
4
|
+
#
|
5
|
+
# Used in conjunction with Spreadsheet::WriteExcel
|
6
|
+
#
|
7
|
+
# Copyright 2000-2008, John McNamara, jmcnamara@cpan.org
|
8
|
+
#
|
9
|
+
# original written in Perl by John McNamara
|
10
|
+
# converted to Ruby by Hideo Nakamura, cxn03651@msj.biglobe.ne.jp
|
11
|
+
#
|
12
|
+
|
13
|
+
|
14
|
+
#require 'tempfile'
|
15
|
+
|
16
|
+
class BIFFWriter
|
17
|
+
BIFF_Version = 0x0600
|
18
|
+
BigEndian = [1].pack("I") == [1].pack("N")
|
19
|
+
|
20
|
+
attr_reader :byte_order, :data, :datasize
|
21
|
+
|
22
|
+
######################################################################
|
23
|
+
# The args here aren't used by BIFFWriter, but they are needed by its
|
24
|
+
# subclasses. I don't feel like creating multiple constructors.
|
25
|
+
######################################################################
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
set_byte_order
|
29
|
+
@data = ''
|
30
|
+
@datasize = 0
|
31
|
+
@limit = 8224
|
32
|
+
@ignore_continue = 0
|
33
|
+
|
34
|
+
# Open a tmp file to store the majority of the Worksheet data. If this fails,
|
35
|
+
# for example due to write permissions, store the data in memory. This can be
|
36
|
+
# slow for large files.
|
37
|
+
@filehandle = Tempfile.new('spreadsheetWriteExcel')
|
38
|
+
@filehandle.binmode
|
39
|
+
|
40
|
+
# failed. store temporary data in memory.
|
41
|
+
@using_tmpfile = @filehandle ? true : false
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
###############################################################################
|
46
|
+
#
|
47
|
+
# _set_byte_order()
|
48
|
+
#
|
49
|
+
# Determine the byte order and store it as class data to avoid
|
50
|
+
# recalculating it for each call to new().
|
51
|
+
#
|
52
|
+
def set_byte_order
|
53
|
+
# Check if "pack" gives the required IEEE 64bit float
|
54
|
+
teststr = [1.2345].pack("d")
|
55
|
+
hexdata = [0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F]
|
56
|
+
number = hexdata.pack("C8")
|
57
|
+
|
58
|
+
if number == teststr
|
59
|
+
@byte_order = 0 # Little Endian
|
60
|
+
elsif number == teststr.reverse
|
61
|
+
@byte_order = 1 # Big Endian
|
62
|
+
else
|
63
|
+
# Give up. I'll fix this in a later version.
|
64
|
+
raise( "Required floating point format not supported " +
|
65
|
+
"on this platform. See the portability section " +
|
66
|
+
"of the documentation."
|
67
|
+
)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
###############################################################################
|
72
|
+
#
|
73
|
+
# _prepend($data)
|
74
|
+
#
|
75
|
+
# General storage function
|
76
|
+
#
|
77
|
+
def prepend(*args)
|
78
|
+
d = args.join
|
79
|
+
d = add_continue(d) if d.length > @limit
|
80
|
+
|
81
|
+
@datasize += d.length
|
82
|
+
@data = d + @data
|
83
|
+
|
84
|
+
#print "prepend\n"
|
85
|
+
#print d.unpack('C*').map! {|c| sprintf("%02X", c) }.join(' ') + "\n\n"
|
86
|
+
return d
|
87
|
+
end
|
88
|
+
|
89
|
+
###############################################################################
|
90
|
+
#
|
91
|
+
# _append($data)
|
92
|
+
#
|
93
|
+
# General storage function
|
94
|
+
#
|
95
|
+
def append(*args)
|
96
|
+
d = args.join
|
97
|
+
# Add CONTINUE records if necessary
|
98
|
+
d = add_continue(d) if d.length > @limit
|
99
|
+
if @using_tmpfile
|
100
|
+
@filehandle.write d
|
101
|
+
@datasize += d.length
|
102
|
+
else
|
103
|
+
@datasize += d.length
|
104
|
+
@data = @data + d
|
105
|
+
end
|
106
|
+
#print "apend\n"
|
107
|
+
#print d.unpack('C*').map! {|c| sprintf("%02X", c) }.join(' ') + "\n\n"
|
108
|
+
return d
|
109
|
+
end
|
110
|
+
|
111
|
+
###############################################################################
|
112
|
+
#
|
113
|
+
# get_data().
|
114
|
+
#
|
115
|
+
# Retrieves data from memory in one chunk, or from disk in $buffer
|
116
|
+
# sized chunks.
|
117
|
+
#
|
118
|
+
def get_data
|
119
|
+
buflen = 4096
|
120
|
+
|
121
|
+
# Return data stored in memory
|
122
|
+
unless @data.nil?
|
123
|
+
tmp = @data
|
124
|
+
@data = nil
|
125
|
+
if @using_tmpfile
|
126
|
+
@filehandle.open
|
127
|
+
@filehandle.binmode
|
128
|
+
end
|
129
|
+
return tmp
|
130
|
+
end
|
131
|
+
|
132
|
+
# Return data stored on disk
|
133
|
+
if @using_tmpfile
|
134
|
+
return @filehandle.read(buflen)
|
135
|
+
end
|
136
|
+
|
137
|
+
# No data to return
|
138
|
+
return nil
|
139
|
+
end
|
140
|
+
|
141
|
+
###############################################################################
|
142
|
+
#
|
143
|
+
# _store_bof($type)
|
144
|
+
#
|
145
|
+
# $type = 0x0005, Workbook
|
146
|
+
# $type = 0x0010, Worksheet
|
147
|
+
#
|
148
|
+
# Writes Excel BOF record to indicate the beginning of a stream or
|
149
|
+
# sub-stream in the BIFF file.
|
150
|
+
#
|
151
|
+
def store_bof(type = 0x0005)
|
152
|
+
record = 0x0809 # Record identifier
|
153
|
+
length = 0x0010 # Number of bytes to follow
|
154
|
+
|
155
|
+
# According to the SDK $build and $year should be set to zero.
|
156
|
+
# However, this throws a warning in Excel 5. So, use these
|
157
|
+
# magic numbers.
|
158
|
+
build = 0x0DBB
|
159
|
+
year = 0x07CC
|
160
|
+
|
161
|
+
bfh = 0x00000041
|
162
|
+
sfo = 0x00000006
|
163
|
+
|
164
|
+
header = [record,length].pack("vv")
|
165
|
+
data = [BIFF_Version,type,build,year,bfh,sfo].pack("vvvvVV")
|
166
|
+
|
167
|
+
prepend(header, data)
|
168
|
+
end
|
169
|
+
|
170
|
+
###############################################################################
|
171
|
+
#
|
172
|
+
# _store_eof()
|
173
|
+
#
|
174
|
+
# Writes Excel EOF record to indicate the end of a BIFF stream.
|
175
|
+
#
|
176
|
+
def store_eof
|
177
|
+
record = 0x000A
|
178
|
+
length = 0x0000
|
179
|
+
header = [record,length].pack("vv")
|
180
|
+
|
181
|
+
append(header)
|
182
|
+
end
|
183
|
+
|
184
|
+
###############################################################################
|
185
|
+
#
|
186
|
+
# _add_continue()
|
187
|
+
#
|
188
|
+
# Excel limits the size of BIFF records. In Excel 5 the limit is 2084 bytes. In
|
189
|
+
# Excel 97 the limit is 8228 bytes. Records that are longer than these limits
|
190
|
+
# must be split up into CONTINUE blocks.
|
191
|
+
#
|
192
|
+
# This function take a long BIFF record and inserts CONTINUE records as
|
193
|
+
# necessary.
|
194
|
+
#
|
195
|
+
# Some records have their own specialised Continue blocks so there is also an
|
196
|
+
# option to bypass this function.
|
197
|
+
#
|
198
|
+
def add_continue(data)
|
199
|
+
record = 0x003C # Record identifier
|
200
|
+
|
201
|
+
# Skip this if another method handles the continue blocks.
|
202
|
+
return data if @ignore_continue != 0
|
203
|
+
|
204
|
+
# The first 2080/8224 bytes remain intact. However, we have to change
|
205
|
+
# the length field of the record.
|
206
|
+
#
|
207
|
+
|
208
|
+
# in perl
|
209
|
+
# $tmp = substr($data, 0, $limit, "");
|
210
|
+
if data.length > @limit
|
211
|
+
tmp = data[0, @limit]
|
212
|
+
data[0, @limit] = ''
|
213
|
+
else
|
214
|
+
tmp = data.dup
|
215
|
+
data = ''
|
216
|
+
end
|
217
|
+
|
218
|
+
tmp[2, 2] = [@limit-4].pack('v')
|
219
|
+
|
220
|
+
# Strip out chunks of 2080/8224 bytes +4 for the header.
|
221
|
+
while (data.length > @limit)
|
222
|
+
header = [record, @limit].pack("vv")
|
223
|
+
tmp = tmp + header + data[0, @limit]
|
224
|
+
data[0, @limit] = ''
|
225
|
+
end
|
226
|
+
|
227
|
+
# Mop up the last of the data
|
228
|
+
header = [record, data.length].pack("vv")
|
229
|
+
tmp = tmp + header + data
|
230
|
+
|
231
|
+
return tmp
|
232
|
+
end
|
233
|
+
|
234
|
+
###############################################################################
|
235
|
+
#
|
236
|
+
# _add_mso_generic()
|
237
|
+
# my $type = $_[0];
|
238
|
+
# my $version = $_[1];
|
239
|
+
# my $instance = $_[2];
|
240
|
+
# my $data = $_[3];
|
241
|
+
#
|
242
|
+
# Create a mso structure that is part of an Escher drawing object. These are
|
243
|
+
# are used for images, comments and filters. This generic method is used by
|
244
|
+
# other methods to create specific mso records.
|
245
|
+
#
|
246
|
+
# Returns the packed record.
|
247
|
+
#
|
248
|
+
def add_mso_generic(type, version, instance, data, length = nil)
|
249
|
+
length = length.nil? ? data.length : length
|
250
|
+
|
251
|
+
# The header contains version and instance info packed into 2 bytes.
|
252
|
+
header = version | (instance << 4)
|
253
|
+
|
254
|
+
record = [header, type, length].pack('vvV') + data
|
255
|
+
|
256
|
+
return record
|
257
|
+
end
|
258
|
+
|
259
|
+
end
|