It was my daughter Boann’s second birthday yesterday. We had a lot of friends and family visit. Thanks especially to my sister Tarynn and brother Michael and their families for making it all the way up from Dublin!

Boann and Jareth both had a great day, which is a big change from Jareth’s other birthday experiences! Jareth’s own second birthday was spent hiding from everyone. Boann is the exact opposite - loves to be the centre of attention.

The rain fell sporadically throughout the day, but that didn’t stop the kids from running around like mad things. After everything was done, Bronwyn spent a while sweeping grass trimmings out of the house.

That night was the first that Boann was to spend in her own room. Up until now, she slept in a bed in our room, and sometimes in our own bed.

We moved that bed into a new room and decided to try put her into the same routine that Jareth is in - every day, we put Jareth up to his room at 8pm. It doesn’t matter to us whether he goes to sleep or potters around in his bedroom for hours, as long as he stays up there!

Anyway, last night at eight, I brought them both up. I spent a few minutes with Jareth getting him ready for bed. When I went into Boann’s room, I found her on her bed already half-asleep!

It didn’t last, though. Throughout the night, Bronwyn and myself spent hours reminding her that she now sleeps in her own room. Between us, we got 6 hours sleep (11-5), with us taking turns to stay in there with her.

Hopefully things will turn out better today!

DOM does not officially have an innerHTML parameter, but it’s incredibly useful. I found a need for something similar when working on some DOM stuff, so had to write a version.

function string_getInsertedString($long_string,$short_string,$is_html=false){
  if($short_string>=strlen($long_string))return false;
  $insertion_length=strlen($long_string)-strlen($short_string);
  for($i=0;$i<strlen($short_string);++$i){
    if($long_string[$i]!=$short_string[$i])break;
  }
  $inserted_string=substr($long_string,$i,$insertion_length);
  if($is_html && $inserted_string[$insertion_length-1]=='<'){
    $inserted_string='<'.substr($inserted_string,0,$insertion_length-1);
  }
  return $inserted_string;
}
function DOMElement_getOuterHTML($document,$element){
  $html=$document->saveHTML();
  $element->parentNode->removeChild($element);
  $html2=$document->saveHTML();
  return string_getInsertedString($html,$html2,true);
}

Okay, this is outerHTML, not inner, but if you want the innerHTML, then just do something like this:

$innerHTML=preg_replace('/^<[^>]*>(.*)<[^>]*>$/','\1',DOMElement_getOuterHTML($document,$element));

There is possibly a better way of doing this, but the above worked for me in the “throwaway” code I was writing.

Every now and then, I get a call from a client who is puzzled why their site is running slow. I would look at their page and see an innocuous image inserted into a paragraph. When I examine the image, though, I see that the client has artificially resized the image using HTML.

One recent example showed on-screen as a 300px-wide image. When I examined it, it was actually 3000px wide (approx). As explained to the client, this had the effect of forcing the browser to use about 100 times more RAM (not counting the overhead of the transformation to 300px-wide), and the download was slower as well.

One solution to all this is to teach all clients how to resize images before they upload them. I did that in this case. But it’s not the easiest solution, and people forget how to do things.

Another solution was proposed by Ken, and that is to parse any submitted HTML for images and check that the size they claim to be is actually correct. he said that he’d had the idea ages ago but never implemented it. I think its time has come, so let’s do it.

There are four ways that images can get resized. through HTML parameters, inline CSS, selector-based CSS and JavaScript. We will address the first two, as the others would be too complex to solve in a small application.

How this will work is that resized images, if detected, will be adjusted in the HTML so their ’src’ parameter points to a pre-created resized version of the image. The entire script is run when the HTML is submitted into a CMS, before the HTML is placed in the database or published to a file.

First, we need to detect image sources and their assigned sizes.

Here is some sample HTML with images from this site.

<p><img src="http://verens.com/wp-content/themes/mandigo-14/images/green/head.jpg" width="76" height="24" /></p>
<p><img src="/wp-content/themes/mandigo-14/images/green/head.jpg" style="width:76px;height:24px" /></p>

What we want is a function which, when fed that HTML, returns HTML which is modified such that images with incorrect widths and heights have their srcs modified to point to a pre-resized version, which is created using ImageMagick.

Here it is:

define('WORKDIR_IMAGERESIZES',$_SERVER['DOCUMENT_ROOT'].'/demos/html_imageresizer/f/');
define('WORKURL_IMAGERESIZES','/demos/html_imageresizer/f/');
function html_fixImageResizes($src){
	// checks for image resizes done with HTML parameters or inline CSS
	//   and redirects those images to pre-resized versions held elsewhere

	preg_match_all('/<img [^>]*>/im',$src,$matches);
	if(!count($matches))return $src;
	foreach($matches[0] as $match){
		$width=0;
		$height=0;
		if(preg_match('/width="[0-9]*"/i',$match) && preg_match('/height="[0-9]*"/i',$match)){
			$width=preg_replace('/.*width="([0-9]*)".*/i','\1',$match);
			$height=preg_replace('/.*height="([0-9]*)".*/i','\1',$match);
		}
		else if(preg_match('/style="[^"]*width: *[0-9]*px/i',$match) && preg_match('/style="[^"]*height: *[0-9]*px/i',$match)){
			$width=preg_replace('/.*style="[^"]*width: *([0-9]*)px.*/i','\1',$match);
			$height=preg_replace('/.*style="[^"]*height: *([0-9]*)px.*/i','\1',$match);
		}
		if(!$width || !$height)continue;
		$imgsrc=preg_replace('/.*src="([^"]*)".*/i','\1',$match);

		// get absolute address of img (naive, but will work for most cases)
		if(!preg_match('/^http/i',$imgsrc))$imgsrc=preg_replace('#^/*#','http://'.$_SERVER['HTTP_HOST'].'/',$imgsrc);

		list($x,$y)=getimagesize($imgsrc);
		if(!$x || !$y || ($x==$width && $y==$height))continue;

		// create address of resized image and update HTML
		$dir=md5($imgsrc);
		$newURL=WORKURL_IMAGERESIZES.$dir.'/'.$width.'x'.$height.'.png';
		$newImgHTML=preg_replace('/(.*src=")[^"]*(".*)/i',"$1$newURL$2",$match);
		$src=str_replace($match,$newImgHTML,$src);

		// create cached image
		$imgdir=WORKDIR_IMAGERESIZES.$dir;
		@mkdir($imgdir);
		$imgfile=$imgdir.'/'.$width.'x'.$height.'.png';
		if(file_exists($imgfile))continue;
		$str='convert "'.addslashes($imgsrc).'" -geometry '.$width.'x'.$height.' "'.$imgfile.'"';
		exec($str);
	}

	return $src;
}

The return string from calling that function with the above HTML is this:

<p><img src="/demos/html_imageresizer/f/6bf7dd2b8232448e85d7fa9cd1009b44/76x24.png" width="76" height="24" /></p>

<p><img src="/demos/html_imageresizer/f/6bf7dd2b8232448e85d7fa9cd1009b44/76x24.png" style="width:76px;height:24px" /></p>

Here is an example of it running, and here is the source of that demo.