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
@@ -102,6 +102,156 @@ module Writexlsx
102
102
  class Worksheet
103
103
  include Writexlsx::Utility
104
104
 
105
+ class CellData # :nodoc:
106
+ include Writexlsx::Utility
107
+
108
+ attr_reader :row, :col, :token, :xf
109
+ attr_reader :result, :range, :link_type, :url, :tip
110
+
111
+ #
112
+ # Write the <cell> element. This is the innermost loop so efficiency is
113
+ # important where possible.
114
+ #
115
+ def write_cell(worksheet) #:nodoc:
116
+ xf_index = 0
117
+ xf_index = xf.get_xf_index if xf.respond_to?(:get_xf_index)
118
+
119
+ attributes = ['r', xl_rowcol_to_cell(row, col)]
120
+
121
+ # Add the cell format index.
122
+ if xf_index != 0
123
+ attributes << 's' << xf_index
124
+ elsif worksheet.set_rows[row] && worksheet.set_rows[row][1]
125
+ row_xf = worksheet.set_rows[row][1]
126
+ attributes << 's' << row_xf.get_xf_index
127
+ elsif worksheet.col_formats[col]
128
+ col_xf = worksheet.col_formats[col]
129
+ attributes << 's' << col_xf.get_xf_index
130
+ end
131
+ attributes
132
+ end
133
+ end
134
+
135
+ class NumberCellData < CellData # :nodoc:
136
+ def initialize(row, col, num, xf)
137
+ @row, @col, @token, @xf = row, col, num, xf
138
+ end
139
+
140
+ def data
141
+ @token
142
+ end
143
+
144
+ def write_cell(worksheet)
145
+ attributes = super(worksheet)
146
+ worksheet.writer.start_tag('c', attributes)
147
+ worksheet.write_cell_value(token)
148
+ worksheet.writer.end_tag('c')
149
+ end
150
+ end
151
+
152
+ class StringCellData < CellData # :nodoc:
153
+ def initialize(row, col, index, xf)
154
+ @row, @col, @token, @xf = row, col, index, xf
155
+ end
156
+
157
+ def data
158
+ { :sst_id => token }
159
+ end
160
+
161
+ def write_cell(worksheet)
162
+ attributes = super(worksheet)
163
+ attributes << 't' << 's'
164
+ worksheet.writer.start_tag('c', attributes)
165
+ worksheet.write_cell_value(token)
166
+ worksheet.writer.end_tag('c')
167
+ end
168
+ end
169
+
170
+ class FormulaCellData < CellData # :nodoc:
171
+ def initialize(row, col, formula, xf, result)
172
+ @row, @col, @token, @xf, @result = row, col, formula, xf, result
173
+ end
174
+
175
+ def data
176
+ @result || 0
177
+ end
178
+
179
+ def write_cell(worksheet)
180
+ attributes = super(worksheet)
181
+ worksheet.writer.start_tag('c', attributes)
182
+ worksheet.write_cell_formula(token)
183
+ worksheet.write_cell_value(result || 0)
184
+ worksheet.writer.end_tag('c')
185
+ end
186
+ end
187
+
188
+ class FormulaArrayCellData < CellData # :nodoc:
189
+ def initialize(row, col, formula, xf, range, result)
190
+ @row, @col, @token, @xf, @range, @result = row, col, formula, xf, range, result
191
+ end
192
+
193
+ def data
194
+ @result || 0
195
+ end
196
+
197
+ def write_cell(worksheet)
198
+ attributes = super(worksheet)
199
+ worksheet.writer.start_tag('c', attributes)
200
+ worksheet.write_cell_array_formula(token, range)
201
+ worksheet.write_cell_value(result)
202
+ worksheet.writer.end_tag('c')
203
+ end
204
+ end
205
+
206
+ class HyperlinkCellData < CellData # :nodoc:
207
+ def initialize(row, col, index, xf, link_type, url, str, tip)
208
+ @row, @col, @token, @xf, @link_type, @url, @str, @tip =
209
+ row, col, index, xf, link_type, url, str, tip
210
+ end
211
+
212
+ def data
213
+ { :sst_id => token }
214
+ end
215
+
216
+ def write_cell(worksheet)
217
+ attributes = super(worksheet)
218
+ attributes << 't' << 's'
219
+ worksheet.writer.start_tag('c', attributes)
220
+ worksheet.write_cell_value(token)
221
+ worksheet.writer.end_tag('c')
222
+
223
+ if link_type == 1
224
+ # External link with rel file relationship.
225
+ worksheet.hlink_count += 1
226
+ worksheet.hlink_refs <<
227
+ [
228
+ link_type, row, col,
229
+ worksheet.hlink_count, @str, @tip
230
+ ]
231
+
232
+ worksheet.external_hyper_links << [ '/hyperlink', @url, 'External' ]
233
+ elsif link_type
234
+ # External link with rel file relationship.
235
+ worksheet.hlink_refs << [link_type, row, col, @url, @str, @tip ]
236
+ end
237
+ end
238
+ end
239
+
240
+ class BlankCellData < CellData # :nodoc:
241
+ def initialize(row, col, index, xf)
242
+ @row, @col, @xf = row, col, xf
243
+ end
244
+
245
+ def data
246
+ ''
247
+ end
248
+
249
+ def write_cell(worksheet)
250
+ attributes = super(worksheet)
251
+ worksheet.writer.empty_tag('c', attributes)
252
+ end
253
+ end
254
+
105
255
  class PrintStyle # :nodoc:
106
256
  attr_accessor :margin_left, :margin_right, :margin_top, :margin_bottom # :nodoc:
107
257
  attr_accessor :margin_header, :margin_footer # :nodoc:
@@ -109,6 +259,7 @@ module Writexlsx
109
259
  attr_accessor :hbreaks, :vbreaks, :scale # :nodoc:
110
260
  attr_accessor :fit_page, :fit_width, :fit_height, :page_setup_changed # :nodoc:
111
261
  attr_accessor :across # :nodoc:
262
+ attr_writer :orientation
112
263
 
113
264
  def initialize # :nodoc:
114
265
  @margin_left = 0.7
@@ -128,6 +279,7 @@ module Writexlsx
128
279
  @fit_height = nil
129
280
  @page_setup_changed = false
130
281
  @across = false
282
+ @orientation = true
131
283
  end
132
284
 
133
285
  def attributes # :nodoc:
@@ -140,18 +292,20 @@ module Writexlsx
140
292
  'footer', @margin_footer
141
293
  ]
142
294
  end
143
- end
144
295
 
145
- RowMax = 1048576 # :nodoc:
146
- ColMax = 16384 # :nodoc:
147
- StrMax = 32767 # :nodoc:
148
- Buffer = 4096 # :nodoc:
296
+ def orientation?
297
+ !!@orientation
298
+ end
299
+ end
149
300
 
150
- attr_reader :index
151
- attr_reader :charts, :images, :drawing
152
- attr_reader :external_hyper_links, :external_drawing_links, :external_comment_links, :drawing_links
153
- attr_reader :vml_data_id, :vml_shape_id, :comments_array
154
- attr_reader :autofilter_area, :hidden
301
+ attr_reader :index # :nodoc:
302
+ attr_reader :charts, :images, :drawing # :nodoc:
303
+ attr_reader :external_hyper_links, :external_drawing_links # :nodoc:
304
+ attr_reader :external_comment_links, :drawing_links # :nodoc:
305
+ attr_reader :vml_data_id # :nodoc:
306
+ attr_reader :autofilter_area # :nodoc:
307
+ attr_reader :writer, :set_rows, :col_formats # :nodoc:
308
+ attr_accessor :vml_shape_id, :hlink_count, :hlink_refs # :nodoc:
155
309
 
156
310
  def initialize(workbook, index, name) #:nodoc:
157
311
  @writer = Package::XMLWriterSimple.new
@@ -160,18 +314,15 @@ module Writexlsx
160
314
  @index = index
161
315
  @name = name
162
316
  @colinfo = []
163
- @table = []
317
+ @cell_data_table = {}
164
318
  @filter_on = false
165
319
 
166
320
  @print_style = PrintStyle.new
167
-
321
+
168
322
  @print_area = ''
169
323
 
170
324
  @screen_gridlines = true
171
325
  @show_zeros = true
172
- @xls_rowmax = RowMax
173
- @xls_colmax = ColMax
174
- @xls_strmax = StrMax
175
326
  @dim_rowmin = nil
176
327
  @dim_rowmax = nil
177
328
  @dim_colmin = nil
@@ -181,8 +332,6 @@ module Writexlsx
181
332
 
182
333
  @tab_color = 0
183
334
 
184
- @orientation = true
185
-
186
335
  @set_cols = {}
187
336
  @set_rows = {}
188
337
  @zoom = 100
@@ -214,8 +363,7 @@ module Writexlsx
214
363
 
215
364
  @merge = []
216
365
 
217
- @has_comments = false
218
- @comments = {}
366
+ @comments = Package::Comments.new(self)
219
367
 
220
368
  @validations = []
221
369
 
@@ -476,7 +624,7 @@ module Writexlsx
476
624
  # and last_col are optional.
477
625
  #
478
626
  # If set_column() is applied to a single column the value of first_col
479
- # and last_col should be the same. In the case where $last_col is zero
627
+ # and last_col should be the same. In the case where last_col is zero
480
628
  # it is set to the same value as first_col.
481
629
  #
482
630
  # It is also possible, and generally clearer, to specify a column range
@@ -614,7 +762,7 @@ module Writexlsx
614
762
  #
615
763
  # This method can be used to specify which cell or cells are selected
616
764
  # in a worksheet. The most common requirement is to select a single cell,
617
- # in which case $last_row and $last_col can be omitted. The active cell
765
+ # in which case last_row and last_col can be omitted. The active cell
618
766
  # within a selected range is determined by the order in which first and
619
767
  # last are specified. It is also possible to specify a cell or a range
620
768
  # using A1 notation. See the note about "Cell notation".
@@ -666,7 +814,7 @@ module Writexlsx
666
814
  # that the splitter bars are not visible. This is the same as the
667
815
  # Window->Freeze Panes menu command in Excel
668
816
  #
669
- # The parameters $row and $col are used to specify the location of
817
+ # The parameters row and col are used to specify the location of
670
818
  # the split. It should be noted that the split is specified at the
671
819
  # top or left of a cell and that the method uses zero based indexing.
672
820
  # Therefore to freeze the first row of a worksheet it is necessary
@@ -674,7 +822,7 @@ module Writexlsx
674
822
  # This might lead you to think that you are using a 1 based index
675
823
  # but this is not the case.
676
824
  #
677
- # You can set one of the $row and $col parameters as zero if you
825
+ # You can set one of the row and col parameters as zero if you
678
826
  # do not want either a vertical or horizontal split.
679
827
  #
680
828
  # Examples:
@@ -686,16 +834,16 @@ module Writexlsx
686
834
  # worksheet.freeze_panes(1, 2) # Freeze first row and first 2 columns
687
835
  # worksheet.freeze_panes('C2') # Same using A1 notation
688
836
  #
689
- # The parameters $top_row and $left_col are optional. They are used
837
+ # The parameters top_row and left_col are optional. They are used
690
838
  # to specify the top-most or left-most visible row or column in the
691
839
  # scrolling region of the panes. For example to freeze the first row
692
840
  # and to have the scrolling region begin at row twenty:
693
841
  #
694
842
  # worksheet.freeze_panes(1, 0, 20, 0)
695
843
  #
696
- # You cannot use A1 notation for the $top_row and $left_col parameters.
844
+ # You cannot use A1 notation for the top_row and left_col parameters.
697
845
  #
698
- # See also the panes.pl program in the examples directory of the
846
+ # See also the panes.rb program in the examples directory of the
699
847
  # distribution.
700
848
  #
701
849
  def freeze_panes(*args)
@@ -722,7 +870,7 @@ module Writexlsx
722
870
  # file format and isn't sufficient to describe all cases of split panes.
723
871
  # It should probably be something like:
724
872
  #
725
- # split_panes($y, $x, $top_row, $left_col, $offset_row, $offset_col)
873
+ # split_panes(y, x, top_row, left_col, offset_row, offset_col)
726
874
  #
727
875
  # I'll look at changing this if it becomes an issue.
728
876
  #++
@@ -731,15 +879,15 @@ module Writexlsx
731
879
  # method in that the splits between the panes will be visible to the user
732
880
  # and each pane will have its own scroll bars.
733
881
  #
734
- # The parameters $y and $x are used to specify the vertical and horizontal
735
- # position of the split. The units for $y and $x are the same as those
882
+ # The parameters y and x are used to specify the vertical and horizontal
883
+ # position of the split. The units for y and x are the same as those
736
884
  # used by Excel to specify row height and column width. However, the
737
885
  # vertical and horizontal units are different from each other. Therefore
738
- # you must specify the $y and $x parameters in terms of the row heights
886
+ # you must specify the y and x parameters in terms of the row heights
739
887
  # and column widths that you have set or the default values which are 15
740
888
  # for a row and 8.43 for a column.
741
889
  #
742
- # You can set one of the $y and $x parameters as zero if you do not want
890
+ # You can set one of the y and x parameters as zero if you do not want
743
891
  # either a vertical or horizontal split. The parameters top_row and left_col
744
892
  # are optional. They are used to specify the top-most or left-most visible
745
893
  # row or column in the bottom-right pane.
@@ -766,7 +914,7 @@ module Writexlsx
766
914
  # need to call this method.
767
915
  #
768
916
  def set_portrait
769
- @orientation = true
917
+ @print_style.orientation = true
770
918
  @print_style.page_setup_changed = true
771
919
  end
772
920
 
@@ -774,7 +922,7 @@ module Writexlsx
774
922
  # Set the page orientation as landscape.
775
923
  #
776
924
  def set_landscape
777
- @orientation = false
925
+ @print_style.orientation = false
778
926
  @print_style.page_setup_changed = true
779
927
  end
780
928
 
@@ -796,7 +944,7 @@ module Writexlsx
796
944
  # worksheet1.set_tab_color('red')
797
945
  # worksheet2.set_tab_color(0x0C)
798
946
  #
799
- # See the tab_colors.pl program in the examples directory of the distro.
947
+ # See the tab_colors.rb program in the examples directory of the distro.
800
948
  #
801
949
  def set_tab_color(color)
802
950
  @tab_color = Colors.new.get_color(color)
@@ -1285,10 +1433,6 @@ module Writexlsx
1285
1433
  @print_style.repeat_cols
1286
1434
  end
1287
1435
 
1288
- def print_area # :nodoc:
1289
- @print_area.dup
1290
- end
1291
-
1292
1436
  #
1293
1437
  # :call-seq:
1294
1438
  # print_area(first_row, first_col, last_row, last_col)
@@ -1297,22 +1441,23 @@ module Writexlsx
1297
1441
  # be printed. All four parameters must be specified. You can also use
1298
1442
  # A1 notation, see the note about "Cell notation".
1299
1443
  #
1300
- # $worksheet1->print_area( 'A1:H20' ); # Cells A1 to H20
1301
- # $worksheet2->print_area( 0, 0, 19, 7 ); # The same
1302
- # $worksheet2->print_area( 'A:H' ); # Columns A to H if rows have data
1444
+ # worksheet1.print_area( 'A1:H20' ); # Cells A1 to H20
1445
+ # worksheet2.print_area( 0, 0, 19, 7 ); # The same
1446
+ # worksheet2.print_area( 'A:H' ); # Columns A to H if rows have data
1303
1447
  #
1304
1448
  def print_area(*args)
1305
- return @print_area if args.empty?
1449
+ return @print_area.dup if args.empty?
1306
1450
  row1, col1, row2, col2 = row_col_notation(args)
1307
1451
  return if [row1, col1, row2, col2].include?(nil)
1308
1452
 
1309
1453
  # Ignore max print area since this is the same as no print area for Excel.
1310
- if row1 == 0 && col1 == 0 && row2 == @xls_rowmax - 1 && col2 == @xls_colmax - 1
1454
+ if row1 == 0 && col1 == 0 && row2 == ROW_MAX - 1 && col2 == COL_MAX - 1
1311
1455
  return
1312
1456
  end
1313
1457
 
1314
1458
  # Build up the print area range "=Sheet2!R1C1:R2C1"
1315
1459
  @print_area = convert_name_area(row1, col1, row2, col2)
1460
+ @print_area.dup
1316
1461
  end
1317
1462
 
1318
1463
  #
@@ -1321,7 +1466,7 @@ module Writexlsx
1321
1466
  def set_zoom(scale = 100)
1322
1467
  # Confine the scale to Excel's range
1323
1468
  if scale < 10 or scale > 400
1324
- # carp "Zoom factor $scale outside range: 10 <= zoom <= 400"
1469
+ # carp "Zoom factor scale outside range: 10 <= zoom <= 400"
1325
1470
  scale = 100
1326
1471
  end
1327
1472
 
@@ -1489,7 +1634,7 @@ module Writexlsx
1489
1634
  # format.set_color('red')
1490
1635
  # format.set_align('center')
1491
1636
  #
1492
- # worksheet.write(4, 0, 'Hello', $format) # Formatted string
1637
+ # worksheet.write(4, 0, 'Hello', format) # Formatted string
1493
1638
  #
1494
1639
  # The write() method will ignore empty strings or nil tokens unless a format
1495
1640
  # is also supplied. As such you needn't worry about special handling for
@@ -1864,14 +2009,14 @@ module Writexlsx
1864
2009
  # This option is used to change the x offset, in pixels, of a comment
1865
2010
  # within a cell:
1866
2011
  #
1867
- # worksheet.write_comment('C3', $comment, :x_offset => 30)
2012
+ # worksheet.write_comment('C3', comment, :x_offset => 30)
1868
2013
  #
1869
2014
  # ===Option: y_offset
1870
2015
  #
1871
2016
  # This option is used to change the y offset, in pixels, of a comment
1872
2017
  # within a cell:
1873
2018
  #
1874
- # worksheet.write_comment('C3', $comment, :x_offset => 30)
2019
+ # worksheet.write_comment('C3', comment, :x_offset => 30)
1875
2020
  #
1876
2021
  # You can apply as many of these options as you require.
1877
2022
  #
@@ -1882,7 +2027,7 @@ module Writexlsx
1882
2027
  # mouse over them.
1883
2028
  #
1884
2029
  # Note about row height and comments. If you specify the height of a
1885
- # row that contains a comment then Excel::Writer::XLSX will adjust the
2030
+ # row that contains a comment then WriteXLSX will adjust the
1886
2031
  # height of the comment to maintain the default or user specified
1887
2032
  # dimensions. However, the height of a row can also be adjusted
1888
2033
  # automatically by Excel if the text wrap property is set or large
@@ -1900,14 +2045,8 @@ module Writexlsx
1900
2045
  check_dimensions(row, col)
1901
2046
  store_row_col_max_min_values(row, col)
1902
2047
 
1903
- @has_comments = true
1904
2048
  # Process the properties of the cell comment.
1905
- if @comments[row]
1906
- @comments[row][col] = comment_params(row, col, string, options)
1907
- else
1908
- @comments[row] = {}
1909
- @comments[row][col] = comment_params(row, col, string, options)
1910
- end
2049
+ @comments.add(Package::Comment.new(@workbook, self, row, col, string, options))
1911
2050
  end
1912
2051
 
1913
2052
  #
@@ -1939,8 +2078,7 @@ module Writexlsx
1939
2078
  check_dimensions(row, col)
1940
2079
  store_row_col_max_min_values(row, col)
1941
2080
 
1942
- store_data_to_table(row, col, [type, num, xf])
1943
- 0
2081
+ store_data_to_table(NumberCellData.new(row, col, num, xf))
1944
2082
  end
1945
2083
 
1946
2084
  #
@@ -1950,8 +2088,6 @@ module Writexlsx
1950
2088
  # Write a string to the specified row and column (zero indexed).
1951
2089
  # format is optional.
1952
2090
  #
1953
- # Returns 0 : normal termination
1954
- #
1955
2091
  # worksheet.write_string(0, 0, 'Your text here')
1956
2092
  # worksheet.write_string('A2', 'or here')
1957
2093
  #
@@ -1979,22 +2115,13 @@ module Writexlsx
1979
2115
  row, col, str, xf = row_col_notation(args)
1980
2116
  raise WriteXLSXInsufficientArgumentError if [row, col, str].include?(nil)
1981
2117
 
1982
- type = 's' # The data type
1983
-
1984
2118
  # Check that row and col are valid and store max and min values
1985
2119
  check_dimensions(row, col)
1986
2120
  store_row_col_max_min_values(row, col)
1987
2121
 
1988
- # Check that the string is < 32767 chars
1989
- str_error = 0
1990
- if str.length > @xls_strmax
1991
- str = str[0, @xls_strmax]
1992
- end
1993
-
1994
- index = shared_string_index(str)
2122
+ index = shared_string_index(str[0, STR_MAX])
1995
2123
 
1996
- store_data_to_table(row, col, [type, index, xf])
1997
- str_error
2124
+ store_data_to_table(StringCellData.new(row, col, index, xf))
1998
2125
  end
1999
2126
 
2000
2127
  #
@@ -2115,7 +2242,7 @@ module Writexlsx
2115
2242
  # If the first token is a string start the <r> element.
2116
2243
  writer.start_tag('r') if !fragments[0].respond_to?(:get_xf_index)
2117
2244
 
2118
- # Write the XML elements for the $format $string fragments.
2245
+ # Write the XML elements for the format string fragments.
2119
2246
  fragments.each do |token|
2120
2247
  if token.respond_to?(:get_xf_index)
2121
2248
  # Write the font run.
@@ -2134,7 +2261,7 @@ module Writexlsx
2134
2261
  # Add the XML string to the shared string table.
2135
2262
  index = shared_string_index(writer.string)
2136
2263
 
2137
- store_data_to_table(row, col, [type, index, xf])
2264
+ store_data_to_table(StringCellData.new(row, col, index, xf))
2138
2265
  end
2139
2266
 
2140
2267
  #
@@ -2172,13 +2299,11 @@ module Writexlsx
2172
2299
  # Don't write a blank cell unless it has a format
2173
2300
  return unless xf
2174
2301
 
2175
- type = 'b' # The data type
2176
-
2177
2302
  # Check that row and col are valid and store max and min values
2178
2303
  check_dimensions(row, col)
2179
2304
  store_row_col_max_min_values(row, col)
2180
2305
 
2181
- store_data_to_table(row, col, [type, nil, xf])
2306
+ store_data_to_table(BlankCellData.new(row, col, nil, xf))
2182
2307
  end
2183
2308
 
2184
2309
  #
@@ -2227,7 +2352,7 @@ module Writexlsx
2227
2352
 
2228
2353
  formula.sub!(/^=/, '')
2229
2354
 
2230
- store_data_to_table(row, col, ['f', formula, format, value])
2355
+ store_data_to_table(FormulaCellData.new(row, col, formula, format, value))
2231
2356
  end
2232
2357
 
2233
2358
  #
@@ -2238,9 +2363,6 @@ module Writexlsx
2238
2363
  #
2239
2364
  # format is optional.
2240
2365
  #
2241
- # write_array_formula methods return:
2242
- # Returns 0 : normal termination
2243
- #
2244
2366
  # In Excel an array formula is a formula that performs a calculation
2245
2367
  # on a set of values. It can return a single value or a range of values.
2246
2368
  #
@@ -2284,8 +2406,6 @@ module Writexlsx
2284
2406
  row1, col1, row2, col2, formula, xf, value = row_col_notation(args)
2285
2407
  raise WriteXLSXInsufficientArgumentError if [row1, col1, row2, col2, formula].include?(nil)
2286
2408
 
2287
- type = 'a' # The data type
2288
-
2289
2409
  # Swap last row/col with first row/col as necessary
2290
2410
  row1, row2 = row2, row1 if row1 > row2
2291
2411
  col1, col2 = col2, col1 if col1 > col2
@@ -2305,7 +2425,7 @@ module Writexlsx
2305
2425
  formula.sub!(/^\{(.*)\}$/, '\1')
2306
2426
  formula.sub!(/^=/, '')
2307
2427
 
2308
- store_data_to_table(row1, col1, [type, formula, xf, range, value])
2428
+ store_data_to_table(FormulaArrayCellData.new(row1, col1, formula, xf, range, value))
2309
2429
  end
2310
2430
 
2311
2431
  # The outline_settings() method is used to control the appearance of
@@ -2424,6 +2544,7 @@ module Writexlsx
2424
2544
  def write_url(*args)
2425
2545
  # Check for a cell reference in A1 notation and substitute row and column
2426
2546
  row, col, url, xf, str, tip = row_col_notation(args)
2547
+ xf, str = str, xf if str.respond_to?(:xf_index)
2427
2548
  raise WriteXLSXInsufficientArgumentError if [row, col, url].include?(nil)
2428
2549
 
2429
2550
  type = 'l' # XML data type
@@ -2455,14 +2576,8 @@ module Writexlsx
2455
2576
  check_dimensions(row, col)
2456
2577
  store_row_col_max_min_values(row, col)
2457
2578
 
2458
- # Check that the string is < 32767 chars
2459
- str_error = 0
2460
- if str.bytesize > @xls_strmax
2461
- str = str[0, @xls_strmax]
2462
- end
2463
-
2464
2579
  # Store the URL displayed text in the shared string table.
2465
- index = shared_string_index(str)
2580
+ index = shared_string_index(str[0, STR_MAX])
2466
2581
 
2467
2582
  # External links to URLs and to other Excel workbooks have slightly
2468
2583
  # different characteristics that we have to account for.
@@ -2484,9 +2599,7 @@ module Writexlsx
2484
2599
  link_type = 1
2485
2600
  end
2486
2601
 
2487
- store_data_to_table(row, col, [type, index, xf, link_type, url, str, tip])
2488
-
2489
- return str_error
2602
+ store_data_to_table(HyperlinkCellData.new(row, col, index, xf, link_type, url, str, tip))
2490
2603
  end
2491
2604
 
2492
2605
  #
@@ -2494,7 +2607,7 @@ module Writexlsx
2494
2607
  # write_date_time (row, col, date_string [ , format ] )
2495
2608
  #
2496
2609
  # Write a datetime string in ISO8601 "yyyy-mm-ddThh:mm:ss.ss" format as a
2497
- # number representing an Excel date. $format is optional.
2610
+ # number representing an Excel date. format is optional.
2498
2611
  #
2499
2612
  # The write_date_time() method can be used to write a date or time
2500
2613
  # to the cell specified by row and column:
@@ -2508,7 +2621,7 @@ module Writexlsx
2508
2621
  # This conforms to an ISO8601 date but it should be noted that the
2509
2622
  # full range of ISO8601 formats are not supported.
2510
2623
  #
2511
- # The following variations on the $date_string parameter are permitted:
2624
+ # The following variations on the date_string parameter are permitted:
2512
2625
  #
2513
2626
  # yyyy-mm-ddThh:mm:ss.sss # Standard format
2514
2627
  # yyyy-mm-ddT # No time
@@ -2519,7 +2632,7 @@ module Writexlsx
2519
2632
  #
2520
2633
  # Note that the T is required in all cases.
2521
2634
  #
2522
- # A date should always have a $format, otherwise it will appear
2635
+ # A date should always have a format, otherwise it will appear
2523
2636
  # as a number, see "DATES AND TIME IN EXCEL" and "CELL FORMATTING".
2524
2637
  # Here is a typical example:
2525
2638
  #
@@ -2537,20 +2650,15 @@ module Writexlsx
2537
2650
  row, col, str, xf = row_col_notation(args)
2538
2651
  raise WriteXLSXInsufficientArgumentError if [row, col, str].include?(nil)
2539
2652
 
2540
- type = 'n' # The data type
2541
-
2542
2653
  # Check that row and col are valid and store max and min values
2543
2654
  check_dimensions(row, col)
2544
2655
  store_row_col_max_min_values(row, col)
2545
2656
 
2546
- str_error = 0
2547
2657
  date_time = convert_date_time(str)
2548
2658
 
2549
2659
  # If the date isn't valid then write it as a string.
2550
2660
  return write_string(args) unless date_time
2551
- store_data_to_table(row, col, [type, date_time, xf])
2552
-
2553
- str_error
2661
+ store_data_to_table(NumberCellData.new(row, col, date_time, xf))
2554
2662
  end
2555
2663
 
2556
2664
  #
@@ -2632,7 +2740,7 @@ module Writexlsx
2632
2740
  # cell. This can be occasionally useful if you wish to align two or more
2633
2741
  # images relative to the same cell.
2634
2742
  #
2635
- # The parameters $scale_x and $scale_y can be used to scale the inserted
2743
+ # The parameters scale_x and scale_y can be used to scale the inserted
2636
2744
  # image horizontally and vertically:
2637
2745
  #
2638
2746
  # # Scale the inserted image: width x 2.0, height x 0.8
@@ -2712,7 +2820,7 @@ module Writexlsx
2712
2820
  end
2713
2821
 
2714
2822
  #
2715
- # convert_date_time($date_time_string)
2823
+ # convert_date_time(date_time_string)
2716
2824
  #
2717
2825
  # The function takes a date and time in ISO8601 "yyyy-mm-ddThh:mm:ss.ss" format
2718
2826
  # and converts it to a decimal number representing a valid Excel date.
@@ -2852,14 +2960,14 @@ module Writexlsx
2852
2960
  # don't have a format. For example
2853
2961
  #
2854
2962
  # worksheet.set_row(0, nil, format1) # Set the format for row 1
2855
- # worksheet.write('A1', 'Hello') # Defaults to $format1
2856
- # worksheet.write('B1', 'Hello', format2) # Keeps $format2
2963
+ # worksheet.write('A1', 'Hello') # Defaults to format1
2964
+ # worksheet.write('B1', 'Hello', format2) # Keeps format2
2857
2965
  #
2858
2966
  # If you wish to define a row format in this way you should call the
2859
2967
  # method before any calls to write(). Calling it afterwards will overwrite
2860
2968
  # any format that was previously specified.
2861
2969
  #
2862
- # The $hidden parameter should be set to 1 if you wish to hide a row.
2970
+ # The hidden parameter should be set to 1 if you wish to hide a row.
2863
2971
  # This can be used, for example, to hide intermediary steps in a
2864
2972
  # complicated calculation:
2865
2973
  #
@@ -2892,7 +3000,7 @@ module Writexlsx
2892
3000
  # programs in the examples directory of the distro.
2893
3001
  #
2894
3002
  # Excel allows up to 7 outline levels. Therefore the level parameter
2895
- # should be in the range 0 <= $level <= 7.
3003
+ # should be in the range 0 <= level <= 7.
2896
3004
  #
2897
3005
  def set_row(*args)
2898
3006
  row = args[0]
@@ -2931,7 +3039,7 @@ module Writexlsx
2931
3039
  end
2932
3040
 
2933
3041
  #
2934
- # merge_range($first_row, $first_col, $last_row, $last_col, $string, $format)
3042
+ # merge_range(first_row, first_col, last_row, last_col, string, format)
2935
3043
  #
2936
3044
  # Merge a range of cells. The first cell should contain the data and the others
2937
3045
  # should be blank. All cells should contain the same format.
@@ -3029,9 +3137,6 @@ module Writexlsx
3029
3137
  # conditional_formatting(row, col, {...})
3030
3138
  # conditional_formatting(first_row, first_col, last_row, last_col, {...})
3031
3139
  #
3032
- # conditional_formatting methods return:
3033
- # Returns 0 : normal termination
3034
- #
3035
3140
  # The conditional_format() method is used to add formatting to a cell
3036
3141
  # or range of cells based on user defined criteria.
3037
3142
  #
@@ -3040,14 +3145,14 @@ module Writexlsx
3040
3145
  # :type => 'cell',
3041
3146
  # :criteria => '>=',
3042
3147
  # :value => 50,
3043
- # :format => $format1
3148
+ # :format => format1
3044
3149
  # }
3045
3150
  # )
3046
3151
  #
3047
3152
  # This method contains a lot of parameters and is described in detail in
3048
3153
  # a separate section "CONDITIONAL FORMATTING IN EXCEL".
3049
3154
  #
3050
- # See also the conditional_format.pl program in the examples directory of the distro
3155
+ # See also the conditional_format.rb program in the examples directory of the distro
3051
3156
  #
3052
3157
  def conditional_formatting(*args)
3053
3158
  # Check for a cell reference in A1 notation and substitute row and column
@@ -3486,7 +3591,7 @@ module Writexlsx
3486
3591
  # :input_title => 'Enter an integer:',
3487
3592
  # :input_message => 'between 1 and 100',
3488
3593
  # });
3489
- # See also the data_validate.pl program in the examples directory
3594
+ # See also the data_validate.rb program in the examples directory
3490
3595
  # of the distro.
3491
3596
  #
3492
3597
  def data_validation(*args)
@@ -3502,111 +3607,38 @@ module Writexlsx
3502
3607
  end
3503
3608
  raise WriteXLSXInsufficientArgumentError if [row1, col1, row2, col2, param].include?(nil)
3504
3609
 
3505
- # Check that row and col are valid without storing the values.
3506
3610
  check_dimensions(row1, col1)
3507
3611
  check_dimensions(row2, col2)
3508
3612
 
3509
- # Check for valid input parameters.
3510
- param.each_key do |param_key|
3511
- unless valid_validation_parameter.include?(param_key)
3512
- raise WriteXLSXOptionParameterError, "Unknown parameter '#{param_key}' in data_validation()"
3513
- end
3514
- end
3613
+ check_for_valid_input_params(param)
3515
3614
 
3516
- # Map alternative parameter names 'source' or 'minimum' to 'value'.
3517
3615
  param[:value] = param[:source] if param[:source]
3518
3616
  param[:value] = param[:minimum] if param[:minimum]
3519
3617
 
3520
- # :validate is a required parameter.
3521
- unless param.has_key?(:validate)
3522
- raise WriteXLSXOptionParameterError, "Parameter :validate is required in data_validation()"
3523
- end
3524
-
3525
- # Check for valid validation types.
3526
- unless valid_validation_type.has_key?(param[:validate].downcase)
3527
- raise WriteXLSXOptionParameterError,
3528
- "Unknown validation type '#{param[:validate]}' for parameter :validate in data_validation()"
3529
- else
3530
- param[:validate] = valid_validation_type[param[:validate].downcase]
3531
- end
3532
-
3533
- # No action is required for validation type 'any'.
3534
- # TODO: we should perhaps store 'any' for message only validations.
3618
+ param[:validate] = valid_validation_type[param[:validate].downcase]
3535
3619
  return if param[:validate] == 'none'
3536
-
3537
- # The list and custom validations don't have a criteria so we use a default
3538
- # of 'between'.
3539
- if param[:validate] == 'list' || param[:validate] == 'custom'
3620
+ if ['list', 'custom'].include?(param[:validate])
3540
3621
  param[:criteria] = 'between'
3541
3622
  param[:maximum] = nil
3542
3623
  end
3543
3624
 
3544
- # 'criteria' is a required parameter.
3545
- unless param.has_key?(:criteria)
3546
- raise WriteXLSXOptionParameterError, "Parameter :criteria is required in data_validation()"
3547
- end
3548
-
3549
- # List of valid criteria types.
3550
- criteria_type = valid_criteria_type
3551
-
3552
- # Check for valid criteria types.
3553
- unless criteria_type.has_key?(param[:criteria].downcase)
3554
- raise WriteXLSXOptionParameterError,
3555
- "Unknown criteria type '#{param[:criteria]}' for parameter :criteria in data_validation()"
3556
- else
3557
- param[:criteria] = criteria_type[param[:criteria].downcase]
3558
- end
3559
-
3560
- # 'Between' and 'Not between' criteria require 2 values.
3561
- if param[:criteria] == 'between' || param[:criteria] == 'notBetween'
3562
- unless param.has_key?(:maximum)
3563
- raise WriteXLSXOptionParameterError,
3564
- "Parameter :maximum is required in data_validation() when using :between or :not between criteria"
3565
- end
3566
- else
3567
- param[:maximum] = nil
3568
- end
3569
-
3570
- # List of valid error dialog types.
3571
- error_type = {
3572
- 'stop' => 0,
3573
- 'warning' => 1,
3574
- 'information' => 2
3575
- }
3576
-
3577
- # Check for valid error dialog types.
3578
- if not param.has_key?(:error_type)
3579
- param[:error_type] = 0
3580
- elsif not error_type.has_key?(param[:error_type].downcase)
3581
- raise WriteXLSXOptionParameterError,
3582
- "Unknown criteria type '#param[:error_type}' for parameter :error_type in data_validation()"
3583
- else
3584
- param[:error_type] = error_type[param[:error_type].downcase]
3585
- end
3625
+ check_criteria_required(param)
3626
+ check_valid_citeria_types(param)
3627
+ param[:criteria] = valid_criteria_type[param[:criteria].downcase]
3586
3628
 
3587
- # Convert date/times value if required.
3588
- if param[:validate] == 'date' || param[:validate] == 'time'
3589
- unless convert_date_time_value(param, :value) && convert_date_time_value(param, :maximum)
3590
- raise WriteXLSXOptionParameterError, "Invalid date/time value."
3591
- end
3592
- end
3629
+ check_maximum_value_when_criteria_is_between_or_notbetween(param)
3630
+ param[:error_type] = param.has_key?(:error_type) ? error_type[param[:error_type].downcase] : 0
3593
3631
 
3594
- # Set some defaults if they haven't been defined by the user.
3595
- param[:ignore_blank] = 1 unless param[:ignore_blank]
3596
- param[:dropdown] = 1 unless param[:dropdown]
3597
- param[:show_input] = 1 unless param[:show_input]
3598
- param[:show_error] = 1 unless param[:show_error]
3632
+ convert_date_time_value_if_required(param)
3633
+ set_some_defaults(param)
3599
3634
 
3600
- # These are the cells to which the validation is applied.
3601
3635
  param[:cells] = [[row1, col1, row2, col2]]
3602
3636
 
3603
3637
  # A (for now) undocumented parameter to pass additional cell ranges.
3604
- if param.has_key?(:other_cells)
3605
- param[:other_cells].each { |cells| param[:cells] << cells }
3606
- end
3638
+ param[:other_cells].each { |cells| param[:cells] << cells } if param.has_key?(:other_cells)
3607
3639
 
3608
3640
  # Store the validation information until we close the worksheet.
3609
- @validations.push(param)
3641
+ @validations << param
3610
3642
  end
3611
3643
 
3612
3644
  #
@@ -4010,8 +4042,12 @@ module Writexlsx
4010
4042
  @comments_author = author if author
4011
4043
  end
4012
4044
 
4045
+ def comments_count # :nodoc:
4046
+ @comments.size
4047
+ end
4048
+
4013
4049
  def has_comments? # :nodoc:
4014
- !!@has_comments
4050
+ !@comments.empty?
4015
4051
  end
4016
4052
 
4017
4053
  def is_chartsheet? # :nodoc:
@@ -4022,42 +4058,26 @@ module Writexlsx
4022
4058
  # Turn the HoH that stores the comments into an array for easier handling
4023
4059
  # and set the external links.
4024
4060
  #
4025
- def prepare_comments(vml_data_id, vml_shape_id, comment_id) # :nodoc:
4026
- comments = []
4027
-
4028
- # We sort the comments by row and column but that isn't strictly required.
4029
- @comments.keys.sort.each do |row|
4030
- @comments[row].keys.sort.each do |col|
4031
- # Set comment visibility if required and not already user defined.
4032
- @comments[row][col][4] ||= 1 if @comments_visible
4033
-
4034
- # Set comment author if not already user defined.
4035
- @comments[row][col][3] ||= @comments_author
4036
- comments << @comments[row][col]
4037
- end
4038
- end
4039
-
4040
- @comments_array = comments
4041
-
4042
- @external_comment_links <<
4043
- ['/vmlDrawing', "../drawings/vmlDrawing#{comment_id}.vml"] <<
4044
- ['/comments', "../comments#{comment_id}.xml"]
4045
-
4046
- count = comments.size
4061
+ def set_vml_data_id(vml_data_id) # :nodoc:
4062
+ count = @comments.sorted_comments.size
4047
4063
  start_data_id = vml_data_id
4048
4064
 
4049
4065
  # The VML o:idmap data id contains a comma separated range when there is
4050
4066
  # more than one 1024 block of comments, like this: data="1,2".
4051
4067
  (1 .. (count / 1024)).each do |i|
4052
- vml_data_id = "vml_data_id,#{start_data_id + i}"
4068
+ vml_data_id = "#{vml_data_id},#{start_data_id + i}"
4053
4069
  end
4054
-
4055
- @vml_data_id = vml_data_id
4056
- @vml_shape_id = vml_shape_id
4070
+ @vml_data_id = vml_data_id
4057
4071
 
4058
4072
  count
4059
4073
  end
4060
4074
 
4075
+ def set_external_comment_links(comment_id) # :nodoc:
4076
+ @external_comment_links <<
4077
+ ['/vmlDrawing', "../drawings/vmlDrawing#{comment_id}.vml"] <<
4078
+ ['/comments', "../comments#{comment_id}.xml"]
4079
+ end
4080
+
4061
4081
  #
4062
4082
  # Set up chart/drawings.
4063
4083
  #
@@ -4094,43 +4114,21 @@ module Writexlsx
4094
4114
  # Return nils for data that doesn't exist since Excel can chart series
4095
4115
  # with data missing.
4096
4116
  #
4097
- def get_range_data(row_start, col_start, row_end, col_end)
4117
+ def get_range_data(row_start, col_start, row_end, col_end) # :nodoc:
4098
4118
  # TODO. Check for worksheet limits.
4099
4119
 
4100
4120
  # Iterate through the table data.
4101
4121
  data = []
4102
4122
  (row_start .. row_end).each do |row_num|
4103
4123
  # Store nil if row doesn't exist.
4104
- if !@table[row_num]
4124
+ if !@cell_data_table[row_num]
4105
4125
  data << nil
4106
4126
  next
4107
4127
  end
4108
4128
 
4109
4129
  (col_start .. col_end).each do |col_num|
4110
- if cell = @table[row_num][col_num]
4111
- type = cell[0]
4112
- token = cell[1]
4113
-
4114
- data << case type
4115
- when 'n'
4116
- # Store a number.
4117
- token
4118
- when 's'
4119
- # Store a string.
4120
- {:sst_id => token}
4121
- when 'f'
4122
- # Store a formula.
4123
- cell[3] || 0
4124
- when 'a'
4125
- # Store an array formula.
4126
- cell[4] || 0
4127
- when 'l'
4128
- # Store the string part a hyperlink.
4129
- {:sst_id => token}
4130
- when 'b'
4131
- # Store a empty cell.
4132
- ''
4133
- end
4130
+ if cell = @cell_data_table[row_num][col_num]
4131
+ data << cell.data
4134
4132
  else
4135
4133
  # Store nil if col doesn't exist.
4136
4134
  data << nil
@@ -4141,49 +4139,262 @@ module Writexlsx
4141
4139
  return data
4142
4140
  end
4143
4141
 
4144
- private
4142
+ #
4143
+ # Calculate the vertices that define the position of a graphical object within
4144
+ # the worksheet in pixels.
4145
+ #
4146
+ # +------------+------------+
4147
+ # | A | B |
4148
+ # +-----+------------+------------+
4149
+ # | |(x1,y1) | |
4150
+ # | 1 |(A1)._______|______ |
4151
+ # | | | | |
4152
+ # | | | | |
4153
+ # +-----+----| BITMAP |-----+
4154
+ # | | | | |
4155
+ # | 2 | |______________. |
4156
+ # | | | (B2)|
4157
+ # | | | (x2,y2)|
4158
+ # +---- +------------+------------+
4159
+ #
4160
+ # Example of an object that covers some of the area from cell A1 to cell B2.
4161
+ #
4162
+ # Based on the width and height of the object we need to calculate 8 vars:
4163
+ #
4164
+ # col_start, row_start, col_end, row_end, x1, y1, x2, y2.
4165
+ #
4166
+ # We also calculate the absolute x and y position of the top left vertex of
4167
+ # the object. This is required for images.
4168
+ #
4169
+ # x_abs, y_abs
4170
+ #
4171
+ # The width and height of the cells that the object occupies can be variable
4172
+ # and have to be taken into account.
4173
+ #
4174
+ # The values of col_start and row_start are passed in from the calling
4175
+ # function. The values of col_end and row_end are calculated by subtracting
4176
+ # the width and height of the object from the width and height of the
4177
+ # underlying cells.
4178
+ #
4179
+ # col_start # Col containing upper left corner of object.
4180
+ # x1 # Distance to left side of object.
4181
+ # row_start # Row containing top left corner of object.
4182
+ # y1 # Distance to top of object.
4183
+ # col_end # Col containing lower right corner of object.
4184
+ # x2 # Distance to right side of object.
4185
+ # row_end # Row containing bottom right corner of object.
4186
+ # y2 # Distance to bottom of object.
4187
+ # width # Width of object frame.
4188
+ # height # Height of object frame.
4189
+ def position_object_pixels(col_start, row_start, x1, y1, width, height, is_drawing = false) #:nodoc:
4190
+ # Calculate the absolute x offset of the top-left vertex.
4191
+ if @col_size_changed
4192
+ x_abs = (1 .. col_start).inject(0) {|sum, col| sum += size_col(col)}
4193
+ else
4194
+ # Optimisation for when the column widths haven't changed.
4195
+ x_abs = 64 * col_start
4196
+ end
4197
+ x_abs += x1
4145
4198
 
4146
- # List of valid input parameters.
4147
- def valid_validation_parameter
4148
- [
4149
- :validate,
4150
- :criteria,
4151
- :value,
4152
- :source,
4153
- :minimum,
4154
- :maximum,
4155
- :ignore_blank,
4156
- :dropdown,
4157
- :show_input,
4158
- :input_title,
4159
- :input_message,
4160
- :show_error,
4161
- :error_title,
4162
- :error_message,
4163
- :error_type,
4164
- :other_cells
4165
- ]
4166
- end
4199
+ # Calculate the absolute y offset of the top-left vertex.
4200
+ # Store the column change to allow optimisations.
4201
+ if @row_size_changed
4202
+ y_abs = (1 .. row_start).inject(0) {|sum, row| sum += size_row(row)}
4203
+ else
4204
+ # Optimisation for when the row heights haven't changed.
4205
+ y_abs = 20 * row_start
4206
+ end
4207
+ y_abs += y1
4167
4208
 
4168
- def valid_validation_type # :nodoc:
4169
- {
4170
- 'any' => 'none',
4171
- 'any value' => 'none',
4172
- 'whole number' => 'whole',
4173
- 'whole' => 'whole',
4174
- 'integer' => 'whole',
4175
- 'decimal' => 'decimal',
4176
- 'list' => 'list',
4177
- 'date' => 'date',
4178
- 'time' => 'time',
4179
- 'text length' => 'textLength',
4209
+ # Adjust start column for offsets that are greater than the col width.
4210
+ x1, col_start = adjust_column_offset(x1, col_start)
4211
+
4212
+ # Adjust start row for offsets that are greater than the row height.
4213
+ y1, row_start = adjust_row_offset(y1, row_start)
4214
+
4215
+ # Initialise end cell to the same as the start cell.
4216
+ col_end = col_start
4217
+ row_end = row_start
4218
+
4219
+ width += x1
4220
+ height += y1
4221
+
4222
+ # Subtract the underlying cell widths to find the end cell of the object.
4223
+ width, col_end = adjust_column_offset(width, col_end)
4224
+
4225
+ # Subtract the underlying cell heights to find the end cell of the object.
4226
+ height, row_end = adjust_row_offset(height, row_end)
4227
+
4228
+ # The following is only required for positioning drawing/chart objects
4229
+ # and not comments. It is probably the result of a bug.
4230
+ if is_drawing
4231
+ col_end -= 1 if width == 0
4232
+ row_end -= 1 if height == 0
4233
+ end
4234
+
4235
+ # The end vertices are whatever is left from the width and height.
4236
+ x2 = width
4237
+ y2 = height
4238
+
4239
+ [col_start, row_start, x1, y1, col_end, row_end, x2, y2, x_abs, y_abs]
4240
+ end
4241
+
4242
+ def comments_visible? # :nodoc:
4243
+ !!@comments_visible
4244
+ end
4245
+
4246
+ def comments_xml_writer=(file) # :nodoc:
4247
+ @comments.set_xml_writer(file)
4248
+ end
4249
+
4250
+ def comments_assemble_xml_file # :nodoc:
4251
+ @comments.assemble_xml_file
4252
+ end
4253
+
4254
+ def comments_array # :nodoc:
4255
+ @comments.sorted_comments
4256
+ end
4257
+
4258
+ #
4259
+ # Write the cell value <v> element.
4260
+ #
4261
+ def write_cell_value(value = '') #:nodoc:
4262
+ value ||= ''
4263
+ value = value.to_i if value == value.to_i
4264
+ @writer.data_element('v', value)
4265
+ end
4266
+
4267
+ #
4268
+ # Write the cell formula <f> element.
4269
+ #
4270
+ def write_cell_formula(formula = '') #:nodoc:
4271
+ @writer.data_element('f', formula)
4272
+ end
4273
+
4274
+ #
4275
+ # Write the cell array formula <f> element.
4276
+ #
4277
+ def write_cell_array_formula(formula, range) #:nodoc:
4278
+ attributes = ['t', 'array', 'ref', range]
4279
+
4280
+ @writer.data_element('f', formula, attributes)
4281
+ end
4282
+
4283
+ private
4284
+
4285
+ def check_for_valid_input_params(param)
4286
+ param.each_key do |param_key|
4287
+ unless valid_validation_parameter.include?(param_key)
4288
+ raise WriteXLSXOptionParameterError, "Unknown parameter '#{param_key}' in data_validation()"
4289
+ end
4290
+ end
4291
+ unless param.has_key?(:validate)
4292
+ raise WriteXLSXOptionParameterError, "Parameter :validate is required in data_validation()"
4293
+ end
4294
+ unless valid_validation_type.has_key?(param[:validate].downcase)
4295
+ raise WriteXLSXOptionParameterError,
4296
+ "Unknown validation type '#{param[:validate]}' for parameter :validate in data_validation()"
4297
+ end
4298
+ if param[:error_type] && !error_type.has_key?(param[:error_type].downcase)
4299
+ raise WriteXLSXOptionParameterError,
4300
+ "Unknown criteria type '#param[:error_type}' for parameter :error_type in data_validation()"
4301
+ end
4302
+ end
4303
+
4304
+ def check_criteria_required(param)
4305
+ unless param.has_key?(:criteria)
4306
+ raise WriteXLSXOptionParameterError, "Parameter :criteria is required in data_validation()"
4307
+ end
4308
+ end
4309
+
4310
+ def check_valid_citeria_types(param)
4311
+ unless valid_criteria_type.has_key?(param[:criteria].downcase)
4312
+ raise WriteXLSXOptionParameterError,
4313
+ "Unknown criteria type '#{param[:criteria]}' for parameter :criteria in data_validation()"
4314
+ end
4315
+ end
4316
+
4317
+ def check_maximum_value_when_criteria_is_between_or_notbetween(param)
4318
+ if param[:criteria] == 'between' || param[:criteria] == 'notBetween'
4319
+ unless param.has_key?(:maximum)
4320
+ raise WriteXLSXOptionParameterError,
4321
+ "Parameter :maximum is required in data_validation() when using :between or :not between criteria"
4322
+ end
4323
+ else
4324
+ param[:maximum] = nil
4325
+ end
4326
+ end
4327
+
4328
+ def error_type
4329
+ {'stop' => 0, 'warning' => 1, 'information' => 2}
4330
+ end
4331
+
4332
+ def convert_date_time_value_if_required(param)
4333
+ if param[:validate] == 'date' || param[:validate] == 'time'
4334
+ unless convert_date_time_value(param, :value) && convert_date_time_value(param, :maximum)
4335
+ raise WriteXLSXOptionParameterError, "Invalid date/time value."
4336
+ end
4337
+ end
4338
+ end
4339
+
4340
+ def set_some_defaults(param)
4341
+ param[:ignore_blank] ||= 1
4342
+ param[:dropdown] ||= 1
4343
+ param[:show_input] ||= 1
4344
+ param[:show_error] ||= 1
4345
+ end
4346
+
4347
+ # Minor modification to allow comparison testing. Change RGB colors
4348
+ # from long format, ffcc00 to short format fc0 used by VML.
4349
+ def rgb_color(rgb)
4350
+ result = sprintf("%02x%02x%02x", *rgb)
4351
+ if result =~ /^([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3$/
4352
+ result = "#{$1}#{$2}#{$3}"
4353
+ end
4354
+ result
4355
+ end
4356
+
4357
+ # List of valid input parameters.
4358
+ def valid_validation_parameter
4359
+ [
4360
+ :validate,
4361
+ :criteria,
4362
+ :value,
4363
+ :source,
4364
+ :minimum,
4365
+ :maximum,
4366
+ :ignore_blank,
4367
+ :dropdown,
4368
+ :show_input,
4369
+ :input_title,
4370
+ :input_message,
4371
+ :show_error,
4372
+ :error_title,
4373
+ :error_message,
4374
+ :error_type,
4375
+ :other_cells
4376
+ ]
4377
+ end
4378
+
4379
+ def valid_validation_type # :nodoc:
4380
+ {
4381
+ 'any' => 'none',
4382
+ 'any value' => 'none',
4383
+ 'whole number' => 'whole',
4384
+ 'whole' => 'whole',
4385
+ 'integer' => 'whole',
4386
+ 'decimal' => 'decimal',
4387
+ 'list' => 'list',
4388
+ 'date' => 'date',
4389
+ 'time' => 'time',
4390
+ 'text length' => 'textLength',
4180
4391
  'length' => 'textLength',
4181
4392
  'custom' => 'custom'
4182
4393
  }
4183
4394
  end
4184
4395
 
4185
- # Convert the list of $format, $string tokens to pairs of ($format, $string)
4186
- # except for the first $string fragment which doesn't require a default
4396
+ # Convert the list of format, string tokens to pairs of (format, string)
4397
+ # except for the first string fragment which doesn't require a default
4187
4398
  # formatting run. Use the default for strings without a leading format.
4188
4399
  def rich_strings_fragments(rich_strings) # :nodoc:
4189
4400
  # Create a temp format with the default font for unformatted fragments.
@@ -4437,68 +4648,6 @@ module Writexlsx
4437
4648
  sprintf("FF%02X%02X%02X", *rgb[0, 3])
4438
4649
  end
4439
4650
 
4440
- #
4441
- # Substitute an Excel cell reference in A1 notation for zero based row and
4442
- # column values in an argument list.
4443
- #
4444
- # Ex: ("A4", "Hello") is converted to (3, 0, "Hello").
4445
- #
4446
- def substitute_cellref(cell, *args) #:nodoc:
4447
- return [*args] if cell.respond_to?(:coerce) # Numeric
4448
-
4449
- cell.upcase!
4450
-
4451
- case cell
4452
- # Convert a column range: 'A:A' or 'B:G'.
4453
- # A range such as A:A is equivalent to A1:65536, so add rows as required
4454
- when /\$?([A-Z]{1,3}):\$?([A-Z]{1,3})/
4455
- row1, col1 = cell_to_rowcol($1 + '1')
4456
- row2, col2 = cell_to_rowcol($2 + @xls_rowmax.to_s)
4457
- return [row1, col1, row2, col2, *args]
4458
- # Convert a cell range: 'A1:B7'
4459
- when /\$?([A-Z]{1,3}\$?\d+):\$?([A-Z]{1,3}\$?\d+)/
4460
- row1, col1 = cell_to_rowcol($1)
4461
- row2, col2 = cell_to_rowcol($2)
4462
- return [row1, col1, row2, col2, *args]
4463
- # Convert a cell reference: 'A1' or 'AD2000'
4464
- when /\$?([A-Z]{1,3}\$?\d+)/
4465
- row1, col1 = cell_to_rowcol($1)
4466
- return [row1, col1, *args]
4467
- else
4468
- raise("Unknown cell reference #{cell}")
4469
- end
4470
- end
4471
-
4472
- #
4473
- # Convert an Excel cell reference in A1 notation to a zero based row and column
4474
- # reference converts C1 to (0, 2).
4475
- #
4476
- # Returns: row, column
4477
- #
4478
- def cell_to_rowcol(cell) #:nodoc:
4479
- cell =~ /(\$?)([A-Z]{1,3})(\$?)(\d+)/
4480
- col_abs = $1 == '' ? 0 : 1
4481
- col = $2
4482
- row_abs = $3 == '' ? 0 : 1
4483
- row = $4.to_i
4484
-
4485
- # Convert base26 column string to number
4486
- # All your Base are belong to us.
4487
- chars = col.split(//)
4488
- expn = 0
4489
- col = 0
4490
- chars.reverse.each do |char|
4491
- col += (char.ord - 'A'.ord + 1) * (26 ** expn)
4492
- expn += 1
4493
- end
4494
-
4495
- # Convert 1-index to zero-index
4496
- row -= 1
4497
- col -= 1
4498
-
4499
- [row, col, row_abs, col_abs]
4500
- end
4501
-
4502
4651
  #
4503
4652
  # This is an internal method that is used to filter elements of the array of
4504
4653
  # pagebreaks used in the _store_hbreak() and _store_vbreak() methods. It:
@@ -4522,106 +4671,6 @@ module Writexlsx
4522
4671
  end
4523
4672
  end
4524
4673
 
4525
- #
4526
- # Calculate the vertices that define the position of a graphical object within
4527
- # the worksheet in pixels.
4528
- #
4529
- # +------------+------------+
4530
- # | A | B |
4531
- # +-----+------------+------------+
4532
- # | |(x1,y1) | |
4533
- # | 1 |(A1)._______|______ |
4534
- # | | | | |
4535
- # | | | | |
4536
- # +-----+----| BITMAP |-----+
4537
- # | | | | |
4538
- # | 2 | |______________. |
4539
- # | | | (B2)|
4540
- # | | | (x2,y2)|
4541
- # +---- +------------+------------+
4542
- #
4543
- # Example of an object that covers some of the area from cell A1 to cell B2.
4544
- #
4545
- # Based on the width and height of the object we need to calculate 8 vars:
4546
- #
4547
- # $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2.
4548
- #
4549
- # We also calculate the absolute x and y position of the top left vertex of
4550
- # the object. This is required for images.
4551
- #
4552
- # $x_abs, $y_abs
4553
- #
4554
- # The width and height of the cells that the object occupies can be variable
4555
- # and have to be taken into account.
4556
- #
4557
- # The values of $col_start and $row_start are passed in from the calling
4558
- # function. The values of $col_end and $row_end are calculated by subtracting
4559
- # the width and height of the object from the width and height of the
4560
- # underlying cells.
4561
- #
4562
- # col_start # Col containing upper left corner of object.
4563
- # x1 # Distance to left side of object.
4564
- # row_start # Row containing top left corner of object.
4565
- # y1 # Distance to top of object.
4566
- # col_end # Col containing lower right corner of object.
4567
- # x2 # Distance to right side of object.
4568
- # row_end # Row containing bottom right corner of object.
4569
- # y2 # Distance to bottom of object.
4570
- # width # Width of object frame.
4571
- # height # Height of object frame.
4572
- def position_object_pixels(col_start, row_start, x1, y1, width, height, is_drawing = false) #:nodoc:
4573
- # Calculate the absolute x offset of the top-left vertex.
4574
- if @col_size_changed
4575
- x_abs = (1 .. col_start).inject(0) {|sum, col| sum += size_col(col)}
4576
- else
4577
- # Optimisation for when the column widths haven't changed.
4578
- x_abs = 64 * col_start
4579
- end
4580
- x_abs += x1
4581
-
4582
- # Calculate the absolute y offset of the top-left vertex.
4583
- # Store the column change to allow optimisations.
4584
- if @row_size_changed
4585
- y_abs = (1 .. row_start).inject(0) {|sum, row| sum += size_row(row)}
4586
- else
4587
- # Optimisation for when the row heights haven't changed.
4588
- y_abs = 20 * row_start
4589
- end
4590
- y_abs += y1
4591
-
4592
- # Adjust start column for offsets that are greater than the col width.
4593
- x1, col_start = adjust_column_offset(x1, col_start)
4594
-
4595
- # Adjust start row for offsets that are greater than the row height.
4596
- y1, row_start = adjust_row_offset(y1, row_start)
4597
-
4598
- # Initialise end cell to the same as the start cell.
4599
- col_end = col_start
4600
- row_end = row_start
4601
-
4602
- width += x1
4603
- height += y1
4604
-
4605
- # Subtract the underlying cell widths to find the end cell of the object.
4606
- width, col_end = adjust_column_offset(width, col_end)
4607
-
4608
- # Subtract the underlying cell heights to find the end cell of the object.
4609
- height, row_end = adjust_row_offset(height, row_end)
4610
-
4611
- # The following is only required for positioning drawing/chart objects
4612
- # and not comments. It is probably the result of a bug.
4613
- if is_drawing
4614
- col_end -= 1 if width == 0
4615
- row_end -= 1 if height == 0
4616
- end
4617
-
4618
- # The end vertices are whatever is left from the width and height.
4619
- x2 = width
4620
- y2 = height
4621
-
4622
- [col_start, row_start, x1, y1, col_end, row_end, x2, y2, x_abs, y_abs]
4623
- end
4624
-
4625
4674
  def adjust_column_offset(x, column)
4626
4675
  while x >= size_col(column)
4627
4676
  x -= size_col(column)
@@ -4744,149 +4793,6 @@ module Writexlsx
4744
4793
  @drawing_links << ['/image', "../media/image#{image_id}.#{image_type}"]
4745
4794
  end
4746
4795
 
4747
- #
4748
- # This method handles the additional optional parameters to write_comment() as
4749
- # well as calculating the comment object position and vertices.
4750
- #
4751
- def comment_params(row, col, string, options) #:nodoc:
4752
- options ||= {}
4753
- default_width = 128
4754
- default_height = 74
4755
-
4756
- params = {
4757
- :author => nil,
4758
- :color => 81,
4759
- :start_cell => nil,
4760
- :start_col => nil,
4761
- :start_row => nil,
4762
- :visible => nil,
4763
- :width => default_width,
4764
- :height => default_height,
4765
- :x_offset => nil,
4766
- :x_scale => 1,
4767
- :y_offset => nil,
4768
- :y_scale => 1
4769
- }
4770
-
4771
- # Overwrite the defaults with any user supplied values. Incorrect or
4772
- # misspelled parameters are silently ignored.
4773
- params.update(options)
4774
-
4775
- # Ensure that a width and height have been set.
4776
- params[:width] ||= default_width
4777
- params[:height] ||= default_height
4778
-
4779
- # Limit the string to the max number of chars.
4780
- max_len = 32767
4781
-
4782
- string = string[0, max_len] if string.length > max_len
4783
-
4784
- # Set the comment background colour.
4785
- color = params[:color]
4786
- color_id = Format.get_color(color)
4787
-
4788
- if color_id == 0
4789
- params[:color] = '#ffffe1'
4790
- else
4791
- # Get the RGB color from the palette.
4792
- rgb = @workbook.palette[color_id - 8]
4793
-
4794
- # Minor modification to allow comparison testing. Change RGB colors
4795
- # from long format, ffcc00 to short format fc0 used by VML.
4796
- rgb_color = sprintf("%02x%02x%02x", *rgb)
4797
-
4798
- if rgb_color =~ /^([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3$/
4799
- rgb_color = "#{$1}#{$2}#{$3}"
4800
- end
4801
-
4802
- params[:color] = sprintf("#%s [%d]\n", rgb_color, color_id)
4803
- end
4804
-
4805
- # Convert a cell reference to a row and column.
4806
- if params[:start_cell]
4807
- params[:start_row], params[:start_col] = substitute_cellref(params[:start_cell])
4808
- end
4809
-
4810
- # Set the default start cell and offsets for the comment. These are
4811
- # generally fixed in relation to the parent cell. However there are
4812
- # some edge cases for cells at the, er, edges.
4813
- #
4814
- row_max = @xls_rowmax
4815
- col_max = @xls_colmax
4816
-
4817
- params[:start_row] ||= case row
4818
- when 0
4819
- 0
4820
- when row_max - 3
4821
- row_max - 7
4822
- when row_max - 2
4823
- row_max - 6
4824
- when row_max - 1
4825
- row_max - 5
4826
- else
4827
- row - 1
4828
- end
4829
-
4830
- params[:y_offset] ||= case row
4831
- when 0
4832
- 2
4833
- when row_max - 3, row_max - 2
4834
- 16
4835
- when row_max - 1
4836
- 14
4837
- else
4838
- 10
4839
- end
4840
-
4841
- params[:start_col] ||= case col
4842
- when col_max - 3
4843
- col_max - 6
4844
- when col_max - 2
4845
- col_max - 5
4846
- when col_max - 1
4847
- col_max - 4
4848
- else
4849
- col + 1
4850
- end
4851
-
4852
- params[:x_offset] ||= case col
4853
- when col_max - 3, col_max - 2, col_max - 1
4854
- 49
4855
- else
4856
- 15
4857
- end
4858
-
4859
- # Scale the size of the comment box if required.
4860
- params[:width] = params[:width] * params[:x_scale] if params[:x_scale]
4861
-
4862
- params[:height] = params[:height] * params[:y_scale] if params[:y_scale]
4863
-
4864
- # Round the dimensions to the nearest pixel.
4865
- params[:width] = (0.5 + params[:width]).to_i
4866
- params[:height] = (0.5 + params[:height]).to_i
4867
-
4868
- # Calculate the positions of comment object.
4869
- vertices = position_object_pixels(
4870
- params[:start_col], params[:start_row], params[:x_offset],
4871
- params[:y_offset], params[:width], params[:height]
4872
- )
4873
-
4874
- # Add the width and height for VML.
4875
- vertices << [params[:width], params[:height]]
4876
-
4877
- return [
4878
- row,
4879
- col,
4880
- string,
4881
-
4882
- params[:author],
4883
- params[:visible],
4884
- params[:color],
4885
-
4886
- vertices
4887
- ]
4888
- end
4889
-
4890
4796
  #
4891
4797
  # Based on the algorithm provided by Daniel Rentz of OpenOffice.
4892
4798
  #
@@ -5076,21 +4982,15 @@ module Writexlsx
5076
4982
  # Write the <col> element.
5077
4983
  #
5078
4984
  def write_col_info(*args) #:nodoc:
5079
- min = args[0] || 0
5080
- max = args[1] || 0
5081
- width = args[2]
5082
- format = args[3]
5083
- hidden = args[4] || 0
5084
- level = args[5] || 0
5085
- collapsed = args[6] || 0
5086
- # min = $_[0] // 0 # First formatted column.
5087
- # max = $_[1] // 0 # Last formatted column.
5088
- # width = $_[2] # Col width in user units.
5089
- # format = $_[3] # Format index.
5090
- # hidden = $_[4] // 0 # Hidden flag.
5091
- # level = $_[5] // 0 # Outline level.
5092
- # collapsed = $_[6] // 0 # Outline level.
5093
- custom_width = 1
4985
+ min = args[0] || 0 # First formatted column.
4986
+ max = args[1] || 0 # Last formatted column.
4987
+ width = args[2] # Col width in user units.
4988
+ format = args[3] # Format index.
4989
+ hidden = args[4] || 0 # Hidden flag.
4990
+ level = args[5] || 0 # Outline level.
4991
+ collapsed = args[6] || 0 # Outline level.
4992
+
4993
+ custom_width = true
5094
4994
  xf_index = 0
5095
4995
  xf_index = format.get_xf_index if format.respond_to?(:get_xf_index)
5096
4996
 
@@ -5098,13 +4998,13 @@ module Writexlsx
5098
4998
  if width.nil?
5099
4999
  if hidden == 0
5100
5000
  width = 8.43
5101
- custom_width = 0
5001
+ custom_width = false
5102
5002
  else
5103
5003
  width = 0
5104
5004
  end
5105
5005
  else
5106
5006
  # Width is defined but same as default.
5107
- custom_width = 0 if width == 8.43
5007
+ custom_width = false if width == 8.43
5108
5008
  end
5109
5009
 
5110
5010
  # Convert column width from user units to character width.
@@ -5121,7 +5021,7 @@ module Writexlsx
5121
5021
 
5122
5022
  (attributes << 'style' << xf_index) if xf_index != 0
5123
5023
  (attributes << 'hidden' << 1) if hidden != 0
5124
- (attributes << 'customWidth' << 1) if custom_width != 0
5024
+ (attributes << 'customWidth' << 1) if custom_width
5125
5025
  (attributes << 'outlineLevel' << level) if level != 0
5126
5026
  (attributes << 'collapsed' << 1) if collapsed != 0
5127
5027
 
@@ -5156,7 +5056,7 @@ module Writexlsx
5156
5056
  span = @row_spans[span_index]
5157
5057
 
5158
5058
  # Write the cells if the row contains data.
5159
- if @table[row_num]
5059
+ if @cell_data_table[row_num]
5160
5060
  if !@set_rows[row_num]
5161
5061
  write_row_element(row_num, span)
5162
5062
  else
@@ -5188,8 +5088,7 @@ module Writexlsx
5188
5088
  return not_contain_formatting_or_data?(row_num)
5189
5089
 
5190
5090
  # Write the cells if the row contains data.
5191
- row_ref = @table[row_num]
5192
- if row_ref
5091
+ if @cell_data_table[row_num]
5193
5092
  if !@set_rows[row_num]
5194
5093
  write_row(row_num)
5195
5094
  else
@@ -5204,17 +5103,16 @@ module Writexlsx
5204
5103
  end
5205
5104
 
5206
5105
  # Reset table.
5207
- @table = []
5106
+ @cell_data_table = {}
5208
5107
  end
5209
5108
 
5210
5109
  def not_contain_formatting_or_data?(row_num) # :nodoc:
5211
- !@set_rows[row_num] && !@table[row_num] && !@comments[row_num]
5110
+ !@set_rows[row_num] && !@cell_data_table[row_num] && !@comments.has_comment_in_row?(row_num)
5212
5111
  end
5213
5112
 
5214
5113
  def write_cell_column_dimension(row_num) # :nodoc:
5215
5114
  (@dim_colmin .. @dim_colmax).each do |col_num|
5216
- col_ref = @table[row_num][col_num]
5217
- write_cell(row_num, col_num, col_ref) if col_ref
5115
+ @cell_data_table[row_num][col_num].write_cell(self) if @cell_data_table[row_num][col_num]
5218
5116
  end
5219
5117
  end
5220
5118
 
@@ -5258,121 +5156,6 @@ module Writexlsx
5258
5156
  write_row_element(*new_args)
5259
5157
  end
5260
5158
 
5261
- #
5262
- # Write the <cell> element. This is the innermost loop so efficiency is
5263
- # important where possible. The basic methodology is that the data of every
5264
- # cell type is passed in as follows:
5265
- #
5266
- # [ $row, $col, $aref]
5267
- #
5268
- # The aref, called $cell below, contains the following structure in all types:
5269
- #
5270
- # [ $type, $token, $xf, @args ]
5271
- #
5272
- # Where $type: represents the cell type, such as string, number, formula, etc.
5273
- # $token: is the actual data for the string, number, formula, etc.
5274
- # $xf: is the XF format object index.
5275
- # @args: additional args relevant to the specific data type.
5276
- #
5277
- def write_cell(row, col, cell) #:nodoc:
5278
- type, token, xf = cell
5279
-
5280
- xf_index = 0
5281
- xf_index = xf.get_xf_index if xf.respond_to?(:get_xf_index)
5282
-
5283
- range = xl_rowcol_to_cell(row, col)
5284
- attributes = ['r', range]
5285
-
5286
- # Add the cell format index.
5287
- if xf_index != 0
5288
- attributes << 's' << xf_index
5289
- elsif @set_rows[row] && @set_rows[row][1]
5290
- row_xf = @set_rows[row][1]
5291
- attributes << 's' << row_xf.get_xf_index
5292
- elsif @col_formats[col]
5293
- col_xf = @col_formats[col]
5294
- attributes << 's' << col_xf.get_xf_index
5295
- end
5296
-
5297
- # Write the various cell types.
5298
- case type
5299
- when 'n'
5300
- # Write a number.
5301
- @writer.start_tag('c', attributes)
5302
- write_cell_value(token)
5303
- @writer.end_tag('c')
5304
- when 's'
5305
- # Write a string.
5306
- attributes << 't' << 's'
5307
- @writer.start_tag('c', attributes)
5308
- write_cell_value(token)
5309
- @writer.end_tag('c')
5310
- when 'f'
5311
- # Write a formula.
5312
- @writer.start_tag('c', attributes)
5313
- write_cell_formula(token)
5314
- write_cell_value(cell[3] || 0)
5315
- @writer.end_tag('c')
5316
- when 'a'
5317
- # Write an array formula.
5318
- @writer.start_tag('c', attributes)
5319
- write_cell_array_formula(token, cell[3])
5320
- write_cell_value(cell[4])
5321
- @writer.end_tag('c')
5322
- when 'l'
5323
- link_type = cell[3]
5324
-
5325
- # Write the string part a hyperlink.
5326
- attributes << 't' << 's'
5327
- @writer.start_tag('c', attributes)
5328
- write_cell_value(token)
5329
- @writer.end_tag('c')
5330
-
5331
- if link_type == 1
5332
- # External link with rel file relationship.
5333
- @hlink_count += 1
5334
- @hlink_refs <<
5335
- [
5336
- link_type, row, col,
5337
- @hlink_count, cell[5], cell[6]
5338
- ]
5339
-
5340
- @external_hyper_links << [ '/hyperlink', cell[4], 'External' ]
5341
- elsif link_type
5342
- # External link with rel file relationship.
5343
- @hlink_refs << [link_type, row, col, cell[4], cell[5], cell[6] ]
5344
- end
5345
- when 'b'
5346
- # Write a empty cell.
5347
- @writer.empty_tag('c', attributes)
5348
- end
5349
- end
5350
-
5351
- #
5352
- # Write the cell value <v> element.
5353
- #
5354
- def write_cell_value(value = '') #:nodoc:
5355
- value ||= ''
5356
- value = value.to_i if value == value.to_i
5357
- @writer.data_element('v', value)
5358
- end
5359
-
5360
- #
5361
- # Write the cell formula <f> element.
5362
- #
5363
- def write_cell_formula(formula = '') #:nodoc:
5364
- @writer.data_element('f', formula)
5365
- end
5366
-
5367
- #
5368
- # Write the cell array formula <f> element.
5369
- #
5370
- def write_cell_array_formula(formula, range) #:nodoc:
5371
- attributes = ['t', 'array', 'ref', range]
5372
-
5373
- @writer.data_element('f', formula, attributes)
5374
- end
5375
-
5376
5159
  #
5377
5160
  # Write the frozen or split <pane> elements.
5378
5161
  #
@@ -5566,7 +5349,7 @@ module Writexlsx
5566
5349
  attributes << 'pageOrder' << "overThenDown" if print_across?
5567
5350
 
5568
5351
  # Set page orientation.
5569
- if orientation?
5352
+ if @print_style.orientation?
5570
5353
  attributes << 'orientation' << 'portrait'
5571
5354
  else
5572
5355
  attributes << 'orientation' << 'landscape'
@@ -6000,7 +5783,7 @@ module Writexlsx
6000
5783
  # Write the <legacyDrawing> element.
6001
5784
  #
6002
5785
  def write_legacy_drawing #:nodoc:
6003
- return unless @has_comments
5786
+ return unless has_comments?
6004
5787
 
6005
5788
  # Increment the relationship id for any drawings or comments.
6006
5789
  id = @hlink_count + 1
@@ -6057,17 +5840,7 @@ module Writexlsx
6057
5840
  # Write the underline font element.
6058
5841
  #
6059
5842
  def write_underline(writer, underline) #:nodoc:
6060
- # Handle the underline variants.
6061
- if underline == 2
6062
- attributes = [val, 'double']
6063
- elsif underline == 33
6064
- attributes = [val, 'singleAccounting']
6065
- elsif underline == 34
6066
- attributes = [val, 'doubleAccounting']
6067
- else
6068
- attributes = [] # Default to single underline.
6069
- end
6070
-
5843
+ attributes = underline_attributes(underline)
6071
5844
  writer.empty_tag('u', attributes)
6072
5845
  end
6073
5846
 
@@ -6237,12 +6010,13 @@ module Writexlsx
6237
6010
  @writer.end_tag('cfRule')
6238
6011
  end
6239
6012
 
6240
- def store_data_to_table(row, col, data) #:nodoc:
6241
- if @table[row]
6242
- @table[row][col] = data
6013
+ def store_data_to_table(cell_data) #:nodoc:
6014
+ row, col = cell_data.row, cell_data.col
6015
+ if @cell_data_table[row]
6016
+ @cell_data_table[row][col] = cell_data
6243
6017
  else
6244
- @table[row] = []
6245
- @table[row][col] = data
6018
+ @cell_data_table[row] = {}
6019
+ @cell_data_table[row][col] = cell_data
6246
6020
  end
6247
6021
  end
6248
6022
 
@@ -6273,7 +6047,7 @@ module Writexlsx
6273
6047
  end
6274
6048
 
6275
6049
  def check_dimensions(row, col)
6276
- if !row || row >= @xls_rowmax || !col || col >= @xls_colmax
6050
+ if !row || row >= ROW_MAX || !col || col >= COL_MAX
6277
6051
  raise WriteXLSXDimensionError
6278
6052
  end
6279
6053
  0
@@ -6288,7 +6062,7 @@ module Writexlsx
6288
6062
  @dim_rowmin = row if !@dim_rowmin || (row < @dim_rowmin)
6289
6063
  @dim_rowmax = row if !@dim_rowmax || (row > @dim_rowmax)
6290
6064
  end
6291
-
6065
+
6292
6066
  def store_col_max_min_values(col)
6293
6067
  @dim_colmin = col if !@dim_colmin || (col < @dim_colmin)
6294
6068
  @dim_colmax = col if !@dim_colmax || (col > @dim_colmax)
@@ -6306,11 +6080,9 @@ module Writexlsx
6306
6080
  span_max = 0
6307
6081
  spans = []
6308
6082
  (@dim_rowmin .. @dim_rowmax).each do |row_num|
6309
- row_ref = @table[row_num]
6310
- if row_ref
6083
+ if @cell_data_table[row_num]
6311
6084
  (@dim_colmin .. @dim_colmax).each do |col_num|
6312
- col_ref = @table[row_num][col_num]
6313
- if col_ref
6085
+ if @cell_data_table[row_num][col_num]
6314
6086
  if !span_min
6315
6087
  span_min = col_num
6316
6088
  span_max = col_num
@@ -6370,11 +6142,11 @@ module Writexlsx
6370
6142
  row_char_2 = "$#{row_num_2 + 1}"
6371
6143
 
6372
6144
  # We need to handle some special cases that refer to rows or columns only.
6373
- if row_num_1 == 0 and row_num_2 == @xls_rowmax - 1
6145
+ if row_num_1 == 0 and row_num_2 == ROW_MAX - 1
6374
6146
  range1 = col_char_1
6375
6147
  range2 = col_char_2
6376
6148
  row_col_only = true
6377
- elsif col_num_1 == 0 and col_num_2 == @xls_colmax - 1
6149
+ elsif col_num_1 == 0 and col_num_2 == COL_MAX - 1
6378
6150
  range1 = row_char_1
6379
6151
  range2 = row_char_2
6380
6152
  row_col_only = true
@@ -6480,10 +6252,6 @@ module Writexlsx
6480
6252
  @print_style.page_setup_changed
6481
6253
  end
6482
6254
 
6483
- def orientation? #:nodoc:
6484
- !!@orientation
6485
- end
6486
-
6487
6255
  def header_footer_changed? #:nodoc:
6488
6256
  !!@header_footer_changed
6489
6257
  end
@@ -6503,7 +6271,7 @@ module Writexlsx
6503
6271
  def print_across?
6504
6272
  @print_style.across
6505
6273
  end
6506
-
6274
+
6507
6275
  # List of valid criteria types.
6508
6276
  def valid_criteria_type # :nodoc:
6509
6277
  {
@@ -6553,7 +6321,7 @@ module Writexlsx
6553
6321
 
6554
6322
  # Convert col ref to a cell ref and then to a col number.
6555
6323
  dummy, col = substitute_cellref("#{col}1")
6556
- raise "Invalid column '#{col_letter}'" if col >= @xls_colmax
6324
+ raise "Invalid column '#{col_letter}'" if col >= COL_MAX
6557
6325
  end
6558
6326
 
6559
6327
  col_first, col_last = @filter_range