Typography & Text Rendering
Text is the part of a design people actually read. A logo can be vague and still work; a price, a phone number, or a paragraph of legal fine print cannot. This section teaches you how text really works inside a computer and on paper — from the letters you type, to the shapes that get drawn, to the pixels and ink that finally appear. We start with the most basic and most-confused idea: the difference between a character and a glyph.
1. Characters vs glyphs — the foundation
- Character
- The abstract meaning of a letter or symbol — the thing you type and store. The computer records it as a Unicode code point, a universal ID number. The capital letter A is
U+0041everywhere on Earth. - Glyph
- The actual drawn shape on screen or paper. One character can be drawn many different ways depending on the font.
The relationship between characters and glyphs is not one-to-one, which trips up almost every beginner:
- One character → many glyphs: the letter "a" can be drawn as a normal "a", a small-cap "A", or a fancy swash alternate.
- Many characters → one glyph: the two characters "f" and "i" often join into a single ligature shape "fi".
- One character → zero glyphs: a combining accent mark may have no standalone shape; it just modifies its neighbour.
Inside the font, a table called the cmap ("character map") translates each Unicode code point into a default glyph. Then the font's layout features can substitute or reposition glyphs from there.
.notdef, the "tofu" box □. A storefront brand that uses an emoji or a rare currency symbol in a font that lacks it will print a box, not the symbol.2. Font formats — what those file extensions really mean
A font is a file full of glyph outlines plus metric tables. Over 40 years the industry settled on a family of formats:
| Format | Outline math | Notes |
|---|---|---|
| PostScript Type 1 (.pfb/.pfm) | Cubic Bézier | Adobe, 1984. The deprecated ancestor — end-of-life in Adobe apps since Jan 2023; these files no longer render. You'll only meet them in old archives. |
| TrueType (.ttf) | Quadratic Bézier (B-spline) | Apple + Microsoft, late 1980s. Famous for powerful "bytecode" hinting. |
| OpenType (.otf or .ttf) | Either type | Adobe + Microsoft, 1996. The modern umbrella format. |
| WOFF / WOFF2 | Either | Compressed OpenType for the web. WOFF2 uses Brotli compression, ~30% smaller than WOFF. |
- Bézier curve
- A smooth curve defined by a few "control points" — the math used to describe every letter outline. Cubic curves use more control points (smoother, heavier); quadratic use fewer (lighter, faster).
The key teaching point about OpenType: OpenType is a container, not a competitor to TrueType. An OpenType font wraps either outline type: TrueType/quadratic outlines (stored in a glyf table, usually saved as .ttf) or PostScript/CFF cubic outlines (Compact Font Format, usually saved as .otf). So:
.ttf vs .otf tells you the outline type inside, not "TrueType vs OpenType." Both files are OpenType. OpenType added Unicode (65,536+ glyphs, vs the old 256-glyph limit), one cross-platform file for Mac and Windows, and the advanced layout engine described next.Variable fonts — one file, infinite weights
Added to OpenType in v1.8 (September 2016), a variable font stores a whole continuous "design space" in a single file. Instead of shipping 12 separate files (Light, Regular, Bold, …), one file interpolates between master designs along named axes (lowercase 4-character tags):
wght— Weight (1–1000)wdth— Width (percentage)ital— Italic (0 or 1)slnt— Slant (degrees, typically −90 to 90)opsz— Optical Size (in points)
Custom axes use UPPERCASE tags. Adoption climbed from ~11% of web pages in 2020 to ~33–34% in 2024 because one small file replaces dozens. For PF360's storefront, a variable font lets a theme nudge heading weight without downloading another file — fewer requests, faster pages.
3. The OpenType layout engine — how features work
Two tables do the clever typography, both keyed by 4-character feature tags:
- GSUB (Glyph Substitution)
- Decides which glyphs to use — swap "f"+"i" for the "fi" ligature, swap digits for small caps, etc.
- GPOS (Glyph Positioning)
- Decides where glyphs sit — kerning pairs, stacking accents onto letters.
The text shaper runs GSUB first, then GPOS (pick the glyphs, then position them). Useful tags:
- GSUB:
ligastandard ligatures (fi, fl — on by default),dligdiscretionary ligatures (st, ct — off by default),smcpsmall caps,onum/lnumoldstyle vs lining figures,fracfractions,salt/ssNNstylistic alternates/sets,swshswashes. - GPOS:
kernkerning,markaccent placement. Modern kerning lives in GPOS, not the old legacykerntable.
4. From outlines to pixels — the rendering pipeline
[glyph outline] -> scale to size -> apply HINTS
(Bézier pts) (ppem grid) (snap to grid)
| |
v v
RASTERIZE -> anti-alias / subpixel -> composite
(fill to pixels) (ClearType RGB ~3x) (on screen)
- em / UPM (units per em)
- The font's internal design grid. All measurements are in these units — typically 1000 (PostScript/CFF) or 2048 (TrueType) — then scaled by your point size.
- ppem (pixels per em)
- How many pixels tall the em box becomes at the chosen size. Small ppem = few pixels = hard to render cleanly.
- Rasterize
- Convert the vector outline into a grid of filled pixels.
Hinting
Hinting means instructions baked into the font that gently distort outlines to snap onto the pixel grid at small sizes / low resolution, so stems stay even and letters stay readable. TrueType hinting is a stack-based assembly bytecode run by an interpreter (a tiny virtual machine), stored in glyf/prep/fpgm tables — fonts like Verdana have thousands of hand-tuned lines. PostScript/CFF hinting is lighter and more automatic.
pdf-service (PDFKit + sharp at port 4000) renders at high resolution — don't burn time tuning hints there.5. Typographic geometry — the text-tool vocabulary
ascender --- b d l <- tops above x-height cap-height- B D H <- top of capitals x-height --- x o n e <- top of lowercase baseline === x o n e B D b d l <- glyphs sit here descender -- g p y <- tails below baseline
- Baseline
- The invisible line glyphs rest on.
- x-height
- Height of a lowercase "x". A tall x-height reads better at small sizes.
- Cap-height
- Top of capital letters (usually a touch below the ascender height).
- Ascender / Descender
- Parts rising above x-height (b, d, h, k, l) / dropping below baseline (g, j, p, q, y).
- Counter, bowl, overshoot
- The enclosed space in "o"; the round stroke of "b"; the tiny amount round glyphs exceed the lines so they look optically equal to flat ones.
The classic spacing trio (clear this up once and for all)
- Leading (line-spacing)
- Vertical distance from one baseline to the next. Named after the lead metal strips printers slid between lines. Rule of thumb: point size + 2–4 pt, or 120–150% of size (12 pt body → ~14.4–18 pt). Longer lines need more leading.
- Tracking (letter-spacing)
- Uniform horizontal space across a whole run/word/line. Tighten big display headlines; add some to CAPS and small caps; never heavily track lowercase body text.
- Kerning
- Space between one specific pair of letters — fixing optical gaps in "AV", "To", "WA", "Ty", "Yo", "LT". Metric kerning uses the font's GPOS pairs; optical kerning is computed by the app.
Workflow order: leading → tracking → kerning (macro to micro).
6. Print legibility — the numbers that matter
- Body text: 9–12 pt (10–11 pt is common). Around 8 pt is the floor where reading speed drops.
- Line length (measure): 50–75 characters per line, 66 ideal. Too long and the eye loses the line on return.
- Leading: 120–150% of size. Size, measure, and leading are coupled — change one, retune the others.
- Black ink: body/small text should print as 100% K only (single-channel black), not rich 4-color black — registration misalignment fuzzes small 4-color text. Reserve rich black for large display type.
- Avoid thin serifs and hairlines in reverse (white on dark) — ink gain fills the counters. Keep legal fine print to ~5–6 pt minimum.
- Embed or outline all fonts in the print PDF (PDF/X) so the print engine (RIP) never substitutes a wrong font. Ligatures and kerning must survive flattening.
7. PF360 designer text tool — the controls users see
The design studio (designer/) is built on Fabric.js. The text controls live in designer/src/views/Editor/CanvasRight/Components/ElementText.vue (toolbar) and ElementStylePanel/TextboxStylePanel.vue (style panel), with config lists in src/configs/texts.ts (FontSizeLibs, LineHeightLibs, CharSpaceLibs). Visible controls: font color picker; Font size +/−; Bold / Italic / Underline; horizontal align Left/Center/Right; vertical align Top/Middle/Bottom; and a "Spacing" row exposing line height and character spacing.
| UI label / control | Typographic term | Implementation note |
|---|---|---|
| Spacing → line height | Leading | Fabric lineHeight is a unitless multiplier (default 1.16) — relative leading, not absolute points. |
| Spacing → char spacing | Tracking | Fabric charSpacing is measured in 1/1000 em, not px or pt; PF360 steps it ±10. Per-character overrides are cleared (clearPerCharCharSpacing) so the global value applies uniformly. |
| (none) | Kerning | No true pairwise kerning UI — Fabric kerns by font metrics only. The "Spacing" control is tracking. |
- Characters are meaning, glyphs are shapes — the mapping is not 1:1; a missing glyph shows a □ tofu box.
- OpenType is the umbrella format.
.ttfvs.otf= which outline math is inside (quadratic/glyf vs cubic/CFF), not "TrueType vs OpenType." PostScript Type 1 is dead since 2023. - Variable fonts pack many weights/widths into one file along axes (
wght,wdth,ital,slnt,opsz) — fewer downloads for the storefront. - GSUB picks glyphs, GPOS positions them; rendering goes outline → scale → hint → rasterize → anti-alias. Hinting is a screen/low-DPI concern, not a print one.
- Leading is vertical, tracking is uniform horizontal, kerning is pairwise. Don't confuse them; work macro to micro.
- For print: 9–12 pt body, 50–75 chars/line, 120–150% leading, 100% K for small black text, embed fonts. PF360 has no auto RGB→CMYK or preflight — these are manual checks.
- In the PF360 designer, "Spacing" = Fabric
lineHeight(unitless leading multiplier) +charSpacing(tracking in 1/1000 em); there is no kerning control — label accordingly.