ReCreateJS/txtjs

View on GitHub
src/Character.ts

Summary

Maintainability
B
6 hrs
Test Coverage
B
84%
import Case from "./Case";
import Glyph from "./Glyph";
import FontLoader from "./FontLoader";
import Font from "./Font";

/**
 * Represents a styled character
 */
export default class Character extends createjs.Shape {
  character = "";
  characterCode: number = null;
  font: string = null;
  tracking: number = null;
  characterCase: Case = null;
  characterCaseOffset = 0;
  index: number = null;
  size: number = null;
  fillColor: string = null;
  strokeColor: string = null;
  strokeWidth: number = null;
  measuredWidth: number = null;
  measuredHeight: number = null;
  hPosition: number = null;
  missing = false;

  _glyph: Glyph;
  _font: Font;

  constructor(character: string, style: {}, index: number = null) {
    super();
    this.set(style);
    this.index = index;

    let upperSmall;

    // flip case depending on characterCase property
    if (this.characterCase == Case.NORMAL) {
      this.character = character;
    } else if (this.characterCase == Case.UPPER) {
      this.character = character.toUpperCase();
    } else if (this.characterCase == Case.LOWER) {
      this.character = character.toLowerCase();
    } else if (this.characterCase == Case.SMALL_CAPS) {
      this.character = character.toUpperCase();
      upperSmall = !(character === this.character);
    } else {
      //fallback case for unknown.
      this.character = character;
    }
    this.characterCode = this.character.charCodeAt(0);

    this._font = FontLoader.getFont(this.font);

    if (this._font.glyphs[this.characterCode]) {
      this._glyph = this._font.glyphs[this.characterCode];

      //flip lower
    } else if (
      this._font.glyphs[
        String.fromCharCode(this.characterCode)
          .toLowerCase()
          .charCodeAt(0)
      ]
    ) {
      this._glyph = this._font.glyphs[
        String.fromCharCode(this.characterCode)
          .toLowerCase()
          .charCodeAt(0)
      ];

      //flip upper
    } else if (
      this._font.glyphs[
        String.fromCharCode(this.characterCode)
          .toUpperCase()
          .charCodeAt(0)
      ]
    ) {
      this._glyph = this._font.glyphs[
        String.fromCharCode(this.characterCode)
          .toUpperCase()
          .charCodeAt(0)
      ];
    }

    //missing glyph
    if (this._glyph === undefined) {
      console.log("MISSING GLYPH:" + this.character);
      this._glyph = this._font.glyphs[42];
      this.missing = true;
    }
    this.graphics = this._glyph.graphic();

    // scale x
    if (this.characterCase === Case.SMALL_CAPS) {
      if (upperSmall) {
        this.scaleX = (this.size / this._font.units) * 0.8;
        this.characterCaseOffset = -0.2 * (this._glyph.offset * this.size);
      } else {
        this.scaleX = this.size / this._font.units;
      }
    } else {
      this.scaleX = this.size / this._font.units;
    }

    this.scaleY = -this.scaleX;

    this.measuredHeight =
      (this._font.ascent - this._font.descent) * this.scaleX;
    this.measuredWidth = this.scaleX * this._glyph.offset * this._font.units;

    const ha = new createjs.Shape();
    ha.graphics
      .beginFill("#000")
      .drawRect(
        0,
        this._font.descent,
        this._glyph.offset * this._font.units,
        this._font.ascent - this._font.descent
      );
    this.hitArea = ha;
  }

  setGlyph(glyph: Glyph) {
    this._glyph = glyph;
    this.graphics = this._glyph.graphic();
  }

  trackingOffset(): number {
    return this.size * (2.5 / this._font.units + 1 / 900 + this.tracking / 990);
  }

  draw(ctx: CanvasRenderingContext2D): boolean {
    this._glyph._fill.style = this.fillColor;
    this._glyph._fill.matrix = null;
    this._glyph._stroke.style = this.strokeColor;
    this._glyph._strokeStyle.width = this.strokeWidth;
    return this._glyph.draw(ctx);
  }

  getWidth() {
    return this.size * this._glyph.offset;
  }
}