#!perl -w use strict; use Imager; use Imager::Matrix2d; use Getopt::Long; use constant PI => 4 * atan2(1,1); # this sample requires Freetype 2.x $Imager::formats{"ft2"} or die "This sample require Freetype 2.x to be configured in Imager\n"; Getopt::Long::Configure("bundling"); my $angle = 30; my $fg = 'white'; my $bg = 'black'; my $size = 20; my $rotate; GetOptions('angle|a=f' => \$angle, 'size|s=i' => \$size, 'foreground|fg|f=s' => \$fg, 'background|bg|b=s' => \$bg, 'rotate|r' => \$rotate) or usage(); # check for sanity if ($angle < -45 or $angle > 45) { # while values outside this range are valid, the text would be hard # to read die "--angle is limited to the range -45 through +45\n"; } elsif ($size < 10) { die "--size must be 10 or greater\n"; } my $fontfile = shift; my $outfile = shift; @ARGV or usage(); my $text = "@ARGV"; my $angle_rads = $angle * (PI / 180); my $trans; # this is the only difference between rotation and shearing: the # transformation matrix if ($rotate) { $trans = Imager::Matrix2d->rotate(radians => $angle_rads); } else { $trans = Imager::Matrix2d->shear(x=>sin($angle_rads)/cos($angle_rads)); } # only the Freetype 2.x driver supports transformations for now my $font = Imager::Font->new(file=>$fontfile, type=>'ft2') or die "Cannot load font $fontfile: ", Imager->errstr, "\n"; $font->transform(matrix=>$trans); my $bbox = $font->bounding_box(string=>$text, size=>$size); # these are in font co-ordinates, so y is flipped my ($left, $miny, $right, $maxy) = transformed_bounds($bbox, $trans); # convert to image relative co-ordinates my ($top, $bottom) = (-$maxy, -$miny); my ($width, $height) = ($right - $left, $bottom - $top); my $img = Imager->new(xsize=>$width, ysize=>$height); # fill with the background $img->box(filled=>1, color=>$bg); # and draw our string in the right place $img->string(text => $text, color => Imager::Color->new('white'), x => -$left, y => -$top, color => $fg, font => $font, size => $size); $img->write(file=>$outfile) or die "Cannot save $outfile: ",$img->errstr,"\n"; sub transformed_bounds { my ($bbox, $matrix) = @_; my $bounds; for my $point ([ $bbox->start_offset, $bbox->ascent ], [ $bbox->start_offset, $bbox->descent ], [ $bbox->end_offset, $bbox->ascent ], [ $bbox->end_offset, $bbox->descent ]) { $bounds = add_bound($bounds, transform_point(@$point, $matrix)); } @$bounds; } sub transform_point { my ($x, $y, $matrix) = @_; return ( $x * $matrix->[0] + $y * $matrix->[1] + $matrix->[2], $x * $matrix->[3] + $y * $matrix->[4] + $matrix->[5] ); } sub add_bound { my ($bounds, $x, $y) = @_; $bounds or return [ $x, $y, $x, $y ]; $x < $bounds->[0] and $bounds->[0] = $x; $y < $bounds->[1] and $bounds->[1] = $y; $x > $bounds->[2] and $bounds->[2] = $x; $y > $bounds->[3] and $bounds->[3] = $y; $bounds; } sub usage { print <<EOS; Usage: $0 [options] fontfile outfile text... Options: --angle <angle> | -a <angle> Set the slant angle in degrees, limited to -45 to +45. Default 30. --size <pixels> | -s <angle> Set the text size in pixels. Must be 10 or greater. Default: 20. --foreground <color> | --fg <color> | -f <color> Set the text foreground color. Default: white. --background <color> | --bg <color> | -b <color> Set the image background color. Default: black --rotate | -r Rotate instead of shearing. Default: shear eg. # shear $0 -a 45 fontfiles/ImUgly.ttf output.ppm "something to say" # rotate at 100 pixel font size, blue foregroune, white background $0 -rs 100 -b white -f blue fontfiles/ImUgly.ttf output.ppm Imager EOS exit 1; } =head1 NAME slant_text.pl - sample for drawing transformed text =head1 SYNOPSIS perl slant_text.pl [options] fontfile output text Run without arguments for option details. =head1 DESCRIPTION This is a sample for drawing transformed text. It's complicated by the need to create an image to put the text into, if you have text, a font, and a good idea where it belongs, it's simple to create the transformation matrix: use Imager::Matrix2d; # or call another method for shearing, etc my $matrix = Imager::Matrix2d->rotate(radians=>$some_angle); Feed the transformation matrix to the font: $font->transform(matrix=>$font); then draw the text as normal: $image->string(string=>$some_text, x => $where_x, y => $where_y, font => $font, size => $size); But if you do need the bounds, the code above does show you how to do it. =head1 FUNCTIONS =over =item transformed_bounds Returns a list of bounds: (minx, miny, maxx, maxy) These are offsets from the text's starting point in font co-ordinates - so positive y is I<up>. Note: this returns the bounds of the transformed bounding box, in most cases the actual text will not be touching these boundaries. =cut =back =head1 AUTHOR Tony Cook <tonyc@cpan.org> =head1 REVISION $Revision$ =head1 SEE ALSO Imager(1), Imager::Cookbook, Imager::Matrix2d =cut