Source: resources/font.js

/*
 * File: font.js
 *
 * logics for loading font into the resource_map
 * Note that "font" consists of two files
 *    => the bitmap font image
 *    => the associated xml descriptor file
 */
"use strict";  // Operate in Strict mode such that variables must be declared before used!

import * as texture from "./texture.js";
import * as xml from "./xml.js";

/**
 * Logics for loading font into the resource_map,
 * note that "font" consists of two files: the bitmap font image and the associated xml descriptor file
 * <p>Found in Chapter 5, page 245 of the textbook</p> 
 * Example:
 * {@link https://mylesacd.github.io/build-your-own-2d-game-engine-2e-doc/BookSourceCode/chapter5/5.4.font_support/index.html 5.4 Font Support}
 * @module font 
 */


let kImageExt = ".png";  // extension for the bitmap font image
let kDescExt = ".fnt";   // extension for the bitmap font description


// for convenient communication of per-character information
// all size returned are in normalize unit (range between 0 to 1)
class CharacterInfo {
    /**
     * @classdesc Object for convenient communication of per-character information,
     * all size returned are in normalize unit (range between 0 to 1)
     * @returns {CharacterInfo} a new CharacterInfo instance
     */
    constructor() {
        // in texture coordinate (0 to 1) maps to the entire image
        this.mTexCoordLeft = 0;
        this.mTexCoordRight = 1;
        this.mTexCoordBottom = 0;
        this.mTexCoordTop = 0;

        // reference to nominal character size, 1 is "standard width/height" of a char
        this.mCharWidth = 1;
        this.mCharHeight = 1;
        this.mCharWidthOffset = 0;
        this.mCharHeightOffset = 0;

        // reference of char width/height ratio
        this.mCharAspectRatio = 1;
    }
}

/**
 * Returns the fontName with ".png" appened
 * @static
 * @param {string} fontName - the path to the font files
 * @returns {string} fontName with file extension
 */
function imageName(fontName) { return fontName+kImageExt;}
/**
 * Returns the path of the font description file with ".fnt" appened
 * @static
 * @param {string} fontName - the path to the font files
 * @returns {string} path to font description file
 */
function descName(fontName) { return fontName+kDescExt;}

/**
 * Load the font image as a texture and the font description as an XMLDocument
 * @static
 * @param {string} fontName - path to the font image and description files
 */
function load(fontName) {
    texture.load(imageName(fontName));
    xml.load(descName(fontName));
}

// Remove the reference to allow associated memory 
// be available for subsequent garbage collection
/**
 * Unload the font to allow for garbage collection
 * @static
 * @param {string} fontName - path to the font image and description files
 */
function unload(fontName) {
    texture.unload(imageName(fontName));
    xml.unload(descName(fontName));
}

/**
 * Returns whether the font is already in the resource map
 * @static
 * @param {string} fontName - path to the font image and description files
 * @returns {boolean} true if the font image and font description are loaded
 */
function has(fontName) {
    return texture.has(imageName(fontName)) && xml.has(descName(fontName));
}

/**
 * Returns a CharacterInfo object with the size and texel information for the character texture
 * @static
 * @param {string} fontName - path to the font image and description files
 * @param {string} aChar - the single character (code) to get information for
 * @returns {CharacterInfo} a new instance of CharacterInfo, null if unsuccessful
 */
function getCharInfo(fontName, aChar) {
    let returnInfo = null;
    let fontInfo = xml.get(descName(fontName));
    let commonPath = "font/common";
    let commonInfo = fontInfo.evaluate(commonPath, fontInfo, null, XPathResult.ANY_TYPE, null);
    commonInfo = commonInfo.iterateNext();
    if (commonInfo === null) {
        return returnInfo;
    }
    let charHeight = commonInfo.getAttribute("base");

    let charPath = "font/chars/char[@id=" + aChar + "]";
    let charInfo = fontInfo.evaluate(charPath, fontInfo, null, XPathResult.ANY_TYPE, null);
    charInfo = charInfo.iterateNext();

    if (charInfo === null) {
        return returnInfo;
    }

    returnInfo = new CharacterInfo();
    let texInfo = texture.get(imageName(fontName));
    let leftPixel = Number(charInfo.getAttribute("x"));
    let rightPixel = leftPixel + Number(charInfo.getAttribute("width")) - 1;
    let topPixel = (texInfo.mHeight - 1) - Number(charInfo.getAttribute("y"));
    let bottomPixel = topPixel - Number(charInfo.getAttribute("height")) + 1;

    // texture coordinate information
    returnInfo.mTexCoordLeft = leftPixel / (texInfo.mWidth - 1);
    returnInfo.mTexCoordTop = topPixel / (texInfo.mHeight - 1);
    returnInfo.mTexCoordRight = rightPixel / (texInfo.mWidth - 1);
    returnInfo.mTexCoordBottom = bottomPixel / (texInfo.mHeight - 1);

    // relative character size
    let charWidth = charInfo.getAttribute("xadvance");
    returnInfo.mCharWidth = charInfo.getAttribute("width") / charWidth;
    returnInfo.mCharHeight = charInfo.getAttribute("height") / charHeight;
    returnInfo.mCharWidthOffset = charInfo.getAttribute("xoffset") / charWidth;
    returnInfo.mCharHeightOffset = charInfo.getAttribute("yoffset") / charHeight;
    returnInfo.mCharAspectRatio = charWidth / charHeight;

    return returnInfo;
}

export {has, load, unload,

    imageName, descName,

    CharacterInfo,

    getCharInfo
}