Pica finds fonts by family name. A name is looked up first among the fonts the application registered, then among the fonts installed on the machine. Registering is how an application pins the exact font files it measures with, whatever the machine has.
In an application
Register fonts once, when the application starts. Pica keeps registered fonts in
:persistent_term, where reading them from any process costs nothing, but registering a family
again or unregistering one makes every process in the VM garbage collect. That suits startup, not
a request handler.
Ship the font files in priv/ and register them in the application's start/2 callback, before
the supervision tree starts anything that measures text:
defmodule MyApp.Application do
use Application
@impl true
def start(_type, _args) do
register_fonts()
children = [
# ...
]
Supervisor.start_link(children, strategy: :one_for_one, name: MyApp.Supervisor)
end
defp register_fonts do
:ok = Pica.register("Lato", plain: font("Lato-Regular.ttf"), bold: font("Lato-Bold.ttf"))
end
defp font(file) do
{:ok, font} =
:my_app
|> Application.app_dir(Path.join("priv/fonts", file))
|> File.read!()
|> Pica.load()
font
end
endPica.load/2 returns {:error, reason} for bytes it cannot read; matching on {:ok, font} at
startup makes a missing or corrupt font file stop the application with the reason, rather than
surface later as a failed lookup.
Anywhere in the application afterwards:
{:ok, font} = Pica.lookup("Lato", :bold)
glyphs = Pica.shape(font, "Average Temperature, °F", scale: 12 * 64)Faces and the installed fonts
A family has up to four faces: :plain, :bold, :italic and :bold_italic. A face the
application did not register is looked for among the installed fonts, never answered with another
registered face: when only Lato's plain face is registered, Pica.lookup("Lato", :bold) finds an
installed Lato bold if the machine has one, and is :error if not.
To measure only with the fonts the application ships, so results never depend on the machine,
pass installed: false:
Pica.lookup("Lato", :italic, installed: false)
#=> :errorThe first lookup that reaches the installed fonts reads every font file on the machine, which can take a second or more, and its result is kept for the life of the VM. An application that relies on installed fonts can make that first lookup at startup too.
The name is yours
The name a family is registered under need not be the font's own family name. An application can
register "Body" and "Headings", or keep two versions of a font apart. Pica.info/1 still
reports the font's own names. Registering a name again replaces the whole family: faces given
before and not given now are no longer registered.
In Livebook
Install Pica in the notebook's setup cell, then load and register fonts in a cell of their own:
Mix.install([{:pica, "~> 0.1"}])load = fn path ->
{:ok, font} = path |> File.read!() |> Pica.load()
font
end
:ok = Pica.register("Lato", plain: load.("/data/fonts/Lato-Regular.ttf"))Running the cell again re-registers the family, which is harmless in a notebook. A notebook can also skip registering and look up a family the machine has installed:
{:ok, font} = Pica.lookup("DejaVu Sans")Or skip names altogether and measure with a font it has just loaded, since every measuring function takes a loaded font:
font = load.("/data/fonts/Lato-Regular.ttf")
Pica.shape(font, "Office affiliation")