spreadsheet 1.3.3 → 1.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.
- checksums.yaml +4 -4
- data/lib/parseexcel/parseexcel.rb +66 -58
- data/lib/parseexcel/parser.rb +1 -1
- data/lib/parseexcel.rb +1 -1
- data/lib/spreadsheet/column.rb +11 -9
- data/lib/spreadsheet/compatibility.rb +3 -1
- data/lib/spreadsheet/datatypes.rb +149 -147
- data/lib/spreadsheet/encodings.rb +20 -16
- data/lib/spreadsheet/errors.rb +2 -2
- data/lib/spreadsheet/excel/error.rb +23 -22
- data/lib/spreadsheet/excel/internals/biff5.rb +11 -11
- data/lib/spreadsheet/excel/internals/biff8.rb +13 -13
- data/lib/spreadsheet/excel/internals.rb +451 -451
- data/lib/spreadsheet/excel/offset.rb +32 -31
- data/lib/spreadsheet/excel/password_hash.rb +18 -18
- data/lib/spreadsheet/excel/reader/biff5.rb +34 -35
- data/lib/spreadsheet/excel/reader/biff8.rb +234 -222
- data/lib/spreadsheet/excel/reader.rb +1320 -1274
- data/lib/spreadsheet/excel/rgb.rb +91 -91
- data/lib/spreadsheet/excel/row.rb +99 -91
- data/lib/spreadsheet/excel/sst_entry.rb +40 -38
- data/lib/spreadsheet/excel/workbook.rb +86 -76
- data/lib/spreadsheet/excel/worksheet.rb +125 -107
- data/lib/spreadsheet/excel/writer/biff8.rb +56 -55
- data/lib/spreadsheet/excel/writer/format.rb +273 -256
- data/lib/spreadsheet/excel/writer/n_worksheet.rb +837 -798
- data/lib/spreadsheet/excel/writer/workbook.rb +671 -635
- data/lib/spreadsheet/excel/writer/worksheet.rb +898 -861
- data/lib/spreadsheet/excel/writer.rb +1 -1
- data/lib/spreadsheet/excel.rb +18 -11
- data/lib/spreadsheet/font.rb +30 -26
- data/lib/spreadsheet/format.rb +74 -59
- data/lib/spreadsheet/link.rb +7 -5
- data/lib/spreadsheet/note.rb +6 -6
- data/lib/spreadsheet/noteObject.rb +5 -5
- data/lib/spreadsheet/row.rb +33 -23
- data/lib/spreadsheet/version.rb +1 -1
- data/lib/spreadsheet/workbook.rb +27 -13
- data/lib/spreadsheet/worksheet.rb +102 -68
- data/lib/spreadsheet/writer.rb +3 -0
- data/lib/spreadsheet.rb +12 -15
- data/test/excel/reader.rb +8 -8
- data/test/excel/row.rb +35 -31
- data/test/excel/writer/workbook.rb +18 -16
- data/test/excel/writer/worksheet.rb +10 -8
- data/test/font.rb +44 -32
- data/test/format.rb +38 -33
- data/test/integration.rb +627 -598
- data/test/row.rb +5 -3
- data/test/suite.rb +7 -7
- data/test/workbook.rb +15 -14
- data/test/workbook_protection.rb +5 -5
- data/test/worksheet.rb +36 -34
- metadata +48 -6
@@ -1,41 +1,42 @@
|
|
1
|
-
require
|
1
|
+
require "spreadsheet/compatibility"
|
2
2
|
|
3
3
|
module Spreadsheet
|
4
4
|
module Excel
|
5
|
-
##
|
6
|
-
# This module is used to keep track of offsets in modified Excel documents.
|
7
|
-
# Considered internal and subject to change without notice.
|
8
|
-
module Offset
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
5
|
+
##
|
6
|
+
# This module is used to keep track of offsets in modified Excel documents.
|
7
|
+
# Considered internal and subject to change without notice.
|
8
|
+
module Offset
|
9
|
+
include Compatibility
|
10
|
+
attr_reader :changes, :offsets
|
11
|
+
def initialize *args
|
12
|
+
super
|
13
|
+
@changes = {}
|
14
|
+
@offsets = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.append_features mod
|
18
|
+
super
|
19
|
+
mod.module_eval do
|
20
|
+
class << self
|
21
|
+
include Compatibility
|
22
|
+
def offset *keys
|
23
|
+
keys.each do |key|
|
24
|
+
attr_reader key unless instance_methods.include? method_name(key)
|
25
|
+
define_method :"#{key}=" do |value|
|
26
|
+
@changes.store key, true
|
27
|
+
instance_variable_set ivar_name(key), value
|
28
|
+
end
|
29
|
+
define_method :"set_#{key}" do |value, pos, len|
|
30
|
+
instance_variable_set ivar_name(key), value
|
31
|
+
@offsets.store key, [pos, len]
|
32
|
+
havename = "have_set_#{key}"
|
33
|
+
send(havename, value, pos, len) if respond_to? havename
|
34
|
+
end
|
35
|
+
end
|
33
36
|
end
|
34
37
|
end
|
35
38
|
end
|
36
39
|
end
|
37
40
|
end
|
38
41
|
end
|
39
|
-
end
|
40
|
-
end
|
41
42
|
end
|
@@ -1,24 +1,24 @@
|
|
1
1
|
module Spreadsheet
|
2
2
|
module Excel
|
3
|
-
module Password
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
3
|
+
module Password
|
4
|
+
class << self
|
5
|
+
##
|
6
|
+
# Makes an excel-compatible hash
|
7
|
+
def password_hash(password)
|
8
|
+
hash = 0
|
9
|
+
password.chars.reverse_each { |chr| hash = rol15(hash ^ chr[0].ord) }
|
10
|
+
hash ^ password.size ^ 0xCE4B
|
11
|
+
end
|
12
12
|
|
13
|
-
|
14
|
-
##
|
15
|
-
# rotates hash 1 bit left, using lower 15 bits
|
16
|
-
def rol15(hash)
|
17
|
-
new_hash = hash << 1
|
18
|
-
(new_hash & 0x7FFF) | (new_hash >> 15)
|
19
|
-
end
|
20
|
-
end
|
13
|
+
private
|
21
14
|
|
22
|
-
|
15
|
+
##
|
16
|
+
# rotates hash 1 bit left, using lower 15 bits
|
17
|
+
def rol15(hash)
|
18
|
+
new_hash = hash << 1
|
19
|
+
(new_hash & 0x7FFF) | (new_hash >> 15)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
23
|
end
|
24
24
|
end
|
@@ -1,42 +1,41 @@
|
|
1
1
|
module Spreadsheet
|
2
2
|
module Excel
|
3
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
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
|
+
results = []
|
29
|
+
return results if len < 2
|
30
|
+
count = work[0..1].unpack1("v")
|
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
|
+
end
|
40
39
|
end
|
41
40
|
end
|
42
41
|
end
|
@@ -1,231 +1,243 @@
|
|
1
1
|
module Spreadsheet
|
2
2
|
module Excel
|
3
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
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
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
|
175
31
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
32
|
+
# When a String is too long for one Opcode, it is continued in a Continue
|
33
|
+
# Opcode. Excel may reconsider compressing the remainder of the string.
|
34
|
+
# This method appends the available remainder (decompressed if necessary) to
|
35
|
+
# the incomplete string.
|
36
|
+
def unpack_string work
|
37
|
+
opts, _ = work.unpack "C"
|
38
|
+
wide = opts & 1
|
39
|
+
string = work[1, -1]
|
40
|
+
if wide == 0
|
41
|
+
wide string
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# When a String is too long for one Opcode, it is continued in a Continue
|
47
|
+
# Opcode. Excel may reconsider compressing the remainder of the string.
|
48
|
+
# This method only evaluates the header and registers the address of the
|
49
|
+
# continuation with the previous SstEntry.
|
50
|
+
def continue_string_header work, oppos
|
51
|
+
opts, _ = work.unpack "C"
|
52
|
+
wide = opts & 1
|
53
|
+
owing = @incomplete_sst.continued_chars
|
54
|
+
size = [work.size, owing * (1 + wide) + 1].min
|
55
|
+
chars = (size - 1) / (1 + wide)
|
56
|
+
skip = size
|
57
|
+
@incomplete_sst.continue oppos + OPCODE_SIZE, size, chars
|
58
|
+
unless @incomplete_sst.continued?
|
59
|
+
@workbook.add_shared_string @incomplete_sst
|
60
|
+
skip += @incomplete_skip
|
61
|
+
@incomplete_sst = nil
|
62
|
+
@incomplete_skip = nil
|
63
|
+
end
|
64
|
+
skip
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Read more data into the Shared String Table. (see also: #read_sst)
|
69
|
+
# This method only evaluates the header, the actual work is done in #_read_sst
|
70
|
+
def continue_sst work, oppos, len
|
71
|
+
pos = 0
|
72
|
+
if @incomplete_sst
|
73
|
+
pos = continue_string_header work, oppos
|
74
|
+
elsif !@incomplete_skip.nil?
|
75
|
+
pos = @incomplete_skip
|
76
|
+
@incomplete_skip = nil
|
77
|
+
end
|
78
|
+
@sst_offset[1] += len
|
79
|
+
_read_sst work, oppos, pos
|
80
|
+
end
|
81
|
+
|
82
|
+
def postread_workbook # :nodoc:
|
83
|
+
super
|
84
|
+
@incomplete_string, @sst_size, @sst_offset, @incomplete_sst = nil, @incomplete_skip = nil
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Store the offset of extsst, so we can write a new extsst when the
|
89
|
+
# sst has changed
|
90
|
+
def read_extsst work, pos, len
|
91
|
+
@workbook.offsets.store :extsst, [pos, len]
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Read the Shared String Table present in all Biff8 Files.
|
96
|
+
# This method only evaluates the header, the actual work is done in #_read_sst
|
97
|
+
def read_sst work, pos, len
|
98
|
+
# Offset Size Contents
|
99
|
+
# 0 4 Total number of strings in the workbook (see below)
|
100
|
+
# 4 4 Number of following strings (nm)
|
101
|
+
# 8 var. List of nm Unicode strings, 16-bit string length (➜ 3.4)
|
102
|
+
_, @sst_size = work.unpack "V2"
|
103
|
+
@sst_offset = [pos, len]
|
104
|
+
@workbook.offsets.store :sst, @sst_offset
|
105
|
+
_read_sst work, pos, 8
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
# Read a string from the Spreadsheet, such as a Worksheet- or Font-Name, or a
|
110
|
+
# Number-Format. See also #read_string_header and #read_string_body
|
111
|
+
def read_string work, count_length = 1
|
112
|
+
# Offset Size Contents
|
113
|
+
# 0 1 or 2 Length of the string (character count, ln)
|
114
|
+
# 1 or 2 1 Option flags:
|
115
|
+
# Bit Mask Contents
|
116
|
+
# 0 0x01 Character compression (ccompr):
|
117
|
+
# 0 = Compressed (8-bit characters)
|
118
|
+
# 1 = Uncompressed (16-bit characters)
|
119
|
+
# 2 0x04 Asian phonetic settings (phonetic):
|
120
|
+
# 0 = Does not contain Asian phonetic settings
|
121
|
+
# 1 = Contains Asian phonetic settings
|
122
|
+
# 3 0x08 Rich-Text settings (richtext):
|
123
|
+
# 0 = Does not contain Rich-Text settings
|
124
|
+
# 1 = Contains Rich-Text settings
|
125
|
+
# [2 or 3] 2 (optional, only if richtext=1)
|
126
|
+
# Number of Rich-Text formatting runs (rt)
|
127
|
+
# [var.] 4 (optional, only if phonetic=1)
|
128
|
+
# Size of Asian phonetic settings block (in bytes, sz)
|
129
|
+
# var. ln Character array (8-bit characters
|
130
|
+
# or 2∙ln or 16-bit characters, dependent on ccompr)
|
131
|
+
# [var.] 4∙rt (optional, only if richtext=1)
|
132
|
+
# List of rt formatting runs (➜ 3.2)
|
133
|
+
# [var.] sz (optional, only if phonetic=1)
|
134
|
+
# Asian Phonetic Settings Block (➜ 3.4.2)
|
135
|
+
chars, offset, wide, _, _, available, owing, _ = read_string_header work, count_length
|
136
|
+
string, _ = read_string_body work, offset, available, wide > 0
|
137
|
+
if owing > 0
|
138
|
+
@incomplete_string = [string, chars]
|
139
|
+
end
|
140
|
+
string
|
141
|
+
end
|
142
|
+
|
143
|
+
##
|
144
|
+
# Read the body of a string. Returns the String (decompressed if necessary) and
|
145
|
+
# the available data (unchanged).
|
146
|
+
def read_string_body work, offset, available, wide
|
147
|
+
data = work[offset, available]
|
148
|
+
widened_data = wide ? data : wide(data)
|
149
|
+
[widened_data, data]
|
150
|
+
end
|
151
|
+
|
152
|
+
##
|
153
|
+
# Read the header of a string. Returns the following information in an Array:
|
154
|
+
# * The total number of characters in the string
|
155
|
+
# * The offset of the actual string data (= the length of this header in bytes)
|
156
|
+
# * Whether or not the string was compressed (0/1)
|
157
|
+
# * Whether or not the string contains asian phonetic settings (0/1)
|
158
|
+
# * Whether or not the string contains richtext formatting (0/1)
|
159
|
+
# * The number of bytes containing characters in this chunk of data
|
160
|
+
# * The number of characters missing from this chunk of data and expected to
|
161
|
+
# follow in a Continue Opcode
|
162
|
+
def read_string_header work, count_length = 1, offset = 0
|
163
|
+
fmt = (count_length == 1) ? "C2" : "vC"
|
164
|
+
chars, opts = work[offset, 1 + count_length].unpack fmt
|
165
|
+
wide = opts & 1
|
166
|
+
phonetic = (opts >> 2) & 1
|
167
|
+
richtext = (opts >> 3) & 1
|
168
|
+
size = chars * (wide + 1)
|
169
|
+
skip = 0
|
170
|
+
if richtext > 0
|
171
|
+
runs, = work[offset + 1 + count_length, 2].unpack "v"
|
172
|
+
skip = 4 * runs
|
173
|
+
end
|
174
|
+
if phonetic > 0
|
175
|
+
psize, = work[offset + 1 + count_length + richtext * 2, 4].unpack "V"
|
176
|
+
skip += psize
|
177
|
+
end
|
178
|
+
flagsize = 1 + count_length + richtext * 2 + phonetic * 4
|
179
|
+
avbl = [work.size - offset, flagsize + size].min
|
180
|
+
have_chrs = (avbl - flagsize) / (1 + wide)
|
181
|
+
owing = chars - have_chrs
|
182
|
+
[chars, flagsize, wide, phonetic, richtext, avbl, owing, skip]
|
183
|
+
end
|
184
|
+
|
185
|
+
def read_range_address_list work, len
|
186
|
+
# Cell range address, BIFF8:
|
187
|
+
# Offset Size Contents
|
188
|
+
# 0 2 Index to first row
|
189
|
+
# 2 2 Index to last row
|
190
|
+
# 4 2 Index to first column
|
191
|
+
# 6 2 Index to last column
|
192
|
+
# ! In several cases, BIFF8 still writes the BIFF2-BIFF5 format of a cell range address
|
193
|
+
# (using 8-bit values for the column indexes). This will be mentioned at the respective place.
|
194
|
+
#
|
195
|
+
results = []
|
196
|
+
return results if len < 2
|
197
|
+
count = work[0..1].unpack1("v")
|
198
|
+
offset = 2
|
199
|
+
count.times do |i|
|
200
|
+
results << work[offset...offset + 8].unpack("v4")
|
201
|
+
offset += 8
|
202
|
+
end
|
203
|
+
results
|
204
|
+
end
|
205
|
+
|
206
|
+
##
|
207
|
+
# Insert null-characters into a compressed UTF-16 string
|
208
|
+
def wide string
|
209
|
+
data = "".dup
|
210
|
+
string.each_byte { |byte| data << byte.chr << 0.chr }
|
211
|
+
data
|
212
|
+
end
|
213
|
+
|
214
|
+
private
|
215
|
+
|
216
|
+
##
|
217
|
+
# Read the Shared String Table present in all Biff8 Files.
|
218
|
+
def _read_sst work, oppos, pos
|
219
|
+
worksize = work.size
|
220
|
+
while @workbook.sst_size < @sst_size && pos < worksize
|
221
|
+
sst = SstEntry.new offset: oppos + OPCODE_SIZE + pos,
|
222
|
+
ole: @data,
|
223
|
+
reader: self
|
224
|
+
sst.chars, sst.flags, wide, sst.phonetic, sst.richtext, sst.available,
|
225
|
+
sst.continued_chars, skip = read_string_header work, 2, pos
|
226
|
+
sst.wide = wide > 0
|
227
|
+
if sst.continued?
|
228
|
+
@incomplete_sst = sst
|
229
|
+
@incomplete_skip = skip
|
230
|
+
pos += sst.available
|
231
|
+
else
|
232
|
+
@workbook.add_shared_string sst
|
233
|
+
pos += sst.available + skip
|
234
|
+
if pos > worksize
|
235
|
+
@incomplete_skip = pos - worksize
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
224
239
|
end
|
225
240
|
end
|
226
241
|
end
|
227
242
|
end
|
228
|
-
end
|
229
|
-
end
|
230
|
-
end
|
231
243
|
end
|