write_xlsx 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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