write_xlsx 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. data/README.rdoc +3 -0
  2. data/Rakefile +1 -1
  3. data/VERSION +1 -1
  4. data/examples/formats.rb +498 -0
  5. data/lib/write_xlsx/chart.rb +15 -15
  6. data/lib/write_xlsx/chartsheet.rb +1 -1
  7. data/lib/write_xlsx/format.rb +10 -3
  8. data/lib/write_xlsx/package/comments.rb +171 -27
  9. data/lib/write_xlsx/package/packager.rb +8 -17
  10. data/lib/write_xlsx/package/shared_strings.rb +36 -15
  11. data/lib/write_xlsx/package/styles.rb +2 -12
  12. data/lib/write_xlsx/package/vml.rb +14 -22
  13. data/lib/write_xlsx/utility.rb +53 -4
  14. data/lib/write_xlsx/workbook.rb +21 -37
  15. data/lib/write_xlsx/worksheet.rb +533 -765
  16. data/test/helper.rb +10 -3
  17. data/test/package/comments/test_write_text_t.rb +1 -1
  18. data/test/package/shared_strings/test_shared_strings01.rb +3 -3
  19. data/test/package/shared_strings/test_shared_strings02.rb +3 -3
  20. data/test/package/shared_strings/test_write_sst.rb +3 -2
  21. data/test/package/vml/test_write_anchor.rb +1 -1
  22. data/test/package/vml/test_write_auto_fill.rb +1 -1
  23. data/test/package/vml/test_write_column.rb +1 -1
  24. data/test/package/vml/test_write_div.rb +1 -1
  25. data/test/package/vml/test_write_fill.rb +1 -1
  26. data/test/package/vml/test_write_idmap.rb +1 -1
  27. data/test/package/vml/test_write_move_with_cells.rb +1 -1
  28. data/test/package/vml/test_write_path.rb +2 -2
  29. data/test/package/vml/test_write_row.rb +1 -1
  30. data/test/package/vml/test_write_shadow.rb +1 -1
  31. data/test/package/vml/test_write_shapelayout.rb +1 -1
  32. data/test/package/vml/test_write_shapetype.rb +1 -1
  33. data/test/package/vml/test_write_size_with_cells.rb +1 -1
  34. data/test/package/vml/test_write_stroke.rb +1 -1
  35. data/test/package/vml/test_write_textbox.rb +1 -1
  36. data/test/perl_output/formats.xlsx +0 -0
  37. data/test/perl_output/indent.xlsx +0 -0
  38. data/test/perl_output/merge4.xlsx +0 -0
  39. data/test/perl_output/merge5.xlsx +0 -0
  40. data/test/test_example_match.rb +482 -0
  41. data/test/worksheet/test_repeat_formula.rb +5 -5
  42. data/test/worksheet/test_write_cell.rb +10 -5
  43. data/test/worksheet/test_write_legacy_drawing.rb +1 -1
  44. data/write_xlsx.gemspec +5 -5
  45. metadata +15 -15
  46. data/test/package/comments/test_comments01.rb +0 -36
  47. data/test/package/vml/test_vml_01.rb +0 -42
@@ -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