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...
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:
Because Simple things should be easy, dammit.
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.
Imager's colors
tonyc on 2007-12-04T05:25:55