lib/write_xlsx/utility.rb in write_xlsx-1.09.4 vs lib/write_xlsx/utility.rb in write_xlsx-1.09.5

- old
+ new

@@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- # frozen_string_literal: true + require 'write_xlsx/col_name' module Writexlsx module Utility ROW_MAX = 1048576 # :nodoc: @@ -25,31 +26,31 @@ # mainly used internally and aren't very useful to the user. # def xl_cell_to_rowcol(cell) cell =~ /(\$?)([A-Z]{1,3})(\$?)(\d+)/ - col_abs = $1 != "" - col = $2 - row_abs = $3 != "" - row = $4.to_i + col_abs = ::Regexp.last_match(1) != "" + col = ::Regexp.last_match(2) + row_abs = ::Regexp.last_match(3) != "" + row = ::Regexp.last_match(4).to_i # Convert base26 column string to number # All your Base are belong to us. chars = col.split(//) expn = 0 col = 0 chars.reverse.each do |char| - col += (char.ord - 'A'.ord + 1) * (26 ** expn) + col += (char.ord - 'A'.ord + 1) * (26**expn) expn += 1 end # Convert 1-index to zero-index row -= 1 col -= 1 - return [row, col, row_abs, col_abs] + [row, col, row_abs, col_abs] end def xl_col_to_name(col, col_absolute) col_str = ColName.instance.col_str(col) "#{absolute_char(col_absolute)}#{col_str}" @@ -70,22 +71,22 @@ def xl_range_formula(sheetname, row_1, row_2, col_1, col_2) # Use Excel's conventions and quote the sheet name if it contains any # non-word character or if it isn't already quoted. sheetname = "'#{sheetname}'" if sheetname =~ /\W/ && !(sheetname =~ /^'/) - range1 = xl_rowcol_to_cell( row_1, col_1, 1, 1 ) - range2 = xl_rowcol_to_cell( row_2, col_2, 1, 1 ) + range1 = xl_rowcol_to_cell(row_1, col_1, 1, 1) + range2 = xl_rowcol_to_cell(row_2, col_2, 1, 1) "=#{sheetname}!#{range1}:#{range2}" end # # Sheetnames used in references should be quoted if they contain any spaces, # special characters or if the look like something that isn't a sheet name. # TODO. We need to handle more special cases. # - def quote_sheetname(sheetname) #:nodoc: + def quote_sheetname(sheetname) # :nodoc: # Use Excel's conventions and quote the sheet name if it comtains any # non-word character or if it isn't already quoted. name = sheetname.dup if name =~ /\W/ && !(name =~ /^'/) # Double quote and single quoted strings. @@ -94,27 +95,26 @@ end name end def check_dimensions(row, col) - if !row || row >= ROW_MAX || !col || col >= COL_MAX - raise WriteXLSXDimensionError - end + raise WriteXLSXDimensionError if !row || row >= ROW_MAX || !col || col >= COL_MAX + 0 end # # convert_date_time(date_time_string) # # The function takes a date and time in ISO8601 "yyyy-mm-ddThh:mm:ss.ss" format # and converts it to a decimal number representing a valid Excel date. # - def convert_date_time(date_time_string) #:nodoc: + def convert_date_time(date_time_string) # :nodoc: date_time = date_time_string.to_s.sub(/^\s+/, '').sub(/\s+$/, '').sub(/Z$/, '') # Check for invalid date char. - return nil if date_time =~ /[^0-9T:\-\.Z]/ + return nil if date_time =~ /[^0-9T:\-.Z]/ # Check for "T" after date or before time. return nil unless date_time =~ /\dT|T\d/ days = 0 # Number of days since epoch @@ -125,34 +125,34 @@ # We allow the time portion of the input DateTime to be optional. if time # Match hh:mm:ss.sss+ where the seconds are optional if time =~ /^(\d\d):(\d\d)(:(\d\d(\.\d+)?))?/ - hour = $1.to_i - min = $2.to_i - sec = $4.to_f || 0 + hour = ::Regexp.last_match(1).to_i + min = ::Regexp.last_match(2).to_i + sec = ::Regexp.last_match(4).to_f || 0 else return nil # Not a valid time format. end # Some boundary checks return nil if hour >= 24 return nil if min >= 60 return nil if sec >= 60 # Excel expresses seconds as a fraction of the number in 24 hours. - seconds = (hour * 60* 60 + min * 60 + sec) / (24.0 * 60 * 60) + seconds = ((hour * 60 * 60) + (min * 60) + sec) / (24.0 * 60 * 60) end # We allow the date portion of the input DateTime to be optional. return seconds if date == '' # Match date as yyyy-mm-dd. if date =~ /^(\d\d\d\d)-(\d\d)-(\d\d)$/ - year = $1.to_i - month = $2.to_i - day = $3.to_i + year = ::Regexp.last_match(1).to_i + month = ::Regexp.last_match(2).to_i + day = ::Regexp.last_match(3).to_i else return nil # Not a valid date format. end # Set the epoch as 1900 or 1904. Defaults to 1900. @@ -161,11 +161,10 @@ return seconds if date == '1899-12-31' # Excel 1900 epoch return seconds if date == '1900-01-00' # Excel 1900 epoch return 60 + seconds if date == '1900-02-29' # Excel false leapday end - # We calculate the date by calculating the number of days since the epoch # and adjust for the number of leap days. We calculate the number of leap # days by normalising the year in relation to the epoch. Thus the year 2000 # becomes 100 for 4 and 100 year leapdays and 400 for 400 year leapdays. # @@ -175,27 +174,27 @@ range = year - epoch # Set month days and check for leap year. mdays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] leap = 0 - leap = 1 if year % 4 == 0 && year % 100 != 0 || year % 400 == 0 - mdays[1] = 29 if leap != 0 + leap = 1 if (year % 4 == 0 && year % 100 != 0) || year % 400 == 0 + mdays[1] = 29 if leap != 0 # Some boundary checks return nil if year < epoch or year > 9999 return nil if month < 1 or month > 12 return nil if day < 1 or day > mdays[month - 1] # Accumulate the number of days since the epoch. days = day # Add days for current month - (0 .. month-2).each do |m| + (0..month - 2).each do |m| days += mdays[m] # Add days for past months end days += range * 365 # Add days for past years - days += ((range) / 4) # Add leapdays - days -= ((range + offset) /100) # Subtract 100 year leapdays - days += ((range + offset + norm)/400) # Add 400 year leapdays + days += (range / 4) # Add leapdays + days -= ((range + offset) / 100) # Subtract 100 year leapdays + days += ((range + offset + norm) / 400) # Add 400 year leapdays days -= leap # Already counted above # Adjust for Excel erroneously treating 1900 as a leap year. days += 1 if !date_1904? and days > 59 @@ -239,18 +238,19 @@ if FileTest.file?(path) File.delete(path) elsif FileTest.directory?(path) Dir.foreach(path) do |file| next if file =~ /^\.\.?$/ # '.' or '..' - delete_files(path.sub(/\/+$/,"") + '/' + file) + + delete_files(path.sub(%r{/+$}, "") + '/' + file) end Dir.rmdir(path) end end def put_deprecate_message(method) - $stderr.puts("Warning: calling deprecated method #{method}. This method will be removed in a future release.") + warn("Warning: calling deprecated method #{method}. This method will be removed in a future release.") end # Check for a cell reference in A1 notation and substitute row and column def row_col_notation(args) # :nodoc: if args[0].to_s =~ /^\D/ @@ -264,52 +264,52 @@ # Substitute an Excel cell reference in A1 notation for zero based row and # column values in an argument list. # # Ex: ("A4", "Hello") is converted to (3, 0, "Hello"). # - def substitute_cellref(cell, *args) #:nodoc: + def substitute_cellref(cell, *args) # :nodoc: return [*args] if cell.respond_to?(:coerce) # Numeric normalized_cell = cell.upcase case normalized_cell # Convert a column range: 'A:A' or 'B:G'. # A range such as A:A is equivalent to A1:65536, so add rows as required when /\$?([A-Z]{1,3}):\$?([A-Z]{1,3})/ - row1, col1 = xl_cell_to_rowcol($1 + '1') - row2, col2 = xl_cell_to_rowcol($2 + ROW_MAX.to_s) - return [row1, col1, row2, col2, *args] + row1, col1 = xl_cell_to_rowcol(::Regexp.last_match(1) + '1') + row2, col2 = xl_cell_to_rowcol(::Regexp.last_match(2) + ROW_MAX.to_s) + [row1, col1, row2, col2, *args] # Convert a cell range: 'A1:B7' when /\$?([A-Z]{1,3}\$?\d+):\$?([A-Z]{1,3}\$?\d+)/ - row1, col1 = xl_cell_to_rowcol($1) - row2, col2 = xl_cell_to_rowcol($2) - return [row1, col1, row2, col2, *args] + row1, col1 = xl_cell_to_rowcol(::Regexp.last_match(1)) + row2, col2 = xl_cell_to_rowcol(::Regexp.last_match(2)) + [row1, col1, row2, col2, *args] # Convert a cell reference: 'A1' or 'AD2000' when /\$?([A-Z]{1,3}\$?\d+)/ - row1, col1 = xl_cell_to_rowcol($1) - return [row1, col1, *args] + row1, col1 = xl_cell_to_rowcol(::Regexp.last_match(1)) + [row1, col1, *args] else raise("Unknown cell reference #{normalized_cell}") end end def underline_attributes(underline) if underline == 2 - [['val', 'double']] + [%w[val double]] elsif underline == 33 - [['val', 'singleAccounting']] + [%w[val singleAccounting]] elsif underline == 34 - [['val', 'doubleAccounting']] + [%w[val doubleAccounting]] else [] # Default to single underline. end end # # Write the <color> element. # - def write_color(writer, name, value) #:nodoc: + def write_color(writer, name, value) # :nodoc: attributes = [[name, value]] writer.empty_tag('color', attributes) end @@ -327,20 +327,20 @@ def check_parameter(params, valid_keys, method) invalids = params.keys - valid_keys unless invalids.empty? raise WriteXLSXOptionParameterError, - "Unknown parameter '#{invalids.join(', ')}' in #{method}." + "Unknown parameter '#{invalids.join(", ")}' in #{method}." end true end # # Check that row and col are valid and store max and min values for use in # other methods/elements. # - def check_dimensions_and_update_max_min_values(row, col, ignore_row = 0, ignore_col = 0) #:nodoc: + def check_dimensions_and_update_max_min_values(row, col, ignore_row = 0, ignore_col = 0) # :nodoc: check_dimensions(row, col) store_row_max_min_values(row) if ignore_row == 0 store_col_max_min_values(col) if ignore_col == 0 0 @@ -356,10 +356,11 @@ @dim_colmax = col if !@dim_colmax || (col > @dim_colmax) end def float_to_str(float) return '' unless float + if float == float.to_i float.to_i.to_s else float.to_s end @@ -377,21 +378,17 @@ # Set the legend layout. legend.layout = layout_properties(params[:layout]) # Turn off the legend. - if params[:none] - legend.position = 'none' - end + legend.position = 'none' if params[:none] # Set the line properties for the legend. line = line_properties(params[:line]) # Allow 'border' as a synonym for 'line'. - if params[:border] - line = line_properties(params[:border]) - end + line = line_properties(params[:border]) if params[:border] # Set the fill properties for the legend. fill = fill_properties(params[:fill]) # Set the pattern properties for the legend. @@ -399,13 +396,11 @@ # Set the gradient fill properties for the legend. gradient = gradient_properties(params[:gradient]) # Pattern fill overrides solid fill. - if pattern - fill = nil - end + fill = nil if pattern # Gradient fill overrides solid and pattern fills. if gradient pattern = nil fill = nil @@ -427,21 +422,19 @@ # Convert user defined layout properties to the format required internally. # def layout_properties(args, is_text = false) return unless ptrue?(args) - properties = is_text ? [:x, :y] : [:x, :y, :width, :height] + properties = is_text ? %i[x y] : %i[x y width height] # Check for valid properties. args.keys.each do |key| - unless properties.include?(key.to_sym) - raise "Property '#{key}' not allowed in layout options\n" - end + raise "Property '#{key}' not allowed in layout options\n" unless properties.include?(key.to_sym) end # Set the layout properties - layout = Hash.new + layout = {} properties.each do |property| value = args[property] # Convert to the format used by Excel for easier testing. layout[property] = sprintf("%.17g", value) end @@ -451,12 +444,12 @@ # # Convert vertices from pixels to points. # def pixels_to_points(vertices) - col_start, row_start, x1, y1, - col_end, row_end, x2, y2, + _col_start, _row_start, _x1, _y1, + _col_end, _row_end, _x2, _y2, left, top, width, height = vertices.flatten left *= 0.75 top *= 0.75 width *= 0.75 @@ -512,11 +505,11 @@ # Write the <v:path> element. # def write_comment_path(gradientshapeok, connecttype) attributes = [] - attributes << ['gradientshapeok', 't'] if gradientshapeok + attributes << %w[gradientshapeok t] if gradientshapeok attributes << ['o:connecttype', connecttype] @writer.empty_tag('v:path', attributes) end @@ -571,11 +564,11 @@ # # Write the <v:stroke> element. # def write_stroke - attributes = [['joinstyle', 'miter']] + attributes = [%w[joinstyle miter]] @writer.empty_tag('v:stroke', attributes) end def r_id_attributes(id) @@ -618,65 +611,65 @@ # Check the foreground color is present. retuen nil unless args.has_key?(:fg_color) types = { - 'percent_5' => 'pct5', - 'percent_10' => 'pct10', - 'percent_20' => 'pct20', - 'percent_25' => 'pct25', - 'percent_30' => 'pct30', - 'percent_40' => 'pct40', + 'percent_5' => 'pct5', + 'percent_10' => 'pct10', + 'percent_20' => 'pct20', + 'percent_25' => 'pct25', + 'percent_30' => 'pct30', + 'percent_40' => 'pct40', - 'percent_50' => 'pct50', - 'percent_60' => 'pct60', - 'percent_70' => 'pct70', - 'percent_75' => 'pct75', - 'percent_80' => 'pct80', - 'percent_90' => 'pct90', + 'percent_50' => 'pct50', + 'percent_60' => 'pct60', + 'percent_70' => 'pct70', + 'percent_75' => 'pct75', + 'percent_80' => 'pct80', + 'percent_90' => 'pct90', - 'light_downward_diagonal' => 'ltDnDiag', - 'light_upward_diagonal' => 'ltUpDiag', - 'dark_downward_diagonal' => 'dkDnDiag', - 'dark_upward_diagonal' => 'dkUpDiag', - 'wide_downward_diagonal' => 'wdDnDiag', - 'wide_upward_diagonal' => 'wdUpDiag', + 'light_downward_diagonal' => 'ltDnDiag', + 'light_upward_diagonal' => 'ltUpDiag', + 'dark_downward_diagonal' => 'dkDnDiag', + 'dark_upward_diagonal' => 'dkUpDiag', + 'wide_downward_diagonal' => 'wdDnDiag', + 'wide_upward_diagonal' => 'wdUpDiag', - 'light_vertical' => 'ltVert', - 'light_horizontal' => 'ltHorz', - 'narrow_vertical' => 'narVert', - 'narrow_horizontal' => 'narHorz', - 'dark_vertical' => 'dkVert', - 'dark_horizontal' => 'dkHorz', + 'light_vertical' => 'ltVert', + 'light_horizontal' => 'ltHorz', + 'narrow_vertical' => 'narVert', + 'narrow_horizontal' => 'narHorz', + 'dark_vertical' => 'dkVert', + 'dark_horizontal' => 'dkHorz', - 'dashed_downward_diagonal' => 'dashDnDiag', - 'dashed_upward_diagonal' => 'dashUpDiag', - 'dashed_horizontal' => 'dashHorz', - 'dashed_vertical' => 'dashVert', - 'small_confetti' => 'smConfetti', - 'large_confetti' => 'lgConfetti', + 'dashed_downward_diagonal' => 'dashDnDiag', + 'dashed_upward_diagonal' => 'dashUpDiag', + 'dashed_horizontal' => 'dashHorz', + 'dashed_vertical' => 'dashVert', + 'small_confetti' => 'smConfetti', + 'large_confetti' => 'lgConfetti', - 'zigzag' => 'zigZag', - 'wave' => 'wave', - 'diagonal_brick' => 'diagBrick', - 'horizontal_brick' => 'horzBrick', - 'weave' => 'weave', - 'plaid' => 'plaid', + 'zigzag' => 'zigZag', + 'wave' => 'wave', + 'diagonal_brick' => 'diagBrick', + 'horizontal_brick' => 'horzBrick', + 'weave' => 'weave', + 'plaid' => 'plaid', - 'divot' => 'divot', - 'dotted_grid' => 'dotGrid', - 'dotted_diamond' => 'dotDmnd', - 'shingle' => 'shingle', - 'trellis' => 'trellis', - 'sphere' => 'sphere', + 'divot' => 'divot', + 'dotted_grid' => 'dotGrid', + 'dotted_diamond' => 'dotDmnd', + 'shingle' => 'shingle', + 'trellis' => 'trellis', + 'sphere' => 'sphere', - 'small_grid' => 'smGrid', - 'large_grid' => 'lgGrid', - 'small_check' => 'smCheck', - 'large_check' => 'lgCheck', - 'outlined_diamond' => 'openDmnd', - 'solid_diamond' => 'solidDmnd' + 'small_grid' => 'smGrid', + 'large_grid' => 'lgGrid', + 'small_check' => 'smCheck', + 'large_check' => 'lgCheck', + 'outlined_diamond' => 'openDmnd', + 'solid_diamond' => 'solidDmnd' } # Check for valid types. if types[args[:pattern]] pattern[:pattern] = types[args[:pattern]] @@ -690,10 +683,11 @@ pattern end def line_fill_properties(params) return { :_defined => 0 } unless params + ret = params.dup ret[:dash_type] = yield if block_given? && ret[:dash_type] ret[:_defined] = 1 ret end @@ -714,10 +708,11 @@ } end def value_or_raise(hash, key, msg) raise "Unknown #{msg} '#{key}'" if hash[key.to_sym].nil? + hash[key.to_sym] end def palette_color(index) # Adjust the colour index. @@ -733,18 +728,18 @@ def process_workbook_options(*params) case params.size when 0 [{}, {}] when 1 # one hash - options_keys = [:tempdir, :date_1904, :optimization, :excel2003_style, :strings_to_urls] + options_keys = %i[tempdir date_1904 optimization excel2003_style strings_to_urls] hash = params.first - options = hash.reject{|k,v| !options_keys.include?(k)} + options = hash.reject { |k, _v| !options_keys.include?(k) } default_format_properties = hash[:default_format_properties] || - hash.reject{|k,v| options_keys.include?(k)} + hash.reject { |k, _v| options_keys.include?(k) } [options, default_format_properties.dup] when 2 # array which includes options and default_format_properties options, default_format_properties = params default_format_properties ||= {} @@ -756,19 +751,18 @@ # # Convert user defined font values into private hash values. # def convert_font_args(params) return unless params + font = params_to_font(params) # Convert font size units. font[:_size] *= 100 if font[:_size] && font[:_size] != 0 # Convert rotation into 60,000ths of a degree. - if ptrue?(font[:_rotation]) - font[:_rotation] = 60_000 * font[:_rotation].to_i - end + font[:_rotation] = 60_000 * font[:_rotation].to_i if ptrue?(font[:_rotation]) font end def params_to_font(params) @@ -789,13 +783,11 @@ # # Write the <c:txPr> element. # def write_tx_pr(font, is_y_axis = nil) # :nodoc: rotation = nil - if font && font.respond_to?(:[]) && font[:_rotation] - rotation = font[:_rotation] - end + rotation = font[:_rotation] if font && font.respond_to?(:[]) && font[:_rotation] @writer.tag_elements('c:txPr') do # Write the a:bodyPr element. write_a_body_pr(rotation, is_y_axis) # Write the a:lstStyle element. write_a_lst_style @@ -812,18 +804,18 @@ attributes = [] if rot if rot == 16_200_000 # 270 deg/stacked angle. attributes << ['rot', 0] - attributes << ['vert', 'wordArtVert'] + attributes << %w[vert wordArtVert] elsif rot == 16_260_000 # 271 deg/stacked angle. attributes << ['rot', 0] - attributes << ['vert', 'eaVert'] + attributes << %w[vert eaVert] else attributes << ['rot', rot] - attributes << ['vert', 'horz'] + attributes << %w[vert horz] end end @writer.empty_tag('a:bodyPr', attributes) end @@ -869,16 +861,12 @@ latin_attributes = get_font_latin_attributes(font) has_color = ptrue?(font) && ptrue?(font[:_color]) if !latin_attributes.empty? || has_color @writer.tag_elements(tag, style_attributes) do - if has_color - write_a_solid_fill(:color => font[:_color]) - end - if !latin_attributes.empty? - write_a_latin(latin_attributes) - end + write_a_solid_fill(:color => font[:_color]) if has_color + write_a_latin(latin_attributes) unless latin_attributes.empty? end else @writer.empty_tag(tag, style_attributes) end end @@ -913,11 +901,11 @@ # # Write the <a:srgbClr> element. # def write_a_srgb_clr(color, transparency = nil) # :nodoc: tag = 'a:srgbClr' - attributes = [ ['val', color] ] + attributes = [['val', color]] if ptrue?(transparency) @writer.tag_elements(tag, attributes) do write_a_alpha(transparency) end @@ -934,10 +922,11 @@ # Convert a HTML style #RRGGBB color. color_code.sub(/^#/, '').upcase else index = Format.color(color_code) raise "Unknown color '#{color_code}' used in chart formatting." unless index + palette_color(index) end end # @@ -949,23 +938,21 @@ attributes = [] attributes << ['sz', font[:_size]] if ptrue?(font[:_size]) attributes << ['b', font[:_bold]] if font[:_bold] attributes << ['i', font[:_italic]] if font[:_italic] - attributes << ['u', 'sng'] if font[:_underline] + attributes << %w[u sng] if font[:_underline] # Turn off baseline when testing fonts that don't have it. - if font[:_baseline] != -1 - attributes << ['baseline', font[:_baseline]] - end + attributes << ['baseline', font[:_baseline]] if font[:_baseline] != -1 attributes end # # Write the <a:endParaRPr> element. # def write_a_end_para_rpr # :nodoc: - @writer.empty_tag('a:endParaRPr', [ ['lang', 'en-US'] ]) + @writer.empty_tag('a:endParaRPr', [%w[lang en-US]]) end end module WriteDPtPoint #