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.
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