Families

A family name groups the faces of one design, such as Inter. A subfamily name tells the faces apart within it, such as Bold Italic. Both are stored in the font's name table under numeric identifiers, and metadata()->family and metadata()->subfamily read them.

Legacy and typographic names

Early font software grouped at most four faces under one family name: regular, bold, italic, and bold italic. A design with more weights spreads them over several legacy families, and each face keeps a legacy subfamily among those four styles. Typographic names, added later as name IDs 16 and 17, keep the whole design under one family and move every style word into the subfamily. ALTO Font prefers the typographic names and falls back to the legacy ones. The example below is Noto Sans Armenian Black as shipped with macOS.

Name ID Field Example Read into
1 Legacy family Noto Sans Armenian Blk family, when 16 is absent
2 Legacy subfamily Regular subfamily, when 17 is absent
16 Typographic family Noto Sans Armenian family
17 Typographic subfamily Black subfamily
4 Full name Noto Sans Armenian Black fullName
6 PostScript name NotoSansArmenian-Black postScriptName

Without that preference, the face above would be reported as a regular face of a family named "Noto Sans Armenian Blk". A face with no usable record reports Unknown and Regular. Name IDs 21 and 22, the WWS family and subfamily, are not read.

Weight, style, and stretch

descriptor() derives a weight, a style, and a stretch from the words of the subfamily name, compared case-insensitively. The OS/2 weight and width classes are not read.

Subfamily contains Weight
Thin 100
Extra Light, Ultra Light 200
Light 300
Medium 500
Semi Bold, Demi Bold 600
Bold 700
Extra Bold, Ultra Bold 800
Black, Heavy 900
none of these 400
Subfamily contains Stretch
Ultra Condensed 50%
Extra Condensed 62%
Condensed 75%
Semi Condensed 87%
none of these 100%
Semi Expanded 112%
Expanded 125%
Extra Expanded 150%
Ultra Expanded 200%

The style is italic when the subfamily contains "Italic", oblique when it contains "Oblique", and normal otherwise.

The keywords are matched as written above, with their space. A subfamily written as one word matches the shorter keyword it contains: "SemiBold" is read as 700, "ExtraLight" as 300, and "SemiCondensed" as 75%. A word that matches no keyword, such as "Book", leaves the default.

Place a font at fonts/Inter-Regular.ttf and run this script beside vendor:

<?php

require __DIR__.'/vendor/autoload.php';

use Alto\Font\Font;

$font = Font::fromFile(__DIR__.'/fonts/Inter-Regular.ttf');
$face = $font->face();
$descriptor = $font->descriptor();

printf("Family: %s, subfamily: %s\n", $descriptor->family, $descriptor->subfamily);
printf("Legacy names: %s, %s\n", $face->name(1) ?? 'none', $face->name(2) ?? 'none');
printf("Typographic names: %s, %s\n", $face->name(16) ?? 'none', $face->name(17) ?? 'none');
printf(
    "Weight %s, style %s, stretch %s\n",
    $descriptor->weight->css(),
    $descriptor->style->value,
    $descriptor->stretch->css(),
);

The first line shows the names the package chose, the next two show the raw records they come from, and the last one shows the derived values.

Match a family

FontFinder keeps the candidates whose family equals the requested one, compared case-insensitively, then picks the closest weight, style, and stretch among them. On a variable font with a wght or wdth axis, the requested weight and stretch select the coordinates of the returned view. See Finding.

Names and languages

Name selection keeps the first decodable record for each field rather than selecting by language. Non-ASCII legacy Mac Roman names may not be transcoded correctly. Applications needing localized names should treat these fields as best available values.