I'm working on a script to turn HTML + CSS content into rendered images, for an application that doesn't (yet) support CSS directly. To start with, I'm using Advogato's Recent Log output as a test bed for this.

What I'm trying to do, is put a border around each of the text strings I output, with a bit of padding around them. I can figure this out manually, by loading the image up in Gimp and checking the x,y, and applying that value to the rectangle() I draw, but that doesn't scale well, if the user changes font faces or font sizes.

This is as close as I could get, but it puts the border right up against the image text. No way to pad it as I see it.

Also, this can probably be refactored to push the levels and colors into a loop. I'm not good with that kind of construct in perl. Any help there?

Working code so far below:

use warnings; use strict; use GD; use Color::Rgb; # Font you wish to use for this output image my $fontpath = '/usr/share/fonts/truetype/'; my $font = $fontpath . 'ttf-bitstream-vera/VeraSeBd.ttf'; my $rgb = new Color::Rgb(rgb_txt=>'/usr/X11R6/lib/X11/rgb.txt'); # Create a new image my $width = 200; my $height = 200; my $im = new GD::Image($width, $height); $im->colorAllocate(0,0,0); my $white = $im->colorAllocate($rgb->hex2rgb('#ffffff')); my $black = $im->colorAllocate($rgb->hex2rgb('#000000')); # Allocate colors for rated levels # From: http://advogato.org/css/global.css my $level0 = $im->colorAllocate($rgb->hex2rgb('#c1c1c1')); my $level1 = $im->colorAllocate($rgb->hex2rgb('#c0ffc8')); my $level2 = $im->colorAllocate($rgb->hex2rgb('#c0d0ff')); my $level3 = $im->colorAllocate($rgb->hex2rgb('#e0d0ff')); my $level4 = $im->colorAllocate($rgb->hex2rgb('#e0d0c0')); my $level0_bord = $im->colorAllocate($rgb->hex2rgb('#606060')); my $level1_bord = $im->colorAllocate($rgb->hex2rgb('#008000')); my $level2_bord = $im->colorAllocate($rgb->hex2rgb('#2040ff')); my $level3_bord = $im->colorAllocate($rgb->hex2rgb('#8000c0')); my $level4_bord = $im->colorAllocate($rgb->hex2rgb('#804020')); # Make the background transparent and interlaced $im->interlaced('false'); # Create a Border around the image $im->rectangle(0, 0, $width-1, $height-1, $level0_bord); my $x1 = 50; my $y1 = 20; # Can I put all of this in a "smarter" loop? my @bounds = $im->stringFT($level0, $font, 10, 0, $x1, $y1, 'Neophyte' +); # This is ugly, but does work for now. This needs a loop. $bounds[2] += 3; $bounds[3] += 3; $bounds[6] += -5; $bounds[7] += -5; $im->rectangle(@bounds[6,7], @bounds[2,3], $level0_bord); $im->stringFT($level1, $font, 10, 0, $x1, $y1 + 40, 'Apprentice'); $im->stringFT($level2, $font, 10, 0, $x1, $y1 + 80, 'Journeyer'); $im->stringFT($level3, $font, 10, 0, $x1, $y1 + 120, 'Master'); $im->stringFT($level4, $font, 10, 0, $x1, $y1 + 160, 'Undefined'); open(IMG, ">picture.png") or die("Cannot open file!"); binmode IMG; print IMG $im->png; close IMG;
Update: on Apr 5 14:54:17 EDT 2004

I think I solved most of this now.. except the ability to resize the image itself to match the width and height of the text. I think I have to create the image larger than I need, and crop it down, using @bounds. This code shows that, but for some reason, some images are cropped more than others. There doesn't seem to be any reason why. Suggestions?

use warnings; use strict; use GD; use GD::Text; use Color::Rgb; # Font you wish to use for this output image my $fontpath = '/usr/share/fonts/truetype/'; my $font = $fontpath . 'ttf-bitstream-vera/VeraSe.ttf'; my $fontsize = '10'; my $rgb = new Color::Rgb(rgb_txt=>'/usr/X11R6/lib/X11/rgb.txt'); # Create a new image my $width = 200; my $height = 200; # Allocate colors for rated levels # From: http://advogato.org/css/global.css my @advo = ( {name => 'Neophyte', color => '#c1c1c1', border => '#606060'}, {name => 'Apprentice', color => '#c0ffc8', border => '#008000'}, {name => 'Journeyer', color => '#c0d0ff', border => '#2040ff'}, {name => 'Master', color => '#e0d0ff', border => '#8000c0'}, {name => 'Undefined', color => '#e0d0c0', border => '#804020'}, ); for my $certs (@advo) { my $gd_text = GD::Text->new() or die GD::Text::error(); $gd_text->set_font($font, $fontsize) or die $gd_text->error; $gd_text->set_text($certs->{name}); my ($w, $h) = $gd_text->get('width', 'height'); my $im = new GD::Image($w+11, $h+6); my $x1 = 5; my $y1 = 15; $im->colorAllocate($rgb->hex2rgb("$certs->{color}")); my $black = $im->colorAllocate(0,0,0); my $color = $im->colorAllocate($rgb->hex2rgb("$certs->{color} +")); my $border = $im->colorAllocate($rgb->hex2rgb("$certs->{border +}")); my @bounds = $im->stringFT($black, $font, $fontsize, 0, $x1, $ +y1, $certs->{name}); $bounds[2] += 3; $bounds[3] += 3; $bounds[6] += -5; $bounds[7] += -5; $im->rectangle(@bounds[6,7], @bounds[2,3], $border); open(IMG, ">$certs->{name}.png") or die("Cannot open file!"); binmode IMG; print IMG $im->png; close IMG; }

In reply to Automagic text-to-image border calculations by hacker

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.