I'm doing a project at the moment where I will need to write text on an image using imagettftext, however the text needs an outline and there was no immediate way for me to do this.
In my head there were a couple of possibilities. One was to first write the text in a larger font first with smaller character spacing, however I couldn't find a way to change character spacing without writing each character individually, which would have been a lot of work for me. The second way I thought was to first render the text, then search for and manually colour all pixels within a certain distance of the text, which seemed quite expensive for something that would be run quite frequently.
My final solution was to just render text in the outline colour multiple times in different directions from the text location, and finally render the final text in the middle of that. At first I tried just four outline renders in each of the diagonal directions, however this caused gaps in the outline. The current version also renders orthogonally (making it 8 renders for the outline, and one for the final text) which fills gaps well as long as the outline distance is not too large. Here is an example of my code, which anyone is free to use:
And here is what it looks like:

In my head there were a couple of possibilities. One was to first write the text in a larger font first with smaller character spacing, however I couldn't find a way to change character spacing without writing each character individually, which would have been a lot of work for me. The second way I thought was to first render the text, then search for and manually colour all pixels within a certain distance of the text, which seemed quite expensive for something that would be run quite frequently.
My final solution was to just render text in the outline colour multiple times in different directions from the text location, and finally render the final text in the middle of that. At first I tried just four outline renders in each of the diagonal directions, however this caused gaps in the outline. The current version also renders orthogonally (making it 8 renders for the outline, and one for the final text) which fills gaps well as long as the outline distance is not too large. Here is an example of my code, which anyone is free to use:
<?php
/**
* Writes text using imagettftext with an outline around characters
* @author Michael Alexander http://beefsack.com
*/
function imagettftextoutline(
$image,
$size,
$angle,
$x,
$y,
$color,
$fontfile,
$text,
$outlinewidth = 1, // 1px outline
$outlinecolor = 0 // black
) {
// First offset diagonally
imagettftext($image, $size, $angle, $x - $outlinewidth,
$y - $outlinewidth, $outlinecolor, $fontfile, $text);
imagettftext($image, $size, $angle, $x - $outlinewidth,
$y + $outlinewidth, $outlinecolor, $fontfile, $text);
imagettftext($image, $size, $angle, $x + $outlinewidth,
$y - $outlinewidth, $outlinecolor, $fontfile, $text);
imagettftext($image, $size, $angle, $x + $outlinewidth,
$y + $outlinewidth, $outlinecolor, $fontfile, $text);
// Then offset orthogonally
imagettftext($image, $size, $angle, $x - $outlinewidth, $y,
$outlinecolor, $fontfile, $text);
imagettftext($image, $size, $angle, $x + $outlinewidth, $y,
$outlinecolor, $fontfile, $text);
imagettftext($image, $size, $angle, $x, $y - $outlinewidth,
$outlinecolor, $fontfile, $text);
imagettftext($image, $size, $angle, $x, $y + $outlinewidth,
$outlinecolor, $fontfile, $text);
// Output text
imagettftext($image, $size, $angle, $x, $y, $color, $fontfile,
$text);
}
// Set the content-type
header('Content-type: image/png');
// Create the image
$im = imagecreatetruecolor(400, 30);
// Create some colors
$white = imagecolorallocate($im, 255, 255, 255);
$grey = imagecolorallocate($im, 128, 128, 128);
$black = imagecolorallocate($im, 0, 0, 0);
imagefilledrectangle($im, 0, 0, 399, 29, $grey);
// The text to draw
$text = 'Testing...';
// Replace path by your own font path
$font = 'arial.ttf';
// Output text with outline
imagettftextoutline($im, 20, 0, 10, 22, $white, $font, $text, 2);
// Using imagepng() results in clearer text compared with imagejpeg()
imagepng($im);
imagedestroy($im);And here is what it looks like:
Tags: beefsack.com
beefsack on 2009-05-07 09:37:21
Today I've been doing a fair bit of work on the beefsack.com backend. One major change was to change every database query to use Zend_Db_Select objects, which has dramatically cleaned up some code and has made some of my more complex SQL statements a lot easier to manage.
I have also disabled Dojo for now due to performance, and have been quite unhappy about how the Dojo Editor outputs. It outputs with some odd characters, and the fact that it converts html safe characters if content is reloaded to the editor is a bit of a security concern for me. It is possible to run scripts and other code when reloading text into the editor, and the only way to stop it is to check and escape input every time it is loaded.
I've decided instead of having a rich text editor that outputs HTML, I'll just use BBCode markup for now and switch to a rich editor again when a better solution comes around.
It's odd, I am much more comfortable typing this up in plain text than in a Javascript editor, I guess that's the nerd in me taking over. Mind you, TinyMCE looks cool, I might give it a spin sometime.
I have also disabled Dojo for now due to performance, and have been quite unhappy about how the Dojo Editor outputs. It outputs with some odd characters, and the fact that it converts html safe characters if content is reloaded to the editor is a bit of a security concern for me. It is possible to run scripts and other code when reloading text into the editor, and the only way to stop it is to check and escape input every time it is loaded.
I've decided instead of having a rich text editor that outputs HTML, I'll just use BBCode markup for now and switch to a rich editor again when a better solution comes around.
It's odd, I am much more comfortable typing this up in plain text than in a Javascript editor, I guess that's the nerd in me taking over. Mind you, TinyMCE looks cool, I might give it a spin sometime.
Tags: beefsack.com
beefsack on 2009-05-07 04:03:45
I spent this morning writing a simple parser for BBCode style tags on beefsack.com. The reason for this is I wanted to add the ability to post monospace properly formatted code in my blog posts, and wanted to do it without writing a Dojo Editor plugin which wouldn't be flexible enough for me.
I found some great proposed components on the way for Zend, being Zend_Markup which seems to be maturing well, and Zend_Syntax_Highlight, which unfortunately hasn't been updated for a while.
Zend_Markup is meant to "provide an extensible way to tokenize and render lightweight markup languages, like BBcode and Textile." Ideally, I would want my Dojo Editor box to output BBCode instead of HTML, and have a parser convert it as needed. This would be particularly handy for me when I write the forums for this site as I want to use BBCode for that as is the standard for most forums around.
I noticed in the code for Zend_Markup they seem to use the PHP function highlight_string if it is passed PHP as a lang attribute. I feel that if a component like Zend_Syntax_Highlight was completed, it could make Zend_Markup very powerful on coding related sites and forums, being able to pass different lang attributes depending on the code that is inside the lang tags. Anyway, I've written a simple parser to detect BBCode style code tags so I'm able to post code on the blog. Here's what it looks like:
I found some great proposed components on the way for Zend, being Zend_Markup which seems to be maturing well, and Zend_Syntax_Highlight, which unfortunately hasn't been updated for a while.
Zend_Markup is meant to "provide an extensible way to tokenize and render lightweight markup languages, like BBcode and Textile." Ideally, I would want my Dojo Editor box to output BBCode instead of HTML, and have a parser convert it as needed. This would be particularly handy for me when I write the forums for this site as I want to use BBCode for that as is the standard for most forums around.
I noticed in the code for Zend_Markup they seem to use the PHP function highlight_string if it is passed PHP as a lang attribute. I feel that if a component like Zend_Syntax_Highlight was completed, it could make Zend_Markup very powerful on coding related sites and forums, being able to pass different lang attributes depending on the code that is inside the lang tags. Anyway, I've written a simple parser to detect BBCode style code tags so I'm able to post code on the blog. Here's what it looks like:
<?php
function thisIsAFunction() {
for ($i = 1; $i < 10; $i++) {
echo "Something $i";
}
}
?>
Which will work in the meantime until Zend_Markup is in an official release.Tags: beefsack.com
beefsack on 2009-05-05 07:09:33
After unloading a truck full of beer at work and digging some holes in the garden, I got around to doing some coding this afternoon.
This afternoon I was playing around with a new Zend component for me, Zend_Feed. Zend_Feed can be used for reading and creating Atom and RSS feeds, and I have used it to aggregate content on beefsack.com.
If your browser supports it, you should see an RSS link on each page which can be used to receive updates to the site. If viewing a specific tag or project, the RSS feed will be tailored to only receive updates from that tag or project.
Zend_Feed has been proof to me once again how flexible Zend is, as I was able to implement it with very few lines of code and virtually no modification to code I had already written. I just created a new controller for RSS, and edited my layout to link to the feed.
Next step, commenting on blog posts, which should be a decent sized update.
This afternoon I was playing around with a new Zend component for me, Zend_Feed. Zend_Feed can be used for reading and creating Atom and RSS feeds, and I have used it to aggregate content on beefsack.com.
If your browser supports it, you should see an RSS link on each page which can be used to receive updates to the site. If viewing a specific tag or project, the RSS feed will be tailored to only receive updates from that tag or project.
Zend_Feed has been proof to me once again how flexible Zend is, as I was able to implement it with very few lines of code and virtually no modification to code I had already written. I just created a new controller for RSS, and edited my layout to link to the feed.
Next step, commenting on blog posts, which should be a decent sized update.
Tags: sacley.com
beefsack on 2009-05-03 05:15:46
Today I'm adding quite an old project I helped out on, which was converting my friend's site sacley.com to PHP to make updates easier and add some functionality.
For me, it was an interesting project, as it was the first time I had taken a static website and converted it to a server side language like PHP. I got my hands dirty with PHP's image functions and was able to make some cool code that allowed my friend to select a region of the uploaded pictures to serve as thumbnails, which were then generated dynamically from the original image.
I also wrote the authentication system, and wrote a public BBS, which is not currently available as it got hit by spam fairly hard. Certainly a lesson in spam for me.
For me, it was an interesting project, as it was the first time I had taken a static website and converted it to a server side language like PHP. I got my hands dirty with PHP's image functions and was able to make some cool code that allowed my friend to select a region of the uploaded pictures to serve as thumbnails, which were then generated dynamically from the original image.
I also wrote the authentication system, and wrote a public BBS, which is not currently available as it got hit by spam fairly hard. Certainly a lesson in spam for me.
Today I'm adding a new project that I did in a few hours late last year called IPRange. I wrote it to help Dad out at work as he had a huge amount of IP ranges he had to check.
IPRange is a fairly simple C++ glib app, only a couple of hundred lines long, that takes a text file input with IP address ranges and it checks for any overlap. The cool thing about IPRange is it takes in a variety of formats, from a single address to wild cards, specific ranges and CIDR ranges.
The project was particularly interesting for me as I'd written a couple of GTK+ apps before and was interested to see glib. Also, I never really understood how CIDR worked, and there is no better way than to have to implement it in some sort of way.
I have uploaded the source of version 0.2, which is licensed under GPL, which can be found on the project page. I was able to compile it under Windows using mingw, and will upload the binary when I can find it again or have the time to set up mingw again under one of my Windows installs. The project is now inactive and probably wont be updated again, as its usefulness is limited and it is feature complete for what I need it for.
IPRange is a fairly simple C++ glib app, only a couple of hundred lines long, that takes a text file input with IP address ranges and it checks for any overlap. The cool thing about IPRange is it takes in a variety of formats, from a single address to wild cards, specific ranges and CIDR ranges.
The project was particularly interesting for me as I'd written a couple of GTK+ apps before and was interested to see glib. Also, I never really understood how CIDR worked, and there is no better way than to have to implement it in some sort of way.
I have uploaded the source of version 0.2, which is licensed under GPL, which can be found on the project page. I was able to compile it under Windows using mingw, and will upload the binary when I can find it again or have the time to set up mingw again under one of my Windows installs. The project is now inactive and probably wont be updated again, as its usefulness is limited and it is feature complete for what I need it for.
Tags: beefsack.com
beefsack on 2009-05-01 04:39:33
The final feature for me to complete on beefsack.com so that I could upload all my projects was a file upload feature. I wanted to implement a simple file management system so files could be archived if they aren't the current release.
I was quite excited to have a play with Zend_File which is quite powerful, and integrates without any fuss into Zend_Form. This made life extremely easy and has built in methods for getting a lot of information from the uploaded file, which I am then able to insert into the database.
With adding the simple file management, I have been able to finish my first milestone on beefsack.com. For my next milestone, I would like to implement the ability for readers to comment on blog posts, which would be the first step for me to writing simple integrated forums for my projects.
I was quite excited to have a play with Zend_File which is quite powerful, and integrates without any fuss into Zend_Form. This made life extremely easy and has built in methods for getting a lot of information from the uploaded file, which I am then able to insert into the database.
With adding the simple file management, I have been able to finish my first milestone on beefsack.com. For my next milestone, I would like to implement the ability for readers to comment on blog posts, which would be the first step for me to writing simple integrated forums for my projects.
Tags: batteredsav.com
beefsack on 2009-04-26 08:47:14
Today I'm adding an old project I did with my friend Steve about Battered Savs, the website batteredsav.com.
For those that have not heard of them before, a Battered Sav is a Saveloy (delicious spicy sausage) that is battered and deep fried. A very good sav with salt to taste can be amazing, and they contributed to my decline in health upon returning to Australia from Japan in 2005.
We wrote the site from the ground up, which was a fair bit of work but very rewarding. Steve and I wrote a rather secure authentication system, and also made use of PHP's image manipulation capabilities and mail functions.
Nowadays we don't update the site very often, but it still gets a large number of hits off Google. It's interesting because every now and then we will get an email from someone who has enjoyed the site and are always keen for a yarn about the good old sav.
For those that have not heard of them before, a Battered Sav is a Saveloy (delicious spicy sausage) that is battered and deep fried. A very good sav with salt to taste can be amazing, and they contributed to my decline in health upon returning to Australia from Japan in 2005.
We wrote the site from the ground up, which was a fair bit of work but very rewarding. Steve and I wrote a rather secure authentication system, and also made use of PHP's image manipulation capabilities and mail functions.
Nowadays we don't update the site very often, but it still gets a large number of hits off Google. It's interesting because every now and then we will get an email from someone who has enjoyed the site and are always keen for a yarn about the good old sav.
Tags: minibgg.com
beefsack on 2009-04-26 07:49:10
It's been a while since I have updated minibgg, but the people here have stumbled across a bug where the search breaks if there is an apostrophe in the search.
I will have a look at it over the next couple of days and hopefully have it working again as it should. I should also get play logging sorted out again soon too, as that was a very handy feature before the big boardgamegeek update.
I will have a look at it over the next couple of days and hopefully have it working again as it should. I should also get play logging sorted out again soon too, as that was a very handy feature before the big boardgamegeek update.
Tags: beefsack.com
beefsack on 2009-04-22 11:06:58
Today I've been able to upload the changes that have been made to beefsack.com since I did my initial upload.
The big change, especially in my eyes, has been to get the CSS looking how it should in IE6+. I got very close to doing it without any strange hacks, floats were all doing what they were meant to and everything looked fairly good, except for a couple of problems...
For some reason IE6 and IE7 were ignoring a couple of my div top and bottom margins (though it was all working in IE8). The fix? For testing, I set the following CSS to see how margins and padding were looking:
And magically, everything aligned and the margins were recognised correctly! After hours of googling and playing around I couldn't find a real fix, so in my CSS you will find that code, however the border colour has been set to white. The border style has to be a visible style too, I tried using hidden instead of solid but the problem came back.
The other visible change is the addition of a links section to project pages. Next step is to have project downloads fully functional and the feature set I had planned for my initial release will be complete.
The big change, especially in my eyes, has been to get the CSS looking how it should in IE6+. I got very close to doing it without any strange hacks, floats were all doing what they were meant to and everything looked fairly good, except for a couple of problems...
For some reason IE6 and IE7 were ignoring a couple of my div top and bottom margins (though it was all working in IE8). The fix? For testing, I set the following CSS to see how margins and padding were looking:
div {
border: 1px solid black;
}And magically, everything aligned and the margins were recognised correctly! After hours of googling and playing around I couldn't find a real fix, so in my CSS you will find that code, however the border colour has been set to white. The border style has to be a visible style too, I tried using hidden instead of solid but the problem came back.
The other visible change is the addition of a links section to project pages. Next step is to have project downloads fully functional and the feature set I had planned for my initial release will be complete.