Adding real small caps to a font (smcp, c2sc)
Real small caps are unencoded .sc glyphs plus a smcp feature — not CSS-synthesized shrunk capitals. Generate 373 of them in one batch and wire the feature, on device.
Fonts with real small caps carry two things: a set of scaled-to-cap-height
capital glyphs under unencoded names (a.sc, b.sc, …), and a smcp
feature whose sub a by a.sc rules swap them in. Fake small caps — the ones
CSS synthesizes when the font lacks the feature — are just capitals shrunk by
the renderer, with stroke weights and proportions that never match. This
walkthrough builds the real thing on a font that had none: 373 small-cap
glyphs added to Noto Sans Display in one batch in
Font Studio, the smcp feature wired after, and
the result verified through the exported font's GSUB table and HarfBuzz.
Why the glyphs must be unencoded
The obvious hack — map the small capitals to the lowercase codepoints —
breaks text: searching, copying, and screen readers all read codepoints, and
a.sc at U+0061 turns every "a" into a capital letter as far as software is
concerned. The correct model keeps a at U+0061 unchanged and lets the
shaper swap in the small-cap glyph at render time. Hence: .sc glyphs exist
in the font but in no cmap entry, reachable only via GSUB. That's also
exactly how professional families (Adobe, Google's own releases) ship it.
Step 1 — generate the glyphs (one batch)
In the workspace, multi-select the Glyphs tab — Select All here grabs the
font's 373 Latin letters and composites — and tap Small Caps in the
selection bar. The planner reviews the selection and shows one tile per
letter — a → a.sc — with the scale it will use:

The scale rule matters: small caps are sized to a small-cap height derived from the font's own metrics (x-height plus a third of the gap to cap height in this implementation), not a blind 70%. For this font — x-height 536, cap height 714 — that's 595 units, an 83% scale, and the note on the planner says exactly that before anything is committed.
What the batch produces:
- One
a.sc-style glyph per letter — a capital glyph scaled to small-cap height, added as an unencoded glyph. - Selecting both cases of a letter is fine: the entry is keyed on the
lowercase codepoint, planned once (both
Aandapoint at the samea.sc—smcpswaps lowercase input; capitals staying capitals is thec2scflow). - A collision can't overwrite anything: a second
a.scbecomesa.sc.1.
Tapping Add 373 Small-Caps Glyph(s) runs the whole batch as one undo step. The glyph creation is free; wiring the feature (next step) is Pro.
Warning Small caps need the font's vertical metrics to size correctly — the planner needs x-height and cap height. Fonts missing those metrics get a fallback estimate, and the planner says so.
Step 2 — wire the feature
Font Engineering → OpenType → Create Feature, tag smcp (the chip is
offered), lookup type Substitution, then one rule per letter: sub a by a.sc, sub b by b.sc, … For capitals-only selections the tag is c2sc
(sub A by A.sc). Tap Apply N Edit(s).
Twenty-six rules through the sheet is fine; for larger scripts the same
OpenType screen has Import feature file, which takes the same rules as a
.fea text file — this walkthrough used exactly that for the 26-rule smcp
feature:
feature smcp {
sub a by a.sc;
sub b by b.sc;
# … one per letter
} smcp;
Step 3 — verify before shipping
The quickest check costs nothing: open the new smcp feature in Font
Engineering and tap Test in Preview — type a word like ham and watch it
render in small caps. The Preview tab shows the same shaping every app using
the font will perform.
If you want the exported file audited, three checks — all passing on the exported font:
- The feature exists and has the rules: fontTools dumps
GSUBwith ansmcpfeature carrying 26 single-substitution lookups. - The glyphs are unencoded:
a.scis in the font, in no cmap entry. - The proportions are right:
a.sc's advance is 503 units vsA's 604 — a 0.833 ratio, matching the planner's 83% promise.
And the shaping test — the thing users will actually experience — in six lines, if you want machine-level proof:
import uharfbuzz as hb
font = hb.Font(hb.Face(hb.Blob.from_file_path("NotoSansDisplay-sc.ttf")))
for feats in ({"smcp": True}, {"smcp": False}):
buf = hb.Buffer(); buf.add_str("ham")
buf.guess_segment_properties(); buf.set_feature(feats)
hb.shape(font, buf)
print([font.glyph_to_string(g.codepoint) for g in buf.glyph_infos])
# ['h.sc', 'a.sc', 'm.sc'] ← smcp on
# ['h', 'a', 'm'] ← smcp off
When to reach for fake small caps
Never for a font you control — but know what the fallback is doing:
font-variant-caps: small-caps on a font without smcp makes the renderer
synthesize (shrink) capitals on the fly. Browser output is passable at text
sizes; in print or design tools it's visibly wrong, and the rendering differs
per platform. If you own the font, ten minutes of smcp wiring beats all of
it. The
OpenType features reference
lists which features the app reads and writes.
What to try next
- The same anatomy with ligatures: How to add a ligature to a font
- Drawing cases where the scaled capital needs retooling (stroke contrast): Glyph Designer guide
- Shipping a subsetted web font with small caps intact: How to reduce font file size
Full documentation
