Blendend.Text.Font (blendend v0.2.0)

View Source

Sized, stateful fonts used by Blendend's text pipeline.

A Font wraps a blend2d BLFont instantiated from a Blendend.Text.Face at a specific size. It exposes the operations needed to:

  • shape GlyphBuffers (kerning, OpenType features, glyph IDs/positions),
  • fetch scaled metrics and text measurement,
  • extract outlines for glyph runs or single glyphs into Blendend.Path,
  • read/write OpenType feature settings and the font transform matrix.

Use this module when you need to turn a loaded face into a drawable font, measure shaped text, or emit vector outlines for custom rendering.

Summary

Functions

Creates a font from a face at the given size.

Same as create/2, but returns the font directly.

Creates a font from face at size, with OpenType feature settings.

Same as create_with_features/3, but returns the font directly.

Returns the current OpenType feature settings of this font.

Same as get_feature_settings/1, but returns the list directly.

Appends the outline of a single glyph_id into path, transformed by matrix.

Computes text metrics for the shaped contents of a glyph buffer.

Same as get_text_metrics/2, but returns the metrics map directly.

Returns the design-space bounding box of glyph_id for this font.

Returns the font's transformation matrix.

Same as matrix/1, but returns the matrix map directly.

Returns scaled metrics for the given font.

Same as metrics/1, but returns the metrics map directly.

Shapes the contents of a glyph buffer in-place using this font.

Pipeline–friendly version of shape/2.

Types

feature_setting()

@type feature_setting() :: {feature_tag(), feature_value()}

feature_tag()

@type feature_tag() :: String.t() | atom()

feature_value()

@type feature_value() :: 0..65535

glyph_id()

@type glyph_id() :: non_neg_integer()

t()

@opaque t()

Functions

create(face, size)

@spec create(Blendend.Text.Face.t(), number()) :: {:ok, t()} | {:error, term()}

Creates a font from a face at the given size.

The size is in user units.

On success, returns {:ok, font}.

On failure, returns {:error, reason}.

Examples

iex> {:ok, face} = Blendend.Text.Face.load("priv/fonts/Alegreya-Regular.otf")
iex> {:ok, font} = Blendend.Text.Font.create(face, 48.0)

create!(face, size)

@spec create!(Blendend.Text.Face.t(), number()) :: t()

Same as create/2, but returns the font directly.

On success, returns font.

On failure, raises Blendend.Error.

create_with_features(face, size, feats)

@spec create_with_features(Blendend.Text.Face.t(), number(), [feature_setting()]) ::
  {:ok, t()} | {:error, term()}

Creates a font from face at size, with OpenType feature settings.

feats is a list of {tag, value} pairs, where:

  • tag – OpenType feature tag (4 characters), as a string or atom, e.g. "liga", "kern", "dlig", "ss01".
  • value – integer in 0..65535, most commonly 0 (off) or 1 (on).

On success, returns {:ok, font}.

On failure, returns {:error, reason}.

create_with_features!(face, size, feats)

@spec create_with_features!(Blendend.Text.Face.t(), number(), [feature_setting()]) ::
  t()

Same as create_with_features/3, but returns the font directly.

On success, returns font.

On failure, raises Blendend.Error.

get_feature_settings(font)

@spec get_feature_settings(t()) :: {:ok, [feature_setting()]} | {:error, term()}

Returns the current OpenType feature settings of this font.

On success, returns {:ok, feats} where feats is a list of {tag, value} pairs, mirroring create_with_features/3.

On failure, returns {:error, reason}.

get_feature_settings!(font)

@spec get_feature_settings!(t()) :: [feature_setting()]

Same as get_feature_settings/1, but returns the list directly.

On success, returns the list of {tag, value} pairs.

On failure, raises Blendend.Error.

get_glyph_outlines(font, glyph_id, m, path)

@spec get_glyph_outlines(t(), glyph_id(), Blendend.Matrix2D.t(), Blendend.Path.t()) ::
  :ok | {:error, term()}

Appends the outline of a single glyph_id into path, transformed by matrix.

This is the "give me one glyph as a path" API:

  • font – a Blendend.Text.Font.t()
  • glyph_id – shaped glyph index (from a GlyphRun or shaper)
  • matrix – a Blendend.Matrix2D.t() transform (position/rotation/scale)
  • path – a Blendend.Path.t() that will be cleared and filled with the outline

On success, returns :ok.

On failure, returns {:error, reason}.

get_glyph_outlines!(font, glyph_id, m, path)

@spec get_glyph_outlines!(t(), glyph_id(), Blendend.Matrix2D.t(), Blendend.Path.t()) ::
  :ok

On success, returns :ok.

On failure, raises Blendend.Error.

get_glyph_run_outlines(font, glyph_run, mtx, path)

@spec get_glyph_run_outlines(
  t(),
  Blendend.Text.GlyphRun.t(),
  Blendend.Matrix2D.t(),
  Blendend.Path.t()
) ::
  :ok | {:error, term()}

Outlines a shaped glyph run into a Blendend.Path.

Given a glyph_run (built from a shaped buffer), a transform matrix mtx and a path, this appends the glyph outlines into path using blend2d's BLFont::getGlyphRunOutlines.

On success, returns :ok (with path mutated in-place).

On failure, returns {:error, reason}.

get_glyph_run_outlines!(path, font, glyph_run, mtx)

@spec get_glyph_run_outlines!(
  Blendend.Path.t(),
  t(),
  Blendend.Text.GlyphRun.t(),
  Blendend.Matrix2D.t()
) ::
  Blendend.Path.t()

Pipeline–friendly version of get_glyph_run_outlines/4.

This variant takes the Path first so we can write:

glyph_path =
  Path.new!()
  |> Blendend.Text.Font.get_glyph_run_outlines!(font, glyph_run, matrix)

On success, returns the same Path (now containing the glyph outlines).

On failure, raises Blendend.Error.

get_text_metrics(font, gb)

@spec get_text_metrics(t(), Blendend.Text.GlyphBuffer.t()) ::
  {:ok, map()} | {:error, term()}

Computes text metrics for the shaped contents of a glyph buffer.

The glyph buffer gb must already be shaped with shape/2 or shape!/2.

On success, returns {:ok, map}. The map contains:

  • "advance_x" / "advance_y" – total pen movement after drawing the run
  • "bbox_x0" / "bbox_y0" – lower-left corner of the run's tight bounding box
  • "bbox_x1" / "bbox_y1" – upper-right corner of the run's tight bounding box

On failure, returns {:error, reason}.

get_text_metrics!(font, gb)

@spec get_text_metrics!(t(), Blendend.Text.GlyphBuffer.t()) :: map()

Same as get_text_metrics/2, but returns the metrics map directly.

On success, returns a map.

On failure, raises Blendend.Error.

glyph_bounds(font, glyph_or_list)

@spec glyph_bounds(t(), glyph_id() | [glyph_id()]) ::
  {:ok, {number(), number(), number(), number()}}
  | {:ok, [{number(), number(), number(), number()}]}
  | {:error, term()}

Returns the design-space bounding box of glyph_id for this font.

For a single glyph id, returns:

{:ok, {x0, y0, x1, y1}}

If we pass a list of glyph ids, returns:

{:ok, [{x0, y0, x1, y1}, ...]}

All coordinates are in font design units, relative to the glyph origin.

glyph_bounds!(font, glyph_or_list)

@spec glyph_bounds!(t(), glyph_id() | [glyph_id()]) ::
  {number(), number(), number(), number()}
  | [{number(), number(), number(), number()}]

Same as glyph_bounds/2.

On success, returns bounding boxes.

On failure, raises Blendend.Error.

matrix(font)

@spec matrix(t()) :: {:ok, map()} | {:error, term()}

Returns the font's transformation matrix.

On success, returns {:ok, map} where map contains:

  • "m00" – horizontal scale
  • "m11" – vertical scale
  • "m01" – x-shear
  • "m10" – y-shear

These describe how the font's internal design units are mapped into user-space.

It’s the transform from font design space -> user/canvas space:

x_px = m00 x_design + m01 y_design y_px = m10 x_design + m11 y_design

Example: if the face has 1000 units-per-em and the font was created at size 42, then:

m00 = 42 / 1000 = 0.042

so a horizontal advance of 534.0 design units becomes 534 * 0.042 = 22.4 pixels on screen.

"m01" = 0.0, "m10" = 0.0 – shear components Both zero -> no skew. If they were non-zero, we'd have some kind of slant / skew applied in the font matrix.

On failure, returns {:error, reason}.

matrix!(font)

@spec matrix!(t()) :: map()

Same as matrix/1, but returns the matrix map directly.

On success, returns map.

On failure, raises Blendend.Error.

Examples

iex> face = Blendend.Text.Face.load!("priv/fonts/ABeeZee-Regular.ttf")
iex> font = Blendend.Text.Font.create!(face, 42.0)
iex> Blendend.Text.Font.matrix!(font)
%{"m00" => sx, "m01" => 0.0, "m10" => 0.0, "m11" => sy}

metrics(font)

@spec metrics(t()) :: {:ok, map()} | {:error, term()}

Returns scaled metrics for the given font.

On success, returns {:ok, map} where map includes keys:

  • "size"
  • "ascent"
  • "v_ascent"
  • "descent"
  • "v_descent"
  • "line_gap"
  • "x_height"
  • "cap_height"
  • "x_min"
  • "y_min"
  • "x_max"
  • "y_max"
  • "underline_position"
  • "underline_thickness"
  • "strikethrough_position"
  • "strikethrough_thickness"

On failure, returns {:error, reason}.

metrics!(font)

@spec metrics!(t()) :: map()

Same as metrics/1, but returns the metrics map directly.

On success, returns a map.

On failure, raises Blendend.Error.

shape(font, gb)

@spec shape(t(), Blendend.Text.GlyphBuffer.t()) :: :ok | {:error, term()}

Shapes the contents of a glyph buffer in-place using this font.

This runs blend2d's shaping pipeline on gb (glyph indices, kerning, OpenType features, etc.).

On success, returns :ok.

On failure, returns {:error, reason}.

shape!(gb, font)

Pipeline–friendly version of shape/2.

This variant takes the GlyphBuffer first so we can write:

gb =
  GlyphBuffer.new!()
  |> GlyphBuffer.set_utf8_text!("Hello")
  |> Blendend.Text.Font.shape!(font)

On success, returns the same GlyphBuffer.

On failure, raises Blendend.Error.