spreadsheet 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/GUIDE.txt +209 -0
- data/History.txt +8 -0
- data/LICENSE.txt +619 -0
- data/Manifest.txt +46 -0
- data/README.txt +54 -0
- data/Rakefile +15 -0
- data/lib/parseexcel.rb +27 -0
- data/lib/parseexcel/parseexcel.rb +75 -0
- data/lib/parseexcel/parser.rb +11 -0
- data/lib/spreadsheet.rb +79 -0
- data/lib/spreadsheet/datatypes.rb +99 -0
- data/lib/spreadsheet/encodings.rb +49 -0
- data/lib/spreadsheet/excel.rb +75 -0
- data/lib/spreadsheet/excel/error.rb +26 -0
- data/lib/spreadsheet/excel/internals.rb +322 -0
- data/lib/spreadsheet/excel/internals/biff5.rb +17 -0
- data/lib/spreadsheet/excel/internals/biff8.rb +19 -0
- data/lib/spreadsheet/excel/offset.rb +37 -0
- data/lib/spreadsheet/excel/reader.rb +798 -0
- data/lib/spreadsheet/excel/reader/biff5.rb +22 -0
- data/lib/spreadsheet/excel/reader/biff8.rb +168 -0
- data/lib/spreadsheet/excel/row.rb +67 -0
- data/lib/spreadsheet/excel/sst_entry.rb +45 -0
- data/lib/spreadsheet/excel/workbook.rb +76 -0
- data/lib/spreadsheet/excel/worksheet.rb +85 -0
- data/lib/spreadsheet/excel/writer.rb +1 -0
- data/lib/spreadsheet/excel/writer/biff8.rb +66 -0
- data/lib/spreadsheet/excel/writer/format.rb +270 -0
- data/lib/spreadsheet/excel/writer/workbook.rb +586 -0
- data/lib/spreadsheet/excel/writer/worksheet.rb +556 -0
- data/lib/spreadsheet/font.rb +86 -0
- data/lib/spreadsheet/format.rb +172 -0
- data/lib/spreadsheet/formula.rb +9 -0
- data/lib/spreadsheet/row.rb +87 -0
- data/lib/spreadsheet/workbook.rb +120 -0
- data/lib/spreadsheet/worksheet.rb +215 -0
- data/lib/spreadsheet/writer.rb +29 -0
- data/test/data/test_copy.xls +0 -0
- data/test/data/test_version_excel5.xls +0 -0
- data/test/data/test_version_excel95.xls +0 -0
- data/test/data/test_version_excel97.xls +0 -0
- data/test/excel/row.rb +29 -0
- data/test/font.rb +163 -0
- data/test/integration.rb +1021 -0
- data/test/workbook.rb +21 -0
- data/test/worksheet.rb +62 -0
- metadata +113 -0
@@ -0,0 +1,215 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'spreadsheet/encodings'
|
3
|
+
require 'spreadsheet/row'
|
4
|
+
|
5
|
+
module Spreadsheet
|
6
|
+
##
|
7
|
+
# The Worksheet class. Contains most of the Spreadsheet data in Rows.
|
8
|
+
#
|
9
|
+
# Interesting Attributes
|
10
|
+
# #name :: The Name of this Worksheet.
|
11
|
+
# #default_format:: The default format used for all cells in this Workhseet
|
12
|
+
# that have no format set explicitly or in
|
13
|
+
# Row#default_format.
|
14
|
+
# #rows :: The Rows in this Worksheet. It is not recommended to
|
15
|
+
# Manipulate this Array directly. If you do, call
|
16
|
+
# #updated_from with the smallest modified index.
|
17
|
+
class Worksheet
|
18
|
+
include Encodings
|
19
|
+
attr_accessor :name, :workbook
|
20
|
+
attr_reader :rows
|
21
|
+
include Enumerable
|
22
|
+
def initialize opts={}
|
23
|
+
@dimensions = [0,0,0,0]
|
24
|
+
@name = opts[:name] || 'Worksheet'
|
25
|
+
@workbook = opts[:workbook]
|
26
|
+
@rows = []
|
27
|
+
end
|
28
|
+
##
|
29
|
+
# Add a Format to the Workbook. If you use Row#set_format, you should not
|
30
|
+
# need to use this Method.
|
31
|
+
def add_format fmt
|
32
|
+
@workbook.add_format fmt
|
33
|
+
end
|
34
|
+
##
|
35
|
+
# Get the enriched value of the Cell at _row_, _column_.
|
36
|
+
# See also Worksheet#[], Row#[].
|
37
|
+
def cell row, column
|
38
|
+
row(row)[column]
|
39
|
+
end
|
40
|
+
##
|
41
|
+
# The number of columns in this Worksheet which contain data.
|
42
|
+
def column_count
|
43
|
+
dimensions[3] - dimensions[2]
|
44
|
+
end
|
45
|
+
##
|
46
|
+
# Delete the Row at _idx_ (0-based) from this Worksheet.
|
47
|
+
def delete_row idx
|
48
|
+
res = @rows.delete_at idx
|
49
|
+
updated_from idx
|
50
|
+
res
|
51
|
+
end
|
52
|
+
##
|
53
|
+
# The default Format of this Worksheet, if you have set one.
|
54
|
+
# Returns the Workbook's default Format otherwise.
|
55
|
+
def default_format
|
56
|
+
@default_format || @workbook.default_format
|
57
|
+
end
|
58
|
+
##
|
59
|
+
# Set the default Format of this Worksheet.
|
60
|
+
def default_format= format
|
61
|
+
@default_format = format
|
62
|
+
add_format format if format
|
63
|
+
format
|
64
|
+
end
|
65
|
+
##
|
66
|
+
# Dimensions:: [ first used row, first unused row,
|
67
|
+
# first used column, first unused column ]
|
68
|
+
# ( First used means that all rows or columns before that are
|
69
|
+
# empty. First unused means that this and all following rows
|
70
|
+
# or columns are empty. )
|
71
|
+
def dimensions
|
72
|
+
@dimensions || recalculate_dimensions
|
73
|
+
end
|
74
|
+
##
|
75
|
+
# If no argument is given, #each iterates over all used Rows (from the first
|
76
|
+
# used Row until but omitting the first unused Row, see also #dimensions).
|
77
|
+
#
|
78
|
+
# If the argument skip is given, #each iterates from that row until but
|
79
|
+
# omitting the first unused Row, effectively skipping the first _skip_ Rows
|
80
|
+
# from the top of the Worksheet.
|
81
|
+
def each skip=dimensions[0], &block
|
82
|
+
skip.upto(dimensions[1] - 1) do |idx|
|
83
|
+
block.call row(idx)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
def encoding # :nodoc:
|
87
|
+
@workbook.encoding
|
88
|
+
end
|
89
|
+
##
|
90
|
+
# Insert a Row at _idx_ (0-based) containing _cells_
|
91
|
+
def insert_row idx, cells=[]
|
92
|
+
res = @rows.insert idx, Row.new(self, idx, cells)
|
93
|
+
updated_from idx
|
94
|
+
res
|
95
|
+
end
|
96
|
+
def inspect
|
97
|
+
"#<#{self.class}:#{object_id} "
|
98
|
+
names = instance_variables
|
99
|
+
names.delete '@rows'
|
100
|
+
variables = names.collect do |name|
|
101
|
+
"%s=%s" % [name, instance_variable_get(name)]
|
102
|
+
end.join(' ')
|
103
|
+
sprintf "#<%s:0x%014x %s @rows[%i]>", self.class, object_id,
|
104
|
+
variables, row_count
|
105
|
+
end
|
106
|
+
##
|
107
|
+
# Replace the Row at _idx_ with the following arguments. Like #update_row,
|
108
|
+
# but truncates the Row if there are fewer arguments than Cells in the Row.
|
109
|
+
def replace_row idx, *cells
|
110
|
+
if(row = @rows[idx]) && cells.size < row.size
|
111
|
+
cells.concat Array.new(row.size - cells.size)
|
112
|
+
end
|
113
|
+
update_row idx, *cells
|
114
|
+
end
|
115
|
+
##
|
116
|
+
# The Row at _idx_ or a new Row.
|
117
|
+
def row idx
|
118
|
+
@rows[idx] || Row.new(self, idx)
|
119
|
+
end
|
120
|
+
##
|
121
|
+
# The number of Rows in this Worksheet which contain data.
|
122
|
+
def row_count
|
123
|
+
dimensions[1] - dimensions[0]
|
124
|
+
end
|
125
|
+
##
|
126
|
+
# Tell Worksheet that the Row at _idx_ has been updated and the #dimensions
|
127
|
+
# need to be recalculated. You should not need to call this directly.
|
128
|
+
def row_updated idx, row
|
129
|
+
@dimensions = nil
|
130
|
+
row = @rows[idx] = shorten(row)
|
131
|
+
format_dates row
|
132
|
+
row
|
133
|
+
end
|
134
|
+
##
|
135
|
+
# Updates the Row at _idx_ with the following arguments.
|
136
|
+
def update_row idx, *cells
|
137
|
+
res = if row = @rows[idx]
|
138
|
+
row[0, cells.size] = cells
|
139
|
+
row
|
140
|
+
elsif cells = shorten(cells)
|
141
|
+
Row.new self, idx, cells
|
142
|
+
end
|
143
|
+
row_updated idx, res
|
144
|
+
res
|
145
|
+
end
|
146
|
+
##
|
147
|
+
# Renumbers all Rows starting at _idx_ and calls #row_updated for each of
|
148
|
+
# them.
|
149
|
+
def updated_from idx
|
150
|
+
idx.upto(@rows.size - 1) do |idx|
|
151
|
+
row = row(idx)
|
152
|
+
row.idx = idx
|
153
|
+
row_updated idx, row
|
154
|
+
end
|
155
|
+
end
|
156
|
+
##
|
157
|
+
# Get the enriched value of the Cell at _row_, _column_.
|
158
|
+
# See also Worksheet#cell, Row#[].
|
159
|
+
def [] row, column
|
160
|
+
row(row)[column]
|
161
|
+
end
|
162
|
+
##
|
163
|
+
# Set the value of the Cell at _row_, _column_ to _value_.
|
164
|
+
# See also Row#[]=.
|
165
|
+
def []= row, column, value
|
166
|
+
row(row)[column] = value
|
167
|
+
end
|
168
|
+
private
|
169
|
+
def format_dates row # :nodoc:
|
170
|
+
## If no format is set in a cell which contains a Date or Time, we will
|
171
|
+
# add a format. At the moment, the number formats correspond to builtin
|
172
|
+
# Excel number-formats. We may need to add a level of abstraction if
|
173
|
+
# additional writers are added to the library.
|
174
|
+
return unless row
|
175
|
+
row.each_with_index do |value, idx|
|
176
|
+
unless row.formats[idx]
|
177
|
+
format = nil
|
178
|
+
case value
|
179
|
+
when Date
|
180
|
+
format = @workbook.formats.find do |fmt| fmt.date? end
|
181
|
+
format ||= Format.new :number_format => client('M/D/YY', 'UTF8')
|
182
|
+
when DateTime, Time
|
183
|
+
format = @workbook.formats.find do |fmt| fmt.datetime? end
|
184
|
+
format ||= Format.new :number_format => client('M/D/YY h:mm', 'UTF8')
|
185
|
+
end
|
186
|
+
if format
|
187
|
+
row.formats[idx] = format
|
188
|
+
@workbook.add_format format
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
def index_of_first ary # :nodoc:
|
194
|
+
return unless ary
|
195
|
+
ary.index(ary.find do |elm| elm end)
|
196
|
+
end
|
197
|
+
def recalculate_dimensions # :nodoc:
|
198
|
+
shorten @rows
|
199
|
+
@dimensions = []
|
200
|
+
@dimensions[0] = index_of_first @rows
|
201
|
+
@dimensions[1] = @rows.size
|
202
|
+
compact = @rows.compact
|
203
|
+
@dimensions[2] = compact.collect do |row| index_of_first row end.compact.min
|
204
|
+
@dimensions[3] = compact.collect do |row| row.size end.max
|
205
|
+
@dimensions
|
206
|
+
end
|
207
|
+
def shorten ary # :nodoc:
|
208
|
+
return unless ary
|
209
|
+
while !ary.empty? && !ary.last
|
210
|
+
ary.pop
|
211
|
+
end
|
212
|
+
ary unless ary.empty?
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Spreadsheet
|
2
|
+
##
|
3
|
+
# Parent Class for all Writers. Implements the copying of unmodified
|
4
|
+
# Spreadsheet documents.
|
5
|
+
class Writer
|
6
|
+
def initialize io_or_path
|
7
|
+
@io_or_path = io_or_path
|
8
|
+
end
|
9
|
+
def write workbook
|
10
|
+
if @io_or_path.is_a? IO
|
11
|
+
write_workbook workbook, @io_or_path
|
12
|
+
else
|
13
|
+
File.open(@io_or_path, "wb+") do |fh|
|
14
|
+
write_workbook workbook, fh
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
private
|
19
|
+
def write_workbook workbook, io
|
20
|
+
reader = workbook.io
|
21
|
+
unless io == reader
|
22
|
+
reader.rewind
|
23
|
+
data = reader.read
|
24
|
+
io.rewind
|
25
|
+
io.write data
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/test/excel/row.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Excel::TestRow -- Spreadsheet -- 12.10.2008 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
$: << File.expand_path('../../lib', File.dirname(__FILE__))
|
5
|
+
|
6
|
+
require 'test/unit'
|
7
|
+
require 'spreadsheet'
|
8
|
+
|
9
|
+
module Spreadsheet
|
10
|
+
module Excel
|
11
|
+
class TestRow < Test::Unit::TestCase
|
12
|
+
def setup
|
13
|
+
@workbook = Excel::Workbook.new
|
14
|
+
@worksheet = Excel::Worksheet.new
|
15
|
+
@workbook.add_worksheet @worksheet
|
16
|
+
end
|
17
|
+
def test_date
|
18
|
+
row = Row.new @worksheet, 0, [nil, 27627.6789]
|
19
|
+
assert_equal Date.new(1975,8,21), row.date(1)
|
20
|
+
end
|
21
|
+
def test_datetime
|
22
|
+
row = Row.new @worksheet, 0, [nil, 27627.6789]
|
23
|
+
d1 = DateTime.new(1975,8,21) + 0.6789
|
24
|
+
d2 = row.datetime 1
|
25
|
+
assert_equal d1, d2
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/test/font.rb
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# TestFont -- Spreadsheet -- 09.10.2008 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
$: << File.expand_path('../lib', File.dirname(__FILE__))
|
5
|
+
|
6
|
+
require 'test/unit'
|
7
|
+
require 'spreadsheet/font'
|
8
|
+
|
9
|
+
module Spreadsheet
|
10
|
+
class TestFont < Test::Unit::TestCase
|
11
|
+
def setup
|
12
|
+
@font = Font.new 'Arial'
|
13
|
+
end
|
14
|
+
def test_italic
|
15
|
+
assert_equal false, @font.italic
|
16
|
+
@font.italic!
|
17
|
+
assert_equal true, @font.italic
|
18
|
+
@font.italic = nil
|
19
|
+
assert_equal false, @font.italic
|
20
|
+
@font.italic = 1
|
21
|
+
assert_equal true, @font.italic
|
22
|
+
end
|
23
|
+
def test_encoding
|
24
|
+
assert_equal :default, @font.encoding
|
25
|
+
@font.encoding = :apple_roman
|
26
|
+
assert_equal :apple_roman, @font.encoding
|
27
|
+
@font.encoding = 'Chinese Simplified'
|
28
|
+
assert_equal :chinese_simplified, @font.encoding
|
29
|
+
assert_raises ArgumentError do @font.size = 'ascii' end
|
30
|
+
assert_equal :chinese_simplified, @font.encoding
|
31
|
+
@font.encoding = nil
|
32
|
+
assert_equal :default, @font.encoding
|
33
|
+
end
|
34
|
+
def test_family
|
35
|
+
assert_equal :none, @font.family
|
36
|
+
@font.family = :roman
|
37
|
+
assert_equal :roman, @font.family
|
38
|
+
@font.family = 'Swiss'
|
39
|
+
assert_equal :swiss, @font.family
|
40
|
+
assert_raises ArgumentError do @font.size = :greek end
|
41
|
+
assert_equal :swiss, @font.family
|
42
|
+
@font.family = nil
|
43
|
+
assert_equal :none, @font.family
|
44
|
+
end
|
45
|
+
def test_name
|
46
|
+
assert_equal 'Arial', @font.name
|
47
|
+
@font.name = 'Helvetica'
|
48
|
+
assert_equal 'Helvetica', @font.name
|
49
|
+
end
|
50
|
+
def test_outline
|
51
|
+
assert_equal false, @font.outline
|
52
|
+
@font.outline!
|
53
|
+
assert_equal true, @font.outline
|
54
|
+
@font.outline = nil
|
55
|
+
assert_equal false, @font.outline
|
56
|
+
@font.outline = 1
|
57
|
+
assert_equal true, @font.outline
|
58
|
+
end
|
59
|
+
def test_escapement
|
60
|
+
assert_equal :normal, @font.escapement
|
61
|
+
@font.escapement = :superscript
|
62
|
+
assert_equal :superscript, @font.escapement
|
63
|
+
@font.escapement = 'sub'
|
64
|
+
assert_equal :subscript, @font.escapement
|
65
|
+
assert_raises ArgumentError do @font.size = "upwards" end
|
66
|
+
assert_equal :subscript, @font.escapement
|
67
|
+
@font.escapement = nil
|
68
|
+
assert_equal :normal, @font.escapement
|
69
|
+
end
|
70
|
+
def test_shadow
|
71
|
+
assert_equal false, @font.shadow
|
72
|
+
@font.shadow!
|
73
|
+
assert_equal true, @font.shadow
|
74
|
+
@font.shadow = nil
|
75
|
+
assert_equal false, @font.shadow
|
76
|
+
@font.shadow = 1
|
77
|
+
assert_equal true, @font.shadow
|
78
|
+
end
|
79
|
+
def test_size
|
80
|
+
assert_equal 10, @font.size
|
81
|
+
@font.size = 12
|
82
|
+
assert_equal 12, @font.size
|
83
|
+
@font.size = 11.2
|
84
|
+
assert_equal 11.2, @font.size
|
85
|
+
assert_raises ArgumentError do @font.size = "123" end
|
86
|
+
end
|
87
|
+
def test_strikeout
|
88
|
+
assert_equal false, @font.strikeout
|
89
|
+
@font.strikeout!
|
90
|
+
assert_equal true, @font.strikeout
|
91
|
+
@font.strikeout = nil
|
92
|
+
assert_equal false, @font.strikeout
|
93
|
+
@font.strikeout = 1
|
94
|
+
assert_equal true, @font.strikeout
|
95
|
+
end
|
96
|
+
def test_underline
|
97
|
+
assert_equal :none, @font.underline
|
98
|
+
@font.underline = :single
|
99
|
+
assert_equal :single, @font.underline
|
100
|
+
@font.underline = 'double accounting'
|
101
|
+
assert_equal :double_accounting, @font.underline
|
102
|
+
assert_raises ArgumentError do @font.size = :triple end
|
103
|
+
assert_equal :double_accounting, @font.underline
|
104
|
+
@font.underline = nil
|
105
|
+
assert_equal :none, @font.underline
|
106
|
+
@font.underline = true
|
107
|
+
assert_equal :single, @font.underline
|
108
|
+
end
|
109
|
+
def test_weight
|
110
|
+
assert_equal :normal, @font.weight
|
111
|
+
@font.weight = :bold
|
112
|
+
assert_equal :bold, @font.weight
|
113
|
+
@font.weight = 100
|
114
|
+
assert_equal 100, @font.weight
|
115
|
+
assert_raises ArgumentError do @font.weight = Object.new end
|
116
|
+
assert_equal 100, @font.weight
|
117
|
+
@font.weight = 'bold'
|
118
|
+
assert_equal :bold, @font.weight
|
119
|
+
@font.weight = nil
|
120
|
+
assert_equal :normal, @font.weight
|
121
|
+
end
|
122
|
+
def test_key
|
123
|
+
expected = 'Arial_10_normal_normal_none_text_none_default'
|
124
|
+
assert_equal expected, @font.key
|
125
|
+
@font.name = 'Helvetica'
|
126
|
+
expected = 'Helvetica_10_normal_normal_none_text_none_default'
|
127
|
+
assert_equal expected, @font.key
|
128
|
+
@font.size = 12
|
129
|
+
expected = 'Helvetica_12_normal_normal_none_text_none_default'
|
130
|
+
assert_equal expected, @font.key
|
131
|
+
@font.weight = :bold
|
132
|
+
expected = 'Helvetica_12_bold_normal_none_text_none_default'
|
133
|
+
assert_equal expected, @font.key
|
134
|
+
@font.italic!
|
135
|
+
expected = 'Helvetica_12_bold_italic_normal_none_text_none_default'
|
136
|
+
assert_equal expected, @font.key
|
137
|
+
@font.strikeout!
|
138
|
+
expected = 'Helvetica_12_bold_italic_strikeout_normal_none_text_none_default'
|
139
|
+
assert_equal expected, @font.key
|
140
|
+
@font.outline!
|
141
|
+
expected = 'Helvetica_12_bold_italic_strikeout_outline_normal_none_text_none_default'
|
142
|
+
assert_equal expected, @font.key
|
143
|
+
@font.shadow!
|
144
|
+
expected = 'Helvetica_12_bold_italic_strikeout_outline_shadow_normal_none_text_none_default'
|
145
|
+
assert_equal expected, @font.key
|
146
|
+
@font.escapement = :super
|
147
|
+
expected = 'Helvetica_12_bold_italic_strikeout_outline_shadow_superscript_none_text_none_default'
|
148
|
+
assert_equal expected, @font.key
|
149
|
+
@font.underline = :double
|
150
|
+
expected = 'Helvetica_12_bold_italic_strikeout_outline_shadow_superscript_double_text_none_default'
|
151
|
+
assert_equal expected, @font.key
|
152
|
+
@font.color = :blue
|
153
|
+
expected = 'Helvetica_12_bold_italic_strikeout_outline_shadow_superscript_double_blue_none_default'
|
154
|
+
assert_equal expected, @font.key
|
155
|
+
@font.family = :swiss
|
156
|
+
expected = 'Helvetica_12_bold_italic_strikeout_outline_shadow_superscript_double_blue_swiss_default'
|
157
|
+
assert_equal expected, @font.key
|
158
|
+
@font.encoding = :iso_latin1
|
159
|
+
expected = 'Helvetica_12_bold_italic_strikeout_outline_shadow_superscript_double_blue_swiss_iso_latin1'
|
160
|
+
assert_equal expected, @font.key
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|