keeguon-spreadsheet 0.9.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +619 -0
- data/Manifest.txt +85 -0
- data/bin/xlsopcodes +18 -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 +80 -0
- data/lib/spreadsheet/column.rb +71 -0
- data/lib/spreadsheet/compatibility.rb +23 -0
- data/lib/spreadsheet/datatypes.rb +161 -0
- data/lib/spreadsheet/encodings.rb +57 -0
- data/lib/spreadsheet/excel.rb +88 -0
- data/lib/spreadsheet/excel/error.rb +26 -0
- data/lib/spreadsheet/excel/internals.rb +458 -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 +41 -0
- data/lib/spreadsheet/excel/password_hash.rb +24 -0
- data/lib/spreadsheet/excel/reader.rb +1302 -0
- data/lib/spreadsheet/excel/reader/biff5.rb +42 -0
- data/lib/spreadsheet/excel/reader/biff8.rb +231 -0
- data/lib/spreadsheet/excel/rgb.rb +122 -0
- data/lib/spreadsheet/excel/row.rb +98 -0
- data/lib/spreadsheet/excel/sst_entry.rb +46 -0
- data/lib/spreadsheet/excel/workbook.rb +80 -0
- data/lib/spreadsheet/excel/worksheet.rb +115 -0
- data/lib/spreadsheet/excel/writer.rb +1 -0
- data/lib/spreadsheet/excel/writer/biff8.rb +75 -0
- data/lib/spreadsheet/excel/writer/format.rb +264 -0
- data/lib/spreadsheet/excel/writer/n_worksheet.rb +888 -0
- data/lib/spreadsheet/excel/writer/workbook.rb +735 -0
- data/lib/spreadsheet/excel/writer/worksheet.rb +940 -0
- data/lib/spreadsheet/font.rb +115 -0
- data/lib/spreadsheet/format.rb +209 -0
- data/lib/spreadsheet/formula.rb +9 -0
- data/lib/spreadsheet/helpers.rb +11 -0
- data/lib/spreadsheet/link.rb +43 -0
- data/lib/spreadsheet/note.rb +23 -0
- data/lib/spreadsheet/noteObject.rb +17 -0
- data/lib/spreadsheet/row.rb +151 -0
- data/lib/spreadsheet/workbook.rb +143 -0
- data/lib/spreadsheet/worksheet.rb +326 -0
- data/lib/spreadsheet/writer.rb +30 -0
- data/test/data/test_adding_data_to_existing_file.xls +0 -0
- data/test/data/test_borders.xls +0 -0
- data/test/data/test_changes.xls +0 -0
- data/test/data/test_comment.xls +0 -0
- data/test/data/test_copy.xls +0 -0
- data/test/data/test_datetime.xls +0 -0
- data/test/data/test_empty.xls +0 -0
- data/test/data/test_formula.xls +0 -0
- data/test/data/test_long_sst_record.xls +0 -0
- data/test/data/test_margin.xls +0 -0
- data/test/data/test_merged_and_protected.xls +0 -0
- data/test/data/test_merged_cells.xls +0 -0
- data/test/data/test_missing_row.xls +0 -0
- data/test/data/test_pagesetup.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/data/test_version_excel97_2010.xls +0 -0
- data/test/data/test_worksheet_visibility.xls +0 -0
- data/test/excel/reader.rb +30 -0
- data/test/excel/row.rb +40 -0
- data/test/excel/writer/workbook.rb +95 -0
- data/test/excel/writer/worksheet.rb +81 -0
- data/test/font.rb +163 -0
- data/test/format.rb +95 -0
- data/test/integration.rb +1390 -0
- data/test/row.rb +33 -0
- data/test/suite.rb +18 -0
- data/test/workbook.rb +55 -0
- data/test/workbook_protection.rb +19 -0
- data/test/worksheet.rb +112 -0
- metadata +148 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
module Spreadsheet
|
2
|
+
module Excel
|
3
|
+
class Reader
|
4
|
+
##
|
5
|
+
# This Module collects reader methods such as read_string that are specific to
|
6
|
+
# Biff5. This Module is likely to be expanded as Support for older Versions
|
7
|
+
# of Excel grows.
|
8
|
+
module Biff5
|
9
|
+
##
|
10
|
+
# Read a String of 8-bit Characters
|
11
|
+
def read_string work, count_length=1
|
12
|
+
# Offset Size Contents
|
13
|
+
# 0 1 or 2 Length of the string (character count, ln)
|
14
|
+
# 1 or 2 ln Character array (8-bit characters)
|
15
|
+
fmt = count_length == 1 ? 'C' : 'v'
|
16
|
+
length, = work.unpack fmt
|
17
|
+
work[count_length, length]
|
18
|
+
end
|
19
|
+
|
20
|
+
def read_range_address_list work, len
|
21
|
+
# Cell range address, BIFF2-BIFF5:
|
22
|
+
# Offset Size Contents
|
23
|
+
# 0 2 Index to first row
|
24
|
+
# 2 2 Index to last row
|
25
|
+
# 4 1 Index to first column
|
26
|
+
# 5 1 Index to last column
|
27
|
+
#
|
28
|
+
offset = 0, results = []
|
29
|
+
return results if len < 2
|
30
|
+
count = work[0..1].unpack('v').first
|
31
|
+
offset = 2
|
32
|
+
count.times do |i|
|
33
|
+
results << work[offset...offset+6].unpack('v2c2')
|
34
|
+
offset += 6
|
35
|
+
end
|
36
|
+
results
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,231 @@
|
|
1
|
+
module Spreadsheet
|
2
|
+
module Excel
|
3
|
+
class Reader
|
4
|
+
##
|
5
|
+
# This Module collects reader methods such as read_string that are specific to
|
6
|
+
# Biff8. This Module is likely to be expanded as Support for older Versions
|
7
|
+
# of Excel grows and methods get moved here for disambiguation.
|
8
|
+
module Biff8
|
9
|
+
include Spreadsheet::Excel::Internals
|
10
|
+
##
|
11
|
+
# When a String is too long for one Opcode, it is continued in a Continue
|
12
|
+
# Opcode. Excel may reconsider compressing the remainder of the string.
|
13
|
+
# This method appends the available remainder (decompressed if necessary) to
|
14
|
+
# the incomplete string.
|
15
|
+
def continue_string work, incomplete_string=@incomplete_string
|
16
|
+
opts, _ = work.unpack 'C'
|
17
|
+
wide = opts & 1
|
18
|
+
head, chars = incomplete_string
|
19
|
+
owing = chars - head.size / 2
|
20
|
+
size = owing * (wide + 1)
|
21
|
+
string = work[1, size]
|
22
|
+
if wide == 0
|
23
|
+
string = wide string
|
24
|
+
end
|
25
|
+
head << string
|
26
|
+
if head.size >= chars * 2
|
27
|
+
@incomplete_string = nil
|
28
|
+
end
|
29
|
+
size + 1
|
30
|
+
end
|
31
|
+
# When a String is too long for one Opcode, it is continued in a Continue
|
32
|
+
# Opcode. Excel may reconsider compressing the remainder of the string.
|
33
|
+
# This method appends the available remainder (decompressed if necessary) to
|
34
|
+
# the incomplete string.
|
35
|
+
def unpack_string work
|
36
|
+
opts, _ = work.unpack 'C'
|
37
|
+
wide = opts & 1
|
38
|
+
string = work[1, -1]
|
39
|
+
if wide == 0
|
40
|
+
string = wide string
|
41
|
+
end
|
42
|
+
end
|
43
|
+
##
|
44
|
+
# When a String is too long for one Opcode, it is continued in a Continue
|
45
|
+
# Opcode. Excel may reconsider compressing the remainder of the string.
|
46
|
+
# This method only evaluates the header and registers the address of the
|
47
|
+
# continuation with the previous SstEntry.
|
48
|
+
def continue_string_header work, oppos
|
49
|
+
opts, _ = work.unpack 'C'
|
50
|
+
wide = opts & 1
|
51
|
+
owing = @incomplete_sst.continued_chars
|
52
|
+
size = [work.size, owing * (1 + wide) + 1].min
|
53
|
+
chars = (size - 1) / (1 + wide)
|
54
|
+
skip = size
|
55
|
+
@incomplete_sst.continue oppos + OPCODE_SIZE, size, chars
|
56
|
+
unless @incomplete_sst.continued?
|
57
|
+
@workbook.add_shared_string @incomplete_sst
|
58
|
+
skip += @incomplete_skip
|
59
|
+
@incomplete_sst = nil
|
60
|
+
@incomplete_skip = nil
|
61
|
+
end
|
62
|
+
skip
|
63
|
+
end
|
64
|
+
##
|
65
|
+
# Read more data into the Shared String Table. (see also: #read_sst)
|
66
|
+
# This method only evaluates the header, the actual work is done in #_read_sst
|
67
|
+
def continue_sst work, oppos, len
|
68
|
+
pos = 0
|
69
|
+
if @incomplete_sst
|
70
|
+
pos = continue_string_header work, oppos
|
71
|
+
elsif !@incomplete_skip.nil?
|
72
|
+
pos = @incomplete_skip
|
73
|
+
@incomplete_skip = nil
|
74
|
+
end
|
75
|
+
@sst_offset[1] += len
|
76
|
+
_read_sst work, oppos, pos
|
77
|
+
end
|
78
|
+
def postread_workbook # :nodoc:
|
79
|
+
super
|
80
|
+
@incomplete_string, @sst_size, @sst_offset, @incomplete_sst = nil, @incomplete_skip = nil
|
81
|
+
end
|
82
|
+
##
|
83
|
+
# Store the offset of extsst, so we can write a new extsst when the
|
84
|
+
# sst has changed
|
85
|
+
def read_extsst work, pos, len
|
86
|
+
@workbook.offsets.store :extsst, [pos, len]
|
87
|
+
end
|
88
|
+
##
|
89
|
+
# Read the Shared String Table present in all Biff8 Files.
|
90
|
+
# This method only evaluates the header, the actual work is done in #_read_sst
|
91
|
+
def read_sst work, pos, len
|
92
|
+
# Offset Size Contents
|
93
|
+
# 0 4 Total number of strings in the workbook (see below)
|
94
|
+
# 4 4 Number of following strings (nm)
|
95
|
+
# 8 var. List of nm Unicode strings, 16-bit string length (➜ 3.4)
|
96
|
+
_, @sst_size = work.unpack 'V2'
|
97
|
+
@sst_offset = [pos, len]
|
98
|
+
@workbook.offsets.store :sst, @sst_offset
|
99
|
+
_read_sst work, pos, 8
|
100
|
+
end
|
101
|
+
##
|
102
|
+
# Read a string from the Spreadsheet, such as a Worksheet- or Font-Name, or a
|
103
|
+
# Number-Format. See also #read_string_header and #read_string_body
|
104
|
+
def read_string work, count_length=1
|
105
|
+
# Offset Size Contents
|
106
|
+
# 0 1 or 2 Length of the string (character count, ln)
|
107
|
+
# 1 or 2 1 Option flags:
|
108
|
+
# Bit Mask Contents
|
109
|
+
# 0 0x01 Character compression (ccompr):
|
110
|
+
# 0 = Compressed (8-bit characters)
|
111
|
+
# 1 = Uncompressed (16-bit characters)
|
112
|
+
# 2 0x04 Asian phonetic settings (phonetic):
|
113
|
+
# 0 = Does not contain Asian phonetic settings
|
114
|
+
# 1 = Contains Asian phonetic settings
|
115
|
+
# 3 0x08 Rich-Text settings (richtext):
|
116
|
+
# 0 = Does not contain Rich-Text settings
|
117
|
+
# 1 = Contains Rich-Text settings
|
118
|
+
# [2 or 3] 2 (optional, only if richtext=1)
|
119
|
+
# Number of Rich-Text formatting runs (rt)
|
120
|
+
# [var.] 4 (optional, only if phonetic=1)
|
121
|
+
# Size of Asian phonetic settings block (in bytes, sz)
|
122
|
+
# var. ln Character array (8-bit characters
|
123
|
+
# or 2∙ln or 16-bit characters, dependent on ccompr)
|
124
|
+
# [var.] 4∙rt (optional, only if richtext=1)
|
125
|
+
# List of rt formatting runs (➜ 3.2)
|
126
|
+
# [var.] sz (optional, only if phonetic=1)
|
127
|
+
# Asian Phonetic Settings Block (➜ 3.4.2)
|
128
|
+
chars, offset, wide, _, _, available, owing, _ = read_string_header work, count_length
|
129
|
+
string, _ = read_string_body work, offset, available, wide > 0
|
130
|
+
if owing > 0
|
131
|
+
@incomplete_string = [string, chars]
|
132
|
+
end
|
133
|
+
string
|
134
|
+
end
|
135
|
+
##
|
136
|
+
# Read the body of a string. Returns the String (decompressed if necessary) and
|
137
|
+
# the available data (unchanged).
|
138
|
+
def read_string_body work, offset, available, wide
|
139
|
+
data = work[offset, available]
|
140
|
+
widened_data = wide ? data : wide(data)
|
141
|
+
[widened_data, data]
|
142
|
+
end
|
143
|
+
##
|
144
|
+
# Read the header of a string. Returns the following information in an Array:
|
145
|
+
# * The total number of characters in the string
|
146
|
+
# * The offset of the actual string data (= the length of this header in bytes)
|
147
|
+
# * Whether or not the string was compressed (0/1)
|
148
|
+
# * Whether or not the string contains asian phonetic settings (0/1)
|
149
|
+
# * Whether or not the string contains richtext formatting (0/1)
|
150
|
+
# * The number of bytes containing characters in this chunk of data
|
151
|
+
# * The number of characters missing from this chunk of data and expected to
|
152
|
+
# follow in a Continue Opcode
|
153
|
+
def read_string_header work, count_length=1, offset=0
|
154
|
+
fmt = count_length == 1 ? 'C2' : 'vC'
|
155
|
+
chars, opts = work[offset, 1 + count_length].unpack fmt
|
156
|
+
wide = opts & 1
|
157
|
+
phonetic = (opts >> 2) & 1
|
158
|
+
richtext = (opts >> 3) & 1
|
159
|
+
size = chars * (wide + 1)
|
160
|
+
skip = 0
|
161
|
+
if richtext > 0
|
162
|
+
runs, = work[offset + 1 + count_length, 2].unpack 'v'
|
163
|
+
skip = 4 * runs
|
164
|
+
end
|
165
|
+
if phonetic > 0
|
166
|
+
psize, = work[offset + 1 + count_length + richtext * 2, 4].unpack 'V'
|
167
|
+
skip += psize
|
168
|
+
end
|
169
|
+
flagsize = 1 + count_length + richtext * 2 + phonetic * 4
|
170
|
+
avbl = [work.size - offset, flagsize + size].min
|
171
|
+
have_chrs = (avbl - flagsize) / (1 + wide)
|
172
|
+
owing = chars - have_chrs
|
173
|
+
[chars, flagsize, wide, phonetic, richtext, avbl, owing, skip]
|
174
|
+
end
|
175
|
+
|
176
|
+
def read_range_address_list work, len
|
177
|
+
# Cell range address, BIFF8:
|
178
|
+
# Offset Size Contents
|
179
|
+
# 0 2 Index to first row
|
180
|
+
# 2 2 Index to last row
|
181
|
+
# 4 2 Index to first column
|
182
|
+
# 6 2 Index to last column
|
183
|
+
# ! In several cases, BIFF8 still writes the BIFF2-BIFF5 format of a cell range address
|
184
|
+
# (using 8-bit values for the column indexes). This will be mentioned at the respective place.
|
185
|
+
#
|
186
|
+
offset = 0, results = []
|
187
|
+
return results if len < 2
|
188
|
+
count = work[0..1].unpack('v').first
|
189
|
+
offset = 2
|
190
|
+
count.times do |i|
|
191
|
+
results << work[offset...offset+8].unpack('v4')
|
192
|
+
offset += 8
|
193
|
+
end
|
194
|
+
results
|
195
|
+
end
|
196
|
+
##
|
197
|
+
# Insert null-characters into a compressed UTF-16 string
|
198
|
+
def wide string
|
199
|
+
data = ''
|
200
|
+
string.each_byte do |byte| data << byte.chr << 0.chr end
|
201
|
+
data
|
202
|
+
end
|
203
|
+
private
|
204
|
+
##
|
205
|
+
# Read the Shared String Table present in all Biff8 Files.
|
206
|
+
def _read_sst work, oppos, pos
|
207
|
+
worksize = work.size
|
208
|
+
while @workbook.sst_size < @sst_size && pos < worksize do
|
209
|
+
sst = SstEntry.new :offset => oppos + OPCODE_SIZE + pos,
|
210
|
+
:ole => @data,
|
211
|
+
:reader => self
|
212
|
+
sst.chars, sst.flags, wide, sst.phonetic, sst.richtext, sst.available,
|
213
|
+
sst.continued_chars, skip = read_string_header work, 2, pos
|
214
|
+
sst.wide = wide > 0
|
215
|
+
if sst.continued?
|
216
|
+
@incomplete_sst = sst
|
217
|
+
@incomplete_skip = skip
|
218
|
+
pos += sst.available
|
219
|
+
else
|
220
|
+
@workbook.add_shared_string sst
|
221
|
+
pos += sst.available + skip
|
222
|
+
if pos > worksize
|
223
|
+
@incomplete_skip = pos - worksize
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# A quick and dirty class for converting color palette values to RGB values.
|
2
|
+
# The values below have the form 0xRRGGBB, where RR is the red level, GG the
|
3
|
+
# green level, and BB the blue level. Each level is a value from 0 to 255,
|
4
|
+
# just as one would expect in HTML markup.
|
5
|
+
|
6
|
+
# Future directions may include:
|
7
|
+
# - support for mapping RGB values to "best fit" palette values
|
8
|
+
#
|
9
|
+
# by Dan Caugherty https://github.com/dancaugherty/spreadsheet/compare/master...rgb
|
10
|
+
|
11
|
+
module Spreadsheet
|
12
|
+
module Excel
|
13
|
+
class Rgb
|
14
|
+
attr_accessor :r, :g, :b
|
15
|
+
|
16
|
+
@@RGB_MAP = {
|
17
|
+
:xls_color_0 => 0x000000,
|
18
|
+
:xls_color_1 => 0xffffff,
|
19
|
+
:xls_color_2 => 0xff0000,
|
20
|
+
:xls_color_3 => 0x00ff00,
|
21
|
+
:xls_color_4 => 0x0000ff,
|
22
|
+
:xls_color_5 => 0xffff00,
|
23
|
+
:xls_color_6 => 0xff00ff,
|
24
|
+
:xls_color_7 => 0x00ffff,
|
25
|
+
:xls_color_8 => 0x800000,
|
26
|
+
:xls_color_9 => 0x008000,
|
27
|
+
:xls_color_10 => 0x008000,
|
28
|
+
:xls_color_11 => 0x000080,
|
29
|
+
:xls_color_12 => 0x808080,
|
30
|
+
:xls_color_13 => 0x008080,
|
31
|
+
:xls_color_14 => 0xc0c0c0,
|
32
|
+
:xls_color_15 => 0x808080,
|
33
|
+
:xls_color_16 => 0x9999ff,
|
34
|
+
:xls_color_17 => 0x993366,
|
35
|
+
:xls_color_18 => 0xffffcc,
|
36
|
+
:xls_color_19 => 0xccffff,
|
37
|
+
:xls_color_20 => 0x660066,
|
38
|
+
:xls_color_21 => 0xff8080,
|
39
|
+
:xls_color_22 => 0x0066cc,
|
40
|
+
:xls_color_23 => 0xccccff,
|
41
|
+
:xls_color_24 => 0x000080,
|
42
|
+
:xls_color_25 => 0xff00ff,
|
43
|
+
:xls_color_26 => 0xffff00,
|
44
|
+
:xls_color_27 => 0x00ffff,
|
45
|
+
:xls_color_28 => 0x800080,
|
46
|
+
:xls_color_29 => 0x800000,
|
47
|
+
:xls_color_30 => 0x008080,
|
48
|
+
:xls_color_31 => 0x0000ff,
|
49
|
+
:xls_color_32 => 0x00ccff,
|
50
|
+
:xls_color_33 => 0xccffff,
|
51
|
+
:xls_color_34 => 0xccffcc,
|
52
|
+
:xls_color_35 => 0xffff99,
|
53
|
+
:xls_color_36 => 0x99ccff,
|
54
|
+
:xls_color_37 => 0xff99cc,
|
55
|
+
:xls_color_38 => 0xcc99ff,
|
56
|
+
:xls_color_39 => 0xffcc99,
|
57
|
+
:xls_color_40 => 0x3366ff,
|
58
|
+
:xls_color_41 => 0x33cccc,
|
59
|
+
:xls_color_42 => 0x99cc00,
|
60
|
+
:xls_color_43 => 0xffcc00,
|
61
|
+
:xls_color_44 => 0xff9900,
|
62
|
+
:xls_color_45 => 0xff6600,
|
63
|
+
:xls_color_46 => 0x666699,
|
64
|
+
:xls_color_47 => 0x969696,
|
65
|
+
:xls_color_48 => 0x003366,
|
66
|
+
:xls_color_49 => 0x339966,
|
67
|
+
:xls_color_50 => 0x003300,
|
68
|
+
:xls_color_51 => 0x333300,
|
69
|
+
:xls_color_52 => 0x993300,
|
70
|
+
:xls_color_53 => 0x993366,
|
71
|
+
:xls_color_54 => 0x333399,
|
72
|
+
:xls_color_55 => 0x333333,
|
73
|
+
:builtin_black => 0x000000,
|
74
|
+
:builtin_white => 0xffffff,
|
75
|
+
:builtin_red => 0xff0000,
|
76
|
+
:builtin_green => 0x00ff00,
|
77
|
+
:builtin_blue => 0x0000ff,
|
78
|
+
:builtin_yellow => 0xffff00,
|
79
|
+
:builtin_magenta => 0xff00ff,
|
80
|
+
:builtin_cyan => 0x00ffff,
|
81
|
+
:aqua => 0x00ffff,
|
82
|
+
:black => 0x000000,
|
83
|
+
:blue => 0x0000ff,
|
84
|
+
:cyan => 0x00ffff,
|
85
|
+
:brown => 0x800000,
|
86
|
+
:fuchsia => 0xff00ff,
|
87
|
+
:gray => 0x808080,
|
88
|
+
:grey => 0x808080,
|
89
|
+
:green => 0x008000,
|
90
|
+
:lime => 0x00ff00,
|
91
|
+
:magenta => 0xff00ff,
|
92
|
+
:navy => 0x000080,
|
93
|
+
:orange => 0xff9900,
|
94
|
+
:purple => 0x800080,
|
95
|
+
:red => 0xff0000,
|
96
|
+
:silver => 0xc0c0c0,
|
97
|
+
:white => 0xffffff,
|
98
|
+
:yellow => 0xffff00
|
99
|
+
}
|
100
|
+
|
101
|
+
def self.to_rgb color_symbol
|
102
|
+
col = @@RGB_MAP[color_symbol]
|
103
|
+
return Rgb.new(col >> 16, (col & 0xff00) >> 8, col & 0xff) if col
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
|
107
|
+
def initialize(r,g,b)
|
108
|
+
@r = r & 0xff
|
109
|
+
@g = g & 0xff
|
110
|
+
@b = b & 0xff
|
111
|
+
end
|
112
|
+
|
113
|
+
def to_i
|
114
|
+
(r * (256 * 256)) + (g * 256) + b
|
115
|
+
end
|
116
|
+
|
117
|
+
def as_hex
|
118
|
+
to_i.to_s(16)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'spreadsheet/row'
|
3
|
+
|
4
|
+
module Spreadsheet
|
5
|
+
module Excel
|
6
|
+
##
|
7
|
+
# Excel-specific Row methods
|
8
|
+
class Row < Spreadsheet::Row
|
9
|
+
##
|
10
|
+
# The Excel date calculation erroneously assumes that 1900 is a leap-year. All
|
11
|
+
# Dates after 28.2.1900 are off by one.
|
12
|
+
LEAP_ERROR = Date.new 1900, 2, 28
|
13
|
+
##
|
14
|
+
# Force convert the cell at _idx_ to a Date
|
15
|
+
def date idx
|
16
|
+
_date at(idx)
|
17
|
+
end
|
18
|
+
##
|
19
|
+
# Force convert the cell at _idx_ to a DateTime
|
20
|
+
def datetime idx
|
21
|
+
_datetime at(idx)
|
22
|
+
end
|
23
|
+
def each
|
24
|
+
size.times do |idx|
|
25
|
+
yield self[idx]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
##
|
29
|
+
# Access data in this Row like you would in an Array. If a cell is formatted
|
30
|
+
# as a Date or DateTime, the decoded Date or DateTime value is returned.
|
31
|
+
def [] idx, len=nil
|
32
|
+
if len
|
33
|
+
idx = idx...(idx+len)
|
34
|
+
end
|
35
|
+
if idx.is_a? Range
|
36
|
+
data = []
|
37
|
+
idx.each do |i|
|
38
|
+
data.push enriched_data(i, at(i))
|
39
|
+
end
|
40
|
+
data
|
41
|
+
else
|
42
|
+
enriched_data idx, at(idx)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
##
|
46
|
+
# Returns data as an array. If a cell is formatted as a Date or DateTime, the
|
47
|
+
# decoded Date or DateTime value is returned.
|
48
|
+
def to_a
|
49
|
+
self[0...length]
|
50
|
+
end
|
51
|
+
private
|
52
|
+
def _date data # :nodoc:
|
53
|
+
return data if data.is_a?(Date)
|
54
|
+
datetime = _datetime data
|
55
|
+
Date.new datetime.year, datetime.month, datetime.day
|
56
|
+
end
|
57
|
+
def _datetime data # :nodoc:
|
58
|
+
return data if data.is_a?(DateTime)
|
59
|
+
base = @worksheet.date_base
|
60
|
+
date = base + data.to_f
|
61
|
+
hour = (data.to_f % 1) * 24
|
62
|
+
min = (hour % 1) * 60
|
63
|
+
sec = ((min % 1) * 60).round
|
64
|
+
min = min.floor
|
65
|
+
hour = hour.floor
|
66
|
+
if sec > 59
|
67
|
+
sec = 0
|
68
|
+
min += 1
|
69
|
+
end
|
70
|
+
if min > 59
|
71
|
+
min = 0
|
72
|
+
hour += 1
|
73
|
+
end
|
74
|
+
if hour > 23
|
75
|
+
hour = 0
|
76
|
+
date += 1
|
77
|
+
end
|
78
|
+
if LEAP_ERROR > base
|
79
|
+
date -= 1
|
80
|
+
end
|
81
|
+
DateTime.new(date.year, date.month, date.day, hour, min, sec)
|
82
|
+
end
|
83
|
+
def enriched_data idx, data # :nodoc:
|
84
|
+
res = nil
|
85
|
+
if link = @worksheet.links[[@idx, idx]]
|
86
|
+
res = link
|
87
|
+
elsif data.is_a?(Numeric) && fmt = format(idx)
|
88
|
+
res = if fmt.datetime? || fmt.time?
|
89
|
+
_datetime data
|
90
|
+
elsif fmt.date?
|
91
|
+
_date data
|
92
|
+
end
|
93
|
+
end
|
94
|
+
res || data
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|