Wrote a BDF (Bitmap Description Format) rederer in JavaScript. BDF is easy to handle by JS because it is just text file.
This only depends on following:
- Canvas element and 2D context
- fillRect() function
Code:
function BDFFont () { this.init.apply(this, arguments) }; BDFFont.prototype = { init : function (bdf) { var self = this; self.glyphs = {}; self.properties = {}; self.parse(bdf); }, parse : function (bdf) { var self = this; var lines = bdf.split(/\n/); var glyph = null, properties = null; for (var i = 0, len = lines.length; i < len; i++) { var line = lines[i]; if (glyph) { if (line != 'ENDCHAR') { if (!glyph['BITMAP']) { var d = line.split(' '); switch (d[0]) { case 'ENCODING': glyph['ENCODING'] = +d[1]; break; case 'SWIDTH': glyph['SWIDTH'] = { x: +d[1], y: +d[2] }; break; case 'DWIDTH': glyph['DWIDTH'] = { x: +d[1], y: +d[2] }; break; case 'BBX': glyph['BBw'] = +d[1]; glyph['BBh'] = +d[2]; glyph['BBox'] = +d[3]; glyph['BBoy'] = +d[4]; break; case 'ATTRIBUTES': break; case 'BITMAP': glyph['BITMAP'] = []; break; } } else { glyph['BITMAP'].bits = line.length * 4; glyph['BITMAP'].push(parseInt(line, 16)); } } else { self.glyphs[glyph['ENCODING']] = glyph; glyph = null; } } else if (properties) { if (line != 'ENDPROPERTIES') { var d = line.split(' ', 2); properties[ d[0] ] = (d[1][0] == '"') ? d[1].substring(1, d[1].length - 2): +d[1]; } else { self.properties = properties; properties = null; } } else { var d = line.split(' '); switch (d[0]) { case 'COMMENT': break; case 'FONT': self['FONT'] = d[1]; break; case 'SIZE': self['SIZE'] = { size : +d[1], xres : +d[2], yres : +d[3] }; break; case 'FONTBOUNDINGBOX': self['FONTBOUNDINGBOX'] = { w : +d[1], h : +d[2], x : +d[3], y : +d[4] }; break; case 'STARTPROPERTIES': properties = {}; break; case 'CHARS': self['CHARS'] = +d[1]; break; case 'STARTCHAR': glyph = {}; case 'ENDCHAR': break; } } } }, drawChar : function (ctx, c, bx, by, t) { var self = this; var g = self.glyphs[ c ] || self.glyphs[ self.properties['DEFAULT_CHAR'] ]; if (t) { var f = function () {}; f.prototype = g; g = new f(); g = t(g); }; var n = g['BBw']; var b = g['BITMAP']; var ox = bx + g['BBox'] - 1; var oy = by - g['BBoy'] - g['BBh'] + 1; for (var y = 0, len = b.length; y < len; y++) { var l = b[y]; for (var i = b.bits, x = 0; i >= 0; i--, x++) { if (l >> i & 0x01 == 1) { ctx.fillRect(ox + x, oy + y, 1, 1); } } } return { x: bx + g['DWIDTH'].x, y : by + g['DWIDTH'].y }; }, drawText : function (ctx, text, x, y, t) { var self = this; for (var i = 0, len = text.length; i < len; i++) { var c = text[i].charCodeAt(0); var r = self.drawChar(ctx, c, x, y, t); x = r.x; y = r.y; } return { x: x, y: y }; }, drawEdgeText : function (ctx, text, x, y, t) { var self = this; self.drawText(ctx, text, x, y, function (g) { var bitmap = new Array(g['BITMAP'].length + 2); bitmap.bits = g['BITMAP'].bits + 2; for (var i = -1, len = bitmap.length; i < len; i++) { bitmap[i+1] = g['BITMAP'][i] | g['BITMAP'][i] >> 1 | g['BITMAP'][i] >> 2 | g['BITMAP'][i+1] | g['BITMAP'][i+1] >> 1 | g['BITMAP'][i+1] >> 2 | g['BITMAP'][i-1] | g['BITMAP'][i-1] >> 1 | g['BITMAP'][i-1] >> 2 ; } g['BITMAP'] = bitmap; g['BBox'] += -3; g['BBoy'] += 1; return g; }); } };