fs = require 'fs' class AFMFont @open: (filename) -> new AFMFont fs.readFileSync filename, 'utf8' constructor: (@contents) -> @attributes = {} @glyphWidths = {} @boundingBoxes = {} @kernPairs = {} @parse() @charWidths = (@glyphWidths[characters[i]] for i in [0..255]) @bbox = (+e for e in @attributes['FontBBox'].split /\s+/) @ascender = +(@attributes['Ascender'] or 0) @descender = +(@attributes['Descender'] or 0) @lineGap = (@bbox[3] - @bbox[1]) - (@ascender - @descender) parse: -> section = '' for line in @contents.split '\n' if match = line.match /^Start(\w+)/ section = match[1] continue else if match = line.match /^End(\w+)/ section = '' continue switch section when 'FontMetrics' match = line.match /(^\w+)\s+(.*)/ key = match[1] value = match[2] if a = @attributes[key] a = @attributes[key] = [a] if !Array.isArray(a) a.push(value) else @attributes[key] = value when 'CharMetrics' continue unless /^CH?\s/.test(line) name = line.match(/\bN\s+(\.?\w+)\s*;/)[1] @glyphWidths[name] = +line.match(/\bWX\s+(\d+)\s*;/)[1] when 'KernPairs' match = line.match /^KPX\s+(\.?\w+)\s+(\.?\w+)\s+(-?\d+)/ if match @kernPairs[match[1] + '\0' + match[2]] = parseInt match[3] return WIN_ANSI_MAP = 402: 131 8211: 150 8212: 151 8216: 145 8217: 146 8218: 130 8220: 147 8221: 148 8222: 132 8224: 134 8225: 135 8226: 149 8230: 133 8364: 128 8240: 137 8249: 139 8250: 155 710: 136 8482: 153 338: 140 339: 156 732: 152 352: 138 353: 154 376: 159 381: 142 382: 158 encodeText: (text) -> res = [] for i in [0...text.length] char = text.charCodeAt(i) char = WIN_ANSI_MAP[char] or char res.push char.toString(16) return res glyphsForString: (string) -> glyphs = [] for i in [0...string.length] charCode = string.charCodeAt(i) glyphs.push @characterToGlyph charCode return glyphs characterToGlyph: (character) -> return characters[WIN_ANSI_MAP[character] or character] or '.notdef' widthOfGlyph: (glyph) -> return @glyphWidths[glyph] or 0 getKernPair: (left, right) -> return @kernPairs[left + '\0' + right] or 0 advancesForGlyphs: (glyphs) -> advances = [] for left, index in glyphs right = glyphs[index + 1] advances.push @widthOfGlyph(left) + @getKernPair(left, right) return advances characters = ''' .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef .notdef space exclam quotedbl numbersign dollar percent ampersand quotesingle parenleft parenright asterisk plus comma hyphen period slash zero one two three four five six seven eight nine colon semicolon less equal greater question at A B C D E F G H I J K L M N O P Q R S T U V W X Y Z bracketleft backslash bracketright asciicircum underscore grave a b c d e f g h i j k l m n o p q r s t u v w x y z braceleft bar braceright asciitilde .notdef Euro .notdef quotesinglbase florin quotedblbase ellipsis dagger daggerdbl circumflex perthousand Scaron guilsinglleft OE .notdef Zcaron .notdef .notdef quoteleft quoteright quotedblleft quotedblright bullet endash emdash tilde trademark scaron guilsinglright oe .notdef zcaron ydieresis space exclamdown cent sterling currency yen brokenbar section dieresis copyright ordfeminine guillemotleft logicalnot hyphen registered macron degree plusminus twosuperior threesuperior acute mu paragraph periodcentered cedilla onesuperior ordmasculine guillemotright onequarter onehalf threequarters questiondown Agrave Aacute Acircumflex Atilde Adieresis Aring AE Ccedilla Egrave Eacute Ecircumflex Edieresis Igrave Iacute Icircumflex Idieresis Eth Ntilde Ograve Oacute Ocircumflex Otilde Odieresis multiply Oslash Ugrave Uacute Ucircumflex Udieresis Yacute Thorn germandbls agrave aacute acircumflex atilde adieresis aring ae ccedilla egrave eacute ecircumflex edieresis igrave iacute icircumflex idieresis eth ntilde ograve oacute ocircumflex otilde odieresis divide oslash ugrave uacute ucircumflex udieresis yacute thorn ydieresis '''.split(/\s+/) module.exports = AFMFont