sub inverse_color { my $color = shift; die "Missing argument to inverse_color()" unless $color; state $color_names; if ( not $color_names ) { #set_default_namespace("www"); $color_names = available_names(); } $color =~ s/^\s+//; $color =~ s/\s+$//; if ( $color =~ /^#[[:xdigit:]]{3}$/ ) { $color =~ s/#//; my $n = hex $color; my $i = 0xFFF - $n; my $inverse = sprintf "#%03x", $i; return $inverse; } elsif ( $color =~ /^#[[:xdigit:]]{6}$/ ) { $color =~ s/#//; my $n = hex $color; my $i = 0xFFFFFF - $n; my $inverse = sprintf "#%06x", $i; return $inverse; } elsif ( $color =~ /rgb \s* \( \s* ([0-9]+) \s* , \s* ([0-9]+) , \s* ([0-9]+) \s* \) /x ) { my ($r, $g, $b) = ($1, $2, $3); my $n = $r * 65536 + $g * 256 + $b; printf "converted %s to %06x\n", $color, $n if $verbose; my $i = 0xFFFFFF - $n; my $inverse = sprintf "#%06x", $i; return $inverse; } elsif ( $color =~ /rgba \s* \( \s* ([0-9]+) \s* , \s* ([0-9]+) , \s* ([0-9]+) \s* , \s* ([0-9.]+) \s* \) /x ) { my ($r, $g, $b, $alpha) = ($1, $2, $3, $4); my $inverse = sprintf "rgba( %d, %d, %d, %0.2f )", 255 - $r, 255 - $g, 255 - $b, 1 - $alpha; return $inverse; } elsif ( $color =~ /hsl \s* \( \s* ([0-9]+) \s* , \s* ([0-9]+)% , \s* ([0-9]+)% \s* \) /x ) { my ( $hue, $saturation, $lightness ) = ($1, $2, $3); my $hue2 = ($hue + 180) % 360; my $sat2 = 100 - $saturation; my $light2 = 100 - $lightness; my $inverse = sprintf "hsl( %d, %d%%, %d%% )", $hue2, $sat2, $light2; return $inverse; } elsif ( $color =~ /hsla \s* \( \s* ([0-9]+) \s* , \s* ([0-9]+)% , \s* ([0-9]+)% \s* , \s* ([0-9.]+) \s* \) /x ) { my ( $hue, $saturation, $lightness, $alpha ) = ($1, $2, $3, $4); my $hue2 = ($hue + 180) % 360; my $sat2 = 100 - $saturation; my $light2 = 100 - $lightness; my $alpha2 = 1 - $alpha; my $inverse = sprintf "hsl( %d, %d%%, %d%%, %0.2f )", $hue2, $sat2, $light2, $alpha2; return $inverse; } elsif ( $color =~ /currentcolor/i ) { warn "Should have removed currentcolor in fix_css_colors()"; } elsif ( $color =~ /inherit/i ) { return "inherit"; } elsif ( $color_names->{ "www:". $color} or $color_names->{ $color} ) { my $hexcolor = name2rgb( $color ); if ( not $hexcolor ) { $hexcolor = name2rgb( "www:" . $color ); if ( not $hexcolor ) { die "Can't resolve color name $color"; } } $hexcolor =~ s/#//; my $i = 0xFFFFFF - hex($hexcolor); my $inverse = sprintf "#%06x", $i; return $inverse; } else { die "Color format not implemented: $color"; } } sub fix_css_colors { my ($csstext, $css_fn, $epub_fn) = @_; return if not $csstext; my $errors = 0; my $corrections = 0; my $printed_filename = 0; say "Checking $epub_fn:$css_fn for bad colors\n" if $verbose; # this might be a good use of negative lookbehind? my @css_blocks = split /(})/, $csstext; for my $block ( @css_blocks ) { if ( $block =~ m/color: \s* ( [^;]+ ) \s* (?:;|$) /x ) { my $fgcolor = $1; print "found color: $fgcolor\n" if $verbose; if ( $fgcolor =~ m/currentcolor/i ) { $block =~ s/(color: \s* currentcolor \s* ;? \s* ) \n* //xi; print "Stripping out $1 as it is a pleonasm\n" if $verbose; $corrections++; next; } if ( $block !~ m/background-color:/ ) { my $bgcolor = inverse_color( $fgcolor ); $block =~ s/(color: \s* [^;}]+ \s* (?:;|$) )/background-color: $bgcolor;\n$1/x; print "corrected block:\n$block\n}\n" if $verbose; $corrections++; } } } if ( $corrections ) { my $new_css_text = join "", @css_blocks; return $new_css_text; } else { return undef; } }