From: Tony Cook Date: Tue, 25 Oct 2005 07:14:07 +0000 (+0000) Subject: - added sampled/slant_text.pl, and notes on shearing/rotating text in X-Git-Tag: Imager-0.48^2~101 X-Git-Url: http://git.imager.perl.org/imager.git/commitdiff_plain/ef1ab93b437ee689c757ebbfa640bbd4b9969303 - added sampled/slant_text.pl, and notes on shearing/rotating text in Imager::Cookbook. --- diff --git a/Changes b/Changes index 426ec4cb..9e023c31 100644 --- a/Changes +++ b/Changes @@ -1152,6 +1152,8 @@ Revision history for Perl extension Imager. from a TIFF file. - added samples/border.pl, and notes on adding a border in Imager::Cookbook. +- added sampled/slant_text.pl, and notes on shearing/rotating text in + Imager::Cookbook. ================================================================= diff --git a/lib/Imager/Cookbook.pod b/lib/Imager/Cookbook.pod index 236a94d3..160770c8 100644 --- a/lib/Imager/Cookbook.pod +++ b/lib/Imager/Cookbook.pod @@ -270,9 +270,44 @@ pasting the source for simplicity: =head2 Word wrapping text -=head2 Rotating text +=head2 Shearing (slanting) or Rotating text -=head2 Shearing (slanting) text +This requires that you have Imager installed with Freetype 2.x support +installed, and that the font be created using the Freetype 2.x driver, +for example: + + my $font = Imager::Font->new(file=>$fontfile, type=>'ft2'); + +First you need a transformation matrix, for shearing that could be: + + my $angle_in_radians = ...; + my $tan_angle = sin($angle_rads) / cos($angle_rads); + # shear horizontally, supply this as y instead to do it vertically + my $matrix = Imager::Matrix2d->shear(x=>$tan_angle); + +For rotation that would be: + + my $matrix = Imager::Matrix2d->rotate(radians => $angle_in_radians); + +or: + + my $matrix = Imager::Matrix2d->rotate(degrees => $angle_in_degrees); + +Feed that to the font object: + + $font->transform(matrix => $matrix); + +and draw the text as normal: + + $image->string(string => $text, + x => $where_x, + y => $where_y, + color => $color, + font => $font); + +See samples/slant_text.pl for a comprehensive example, including +calculating the transformed bounding box to create an image to fit the +transformed text into. =head1 IMAGE TRANSFORMATION diff --git a/lib/Imager/Font.pm b/lib/Imager/Font.pm index 5d21a976..d6e802bc 100644 --- a/lib/Imager/Font.pm +++ b/lib/Imager/Font.pm @@ -692,6 +692,12 @@ See the end of the test script t/t38ft2font.t for an example. Currently only the ft2 (Freetype 2.x) driver supports the transform() method. +See samples/slant_text.pl for a sample using this function. + +Note that the transformation is done in font co-ordinates where y +increases as you move up, not image co-ordinates where y decreases as +you move up. + =item has_chars(string=>$text) Checks if the characters in $text are defined by the font. diff --git a/lib/Imager/Matrix2d.pm b/lib/Imager/Matrix2d.pm index d5467b58..84459bb1 100644 --- a/lib/Imager/Matrix2d.pm +++ b/lib/Imager/Matrix2d.pm @@ -291,6 +291,7 @@ newline. I tried to make it fairly nicely formatted. You might disagree :) =cut + sub _string { my ($m) = @_; diff --git a/samples/README b/samples/README index f646e89d..e71a210c 100644 --- a/samples/README +++ b/samples/README @@ -61,3 +61,13 @@ samp-tags.cgi border.pl Example of adding a border to an image. + +slant_text.pl + + Example of drawing transformed text. + + Draws slanted or rotated text to a new image and saves it to a file. + + As part of this it demonstrates calculating the transformed bounding + box of the text. + diff --git a/samples/slant_text.pl b/samples/slant_text.pl new file mode 100644 index 00000000..97bec23d --- /dev/null +++ b/samples/slant_text.pl @@ -0,0 +1,211 @@ +#!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"; + +=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. + +Note: this returns the bounds of the transformed bounding box, in most +cases the actual text will not be touching these boundaries. + +=cut + +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 < | -a + Set the slant angle in degrees, limited to -45 to +45. Default 30. + --size | -s + Set the text size in pixels. Must be 10 or greater. Default: 20. + --foreground | --fg | -f + Set the text foreground color. Default: white. + --background | --bg | -b + 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 AUTHOR + +Tony Cook + +=head1 REVISION + +$Revision$ + +=head1 SEE ALSO + +Imager(1), Imager::Cookbook, Imager::Matrix2d + +=cut