I must have enemies...

renodino on 2007-12-01T01:51:30

For the past week I've been building a package to provide bitmapped fonts for HTML <canvas> elements (which don't provide native text drawing - someone at Apple needs to be flogged for that omission...). All the existing solutions I can google up require manual intervention at some point. Not very 21st Century, and certainly unacceptable for lazy Perl programmers.

On Monday, after some deep cogitation during a restless slumber, I devise a plan. "Should be simple", thinks I, "just use GD::Text to progressively render a text string in a specified font, accumulating character positions and widths as I go. Write a bit of Javascript to load and slice up the character set image and apply to the canvas via drawImage(). Steal a bit of Javascript from some other similar solutions, and I'm done."

Then a sudden shudder down my spine: "ooooh...GD::Text... ." A minor headache begins to throb in my temples as I recall painful memories of trying to get the damn thing to work. Vague recollections that the POD is actually a work of fiction. And bugs, strange little bugs, but I can't recall what they were. Perhaps its been updated ?

Alas, No. Untouched since 2003.

"But this is too simple, shouldn't take me more than an afternoon to find and workaround the bugs."

Yes, I'm a cockeyed optimist.

So I dig in. The code nearly writes itself. "Simple things should be easy!". Even the Javascript is a breeze.

And then I begin testing. And the painful memories of GD::Text become tangible welts across my back once again. Strange and silent errors. getBounds() behavior and interface only vaguely resembles the POD. Printing a zero character by itself on a line results in nothing printed...unless I add another character. And when, 36 hours later, I finally manage to get the character set rendered, it looks like shit...and strings reassembled from sliced out characters look even worse.

(My neighbors are now quite certain they've moved next to a drunken, angry Russion sailor. Fortunately, they have no children, so no one learned any new vocabulary words)

So I'm done with GD::Text (and probably GD, too). They were an adequate solution in the long ago, far away time, and they are still a nice lightweight solution for "Simple should be easy" projects. But the accumulation of frustrations with the pair of them has done me in.

So I jump to Imager. I'd looked at it some years ago when it was still fairly nascent, and it looked promising, but the docs were confusing, and I didn't have time to spend on it. But the latest versions look pretty robust, and the docs seem better, so I PPM's me a copy and begin code conversion in earnest.

OK, first, I need to make a transparent fill color. So how do I do that ? Looks in Imager docs. Nothing. Looks in Imager::Color docs. Nothing. Imager::Draw ? Nope. WTF ???

Fortunately, as I've been on this merry-go-round a few times now, I figure it must be the alpha channel. Then I find a code sample buried deep in the Imager source tree. OK, 3 hours to find a very basic thing that should be one of the 1st 3 items described in Imager::Color. But I'm a n00b, so I take it like a man and move on.

Well, thinks I, since I'm screwing with alpha channels anyway, why not add opacity to the text colors. So I does. 'Cept the docs never state what kind of value the alpha channel actually uses. After trying a few fractional values, I discover it ain't like CSS. <sigh/>. So i start throwing numbers at it and seeing what comes out. Big numbers, huge numbers, little numbers. By trial and error I discover its a value betweem 0 and 255. Thanks for the helpful docs, Imager. And another 3 hours pissed away.

So now I'm ready to start trying fonts. And Imager has a very nice font metrics function. "Ah, at last I'm on the downhill side!", thinks I, and beginning typing furiously. "Now, which of these 9 values do I use to figure out how big a box I need to render my string ? Er...". Another painful trial and error session until I finally get satisfactory results.

But at least it looks good. And assembling strings from extracted bits looks great. Tweak the API to render the Javascript better, fixup the test suite, and things are humming. At 8PM Thursday night - after starting bright and early Monday AM - I've finally got the Perl working. Yep, "Simple things should be easy."

I awake Friday after yet another fretful slumber, hack in a few niceties I'd conjured during the night, and start testing the Javascript. Firefox + Firebug, the Web Developer's best friends. Whats this, do I want to update to 2.0.0.10 ? Oh sure, what the heck, can't hurt...

After fixing a few Javascript syntax errors, things look ready to roll...except for this nasty "Uncaught exception:.... NS_ERROR_NOT_AVAILABLE...". Hmm, the docs say canvas.drawImage() waits for the image to load. Lemme try this...no ? Ok, what about this ? Huh ? Wait one goddamn minute...

So, reverting to first principles, I grab example code direct from the MDC canvas tutorial site and give it a go. "Uncaught exception:....NS_ERROR_NOT_AVAILABLE..." WTF???

Fiddle some more. Still getting the error. Time for drastic measures. Clicks the Opera icon and loads the MDC tutorial page. Runs like a charm.

Ok, now this is bullshit. "Google, please assure me I'm not insane!", I think, as I feverishly type in "canvas firefox NS_ERROR_NOT_AVAILABLE". First hit: 2.0.0.10 Gaffe Prompts Quickfix. Follow the links to Bugzilla. Seems our friends at Firefox are doing a less than thorough job of testing standard API functions.

AARRRRRRGGHH!

And from the comments in the Bugzilla entry, there are *many* others in far worse shape than I. Hopefully, this episode will light a fire under the Firefox.

Its now Friday evening. I've poured a a tall spiced ale, and will top it off with a tall Jack and soda soon. This little exersize - that should never have taken more than a day, maybe 2 tops - is now stretching into day 6. I'm frustrated, tired, pissed off, and seriously wondering if its time to throw away OSS and embrace the C#.

My takeways:

  1. Start using Opera for development, and stop recommending Firefox.
  2. Stop using GD anything.
  3. Submit some doc patches for Imager::Cookbook. Imager is a great package, but the docs seem to assume the reader either has an advanced degree in CG, or was a contributor.
  4. If anyone from WHATWG reads this, PLEASE FOR THE LOVE OF GOD ADD drawString() TO HTML5!!!!

Because Simple things should be easy, dammit.


Imager's colors

tonyc on 2007-12-04T05:25:55

I'm adding a lead in to Imager::Color describing the color model.

Unfortunately, for historical reasons, the meaning of the channels can vary depending on the type of image - grey vs color - you use it with. If I can find a way to fix this without breaking too much code, I'll consider it.