module Paint
Constants
- ANSI_COLORS
Basic colors (often, the color differs when using the bright effect) Final color will be 30 + value for foreground and 40 + value for background
- ANSI_COLORS_BACKGROUND
- ANSI_COLORS_FOREGROUND
- ANSI_EFFECTS
Terminal effects - most of them are not supported ;) See en.wikipedia.org/wiki/ANSI_escape_code
- NOTHING
- RGB_COLORS
A list of color names, based on X11's rgb.txt Can be used with ::[] by passing a string containing the color name See Paint::Util::update_rgb_colors for generating
- RGB_COLORS_ANSI
A list of color names for standard ansi colors, needed for 16/8 color fallback mode See en.wikipedia.org/wiki/ANSI_escape_code#Colors
- RGB_COLORS_ANSI_BRIGHT
A list of color names for standard bright ansi colors, needed for 16 color fallback mode See en.wikipedia.org/wiki/ANSI_escape_code#Colors
- VERSION
Attributes
This variable influences the color code generation Currently supported values:
-
256 - 256 colors
-
16 - only ansi colors and bright effect
-
8 - only ansi colors
-
0 - no colorization!
Public Class Methods
Takes a string and color options and colorizes the string See README.rdoc for details
# File lib/paint.rb, line 83 def [](string, *options) return string.to_s if @mode.zero? || options.empty? options = options.first if options.size == 1 && !options.first.respond_to?(:to_ary) @cache[options] + string.to_s + NOTHING end
Transforms options into the desired color. Used by @cache
# File lib/paint.rb, line 90 def color(*options) return '' if @mode.zero? || options.empty? mix = [] color_seen = false colors = ANSI_COLORS_FOREGROUND options.each{ |option| case option when Symbol if color = colors[option] mix << color color_seen = :set elsif ANSI_EFFECTS.key?(option) mix << effect(option) else raise ArgumentError, "Unknown color or effect: #{ option }" end when Array if option.size == 3 && option.all?{ |n| n.is_a? Numeric } mix << rgb(*[*option, color_seen]) color_seen = :set else raise ArgumentError, "Array argument must contain 3 numerals" end when ::String if option =~ /^#?(?:[0-9a-f]{3}){1,2}$/i mix << hex(option, color_seen) color_seen = :set else mix << rgb_name(option, color_seen) color_seen = :set end when Numeric integer = option.to_i color_seen = :set if (30..49).include?(integer) mix << integer when nil color_seen = :set else raise ArgumentError, "Invalid argument: #{ option.inspect }" end if color_seen == :set colors = ANSI_COLORS_BACKGROUND color_seen = true end } wrap(*mix) end
Determine supported colors
# File lib/paint.rb, line 204 def detect_mode if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ # windows if ENV['ANSICON'] 16 elsif ENV['ConEmuANSI'] == 'ON' 256 else 0 end else case ENV['TERM'] when /-256color$/, 'xterm' 256 when /-color$/, 'rxvt' 16 else # optimistic default 256 end end end
Creates the specified effect by looking it up in Paint::ANSI_EFFECTS
# File lib/paint.rb, line 199 def effect(effect_name) ANSI_EFFECTS[effect_name] end
Creates 256-compatible color from a html-like color string
# File lib/paint.rb, line 176 def hex(source, background = false) string = source.tr '#','' color_code = if string.size == 6 string.each_char.each_slice(2).map{ |hex_color| hex_color.join.to_i(16) } else string.each_char.map{ |hex_color_half| (hex_color_half*2).to_i(16) } end rgb(*[*color_code, background]) end
# File lib/paint.rb, line 154 def mode=(val) @cache.clear; @mode = val end
Tries to print all 256 colors
# File lib/paint/util.rb, line 12 def rainbow (0...256).each{ |color| print Paint[' ', 48, 5, color] # print empty bg color field } puts end
Creates a random ansi color
# File lib/paint.rb, line 194 def random(background = false) (background ? 40 : 30) + rand(8) end
Creates a 256-compatible color from rgb values
# File lib/paint.rb, line 167 def rgb(red, green, blue, background = false) if @mode == 8 || @mode == 16 "#{background ? 4 : 3}#{rgb_like_value(red, green, blue, @mode == 16)}" else "#{background ? 48 : 38}#{rgb_value(red, green, blue)}" end end
Creates a 256-color from a name found in Paint::RGB_COLORS (based on rgb.txt)
# File lib/paint.rb, line 187 def rgb_name(color_name, background = false) if color_code = RGB_COLORS[color_name] rgb(*[*color_code, background]) end end
Creates simple ansi color by looking it up on Paint::ANSI_COLORS
# File lib/paint.rb, line 162 def simple(color_name, background = false) (background ? 40 : 30) + ANSI_COLORS[color_name] end
Removes any color and effect strings
# File lib/paint/util.rb, line 7 def unpaint(string) string.gsub(/\e\[(?:[0-9];?)+m/, '') end
Updates color names
# File lib/paint/util.rb, line 20 def update_rgb_colors(path = '/etc/X11/rgb.txt') if File.file?(path) Paint::RGB_COLORS.clear File.open(path, 'r') do |file| file.each_line{ |line| line.chomp =~ /(\d+)\s+(\d+)\s+(\d+)\s+(.*)$/ Paint::RGB_COLORS[$4] = [$1.to_i, $2.to_i, $3.to_i] if $4 } end else raise ArgumentError, "Not a valid file: #{path}" end end
Adds ansi sequence
# File lib/paint.rb, line 157 def wrap(*ansi_codes) "\033[" + ansi_codes*";" + "m" end
Private Class Methods
# File lib/paint.rb, line 266 def distance(rgb1, rgb2) rgb1.zip(rgb2).inject(0){ |acc, (cur1, cur2)| acc + (cur1 - cur2)**2 } end
Returns ansi color matching an rgb value, without fore-/background information See mail.python.org/pipermail/python-list/2008-December/1150496.html
# File lib/paint.rb, line 252 def rgb_like_value(red, green, blue, use_bright = false) color_pool = RGB_COLORS_ANSI.values color_pool += RGB_COLORS_ANSI_BRIGHT.values if use_bright ansi_color_rgb = color_pool.min_by{ |col| distance([red, green, blue],col) } key_method = RUBY_VERSION < "1.9" ? :index : :key if ansi_color = RGB_COLORS_ANSI.send(key_method, ansi_color_rgb) ANSI_COLORS[ansi_color] else ansi_color = RGB_COLORS_ANSI_BRIGHT.send(key_method, ansi_color_rgb) "#{ANSI_COLORS[ansi_color]};1" end end
Returns nearest supported 256-color an rgb value, without fore-/background information Inspired by the rainbow gem
# File lib/paint.rb, line 229 def rgb_value(red, green, blue) gray_possible = true sep = 42.5 while gray_possible if red < sep || green < sep || blue < sep gray = red < sep && green < sep && blue < sep gray_possible = false end sep += 42.5 end if gray ";5;#{ 232 + ((red.to_f + green.to_f + blue.to_f)/33).round }" else # rgb ";5;#{ [16, *[red, green, blue].zip([36, 6, 1]).map{ |color, mod| (6 * (color.to_f / 256)).to_i * mod }].inject(:+) }" end end