writeexcel 0.6.9 → 0.6.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|