package Graphics::ColorNames; require 5.006; use strict; use warnings; use Carp; use IO::File; use Module::Load; our @ISA = qw( Exporter ); our $VERSION = '1.06'; # $VERSION = eval $VERSION; our @EXPORT = qw( ); our @EXPORT_OK = qw( hex2tuple tuple2hex ); sub _load_file { my $self = shift; my $file = shift; unless (ref($file)) { unless (-r $file) { croak "Cannot load scheme from file: \'$file\'"; } } my $colors = { }; my $fh = ref($file) ? $file : (new IO::File); unless (ref($file)) { open($fh, $file) or croak "Cannot open file: \'$file\'"; } while (my $line = <$fh>) { unless ($line =~ /^[\!\#]/) { chomp($line); if ($line) { my ($red, $green, $blue, $name, $rgb); $red = eval substr($line, 0, 3); $green = eval substr($line, 4, 3); $blue = eval substr($line, 8, 3); $rgb = ($red << 16) | ($green << 8) | ($blue); $name = substr($line, 12); $name =~ s/^\s+//; # remove leading and trailing spaces $name =~ s/\s+$//; $colors->{lc($name)} = $rgb; } } } unless (ref($file)) { close $fh; } $self->{SCHEMES}->[ $self->{COUNT}++ ] = $colors; # push @{ $self->{SCHEMES} }, $colors; } sub _load_scheme { my $self = shift; my $scheme = shift; # Should this be switched to use Module::Pluggable? Hm.... my $module = join('::', __PACKAGE__, $scheme); eval { load $module; }; if ($@) { croak "Cannot load color naming scheme \`$scheme\'"; } else { no strict 'refs'; $self->{SCHEMES}->[ $self->{COUNT}++ ] = $module->NamesRgbTable(); } } sub TIEHASH { my $class = shift; my $self = { SCHEMES => [ ], # a list of naming schemes, in priority search order COUNT => 0, }; bless $self, $class; if (@_) { foreach my $scheme (@_) { if (-e $scheme) { $self->_load_file( $scheme ); } else { $self->_load_scheme( $scheme ); } } } else { $self->_load_scheme( 'X' ); } return $self; } sub FETCH { my ($self, $key) = @_; my $value; if ($key =~ m/^\x23?[\da-f]{6}$/i) { $value = $key; $value =~ s/\x23//; } else { my $i = 0; while ( (!defined $value) and ($i < $self->{COUNT}) ) { my $scheme = $self->{SCHEMES}->[$i++]; my $type = ref($scheme); if ($type eq 'HASH') { $value = $scheme->{ lc($key) }; } elsif ($type eq 'CODE') { $value = &$scheme(lc($key)); } else { croak "Unsupported scheme type: ", $type; } } $value = sprintf( '%06x', $value ), if (defined $value); } return $value; } sub EXISTS { my ($self, $key) = @_; defined ($self->FETCH($key)); } sub FIRSTKEY { my $self = shift; my $scheme = $self->{SCHEMES}->[0]; if (ref($scheme) ne 'HASH') { croak "Scheme is not a hash"; } each %{$self->{SCHEMES}->[0]}; } sub NEXTKEY { my $self = shift; my $scheme = $self->{SCHEMES}->[0]; if (ref($scheme) ne 'HASH') { croak "Scheme is not a hash"; } each %{$self->{SCHEMES}->[0]}; } sub hex { my $self = shift; my $name = shift; my $rgb = $self->FETCH($name); my $pre = shift; unless (defined $pre) { $pre = ""; } return ($pre.$rgb); } sub rgb { my $self = shift; my $name = shift; my @rgb = hex2tuple($self->FETCH($name)); my $sep = shift || ','; return wantarray ? @rgb : join($sep,@rgb); } sub hex2tuple # Convert 6-digit hexidecimal code (used for HTML etc.) to an array of # RGB values { my $rgb = CORE::hex( shift ); my ($red, $green, $blue); $blue = ($rgb & 0x0000ff); $green = ($rgb & 0x00ff00) >> 8; $red = ($rgb & 0xff0000) >> 16; return ($red, $green, $blue); } sub tuple2hex # Convert list of RGB values to 6-digit hexidecimal code (used for HTML, etc.) { my ($red, $green, $blue) = @_; my $rgb = sprintf "%.2x%.2x%.2x", $red, $green, $blue; return $rgb; } sub _readonly_error { croak "Cannot modify a read-only value"; } BEGIN { no strict 'refs'; *STORE = \ &_readonly_error; *DELETE = \ &_readonly_error; *CLEAR = \ &_readonly_error; # causes problems with 'undef' *new = \ &TIEHASH; } 1; __END__ =head1 NAME Graphics::ColorNames - defines RGB values for common color names =head1 REQUIREMENTS C should work on Perl 5.6.0. It requires the following non-standard modules: Module::Load =head2 Installation Installation is pretty standard: perl Makefile.PL make make test make install =head1 SYNOPSIS use Graphics::ColorNames qw( hex2tuple tuple2hex ); tie %NameTable, 'Graphics::ColorNames', 'X'; my $rgbhex1 = $NameTable{'green'}; # returns '00ff00' my $rgbhex2 = tuple2hex( 0, 255, 0 ); # returns '00ff00' my @rgbtup = hex2tuple( $rgbhex ); # returns (0, 255, 0) my $rgbhex3 = $NameTable{'#123abc'}; # returns '123abc' my $rgbhex4 = $NameTable{'123abc'}; # returns '123abc' =head1 DESCRIPTION This module defines RGB values for common color names. The intention is to (1) provide a common module that authors can use with other modules to specify colors; and (2) free module authors from having to "re-invent the wheel" whenever they decide to give the users the option of specifying a color by name rather than RGB value. For example, use Graphics::ColorNames 'hex2tuple'; tie %COLORS, 'Graphics::ColorNames'; use GD; $img = new GD::Image(100, 100); $bgColor = $img->colorAllocate( hex2tuple( $COLORS{'CadetBlue3'} ) ); Though a little 'bureaucratic', the meaning of this code is clearer: C<$bgColor> (or background color) is 'CadetBlue3' (which is easier to for one to understand than C<0x7A, 0xC5, 0xCD>). The variable is named for its function, not form (ie, C<$CadetBlue3>) so that if the author later changes the background color, the variable name need not be changed. As an added feature, a hexidecimal RGB value in the form of #RRGGBB or RRGGBB will return itself: my $rgbhex3 = $NameTable{'#123abc'}; # returns '123abc' =head2 Tied Interface The standard interface (prior to version 0.40) is through a tied hash: tie %NAMETABLE, 'Graphics::ColorNames', @SCHEME where C<%NAMETABLE> is the tied hash and C<@SCHEME> is a list of L or the path to or open filehandle for a F file. Multiple schemes can be used: tie %COLORS, 'Graphics::ColorNames', qw(HTML Windows Netscape); In this case, if the name is not a valid HTML color, the Windows name will be used; if it is not a valid Windows name, then the Netscape name will be used. RGB values can be retrieved with a case-insensitive hash key: $rgb = $colors{'AliceBlue'}; The value returned is in the six-digit hexidecimal format used in HTML and CSS (without the initial '#'). To convert it to separate red, green, and blue values (between 0 and 255), use the L function. =head2 Object-Oriented Interface If you prefer, an object-oriented interface is available: $obj = Graphics::ColorNames->new('/etc/rgb.txt'); $hex = $obj->hex('skyblue'); # returns "87ceeb" @rgb = $obj->rgb('skyblue'); # returns (0x87, 0xce, 0xeb) The interface is similar to the L module: =over =item new $obj = Graphics::ColorNames->new( @SCHEMES ); Creates the object, using the default L. If none are specified, it uses the C scheme. =item hex $hex = $obj->hex($name, $prefix); Returns a 6-digit hexidecimal RGB code for the color. If an optional prefix is specified, it will prefix the code with that string. For example, $hex = $obj->hex('blue', '#'); # returns "#0000ff" =item rgb @rgb = $obj->rgb($name); $rgb = $obj->rgb($name, $separator); If called in a list context, returns a triplet. If called in a scalar context, returns a string separated by an optional separator (which defauls to a comma). For example, @rgb = $obj->rgb('blue'); # returns (0, 0, 255) $rgb = $obj->rgb('blue', ','); # returns "0,0,255" =back =head2 Utility Functions These functions are not exported by default, so much be specified to be used: use Graphics::ColorNames qw( hex2tuple tuple2hex ); =over =item hex2tuple ($red, $green, $blue) = hex2tuple( $colors{'AliceBlue'}); =item tuple2hex $rgb = tuple2hex( $red, $green, $blue ); =back =head2 Color Schemes The following schemes are available by default: =over =item X About 750 color names used in X-Windows. I, since it provides the most names. =item HTML 16 common color names defined in the HTML 4.0 specification. These names are also used with CSS and SVG. =item Netscape 100 color names names associated Netscape 1.1 (I cannot determine whether they were once usable in Netscape or were arbitrary names for RGB values-- many of these names are not recognized by later versions of Netscape). This scheme may be deprecated in future versions, but available as a separate module. =item Windows 16 commom color names used with Microsoft Windows and related products. These are actually the same colors as C, although with different names. =back Rather than a color scheme, the path or open filehandle for a F file may be specified. Additional color schemes may be available on CPAN. =head2 Adding Naming Schemes You can add naming scheme files by creating a Perl module is the name C which has a subroutine named C that returns a hash of color names and RGB values. The color names must be in all lower-case, and the RGB values must be 24-bit numbers containing the red, green, and blue values in most- significant to least- significant byte order. An example naming schema is below: package Graphics::ColorNames::Metallic; sub NamesRgbTable() { use integer; return { copper => 0xb87333, gold => 0xcd7f32, silver => 0xe6e8fa, }; } You would use the above schema as follows: tie %colors, 'Graphics::ColorNames', 'Metallic'; An example of an additional module is Steve Pomeroy's L module. Since version 1.03, C may also return a code reference: package Graphics::ColorNames::Orange; sub NamesRgbTable() { return sub { my $name = shift; return 0xffa500; }; } See L for an example. Note that extentions of the form "Graphics::ColourNames::*" are not supported. =head1 SEE ALSO L has a similar function to this module, but parses an F file. L can convert between RGB and other color space types. =head1 DSLIP R - Released d - Developer p - Perl-only h - Hybrid interface p - Standard Perl See L =head1 AUTHOR Robert Rothenberg =head2 Acknowledgements Alan D. Salewski for feedback and the addition of C. Steve Pomeroy for pointing out invalid color definitions in X color space. who pointed out a mispelling of "fuchsia" in the HTML color space L. who pointed out mispellings and naming inconsistencies. =head2 Suggestions and Bug Reporting Feedback is always welcome. Please use the CPAN Request Tracker at L to submit bug reports. If you create additional color schemes, please make them available separately in CPAN rather than submit them to me for inclusion into this module. =head1 LICENSE Copyright (c) 2001-2005 Robert Rothenberg. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut