writeexcel 0.6.9 → 0.6.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/README.rdoc +2 -0
  2. data/VERSION +1 -1
  3. data/lib/writeexcel/biffwriter.rb +29 -43
  4. data/lib/writeexcel/cell_range.rb +332 -0
  5. data/lib/writeexcel/chart.rb +50 -51
  6. data/lib/writeexcel/col_info.rb +87 -0
  7. data/lib/writeexcel/comments.rb +456 -0
  8. data/lib/writeexcel/convert_date_time.rb +117 -0
  9. data/lib/writeexcel/data_validations.rb +370 -0
  10. data/lib/writeexcel/debug_info.rb +5 -1
  11. data/lib/writeexcel/embedded_chart.rb +35 -0
  12. data/lib/writeexcel/format.rb +1 -1
  13. data/lib/writeexcel/formula.rb +3 -3
  14. data/lib/writeexcel/helper.rb +3 -0
  15. data/lib/writeexcel/image.rb +61 -1
  16. data/lib/writeexcel/olewriter.rb +2 -8
  17. data/lib/writeexcel/outline.rb +24 -0
  18. data/lib/writeexcel/shared_string_table.rb +153 -0
  19. data/lib/writeexcel/workbook.rb +86 -444
  20. data/lib/writeexcel/worksheet.rb +693 -2029
  21. data/lib/writeexcel/worksheets.rb +25 -0
  22. data/lib/writeexcel/write_file.rb +34 -15
  23. data/test/test_02_merge_formats.rb +0 -4
  24. data/test/test_04_dimensions.rb +0 -4
  25. data/test/test_05_rows.rb +0 -4
  26. data/test/test_06_extsst.rb +3 -6
  27. data/test/test_11_date_time.rb +0 -4
  28. data/test/test_12_date_only.rb +262 -231
  29. data/test/test_13_date_seconds.rb +0 -4
  30. data/test/test_21_escher.rb +71 -84
  31. data/test/test_22_mso_drawing_group.rb +0 -4
  32. data/test/test_23_note.rb +5 -21
  33. data/test/test_24_txo.rb +6 -7
  34. data/test/test_25_position_object.rb +0 -4
  35. data/test/test_26_autofilter.rb +0 -5
  36. data/test/test_27_autofilter.rb +0 -5
  37. data/test/test_28_autofilter.rb +0 -5
  38. data/test/test_29_process_jpg.rb +1 -1
  39. data/test/test_30_validation_dval.rb +4 -7
  40. data/test/test_31_validation_dv_strings.rb +9 -12
  41. data/test/test_32_validation_dv_formula.rb +11 -14
  42. data/test/test_42_set_properties.rb +0 -3
  43. data/test/test_50_name_stored.rb +0 -4
  44. data/test/test_51_name_print_area.rb +0 -4
  45. data/test/test_52_name_print_titles.rb +0 -4
  46. data/test/test_53_autofilter.rb +0 -4
  47. data/test/test_60_chart_generic.rb +42 -46
  48. data/test/test_61_chart_subclasses.rb +7 -11
  49. data/test/test_62_chart_formats.rb +12 -16
  50. data/test/test_63_chart_area_formats.rb +3 -7
  51. data/test/test_biff.rb +0 -4
  52. data/test/test_big_workbook.rb +17 -0
  53. data/test/test_format.rb +0 -3
  54. data/test/test_ole.rb +0 -4
  55. data/test/test_storage_lite.rb +0 -9
  56. data/test/test_workbook.rb +0 -4
  57. data/test/test_worksheet.rb +3 -7
  58. data/writeexcel.gemspec +12 -2
  59. 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.unpack('C*').map! {|byte| sprintf("%02X", byte) }.join(' ') + "\n\n"
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
@@ -376,7 +376,7 @@ def get_font # :nodoc:
376
376
  if (encoding == 1)
377
377
  raise "Uneven number of bytes in Unicode font name" if cch % 2 != 0
378
378
  cch /= 2 if encoding !=0
379
- rgch = rgch.unpack('n*').pack('v*')
379
+ rgch = utf16be_to_16le(rgch)
380
380
  end
381
381
 
382
382
  record = 0x31
@@ -19,7 +19,7 @@ module Writeexcel
19
19
  class Formula < ExcelFormulaParser #:nodoc:
20
20
  require 'writeexcel/helper'
21
21
 
22
- attr_accessor :byte_order, :workbook, :ext_sheets, :ext_refs, :ext_ref_count
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
- (0..tokens.size - 1).each do |i|
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 != 0 && @byte_order != ''
271
+ num.reverse! if @byte_order
272
272
  return [@ptg['ptgNum']].pack("C") + num
273
273
  end
274
274
  end
@@ -19,6 +19,9 @@ def convert_to_ascii_if_ascii(str)
19
19
  end
20
20
  private :convert_to_ascii_if_ascii
21
21
 
22
+ def utf16be_to_16le(utf16be)
23
+ utf16be.unpack('n*').pack('v*')
24
+ end
22
25
 
23
26
  def utf8_to_16be(utf8)
24
27
  ruby_18 { NKF.nkf('-w16B0 -m0 -W', utf8) } ||
@@ -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
@@ -98,7 +98,7 @@ def write(data)
98
98
 
99
99
  ###############################################################################
100
100
  #
101
- # set_size($biffsize)
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
@@ -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, :sinfo
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
- @sinfo = {
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[index] = worksheet # Store ref for iterator
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[index] = chart # Store ref for iterator
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
- index,
377
- name_utf16be,
378
- @sinfo
364
+ name_utf16be
379
365
  ]
380
366
 
381
367
  chart = Writeexcel::Chart.factory(self, type, init_data)
382
- @worksheets[index] = chart # Store ref for iterator
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[0].select if @sinfo[:activesheet] == 0
1123
-
1124
- # Calculate the number of selected sheet tabs and set the active sheet.
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
- store_bof(0x0005)
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
- num_images = sheet.num_images
1319
- num_comments = sheet.prepare_comments
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 += 86 if mso_size != 0 # Smallest size is 86+8=94
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, num_clusters, shapes_saved,
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.prepare_images == 0
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
- (0..3).each do
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
- index = worksheet.index
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
- index = worksheet.index
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 rowmin && colmin
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
- store_name_long(
1681
- worksheet.index,
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
- store_name_short(
1692
- worksheet.index,
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
- store_name_short(
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 = @selected # Number of workbook tabs selected
1631
+ ctabsel = @worksheets.selected_count # Number of workbook tabs selected
1738
1632
  tab_ratio = 0x0258 # Tab to scrollbar ratio
1739
1633
 
1740
- tab_cur = @sinfo[:activesheet] # Active worksheet
1741
- tab_first = @sinfo[:firstsheet] # 1st displayed worksheet
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
- sheetname = sheet.name
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.unpack('n*').pack('v*')
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.print_rowmin
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 rowmin and colmin
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 rowmin or colmin
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
- strings = Array.new(@sinfo[:str_unique])
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
- length = 12
2346
- length += block_sizes.shift unless block_sizes.empty? # SST
2347
- while !block_sizes.empty? do
2348
- length += 4 + block_sizes.shift # CONTINUEs
2349
- end
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 = @str_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 = [@sinfo[:str_total], @sinfo[:str_unique]].pack("VV")
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 = @sinfo[:str_unique]
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 = 1
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 = 0
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