- added sampled/slant_text.pl, and notes on shearing/rotating text in
authorTony Cook <tony@develop=help.com>
Tue, 25 Oct 2005 07:14:07 +0000 (07:14 +0000)
committerTony Cook <tony@develop=help.com>
Tue, 25 Oct 2005 07:14:07 +0000 (07:14 +0000)
  Imager::Cookbook.

Changes
lib/Imager/Cookbook.pod
lib/Imager/Font.pm
lib/Imager/Matrix2d.pm
samples/README
samples/slant_text.pl [new file with mode: 0644]

diff --git a/Changes b/Changes
index 426ec4c..9e023c3 100644 (file)
--- 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.
 
 =================================================================
 
index 236a94d..160770c 100644 (file)
@@ -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
 
index 5d21a97..d6e802b 100644 (file)
@@ -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.
index d5467b5..84459bb 100644 (file)
@@ -291,6 +291,7 @@ newline.
 I tried to make it fairly nicely formatted.  You might disagree :)
 
 =cut
+
 sub _string {
   my ($m) = @_;
 
index f646e89..e71a210 100644 (file)
@@ -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 (file)
index 0000000..97bec23
--- /dev/null
@@ -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<up>.
+
+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 <<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 AUTHOR
+
+Tony Cook <tony@imager.perl.org>
+
+=head1 REVISION
+
+$Revision$
+
+=head1 SEE ALSO
+
+Imager(1), Imager::Cookbook, Imager::Matrix2d
+
+=cut