photopea / Typr.js

Typr.js - process fonts in Javascript
MIT License
914 stars 73 forks source link

Typr.js

LIVE DEMO Typr.js is a Javascript parser and utility for working with fonts (TTF, OTF, TTC). It is an alternative to opentype.js. It is the main text engine for Photopea image editor.

Typr.js preview

Typr.js consists of static functions only, it can be easily rewritten into C or any other procedural language. There are no constructors, no methods, no complex structure. It consists of two independent parts (separate files):

Typr

Typr.parse(buffer)

The font object has a structure, wich corresponds to the structure of the TTF/OTF file. I.e. it is a set of tables, each table has its own structure.

var fonts = Typr.parse(buffer);
console.log(fonts[0]);

Typr.U

Typr.U.codeToGlyph(font, code)

Typr.U.shape(font, str, ltr)

The shape can have a different length, than the input string (because of ligatures, etc). The cluster index says, which part of string the glyph represents.

Typr.U.glyphToPath(font, gid)

Typr.U.shapeToPath(font, shape)

Typr.js uses the following structure to represent the path:

{ cmds: [CMD,CMD,CMD, ...], crds:[X,Y,X,Y, ...] }

cmds is an array of commands (Strings), crds is an array of coordinates (Numbers). Each command needs a specific number of coordinates. The path can be processed by passing both arrays from the left, index into crds depends on the types of previous commands.

A "raindrop" shape: { cmds:["M","L","Q","Z"], crds:[0,0,20,80,0,120,-20,80] } (2 + 2 + 4 + 0 coordinates).

The format is similar to SVG, but commands and coordinates are separated. It is comfortable to work with coordinates as a set of 2D points, apply affine transformations etc.

Typr.U.pathToContext(path, ctx)

It executes each command of the path with a corresponding command of context2D: moveTo(), lineTo(), ... and fill(). It does nothing else (you must call translate(), scale(), fillStyle ... manually).

Typr.U.pathToSVG(path)

Converts a path to an "SVG path string", which can be used in <path d="..." />.

Extending Typr

Let's implement a little function for drawing a string:

Typr.U.stringToContext = function(font, str, ctx, size, color, x, y)
{
  var shape = Typr.U.shape(font, str);
  var path  = Typr.U.shapeToPath(font, shape);
  var scale = size / font.head.unitsPerEm;

  ctx.translate(x,y);  ctx.scale(scale,-scale);

  ctx.fillStyle = color;
  Typr.U.pathToContext(path, ctx);

  ctx.scale(1/scale,-1/scale);  ctx.translate(-x,-y);
}

Shaping with HarfBuzz

Typr.U.shape() provides only basic text shaping. For advanced shaping, Typr.js can be integrated with a HarfBuzz shaping library. HarfBuzz supports advanced shaping of Arabic, Urdu, Farsi, Khmer, You need a WASM version of the library (can be found here). The integration is done through a following function.

Typr.U.initHB(url, clb)

Once the HarfBuzz is loaded, you can use Typr.U.shapeHB() instead of Typr.U.shape(). It accepts identical parameters and returns a shape in the identical format, which can be used with e.g. Typr.U.shapeToPath().