Home Reference Source Test

src/text.js

const opentype = require('opentype.js');


/**
 * @type {object} Map containing all the fonts available for use
 */
var _fonts = { };

/**
 * The default font family to use for text
 * @type {string}
 */
const DEFAULT_FONT_FAMILY = 'source';

/**
 * Register Font
 *
 * @param {string} binaryPath Path to the font binary file(.eot, .ttf etc.)
 * @param {string} family     The name to give the font
 * @param {number} weight     The font weight to use
 * @param {string} style      Font style
 * @param {string} variant    Font variant
 *
 * @returns {font} Font instance
 */
exports.registerFont = function(binaryPath, family, weight, style, variant) {
    _fonts[family] = {
        binary: binaryPath,
        family: family,
        weight: weight,
        style: style,
        variant: variant,
        loaded: false,
        font: null,
        load: function(cb) {
            if(this.loaded) {
                if(cb)cb();
                return;
            }
            var self = this;
            opentype.load(binaryPath, function (err, font) {
                if (err) throw new Error('Could not load font: ' + err);
                self.loaded = true;
                self.font = font;
                if(cb)cb();
            });
        },
        loadSync: function() {
            if(this.loaded) {
                return;
            }
            try {
                this.font = opentype.loadSync(binaryPath);
                this.loaded = true;
                return this;
            } catch (err) {
                throw new Error('Could not load font: ' + err);
            }
        }
    };
    return _fonts[family];
};
/**@ignore */
exports.debug_list_of_fonts = _fonts;

/**
 * Find Font
 *
 * Search the `fonts` array for a given font family name
 *
 * @param {string} family The name of the font family to search for
 *
 * @returns {object}
 */
function findFont(family) {
    if(_fonts[family]) return _fonts[family];
    family =  Object.keys(_fonts)[0];
    return _fonts[family];
}

/**
 * Process Text Path
 *
 * @param {Context} ctx  The {@link Context} to paint on
 * @param {string}  text The text to write to the given Context
 * @param {number}  x    X position
 * @param {number}  y    Y position
 * @param {boolean} fill Indicates wether or not the font should be filled
 *
 * @returns {void}
 */
exports.processTextPath = function(ctx,text,x,y, fill, hAlign, vAlign) {
    let font = findFont(ctx._font.family);
    if(!font) {
        console.warn("Font missing",ctx._font)
    }
    const metrics = exports.measureText(ctx,text)
    if(hAlign === 'start' || hAlign === 'left') /* x = x*/ ;
    if(hAlign === 'end'   || hAlign === 'right')  x = x - metrics.width
    if(hAlign === 'center')  x = x - metrics.width/2

    if(vAlign === 'alphabetic') /* y = y */ ;
    if(vAlign === 'top') y = y + metrics.emHeightAscent
    if(vAlign === 'middle') y = y + metrics.emHeightAscent/2+metrics.emHeightDescent/2
    if(vAlign === 'bottom') y = y + metrics.emHeightDescent
    var size = ctx._font.size;
    if(ctx.USE_FONT_GLYPH_CACHING) {
        var off = 0;
        for(var i=0; i<text.length; i++) {
            var ch = text[i];
            if(!cache.contains(font,size,ch)) {
                var glyph = renderGlyphToBitmap(font,ch,size);
                cache.insert(font,size,ch,glyph);
            }
            var glyph = cache.get(font,size,ch);
            var fx = x+off;
            var fy = y-glyph.ascent;
            var fpt = ctx.transform.transformPoint(fx,fy);
            ctx.copyImage(glyph.bitmap, Math.floor(fpt.x), Math.floor(fpt.y), ctx._fillColor);
            off += glyph.advance;
        }
    } else {
        font.load(function(){
            var path = font.font.getPath(text, x, y, size);
            ctx.beginPath();
            path.commands.forEach(function(cmd) {
                switch(cmd.type) {
                    case 'M': ctx.moveTo(cmd.x,cmd.y); break;
                    case 'Q': ctx.quadraticCurveTo(cmd.x1,cmd.y1,cmd.x,cmd.y); break;
                    case 'L': ctx.lineTo(cmd.x,cmd.y); break;
                    case 'Z':
                    {
                        ctx.closePath();
                        fill ? ctx.fill() : ctx.stroke();
                        ctx.beginPath();
                        break;
                    }
                }
            });
        });
    }
};

/**
 * Process Text Path
 *
 * @param {Context} ctx The {@link Context} to paint on
 * @param {string} text The name to give the font
 *
 * @returns {object}
 */
exports.measureText = function(ctx,text) {
    let font = findFont(ctx._font.family);
    if(!font) console.warn("WARNING. Can't find font family ", ctx._font);
    var fsize   = ctx._font.size;
    var glyphs  = font.font.stringToGlyphs(text);
    var advance = 0;
    glyphs.forEach(function(g) { advance += g.advanceWidth; });

    return {
        width: advance/font.font.unitsPerEm*fsize,
        emHeightAscent: font.font.ascender/font.font.unitsPerEm*fsize,
        emHeightDescent: font.font.descender/font.font.unitsPerEm*fsize,
    };
};