write_xlsx 0.58.0 → 0.59.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/README.rdoc +7 -1
  2. data/bin/extract_vba.rb +29 -0
  3. data/examples/add_vba_project.rb +36 -0
  4. data/examples/vbaProject.bin +0 -0
  5. data/lib/write_xlsx/chart.rb +22 -37
  6. data/lib/write_xlsx/package/conditional_format.rb +593 -0
  7. data/lib/write_xlsx/package/content_types.rb +17 -0
  8. data/lib/write_xlsx/package/packager.rb +26 -6
  9. data/lib/write_xlsx/package/relationships.rb +11 -1
  10. data/lib/write_xlsx/package/table.rb +284 -62
  11. data/lib/write_xlsx/utility.rb +179 -0
  12. data/lib/write_xlsx/version.rb +1 -1
  13. data/lib/write_xlsx/workbook.rb +14 -1
  14. data/lib/write_xlsx/worksheet.rb +667 -875
  15. data/test/package/table/test_table01.rb +1 -2
  16. data/test/package/table/test_table02.rb +1 -2
  17. data/test/package/table/test_table03.rb +1 -2
  18. data/test/package/table/test_table04.rb +1 -2
  19. data/test/package/table/test_table05.rb +1 -2
  20. data/test/package/table/test_table06.rb +1 -2
  21. data/test/package/table/test_table07.rb +1 -2
  22. data/test/package/table/test_table08.rb +1 -2
  23. data/test/package/table/test_table09.rb +1 -2
  24. data/test/package/table/test_table10.rb +1 -2
  25. data/test/package/table/test_table11.rb +1 -2
  26. data/test/package/table/test_table12.rb +1 -2
  27. data/test/package/table/test_write_auto_filter.rb +10 -3
  28. data/test/package/table/test_write_table_column.rb +9 -2
  29. data/test/package/table/test_write_table_style_info.rb +12 -11
  30. data/test/package/table/test_write_xml_declaration.rb +6 -1
  31. data/test/perl_output/add_vba_project.xlsm +0 -0
  32. data/test/regression/test_macro01.rb +29 -0
  33. data/test/regression/xlsx_files/macro01.xlsm +0 -0
  34. data/test/regression/xlsx_files/vbaProject01.bin +0 -0
  35. data/test/test_example_match.rb +22 -0
  36. data/test/vbaProject.bin +0 -0
  37. metadata +18 -3
@@ -5,6 +5,7 @@ require 'write_xlsx/format'
5
5
  require 'write_xlsx/drawing'
6
6
  require 'write_xlsx/compatibility'
7
7
  require 'write_xlsx/utility'
8
+ require 'write_xlsx/package/conditional_format'
8
9
  require 'tempfile'
9
10
 
10
11
  module Writexlsx
@@ -354,6 +355,7 @@ module Writexlsx
354
355
  attr_reader :writer, :set_rows, :col_formats # :nodoc:
355
356
  attr_accessor :vml_shape_id, :rel_count, :hlink_refs # :nodoc:
356
357
  attr_reader :comments_author # :nodoc:
358
+ attr_accessor :dxf_priority # :nodoc:
357
359
 
358
360
  def initialize(workbook, index, name) #:nodoc:
359
361
  @writer = Package::XMLWriterSimple.new
@@ -2901,133 +2903,6 @@ module Writexlsx
2901
2903
  write_formula(row, col, formula, format, value)
2902
2904
  end
2903
2905
 
2904
- #
2905
- # convert_date_time(date_time_string)
2906
- #
2907
- # The function takes a date and time in ISO8601 "yyyy-mm-ddThh:mm:ss.ss" format
2908
- # and converts it to a decimal number representing a valid Excel date.
2909
- #
2910
- # Dates and times in Excel are represented by real numbers. The integer part of
2911
- # the number stores the number of days since the epoch and the fractional part
2912
- # stores the percentage of the day in seconds. The epoch can be either 1900 or
2913
- # 1904.
2914
- #
2915
- # Parameter: Date and time string in one of the following formats:
2916
- # yyyy-mm-ddThh:mm:ss.ss # Standard
2917
- # yyyy-mm-ddT # Date only
2918
- # Thh:mm:ss.ss # Time only
2919
- #
2920
- # Returns:
2921
- # A decimal number representing a valid Excel date, or
2922
- # nil if the date is invalid.
2923
- #
2924
- def convert_date_time(date_time_string) #:nodoc:
2925
- date_time = date_time_string
2926
-
2927
- days = 0 # Number of days since epoch
2928
- seconds = 0 # Time expressed as fraction of 24h hours in seconds
2929
-
2930
- # Strip leading and trailing whitespace.
2931
- date_time.sub!(/^\s+/, '')
2932
- date_time.sub!(/\s+$/, '')
2933
-
2934
- # Check for invalid date char.
2935
- return nil if date_time =~ /[^0-9T:\-\.Z]/
2936
-
2937
- # Check for "T" after date or before time.
2938
- return nil unless date_time =~ /\dT|T\d/
2939
-
2940
- # Strip trailing Z in ISO8601 date.
2941
- date_time.sub!(/Z$/, '')
2942
-
2943
- # Split into date and time.
2944
- date, time = date_time.split(/T/)
2945
-
2946
- # We allow the time portion of the input DateTime to be optional.
2947
- if time
2948
- # Match hh:mm:ss.sss+ where the seconds are optional
2949
- if time =~ /^(\d\d):(\d\d)(:(\d\d(\.\d+)?))?/
2950
- hour = $1.to_i
2951
- min = $2.to_i
2952
- sec = $4.to_f || 0
2953
- else
2954
- return nil # Not a valid time format.
2955
- end
2956
-
2957
- # Some boundary checks
2958
- return nil if hour >= 24
2959
- return nil if min >= 60
2960
- return nil if sec >= 60
2961
-
2962
- # Excel expresses seconds as a fraction of the number in 24 hours.
2963
- seconds = (hour * 60* 60 + min * 60 + sec) / (24.0 * 60 * 60)
2964
- end
2965
-
2966
- # We allow the date portion of the input DateTime to be optional.
2967
- return seconds if date == ''
2968
-
2969
- # Match date as yyyy-mm-dd.
2970
- if date =~ /^(\d\d\d\d)-(\d\d)-(\d\d)$/
2971
- year = $1.to_i
2972
- month = $2.to_i
2973
- day = $3.to_i
2974
- else
2975
- return nil # Not a valid date format.
2976
- end
2977
-
2978
- # Set the epoch as 1900 or 1904. Defaults to 1900.
2979
- # Special cases for Excel.
2980
- unless date_1904?
2981
- return seconds if date == '1899-12-31' # Excel 1900 epoch
2982
- return seconds if date == '1900-01-00' # Excel 1900 epoch
2983
- return 60 + seconds if date == '1900-02-29' # Excel false leapday
2984
- end
2985
-
2986
-
2987
- # We calculate the date by calculating the number of days since the epoch
2988
- # and adjust for the number of leap days. We calculate the number of leap
2989
- # days by normalising the year in relation to the epoch. Thus the year 2000
2990
- # becomes 100 for 4 and 100 year leapdays and 400 for 400 year leapdays.
2991
- #
2992
- epoch = date_1904? ? 1904 : 1900
2993
- offset = date_1904? ? 4 : 0
2994
- norm = 300
2995
- range = year - epoch
2996
-
2997
- # Set month days and check for leap year.
2998
- mdays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
2999
- leap = 0
3000
- leap = 1 if year % 4 == 0 && year % 100 != 0 || year % 400 == 0
3001
- mdays[1] = 29 if leap != 0
3002
-
3003
- # Some boundary checks
3004
- return nil if year < epoch or year > 9999
3005
- return nil if month < 1 or month > 12
3006
- return nil if day < 1 or day > mdays[month - 1]
3007
-
3008
- # Accumulate the number of days since the epoch.
3009
- days = day # Add days for current month
3010
- (0 .. month-2).each do |m|
3011
- days += mdays[m] # Add days for past months
3012
- end
3013
- days += range * 365 # Add days for past years
3014
- days += ((range) / 4) # Add leapdays
3015
- days -= ((range + offset) /100) # Subtract 100 year leapdays
3016
- days += ((range + offset + norm)/400) # Add 400 year leapdays
3017
- days -= leap # Already counted above
3018
-
3019
- # Adjust for Excel erroneously treating 1900 as a leap year.
3020
- days += 1 if !date_1904? and days > 59
3021
-
3022
- date_time = sprintf("%0.10f", days + seconds)
3023
- date_time = date_time.sub(/\.?0+$/, '') if date_time =~ /\./
3024
- if date_time =~ /\./
3025
- date_time.to_f
3026
- else
3027
- date_time.to_i
3028
- end
3029
- end
3030
-
3031
2906
  #
3032
2907
  # :call-seq:
3033
2908
  # set_row(row [ , height, format, hidden, level, collapsed ] )
@@ -3222,6 +3097,9 @@ module Writexlsx
3222
3097
  #
3223
3098
  # This method handles the interface to Excel conditional formatting.
3224
3099
  #
3100
+ # This method contains a lot of parameters and is described in detail in
3101
+ # the section below.
3102
+ #
3225
3103
  # We allow the format to be called on one cell or a range of cells. The
3226
3104
  # hashref contains the formatting parameters and must be the last param:
3227
3105
  #
@@ -3240,170 +3118,604 @@ module Writexlsx
3240
3118
  # }
3241
3119
  # )
3242
3120
  #
3243
- # This method contains a lot of parameters and is described in detail in
3244
- # a separate section "CONDITIONAL FORMATTING IN EXCEL".
3121
+ # See also the conditional_format.rb program in the examples directory of
3122
+ # the distro.
3123
+ #
3124
+ # The conditional_formatting method is used to apply formatting based
3125
+ # on user defined criteria to an write_xlsx file.
3126
+ #
3127
+ # It can be applied to a single cell or a range of cells.
3128
+ # You can pass 3 parameters such as (row, col, {...})
3129
+ # or 5 parameters such as (first_row, first_col, last_row, last_col, {...}).
3130
+ # You can also use A1 style notation. For example:
3131
+ #
3132
+ # worksheet.conditional_formatting( 0, 0, {...} )
3133
+ # worksheet.conditional_formatting( 0, 0, 4, 1, {...} )
3134
+ #
3135
+ # # Which are the same as:
3136
+ #
3137
+ # worksheet.conditional_formatting( 'A1', {...} )
3138
+ # worksheet.conditional_formatting( 'A1:B5', {...} )
3139
+ #
3140
+ # Using A1 style notation is is also possible to specify
3141
+ # non-contiguous ranges, separated by a comma. For example:
3142
+ #
3143
+ # worksheet.conditional_formatting( 'A1:D5,A8:D12', {...} )
3144
+ # The last parameter in conditional_formatting must be a hash containing
3145
+ # the parameters that describe the type and style of the data validation.
3146
+ #
3147
+ # The main parameters are:
3148
+ #
3149
+ # :type
3150
+ # :format
3151
+ # :criteria
3152
+ # :value
3153
+ # :minimum
3154
+ # :maximum
3155
+ # Other, less commonly used parameters are:
3156
+ #
3157
+ # :min_type
3158
+ # :mid_type
3159
+ # :max_type
3160
+ # :min_value
3161
+ # :mid_value
3162
+ # :max_value
3163
+ # :min_color
3164
+ # :mid_color
3165
+ # :max_color
3166
+ # :bar_color
3167
+ # Additional parameters which are used for specific conditional format types
3168
+ # are shown in the relevant sections below.
3169
+ #
3170
+ # == :type
3171
+ #
3172
+ # This parameter is passed in a hash to conditional_formatting.
3173
+ #
3174
+ # The type parameter is used to set the type of conditional formatting
3175
+ # that you wish to apply. It is always required and it has no default value.
3176
+ # Allowable type values and their associated parameters are:
3177
+ #
3178
+ # Type Parameters
3179
+ # ====== ==========
3180
+ # 'cell' :criteria
3181
+ # :value
3182
+ # :minimum
3183
+ # :maximum
3184
+ #
3185
+ # 'date' :criteria
3186
+ # :value
3187
+ # :minimum
3188
+ # :maximum
3189
+ #
3190
+ # 'time_period' :criteria
3191
+ #
3192
+ # 'text' :criteria
3193
+ # :value
3194
+ #
3195
+ # 'average' :criteria
3196
+ #
3197
+ # 'duplicate' (none)
3198
+ #
3199
+ # 'unique' (none)
3200
+ #
3201
+ # 'top' :criteria
3202
+ # :value
3203
+ #
3204
+ # 'bottom' :criteria
3205
+ # :value
3206
+ #
3207
+ # 'blanks' (none)
3208
+ #
3209
+ # 'no_blanks' (none)
3210
+ #
3211
+ # 'errors' (none)
3212
+ #
3213
+ # 'no_errors' (none)
3214
+ #
3215
+ # '2_color_scale' (none)
3216
+ #
3217
+ # '3_color_scale' (none)
3218
+ #
3219
+ # 'data_bar' (none)
3220
+ #
3221
+ # 'formula' :criteria
3222
+ # All conditional formatting types have a format parameter, see below.
3223
+ # Other types and parameters such as icon sets will be added in time.
3224
+ #
3225
+ # == :type => 'cell'
3226
+ #
3227
+ # This is the most common conditional formatting type. It is used when
3228
+ # a format is applied to a cell based on a simple criterion. For example:
3229
+ #
3230
+ # worksheet.conditional_formatting( 'A1',
3231
+ # {
3232
+ # :type => 'cell',
3233
+ # :criteria => 'greater than',
3234
+ # :value => 5,
3235
+ # :format => red_format
3236
+ # }
3237
+ # )
3238
+ # Or, using the between criteria:
3239
+ #
3240
+ # worksheet.conditional_formatting( 'C1:C4',
3241
+ # {
3242
+ # :type => 'cell',
3243
+ # :criteria => 'between',
3244
+ # :minimum => 20,
3245
+ # :maximum => 30,
3246
+ # :format => green_format
3247
+ # }
3248
+ # )
3249
+ # == :criteria
3250
+ #
3251
+ # The criteria parameter is used to set the criteria by which the cell data
3252
+ # will be evaluated. It has no default value. The most common criteria
3253
+ # as applied to { type => 'cell' } are:
3254
+ #
3255
+ # 'between'
3256
+ # 'not between'
3257
+ # 'equal to' | '==' | '='
3258
+ # 'not equal to' | '!=' | '<>'
3259
+ # 'greater than' | '>'
3260
+ # 'less than' | '<'
3261
+ # 'greater than or equal to' | '>='
3262
+ # 'less than or equal to' | '<='
3263
+ # You can either use Excel's textual description strings,
3264
+ # in the first column above, or the more common symbolic alternatives.
3265
+ #
3266
+ # Additional criteria which are specific to other conditional format types
3267
+ # are shown in the relevant sections below.
3268
+ #
3269
+ # == :value
3270
+ #
3271
+ # The value is generally used along with the criteria parameter to set the
3272
+ # rule by which the cell data will be evaluated.
3273
+ #
3274
+ # :type => 'cell',
3275
+ # :criteria => '>',
3276
+ # :value => 5
3277
+ # :format => format
3278
+ # The value property can also be an cell reference.
3279
+ #
3280
+ # :type => 'cell',
3281
+ # :criteria => '>',
3282
+ # :value => '$C$1',
3283
+ # :format => format
3284
+ # == :format
3285
+ #
3286
+ # The format parameter is used to specify the format that will be applied
3287
+ # to the cell when the conditional formatting criterion is met.
3288
+ # The format is created using the add_format method in the same way as cell
3289
+ # formats:
3290
+ #
3291
+ # format = workbook.add_format( :bold => 1, :italic => 1 )
3292
+ #
3293
+ # worksheet.conditional_formatting( 'A1',
3294
+ # {
3295
+ # :type => 'cell',
3296
+ # :criteria => '>',
3297
+ # :value => 5
3298
+ # :format => format
3299
+ # }
3300
+ # )
3301
+ # The conditional format follows the same rules as in Excel:
3302
+ # it is superimposed over the existing cell format and not all font and
3303
+ # border properties can be modified. Font properties that can't be modified
3304
+ # are font name, font size, superscript and subscript.
3305
+ # The border property that cannot be modified is diagonal borders.
3306
+ #
3307
+ # Excel specifies some default formats to be used with conditional
3308
+ # formatting. You can replicate them using the following write_xlsx formats:
3309
+ #
3310
+ # # Light red fill with dark red text.
3311
+ #
3312
+ # format1 = workbook.add_format(
3313
+ # :bg_color => '#FFC7CE',
3314
+ # :color => '#9C0006'
3315
+ # )
3316
+ #
3317
+ # # Light yellow fill with dark yellow text.
3318
+ #
3319
+ # format2 = workbook.add_format(
3320
+ # :bg_color => '#FFEB9C',
3321
+ # :color => '#9C6500'
3322
+ # )
3323
+ #
3324
+ # # Green fill with dark green text.
3325
+ #
3326
+ # format3 = workbook.add_format(
3327
+ # :bg_color => '#C6EFCE',
3328
+ # :color => '#006100'
3329
+ # )
3330
+ # == :minimum
3331
+ #
3332
+ # The minimum parameter is used to set the lower limiting value when the
3333
+ # criteria is either 'between' or 'not between':
3334
+ #
3335
+ # :validate => 'integer',
3336
+ # :criteria => 'between',
3337
+ # :minimum => 1,
3338
+ # :maximum => 100
3339
+ # == :maximum
3340
+ #
3341
+ # The maximum parameter is used to set the upper limiting value when the
3342
+ # criteria is either 'between' or 'not between'. See the previous example.
3343
+ #
3344
+ # == :type => 'date'
3345
+ #
3346
+ # The date type is the same as the cell type and uses the same criteria
3347
+ # and values. However it allows the value, minimum and maximum properties
3348
+ # to be specified in the ISO8601 yyyy-mm-ddThh:mm:ss.sss date format which
3349
+ # is detailed in the write_date_time() method.
3350
+ #
3351
+ # worksheet.conditional_formatting( 'A1:A4',
3352
+ # {
3353
+ # :type => 'date',
3354
+ # :criteria => 'greater than',
3355
+ # :value => '2011-01-01T',
3356
+ # :format => format
3357
+ # }
3358
+ # )
3359
+ # == :type => 'time_period'
3360
+ #
3361
+ # The time_period type is used to specify Excel's "Dates Occurring" style
3362
+ # conditional format.
3363
+ #
3364
+ # worksheet.conditional_formatting( 'A1:A4',
3365
+ # {
3366
+ # :type => 'time_period',
3367
+ # :criteria => 'yesterday',
3368
+ # :format => format
3369
+ # }
3370
+ # )
3371
+ # The period is set in the criteria and can have one of the following
3372
+ # values:
3373
+ #
3374
+ # :criteria => 'yesterday',
3375
+ # :criteria => 'today',
3376
+ # :criteria => 'last 7 days',
3377
+ # :criteria => 'last week',
3378
+ # :criteria => 'this week',
3379
+ # :criteria => 'next week',
3380
+ # :criteria => 'last month',
3381
+ # :criteria => 'this month',
3382
+ # :criteria => 'next month'
3383
+ # == :type => 'text'
3384
+ #
3385
+ # The text type is used to specify Excel's "Specific Text" style conditional
3386
+ # format. It is used to do simple string matching using the criteria and
3387
+ # value parameters:
3388
+ #
3389
+ # worksheet.conditional_formatting( 'A1:A4',
3390
+ # {
3391
+ # :type => 'text',
3392
+ # :criteria => 'containing',
3393
+ # :value => 'foo',
3394
+ # :format => format
3395
+ # }
3396
+ # )
3397
+ # The criteria can have one of the following values:
3398
+ #
3399
+ # :criteria => 'containing',
3400
+ # :criteria => 'not containing',
3401
+ # :criteria => 'begins with',
3402
+ # :criteria => 'ends with'
3403
+ # The value parameter should be a string or single character.
3404
+ #
3405
+ # == :type => 'average'
3406
+ #
3407
+ # The average type is used to specify Excel's "Average" style conditional
3408
+ # format.
3409
+ #
3410
+ # worksheet.conditional_formatting( 'A1:A4',
3411
+ # {
3412
+ # :type => 'average',
3413
+ # :criteria => 'above',
3414
+ # :format => format
3415
+ # }
3416
+ # )
3417
+ # The type of average for the conditional format range is specified by the
3418
+ # criteria:
3419
+ #
3420
+ # :criteria => 'above',
3421
+ # :criteria => 'below',
3422
+ # :criteria => 'equal or above',
3423
+ # :criteria => 'equal or below',
3424
+ # :criteria => '1 std dev above',
3425
+ # :criteria => '1 std dev below',
3426
+ # :criteria => '2 std dev above',
3427
+ # :criteria => '2 std dev below',
3428
+ # :criteria => '3 std dev above',
3429
+ # :criteria => '3 std dev below'
3430
+ # == :type => 'duplicate'
3431
+ #
3432
+ # The duplicate type is used to highlight duplicate cells in a range:
3433
+ #
3434
+ # worksheet.conditional_formatting( 'A1:A4',
3435
+ # {
3436
+ # :type => 'duplicate',
3437
+ # :format => format
3438
+ # }
3439
+ # )
3440
+ # == :type => 'unique'
3441
+ #
3442
+ # The unique type is used to highlight unique cells in a range:
3443
+ #
3444
+ # worksheet.conditional_formatting( 'A1:A4',
3445
+ # {
3446
+ # :type => 'unique',
3447
+ # :format => format
3448
+ # }
3449
+ # )
3450
+ # == :type => 'top'
3451
+ #
3452
+ # The top type is used to specify the top n values by number or percentage
3453
+ # in a range:
3454
+ #
3455
+ # worksheet.conditional_formatting( 'A1:A4',
3456
+ # {
3457
+ # :type => 'top',
3458
+ # :value => 10,
3459
+ # :format => format
3460
+ # }
3461
+ # )
3462
+ # The criteria can be used to indicate that a percentage condition is
3463
+ # required:
3464
+ #
3465
+ # worksheet.conditional_formatting( 'A1:A4',
3466
+ # {
3467
+ # :type => 'top',
3468
+ # :value => 10,
3469
+ # :criteria => '%',
3470
+ # :format => format
3471
+ # }
3472
+ # )
3473
+ # == :type => 'bottom'
3474
+ #
3475
+ # The bottom type is used to specify the bottom n values by number or
3476
+ # percentage in a range.
3477
+ #
3478
+ # It takes the same parameters as top, see above.
3479
+ #
3480
+ # == :type => 'blanks'
3481
+ #
3482
+ # The blanks type is used to highlight blank cells in a range:
3483
+ #
3484
+ # worksheet.conditional_formatting( 'A1:A4',
3485
+ # {
3486
+ # :type => 'blanks',
3487
+ # :format => format
3488
+ # }
3489
+ # )
3490
+ # == :type => 'no_blanks'
3491
+ #
3492
+ # The no_blanks type is used to highlight non blank cells in a range:
3493
+ #
3494
+ # worksheet.conditional_formatting( 'A1:A4',
3495
+ # {
3496
+ # :type => 'no_blanks',
3497
+ # :format => format
3498
+ # }
3499
+ # )
3500
+ # == :type => 'errors'
3501
+ #
3502
+ # The errors type is used to highlight error cells in a range:
3503
+ #
3504
+ # worksheet.conditional_formatting( 'A1:A4',
3505
+ # {
3506
+ # :type => 'errors',
3507
+ # :format => format
3508
+ # }
3509
+ # )
3510
+ # == :type => 'no_errors'
3511
+ #
3512
+ # The no_errors type is used to highlight non error cells in a range:
3513
+ #
3514
+ # worksheet.conditional_formatting( 'A1:A4',
3515
+ # {
3516
+ # :type => 'no_errors',
3517
+ # :format => format
3518
+ # }
3519
+ # )
3520
+ # == :type => '2_color_scale'
3521
+ #
3522
+ # The 2_color_scale type is used to specify Excel's "2 Color Scale" style
3523
+ # conditional format.
3524
+ #
3525
+ # worksheet.conditional_formatting( 'A1:A12',
3526
+ # {
3527
+ # :type => '2_color_scale'
3528
+ # }
3529
+ # )
3530
+ # At the moment only the default colors and properties can be used. These
3531
+ # will be extended in time.
3532
+ #
3533
+ # == :type => '3_color_scale'
3534
+ #
3535
+ # The 3_color_scale type is used to specify Excel's "3 Color Scale" style
3536
+ # conditional format.
3537
+ #
3538
+ # worksheet.conditional_formatting( 'A1:A12',
3539
+ # {
3540
+ # :type => '3_color_scale'
3541
+ # }
3542
+ # )
3543
+ # At the moment only the default colors and properties can be used.
3544
+ # These will be extended in time.
3545
+ #
3546
+ # == :type => 'data_bar'
3547
+ #
3548
+ # The data_bar type is used to specify Excel's "Data Bar" style conditional
3549
+ # format.
3550
+ #
3551
+ # worksheet.conditional_formatting( 'A1:A12',
3552
+ # {
3553
+ # :type => 'data_bar',
3554
+ # }
3555
+ # )
3556
+ # At the moment only the default colors and properties can be used. These
3557
+ # will be extended in time.
3558
+ #
3559
+ # == :type => 'formula'
3560
+ #
3561
+ # The formula type is used to specify a conditional format based on
3562
+ # a user defined formula:
3563
+ #
3564
+ # worksheet.conditional_formatting( 'A1:A4',
3565
+ # {
3566
+ # :type => 'formula',
3567
+ # :criteria => '=$A$1 > 5',
3568
+ # :format => format
3569
+ # }
3570
+ # )
3571
+ # The formula is specified in the criteria.
3572
+ #
3573
+ # == :min_type, :mid_type, :max_type
3574
+ #
3575
+ # The min_type and max_type properties are available when the conditional
3576
+ # formatting type is 2_color_scale, 3_color_scale or data_bar. The mid_type
3577
+ # is available for 3_color_scale. The properties are used as follows:
3578
+ #
3579
+ # worksheet.conditional_formatting( 'A1:A12',
3580
+ # {
3581
+ # :type => '2_color_scale',
3582
+ # :min_type => 'percent',
3583
+ # :max_type => 'percent'
3584
+ # }
3585
+ # )
3586
+ # The available min/mid/max types are:
3587
+ #
3588
+ # 'num'
3589
+ # 'percent'
3590
+ # 'percentile'
3591
+ # 'formula'
3592
+ # == :min_value, :mid_value, :max_value
3593
+ #
3594
+ # The min_value and max_value properties are available when the conditional
3595
+ # formatting type is 2_color_scale, 3_color_scale or data_bar. The mid_value
3596
+ # is available for 3_color_scale. The properties are used as follows:
3597
+ #
3598
+ # worksheet.conditional_formatting( 'A1:A12',
3599
+ # {
3600
+ # :type => '2_color_scale',
3601
+ # :min_value => 10,
3602
+ # :max_value => 90
3603
+ # }
3604
+ # )
3605
+ # == :min_color, :mid_color, :max_color, :bar_color
3606
+ #
3607
+ # The min_color and max_color properties are available when the conditional
3608
+ # formatting type is 2_color_scale, 3_color_scale or data_bar. The mid_color
3609
+ # is available for 3_color_scale. The properties are used as follows:
3610
+ #
3611
+ # worksheet.conditional_formatting( 'A1:A12',
3612
+ # {
3613
+ # ;type => '2_color_scale',
3614
+ # :min_color => "#C5D9F1",
3615
+ # :max_color => "#538ED5"
3616
+ # }
3617
+ # )
3618
+ # The color can be specifies as an Excel::Writer::XLSX color index or,
3619
+ # more usefully, as a HTML style RGB hex number, as shown above.
3620
+ #
3621
+ # == Conditional Formatting Examples
3622
+ #
3623
+ # === Example 1. Highlight cells greater than an integer value.
3624
+ #
3625
+ # worksheet.conditional_formatting( 'A1:F10',
3626
+ # {
3627
+ # :type => 'cell',
3628
+ # :criteria => 'greater than',
3629
+ # :value => 5,
3630
+ # :format => format
3631
+ # }
3632
+ # )
3633
+ # === Example 2. Highlight cells greater than a value in a reference cell.
3634
+ #
3635
+ # worksheet.conditional_formatting( 'A1:F10',
3636
+ # {
3637
+ # :type => 'cell',
3638
+ # :criteria => 'greater than',
3639
+ # :value => '$H$1',
3640
+ # :format => format
3641
+ # }
3642
+ # )
3643
+ # === Example 3. Highlight cells greater than a certain date:
3644
+ #
3645
+ # worksheet.conditional_formatting( 'A1:F10',
3646
+ # {
3647
+ # :type => 'date',
3648
+ # :criteria => 'greater than',
3649
+ # :value => '2011-01-01T',
3650
+ # :format => format
3651
+ # }
3652
+ # )
3653
+ # === Example 4. Highlight cells with a date in the last seven days:
3654
+ #
3655
+ # worksheet.conditional_formatting( 'A1:F10',
3656
+ # {
3657
+ # :type => 'time_period',
3658
+ # :criteria => 'last 7 days',
3659
+ # :format => format
3660
+ # }
3661
+ # )
3662
+ # === Example 5. Highlight cells with strings starting with the letter b:
3663
+ #
3664
+ # worksheet.conditional_formatting( 'A1:F10',
3665
+ # {
3666
+ # :type => 'text',
3667
+ # :criteria => 'begins with',
3668
+ # :value => 'b',
3669
+ # :format => format
3670
+ # }
3671
+ # )
3672
+ # === Example 6. Highlight cells that are 1 std deviation above the average for the range:
3673
+ #
3674
+ # worksheet.conditional_formatting( 'A1:F10',
3675
+ # {
3676
+ # :type => 'average',
3677
+ # :format => format
3678
+ # }
3679
+ # )
3680
+ # === Example 7. Highlight duplicate cells in a range:
3681
+ #
3682
+ # worksheet.conditional_formatting( 'A1:F10',
3683
+ # {
3684
+ # :type => 'duplicate',
3685
+ # :format => format
3686
+ # }
3687
+ # )
3688
+ # === Example 8. Highlight unique cells in a range.
3689
+ #
3690
+ # worksheet.conditional_formatting( 'A1:F10',
3691
+ # {
3692
+ # :type => 'unique',
3693
+ # :format => format
3694
+ # }
3695
+ # )
3696
+ # === Example 9. Highlight the top 10 cells.
3245
3697
  #
3246
- # See also the conditional_format.rb program in the examples directory of the distro
3698
+ # worksheet.conditional_formatting( 'A1:F10',
3699
+ # {
3700
+ # :type => 'top',
3701
+ # :value => 10,
3702
+ # :format => format
3703
+ # }
3704
+ # )
3705
+ # === Example 10. Highlight blank cells.
3706
+ #
3707
+ # worksheet.conditional_formatting( 'A1:F10',
3708
+ # {
3709
+ # :type => 'blanks',
3710
+ # :format => format
3711
+ # }
3712
+ # )
3713
+ # See also the conditional_format.rb example program in EXAMPLES.
3247
3714
  #
3248
3715
  def conditional_formatting(*args)
3249
- # Check for a cell reference in A1 notation and substitute row and column
3250
- if args[0] =~ /^\D/
3251
- # Check for a user defined multiple range like B3:K6,B8:K11.
3252
- user_range = args[0].gsub(/\s*,\s*/, ' ').gsub(/\$/, '') if args[0] =~ /,/
3253
- end
3254
- row1, col1, row2, col2, param = row_col_notation(args)
3255
- if row2.respond_to?(:keys)
3256
- param = row2
3257
- row2, col2 = row1, col1
3258
- end
3259
- raise WriteXLSXInsufficientArgumentError if [row1, col1, row2, col2, param].include?(nil)
3260
-
3261
- # Check that row and col are valid without storing the values.
3262
- check_dimensions(row1, col1)
3263
- check_dimensions(row2, col2)
3264
- check_conditional_formatting_parameters(param)
3265
-
3266
- # Swap last row/col for first row/col as necessary
3267
- row1, row2 = row2, row1 if row1 > row2
3268
- col1, col2 = col2, col1 if col1 > col2
3269
-
3270
- # If the first and last cell are the same write a single cell.
3271
- if row1 == row2 && col1 == col2
3272
- range = xl_rowcol_to_cell(row1, col1)
3273
- start_cell = range
3274
- else
3275
- range = xl_range(row1, row2, col1, col2)
3276
- start_cell = xl_rowcol_to_cell(row1, col1)
3277
- end
3278
-
3279
- # Override with user defined multiple range if provided.
3280
- range = user_range if user_range
3281
-
3282
- param[:format] = param[:format].get_dxf_index if param[:format]
3283
- param[:priority] = @dxf_priority
3284
- @dxf_priority += 1
3285
-
3286
- # Special handling of text criteria.
3287
- if param[:type] == 'text'
3288
- case param[:criteria]
3289
- when 'containsText'
3290
- param[:type] = 'containsText';
3291
- param[:formula] = %Q!NOT(ISERROR(SEARCH("#{param[:value]}",#{start_cell})))!
3292
- when 'notContains'
3293
- param[:type] = 'notContainsText';
3294
- param[:formula] = %Q!ISERROR(SEARCH("#{param[:value]}",#{start_cell}))!
3295
- when 'beginsWith'
3296
- param[:type] = 'beginsWith'
3297
- param[:formula] =
3298
- %Q!LEFT(#{start_cell},#{param[:value].size})="#{param[:value]}"!
3299
- when 'endsWith'
3300
- param[:type] = 'endsWith'
3301
- param[:formula] =
3302
- %Q!RIGHT(#{start_cell},#{param[:value].size})="#{param[:value]}"!
3303
- else
3304
- raise "Invalid text criteria '#{param[:criteria]} in conditional_formatting()"
3305
- end
3306
- end
3307
-
3308
- # Special handling of time time_period criteria.
3309
- if param[:type] == 'timePeriod'
3310
- case param[:criteria]
3311
- when 'yesterday'
3312
- param[:formula] = "FLOOR(#{start_cell},1)=TODAY()-1"
3313
- when 'today'
3314
- param[:formula] = "FLOOR(#{start_cell},1)=TODAY()"
3315
- when 'tomorrow'
3316
- param[:formula] = "FLOOR(#{start_cell},1)=TODAY()+1"
3317
- when 'last7Days'
3318
- param[:formula] =
3319
- "AND(TODAY()-FLOOR(#{start_cell},1)<=6,FLOOR(#{start_cell},1)<=TODAY())"
3320
- when 'lastWeek'
3321
- param[:formula] =
3322
- "AND(TODAY()-ROUNDDOWN(#{start_cell},0)>=(WEEKDAY(TODAY())),TODAY()-ROUNDDOWN(#{start_cell},0)<(WEEKDAY(TODAY())+7))"
3323
- when 'thisWeek'
3324
- param[:formula] =
3325
- "AND(TODAY()-ROUNDDOWN(#{start_cell},0)<=WEEKDAY(TODAY())-1,ROUNDDOWN(#{start_cell},0)-TODAY()<=7-WEEKDAY(TODAY()))"
3326
- when 'nextWeek'
3327
- param[:formula] =
3328
- "AND(ROUNDDOWN(#{start_cell},0)-TODAY()>(7-WEEKDAY(TODAY())),ROUNDDOWN(#{start_cell},0)-TODAY()<(15-WEEKDAY(TODAY())))"
3329
- when 'lastMonth'
3330
- param[:formula] =
3331
- "AND(MONTH(#{start_cell})=MONTH(TODAY())-1,OR(YEAR(#{start_cell})=YEAR(TODAY()),AND(MONTH(#{start_cell})=1,YEAR(A1)=YEAR(TODAY())-1)))"
3332
- when 'thisMonth'
3333
- param[:formula] =
3334
- "AND(MONTH(#{start_cell})=MONTH(TODAY()),YEAR(#{start_cell})=YEAR(TODAY()))"
3335
- when 'nextMonth'
3336
- param[:formula] =
3337
- "AND(MONTH(#{start_cell})=MONTH(TODAY())+1,OR(YEAR(#{start_cell})=YEAR(TODAY()),AND(MONTH(#{start_cell})=12,YEAR(#{start_cell})=YEAR(TODAY())+1)))"
3338
- else
3339
- raise "Invalid time_period criteria '#{param[:criteria]}' in conditional_formatting()"
3340
- end
3341
- end
3342
-
3343
- # Special handling of blanks/error types.
3344
- case param[:type]
3345
- when 'containsBlanks'
3346
- param[:formula] = "LEN(TRIM(#{start_cell}))=0"
3347
- when 'notContainsBlanks'
3348
- param[:formula] = "LEN(TRIM(#{start_cell}))>0"
3349
- when 'containsErrors'
3350
- param[:formula] = "ISERROR(#{start_cell})"
3351
- when 'notContainsErrors'
3352
- param[:formula] = "NOT(ISERROR(#{start_cell}))"
3353
- when '2_color_scale'
3354
- param[:type] = 'colorScale'
3355
-
3356
- # Color scales don't use any additional formatting.
3357
- param[:format] = nil
3358
-
3359
- # Turn off 3 color parameters.
3360
- param[:mid_type] = nil
3361
- param[:mid_color] = nil
3362
-
3363
- param[:min_type] ||= 'min'
3364
- param[:max_type] ||= 'max'
3365
- param[:min_value] ||= 0
3366
- param[:max_value] ||= 0
3367
- param[:min_color] ||= '#FF7128'
3368
- param[:max_color] ||= '#FFEF9C'
3369
-
3370
- param[:max_color] = get_palette_color( param[:max_color] )
3371
- param[:min_color] = get_palette_color( param[:min_color] )
3372
- when '3_color_scale'
3373
- param[:type] = 'colorScale'
3374
-
3375
- # Color scales don't use any additional formatting.
3376
- param[:format] = nil
3377
-
3378
- param[:min_type] ||= 'min'
3379
- param[:mid_type] ||= 'percentile'
3380
- param[:max_type] ||= 'max'
3381
- param[:min_value] ||= 0
3382
- param[:mid_value] ||= 50
3383
- param[:max_value] ||= 0
3384
- param[:min_color] ||= '#F8696B'
3385
- param[:mid_color] ||= '#FFEB84'
3386
- param[:max_color] ||= '#63BE7B'
3387
-
3388
- param[:max_color] = get_palette_color(param[:max_color])
3389
- param[:mid_color] = get_palette_color(param[:mid_color])
3390
- param[:min_color] = get_palette_color(param[:min_color])
3391
- when 'dataBar'
3392
- # Color scales don't use any additional formatting.
3393
- param[:format] = nil
3394
-
3395
- param[:min_type] ||= 'min'
3396
- param[:max_type] ||= 'max'
3397
- param[:min_value] ||= 0
3398
- param[:max_value] ||= 0
3399
- param[:bar_color] ||= '#638EC6'
3400
-
3401
- param[:bar_color] = get_palette_color(param[:bar_color])
3402
- end
3403
-
3404
- # Store the validation information until we close the worksheet.
3405
- @cond_formats[range] ||= []
3406
- @cond_formats[range] << param
3716
+ cond_format = Package::ConditionalFormat.factory(self, *args)
3717
+ @cond_formats[cond_format.range] ||= []
3718
+ @cond_formats[cond_format.range] << cond_format
3407
3719
  end
3408
3720
 
3409
3721
  #
@@ -3420,218 +3732,37 @@ module Writexlsx
3420
3732
  # See also the tables.rb program in the examples directory of the distro
3421
3733
  #
3422
3734
  def add_table(*args)
3423
- col_formats = []
3424
- =begin
3425
- # We would need to order the write statements very carefully within this
3426
- # function to support optimisation mode. Disable add_table() when it is
3427
- # on for now.
3428
- if @optimization
3429
- carp "add_table() isn't supported when set_optimization() is on"
3430
- return -1
3431
- end
3432
- =end
3433
- # Check for a cell reference in A1 notation and substitute row and column
3434
- row1, col1, row2, col2, param = row_col_notation(args)
3435
-
3436
- # Check for a valid number of args.
3437
- raise "Not enough parameters to add_table()" if [row1, col1, row2, col2].include?(nil)
3438
-
3439
- # Check that row and col are valid without storing the values.
3440
- check_dimensions_and_update_max_min_values(row1, col1, 1, 1)
3441
- check_dimensions_and_update_max_min_values(row2, col2, 1, 1)
3442
-
3443
- # The final hashref contains the validation parameters.
3444
- param ||= {}
3445
-
3446
- check_parameter(param, valid_table_parameter, 'add_table')
3447
-
3448
3735
  # Table count is a member of Workbook, global to all Worksheet.
3449
3736
  @workbook.table_count += 1
3450
- table = {}
3451
- table[:_columns] = []
3452
- table[:id] = @workbook.table_count
3453
-
3454
- # Turn on Excel's defaults.
3455
- param[:banded_rows] ||= 1
3456
- param[:header_row] ||= 1
3457
- param[:autofilter] ||= 1
3458
-
3459
- # Set the table options.
3460
- table[:_show_first_col] = ptrue?(param[:first_column]) ? 1 : 0
3461
- table[:_show_last_col] = ptrue?(param[:last_column]) ? 1 : 0
3462
- table[:_show_row_stripes] = ptrue?(param[:banded_rows]) ? 1 : 0
3463
- table[:_show_col_stripes] = ptrue?(param[:banded_columns]) ? 1 : 0
3464
- table[:_header_row_count] = ptrue?(param[:header_row]) ? 1 : 0
3465
- table[:_totals_row_shown] = ptrue?(param[:total_row]) ? 1 : 0
3466
-
3467
- # Set the table name.
3468
- if param[:name]
3469
- table[:_name] = param[:name]
3470
- else
3471
- # Set a default name.
3472
- table[:_name] = "Table#{table[:id]}"
3473
- end
3474
-
3475
- # Set the table style.
3476
- if param[:style]
3477
- table[:_style] = param[:style]
3478
- # Remove whitespace from style name.
3479
- table[:_style].gsub!(/\s/, '')
3480
- else
3481
- table[:_style] = "TableStyleMedium9"
3482
- end
3483
-
3484
- # Swap last row/col for first row/col as necessary.
3485
- row1, row2 = row2, row1 if row1 > row2
3486
- col1, col2 = col2, col1 if col1 > col2
3737
+ table = Package::Table.new(self, @workbook.table_count, *args)
3487
3738
 
3488
- # Set the data range rows (without the header and footer).
3489
- first_data_row = row1
3490
- last_data_row = row2
3491
- first_data_row += 1 if param[:header_row] != 0
3492
- last_data_row -= 1 if param[:total_row]
3493
-
3494
- # Set the table and autofilter ranges.
3495
- table[:_range] = xl_range(row1, row2, col1, col2)
3496
- table[:_a_range] = xl_range(row1, last_data_row, col1, col2)
3497
-
3498
- # If the header row if off the default is to turn autofilter off.
3499
- param[:autofilter] = 0 if param[:header_row] == 0
3500
-
3501
- # Set the autofilter range.
3502
- if param[:autofilter] && param[:autofilter] != 0
3503
- table[:_autofilter] = table[:_a_range]
3504
- end
3505
-
3506
- # Add the table columns.
3507
- col_id = 1
3508
- (col1..col2).each do |col_num|
3509
- # Set up the default column data.
3510
- col_data = {
3511
- :_id => col_id,
3512
- :_name => "Column#{col_id}",
3513
- :_total_string => '',
3514
- :_total_function => '',
3515
- :_formula => '',
3516
- :_format => nil
3517
- }
3518
-
3519
- # Overwrite the defaults with any use defined values.
3520
- if param[:columns]
3521
- # Check if there are user defined values for this column.
3522
- if user_data = param[:columns][col_id - 1]
3523
- # Map user defined values to internal values.
3524
- if user_data[:header] && !user_data[:header].empty?
3525
- col_data[:_name] = user_data[:header]
3526
- end
3527
- # Handle the column formula.
3528
- if user_data[:formula]
3529
- formula = user_data[:formula]
3530
- # Remove the leading = from formula.
3531
- formula.sub!(/^=/, '')
3532
- # Covert Excel 2010 "@" ref to 2007 "#This Row".
3533
- formula.gsub!(/@/,'[#This Row],')
3534
-
3535
- col_data[:_formula] = formula
3536
-
3537
- (first_data_row..last_data_row).each do |row|
3538
- write_formula(row, col_num, formula, user_data[:format])
3539
- end
3540
- end
3541
-
3542
- # Handle the function for the total row.
3543
- if user_data[:total_function]
3544
- function = user_data[:total_function]
3545
-
3546
- # Massage the function name.
3547
- function = function.downcase
3548
- function.gsub!(/_/, '')
3549
- function.gsub!(/\s/,'')
3550
-
3551
- function = 'countNums' if function == 'countnums'
3552
- function = 'stdDev' if function == 'stddev'
3553
-
3554
- col_data[:_total_function] = function
3555
-
3556
- formula = table_function_to_formula(function, col_data[:_name])
3557
- write_formula(row2, col_num, formula, user_data[:format])
3558
- elsif user_data[:total_string]
3559
- # Total label only (not a function).
3560
- total_string = user_data[:total_string]
3561
- col_data[:_total_string] = total_string
3562
-
3563
- write_string(row2, col_num, total_string, user_data[:format])
3564
- end
3565
-
3566
- # Get the dxf format index.
3567
- if user_data[:format]
3568
- col_data[:_format] = user_data[:format].get_dxf_index
3569
- end
3570
-
3571
- # Store the column format for writing the cell data.
3572
- # It doesn't matter if it is undefined.
3573
- col_formats[col_id - 1] = user_data[:format]
3574
- end
3575
- end
3576
-
3577
- # Store the column data.
3578
- table[:_columns] << col_data
3579
-
3580
- # Write the column headers to the worksheet.
3581
- if param[:header_row] != 0
3582
- write_string(row1, col_num, col_data[:_name])
3583
- end
3584
-
3585
- col_id += 1
3586
- end # Table columns.
3587
-
3588
- # Write the cell data if supplied.
3589
- if data = param[:data]
3590
-
3591
- i = 0 # For indexing the row data.
3592
- (first_data_row..last_data_row).each do |row|
3593
- next unless data[i]
3594
-
3595
- j = 0 # For indexing the col data.
3596
- (col1..col2).each do |col|
3597
- token = data[i][j]
3598
- write(row, col, token, col_formats[j]) if token
3599
- j += 1
3600
- end
3601
- i += 1
3602
- end
3603
- end
3604
-
3605
- # Store the table data.
3739
+ @external_table_links << ['/table', "../tables/table#{table.id}.xml"]
3606
3740
  @tables << table
3607
-
3608
- # Store the link used for the rels file.
3609
- @external_table_links << ['/table', "../tables/table#{table[:id]}.xml"]
3610
-
3611
- return table
3741
+ table
3612
3742
  end
3613
3743
 
3614
- # List of valid input parameters.
3615
- def valid_table_parameter
3616
- [
3617
- :autofilter,
3618
- :banded_columns,
3619
- :banded_rows,
3620
- :columns,
3621
- :data,
3622
- :first_column,
3623
- :header_row,
3624
- :last_column,
3625
- :name,
3626
- :style,
3627
- :total_row
3628
- ]
3629
- end
3630
- private :valid_table_parameter
3631
-
3632
3744
  #
3633
3745
  # Add sparklines to the worksheet.
3634
3746
  #
3747
+ # The add_sparkline worksheet method is used to add sparklines to a cell or a range of cells.
3748
+ #
3749
+ # worksheet.add_sparkline(
3750
+ # {
3751
+ # :location => 'F2',
3752
+ # :range => 'Sheet1!A2:E2',
3753
+ # :type => 'column',
3754
+ # :style => 12
3755
+ # }
3756
+ # )
3757
+ #
3758
+ # See also the sparklines1.rb and sparklines2.rb example programs in the examples directory of the distro.
3759
+ #
3760
+ # Note: Sparklines are a feature of Excel 2010+ only.
3761
+ # You can write them to an XLSX file that can be read by Excel 2007 but they won't be displayed.
3762
+ #
3763
+ # Sparklines are a feature of Excel 2010+ which allows you to add small charts to worksheet cells.
3764
+ # These are useful for showing visual trends in data in a compact format.
3765
+ #
3635
3766
  def add_sparkline(param)
3636
3767
  sparkline = {}
3637
3768
 
@@ -4840,29 +4971,31 @@ module Writexlsx
4840
4971
  @writer.data_element('f', formula, attributes)
4841
4972
  end
4842
4973
 
4843
- private
4974
+ def date_1904? #:nodoc:
4975
+ @workbook.date_1904?
4976
+ end
4844
4977
 
4845
4978
  #
4846
- # Convert a table total function to a worksheet formula.
4979
+ # Convert from an Excel internal colour index to a XML style #RRGGBB index
4980
+ # based on the default or user defined values in the Workbook palette.
4847
4981
  #
4848
- def table_function_to_formula(function, col_name)
4849
- subtotals = {
4850
- :average => 101,
4851
- :countNums => 102,
4852
- :count => 103,
4853
- :max => 104,
4854
- :min => 105,
4855
- :stdDev => 107,
4856
- :sum => 109,
4857
- :var => 110
4858
- }
4859
-
4860
- unless func_num = subtotals[function.to_sym]
4861
- raise "Unsupported function '#{function}' in add_table()"
4982
+ def get_palette_color(index) #:nodoc:
4983
+ if index =~ /^#([0-9A-F]{6})$/i
4984
+ return "FF#{$~[1]}"
4862
4985
  end
4863
- "SUBTOTAL(#{func_num},[#{col_name}])"
4986
+
4987
+ # Adjust the colour index.
4988
+ index -= 8
4989
+
4990
+ # Palette is passed in from the Workbook class.
4991
+ rgb = @workbook.palette[index]
4992
+
4993
+ # TODO Add the alpha part to the RGB.
4994
+ sprintf("FF%02X%02X%02X", *rgb[0, 3])
4864
4995
  end
4865
4996
 
4997
+ private
4998
+
4866
4999
  def check_for_valid_input_params(param)
4867
5000
  check_parameter(param, valid_validation_parameter, 'data_validation')
4868
5001
 
@@ -4999,139 +5132,6 @@ module Writexlsx
4999
5132
  [fragments, length]
5000
5133
  end
5001
5134
 
5002
- def check_conditional_formatting_parameters(param) # :nodoc:
5003
- # Check for valid input parameters.
5004
- unless (param.keys.uniq - valid_parameter_for_conditional_formatting).empty? &&
5005
- param.has_key?(:type) &&
5006
- valid_type_for_conditional_formatting.has_key?(param[:type].downcase)
5007
- raise WriteXLSXOptionParameterError, "Invalid type : #{param[:type]}"
5008
- end
5009
-
5010
- param[:direction] = 'bottom' if param[:type] == 'bottom'
5011
- param[:type] = valid_type_for_conditional_formatting[param[:type].downcase]
5012
-
5013
- # Check for valid criteria types.
5014
- if param.has_key?(:criteria) && valid_criteria_type_for_conditional_formatting.has_key?(param[:criteria].downcase)
5015
- param[:criteria] = valid_criteria_type_for_conditional_formatting[param[:criteria].downcase]
5016
- end
5017
-
5018
- # Convert date/times value if required.
5019
- if %w[date time cellIs].include?(param[:type])
5020
- param[:type] = 'cellIs'
5021
-
5022
- param[:value] = convert_date_time_if_required(param[:value])
5023
- param[:minimum] = convert_date_time_if_required(param[:minimum])
5024
- param[:maximum] = convert_date_time_if_required(param[:maximum])
5025
- end
5026
-
5027
- # 'Between' and 'Not between' criteria require 2 values.
5028
- if param[:criteria] == 'between' || param[:criteria] == 'notBetween'
5029
- unless param.has_key?(:minimum) || param.has_key?(:maximum)
5030
- raise WriteXLSXOptionParameterError, "Invalid criteria : #{param[:criteria]}"
5031
- end
5032
- else
5033
- param[:minimum] = nil
5034
- param[:maximum] = nil
5035
- end
5036
-
5037
- # Convert date/times value if required.
5038
- if param[:type] == 'date' || param[:type] == 'time'
5039
- unless convert_date_time_value(param, :value) || convert_date_time_value(param, :maximum)
5040
- raise WriteXLSXOptionParameterError
5041
- end
5042
- end
5043
- end
5044
-
5045
- def convert_date_time_if_required(val)
5046
- if val =~ /T/
5047
- date_time = convert_date_time(val)
5048
- raise "Invalid date/time value '#{val}' in conditional_formatting()" unless date_time
5049
- date_time
5050
- else
5051
- val
5052
- end
5053
- end
5054
-
5055
- # List of valid input parameters for conditional_formatting.
5056
- def valid_parameter_for_conditional_formatting
5057
- [
5058
- :type,
5059
- :format,
5060
- :criteria,
5061
- :value,
5062
- :minimum,
5063
- :maximum,
5064
- :min_type,
5065
- :mid_type,
5066
- :max_type,
5067
- :min_value,
5068
- :mid_value,
5069
- :max_value,
5070
- :min_color,
5071
- :mid_color,
5072
- :max_color,
5073
- :bar_color
5074
- ]
5075
- end
5076
-
5077
- # List of valid validation types for conditional_formatting.
5078
- def valid_type_for_conditional_formatting
5079
- {
5080
- 'cell' => 'cellIs',
5081
- 'date' => 'date',
5082
- 'time' => 'time',
5083
- 'average' => 'aboveAverage',
5084
- 'duplicate' => 'duplicateValues',
5085
- 'unique' => 'uniqueValues',
5086
- 'top' => 'top10',
5087
- 'bottom' => 'top10',
5088
- 'text' => 'text',
5089
- 'time_period' => 'timePeriod',
5090
- 'blanks' => 'containsBlanks',
5091
- 'no_blanks' => 'notContainsBlanks',
5092
- 'errors' => 'containsErrors',
5093
- 'no_errors' => 'notContainsErrors',
5094
- '2_color_scale' => '2_color_scale',
5095
- '3_color_scale' => '3_color_scale',
5096
- 'data_bar' => 'dataBar',
5097
- 'formula' => 'expression'
5098
- }
5099
- end
5100
-
5101
- # List of valid criteria types for conditional_formatting.
5102
- def valid_criteria_type_for_conditional_formatting
5103
- {
5104
- 'between' => 'between',
5105
- 'not between' => 'notBetween',
5106
- 'equal to' => 'equal',
5107
- '=' => 'equal',
5108
- '==' => 'equal',
5109
- 'not equal to' => 'notEqual',
5110
- '!=' => 'notEqual',
5111
- '<>' => 'notEqual',
5112
- 'greater than' => 'greaterThan',
5113
- '>' => 'greaterThan',
5114
- 'less than' => 'lessThan',
5115
- '<' => 'lessThan',
5116
- 'greater than or equal to' => 'greaterThanOrEqual',
5117
- '>=' => 'greaterThanOrEqual',
5118
- 'less than or equal to' => 'lessThanOrEqual',
5119
- '<=' => 'lessThanOrEqual',
5120
- 'containing' => 'containsText',
5121
- 'not containing' => 'notContains',
5122
- 'begins with' => 'beginsWith',
5123
- 'ends with' => 'endsWith',
5124
- 'yesterday' => 'yesterday',
5125
- 'today' => 'today',
5126
- 'last 7 days' => 'last7Days',
5127
- 'last week' => 'lastWeek',
5128
- 'this week' => 'thisWeek',
5129
- 'next week' => 'nextWeek',
5130
- 'last month' => 'lastMonth',
5131
- 'this month' => 'thisMonth',
5132
- 'next month' => 'nextMonth'
5133
- }
5134
- end
5135
5135
  # Pad out the rest of the area with formatted blank cells.
5136
5136
  def write_formatted_blank_to_area(row_first, row_last, col_first, col_last, format)
5137
5137
  (row_first .. row_last).each do |row|
@@ -5301,25 +5301,6 @@ module Writexlsx
5301
5301
  [operator, token]
5302
5302
  end
5303
5303
 
5304
- #
5305
- # Convert from an Excel internal colour index to a XML style #RRGGBB index
5306
- # based on the default or user defined values in the Workbook palette.
5307
- #
5308
- def get_palette_color(index) #:nodoc:
5309
- if index =~ /^#([0-9A-F]{6})$/i
5310
- return "FF#{$~[1]}"
5311
- end
5312
-
5313
- # Adjust the colour index.
5314
- index -= 8
5315
-
5316
- # Palette is passed in from the Workbook class.
5317
- rgb = @workbook.palette[index]
5318
-
5319
- # TODO Add the alpha part to the RGB.
5320
- sprintf("FF%02X%02X%02X", *rgb[0, 3])
5321
- end
5322
-
5323
5304
  #
5324
5305
  # This is an internal method that is used to filter elements of the array of
5325
5306
  # pagebreaks used in the _store_hbreak() and _store_vbreak() methods. It:
@@ -6764,15 +6745,6 @@ module Writexlsx
6764
6745
  writer.empty_tag('vertAlign', attributes)
6765
6746
  end
6766
6747
 
6767
- #
6768
- # Write the <color> element.
6769
- #
6770
- def write_color(writer, name, value) #:nodoc:
6771
- attributes = [name, value]
6772
-
6773
- writer.empty_tag('color', attributes)
6774
- end
6775
-
6776
6748
  #
6777
6749
  # Write the <tableParts> element.
6778
6750
  #
@@ -7313,36 +7285,30 @@ module Writexlsx
7313
7285
  # maxAxisType="custom"
7314
7286
  # rightToLeft="1">
7315
7287
  #
7316
- def write_sparkline_group(opts) # :nodoc:
7317
- empty = opts[:_empty]
7318
- user_max = 0
7319
- user_min = 0
7320
- a = []
7288
+ def write_sparkline_group(sparkline) # :nodoc:
7289
+ @writer.start_tag(
7290
+ 'x14:sparklineGroup',
7291
+ attributes_from_sparkline(sparkline)
7292
+ )
7293
+ end
7321
7294
 
7322
- if opts[:_max]
7323
- if opts[:_max] == 'group'
7324
- opts[:_cust_max] = 'group'
7325
- else
7326
- a << 'manualMax' << opts[:_max]
7327
- opts[:_cust_max] = 'custom'
7328
- end
7329
- end
7295
+ def attributes_from_sparkline(opts) # :nodoc:
7296
+ opts[:_cust_max] = cust_max_min(opts[:_max]) if opts[:_max]
7297
+ opts[:_cust_min] = cust_max_min(opts[:_min]) if opts[:_min]
7330
7298
 
7331
- if opts[:_min]
7332
- if opts[:_min] == 'group'
7333
- opts[:_cust_min] = 'group'
7334
- else
7335
- a << 'manualMin' << opts[:_min]
7336
- opts[:_cust_min] = 'custom'
7337
- end
7338
- end
7299
+ opts[:_cust_max] = cust_max_min(opts[:_max]) if opts[:_max]
7300
+ opts[:_cust_min] = cust_max_min(opts[:_min]) if opts[:_min]
7301
+
7302
+ a = []
7303
+ a << 'manualMax' << opts[:_max] if opts[:_max] && opts[:_max] != 'group'
7304
+ a << 'manualMin' << opts[:_min] if opts[:_min] && opts[:_min] != 'group'
7339
7305
 
7340
7306
  # Ignore the default type attribute (line).
7341
7307
  a << 'type' << opts[:_type] if opts[:_type] != 'line'
7342
7308
 
7343
7309
  a << 'lineWeight' << opts[:_weight] if opts[:_weight]
7344
7310
  a << 'dateAxis' << 1 if opts[:_date_axis]
7345
- a << 'displayEmptyCellsAs' << empty if ptrue?(empty)
7311
+ a << 'displayEmptyCellsAs' << opts[:_empty] if ptrue?(opts[:_empty])
7346
7312
 
7347
7313
  a << 'markers' << 1 if opts[:_markers]
7348
7314
  a << 'high' << 1 if opts[:_high]
@@ -7355,8 +7321,11 @@ module Writexlsx
7355
7321
  a << 'minAxisType' << opts[:_cust_min] if opts[:_cust_min]
7356
7322
  a << 'maxAxisType' << opts[:_cust_max] if opts[:_cust_max]
7357
7323
  a << 'rightToLeft' << 1 if opts[:_reverse]
7324
+ a
7325
+ end
7358
7326
 
7359
- @writer.start_tag('x14:sparklineGroup', a)
7327
+ def cust_max_min(max_min) # :nodoc:
7328
+ max_min == 'group' ? 'group' : 'custom'
7360
7329
  end
7361
7330
 
7362
7331
  #
@@ -7514,153 +7483,23 @@ module Writexlsx
7514
7483
  @writer.data_element('formula2', formula)
7515
7484
  end
7516
7485
 
7517
- # in Perl module : _write_formula()
7518
- #
7519
- def write_formula_tag(data) #:nodoc:
7520
- data = data.sub(/^=/, '') if data.respond_to?(:sub)
7521
- @writer.data_element('formula', data)
7522
- end
7523
-
7524
- #
7525
- # Write the <colorScale> element.
7526
- #
7527
- def write_color_scale(param)
7528
- @writer.tag_elements('colorScale') do
7529
- write_cfvo(param[:min_type], param[:min_value])
7530
- write_cfvo(param[:mid_type], param[:mid_value]) if param[:mid_type]
7531
- write_cfvo(param[:max_type], param[:max_value])
7532
- write_color(@writer, 'rgb', param[:min_color])
7533
- write_color(@writer, 'rgb', param[:mid_color]) if param[:mid_color]
7534
- write_color(@writer, 'rgb', param[:max_color])
7535
- end
7536
- end
7537
-
7538
- #
7539
- # Write the <dataBar> element.
7540
- #
7541
- def write_data_bar(param)
7542
- @writer.tag_elements('dataBar') do
7543
- write_cfvo(param[:min_type], param[:min_value])
7544
- write_cfvo(param[:max_type], param[:max_value])
7545
-
7546
- write_color(@writer, 'rgb', param[:bar_color])
7547
- end
7548
- end
7549
-
7550
- #
7551
- # Write the <cfvo> element.
7552
- #
7553
- def write_cfvo(type, val)
7554
- attributes = [
7555
- 'type', type,
7556
- 'val', val
7557
- ]
7558
-
7559
- @writer.empty_tag('cfvo', attributes)
7560
- end
7561
-
7562
7486
  #
7563
7487
  # Write the Worksheet conditional formats.
7564
7488
  #
7565
- def write_conditional_formats #:nodoc:
7566
- ranges = @cond_formats.keys.sort
7567
- return if ranges.empty?
7568
-
7569
- ranges.each { |range| write_conditional_formatting(range, @cond_formats[range]) }
7489
+ def write_conditional_formats #:nodoc:
7490
+ @cond_formats.keys.sort.each do |range|
7491
+ write_conditional_formatting(range, @cond_formats[range])
7492
+ end
7570
7493
  end
7571
7494
 
7572
7495
  #
7573
7496
  # Write the <conditionalFormatting> element.
7574
7497
  #
7575
- # The conditional_formatting() method is used to add formatting
7576
- # to a cell or range of cells based on user defined criteria.
7577
- #
7578
- # worksheet.conditional_formatting('A1:J10',
7579
- # {
7580
- # :type => 'cell',
7581
- # :criteria => '>=',
7582
- # :value => 50,
7583
- # :format => format1
7584
- # }
7585
- # )
7586
- # This method contains a lot of parameters and is described
7587
- # in detail in a separate section "CONDITIONAL FORMATTING IN EXCEL".
7588
- #
7589
- # See also the conditional_format.rb program in the examples directory
7590
- # of the distro
7591
- #
7592
- def write_conditional_formatting(range, params) #:nodoc:
7498
+ def write_conditional_formatting(range, cond_formats) #:nodoc:
7593
7499
  attributes = ['sqref', range]
7594
7500
 
7595
7501
  @writer.tag_elements('conditionalFormatting', attributes) do
7596
- params.each { |param| write_cf_rule(param) }
7597
- end
7598
- end
7599
-
7600
- #
7601
- # Write the <cfRule> element.
7602
- #
7603
- def write_cf_rule(param) #:nodoc:
7604
- attributes = ['type' , param[:type]]
7605
-
7606
- if param[:format]
7607
- attributes << 'dxfId' << param[:format]
7608
- end
7609
- attributes << 'priority' << param[:priority]
7610
-
7611
- case param[:type]
7612
- when 'cellIs'
7613
- attributes << 'operator' << param[:criteria]
7614
- @writer.tag_elements('cfRule', attributes) do
7615
- if param[:minimum] && param[:maximum]
7616
- write_formula_tag(param[:minimum])
7617
- write_formula_tag(param[:maximum])
7618
- else
7619
- write_formula_tag(param[:value])
7620
- end
7621
- end
7622
- when 'aboveAverage'
7623
- attributes << 'aboveAverage' << 0 if param[:criteria] =~ /below/
7624
- attributes << 'equalAverage' << 1 if param[:criteria] =~ /equal/
7625
- if param[:criteria] =~ /([123]) std dev/
7626
- attributes << 'stdDev' << $~[1]
7627
- end
7628
- @writer.empty_tag('cfRule', attributes)
7629
- when 'top10'
7630
- attributes << 'percent' << 1 if param[:criteria] == '%'
7631
- attributes << 'bottom' << 1 if param[:direction]
7632
- rank = param[:value] || 10
7633
- attributes << 'rank' << rank
7634
- @writer.empty_tag('cfRule', attributes)
7635
- when 'duplicateValues', 'uniqueValues'
7636
- @writer.empty_tag('cfRule', attributes)
7637
- when 'containsText', 'notContainsText', 'beginsWith', 'endsWith'
7638
- attributes << 'operator' << param[:criteria]
7639
- attributes << 'text' << param[:value]
7640
- @writer.tag_elements('cfRule', attributes) do
7641
- write_formula_tag(param[:formula])
7642
- end
7643
- when 'timePeriod'
7644
- attributes << 'timePeriod' << param[:criteria]
7645
- @writer.tag_elements('cfRule', attributes) do
7646
- write_formula_tag(param[:formula])
7647
- end
7648
- when 'containsBlanks', 'notContainsBlanks', 'containsErrors', 'notContainsErrors'
7649
- @writer.tag_elements('cfRule', attributes) do
7650
- write_formula_tag(param[:formula])
7651
- end
7652
- when 'colorScale'
7653
- @writer.tag_elements('cfRule', attributes) do
7654
- write_color_scale(param)
7655
- end
7656
- when 'dataBar'
7657
- @writer.tag_elements('cfRule', attributes) do
7658
- write_data_bar(param)
7659
- end
7660
- when 'expression'
7661
- @writer.tag_elements('cfRule', attributes) do
7662
- write_formula_tag(param[:criteria])
7663
- end
7502
+ cond_formats.each { |cond_format| cond_format.write_cf_rule }
7664
7503
  end
7665
7504
  end
7666
7505
 
@@ -7674,54 +7513,11 @@ module Writexlsx
7674
7513
  end
7675
7514
  end
7676
7515
 
7677
- # Check for a cell reference in A1 notation and substitute row and column
7678
- def row_col_notation(args) # :nodoc:
7679
- if args[0] =~ /^\D/
7680
- substitute_cellref(*args)
7681
- else
7682
- args
7683
- end
7684
- end
7685
-
7686
- #
7687
- # Check that row and col are valid and store max and min values for use in
7688
- # other methods/elements.
7689
- #
7690
- # The ignore_row/ignore_col flags is used to indicate that we wish to
7691
- # perform the dimension check without storing the value.
7692
- #
7693
- # The ignore flags are use by set_row() and data_validate.
7694
- #
7695
- def check_dimensions_and_update_max_min_values(row, col, ignore_row = 0, ignore_col = 0) #:nodoc:
7696
- check_dimensions(row, col)
7697
- store_row_max_min_values(row) if ignore_row == 0
7698
- store_col_max_min_values(col) if ignore_col == 0
7699
-
7700
- 0
7701
- end
7702
-
7703
- def check_dimensions(row, col)
7704
- if !row || row >= ROW_MAX || !col || col >= COL_MAX
7705
- raise WriteXLSXDimensionError
7706
- end
7707
- 0
7708
- end
7709
-
7710
7516
  def store_row_col_max_min_values(row, col)
7711
7517
  store_row_max_min_values(row)
7712
7518
  store_col_max_min_values(col)
7713
7519
  end
7714
7520
 
7715
- def store_row_max_min_values(row)
7716
- @dim_rowmin = row if !@dim_rowmin || (row < @dim_rowmin)
7717
- @dim_rowmax = row if !@dim_rowmax || (row > @dim_rowmax)
7718
- end
7719
-
7720
- def store_col_max_min_values(col)
7721
- @dim_colmin = col if !@dim_colmin || (col < @dim_colmin)
7722
- @dim_colmax = col if !@dim_colmax || (col > @dim_colmax)
7723
- end
7724
-
7725
7521
  #
7726
7522
  # Calculate the "spans" attribute of the <row> tag. This is an XLSX
7727
7523
  # optimisation and isn't strictly required. However, it makes comparing
@@ -7885,10 +7681,6 @@ module Writexlsx
7885
7681
  !!@autofilter_ref
7886
7682
  end
7887
7683
 
7888
- def date_1904? #:nodoc:
7889
- @workbook.date_1904?
7890
- end
7891
-
7892
7684
  def print_options_changed? #:nodoc:
7893
7685
  !!@print_options_changed
7894
7686
  end