tecs.gfx.truetype
TrueType outlines, metrics, coverage rasterization, and signed distance fields.
A face is parsed once from the bytes of a .ttf or .ttc-free single-font file and answers three questions: which glyph a codepoint selects, what that glyph advances, and where its outline runs. rasterize turns one outline into an antialiased coverage bitmap and distanceField turns that bitmap into the field a distance-field text shader samples.
const face, reason = tecs.gfx.truetype.parse(bytes)
assert(face, reason)
const glyph = face:glyphIndex(0x41)
const contours = tecs.gfx.truetype.outline(face, glyph, 48.0 / face.unitsPerEm)Everything here works in font units until a scale is supplied. A font unit is one of unitsPerEm divisions of the em square, so dividing by unitsPerEm gives em units and multiplying those by a point size gives pixels.
Only the glyf outline format is read. A face whose outlines live in a CFF table, which is what an OpenType font built from PostScript curves carries, parses to a failure reason rather than to empty glyphs. Kerning comes from a format-zero kern table when one is present; GPOS positioning is not read, so a font that kerns only through GPOS lays out with its advance widths alone.
Module contents
Types
| Type | Kind | Description |
|---|---|---|
Face | record | A parsed font file. |
Outline | type | One glyph outline, as closed contours of flattened points. |
Functions
| Function | Kind | Description |
|---|---|---|
distanceField | function | Turns one coverage bitmap into a signed distance field. |
outline | function | Returns one glyph's flattened outline and its bounding box. |
parse | function | Parses a single-font TrueType file. |
rasterize | function | Fills one outline into an antialiased coverage bitmap. |
Types#
Facerecord#
record Face
unitsPerEm: integer
ascender: integer
descender: integer
lineGap: integer
numGlyphs: integer
glyphIndex: function(borrows self: Face, codepoint: integer): integer
advanceOf: function(borrows self: Face, glyph: integer): number
bearingOf: function(borrows self: Face, glyph: integer): number
kerningOf: function(exclusive self: Face, left: integer, right: integer): number
endA parsed font file.
Immutable once parse returns, apart from the kerning table it fills in on first use.
Methods
glyphIndex#
glyphIndex: function(borrows self: Face, codepoint: integer): integerReturns the glyph one codepoint selects.
Zero is the .notdef glyph rather than an error, which is the glyph a font draws for a character it does not carry, so a caller renders it instead of branching.
Arguments
| Name | Type | Description |
|---|---|---|
borrows self | Face | |
codepoint | integer |
Returns
| Type | Description |
|---|---|
integer |
advanceOf#
Returns one glyph's horizontal advance in font units.
Arguments
| Name | Type | Description |
|---|---|---|
borrows self | Face | |
glyph | integer |
Returns
| Type | Description |
|---|---|
number |
bearingOf#
Returns one glyph's left side bearing in font units.
Arguments
| Name | Type | Description |
|---|---|---|
borrows self | Face | |
glyph | integer |
Returns
| Type | Description |
|---|---|
number |
kerningOf#
Returns the kerning between two glyphs in font units.
Reads the pairs on first use and keeps them, because a face that kerns is asked about most of its adjacent pairs within one line of text.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive self | Face | |
left | integer | |
right | integer |
Returns
| Type | Description |
|---|---|
number |
Fields
unitsPerEm#
unitsPerEm: integerRead-only. Reports the em square's divisions, which every font unit in this face is one of.
ascender#
ascender: integerRead-only. Reports the distance from the baseline to the top of the typographic ascent, in font units.
descender#
descender: integerRead-only. Reports the distance from the baseline to the bottom of the typographic descent, in font units. It is negative in every ordinary font.
numGlyphs#
numGlyphs: integerRead-only. Reports how many glyphs the face holds, including .notdef at index zero.
Outlinetype#
type Outline = {{number}}One glyph outline, as closed contours of flattened points.
Each contour is a flat array of alternating x and y in font units, with y increasing upward the way the font file stores it. The last point joins the first; the closing segment is not repeated.
Functions#
distanceFieldfunction#
function distanceField(coverage: {integer}, width: integer, height: integer, spread: number): {integer}Turns one coverage bitmap into a signed distance field.
The field is stored the way a distance-field shader expects it: 0.5 sits on the outline, values above it are inside, and one texel of distance is 0.5 / spread of the stored range. A shader recovers coverage by smoothstepping around 0.5.
Distance comes from a two-pass sequential Euclidean transform over the thresholded coverage, which is exact for the texel centers it visits and within a fraction of a texel elsewhere. That is well inside what a spread of several texels can represent.
Arguments
| Name | Type | Description |
|---|---|---|
coverage | {integer} | the bitmap |
width | integer | the bitmap width in texels |
height | integer | the bitmap height in texels |
spread | number | the distance in texels the field reaches on each side of the outline, which must be positive |
Returns
| Type | Description |
|---|---|
{integer} | a |
outlinefunction#
function outline(self: Face, glyph: integer, scale: number): Outline, number, number, number, numberReturns one glyph's flattened outline and its bounding box.
The box comes from the flattened points rather than from the glyph header, so it encloses exactly what rasterize will fill. An empty glyph, which is what a space is, returns no contours and a zero box.
Arguments
| Name | Type | Description |
|---|---|---|
self | Face | the parsed face |
glyph | integer | the glyph index |
scale | number | the font units to pixels factor the flattening is tuned for |
Returns
| Type | Description |
|---|---|
Outline | the contours, in font units with y increasing upward |
number | the smallest x, then the smallest y, then the largest x, then the largest y, all in font units |
number | |
number | |
number |
parsefunction#
Parses a single-font TrueType file.
Arguments
| Name | Type | Description |
|---|---|---|
bytes | string | the complete contents of a |
Returns
| Type | Description |
|---|---|
Face? | the face, or nil when the bytes are not a readable TrueType font |
string? | why the bytes were rejected, when unsuccessful |
rasterizefunction#
function rasterize(contours: Outline, width: integer, height: integer, originX: number, originY: number, scale: number): {integer}Fills one outline into an antialiased coverage bitmap.
The bitmap runs left to right and top to bottom, so it uploads as image rows without a flip. Font y increases upward and texel y increases downward, which is the only place the two conventions meet.
Arguments
| Name | Type | Description |
|---|---|---|
contours | Outline | the flattened contours, in font units |
width | integer | the bitmap width in texels, which must be positive |
height | integer | the bitmap height in texels, which must be positive |
originX | number | the font-unit x that lands on the bitmap's left edge |
originY | number | the font-unit y that lands on the bitmap's top edge |
scale | number | the font units to texels factor |
Returns
| Type | Description |
|---|---|
{integer} | a |