3 OpenType Data
dxtr edited this page 2026-10-18 16:07:14 +02:00

OpenType data

Read-only access to font data beyond shaping. Metrics are on Fonts and metrics, variations on Font variations, color tables on Color glyphs, and raw tables through face-table.

Names (name table)

face-name face name-id &key language → string or nil

A name string, decoded to a Lisp string. name-id is a keyword or an integer ID (fonts may define their own IDs from 256 up, e.g. for axis and instance names). language is a BCP 47 string; by default HarfBuzz picks the best available, preferring English. nil if the face has no such name.

(hb:face-name face :font-family)       ; => "DejaVu Sans"
(hb:face-name face :font-subfamily)    ; => "Book"
(hb:face-name face :postscript-name)   ; => "DejaVuSans"
(hb:face-name face 1 :language "en")   ; => "DejaVu Sans"
Keyword ID Keyword ID
:copyright 0 :license 13
:font-family 1 :license-url 14
:font-subfamily 2 :typographic-family 16
:unique-id 3 :typographic-subfamily 17
:full-name 4 :mac-full-name 18
:version-string 5 :sample-text 19
:postscript-name 6 :cid-findfont-name 20
:trademark 7 :wws-family 21
:manufacturer 8 :wws-subfamily 22
:designer 9 :light-background 23
:description 10 :dark-background 24
:vendor-url 11 :variations-ps-prefix 25
:designer-url 12

These keywords and the integers from 0 to #xFFFE make up the type name-id; anything else is a type-error.

For family names shown to users, prefer :typographic-family / :typographic-subfamily and fall back to :font-family / :font-subfamily when they are nil (as for DejaVu Sans Mono). IDs 1 and 2 follow the old four-style model (Regular, Italic, Bold, Bold Italic), so a family with more weights files its Light or Black styles under separate family names there; IDs 16 and 17 group them properly.

face-name-entries face → list of (name-id . language)

Every name the face has, with integer IDs and BCP 47 languages, so you can enumerate localized names:

(subseq (hb:face-name-entries face) 0 5)
;; => ((0 . "en") (1 . "en") (2 . "en") (3 . "en") (4 . "en"))

(loop for (id . lang) in (hb:face-name-entries face)
      when (= id 1) collect (cons lang (hb:face-name face 1 :language lang)))

Layout tables

face-script-tags face &key (table :gsub) → list of strings

face-feature-tags face &key (table :gsub) → list of strings

The OpenType script tags and feature tags in the face's GSUB or GPOS table (table :gsub or :gpos, the type layout-table). Feature tags are deduplicated, in first-seen order.

(hb:face-script-tags face)
;; => ("DFLT" "arab" "armn" "brai" "cans" "cher" "cyrl" "geor" "grek" "hani"
;;     "hebr" "kana" "lao " "latn" "math" "nko " "ogam" "runr" "tfng" "thai")
(hb:face-feature-tags face)
;; => (" RQD" "aalt" "case" "ccmp" "dlig" "fina" "hlig" "init" "liga" "locl"
;;     "medi" "rlig" "salt")
(hb:face-feature-tags face :table :gpos)
;; => ("kern" "mark" "mkmk")

Notes:

  • These are OpenType script tags (lowercase, sometimes padded: "lao ", "nko "; Indic scripts have v2 tags like "dev2"), not the ISO 15924 codes buffers use ("Laoo", "Nkoo", "Deva").
  • Use face-feature-tags to build a font-features UI, or to check before relying on a discretionary feature (smcp, onum, ss01, …): features the font lacks are silently ignored when shaping.
  • The lists cover every script and language system; a feature may exist only for some scripts.
  • " RQD" is a private tag some fonts use for required features.
  • Kerning in an old-style kern table (no GPOS) does not show up here, but HarfBuzz still applies it.

Raw tables

Anything else is one call away as octets:

(let ((os2 (hb:face-table face "OS/2")))
  ;; usWeightClass is the big-endian uint16 at offset 4.
  (+ (ash (aref os2 4) 8) (aref os2 5)))
;; => 400

See face-table-tags, face-table.