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
@@ -76,6 +76,8 @@ You must save source file in UTF8 and run ruby with -Ku option or set $KCODE='u'
76
76
  when use urf8 string data.
77
77
 
78
78
  == Recent Changes
79
+ v0.6.10
80
+ * Bug fix. method missing split_string_setup() in shared_string_table.rb. see https://github.com/cxn03651/writeexcel/pull/13
79
81
 
80
82
  v0.6.9
81
83
  * Bug fix. When sheetname is cell's A1 notation such as 'D1', Worksheet#autofilter causes exception.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.9
1
+ 0.6.10
@@ -20,7 +20,7 @@ class BIFFWriter < WriteFile #:nodoc:
20
20
  BIFF_Version = 0x0600
21
21
  BigEndian = [1].pack("I") == [1].pack("N")
22
22
 
23
- attr_reader :byte_order, :data, :datasize
23
+ attr_reader :data, :datasize
24
24
 
25
25
  ######################################################################
26
26
  # The args here aren't used by BIFFWriter, but they are needed by its
@@ -28,21 +28,9 @@ class BIFFWriter < WriteFile #:nodoc:
28
28
  ######################################################################
29
29
 
30
30
  def initialize
31
+ super
31
32
  set_byte_order
32
- @data = ''
33
- @datasize = 0
34
- @limit = 8224
35
- @ignore_continue = 0
36
-
37
- # Open a tmp file to store the majority of the Worksheet data. If this fails,
38
- # for example due to write permissions, store the data in memory. This can be
39
- # slow for large files.
40
- @filehandle = Tempfile.new('writeexcel')
41
- @filehandle.binmode
42
-
43
- # failed. store temporary data in memory.
44
- @using_tmpfile = @filehandle ? true : false
45
-
33
+ @ignore_continue = false
46
34
  end
47
35
 
48
36
  ###############################################################################
@@ -59,9 +47,9 @@ def set_byte_order
59
47
  number = hexdata.pack("C8")
60
48
 
61
49
  if number == teststr
62
- @byte_order = 0 # Little Endian
50
+ @byte_order = false # Little Endian
63
51
  elsif number == teststr.reverse
64
- @byte_order = 1 # Big Endian
52
+ @byte_order = true # Big Endian
65
53
  else
66
54
  # Give up. I'll fix this in a later version.
67
55
  raise( "Required floating point format not supported " +
@@ -160,37 +148,23 @@ def store_eof
160
148
  # option to bypass this function.
161
149
  #
162
150
  def add_continue(data)
163
- record = 0x003C # Record identifier
164
-
165
151
  # Skip this if another method handles the continue blocks.
166
- return data if @ignore_continue != 0
152
+ return data if @ignore_continue
153
+
154
+ record = 0x003C # Record identifier
155
+ header = [record, @limit].pack("vv")
167
156
 
168
157
  # The first 2080/8224 bytes remain intact. However, we have to change
169
158
  # the length field of the record.
170
159
  #
171
-
172
- # in perl
173
- # $tmp = substr($data, 0, $limit, "");
174
- if data.bytesize > @limit
175
- tmp = data[0, @limit]
176
- data[0, @limit] = ''
177
- else
178
- tmp = data.dup
179
- data = ''
180
- end
181
-
182
- tmp[2, 2] = [@limit-4].pack('v')
183
-
184
- # Strip out chunks of 2080/8224 bytes +4 for the header.
185
- while (data.bytesize > @limit)
186
- header = [record, @limit].pack("vv")
187
- tmp += header + data[0, @limit]
188
- data[0, @limit] = ''
189
- end
190
-
191
- # Mop up the last of the data
192
- header = [record, data.bytesize].pack("vv")
193
- tmp += header + data
160
+ data_array = split_by_length(data, @limit)
161
+ first_data = data_array.shift
162
+ last_data = data_array.pop || ''
163
+ first_data[2, 2] = [@limit-4].pack('v')
164
+ first_data <<
165
+ data_array.join(header) <<
166
+ [record, last_data.bytesize].pack('vv') <<
167
+ last_data
194
168
  end
195
169
 
196
170
  ###############################################################################
@@ -234,4 +208,16 @@ def cleanup # :nodoc:
234
208
  def inspect # :nodoc:
235
209
  to_s
236
210
  end
211
+
212
+ private
213
+
214
+ def split_by_length(data, length)
215
+ array = []
216
+ s = 0
217
+ while s < data.length
218
+ array << data[s, length]
219
+ s += length
220
+ end
221
+ array
222
+ end
237
223
  end
@@ -0,0 +1,332 @@
1
+ module Writeexcel
2
+
3
+ class Worksheet < BIFFWriter
4
+ class CellRange
5
+ attr_accessor :row_min, :row_max, :col_min, :col_max
6
+
7
+ def initialize(worksheet)
8
+ @worksheet = worksheet
9
+ end
10
+
11
+ def increment_row_max
12
+ @row_max += 1 if @row_max
13
+ end
14
+
15
+ def increment_col_max
16
+ @col_max += 1 if @col_max
17
+ end
18
+
19
+ def row(val)
20
+ @row_min = val if !@row_min || (val < row_min)
21
+ @row_max = val if !@row_max || (val > row_max)
22
+ end
23
+
24
+ def col(val)
25
+ @col_min = val if !@col_min || (val < col_min)
26
+ @col_max = val if !@col_max || (val > col_max)
27
+ end
28
+
29
+ #
30
+ # assemble the NAME record in the long format that is used for storing the repeat
31
+ # rows and columns when both are specified. This share a lot of code with
32
+ # name_record_short() but we use a separate method to keep the code clean.
33
+ # Code abstraction for reuse can be carried too far, and I should know. ;-)
34
+ #
35
+ # type
36
+ # ext_ref # TODO
37
+ #
38
+ def name_record_long(type, ext_ref) #:nodoc:
39
+ record = 0x0018 # Record identifier
40
+ length = 0x002a # Number of bytes to follow
41
+
42
+ grbit = 0x0020 # Option flags
43
+ chkey = 0x00 # Keyboard shortcut
44
+ cch = 0x01 # Length of text name
45
+ cce = 0x001a # Length of text definition
46
+ unknown01 = 0x0000 #
47
+ ixals = @worksheet.index + 1 # Sheet index
48
+ unknown02 = 0x00 #
49
+ cch_cust_menu = 0x00 # Length of cust menu text
50
+ cch_description = 0x00 # Length of description text
51
+ cch_helptopic = 0x00 # Length of help topic text
52
+ cch_statustext = 0x00 # Length of status bar text
53
+ rgch = type # Built-in name type
54
+
55
+ unknown03 = 0x29
56
+ unknown04 = 0x0017
57
+ unknown05 = 0x3b
58
+
59
+ header = [record, length].pack("vv")
60
+ data = [grbit].pack("v")
61
+ data += [chkey].pack("C")
62
+ data += [cch].pack("C")
63
+ data += [cce].pack("v")
64
+ data += [unknown01].pack("v")
65
+ data += [ixals].pack("v")
66
+ data += [unknown02].pack("C")
67
+ data += [cch_cust_menu].pack("C")
68
+ data += [cch_description].pack("C")
69
+ data += [cch_helptopic].pack("C")
70
+ data += [cch_statustext].pack("C")
71
+ data += [rgch].pack("C")
72
+
73
+ # Column definition
74
+ data += [unknown03].pack("C")
75
+ data += [unknown04].pack("v")
76
+ data += [unknown05].pack("C")
77
+ data += [ext_ref].pack("v")
78
+ data += [0x0000].pack("v")
79
+ data += [0xffff].pack("v")
80
+ data += [@col_min].pack("v")
81
+ data += [@col_max].pack("v")
82
+
83
+ # Row definition
84
+ data += [unknown05].pack("C")
85
+ data += [ext_ref].pack("v")
86
+ data += [@row_min].pack("v")
87
+ data += [@row_max].pack("v")
88
+ data += [0x00].pack("v")
89
+ data += [0xff].pack("v")
90
+ # End of data
91
+ data += [0x10].pack("C")
92
+
93
+ [header, data]
94
+ end
95
+
96
+ #
97
+ # assemble the NAME record in the short format that is used for storing the print
98
+ # area, repeat rows only and repeat columns only.
99
+ #
100
+ # type
101
+ # ext_ref # TODO
102
+ # hidden # Name is hidden
103
+ #
104
+ def name_record_short(type, ext_ref, hidden = nil) #:nodoc:
105
+ record = 0x0018 # Record identifier
106
+ length = 0x001b # Number of bytes to follow
107
+
108
+ grbit = 0x0020 # Option flags
109
+ chkey = 0x00 # Keyboard shortcut
110
+ cch = 0x01 # Length of text name
111
+ cce = 0x000b # Length of text definition
112
+ unknown01 = 0x0000 #
113
+ ixals = @worksheet.index + 1 # Sheet index
114
+ unknown02 = 0x00 #
115
+ cch_cust_menu = 0x00 # Length of cust menu text
116
+ cch_description = 0x00 # Length of description text
117
+ cch_helptopic = 0x00 # Length of help topic text
118
+ cch_statustext = 0x00 # Length of status bar text
119
+ rgch = type # Built-in name type
120
+ unknown03 = 0x3b #
121
+
122
+ grbit = 0x0021 if hidden
123
+
124
+ rowmin = row_min
125
+ rowmax = row_max
126
+ rowmin, rowmax = 0x0000, 0xffff unless row_min
127
+
128
+ colmin = col_min
129
+ colmax = col_max
130
+ colmin, colmax = 0x00, 0xff unless col_min
131
+
132
+ header = [record, length].pack("vv")
133
+ data = [grbit].pack("v")
134
+ data += [chkey].pack("C")
135
+ data += [cch].pack("C")
136
+ data += [cce].pack("v")
137
+ data += [unknown01].pack("v")
138
+ data += [ixals].pack("v")
139
+ data += [unknown02].pack("C")
140
+ data += [cch_cust_menu].pack("C")
141
+ data += [cch_description].pack("C")
142
+ data += [cch_helptopic].pack("C")
143
+ data += [cch_statustext].pack("C")
144
+ data += [rgch].pack("C")
145
+ data += [unknown03].pack("C")
146
+ data += [ext_ref].pack("v")
147
+
148
+ data += [rowmin].pack("v")
149
+ data += [rowmax].pack("v")
150
+ data += [colmin].pack("v")
151
+ data += [colmax].pack("v")
152
+
153
+ [header, data]
154
+ end
155
+ end
156
+
157
+ class CellDimension < CellRange
158
+ def row_min
159
+ @row_min || 0
160
+ end
161
+
162
+ def col_min
163
+ @col_min || 0
164
+ end
165
+
166
+ def row_max
167
+ @row_max || 0
168
+ end
169
+
170
+ def col_max
171
+ @col_max || 0
172
+ end
173
+ end
174
+
175
+ class PrintRange < CellRange
176
+ def name_record_short(ext_ref, hidden)
177
+ super(0x06, ext_ref, hidden) # 0x06 NAME type = Print_Area
178
+ end
179
+ end
180
+
181
+ class TitleRange < CellRange
182
+ def name_record_long(ext_ref)
183
+ super(0x07, ext_ref) # 0x07 NAME type = Print_Titles
184
+ end
185
+
186
+ def name_record_short(ext_ref, hidden)
187
+ super(0x07, ext_ref, hidden) # 0x07 NAME type = Print_Titles
188
+ end
189
+ end
190
+
191
+ class FilterRange < CellRange
192
+ def name_record_short(ext_ref, hidden)
193
+ super(0x0D, ext_ref, hidden) # 0x0D NAME type = Filter Database
194
+ end
195
+
196
+ def count
197
+ if @col_min && @col_max
198
+ 1 + @col_max - @col_min
199
+ else
200
+ 0
201
+ end
202
+ end
203
+
204
+ def inside?(col)
205
+ @col_min <= col && col <= @col_max
206
+ end
207
+
208
+ def store
209
+ record = 0x00EC # Record identifier
210
+
211
+ spid = @worksheet.object_ids.spid
212
+
213
+ # Number of objects written so far.
214
+ num_objects = @worksheet.images_size + @worksheet.charts_size
215
+
216
+ (0 .. count-1).each do |i|
217
+ if i == 0 && num_objects
218
+ spid, data = write_parent_msodrawing_record(count, @worksheet.comments_size, spid, vertices(i))
219
+ else
220
+ spid, data = write_child_msodrawing_record(spid, vertices(i))
221
+ end
222
+ length = data.bytesize
223
+ header = [record, length].pack("vv")
224
+ append(header, data)
225
+
226
+ store_obj_filter(num_objects + i + 1, col_min + i)
227
+ end
228
+ spid
229
+ end
230
+
231
+ private
232
+
233
+ def write_parent_msodrawing_record(num_filters, num_comments, spid, vertices)
234
+ # Write the parent MSODRAWIING record.
235
+ dg_length = 168 + 96 * (num_filters - 1)
236
+ spgr_length = 144 + 96 * (num_filters - 1)
237
+
238
+ dg_length += 128 * num_comments
239
+ spgr_length += 128 * num_comments
240
+
241
+ data = store_parent_mso_record(dg_length, spgr_length, spid)
242
+ spid += 1
243
+ data += store_child_mso_record(spid, *vertices)
244
+ spid += 1
245
+ [spid, data]
246
+ end
247
+
248
+ def write_child_msodrawing_record(spid, vertices)
249
+ data = store_child_mso_record(spid, *vertices)
250
+ spid += 1
251
+ [spid, data]
252
+ end
253
+
254
+ def store_parent_mso_record(dg_length, spgr_length, spid)
255
+ @worksheet.__send__("store_parent_mso_record", dg_length, spgr_length, spid)
256
+ end
257
+
258
+ def store_child_mso_record(spid, *vertices)
259
+ @worksheet.__send__("store_child_mso_record", spid, *vertices)
260
+ end
261
+
262
+ def vertices(i)
263
+ [
264
+ col_min + i, 0,
265
+ row_min, 0,
266
+ col_min + i + 1, 0,
267
+ row_min + 1, 0
268
+ ]
269
+ end
270
+
271
+ #
272
+ # Write the OBJ record that is part of filter records.
273
+ # obj_id # Object ID number.
274
+ # col
275
+ #
276
+ def store_obj_filter(obj_id, col) #:nodoc:
277
+ record = 0x005D # Record identifier
278
+ length = 0x0046 # Bytes to follow
279
+
280
+ obj_type = 0x0014 # Object type (combo box).
281
+ data = '' # Record data.
282
+
283
+ sub_record = 0x0000 # Sub-record identifier.
284
+ sub_length = 0x0000 # Length of sub-record.
285
+ sub_data = '' # Data of sub-record.
286
+ options = 0x2101
287
+ reserved = 0x0000
288
+
289
+ # Add ftCmo (common object data) subobject
290
+ sub_record = 0x0015 # ftCmo
291
+ sub_length = 0x0012
292
+ sub_data = [obj_type, obj_id, options, reserved, reserved, reserved].pack('vvvVVV')
293
+ data = [sub_record, sub_length].pack('vv') + sub_data
294
+
295
+ # Add ftSbs Scroll bar subobject
296
+ sub_record = 0x000C # ftSbs
297
+ sub_length = 0x0014
298
+ sub_data = ['0000000000000000640001000A00000010000100'].pack('H*')
299
+ data += [sub_record, sub_length].pack('vv') + sub_data
300
+
301
+ # Add ftLbsData (List box data) subobject
302
+ sub_record = 0x0013 # ftLbsData
303
+ sub_length = 0x1FEE # Special case (undocumented).
304
+
305
+ # If the filter is active we set one of the undocumented flags.
306
+
307
+ if @worksheet.instance_variable_get(:@filter_cols)[col]
308
+ sub_data = ['000000000100010300000A0008005700'].pack('H*')
309
+ else
310
+ sub_data = ['00000000010001030000020008005700'].pack('H*')
311
+ end
312
+
313
+ data += [sub_record, sub_length].pack('vv') + sub_data
314
+
315
+ # Add ftEnd (end of object) subobject
316
+ sub_record = 0x0000 # ftNts
317
+ sub_length = 0x0000
318
+ data += [sub_record, sub_length].pack('vv')
319
+
320
+ # Pack the record.
321
+ header = [record, length].pack('vv')
322
+
323
+ append(header, data)
324
+ end
325
+
326
+ def append(*args)
327
+ @worksheet.append(*args)
328
+ end
329
+ end
330
+ end
331
+
332
+ end