write_xlsx 0.0.2 → 0.0.3

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 (47) hide show
  1. data/README.rdoc +3 -0
  2. data/Rakefile +1 -1
  3. data/VERSION +1 -1
  4. data/examples/formats.rb +498 -0
  5. data/lib/write_xlsx/chart.rb +15 -15
  6. data/lib/write_xlsx/chartsheet.rb +1 -1
  7. data/lib/write_xlsx/format.rb +10 -3
  8. data/lib/write_xlsx/package/comments.rb +171 -27
  9. data/lib/write_xlsx/package/packager.rb +8 -17
  10. data/lib/write_xlsx/package/shared_strings.rb +36 -15
  11. data/lib/write_xlsx/package/styles.rb +2 -12
  12. data/lib/write_xlsx/package/vml.rb +14 -22
  13. data/lib/write_xlsx/utility.rb +53 -4
  14. data/lib/write_xlsx/workbook.rb +21 -37
  15. data/lib/write_xlsx/worksheet.rb +533 -765
  16. data/test/helper.rb +10 -3
  17. data/test/package/comments/test_write_text_t.rb +1 -1
  18. data/test/package/shared_strings/test_shared_strings01.rb +3 -3
  19. data/test/package/shared_strings/test_shared_strings02.rb +3 -3
  20. data/test/package/shared_strings/test_write_sst.rb +3 -2
  21. data/test/package/vml/test_write_anchor.rb +1 -1
  22. data/test/package/vml/test_write_auto_fill.rb +1 -1
  23. data/test/package/vml/test_write_column.rb +1 -1
  24. data/test/package/vml/test_write_div.rb +1 -1
  25. data/test/package/vml/test_write_fill.rb +1 -1
  26. data/test/package/vml/test_write_idmap.rb +1 -1
  27. data/test/package/vml/test_write_move_with_cells.rb +1 -1
  28. data/test/package/vml/test_write_path.rb +2 -2
  29. data/test/package/vml/test_write_row.rb +1 -1
  30. data/test/package/vml/test_write_shadow.rb +1 -1
  31. data/test/package/vml/test_write_shapelayout.rb +1 -1
  32. data/test/package/vml/test_write_shapetype.rb +1 -1
  33. data/test/package/vml/test_write_size_with_cells.rb +1 -1
  34. data/test/package/vml/test_write_stroke.rb +1 -1
  35. data/test/package/vml/test_write_textbox.rb +1 -1
  36. data/test/perl_output/formats.xlsx +0 -0
  37. data/test/perl_output/indent.xlsx +0 -0
  38. data/test/perl_output/merge4.xlsx +0 -0
  39. data/test/perl_output/merge5.xlsx +0 -0
  40. data/test/test_example_match.rb +482 -0
  41. data/test/worksheet/test_repeat_formula.rb +5 -5
  42. data/test/worksheet/test_write_cell.rb +10 -5
  43. data/test/worksheet/test_write_legacy_drawing.rb +1 -1
  44. data/write_xlsx.gemspec +5 -5
  45. metadata +15 -15
  46. data/test/package/comments/test_comments01.rb +0 -36
  47. data/test/package/vml/test_vml_01.rb +0 -42
@@ -1,35 +1,195 @@
1
1
  # -*- coding: utf-8 -*-
2
+ require 'write_xlsx/format'
2
3
  require 'write_xlsx/package/xml_writer_simple'
3
4
  require 'write_xlsx/utility'
4
5
 
5
6
  module Writexlsx
6
7
  module Package
8
+
9
+ class Comment
10
+
11
+ include Writexlsx::Utility
12
+
13
+ DEFAULT_COLOR = 81 # what color ?
14
+ DEFAULT_WIDTH = 128
15
+ DEFAULT_HEIGHT = 74
16
+
17
+ attr_reader :row, :col, :string, :color, :vertices
18
+ attr_accessor :author, :visible
19
+
20
+ def initialize(workbook, worksheet, row, col, string, options = {})
21
+ options ||= {}
22
+ @workbook = workbook
23
+ @worksheet = worksheet
24
+ @row = row
25
+ @col = col
26
+ @string = string[0, STR_MAX]
27
+ @author = options[:author]
28
+ @color = backgrount_color(options[:color] || DEFAULT_COLOR)
29
+ @start_cell = options[:start_cell]
30
+ @start_row, @start_col = if @start_cell
31
+ substitute_cellref(@start_cell)
32
+ else
33
+ [ options[:start_row], options[:start_col] ]
34
+ end
35
+ @start_row ||= default_start_row(row)
36
+ @start_col ||= default_start_col(col)
37
+ @visible = options[:visible]
38
+ @x_offset = options[:x_offset] || default_x_offset(col)
39
+ @y_offset = options[:y_offset] || default_y_offset(row)
40
+ @x_scale = options[:x_scale] || 1
41
+ @y_scale = options[:y_scale] || 1
42
+ @width = (0.5 + (options[:width] || DEFAULT_WIDTH) * @x_scale).to_i
43
+ @height = (0.5 + (options[:height] || DEFAULT_HEIGHT) * @y_scale).to_i
44
+ @vertices = @worksheet.position_object_pixels(
45
+ @start_col, @start_row, @x_offset, @y_offset,
46
+ @width, @height
47
+ ) << [@width, @height]
48
+ end
49
+
50
+ def backgrount_color(color)
51
+ color_id = Format.get_color(color)
52
+
53
+ if color_id == 0
54
+ @color = '#ffffe1'
55
+ else
56
+ rgb = @workbook.palette[color_id - 8]
57
+ @color = "##{rgb_color(rgb)} [#{color_id}]\n"
58
+ end
59
+ end
60
+
61
+ # Minor modification to allow comparison testing. Change RGB colors
62
+ # from long format, ffcc00 to short format fc0 used by VML.
63
+ def rgb_color(rgb)
64
+ result = sprintf("%02x%02x%02x", *rgb)
65
+ if result =~ /^([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3$/
66
+ result = "#{$1}#{$2}#{$3}"
67
+ end
68
+ result
69
+ end
70
+
71
+ def default_start_row(row)
72
+ case row
73
+ when 0
74
+ 0
75
+ when ROW_MAX - 3
76
+ ROW_MAX - 7
77
+ when ROW_MAX - 2
78
+ ROW_MAX - 6
79
+ when ROW_MAX - 1
80
+ ROW_MAX - 5
81
+ else
82
+ row - 1
83
+ end
84
+ end
85
+
86
+ def default_start_col(col)
87
+ case col
88
+ when COL_MAX - 3
89
+ COL_MAX - 6
90
+ when COL_MAX - 2
91
+ COL_MAX - 5
92
+ when COL_MAX - 1
93
+ COL_MAX - 4
94
+ else
95
+ col + 1
96
+ end
97
+ end
98
+
99
+ def default_x_offset(col)
100
+ case col
101
+ when COL_MAX - 3, COL_MAX - 2, COL_MAX - 1
102
+ 49
103
+ else
104
+ 15
105
+ end
106
+ end
107
+
108
+ def default_y_offset(row)
109
+ case row
110
+ when 0
111
+ 2
112
+ when ROW_MAX - 3, ROW_MAX - 2
113
+ 16
114
+ when ROW_MAX - 1
115
+ 14
116
+ else
117
+ 10
118
+ end
119
+ end
120
+ end
121
+
7
122
  class Comments
8
123
 
9
124
  include Writexlsx::Utility
10
125
 
11
- def initialize
126
+ def initialize(worksheet)
127
+ @worksheet = worksheet
12
128
  @writer = Package::XMLWriterSimple.new
13
129
  @author_ids = {}
130
+ @comments = {}
131
+ end
132
+
133
+ def add(comment)
134
+ if @comments[comment.row]
135
+ @comments[comment.row][comment.col] = comment
136
+ else
137
+ @comments[comment.row] = {}
138
+ @comments[comment.row][comment.col] = comment
139
+ end
140
+ end
141
+
142
+ def empty?
143
+ @comments.empty?
144
+ end
145
+
146
+ def size
147
+ sorted_comments.size
14
148
  end
15
149
 
16
150
  def set_xml_writer(filename)
17
151
  @writer.set_xml_writer(filename)
18
152
  end
19
153
 
20
- def assemble_xml_file(comments_data)
154
+ def assemble_xml_file
21
155
  write_xml_declaration
22
156
  write_comments
23
- write_authors(comments_data)
24
- write_comment_list(comments_data)
157
+ write_authors(sorted_comments)
158
+ write_comment_list(sorted_comments)
25
159
 
26
160
  @writer.end_tag('comments')
27
161
  @writer.crlf
28
162
  @writer.close
29
163
  end
30
164
 
165
+ def sorted_comments
166
+ @sorted_comments if @sorted_comments
167
+
168
+ @sorted_comments = []
169
+ # We sort the comments by row and column but that isn't strictly required.
170
+ @comments.keys.sort.each do |row|
171
+ @comments[row].keys.sort.each do |col|
172
+ # Set comment visibility if required and not already user defined.
173
+ @comments[row][col].visible ||= 1 if comments_visible?
174
+
175
+ # Set comment author if not already user defined.
176
+ @comments[row][col].author ||= @comments_author
177
+ @sorted_comments << @comments[row][col]
178
+ end
179
+ end
180
+ @sorted_comments
181
+ end
182
+
183
+ def has_comment_in_row?(row)
184
+ !!@comments[row]
185
+ end
186
+
31
187
  private
32
188
 
189
+ def comments_visible?
190
+ @worksheet.comments_visible?
191
+ end
192
+
33
193
  def write_xml_declaration
34
194
  @writer.xml_decl
35
195
  end
@@ -53,8 +213,7 @@ module Writexlsx
53
213
 
54
214
  @writer.start_tag('authors')
55
215
  comment_data.each do |comment|
56
- author = comment[3] || ''
57
-
216
+ author = comment.author || ''
58
217
  if author && !@author_ids[author]
59
218
  # Store the author id.
60
219
  @author_ids[author] = author_count
@@ -80,37 +239,22 @@ module Writexlsx
80
239
  #
81
240
  def write_comment_list(comment_data)
82
241
  @writer.start_tag('commentList')
83
-
84
- comment_data.each do |comment|
85
- row = comment[0]
86
- col = comment[1]
87
- text = comment[2]
88
- author = comment[3]
89
-
90
- # Look up the author id.
91
- author_id = nil
92
- author_id = @author_ids[author] if author
93
-
94
- # Write the comment element.
95
- write_comment(row, col, text, author_id)
96
- end
97
-
242
+ comment_data.each { |comment| write_comment(comment) }
98
243
  @writer.end_tag( 'commentList' )
99
244
  end
100
245
 
101
246
  #
102
247
  # Write the <comment> element.
103
248
  #
104
- def write_comment(row, col, text, author_id)
105
- ref = xl_rowcol_to_cell( row, col )
106
- author_id ||= 0
107
-
249
+ def write_comment(comment)
250
+ ref = xl_rowcol_to_cell( comment.row, comment.col )
108
251
  attributes = ['ref', ref]
109
252
 
110
- (attributes << 'authorId' << author_id ) if author_id
253
+ author_id = (@author_ids[comment.author] if comment.author) || 0
254
+ attributes << 'authorId' << author_id
111
255
 
112
256
  @writer.start_tag('comment', attributes)
113
- write_text(text)
257
+ write_text(comment.string)
114
258
  @writer.end_tag('comment')
115
259
  end
116
260
 
@@ -157,14 +157,10 @@ module Writexlsx
157
157
  next unless worksheet.has_comments?
158
158
  FileUtils.mkdir_p("#{@package_dir}/xl/drawings")
159
159
 
160
- vml = Package::VML.new
160
+ vml = Package::Vml.new
161
161
  vml.set_xml_writer("#{@package_dir}/xl/drawings/vmlDrawing#{index}.vml")
162
162
  index += 1
163
- vml.assemble_xml_file(
164
- worksheet.vml_data_id,
165
- worksheet.vml_shape_id,
166
- worksheet.comments_array
167
- )
163
+ vml.assemble_xml_file(worksheet)
168
164
  end
169
165
  end
170
166
 
@@ -177,12 +173,11 @@ module Writexlsx
177
173
  next unless worksheet.has_comments?
178
174
 
179
175
  FileUtils.mkdir_p("#{@package_dir}/xl/drawings")
180
- comment = Package::Comments.new
181
176
 
182
- comment.set_xml_writer("#{@package_dir}/xl/comments#{index}.xml")
177
+ worksheet.comments_xml_writer = "#{@package_dir}/xl/comments#{index}.xml"
183
178
  index += 1
184
179
 
185
- comment.assemble_xml_file(worksheet.comments_array)
180
+ worksheet.comments_assemble_xml_file
186
181
  end
187
182
  end
188
183
 
@@ -190,15 +185,11 @@ module Writexlsx
190
185
  # Write the sharedStrings.xml file.
191
186
  #
192
187
  def write_shared_strings_file
193
- sst = Package::SharedStrings.new
188
+ sst = @workbook.shared_strings
194
189
 
195
190
  FileUtils.mkdir_p("#{@package_dir}/xl")
196
191
 
197
- return unless @workbook.str_total > 0
198
-
199
- sst.string_count = @workbook.str_total
200
- sst.unique_count = @workbook.str_unique
201
- sst.add_strings(@workbook.str_array)
192
+ return if @workbook.shared_strings_empty?
202
193
 
203
194
  sst.set_xml_writer("#{@package_dir}/xl/sharedStrings.xml")
204
195
  sst.assemble_xml_file
@@ -288,7 +279,7 @@ module Writexlsx
288
279
  (1 .. @num_comment_files).each { |i| content.add_comment_name("comments#{i}") }
289
280
 
290
281
  # Add the sharedString rel if there is string data in the workbook.
291
- content.add_shared_strings if @workbook.str_total > 0
282
+ content.add_shared_strings unless @workbook.shared_strings_empty?
292
283
 
293
284
  content.set_xml_writer("#{@package_dir}/[Content_Types].xml")
294
285
  content.assemble_xml_file
@@ -380,7 +371,7 @@ module Writexlsx
380
371
  rels.add_document_relationship('/styles', 'styles.xml')
381
372
 
382
373
  # Add the sharedString rel if there is string data in the workbook.
383
- rels.add_document_relationship('/sharedStrings', 'sharedStrings.xml') if @workbook.str_total != 0
374
+ rels.add_document_relationship('/sharedStrings', 'sharedStrings.xml') unless @workbook.shared_strings_empty?
384
375
  rels.set_xml_writer("#{@package_dir}/xl/_rels/workbook.xml.rels")
385
376
  rels.assemble_xml_file
386
377
  end
@@ -8,13 +8,33 @@ module Writexlsx
8
8
 
9
9
  include Writexlsx::Utility
10
10
 
11
- attr_writer :string_count, :unique_count
12
-
13
11
  def initialize
14
- @writer = Package::XMLWriterSimple.new
15
- @strings = []
16
- @string_count = 0
17
- @unique_count = 0
12
+ @writer = Package::XMLWriterSimple.new
13
+ @strings = [] # string table
14
+ @count = {} # => count
15
+ end
16
+
17
+ def index(string)
18
+ add(string)
19
+ @strings.index(string)
20
+ end
21
+
22
+ def add(string)
23
+ str = string.dup
24
+ if @count[str]
25
+ @count[str] += 1
26
+ else
27
+ @strings << str
28
+ @count[str] = 1
29
+ end
30
+ end
31
+
32
+ def string(index)
33
+ @strings[index].dup
34
+ end
35
+
36
+ def empty?
37
+ @strings.empty?
18
38
  end
19
39
 
20
40
  def set_xml_writer(filename)
@@ -36,13 +56,6 @@ module Writexlsx
36
56
  @writer.close
37
57
  end
38
58
 
39
- #
40
- # Add the array ref of strings to be written.
41
- #
42
- def add_strings(strings)
43
- @strings = strings
44
- end
45
-
46
59
  private
47
60
 
48
61
  def write_xml_declaration
@@ -58,8 +71,8 @@ module Writexlsx
58
71
  attributes =
59
72
  [
60
73
  'xmlns', schema + '/spreadsheetml/2006/main',
61
- 'count', @string_count,
62
- 'uniqueCount', @unique_count
74
+ 'count', total_count,
75
+ 'uniqueCount', unique_count
63
76
  ]
64
77
 
65
78
  @writer.start_tag('sst', attributes)
@@ -91,6 +104,14 @@ module Writexlsx
91
104
 
92
105
  @writer.end_tag('si')
93
106
  end
107
+
108
+ def total_count
109
+ @count.values.inject(0) { |sum, count| sum += count }
110
+ end
111
+
112
+ def unique_count
113
+ @strings.size
114
+ end
94
115
  end
95
116
  end
96
117
  end
@@ -238,17 +238,7 @@ module Writexlsx
238
238
  # Write the underline font element.
239
239
  #
240
240
  def write_underline(underline)
241
- # Handle the underline variants.
242
- if underline == 2
243
- attributes = [val, 'double']
244
- elsif underline == 33
245
- attributes = [val, 'singleAccounting']
246
- elsif underline == 34
247
- attributes = [val, 'doubleAccounting']
248
- else
249
- attributes = [] # Default to single underline.
250
- end
251
-
241
+ attributes = underline_attributes(underline)
252
242
  @writer.empty_tag('u', attributes)
253
243
  end
254
244
 
@@ -523,7 +513,7 @@ module Writexlsx
523
513
  attributes << 'applyNumberFormat' << 1 if format.num_format_index > 0
524
514
 
525
515
  # Add applyFont attribute if XF format uses a font element.
526
- attributes << 'applyFont' << 1 if format.has_font != 0 && format.font_index > 0
516
+ attributes << 'applyFont' << 1 if format.font_index > 0
527
517
 
528
518
  # Add applyFill attribute if XF format uses a fill element.
529
519
  attributes << 'applyFill' << 1 if format.fill_index > 0
@@ -4,7 +4,7 @@ require 'write_xlsx/utility'
4
4
 
5
5
  module Writexlsx
6
6
  module Package
7
- class VML
7
+ class Vml
8
8
 
9
9
  include Writexlsx::Utility
10
10
 
@@ -16,19 +16,20 @@ module Writexlsx
16
16
  @writer.set_xml_writer(filename)
17
17
  end
18
18
 
19
- def assemble_xml_file(data_id, vml_shape_id, comments_data)
19
+ def assemble_xml_file(worksheet)
20
20
  return unless @writer
21
21
 
22
22
  write_xml_namespace
23
23
 
24
24
  # Write the o:shapelayout element.
25
- write_shapelayout(data_id)
25
+ write_shapelayout(worksheet.vml_data_id)
26
26
 
27
27
  # Write the v:shapetype element.
28
28
  write_shapetype
29
29
 
30
30
  z_index = 1
31
- comments_data.each do |comment|
31
+ vml_shape_id = worksheet.vml_shape_id
32
+ worksheet.comments_array.each do |comment|
32
33
  # Write the v:shape element.
33
34
  vml_shape_id += 1
34
35
  write_shape(vml_shape_id, z_index, comment)
@@ -167,19 +168,10 @@ module Writexlsx
167
168
  # Set the shape index.
168
169
  id = '_x0000_s' + id.to_s
169
170
 
170
- # Get the comment parameters
171
- row = comment[0]
172
- col = comment[1]
173
- string = comment[2]
174
- author = comment[3]
175
- visible = comment[4]
176
- fillcolor = comment[5]
177
- vertices = comment[6]
178
-
179
- left, top, width, height = pixels_to_points(vertices)
171
+ left, top, width, height = pixels_to_points(comment.vertices)
180
172
 
181
173
  # Set the visibility.
182
- visibility = 'visible' if visible != 0 && !visible.nil?
174
+ visibility = 'visible' if comment.visible != 0 && !comment.visible.nil?
183
175
 
184
176
  left_str = float_to_str(left)
185
177
  top_str = float_to_str(top)
@@ -207,7 +199,7 @@ module Writexlsx
207
199
  'id', id,
208
200
  'type', type,
209
201
  'style', style,
210
- 'fillcolor', fillcolor,
202
+ 'fillcolor', comment.color,
211
203
  'o:insetmode', insetmode
212
204
  ]
213
205
 
@@ -226,7 +218,7 @@ module Writexlsx
226
218
  write_textbox
227
219
 
228
220
  # Write the x:ClientData element.
229
- write_client_data(row, col, visible, vertices)
221
+ write_client_data(comment)
230
222
 
231
223
  @writer.end_tag('v:shape')
232
224
  end
@@ -297,7 +289,7 @@ module Writexlsx
297
289
  #
298
290
  # Write the <x:ClientData> element.
299
291
  #
300
- def write_client_data(row, col, visible, vertices)
292
+ def write_client_data(comment)
301
293
  object_type = 'Note'
302
294
 
303
295
  attributes = ['ObjectType', object_type]
@@ -311,19 +303,19 @@ module Writexlsx
311
303
  write_size_with_cells
312
304
 
313
305
  # Write the x:Anchor element.
314
- write_anchor(vertices)
306
+ write_anchor(comment.vertices)
315
307
 
316
308
  # Write the x:AutoFill element.
317
309
  write_auto_fill
318
310
 
319
311
  # Write the x:Row element.
320
- write_row(row)
312
+ write_row(comment.row)
321
313
 
322
314
  # Write the x:Column element.
323
- write_column(col)
315
+ write_column(comment.col)
324
316
 
325
317
  # Write the x:Visible element.
326
- write_visible if visible != 0 && !visible.nil?
318
+ write_visible if comment.visible != 0 && !comment.visible.nil?
327
319
 
328
320
  @writer.end_tag('x:ClientData')
329
321
  end
@@ -1,8 +1,13 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  module Writexlsx
3
3
  module Utility
4
+ ROW_MAX = 1048576 # :nodoc:
5
+ COL_MAX = 16384 # :nodoc:
6
+ STR_MAX = 32767 # :nodoc:
7
+ SHEETNAME_MAX = 31 # :nodoc:
8
+
4
9
  #
5
- # xl_rowcol_to_cell($row, $col, $row_absolute, $col_absolute)
10
+ # xl_rowcol_to_cell($row, col, row_absolute, col_absolute)
6
11
  #
7
12
  def xl_rowcol_to_cell(row, col, row_absolute = false, col_absolute = false)
8
13
  row += 1 # Change from 0-indexed to 1 indexed.
@@ -13,9 +18,9 @@ module Writexlsx
13
18
  end
14
19
 
15
20
  #
16
- # Returns: ($row, $col, $row_absolute, $col_absolute)
21
+ # Returns: [row, col, row_absolute, col_absolute]
17
22
  #
18
- # The $row_absolute and $col_absolute parameters aren't documented because they
23
+ # The row_absolute and col_absolute parameters aren't documented because they
19
24
  # mainly used internally and aren't very useful to the user.
20
25
  #
21
26
  def xl_cell_to_rowcol(cell)
@@ -93,7 +98,7 @@ module Writexlsx
93
98
  def xml_str
94
99
  @writer.string
95
100
  end
96
-
101
+
97
102
  def self.delete_files(path)
98
103
  if FileTest.file?(path)
99
104
  File.delete(path)
@@ -109,5 +114,49 @@ module Writexlsx
109
114
  def put_deprecate_message(method)
110
115
  $stderr.puts("Warning: calling deprecated method #{method}. This method will be removed in a future release.")
111
116
  end
117
+
118
+ #
119
+ # Substitute an Excel cell reference in A1 notation for zero based row and
120
+ # column values in an argument list.
121
+ #
122
+ # Ex: ("A4", "Hello") is converted to (3, 0, "Hello").
123
+ #
124
+ def substitute_cellref(cell, *args) #:nodoc:
125
+ return [*args] if cell.respond_to?(:coerce) # Numeric
126
+
127
+ cell.upcase!
128
+
129
+ case cell
130
+ # Convert a column range: 'A:A' or 'B:G'.
131
+ # A range such as A:A is equivalent to A1:65536, so add rows as required
132
+ when /\$?([A-Z]{1,3}):\$?([A-Z]{1,3})/
133
+ row1, col1 = xl_cell_to_rowcol($1 + '1')
134
+ row2, col2 = xl_cell_to_rowcol($2 + ROW_MAX.to_s)
135
+ return [row1, col1, row2, col2, *args]
136
+ # Convert a cell range: 'A1:B7'
137
+ when /\$?([A-Z]{1,3}\$?\d+):\$?([A-Z]{1,3}\$?\d+)/
138
+ row1, col1 = xl_cell_to_rowcol($1)
139
+ row2, col2 = xl_cell_to_rowcol($2)
140
+ return [row1, col1, row2, col2, *args]
141
+ # Convert a cell reference: 'A1' or 'AD2000'
142
+ when /\$?([A-Z]{1,3}\$?\d+)/
143
+ row1, col1 = xl_cell_to_rowcol($1)
144
+ return [row1, col1, *args]
145
+ else
146
+ raise("Unknown cell reference #{cell}")
147
+ end
148
+ end
149
+
150
+ def underline_attributes(underline)
151
+ if underline == 2
152
+ ['val', 'double']
153
+ elsif underline == 33
154
+ ['val', 'singleAccounting']
155
+ elsif underline == 34
156
+ ['val', 'doubleAccounting']
157
+ else
158
+ [] # Default to single underline.
159
+ end
160
+ end
112
161
  end
113
162
  end