<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>

<channel>
	<title>Bubble Foundry</title>
	<atom:link href="http://www.bubblefoundry.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.bubblefoundry.com</link>
	<description></description>
	<pubDate>Mon, 23 Jun 2008 13:27:04 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.5</generator>
	<language>en</language>
			<item>
		<title>Loading Javascript Libraries When Needed</title>
		<link>http://www.bubblefoundry.com/blog/2008/06/loading-javascript-libraries-when-needed/</link>
		<comments>http://www.bubblefoundry.com/blog/2008/06/loading-javascript-libraries-when-needed/#comments</comments>
		<pubDate>Mon, 23 Jun 2008 13:27:04 +0000</pubDate>
		<dc:creator>Peter</dc:creator>
		
		<category><![CDATA[Blog]]></category>

		<category><![CDATA[From the Laboratory]]></category>

		<category><![CDATA[Javascript]]></category>

		<category><![CDATA[Prototype]]></category>

		<category><![CDATA[widgets]]></category>

		<guid isPermaLink="false">http://www.bubblefoundry.com/?p=33</guid>
		<description><![CDATA[Here is a nice little Javascript function to load some Javascript libraries:

function includeLibs()
{
var libs = [{object: 'Prototype', src: 'http://www.mobypicture.com/slideshow/prototype.js'}, {object: 'Lightbox', src: 'http://www.mobypicture.com/slideshow/lightbox.js'}];
for (var k = 0; k &#60; libs.length; k++)
{
try
{
var obj = eval(libs[k].object);
}
catch (err)
{
var obj = false;
}
if (obj == false)
{
var newjs=document.createElement('script');
newjs.type='text/javascript';
newjs.src=libs[k].src;
document.getElementsByTagName('head')[0].appendChild(newjs);
}
}
}
This is particularly useful if you&#8217;re writing a widget that requires external libraries but [...]]]></description>
			<content:encoded><![CDATA[<p>Here is a nice little Javascript function to load some Javascript libraries:<br />
<code><br />
function includeLibs()<br />
{<br />
var libs = [{object: 'Prototype', src: 'http://www.mobypicture.com/slideshow/prototype.js'}, {object: 'Lightbox', src: 'http://www.mobypicture.com/slideshow/lightbox.js'}];<br />
for (var k = 0; k &lt; libs.length; k++)<br />
{<br />
try<br />
{<br />
var obj = eval(libs[k].object);<br />
}<br />
catch (err)<br />
{<br />
var obj = false;<br />
}<br />
if (obj == false)<br />
{<br />
var newjs=document.createElement('script');<br />
newjs.type='text/javascript';<br />
newjs.src=libs[k].src;<br />
document.getElementsByTagName('head')[0].appendChild(newjs);<br />
}<br />
}<br />
}</code><br />
This is particularly useful if you&#8217;re writing a widget that requires external libraries but the user might already have them loaded, such as Prototype.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bubblefoundry.com/blog/2008/06/loading-javascript-libraries-when-needed/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Multilink</title>
		<link>http://www.bubblefoundry.com/blog/2008/06/multilink/</link>
		<comments>http://www.bubblefoundry.com/blog/2008/06/multilink/#comments</comments>
		<pubDate>Sat, 21 Jun 2008 17:26:50 +0000</pubDate>
		<dc:creator>Peter</dc:creator>
		
		<category><![CDATA[Blog]]></category>

		<category><![CDATA[From the Laboratory]]></category>

		<category><![CDATA[Javascript]]></category>

		<category><![CDATA[Multilink]]></category>

		<category><![CDATA[Prototip]]></category>

		<category><![CDATA[Prototype]]></category>

		<guid isPermaLink="false">http://www.bubblefoundry.com/?p=31</guid>
		<description><![CDATA[Multilink is a very simple Javascript library that creates tooltips with multiple links per &#8216;normal&#8217; link, its simplicity due to Prototip2 and Prototype. Why have one link when you can have ten! Check out the Multilink page for an example.
]]></description>
			<content:encoded><![CDATA[<p><a href="http://labs.bubblefoundry.com/multilink/">Multilink</a> is a <em>very</em> simple Javascript library that creates tooltips with multiple links per &#8216;normal&#8217; link, its simplicity due to <a href="http://www.nickstakenburg.com/projects/prototip2/">Prototip2</a> and <a href="http://www.prototypejs.org">Prototype</a>. Why have one link when you can have ten! Check out the <a href="http://labs.bubblefoundry.com/multilink/">Multilink</a> page for an example.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bubblefoundry.com/blog/2008/06/multilink/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Repairing SVN Working Copies</title>
		<link>http://www.bubblefoundry.com/blog/2008/05/repairing-svn-working-copies/</link>
		<comments>http://www.bubblefoundry.com/blog/2008/05/repairing-svn-working-copies/#comments</comments>
		<pubDate>Fri, 30 May 2008 12:33:11 +0000</pubDate>
		<dc:creator>Peter</dc:creator>
		
		<category><![CDATA[Blog]]></category>

		<category><![CDATA[directories]]></category>

		<category><![CDATA[repair]]></category>

		<category><![CDATA[Subversion]]></category>

		<category><![CDATA[SVN]]></category>

		<category><![CDATA[working copies]]></category>

		<guid isPermaLink="false">http://www.bubblefoundry.com/?p=30</guid>
		<description><![CDATA[Unfortunately an all too common problem with Subversion is accidentally deleting the .svn directory used to track changes on a working copy. The accepted solution seems to be to check out to somewhere else the directory to which .svn belongs and then copy the new .svn directory into where the working copy&#8217;s should have been.
I [...]]]></description>
			<content:encoded><![CDATA[<p>Unfortunately an all too common problem with Subversion is accidentally deleting the <code>.svn</code> directory used to track changes on a working copy. The accepted solution seems to be to check out to somewhere else the directory to which <code>.svn</code> belongs and then copy the new <code>.svn</code> directory into where the working copy&#8217;s should have been.</p>
<p>I wrote the following bash script to automate this repair process:</p>
<pre>#!/bin/bash

if [ "$1" == "help" ]; then
  echo "Useage: svnrepair repository_dir local_dir"
  exit 1;
fi

if [ -n $1 ] || [ "$1" == "." ]; then
  repo_dir=`svn info | awk '/URL: (.*?)/ {print $2}'`
else
  repo_dir=$1
fi

if [ -n $2 ] || [ $2 == "." ]; then
  local_dir=`pwd`
else
  local_dir=$2
fi

svn co $repo_dir delme; cp -r delme/.svn $local_dir; rm -rf delme</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.bubblefoundry.com/blog/2008/05/repairing-svn-working-copies/feed/</wfw:commentRss>
		</item>
		<item>
		<title>From the Laboratory: Closed Standards</title>
		<link>http://www.bubblefoundry.com/blog/2008/05/closed-standards/</link>
		<comments>http://www.bubblefoundry.com/blog/2008/05/closed-standards/#comments</comments>
		<pubDate>Mon, 26 May 2008 23:44:58 +0000</pubDate>
		<dc:creator>Peter</dc:creator>
		
		<category><![CDATA[Blog]]></category>

		<category><![CDATA[From the Laboratory]]></category>

		<category><![CDATA[3GPP]]></category>

		<category><![CDATA[AMR]]></category>

		<category><![CDATA[FFMPEG]]></category>

		<category><![CDATA[licenses]]></category>

		<category><![CDATA[openess]]></category>

		<category><![CDATA[patents]]></category>

		<category><![CDATA[standards]]></category>

		<guid isPermaLink="false">http://www.bubblefoundry.com/?p=29</guid>
		<description><![CDATA[Closed standards are an oxymoron and any standard that is closed is moronic.
This is going to be a rant, so bear with me&#8230;.
How can you call anything a standard when you don&#8217;t make the standard readily available? What is the point of a standard that no one can implement? Unfortunately, many &#8217;standardized&#8217; audio and video [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Closed standards are an oxymoron and any standard that is closed is moronic.</strong></p>
<p>This is going to be a rant, so bear with me&#8230;.</p>
<p>How can you call anything a <em>standard</em> when you don&#8217;t make the standard readily available? What is the point of a standard that no one can implement? Unfortunately, many &#8217;standardized&#8217; audio and video <a href="http://en.wikipedia.org/wiki/Codec">codecs</a> are encumbered by patents, trademarks, and obscenely expensive reference documents. Surprise, surprise, but many of the telco standards bodies are the worst offenders! (As an aside, the movie people, thanks to <a href="http://en.wikipedia.org/wiki/Mpeg">MPEG</a>, seem to be <em>somewhat</em> more sensible).</p>
<p><span id="more-29"></span>Let&#8217;s take <a href="http://www.3gpp.org/">3GPP</a>, the industry standards body for 3G mobile phone services. We won&#8217;t even go into their website, though needless to say it isn&#8217;t helping things. For one, it&#8217;s basically impossible to find anything, such as the all important reference libraries for their standards. As part of my work developing the new <a href="http://www.mobypicture.com">MobyPicture</a> site, I need to implement support for common mobile phone audio and video formats, including <a href="http://en.wikipedia.org/wiki/Adaptive_multi-rate_compression">AMR</a> audio and <a href="http://en.wikipedia.org/wiki/3GP">3GPP</a> video container files.</p>
<p>Like many others, I am using <a href="http://ffmpeg.mplayerhq.hu/">FFMPEG</a>, a fantastic video and audio transcoder. Various codecs and formats are enabled by compiling the relevant libraries and then compiling FFMPEG with reference to them. Great, so let&#8217;s enable AMR support! Oh, wait, where&#8217;s an AMR library we can use? Unfortunately the only real option seems to still be the reference implementation, perhaps from no fault of 3GPP, though I don&#8217;t think they&#8217;ve made <a href="http://rob.opendot.cl/index.php/category/amr/">things</a> <a href="http://wiki.multimedia.cx/index.php?title=AMR-NB">easy</a>. Fine, so let&#8217;s download it and we&#8217;re golden! But where is it? I dare you to find it just browsing the 3GPP site. So you resort to Google and you find various <a href="http://flixforums.com/archive/index.php/t-240.html">whisperings</a> of the secret URLs. But wouldn&#8217;t it be better if projects like FFMPEG just bundled the library source code or, even better, the compiled binary files?</p>
<p>Of course it would, so of course we can&#8217;t have that. For instance, the Word document file included with the reference implementation has a legal license prohibiting ALL reproduction without written authorization. For fraks sakes, it&#8217;s a document just saying what the code is doing, it&#8217;s not top secret or even trade secrets! You make the files freely (though invisibly) available on your servers! But wait, they do contain trade secrets: the reference implementation uses patented technology which people can&#8217;t, in some countries, use without a license. That means that they&#8217;re basically saying: &#8220;This is our standard and here is the code implementing it. But it has patented technology we want you to pay for. So really, to use this reference code you need to pay us money.&#8221; It&#8217;s the look-maybe-if-we-like-you-but-don&#8217;t-touch form of open source! The next best thing to precompiled libraries? A very useful but ugly work-around: write a script without the libraries, so it can be freely distributed, that will <a href="http://www.penguin.cz/~utx/amr">download and then compile them</a>, leaving the patent licensing issues to the final user or developer.</p>
<p>And unsurprisingly, as if a parody of viral licenses such as the GPL, this patented library infects the code that uses it. Enabling AMR support in FFMPEG requires me to pass &#8220;&#8211;enable-nonfree&#8221; to the configure script and means that the resulting compiled binary is technically, legally, <a href="http://archives.free.net.ph/message/20080126.132816.7f6d0063.en.html">nonredistributable</a>. It&#8217;s little surprise that FFMPEG developers <a href="http://archives.free.net.ph/thread/20080127.003734.3ebc3674.en.html">have been tempted</a> to not bother maintaining support.</p>
<p>Why would you charge people to license a standard? That is, why would you seek to have your patented technology used in a standard when you plan (on continuing) to seek royalty payments? That&#8217;s defeating the whole point of a standard, where everyone can and should follow the standard. Instead it&#8217;s pay-to-play. Of course these patent costs are chump change to the likes of Nokia and so on who are members of 3GPP (and will all license some patent or another to each other eventually) but they aren&#8217;t, actually, the best way to encourage adoption of a standard. Remember, people like mp3 format audio not because of its audio quality but because of its small file sizes and, thanks to generally liberal licensing, its implementation on just about any sort of device you could imagine.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bubblefoundry.com/blog/2008/05/closed-standards/feed/</wfw:commentRss>
		</item>
		<item>
		<title>From the Laboratory: IP2FireEagle</title>
		<link>http://www.bubblefoundry.com/blog/2008/05/from-the-laboratory-ip2fireeagle/</link>
		<comments>http://www.bubblefoundry.com/blog/2008/05/from-the-laboratory-ip2fireeagle/#comments</comments>
		<pubDate>Thu, 22 May 2008 23:03:00 +0000</pubDate>
		<dc:creator>Peter</dc:creator>
		
		<category><![CDATA[Blog]]></category>

		<category><![CDATA[From the Laboratory]]></category>

		<category><![CDATA[Fire Eagle]]></category>

		<category><![CDATA[IP]]></category>

		<category><![CDATA[location]]></category>

		<category><![CDATA[status]]></category>

		<category><![CDATA[Yahoo]]></category>

		<guid isPermaLink="false">http://www.bubblefoundry.com/?p=28</guid>
		<description><![CDATA[IP2FireEagle is small PHP script I wrote which will update your location in Yahoo Fire Eagle based upon your current IP address. You can find more information on the IP2FireEagle page.
]]></description>
			<content:encoded><![CDATA[<p><a href="http://labs.bubblefoundry.com/ip2fireeagle/">IP2FireEagle</a> is small PHP script I wrote which will update your location in Yahoo <a href="http://fireeagle.yahoo.net/">Fire Eagle</a> based upon your current IP address. You can find more information on the IP2FireEagle page.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bubblefoundry.com/blog/2008/05/from-the-laboratory-ip2fireeagle/feed/</wfw:commentRss>
		</item>
		<item>
		<title>BarCamps</title>
		<link>http://www.bubblefoundry.com/blog/2008/04/barcamps/</link>
		<comments>http://www.bubblefoundry.com/blog/2008/04/barcamps/#comments</comments>
		<pubDate>Fri, 11 Apr 2008 13:56:36 +0000</pubDate>
		<dc:creator>Peter</dc:creator>
		
		<category><![CDATA[Blog]]></category>

		<category><![CDATA[BarCamp]]></category>

		<category><![CDATA[BarCamp Amsterdam IV]]></category>

		<category><![CDATA[unconferences]]></category>

		<guid isPermaLink="false">http://www.bubblefoundry.com/?p=25</guid>
		<description><![CDATA[I apologize for writing this after the fact, but last Saturday I was proud to host BarCamp Amsterdam IV at my office. The event was a fabulous success, with people from six countries (Netherlands, Belgium, UK, Sweden, Finland, and Romania) attending, including several startup founders who had presented at The Next Web Conference. Building on [...]]]></description>
			<content:encoded><![CDATA[<p>I apologize for writing this after the fact, but last Saturday I was proud to host <a href="http://barcamp.org/BarCampAmsterdamIV">BarCamp Amsterdam IV</a> at my office. The event was a fabulous success, with people from six countries (Netherlands, Belgium, UK, Sweden, Finland, and Romania) attending, including several startup founders who had presented at <a href="http://2008.thenextweb.org">The Next Web Conference</a>. Building on that success, I will be hosting a <a href="http://barcamp.pbwiki.com/Health20UnconferenceAmsterdam">Health 2.0 BarCamp</a> <strong>tomorrow</strong> and <a href="http://barcamp.pbwiki.com/BarcampNLGovweb">BarCamp NLGovWeb</a> in June.</p>
<p>And for those who don&#8217;t know <a href="http://en.wikipedia.org/wiki/BarCamp">what a BarCamp is</a>, it is an &#8216;unconference&#8217; where attendees make the schedule and everyone is encouraged to participate in presentations. I highly recommend attending one, so if you&#8217;re free, come by tomorrow or in June!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bubblefoundry.com/blog/2008/04/barcamps/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Startups In The Netherlands?</title>
		<link>http://www.bubblefoundry.com/blog/2008/04/startups-in-the-netherlands/</link>
		<comments>http://www.bubblefoundry.com/blog/2008/04/startups-in-the-netherlands/#comments</comments>
		<pubDate>Thu, 10 Apr 2008 11:24:41 +0000</pubDate>
		<dc:creator>Peter</dc:creator>
		
		<category><![CDATA[Blog]]></category>

		<category><![CDATA[Netherlands]]></category>

		<category><![CDATA[startups]]></category>

		<category><![CDATA[Web 2.0]]></category>

		<guid isPermaLink="false">http://www.bubblefoundry.com/?p=24</guid>
		<description><![CDATA[Yes, there are startups in the Netherlands, as proven by the many in attendance at The Next Web last week. I&#8217;ve been gathering a list over the last month, and I now how a list of almost 40 companies. I&#8217;ll admit that CrunchBase coming to Europe has also forced my hand. So please, check it [...]]]></description>
			<content:encoded><![CDATA[<p>Yes, there are startups in the Netherlands, as proven by the many in attendance at <a href="http://2008.thenextweb.org">The Next Web</a> last week. I&#8217;ve been gathering a list over the last month, and I now how a <a href="http://www.bubblefoundry.com/dutch-web-companies/">list</a> of almost 40 companies. I&#8217;ll admit that <a href="http://www.crunchbase.com/">CrunchBase</a> <a href="http://uk.techcrunch.com/2008/04/10/launching-crunchbase-in-europe/">coming to Europe</a> has also forced my hand. So please, <a href="http://www.bubblefoundry.com/dutch-web-companies/">check it out</a> and tell me who I&#8217;ve missed!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bubblefoundry.com/blog/2008/04/startups-in-the-netherlands/feed/</wfw:commentRss>
		</item>
		<item>
		<title>From the Laboratory: Image Resizing and Thumbnail Creation</title>
		<link>http://www.bubblefoundry.com/blog/2008/02/from-the-laboratory-image-resizing-and-thumbnail-creation/</link>
		<comments>http://www.bubblefoundry.com/blog/2008/02/from-the-laboratory-image-resizing-and-thumbnail-creation/#comments</comments>
		<pubDate>Sun, 24 Feb 2008 21:44:26 +0000</pubDate>
		<dc:creator>Peter</dc:creator>
		
		<category><![CDATA[Blog]]></category>

		<category><![CDATA[From the Laboratory]]></category>

		<category><![CDATA[Bubble Foundry Labs]]></category>

		<category><![CDATA[GD]]></category>

		<category><![CDATA[images]]></category>

		<category><![CDATA[PHP]]></category>

		<category><![CDATA[PIL]]></category>

		<category><![CDATA[Python]]></category>

		<category><![CDATA[Python Imaging Library]]></category>

		<category><![CDATA[resizing]]></category>

		<category><![CDATA[thumbnails]]></category>

		<guid isPermaLink="false">http://www.bubblefoundry.com/blog/2008/02/from-the-laboratory-image-resizing-and-thumbnail-creation/</guid>
		<description><![CDATA[This is the first in a series of posts on technical issues related to web site development and making user-friendly websites. These will focus on the technical details of developing web sites and applications so will probably be interesting to only a subset of readers. Within the next few months, time permitting, we will be [...]]]></description>
			<content:encoded><![CDATA[<p><em>This is the first in a series of posts on technical issues related to web site development and making user-friendly websites. These will focus on the technical details of developing web sites and applications so will probably be interesting to only a subset of readers. Within the next few months, time permitting, we will be launching a new section of the site called Bubble Foundry Labs, where you will be able to find both technical articles and experimental web applications. For non-technical types, these demo applications may prove easier to understand than this article series. </em></p>
<p><span id="more-22"></span><br />
In this post I will discuss how to take a user-uploaded image and resize it, presumably reducing it so as to create a thumbnail. I will try to discuss the methods in language-neutral terms, though occasionally I will give examples in PHP and Python. This is an important topic, as good content management systems and other web-publishing systems should let users interact with images with a minimum of fuss.</p>
<h3>The Logic of Thumbnails</h3>
<p align="left">First, the question &#8216;Why create thumbnails at all?&#8217; must be answered. In <code>img</code> HTML tags you can set both the width and height. So you can create smaller images without creating more files! But no, the image may <em>look</em> smaller but it still is the same size (in kb) as the original, for it is the original. In doing so you have made a trade-off, and a bad one: the server saves minuscule amounts of processing time and disk space while causing increased bandwidth demands for the site, and thus web site visitors. Essentially the work of creating thumbnails is offloaded to the client&#8217;s computer and internet connection, hardly the most user friendly choice!</p>
<p align="left">The next issue to address is when to create a thumbnail. There are several different approaches: when the original image is uploaded, when the thumbnail is first requested, or every time the thumbnail is requested. There are advantages to every approach, though I generally subscribe to the first one. I will briefly highlight the advantages and disadvantages of the other methods. Creating a thumbnail when needed, either the first time or every time, reduces initial processing and disk space requirements. Generating a thumbnail when requested means that thumbnail sizes can change over time without the need to update existing thumbnails. However, outside of scientific computing there are few if any websites dealing with images that are hundreds of megabytes each. Processing time and disk space are cheap, a user&#8217;s time is not. Therefore I believe it is poor choice.</p>
<p align="left">So we come to my preferred method, creating thumbnails when the initial image is uploaded. This provides some assurance about thumbnail locations (once the image is there, it is always there) and minimizes user inconvenience. The uploading user is more tolerant of somewhat limited responsiveness because they are aware that a new image is being added to the web site.</p>
<h3>Saving an Uploaded Image</h3>
<p align="left">So, how do we actually create a thumbnail? First, we take the uploaded file and save it to our destination for original images.  In PHP the uploaded file is stored in <code>$_FILES['<em>variable_name</em>&#8216;]</code>, while in the <a href="http://pylonshq.com/">Pylons</a> Python framework it is <code>request.POST['<em>variable_name</em>&#8216;]</code>. In PHP I use the function <code><a href="http://nl3.php.net/manual/en/function.move-uploaded-file.php">move_uploaded_file()</a></code> to move the file from a temporary location to its destination, while in Python I open the destination file for writing (<code>permanent_image = open(<em>destination</em>, &#8216;w&#8217;)</code>) and then copy the uploaded file to it (<code>shutil.copyfileobj(image_file.file, permanent_image)</code>).</p>
<h3>Creating a Basic Thumbnail</h3>
<p align="left">Now that we have our original, full-size images saved to disk we can create the thumbnails, with <a href="http://nl3.php.net/manual/en/ref.image.php">GD</a> (PHP) and <a href="http://www.pythonware.com/library/pil/handbook/index.htm">PIL</a> (Python) respectively. What I like to do is have an array (or dictionary, in Python) of thumbnail sizes. I will loop through the sizes, creating thumbnails. Now, doing so introduces some wrinkles. On one hand it makes (graphic) designers&#8217; lives a lot easier if you can promise them that the thumbnails will conform to certain dimensions. However, two questions come up: what to do when the requested thumbnail image would actually be larger than the original image, and what to do if the image doesn&#8217;t scale nicely (ie unlike from from 1024 x 768 to 640 x 480)?</p>
<p align="left">The question of thumbnails larger than the original images is a relatively simple one: I recommend either not creating a thumbnail at all or just copying the original image. The PIL <code><a href="http://www.pythonware.com/library/pil/handbook/image.htm#Image.thumbnail">Image.thumbnail()</a></code> method makes your life easier by just preserving the original image&#8217;s dimensions. That being said, if the website design absolutely requires a thumbnail with this larger dimension, you might want to just enlarge the image. Another approach will discussed next in the consideration of different image dimensions.</p>
<p align="left">The question of how to create a thumbnail with different target dimensions or width-height ratio than the original  is a tricky one, as you can have many different situations. If your thumbnail dimensions aren&#8217;t strict, you can respect one of the dimensions, using the equation <code>thumbnail_dimension2 = thumbnail_dimension1 / original_dimension1 * original_dimension2</code> to calculate the other. In PHP we could then use the following code to create our thumbnail:<br />
<code><br />
$thumbnail_image = imagecreatetruecolor($thumbnail_width, $thumbnail_height);<br />
imagecopyresampled($thumbnail_image, $original_image, 0, 0, 0, 0, $thumbnail_width, $thumbnail_height, $original_width, $original_height);<br />
image<em>imagetype</em>($thumbnail_image, $thumbnail_location);<br />
</code><br />
Note that the PHP image library is funny, able to create the thumbnail image without being explicitly told the image type (JPEG, PNG, etc) but requires the image type in order to write it to disk.</p>
<p>And in Python:<br />
<code><br />
thumbnail_image = original_image.copy()<br />
thumbnail_image.thumbnail((thumbnail_width, thumbnail_height), Image.ANTIALIAS)<br />
thumbnail_image.save(thumbnail_location)<br />
</code><br />
Make sure to copy the original image first when using PIL, as otherwise you&#8217;ll end up reducing the size of your original file! Also, it is important that you call <code>thumbnail()</code> with the &#8216;antialias&#8217; filter. If you don&#8217;t it will default to the &#8216;nearest&#8217; filter, which creates ugly images.</p>
<p>The code and methodology above are all you need to create basic thumbnails that preserve the original image&#8217;s aspect ratio, ensuring that the thumbnail image doesn&#8217;t look stretched, pinched, or otherwise distorted.</p>
<h3>Advanced Thumbnails</h3>
<p>My strategy is in an intermediate step to shrink the original image to a placeholder image which fits within the dimensions of the target image but maintains the proper aspect ratio. Having done that, I then create the thumbnail image with a basic background (usually white, though it could be any other color or even transparent, depending on what image format you&#8217;re working with) and overlay the place-holder image on top. I make sure to center the placeholder image within the thumbnail image so that the thumbnail won&#8217;t look lopsided. That being said, if some but not all the dimensions concerned have odd numbers of pixels, the placeholder image will be 1 pixel off of the center.</p>
<p>Reversing the order of my previous examples, I will give the Python code first, as it is much simpler:<br />
<code><br />
placeholder_image = original_image.copy()<br />
placeholder_image.thumbnail(thumbnail_width, thumbnail_height)<br />
placeholder_width, placeholder_height = placeholder_image.size<br />
thumbnail_image = Image.new("RGB", (thumbnail_width, thumbnail_height), (255, 255, 255))<br />
delta_width = thumbnail_width - placeholder_width<br />
delta_height = thumbnail_height - placeholder_height<br />
offset = (int(round(float(delta_width) / float(2))), int(round(float(delta_height) / float(2))))<br />
thumbnail_image.paste(placeholder_image, offset)<br />
</code></p>
<p>As you can see, first we copy the original image and then we call <code>thumbnail()</code> on our copy. Because the <code>thumbnail()</code> function maintains the original image&#8217;s aspect ratio and in this case we assume that our thumbnail dimensions does not have the same ratio, the placeholder dimensions are not equal to the thumbnail ones. Therefore we calculate the difference between the values, giving us the delta values. Dividing the delta values in half us gives a tuple for the offset of the placeholder image from the top left corner of the final, thumbnail image. We then paste the placeholder image into the thumbnail image using this offset.</p>
<p>Unfortunately PHP does not have anything comparable to PIL&#8217;s thumbnail() function, so we will have to discover appropriate dimensions for the placeholder image ourselves. In the code below we will assume that we already know that we will need to create a placeholder image.<br />
<code><br />
if ($original_width &gt; $original_height)<br />
{<br />
$placeholder_width = $thumbnail_width;<br />
$placeholder_height = ($placeholder_width / $original_width) * $original_height;<br />
}<br />
else<br />
{<br />
$placeholder_height = $thumbnail_height;<br />
$placeholder_width = ($placeholder_height / $original_height) * $original_width;<br />
}<br />
$thumbnail_image = imagecreatetruecolor($thumbnail_width, $thumbnail_height);<br />
$white = imagecolorallocate($thumbnail_image, 255, 255, 255);<br />
imagefill($thumbnail_image, 0, 0, $white);<br />
$offset_width = round(($thumbnail_width - $placeholder_width) / 2);<br />
$offset_height = round(($thumbnail_height - $placeholder_height) / 2);<br />
imagecopyresampled($thumbnail_image, $original_image, $offset_width, $offset_width, 0, 0, $placeholder_width, $placeholder_height, $original_width, $original_height);<br />
</code><br />
Note that, unlike in Python, we do not actually create an intermediate image. In fact, the code is flexible enough that it would also work just fine if the placeholder and thumbnail values are the same, as the offset values would simply be zero, like in the previous PHP code example.</p>
<p>With the code above you should be able to always create thumbnails that both respect the original images&#8217; aspect ratios and have the final dimensions that you desire.</p>
<h3>Conclusion</h3>
<p>Resizing images and creating thumbnails are not terribly difficult, but there are several checks you need to make so as to insure that the thumbnails you are creating don&#8217;t unnecessarily distort the image or degrade its quality. Happy coding!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bubblefoundry.com/blog/2008/02/from-the-laboratory-image-resizing-and-thumbnail-creation/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Honderdhandigen Party</title>
		<link>http://www.bubblefoundry.com/blog/2008/01/honderdhandigen-party/</link>
		<comments>http://www.bubblefoundry.com/blog/2008/01/honderdhandigen-party/#comments</comments>
		<pubDate>Tue, 22 Jan 2008 18:16:49 +0000</pubDate>
		<dc:creator>Peter</dc:creator>
		
		<category><![CDATA[Blog]]></category>

		<category><![CDATA[Honderdhangiden]]></category>

		<category><![CDATA[party]]></category>

		<category><![CDATA[Volkskrantgebouw]]></category>

		<guid isPermaLink="false">http://www.bubblefoundry.com/blog/2008/01/honderdhandigen-party/</guid>
		<description><![CDATA[I rent my office in the Volkskrantgebouw as part of the Honderdhandigen group. We are having an opening party on February 1 and I invite anyone interested in the building to come by and check it  out. With Lunch 2.0 at eBuddy at noon, I&#8217;ve got most of your day planned out for you!

]]></description>
			<content:encoded><![CDATA[<p>I rent my office in the <a href="http://www.volkskrantgebouw.nl">Volkskrantgebouw</a> as part of the Honderdhandigen group. We are having an opening party on February 1 and I invite anyone interested in the building to come by and check it  out. With <a href="http://upcoming.yahoo.com/event/383338/">Lunch 2.0 at eBuddy</a> at noon, I&#8217;ve got most of your day planned out for you!</p>
<p style="text-align: center"><a href="http://www.bubblefoundry.com/files/2008/01/flyerhh.gif" title="Honderdhandigen Flyer"><img src="http://www.bubblefoundry.com/files/2008/01/flyerhh.gif" alt="Honderdhandigen Flyer" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.bubblefoundry.com/blog/2008/01/honderdhandigen-party/feed/</wfw:commentRss>
		</item>
		<item>
		<title>First Lunch 2.0.nl A Success</title>
		<link>http://www.bubblefoundry.com/blog/2008/01/first-lunch-20nl-a-success/</link>
		<comments>http://www.bubblefoundry.com/blog/2008/01/first-lunch-20nl-a-success/#comments</comments>
		<pubDate>Mon, 21 Jan 2008 12:51:59 +0000</pubDate>
		<dc:creator>Peter</dc:creator>
		
		<category><![CDATA[Blog]]></category>

		<category><![CDATA[Hyves]]></category>

		<category><![CDATA[Lunch 2.0]]></category>

		<category><![CDATA[Lunch 2.0.nl]]></category>

		<category><![CDATA[lunch20]]></category>

		<category><![CDATA[lunch20hyves]]></category>

		<category><![CDATA[lunch20nl]]></category>

		<guid isPermaLink="false">http://www.bubblefoundry.com/blog/2008/01/first-lunch-20nl-a-success/</guid>
		<description><![CDATA[Thank you to everyone who came to the first Lunch 2.0.nl last Friday at Hyves. We had a great turnout and I think we are at the start of something good. To see more, I am uploading some photos to Flickr, as have many others. Merieke Hensel videotaped the Hyves presentations.
PS My apologies to anyone [...]]]></description>
			<content:encoded><![CDATA[<p>Thank you to everyone who came to the first <a href="http://www.lunch20.nl">Lunch 2.0.nl</a> last Friday at Hyves. We had a great turnout and I think we are at the start of something good. To see more, I am uploading <a href="http://flickr.com/photos/pr1001/sets/72157603763364510/">some photos</a> to Flickr, as have <a href="http://www.flickr.com/photos/tags/lunch20hyves">many others</a>. Merieke Hensel <a href="http://weblog.hensel.nl/lunch-20-bij-hyves-in-amsterdam/">videotaped</a> the Hyves presentations.</p>
<p>PS My apologies to anyone who tried to email me at peter@lunch20.nl on Friday afternoon. The address is working now.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bubblefoundry.com/blog/2008/01/first-lunch-20nl-a-success/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
