(none) imager-devel
/ help / lists / applications / search /
 

Re: Scaling fonts not working as expected

From: Tony Cook (
20399@xyz.molar.is)
Date: Fri 22 Mar 2013 - 08:23:18 GMT


On Thu, Mar 21, 2013 at 12:52:08PM +0000, Peter Wood wrote:
> Tony,
>
> >
> >I suspect the simplest solution is to find a width that fits within
> >the image (round down a little) and then render character by
> >character, adjusting the output position incrementally to fill the
> >image width.
>
> I'm not sure I understand what you're saying here. I'll restate what I
> think you're saying and you can correct me if I'm wrong. I'd perform my
> size calculation as I currently do, getting the display width of the
> string at the default font size, calculating the amount it exceeds the
> rounded-down width, and then decreasing the font size by this amount.
> Then, instead of rendering the string as a whole, I'd render one character
> at a time using the new font size. I would calculate the width of each
> character and then align the next character just to the right of that, and
> so on, until I'm finished with the string. Thus I would end up with a
> string that fits within the specified width?
>

I've included a CGI version of your script below that demonstrates the
technique.

Tony

#!/usr/bin/perl
use strict;
use CGI;
use Imager;
use Imager::Font;

my $q = CGI->new();

# Some variables to control our test
my $string = $q->param( 'string' ) || 'TEST';
my $image_width = 400;
my $image_height = 400;
my $font_size = 70;
my $font_file = '/opt/rt4/share/fonts/DroidSans.ttf';

# Create a basic imager object to work in
my $image = Imager->new( xsize => $image_width, ysize => $image_height );

# Create a box on the image
$image->box(
    filled => 1,
    color => 'blue'
);

# Set up the font
my $font = Imager::Font->new( file => $font_file, size => $font_size, type => "ft2", color => "#FFF" );

# Find out how wide our test string would be in this font
my $bbox1 = $font->bounding_box( string => $string );
my $initial_width = $bbox1->display_width();
print STDERR "Initial width:$initial_width\n";

# Find out whether the test string is wider than the image
my $total_spacing = 0;
if ( $initial_width > $image_width ) {

    # Find the difference between the string width and the image width
    my $difference = $initial_width - $image_width;

    # Find the percentage the image width is of the initial width,
    # rounding down
    my $percentage = $image_width / $initial_width;

    $font_size = int($font_size * $percentage);
    my $bbox2 = $font->bounding_box( string => $string, size => $font_size );
    my $scaled_width = $bbox2->display_width();
    $total_spacing = $image_width - $scaled_width;
    print STDERR "Scaled width:$scaled_width spacing: $total_spacing\n";
    my $base = ($image_height - $bbox2->text_height) / 2 + $bbox2->ascent;

    my $pos = -$bbox2->left_bearing;
    my $count = 0;
    for my $ch (split //, $string) {
        $image->string
          (
           x => $pos + ($total_spacing * $count / (length($string) - 1)),
           y => $base,
           string => $ch,
           size => $font_size,
           font => $font,
          );

        $pos += $font->bounding_box(string => $ch, size => $font_size)->advance_width;
        ++$count;
    }
}
else {
    # Place the string on the image
    $image->align_string(
      valign => 'center',
      halign => 'center',
      font => $font,
      x => $image->getwidth / 2,
      y => $image->getheight / 2,
      text => $string,
      size => $font_size,
    );
  }

# Print the image out to the browser.
my $output;
$image->write( type => 'png', data => \$output );
binmode STDOUT;
print "Content-Type: image/jpeg\n\n$output";