writeexcel 0.6.9 → 0.6.10
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +2 -0
- data/VERSION +1 -1
- data/lib/writeexcel/biffwriter.rb +29 -43
- data/lib/writeexcel/cell_range.rb +332 -0
- data/lib/writeexcel/chart.rb +50 -51
- data/lib/writeexcel/col_info.rb +87 -0
- data/lib/writeexcel/comments.rb +456 -0
- data/lib/writeexcel/convert_date_time.rb +117 -0
- data/lib/writeexcel/data_validations.rb +370 -0
- data/lib/writeexcel/debug_info.rb +5 -1
- data/lib/writeexcel/embedded_chart.rb +35 -0
- data/lib/writeexcel/format.rb +1 -1
- data/lib/writeexcel/formula.rb +3 -3
- data/lib/writeexcel/helper.rb +3 -0
- data/lib/writeexcel/image.rb +61 -1
- data/lib/writeexcel/olewriter.rb +2 -8
- data/lib/writeexcel/outline.rb +24 -0
- data/lib/writeexcel/shared_string_table.rb +153 -0
- data/lib/writeexcel/workbook.rb +86 -444
- data/lib/writeexcel/worksheet.rb +693 -2029
- data/lib/writeexcel/worksheets.rb +25 -0
- data/lib/writeexcel/write_file.rb +34 -15
- data/test/test_02_merge_formats.rb +0 -4
- data/test/test_04_dimensions.rb +0 -4
- data/test/test_05_rows.rb +0 -4
- data/test/test_06_extsst.rb +3 -6
- data/test/test_11_date_time.rb +0 -4
- data/test/test_12_date_only.rb +262 -231
- data/test/test_13_date_seconds.rb +0 -4
- data/test/test_21_escher.rb +71 -84
- data/test/test_22_mso_drawing_group.rb +0 -4
- data/test/test_23_note.rb +5 -21
- data/test/test_24_txo.rb +6 -7
- data/test/test_25_position_object.rb +0 -4
- data/test/test_26_autofilter.rb +0 -5
- data/test/test_27_autofilter.rb +0 -5
- data/test/test_28_autofilter.rb +0 -5
- data/test/test_29_process_jpg.rb +1 -1
- data/test/test_30_validation_dval.rb +4 -7
- data/test/test_31_validation_dv_strings.rb +9 -12
- data/test/test_32_validation_dv_formula.rb +11 -14
- data/test/test_42_set_properties.rb +0 -3
- data/test/test_50_name_stored.rb +0 -4
- data/test/test_51_name_print_area.rb +0 -4
- data/test/test_52_name_print_titles.rb +0 -4
- data/test/test_53_autofilter.rb +0 -4
- data/test/test_60_chart_generic.rb +42 -46
- data/test/test_61_chart_subclasses.rb +7 -11
- data/test/test_62_chart_formats.rb +12 -16
- data/test/test_63_chart_area_formats.rb +3 -7
- data/test/test_biff.rb +0 -4
- data/test/test_big_workbook.rb +17 -0
- data/test/test_format.rb +0 -3
- data/test/test_ole.rb +0 -4
- data/test/test_storage_lite.rb +0 -9
- data/test/test_workbook.rb +0 -4
- data/test/test_worksheet.rb +3 -7
- data/writeexcel.gemspec +12 -2
- metadata +12 -2
@@ -31,7 +31,11 @@ def print_caller_info(data, param = {})
|
|
31
31
|
print " in #{info[:method]}" if info[:method]
|
32
32
|
print "\n"
|
33
33
|
end
|
34
|
-
print data
|
34
|
+
print unpack_record(data) + "\n\n"
|
35
|
+
end
|
36
|
+
|
37
|
+
def unpack_record(data) # :nodoc:
|
38
|
+
data.unpack('C*').map! {|c| sprintf("%02X", c) }.join(' ')
|
35
39
|
end
|
36
40
|
end
|
37
41
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Writeexcel
|
2
|
+
|
3
|
+
class Worksheet < BIFFWriter
|
4
|
+
require 'writeexcel/helper'
|
5
|
+
|
6
|
+
class EmbeddedChart
|
7
|
+
attr_reader :row, :col, :chart, :vertices
|
8
|
+
|
9
|
+
def initialize(worksheet, row, col, chart, x_offset = 0, y_offset = 0, scale_x = 1, scale_y = 1)
|
10
|
+
@worksheet = worksheet
|
11
|
+
@row, @col, @chart, @x_offset, @y_offset, @scale_x, @scale_y =
|
12
|
+
row, col, chart, x_offset, y_offset, scale_x, scale_y
|
13
|
+
@width = default_width * scale_x
|
14
|
+
@height = default_height * scale_y
|
15
|
+
@vertices = calc_vertices
|
16
|
+
end
|
17
|
+
|
18
|
+
# Calculate the positions of comment object.
|
19
|
+
def calc_vertices
|
20
|
+
@worksheet.position_object( @col, @row, @x_offset, @y_offset, @width, @height)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def default_width
|
26
|
+
526
|
27
|
+
end
|
28
|
+
|
29
|
+
def default_height
|
30
|
+
319
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
data/lib/writeexcel/format.rb
CHANGED
data/lib/writeexcel/formula.rb
CHANGED
@@ -19,7 +19,7 @@ module Writeexcel
|
|
19
19
|
class Formula < ExcelFormulaParser #:nodoc:
|
20
20
|
require 'writeexcel/helper'
|
21
21
|
|
22
|
-
attr_accessor :
|
22
|
+
attr_accessor :workbook, :ext_sheets, :ext_refs, :ext_ref_count
|
23
23
|
|
24
24
|
def initialize(byte_order)
|
25
25
|
@byte_order = byte_order
|
@@ -231,7 +231,7 @@ def reverse(expression)
|
|
231
231
|
def check_volatile(tokens)
|
232
232
|
volatile = 0
|
233
233
|
|
234
|
-
|
234
|
+
tokens.each_index do |i|
|
235
235
|
# If the next token is a function check if it is volatile.
|
236
236
|
if tokens[i] == '_func' and @functions[tokens[i+1]][3] != 0
|
237
237
|
volatile = 1
|
@@ -268,7 +268,7 @@ def convert_number(num)
|
|
268
268
|
return [@ptg['ptgInt'], num.to_i].pack("Cv")
|
269
269
|
else # A float
|
270
270
|
num = [num.to_f].pack("d")
|
271
|
-
num.reverse! if @byte_order
|
271
|
+
num.reverse! if @byte_order
|
272
272
|
return [@ptg['ptgNum']].pack("C") + num
|
273
273
|
end
|
274
274
|
end
|
data/lib/writeexcel/helper.rb
CHANGED
data/lib/writeexcel/image.rb
CHANGED
@@ -7,7 +7,8 @@ class Image
|
|
7
7
|
attr_reader :data, :size, :checksum1, :checksum2
|
8
8
|
attr_accessor :id, :type, :width, :height, :ref_count
|
9
9
|
|
10
|
-
def initialize(row, col, filename, x_offset = 0, y_offset = 0, scale_x = 1, scale_y = 1)
|
10
|
+
def initialize(worksheet, row, col, filename, x_offset = 0, y_offset = 0, scale_x = 1, scale_y = 1)
|
11
|
+
@worksheet = worksheet
|
11
12
|
@row = row
|
12
13
|
@col = col
|
13
14
|
@filename = filename
|
@@ -29,6 +30,30 @@ def import
|
|
29
30
|
process
|
30
31
|
end
|
31
32
|
|
33
|
+
def store_image_record(i, num_images, num_charts, num_filters, num_comments, spid)
|
34
|
+
image_width = width
|
35
|
+
image_height = height
|
36
|
+
|
37
|
+
image_width = width * scale_x unless scale_x == 0
|
38
|
+
image_height = height * scale_y unless scale_y == 0
|
39
|
+
|
40
|
+
# Calculate the positions of image object.
|
41
|
+
vertices = @worksheet.position_object(col, row, x_offset, y_offset, image_width, image_height)
|
42
|
+
|
43
|
+
if (i == 0)
|
44
|
+
data = images_parent_msodrawing_record(num_images, num_charts, num_filters, num_comments, spid, id, vertices)
|
45
|
+
else
|
46
|
+
data = images_child_msodrawing_record(spid, id, vertices)
|
47
|
+
end
|
48
|
+
record = 0x00EC # Record identifier
|
49
|
+
length = 0x0000 # Bytes to follow
|
50
|
+
|
51
|
+
length = data.bytesize
|
52
|
+
header = [record, length].pack("vv")
|
53
|
+
|
54
|
+
append(header, data)
|
55
|
+
end
|
56
|
+
|
32
57
|
private
|
33
58
|
|
34
59
|
# Process the image and extract dimensions.
|
@@ -154,5 +179,40 @@ def image_checksum(data, index1 = 0, index2 = 0) #:nodoc:
|
|
154
179
|
def get_checksum_method #:nodoc:
|
155
180
|
@checksum_method = 3
|
156
181
|
end
|
182
|
+
|
183
|
+
def images_parent_msodrawing_record(num_images, charts_size, num_filters, num_comments, spid, image_id, vertices)
|
184
|
+
dg_length = 156 + 84*(num_images - 1)
|
185
|
+
spgr_length = 132 + 84*(num_images - 1)
|
186
|
+
|
187
|
+
dg_length += 120 * charts_size
|
188
|
+
spgr_length += 120 * charts_size
|
189
|
+
|
190
|
+
dg_length += 96 * num_filters
|
191
|
+
spgr_length += 96 * num_filters
|
192
|
+
|
193
|
+
dg_length += 128 * num_comments
|
194
|
+
spgr_length += 128 * num_comments
|
195
|
+
|
196
|
+
data = @worksheet.store_parent_mso_record(dg_length, spgr_length, spid)
|
197
|
+
spid += 1
|
198
|
+
data += @worksheet.store_mso_sp_container(76)
|
199
|
+
data += @worksheet.store_mso_sp(75, spid, 0x0A00)
|
200
|
+
spid += 1
|
201
|
+
data += @worksheet.store_mso_opt_image(image_id)
|
202
|
+
data += @worksheet.store_mso_client_anchor(2, *vertices)
|
203
|
+
data += @worksheet.store_mso_client_data
|
204
|
+
end
|
205
|
+
|
206
|
+
def images_child_msodrawing_record(spid, image_id, vertices)
|
207
|
+
data = @worksheet.store_mso_sp_container(76) + store_mso_sp(75, spid, 0x0A00)
|
208
|
+
spid = spid + 1
|
209
|
+
data += @worksheet.store_mso_opt_image(image_id) +
|
210
|
+
@worksheet.store_mso_client_anchor(2, *vertices) +
|
211
|
+
@worksheet.store_mso_client_data
|
212
|
+
end
|
213
|
+
|
214
|
+
def append(*args)
|
215
|
+
@worksheet.append(*args)
|
216
|
+
end
|
157
217
|
end
|
158
218
|
end
|
data/lib/writeexcel/olewriter.rb
CHANGED
@@ -98,7 +98,7 @@ def write(data)
|
|
98
98
|
|
99
99
|
###############################################################################
|
100
100
|
#
|
101
|
-
# set_size(
|
101
|
+
# set_size(biffsize)
|
102
102
|
#
|
103
103
|
# Set the size of the data to be written to the OLE stream
|
104
104
|
#
|
@@ -112,13 +112,7 @@ def set_size(size = BlockSize)
|
|
112
112
|
end
|
113
113
|
|
114
114
|
@biff_size = size
|
115
|
-
|
116
|
-
if size > BlockSize
|
117
|
-
@book_size = size
|
118
|
-
else
|
119
|
-
@book_size = BlockSize
|
120
|
-
end
|
121
|
-
|
115
|
+
@book_size = [size, BlockSize].max
|
122
116
|
@size_allowed = true
|
123
117
|
end
|
124
118
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Writeexcel
|
2
|
+
|
3
|
+
class Worksheet < BIFFWriter
|
4
|
+
require 'writeexcel/helper'
|
5
|
+
|
6
|
+
class Outline
|
7
|
+
attr_accessor :row_level, :style, :below, :right
|
8
|
+
attr_writer :visible
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@row_level = 0
|
12
|
+
@style = 0
|
13
|
+
@below = 1
|
14
|
+
@right = 1
|
15
|
+
@visible = true
|
16
|
+
end
|
17
|
+
|
18
|
+
def visible?
|
19
|
+
!!@visible
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
class Workbook < BIFFWriter
|
2
|
+
require 'writeexcel/properties'
|
3
|
+
require 'writeexcel/helper'
|
4
|
+
|
5
|
+
class SharedString
|
6
|
+
attr_reader :string, :str_id
|
7
|
+
|
8
|
+
def initialize(string, str_id)
|
9
|
+
@string, @str_id = string, str_id
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class SharedStringTable
|
14
|
+
attr_reader :str_total
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@shared_string_table = []
|
18
|
+
@string_to_shared_string = {}
|
19
|
+
@str_total = 0
|
20
|
+
end
|
21
|
+
|
22
|
+
def has_string?(string)
|
23
|
+
!!@string_to_shared_string[string]
|
24
|
+
end
|
25
|
+
|
26
|
+
def <<(string)
|
27
|
+
@str_total += 1
|
28
|
+
unless has_string?(string)
|
29
|
+
shared_string = SharedString.new(string, str_unique)
|
30
|
+
@shared_string_table << shared_string
|
31
|
+
@string_to_shared_string[string] = shared_string
|
32
|
+
end
|
33
|
+
id(string)
|
34
|
+
end
|
35
|
+
|
36
|
+
def strings
|
37
|
+
@shared_string_table.collect { |shared_string| shared_string.string }
|
38
|
+
end
|
39
|
+
|
40
|
+
def id(string)
|
41
|
+
@string_to_shared_string[string].str_id
|
42
|
+
end
|
43
|
+
|
44
|
+
def str_unique
|
45
|
+
@shared_string_table.size
|
46
|
+
end
|
47
|
+
|
48
|
+
def block_sizes
|
49
|
+
@block_sizes ||= calculate_block_sizes
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# Handling of the SST continue blocks is complicated by the need to include an
|
54
|
+
# additional continuation byte depending on whether the string is split between
|
55
|
+
# blocks or whether it starts at the beginning of the block. (There are also
|
56
|
+
# additional complications that will arise later when/if Rich Strings are
|
57
|
+
# supported). As such we cannot use the simple CONTINUE mechanism provided by
|
58
|
+
# the add_continue() method in BIFFwriter.pm. Thus we have to make two passes
|
59
|
+
# through the strings data. The first is to calculate the required block sizes
|
60
|
+
# and the second, in store_shared_strings(), is to write the actual strings.
|
61
|
+
# The first pass through the data is also used to calculate the size of the SST
|
62
|
+
# and CONTINUE records for use in setting the BOUNDSHEET record offsets. The
|
63
|
+
# downside of this is that the same algorithm repeated in store_shared_strings.
|
64
|
+
#
|
65
|
+
def calculate_block_sizes
|
66
|
+
# Iterate through the strings to calculate the CONTINUE block sizes.
|
67
|
+
#
|
68
|
+
# The SST blocks requires a specialised CONTINUE block, so we have to
|
69
|
+
# ensure that the maximum data block size is less than the limit used by
|
70
|
+
# add_continue() in BIFFwriter.pm. For simplicity we use the same size
|
71
|
+
# for the SST and CONTINUE records:
|
72
|
+
# 8228 : Maximum Excel97 block size
|
73
|
+
# -4 : Length of block header
|
74
|
+
# -8 : Length of additional SST header information
|
75
|
+
# -8 : Arbitrary number to keep within add_continue() limit
|
76
|
+
# = 8208
|
77
|
+
#
|
78
|
+
continue_limit = 8208
|
79
|
+
block_length = 0
|
80
|
+
written = 0
|
81
|
+
block_sizes = []
|
82
|
+
continue = 0
|
83
|
+
|
84
|
+
strings.each do |string|
|
85
|
+
string_length = string.bytesize
|
86
|
+
|
87
|
+
# Block length is the total length of the strings that will be
|
88
|
+
# written out in a single SST or CONTINUE block.
|
89
|
+
#
|
90
|
+
block_length += string_length
|
91
|
+
|
92
|
+
# We can write the string if it doesn't cross a CONTINUE boundary
|
93
|
+
if block_length < continue_limit
|
94
|
+
written += string_length
|
95
|
+
next
|
96
|
+
end
|
97
|
+
|
98
|
+
# Deal with the cases where the next string to be written will exceed
|
99
|
+
# the CONTINUE boundary. If the string is very long it may need to be
|
100
|
+
# written in more than one CONTINUE record.
|
101
|
+
encoding = string.unpack("xx C")[0]
|
102
|
+
split_string = 0
|
103
|
+
while block_length >= continue_limit
|
104
|
+
header_length, space_remaining, align, split_string =
|
105
|
+
Workbook.split_string_setup(encoding, split_string, continue_limit, written, continue)
|
106
|
+
|
107
|
+
if space_remaining > header_length
|
108
|
+
# Write as much as possible of the string in the current block
|
109
|
+
written += space_remaining
|
110
|
+
|
111
|
+
# Reduce the current block length by the amount written
|
112
|
+
block_length -= continue_limit -continue -align
|
113
|
+
|
114
|
+
# Store the max size for this block
|
115
|
+
block_sizes.push(continue_limit -align)
|
116
|
+
|
117
|
+
# If the current string was split then the next CONTINUE block
|
118
|
+
# should have the string continue flag (grbit) set unless the
|
119
|
+
# split string fits exactly into the remaining space.
|
120
|
+
#
|
121
|
+
if block_length > 0
|
122
|
+
continue = 1
|
123
|
+
else
|
124
|
+
continue = 0
|
125
|
+
end
|
126
|
+
else
|
127
|
+
# Store the max size for this block
|
128
|
+
block_sizes.push(written +continue)
|
129
|
+
|
130
|
+
# Not enough space to start the string in the current block
|
131
|
+
block_length -= continue_limit -space_remaining -continue
|
132
|
+
continue = 0
|
133
|
+
end
|
134
|
+
|
135
|
+
# If the string (or substr) is small enough we can write it in the
|
136
|
+
# new CONTINUE block. Else, go through the loop again to write it in
|
137
|
+
# one or more CONTINUE blocks
|
138
|
+
#
|
139
|
+
if block_length < continue_limit
|
140
|
+
written = block_length
|
141
|
+
else
|
142
|
+
written = 0
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Store the max size for the last block unless it is empty
|
148
|
+
block_sizes.push(written +continue) if written +continue != 0
|
149
|
+
|
150
|
+
block_sizes
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
data/lib/writeexcel/workbook.rb
CHANGED
@@ -12,6 +12,7 @@
|
|
12
12
|
# converted to Ruby by Hideo Nakamura, cxn03651@msj.biglobe.ne.jp
|
13
13
|
#
|
14
14
|
require 'nkf'
|
15
|
+
require 'forwardable'
|
15
16
|
require 'writeexcel/biffwriter'
|
16
17
|
require 'writeexcel/worksheet'
|
17
18
|
require 'writeexcel/chart'
|
@@ -20,13 +21,19 @@
|
|
20
21
|
require 'writeexcel/olewriter'
|
21
22
|
require 'writeexcel/storage_lite'
|
22
23
|
require 'writeexcel/compatibility'
|
24
|
+
require 'writeexcel/shared_string_table'
|
25
|
+
require 'writeexcel/worksheets'
|
23
26
|
|
24
27
|
class Workbook < BIFFWriter
|
25
28
|
require 'writeexcel/properties'
|
26
29
|
require 'writeexcel/helper'
|
27
30
|
|
31
|
+
extend Forwardable
|
32
|
+
|
28
33
|
attr_reader :url_format, :parser, :tempdir, :date_1904
|
29
|
-
attr_reader :compatibility, :palette
|
34
|
+
attr_reader :compatibility, :palette
|
35
|
+
attr_reader :ext_refs
|
36
|
+
attr_reader :worksheets
|
30
37
|
|
31
38
|
BOF = 12 # :nodoc:
|
32
39
|
EOF = 4 # :nodoc:
|
@@ -77,14 +84,13 @@ def initialize(file, default_formats = {})
|
|
77
84
|
@parser = Writeexcel::Formula.new(@byte_order)
|
78
85
|
@tempdir = nil
|
79
86
|
@date_1904 = false
|
80
|
-
@selected = 0
|
81
87
|
@xf_index = 0
|
82
88
|
@biffsize = 0
|
83
89
|
@sheet_count = 0
|
84
90
|
@chart_count = 0
|
85
91
|
@codepage = 0x04E4
|
86
92
|
@country = 1
|
87
|
-
@worksheets =
|
93
|
+
@worksheets = Worksheets.new
|
88
94
|
@formats = []
|
89
95
|
@palette = []
|
90
96
|
@biff_only = 0
|
@@ -92,15 +98,7 @@ def initialize(file, default_formats = {})
|
|
92
98
|
@internal_fh = 0
|
93
99
|
@fh_out = ""
|
94
100
|
|
95
|
-
@
|
96
|
-
:activesheet => 0,
|
97
|
-
:firstsheet => 0,
|
98
|
-
:str_total => 0,
|
99
|
-
:str_unique => 0,
|
100
|
-
:str_table => {}
|
101
|
-
}
|
102
|
-
@str_array = []
|
103
|
-
@str_block_sizes = []
|
101
|
+
@shared_string_table = SharedStringTable.new
|
104
102
|
@extsst_offsets = [] # array of [global_offset, local_offset]
|
105
103
|
@extsst_buckets = 0
|
106
104
|
@extsst_bucket_size = 0
|
@@ -224,19 +222,16 @@ def sheets(*args)
|
|
224
222
|
# worksheet = workbook.add_worksheet(name, true) # Smiley
|
225
223
|
#
|
226
224
|
def add_worksheet(sheetname = '', name_utf16be = false)
|
227
|
-
index = @worksheets.size
|
228
|
-
|
229
225
|
name, name_utf16be = check_sheetname(sheetname, name_utf16be)
|
230
226
|
|
231
227
|
init_data = [
|
232
228
|
self,
|
233
229
|
name,
|
234
|
-
index,
|
235
230
|
name_utf16be
|
236
231
|
]
|
237
232
|
worksheet = Writeexcel::Worksheet.new(*init_data)
|
238
|
-
@worksheets
|
239
|
-
@parser.set_ext_sheets(name, index) # Store names in Formula.rb
|
233
|
+
@worksheets << worksheet # Store ref for iterator
|
234
|
+
@parser.set_ext_sheets(name, worksheet.index) # Store names in Formula.rb
|
240
235
|
worksheet
|
241
236
|
end
|
242
237
|
|
@@ -315,7 +310,6 @@ def add_worksheet(sheetname = '', name_utf16be = false)
|
|
315
310
|
def add_chart(properties)
|
316
311
|
name = ''
|
317
312
|
name_utf16be = false
|
318
|
-
index = @worksheets.size
|
319
313
|
|
320
314
|
# Type must be specified so we can create the required chart instance.
|
321
315
|
type = properties[:type]
|
@@ -334,19 +328,14 @@ def add_chart(properties)
|
|
334
328
|
init_data = [
|
335
329
|
self,
|
336
330
|
name,
|
337
|
-
index,
|
338
331
|
name_utf16be
|
339
332
|
]
|
340
333
|
|
341
334
|
chart = Writeexcel::Chart.factory(type, *init_data)
|
342
335
|
# If the chart isn't embedded let the workbook control it.
|
343
336
|
if !embedded
|
344
|
-
@worksheets
|
337
|
+
@worksheets << chart # Store ref for iterator
|
345
338
|
else
|
346
|
-
# Set index to 0 so that the activate() and set_first_sheet() methods
|
347
|
-
# point back to the first worksheet if used for embedded charts.
|
348
|
-
chart.index = 0
|
349
|
-
|
350
339
|
chart.set_embedded_config_data
|
351
340
|
end
|
352
341
|
chart
|
@@ -365,7 +354,6 @@ def add_chart(properties)
|
|
365
354
|
# directory of the distro for a full explanation.
|
366
355
|
#
|
367
356
|
def add_chart_ext(filename, chartname, name_utf16be = false)
|
368
|
-
index = @worksheets.size
|
369
357
|
type = 'extarnal'
|
370
358
|
|
371
359
|
name, name_utf16be = check_sheetname(chartname, name_utf16be)
|
@@ -373,13 +361,11 @@ def add_chart_ext(filename, chartname, name_utf16be = false)
|
|
373
361
|
init_data = [
|
374
362
|
filename,
|
375
363
|
name,
|
376
|
-
|
377
|
-
name_utf16be,
|
378
|
-
@sinfo
|
364
|
+
name_utf16be
|
379
365
|
]
|
380
366
|
|
381
367
|
chart = Writeexcel::Chart.factory(self, type, init_data)
|
382
|
-
@worksheets
|
368
|
+
@worksheets << chart # Store ref for iterator
|
383
369
|
chart
|
384
370
|
end
|
385
371
|
|
@@ -398,7 +384,7 @@ def add_format(*args)
|
|
398
384
|
args.each { |arg| fmts = fmts.merge(arg) }
|
399
385
|
format = Writeexcel::Format.new(@xf_index, @default_formats.merge(fmts))
|
400
386
|
@xf_index += 1
|
401
|
-
formats.push format # Store format reference
|
387
|
+
@formats.push format # Store format reference
|
402
388
|
format
|
403
389
|
end
|
404
390
|
|
@@ -806,10 +792,6 @@ def set_properties(params)
|
|
806
792
|
@add_doc_properties = true
|
807
793
|
end
|
808
794
|
|
809
|
-
def str_unique=(val) # :nodoc:
|
810
|
-
@sinfo[:str_unique] = val
|
811
|
-
end
|
812
|
-
|
813
795
|
def extsst_buckets # :nodoc:
|
814
796
|
@extsst_buckets
|
815
797
|
end
|
@@ -830,6 +812,10 @@ def localtime=(val) # :nodoc:
|
|
830
812
|
@localtime = val
|
831
813
|
end
|
832
814
|
|
815
|
+
def update_str_table(str)
|
816
|
+
@shared_string_table << str
|
817
|
+
end
|
818
|
+
|
833
819
|
#==========================================
|
834
820
|
# Internal method
|
835
821
|
#==========================================
|
@@ -1119,25 +1105,13 @@ def store_workbook #:nodoc:
|
|
1119
1105
|
calc_mso_sizes
|
1120
1106
|
|
1121
1107
|
# Ensure that at least one worksheet has been selected.
|
1122
|
-
@worksheets
|
1123
|
-
|
1124
|
-
|
1125
|
-
@worksheets.each do |sheet|
|
1126
|
-
@selected += 1 if sheet.selected?
|
1127
|
-
sheet.active = 1 if sheet.index == @sinfo[:activesheet]
|
1108
|
+
unless @worksheets.activesheet
|
1109
|
+
@worksheets.activesheet = @worksheets.first
|
1110
|
+
@worksheets.activesheet.select
|
1128
1111
|
end
|
1129
1112
|
|
1130
1113
|
# Add Workbook globals
|
1131
|
-
|
1132
|
-
store_codepage
|
1133
|
-
store_window1
|
1134
|
-
store_hideobj
|
1135
|
-
store_1904
|
1136
|
-
store_all_fonts
|
1137
|
-
store_all_num_formats
|
1138
|
-
store_all_xfs
|
1139
|
-
store_all_styles
|
1140
|
-
store_palette
|
1114
|
+
add_workbook_globals
|
1141
1115
|
|
1142
1116
|
# Calculate the offsets required by the BOUNDSHEET records
|
1143
1117
|
calc_sheet_offsets
|
@@ -1166,6 +1140,19 @@ def store_workbook #:nodoc:
|
|
1166
1140
|
store_ole_file
|
1167
1141
|
end
|
1168
1142
|
|
1143
|
+
def add_workbook_globals
|
1144
|
+
store_bof(0x0005)
|
1145
|
+
store_codepage
|
1146
|
+
store_window1
|
1147
|
+
store_hideobj
|
1148
|
+
store_1904
|
1149
|
+
store_all_fonts
|
1150
|
+
store_all_num_formats
|
1151
|
+
store_all_xfs
|
1152
|
+
store_all_styles
|
1153
|
+
store_palette
|
1154
|
+
end
|
1155
|
+
|
1169
1156
|
#
|
1170
1157
|
# Store the workbook in an OLE container using the default handler or using
|
1171
1158
|
# OLE::Storage_Lite if the workbook data is > ~ 7MB.
|
@@ -1263,7 +1250,7 @@ def calc_sheet_offsets #:nodoc:
|
|
1263
1250
|
offset += calculate_shared_string_sizes
|
1264
1251
|
|
1265
1252
|
# Add the length of the EXTSST record.
|
1266
|
-
offset += calculate_extsst_size
|
1253
|
+
offset += calculate_extsst_size(@shared_string_table.str_unique)
|
1267
1254
|
|
1268
1255
|
# Add the length of the SUPBOOK, EXTERNSHEET and NAME records
|
1269
1256
|
offset += calculate_extern_sizes
|
@@ -1296,10 +1283,7 @@ def calc_sheet_offsets #:nodoc:
|
|
1296
1283
|
#
|
1297
1284
|
def calc_mso_sizes #:nodoc:
|
1298
1285
|
mso_size = 0 # Size of the MSODRAWINGGROUP record
|
1299
|
-
start_spid = 1024 # Initial spid for each sheet
|
1300
1286
|
max_spid = 1024 # spidMax
|
1301
|
-
num_clusters = 1 # cidcl
|
1302
|
-
shapes_saved = 0 # cspSaved
|
1303
1287
|
drawings_saved = 0 # cdgSaved
|
1304
1288
|
clusters = []
|
1305
1289
|
|
@@ -1314,52 +1298,23 @@ def calc_mso_sizes #:nodoc:
|
|
1314
1298
|
#
|
1315
1299
|
@worksheets.each do |sheet|
|
1316
1300
|
next unless sheet.type == 0x0000
|
1301
|
+
next if sheet.num_shapes == 1
|
1317
1302
|
|
1318
|
-
|
1319
|
-
|
1320
|
-
num_charts = sheet.prepare_charts
|
1321
|
-
num_filters = sheet.filter_count
|
1322
|
-
|
1323
|
-
next if num_images + num_comments + num_charts + num_filters == 0
|
1324
|
-
|
1325
|
-
# Include 1 parent MSODRAWING shape, per sheet, in the shape count.
|
1326
|
-
num_shapes = 1 + num_images + num_comments +
|
1327
|
-
num_charts + num_filters
|
1328
|
-
shapes_saved += num_shapes
|
1329
|
-
mso_size += sheet.image_mso_size
|
1330
|
-
|
1331
|
-
# Add a drawing object for each sheet with comments.
|
1332
|
-
drawings_saved += 1
|
1333
|
-
|
1334
|
-
# For each sheet start the spids at the next 1024 interval.
|
1335
|
-
max_spid = 1024 * (1 + Integer((max_spid -1)/1024.0))
|
1336
|
-
start_spid = max_spid
|
1337
|
-
|
1338
|
-
# Max spid for each sheet and eventually for the workbook.
|
1339
|
-
max_spid += num_shapes
|
1340
|
-
|
1341
|
-
# Store the cluster ids
|
1342
|
-
i = num_shapes
|
1343
|
-
while i > 0
|
1344
|
-
num_clusters += 1
|
1345
|
-
mso_size += 8
|
1346
|
-
size = i > 1024 ? 1024 : i
|
1347
|
-
|
1348
|
-
clusters.push([drawings_saved, size])
|
1349
|
-
i -= 1024
|
1350
|
-
end
|
1351
|
-
|
1352
|
-
# Pass calculated values back to the worksheet
|
1353
|
-
sheet.object_ids = [start_spid, drawings_saved, num_shapes, max_spid -1]
|
1303
|
+
mso_size, drawings_saved, max_spid, start_spid =
|
1304
|
+
sheet.push_object_ids(mso_size, drawings_saved, max_spid, start_spid, clusters)
|
1354
1305
|
end
|
1355
1306
|
|
1356
1307
|
|
1357
1308
|
# Calculate the MSODRAWINGGROUP size if we have stored some shapes.
|
1358
|
-
mso_size
|
1309
|
+
mso_size += 86 if mso_size != 0 # Smallest size is 86+8=94
|
1310
|
+
|
1311
|
+
shapes_saved = sheets.collect { |sheet| sheet.num_shapes }.
|
1312
|
+
select { |num_shapes| num_shapes > 1 }.
|
1313
|
+
inject(0) { |result, num_shapes| result + num_shapes }
|
1359
1314
|
|
1360
1315
|
@mso_size = mso_size
|
1361
1316
|
@mso_clusters = [
|
1362
|
-
max_spid,
|
1317
|
+
max_spid, clusters.size + 1, shapes_saved,
|
1363
1318
|
drawings_saved, clusters
|
1364
1319
|
]
|
1365
1320
|
end
|
@@ -1384,7 +1339,7 @@ def process_images #:nodoc:
|
|
1384
1339
|
|
1385
1340
|
@worksheets.each do |sheet|
|
1386
1341
|
next unless sheet.type == 0x0000
|
1387
|
-
next if sheet.
|
1342
|
+
next if sheet.images_size == 0
|
1388
1343
|
|
1389
1344
|
num_images = 0
|
1390
1345
|
image_mso_size = 0
|
@@ -1439,13 +1394,11 @@ def process_images #:nodoc:
|
|
1439
1394
|
# Store the Excel FONT records.
|
1440
1395
|
#
|
1441
1396
|
def store_all_fonts #:nodoc:
|
1442
|
-
format = formats[15] # The default cell format.
|
1397
|
+
format = @formats[15] # The default cell format.
|
1443
1398
|
font = format.get_font
|
1444
1399
|
|
1445
1400
|
# Fonts are 0-indexed. According to the SDK there is no index 4,
|
1446
|
-
(
|
1447
|
-
append(font)
|
1448
|
-
end
|
1401
|
+
4.times { append(font) }
|
1449
1402
|
|
1450
1403
|
# Add the default fonts for charts and comments. This aren't connected
|
1451
1404
|
# to XF formats. Note, the font size, and some other properties of
|
@@ -1502,7 +1455,7 @@ def store_all_fonts #:nodoc:
|
|
1502
1455
|
# Fonts that are marked as '_font_only' are always stored. These are used
|
1503
1456
|
# mainly for charts and may not have an associated XF record.
|
1504
1457
|
|
1505
|
-
formats.each do |fmt|
|
1458
|
+
@formats.each do |fmt|
|
1506
1459
|
key = fmt.get_font_key
|
1507
1460
|
if fmt.font_only == 0 and !fonts[key].nil?
|
1508
1461
|
# FONT has already been used
|
@@ -1532,7 +1485,7 @@ def store_all_num_formats #:nodoc:
|
|
1532
1485
|
# Iterate through the XF objects and write a FORMAT record if it isn't a
|
1533
1486
|
# built-in format type and if the FORMAT string hasn't already been used.
|
1534
1487
|
#
|
1535
|
-
formats.each do |format|
|
1488
|
+
@formats.each do |format|
|
1536
1489
|
num_format = format.num_format
|
1537
1490
|
encoding = format.num_format_enc
|
1538
1491
|
|
@@ -1561,7 +1514,7 @@ def store_all_num_formats #:nodoc:
|
|
1561
1514
|
# Write all XF records.
|
1562
1515
|
#
|
1563
1516
|
def store_all_xfs #:nodoc:
|
1564
|
-
formats.each do |format|
|
1517
|
+
@formats.each do |format|
|
1565
1518
|
xf = format.get_xf
|
1566
1519
|
append(xf)
|
1567
1520
|
end
|
@@ -1624,90 +1577,31 @@ def store_names # :nodoc:
|
|
1624
1577
|
|
1625
1578
|
def create_autofilter_name_records(sorted_worksheets) #:nodoc:
|
1626
1579
|
sorted_worksheets.each do |worksheet|
|
1627
|
-
|
1628
|
-
|
1629
|
-
# Write a Name record if Autofilter has been defined
|
1630
|
-
if worksheet.filter_count != 0
|
1631
|
-
store_name_short(
|
1632
|
-
worksheet.index,
|
1633
|
-
0x0D, # NAME type = Filter Database
|
1634
|
-
@ext_refs["#{index}:#{index}"],
|
1635
|
-
worksheet.filter_area[0],
|
1636
|
-
worksheet.filter_area[1],
|
1637
|
-
worksheet.filter_area[2],
|
1638
|
-
worksheet.filter_area[3],
|
1639
|
-
1 # Hidden
|
1640
|
-
)
|
1641
|
-
end
|
1580
|
+
append(*worksheet.autofilter_name_record_short(true))
|
1642
1581
|
end
|
1643
1582
|
end
|
1644
1583
|
|
1645
1584
|
def create_print_area_name_records(sorted_worksheets) #:nodoc:
|
1646
1585
|
sorted_worksheets.each do |worksheet|
|
1647
|
-
|
1648
|
-
|
1649
|
-
# Write a Name record if the print area has been defined
|
1650
|
-
if !worksheet.print_rowmin.nil?
|
1651
|
-
store_name_short(
|
1652
|
-
worksheet.index,
|
1653
|
-
0x06, # NAME type = Print_Area
|
1654
|
-
@ext_refs["#{index}:#{index}"],
|
1655
|
-
worksheet.print_rowmin,
|
1656
|
-
worksheet.print_rowmax,
|
1657
|
-
worksheet.print_colmin,
|
1658
|
-
worksheet.print_colmax
|
1659
|
-
)
|
1660
|
-
end
|
1586
|
+
append(*worksheet.print_area_name_record_short)
|
1661
1587
|
end
|
1662
1588
|
end
|
1663
1589
|
|
1664
1590
|
def create_print_title_name_records(sorted_worksheets) #:nodoc:
|
1665
1591
|
sorted_worksheets.each do |worksheet|
|
1666
|
-
index = worksheet.index
|
1667
|
-
rowmin = worksheet.title_rowmin
|
1668
|
-
rowmax = worksheet.title_rowmax
|
1669
|
-
colmin = worksheet.title_colmin
|
1670
|
-
colmax = worksheet.title_colmax
|
1671
|
-
key = "#{index}:#{index}"
|
1672
|
-
ref = @ext_refs[key]
|
1673
|
-
|
1674
1592
|
# Determine if row + col, row, col or nothing has been defined
|
1675
1593
|
# and write the appropriate record
|
1676
1594
|
#
|
1677
|
-
if
|
1595
|
+
if worksheet.title_range.row_min && worksheet.title_range.col_min
|
1678
1596
|
# Row and column titles have been defined.
|
1679
1597
|
# Row title has been defined.
|
1680
|
-
|
1681
|
-
|
1682
|
-
0x07, # NAME type = Print_Titles
|
1683
|
-
ref,
|
1684
|
-
rowmin,
|
1685
|
-
rowmax,
|
1686
|
-
colmin,
|
1687
|
-
colmax
|
1688
|
-
)
|
1689
|
-
elsif rowmin
|
1598
|
+
append(*worksheet.print_title_name_record_long)
|
1599
|
+
elsif worksheet.title_range.row_min
|
1690
1600
|
# Row title has been defined.
|
1691
|
-
|
1692
|
-
|
1693
|
-
0x07, # NAME type = Print_Titles
|
1694
|
-
ref,
|
1695
|
-
rowmin,
|
1696
|
-
rowmax,
|
1697
|
-
0x00,
|
1698
|
-
0xff
|
1699
|
-
)
|
1700
|
-
elsif colmin
|
1601
|
+
append(*worksheet.print_title_name_record_short)
|
1602
|
+
elsif worksheet.title_range.col_min
|
1701
1603
|
# Column title has been defined.
|
1702
|
-
|
1703
|
-
worksheet.index,
|
1704
|
-
0x07, # NAME type = Print_Titles
|
1705
|
-
ref,
|
1706
|
-
0x0000,
|
1707
|
-
0xffff,
|
1708
|
-
colmin,
|
1709
|
-
colmax
|
1710
|
-
)
|
1604
|
+
append(*worksheet.print_title_name_record_short)
|
1711
1605
|
else
|
1712
1606
|
# Nothing left to do
|
1713
1607
|
end
|
@@ -1734,12 +1628,11 @@ def store_window1 #:nodoc:
|
|
1734
1628
|
dy_win = 0x30ED # Height of window
|
1735
1629
|
|
1736
1630
|
grbit = 0x0038 # Option flags
|
1737
|
-
ctabsel = @
|
1631
|
+
ctabsel = @worksheets.selected_count # Number of workbook tabs selected
|
1738
1632
|
tab_ratio = 0x0258 # Tab to scrollbar ratio
|
1739
1633
|
|
1740
|
-
tab_cur = @
|
1741
|
-
tab_first = @
|
1742
|
-
|
1634
|
+
tab_cur = @worksheets.activesheet_index # Active worksheet
|
1635
|
+
tab_first = @worksheets.firstsheet_index # 1st displayed worksheet
|
1743
1636
|
header = [record, length].pack("vv")
|
1744
1637
|
data = [
|
1745
1638
|
x_pos, y_pos, dx_win, dy_win,
|
@@ -1761,29 +1654,7 @@ def store_window1 #:nodoc:
|
|
1761
1654
|
# encoding # Sheet name encoding
|
1762
1655
|
#
|
1763
1656
|
def store_boundsheet(sheet) #:nodoc:
|
1764
|
-
|
1765
|
-
offset = sheet.offset
|
1766
|
-
type = sheet.type
|
1767
|
-
hidden = sheet.hidden? ? 1 : 0
|
1768
|
-
encoding = sheet.is_name_utf16be? ? 1 : 0
|
1769
|
-
|
1770
|
-
record = 0x0085 # Record identifier
|
1771
|
-
length = 0x08 + sheetname.bytesize # Number of bytes to follow
|
1772
|
-
|
1773
|
-
cch = sheetname.bytesize # Length of sheet name
|
1774
|
-
|
1775
|
-
grbit = type | hidden
|
1776
|
-
|
1777
|
-
# Character length is num of chars not num of bytes
|
1778
|
-
cch /= 2 if encoding != 0
|
1779
|
-
|
1780
|
-
# Change the UTF-16 name from BE to LE
|
1781
|
-
sheetname = sheetname.unpack('v*').pack('n*') if encoding != 0
|
1782
|
-
|
1783
|
-
header = [record, length].pack("vv")
|
1784
|
-
data = [offset, grbit, cch, encoding].pack("VvCC")
|
1785
|
-
|
1786
|
-
append(header, data, sheetname)
|
1657
|
+
append(sheet.boundsheet)
|
1787
1658
|
end
|
1788
1659
|
|
1789
1660
|
#
|
@@ -1832,7 +1703,7 @@ def store_num_format(format, ifmt, encoding) #:nodoc:
|
|
1832
1703
|
if encoding == 1
|
1833
1704
|
raise "Uneven number of bytes in Unicode font name" if cch % 2 != 0
|
1834
1705
|
cch /= 2 if encoding != 0
|
1835
|
-
format = format
|
1706
|
+
format = utf16be_to_16le(format)
|
1836
1707
|
end
|
1837
1708
|
|
1838
1709
|
=begin
|
@@ -1963,135 +1834,6 @@ def store_name(name, encoding, sheet_index, formula) # :nodoc:
|
|
1963
1834
|
append(header, data)
|
1964
1835
|
end
|
1965
1836
|
|
1966
|
-
#
|
1967
|
-
# Store the NAME record in the short format that is used for storing the print
|
1968
|
-
# area, repeat rows only and repeat columns only.
|
1969
|
-
#
|
1970
|
-
# index # Sheet index
|
1971
|
-
# type
|
1972
|
-
# ext_ref # TODO
|
1973
|
-
# rowmin # Start row
|
1974
|
-
# rowmax # End row
|
1975
|
-
# colmin # Start column
|
1976
|
-
# colmax # end column
|
1977
|
-
# hidden # Name is hidden
|
1978
|
-
#
|
1979
|
-
def store_name_short(index, type, ext_ref, rowmin, rowmax, colmin, colmax, hidden = nil) #:nodoc:
|
1980
|
-
record = 0x0018 # Record identifier
|
1981
|
-
length = 0x001b # Number of bytes to follow
|
1982
|
-
|
1983
|
-
grbit = 0x0020 # Option flags
|
1984
|
-
chkey = 0x00 # Keyboard shortcut
|
1985
|
-
cch = 0x01 # Length of text name
|
1986
|
-
cce = 0x000b # Length of text definition
|
1987
|
-
unknown01 = 0x0000 #
|
1988
|
-
ixals = index + 1 # Sheet index
|
1989
|
-
unknown02 = 0x00 #
|
1990
|
-
cch_cust_menu = 0x00 # Length of cust menu text
|
1991
|
-
cch_description = 0x00 # Length of description text
|
1992
|
-
cch_helptopic = 0x00 # Length of help topic text
|
1993
|
-
cch_statustext = 0x00 # Length of status bar text
|
1994
|
-
rgch = type # Built-in name type
|
1995
|
-
unknown03 = 0x3b #
|
1996
|
-
|
1997
|
-
grbit = 0x0021 if hidden
|
1998
|
-
|
1999
|
-
header = [record, length].pack("vv")
|
2000
|
-
data = [grbit].pack("v")
|
2001
|
-
data += [chkey].pack("C")
|
2002
|
-
data += [cch].pack("C")
|
2003
|
-
data += [cce].pack("v")
|
2004
|
-
data += [unknown01].pack("v")
|
2005
|
-
data += [ixals].pack("v")
|
2006
|
-
data += [unknown02].pack("C")
|
2007
|
-
data += [cch_cust_menu].pack("C")
|
2008
|
-
data += [cch_description].pack("C")
|
2009
|
-
data += [cch_helptopic].pack("C")
|
2010
|
-
data += [cch_statustext].pack("C")
|
2011
|
-
data += [rgch].pack("C")
|
2012
|
-
data += [unknown03].pack("C")
|
2013
|
-
data += [ext_ref].pack("v")
|
2014
|
-
|
2015
|
-
data += [rowmin].pack("v")
|
2016
|
-
data += [rowmax].pack("v")
|
2017
|
-
data += [colmin].pack("v")
|
2018
|
-
data += [colmax].pack("v")
|
2019
|
-
|
2020
|
-
append(header, data)
|
2021
|
-
end
|
2022
|
-
|
2023
|
-
#
|
2024
|
-
# Store the NAME record in the long format that is used for storing the repeat
|
2025
|
-
# rows and columns when both are specified. This share a lot of code with
|
2026
|
-
# store_name_short() but we use a separate method to keep the code clean.
|
2027
|
-
# Code abstraction for reuse can be carried too far, and I should know. ;-)
|
2028
|
-
#
|
2029
|
-
# index # Sheet index
|
2030
|
-
# type
|
2031
|
-
# ext_ref # TODO
|
2032
|
-
# rowmin # Start row
|
2033
|
-
# rowmax # End row
|
2034
|
-
# colmin # Start column
|
2035
|
-
# colmax # end column
|
2036
|
-
#
|
2037
|
-
def store_name_long(index, type, ext_ref, rowmin, rowmax, colmin, colmax) #:nodoc:
|
2038
|
-
record = 0x0018 # Record identifier
|
2039
|
-
length = 0x002a # Number of bytes to follow
|
2040
|
-
|
2041
|
-
grbit = 0x0020 # Option flags
|
2042
|
-
chkey = 0x00 # Keyboard shortcut
|
2043
|
-
cch = 0x01 # Length of text name
|
2044
|
-
cce = 0x001a # Length of text definition
|
2045
|
-
unknown01 = 0x0000 #
|
2046
|
-
ixals = index + 1 # Sheet index
|
2047
|
-
unknown02 = 0x00 #
|
2048
|
-
cch_cust_menu = 0x00 # Length of cust menu text
|
2049
|
-
cch_description = 0x00 # Length of description text
|
2050
|
-
cch_helptopic = 0x00 # Length of help topic text
|
2051
|
-
cch_statustext = 0x00 # Length of status bar text
|
2052
|
-
rgch = type # Built-in name type
|
2053
|
-
|
2054
|
-
unknown03 = 0x29
|
2055
|
-
unknown04 = 0x0017
|
2056
|
-
unknown05 = 0x3b
|
2057
|
-
|
2058
|
-
header = [record, length].pack("vv")
|
2059
|
-
data = [grbit].pack("v")
|
2060
|
-
data += [chkey].pack("C")
|
2061
|
-
data += [cch].pack("C")
|
2062
|
-
data += [cce].pack("v")
|
2063
|
-
data += [unknown01].pack("v")
|
2064
|
-
data += [ixals].pack("v")
|
2065
|
-
data += [unknown02].pack("C")
|
2066
|
-
data += [cch_cust_menu].pack("C")
|
2067
|
-
data += [cch_description].pack("C")
|
2068
|
-
data += [cch_helptopic].pack("C")
|
2069
|
-
data += [cch_statustext].pack("C")
|
2070
|
-
data += [rgch].pack("C")
|
2071
|
-
|
2072
|
-
# Column definition
|
2073
|
-
data += [unknown03].pack("C")
|
2074
|
-
data += [unknown04].pack("v")
|
2075
|
-
data += [unknown05].pack("C")
|
2076
|
-
data += [ext_ref].pack("v")
|
2077
|
-
data += [0x0000].pack("v")
|
2078
|
-
data += [0xffff].pack("v")
|
2079
|
-
data += [colmin].pack("v")
|
2080
|
-
data += [colmax].pack("v")
|
2081
|
-
|
2082
|
-
# Row definition
|
2083
|
-
data += [unknown05].pack("C")
|
2084
|
-
data += [ext_ref].pack("v")
|
2085
|
-
data += [rowmin].pack("v")
|
2086
|
-
data += [rowmax].pack("v")
|
2087
|
-
data += [0x00].pack("v")
|
2088
|
-
data += [0xff].pack("v")
|
2089
|
-
# End of data
|
2090
|
-
data += [0x10].pack("C")
|
2091
|
-
|
2092
|
-
append(header, data)
|
2093
|
-
end
|
2094
|
-
|
2095
1837
|
#
|
2096
1838
|
# Stores the PALETTE biff record.
|
2097
1839
|
#
|
@@ -2173,25 +1915,22 @@ def calculate_extern_sizes # :nodoc:
|
|
2173
1915
|
end
|
2174
1916
|
|
2175
1917
|
@worksheets.each do |worksheet|
|
2176
|
-
|
2177
|
-
rowmin = worksheet.title_rowmin
|
2178
|
-
colmin = worksheet.title_colmin
|
2179
1918
|
key = "#{index}:#{index}"
|
2180
1919
|
index += 1
|
2181
1920
|
|
2182
1921
|
# Add area NAME records
|
2183
1922
|
#
|
2184
|
-
if worksheet.
|
1923
|
+
if worksheet.print_range.row_min
|
2185
1924
|
add_ext_refs(ext_refs, key) unless ext_refs[key]
|
2186
1925
|
length += 31
|
2187
1926
|
end
|
2188
1927
|
|
2189
1928
|
# Add title NAME records
|
2190
1929
|
#
|
2191
|
-
if
|
1930
|
+
if worksheet.title_range.row_min && worksheet.title_range.col_min
|
2192
1931
|
add_ext_refs(ext_refs, key) unless ext_refs[key]
|
2193
1932
|
length += 46
|
2194
|
-
elsif
|
1933
|
+
elsif worksheet.title_range.row_min || worksheet.title_range.col_min
|
2195
1934
|
add_ext_refs(ext_refs, key) unless ext_refs[key]
|
2196
1935
|
length += 31
|
2197
1936
|
else
|
@@ -2242,116 +1981,23 @@ def add_ext_refs(ext_refs, key) #:nodoc:
|
|
2242
1981
|
# downside of this is that the same algorithm repeated in store_shared_strings.
|
2243
1982
|
#
|
2244
1983
|
def calculate_shared_string_sizes #:nodoc:
|
2245
|
-
|
2246
|
-
|
2247
|
-
@sinfo[:str_table].each_key do |key|
|
2248
|
-
strings[@sinfo[:str_table][key]] = key
|
2249
|
-
end
|
2250
|
-
# The SST data could be very large, free some memory (maybe).
|
2251
|
-
@sinfo[:str_table] = nil
|
2252
|
-
@str_array = strings
|
2253
|
-
|
2254
|
-
# Iterate through the strings to calculate the CONTINUE block sizes.
|
2255
|
-
#
|
2256
|
-
# The SST blocks requires a specialised CONTINUE block, so we have to
|
2257
|
-
# ensure that the maximum data block size is less than the limit used by
|
2258
|
-
# add_continue() in BIFFwriter.pm. For simplicity we use the same size
|
2259
|
-
# for the SST and CONTINUE records:
|
2260
|
-
# 8228 : Maximum Excel97 block size
|
2261
|
-
# -4 : Length of block header
|
2262
|
-
# -8 : Length of additional SST header information
|
2263
|
-
# -8 : Arbitrary number to keep within add_continue() limit
|
2264
|
-
# = 8208
|
2265
|
-
#
|
2266
|
-
continue_limit = 8208
|
2267
|
-
block_length = 0
|
2268
|
-
written = 0
|
2269
|
-
block_sizes = []
|
2270
|
-
continue = 0
|
2271
|
-
|
2272
|
-
strings.each do |string|
|
2273
|
-
string_length = string.bytesize
|
2274
|
-
|
2275
|
-
# Block length is the total length of the strings that will be
|
2276
|
-
# written out in a single SST or CONTINUE block.
|
2277
|
-
#
|
2278
|
-
block_length += string_length
|
2279
|
-
|
2280
|
-
# We can write the string if it doesn't cross a CONTINUE boundary
|
2281
|
-
if block_length < continue_limit
|
2282
|
-
written += string_length
|
2283
|
-
next
|
2284
|
-
end
|
2285
|
-
|
2286
|
-
# Deal with the cases where the next string to be written will exceed
|
2287
|
-
# the CONTINUE boundary. If the string is very long it may need to be
|
2288
|
-
# written in more than one CONTINUE record.
|
2289
|
-
encoding = string.unpack("xx C")[0]
|
2290
|
-
split_string = 0
|
2291
|
-
while block_length >= continue_limit
|
2292
|
-
header_length, space_remaining, align, split_string =
|
2293
|
-
split_string_setup(encoding, split_string, continue_limit, written, continue)
|
2294
|
-
|
2295
|
-
if space_remaining > header_length
|
2296
|
-
# Write as much as possible of the string in the current block
|
2297
|
-
written += space_remaining
|
2298
|
-
|
2299
|
-
# Reduce the current block length by the amount written
|
2300
|
-
block_length -= continue_limit -continue -align
|
2301
|
-
|
2302
|
-
# Store the max size for this block
|
2303
|
-
block_sizes.push(continue_limit -align)
|
2304
|
-
|
2305
|
-
# If the current string was split then the next CONTINUE block
|
2306
|
-
# should have the string continue flag (grbit) set unless the
|
2307
|
-
# split string fits exactly into the remaining space.
|
2308
|
-
#
|
2309
|
-
if block_length > 0
|
2310
|
-
continue = 1
|
2311
|
-
else
|
2312
|
-
continue = 0
|
2313
|
-
end
|
2314
|
-
else
|
2315
|
-
# Store the max size for this block
|
2316
|
-
block_sizes.push(written +continue)
|
2317
|
-
|
2318
|
-
# Not enough space to start the string in the current block
|
2319
|
-
block_length -= continue_limit -space_remaining -continue
|
2320
|
-
continue = 0
|
2321
|
-
end
|
2322
|
-
|
2323
|
-
# If the string (or substr) is small enough we can write it in the
|
2324
|
-
# new CONTINUE block. Else, go through the loop again to write it in
|
2325
|
-
# one or more CONTINUE blocks
|
2326
|
-
#
|
2327
|
-
if block_length < continue_limit
|
2328
|
-
written = block_length
|
2329
|
-
else
|
2330
|
-
written = 0
|
2331
|
-
end
|
2332
|
-
end
|
2333
|
-
end
|
2334
|
-
|
2335
|
-
# Store the max size for the last block unless it is empty
|
2336
|
-
block_sizes.push(written +continue) if written +continue != 0
|
2337
|
-
|
2338
|
-
@str_block_sizes = block_sizes.dup
|
1984
|
+
str_block_sizes = @shared_string_table.block_sizes
|
2339
1985
|
|
2340
1986
|
# Calculate the total length of the SST and associated CONTINUEs (if any).
|
2341
1987
|
# The SST record will have a length even if it contains no strings.
|
2342
1988
|
# This length is required to set the offsets in the BOUNDSHEET records since
|
2343
1989
|
# they must be written before the SST records
|
2344
1990
|
#
|
2345
|
-
|
2346
|
-
length
|
2347
|
-
|
2348
|
-
|
2349
|
-
|
1991
|
+
# when array = [a, b, c] # array not empty?
|
1992
|
+
# length = 12 + a + 4 + b + 4 + c = 12 + a + b + c + 4 * (array.size - 1)
|
1993
|
+
#
|
1994
|
+
length = str_block_sizes.inject(12){|result, item| result + item}
|
1995
|
+
length += 4 * (str_block_sizes.size - 1) unless str_block_sizes.empty?
|
2350
1996
|
|
2351
1997
|
length
|
2352
1998
|
end
|
2353
1999
|
|
2354
|
-
def split_string_setup(encoding, split_string, continue_limit, written, continue) #:nodoc:
|
2000
|
+
def self.split_string_setup(encoding, split_string, continue_limit, written, continue) #:nodoc:
|
2355
2001
|
# We need to avoid the case where a string is continued in the first
|
2356
2002
|
# n bytes that contain the string header information.
|
2357
2003
|
header_length = 3 # Min string + header size -1
|
@@ -2382,6 +2028,8 @@ def split_string_setup(encoding, split_string, continue_limit, written, continue
|
|
2382
2028
|
[header_length, space_remaining, align, split_string]
|
2383
2029
|
end
|
2384
2030
|
|
2031
|
+
def_delegator self, :split_string_setup
|
2032
|
+
|
2385
2033
|
#
|
2386
2034
|
# Write all of the workbooks strings into an indexed array.
|
2387
2035
|
#
|
@@ -2393,8 +2041,6 @@ def split_string_setup(encoding, split_string, continue_limit, written, continue
|
|
2393
2041
|
# occurs wherever the start of the bucket string is written out via append().
|
2394
2042
|
#
|
2395
2043
|
def store_shared_strings #:nodoc:
|
2396
|
-
strings = @str_array
|
2397
|
-
|
2398
2044
|
record = 0x00FC # Record identifier
|
2399
2045
|
length = 0x0008 # Number of bytes to follow
|
2400
2046
|
total = 0x0000
|
@@ -2407,7 +2053,7 @@ def store_shared_strings #:nodoc:
|
|
2407
2053
|
|
2408
2054
|
# The SST and CONTINUE block sizes have been pre-calculated by
|
2409
2055
|
# calculate_shared_string_sizes()
|
2410
|
-
block_sizes = @
|
2056
|
+
block_sizes = @shared_string_table.block_sizes
|
2411
2057
|
|
2412
2058
|
# The SST record is required even if it contains no strings. Thus we will
|
2413
2059
|
# always have a length
|
@@ -2425,12 +2071,12 @@ def store_shared_strings #:nodoc:
|
|
2425
2071
|
|
2426
2072
|
# Write the SST block header information
|
2427
2073
|
header = [record, length].pack("vv")
|
2428
|
-
data = [@
|
2074
|
+
data = [@shared_string_table.str_total, @shared_string_table.str_unique].pack("VV")
|
2429
2075
|
append(header, data)
|
2430
2076
|
|
2431
2077
|
# Iterate through the strings and write them out
|
2432
|
-
return if strings.empty?
|
2433
|
-
strings.each do |string|
|
2078
|
+
return if @shared_string_table.strings.empty?
|
2079
|
+
@shared_string_table.strings.each do |string|
|
2434
2080
|
|
2435
2081
|
string_length = string.bytesize
|
2436
2082
|
|
@@ -2541,8 +2187,8 @@ def store_shared_strings #:nodoc:
|
|
2541
2187
|
# size of 8. The following algorithm generates the same size/bucket ratio
|
2542
2188
|
# as Excel.
|
2543
2189
|
#
|
2544
|
-
def calculate_extsst_size #:nodoc:
|
2545
|
-
unique_strings =
|
2190
|
+
def calculate_extsst_size(str_unique) #:nodoc:
|
2191
|
+
unique_strings = str_unique
|
2546
2192
|
|
2547
2193
|
if unique_strings < 1024
|
2548
2194
|
bucket_size = 8
|
@@ -2628,7 +2274,7 @@ def add_mso_drawing_group_continue(data) #:nodoc:
|
|
2628
2274
|
block_count = 1
|
2629
2275
|
|
2630
2276
|
# Ignore the base class add_continue() method.
|
2631
|
-
@ignore_continue =
|
2277
|
+
@ignore_continue = true
|
2632
2278
|
|
2633
2279
|
# Case 1 above. Just return the data as it is.
|
2634
2280
|
if data.bytesize <= limit
|
@@ -2661,7 +2307,7 @@ def add_mso_drawing_group_continue(data) #:nodoc:
|
|
2661
2307
|
append(header, data)
|
2662
2308
|
|
2663
2309
|
# Turn the base class add_continue() method back on.
|
2664
|
-
@ignore_continue =
|
2310
|
+
@ignore_continue = false
|
2665
2311
|
end
|
2666
2312
|
|
2667
2313
|
def devide_string(string, nth) #:nodoc:
|
@@ -2821,10 +2467,6 @@ def add_doc_properties
|
|
2821
2467
|
@add_doc_properties ||= false
|
2822
2468
|
end
|
2823
2469
|
|
2824
|
-
def formats
|
2825
|
-
@formats
|
2826
|
-
end
|
2827
|
-
|
2828
2470
|
def defined_names
|
2829
2471
|
@defined_names
|
2830
2472
|
end
|