#!/usr/bin/env perl ########################## # Scott Baker # 2017-06-29 ########################## use strict; use warnings; use Data::Dump::Color; use Test::More; use v5.16; ############################################################################### ############################################################################### color_demo(); print "\n"; is(ansi_reveal(color("reset")) , "[RESET]" , "Reset"); is(ansi_reveal(color("bold")) , "[BOLD]" , "Bold"); is(ansi_reveal(color("blink")) , "[SLOWBLINK]", "Blink"); is(ansi_reveal(color("italic")) , "[ITALIC]" , "Italic"); is(ansi_reveal(color("underline")), "[UNDERLINE]", "Underline"); is(ansi_reveal(color("inverse")) , "[INVERSE]" , "Inverse"); is(ansi_reveal(color("red")) , "[FG-160]" , "Color: Red"); is(ansi_reveal(color("blue")) , "[FG-27]" , "Color: Blue"); is(ansi_reveal(color("green")) , "[FG-34]" , "Color: Green"); is(ansi_reveal(color("yellow")) , "[FG-226]" , "Color: Yellow"); is(ansi_reveal(color("orange")) , "[FG-214]" , "Color: Orange"); is(ansi_reveal(color("purple")) , "[FG-93]" , "Color: Purple"); is(ansi_reveal(color("white")) , "[FG-15]" , "Color: White"); is(ansi_reveal(color("black")) , "[FG-0]" , "Color: Black"); is(ansi_reveal(color("red_bold")) , "[BOLD][FG-160]" , "Color: Bold Red"); is(ansi_reveal(color("213_italic")) , "[ITALIC][FG-213]" , "Color: 213 Italic"); is(ansi_reveal(color("red_on_blue")), "[FG-160][BG-27]" , "Color: Red on Blue"); is(ansi_reveal(color("165_blink")) , "[SLOWBLINK][FG-165]", "Color: 164 Blink"); is(ansi_reveal(color("on_white")) , "[BG-15]" , "Color: On White"); is(ansi_reveal(color(160, '0')) , '[FG-160]0[RESET]' , "Colored text '0'"); is(ansi_reveal(color(123, '1')) , '[FG-123]1[RESET]' , "Colored text '1'"); is(ansi_reveal(color(99 , '')) , '[FG-99][RESET]' , "Colored text ''"); is(ansi_reveal(color(82 , 'Scott')), '[FG-82]Scott[RESET]', "Colored text 'Scott'"); is(ansi_reveal(color(66)) , '[FG-66]', "Number only integer"); is(ansi_reveal(color("67")), '[FG-67]', "Number only string"); is(ansi_reveal(color(670)) , '' , "Number > 255 (not valid)"); is(ansi_reveal(color(-11)) , '' , "Negative number"); done_testing(); ############################################################################### ############################################################################### sub color_demo { my @colors = qw(red blue green yellow orange purple white black); for (my $i = 0; $i < 3; $i++) { foreach my $color (sort @colors) { my $ansi = color($color); print color("bold") . $ansi . "XXXXXX"; } print color() . "\n"; } print color("bold") . color("yellow") . " Scott Baker " . color() . "\n"; print color("83_bold") . " Scott Baker " . color() . "\n"; print color("blink") . color("198") . " Blink Baker " . color() . "\n"; print color("underline") . color("57") . " Underline Baker " . color() . "\n"; print color("italic") . color("26") . " Italic Baker " . color() . "\n"; print color("inverse") . color("26") . " Invert Baker " . color() . "\n"; print color("213_italic") . " Scott pink italic" . color() . "\n"; print color("green_bold") . " Green Baker " . color() . "\n"; print color("black_bold") . color("inverse") . " Inverse Baker " . color() . "\n"; print color("bold") . color("yellow_on_blue") . " SUPERMAN " . color() . "\n"; print color("bold") . color("red_blink") . " RED BLINK " . color() . "\n"; } sub bleach_text { my $str = shift(); $str =~ s/\e\[\d*(;\d+)*m//g; return $str; } # Debug print variable using either Data::Dump::Color (preferred) or Data::Dumper # Creates methods k() and kd() to print, and print & die respectively BEGIN { if (eval { require Data::Dump::Color }) { *k = sub { Data::Dump::Color::dd(@_) }; } else { require Data::Dumper; *k = sub { print Data::Dumper::Dumper(\@_) }; } sub kd { k(@_); printf("Died at %2\$s line #%3\$s\n",caller()); exit(15); } } sub ansi_numbers { my $str = shift(); my @p = split(/;/, $str); my @ret = (); #k(\@p); for (my $i = 0; $i < scalar(@p); $i++) { my $x = $p[$i]; my $y = int($p[$i + 1] || 0); my $z = int($p[$i + 2] || 0); my $r = $p[$i + 2] || ""; my $g = $p[$i + 3] || ""; my $b = $p[$i + 4] || ""; if ($x == 0) { push(@ret, 'RESET'); } elsif ($x == 1) { push(@ret, 'BOLD'); } elsif ($x == 3) { push(@ret, 'ITALIC'); } elsif ($x == 4) { push(@ret, 'UNDERLINE'); } elsif ($x == 5) { push(@ret, 'SLOWBLINK'); } elsif ($x == 6) { push(@ret, 'FASTBLINK'); } elsif ($x == 7) { push(@ret, 'INVERSE'); # 4bit } elsif ($x >= 30 && $x < 38) { push(@ret, "FCOLOR-" . ($x - 30)); } elsif ($x >= 90 && $x < 98) { push(@ret, "FCOLOR-" . ($x - 82)); } elsif ($x >= 40 && $x < 48) { push(@ret, "BCOLOR-" . ($x - 40)); } elsif ($x >= 100 && $x < 108) { push(@ret, "BCOLOR-" . ($x - 92)); # 8bit } elsif ($x == 38 && $y == 5) { push(@ret, "FG-$z"); $i += 2; } elsif ($x == 48 && $y == 5) { push(@ret, "BG-$z"); $i += 2; # 24bit } elsif ($x == 38 && $y == 2) { push(@ret, sprintf("FG-#%X%X%X", $r, $g, $b)); $i += 4; } elsif ($x == 48 && $y == 2) { push(@ret, sprintf("BG-#%X%X%X", $r, $g, $b)); $i += 4; } else { push(@ret, "??$x??"); } } # Quote each item foreach (@ret) { $_ = "[$_]"; } my $ret = join("", @ret); return $ret; } sub ansi_reveal { my $str = shift(); my $raw = $str; # Make the escape chars visible $raw =~ s/\e\[(.*?)m/ESC[$1]/g; # Convert the numbers to english #k($raw); $raw =~ s/ESC\[(.*?)\]/ansi_numbers($1)/eg; return $raw; } ########################################################################################## ########################################################################################## ########################################################################################## # String format: '115', '165_bold', '10_on_140', 'reset', 'on_173', 'red', 'white_on_blue' sub color { my ($str, $txt) = @_; # If we're NOT connected to a an interactive terminal don't do color if (-t STDOUT == 0) { return $txt || ""; } # No string sent in, so we just reset if (!length($str) || $str eq 'reset') { return "\e[0m"; } # Some predefined colors my %color_map = qw(red 160 blue 27 green 34 yellow 226 orange 214 purple 93 white 15 black 0); $str =~ s|([A-Za-z]+)|$color_map{$1} // $1|eg; # Get foreground/background and any commands my ($fc,$cmd) = $str =~ /^(\d{1,3})?_?(\w+)?$/g; my ($bc) = $str =~ /on_(\d{1,3})$/g; if (defined($fc) && int($fc) > 255) { $fc = undef; } # above 255 is invalid # Some predefined commands my %cmd_map = qw(bold 1 italic 3 underline 4 blink 5 inverse 7); my $cmd_num = $cmd_map{$cmd // 0}; my $ret = ''; if ($cmd_num) { $ret .= "\e[${cmd_num}m"; } if (defined($fc)) { $ret .= "\e[38;5;${fc}m"; } if (defined($bc)) { $ret .= "\e[48;5;${bc}m"; } if (defined($txt)) { $ret .= $txt . "\e[0m"; } return $ret; }